• 🏆 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!
  • ✅ Time to vote for the top 3 models! The POLL for Hive's 6th HD Modeling Contest: Mechanical is now open! 📅 Poll close on July 16, 2024! 🔗 Cast your vote now!

Death Carrier

Death Carrier
By moyack. 2011.




A deadly mark moves randomly over the enemies in an area of effect, jumping 5 times over them before exploding and dealing damage to the nearest enemies to the explosion.

Level 1 - The explosion deals 100 damage.
Level 2 - The explosion deals 170 damage.
Level 3 - The explosion deals 240 damage.

Death Carrier.gif

How to install:

  1. Open the test map.
  2. Add TimerUtils to your map if you haven't it installed. (Could it be possible??).
  3. Copy the trigger, the ability and the effect used in this spell into you map.
  4. Voila!! spell installed.

//*                                                                                                             *
//*                                        Death Carrier (vJASSified).                                          *
//*                                                By Moyack.                                                   *
//*                                                  V.2.0.                                                     *
//*                                                                                                             *
// Made for the spell contest No 13 (for the bad luck maybe??)
// Requires TimerUtils (http://www.wc3c.net/showthread.php?t=101322) by Vexorian.

library DeathCarrier initializer init requires TimerUtils

// Configuration part...
    private constant integer SpellID = 'A000' // Sets the ability Rawcode
    private constant real    TIMEOUT = 0.5    // Sets the timer frequency
    private constant attacktype ATTACK = ATTACK_TYPE_SIEGE  // Sets the type of attack that deals the spell
    private constant damagetype DAMAGE = DAMAGE_TYPE_NORMAL // Sets the type of damage that deals the spell

private constant function Damage takes integer lvl returns real
    return 100. + 70. * (lvl - 1)

private constant function AOE takes integer lvl returns real
    return 500. // I left it in this way so it can be configurable to a variable area

private constant function Jumps takes integer lvl returns integer
    return 5 // sets the number of times the maks jumps on units before explode
// end configuration part...  

private function GetEnemies takes nothing returns boolean
    return GetUnitState(GetFilterUnit(), UNIT_STATE_LIFE) > 0.405 and IsUnitEnemy(GetFilterUnit(), GetOwningPlayer(bj_groupRandomCurrentPick)) 

private struct data
    group g
    unit c
    integer counter = 0
    private method onDestroy takes nothing returns nothing
        call GroupClear(.g)
        set .c = null
    private static method PickRandomUnit takes nothing returns nothing
        set bj_groupRandomConsidered = bj_groupRandomConsidered + 1
        if GetWidgetLife(GetEnumUnit()) > 0.405 and GetRandomInt(1,bj_groupRandomConsidered) == 1 then
            set bj_groupRandomCurrentPick = GetEnumUnit()
    static method effect takes nothing returns nothing
        local data d = thistype( GetTimerData(GetExpiredTimer()) )
        local real x
        local real y
        set bj_groupRandomConsidered = 0
        set bj_groupRandomCurrentPick = null
        call ForGroup(d.g, function thistype.PickRandomUnit)
        if bj_groupRandomCurrentPick == null then
            call d.destroy()
            call ReleaseTimer(GetExpiredTimer())
        call DestroyEffect(AddSpellEffectTargetById(SpellID, EFFECT_TYPE_SPECIAL, bj_groupRandomCurrentPick, "overhead"))
        set d.counter = d.counter + 1
        if d.counter > Jumps(GetUnitAbilityLevel(d.c, SpellID)) then
            set x = GetUnitX(bj_groupRandomCurrentPick)
            set y = GetUnitY(bj_groupRandomCurrentPick)
            call DestroyEffect(AddSpecialEffect(GetAbilityEffectById(SpellID, EFFECT_TYPE_SPECIAL, 1), x, y))
            // at this point, d.g is not needed, so I'll use to do the final task :P
            set bj_groupRandomCurrentPick = d.c
            call GroupEnumUnitsInRange(d.g, x, y, 0.5 * AOE(GetUnitAbilityLevel(d.c, SpellID)), Condition(function GetEnemies))
                set bj_groupRandomCurrentPick = FirstOfGroup(d.g)
                exitwhen bj_groupRandomCurrentPick == null
                call UnitDamageTarget(d.c, bj_groupRandomCurrentPick, Damage(GetUnitAbilityLevel(d.c, SpellID)), true, true, ATTACK, DAMAGE, WEAPON_TYPE_ROCK_HEAVY_BASH)
                call DestroyEffect(AddSpecialEffectTarget(GetAbilityEffectById(SpellID, EFFECT_TYPE_SPECIAL, 2), bj_groupRandomCurrentPick, "chest"))
                call GroupRemoveUnit(d.g, bj_groupRandomCurrentPick)
            call d.destroy()
            call ReleaseTimer(GetExpiredTimer())
            call UnitDamageTarget(d.c, bj_groupRandomCurrentPick, GetRandomReal(0, Damage(GetUnitAbilityLevel(d.c, SpellID))), true, false, ATTACK, DAMAGE, WEAPON_TYPE_ROCK_HEAVY_BASH)
    static method Start takes unit c, real x, real y returns nothing
        local data d = data.allocate()
        local timer t = NewTimer()
        if d.g == null then
            set d.g = CreateGroup()
        set d.c = c
        set bj_groupRandomCurrentPick = c
        call GroupEnumUnitsInRange(d.g, x, y, AOE(GetUnitAbilityLevel(c, SpellID)), Condition(function GetEnemies))
        call SetTimerData(t, integer(d))
        call TimerStart(t, TIMEOUT, true, function data.effect)
        set t = null

private function Conditions takes nothing returns boolean
    if GetSpellAbilityId() == SpellID then
        call data.Start(GetTriggerUnit(), GetSpellTargetX(), GetSpellTargetY())
    return false

private function init takes nothing returns nothing
    local trigger t = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_SPELL_EFFECT )
    call TriggerAddCondition( t, Condition( function Conditions ) )
    set t = null



(2/2/2009) Removed the timer from the struct, now it's managed via local variables.
(11/3/2009) Now every jump deals a random damage to the jumping unit.
(4/12/2011) Improved the code and the usage of some variables according to the new vJASS tendencies.

Death, Carrier, Explosions, vJASS, moyack

Dead Carrier (Map)

5 Dec 2011 Bribe: I still don't think you need a dynamic group "g" for each struct, you enumerate and then loop it just 0.5 seconds later, why not just enumerate and do the loop at the same time (that way you could use a FirstOfGroup loop + null...




5 Dec 2011
Bribe: I still don't think you need a dynamic group "g" for each struct, you enumerate and then loop it just 0.5 seconds later, why not just enumerate and do the loop at the same time (that way you could use a FirstOfGroup loop + null filter and get the compression benefit of having only one function, not needing global variables and only needing 1 global group instead of "N" dynamic groups).

In such a case you also wouldn't need an "onDestroy" method, just "set .c = null" before calling ".destroy".

IsUnitType, UNIT_TYPE_DEAD does not fail with spells like tranquility/gargoyle form (also if some trigger modified its life after death for example). I recommend just using the IsUnitType stuff for this because checking its life is not 100%.

Well the spell works but it can really benefit from these changes. So, Approved, and with some updates it will get a better rating.