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

Increasing handle counter after each spell usage

Status
Not open for further replies.
Level 13
Joined
Mar 24, 2013
Messages
1,105
Hi all,

Saw someone post on this thread Debugging and I was reminded of the Handle Counter, however based on my reading the handle counter is supposed to go back down eventually. Mine is increasing marginally, it will increase by about 15 to 20 then drop to an increase of about 10 or so each time my spell is fired.

Can anyone take a look to see where I might be going wrong? I've also attached a test map.

JASS:
library Space initializer init requires TimedEffect

        globals

 
    private unit array u
    private effect array e
    private real array d
    private real array px
    private real array py
    private real array pulse
    private boolean array b
    private real array rAngle
    private player array p
    private integer index = 0
    private integer index2 = 0
    private unit array u2
    private real array fireAngle
    private constant real DISTANCE = 900./32.
    private real array fireDuration
    private group array fireGroup
    private integer array counter
    private constant integer SPX_COUNTER = 2
    private constant string ROCKET_EFFECT = "Abilities\\Spells\\Human\\ManaFlare\\ManaFlareBoltImpact.mdl"
    private constant real ROCKET_EFFECT_DURATION = .5
    private constant string ROCKET_EFFECT_TARGET = "Abilities\\Spells\\Other\\Monsoon\\MonsoonBoltTarget.mdl"
    private constant real ROCKET_DAMAGE = 300.
    private constant real ROCKET_AOE_CHECK = 150.
 
    private constant group TR_GROUP = CreateGroup()
    private constant real TIMEOUT = .03125
    private timer t = CreateTimer()
    private timer t2 = CreateTimer()
    private constant integer THUNDER_ROCKET = 'A000'
    private constant real DURATION = 6.
    private constant real EFFECT_JITTER_AOE = 625. // to avoid using Sqrt for efficency, multiple the jitter AOE by itself (25*25)
 
    private constant string EFFECT = "Abilities\\Spells\\Other\\FrostBolt\\FrostBoltMissile.mdl" //"Abilities\\Weapons\\Bolt\\BoltImpact.mdl" //"Abilities\\Weapons\\PhoenixMissile\\Phoenix_Missile.mdl""Abilities\\Weapons\\FarseerMissile\\FarseerMissile.mdl"
 
    private constant real OFFSET = 200.
    private constant real OFFSET2 = 18.
 
    private constant string PULSE_EFFECT = "Abilities\\Spells\\Orc\\Purge\\PurgeBuffTarget.mdl"
    private constant real PULSE_TIMEOUT = 1.
    private constant real PULSE_AOE_CHECK = 200.
    private constant real PULSE_EFFECT_SCALE = 2.4
    private constant real PULSE_EFFECT_DURATION = .8
    private constant real PULSE_DAMAGE = 150.
 
    private constant real TRACK_DAMAGE = 5.
    private constant real TRACK_AOE_CHECK = 100. 
        endglobals
     
function UnitAlive takes unit u returns boolean
     return not IsUnitType(u, UNIT_TYPE_DEAD) and GetUnitTypeId(u) != 0
endfunction

private function TRLoop2 takes nothing returns nothing
        local integer i = 0
        local real x
        local real y
        local real angle
        local group g = TR_GROUP
        local unit tU
        loop
            exitwhen i >= index2
         
            set fireDuration[i] = fireDuration[i] - TIMEOUT
         
            if fireDuration[i] <= 0. then
                call SetUnitPropWindow(u2[i], GetUnitDefaultPropWindow(u2[i]))
                call SetUnitPathing(u2[i], true)
                call DestroyGroup(fireGroup[i])
                set index2 = index2 - 1
                set u2[i] = u2[index2]
                set fireAngle[i] = fireAngle[index2]
                set fireDuration[i] = fireDuration[index2]
                set counter[i] = counter[index2]
                set fireGroup[i] = fireGroup[index2]
                set i = i - 1
                if index2 == 0 then
                    call PauseTimer(t2)
                endif
            else
         
                if GetUnitPropWindow(u2[i]) != 0. then
                    call SetUnitPropWindow(u2[i], 0.)
                endif
             
                set x = GetUnitX(u2[i])
                set y = GetUnitY(u2[i])
             
                if counter[i] <= 0 then
                    set counter[i] = SPX_COUNTER
                    call EffectApplyTimedLife(AddSpecialEffect(ROCKET_EFFECT, x, y), ROCKET_EFFECT_DURATION)
                else
                    set counter[i] = counter[i] - 1
                endif

                set angle = DISTANCE * Cos(fireAngle[i])
                set x = x + angle
                set angle = DISTANCE * Sin(fireAngle[i])
                set y = y + angle
             
             
                call GroupEnumUnitsInRange(g, x, y, ROCKET_AOE_CHECK, null)
             
                loop
                    set tU = FirstOfGroup(g)
                    exitwhen tU == null
                 
                    call GroupRemoveUnit(g, tU)
                    if UnitAlive(tU) and IsUnitEnemy(tU, GetOwningPlayer(u2[i])) and not IsUnitInGroup(tU, fireGroup[i]) then
                      call GroupAddUnit(fireGroup[i], tU)
                      call EffectApplyTimedLife(AddSpecialEffect(ROCKET_EFFECT_TARGET, GetUnitX(tU), GetUnitY(tU)), ROCKET_EFFECT_DURATION)
                      call UnitDamageTarget(u[i], tU, ROCKET_DAMAGE, true, false, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_UNIVERSAL, WEAPON_TYPE_WHOKNOWS)               
                    endif
                 
                endloop
             
                call GroupClear(g)
             
                // check and destroy trees/destructibles prior to moving
                call TreeSearch(x,y, 0.)
             
                if FreePathing(x,y) then
                    call SetUnitX(u2[i], x)
                    call SetUnitY(u2[i], y)
                else
                    set fireDuration[i] = 0. // end early if unable to move.
                endif
            endif
            set i = i + 1
        endloop
 
    set g = null
    set tU = null
endfunction

private function TRFire takes unit u, real x, real y, real z returns nothing
        local real x2
        local real y2
     
        set u2[index2] = u
        call SetUnitPathing(u2[index2], false)
        call SetUnitPropWindow(u2[index2], 0.)
        set x2 = GetUnitX(u2[index2])
        set y2 = GetUnitY(u2[index2])
        set fireAngle[index2] = z
        set fireDuration[index2] = 1.
        set counter[index2] = 0
        set fireGroup[index2] = CreateGroup()
        set index2 = index2 + 1
        if index2 == 1 then
            call TimerStart(t2, TIMEOUT, true, function TRLoop2)
        endif

endfunction

private function TRLoop takes nothing returns nothing
    local integer i = 0
    local group g = TR_GROUP
    local real x
    local real y
    local real x2
    local real y2
    local real angle
    local real angle2
    local real dx
    local real dy
    local real dz
    local unit tU
    local boolean tB
    local effect tE
        loop
            exitwhen i >= index
         
            set d[i] = d[i] - TIMEOUT
            set pulse[i] = pulse[i] - TIMEOUT
         
            set x = BlzGetLocalSpecialEffectX(e[i])
            set y = BlzGetLocalSpecialEffectY(e[i])
            set x2 = x
            set y2 = y
         
            if b[i] == true then
                set angle = rAngle[i]
            else
                set angle = Atan2(py[i]-y, px[i]-x)
                set rAngle[i] = angle
            endif

            set angle2 = OFFSET2 * Cos(angle)
            set x = x + angle2
            set angle2 = OFFSET2 * Sin(angle)
            set y = y + angle2

            set dx = x2 - px[i]
            set dy = y2 - py[i]
         
            set dz = ((dx * dx) + (dy * dy)) * ((dx * dx) + (dy * dy))
         
            if dz > EFFECT_JITTER_AOE or b[i] then
                call BlzSetSpecialEffectX(e[i], x)
                call BlzSetSpecialEffectY(e[i], y)
                call BlzSetSpecialEffectHeight( e[i], 100.00 )
                call BlzSetSpecialEffectOrientation(e[i], angle,0,0)
            endif
         
            if pulse[i] <= 0. then
                set tE = AddSpecialEffect(PULSE_EFFECT, x,y)
                call BlzSetSpecialEffectScale(tE, PULSE_EFFECT_SCALE)
                call EffectApplyTimedLife(tE, PULSE_EFFECT_DURATION)
         
                call GroupEnumUnitsInRange(g, x, y, PULSE_AOE_CHECK, null)
             
                loop
                    set tU = FirstOfGroup(g)
                    exitwhen tU == null
                 
                    call GroupRemoveUnit(g, tU)
                    if UnitAlive(tU) and IsUnitEnemy(tU, GetOwningPlayer(u[i])) then
                      call UnitDamageTarget(u[i], tU, PULSE_DAMAGE, true, false, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_UNIVERSAL, WEAPON_TYPE_WHOKNOWS)               
                    endif
                 
                endloop
             
                call GroupClear(g)
             
                set pulse[i] = PULSE_TIMEOUT
            endif
         
            call GroupEnumUnitsInRange(g, x, y, TRACK_AOE_CHECK, null)
         
            set tB = false
            loop
                set tU = FirstOfGroup(g)
                exitwhen tU == null or tB == true
                call GroupRemoveUnit(g, tU)
             
                if tU == u[i] then
                    set tB = true
                    set d[i] = 0.
                    call TRFire(u[i], x, y, angle)
                elseif UnitAlive(tU) and IsUnitEnemy(tU, GetOwningPlayer(u[i])) then
                  call UnitDamageTarget(u[i], tU, TRACK_DAMAGE, true, false, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_UNIVERSAL, WEAPON_TYPE_WHOKNOWS)               
                endif
             
            endloop
         
            call GroupClear(g)

            if d[i] <= 0. then
                call DestroyEffect(e[i])
                set index = index - 1
                set u[i] = u[index]
                set p[i] = p[index]
                set d[i] = d[index]
                set e[i] = e[index]
                set px[i] = px[index]
                set py[i] = py[index]
                set pulse[i] = pulse[index]
                set b[i] = b[index]
                set i = i - 1
                if index == 0 then
                    call PauseTimer(t)
                endif
            endif
            set i = i + 1
        endloop
     
    set g = null
    set tU = null
    set tE = null
 
endfunction

private function ThunderRocket takes nothing returns boolean
    local real angle
    local real angle2
    local real angle3
    local real x
    local real y
    if GetSpellAbilityId() == THUNDER_ROCKET then
        set u[index] = GetTriggerUnit()
        set p[index] = GetTriggerPlayer()
        set d[index] = DURATION
        set b[index] = false
        set pulse[index] = PULSE_TIMEOUT
        set rAngle[index] = 0.
        set angle = GetUnitFacing(u[index])
        set x = GetUnitX(u[index])
        set y = GetUnitY(u[index])
        set angle3 = angle * bj_DEGTORAD
        set angle2 = OFFSET * Cos(angle3)
        set x = x + angle2
        set angle2 = OFFSET * Sin(angle3)
        set y = y + angle2
        set e[index] = AddSpecialEffect(EFFECT, x,y)
        call BlzSetSpecialEffectOrientation(e[index], angle3,0,0)
        set px[index] = x
        set py[index] = y
        call BlzSetSpecialEffectHeight( e[index], 50.00 )
        set index = index + 1
     
        if index == 1 then
            call TimerStart(t, TIMEOUT, true, function TRLoop)
        endif

    endif

    return false

endfunction


function ThunderRocketDetect takes nothing returns boolean
    local player p1 = GetTriggerPlayer()
    local integer i = 0
    local real x
    local real y

        loop
            exitwhen i >= index
         
            if GetOwningPlayer(u[i]) == p1 then
                set x = BlzGetTriggerPlayerMouseX()
                set y = BlzGetTriggerPlayerMouseY()
             
                if x == 0. and y == 0. then
                    // retain angle?
                    set b[i] = true
                 
                else
                    set px[i] = x
                    set py[i] = y
                set b[i] = false
                endif 
             
            endif
         
            set i = i + 1
        endloop
     
    set p1 = null
    return false
endfunction


private function init takes nothing returns nothing
    local trigger t10 = CreateTrigger()
    local trigger t11 = CreateTrigger()
 
    call TriggerRegisterAnyUnitEventBJ( t10, EVENT_PLAYER_UNIT_SPELL_EFFECT)
    call TriggerAddCondition(t10, Condition(function ThunderRocket))
 
    call TriggerRegisterPlayerMouseEventBJ( t11, Player(0), bj_MOUSEEVENTTYPE_MOVE )
    call TriggerAddCondition(t11, Condition(function ThunderRocketDetect))

endfunction

endlibrary

JASS:
library TimedEffect

    globals
     
        private integer effectIndex = 0
        private real array effectDuration
        private effect array effects
        private timer effectLoop = CreateTimer()
        private constant real EFFECT_TIMEOUT = .05
     
   endglobals
 
   function EffectLoop takes nothing returns nothing
        local integer i = 0
     
        loop
            exitwhen i >= effectIndex
            set effectDuration[i] = effectDuration[i] - EFFECT_TIMEOUT
            if effectDuration[i] <= 0. then
                call DestroyEffect(effects[i])
                set effectIndex = effectIndex - 1
                set effects[i] = effects[effectIndex]
                set effectDuration[i] = effectDuration[effectIndex]
                set i = i - 1
                if effectIndex == 0 then
                    call PauseTimer(effectLoop)
                endif
            endif
            set i = i + 1
        endloop

    endfunction

    function EffectApplyTimedLife takes effect e, real timeout returns nothing
        set effects[effectIndex] = e
        set effectDuration[effectIndex] = timeout
        set effectIndex = effectIndex + 1
        if effectIndex == 1 then
            call TimerStart(effectLoop, EFFECT_TIMEOUT, true, function EffectLoop)
        endif
    endfunction
 
endlibrary
JASS:
library TreeSearch initializer init

        globals
     
        private rect TREE_REGION
        private real maxX
        private real maxY
        private real minX
        private real minY
     
        private real defaultDestRadius = 128.
        private real maxDestRadius = defaultDestRadius * 2.
        private string treeOrDebris = "attack"
        private unit debrisKiller
        private real tX
        private real tY
        private real tRadius
     
        endglobals
     
private function KillTree takes nothing returns nothing
    local real x
    local real y
    local destructable d = GetEnumDestructable()
    if GetWidgetLife(d) > .405 and IssueTargetOrder(debrisKiller, treeOrDebris, d) then
        set x = GetWidgetX(d) - tX
        set y = GetWidgetY(d) - tY
        if x*x + y*y <= tRadius then
            call KillDestructable(d)
        endif
    endif
 
    set d = null
endfunction

function TreeSearch takes real x, real y, real radius returns nothing
    call SetUnitX(debrisKiller, x)
    call SetUnitY(debrisKiller, y)
    call PauseUnit(debrisKiller, false)
    set tX = x
    set tY = y
    if radius == 0. then
        call SetRect(TREE_REGION, 0., 0., maxDestRadius, maxDestRadius)
        set radius = defaultDestRadius
    else
        call SetRect(TREE_REGION, 0., 0., radius*2, radius*2)
    endif
 
    set tRadius = radius*radius
    call MoveRectTo(TREE_REGION, x, y)
    call EnumDestructablesInRect(TREE_REGION, null, function KillTree)
    call PauseUnit(debrisKiller, true)
endfunction

private function init takes nothing returns nothing
    set TREE_REGION = GetWorldBounds()
    set maxX = GetRectMaxX(TREE_REGION)
    set maxY = GetRectMaxY(TREE_REGION)
    set minX = GetRectMinX(TREE_REGION)
    set minY = GetRectMinY(TREE_REGION)
 
    set debrisKiller = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE), 'ugho', 0,0, 270.)
    call UnitAddAbility(debrisKiller, 'Aloc')
    call ShowUnit(debrisKiller, false)
    call PauseUnit(debrisKiller, true)
    call SetRect(TREE_REGION, 0., 0., maxDestRadius, maxDestRadius)

endfunction


endlibrary
JASS:
library TerrainPathability initializer Init
//******************************************************************************
//* BY: Rising_Dusk
//*
//* This script can be used to detect the type of pathing at a specific point.
//* It is valuable to do it this way because the IsTerrainPathable is very
//* counterintuitive and returns in odd ways and aren't always as you would
//* expect. This library, however, facilitates detecting those things reliably
//* and easily.
//*
//******************************************************************************
//*
//*    > function IsTerrainDeepWater    takes real x, real y returns boolean
//*    > function IsTerrainShallowWater takes real x, real y returns boolean
//*    > function IsTerrainLand         takes real x, real y returns boolean
//*    > function IsTerrainPlatform     takes real x, real y returns boolean
//*    > function IsTerrainWalkable     takes real x, real y returns boolean
//*
//* These functions return true if the given point is of the type specified
//* in the function's name and false if it is not. For the IsTerrainWalkable
//* function, the MAX_RANGE constant below is the maximum deviation range from
//* the supplied coordinates that will still return true.
//*
//* The IsTerrainPlatform works for any preplaced walkable destructable. It will
//* return true over bridges, destructable ramps, elevators, and invisible
//* platforms. Walkable destructables created at runtime do not create the same
//* pathing hole as preplaced ones do, so this will return false for them. All
//* other functions except IsTerrainWalkable return false for platforms, because
//* the platform itself erases their pathing when the map is saved.
//*
//* After calling IsTerrainWalkable(x, y), the following two global variables
//* gain meaning. They return the X and Y coordinates of the nearest walkable
//* point to the specified coordinates. These will only deviate from the
//* IsTerrainWalkable function arguments if the function returned false.
//*
//* Variables that can be used from the library:
//*     [real]    TerrainPathability_X
//*     [real]    TerrainPathability_Y
//*
globals
    private constant real    MAX_RANGE     = 10.
    private constant integer DUMMY_ITEM_ID = 'wolg'
endglobals

globals 
    private item       Item   = null
    private rect       Find   = null
    private item array Hid
    private integer    HidMax = 0
    public  real       X      = 0.
    public  real       Y      = 0.
endglobals

function IsTerrainDeepWater takes real x, real y returns boolean
    return not IsTerrainPathable(x, y, PATHING_TYPE_FLOATABILITY) and IsTerrainPathable(x, y, PATHING_TYPE_WALKABILITY)
endfunction
function IsTerrainShallowWater takes real x, real y returns boolean
    return not IsTerrainPathable(x, y, PATHING_TYPE_FLOATABILITY) and not IsTerrainPathable(x, y, PATHING_TYPE_WALKABILITY) and IsTerrainPathable(x, y, PATHING_TYPE_BUILDABILITY)
endfunction
function IsTerrainLand takes real x, real y returns boolean
    return IsTerrainPathable(x, y, PATHING_TYPE_FLOATABILITY)
endfunction
function IsTerrainPlatform takes real x, real y returns boolean
    return not IsTerrainPathable(x, y, PATHING_TYPE_FLOATABILITY) and not IsTerrainPathable(x, y, PATHING_TYPE_WALKABILITY) and not IsTerrainPathable(x, y, PATHING_TYPE_BUILDABILITY)
endfunction

function FreePathing takes real x, real y returns boolean
    return not IsTerrainPathable(x,y, PATHING_TYPE_FLYABILITY)
endfunction

private function HideItem takes nothing returns nothing
    if IsItemVisible(GetEnumItem()) then
        set Hid[HidMax] = GetEnumItem()
        call SetItemVisible(Hid[HidMax], false)
        set HidMax = HidMax + 1
    endif
endfunction
function IsTerrainWalkable takes real x, real y returns boolean
    //Hide any items in the area to avoid conflicts with our item
    call MoveRectTo(Find, x, y)
    call EnumItemsInRect(Find ,null, function HideItem)
    //Try to move the test item and get its coords
    call SetItemPosition(Item, x, y) //Unhides the item
    set X = GetItemX(Item)
    set Y = GetItemY(Item)
    static if LIBRARY_IsTerrainWalkable then
        //This is for compatibility with the IsTerrainWalkable library
        set IsTerrainWalkable_X = X
        set IsTerrainWalkable_Y = Y
    endif
    call SetItemVisible(Item, false)//Hide it again
    //Unhide any items hidden at the start
    loop
        exitwhen HidMax <= 0
        set HidMax = HidMax - 1
        call SetItemVisible(Hid[HidMax], true)
        set Hid[HidMax] = null
    endloop
    //Return walkability
    return (X-x)*(X-x)+(Y-y)*(Y-y) <= MAX_RANGE*MAX_RANGE and not IsTerrainPathable(x, y, PATHING_TYPE_WALKABILITY)
endfunction

private function Init takes nothing returns nothing
    set Find = Rect(0., 0., 128., 128.)
    set Item = CreateItem(DUMMY_ITEM_ID, 0, 0)
    call SetItemVisible(Item, false)
endfunction
endlibrary
 

Attachments

  • Predator Reforged.w3x
    82.3 KB · Views: 17
Level 39
Joined
Feb 27, 2007
Messages
4,994
In EffectLoop when you swap the i'th index for the effectIndex'th one you're not nulling the effectIndex'th effect after the swap, resulting in a reference 'leak'. I put that in quotes because it's not really leaking because you expect to re-use that array index eventually but it will cause the handle ID from that effect to stay in use until that index of the array is eventually overwritten later, so in this instance it will cause the value of newly issued handle ids to be higher. This same behavior will happen with any dynamically indexed array of handle types, for example e and fireGroup in the spell.

Basically just null the no-longer-used indices after the swap (though in general you don't need to do this):
JASS:
if EndCondition(i) then
  set someeffect[i] = someeffect[last]
  set somereal[i] = somereal[last]
  set someffect[last] = null
  set i = i-1
  set last = last-1
endif
 
Level 13
Joined
Mar 24, 2013
Messages
1,105
Thank you Pyro, so there is no true problem here, the increase is rooted in the expectation that the handles will be recycled on the next cast.

However, if the spell will be used in bursts, numerous times in a row followed by minutes of downtime, would it be better to null those handles to not retain them in the interim?
 
Level 13
Joined
Mar 24, 2013
Messages
1,105
Nulled the ones you recommended, along with my unit, player ones.

There still seems like there is some kind of growth, although the numbers bounce around.

On start up the count will be 311, after over 150 casts it seems like it levels out at about 400.

I suppose I shouldn't sweat it.
 
Status
Not open for further replies.
Top