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
Section B - The Master TowerCast Struct
Section C - Creating a Timer for a Constructed Tower
Section D - Additional Code for Select Towers
Section E - The Init function
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.
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
Last edited: