• 🏆 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!

Death Carrier

Death Carrier
By moyack. 2011.

attachment.php


Requires:

Description:

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.


JASS:
//***************************************************************************************************************
//*                                                                                                             *
//*                                        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...
globals
    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
endglobals

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

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
endfunction

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

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

private struct data
    group g
    unit c
    integer counter = 0
    
    private method onDestroy takes nothing returns nothing
        call GroupClear(.g)
        set .c = null
    endmethod
    
    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()
        endif
    endmethod
    
    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())
            return
        endif
        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))
            loop
                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)
            endloop
            call d.destroy()
            call ReleaseTimer(GetExpiredTimer())
        else
            call UnitDamageTarget(d.c, bj_groupRandomCurrentPick, GetRandomReal(0, Damage(GetUnitAbilityLevel(d.c, SpellID))), true, false, ATTACK, DAMAGE, WEAPON_TYPE_ROCK_HEAVY_BASH)
        endif
    endmethod
    
    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()
        endif
        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
    endmethod
endstruct

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

//===========================================================================
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
endfunction

endlibrary


Changelog:

(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.

Keywords:
Death, Carrier, Explosions, vJASS, moyack
Contents

Dead Carrier (Map)

Reviews
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...

Moderator

M

Moderator

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.
 
Top