vJASS Charge 1.00

A Simple Charge Spell made in vJASS, Highly Customizable.
Yeah, it's not an original spell, but i used this as training for vJASS coding. Since i made it highly customizable and easy to import i thought that sharing it would've helped someone, maybe.

It uses Silvenon's Knockback Function, Vexorian's Handle Timers Function and Captain Griffen's Attach Timer Function.

IT USES vJASS SYNTAX, SO IT REQUIRES JASS NEW GEN PACK TO BE MODIFIED

Since this is my first spell i've surely messed something up, so any comment is very appreciated.


1.00
- Minor changes, Final Release (hopefully :D)

0.95
- Modified the path checker function, now it works even in the (very unlikely to happen) case of a wall in the middle of an area covered by items (so now you will understand why i put all thoose items in the right part of the map) Thanks to Mooglefrooglian for the tip

0.92
- Modified the initTrig function (Thanks to Thanatos for the tips)

0.91
- Now it uses coordinates instead of locations
- Merged Some triggers, others left for readability
- Made a tinier map (no point for a 128x128 testing map)

- 0.9
First Release




JASS:
// **************************************************************************
// ** **
// ** Charge **
// ** ————————————— **
// ** **
// ** A simple Charge Spell **
// ** **
// ** By: Majin **
// ** 
// ** **
// **************************************************************************

library ChargeExec requires Knockback

    struct ChargeLoop
        private real ChargeTargetx
        private real ChargeTargety
        private real ChargeEndx
        private real ChargeEndy
        private real ChargeLocx
        private real ChargeLocy
        private real ChargeNextx
        private real ChargeNexty
        private unit ChargeTarget
        private unit ChargeCaster
        private real ChargeAngle
        private real ChargeDistance
        private effect ChargeEffect
        private timer ChargeTimer
        private boolean ChargeCond = false
        private integer SpellLvl
        
        //START OF CONFIGURATION METHODS
        
        //With this method you can set the "acceleration rate" (Physicists  Forgive me) of the charge.
        //You can use any kind of numeric function (for example this.ChargeDistance * 2.00 will double
        //The speed at every loop)
        private method ChangeDistance takes nothing returns real
            return this.ChargeDistance + 1.00
        endmethod
        //END OF CONFIGURATION METHODS
        
        //START OF CUSTOMIZABLE METHODS
        
        //This method sets the conditions when to activate the AlmostCharge method. In particular
        //You may be interested in changing the distance at where the condition is true. 
        //In this Example it will be activated when the Caster is 16x times the Charge Speed Far away
        //From the target
        private method AlmostChargeCond takes nothing returns boolean
            return ((GetDistance(this.ChargeLocx, this.ChargeLocy, this.ChargeEndx,  this.ChargeEndy) < ( this.ChargeDistance * 16.00 )) and this.ChargeCond == false)
        endmethod
        
        //Actions to take when the Charge is almost completed. In this example the caster will play
        //The slam animation (Being a Blademaster, it will jump in the air while attacking)
        private method AlmostCharge takes nothing returns nothing
            set this.ChargeCond = true
            call SetUnitTimeScale( this.ChargeCaster, 2.00 )
            call SetUnitAnimation( this.ChargeCaster, "slam" )
            call QueueUnitAnimation( this.ChargeCaster, "stand")
        endmethod
        
        //Actions to take when the Caster reaches its target and the charge is completed. 
        //In this example the target unit will be stunned and you will see a Thunderclap effect on the ground
        private method CompleteCharge takes nothing returns nothing
            local effect tempEffect
            local unit dummy
            local real angle=GetAngle(this.ChargeLocx, this.ChargeLocy, this.ChargeEndx, this.ChargeEndy)
            
            set this.ChargeLocx = this.ChargeNextx
            set this.ChargeLocy = this.ChargeNexty
            set this.ChargeNextx = PolarProjectionx(this.ChargeLocx, this.ChargeDistance, this.ChargeAngle)
            set this.ChargeNexty = PolarProjectiony(this.ChargeLocy, this.ChargeDistance, this.ChargeAngle)
            
            set tempEffect = AddSpecialEffect( WALKEFFECT, this.ChargeLocx, this.ChargeLocy )
            call DestroyEffect( tempEffect )
            
            call SetUnitX( this.ChargeCaster, this.ChargeEndx )
            call SetUnitY( this.ChargeCaster, this.ChargeEndy )
            
            set dummy = CreateUnit( GetOwningPlayer(this.ChargeCaster), DUMMYID, this.ChargeLocx, this.ChargeLocy, 0 )
            call UnitAddAbility( dummy, DUMMYSPELLID )
            call SetUnitAbilityLevel(dummy, DUMMYSPELLID, this.SpellLvl)
            call IssueTargetOrder( dummy, SPELLORDERID, this.ChargeTarget )
            call UnitApplyTimedLife( dummy, 'BTLF', 1.00)
            
            set tempEffect = AddSpecialEffect( ENDINGEFFECT, this.ChargeLocx, this.ChargeLocy )
            call DestroyEffect( tempEffect )
            
            call UnitDamageTarget( this.ChargeCaster, this.ChargeTarget, CHARGEDAMAGE*this.SpellLvl, true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_UNIVERSAL, WEAPON_TYPE_WHOKNOWS )
            call PauseUnit( this.ChargeCaster, false )
            //This is the Knockback effect, see the Knock Trigger for more customization options
            call KnockbackEx(this.ChargeTarget, KNOCKDISTANCE*this.SpellLvl, angle, KNOCKDURATION, KnockTree(), KNOCKPLAYEFFECT, KNOCKEFFECT, KNOCKATTACH)
            call IssueTargetOrder( this.ChargeCaster, "attack", this.ChargeTarget )
            
            set tempEffect = null
            set dummy = null
        endmethod

        //What to do during the charge, the unit is moved, an effect is placed on the ground and
        //the charging speed is increased
        private method ContinueCharge takes nothing returns nothing
            local effect tempEffect
            set tempEffect = AddSpecialEffect( WALKEFFECT, this.ChargeLocx, this.ChargeLocy )
            call DestroyEffect( tempEffect )
            
            call SetUnitX(this.ChargeCaster, this.ChargeNextx)
            call SetUnitY(this.ChargeCaster, this.ChargeNexty)
            
            call SetUnitFacing(this.ChargeCaster, bj_RADTODEG * GetAngle(this.ChargeNextx, this.ChargeNexty, this.ChargeTargetx, this.ChargeTargety))
            
            set this.ChargeLocx = this.ChargeNextx
            set this.ChargeLocy = this.ChargeNexty
            set this.ChargeNextx = PolarProjectionx(this.ChargeLocx, this.ChargeDistance, this.ChargeAngle)
            set this.ChargeNexty = PolarProjectiony(this.ChargeLocy, this.ChargeDistance, this.ChargeAngle)
                        
            if (this.ChargeDistance <= MAXSPEED) then
                set this.ChargeDistance = this.ChangeDistance()
            endif
            
            set tempEffect = null
        endmethod
        
        //END OF CUSTOMIZABLE METHODS
        
        //START OF OTHER METHODS
        //This is the main method executed during the charge
        public static method ChargeActions takes nothing returns nothing
            local ChargeLoop l=GetTimerInt(GetExpiredTimer())
            
            set l.ChargeTargetx = GetUnitX(l.ChargeTarget)
            set l.ChargeTargety = GetUnitY(l.ChargeTarget)
            set l.ChargeEndx = PolarProjectionx(l.ChargeTargetx, 100.00, GetAngle(l.ChargeTargetx, l.ChargeTargety, l.ChargeLocx, l.ChargeLocy))
            set l.ChargeEndy = PolarProjectiony(l.ChargeTargety, 100.00, GetAngle(l.ChargeTargetx, l.ChargeTargety, l.ChargeLocx, l.ChargeLocy))
            
            set l.ChargeAngle = GetAngle(l.ChargeLocx, l.ChargeLocy, l.ChargeEndx, l.ChargeEndy)
            if ( l.AlmostChargeCond() ) then
            //What to do when the caster is about to reach the target
                call l.AlmostCharge()
            endif
            //If the Target gets too far away (by teleportation, for example) then stops the Charge
            if ( GetDistance(l.ChargeLocx, l.ChargeLocy, l.ChargeEndx, l.ChargeEndy) >= MAXDISTANCE and (MAXDISTANCE != 0.00) ) then
                call l.StopCharge()
            endif
            if ( l.ChargeConditions() ) then
            //If the unit hasn't reached the target yet, then execute this
                call l.ContinueCharge()
                call TimerAttach(l.ChargeTimer,0.03,l,function ChargeLoop.ChargeActions)
            else
                if ( l.ChargeCompleted() ) then
            //What to do if the charge is succesfully completed
                    call l.CompleteCharge()
                endif
                call l.StopCharge()
            endif
        endmethod
        
        //Actions to take once the Charge is being stopped (either because it wasn't possible to complete
        //the charge or because the caster reached its target)
        private method StopCharge takes nothing returns nothing
            set this.ChargeCond = false
            call SetUnitPathing(this.ChargeCaster, true)
            call PauseUnit(this.ChargeCaster, false)
            call SetUnitAnimation(this.ChargeCaster, "stand")
            call SetUnitTimeScale(this.ChargeCaster, 1.00)
            call DestroyEffect(this.ChargeEffect)
            call this.destroy()
        endmethod
        
        private method ChargeCompleted takes nothing returns boolean
            if ((GetDistance(this.ChargeLocx, this.ChargeLocy, this.ChargeEndx, this.ChargeEndy) <= this.ChargeDistance*1.10)  and IsPointWalkable(this.ChargeNextx, this.ChargeNexty)) then
                if ((IsTerrainPathable(this.ChargeNextx, this.ChargeNexty, PATHING_TYPE_WALKABILITY) == false ) and ( (GetUnitState(this.ChargeTarget, UNIT_STATE_LIFE)<=0) == false )) then
                    if(((GetUnitState(this.ChargeCaster, UNIT_STATE_LIFE) <= 0) == false)) then
                        return true
                    endif
                endif
            endif
                return false
        endmethod

        private method ChargeConditions takes nothing returns boolean
            if ((GetDistance(this.ChargeLocx, this.ChargeLocy, this.ChargeEndx, this.ChargeEndy) > this.ChargeDistance*1.10)  and IsPointWalkable(this.ChargeNextx, this.ChargeNexty)) then
                if ((IsTerrainPathable(this.ChargeNextx, this.ChargeNexty, PATHING_TYPE_WALKABILITY) == false ) and ( (GetUnitState(this.ChargeTarget, UNIT_STATE_LIFE)<=0) == false )) then 
                    if(((GetUnitState(this.ChargeCaster, UNIT_STATE_LIFE) <= 0) == false)) then
                        return true
                    endif
                endif
            endif
                return false
        endmethod
        
        private method onDestroy takes nothing returns nothing
            call ReleaseTimer(this.ChargeTimer)
            set this.ChargeTarget = null
            set this.ChargeCaster = null
            set this.ChargeEffect = null
        endmethod
        
        static method create takes real pChargeTargetx, real pChargeTargety, real pChargeEndx, real pChargeEndy, real pChargeLocx, real pChargeLocy, real pChargeNextx, real pChargeNexty, unit pChargeTarget, unit pChargeCaster, real pChargeAngle, real pChargeDistance, effect pChargeEffect, timer pChargeTimer returns ChargeLoop
            local ChargeLoop l = ChargeLoop.allocate()
            set l.ChargeTargetx = pChargeTargetx
            set l.ChargeTargety = pChargeTargety
            set l.ChargeEndx = pChargeEndx
            set l.ChargeEndy = pChargeEndy
            set l.ChargeLocx = pChargeLocx
            set l.ChargeLocy = pChargeLocy
            set l.ChargeNextx = pChargeNextx
            set l.ChargeNexty = pChargeNexty
            set l.ChargeTarget = pChargeTarget
            set l.ChargeCaster = pChargeCaster
            set l.ChargeAngle = pChargeAngle
            set l.ChargeDistance = pChargeDistance
            set l.ChargeEffect = pChargeEffect
            set l.ChargeTimer = pChargeTimer
            set l.SpellLvl = GetUnitAbilityLevel(l.ChargeCaster, SPELLID) 
            return l
        endmethod
        
        //END OF OTHER METHODS
    endstruct
endlibrary

//TRIGGER OF THE CHARGE SPELL
scope Charge initializer InitTrig
    private function Conditions takes nothing returns boolean
        return GetSpellAbilityId() == SPELLID
    endfunction

    private function CheckActions takes nothing returns nothing
        local real ChargeLocx = GetUnitX(GetTriggerUnit())
        local real ChargeLocy = GetUnitY(GetTriggerUnit())
        local real ChargeEndx = GetUnitX(GetSpellTargetUnit())
        local real ChargeEndy = GetUnitY(GetSpellTargetUnit())
        
        if ( GetDistance(ChargeLocx, ChargeLocy, ChargeEndx, ChargeEndy) <= (MINDISTANCE) ) then
            call DisplayTextToPlayer( GetOwningPlayer(GetTriggerUnit()), 0, 0, "|cffFFCC00Target is too close!|r" )
            call PlaySound( "Sound\\Interface\\Error.wav" )
            call IssueImmediateOrder( GetTriggerUnit(), "stop" )
        endif
    endfunction
    
    private function InitActions takes nothing returns nothing
        local timer ChargeTimer = NewTimer()
        local effect ChargeEffect
        local unit ChargeCaster = GetTriggerUnit()
        local unit ChargeTarget = GetSpellTargetUnit()
        local real ChargeDistance = STARTSPEED
        local real ChargeLocx = GetUnitX(ChargeCaster)
        local real ChargeLocy = GetUnitY(ChargeCaster)
        local real ChargeTargetx = GetUnitX(ChargeTarget)
        local real ChargeTargety = GetUnitY(ChargeTarget)
        local real ChargeEndx = PolarProjectionx(ChargeTargetx, 100.00, GetAngle(ChargeTargetx, ChargeTargety, ChargeLocx, ChargeLocy))
        local real ChargeEndy = PolarProjectiony(ChargeTargety, 100.00, GetAngle(ChargeTargetx, ChargeTargety, ChargeLocx, ChargeLocy))
        local real ChargeAngle = GetAngle(ChargeLocx, ChargeLocy, ChargeEndx, ChargeEndy)
        local real ChargeNextx = PolarProjectionx(ChargeLocx, ChargeDistance, ChargeAngle)
        local real ChargeNexty = PolarProjectiony(ChargeLocy, ChargeDistance, ChargeAngle)
        local ChargeLoop stru
        
        set ChargeEffect = AddSpecialEffectTarget(ATTACHEDEFFECT, ChargeCaster, "chest" )
        
        set stru = ChargeLoop.create(ChargeTargetx, ChargeTargety, ChargeEndx, ChargeEndy, ChargeLocx, ChargeLocy, ChargeNextx, ChargeNexty, ChargeTarget, ChargeCaster, ChargeAngle, ChargeDistance, ChargeEffect, ChargeTimer)
        call SetUnitPathing(ChargeCaster, false)
        call SetUnitTimeScale(ChargeCaster, 3)
        call IssueImmediateOrder(ChargeCaster, "stop" )
        call PauseUnit(ChargeCaster, true)
        call SetUnitAnimationByIndex(ChargeCaster,6)
        call TimerAttach(ChargeTimer,0,stru,function ChargeLoop.ChargeActions)
        
        set ChargeTimer = null
        set ChargeEffect = null
        set ChargeCaster = null
        set ChargeTarget = null
    endfunction

    private function AntiLeak takes nothing returns boolean
        return true
    endfunction

    private function InitTrig takes nothing returns nothing
        local trigger ChargeCheck = CreateTrigger()
        local trigger ChargeInit = CreateTrigger()
        local filterfunc f = Filter(function AntiLeak)
        local integer i = 0
        loop
            exitwhen i == 16
            call TriggerRegisterPlayerUnitEvent(ChargeCheck,Player(i),EVENT_PLAYER_UNIT_SPELL_CAST,f)
            call TriggerRegisterPlayerUnitEvent(ChargeInit,Player(i),EVENT_PLAYER_UNIT_SPELL_EFFECT,f)
            set i = i + 1
        endloop
        call TriggerAddCondition(ChargeCheck, Condition(function Conditions))
        call TriggerAddAction(ChargeCheck, function CheckActions)
        call TriggerAddCondition(ChargeInit, Condition(function Conditions))
        call TriggerAddAction(ChargeInit, function InitActions)
        set ChargeCheck = null
        set ChargeInit = null
        set f = null
    endfunction
endscope


Keywords:
Charge, vJASS, Script, Run, Blademaster, Animation, Timers, Pathchecker, Knockback, Stun, Path, Checker, Path Checker, Math, Kill, Knock, Back, Charge
Contents

Charge Spell (Map)

Reviews
18:06, 21st Sep 2009 The_Reborn_Devil: Triggering looks nice and I couldn't find any leaks. I really like the effects (especially the running part). Anyway, spell approved. I give this spell the rating useful. It's almost recommended...

Moderator

M

Moderator

18:06, 21st Sep 2009
The_Reborn_Devil:

Triggering looks nice and I couldn't find any leaks.
I really like the effects (especially the running part).

Anyway, spell approved.
I give this spell the rating useful.
It's almost recommended though, something like 3.8/5.
There were some other small things, but it's not that important.
 
Level 9
Joined
Aug 2, 2008
Messages
219
from what i´ve seen it looks quite good, but haven´t checked for leaks yet...
one thing i complain about is that you used some BJ (mainly for mathematical calculations), but you could have inlined them. There is also another faw, some of your functions are heavy actions blocks which are hard2read, so you could add some free lines to them. For now i give you a 3/5 because of the BJ´s and a lack of creativity.
Ehmn and why did you rename the triggers for the libraries, it´s a bit confusing.

~TNT
 
Level 7
Joined
Mar 24, 2008
Messages
184
Ehmn and why did you rename the triggers for the libraries, it´s a bit confusing.

Sorry?

Anyways, not that i was expecting big things from this spell. Also the only BJs function i used are distance between points and PolarProjection, i always thought the important thing was to avoid swapped versions of normal functions, thoose are inefficient, other BJs should work as fine as if you defined your own function and used in your map. Well, i could be wrong ofc...

Anyways, Thanks for your comment :)
 
Level 17
Joined
Mar 17, 2009
Messages
1,349
the unit will pass through other units
Umm that is one failure :p no offense...

Umm, as TNT said, Polar projections are bad, and even angle between points...

Umm, three triggers in Jass? It's Jass and thus I won't be wanting to copy more then one trigger (systems are exceptions...) and so make the three charge triggers one...

Gd job...
 
Level 7
Joined
Mar 24, 2008
Messages
184
Umm that is one failure :p no offense...

Umm, as TNT said, Polar projections are bad, and even angle between points...

Umm, three triggers in Jass? It's Jass and thus I won't be wanting to copy more then one trigger (systems are exceptions...) and so make the three charge triggers one...

Gd job...

Yeah, i've realized that now, i'm modifying it so it'll use coords instead of locations, i'll also make that change...actually it was planned but i was just too tired today and i wanted to upload it as it was :p

Oh and the move through units while charging is intentional


EDIT:

Spell Updated, now it uses coords instead of locations, and i've merged some triggers, the others are left for more readability
 
Last edited:
Level 17
Joined
Mar 17, 2009
Messages
1,349
Believe me, whoever doesn't know Jass won't know how to read both ways :p and that who knows, can read anything :p

And since the pass through units is intentional, here's a nice idea so that it seems professional... everytime the unit passes through another, create a "magic-like" special effect :) what do you think about that?

EDIT:
and maybe you could minorly damage that who the unit goes through ;)
 
Level 7
Joined
Mar 24, 2008
Messages
184
Believe me, whoever doesn't know Jass won't know how to read both ways :p and that who knows, can read anything :p

Yes but there's a difference between being able to read anything, and the difficulty of reading something.

What would you think of a book written in the Language you know best but on a single very long page? How easily would you find things on such a book?

I don't think there's the need of merging everything on a single trigger...i like thinking of thoose as if they were java classes, each one provides a single functionality (and i even merged all the libraries, except the knockback one, in a single trigger) or packages in the worst case (providing a set of functionalities), and i think it's fine as it is now.

And since the pass through units is intentional, here's a nice idea so that it seems professional... everytime the unit passes through another, create a "magic-like" special effect :) what do you think about that?

EDIT:
and maybe you could minorly damage that who the unit goes through ;)

I think that there are already too many effects, adding more would mean that some effect will go unnoticed or either it'll be too much.

This spell is meant to be simple but highly customizable, i like to think that if someone would do such a Spell (which does what you are saying, let's call it..."Meteor Strike" or "Cometary Collision", whatev...) using mine as base, would be able to do it without any problem if he has basic JASS (not even vJASS) knowledge (for example, he could put in the ContinueCharge method this functionality, picking all the units near the Caster unit, slightly Knocking them away and damaging them).

Also, i'm going to use this spell on a map of mine (with some changes) and the idea under this spell was to a sort of Blink for non-mages unit, that's why i added this functionality. If you don't want it then you could just modify the "IsPointWalkable" function according to your needs.

Notice that buildings, trees and other obstacles will still interrupt the charge
 
Level 9
Joined
Aug 2, 2008
Messages
219
Ok some code changes made but there are still some things...
Why did you merge the triggers for the libraries ? It´s good to have for each library a different trigger, what you really should merge are your three triggers. (Just put the configuration part on top).

And whats the deal with your InitTrig in the Charge trigger ? Don´t make i public, use initializer instead and get rid of the last BJ´s :p
JASS:
scope Charge initializer InitTrig

    //   [Your Stuff Here]
    
    private function AntiLeak takes nothing returns boolean
        return true
    endfunction    
    
    private function InitTrig takes nothing returns nothing
        local trigger ChargeCheck = CreateTrigger()
        local trigger ChargeInit = CreateTrigger()
        local filterfunc f = Filter(function AntiLeak)
        local integer i = 0
        loop
            exitwhen i == 16
            call TriggerRegisterPlayerUnitEvent(ChargeCheck,Player(i),EVENT_PLAYER_UNIT_SPELL_CAST,f)
            call TriggerRegisterPlayerUnitEvent(ChargeInit,Player(i),EVENT_PLAYER_UNIT_SPELL_EFFECT,f)
            set i = i + 1
        endloop
        call TriggerAddCondition(ChargeCheck, Condition(function Conditions))
        call TriggerAddAction(ChargeCheck, function CheckActions)
        call TriggerAddCondition(ChargeInit, Condition(function Conditions))
        call TriggerAddAction(ChargeInit, function InitActions)
        set ChargeCheck = null
        set ChargeInit = null
        set f = null
    endfunction

endscope
You can c´n´p this.


Btw how could you compile Slivenons Knockback ? Everytime i try my JNGP says: Error at Memory 0xFFFFFFFF. Could not be read. (0xFFFFFFFF = -1 in Decimal System) So i always wonder...
 
Level 7
Joined
Mar 24, 2008
Messages
184
Maybe because you are using a new version of JNGP, i'm still using the one compatible with 1.21 (i had some problems with wc3 1.22, the new JNGP and some maps so i went back to 1.21, then i never updated again since i stopped making maps for some time). I'll see if i can find some other reason

Anyways, i still don't see the point in merging the triggers, i think it's better to have configuration and code on two separate triggers, i can merge the trigger and the struct part, but i'm not going to merge config.

EDIT: Modified the init trig and merged with ChargeExec, but that's all. I'm not going to merge config and exec, because it's pointless and doesn't improve efficiency, it just reduces readability.

EDIT 2: I downloaded the last JNGP and War3 patch, and my map works fine

EDIT 3: Modified the path checker function :)
 
Last edited:
Level 15
Joined
Jul 6, 2009
Messages
889
JASS:
        private method AlmostCharge takes nothing returns nothing
            set this.ChargeCond = true
            call SetUnitTimeScale( this.ChargeCaster, 2.00 )
            call SetUnitAnimation( this.ChargeCaster, "slam" )
            call QueueUnitAnimation( this.ChargeCaster, "stand")
        endmethod
Configurable "slam" and "Stand" and 2.00?

JASS:
    private function CheckActions takes nothing returns nothing
        local real ChargeLocx = GetUnitX(GetTriggerUnit())
        local real ChargeLocy = GetUnitY(GetTriggerUnit())
        local real ChargeEndx = GetUnitX(GetSpellTargetUnit())
        local real ChargeEndy = GetUnitY(GetSpellTargetUnit())
        local sound s
        
        if ( GetDistance(ChargeLocx, ChargeLocy, ChargeEndx, ChargeEndy) <= (MINDISTANCE) ) then
            call DisplayTextToPlayer( GetOwningPlayer(GetTriggerUnit()), 0, 0, "|cffFFCC00Target is too close!|r" )
            set s = CreateSound("Sound\\Interface\\Error.wav", false, false, true, 12700, 12700, "")
            call StartSound(s)
            call KillSoundWhenDone(s)
            set s = null
            call IssueImmediateOrder( GetTriggerUnit(), "stop" )
        endif
    endfunction
SimError is there for a reason, oh well. Also, Configurable message in your configs trigger? "Target is too close!" make configurable?

Also, I hate the pausing of the unit, it is annoying.
 
Level 7
Joined
Mar 24, 2008
Messages
184
1) The whole method is supposed to be changed (how many units, not counting custom models, will you make jump by using that code? i'll tell you: two! the blademaster and Grom Hellscream), it's even written it's a customizable method and it's even written it's an example.

2) You are right about the message instead, i'm also leaking the string by not recycling it, still, i didn't want to use too many 3rd part libraries in the spell. Anyways...are you using SimError in your map? Great, go ahead...delete those 5 lines and write a call to SimError...is it so difficult?

3) You cannot play a unit walk animation if it's not paused
 
Level 7
Joined
Mar 24, 2008
Messages
184
Have you tried it?

Try to remove the PauseUnit from the code of my charge (it's just before the SetUnitAnimationByIndex, which is what i'm using since the first release, a simple SetUnitAnimation(unit, "walk") doesn't work, there's a post i made about that) and look how the blademaster will act. My mistake about the Walk animation on non paused units, i remember there was something that made the blademaster not play the animation but well...looks like it wasn't the pause.
Still, not pausing the unit will cause the charging unit act weird (it will attempt to attack other units in its way), and it will not jump at the end of the charge

Also, i don't see what's the problem with the paused unit. It's because of the buffs?
 
Last edited:
Level 15
Joined
Jul 6, 2009
Messages
889
I hate pausing, it is annoying in-game.

You can add Cargo Hold ability to prevent the caster attacking during the charge. Or Soul Burn it or something.

As for the attack at the end, you can always reset animation beforehand and then play it, or you can pause, play animation, unpause instantly.

(I haven't tried, you can test around with that).
 
Level 7
Joined
Mar 24, 2008
Messages
184
And i should also disable abilities for the unit as he'd be able to cast while charging. I find all this unnecessarily complicated, also i don't mind units being paused (and so do other people, as you can see).

If you, or someone else, need a charge spell for your map where for some reason you don't want your units to be paused, feel free to modify my script since i'm not going to do that :) i should modify the error message instead, maybe using SimErr is not a bad idea
 
Level 2
Joined
Jun 14, 2012
Messages
18
Is there anyone here still?

Hello everyone, I know this thread is old as ever, but I am new to this and I hope I'm not bothering anyone (if there is still somebody following), I'm having a hard time figuring out what's what...
What I don't understand is, who is casting what, because it is kinda unclear in the tutorial... There is the Charge spell, the Permawalk, two dummys and a Hero... who should have which?
Plus, even though I don't understand any of it, it tells me I need to create other triggers... And, ummm... am I supposed to activate the triggers in the library? Cause, it keeps telling me it has like a gazillion compilation errors...

P.S. I tried the spell on your map: AMAZING!! which is why I don't want to give up on it...
 
Level 7
Joined
Mar 24, 2008
Messages
184
Hi and thank you :) I haven't been doing spells for W3 since a lot of time (recently i've helped someone to modify this very spell, but that's all) so i don't remember much of what i've done. Let's see what i can do for you:

First of all it would be nice to know if you use JassNewGenPack or the normal map editor. You need a vJASS compiler in order to save a map with my spell.
That said, the Dummy called Pathchecker should have the Permawalk spell, while the other will have the "Charge Stun", the hero will have the charge spell.
You could just copy/paste them from my map (first the spells, then the units). But if you haven't got newgenpack it'll be impossible for you to use this spell.

I suggest you to do some research on vJASS
 
Level 2
Joined
Jun 14, 2012
Messages
18
Hi and thank you :) I haven't been doing spells for W3 since a lot of time (recently i've helped someone to modify this very spell, but that's all) so i don't remember much of what i've done. Let's see what i can do for you:

First of all it would be nice to know if you use JassNewGenPack or the normal map editor. You need a vJASS compiler in order to save a map with my spell.
That said, the Dummy called Pathchecker should have the Permawalk spell, while the other will have the "Charge Stun", the hero will have the charge spell.
You could just copy/paste them from my map (first the spells, then the units). But if you haven't got newgenpack it'll be impossible for you to use this spell.

I suggest you to do some research on vJASS

Oh, I see, but I have to go through the trigger line by line to make sure they're configured the right way? (Which, by the way, I don't know how to do)
I have the NewGenPack but when I use that World Editor, I have a hard time finding my map in the game, or even in the directory... They just go "hidden". And whenever I save, it tells me, for example, within the Library Trigger, I have like 1088 compile errors, and sometimes it's stuff like, "Expected end of line: //TESH.alwaysfold=0", which for me is kinda Chinese...
 
This is an old vJass spell, and it's possible that JassHelper forbade it from working correctly after a certain update. This hotfix will patch that particular issue:

vJASS:
// **************************************************************************
// ** **
// ** Charge **
// ** ————————————— **
// ** **
// ** A simple Charge Spell **
// ** **
// ** By: Majin **
// ** 
// ** **
// **************************************************************************

library ChargeExec requires Knockback

    struct ChargeLoop
        private real ChargeTargetx
        private real ChargeTargety
        private real ChargeEndx
        private real ChargeEndy
        private real ChargeLocx
        private real ChargeLocy
        private real ChargeNextx
        private real ChargeNexty
        private unit ChargeTarget
        private unit ChargeCaster
        private real ChargeAngle
        private real ChargeDistance
        private effect ChargeEffect
        private timer ChargeTimer
        private boolean ChargeCond = false
        private integer SpellLvl
        
        //START OF CONFIGURATION METHODS
        
        //With this method you can set the "acceleration rate" (Physicists  Forgive me) of the charge.
        //You can use any kind of numeric function (for example this.ChargeDistance * 2.00 will double
        //The speed at every loop)
        private method ChangeDistance takes nothing returns real
            return this.ChargeDistance + 1.00
        endmethod
        //END OF CONFIGURATION METHODS
        
        //START OF CUSTOMIZABLE METHODS
        
        //This method sets the conditions when to activate the AlmostCharge method. In particular
        //You may be interested in changing the distance at where the condition is true. 
        //In this Example it will be activated when the Caster is 16x times the Charge Speed Far away
        //From the target
        private method AlmostChargeCond takes nothing returns boolean
            return ((GetDistance(this.ChargeLocx, this.ChargeLocy, this.ChargeEndx,  this.ChargeEndy) < ( this.ChargeDistance * 16.00 )) and this.ChargeCond == false)
        endmethod
        
        //Actions to take when the Charge is almost completed. In this example the caster will play
        //The slam animation (Being a Blademaster, it will jump in the air while attacking)
        private method AlmostCharge takes nothing returns nothing
            set this.ChargeCond = true
            call SetUnitTimeScale( this.ChargeCaster, 2.00 )
            call SetUnitAnimation( this.ChargeCaster, "slam" )
            call QueueUnitAnimation( this.ChargeCaster, "stand")
        endmethod
        
        //Actions to take when the Caster reaches its target and the charge is completed. 
        //In this example the target unit will be stunned and you will see a Thunderclap effect on the ground
        private method CompleteCharge takes nothing returns nothing
            local effect tempEffect
            local unit dummy
            local real angle=GetAngle(this.ChargeLocx, this.ChargeLocy, this.ChargeEndx, this.ChargeEndy)
            
            set this.ChargeLocx = this.ChargeNextx
            set this.ChargeLocy = this.ChargeNexty
            set this.ChargeNextx = PolarProjectionx(this.ChargeLocx, this.ChargeDistance, this.ChargeAngle)
            set this.ChargeNexty = PolarProjectiony(this.ChargeLocy, this.ChargeDistance, this.ChargeAngle)
            
            set tempEffect = AddSpecialEffect( WALKEFFECT, this.ChargeLocx, this.ChargeLocy )
            call DestroyEffect( tempEffect )
            
            call SetUnitX( this.ChargeCaster, this.ChargeEndx )
            call SetUnitY( this.ChargeCaster, this.ChargeEndy )
            
            set dummy = CreateUnit( GetOwningPlayer(this.ChargeCaster), DUMMYID, this.ChargeLocx, this.ChargeLocy, 0 )
            call UnitAddAbility( dummy, DUMMYSPELLID )
            call SetUnitAbilityLevel(dummy, DUMMYSPELLID, this.SpellLvl)
            call IssueTargetOrder( dummy, SPELLORDERID, this.ChargeTarget )
            call UnitApplyTimedLife( dummy, 'BTLF', 1.00)
            
            set tempEffect = AddSpecialEffect( ENDINGEFFECT, this.ChargeLocx, this.ChargeLocy )
            call DestroyEffect( tempEffect )
            
            call UnitDamageTarget( this.ChargeCaster, this.ChargeTarget, CHARGEDAMAGE*this.SpellLvl, true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_UNIVERSAL, WEAPON_TYPE_WHOKNOWS )
            call PauseUnit( this.ChargeCaster, false )
            //This is the Knockback effect, see the Knock Trigger for more customization options
            call KnockbackEx(this.ChargeTarget, KNOCKDISTANCE*this.SpellLvl, angle, KNOCKDURATION, KnockTree(), KNOCKPLAYEFFECT, KNOCKEFFECT, KNOCKATTACH)
            call IssueTargetOrder( this.ChargeCaster, "attack", this.ChargeTarget )
            
            set tempEffect = null
            set dummy = null
        endmethod

        //What to do during the charge, the unit is moved, an effect is placed on the ground and
        //the charging speed is increased
        private method ContinueCharge takes nothing returns nothing
            local effect tempEffect
            set tempEffect = AddSpecialEffect( WALKEFFECT, this.ChargeLocx, this.ChargeLocy )
            call DestroyEffect( tempEffect )
            
            call SetUnitX(this.ChargeCaster, this.ChargeNextx)
            call SetUnitY(this.ChargeCaster, this.ChargeNexty)
            
            call SetUnitFacing(this.ChargeCaster, bj_RADTODEG * GetAngle(this.ChargeNextx, this.ChargeNexty, this.ChargeTargetx, this.ChargeTargety))
            
            set this.ChargeLocx = this.ChargeNextx
            set this.ChargeLocy = this.ChargeNexty
            set this.ChargeNextx = PolarProjectionx(this.ChargeLocx, this.ChargeDistance, this.ChargeAngle)
            set this.ChargeNexty = PolarProjectiony(this.ChargeLocy, this.ChargeDistance, this.ChargeAngle)
                        
            if (this.ChargeDistance <= MAXSPEED) then
                set this.ChargeDistance = this.ChangeDistance()
            endif
            
            set tempEffect = null
        endmethod
        
        //END OF CUSTOMIZABLE METHODS
        
        //START OF OTHER METHODS
        //This is the main method executed during the charge
        public static method ChargeActions takes nothing returns nothing
            local ChargeLoop l=GetTimerInt(GetExpiredTimer())
            
            set l.ChargeTargetx = GetUnitX(l.ChargeTarget)
            set l.ChargeTargety = GetUnitY(l.ChargeTarget)
            set l.ChargeEndx = PolarProjectionx(l.ChargeTargetx, 100.00, GetAngle(l.ChargeTargetx, l.ChargeTargety, l.ChargeLocx, l.ChargeLocy))
            set l.ChargeEndy = PolarProjectiony(l.ChargeTargety, 100.00, GetAngle(l.ChargeTargetx, l.ChargeTargety, l.ChargeLocx, l.ChargeLocy))
            
            set l.ChargeAngle = GetAngle(l.ChargeLocx, l.ChargeLocy, l.ChargeEndx, l.ChargeEndy)
            if ( l.AlmostChargeCond() ) then
            //What to do when the caster is about to reach the target
                call l.AlmostCharge()
            endif
            //If the Target gets too far away (by teleportation, for example) then stops the Charge
            if ( GetDistance(l.ChargeLocx, l.ChargeLocy, l.ChargeEndx, l.ChargeEndy) >= MAXDISTANCE and (MAXDISTANCE != 0.00) ) then
                call l.StopCharge()
            endif
            if ( l.ChargeConditions() ) then
            //If the unit hasn't reached the target yet, then execute this
                call l.ContinueCharge()
                call TimerAttach(l.ChargeTimer,0.03,l,function ChargeLoop.ChargeActions)
            else
                if ( l.ChargeCompleted() ) then
            //What to do if the charge is succesfully completed
                    call l.CompleteCharge()
                endif
                call l.StopCharge()
            endif
        endmethod
        
        //Actions to take once the Charge is being stopped (either because it wasn't possible to complete
        //the charge or because the caster reached its target)
        private method StopCharge takes nothing returns nothing
            set this.ChargeCond = false
            call SetUnitPathing(this.ChargeCaster, true)
            call PauseUnit(this.ChargeCaster, false)
            call SetUnitAnimation(this.ChargeCaster, "stand")
            call SetUnitTimeScale(this.ChargeCaster, 1.00)
            call DestroyEffect(this.ChargeEffect)
            call this.destroy()
        endmethod
        
        private method ChargeCompleted takes nothing returns boolean
            if ((GetDistance(this.ChargeLocx, this.ChargeLocy, this.ChargeEndx, this.ChargeEndy) <= this.ChargeDistance*1.10)  and IsPointWalkable(this.ChargeNextx, this.ChargeNexty)) then
                if ((IsTerrainPathable(this.ChargeNextx, this.ChargeNexty, PATHING_TYPE_WALKABILITY) == false ) and ( (GetUnitState(this.ChargeTarget, UNIT_STATE_LIFE)<=0) == false )) then
                    if(((GetUnitState(this.ChargeCaster, UNIT_STATE_LIFE) <= 0) == false)) then
                        return true
                    endif
                endif
            endif
                return false
        endmethod

        private method ChargeConditions takes nothing returns boolean
            if ((GetDistance(this.ChargeLocx, this.ChargeLocy, this.ChargeEndx, this.ChargeEndy) > this.ChargeDistance*1.10)  and IsPointWalkable(this.ChargeNextx, this.ChargeNexty)) then
                if ((IsTerrainPathable(this.ChargeNextx, this.ChargeNexty, PATHING_TYPE_WALKABILITY) == false ) and ( (GetUnitState(this.ChargeTarget, UNIT_STATE_LIFE)<=0) == false )) then 
                    if(((GetUnitState(this.ChargeCaster, UNIT_STATE_LIFE) <= 0) == false)) then
                        return true
                    endif
                endif
            endif
                return false
        endmethod
        
        private method onDestroy takes nothing returns nothing
            call ReleaseTimer(this.ChargeTimer)
            set this.ChargeTarget = null
            set this.ChargeCaster = null
            set this.ChargeEffect = null
        endmethod
        
        static method create takes real pChargeTargetx, real pChargeTargety, real pChargeEndx, real pChargeEndy, real pChargeLocx, real pChargeLocy, real pChargeNextx, real pChargeNexty, unit pChargeTarget, unit pChargeCaster, real pChargeAngle, real pChargeDistance, effect pChargeEffect, timer pChargeTimer returns ChargeLoop
            local ChargeLoop l = ChargeLoop.allocate()
            set l.ChargeTargetx = pChargeTargetx
            set l.ChargeTargety = pChargeTargety
            set l.ChargeEndx = pChargeEndx
            set l.ChargeEndy = pChargeEndy
            set l.ChargeLocx = pChargeLocx
            set l.ChargeLocy = pChargeLocy
            set l.ChargeNextx = pChargeNextx
            set l.ChargeNexty = pChargeNexty
            set l.ChargeTarget = pChargeTarget
            set l.ChargeCaster = pChargeCaster
            set l.ChargeAngle = pChargeAngle
            set l.ChargeDistance = pChargeDistance
            set l.ChargeEffect = pChargeEffect
            set l.ChargeTimer = pChargeTimer
            set l.SpellLvl = GetUnitAbilityLevel(l.ChargeCaster, SPELLID) 
            return l
        endmethod
        
        //END OF OTHER METHODS
    endstruct
endlibrary

//TRIGGER OF THE CHARGE SPELL
scope Charge initializer Init
    private function Conditions takes nothing returns boolean
        return GetSpellAbilityId() == SPELLID
    endfunction

    private function CheckActions takes nothing returns nothing
        local real ChargeLocx = GetUnitX(GetTriggerUnit())
        local real ChargeLocy = GetUnitY(GetTriggerUnit())
        local real ChargeEndx = GetUnitX(GetSpellTargetUnit())
        local real ChargeEndy = GetUnitY(GetSpellTargetUnit())
        
        if ( GetDistance(ChargeLocx, ChargeLocy, ChargeEndx, ChargeEndy) <= (MINDISTANCE) ) then
            call DisplayTextToPlayer( GetOwningPlayer(GetTriggerUnit()), 0, 0, "|cffFFCC00Target is too close!|r" )
            call PlaySound( "Sound\\Interface\\Error.wav" )
            call IssueImmediateOrder( GetTriggerUnit(), "stop" )
        endif
    endfunction
    
    private function InitActions takes nothing returns nothing
        local timer ChargeTimer = NewTimer()
        local effect ChargeEffect
        local unit ChargeCaster = GetTriggerUnit()
        local unit ChargeTarget = GetSpellTargetUnit()
        local real ChargeDistance = STARTSPEED
        local real ChargeLocx = GetUnitX(ChargeCaster)
        local real ChargeLocy = GetUnitY(ChargeCaster)
        local real ChargeTargetx = GetUnitX(ChargeTarget)
        local real ChargeTargety = GetUnitY(ChargeTarget)
        local real ChargeEndx = PolarProjectionx(ChargeTargetx, 100.00, GetAngle(ChargeTargetx, ChargeTargety, ChargeLocx, ChargeLocy))
        local real ChargeEndy = PolarProjectiony(ChargeTargety, 100.00, GetAngle(ChargeTargetx, ChargeTargety, ChargeLocx, ChargeLocy))
        local real ChargeAngle = GetAngle(ChargeLocx, ChargeLocy, ChargeEndx, ChargeEndy)
        local real ChargeNextx = PolarProjectionx(ChargeLocx, ChargeDistance, ChargeAngle)
        local real ChargeNexty = PolarProjectiony(ChargeLocy, ChargeDistance, ChargeAngle)
        local ChargeLoop stru
        
        set ChargeEffect = AddSpecialEffectTarget(ATTACHEDEFFECT, ChargeCaster, "chest" )
        
        set stru = ChargeLoop.create(ChargeTargetx, ChargeTargety, ChargeEndx, ChargeEndy, ChargeLocx, ChargeLocy, ChargeNextx, ChargeNexty, ChargeTarget, ChargeCaster, ChargeAngle, ChargeDistance, ChargeEffect, ChargeTimer)
        call SetUnitPathing(ChargeCaster, false)
        call SetUnitTimeScale(ChargeCaster, 3)
        call IssueImmediateOrder(ChargeCaster, "stop" )
        call PauseUnit(ChargeCaster, true)
        call SetUnitAnimationByIndex(ChargeCaster,6)
        call TimerAttach(ChargeTimer,0,stru,function ChargeLoop.ChargeActions)
        
        set ChargeTimer = null
        set ChargeEffect = null
        set ChargeCaster = null
        set ChargeTarget = null
    endfunction

    private function AntiLeak takes nothing returns boolean
        return true
    endfunction

    private function Init takes nothing returns nothing
        local trigger ChargeCheck = CreateTrigger()
        local trigger ChargeInit = CreateTrigger()
        local filterfunc f = Filter(function AntiLeak)
        local integer i = 0
        loop
            exitwhen i == 16
            call TriggerRegisterPlayerUnitEvent(ChargeCheck,Player(i),EVENT_PLAYER_UNIT_SPELL_CAST,f)
            call TriggerRegisterPlayerUnitEvent(ChargeInit,Player(i),EVENT_PLAYER_UNIT_SPELL_EFFECT,f)
            set i = i + 1
        endloop
        call TriggerAddCondition(ChargeCheck, Condition(function Conditions))
        call TriggerAddAction(ChargeCheck, function CheckActions)
        call TriggerAddCondition(ChargeInit, Condition(function Conditions))
        call TriggerAddAction(ChargeInit, function InitActions)
        set ChargeCheck = null
        set ChargeInit = null
        set f = null
    endfunction
endscope
 
Level 41
Joined
Feb 27, 2007
Messages
5,233
I'm not sure it will exactly work how you want. In order to do that, you'll have to disable the pathability checks entirely, since that's what's 'failing' when the spell stops by an obstacle like that. This will mean that the spell can be used into water, into a group of trees, and off/up cliffs.
JASS:
//REPLACE THESE TWO FUNCTIONS:
        private method ChargeCompleted takes nothing returns boolean
            if ((GetDistance(this.ChargeLocx, this.ChargeLocy, this.ChargeEndx, this.ChargeEndy) <= this.ChargeDistance*1.10)  and IsPointWalkable(this.ChargeNextx, this.ChargeNexty)) then
                if ((IsTerrainPathable(this.ChargeNextx, this.ChargeNexty, PATHING_TYPE_WALKABILITY) == false ) and ( (GetUnitState(this.ChargeTarget, UNIT_STATE_LIFE)<=0) == false )) then
                    if(((GetUnitState(this.ChargeCaster, UNIT_STATE_LIFE) <= 0) == false)) then
                        return true
                    endif
                endif
            endif
                return false
        endmethod

        private method ChargeConditions takes nothing returns boolean
            if ((GetDistance(this.ChargeLocx, this.ChargeLocy, this.ChargeEndx, this.ChargeEndy) > this.ChargeDistance*1.10)  and IsPointWalkable(this.ChargeNextx, this.ChargeNexty)) then
                if ((IsTerrainPathable(this.ChargeNextx, this.ChargeNexty, PATHING_TYPE_WALKABILITY) == false ) and ( (GetUnitState(this.ChargeTarget, UNIT_STATE_LIFE)<=0) == false )) then
                    if(((GetUnitState(this.ChargeCaster, UNIT_STATE_LIFE) <= 0) == false)) then
                        return true
                    endif
                endif
            endif
                return false
        endmethod


//WITH THESE VERSIONS:
        private method ChargeCompleted takes nothing returns boolean
            if (GetDistance(this.ChargeLocx, this.ChargeLocy, this.ChargeEndx, this.ChargeEndy) <= this.ChargeDistance*1.10) then
                if (GetUnitState(this.ChargeTarget, UNIT_STATE_LIFE)<=0) == false then
                    if (GetUnitState(this.ChargeCaster, UNIT_STATE_LIFE) <= 0) == false then
                        return true
                    endif
                endif
            endif
                return false
        endmethod

        private method ChargeConditions takes nothing returns boolean
            if (GetDistance(this.ChargeLocx, this.ChargeLocy, this.ChargeEndx, this.ChargeEndy) > this.ChargeDistance*1.10) then
                if (GetUnitState(this.ChargeTarget, UNIT_STATE_LIFE)<=0) == false then
                    if (GetUnitState(this.ChargeCaster, UNIT_STATE_LIFE) <= 0) == false then
                        return true
                    endif
                endif
            endif
                return false
        endmethod
 
Last edited:
Level 10
Joined
May 25, 2021
Messages
376
I'm not sure it will exactly work how you want. In order to do that, you'll have to disable the pathability checks entirely, since that's what's 'failing' when the spell stops by an obstacle like that. This will mean that the spell can be used into water, into a group of trees, and off/up cliffs.
JASS:
//REPLACE THESE TWO FUNCTIONS:
        private method ChargeCompleted takes nothing returns boolean
            if ((GetDistance(this.ChargeLocx, this.ChargeLocy, this.ChargeEndx, this.ChargeEndy) <= this.ChargeDistance*1.10)  and IsPointWalkable(this.ChargeNextx, this.ChargeNexty)) then
                if ((IsTerrainPathable(this.ChargeNextx, this.ChargeNexty, PATHING_TYPE_WALKABILITY) == false ) and ( (GetUnitState(this.ChargeTarget, UNIT_STATE_LIFE)<=0) == false )) then
                    if(((GetUnitState(this.ChargeCaster, UNIT_STATE_LIFE) <= 0) == false)) then
                        return true
                    endif
                endif
            endif
                return false
        endmethod

        private method ChargeConditions takes nothing returns boolean
            if ((GetDistance(this.ChargeLocx, this.ChargeLocy, this.ChargeEndx, this.ChargeEndy) > this.ChargeDistance*1.10)  and IsPointWalkable(this.ChargeNextx, this.ChargeNexty)) then
                if ((IsTerrainPathable(this.ChargeNextx, this.ChargeNexty, PATHING_TYPE_WALKABILITY) == false ) and ( (GetUnitState(this.ChargeTarget, UNIT_STATE_LIFE)<=0) == false )) then
                    if(((GetUnitState(this.ChargeCaster, UNIT_STATE_LIFE) <= 0) == false)) then
                        return true
                    endif
                endif
            endif
                return false
        endmethod


//WITH THESE VERSIONS:
        private method ChargeCompleted takes nothing returns boolean
            if (GetDistance(this.ChargeLocx, this.ChargeLocy, this.ChargeEndx, this.ChargeEndy) <= this.ChargeDistance*1.10) then
                if (GetUnitState(this.ChargeTarget, UNIT_STATE_LIFE)<=0) == false then
                    if (GetUnitState(this.ChargeCaster, UNIT_STATE_LIFE) <= 0) == false then
                        return true
                    endif
                endif
            endif
                return false
        endmethod

        private method ChargeConditions takes nothing returns boolean
            if (GetDistance(this.ChargeLocx, this.ChargeLocy, this.ChargeEndx, this.ChargeEndy) > this.ChargeDistance*1.10) then
                if (GetUnitState(this.ChargeTarget, UNIT_STATE_LIFE)<=0) == false then
                    if (GetUnitState(this.ChargeCaster, UNIT_STATE_LIFE) <= 0) == false then
                        return true
                    endif
                endif
            endif
                return false
        endmethod[/jass]
Thank you for your prompt reply!
 
Wow, how can I do that?
Blizzard has taken the very confusing decision to make JassHelper 'disabled' by default, meaning that every new map that uses a vJass resource has to manually enable JassHelper. Please see this thread if you want to know where to find the Enable JassHelper option.
 
Level 41
Joined
Feb 27, 2007
Messages
5,233
This spell has poor naming conventions and is not well encapsulated (note how the CheckPathability library actually requires the spell config library); normally you should only have to duplicate the (non-requirements) code and change the name of the outermost library, but that won't work here. You'll have to make a copy of all the spell triggers/code and intentionally rename some libraries/scopes and edit some of the code to avoid namespace conflicts.

Halfway through fixing this mess... wow this spell has terrible encapsulation! Most constants and functions are not private or public, there's a mess of requirements that need stuff from the main spell, dummy ID is defined in a weird place because of how its used, generic function names aren't given a keyword and pollute the namespace (one of them is just called "function init"!!!!!) ....

Anyway I reorganized the whole spell to make it portable and renamable with no conflicts. This altered structure and keywords in the requirements libraries, rolled the ChargeConfig library into the main spell code, and fixed encapsulation for everything I could find. What remains:
JASS:
// **************************************************************************
// ** **
// ** Mathematical Functions **
// ** ————————————— **
// ** **
// ** Functions used instead of BJs to calculate some values **
// ** **
// ** By: Majin **
// **
// ** **
// **************************************************************************

library Math
    function GetDistance takes real ax, real ay, real bx, real by returns real
        return SquareRoot((bx-ax)*(bx-ax)+(by-ay)*(by-ay))
    endfunction
   
    function PolarProjectionx takes real ax, real dist, real angle returns real
        return ax + dist * Cos(angle)
    endfunction
   
    function PolarProjectiony takes real ay, real dist, real angle returns real
        return ay + dist * Sin(angle)
    endfunction

    function GetAngle takes real ax, real ay, real bx, real by returns real
        return Atan2(by-ay, bx-ax)
    endfunction   
   
    function RAbs takes real a returns real
        if (a >= 0) then
            return a
        else
            return -a
        endif
    endfunction
   
    function AntiLeak takes nothing returns boolean
        return true
    endfunction
endlibrary



// **************************************************************************
// ** **
// ** Check Pathability Function **
// ** ————————————— **
// ** **
// ** A Function that checks if a unit can walk over a certain area **
// ** (Ignores other units but not buildings, trees and other obstacles) **
// ** **
// ** By: Majin **
// **
// ** **
// **************************************************************************

library Walkable initializer init requires Math
    globals
      //ID of the dummy unit used for the attack at the end of the charge.
        constant integer DUMMYID = 'e000'
       
      //PATHCHECKER SETTINGS
      //ID of the dummy unit used for the path checker function (has to be an invulnerable locusted unit with No model)
        constant integer PATHCHECKERID = 'e001'
    
      //ID of the permanent windwalk spell used for the path checker function
        constant integer SPELLPATHID = 'A001'
   
        unit pathchecker
        rect pathrect
        filterfunc truefilter
    endglobals
   
    private function HideItems takes nothing returns nothing
        call SetItemVisible(GetEnumItem(), false)   
    endfunction
   
    private function UnHideItems takes nothing returns nothing
        call SetItemVisible(GetEnumItem(), true)   
    endfunction
   
    function IsPointWalkable takes real x, real y returns boolean
        local boolean b
        call SetUnitPosition(pathchecker,x,y)
        set b=((GetUnitX(pathchecker)-x)*(GetUnitX(pathchecker)-x)+((GetUnitY(pathchecker)-y)*(GetUnitY(pathchecker)-y))<=1)
        if (b==false) then
            call MoveRectTo(pathrect, x, y)
            call EnumItemsInRect(pathrect,truefilter,function HideItems)
            call SetUnitPosition(pathchecker,x,y)
            set b=((GetUnitX(pathchecker)-x)*(GetUnitX(pathchecker)-x)+((GetUnitY(pathchecker)-y)*(GetUnitY(pathchecker)-y))<=1)
            call EnumItemsInRect(pathrect,truefilter,function UnHideItems)
        endif
        return b
    endfunction

    private function init takes nothing returns nothing
        local real x = GetRectMinX(bj_mapInitialPlayableArea)
        local real y = GetRectMinY(bj_mapInitialPlayableArea)
        set pathrect=Rect(x,y,x+200.00,y+200.00)
        set pathchecker=CreateUnit( Player(PLAYER_NEUTRAL_PASSIVE), PATHCHECKERID, 0, 0, 0)
        set truefilter=Filter(function AntiLeak)
        call UnitAddAbility(pathchecker, SPELLPATHID)
        call IssueImmediateOrder( pathchecker, "windwalk" )
    endfunction
endlibrary



//Functions used to handle timers. Credits to Vexorian and Captain Griffen
//Check http://www.wc3c.net/showthread.php?t=89072 for more informations
library HandleTimers

    globals
        private timer array timers
        private integer N = 0
    endglobals

    function NewTimer takes nothing returns timer
        if (N==0) then
            return CreateTimer()
        endif
        set N=N-1
        return timers[N]
    endfunction

    function ReleaseTimer takes timer t returns nothing
        call PauseTimer(t)
        if (N==8191) then
            debug call BJDebugMsg("Warning: Timer stack is full, destroying timer!!")
            //stack is full, the map already has much more troubles than the chance of bug
            call DestroyTimer(t)
        else
            set timers[N]=t
            set N=N+1
        endif   
    endfunction

    function TimerAttach takes timer t, real time, real value, code func returns nothing
        call TimerStart(t, value, false, null)
        call PauseTimer(t)
        call TimerStart(t, time, false, func)
    endfunction

// ONLY call on an expired timer.
    function GetTimerInt takes timer t returns integer
        return R2I(TimerGetRemaining(t) + 0.5)
    endfunction
endlibrary   



library Knockback initializer Init requires HandleTimers, Walkable

// **************************************************************************
// ** **
// ** Knockback(Ex) **
// ** ————————————— **
// ** **
// ** A function made for efficient knockbacking **
// ** **
// ** By: Silvenon **
// ** **
// **************************************************************************

//=======================================//
//Credits to PitzerMike for this function//
//=======================================//

private function TreeFilter takes nothing returns boolean
    local destructable d = GetFilterDestructable()
    local boolean i = IsDestructableInvulnerable(d)
    local unit u = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE), DUMMYID, GetWidgetX(d), GetWidgetY(d), 0)
    local boolean result = false

    call UnitAddAbility(u, 'Ahrl')

    if i then
        call SetDestructableInvulnerable(d, false)
    endif

    set result = IssueTargetOrder(u, "harvest", d)
    call RemoveUnit(u)

    if i then
      call SetDestructableInvulnerable(d, true)
    endif

    set u = null
    set d = null
    return result
endfunction

//===========================================================================

globals
    private timer Tim = CreateTimer()
    private integer Total = 0
    private boolexpr Cond = null

    private integer array Ar
    private boolean array BoolAr

    private real MAX_X
    private real MAX_Y
    private real MIN_X
    private real MIN_Y
endglobals

private constant function Interval takes nothing returns real
    return 0.04
endfunction

private function KillTree takes nothing returns nothing
    if BoolAr[0] then
        call KillDestructable(GetEnumDestructable())
    else
        set BoolAr[1] = true
    endif
endfunction

public struct Data
    unit u
    real d1
    real d2

    real sin
    real cos

    real r

    string s = ""
    effect e = null

    static method create takes unit u, integer q, real d, real a, real r, integer t, string s, string p returns Data
        local Data dat = Data.allocate()

        set dat.u = u
        set dat.d1 = 2 * d / (q + 1)
        set dat.d2 = dat.d1 / q

        set dat.sin = Sin(a)
        set dat.cos = Cos(a)

        set dat.r = r

        if s != "" and s != null then
            if t == 2 then
                if p != "" and p != null then
                    set dat.e = AddSpecialEffectTarget(s, u, p)
                else
                    set dat.e = AddSpecialEffectTarget(s, u, "chest")
                endif
            elseif t == 1 then
                set dat.s = s
            endif
        endif

        call SetUnitPosition(u, GetUnitX(u), GetUnitY(u))
        call PauseUnit(u, true)

        if Total == 0 then
            call TimerStart(Tim, Interval(), true, function Data.Execute)
        endif

        set Total = Total + 1
        set Ar[Total - 1] = dat

        return dat
    endmethod

    static method Execute takes nothing returns nothing
        local Data dat
        local integer i = 0
        local real x
        local real y
        local rect r
        local real rad

        loop
            exitwhen i >= Total
            set dat = Ar[i]

            if dat.s != "" and dat.s != null then
                set x = GetUnitX(dat.u)
                set y = GetUnitY(dat.u)

                call DestroyEffect(AddSpecialEffect(dat.s, x, y))

                set x = x + dat.d1 * dat.cos
                set y = y + dat.d1 * dat.sin
            else
                set x = GetUnitX(dat.u) + dat.d1 * dat.cos
                set y = GetUnitY(dat.u) + dat.d1 * dat.sin
            endif

            if dat.r != 0 then
                set BoolAr[0] = dat.r > 0

                set rad = dat.r

                if not BoolAr[0] then
                    set rad = rad * (-1)
                endif

                set r = Rect(x - rad, y - rad, x + rad, y + rad)

                call EnumDestructablesInRect(r, Cond, function KillTree)
                call RemoveRect(r)

                set r = null
            endif

            if (x < MAX_X and y < MAX_Y and x > MIN_X and y > MIN_Y) and not BoolAr[1] and (IsPointWalkable(x, y)) then
                call SetUnitX(dat.u, x)
                call SetUnitY(dat.u, y)
            endif

            set dat.d1 = dat.d1 - dat.d2

            if dat.d1 <= 0 or (x > MAX_X or y > MAX_Y or x < MIN_X or y < MIN_Y) or BoolAr[1] or not(IsPointWalkable(x, y)) then
                set Ar[i] = Ar[Total - 1]
                set Total = Total - 1

                call dat.destroy()
            endif

            set i = i + 1
        endloop

        if Total == 0 then
            call PauseTimer(Tim)
        endif
    endmethod

    method onDestroy takes nothing returns nothing
        if .e != null then
            call DestroyEffect(.e)
        endif

        call PauseUnit(.u, false)

        set BoolAr[0] = false
        set BoolAr[1] = false
    endmethod
endstruct

function KnockbackEx takes unit u, real d, real a, real w, real r, integer t, string s, string p returns nothing
    call Data.create(u, R2I(w / Interval()), d, a, r, t, s, p)
endfunction

function Knockback takes unit u, real d, real a, real w returns nothing
    call Data.create(u, R2I(w / Interval()), d, a, 0, 0, "", "")
endfunction

private function Init takes nothing returns nothing
    set Cond = Filter(function TreeFilter)

    set BoolAr[0] = false
    set BoolAr[1] = false

    set MAX_X = GetRectMaxX(bj_mapInitialPlayableArea) - 64
    set MAX_Y = GetRectMaxY(bj_mapInitialPlayableArea) - 64
    set MIN_X = GetRectMinX(bj_mapInitialPlayableArea) + 64
    set MIN_Y = GetRectMinY(bj_mapInitialPlayableArea) + 64
endfunction

endlibrary
JASS:
// **************************************************************************
// ** **
// ** Charge **
// ** ————————————— **
// ** **
// ** A simple Charge Spell **
// ** **
// ** By: Majin **
// ** Edited: Pyrogasm, because this had terrible structure and encapsulation
// **
// ** **
// **************************************************************************

library ChargeExec initializer InitTrig requires Knockback
    globals
    //CHECK THE CHARGEEXEC TRIGGER FOR MORE OPTIONS
    //ID CONFIGURATIONS FOR SPELLS AND DUMMY UNITS
    //ID of the Charge spell
        private constant integer SPELLID = 'A003'
       
    //ID of the spell to be casted by the dummy unit once the charge is complete (Has to be a spell with
    //a Target). Set this to 0 for no spell. The Spell effect depends on the level of the Charge Spell
    //So it's advisable to set the number of levels of this spell at the same value of the level of the
    //Charge Spell
        private constant integer DUMMYSPELLID = 'A000'
       
    //Order String for the spell to be casted by the dummy unit
        private constant string SPELLORDERID = "thunderbolt"

    //SPECIAL EFFECTS CONFIGURATION
    //You can set this to a non existand model (or change the code of the spell) if you don't want that
    //effect

    //Special Effect attached on the unit (in this example it will look like a red trail)
        private constant string ATTACHEDEFFECT = "Abilities\\Weapons\\PhoenixMissile\\Phoenix_Missile_mini.mdl"
       
    //Special Effect created by the unit while walking
        private constant string WALKEFFECT = "Objects\\Spawnmodels\\Undead\\ImpaleTargetDust\\ImpaleTargetDust.mdl"

    //Special Effect used at the end of the Charge
        private constant string ENDINGEFFECT = "Abilities\\Spells\\Human\\Thunderclap\\ThunderClapCaster.mdl"

    //NUMERICAL SETTINGS

    //Charge Starting Speed
        private constant real STARTSPEED = 20.00

    //Max Speed that can be reached by the Caster unit while charging
        private constant real MAXSPEED = 100.00

    //Damage Dealt at the end of the Charge (0 for no damage). DEPENDS ON LEVEL
        private constant real CHARGEDAMAGE = 5.00
       
    //Minimum Distance at where a unit can Charge (0 for no min distance)
    //Make sure the dummy spell has a longer distance than this
        private constant real MINDISTANCE = 400.00

    //Maximum Distance at where a unit can Charge (0 for no max distance)
    //This is mainly used to interrupt the charge if the target unit gets teleported somewhere else
    //Make sure the dummy spell has a distance equal or less than this number
        private constant real MAXDISTANCE = 2000.00
       
    //KNOCKBACK SETTINGS
    //Theese settings refer to the Knock trigger which is not made by me but by Silvenon. If you want
    //to know more about this script, got to
    //http://www.hiveworkshop.com/forums/jass-functions-413/knockback-unit-35545/
    //Special Effect created by the knockback
        private constant string KNOCKEFFECT = "Objects\\Spawnmodels\\Undead\\ImpaleTargetDust\\ImpaleTargetDust.mdl"

    //The Distance of the Knockback effect. DEPENDS ON LEVEL
        private constant real KNOCKDISTANCE = 50.00
       
    //The Duration of the Knockback effect
        private constant real KNOCKDURATION = 1.5
       
    //Tree Destroying effect
    //If you want trees destroyed behind your target while he's knocked back, then set this to true
    //Otherwise set this to false. See the Knock Trigger for more configuration options
        private constant boolean KNOCKTREECHECK = true

    //Knockback Effect
    //This is the effect played on the Knocked Back Unit. If you set this to 0 then no effect will be played
    //If you set this to 1 then the effect set on the KnockEffect() function is played as a periodic effect
    //If you set this to 2 then the effect will be attached on the unit and destroyed once the knocback ends
        private constant integer KNOCKPLAYEFFECT = 1
       
    //Attachement point of the effect played during the knockback. Default is "chest"
        private constant string KNOCKATTACH = "Foot"

    //You may want to check the Walkable library definition for a couple Configuration Options that were moved by Pyrogasm
    endglobals
   
    private function KnockTree takes nothing returns real
        if (KNOCKTREECHECK==true) then
            return 150.00
        endif
            return 0.00
    endfunction

    private struct ChargeLoop
        private real ChargeTargetx
        private real ChargeTargety
        private real ChargeEndx
        private real ChargeEndy
        private real ChargeLocx
        private real ChargeLocy
        private real ChargeNextx
        private real ChargeNexty
        private unit ChargeTarget
        private unit ChargeCaster
        private real ChargeAngle
        private real ChargeDistance
        private effect ChargeEffect
        private timer ChargeTimer
        private boolean ChargeCond = false
        private integer SpellLvl
       
        //START OF CONFIGURATION METHODS
       
        //With this method you can set the "acceleration rate" (Physicists  Forgive me) of the charge.
        //You can use any kind of numeric function (for example this.ChargeDistance * 2.00 will double
        //The speed at every loop)
        private method ChangeDistance takes nothing returns real
            return this.ChargeDistance + 1.00
        endmethod
        //END OF CONFIGURATION METHODS
       
        //START OF CUSTOMIZABLE METHODS
       
        //This method sets the conditions when to activate the AlmostCharge method. In particular
        //You may be interested in changing the distance at where the condition is true.
        //In this Example it will be activated when the Caster is 16x times the Charge Speed Far away
        //From the target
        private method AlmostChargeCond takes nothing returns boolean
            return ((GetDistance(this.ChargeLocx, this.ChargeLocy, this.ChargeEndx,  this.ChargeEndy) < ( this.ChargeDistance * 16.00 )) and this.ChargeCond == false)
        endmethod
       
        //Actions to take when the Charge is almost completed. In this example the caster will play
        //The slam animation (Being a Blademaster, it will jump in the air while attacking)
        private method AlmostCharge takes nothing returns nothing
            set this.ChargeCond = true
            call SetUnitTimeScale( this.ChargeCaster, 2.00 )
            call SetUnitAnimation( this.ChargeCaster, "slam" )
            call QueueUnitAnimation( this.ChargeCaster, "stand")
        endmethod
       
        //Actions to take when the Caster reaches its target and the charge is completed.
        //In this example the target unit will be stunned and you will see a Thunderclap effect on the ground
        private method CompleteCharge takes nothing returns nothing
            local effect tempEffect
            local unit dummy
            local real angle=GetAngle(this.ChargeLocx, this.ChargeLocy, this.ChargeEndx, this.ChargeEndy)
           
            set this.ChargeLocx = this.ChargeNextx
            set this.ChargeLocy = this.ChargeNexty
            set this.ChargeNextx = PolarProjectionx(this.ChargeLocx, this.ChargeDistance, this.ChargeAngle)
            set this.ChargeNexty = PolarProjectiony(this.ChargeLocy, this.ChargeDistance, this.ChargeAngle)
           
            call DestroyEffect(AddSpecialEffect( WALKEFFECT, this.ChargeLocx, this.ChargeLocy ))
           
            call SetUnitX( this.ChargeCaster, this.ChargeEndx )
            call SetUnitY( this.ChargeCaster, this.ChargeEndy )
           
            set dummy = CreateUnit( GetOwningPlayer(this.ChargeCaster), DUMMYID, this.ChargeLocx, this.ChargeLocy, 0 )
            call UnitAddAbility( dummy, DUMMYSPELLID )
            call SetUnitAbilityLevel(dummy, DUMMYSPELLID, this.SpellLvl)
            call IssueTargetOrder( dummy, SPELLORDERID, this.ChargeTarget )
            call UnitApplyTimedLife( dummy, 'BTLF', 1.00)
           
            call DestroyEffect(AddSpecialEffect( ENDINGEFFECT, this.ChargeLocx, this.ChargeLocy ))
           
            call UnitDamageTarget( this.ChargeCaster, this.ChargeTarget, CHARGEDAMAGE*this.SpellLvl, true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_UNIVERSAL, WEAPON_TYPE_WHOKNOWS )
            call PauseUnit( this.ChargeCaster, false )
            //This is the Knockback effect, see the Knock Trigger for more customization options
            call KnockbackEx(this.ChargeTarget, KNOCKDISTANCE*this.SpellLvl, angle, KNOCKDURATION, KnockTree(), KNOCKPLAYEFFECT, KNOCKEFFECT, KNOCKATTACH)
            call IssueTargetOrder( this.ChargeCaster, "attack", this.ChargeTarget )
           
            set dummy = null
        endmethod

        //What to do during the charge, the unit is moved, an effect is placed on the ground and
        //the charging speed is increased
        private method ContinueCharge takes nothing returns nothing
            call DestroyEffect(AddSpecialEffect( WALKEFFECT, this.ChargeLocx, this.ChargeLocy ))
           
            call SetUnitX(this.ChargeCaster, this.ChargeNextx)
            call SetUnitY(this.ChargeCaster, this.ChargeNexty)
           
            call SetUnitFacing(this.ChargeCaster, bj_RADTODEG * GetAngle(this.ChargeNextx, this.ChargeNexty, this.ChargeTargetx, this.ChargeTargety))
           
            set this.ChargeLocx = this.ChargeNextx
            set this.ChargeLocy = this.ChargeNexty
            set this.ChargeNextx = PolarProjectionx(this.ChargeLocx, this.ChargeDistance, this.ChargeAngle)
            set this.ChargeNexty = PolarProjectiony(this.ChargeLocy, this.ChargeDistance, this.ChargeAngle)
                       
            if (this.ChargeDistance <= MAXSPEED) then
                set this.ChargeDistance = this.ChangeDistance()
            endif
        endmethod
       
        //END OF CUSTOMIZABLE METHODS
       
        //START OF OTHER METHODS
        //This is the main method executed during the charge
        public static method ChargeActions takes nothing returns nothing
            local ChargeLoop l=GetTimerInt(GetExpiredTimer())
           
            set l.ChargeTargetx = GetUnitX(l.ChargeTarget)
            set l.ChargeTargety = GetUnitY(l.ChargeTarget)
            set l.ChargeEndx = PolarProjectionx(l.ChargeTargetx, 100.00, GetAngle(l.ChargeTargetx, l.ChargeTargety, l.ChargeLocx, l.ChargeLocy))
            set l.ChargeEndy = PolarProjectiony(l.ChargeTargety, 100.00, GetAngle(l.ChargeTargetx, l.ChargeTargety, l.ChargeLocx, l.ChargeLocy))
           
            set l.ChargeAngle = GetAngle(l.ChargeLocx, l.ChargeLocy, l.ChargeEndx, l.ChargeEndy)
            if ( l.AlmostChargeCond() ) then
            //What to do when the caster is about to reach the target
                call l.AlmostCharge()
            endif
            //If the Target gets too far away (by teleportation, for example) then stops the Charge
            if ( GetDistance(l.ChargeLocx, l.ChargeLocy, l.ChargeEndx, l.ChargeEndy) >= MAXDISTANCE and (MAXDISTANCE != 0.00) ) then
                call l.StopCharge()
            endif
            if ( l.ChargeConditions() ) then
            //If the unit hasn't reached the target yet, then execute this
                call l.ContinueCharge()
                call TimerAttach(l.ChargeTimer,0.03,l,function ChargeLoop.ChargeActions)
            else
                if ( l.ChargeCompleted() ) then
            //What to do if the charge is succesfully completed
                    call l.CompleteCharge()
                endif
                call l.StopCharge()
            endif
        endmethod
       
        //Actions to take once the Charge is being stopped (either because it wasn't possible to complete
        //the charge or because the caster reached its target)
        private method StopCharge takes nothing returns nothing
            set this.ChargeCond = false
            call SetUnitPathing(this.ChargeCaster, true)
            call PauseUnit(this.ChargeCaster, false)
            call SetUnitAnimation(this.ChargeCaster, "stand")
            call SetUnitTimeScale(this.ChargeCaster, 1.00)
            call DestroyEffect(this.ChargeEffect)
            call this.destroy()
        endmethod
       
        private method ChargeCompleted takes nothing returns boolean
            if ((GetDistance(this.ChargeLocx, this.ChargeLocy, this.ChargeEndx, this.ChargeEndy) <= this.ChargeDistance*1.10)  and IsPointWalkable(this.ChargeNextx, this.ChargeNexty)) then
                if ((IsTerrainPathable(this.ChargeNextx, this.ChargeNexty, PATHING_TYPE_WALKABILITY) == false ) and ( (GetUnitState(this.ChargeTarget, UNIT_STATE_LIFE)<=0) == false )) then
                    if(((GetUnitState(this.ChargeCaster, UNIT_STATE_LIFE) <= 0) == false)) then
                        return true
                    endif
                endif
            endif
                return false
        endmethod

        private method ChargeConditions takes nothing returns boolean
            if ((GetDistance(this.ChargeLocx, this.ChargeLocy, this.ChargeEndx, this.ChargeEndy) > this.ChargeDistance*1.10)  and IsPointWalkable(this.ChargeNextx, this.ChargeNexty)) then
                if ((IsTerrainPathable(this.ChargeNextx, this.ChargeNexty, PATHING_TYPE_WALKABILITY) == false ) and ( (GetUnitState(this.ChargeTarget, UNIT_STATE_LIFE)<=0) == false )) then
                    if(((GetUnitState(this.ChargeCaster, UNIT_STATE_LIFE) <= 0) == false)) then
                        return true
                    endif
                endif
            endif
                return false
        endmethod
       
        private method onDestroy takes nothing returns nothing
            call ReleaseTimer(this.ChargeTimer)
            set this.ChargeTarget = null
            set this.ChargeCaster = null
            set this.ChargeEffect = null
        endmethod
       
        static method create takes real pChargeTargetx, real pChargeTargety, real pChargeEndx, real pChargeEndy, real pChargeLocx, real pChargeLocy, real pChargeNextx, real pChargeNexty, unit pChargeTarget, unit pChargeCaster, real pChargeAngle, real pChargeDistance, effect pChargeEffect, timer pChargeTimer returns ChargeLoop
            local ChargeLoop l = ChargeLoop.allocate()
            set l.ChargeTargetx = pChargeTargetx
            set l.ChargeTargety = pChargeTargety
            set l.ChargeEndx = pChargeEndx
            set l.ChargeEndy = pChargeEndy
            set l.ChargeLocx = pChargeLocx
            set l.ChargeLocy = pChargeLocy
            set l.ChargeNextx = pChargeNextx
            set l.ChargeNexty = pChargeNexty
            set l.ChargeTarget = pChargeTarget
            set l.ChargeCaster = pChargeCaster
            set l.ChargeAngle = pChargeAngle
            set l.ChargeDistance = pChargeDistance
            set l.ChargeEffect = pChargeEffect
            set l.ChargeTimer = pChargeTimer
            set l.SpellLvl = GetUnitAbilityLevel(l.ChargeCaster, SPELLID)
            return l
        endmethod
       
        //END OF OTHER METHODS
    endstruct

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

    private function CheckActions takes nothing returns nothing
        local real ChargeLocx = GetUnitX(GetTriggerUnit())
        local real ChargeLocy = GetUnitY(GetTriggerUnit())
        local real ChargeEndx = GetUnitX(GetSpellTargetUnit())
        local real ChargeEndy = GetUnitY(GetSpellTargetUnit())
        local sound s
       
        if ( GetDistance(ChargeLocx, ChargeLocy, ChargeEndx, ChargeEndy) <= (MINDISTANCE) ) then
            call DisplayTextToPlayer( GetOwningPlayer(GetTriggerUnit()), 0, 0, "|cffFFCC00Target is too close!|r" )
            set s = CreateSound("Sound\\Interface\\Error.wav", false, false, true, 12700, 12700, "")
            call StartSound(s)
            call KillSoundWhenDone(s)
            set s = null
            call IssueImmediateOrder( GetTriggerUnit(), "stop" )
        endif
    endfunction
   
    private function InitActions takes nothing returns nothing
        local timer ChargeTimer = NewTimer()
        local effect ChargeEffect
        local unit ChargeCaster = GetTriggerUnit()
        local unit ChargeTarget = GetSpellTargetUnit()
        local real ChargeDistance = STARTSPEED
        local real ChargeLocx = GetUnitX(ChargeCaster)
        local real ChargeLocy = GetUnitY(ChargeCaster)
        local real ChargeTargetx = GetUnitX(ChargeTarget)
        local real ChargeTargety = GetUnitY(ChargeTarget)
        local real ChargeEndx = PolarProjectionx(ChargeTargetx, 100.00, GetAngle(ChargeTargetx, ChargeTargety, ChargeLocx, ChargeLocy))
        local real ChargeEndy = PolarProjectiony(ChargeTargety, 100.00, GetAngle(ChargeTargetx, ChargeTargety, ChargeLocx, ChargeLocy))
        local real ChargeAngle = GetAngle(ChargeLocx, ChargeLocy, ChargeEndx, ChargeEndy)
        local real ChargeNextx = PolarProjectionx(ChargeLocx, ChargeDistance, ChargeAngle)
        local real ChargeNexty = PolarProjectiony(ChargeLocy, ChargeDistance, ChargeAngle)
        local ChargeLoop stru
       
        set ChargeEffect = AddSpecialEffectTarget(ATTACHEDEFFECT, ChargeCaster, "chest" )
       
        set stru = ChargeLoop.create(ChargeTargetx, ChargeTargety, ChargeEndx, ChargeEndy, ChargeLocx, ChargeLocy, ChargeNextx, ChargeNexty, ChargeTarget, ChargeCaster, ChargeAngle, ChargeDistance, ChargeEffect, ChargeTimer)
        call SetUnitPathing(ChargeCaster, false)
        call SetUnitTimeScale(ChargeCaster, 3)
        call IssueImmediateOrder(ChargeCaster, "stop" )
        call PauseUnit(ChargeCaster, true)
        call SetUnitAnimationByIndex(ChargeCaster,6)
        call TimerAttach(ChargeTimer,0,stru,function ChargeLoop.ChargeActions)
       
        set ChargeTimer = null
        set ChargeEffect = null
        set ChargeCaster = null
        set ChargeTarget = null
    endfunction

    private function InitTrig takes nothing returns nothing
        local trigger ChargeCheck = CreateTrigger()
        local trigger ChargeInit = CreateTrigger()
        local filterfunc f = Filter(function AntiLeak)
        local integer i = 0
        loop
            exitwhen i == 16
            call TriggerRegisterPlayerUnitEvent(ChargeCheck,Player(i),EVENT_PLAYER_UNIT_SPELL_CAST,truefilter)
            call TriggerRegisterPlayerUnitEvent(ChargeInit,Player(i),EVENT_PLAYER_UNIT_SPELL_EFFECT,truefilter)
            set i = i + 1
        endloop
        call TriggerAddCondition(ChargeCheck, Condition(function Conditions))
        call TriggerAddAction(ChargeCheck, function CheckActions)
        call TriggerAddCondition(ChargeInit, Condition(function Conditions))
        call TriggerAddAction(ChargeInit, function InitActions)
        set ChargeCheck = null
        set ChargeInit = null
        set f = null
    endfunction
endlibrary
I did test this and make sure it saved duplicates without conflicts. The main spell code can be duplicated and all you have do is rename the library. Note the one strange choice I was forced to make is putting the dummy caster unitid definition inside of the Walkability library. That shouldn't matter because it's just a dummy unit but don't forget to change the rawcode there if you have to.

@Wrda Could you alter the spell code in the main description or update the map? I put the new code into the original map and attached it here. This resource is well and truly awfully structured and should never have been approved even in 2009.
 

Attachments

  • vJASS Charge - Pyrogasm Edit.w3x
    101.8 KB · Views: 5
Last edited:
Top