-
Watch the master himself teach... Join us to learn how you can implement TypeScript into your Warcraft 3 projects tonight at 2000CET (UTC+1) on Twitch for the live recording of the first of many* Adventures in TypeScript.Dismiss Notice
-
Are you planning to upload your awesome spell or system to Hive? Please review the rules here.Dismiss Notice
-
Dismiss Notice
Awaken what lies in the heart of your swarm. The 17th Techtree Contest has arrived!
-
Dismiss Notice
The Hive Workshop is launching its first HD modelling contest. How HD should it be?
-
Dismiss Notice
Check out the Staff Job Openings thread.
Dismiss Notice
Hive 3 Remoosed BETA - NOW LIVE. Go check it out at BETA Hive Workshop! Post your feedback in this new forum BETA Feedback.
Dismiss Notice
60,000 passwords have been reset on July 8, 2019. If you cannot login, read this.
Tower Defence Toolkit (TDTK)
Submitted by
bigbull
Tower Defence Toolkit (TDTK) v0.2
Created by B1Gbull#2338
Info:
A simple and quick System to create your own Tower Defence game.
Note:
- Each path and each wave can be customized with the number of units, spawn intervals and unit types.
- The focus this System is on handling waves and paths.
- Towers and builders can be created as usual using the object editor.
- The system itself does not require any object editor data.
- Lua knowledge is an advantage but not necessary. For the basic functionality just copy paste some predefined lines, which are shown in the example.
- Player 24 (Peanut) is used for the creep waves. Therefore this player should not be used.
Example:
Lets create a very basic TD where every player has their own path.
To keep things simple, we limit the number of players to 2.
Both players have the same conditions and therefore get the same waves.
The paths that the creep should run are as follows:
Red Path: R1 > R2 > R3 > R4
Blue Path: B1 > B2 > B3 > B4
First create a new custom script in the trigger editor and copy paste the TDTK Code.
Create a second custom script which will be used for our own settings.
Create a function and name it whatever you want. In this example i use MyCustomMod().
I recommend calling the function on the Melee Initialization trigger.
-
Melee Initialization
-
Events
-
Map initialization
-
-
Conditions
-
Actions
-
Custom script: MyCustomMod()
-
-
Copy paste the following code inside your second custom script.
The creep waves will be defined later within the TDTK.Settings() function.
Code (Lua):
--Call this function on Map Init
function MyCustomMod()
--Starts the TDTK system
TDTK.Init()
--Overwrite the global TDTK.Settings() function
function TDTK.Settings()
--Your own settings ...
end
end
The steps are now as follows:
1. Create a WaveManager who tracks and handles all units and waves for you. Every path requires its own WaveManager
2. Add the regions to the WaveManager in the correct order.
3. Add waves of units
4. Add the accountable players who will be punished if a creep reaches the final destination
5. Repeat 1. for every path
Full code that includes 4 waves.
Code (Lua):
--Call this function on Map Init
function MyCustomMod()
--Initialize TDTK
TDTK.Init()
--Overwrite the global TDTK.Settings() function
function TDTK.Settings()
-- 1. Create a WaveManager for the red path
local wmRed = TDTK.WaveManager():Create()
-- 2. Add the regions in the correct order (R1 > R2 > R3 > R4)
-- We need at least 2 regions, the first one is the spawning region and the last one the final destination
wmRed:AddDestination("R1")
wmRed:AddDestination("R2")
wmRed:AddDestination("R3")
wmRed:AddDestination("R4")
-- 3. Adding unit Waves
-- You can get the UnitType Id by pressing STRG + D in the object editor
wmRed:AddWave("nban") -- Bandit (First Wave)
wmRed:AddWave("nkot") -- Kobold (Second Wave)
wmRed:AddWave("nomg") -- Ogre Mage (...)
wmRed:AddWave("nhrh") -- Harpy (Last Wave)
-- Four waves are enough. I think you got the idea
-- 4. Add the Player to the path
wmRed:AddPlayer(1) -- Player 1 (Red)
--------------------------------------------------------------------------------------------------
-- 5. Repeat for the blue path
-- 1. Create a WaveManager for the blue path
local wmBlue = TDTK.WaveManager():Create()
-- 2. Add the regions in the correct order (B1 > B2 > B3 > B4)
wmBlue:AddDestination("B1")
wmBlue:AddDestination("B2")
wmBlue:AddDestination("B3")
wmBlue:AddDestination("B4")
-- 3. Since the blue player should receive the same waves, we can simply copy the waves from the red manager
wmBlue:CopyWaves(wmRed)
-- 4. Add the second Player
wmBlue:AddPlayer(2) -- Player 2 (Blue)
end
end
Check out the Team example for a more advanced version in which individual waves are also customized.
-
First check out the Solo example for a more beginner friendly version where all steps are explained from the beginning.
In this example we create a Team version with 3 paths.
The red and blue paths are supposed to spawn the same units and the green path is used for the boss waves.
For the sake of simplicity, the first waves are the same as in the solo example.
Code (Lua):
--Call this function on Map Init
function MyCustomMod()
--Initialize TDTK
TDTK.Init()
--Overwrite the global TDTK.Settings() function
function TDTK.Settings()
-- 1. Create a WaveManager for the red path
local wmRed = TDTK.WaveManager():Create()
-- 2. Add the regions in the correct order (R1 > R4 > R5 > R6 > R7 > R10 > R11)
wmRed:AddDestination("R1") -- Spawn Region
wmRed:AddDestination("R4")
wmRed:AddDestination("R5")
wmRed:AddDestination("R6")
wmRed:AddDestination("R7")
wmRed:AddDestination("R10")
wmRed:AddDestination("R11") -- Final Destination
-- 3. Adding unit Waves
wmRed:AddWave("nban") -- Bandit
wmRed:AddWave("nkot") -- Kobold
wmRed:AddWave("nomg") -- Ogre
wmRed:AddWave("nhrh") -- Harpy
-- 4. Add both Player to the path
wmRed:AddPlayer(1)
wmRed:AddPlayer(2)
--------------------------------------------------------------------------------------------------
-- 5. Repeat the same for the blue path
local wmBlue = TDTK.WaveManager():Create()
wmBlue:AddDestination("R2")
wmBlue:AddDestination("R4")
wmBlue:AddDestination("R5")
wmBlue:AddDestination("R8")
wmBlue:AddDestination("R9")
wmBlue:AddDestination("R10")
wmBlue:AddDestination("R11")
-- Copy the waves from the red manager
wmBlue:CopyWaves(wmRed)
-- Since the blue path effects the same players, lets copy them from the red path
wmBlue:CopyPlayers(wmRed)
--------------------------------------------------------------------------------------------------
-- Now for the boss path
local wmGreen = TDTK.WaveManager():Create()
wmGreen:AddDestination("R3")
wmGreen:AddDestination("R11")
-- We want a boss to spawn at wave 4 so lets add 3 empty waves
wmGreen:AddWave(nil) -- Empty wave
wmGreen:AddWave(nil)
wmGreen:AddWave(nil)
-- We want to change the default settings for the boss wave
-- These settings are only for this specific wave. You can therefore set each wave individually.
local bossWave = wmGreen:AddWave("emtg") -- Mountain Giant
bossWave.count = 3 -- Only 3 units will spawn in total (default is 10)
bossWave.interval = 5 -- Units spawn every 5 seconds (default is 2 seconds)
bossWave.damage = 5 -- Every unit will remove 5 life from the player if it reach the final destination (default is 1)
-- Copy the players from the red path
wmGreen:CopyPlayers(wmRed)
--------------------------------------------------------------------------------------------------
-- Global Settings
TDTK.globals.startResource.gold = 200 -- All Player will start with 200 Gold (default is 0)
TDTK.globals.startResource.life = 30 -- All Player will have 30 life (default is 20)
TDTK.globals.wave.interval = 45 -- Every 45 seconds a new wave will start (default is 30)
TDTK.globals.wave.gold = 20 -- All Player gain 20 gold at the start of every wave (default is 0)
end
end
Optional texts that can be changed.
Code (Lua):
TDTK.globals.text.playerLoseLife = "${1} life remaining", -- Use ${1} to show the player life
TDTK.globals.text.newWave = "Wave ${1}/${2}", -- Use ${1} to show the current wave number and/or ${2} to show the last wave number
TDTK.globals.text.waveDialog = "Wave in:" -- =nil will remove the dialog
OPTIONAL: Events
Also goes into the MyCustomMod() function.
Code (Lua):
--Optional
function TDTK.AWaveBegins(number)
-- Is triggered at the start of every wave
end
--Optional
function TDTK.AUnitIsCreated(unitObj)
-- Is triggered when a creep has been created by the TDTK System
-- unitObj.this is the triggering unit
end
--Optional
function TDTK.AUnitDies(unitObj)
-- Is triggered when a creep (which belongs to the TDTK System) dies
-- unitObj.this is the triggering unit
end
--Optional
function TDTK.AUnitEntersFinalDestination(unitObj)
-- Is triggered when a creep (which belongs to the TDTK System) reaches the end
-- unitObj.this is the triggering unit
end
--Optional
function TDTK.APlayerCompletedAllWaves(playerObj)
-- Is triggered when a Player has complete all Waves
-- playerObj.this is the triggering player
end
--Optional
function TDTK.APlayerIsDefeat(playerObj)
-- Is triggered when a Player has lost all of his life
-- playerObj.this is the triggering player
end
OPTIONAL: Custom Units (Advanced)
In this example we create a unit that spawns other units on death, that will be registered by the TDTK System.
Also goes into the MyCustomMod() function.
You can name the new UnitType as you like. I used BreederType in this example.
Two functions can be overwritten:
Code (Lua):
UnitType:OnAlive() -- Is called when the unit is created
UnitType:OnDeath() -- Is called when the unit dies
Code (Lua):
-- Inherit from TDTK.UnitType
local BreederType = TDTK.UnitType():Create()
-- Create a BreederType Class
function BreederType:Create(parentId, childId)
local this = TDTK.UnitType():Create(FourCC(parentId))
-------------------------------------------------------------
--Here goes your custom values that will be use in OnAlive and OnDeath
this.childId = FourCC(childId) -- use FourCC to convert the string to id
this.count = 2 -- default creep count
-------------------------------------------------------------
self.__index = self
setmetatable(this, self)
return this
end
-- Override the OnDeath function (we dont need OnAlive in this example)
function BreederType:OnDeath(unitObj)
for i = 1, self.count, 1 do
local loc = GetUnitLoc(unitObj.this)
-- Create a creep and saves it in the TDTK database
local childObj = TDTK.Unit().NewUnit(loc, self.childId)
RemoveLocation(loc)
-- Copy all information from the parent creep (path, current destination). This information is also provided to the WaveManager
childObj:CopyState(unitObj)
-- Immediately order the creep to move to the current destination
childObj:Order()
end
end
-- Add the new UnitTypes to the TDTK System
-- Bandit will Spawn 2 Spiders on death
BreederType:Create("nban","nspb")
-- Ogre Mage will Spawn 1 Grunt on death
local unitType = BreederType:Create("nomg","ogru")
unitType.count = 1
TDTK Code
Code (Lua):
------------------------------------------------------------------------------------------------------
-- Tower Defence Toolkit (TDTK) v0.2
-- By B1Gbull#2338
------------------------------------------------------------------------------------------------------
TDTK = {}
-- Global values that can be changed inside the Settings() function
TDTK.globals = {
wave = {
interval = 30,
gold = 0
},
startResource = {
gold = 0,
lumber = 0,
life = 20
},
text = {
playerLoseLife = "${1} life remaining", -- Use ${1} to show the player life
newWave = "Wave ${1}/${2}", -- Use ${1} to show the current wave number and/or ${2} to show the last wave number
waveDialog = "Wave in:" -- =nil will remove the dialog
}
}
-- Global references
TDTK.collection = {
unit = {},
unitType = {},
region = {},
waveManager = {},
player = {},
timer = {}
}
local Event = {onEnterRegionTrigger}
local Util = {}
-- Classes
local Region = {}
TDTK.Region = function() return Region end
local WaveManager = {}
TDTK.WaveManager = function() return WaveManager end
local Wave = {}
TDTK.Wave = function() return Wave end
local WaveType = {}
TDTK.WaveType = function() return WaveType end
local Unit = {}
TDTK.Unit = function() return Unit end
local UnitType = {}
TDTK.UnitType = function() return UnitType end
local Timer = {}
TDTK.Timer = function() return Timer end
local User = {} -- Player is already taken
TDTK.Player = function() return User end
------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------
-- Global functions that can be overwritten
function TDTK.Settings()
Util.Error("Error: No Settings found")
end
function TDTK.AWaveBegins(number)
end
function TDTK.AUnitIsCreated(unitObj)
end
function TDTK.AUnitDies(unitObj)
end
function TDTK.AUnitEntersFinalDestination(unitObj)
end
function TDTK.APlayerCompletedAllWaves(playerObj)
end
function TDTK.APlayerIsDefeat(playerObj)
end
------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------
-- Initializes the system
TDTK.Init = function()
Event.onEnterRegionTrigger = CreateTrigger()
TriggerAddAction(Event.onEnterRegionTrigger, Event.OnEnterRegion)
local trig = CreateTrigger()
TriggerRegisterAnyUnitEventBJ(trig, EVENT_PLAYER_UNIT_DEATH)
TriggerAddAction(trig, Event.OnUnitDie)
local ExpiredFunc = function()
local t = TDTK.collection.timer[GetExpiredTimer()]
TDTK.Settings()
User.SetStartResource()
SetPlayerState(Player(23), PLAYER_STATE_GIVES_BOUNTY, 1)
Timer:Create(1, true, Unit.OrderIdle)
WaveManager.Execute()
t:Remove()
end
Timer:Create(0.01,false,ExpiredFunc)
end
------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------
--local trigger events that are used
Event.OnEnterRegion = function()
local unit = GetTriggerUnit()
local unitObj = Unit.Get(unit)
if unitObj ~= nil then
--Check if triggering region is the current destination of the unit
local region = unitObj:GetCurrentDestination()
if region.this == GetTriggeringRegion() then
--Set the next destination for triggering unit
unitObj:SetNextDestination()
if unitObj:GetCurrentDestination() ~= nil then
--Order unit to move to his destination
unitObj:Order()
else
--Final region reached
TDTK.AUnitEntersFinalDestination(unitObj)
unitObj:RemoveLife()
unitObj:Clear()
RemoveUnit(unitObj.this)
end
end
end
end
Event.OnUnitDie = function()
local unit = GetTriggerUnit()
local unitObj = Unit.Get(unit)
if unitObj ~= nil then
TDTK.AUnitDies(unitObj)
unitObj:OnDeath()
unitObj:Clear()
end
end
------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------
--Is used to track the triggering rect from GetTriggeringRegion()
function Region:Create(rect)
local this = {}
this.this = CreateRegion()
this.rect = rect
RegionAddRect(this.this, rect)
self.__index = self
setmetatable(this, self)
TDTK.collection.region[this.rect] = this
return this
end
------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------
--Contains all information about a path
function WaveManager:Create()
local this = {}
this.regions = {}
this.players = {}
this.waves = {}
this.enabled = true
self.__index = self
setmetatable(this, self)
TDTK.collection.waveManager[this] = this
return this
end
--Add a new Region as destination
function WaveManager:AddDestination(regionName)
local rectRegion = _G["gg_rct_"..tostring(regionName)]
if rectRegion then
local regionObj = TDTK.collection.region[rectRegion]
if regionObj == nil then
regionObj = Region:Create(rectRegion)
TriggerRegisterEnterRegionSimple(Event.onEnterRegionTrigger, regionObj.this)
end
table.insert(self.regions, regionObj)
return regionObj
else
return nil
end
end
function WaveManager:AddPlayer(number)
local playerNum = math.max(number-1,0)
playerNum = math.min(playerNum,23)
local player = Player(playerNum)
if GetPlayerSlotState(player) == PLAYER_SLOT_STATE_PLAYING and GetPlayerController(player) == MAP_CONTROL_USER then
local playerObj = User:Create(Player(playerNum))
playerObj.waveManagers[self] = self
self.players[player] = playerObj
table.insert(playerObj.waveManagers, self)
end
end
--Return the region by index. 1 = spawn region ...
function WaveManager:GetDestinationByIndex(index)
return self.regions[index]
end
--Return the wave by index. 1 = first wave ...
function WaveManager:GetWaveByIndex(index)
return self.waves[index]
end
function WaveManager:AddWave(unitCode)
local id = unitCode and FourCC(unitCode) or nil
local waveType = WaveType:Create(id)
local wave = Wave:Create(waveType,self)
table.insert(self.waves, wave)
return waveType
end
--Copy the waves from another WaveManager
function WaveManager:CopyWaves(waveManager)
for _, w in pairs(waveManager.waves) do
local wave = Wave:Create(w.waveType,self)
table.insert(self.waves, wave)
end
end
--Copy the player from another WaveManager
function WaveManager:CopyPlayers(waveManager)
self.players = waveManager.players
end
--Return the last wave number of the current WaveManager
function WaveManager:GetWaveCount()
local count = 0
for _ in pairs(self.waves) do
count = count + 1
end
return count
end
--Checks if the WaveManager has at least 2 destinations, 1 player and 1 wave
--Is Called once after Settings()
function WaveManager:Validate()
local count = 0
for _ in pairs(self.regions) do count = count + 1 end
if count < 2 or next(self.players) == nil or next(self.waves) == nil then
return false
end
return true
end
--Checks whether the manager has completed all waves
function WaveManager:IsComplete()
for _, w in pairs(self.waves) do
if w:IsComplete() == false then
return false
end
end
return true
end
function WaveManager:Disable()
self.enabled = false
end
--Starts all waves of all WaveManagers
--Called once after Settings()
WaveManager.Execute = function()
local ExpiredFunc = function()
local t = TDTK.collection.timer[GetExpiredTimer()]
local currentIndex = t.arg[1]
local lastIndex = t.arg[2]
local dialog = t.arg[3]
--Start new wave if the timer expire
for _, wm in pairs(TDTK.collection.waveManager) do
if wm.enabled then
local w = wm:GetWaveByIndex(currentIndex)
w:Execute()
end
end
--Add gold and send a message
local changeGold = TDTK.globals.wave.gold ~= 0 and true or false
for _, p in pairs(TDTK.collection.player) do
if TDTK.globals.text.newWave then
Util.WarningToPlayer(Util.EmbedToString(TDTK.globals.text.newWave,currentIndex,lastIndex),p.this)
end
if changeGold then
p:AddGold(TDTK.globals.wave.gold)
end
end
TDTK.AWaveBegins(currentIndex)
--Stops the timer if it is the last one
if currentIndex >= lastIndex then
if dialog then
DestroyTimerDialog(dialog)
end
t:Remove()
end
--Increase current wave index
t.arg[1] = t.arg[1] + 1
end
local lastIndex = 1
--Find out the last wave index
for _, wm in pairs(TDTK.collection.waveManager) do
if wm:Validate() then
local count = wm:GetWaveCount()
if lastIndex < count then
lastIndex = count
end
else
wm:Disable()
end
end
--Create the wave timer that starts all waves
local t = Timer:Create(TDTK.globals.wave.interval,true,ExpiredFunc,1,lastIndex)
if TDTK.globals.text.waveDialog then
t.arg[3] = CreateTimerDialogBJ(t.this, tostring(TDTK.globals.text.waveDialog))
end
end
------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------
--Stores the static wave information (like UnitType)
function WaveType:Create(unitId)
local this = {}
this.unitId = unitId
this.count = 10
this.interval = 2
this.damage = 1
self.__index = self
setmetatable(this, self)
return this
end
------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------
function Wave:Create(waveType,waveManager)
local this = {}
this.waveType = waveType
this.waveManager = waveManager
this.spawnCounter = 0
this.enabled = waveType.unitId ~= nil and true or false
this.units = {}
self.__index = self
setmetatable(this, self)
return this
end
--Starts the wave
--Is called when the global wave timer expire
function Wave:Execute()
if self.enabled then
self:Spawn()
local ExpiredFunc = function()
local t = TDTK.collection.timer[GetExpiredTimer()]
local wave = t.arg[1]
if wave.spawnCounter<wave.waveType.count then
wave:Spawn()
else
wave:Disable()
t:Remove()
end
end
Timer:Create(self.waveType.interval,true,ExpiredFunc,self)
end
end
--Creates a unit that belongs to the wave
--Is called when the internal spawn timer expire
function Wave:Spawn()
local loc = GetRandomLocInRect(self.waveManager.regions[1].rect)
local unitObj = Unit.NewUnit(loc, self.waveType.unitId)
RemoveLocation(loc)
self:AddUnit(unitObj)
self.spawnCounter = self.spawnCounter + 1
end
--Is called every time a unit die
function Wave:IsComplete()
-- enabled = false means it will no longer spawn untis
-- But there might be units alive, that belongs to this wave
if self.enabled == false and next(self.units) == nil then
return true
else
return false
end
end
function Wave:Disable()
self.enabled = false
end
--Called when a unit is created to link the unit to the wave
function Wave:AddUnit(unitObj)
unitObj.wave = self
self.units[unitObj.this] = unitObj
TDTK.AUnitIsCreated(unitObj)
unitObj:OnAlive()
end
------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------
-- Player
function User:Create(player)
local this = TDTK.collection.player[player]
if this == nil then
this = {}
this.this = player
this.life = 1
this.waveManagers = {}
self.__index = self
setmetatable(this, self)
TDTK.collection.player[player] = this
end
return this
end
--Set startig resource for all player
--Called once after Settings()
User.SetStartResource = function()
for _, p in pairs(TDTK.collection.player) do
if TDTK.globals.startResource.life > 0 then
p.life = TDTK.globals.startResource.life
end
if TDTK.globals.startResource.gold > 0 then
SetPlayerState(p.this, PLAYER_STATE_RESOURCE_GOLD, TDTK.globals.startResource.gold)
end
if TDTK.globals.startResource.lumber > 0 then
SetPlayerState(p.this, PLAYER_STATE_RESOURCE_LUMBER, TDTK.globals.startResource.lumber)
end
end
end
User.Get = function(player)
return TDTK.collection.player[player]
end
--Remove life from Player
--Is called when a unit enters final region
function User:Damage(value)
self.life = math.max(self.life - value, 0)
if TDTK.globals.text.playerLoseLife then
Util.WarningToPlayer(Util.EmbedToString(TDTK.globals.text.playerLoseLife,self.life),self.this)
end
if self.life <= 0 then
--Player is defeat
TDTK.APlayerIsDefeat(self)
self:Lose()
self:Clear()
end
end
--Player completed all waves
--Is called every time a unit dies
function User:IsComplete()
for _, wm in pairs(self.waveManagers) do
if wm:IsComplete() == false then
return false
end
end
return true
end
function User:Win()
CustomVictoryBJ(self.this, true, true)
end
function User:Lose()
CustomDefeatBJ(self.this, "Defeat!")
end
--Remove all references to prevent leaks
--Called after a player win or lose
function User:Clear()
for _, wm in pairs(self.waveManagers) do
wm.players[self.this] = nil
--TDTK.collection.player[self.this] = nil
if next(wm.players) == nil then
wm.Disable()
end
end
end
function User:AddGold(value)
local gold = GetPlayerState(self.this, PLAYER_STATE_RESOURCE_GOLD)
SetPlayerState(self.this, PLAYER_STATE_RESOURCE_GOLD, gold+value)
end
function User:AddLumber(value)
local lumber = GetPlayerState(self.this, PLAYER_STATE_RESOURCE_LUMBER)
SetPlayerState(self.this, PLAYER_STATE_RESOURCE_LUMBER,lumber+value)
end
------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------
--Base class to inherit from
--See custom unit tutorial
function UnitType:Create(id)
local this = TDTK.collection.unitType[id]
if this == nil then
this = {}
this.id = id
self.__index = self
setmetatable(this, self)
if id ~= nil then TDTK.collection.unitType[id] = this end
end
return this
end
function UnitType:OnAlive(unitObj) end
function UnitType:OnDeath(unitObj) end
------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------
function Unit:Create(unit)
local this = TDTK.collection.unit[unit]
if this == nil then
this = {}
this.this = unit
this.typeObj = UnitType:Create(GetUnitTypeId(unit))
this.wave = nil
this.destinationIndex = 1
self.__index = self
setmetatable(this, self)
TDTK.collection.unit[unit] = this
end
return this
end
function Unit:OnAlive()
self.typeObj:OnAlive(self)
end
function Unit:OnDeath()
self.typeObj:OnDeath(self)
end
Unit.Get = function(unit)
return TDTK.collection.unit[unit]
end
--Remove all references to prevent leaks
--Called after a unit dies or before is removed
function Unit:Clear()
self.wave.units[self.this] = nil
TDTK.collection.unit[self.this] = nil
local players = self.wave.waveManager.players
for _, p in pairs(players) do
if p:IsComplete() and p.life > 0 then
TDTK.APlayerCompletedAllWaves(p)
p:Win()
p:Clear()
end
end
end
--Is used to create units for the TDTK System
Unit.NewUnit = function(loc, id)
local unit = CreateUnitAtLoc(Player(23), id, loc, bj_UNIT_FACING)
local unitObj = Unit:Create(unit)
return unitObj
end
--Transfers all information from one to another unit
--See custom unit tutorial
function Unit:CopyState(unitObj)
self.destinationIndex = unitObj.destinationIndex
unitObj.wave:AddUnit(self)
end
--Reorder idle units.
--Is called every 1 sec
Unit.OrderIdle = function()
for _, unitObj in pairs(TDTK.collection.unit) do
if GetUnitCurrentOrder(unitObj.this) == 0 then
unitObj:Order()
end
end
end
--Order the unit to his current destination
--Is called if the unit reaches his destination or is idle
function Unit:Order()
region = self:GetCurrentDestination()
if region ~= nil then
local loc = GetRectCenter(region.rect)
IssuePointOrderLoc(self.this, "move", loc)
RemoveLocation(loc)
end
end
--Gives the unit the next destination
--Is called if the unit has enter his destination region
function Unit:SetNextDestination()
self.destinationIndex=self.destinationIndex+1
end
function Unit:GetPlayers()
return self.wave.waveManager.players
end
function Unit:GetCurrentDestination()
return self.wave.waveManager:GetDestinationByIndex(self.destinationIndex)
end
--Is Called when the unit reaches the final destination
function Unit:RemoveLife()
for _, p in pairs(self.wave.waveManager.players) do
p:Damage(self.wave.waveType.damage)
end
end
------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------
function Timer:Create(duration,isPeriodic,ExpiredFunc,...)
local this = {}
this.this = CreateTimer()
this.arg = {...}
self.__index = self
setmetatable(this, self)
TDTK.collection.timer[this.this] = this
TimerStart(this.this, duration, isPeriodic, ExpiredFunc)
return this
end
function Timer:Remove()
DestroyTimer(self.this)
TDTK.collection.timer[self] = nil
end
------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------
Util.WarningToPlayer = function(text,player)
DisplayTimedTextToPlayer(player, 0, 0, 2, "|cffe6be00"..tostring(text).."|r")
local sound = "Sound\\Interface\\Error.flac"
if GetLocalPlayer() ~= player then
sound = ""
end
PlaySound(sound)
end
Util.Error = function(text)
DisplayTextToForce( GetPlayersAll(),"|cffff0000"..tostring(text).."|r" )
end
Util.EmbedToString = function(txt,...)
local arg = {...}
local newTxt = tostring(txt)
for i, var in ipairs(arg) do
newTxt = string.gsub(newTxt,[[${]]..tostring(i)..[[}]], tostring(var))
end
return newTxt
end
Change Log
Version 0.1
- Initial release
Version 0.2
- Readability of the code improved
- Unified embedded text var's: from #life, #number, #last to ${1}, ${2} ...
Contents