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

[JASS] Does it leak?

Status
Not open for further replies.
Level 13
Joined
Jul 26, 2008
Messages
1,009
Alright I've been making a series of skills that detect damage and either reduce or deflect it. I'm curious if I'm doing it right (The spells work, but do they leak and can they be fixed up/made better?)

Thank you for the help :)

JASS:
scope Cover initializer InitTrig_Cover

globals
//"Abilities\\Spells\\Items\\StaffOfSanctuary\\Staff_Sanctuary_Target.mdl"
    private constant integer SPELLID    = 'Cove'
    private constant integer BUFFID     = 'BCov'
    private unit array CASTER
    private trigger array EVENT
    private effect array CoverFX
endglobals

private struct Data

    unit c
    unit t

    static method Timer takes nothing returns nothing
     local timer tim = GetExpiredTimer()
     local Data D = Data(GetTimerData(tim))
        call DestroyEffect(CoverFX[GetUnitId(D.t)])
        call ReleaseTimer(tim)
        call DestroyTrigger(EVENT[GetUnitId(D.t)])
        set EVENT[GetUnitId(D.t)] = null
        set D.c = null
        set CoverFX[GetUnitId(D.t)] = null
        set D.t = null
        call D.destroy()
    endmethod

    static method Conditions takes nothing returns boolean
     local unit caster = CASTER[GetUnitId(GetTriggerUnit())]
     local integer lvl = GetUnitAbilityLevel(caster, SPELLID)
     local real dist = SquareRoot((GetUnitX(caster)-GetUnitX(GetTriggerUnit())) * (GetUnitX(caster)-GetUnitX(GetTriggerUnit())) + (GetUnitY(caster)-GetUnitY(GetTriggerUnit())) * (GetUnitY(caster)-GetUnitY(GetTriggerUnit())))
        if GetUnitAbilityLevel(GetTriggerUnit(), BUFFID) > 0 then
            if GetRandomInt(1,3) == 3 then
                call DestroyEffect(AddSpecialEffectTarget(GetAbilityEffectById(SPELLID,EFFECT_TYPE_MISSILE,1), GetTriggerUnit(), "origin"))
            endif
            call SetWidgetLife(GetTriggerUnit(), GetWidgetLife(GetTriggerUnit()) + GetEventDamage())
            call SetWidgetLife(caster, GetWidgetLife(caster) - GetEventDamage()*(1.1-0.05*lvl))
            if GetWidgetLife(caster)/GetUnitState(caster, UNIT_STATE_MAX_LIFE) < 0.25 or dist > 400+50*lvl then
                call UnitRemoveAbility(GetTriggerUnit(), BUFFID)
                call DestroyEffect(CoverFX[GetUnitId(GetTriggerUnit())])
                call DestroyTrigger(EVENT[GetUnitId(GetTriggerUnit())])
                set EVENT[GetUnitId(GetTriggerUnit())] = null
                set CoverFX[GetUnitId(GetTriggerUnit())] = null
            endif
        else
            call UnitRemoveAbility(GetTriggerUnit(), BUFFID)
            call DestroyEffect(CoverFX[GetUnitId(GetTriggerUnit())])
            call DestroyTrigger(EVENT[GetUnitId(GetTriggerUnit())])
            set EVENT[GetUnitId(GetTriggerUnit())] = null
            set CoverFX[GetUnitId(GetTriggerUnit())] = null
        endif
        set caster = null
     return false
    endmethod

    static method create takes unit caster, unit target, integer lvl returns Data
     local Data D = Data.allocate()
     local timer tim = NewTimer()
        set D.c = caster
        set D.t = target
        set CASTER[GetUnitId(target)] = D.c

        if EVENT[GetUnitId(D.t)] == null then
            set EVENT[GetUnitId(D.t)] = CreateTrigger()
            call TriggerRegisterUnitEvent(EVENT[GetUnitId(D.t)], D.t, EVENT_UNIT_DAMAGED)
            call TriggerAddCondition(EVENT[GetUnitId(D.t)], function Data.Conditions)
        endif

        if CoverFX[GetUnitId(D.t)] == null then
            set CoverFX[GetUnitId(D.t)] = AddSpecialEffectTarget(GetAbilityEffectById(SPELLID,EFFECT_TYPE_MISSILE,0), D.t, "origin")
        endif

        call SetTimerData(tim,D)
        call TimerStart(tim, 10+5*lvl, false, function Data.Timer)

     return D
    endmethod
    
endstruct

private function Conditions takes nothing returns boolean
    if GetSpellAbilityId() == SPELLID then
        call Data.create(GetTriggerUnit(), GetSpellTargetUnit(), GetUnitAbilityLevel(GetTriggerUnit(), SPELLID))
    endif
 return false
endfunction

//===========================================================================
public function InitTrig_Cover takes nothing returns nothing
 local trigger t = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_SPELL_EFFECT )
    call TriggerAddCondition( t, function Conditions )
    call XE_PreloadAbility(SPELLID)
 set t = null
endfunction

endscope
 
Level 14
Joined
Nov 18, 2007
Messages
1,084
You might be interested in using a library like this

Here's an example of a shield spell I made with that.

JASS:
scope Wall
    globals
        private constant integer SPELL_ID           = 'A01D' // The raw id of the SimpleWall ability
        private constant integer SHIELD_PRIORITY    = 0 // Determines how much priority the shield has. 
        private constant real   SHIELD_REFRESH_RATE = 0.5 // If a another shield is casted on the target, the new shield's life will have a bonus of SHIELD_REFRESH_RATE*(old shield life). 
        // SFX settings
        private constant string SHIELD_SFX              = "Abilities\\Spells\\Human\\DivineShield\\DivineShieldTarget.mdl" // The sfx for the shield.
        private constant string SHIELD_SFX_ATTACH       = "origin" // Attachment point for SHIELD_SFX.
        private constant string SHIELD_HIT_SFX          = "Abilities\\Spells\\Human\\Defend\\DefendCaster.mdl" // The sfx that plays whenever the shield blocks damage.
        private constant string SHIELD_HIT_SFX_ATTACH   = "origin" // Attachment point for SHIELD_HIT_SFX.
        // Bar settings:
        private constant string SHIELD_BAR_TEXT         = "llllllllllllllllllll" // The characters shown for the bar.
        private constant string SHEILD_COLOR_CODE       = "|cffffcc00" // The color code used to differentiate how much life the shield has. 
        private constant real   SHIELD_BAR_TEXT_SIZE    = 0.024 // The text size for SHIELD_BAR_TEXT
        private constant real   SHIELD_BAR_X_OFFSET     = -50 // bar's x offset from the unit
        private constant real   SHIELD_BAR_Y_OFFSET     = 0 // bar's y offset from the unit
        private constant real   SHIELD_BAR_HEIGHT       = 100 // bar's height
        private constant real   TIMER_LOOP              = 0.05 // How often the timer should update the text tag. 
    endglobals
    
    // How much life the shield has
    private constant function ShieldLife takes integer lvl returns real
        return 100.*lvl
    endfunction
    
//==========================================================================================
// END OF CONFIGURATION
//==========================================================================================
    
    private keyword Data
    
    globals
        private Data array Info
    endglobals
    
    private struct Data extends DamageModifier
        unit targ
        real shieldFullLife
        real shieldLife
        effect shieldSFX
        texttag text 
        timer t
        
        static method create takes unit u, integer lvl returns thistype
            local thistype this = thistype.allocate(u,SHIELD_PRIORITY)
            set .targ = u
            
            set .shieldFullLife = ShieldLife(lvl)
            set .shieldLife = .shieldFullLife            
            
            set .shieldSFX = AddSpecialEffectTarget(SHIELD_SFX,u,SHIELD_SFX_ATTACH)            
            set .text = CreateTextTag()
            
            set .t = NewTimer()
            call SetTimerData(.t,this)
            call TimerStart(.t,TIMER_LOOP,true,function thistype.onLoop)
            
            set Info[GetUnitId(targ)] = this
            return this
        endmethod  
        
        // Adds the new life to the shield + the old life.
        static method refresh takes unit u, integer lvl returns nothing
            local thistype this = Info[GetUnitId(u)]
            set .shieldFullLife = ShieldLife(lvl) + .shieldFullLife*SHIELD_REFRESH_RATE
            set .shieldLife = ShieldLife(lvl) + .shieldLife*SHIELD_REFRESH_RATE
        endmethod
        
        method onDamageTaken takes unit damageSource, real damage returns real
            local real initShieldLife = .shieldLife            
            set .shieldLife = .shieldLife - damage
            call DestroyEffect(AddSpecialEffectTarget(SHIELD_HIT_SFX,.targ,SHIELD_HIT_SFX_ATTACH))
            if .shieldLife > 0 then
                return -damage
            else
                call .destroy()
                return -initShieldLife
            endif
        endmethod  
        
        // Set the texttag to the correct position and update the text.
        static method onLoop takes nothing returns nothing
            local thistype this = GetTimerData(GetExpiredTimer())
            local integer k = R2I(StringLength(SHIELD_BAR_TEXT)*(.shieldLife /.shieldFullLife))
            call SetTextTagText(.text,SHEILD_COLOR_CODE+SubString(SHIELD_BAR_TEXT,0,k ) + "|r"  + SubString(SHIELD_BAR_TEXT,k + 1,StringLength(SHIELD_BAR_TEXT)),SHIELD_BAR_TEXT_SIZE)
            call SetTextTagPos(.text,GetUnitX(.targ)+SHIELD_BAR_X_OFFSET,GetUnitY(.targ)+SHIELD_BAR_Y_OFFSET,SHIELD_BAR_HEIGHT)
        endmethod
        
        // Clear any potential leaks
        method onDestroy takes nothing returns nothing
            call DestroyEffect(.shieldSFX)
            call DestroyTextTag(.text)
            call ReleaseTimer(.t)
            set Info[GetUnitId(targ)] = 0
        endmethod
        
        static method spellActions takes nothing returns boolean
            if GetSpellAbilityId() == SPELL_ID then
                if Info[GetUnitId(GetSpellTargetUnit())] == 0 then
                    call thistype.create(GetSpellTargetUnit(),GetUnitAbilityLevel(GetTriggerUnit(),SPELL_ID))                    
                else
                    call thistype.refresh(GetSpellTargetUnit(),GetUnitAbilityLevel(GetTriggerUnit(),SPELL_ID))
                endif  
            endif
            return false
        endmethod
        
        static method onInit takes nothing returns nothing
            local trigger t = CreateTrigger()
            call TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_SPELL_EFFECT)
            call TriggerAddCondition(t,Condition(function thistype.spellActions))
            call Preload(SHIELD_SFX)   
            call Preload(SHIELD_HIT_SFX)
        endmethod
    endstruct    
endscope
 
Status
Not open for further replies.
Top