• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

[JASS] Spell Like Lightning Shield

Status
Not open for further replies.
Level 16
Joined
Mar 3, 2006
Messages
1,564
Is this a good script for making a spell like lightning shield


JASS:
library FireShield initializer Init
globals
    constant real t = 0.001
    constant integer charges = 3
    constant real r = 128
    constant real dA = 0.5
    constant real Shift = 360 / charges
    unit array u
    unit caster
    location centre
    timer RotSpd = CreateTimer()
    real A
endglobals
function Move takes nothing returns nothing
    local integer i = 0
    local real tempA = 0
    set tempA = A
    loop
        exitwhen i == charges
        call SetUnitPositionLoc( u[i] , PolarProjectionBJ( GetUnitLoc(caster) , r , tempA))
        set i = i + 1
        set tempA = tempA + Shift
    endloop
        set A = A + dA
    if A >= 360 then
        set A = 0
    endif
endfunction
function Create takes nothing returns nothing
    local integer i = 0
    local real tempA = 0
    call DisplayTextToPlayer(Player(0) , 0 , 0 , "WORKS")
    set centre = Location( 0 , 0 )
    set caster = CreateUnitAtLoc(Player(0) , 'Hblm' , centre , 0)
    set A = 0
 
    loop
        exitwhen i == charges
        set u[i] = CreateUnitAtLoc( Player(0) , 'e000' , PolarProjectionBJ(GetUnitLoc(caster) , r , tempA) , 0)
        set tempA = tempA + Shift
        set i = i + 1
    endloop
 
    call TimerStart(RotSpd , t , true , function Move)
endfunction
function Init takes nothing returns nothing
    local trigger t = CreateTrigger()
    call TriggerRegisterPlayerEvent( t , Player(0) , EVENT_PLAYER_END_CINEMATIC )
    call TriggerAddAction(t , function Create)
endfunction
endlibrary


I know there must be a struct and all that vJASS stuff but I am talking about the formula and functions in this spell are they correct for such spell ?
 
Level 11
Joined
Apr 6, 2008
Messages
760
JASS:
globals
    constant real t = 0.001
    constant integer charges = 3
    constant real r = 128
    constant real dA = 0.5
    constant real Shift = 360 / charges
    unit array u
    unit caster
    location centre
    timer RotSpd = CreateTimer()
    real A
endglobals

THIS IS ALSO PURE EVIL FOR A SPELL, its not even MPI
 
Level 16
Joined
Mar 3, 2006
Messages
1,564
OK. May be for some BJ functions they are evil.

JASS:
function DistanceBetweenPoints takes location locA, location locB returns real
    local real dx = GetLocationX(locB) - GetLocationX(locA)
    local real dy = GetLocationY(locB) - GetLocationY(locA)
    return SquareRoot(dx * dx + dy * dy)
endfunction

I think you will make the same functions if you are going to calculate a distance between 2 locations so whats the harm if I used this BJ function between 2 points, it will cost me the same time as when I use native functions.
 
Level 11
Joined
Apr 6, 2008
Messages
760
Locations are just pure evil, guess u have heard this before... use cordinations!

and also you have 2 location leaks

JASS:
set u[i] = CreateUnitAtLoc( Player(0) , 'e000' , PolarProjectionBJ(GetUnitLoc(caster) , r , tempA) , 0)

this leaks a location (GetUnitLoc(caster))

JASS:
call SetUnitPositionLoc( u[i] , PolarProjectionBJ( GetUnitLoc(caster) , r , tempA))

this also leaks a location (GetUnitLoc(caster))
 
Level 16
Joined
Mar 3, 2006
Messages
1,564
You mean that this function
JASS:
function DistanceBetweenPoints takes location locA, location locB returns real
    local real dx = GetLocationX(locB) - GetLocationX(locA)
    local real dy = GetLocationY(locB) - GetLocationY(locA)
    return SquareRoot(dx * dx + dy * dy)
endfunction
will take more time than if I make it using the natives that this function uses by myself ?
 
Level 16
Joined
Mar 3, 2006
Messages
1,564
Yes. For every native which takes locations, there is an equivalent native that uses coordinates instead. Use the coordinate versions.

If coordinates is always better than locations how can I use point w/ polar offset especially if this point is inside a loop or a timer; do I have to convert each point to r*cos θ and r*sin θ ?
 
Level 11
Joined
Apr 6, 2008
Messages
760
JASS:
X+'Distance'*Cos('Radiance')
Y+'Distance'*Sin('Radiance')

360 degrees in radiance is PI*2(6.28318) , And you want to create 3 'dummys' around the caster do this

JASS:
globals
    constant integer charges = 3
    constant real Radi = (bj_PI*2)/charges //Just we could in put 6.28318/charges, but this is just to show you :)
    //Using Radiance instead of degrees is faster because then we dont need to convert the degrees to radiance 'bj_DEGTORAD' constant that is
endglobals

//just to show how the loop works
   local real x = GetUnitX('Unit') //This is the X/Y cords of the unit they gonna spin around
   local real y = GetUnitY('Unit')
   //Cordinates are alot faster then location, they are faster to define and set, And they cannot leak! so dont need to be cleaned up

loop
    exitwhen i >= charges
    set u[i] = CreateUnit(Player(0),'e000',x+r*Cos(Radi*i),y+r*Sin(Radi*i),0)
    set i = i + 1
endloop

As Dreadnought[dA] said use Atan2 get to angle between 2 Cords

Here is a small example

JASS:
private function Action takes nothing returns nothing
    local unit u = GetTriggerUnit()
    local unit t = GetSpellTargetUnit()
    local real ux = GetUnitX(u)
    local real uy = GetUnitY(u)
    local real tx = GetUnitX(t)
    local real ty = GetUnitY(t)
    local real angle = Atan2(ty-uy,tx-ux) //atan2 returns radiance, u acctuly revers the x/y like i show you y2-y1 not y1-y2 this will return wrong angle
endfunction

You could allways add me on msn if you got some questions :) (ill send it in a private msg posting it here will be like getting raped by bots)

EDIT:

Here is also a Tut you can check out
http://www.hiveworkshop.com/forums/...vjass-practice-session-1-a-114519/#post982566
 
Last edited:
Level 16
Joined
Mar 3, 2006
Messages
1,564
don't use degress, get use to radiance instead :)

Degrees are much more easier than radian, at least for me, when dealing with rotation around a point.

But I have completed a bit of what I want but there are 2 problems.

[SOLVED]
First, the scripts I made has something wrong causing it not to be MUI.
I forgot to add
JASS:
if index == 0 then
  call PauseTimer(tmr)
endif
instead I wrote
JASS:
call PauseTimer(tmr)
[/SOLVED]

Second, the spell Lightning Shield isn't cummulative; so that every time it is cast it removes the first one and start anew. How can I make such thing -if possible- ?

JASS:
scope FireShield initializer Init
    private keyword Data
    globals
        private constant integer ABIL_ID = 'A002'
        private constant integer UNIT_ID = 'e002'
        private constant integer N = 3
        private constant real shift = 360.00 / N
        private constant real r = 128.00
        private constant real dA = 5
        private constant real INTERVAL = 0.03
        private timer tmr = CreateTimer()
        private integer index = 0
        private Data array temp_data
    endglobals
 
    private function Duration takes integer aldur returns real
        return aldur*6.
    endfunction
 
    private function Damage takes integer aldam returns real
        return aldam*10.
    endfunction
    private struct Data
        unit Caster
        unit array Charge[N]
        real A
        real temp_A
        real durtime
        timer dur = CreateTimer()
        static method rotate takes nothing returns nothing
            local Data d
            local integer i = 0
            local integer j
            local real X
            local real Y
            loop
                exitwhen i >= index
                set d = temp_data[i]
                if TimerGetRemaining(d.dur) > 0.00 then
                    set d.A = d.A + dA
                    if d.A >= 360 then
                        set d.A = d.A - 360
                    endif
                    set d.temp_A = d.A
                    set j = 0
                    loop
                        exitwhen j >= N
                        set X = GetUnitX(d.Caster) + r * Cos(d.temp_A * bj_DEGTORAD)
                        set Y = GetUnitY(d.Caster) + r * Sin(d.temp_A * bj_DEGTORAD)
                        call SetUnitPosition(d.Charge[j],X,Y)
                        call SetUnitFacing(d.Charge[j],d.temp_A + 90)
                        set d.temp_A = d.temp_A + shift
                        set j = j + 1
                    endloop
                else
                    set index = index - 1
                    set temp_data[i] = temp_data[index]
                    set i = i - 1
                    call d.destroy()
                endif
                set i = i + 1
            endloop
        endmethod
        static method create takes unit u returns Data
            local Data d = Data.allocate()
            local integer i = 0
            local real temp_A = 0
            local real X
            local real Y
            set d.Caster = u
            set d.durtime = Duration(GetUnitAbilityLevel(d.Caster,ABIL_ID))
            loop    
                exitwhen i >= N
                set X = GetUnitX(d.Caster) + r * Cos(temp_A * bj_DEGTORAD)
                set Y = GetUnitY(d.Caster) + r * Sin(temp_A * bj_DEGTORAD)
                set d.Charge[i] =CreateUnit(GetOwningPlayer(d.Caster),UNIT_ID,X,Y,temp_A + 90)
                set i = i + 1
                set temp_A = temp_A + shift
            endloop
            call TimerStart(d.dur,d.durtime,false,null)
            if index == 0 then
                call TimerStart(tmr,INTERVAL,true,function Data.rotate)
            endif
            set temp_data[index] = d
            set index = index + 1
            return d
        endmethod
 
        method onDestroy takes nothing returns nothing
            local integer i = 0
            call PauseTimer(tmr)
            loop
                exitwhen i >= N
                call KillUnit(.Charge[i])
                set.Charge[i] = null
                set i = i + 1
            endloop
        endmethod
    endstruct
    private function Conditions takes nothing returns boolean
        return GetSpellAbilityId() == ABIL_ID
    endfunction
    private function Actions takes nothing returns nothing
        call Data.create(GetTriggerUnit())
    endfunction
    private function Init takes nothing returns nothing
        local trigger t = CreateTrigger()
        call TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_SPELL_EFFECT)
        call TriggerAddCondition(t,Condition(function Conditions))
        call TriggerAddAction(t,function Actions)
    endfunction
endscope
 
Last edited:

Cokemonkey11

Spell Reviewer
Level 29
Joined
May 9, 2006
Messages
3,534
Because of the way a struct stack works you don't have to. You just add 1 to the stack!

in the future name all constants in CAPITALS.

What the hell is this for?

call TimerStart(d.dur,d.durtime,false,null)

Why do you even have that timer in your struct?

You're also pausing the timer on destroy no matter what. You need to check if the dbIndex is back to it's start value (0 in this case)
 
Well, you need a global HandleTable, and everytime you start a lightning shield you set table = YourStruct. When the spell ends you set table = 0. Now, to check if the unit already has the spell on it, do

JASS:
if table.exists(u) and table[u] > 0 then
    //the spell is already on the unit. The value of table[u] is the struct currently used by the unit
endif
 
Something like this...

JASS:
scope LightningShield initializer Init
    private keyword Data

    globals
        private constant real TIMER_INTERVAL = 0.035
        private constant real MAX_TIME = 10.00
        private HandleTable ht
        
        private timer tim = CreateTimer()
        private Data array dat
        private integer index = 0
    endglobals
    
    private function Execute takes nothing returns nothing
        local integer i = 0
        local Data d
        
        loop
            exitwhen i >= index
            
            set d = dat[i]
            set d.time = d.time + TIMER_INTERVAL
            
            if d.time >= MAX_TIME then
                call d.destroy()
            endif
            
            //rest of timer loop stuff here
            
            set i = i + 1
        endloop
    endfunction
    
    private struct Data
        unit u
        real time
        
        integer ID
        
        static method create takes unit u, real time returns
            local Data d = Data.allocate
            local Data d2
            
            if ht.exists(u) and ht[u] > 0 then
                set d2 = ht[u]
                call d2.destroy()
            endif
            
            set d.u = u
            set d.time = time
            set ht[u] = d
            
            //other create actions here...
            
            set dat[index] = d
            set d.ID = index
            
            if index == 0 then
                call TimerStart(tim, TIMER_INTERVAL, true, function Execute)
            endif
            
            set index = index + 1
            
            return d
        endmethod
        
        method onDestroy takes nothing returns nothing
            set ht[.u] = 0
            
            set index = index - 1
            set dat[.ID] = dat[index]
            set dat[.ID].ID = .ID
            
            if index == 0 then
                call PauseTimer(tim)
            endif
        endmethod
        
    endstruct
    
    private function LightningShield takes unit u, real time returns nothing
        call Data.create(u, time)
    endfunction
    
    private function Init takes nothing returns nothing
        set ht = HandleTable.create()
    endmethod
endscope
 
Level 16
Joined
Mar 3, 2006
Messages
1,564
I pasted you code in an empty trigger and saved but I got an error in the line
JASS:
if LightningShield___ht.exists(u) and LightningShield___ht[u] > 0 then
And the error says:
Code:
LightningShield___ht is not of a type that allows . syntax
so whats wrong ?

Even when I add the line
JASS:
private HandleTable ht
to the globals block and
JASS:
set ht = HandleTable.create()
to the Init function, I get the same error.

The script that give me the error I am saying looks like this:


JASS:
scope FireShield initializer Init
    private keyword Data
    globals
        private constant integer ABIL_ID = 'A002'
        private constant integer UNIT_ID = 'e002'
        private constant integer N = 3
        private constant real SHIFT = 360.00 / N
        private constant real r = 128.00
        private constant real dA = 5
        private constant real INTERVAL = 0.02
        private timer tmr = CreateTimer()
        private integer index = 0
        private Data array temp_data
        private HandleTable ht      // this is the line I added
    endglobals
//  
// the rest of the script ...
//
    private function Init takes nothing returns nothing
        local trigger t = CreateTrigger()
        set ht = HandleTable.create()      // and here is the last one
        call TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_SPELL_EFFECT)
        call TriggerAddCondition(t,Condition(function FS_Conditions))
        call TriggerAddAction(t,function FS_Actions)
    endfunction
endscope


Does it has something to do with my the version of my vJASS or the patch ?
 
*sigh*. This table library...

JASS:
library Table
//***************************************************************
//* Table object 2.0
//* ------------
//*
//*   set t=Table.create() - instanceates a new table object
//*   call t.destroy()     - destroys it
//*   t[1234567]           - Get value for key 1234567
//*                          (zero if not assigned previously)
//*   set t[12341]=32      - Assigning it.
//*   call t.flush(12341)  - Flushes the stored value, so it
//*                          doesn't use any more memory
//*   t.exists(32)         - Was key 32 assigned? Notice
//*                          that flush() unassigns values.
//*   call t.reset()       - Flushes the whole contents of the
//*                          Table.
//*
//*   call t.destroy()     - Does reset() and also recycles the id.
//*
//*   If you use HandleTable instead of Table, it is the same
//* but it uses handles as keys, the same with StringTable.
//*
//*  You can use Table on structs' onInit  if the struct is
//* placed in a library that requires Table or outside a library.
//*
//*  You can also do 2D array syntax if you want to touch
//* mission keys directly, however, since this is shared space
//* you may want to prefix your mission keys accordingly:
//*
//*  set Table["thisstring"][ 7 ] = 2
//*  set Table["thisstring"][ 5 ] = Table["thisstring"][7]
//*
//***************************************************************

//=============================================================
    globals
        private constant integer MAX_INSTANCES=8100 //400000

    //=========================================================
        private gamecache gc
    endglobals

    private keyword hack

    private struct GTable[MAX_INSTANCES]
        readonly string sthis

        static method create takes nothing returns GTable
         local GTable this = GTable.allocate()
             set this.sthis = SCOPE_PRIVATE + I2S(this)
         return this
        endmethod

        method reset takes nothing returns nothing
            call FlushStoredMission(gc,  this.sthis  )
        endmethod

        private method onDestroy takes nothing returns nothing
            call FlushStoredMission(gc,  this.sthis  )
        endmethod

        static method hack takes string s returns nothing
             set GTable(0).sthis  = s
        endmethod

        //=============================================================
        // initialize it all.
        //
        private static method onInit takes nothing returns nothing
            call FlushGameCache(InitGameCache("libtable.gc"))
            set gc=InitGameCache("libtable.gc")
        endmethod

    endstruct

    //Hey: Don't instanciate other people's textmacros that you are not supposed to, thanks.
    //! textmacro Table__make takes name, type, key
    struct $name$ extends GTable

        method operator [] takes $type$ key returns integer
            return GetStoredInteger(gc,  this.sthis  ,$key$)
        endmethod

        method operator []= takes $type$ key, integer value returns nothing
            call StoreInteger(gc,  this.sthis  ,$key$, value)
        endmethod

        method flush takes $type$ key returns nothing
            call FlushStoredInteger(gc,  this.sthis  ,$key$)
        endmethod

        method exists takes $type$ key returns boolean
            return HaveStoredInteger(gc,  this.sthis  ,$key$)
        endmethod

        static method flush2D takes string firstkey returns nothing
            call GTable.hack(firstkey) //inline friendly, I hope
            call $name$(0).reset()
        endmethod

        static method operator [] takes string firstkey returns $name$
            call GTable.hack(firstkey) //inline friendly, I hope
            return $name$(0)
        endmethod

    endstruct
    //! endtextmacro

    private function H2I takes handle h returns integer
        return h
        return 0
    endfunction
    
    //! runtextmacro Table__make("Table","integer","I2S(key)" )
    //! runtextmacro Table__make("StringTable","string","key" )
    //! runtextmacro Table__make("HandleTable","handle","I2S(H2I(key))" )

endlibrary
 
Level 11
Joined
Apr 6, 2008
Messages
760
Tabels get hashtables next patch <3

But ye EoW is right attach the struct to the unit with that like this

JASS:
scope TEST initializer init

globals
    private HandleTable Table
endglobals

struct Data
unit Caster
endstruct

function Action takes nothing returns nothing
    local Data dat
    local unit Caster = GetTriggerUnit()

    if Table.exists(Caster) then //this checks if the unit is saved allready
        set dat = Table[Caster] //We load the struct that is saved to the unit
        //the thing you want with the spell to happen (maybe destroy it?)
        //or you could "refresh" it set the damage for its current level and make so it have full duration ect ect
    endif

    //stuff to create the spell
    set dat = Data.create()
    set dat.Caster = Caster

    set Table[Caster] = dat //this saves the struct to the unit

endfunction

function init takes nothing returns nothing
//stuff
   set Table = HandleTable.create() //this create the table
endfunction

endscope
 
Last edited:
Status
Not open for further replies.
Top