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

Energy Drain v1.0

Drains the essences of nearby enemies, healing the caster for each essence drained. Drained units take minor damage.


Yet another simple spell from me. If you guys have any ideas on how to improve it, just say. I'm willing to make it less simple but I don't have any ideas.

JASS:
scope Energy initializer Initi

globals

    private constant integer SPELL_ID = 'A000'
    private constant integer DUMMY_ID = 'h000'
    private constant real PERIOD = 0.04        //Timer period
    private constant real RADIUS = 450.        //Spell radius
    private constant string DUMMY_EFFECT = "Abilities\\Weapons\\FaerieDragonMissile\\FaerieDragonMissile.mdl"        //The missle effect that appears on the dummy
    private constant string HEAL_EFFECT = "Abilities\\Spells\\Human\\Heal\\HealTarget.mdl"                           //The healing effect that appears on the caster
    
    
    private group TempGroup = CreateGroup()
    private timer T = CreateTimer()

endglobals

private function HEAL takes integer lvl returns real        //The ammount healed for each essence
    return lvl * 10 + 25.
endfunction

private function DAMAGE takes integer lvl returns real      //The ammount of damage dealt to each target
    return lvl * 10 + 25.
endfunction

private function Filter takes nothing returns boolean
    return (IsUnitType(GetFilterUnit(), UNIT_TYPE_STRUCTURE) == false) and (IsUnitType(GetFilterUnit(), UNIT_TYPE_MECHANICAL) == false) and (IsUnitType(GetFilterUnit(), UNIT_TYPE_MAGIC_IMMUNE) == false) and (GetWidgetLife(GetFilterUnit()) > 0.405)
endfunction

////////////////////////Don't touch anything below unless you know what you're doing///////////////////////////////////////////

private struct data

    unit caster
    unit dummy
    integer lvl
    effect sfx
    
    static integer Total = 0
    static data array ar
    
    static method Callback takes nothing returns nothing
        local data d
        local integer i = 0
        local real x
        local real y
        local real angle
        loop
            exitwhen i >= data.Total
            set d = data.ar[i]
            set x = GetUnitX(d.dummy)
            set y = GetUnitY(d.dummy)
            set angle = Atan2(GetUnitY(d.caster) - y,GetUnitX(d.caster) - x)
            if SquareRoot((x - GetUnitX(d.caster)) * (x - GetUnitX(d.caster)) + (y - GetUnitY(d.caster)) * (y - GetUnitY(d.caster))) > 30 then
                set x = x + 20 * Cos(angle)
                set y = y + 20 * Sin(angle)
                call SetUnitX(d.dummy, x)
                call SetUnitY(d.dummy, y)
                call SetUnitFacing(d.dummy, angle * bj_RADTODEG)
            else
                call DestroyEffect(AddSpecialEffectTarget(HEAL_EFFECT, d.caster, "chest"))
                call SetWidgetLife(d.caster, GetWidgetLife(d.caster) + HEAL(d.lvl))
                call DestroyEffect(d.sfx)
                call RemoveUnit(d.dummy)
                call d.destroy()
                set data.Total = data.Total - 1
                set data.ar[i] = data.ar[data.Total]
                set i = i - 1
            endif
            set i = i + 1
        endloop
        if data.Total == 0 then
            call PauseTimer(T)
        endif
    endmethod
    
    static method create takes unit caster, unit target returns data
        local data d = data.allocate()
        set d.caster = caster
        set d.dummy = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE), DUMMY_ID, GetUnitX(target), GetUnitY(target), Atan2(GetUnitY(caster) - GetUnitY(target), GetUnitX(caster) - GetUnitX(target)) * bj_RADTODEG)
        set d.sfx = AddSpecialEffectTarget(DUMMY_EFFECT, d.dummy, "chest")
        set data.ar[data.Total] = d
        if data.Total == 0 then
            call TimerStart(T, PERIOD, true, function data.Callback)
        endif
        set data.Total = data.Total + 1
        return d
    endmethod

endstruct

private function Cond takes nothing returns boolean
    return GetSpellAbilityId() == SPELL_ID
endfunction

private function Act takes nothing returns nothing
    local unit temp
    local unit caster = GetTriggerUnit()
    call GroupEnumUnitsInRange(TempGroup, GetUnitX(caster), GetUnitY(caster), RADIUS, Condition(function Filter))
    loop
        set temp = FirstOfGroup(TempGroup)
        exitwhen temp == null
        if IsUnitEnemy(temp, GetTriggerPlayer()) then
            call UnitDamageTarget(caster, temp, DAMAGE(GetUnitAbilityLevel(caster, SPELL_ID)), false, true, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_NORMAL, WEAPON_TYPE_WHOKNOWS)
            call data.create(caster, temp)
        endif
        call GroupRemoveUnit(TempGroup, temp)
    endloop
    set caster = null
endfunction

private function Initi takes nothing returns nothing
    local trigger t = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
    call TriggerAddCondition(t, Condition(function Cond))
    call TriggerAddAction(t, function Act)
endfunction

endscope

Keywords:
Life Drain, Drain, AOE, Heal
Contents

Energy Drain (Map)

Reviews
11:08, 10th Jun 2010 TriggerHappy: Overall, it's good enough to be approved. It would be ideal if you didn't use FOG loops. You should make the dummy unit player configurable.

Moderator

M

Moderator

11:08, 10th Jun 2010
TriggerHappy:

Overall, it's good enough to be approved.

  • It would be ideal if you didn't use FOG loops.
  • You should make the dummy unit player configurable.
 
Level 14
Joined
Nov 18, 2007
Messages
1,084
I haven't tested this spell yet; I've only looked at the code on the site.

  • FirstOfGroup loops are slower than using the filter directly to execute your actions or using ForGroup
  • In the static method Callback, you should probably have variables for getting the x and y coordinate of the caster. (You could even have variables for GetUnitX(d.caster) - x and GetUnitY(d.caster) - y since you could use it in calculating the angle and distance.)
  • Make more "stuff" easy to configure.
    Ex: Attacktype, damagetype, weapontype, speed of the dummy unit, distance the dummy unit should be from the caster, etc.
  • Try to recycle your dummy units.

Edit: I tested it in-game. It's a pretty simple effect. I noticed that the healing effect never gets shown though since it gets destroyed immediately, making it play its death animation instead of its birth animation. You could try to fix this or use a different effect.
 
Level 6
Joined
Jul 18, 2009
Messages
200
JASS:
private function Filter takes nothing returns boolean
    return (IsUnitType(GetFilterUnit(), UNIT_TYPE_STRUCTURE) == false) and (IsUnitType(GetFilterUnit(), UNIT_TYPE_MECHANICAL) == false) and (IsUnitType(GetFilterUnit(), UNIT_TYPE_MAGIC_IMMUNE) == false) and (GetWidgetLife(GetFilterUnit()) > 0.405)
endfunction

-->

JASS:
private function Filter takes nothing returns boolean
    return (IsUnitType(GetFilterUnit(), UNIT_TYPE_STRUCTURE) == false)/*
*/ and (IsUnitType(GetFilterUnit(), UNIT_TYPE_MECHANICAL) == false)/*
*/ and (IsUnitType(GetFilterUnit(), UNIT_TYPE_MAGIC_IMMUNE) == false)/*
*/ and (GetWidgetLife(GetFilterUnit()) > 0.405)
endfunction

Unreadable( Thanks BlackRose ) :D
 
Level 7
Joined
Oct 11, 2008
Messages
304
JASS:
private function Filter takes nothing returns boolean
    return (IsUnitType(GetFilterUnit(), UNIT_TYPE_STRUCTURE) == false)/*
*/ and (IsUnitType(GetFilterUnit(), UNIT_TYPE_MECHANICAL) == false)/*
*/ and (IsUnitType(GetFilterUnit(), UNIT_TYPE_MAGIC_IMMUNE) == false)/*
*/ and (GetWidgetLife(GetFilterUnit()) > 0.405)
endfunction

>>

JASS:
private function Filter takes nothing returns boolean
    return not (IsUnitType(GetFilterUnit(), UNIT_TYPE_STRUCTURE)/*
*/ and not (IsUnitType(GetFilterUnit(), UNIT_TYPE_MECHANICAL)/*
*/ and not (IsUnitType(GetFilterUnit(), UNIT_TYPE_MAGIC_IMMUNE)/*
*/ and (GetWidgetLife(GetFilterUnit()) > 0.405) //you can use the UnitAlive native here
endfunction

EDIT: and change the name of the function, Filter is already a native
 
Level 15
Joined
Jul 6, 2009
Messages
889
Filter --> Energy_Filter. Or is it Energy___Filter? Eitherway, Scope fixes it. Although something like UnitFilter is more better. Could be a filter for anything.

Also:

JASS:
private function Filter takes nothing returns boolean
    set someGlobalUnit = GetFilterUnit()
    return    IsUnitType(someGlobalUnit, UNIT_TYPE_STRUCTURE    ) == false        and /*
    */        IsUnitType(someGlobalUnit, UNIT_TYPE_MECHANICAL   ) == false        and /*
    */        IsUnitType(someGlobalUnit, UNIT_TYPE_MAGIC_IMMUNE ) == false        and /*
    */        GetWidgetLife(someGlobalUnit) > 0.405
endfunction
Have a global unit for the filter.

Please fix this even if it's approved. Approved things should still be updated.
Also, expanding on the readability, I think that is even more readable. Don't need the brackets outside each condition. And the conditions indented, with the and's a tab or 2 off.
 
Top