HomeUser Control Panel (unavailable in archive)ForumsTutorialsArt GalleryResourcesMaps

Need vJass xpert, possible Crash Suspect...

03-18-2009, 02:07 AM#1
StRoNgFoE_2000
Hello, some of you may or may not know I create the WMW Tournament Edition games that are mildly popular on B.net. My problem at the moment is I'm facing a random crash, that I think I have narrowed down to my tower spell casting script. And just to note..

I'M NOT 100% SURE THIS IS THE CAUSE OF THE CRASH, I just think it is because the crash started about the time I implemented this script. It could very well be my creep cast system that uses a very similar approach, but the big difference is this uses a global player variable that is overwritten constantly.

First off, I'd like to just skip any animosity pertaining to me working on a series of WMW maps, the whole blah blah I didn't create it, Duke Wintermaul did, and blah blah there's already 10000's of versions of WMW we don't need more... No I didn't create WMW, and yes, there's hundreds of variations of it. I love the concept but nobody before me has ever done it professionally and it has always been plagued with units cluttering and end game lag, nor has anyone brought it to the level that I have. Long story short, I work on the map because I enjoy doing doing it, it's a learning experience and it's fun packed full of features.

I'm hoping this thread stays on topic relating to my crash as opposed to the history and disambiguation to line of WMW maps.

Ok, for the system. Basically I created a tower cast system that uses a timer per tower to cast spells, as opposed to the general well known method of detecting a tower attack and casting a spell on the attacked unit. I'll point out now that it uses Vexorians TimerUtils system for struct data attaching.

How it works:
I figured I'd better briefly explain how it works before anything, it's a simple concept that takes quite a bit of code.
1.) A player builds a tower.
2.) A timer is created for the tower.
3.) When the timer expires, the tower finds a nearby unit, X and Y point, or itself to cast a spell.
4.) If a unit is found, cast spell onto unit. If no unit is found, timer checks again in 1 second.
5.) Reset timer and repeat 1-4.

The reason I use timers as opposed to unit attack events is because of speed and efficiency. Before I had several triggers responsible for the tower spells. It got to the point there was close to 20k events firing a second, depending on how many towers were in the map.

Example: 50 tower cast triggers, 400 towers on map each with 1 second cooldown = 20k unit attack events a second. I found this to be very inefficient, and end game the map lagged hard. Since this timer system, there can be 1000 towers on map with no lag.

The Crash
Never crashes for more than one player, meaning the crash is player specific. The map does not crash for everyone, one player crashes and the game goes on. I'm not even sure if the crash is relating to this script.

My belief is that it crashes because of the global player variable I use "caster". The purpose of this variable is to use in my filter functions for a quick reference to see if the unit is an enemy of the player who owns the tower. Although I do not use waits anywhere, I have this feeling it's being overwritten before its referenced for a player, because of the high number of towers using this script late game, causing the game to crash, if this even makes any sense. Because even then it should just return false if it's not the same player. I dunno, I'm at my wits end.

Collapse JASS:

  //==========================================================================    
 // -- Global variables.
//============================================================================

globals
    private player   caster       // Global to carry to another function.
    private boolexpr anycast      // The boolexpr for grabbing any units.
    private boolexpr grdcast      // The boolexpr for grabbing ground units.
    private boolexpr aircast      // The boolexpr for grabbing air units.
    private boolexpr selfcast      // The boolexpr for grabbing self.
    constant integer CTar = 0     // Integer for boolexpr for any Creep.
    constant integer GTar = 1     // Integer for boolexpr for Ground type of creep.
    constant integer ATar = 2     // Integer for boolean for Air type of greep.
    constant integer STar = 3     // Integer for boolean for Self target.
    constant string  NSTR = " "   // Simple no string (dunno why I bothered).
endglobals

  //==========================================================================    
 // -- Filter functions for obtaining a group of units to cast onto.
//============================================================================

private function anyenemy takes nothing returns boolean    // Filter for ground or air units.
    return IsUnitEnemy(GetFilterUnit(), caster) and IsUnitType(GetFilterUnit(), UNIT_TYPE_GIANT) == true and IsUnitType(GetFilterUnit(), UNIT_TYPE_DEAD) == false
endfunction
private function groundenemy takes nothing returns boolean // Filter for ground units only.
    return IsUnitEnemy(GetFilterUnit(), caster) and IsUnitType(GetFilterUnit(), UNIT_TYPE_GIANT) == true and IsUnitType(GetFilterUnit(), UNIT_TYPE_GROUND) == true and IsUnitType(GetFilterUnit(), UNIT_TYPE_DEAD) == false
endfunction
private function airenemy takes nothing returns boolean    // Filter for air units only.
    return IsUnitEnemy(GetFilterUnit(), caster) and IsUnitType(GetFilterUnit(), UNIT_TYPE_GIANT) == true and IsUnitType(GetFilterUnit(), UNIT_TYPE_FLYING) == true and IsUnitType(GetFilterUnit(), UNIT_TYPE_DEAD) == false
endfunction
private function self takes nothing returns boolean        // Filter for Cloud Strife only (so far).
    return GetUnitTypeId(GetFilterUnit()) == 'e015'
endfunction

And i'm sorry for multiple posts, the script doesn't fit in one post. If I try to post it all in one, the site deletes all my text.
03-18-2009, 02:13 AM#2
StRoNgFoE_2000
And continued..

Collapse JASS:
  //============================================================================
 // -- The master tower spell struct to handle all spell cast data.
//==============================================================================

struct towercast   // ===== Defaults =====
    real      r    // Range of nearest unit.
    real      x    // X Position of tower.
    real      y    // Y Position of tower.
    real      c    // Cooldown of tower spell. 
    unit      u    // Casting tower.
    unit      z    // Target creep.
    timer     t    // Timer for cooldown.
    group     g    // Group to pick a unit from
    string    s    // String order of spell.
    player    p    // Owner of the tower.
    integer   i    // Type ID of tower.
    integer   d    // Comparison of Type ID of tower.
    integer   a    // Sets the boolexpr.
    boolean   b    // If the tower casted a spell or not.

  //============================================================================
 // -- Methods for setting up struct data.
//==============================================================================

   // Creates the necessary data.
    static method create takes string castorder, real range, real cooldown, integer boolfilter returns towercast
        local towercast data = towercast.allocate()
        set data.s = castorder
        set data.r = range
        set data.c = cooldown
        set data.a = boolfilter
        set data.t = NewTimer()
        set data.b = false
        set data.u = GetTriggerUnit()
        set data.p = GetTriggerPlayer()
        set data.x = GetUnitX(data.u)
        set data.y = GetUnitY(data.u)
        set data.i = GetUnitTypeId(data.u)
        if data.g == null then
            set data.g = CreateGroup()
        endif
        return data
    endmethod

   // Enums a group of units depending on the filter set.
    private method groupenum takes nothing returns nothing
        set caster = .p
        if .a == CTar then
            call GroupEnumUnitsInRange(.g, .x, .y, .r, anycast)
        elseif .a == GTar then
            call GroupEnumUnitsInRange(.g, .x, .y, .r, grdcast)
        elseif .a == ATar then
            call GroupEnumUnitsInRange(.g, .x, .y, .r, aircast)
        elseif .a == STar then
            call GroupEnumUnitsInRange(.g, .x, .y, .r, selfcast)
        endif
    endmethod

  //============================================================================
 // -- These methods handle the majority of spell casting towers.
//==============================================================================

// ========= Method for all towers that cast spells on a unit. =========
    static method target takes nothing returns nothing
        local timer t = GetExpiredTimer()
        local towercast data = GetTimerData(t)
        set data.d = GetUnitTypeId(data.u)
        if IsUnitType(data.u, UNIT_TYPE_DEAD) == false and data.i == data.d then
            call data.groupenum()
            if CountUnitsInGroup(data.g) > 0 then
                set data.z = GroupPickRandomUnit(data.g) 
                call SetUnitState(data.u, UNIT_STATE_MANA, 1) 
                set data.b = IssueTargetOrder(data.u, data.s, data.z)
            endif
            call GroupClear(data.g)
            if data.b then
                set data.b = false
                call TimerStart(t, data.c, false, function towercast.target)
            else
                call SetUnitState(data.u, UNIT_STATE_MANA, 0)
                call TimerStart(t, 1, false, function towercast.target)
            endif
        else
            call data.destroy()
        endif
        set t = null
    endmethod

// ========= Method for all towers that cast at a point. =========
    static method point takes nothing returns nothing
        local real  x
        local real  y
        local timer t = GetExpiredTimer()
        local towercast data = GetTimerData(t)
        set data.d = GetUnitTypeId(data.u)
        if IsUnitType(data.u, UNIT_TYPE_DEAD) == false and data.i == data.d then
            call data.groupenum()
            if CountUnitsInGroup(data.g) > 0 then
                set data.z = GroupPickRandomUnit(data.g)
                set x = GetUnitX(data.z)
                set y = GetUnitY(data.z) 
                call SetUnitState(data.u, UNIT_STATE_MANA, 1) 
                call IssuePointOrder(data.u, data.s, x, y)
                call TimerStart(t, data.c, false, function towercast.point)
            else
                call TimerStart(t, 1, false, function towercast.point)
            endif
            call GroupClear(data.g)
        else
            call data.destroy()
        endif
        set t = null
    endmethod

// ========= Method for all towers that immediately cast spells. =========
    static method immediate takes nothing returns nothing
        local timer t = GetExpiredTimer()
        local towercast data = GetTimerData(t)
        set data.d = GetUnitTypeId(data.u)
        if IsUnitType(data.u, UNIT_TYPE_DEAD) == false and data.i == data.d then
            call data.groupenum()
            if CountUnitsInGroup(data.g) > 0 then
                set data.z = GroupPickRandomUnit(data.g)
                call SetUnitState(data.u, UNIT_STATE_MANA, 1) 
                call IssueImmediateOrder(data.u, data.s)
                call TimerStart(t, data.c, false, function towercast.immediate)
            else
                call TimerStart(t, 1, false, function towercast.immediate)
            endif
            call GroupClear(data.g)
        else
            call data.destroy()
        endif
        set t = null
    endmethod

  //============================================================================
 // -- These methods handle the towers that other one's can't.
//==============================================================================

// ========= Method for Chain Lightning Towers. =========
    static method chainlightning takes nothing returns nothing
        local timer   t = GetExpiredTimer()
        local integer i
        local towercast data = GetTimerData(t)
        set data.d = GetUnitTypeId(data.u)
        if IsUnitType(data.u, UNIT_TYPE_DEAD) == false and data.i == data.d then
            call data.groupenum()
            if CountUnitsInGroup(data.g) > 0 then
                set data.z = GroupPickRandomUnit(data.g) 
                call SetUnitState(data.u, UNIT_STATE_MANA, 1) 
                set data.b = IssueTargetOrder(data.u, data.s, data.z)
            endif
            call GroupClear(data.g)
            if data.b then
                set data.b = false
                set i = GetPlayerId(data.p) + 1
                call TimerStart(t, ChainCooldown[i], false, function towercast.chainlightning)
            else
                call SetUnitState(data.u, UNIT_STATE_MANA, 0)
                call TimerStart(t, 1, false, function towercast.chainlightning)
            endif
        else
            call data.destroy()
        endif
        set t = null
    endmethod

// ========= Method for Storm and Thunder Cloud Towers. =========
    static method stormcloud takes nothing returns nothing
        local timer   t = GetExpiredTimer()
        local integer i
        local towercast data = GetTimerData(t)
        set data.d = GetUnitTypeId(data.u)
        if IsUnitType(data.u, UNIT_TYPE_DEAD) == false and data.i == data.d then
            call data.groupenum()
            if CountUnitsInGroup(data.g) > 0 then
                set data.z = GroupPickRandomUnit(data.g) 
                call SetUnitState(data.u, UNIT_STATE_MANA, 1) 
                set data.b = IssueTargetOrder(data.u, data.s, data.z)
            endif
            call GroupClear(data.g)
            if data.b then
                set data.b = false
                set i = GetPlayerId(data.p) + 1
                call TimerStart(t, StormCooldown[i], false, function towercast.stormcloud)
            else
                call SetUnitState(data.u, UNIT_STATE_MANA, 0)
                call TimerStart(t, 1, false, function towercast.stormcloud)
            endif
        else
            call data.destroy()
        endif
        set t = null
    endmethod

// ========= Method for Good Tower. =========      
    static method goodtower takes nothing returns nothing
        local timer t = GetExpiredTimer()
        local towercast data = GetTimerData(t)
        if IsUnitType(data.u, UNIT_TYPE_DEAD) == false then
            call data.groupenum()
            if CountUnitsInGroup(data.g) > 0 then
                set data.z = GroupPickRandomUnit(data.g) 
                call SetUnitState(data.u, UNIT_STATE_MANA, 1) 
                call IssueTargetOrder(data.u, data.s, data.z)
                call SetUnitState(data.z, UNIT_STATE_LIFE, GetWidgetLife(data.z) * 0.80)
                call TimerStart(t, data.c, false, function towercast.goodtower)
            else
                call SetUnitState(data.u, UNIT_STATE_MANA, 0)
                call TimerStart(t, 1, false, function towercast.goodtower)
            endif
            call GroupClear(data.g)
        else
            call data.destroy()
        endif
        set t = null
    endmethod

// ========= Method for Gate Keeper. =========      
    static method gatekeeper takes nothing returns nothing
        local unit  u
        local timer t = GetExpiredTimer()
        local integer i = 0
        local towercast data = GetTimerData(t)
        set data.d = GetUnitTypeId(data.u)
        if IsUnitType(data.u, UNIT_TYPE_DEAD) == false and data.i == data.d then
            call data.groupenum()
            if CountUnitsInGroup(data.g) > 0 then
                set u = CreateUnit(data.p, 'h062', data.x, data.y, 270)
                call UnitAddAbility(u, 'A01F')
                call UnitApplyTimedLife(u, 'BTLF', 2)
                loop
                    set data.z = FirstOfGroup(data.g)
                    exitwhen data.z == null or i > 3
                    call IssueTargetOrder(u, data.s, data.z)
                    call GroupRemoveUnit(data.g, data.z)
                    set i = i + 1
                endloop    
                call DestroyEffect(AddSpecialEffectTarget("Abilities\\Spells\\Human\\Thunderclap\\ThunderClapCaster.mdl", data.u, "origin"))
                call TimerStart(t, data.c, false, function towercast.gatekeeper)
            else
                call TimerStart(t, 1, false, function towercast.gatekeeper)
            endif
            call GroupClear(data.g)
        else
            call data.destroy()
        endif
        set u = null
        set t = null
    endmethod
    
// ========= Method for Lazy Tower. =========              
    static method lazytower takes nothing returns nothing
        local timer t = GetExpiredTimer()
        local towercast data = GetTimerData(t)
        if UnitHasAbility(data.z, 'B00R') then
            set data.x = GetUnitX(data.z)
            set data.y = GetUnitY(data.z)
            set data.u = CreateUnit(data.p, 'h062', data.x, data.y, 270)
            call UnitAddAbility(data.u, 'A00F')
            call UnitApplyTimedLife(data.u, 'BTLF', 5)
            call data.groupenum()
            loop
                set data.z = FirstOfGroup(data.g)
                exitwhen data.z == null
                call IssueTargetOrder(data.u, data.s, data.z)
                call DestroyEffect(AddSpecialEffectTarget("Abilities\\Spells\\Orc\\Purge\\PurgeBuffTarget.mdl", data.z, "origin"))
                call GroupRemoveUnit(data.g, data.z)
            endloop
            call data.destroy()
        elseif IsUnitType(data.z, UNIT_TYPE_DEAD) then
            call data.destroy()
        else
            call TimerStart(t, data.c, false, function towercast.lazytower)
        endif
        set t = null
    endmethod

// ========= Method for Insane Gravity. =========         
    static method insanegravity takes nothing returns nothing
        local timer t = GetExpiredTimer()
        local integer i = 0
        local towercast data = GetTimerData(t)
        set data.i = GetUnitTypeId(data.u)
        if UnitHasAbility(data.z, 'B00Q') then
            set data.x = GetUnitX(data.z)
            set data.y = GetUnitY(data.z)
            call data.groupenum()
            loop
                set data.z = FirstOfGroup(data.g)
                exitwhen data.z == null or i > 4
                call SetUnitState(data.z, UNIT_STATE_LIFE, GetWidgetLife(data.z) * 0.98)
                call DestroyEffect(AddSpecialEffectTarget("Abilities\\Spells\\Orc\\LightningShield\\LightningShieldBuff.mdl", data.z, "overhead"))
                call GroupRemoveUnit(data.g, data.z)
                set i = i + 1
            endloop
            call data.destroy()
        elseif IsUnitType(data.z, UNIT_TYPE_DEAD) or data.i != data.d then
            call data.destroy()
        else
            call TimerStart(t, data.c, false, function towercast.insanegravity)
        endif
        set t = null
    endmethod

// ========= Methods for Negation Tower. =========         
    static method negationdispel takes nothing returns nothing
        local real  x
        local real  y
        local timer t = GetExpiredTimer()
        local towercast data = GetTimerData(t)
        if IsUnitType(data.u, UNIT_TYPE_DEAD) == false then
            call data.groupenum()
            if CountUnitsInGroup(data.g) > 0 then
                set data.z = FirstOfGroup(data.g) 
                set x = GetUnitX(data.z)
                set y = GetUnitY(data.z)
                call SetUnitState(data.u, UNIT_STATE_MANA, 1) 
                call IssuePointOrder(data.u, data.s, x, y)
                call GroupClear(data.g)
                call TimerStart(t, data.c, false, function towercast.negationdispel)
            else
                call SetUnitState(data.u, UNIT_STATE_MANA, 0)
                call TimerStart(t, 1, false, function towercast.negationdispel)
            endif
        else
            call data.destroy()
        endif
        set t = null
    endmethod
    static method negationsilence takes nothing returns nothing
        local real  x
        local real  y
        local timer t = GetExpiredTimer()
        local towercast data = GetTimerData(t)
        if IsUnitType(data.u, UNIT_TYPE_DEAD) == false then
            set data.i = GetRandomInt(1, 3)
            if data.i == 1 then
                call data.groupenum()
                if CountUnitsInGroup(data.g) > 0 then
                    set data.z = FirstOfGroup(data.g)
                    set x = GetUnitX(data.z)
                    set y = GetUnitY(data.z)
                    call SetUnitState(data.u, UNIT_STATE_MANA, 1) 
                    call IssuePointOrder(data.u, data.s, x, y)
                    call GroupClear(data.g)
                endif
            endif
        else
            call data.destroy()
        endif
        set t = null
    endmethod
    
// ========= Methods for Noah. ========= 
    static method noahloop takes nothing returns nothing
        local timer t = GetExpiredTimer()
        local towercast data = GetTimerData(t)
        if IsUnitType(data.u, UNIT_TYPE_DEAD) == false then
        if data.i < 3 then
        if data.i == 0 then
            set data.d = 'A017'
        elseif data.i == 1 then
            call UnitRemoveAbility(data.u, 'A017')
            set data.d = 'A007'
        elseif data.i == 2 then
            call UnitRemoveAbility(data.u, 'A007')
            set data.d = 'A018'
        endif
                call UnitAddAbility(data.u, data.d)
                call SetUnitState(data.u, UNIT_STATE_MANA, 1)
        call IssuePointOrder(data.u, data.s, data.x, data.y)
        set data.i = data.i + 1
        call SetTimerData(t, data)
        call TimerStart(t, 1.28, false, function towercast.noahloop)
        else
        call UnitRemoveAbility(data.u, 'A018')
        call TimerStart(t, 1.28, false, function towercast.noahcast)
        endif
    else
        call data.destroy()
    endif
    set t = null
    endmethod
    static method noahcast takes nothing returns nothing
        local timer t = GetExpiredTimer()
        local towercast data = GetTimerData(t)
        if IsUnitType(data.u, UNIT_TYPE_DEAD) == false then
            call data.groupenum()
            if CountUnitsInGroup(data.g) > 0 then
                set data.i = 0
                set data.z = FirstOfGroup(data.g) 
                set data.x = GetUnitX(data.z)
                set data.y = GetUnitY(data.z)
                call GroupClear(data.g)
        call SetTimerData(t, data)
        call TimerStart(t, data.c, false, function towercast.noahloop)
        else
        call TimerStart(t, 1, false, function towercast.noahcast)
        endif
    else
        call data.destroy()
    endif
    set t = null
    endmethod
    
// ========= Method for Mega Tower. =========              
    static method megatower takes nothing returns nothing
        local timer t = GetExpiredTimer()
        local towercast data = GetTimerData(t)
        if UnitHasAbility(data.z, 'B00S') then
            set data.x = GetUnitX(data.z)
            set data.y = GetUnitY(data.z)
            call DestroyEffect(AddSpecialEffect("Objects\\Spawnmodels\\NightElf\\NEDeathMedium\\NEDeath.mdl", data.x, data.y))
            call data.destroy()
        elseif IsUnitType(data.z, UNIT_TYPE_DEAD) then
            call data.destroy()
        endif
        set t = null
    endmethod

// ========= SMASH, CRUSH, POUND, DESTROY! =========              
    method onDestroy takes nothing returns nothing
        call GroupClear(.g)
        call ReleaseTimer(.t)
    endmethod
endstruct

03-18-2009, 02:14 AM#3
StRoNgFoE_2000
and continued...

Collapse JASS:
    //============================================================================
 // -- Master Function for when a player builds a tower.
//==============================================================================

function TowerCastCond takes nothing returns boolean
    return IsUnitType(GetTriggerUnit(), UNIT_TYPE_MECHANICAL) == true
endfunction
function TowerCastCreate takes nothing returns nothing
    local towercast data
    local towercast next
    local unit    u = GetTriggerUnit()
    local integer d = GetUnitTypeId(u)
    local player  p = GetTriggerPlayer()
    local integer i = GetPlayerId(p) + 1
    local boolean PointTarTower = false
    local boolean SpellTarTower = false
    local boolean ImmedTarTower = false

//========= Point Target Spell Towers. =========

  // ----- Kain Lightning Reaver Charged -----
    if d == 'o00P' then
        set data = towercast.create("monsoon", 750, 20, CTar)
        set PointTarTower = true

  // ----- The Sun and Solar Eclipse -----
    elseif d == 'h03P' or d == 'h048' then
        set data = towercast.create("rainoffire", 300, 15, GTar)
        set PointTarTower = true

  // ----- Raziel Earth Reaver Charged -----
    elseif d == 'o00J' then
        set data = towercast.create("shockwave", 550, 8, GTar)
        set PointTarTower = true

//========= Point Target Spell Towers (Special). =========

  // ----- Negation Tower -----
    elseif d == 'h02X' then
        set data = towercast.create("dispel", 600, 15, GTar)
        set next = towercast.create("silence", 600, 0, GTar)
        call SetTimerData(data.t, data)
        call SetTimerData(next.t, next)
        call TimerStart(data.t, 1, false, function towercast.negationdispel)
        call TimerStart(next.t, 4, true, function towercast.negationsilence)
    
//========= Unit Target Spell Towers. =========

  // ----- Cloud Strife -----
    elseif d == 'e015' then
        set data = towercast.create("bloodlust", 25, 30, STar)
        set SpellTarTower = true

  // ----- Death Tower -----
    elseif d == 'h03W' then
        set data = towercast.create("acidbomb", 2500, 20, CTar)
        set SpellTarTower = true

  // ----- Faerie Mage, Faerie Chancellor, and Druid of Talon -----
    elseif d == 'o016' or d == 'oC26' or d == 'o02Q' then
        set data = towercast.create("faeriefire", 650, 6, CTar)
        set SpellTarTower = true

  // ----- Gravity Source -----
    elseif d == 'h019' then
        set data = towercast.create("slow", 525, 10, CTar)
        set SpellTarTower = true

  // ----- Tesla and Thunder Rod -----
    elseif d == 'o00R' or d == 'oC60' then
        set data = towercast.create("lightningshield", 575, 3, CTar)
        set SpellTarTower = true

  // ----- Marksman -----
    elseif d == 'h044' then
        set data = towercast.create("web", 1375, 5, ATar)
        set SpellTarTower = true

  // ----- Raziel Air Reaver Charged -----
    elseif d == 'o00F' then
        set data = towercast.create("cyclone", 575, 8, GTar)
        set SpellTarTower = true

  // ----- Soul Reaver TK Blast towers. -----
    elseif d == 'oC65' or d == 'o00N' or d == 'o00M' or d == 'o00O' or d == 'o00Q' or d == 'o03D' or d == 'o00E' or d == 'o00I' then
        set data = towercast.create("thunderbolt", 575, 8, CTar)
        set SpellTarTower = true

  // ----- Storm Mage -----
    elseif d == 'hC85' then
        set data = towercast.create("cyclone", 575, 10, GTar)
        set SpellTarTower = true

  // ----- The Void -----
    elseif d == 'o01W' then
        set data = towercast.create("devour", 650, 10, GTar)
        set SpellTarTower = true

  // ----- Water Sprayer -----
    elseif d == 'o00U' then
        set data = towercast.create("thunderbolt", 600, 6, CTar)
        set SpellTarTower = true

  // ----- Web Tower -----
    elseif d == 'h03T' then
        set data = towercast.create("web", 975, 4.5, ATar)
        set SpellTarTower = true

  // ----- Mega Tower -----
    elseif d == 'h060' then
        set data = towercast.create("acidbomb", 12000, 4, CTar)
        set SpellTarTower = true

//========= Unit Target Spell Towers (Special). =========

  // ----- Root Tower (Requires the functions found above this one!)
    elseif d == 'h03U' then
        set data = towercast.create("entanglingroots", 450, 8, GTar)
        set SpellTarTower = true

  // ----- Teleport Tower (Requires the functions found above this one!)
    elseif d == 'h08O' then
        set data = towercast.create("acidbomb", 650, 5, CTar)
        set SpellTarTower = true

 // ----- Chain Lightning Caster and Blaster -----
    elseif d == 'hC36' or d == 'h00B' then
        set data = towercast.create("chainlightning", 650, ChainCooldown[i], CTar)
        call SetTimerData(data.t, data)
        call TimerStart(data.t, 1, false, function towercast.chainlightning)

  // ----- Storm and Thunder Cloud -----
    elseif d == 'h06E' or d == 'h06F' then
        set data = towercast.create("forkedlightning", 575, StormCooldown[i], CTar)
        call SetTimerData(data.t, data)
        call TimerStart(data.t, 1, false, function towercast.stormcloud)

 // ----- Good Tower -----
    elseif d == 'h09B' then
        set data = towercast.create("acidbomb", 500, 3, CTar)
        call SetTimerData(data.t, data)
        call TimerStart(data.t, 1, false, function towercast.goodtower)

  // ----- Lazy Tower (Requires the functions found above this one!) 
    elseif d == 'h03Z' then
        set data = towercast.create("acidbomb", 650, 6, CTar)
        call SetTimerData(data.t, data)
        call TimerStart(data.t, 1, false, function towercast.target)

  // ----- Insane Gravity (Requires the functions found above this one!)
    elseif d == 'o01V' then
        set data = towercast.create("slow", 525, 10, CTar)
        set next = towercast.create("acidbomb", 650, 6, CTar)
        call SetTimerData(data.t, data)
        call SetTimerData(next.t, next)
        call TimerStart(data.t, 1, false, function towercast.target)
        call TimerStart(next.t, 1, false, function towercast.target)

//========= Immediate Cast Spell Towers. =========

  // ----- Rock and Slate Golem -----
    elseif d == 'o02X' or d == 'o03T' then
        set data = towercast.create("thunderclap", 300, 15, GTar)
        set ImmedTarTower = true

  // ----- Mind Blaster and Washer -----
    elseif d == 'h01A' or d == 'h01S' then
        set data = towercast.create("starfall", 500, 6, CTar)
        set ImmedTarTower = true

  // ----- Overgrown Spike Plant -----
    elseif d == 'h036' then
        set data = towercast.create("fanofknives", 625, 10, CTar)
        set ImmedTarTower = true

  // ----- Priestess of the Moon (Night Elf) -----
    elseif d == 'o02U' then
        set data = towercast.create("starfall", 500, 20, CTar)
        set ImmedTarTower = true

  // ----- Raziel Fire Reaver Charged -----
    elseif d == 'o00H' then
        set data = towercast.create("stomp", 350, 10, GTar)
        set ImmedTarTower = true

  // ----- Warden -----
    elseif d == 'o05A' then
        set data = towercast.create("fanofknives", 625, 12, CTar)
        set ImmedTarTower = true

  // ----- The Moon and Lunar Eclipse -----
    elseif d == 'h03J' or d == 'h049' then
        set data = towercast.create("starfall", 450, 15, CTar)
        set ImmedTarTower = true

//========= Unique Towers ========= 

  // ----- Gate Keeper -----
    elseif d == 'h01H' then
        set data = towercast.create("purge", 500, 10, CTar)
        call SetTimerData(data.t, data)
        call TimerStart(data.t, 1, false, function towercast.gatekeeper)
    
  // ----- Noah -----
    elseif d == 'h06J' then
        set data = towercast.create("stampede", 500, 0.01, CTar)
        call SetTimerData(data.t, data)
        call TimerStart(data.t, 1, false, function towercast.noahcast)
    endif
    
//========= Execute tower spell if available. ========= 
    if SpellTarTower then
        call SetTimerData(data.t, data)
        call TimerStart(data.t, 1, false, function towercast.target)
    elseif PointTarTower then
        call SetTimerData(data.t, data)
        call TimerStart(data.t, 1, false, function towercast.point)
    elseif ImmedTarTower then
        call SetTimerData(data.t, data)
        call TimerStart(data.t, 1, false, function towercast.immediate)
    endif

    set u = null
    set p = null
endfunction

  //============================================================================
 // -- Special functions for select towers.
//==============================================================================

// ========= Functions for the Mega Tower. ========= 

private function MegaCond takes nothing returns boolean
    return GetSpellAbilityId() == 'A01T'
endfunction
private function MegaCast takes nothing returns nothing
    local towercast data = towercast.create(NSTR, 0, .0, CTar)
    set data.z = GetSpellTargetUnit()
    call SetTimerData(data.t, data)
    call TimerStart(data.t, .03, true, function towercast.megatower)
endfunction

// ========= Functions for the Root Tower. ========= 

// Note: Uses struct "reorder" found in the MainLib.

private function RootCond takes nothing returns boolean
    return GetSpellAbilityId() == 'Aenr'
endfunction
private function RootCast takes nothing returns nothing
    local unit u = GetSpellTargetUnit()
    local reorder data = reorder.create(u)
    call SetTimerData(data.t, data)
    call TimerStart(data.t, 5.25, false, function reorder.moveunit)
    set u = null
endfunction

// ========= Functions for the Lazy Tower. ========= 

private function LazyCond takes nothing returns boolean
    return GetSpellAbilityId() == 'A01W'
endfunction
private function LazyCast takes nothing returns nothing
    local towercast data = towercast.create("purge", 300, .03, CTar)
    set data.z = GetSpellTargetUnit()
    call SetTimerData(data.t, data)
    call TimerStart(data.t, .03, false, function towercast.lazytower)
endfunction

// ========= Functions for the Insane Gravity. ========= 

private function InsaneCond takes nothing returns boolean
    return GetSpellAbilityId() == 'A02M'
endfunction
private function InsaneCast takes nothing returns nothing
    local towercast data = towercast.create(NSTR, 280, .03, CTar)
    set data.z = GetSpellTargetUnit()
    call SetTimerData(data.t, data)
    call TimerStart(data.t, .03, false, function towercast.insanegravity)    
endfunction

// ========= Functions for the Teleport Tower. =========      

private function TeleCond takes nothing returns boolean
    return GetSpellAbilityId() == 'A01R'
endfunction
private function TeleCast takes nothing returns nothing
    local real    x
    local real    y
    local real    c
    local real    d
    local unit    u = GetSpellTargetUnit()
    local integer i = GetRandomInt(1,2)
    call DestroyEffect(AddSpecialEffectTarget("Abilities\\Spells\\NightElf\\Blink\\BlinkTarget.mdl", u, "origin"))
    call TriggerSleepAction(.3)
    if IsUnitInForce(u, EnemiesTop) then
        if i == 1 then
            set x = GetRectCenterX(gg_rct_Middle_Center_Top_Right)
            set y = GetRectCenterY(gg_rct_Middle_Center_Top_Right)
            set c = GetRectCenterX(gg_rct_Middle_Right_Top)
            set d = GetRectCenterY(gg_rct_Middle_Right_Top)            
            call GroupSetup(u, UnitsCenterTopRight)
        else
            set x = GetRectCenterX(gg_rct_Middle_Center_Top_Left)
            set y = GetRectCenterY(gg_rct_Middle_Center_Top_Left)
            set c = GetRectCenterX(gg_rct_Middle_Left_Top)
            set d = GetRectCenterY(gg_rct_Middle_Left_Top)            
            call GroupSetup(u, UnitsCenterTopLeft)
        endif
    else
        if i == 1 then
            set x = GetRectCenterX(gg_rct_Middle_Center_Bottom_Left)
            set y = GetRectCenterY(gg_rct_Middle_Center_Bottom_Left)
            set c = GetRectCenterX(gg_rct_Middle_Left_Bottom)
            set d = GetRectCenterY(gg_rct_Middle_Left_Bottom)            
            call GroupSetup(u, UnitsCenterBottomLeft)
        else
            set x = GetRectCenterX(gg_rct_Middle_Center_Bottom_Right)
            set y = GetRectCenterY(gg_rct_Middle_Center_Bottom_Right)
            set c = GetRectCenterX(gg_rct_Middle_Right_Bottom)
            set d = GetRectCenterY(gg_rct_Middle_Right_Bottom)            
            call GroupSetup(u, UnitsCenterBottomRight)
        endif
    endif
    call SetUnitPosition(u, x, y)
    call IssuePointOrder(u, "move", c, d)
    set u = null
endfunction

  //============================================================================
 // -- The initializer function for this script.
//==============================================================================

private function Init takes nothing returns nothing
    local trigger t

  // ----- Boolexprs used for the tower casting. -----
    set anycast  = Condition(function anyenemy)
    set grdcast  = Condition(function groundenemy)
    set aircast  = Condition(function airenemy)
    set selfcast = Condition(function self)

  // ----- Sets up the trigger for the Mega Tower. -----
    set t = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
    call TriggerAddCondition(t, Condition(function MegaCond))
    call TriggerAddAction(t, function MegaCast)

  // ----- Sets up the trigger for the Root Tower. -----
    set t = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
    call TriggerAddCondition(t, Condition(function RootCond))
    call TriggerAddAction(t, function RootCast)

  // ----- Sets up the trigger for the Teleport Tower. -----
    set t = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
    call TriggerAddCondition(t, Condition(function TeleCond))
    call TriggerAddAction(t, function TeleCast)

  // ----- Sets up the trigger for the Lazy Tower. -----
    set t = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
    call TriggerAddCondition(t, Condition(function LazyCond))
    call TriggerAddAction(t, function LazyCast)

  // ----- Sets up the trigger for the Insane Gravity. -----
    set t = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
    call TriggerAddCondition(t, Condition(function InsaneCond))
    call TriggerAddAction(t, function InsaneCast)

  // ----- Sets up the trigger for the when any player builds a tower. -----
    set t = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_UPGRADE_FINISH)
    call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_CONSTRUCT_FINISH)
    call TriggerAddCondition(t, Condition(function TowerCastCond))
    call TriggerAddAction(t, function TowerCastCreate)
    
    set t = null
endfunction

endlibrary

I also posted my problem on hive workshop. It's a little easier to read massive lines of script there so I'll add a link HERE.
03-18-2009, 04:38 AM#4
Jazradel
I'm tempted to blame the timers.

You could try rewriting it so you're only using a single timer, with the data stored to the towers, and have all the towers in a group that you loop through.
03-19-2009, 06:22 AM#5
StRoNgFoE_2000
Yeah the scary notion of having that many timers at any given time did put me into question whether or not it would remain stable after so long. The thought of using only 1 timer per tower type instead of per tower has entered my thoughts more than once, i just didn't want all towers to cast spells in synchronization. I think I can come up with a way to use one timer and even space out each cast in an unsynchronized fashion, just gonna take some clever concepts, maybe space out the casts over the total cooldown time depending on number of towers.

Kinda disappointing tho, during games this system has proven to be very fast and doesn't lag whatsover even with 100s of towers that cast spells on the map at a time. But like I said the crash is random, and this is the only script thats run automatically, so it has to be crash victim.

I guess it could be something like this to avoid synchronization.

somethings like..
-group of towers, grabs all on map, example: 5
-cooldown = 10 seconds
-5 towers in group
-set integer = count units in group
-set repeat = cooldown/integer
-call TimerStart(timer, repeat, false, function xx)
-restart timer 4 more times, this will cast all 5 towers within the 10 second period
-repeat

W/e I come up with, I'll give fewer timers a shot. Before I recode this massive block of script, is there any verification from anyone that a large number of timers can crash the map? Because this seems to work for a very long time, and the crash only happens like 1/10 games, and only for one player.
03-19-2009, 08:23 AM#6
StRoNgFoE_2000
A proof of concept of my previous message, just posting it here to see if there would be any problems with this method. It's ugly thus far, but it works. So far its just for chain lightning towers..

Collapse JASS:
library TowerSpells initializer Init requires MainLib

globals
    player   caster
    boolexpr anycast
    boolexpr grdcast
    boolexpr aircast
    boolexpr selfcast
endglobals

//==================================================
//  Filter Functions for Getting Creeps
//==================================================

private function anyenemy takes nothing returns boolean    // Filter for ground or air units.
    return IsUnitEnemy(GetFilterUnit(), caster) and IsUnitType(GetFilterUnit(), UNIT_TYPE_GIANT) == true and IsUnitType(GetFilterUnit(), UNIT_TYPE_DEAD) == false
endfunction
private function groundenemy takes nothing returns boolean // Filter for ground units only.
    return IsUnitEnemy(GetFilterUnit(), caster) and IsUnitType(GetFilterUnit(), UNIT_TYPE_GIANT) == true and IsUnitType(GetFilterUnit(), UNIT_TYPE_GROUND) == true and IsUnitType(GetFilterUnit(), UNIT_TYPE_DEAD) == false
endfunction
private function airenemy takes nothing returns boolean    // Filter for air units only.
    return IsUnitEnemy(GetFilterUnit(), caster) and IsUnitType(GetFilterUnit(), UNIT_TYPE_GIANT) == true and IsUnitType(GetFilterUnit(), UNIT_TYPE_FLYING) == true and IsUnitType(GetFilterUnit(), UNIT_TYPE_DEAD) == false
endfunction
private function self takes nothing returns boolean        // Filter for Cloud Strife only (so far).
    return GetUnitTypeId(GetFilterUnit()) == 'e015'
endfunction

//==================================================
//  Chain Lightning Towers
//==================================================

scope ChainLightningTowers
globals
    timer    ChainTimer        // - Global timer for all chain lightning towers.
    group    ChainTowers    // - Group for all chain lightning towers on map.
    group    ChainEnemies    // - Recycled group to grab nearby units to cast chain lightning onto.
    real     ChainUpdate    // - How many times (based on number of towers) chain lightning will be cast in the cooldown period.
    boolexpr ChainFilter    // - Filter to grab chain lightning towers.
endglobals
function ChainTowerFilter takes nothing returns boolean
    return GetUnitTypeId(GetFilterUnit()) == 'hC36' or GetUnitTypeId(GetFilterUnit()) == 'h00B'
endfunction
function ChainLightning takes nothing returns nothing
    local unit u
    local unit v
    local real x
    local real y
    call PauseTimer(ChainTimer)
    if CountUnitsInGroup(ChainTowers) > 0 then // This will be false until at least one tower is built.
        set u = FirstOfGroup(ChainTowers)
        set x = GetUnitX(u)
        set y = GetUnitY(u)
        set caster = GetOwningPlayer(u)
        call GroupEnumUnitsInRange(ChainEnemies, x, y, 650, anycast)
        if CountUnitsInGroup(ChainEnemies) > 0 then
            set v = GroupPickRandomUnit(ChainEnemies) 
            call SetUnitState(u, UNIT_STATE_MANA, 1) 
            call IssueTargetOrder(u, "chainlightning", v)
        endif
        call GroupClear(ChainEnemies)
        call GroupRemoveUnit(ChainTowers, u)
        if CountUnitsInGroup(ChainTowers) > 0 then
            call TimerStart(ChainTimer, ChainUpdate, false, function ChainLightning)
        else
            call TimerStart(ChainTimer, .01, false, function ChainLightning) // Once all towers casted, restart the global cooldown math.
        endif
        set caster = null
    else
        call GroupClear(ChainTowers)
        call GroupEnumUnitsInRect(ChainTowers, EntireMap, ChainFilter)
        if CountUnitsInGroup(ChainTowers) > 0 then
            set ChainUpdate = 4.5 / CountUnitsInGroup(ChainTowers)
            call TimerStart(ChainTimer, ChainUpdate, false, function ChainLightning)
        else
            call TimerStart(ChainTimer, 1.5, false, function ChainLightning)
        endif
    endif
    set u = null
    set v = null
endfunction
endscope    

//==================================================
//  Whatever is next.
//==================================================

private function Init takes nothing returns nothing
    local trigger t
    
    set anycast  = Condition(function anyenemy)
    set grdcast  = Condition(function groundenemy)
    set aircast  = Condition(function airenemy)
    set selfcast = Condition(function self)

    set ChainTimer   = CreateTimer()
    set ChainTowers  = CreateGroup()
    set ChainEnemies = CreateGroup()
    set ChainFilter  = Condition(function ChainTowerFilter)
    call GroupEnumUnitsInRect(ChainTowers, EntireMap, ChainFilter)
    call TimerStart(ChainTimer, 5, false, function ChainLightning)
endfunction

endlibrary

With this method, a type of tower can never be synchronized with the cast of a tower of the same type, but only uses one timer per tower type. It will make sure that each cast is spaced out evenly over the course of 4.5 seconds, which is the global cooldown of every chain lightning tower. So, if theres 10 towers on the map, a tower will cast every .45 seconds, then reset, or if theres 50 towers on the map, a tower will cast every 0.09 seconds. The end result, every tower casts after 4.5 seconds, and will never be synchronized.

What I need here is, will this method be cleaner than my previous one (meaning no random crashes). I pretty much eliminated the use of TimerUtils and vJass altogether.
03-19-2009, 02:10 PM#7
Vexorian
I absolutely doubt it was the timers.

However, if you were using timer utils, maybe you should have tried blue timer utils, I can't think of how many timers you had, but if they were more than 500 then maybe red wouldn't work right with it - however it wouldn't crash at least not alone.

Quote:
What I need here is, will this method be cleaner than my previous one (meaning no random crashes). I pretty much eliminated the use of TimerUtils and vJass altogether.
Did you really recode everything in your map? You shouldn't rush to recode stuff if you are not sure what the problem was.

You should avoid calling event responses in methods or in any place outside the "triggering function" it complicates your code, believe me.

Reasons for crashes? To tell you the truth just reading your code doesn't help, could you post your map?

---
Your new method doesn't seem like it would work all right. I think with more and more towers the chain lighting per tower will get rarer , I don't think that's what you want...

You don't like synchronization... How could you fix this with a single timer? It sounds kind of complicated, I think multiple timers works better in this case if only because you want to accurately avoid the synchronizity. Though if you want a single timer per type, you need to work around it. You could keep a counter per tower and once a tower's counter reaches a point, you make it throw the spell... However this doesn't perform too well.
03-19-2009, 03:27 PM#8
Anitarf
Have you tried reproducing the crash while playing with war3err enabled? That could give you a valuable clue as to what's to blame.
03-20-2009, 12:50 AM#9
StRoNgFoE_2000
To Vex: I used blue flavor from the start because I feared the 256 timer limit of the red version. I use TimerUtils for almost all my timer needs everywhere in the map, so I'm almost certain that 256 is exceeded easily.

And no I didn't recode my entire map, I just started to recode this section involving creep casting because it was my highest suspect to my random crash, being its about the only script that runs at random with the level of complexity that it has. I came up with an alternative but I hate it (see below), and you are right it only adds new problems. When I said eliminate vJASS and TimerUtils, I only meant for this system not the entire map. Sorry for the confusion.

I sent a PM with a link to the map. I don't like to share it unoptimized by your optimizer, I'm sure any protection it offers can be breached, but not for the common noob. I don't want any cheap spinoffs that claim they created it.

To Anitarf: I have not been able to get grimoire or war3err to work since the 1.22 patch. I know it works if you play the map in a 1.21 installation and grimoire will work, but this doesn't help me because it's never crashed in single player, and the player that crashes is always random, never happens to more than one player (except once it happened to two players simultaneously, but never seen that again).

There's no easy alternative to my last system, it was perfect in my eyes... it's fast, and it doesn't fail even after 2000000 tower casts. I'm not even sure if its the cause of the crash, which is why I started recoding an alternate system.. see if the map still crashes with this one. But even this one has problems, if towers are sold during the cast loop, then the group still sees the units as there and freezes all casting completely. Plus it's ugly, and the result of all those text macros is only going to create massive lines of unnecessary code.

Collapse JASS:
library TowerSpells initializer Init requires MainLib

globals
    player   caster
    boolexpr anycast
    boolexpr grdcast
    boolexpr aircast
    group     TowerEnemies
endglobals

private function anyenemy takes nothing returns boolean
    return IsUnitEnemy(GetFilterUnit(), caster) and IsUnitType(GetFilterUnit(), UNIT_TYPE_GIANT) == true and IsUnitType(GetFilterUnit(), UNIT_TYPE_DEAD) == false
endfunction
private function groundenemy takes nothing returns boolean
    return IsUnitEnemy(GetFilterUnit(), caster) and IsUnitType(GetFilterUnit(), UNIT_TYPE_GIANT) == true and IsUnitType(GetFilterUnit(), UNIT_TYPE_GROUND) == true and IsUnitType(GetFilterUnit(), UNIT_TYPE_DEAD) == false
endfunction
private function airenemy takes nothing returns boolean
    return IsUnitEnemy(GetFilterUnit(), caster) and IsUnitType(GetFilterUnit(), UNIT_TYPE_GIANT) == true and IsUnitType(GetFilterUnit(), UNIT_TYPE_FLYING) == true and IsUnitType(GetFilterUnit(), UNIT_TYPE_DEAD) == false
endfunction

private function Init takes nothing returns nothing
    set anycast  = Condition(function anyenemy)
    set grdcast  = Condition(function groundenemy)
    set aircast  = Condition(function airenemy)
    set TowerEnemies = CreateGroup()
endfunction

endlibrary

//=================================================================================================================
//	TOWER SET A - TOWER TARGET CREEP TOWERS
//  The below TextMacro creates the necessary script for many of the unit target spell casting towers.
//=================================================================================================================

//! textmacro CreateTowerSpell takes TOWERNAME, FILTERID, ORDERID, RANGE, COOLDOWN, BOOLEXPR
scope $TOWERNAME$ initializer $TOWERNAME$Init
globals
    timer    $TOWERNAME$Timer
    group    $TOWERNAME$Towers
    real     $TOWERNAME$Update
    boolexpr $TOWERNAME$Filter
endglobals
function $TOWERNAME$FilterX takes nothing returns boolean
    return GetUnitTypeId(GetFilterUnit()) == '$FILTERID$'
endfunction
function $TOWERNAME$Cast takes nothing returns nothing
    local unit u
    local unit v
    local real x
    local real y
    call PauseTimer($TOWERNAME$Timer)
    if CountUnitsInGroup($TOWERNAME$Towers) > 0 then
        set u = FirstOfGroup($TOWERNAME$Towers)
        set x = GetUnitX(u)
        set y = GetUnitY(u)
        set caster = GetOwningPlayer(u)
        call GroupEnumUnitsInRange(TowerEnemies, x, y, $RANGE$, $BOOLEXPR$)
        set caster = null
        if CountUnitsInGroup(TowerEnemies) > 0 then
            set v = GroupPickRandomUnit(TowerEnemies) 
            call SetUnitState(u, UNIT_STATE_MANA, 1) 
            call IssueTargetOrder(u, "$ORDERID$", v)
        endif
        call GroupClear(TowerEnemies)
        call GroupRemoveUnit($TOWERNAME$Towers, u)
        if CountUnitsInGroup($TOWERNAME$Towers) > 0 then
            call TimerStart($TOWERNAME$Timer, $TOWERNAME$Update, false, function $TOWERNAME$Cast)
        else
            call TimerStart($TOWERNAME$Timer, .01, false, function $TOWERNAME$Cast)
        endif
    else
        call GroupClear($TOWERNAME$Towers)
        call GroupEnumUnitsInRect($TOWERNAME$Towers, EntireMap, $TOWERNAME$Filter)
        if CountUnitsInGroup($TOWERNAME$Towers) > 0 then
            set $TOWERNAME$Update = $COOLDOWN$ / CountUnitsInGroup($TOWERNAME$Towers)
            call TimerStart($TOWERNAME$Timer, $TOWERNAME$Update, false, function $TOWERNAME$Cast)
        else
            call TimerStart($TOWERNAME$Timer, 2.0, false, function $TOWERNAME$Cast)
        endif
    endif
    set u = null
    set v = null
endfunction
private function $TOWERNAME$Init takes nothing returns nothing
    set $TOWERNAME$Timer   = CreateTimer()
    set $TOWERNAME$Towers  = CreateGroup()
    set $TOWERNAME$Filter  = Condition(function $TOWERNAME$FilterX)
    call GroupEnumUnitsInRect($TOWERNAME$Towers, EntireMap, $TOWERNAME$Filter)
    call TimerStart($TOWERNAME$Timer, 5.0, false, function $TOWERNAME$Cast)
endfunction
endscope
//! endtextmacro

// REFERENCE: CreateTowerSpell takes TOWERNAME, FILTERID, ORDERID, RANGE, COOLDOWN, BOOLEXPR

//! runtextmacro CreateTowerSpell("DeathTower", "h03W", "acidbomb", "2500", "20.0", "anycast")
//! runtextmacro CreateTowerSpell("DruidTalon", "o02Q", "faeriefire", "650", "6.0", "anycast")
//! runtextmacro CreateTowerSpell("FaerieMage", "o016", "faeriefire", "650", "6.0", "anycast")
//! runtextmacro CreateTowerSpell("FaerieChanc", "oC26", "faeriefire", "650", "6.0", "anycast")
//! runtextmacro CreateTowerSpell("GravityPull", "h019", "slow", "525", "8.0", "anycast")
//! runtextmacro CreateTowerSpell("Marksman", "h044", "web", "1375", "5.0", "aircast")
//! runtextmacro CreateTowerSpell("RazAirCharged", "o00F", "cyclone", "575", "10.0", "grdcast")
//! runtextmacro CreateTowerSpell("StormMage", "hC85", "cyclone", "575", "10.0", "grdcast")
//! runtextmacro CreateTowerSpell("TKBlastA", "oC65", "thunderbolt", "575", "8.0", "anycast")
//! runtextmacro CreateTowerSpell("TKBlastB", "o00N", "thunderbolt", "575", "8.0", "anycast")
//! runtextmacro CreateTowerSpell("TKBlastC", "o00M", "thunderbolt", "575", "8.0", "anycast")
//! runtextmacro CreateTowerSpell("TKBlastD", "o00O", "thunderbolt", "575", "8.0", "anycast")
//! runtextmacro CreateTowerSpell("TKBlastE", "o00Q", "thunderbolt", "575", "8.0", "anycast")
//! runtextmacro CreateTowerSpell("TKBlastF", "o03D", "thunderbolt", "575", "8.0", "anycast")
//! runtextmacro CreateTowerSpell("TKBlastG", "o00E", "thunderbolt", "575", "8.0", "anycast")
//! runtextmacro CreateTowerSpell("TKBlastH", "o00I", "thunderbolt", "575", "8.0", "anycast")
//! runtextmacro CreateTowerSpell("TheVoid", "o01W", "devour", "650", "10.0", "grdcast")
//! runtextmacro CreateTowerSpell("ThunderRod", "o00R", "lightningshield", "575", "3.0", "grdcast")
//! runtextmacro CreateTowerSpell("TeslaRod", "oC60", "lightningshield", "575", "3.0", "grdcast")
//! runtextmacro CreateTowerSpell("WaterSprayer", "o00U", "thunderbolt", "600", "8.0", "anycast")
//! runtextmacro CreateTowerSpell("WebTower", "h03T", "web", "975", "4.5", "aircast")


Edit: Something I didn't really give thought to until reading it again..

Quote:
Originally Posted by Vexorian
You should avoid calling event responses in methods or in any place outside the "triggering function" it complicates your code, believe me.

Does this mean I shouldn't use them even in the create method, and pass them with arguments instead? And if so, why?
03-22-2009, 04:10 AM#10
StRoNgFoE_2000
I haven't seen the crash in while, but someone who crashed was nice enough to email me a screenshot of what it says... Any idea of what this means? It only happens to one player.
Attached Images
File type: jpgcrash.jpg (37.5 KB)
03-22-2009, 06:55 AM#11
Bobo_The_Kodo
It means he needs to turn on his page file or get more ram
03-22-2009, 09:01 AM#12
Viikuna-
I've never used textmacros with strings. Do they work like this?
edit. meh, useless question. I checked JassHelper manual, I guess I just missed it before.

Collapse JASS:
"$ORDERID$"
03-22-2009, 09:08 AM#13
Blackroot
This is an AWFUL way to abuse textmacros. There are far better ways to do what you want to do; this is just bad.

You can use one timer and toss the towers into a single stack when they're constructed instead, cloning parameters from a registration class. Anyways; I suggest you rethink this because it's going to hurt you in the end.

Textmacros are simple; but I suggest using them sparringly. A full detail of them is in the jasshelper manual I beileive; but the jist is to instanciate the macro and use $MACROARGUMENT$ where you want the macro to replace text.
03-22-2009, 10:34 AM#14
StRoNgFoE_2000
Quote:
Originally Posted by Blackroot
This is an AWFUL way to abuse textmacros. There are far better ways to do what you want to do; this is just bad.

You can use one timer and toss the towers into a single stack when they're constructed instead, cloning parameters from a registration class. Anyways; I suggest you rethink this because it's going to hurt you in the end.

Textmacros are simple; but I suggest using them sparringly. A full detail of them is in the jasshelper manual I beileive; but the jist is to instanciate the macro and use $MACROARGUMENT$ where you want the macro to replace text.

I already scrapped that script a few days ago because I felt the same way about it. I just want to focus on the issue at hand, this random annoying crash.

Quote:
Originally Posted by Bobo_The_Kodo
It means he needs to turn on his page file or get more ram

Are you sure? Because I think this is the crash message that I randomly get, and my comp has 8gb of RAM in it and my pagefile max is set to 10gb on my hard drive (with 1tb hard drive 10gb is nothing to fear). Unless I'm suffering a MAJOR MAJOR memory leak issue here... which I'm certain I'm not.

I was playing with a friend a few weeks ago, I was the only one in the game who crashed. His PC wields 1gb of RAM and he's on XP so I'm pretty sure his pagefile never exceeds around 1gb (most likely windows managed), shouldn't he have crashed way before me? Plus, I play with so many people with computers back from the Flintstones days (im talking 256-512mb RAM), and we can go 20 games that literally go on over an hour and never crash.

So it makes me wonder.. is there any other reason for this crash message other than insufficient memory/pagefile?
03-22-2009, 10:52 AM#15
Blackroot
Common crashs are caused by:

H2I bug failing

Handle stack corruption (same difference)

"Bad Strings" strings which are to large (don't remember exactly how big.)

Bad handle operations - sometimes this just results in thread crashs.

Acessing nTh array index where the nTh array index does not exist.

Excessive amounts of operations at once.

To many special effects being created at once.

Rending a model with certain invalid parameters.

Casting certain spells such as Chain Lightning which have parameters that cause a crash.

Using a TriggerSleepAction in a thread will sometimes too.

Units going out of map bounds.



Most of these are obvious and you don't have them; there are other reasons things crash to, but look for these. Post any suspect code which may do these.

also; see if the map still crashs with Bound Sentinel, if you don't already have it in.