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

Overrun v2.1

Discription:
The caster runs towards the target location, getting more faster and transparent the longer he runs. Damages every enemy he strifes on his way depending on the moved distance and level. When the caster reaches his target, all enemy units get knocked back.

Credits:
Vexorian: vJass and TimerUtils
Rising Duks: Knockback, Last Order & Group Utils (and those required systems)
Antinarf: IsTerrainWalkable

Works properly with patch 1.24!

IMPORTANT:

v2.0:
- alot code changes
- Spellbook now with spell immunity works properly
- Damage and Transparency now depends on the max distance and not of the casted distance
- Added the possiblity to destroy trees when moving
- The onDestroy method now checks, if caster is alive and then do the knockback
- Now uses xe to deal damage and preload the abilitys
- now uses "thistype" instead of the structname for faster change of the name in other maps

v2.1:
- added the possibility to setup the attachment points of created effects



JASS:
//****************************************************************************\\
//**                                                                        **\\
//**                              OVERRUN v2.0                              **\\
//**                                by Kricz                                **\\
//**                                                                        **\\
//**                                Requires:                               **\\
//**                 vJass, this trigger & Wc3 version 1.24                 **\\
//**                                                                        **\\
//**                                Credits:                                **\\
//**                   Vexorian: vJass, TimerUtils and xe                   **\\
//**            Rising Duks: Knockback, Last Order & Group Utils            **\\
//**                      Antinarf: IsTerrainWalkable                       **\\
//**                                                                        **\\
//**                                                                        **\\
//**                              Discription:                              **\\
//**              The caster runs towards the target location,              **\\
//**         getting more faster and transparent the longer he runs.        **\\
//**       Damaged every enemy he strifes on his way depending on the       **\\
//**      moved distance and level. When the caster reaches his target,     **\\
//**                   all enemy units get knocked back.                    **\\
//**                                                                        **\\
//****************************************************************************\\


scope Overrun //requires TimerUtils, Knockback, Last Order, Group Utils, xebasic, xedamage, xepreload

globals
    //Raw-Code of the Spell
    private constant integer SPELL_ID           = 'A000'
    //Rawcode of the Spellbook for Spell Immunity
    private constant integer SPELLBOOK_ID       = 'A001'
    //The acceleration with each interval
    private constant real ACCELERATION          = 375.
    //Should trees get destroyed (ignores collosion with them!)
    private constant boolean DESTROY_TREES      = true
    //The max transparency (0 = transparent, 255 = normal)
    private constant real MAX_TRANSPARENT       = 50.
    //The Start-Factor of the units animation
    private constant real ANIM_START_FACTOR     = 1.25
    //The End-Factor the unit will have when it reaches his target or the max range for this level
    private constant real ANIM_END_FACTOR       = 3.
    //The Animation-Id you want to have while the unit moves
    private constant integer AMIN_ID            = 0
    //The effect created at the start
    private constant string EFFECT_START        = "Abilities\\Spells\\Human\\Defend\\DefendCaster.mdl"
    //The effect attached to the unit while moving
    private constant string EFFECT_MOVE         = "MDX\\KnockbackDust.mdx"
    //The effect when the unit reaches its target / max range for the level
    private constant string EFFECT_END          = "Abilities\\Spells\\NightElf\\BattleRoar\\RoarCaster.mdl"
    //The "collision-size" of the unit while it move.
    private constant real DAMAGE_AOE            = 165.
    //The knockback-AoE at the end
    private constant real KNOCKBACK_AOE         = 250.
    //The knockback-StartSpeed at the end
    private constant real KNOCKBACK_START_SPEED = 675.
    //The knockback-speed-decrement
    //Rising Dusk's KnockbackSystem will change this value itself (!!!) :
    //KNOCKBACK_DECREMENT * 0.05 * 0.05
    private constant real KNOCKBACK_DECREMENT   = 1400.
endglobals

//In this function, you can setup the damage
//Please note, that the damage depends on the distance the unit moved! So this function uses the damage for 100% moved
private function DAMAGE takes integer lvl returns real
    return 100. + 75. * lvl
endfunction

//The max-range of the spell
private function MAX_RANGE takes integer lvl returns real
    return 800. + 200. * lvl
endfunction

//! textmacro OR_SETUP_DAMAGE
    set .dtype = DAMAGE_TYPE_NORMAL
    set .atype = ATTACK_TYPE_HERO
    set .wtype = WEAPON_TYPE_WOOD_HEAVY_SLICE
//! endtextmacro
    
//Don't change things below here unless you really know what you're doing!!!    
private struct spelldata
    unit caster = null
    integer lvl = 0
    real cos = 0.
    real sin = 0.
    real dist = 0.
    real speed = 0.
    real angle = 0.
    real x = 0.
    real y = 0.
    real moved = 0.
    real percent = 0.
    timer t = null
    group g = null
    effect fx = null
    static group group
    static boolexpr filter
    static thistype temp
    static delegate xedamage dmg
    
    static method create takes unit caster, real distance, real angle returns thistype
        local thistype this = thistype.allocate()
        set .caster = caster
        set .lvl = GetUnitAbilityLevel(.caster, SPELL_ID)
        set .dist = distance
        set .cos = Cos(angle)
        set .sin = Sin(angle)
        set .angle = angle
        set .x = GetUnitX(.caster)
        set .y = GetUnitY(.caster)
        set .speed = GetUnitMoveSpeed(.caster)
        set .t = NewTimer()
        set .g = NewGroup()
        set .fx = AddSpecialEffectTarget(EFFECT_MOVE, .caster, "origin")
        
        call SetUnitPathing(.caster, false)
        call SetUnitTimeScale(.caster, ANIM_START_FACTOR)
        call SetTimerData(.t, this)
        call UnitAddAbility(.caster, SPELLBOOK_ID)
        
        call DestroyEffect(AddSpecialEffectTarget(EFFECT_START, .caster, "origin"))        
        call TimerStart(.t, XE_ANIMATION_PERIOD, true, function thistype.periodic)
        return this
    endmethod
    
    private method onDestroy takes nothing returns nothing
        local unit u = null
        local real angle = 0
        
        call DestroyEffect(.fx)
        call SetUnitTimeScale(.caster, 1.)
        call SetUnitPathing(.caster, true)
        call SetUnitVertexColor(.caster, 255, 255, 255, 255)
        call UnitRemoveAbility(.caster, SPELLBOOK_ID)
        call ReleaseGroup(.g)
              
        
        if GetWidgetLife(.caster) > 0.405 then
            call DestroyEffect(AddSpecialEffectTarget(EFFECT_END, .caster, "origin"))
            call SetUnitAnimation(.caster, "stand")
            set .temp = this
            call GroupEnumUnitsInRange(.group, .x, .y, KNOCKBACK_AOE, .filter)
            call IssueLastOrder(.caster)
            loop
                set u = FirstOfGroup(.group)
                exitwhen u == null
                set angle = Atan2(GetUnitY(u)- .y, GetUnitX(u) - .x) * bj_RADTODEG
                call KnockbackTarget(.caster, u, angle, KNOCKBACK_START_SPEED, KNOCKBACK_DECREMENT, true, true, false)
                call GroupRemoveUnit(.group, u)
            endloop
            call GroupClear(.group)
        endif
    endmethod
        
    private static method FilterFunc takes nothing returns boolean
        return GetWidgetLife(GetFilterUnit()) > 0.405 and not IsUnit(GetFilterUnit(), .temp.caster) and IsUnitEnemy(GetFilterUnit(), GetOwningPlayer(.temp.caster)) and not IsUnitType(GetFilterUnit(), UNIT_TYPE_FLYING) and not IsUnitType(GetFilterUnit(), UNIT_TYPE_MECHANICAL) and not IsUnitType(GetFilterUnit(), UNIT_TYPE_STRUCTURE)
    endmethod
    
    private static method periodic takes nothing returns nothing
        local thistype this = thistype(GetTimerData(GetExpiredTimer()))
        local real damage = 0
        local unit u = null
        local real nx = 0.
        local real ny = 0.
        
        
        set .speed = .speed + ACCELERATION * XE_ANIMATION_PERIOD
        set nx = .x + (.speed * XE_ANIMATION_PERIOD) * .cos
        set ny = .y + (.speed * XE_ANIMATION_PERIOD) * .sin
        
        if DESTROY_TREES then
            call .damageDestructablesAOE(.caster, nx, ny, DAMAGE_AOE, 100000.00) 
        endif
        
        if .moved >= .dist or .moved >= MAX_RANGE(.lvl) or GetWidgetLife(.caster) < 0.405 or not IsTerrainWalkable(nx, ny) then
            call ReleaseTimer(.t)
            call .destroy()
        else            
            
            call SetUnitAnimationByIndex(.caster, AMIN_ID)
            set .moved = .moved + .speed * XE_ANIMATION_PERIOD
            
            call SetUnitPosition(.caster, nx, ny)
            
            set .x = GetUnitX(.caster)
            set .y = GetUnitY(.caster)
            set .percent = (.moved / MAX_RANGE(.lvl))
            
            set damage = DAMAGE(.lvl) * .percent
            
            set .temp = this
            call GroupEnumUnitsInRange(.group, .x, .y, DAMAGE_AOE, .filter)
            
            loop
                set u = FirstOfGroup(.group)
                exitwhen u == null
                    if not IsUnitInGroup(u, .g) then
                        call .damageTarget(.caster, u, damage)
                    endif
                call GroupRemoveUnit(.group, u)
                call GroupAddUnit(.g, u)
            endloop
            call GroupClear(.group)
            
            call SetUnitFacing(.caster, .angle * bj_RADTODEG)
            call SetUnitVertexColor(.caster, 255, 255, 255, R2I(255. * (1. - .percent) + MAX_TRANSPARENT * .percent))
            call SetUnitTimeScale(.caster, ANIM_START_FACTOR * (1. - .percent) + ANIM_END_FACTOR * .percent)
        endif
    endmethod        
    
    private static method register takes nothing returns boolean
        local unit caster = null
        local real dx = 0.
        local real dy = 0.
        local real distance = 0.
        local real angle = 0.
        if GetSpellAbilityId() == SPELL_ID then
            set caster = GetSpellAbilityUnit()
            set dx = GetSpellTargetX() - GetUnitX(caster)
            set dy = GetSpellTargetY() - GetUnitY(caster)
            set distance = SquareRoot(dx * dx + dy * dy)
            set angle = Atan2(GetSpellTargetY() - GetUnitY(caster), GetSpellTargetX() - GetUnitX(caster))
            call thistype.create(caster, distance, angle)
            set caster = null
            return true
        endif
        return false
    endmethod
    
    private static method onInit takes nothing returns nothing
        local trigger t = CreateTrigger()
        local integer i = 0
        call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
        call TriggerAddCondition(t, Condition(function thistype.register))
        set .filter = Condition(function thistype.FilterFunc)
        set .group = NewGroup()
        set .dmg = xedamage.create()
        //! runtextmacro OR_SETUP_DAMAGE()
        call XE_PreloadAbility(SPELL_ID)
        call XE_PreloadAbility(SPELLBOOK_ID)
        loop
            call SetPlayerAbilityAvailable(Player(i), SPELLBOOK_ID, false)
            set i = i + 1
            exitwhen i >= bj_MAX_PLAYER_SLOTS
        endloop
        set t = null
    endmethod
    
endstruct

endscope

Keywords:
Overrun, Spike, Knockback, Knight, transparent, move, run, damage, 1.24
Contents

Overrun v2.1 (Map)

Reviews
19:02, 4th Oct 2009 TriggerHappy187: Decent spell, and well coded. Though a few minor changes could be made.
Level 17
Joined
Mar 17, 2009
Messages
1,349
JASS:
//filter-function, which units can be damaged?
private function filter takes nothing returns boolean
    return not IsUnit(GetFilterUnit(), TempUnit) and GetWidgetLife(GetFilterUnit()) > 0.405 and IsUnitEnemy(GetFilterUnit(), GetOwningPlayer(TempUnit)) and not IsUnitType(GetFilterUnit(), UNIT_TYPE_FLYING) and not IsUnitType(GetFilterUnit(), UNIT_TYPE_MECHANICAL) and not IsUnitType(GetFilterUnit(), UNIT_TYPE_STRUCTURE)
endfunction
Well, at first cast you happen to use TempUnit before setting it to some value...

Other than that, I skimmed the trigger and well, it's obviously done by someone who knows what he's doing :p

Yet, Aspard's question is interesting ;)
 
Level 8
Joined
Aug 2, 2008
Messages
193
Hmm...^^
Yeah the unit will move if it gets stunned while it move...
But how to remove this "bug" ?..^^
I could add the unit magic immunity in a spellbook or ignore the stun...
Btw, fixed the thing you said Deuterium and fixed a additional bug in the destroy method (TempUnit wasn't set to this.caster for filter --> knockback could knock caster back :D)
 
Level 17
Joined
Mar 17, 2009
Messages
1,349
Ok to fix the stun thing, what you can do isn't a total fix, but it'll work. Unless someone has a better idea, there's what you can do.

Basically, as you're moving the unit, check if the unit has any of the two Root Stun Buffs, and if it has it you stop the casting process.

And you can adjust a buff check filter, where the user can add all the custom stun buffs - along the two root ones - altogether there...

Sounds good enough?
 
Level 8
Joined
Aug 2, 2008
Messages
193
Hmm...
I think i will simply add the unit while it runs spell immunity but change something on the mechanic:
At the moment, the moved distance it calculated by the distance you set (this mean, if you set 100 at distance, youll get full power at 100 range, lil bit imba^^)
I'll change it so, that the spell doesn't take the distance you chose, but the Max-Distance of this level.
For Example:
On Level 1 is the max-distance 1000. So 1% would be 10 units moved, so it isn't overpowered :D

And I'll remove the 1.23 Spell, because 1.24 is out now..

Edit:
well, i guess the update take some time...
Damned Patch 1.24...

Edit 2:
hmkay, found a way to remove these blizzard bugs or how to avoid them..
Will change something in the triggers and update the tower then...
 
Last edited:


Concept (30):

Idea
6 /10
Complexity
6 /10
Style
8 /10
Comment:
The idea is quite good, however, this already has been made, and the concept could be a bit more creative.
Effects (10):

All:
6 /10
Comment:
Not that much effects, but it was quite ok. You were able to chose them, aswell.
Scripting (30):

Speach:
7 /10
Leakage:
10 /10
Effective Coding:
9 /10
Comment:
I really like the scripting. The speach has been decreased by 3 points because:

  • It lacks comments on and in methods
  • It contains blocks of code without any logical link
Else it looks really fine, no big leak seen, runs smooth, customizeable.
Score: 50/70
Final Score: 3,58 / 5

In total the spell is really fine. I suggest using this if its what you are looking for. [4/5] vote

[X] +Rep
[ ] Report
(Blank box means no)

 
Last edited:
Level 8
Joined
Aug 2, 2008
Messages
193
Hey thanks very much for the feedback and the rep :D
Hmm.. i could an ann effect when units get damaged, think that would be a good idea.
And I'll add some commands to some important methods and functions :p

And i had the idea to add a second spell, target unit spell, which is quite the same but the caster runs to the target (homing...^^)
But I have to sea how i can make this looking good... :D
 
Very nice spell and code.

  • Whats the point of the textmacro, globals would be perfectly fitting.
  • Maybe make the attachment point configurable ("origin")?
  • Currently it looks as if your setting the casters position in a way which it will acknowledge pathing. You might want to make a constant boolean (IGNORE_PATHING) that would work like this;
  • JASS:
    if IGNORE_POSITION then
        call SetUnitPosition(.caster, nx, ny)
    else
        call SetUnitX(.caster, nx)
        call SetUnitY(.caster, ny)
    endif
 
Level 8
Joined
Aug 2, 2008
Messages
193
I think you don't need extra globals for the xedamage textmacros, you simply can change this values by yourself in the macro.
Will now add custom attachpoints to the system.
I disable the unit pathing in the code, calling a native. And a huge problem with SetUnitPosition is, that the ordered unit order will be delated.
That means, that Rising_Dusks LastOrder wouldn't work correctly.

Edit:
Updated Spell to Version 2.1!
 
Last edited:
Level 14
Joined
Nov 18, 2007
Messages
816
replace the static .group member with ENUM_GROUP

FirstOfGroup loops are less efficient than simply using the boolexpr of the enum

I also think you should use a stack, instead of using one timer per instance.

Other than that, i have nothing really to say. You could have used an event stack but some dont like that, and frankly, unless youre dealing with heaps of abilities in your map it wont matter.
Hmmm, maybe you could use an active ability granting you magic immunity and a buff, and tie the rage to that buff, so that if the buff is removed, so is your rage (this would save you the spell book).

Hes spelled Anitarf ;).

Also, strictly speaking, this spell isnt JESP compatible. Not that this has anything to say though.
You might also want to think about just ordering the unit to attack the target unit, and just check the distance and current order.
And why dont you make it a library so you can move the requires list out of the comments?
 
Level 8
Joined
Aug 2, 2008
Messages
193
Whats so bad with ENUM_GROUP?
A static member is the same as a globals group, just inside the struct oO
If think I could add FirstOfGroup pick into the boolexpr.
But one thing I don't like is stacks in spells, if they aren't really required. If someone need one, he could add this very easy i think. Is there any ability, which grants a unit spell immunity and a buff? oO
And thank for the typo, will fix this.
But why isn't this spell JESP?
And i make spells always in scopes, with headers and so on :)
 
Top