| 03-18-2009, 02:07 AM | #1 |
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. 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 |
And continued.. 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 |
and continued... 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 |
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 |
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 |
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.. 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 | |
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:
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 |
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 | |
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. 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:
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 |
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. |
| 03-22-2009, 06:55 AM | #11 |
It means he needs to turn on his page file or get more ram |
| 03-22-2009, 09:01 AM | #12 |
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. JASS:"$ORDERID$" |
| 03-22-2009, 09:08 AM | #13 |
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 | ||
Quote:
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:
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 |
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. |
