• Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
  • Read Evilhog's interview with Gregory Alper, the original composer of the music for WarCraft: Orcs & Humans 🔗Click here to read the full interview.

[JASS] Charge Script Help

Status
Not open for further replies.
Level 3
Joined
Apr 7, 2007
Messages
48
Ok before I post the actual code this is my absolute first time using JASS. I did this in GUI and couldnt get it to be MUI, so i decided to have a go at this. So don't bite my head off if there are a thousand things wrong with it *whimpers*
JASS:
function Charge_Move takes unit u, unit t returns nothing
    local location p = GetUnitLoc(u)
    local location o = GetUnitLoc(t)
    local real d = DistanceBetweenPoints(p,o)
    local integer i = 0
    call PauseUnit(u,TRUE)
    call SetUnitPathing(u,FALSE)
    call SetUnitFacing(u,AngleBetweenPoints(p,o))
    loop
        exitwhen i==10
        call SetUnitPositionLoc(u,PolarProjectionBJ(p,d/10,GetUnitFacing(u)))
        set p = GetUnitLoc(u)
        set i = i+1
        call TriggerSleepAction(.1)
    endloop
    call PauseUnit(u,FALSE)
    call SetUnitPathing(u,TRUE)
    call IssueTargetOrder(u, "attack", t)
    call RemoveLocation(p)
    call RemoveLocation(o)    
endfunction

Its supposed to take a unit, charge it in a straight line at another unit ignoring collisons and stuff, be MUI, and hopefully leak free. It works perfectly fine but I want to know how to get a smoother motion for the charge and anything else horribly incorrect with my script. Also, I don't have to worry about the target unit moving, its stunned, nor do i care if it does move.
 
Level 11
Joined
Aug 25, 2006
Messages
971
First and Worst. Don't use Polar Projection BJ it leaks a location every time. Instead simply copy and paste what it does into your script and cover the location leak yourself. I'm looking more into it presently. For smoother motion You could just make it loop more, that would be an option. Also don't use TriggerSleepAction, it has a minimum wait time of about .1(even if you set it lower)

Here it is.
JASS:
globals
    unit AttackMe
    unit Attacker
    integer LoopAmount
    real UAngleFace
endglobals

function Charge_Move_Timer takes nothing returns nothing
local timer Sk = GetExpiredTimer()
local real Ms1x = GetUnitX(Attacker) + 3 * Cos(UAngleFace * bj_DEGTORAD) //Replaces PolarProjectionBJ
local real Ms1y = GetUnitY(Attacker) + 3 * Sin(UAngleFace * bj_DEGTORAD)  //I used more locals then I had to so that my code is easier to follow (at least to me) 
    call SetUnitX(Attacker,Ms1x) //A quick native for moving units (very quick) doesn't cancel orders
    call SetUnitY(Attacker,Ms1y)
    call SetUnitFacing(Attacker, UAngleFace) //Stupid blizzard made it impossible to change a units facing pos. instantly
    set LoopAmount = LoopAmount - 1
    if LoopAmount == 0 then //Ok were there, turn off spell stuff
        call PauseUnit(Attacker,FALSE)
        call SetUnitPathing(Attacker,TRUE)
        call IssueTargetOrder(Attacker, "attack", AttackMe)
        call DestroyTimer(Sk) //Destroy timer
        set Sk = null //Null timer
    endif        
endfunction

function NewCharge_Move takes unit u, unit t returns nothing
local timer M = CreateTimer()
local real Ka
local real Ms1x = GetUnitX(t) - GetUnitX(u) //Replaces DistanceBetweenPoints
local real Ms1y = GetUnitY(t) - GetUnitY(u)
    set UAngleFace = bj_RADTODEG * Atan2(GetUnitY(t) - GetUnitY(u), GetUnitX(t) - GetUnitX(u)) //Replaces AngleBetweenPoints
    set AttackMe = t //Set up global variables
    set Attacker = u
    call PauseUnit(u,TRUE)
    call SetUnitPathing(u,FALSE)
    set LoopAmount = (R2I(SquareRoot(Ms1x * Ms1x + Ms1y * Ms1y)/3) - 25) //Set amount of times to loop (The -25 is for effect, it makes the unit stop a little infront of target)
    call TimerStart(M,.003,true,function Charge_Move_Timer) //This is much faster then TriggerSleepAction
endfunction
The timer has a minimum time of .001 while TriggerSleepAction has a minimum of about .1

+Smooth movement
+Doesn't Leak (Unless I missed something)
-Is not MUI
-It uses globals

To make this MUI you need to use attachables. Attachables are variables which you can virtually glue to a almost any type of variable. I don't have time to go over them now. I'll explain it tomorrow. Its kind of complex.
 
Last edited:
Level 11
Joined
Feb 18, 2004
Messages
394
TriggerSleepAction minimum ~ 0.27
Timer minimum ~ 0.00

Those "Attachables" are known as Local Handle Vars, the examples of usage can be viewed here and here. Bomber doesn't have to explain you anything, KaTTaNa explained it well enough. Just practice a bit with them and you'll figure out how they work.

#1: TriggerSleepAction(0) works just fine. It tells the thread to sleep the minimal ammount of time it can, esentally running the next time no other thread is running.

#2: Timers with a 0 timeout work the same way. its how a passive mana shield works. Damage events happen right before damage is delt, so increase max life beyond the damage delt, start a 0 timeout timer, in the callback, decrease max life to where it was before and subtract damage from mana.

#3: Local Handle Variables is a flawed system due to I2H() usage. Casting from a type-safe type to a type-unsafe type (handle to int) is all good and fine, but when you cast back, you get issues. vJASS solves the problem of attatching things to handles in a user-friendly way via structs. Structs can be cast to integers and back safely, thus you only have to attatch an integer to a unit, which at most would be an H2I call. Link in my sig to JASS NewGen, the easyest way to use vJASS. (includes the JASS Helper manual which explains all of vJASS's extensions to JASS)
 
Level 11
Joined
Aug 25, 2006
Messages
971
Your wrong, it still waits at least .1 even if the wait is set to zero. Check the map.

Anyway here we go. I made it with the magical structs!
JASS:
struct SpellStuff  //By god, this isn't a gobal! BEHOLD A STRUCT! (You will need vJass)
    unit AttackMe
    unit Attacker
    integer LoopAmount
    real UAngleFace
endstruct

function Charge_Move_Timer takes nothing returns nothing
local timer Sk = GetExpiredTimer() //Grab the timer so we can get attachable
local SpellStuff RA = GetAttachedInt(Sk,"AnythingIWant") //Get that attachable
local real Ms1x = GetUnitX(RA.Attacker) + 3 * Cos(RA.UAngleFace * bj_DEGTORAD) //Replaces PolarProjectionBJ
local real Ms1y = GetUnitY(RA.Attacker) + 3 * Sin(RA.UAngleFace * bj_DEGTORAD)  //I used more locals then I had to so that my code is easier to follow (at least to me) 
    call SetUnitX(RA.Attacker,Ms1x) //A quick native for moving units (very quick) doesn't cancel orders
    call SetUnitY(RA.Attacker,Ms1y)
    call SetUnitFacing(RA.Attacker, RA.UAngleFace) //Stupid blizzard made it impossible to change a units facing pos. instantly
    set RA.LoopAmount = RA.LoopAmount - 1
    if RA.LoopAmount == 0 then //Ok were there, turn off spell stuff
        call PauseUnit(RA.Attacker,FALSE)
        call SetUnitPathing(RA.Attacker,TRUE)
        call IssueTargetOrder(RA.Attacker, "attack", RA.AttackMe)
        call AttachInt(Sk,"AnythingIWant", 0) //Destroy attachable
        call SpellStuff.destroy(RA) //Destroy struct
        call ReleaseTimer(Sk) //A handy function by Vexorian (CsSaftey)
    endif        
endfunction

function NewCharge_Move takes unit u, unit t returns nothing
local SpellStuff KA = SpellStuff.create() //This creates the struct
local timer M = NewTimer() //Part of CsSaftey
local real Ka
local real Ms1x = GetUnitX(t) - GetUnitX(u) //Replaces DistanceBetweenPoints
local real Ms1y = GetUnitY(t) - GetUnitY(u)
    set KA.UAngleFace = bj_RADTODEG * Atan2(GetUnitY(t) - GetUnitY(u), GetUnitX(t) - GetUnitX(u)) //Replaces AngleBetweenPoints
    set KA.AttackMe = t //All the values are contained IN each struct
    set KA.Attacker = u 
    call PauseUnit(u,TRUE)
    call SetUnitPathing(u,FALSE)
    set KA.LoopAmount = (R2I(SquareRoot(Ms1x * Ms1x + Ms1y * Ms1y)/3) - 25) //Set amount of times to loop (The -25 is for effect, it makes the unit stop a little infront of target)
    call TimerStart(M,.003,true,function Charge_Move_Timer) //This is much faster then TriggerSleepAction
    call AttachInt(M,"AnythingIWant", KA) //This glues the struct (structs are ints) to the timer
endfunction
This uses vJass structs. It also requires Vexorian's CSCache and CSSaftey (Found in the Caster System) Notice how the struct contains all the variables. I only used one handle.
 

Attachments

  • JactiksDtest.w3m
    12.4 KB · Views: 83
Level 20
Joined
Apr 22, 2007
Messages
1,960
You could attach the struct to the timer without CSCache...
http://www.hiveworkshop.com/forums/member.php?u=118243
Look at this guy's signature, it has a neat function for attaching integers to timers (the struct is just an integer 'pointing' to an array's index). I'm not sure if this part is needed, but it definitely makes the code nicer:
JASS:
//Typecasting structs-to-integers:
struct MyStruct
    unit u
    real r
endstruct

function MyFunction takes nothing returns nothing
    local MyStruct ms=MyStruct.create()
    set ms.u=GetTriggerUnit()
    set ms.r=300.0
    call SetUnitUserData(ms.u, integer(ms) ) // See the integer(ms) part. That's typecasting to an integer.
    // The same is true for typecasting to a struct. Instead of the 'integer(ms)' part, you would write 'MyStruct(someinteger)'.
endfunction
 
Level 11
Joined
Aug 25, 2006
Messages
971
Thats interesting. I've never seen that before. Has a very limited timer range, but it'll work in this case. Hmmm let me do a speed comparison. (I'll post results in about an hour, I'm kinda busy)
 
Level 11
Joined
Aug 25, 2006
Messages
971
Well, no matter what I do, I can't seem to get a time reading. I remember somewhere, someone built a function specifically for time reading, but I can't remember where. Also gamecache is notoriously slow, so I'll just take your word for it. Even though one call gamecache barley makes a difference on performance.

I don't quite understand the method he uses, (for the timer-int attachment) but I'm willing to bet it has something to do with the fundamental way signed integers work.
 
Last edited:
Level 11
Joined
Aug 25, 2006
Messages
971
Ok I updated my previous code with the few suggestions.

Here it is:
JASS:
struct SpellStuff  //By god, this isn't a gobal! BEHOLD A STRUCT! (You will need vJass)
    unit AttackMe
    unit Attacker
    integer LoopAmount
    real UAngleFace
endstruct

//Super wierd int2timer attachment method made by DiscipleOfLife
function TimerStartWithUserData takes timer t, real timeout, integer data, boolean periodic, code handlerFunc returns nothing 
call TimerStart(t, timeout+data/67108871., periodic, handlerFunc) 
endfunction 
function GetTimerUserData takes timer t, real timeout returns integer 
return R2I((TimerGetTimeout(t)-timeout)*67108871.+0.5) 
endfunction

function Charge_Move_Timer takes nothing returns nothing
local timer Sk = GetExpiredTimer() //Grab the timer
local SpellStuff RA = GetTimerUserData(Sk,.003) //Get that struct
local real Ms1x = GetUnitX(RA.Attacker) + 3 * Cos(RA.UAngleFace * bj_DEGTORAD) //Replaces PolarProjectionBJ
local real Ms1y = GetUnitY(RA.Attacker) + 3 * Sin(RA.UAngleFace * bj_DEGTORAD)  //I used more locals then I had to so that my code is easier to follow (at least to me) 
    call SetUnitPosition(RA.Attacker,Ms1x,Ms1y) //Replaced with something that DOES cancel orders (also native)
    call SetUnitFacing(RA.Attacker, RA.UAngleFace) //Stupid blizzard made it impossible to change a units facing pos. instantly
    set RA.LoopAmount = RA.LoopAmount - 1
    if RA.LoopAmount == 0 then //Ok were there, turn off spell stuff
        call PauseUnit(RA.Attacker,FALSE)
        call SetUnitPathing(RA.Attacker,TRUE)
        call IssueTargetOrder(RA.Attacker, "attack", RA.AttackMe)
        call AttachInt(Sk,"AnythingIWant", 0) //Destroy attachable
        call SpellStuff.destroy(RA) //Destroy struct
        call ReleaseTimer(Sk) //A handy function by Vexorian (CsSaftey)
    endif        
endfunction

function NewCharge_Move takes unit u, unit t returns nothing
local SpellStuff KA = SpellStuff.create() //This creates the struct
local timer M = NewTimer() //Part of CsSaftey
local real Ka
local real Ms1x = GetUnitX(t) - GetUnitX(u) //Replaces DistanceBetweenPoints
local real Ms1y = GetUnitY(t) - GetUnitY(u)
    set KA.UAngleFace = bj_RADTODEG * Atan2(GetUnitY(t) - GetUnitY(u), GetUnitX(t) - GetUnitX(u)) //Replaces AngleBetweenPoints
    set KA.AttackMe = t //All the values are contained IN each struct
    set KA.Attacker = u 
    call PauseUnit(u,TRUE)
    call SetUnitPathing(u,FALSE)
    set KA.LoopAmount = (R2I(SquareRoot(Ms1x * Ms1x + Ms1y * Ms1y)/3) - 25) //Set amount of times to loop (The -25 is for effect, it makes the unit stop a little infront of target)
    call TimerStartWithUserData(M,.003,integer(KA),true,function Charge_Move_Timer) //Super wierd timer attachment method
endfunction
Hope this helps you!
 
Level 13
Joined
Nov 22, 2006
Messages
1,260
JASS:
//Typecasting structs-to-integers:
struct MyStruct
    unit u
    real r
endstruct

function MyFunction takes nothing returns nothing
    local MyStruct ms=MyStruct.create()
    set ms.u=GetTriggerUnit()
    set ms.r=300.0
    call SetUnitUserData(ms.u, integer(ms) ) // See the integer(ms) part. That's typecasting to an integer.
    // The same is true for typecasting to a struct. Instead of the 'integer(ms)' part, you would write 'MyStruct(someinteger)'.
endfunction

Well, that kinda sucks since it's not MUI.
 
Level 20
Joined
Apr 22, 2007
Messages
1,960
I don't think that really is the meaning of MUI, but bleh.

Like I said, that was just an example... Here's another one and maybe you'll be happy:
JASS:
globals
    real array Silvenon
endglobals
struct MaiStructz
    unit u
endstruct
function MaiFunctionLul takes nothing returns nothing
    local MaiStructz m=MaiStructz.create()
    set m.u=GetTriggerUnit()
    set Silvenon[integer(m)]=GetUnitFacing(m.u)
endfunction
Happy now? I even used your username in it! =D
 
Level 13
Joined
Nov 22, 2006
Messages
1,260
Happy now? I even used your username in it! =D

Yeah, I'm happy..... :)

Arghhh I know what MUI means, you don't have to be a living dictionary all the time, Pooty. It's when there can be multiple instances of something in the same time without interfering (using globals can't be MUI if there are waits, only locals blabla...).

A code is worth a thousand words:

JASS:
struct CrapData
    real crap
endstruct

struct UnitData
    unit crapcarrier
endstruct

function ItIsntMUI_Execute takes nothing returns nothing
    local timer t = GetExpiredTimer()
    local UnitData ud = UnitData(GetHandleInt(t, "ud"))
    local CrapData cd = CrapData(GetUnitUserData(ud.u)) // i got the wrong struct!! my crappy real is the wrong one
                                                        // because the struct i wanted was overwritten!!
    call DoSomethingWithCrap(cd.crap)    // wrong crap!
    set t = null
endfunction

function ItIsntMUI takes nothing returns nothing
    local UnitData ud = UnitData.create()
    local CrapData cd = CrapData.create()
    local timer t = CreateTimer()
    set ud.crapcarrier = GetTriggerUnit() // 1st initiation: THE SAME UNIT! // 2nd initiation: STILL THE SAME UNIT!
    set cd.crap = GetRandomCrappyReal(0, 5)
    call SetUnitUserData(ud.crapcarrier, integer(cd)) // 1st initiation: everything ok // 2nd initiation: the previous struct is overwritten!!!
    call SetHandleInt(t, "ud", ud)
    call TimerStart(t, 5, false, function ItIsntMUI_Execute)
    set t = null
endfunction

// 2nd initiation happens two seconds after the 1st one
 
Status
Not open for further replies.
Top