• 🏆 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] Need vJASS Expert, Possible Crash

Status
Not open for further replies.
Level 2
Joined
Jun 25, 2006
Messages
18
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.

The System
Although the entire script is stored into one trigger in my editor, I'll break it into sections so its easier to understand. Not the order in my script, but in the logical order that it is ran.

Section A - Globals and BoolExpr filter functions
JASS:
library TowerSpells initializer Init requires TimerUtils, MainLib

  //==========================================================================    
 // -- 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

Section B - The Master TowerCast Struct
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

Section C - Creating a Timer for a Constructed Tower
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

Section D - Additional Code for Select Towers
JASS:
  //============================================================================
 // -- 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

Section E - The Init function
JASS:
  //============================================================================
 // -- 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

Well that's it, hopefully this script is the reason the map is crashing. If anyone can help me out I would greatly appreciate it. If this script cannot possibly crash, please let me know that much as well.

Edit: Added map for dl to be played by anyone interested, cannot be opened in WE.
 

Attachments

  • wmw-te-v5.0-public-beta.05.w3x
    2.9 MB · Views: 48
Last edited:
Level 2
Joined
Jun 25, 2006
Messages
18
Sure I'll try to narrow it down, I'm thinking its the way I use the global player variable caster. It looks like it works in theory, and there should be no problems, but the crashes started when i made and implemented this system.

The data in this variable is being overwritten every time any tower casts a spell, which happens often so I'm thinking its the culprit when being referenced in the filter functions.

JASS:
// My only guess is this variable,
private player caster // Global to carry to another function.

// used in these functions,
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

// from this method,
   // 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

// which is called from all methods looking like this.
    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

My guess is it happens somewhere along those lines of code. For all I know something else in the map is causing the crash, but it's very doubtful because it happens randomly, it only happens to one person, and this script is the only script in the entire map that runs at random.
 
Level 9
Joined
Apr 5, 2008
Messages
529
It's funny, I had the exact same problem yesterday when making a spell. =P

I don't remember exactly how I fixed it, I think nullifying the variable fixed it, and I know it sounds absurdly weird. I was using a lightning array member in a struct, and it caused random crashes when I casted it.

I'm not sure that it's your problem, since player variables aren't like other variables.
 
Level 2
Joined
Jun 25, 2006
Messages
18
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.
 

Attachments

  • crash.jpg
    crash.jpg
    37.5 KB · Views: 156
Level 22
Joined
Dec 31, 2006
Messages
2,216
When you get that error it could be many things ^^.
I have gotten that error myself a lot. I strongly doubt it got something to do with memory leaks. Usually I get it when wc3 tries to do something that it cannot do, like executing an invalid function (ExecuteFunc("something")) or moving units outside the map. I also get that error sometimes when I try do destroy a handle that is already destroyed, but not nulled.
 
Level 2
Joined
Jun 25, 2006
Messages
18
When you get that error it could be many things ^^.
I have gotten that error myself a lot. I strongly doubt it got something to do with memory leaks. Usually I get it when wc3 tries to do something that it cannot do, like executing an invalid function (ExecuteFunc("something")) or moving units outside the map. I also get that error sometimes when I try do destroy a handle that is already destroyed, but not nulled.

Thanks for the info, I'll have to relook my entire map script to see if I did anything relating to the sort.
 
Level 2
Joined
Jun 25, 2006
Messages
18
Another case is an infinite loop which is threaded so as to avoid the op limit.

PS. I just skimmed over the script. CountUnitsInGroup is a lot slower than just checking FirstOfGroup(g) != null.

Thats a nice piece of advice, would it leak using it directly or do i have to set it to a variable first, like...

JASS:
set u = FirstOfGroup(g)
if u != null then
    DoActions()
endif
set u = null

Also regarding the crash and memory, I don't think it's a memory related issue, but maybe it is.. I have 8GB of RAM in my PC and I just got the crash today. It has to be an error in my script somewhere in the map, I'm still searching...
 
Level 21
Joined
Aug 21, 2005
Messages
3,699
Another case is an infinite loop which is threaded so as to avoid the op limit.

Perhaps, but the error reads "error at cmemblock.cpp, line 372". So it looks like it is related to memory allocation or de-allocation, that goes wrong... At least if cmemblock.cpp does have something to do with memory, which seems plausible.
The most obvious reason would be that you don't have enough RAM, but that doesn't seem to be the case.

If only we could see at cmemblock.cpp...
 
Status
Not open for further replies.
Top