• 🏆 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] Need help(code)

Status
Not open for further replies.
Level 8
Joined
Jul 28, 2008
Messages
211
I said i would post my code when I get to my computer, but I couldn't wait so I made a new one. Here's the code:
JASS:
scope Missle initializer Init

globals

    constant integer SPELL_ID = 'A000'
    constant integer UNIT_ID = 'h000'
    
    timer Tim
    
endglobals

struct Data

    unit u
    real cos
    real sin
    real angle

    static Data array Index
    static integer Total
    
    static method Move takes nothing returns nothing
        local Data dat
        local integer i = 0
        local real x
        local real y
        loop
            exitwhen i >= dat.Total
            set dat = dat.Index[i]
            set y = GetUnitY(dat.u) + 5 * dat.sin
            set x = GetUnitX(dat.u) + 5 * dat.cos
            call SetUnitX(dat.u, x)
            call SetUnitY(dat.u, y)
            call DestroyEffect( AddSpecialEffect( "Abilities\\Weapons\\LordofFlameMissile\\LordofFlameMissile.mdl", x, y) )
            call dat.destroy()
            set dat.Total = dat.Total - 1
            set dat.Index[i] = dat.Index[dat.Total]
        endloop
        if dat.Total == 0 then
            call PauseTimer(Tim)
        endif
    endmethod
    
    static method Start takes unit caster, real targetX, real targetY returns nothing
        local Data dat = Data.allocate()
        set dat.angle = bj_RADTODEG * Atan2(targetY - GetUnitY(caster), targetX - GetUnitX(caster))
        set dat.u = CreateUnit(GetOwningPlayer(caster), UNIT_ID, GetUnitX(caster), GetUnitY(caster), dat.angle)
        set dat.cos = Cos(dat.angle)
        set dat.sin = Sin(dat.angle)
        if dat.Total == 0 then
            call TimerStart(Tim, 0.035, true, function Data.Move )
        endif
        set dat.Index[dat.Total] = dat
        set dat.Total = dat.Total + 1
    endmethod
        
endstruct

private function Conditions takes nothing returns boolean
    return GetSpellAbilityId() == SPELL_ID
endfunction

private function Actions takes nothing returns nothing
    local unit c = GetTriggerUnit()
    local location l = GetSpellTargetLoc()
    local real x = GetLocationX(l)
    local real y = GetLocationY(l)
    call Data.Start(c, x, y)
    call RemoveLocation(l)
    set c = null
    set l = null
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 )
    
    set Tim = CreateTimer()
endfunction

endscope

Earlier I sad that i tried creating an effect at x and y and that it did move, but now, like the dummy unit, it doesn't. What's the problem here?

Thanks in advance!
 
Level 8
Joined
Jul 28, 2008
Messages
211
Oh yea, I remember that.

Lol yea I could make an If/Then/Else statement.

One last question: I tried to remove dat.destroy (yes, I know the struct wouldn't get destroyed and after 8191 casts it would stop working but I did it just to test would it work) and it still didn't work. Can you tell me why? Thanks for your help.
 
Level 29
Joined
Jul 29, 2007
Messages
5,174
Oh yea, I remember that.

Lol yea I could make an If/Then/Else statement.

One last question: I tried to remove dat.destroy (yes, I know the struct wouldn't get destroyed and after 8191 casts it would stop working but I did it just to test would it work) and it still didn't work. Can you tell me why? Thanks for your help.

You supposedly "remove" the current instance by

JASS:
set dat.Total = dat.Total - 1
set dat.Index[i] = dat.Index[dat.Total

So you also remove it, and also don't destroy it. Two bad things.
 
Level 8
Joined
Aug 4, 2006
Messages
357
There are several things wrong with this.
1. Every .035 seconds, all of the Data instances are destroyed.
2. In the Start method, you make .angle in degrees, and you use it in Cos/Sin. However, Cos/Sin take the angle in RADIANS. No doubt this is causing your missiles to end up at the wrong targets.
3. Make a struct real variable x and real variable y to store the current position of the missile.
4. Index is a terrible variable name for the static array. An "index" is a position in an array... replace that name with something like "dataArray".
5. Put the .035 in a constant real global called INTERVAL.
6. Make a constant real global called SPEED that stores the speed of your missiles in wc3 units per second.
7. Make the struct private, as well as all your global variables.
8. (relating to #2) There is a more efficient way to move missiles than saving the sin/cos of the angle. You can instead store how much x and how much y to move the missile each time the timer goes off. To do this, make two struct real variables "dx" and "dy". In your Start method, do:
JASS:
    static method Start takes unit caster, real targetX, real targetY returns nothing
        local Data dat = Data.allocate()
        local real distanceX
        local real distanceY
        local real distance
        local real angle
        set dat.x = GetUnitX(caster)
        set dat.y = GetUnitY(caster)
        set distanceX = targetX - dat.x
        set distanceY = targetY - dat.y
        set distance = SquareRoot(distanceX*distanceX + distanceY*distanceY)
        set dat.dx = distanceX/distance*SPEED*INTERVAL
        set dat.dy = distanceY/distance*SPEED*INTERVAL
        set angle = bj_RADTODEG*Atan2(distanceY, distanceX)
        set dat.u = CreateUnit(GetOwningPlayer(caster), UNIT_ID, dat.x, dat.y, angle)
        if dat.Total == 0 then
            call TimerStart(Tim, INTERVAL, true, function Data.Move )
        endif
        set dat.dataArray[dat.Total] = dat
        set dat.Total = dat.Total + 1
    endmethod
These small precalculations are faster than Cos and Sin, and now your Move method can now set dat.x = dat.x + dat.dx without multiplying.
 
Level 8
Joined
Jul 28, 2008
Messages
211
About the global SPEED and INTERVAL things, I didn't make them cuz this was just practice, I just wanted to see if I can make a system like this and make it work.

. In the Start method, you make .angle in degrees
My code:
set dat.angle = bj_RADTODEG * Atan2(targetY - GetUnitY(caster), targetX - GetUnitX(caster))


8. (relating to #2) There is a more efficient way to move missiles than saving the sin/cos of the angle. You can instead store how much x and how much y to move the missile each time the timer goes off. To do this, make two struct real variables "dx" and "dy". In your Start method, do:

Thanks :)

EDIT:I did everything as u said, but it still doesn't move. Still haven't found the mistake.
 
Level 8
Joined
Jul 28, 2008
Messages
211
New code:
JASS:
scope Missle initializer Init

globals

    constant integer SPELL_ID = 'A000'
    constant integer UNIT_ID = 'h000'
    constant real SPEED = 800
    constant real INTERVAL = 0.035
    
    timer Tim
    
endglobals

struct Data

    unit u
    real x
    real y
    real dx
    real dy

    static Data array Index
    static integer Total
    
    static method Move takes nothing returns nothing
        local Data dat
        local integer i = 0
        local real x
        local real y
        loop
            exitwhen i >= dat.Total
            set dat = dat.Index[i]
            set dat.x = dat.x + dat.dx
            set dat.y = dat.y + dat.dy
            
            call SetUnitX(dat.u, dat.x)
            call SetUnitY(dat.u, dat.y)
            
            call DestroyEffect( AddSpecialEffect( "Abilities\\Weapons\\LordofFlameMissile\\LordofFlameMissile.mdl", dat.x, dat.y) )
            
            set dat.Total = dat.Total - 1
            set dat.Index[i] = dat.Index[dat.Total]
        endloop
        if dat.Total == 0 then
            call PauseTimer(Tim)
        endif
    endmethod
    
    static method Start takes unit caster, real targetX, real targetY returns nothing
        local Data dat = Data.allocate()
        local real angle
        local real distanceX
        local real distanceY
        local real distance
        set dat.x = GetUnitX(caster)
        set dat.y = GetUnitY(caster)
        set distanceX = targetX - dat.x
        set distanceY = targetY - dat.y
        set distance = SquareRoot(distanceX * distanceX - distanceY * distanceY)
        set dat.dx = distanceX/distance*SPEED*INTERVAL
        set dat.dy = distanceY/distance*SPEED*INTERVAL
        set angle = bj_DEGTORAD * Atan2(distanceX, distanceY)
        
        set dat.u = CreateUnit(GetOwningPlayer(caster), UNIT_ID, GetUnitX(caster), GetUnitY(caster), angle)
        if dat.Total == 0 then
            call TimerStart(Tim, INTERVAL, true, function Data.Move )
        endif
        set dat.Index[dat.Total] = dat
        set dat.Total = dat.Total + 1
    endmethod
        
endstruct

private function Conditions takes nothing returns boolean
    return GetSpellAbilityId() == SPELL_ID
endfunction

private function Actions takes nothing returns nothing
    local unit c = GetTriggerUnit()
    local location l = GetSpellTargetLoc()
    local real x = GetLocationX(l)
    local real y = GetLocationY(l)
    call Data.Start(c, x, y)
    call RemoveLocation(l)
    set c = null
    set l = null
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 )
    
    set Tim = CreateTimer()
endfunction

endscope

I did set the movment speed to 1.

EDIT:I don't know where the problem is, but i noticed that it doesn't run the Move method. I tried adding a debug message to it, and it doesn't appear. I also added the msg to If dat.Total == 0 (where it starts the timer) and it never appears. But it does appear if i put it right after locals in the Start method
 
Level 8
Joined
Aug 4, 2006
Messages
357
1. static Data array Index-->static Data array dataArray. Seriously, Index is a terrible name for an array.
2. static integer Total-->static integer Total = 0 (I think this was your main problem).
3. delete local real x and local real y They're not needed anymore.
4. put
JASS:
set dat.Total = dat.Total - 1
set dat.Index[i] = dat.Index[dat.Total]
if dat.Total == 0 then
     call PauseTimer(Tim)
endif
in an onDestroy method.
5. Figure out a way to know when the missile reaches its destination. Call dat.destroy() when it does. I know an efficient way to do this, but I feel like you won't learn anything from me telling you all the answers.
6. SquareRoot(distanceX * distanceX - distanceY * distanceY) AAA! That should be a + not a -.
7. bj_DEGTORAD * Atan2(distanceX, distanceY) Yikes! Atan2 returns an angle in radians. We want to convert that angle to degrees, thus we need to use bj_RADTODEG.

The rest looks good. Good work, and good job avoiding the temptation to copy/paste my code directly. Writing your own code is how you learn.
 
Level 8
Joined
Jul 28, 2008
Messages
211
put
set dat.Total = dat.Total - 1set dat.Index = dat.Index[dat.Total]if dat.Total == 0 then call PauseTimer(Tim)endif
in an onDestroy method.


Hmm....how do i do that? i can't put dataArray there cuz i don't have i in this method. Any solutions?

. Figure out a way to know when the missile reaches its destination. Call dat.destroy() when it does. I know an efficient way to do this, but I feel like you won't learn anything from me telling you all the answers
That's ok, I wouldn't use your code anyway. I hate using other ppl's stuff and I think that it helps me learn when i figure stuff on my own.

6. SquareRoot(distanceX * distanceX - distanceY * distanceY) AAA! That should be a + not a -.
:grin:

bj_DEGTORAD * Atan2(distanceX, distanceY)
:grin: again

The rest looks good. Good work, and good job avoiding the temptation to copy/paste my code directly. Writing your own code is how you learn.
Like I said, I hate using other ppl's text. But i don't mind if I type it by myself. But I do hate copying it.

Thanks. I'll see if it works now.

EDIT:Well, tested it and.....IT WORKS!!!! I just need a way to pu that stuff in an onDestroy method and it'll work perfectly.

Thanks guys!
 
Level 8
Joined
Aug 4, 2006
Messages
357
Hmm....how do i do that? i can't put dataArray there cuz i don't have i in this method. Any solutions?


Refer to this spell template and you can figure out how (hint: it has to do with "ID").
JASS:
scope SpellTemplate initializer Init

private keyword Data

globals
    private constant real TIMER_INTERVAL = 0.035
    private constant integer ABIL_ID = 'A000' //change this to the rawcode of your spell
    //Other constant globals here...


    private Data array data
    private integer index=0
    private timer tim

    //Other changeable globals here...

endglobals

private function Execute takes nothing returns nothing
    local integer i=0
    local Data d
    //Other local variables here...


    loop
        exitwhen i>=index

        set d=data[i]

        //Code to run each timer execution here...


        set i=i+1
    endloop
    //Any leak cleaning code here...

endfunction

private struct Data
    //Declare struct members here...
    unit caster

    integer ID

    static method create takes unit caster returns Data //make sure you change this based on what data you need
        local Data d = Data.allocate()

        //Other create code here...


        set data[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 index=index-1
        set data[this.ID]=data[index]
        set data[this.ID].ID=this.ID

        if index==0 then
            call PauseTimer(tim)
        endif
        //Other destroy code here (such as leak cleaning, removing units etc)...

    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()) //make sure you change this based on what data your spell needs
endfunction

private function Init takes nothing returns nothing
    local trigger t = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
    call TriggerAddAction(t, function Actions)
    call TriggerAddCondition(t, Condition(function Conditions))

    set tim = CreateTimer()
    //Other initialisation things here...

endfunction

endscope
 
Status
Not open for further replies.
Top