Spell Simple Dash

Trollvottel

never aging title
Reaction score
262
SIMPLE DASH, made by Trollvottel.....


DESCRIPTION:
- The caster dashes to the target point
- Units standing in his way will be dragged with him
- At the target point the caster (well actually a dummy) stomps the ground and stunns all units
near him


Screenshot( it's bad, look at it ingame :p):
neubitmapnr6.jpg


Code (hope its not too bad, the first jass code i have written since month...):

JASS:

scope SimpleDash initializer Init

    //=================================================================================================
    //So thats my Simple Dash Spell
    // DESCRIPTION:
    //    - The caster dashes to the target point
    //    - Units standing in his way will be dragged with him
    //    - At the target point the caster (well actually a dummy) stomps the ground and stunns all units
    //      near him
    //==================================================================================================
    // HOW TO IMPORT:
    //    - Import the custom Buff
    //    - Import the custom Abilities ( the caster spell and the dummy spell)
    //    - Import the fade dummy
    //    - Import the Fading System
    //    - Import this trigger
    //    - Change the values below
    //==================================================================================================

    globals
        // The rawcode of the Spell your hero is casting    
        private constant integer SpellId = 'A000'
        // The rawcode of the Unit which is created as Fade-Dummy
        private constant integer UnitID = 'h000'
        // The rawcode of your caster dummy, you can use your own dummy if you alread have one
        private constant integer DummyCasterId = 'h001'
        // The rawcode of the spell casted by the caster dummy, should be an instant spell (without target)
        private constant integer DummyCastSpellId = 'A001'
        // Life time of the fading dummys
        private constant real Dur = 0.6
        // Speed the caster is dashing forward with
        private constant real DashSpeed = 800.
        // Radius of the Units to be picked and takes with the caster
        private constant real PickRadius = 180.
        // Frequence of the timer = 1 / Period
        private constant real Period = 0.03125
        // Creates a new fade dummy every XXX ticks
        private constant integer NewEffectEvery = 5
        // Animation played by the fade dummys
        private constant string Animation = "attack"
        // Animationspeed of the fade dummys
        private constant real AnimationSpeed = 0.6
        // Path of the effect which is attached to the caster while dashing
        private constant string AttachedEffectPath = "Abilities\\Spells\\Other\\CrushingWave\\CrushingWaveMissile.mdl"
        // Path of the effect which is created at the end of dashing
        private constant string SpecialEndEffectPath = "Objects\\Spawnmodels\\Naga\\NagaDeath\\NagaDeath.mdl"
        // Attachment point for the AttachedEffectPath
        private constant string AttachedEffectPoint = "origin"
        // Order string for the dummy spell which is casted at the end of dashing
        private constant string DummyCastOrderString = "stomp"
    endglobals
    
    
   // ==================================================================================================
   // END OF EDITING ZONE !!!! END OF EDITING ZONE !!!! END OF EDITING ZONE !!!! END OF EDITING ZONE !!! 
   // ==================================================================================================
   // (has some comments, so jass beginners can learn from it)
    
    private struct SimpleDash 
        // struct members
        unit which
        integer ticks = 0
        real xSpeed
        real ySpeed
        real cx
        real cy
        real facing
        effect e
    endstruct
    
     globals
        // needed for the timer loop
        private integer N = 0 // number of struct instances at the moment
        private SimpleDash array D // thats where we store all the values in
        private integer MaxAlloc = 0 // number of max amount of structs allocated at the same time
        private timer T = CreateTimer()
    endglobals
    
    // some filter which checks if the filter unit meets some conditions
    private function IsGroundUnit takes nothing returns boolean
        local unit g = GetFilterUnit()
        local boolean b = IsUnitType(g, UNIT_TYPE_STRUCTURE) == false and IsUnitType(g, UNIT_TYPE_FLYING) == false and IsUnitType(g, UNIT_TYPE_MAGIC_IMMUNE) == false
        set g = null
        return b
    endfunction
    
    // function which gets units in the front of the caster, dont want to explain how it works...
    private function UnitsInFront takes unit which, real radius returns group
        local real x = GetUnitX(which)
        local real y = GetUnitY(which)
        local real xx
        local real yy
        local real angle
        local real face = GetUnitFacing(which)
        local real absmod
        local group g = CreateGroup()
        local group gg = CreateGroup()
        local unit u 
        call GroupEnumUnitsInRange(g, x, y, radius, Condition(function IsGroundUnit))
        
        loop
            set u = FirstOfGroup(g)
            
            exitwhen u == null
            
            call GroupRemoveUnit(g, u)
            set xx = GetUnitX(u)
            set yy = GetUnitY(u)
            set angle = Atan2(y-yy,x-xx) * bj_RADTODEG
            
            set absmod = ModuloReal(RAbsBJ(angle-face), 360)
            if absmod > 90 and absmod < 270 then
                call GroupAddUnit(gg, u)
            endif
            
        endloop
        call DestroyGroup(g)
        set g = null
        return gg    
    endfunction
    
    // just loops through a unit group and adds x/y-offset to their current position
    private function Looping takes group g, real xOffset, real yOffset returns nothing
        local unit u 
        loop
            set u = FirstOfGroup(g)
            exitwhen u == null
            call GroupRemoveUnit(g, u)
            call SetUnitX(u, GetUnitX(u)+xOffset)
            call SetUnitY(u, GetUnitY(u)+yOffset)
        endloop
    endfunction
    
    // this actually does the work
    private function Callback takes nothing returns nothing
        local integer i = N 
        local SimpleDash sw
        local group g
        local unit dummy
        
        // this loop works through all instances 
        loop
            exitwhen i <= 0
            set sw = D<i> // makes the code easier to read
            
            
            if ModuloInteger(sw.ticks, NewEffectEvery) == 0 then
                // creates a fade dummy
                call UFSYS_CreateFadeUnitAnimation(UnitID, sw.cx, sw.cy, sw.facing, Dur, Animation, AnimationSpeed)
                call SetUnitAnimation(sw.which, Animation)
            endif
            
            // this moves the unit
            set sw.cx = sw.cx + sw.xSpeed
            set sw.cy = sw.cy + sw.ySpeed
            
            call SetUnitX(sw.which, sw.cx)
            call SetUnitY(sw.which, sw.cy)
            
            // this picks the units in front and moves them
            set g = UnitsInFront(sw.which, PickRadius)
            call GroupRemoveUnit(g, sw.which)
            call Looping.execute(g, sw.xSpeed, sw.ySpeed)
            call DestroyGroup(g)
            set g = null
            
            set sw.ticks = sw.ticks - 1
            
            
            if sw.ticks == 0 then
                // END SPELL
                call DestroyEffect(AddSpecialEffect(SpecialEndEffectPath, sw.cx, sw.cy)) // Eyecandy
                
                // Creates a dummy and let cast him some ability
                set dummy = CreateUnit(GetOwningPlayer(sw.which), DummyCasterId, sw.cx, sw.cy, 0)
                call UnitAddAbility(dummy, DummyCastSpellId)
                call SetUnitAbilityLevel(dummy, DummyCastSpellId, GetUnitAbilityLevel(dummy, SpellId))
                call IssueImmediateOrder(dummy, DummyCastOrderString)
                call UnitApplyTimedLife(dummy, 0, 1.5)
                set dummy = null
                
                // swap the current struct instance with the last active struct instance
                set D<i> = D[N]
                set D[N] = sw
                // dont loop through this struct instance anymore
                set N = N - 1
                
                if N == 0 then
                    call PauseTimer(T)
                endif
                
                // make unit invulnerable and remove effect
                call SetUnitInvulnerable(sw.which, false)
                call SetUnitTimeScale(sw.which, 1)
                call DestroyEffect(sw.e)
            endif
            
            set i = i - 1
        endloop
    endfunction
    
    private function SpellActions takes unit caster, location point returns nothing
        local real xx = GetLocationX(point)
        local real yy = GetLocationY(point)
        
        local real x = GetUnitX(caster)
        local real y = GetUnitY(caster)
        
        // get the deltas for:
        local real deltaX = xx-x
        local real deltaY = yy-y
        
        //pythagoras....
        local real dist = SquareRoot(deltaX*deltaX+deltaY*deltaY)
        
        // calculates the number of ticks needed to move a certain distance with a certain speed
        local integer ticks = R2I(dist / DashSpeed / Period) 
        
        // x and y moved with each tick
        local real tickX = deltaX / I2R(ticks) 
        local real tickY = deltaY / I2R(ticks)
        
        local real facing = Atan2(tickY, tickX) * bj_RADTODEG
            
        
        //Blahblahblah....blah

        call SetUnitAnimation(caster, &quot;Attack&quot;)
        
        call SetUnitFacing(caster, facing)
        
        call SetUnitInvulnerable(caster, true)
        
        call SetUnitTimeScale(caster, AnimationSpeed)
        
        // increases number of instances we will loop through
        set N = N + 1
        
        if N &gt; MaxAlloc then
            // if the current number of instances is higher than the max-number we have to allocate a new
            // instance:
            set MaxAlloc = MaxAlloc + 1
            set D[N] = SimpleDash.create()
        endif
        
        // applies the values
        set D[N].which = caster
        set D[N].cx = x
        set D[N].cy = y
        set D[N].xSpeed = tickX
        set D[N].ySpeed = tickY
        set D[N].ticks = ticks
        set D[N].facing = facing
        set D[N].e = AddSpecialEffectTarget(AttachedEffectPath, caster, AttachedEffectPoint)
        
        
        call RemoveLocation(point)
        set point = null
        
        if N == 1 then
            call TimerStart(T, Period, true, function Callback)
        endif
        
        
    endfunction
    
    
    private function Actions takes nothing returns boolean
        // does this need a comment? no...
        if SpellId == GetSpellAbilityId() then
            call SpellActions(GetTriggerUnit(), GetSpellTargetLoc())
        endif        
        // dont even run the trigger
        return false
    endfunction
    


    private function Init takes nothing returns nothing
        local trigger t = CreateTrigger()
        
        call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_CHANNEL)
        
        // add it as a condition
        call TriggerAddCondition(t, Condition(function Actions))
        
        
    endfunction

endscope



</i></i>


Q: why is it called Simple dash?
A: because i simply was to lazy to think a better name. and because it's effect and coding are quite simple.
 

Attachments

  • SimpleDash.w3x
    30.1 KB · Views: 713

Naga'sShadow

Ultra Cool Member
Reaction score
49
Not exactly a simple dash in the least, as it took me several minutes just to dissect how the code worked. Didn't even try to understand how your two systems it requires to work operate. Still its a very nice looking code and spell. +rep.
 

Kenny

Back for now.
Reaction score
202
I find it odd how you are using a struct array, with TT. A few minor adjustments and it could be system independant, well except for the fade system.

Also, it may have been planned, but the spell targets dead units, which seems weird to me, maybe just chuck in a GetWidgetLife(g) > 0.405 ?

Other than that, this is very good, i have made hundreds of dash spell, but its good to finally see some one has submitted one for people to learn off. Good job.
 

Trollvottel

never aging title
Reaction score
262
->I find it odd how you are using a struct array, with TT. A few minor adjustments and it could be system independant, well except for the fade system.

many people use TT. and if everyone would make his spells system independend like this we would have 100 timers in some bigger maps in the end, which would rapidly decrease the peformance of the map.

>Also, it may have been planned, but the spell targets dead units, which seems weird to me, maybe just chuck in a GetWidgetLife(g) > 0.405 ?

so dead units just lie on the ground and cant get moved? if you see a dead bird lying on the street and then kick it, will it still lie at the same place?

>Other than that, this is very good, i have made hundreds of dash spell, but its good to finally see some one has submitted one for people to learn off. Good job.

thank you =)
 

Romek

Super Moderator
Reaction score
963
Why exactly are you even using TT?

JASS:
call TT_Start( function Callback, 0)

You might as well do:
JASS:
call TimerStart(CreateTimer(), 0.03125, true, function Callback)


It'll make this system independent. There won't be much of a difference in speed. You could also pause the timer when there are no instances, so it doesn't run.

I find TT to be quite a useless system, as the only time you really need timer attachment systems is for low frequency timers.
I believe using ordinary timers is more efficient than TT anyway (Assuming they're paused when not needed). :)
 
Reaction score
91
> There won't be much of a difference in speed.
Come to think of it, it should be even faster since it executes only one thread.

Anyway, it isn't that simple - you're using some complicated stuff which could be done a bit easier. Good job on it.
 

Kenny

Back for now.
Reaction score
202
many people use TT. and if everyone would make his spells system independend like this we would have 100 timers in some bigger maps in the end, which would rapidly decrease the peformance of the map.

Currently, TT serves no purpose in your spell, it is redundant, that is why i said it is probably better to get rid of it. Also 100 timers within a map will not really affect performance as long as you are doing everything correctly, and with struct arrays, you can pause timers when they are not in use, which you currently cannot do, and you are just repeating a timer with no purpose throughout the duration of a map.

And struct arrays are safer and faster then timer attaching, so again it will reduce the effects the spell has on map performance.

Anyway, it isn't that simple - you're using some complicated stuff which could be done a bit easier. Good job on it.

I believe this is a pretty simple spell, due to the lay out mostly. Even a GUI user could understand what was going on if they read through it. The has everything well documented and he has separated function to serve single purposes.

I don't see much that can be improved, besides what is stated above.
 

Trollvottel

never aging title
Reaction score
262
-> Update:

- you dont need TT anymore
- added Period to configurables

Any comments not concerning the coding and the system it uses but the spell?
 

Kenny

Back for now.
Reaction score
202
I may be blind but i don't see you destroying the struct instance once it is finished.

JASS:
if N == 0 then
    call PauseTimer(T)
endif


That can be put outside the loop, down the bottom.

And the return false isn't really needed anymore, even though it wont impede on performance or anything.

Everything else seems to be okay.

EDIT:

Oh and i tried the spell, it is pretty cool, and looks much better in game (when compared to the screeny).
 

Trollvottel

never aging title
Reaction score
262
-> That can be put outside the loop, down the bottom.
i only check if i have to pause it when i release an instance.


-> I may be blind but i don't see you destroying the struct instance once it is finished.
i dont have to do this. i only create new instances if they are needed and reuse older instances...

-> And the return false isn't really needed anymore, even though it wont impede on performance or anything.

i will remove it in the next version

-> Oh and i tried the spell, it is pretty cool, and looks much better in game (when compared to the screeny).

im glad you like it =)
 

Romek

Super Moderator
Reaction score
963
JASS:
    private function Actions takes nothing returns boolean
        // does this need a comment? no...
        if SpellId == GetSpellAbilityId() then
            call SpellActions(GetTriggerUnit(), GetSpellTargetLoc())
        endif        
        // dont even run the trigger
        return false
    endfunction

That's a bit retarded isn't it? :p

You may also want to update your fade system to not use TT either, in the same way as this.
 

Trollvottel

never aging title
Reaction score
262
JASS:
    private function Actions takes nothing returns boolean
        // does this need a comment? no...
        if SpellId == GetSpellAbilityId() then
            call SpellActions(GetTriggerUnit(), GetSpellTargetLoc())
        endif        
        // dont even run the trigger
        return false
    endfunction

That's a bit retarded isn't it? :p

You may also want to update your fade system to not use TT either, in the same way as this.


why is it retarded?
calling functions is faster than running triggers :p and they both dont use TT....
Anyway: removed the return false from the callback function
 

Romek

Super Moderator
Reaction score
963
> calling functions is faster than running triggers
The trigger is being ran anyway. Except you're doing it all in a condition, instead of having an action, like ordinary people have.
Also, got any reliable benchmarks?

> and they both dont use TT....
I got the wrong idea from the documentation then. Maybe you should change it a little?
 

Trollvottel

never aging title
Reaction score
262
> calling functions is faster than running triggers
The trigger is being ran anyway. Except you're doing it all in a condition, instead of having an action, like ordinary people have.

Thats what i do. I do it all in the Condition instead of having an action.

Also, got any reliable benchmarks?

Not in english, so no.

But it says that 10.000.000 function calls last 3.5 seconds while
10.000.000 TriggerExecutes last 172 Seconds. Sure, the Triggers dont get Executed by Jass code, but anyway that shows that functions are faster than triggers.
 

Romek

Super Moderator
Reaction score
963
This isn't a comparison of call to TriggerExecute.
The trigger is being evaluated anyway, for your condition.

And I really doubt those benchmarks. What did you use?
 

Trollvottel

never aging title
Reaction score
262
I didnt do them. I read about them at some german mapping forum (the benchmarks were made by the creator of castle fight).
Anyway do you think a single function call would be slower than running a trigger? Then prove it.

The trigger is being evaluated anyway, for your condition.

Evaluate != Execute
 

Romek

Super Moderator
Reaction score
963
Those benchmarks seem unrealistic.
Well, I can't really prove anything.

It looks retarded though. And the speed difference is minimal regardless of which one you do.
Also, note that you wouldn't be able to use waits when running actions like that.

Well. This is a nice spell with a cool effect.
Approved. :)
 

Viikuna

No Marlo no game.
Reaction score
265
JASS:
    private function Actions takes nothing returns boolean
        // does this need a comment? no...
        if SpellId == GetSpellAbilityId() then
            call SpellActions(GetTriggerUnit(), GetSpellTargetLoc())
        endif        
        // dont even run the trigger
        return false
    endfunction

That's a bit retarded isn't it? :p


Actually, I think it is faster to have only a condition than both condition and action. I used this method for my newest spell, and it looks pretty good and clear also.
 
General chit-chat
Help Users
  • No one is chatting at the moment.

      The Helper Discord

      Staff online

      Members online

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top