Spell Death Carrier

moyack

Cool Member
Reaction score
9
Death Carrier
By moyack. 2009.

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 (hmm probably the name is not the best but meh...)
// By moyack. 2009.
// Made for the spell contest No 13 (for the bad luck maybe??)

// Requires TimerUtils.


library DeathCarrier initializer init requires TimerUtils

// Configuration part...
globals
    private constant integer SpellID = 'A000'
    private constant real    dt      = 0.5 // 
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 GetWidgetLife(GetFilterUnit()) > 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
    
    private static method DealDamage takes nothing returns nothing
        call UnitDamageTarget(GetEnumUnit(), GetEnumUnit(), Damage(GetUnitAbilityLevel(bj_groupRandomCurrentPick, SpellID)), true, true, ATTACK_TYPE_SIEGE, DAMAGE_TYPE_NORMAL, WEAPON_TYPE_ROCK_HEAVY_BASH)
        call DestroyEffect(AddSpecialEffectTarget(GetAbilityEffectById(SpellID, EFFECT_TYPE_SPECIAL, 2), GetEnumUnit(), "chest"))
    endmethod
    
    static method effect takes nothing returns nothing
        local data d = data( GetTimerData(GetExpiredTimer()) )
        local real x
        local real y
        set bj_groupRandomConsidered = 0
        set bj_groupRandomCurrentPick = null
        call ForGroup(d.g, function data.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 GroupClear(d.g)
            call DestroyEffect(AddSpecialEffect(GetAbilityEffectById(SpellID, EFFECT_TYPE_SPECIAL, 1), x, y))
            set bj_groupRandomCurrentPick = d.c
            call GroupEnumUnitsInRange(d.g, x, y, 0.5*AOE(GetUnitAbilityLevel(d.c, SpellID)), Condition(function GetEnemies))
            call ForGroup(d.g, function data.DealDamage)
            call d.destroy()
            call ReleaseTimer(GetExpiredTimer())
        else
            call UnitDamageTarget(d.c, bj_groupRandomCurrentPick, GetRandomReal(0, Damage(GetUnitAbilityLevel(d.c, SpellID))), true, false, ATTACK_TYPE_SIEGE, DAMAGE_TYPE_NORMAL, WEAPON_TYPE_ROCK_HEAVY_BASH)
        endif
    endmethod
    
    static method Start takes unit c, location l 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 GroupEnumUnitsInRangeOfLoc(d.g, l, AOE(GetUnitAbilityLevel(c, SpellID)), Condition(function GetEnemies))
        call SetTimerData(t, integer(d))
        call TimerStart(t, dt, true, function data.effect)
        call RemoveLocation(l)
        set l = null
        set t = null
    endmethod
endstruct

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

private function Actions takes nothing returns nothing
    call data.Start(GetTriggerUnit(), GetSpellTargetLoc())
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 ) )
    call TriggerAddAction( t, function Actions )
    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.
 

Attachments

  • Death Carrier.w3x
    38 KB · Views: 303
  • WC3ScrnShot_031809_212020_04.jpg
    WC3ScrnShot_031809_212020_04.jpg
    101.2 KB · Views: 1,765
  • Death Carrier.jpg
    Death Carrier.jpg
    62.9 KB · Views: 298

Tyman2007

Ya Rly >.
Reaction score
74
i'm surprised that no one gave this any consent.

Coding, well because it's advanced vjass, it's good

however, you use a lot of group bj's
 

moyack

Cool Member
Reaction score
9
i'm surprised that no one gave this any consent.

Coding, well because it's advanced vjass, it's good

however, you use a lot of group bj's
All of the BJ variables are used instantly, so there's no problem in using something already defined. I can change then by a private global, but performance won't change at all.
 

RaiJin

New Member
Reaction score
40
couldnt u have just used scope instead of library?

and ive heard theres no use in nulling struct members

anyways u dont destroy your groups after u used them

JASS:
private method onDestroy takes nothing returns nothing
        call GroupClear(.g)
        set .c = null
    endmethod


JASS:
private method onDestroy takes nothing returns nothing
        call DestroyGroup(.g)
        set .c = null
endmethod
 

Flare

Stops copies me!
Reaction score
662
couldnt u have just used scope instead of library?

and ive heard theres no use in nulling struct members

anyways u dont destroy your groups after u used them

JASS:
private method onDestroy takes nothing returns nothing
        call GroupClear(.g)
        set .c = null
    endmethod


JASS:
private method onDestroy takes nothing returns nothing
        call DestroyGroup(.g)
        set .c = null
endmethod
1) Does it make a difference?

2) There's nothing specifically wrong (to my knowledge) with nulling struct members.

3) That's because the groups are being recycled (in the Start function)
JASS:
        if d.g == null then
            set d.g = CreateGroup()
        endif
 

moyack

Cool Member
Reaction score
9
couldnt u have just used scope instead of library?
Doing it in this way I ensure that the TimerUtils requirement gets accomplished.

and ive heard theres no use in nulling struct members
Nulling units is a sane habit I have... yes, probably it won't have any effect, but meh!!

anyways u dont destroy your groups after u used them
I don't destroy them because I recycle the groups, making my spell faster.

Thanks for your comments. I've seen nobody has downloaded the map, there's a minigame that you could enjoy. Try it :)
 

Romek

Super Moderator
Reaction score
963
I like the way you get the effects from the ability in the Object Editor, it's interesting. :p

Why are you using both an 'Actions' function and a static method to start the spell?
Pick one or the other. The 'Start' method could be merged with the 'Actions' function, or the other way around.

There's no need to null handles which are never destroyed. This includes the trigger in the 'init' function, and the timers created with [ljass]NewTimer()[/ljass]

You could use [ljass]GetSpellTargetX[/ljass] and [ljass]GetSpellTargetY[/ljass] instead of [ljass]GetSpellTargetLoc[/ljass]. I'm aware that these probably didn't exist when this spell was created, but they're certainly here now. ;)

You seem to be calling [ljass]GetUnitAbilityLevel[/ljass] an awful lot. You might as well store it in a struct member.

You're calling [ljass]call GroupClear(d.g)[/ljass] for no clear reason in the 'effect' method. Note that groups are cleared when GroupEnum... natives are used on them.

> [ljass]UnitDamageTarget(GetEnumUnit(), GetEnumUnit() ...[/ljass]
Why are the enemies damaging themselves? o_O

> [ljass]ATTACK_TYPE_SIEGE, DAMAGE_TYPE_NORMAL, WEAPON_TYPE_ROCK_HEAVY_BASH[/ljass]
Configurable?

> [ljass]GetRandomReal(0, Damage(...))[/ljass]
Get the random real in the Damage function. Users people may want a fixed amount of damage.
Constant functions have no advantages over non-constant functions anyway, so that's not a problem.
 
Reaction score
341
You're calling call GroupClear(d.g) for no clear reason in the 'effect' method. Note that groups are cleared when GroupEnum... natives are used on them.

Units that are removed from the game or decay while part of a group remain in the hash table. They also appear to remain in the list (which can cause issues with FirstOfGroup loops).

Therefore, if units are not removed manually from the group before this happens, there is a minor leak, and the CPU cost of many group operations increases dramatically. Verification is in this thread here.

NB: This only applies to where a group has units in it for longer than an instant, and where those units may decay / be removed. Most uses of groups are not vulnerable to that (most uses you should be using a static group and a boolexpr these days - those are completely unaffected).

Fortunately, GroupClear solves the problem, enabling the creation of a function that can be called on a group and flushes out all of the shadow units in it. The cost is pretty much O(n), where n is the number of real units in the group (rather than shadows).

http://www.wc3c.net/showthread.php?t=102162
 

Romek

Super Moderator
Reaction score
963
@ TriggerHappy:
Those two quotes have nothing to do with each other.
That thread is about shadow units in groups (units that have decayed, but haven't been removed from groups).
 
Reaction score
341
I haven't looked at the code so I'm sure if he uses a global group.

If not, then the above quote doesn't apply. But try reading it again...

EDIT: Nevermind, I didn't see d.g so he's probably using dynamic group.s
 

Romek

Super Moderator
Reaction score
963
If you don't know what you're talking about, then don't bother talking.
 

moyack

Cool Member
Reaction score
9
First of all, thanks for the review. Just to keep in mind, I've been away fro mWC3 modding a long time ago, so some stuff I just simply I can't remember or I'm misinformed (or outdated)...

I like the way you get the effects from the ability in the Object Editor, it's interesting. :p
I think it's the best way to configure effects instead of writing paths and having the chance of doing mistakes.

Why are you using both an 'Actions' function and a static method to start the spell?
Pick one or the other. The 'Start' method could be merged with the 'Actions' function, or the other way around.
Well, I did this spell a long time ago that I don't remember why I did it in that way, but yes, it can be "merged" in the start function.

There's no need to null handles which are never destroyed. This includes the trigger in the 'init' function, and the timers created with [ljass]NewTimer()[/ljass]
As far as I remember nulling is a good practice in order to keep the handle counter low (probably I'm mistaken), so I kept this nulling costume.

You could use [ljass]GetSpellTargetX[/ljass] and [ljass]GetSpellTargetY[/ljass] instead of [ljass]GetSpellTargetLoc[/ljass]. I'm aware that these probably didn't exist when this spell was created, but they're certainly here now. ;)
that's right :p, this spell was done before the 1.24 patch.

You seem to be calling [ljass]GetUnitAbilityLevel[/ljass] an awful lot. You might as well store it in a struct member.
When I coded fast, I usually did that, but yes, it can be fixed too.

You're calling [ljass]call GroupClear(d.g)[/ljass] for no clear reason in the 'effect' method. Note that groups are cleared when GroupEnum... natives are used on them.
Ok, it can be fixed...

> [ljass]UnitDamageTarget(GetEnumUnit(), GetEnumUnit() ...[/ljass]
Why are the enemies damaging themselves? o_O
I realized that I was lazy that time and I just wanted to deal damage, but a correct way should pass the caster data in order to manage properly the damage credit.

> [ljass]ATTACK_TYPE_SIEGE, DAMAGE_TYPE_NORMAL, WEAPON_TYPE_ROCK_HEAVY_BASH[/ljass]
Configurable?
Right now.... no, but easily fixable.

> [ljass]GetRandomReal(0, Damage(...))[/ljass]
Get the random real in the Damage function. Users people may want a fixed amount of damage.
Constant functions have no advantages over non-constant functions anyway, so that's not a problem.
Ok...

----------------------------------

Right now I have one issue: I don't have WC3 installed in my pc, and the maximum thing I can do is to pass a corrected version of this spell without testing.

I believe a better solution could be that anyone in this community could help me to do the fixes, and of course receive the respective credit and manlove.

I'll expect your opinion.
 
General chit-chat
Help Users
  • No one is chatting at the moment.

      The Helper Discord

      Staff online

      Members online

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top