//TESH.scrollpos=0
//TESH.alwaysfold=0
Name | Type | is_array | initial_value |
TempPoint | location | No |
//TESH.scrollpos=0
//TESH.alwaysfold=0
//Importing these systems is very easy.
//Just copy the triggers "IndexStoring", "AggroSystem" and "OOCSystem" into your map.
//You should configurate the values in the triggers, but all is explained
//behind the variables :)
//
//Please remember to give credits if you use it to:
//
//PandaMine (from wc3campaigns.net) for the HSAS. The IndexStoring system is created on his idea
//Dr Super Good (from hiveworkshop.com) for his help considering the performance (showed me HSAS :P)
//Me (Justify), you may guess for what :P
//TESH.scrollpos=28
//TESH.alwaysfold=0
//Index storing system, helps to keep track of data and is very fast.
//Credits go to PandaMine for his HSAS since this is nearly HSAS
//(Just without some features I didn't need and with the initialization).
//! textmacro Indexes takes NAME
library Index$NAME$Storage initializer Init$NAME$Storage
globals
private constant integer HandleMinimum = 0x100000
private constant integer Amount = 65536
private integer array $NAME$Indexes [Amount]
endglobals
function Set$NAME$Index takes unit u, integer index returns nothing
set $NAME$Indexes[GetHandleId(u)-HandleMinimum] = index
endfunction
function Get$NAME$Index takes unit u returns integer
return $NAME$Indexes[GetHandleId(u)-HandleMinimum]
endfunction
private function Init$NAME$Values takes nothing returns nothing
set $NAME$Indexes[GetHandleId(GetTriggerUnit())-HandleMinimum] = -1
endfunction
//Initializing the array since GetIndex will be called before AddIndex
//The index has to be initialized with -1, since 0 is a possible value
private function Init$NAME$Storage takes nothing returns nothing
local trigger t = CreateTrigger()
local region r = CreateRegion()
local rect bounds = GetWorldBounds()
local group g = CreateGroup()
local unit u
call RegionAddRect(r, bounds)
call TriggerRegisterEnterRegion(t, r, null)
call TriggerAddAction(t, function Init$NAME$Values)
call GroupEnumUnitsInRect(g, bounds, null)
loop
set u = FirstOfGroup(g)
exitwhen u ==null
set $NAME$Indexes[GetHandleId(u)-HandleMinimum] = -1
call GroupRemoveUnit(g, u)
endloop
call RemoveRegion(r)
call RemoveRect(bounds)
call DestroyGroup(g)
set t = null
set r = null
set bounds = null
set g = null
endfunction
endlibrary
//! endtextmacro
//TESH.scrollpos=104
//TESH.alwaysfold=0
//! runtextmacro Indexes("Aggro")
library AggroSystem requires IndexAggroStorage
private keyword AggroStruct
globals
private constant integer MAXUNITSPERSTRUCT = 20 //Maximum number of attacked units at the same time
private constant integer MAXSTRUCTS = 100 //Maximum number of units attacking a single unit at the same time
private constant real AGGROCHANGE = 1.2 //In this case a units needs 20% more aggro to be attacked
private AggroStruct array AggroData [MAXSTRUCTS]//Array to store the structs
private integer StructCount = 0 //Need to count the structs. Without this integer we would have to loop through them.
endglobals
private struct AggroStruct [MAXSTRUCTS]
unit Unit //Attacked unit
unit array AggroUnit[MAXUNITSPERSTRUCT] //All attacking units
real array AggroAmount[MAXUNITSPERSTRUCT] //The "aggro" of the attackers
integer UnitsSaved //Amount of attackers to avoid countloops
integer MaxAggro //ID of the attacker with the most aggro
trigger UnitDeath //Trigger to clear the structs of dying units
triggeraction TrigAction //Triggeractions leak ;)
static method create takes unit Target, unit Source, real Amount returns AggroStruct
local AggroStruct Data = AggroStruct.allocate()
local integer i = 1 //0 will be set with the attacker
//Initialize the struct
loop
exitwhen i >= MAXUNITSPERSTRUCT
set Data.AggroAmount[i] = 0
set Data.AggroUnit[i] = null
set i = i+1
endloop
set Data.Unit = Target
set Data.AggroUnit[0] = Source
set Data.AggroAmount[0] = Amount
set Data.UnitsSaved = 1
set Data.MaxAggro = 0
set Data.UnitDeath = CreateTrigger()
call TriggerRegisterUnitEvent(Data.UnitDeath, Target, EVENT_UNIT_DEATH)
set Data.TrigAction = TriggerAddAction(Data.UnitDeath, function AggroStruct.Destroy)
call IssueTargetOrder(Target, "attack", Source)
return Data
endmethod
//Struct destroy function for the UnitDeath trigger
static method Destroy takes nothing returns nothing
local integer index = GetAggroIndex(GetTriggerUnit())
local AggroStruct Data = AggroData[index]
set StructCount = StructCount-1
set AggroData[index] = AggroData[StructCount]
set AggroData[StructCount] = 0
call Data.destroy()
endmethod
method onDestroy takes nothing returns nothing
//Remove the leaks
local integer i = 0
set .Unit = null
loop
exitwhen i >= .UnitsSaved
set .AggroUnit[i] = null
set i = i+1
endloop
call TriggerRemoveAction(.UnitDeath,.TrigAction)
set .TrigAction = null
call DestroyTrigger(.UnitDeath)
set .UnitDeath = null
endmethod
endstruct
//Function to get the aggro of a unit on another one
function GetUnitAggro takes unit Target, unit Source returns real
local AggroStruct Data = AggroData[GetAggroIndex(Target)]
local integer i = 0
//Loop through the attackers
set i = 0
loop
exitwhen Data.AggroUnit[i] == Source or i >= MAXUNITSPERSTRUCT-1
set i = i+1
endloop
//i is smaller then MAXUNITSPERSTRUCT-1 if any unit was found
if i < MAXUNITSPERSTRUCT-1 then
return Data.AggroAmount[i]
else
//No unit found
return 0.
endif
endfunction
//Manual reset, used by the OOC System
function ResetUnitAggro takes unit Target returns nothing
local integer index = GetAggroIndex(Target)
local AggroStruct Data = AggroData[index]
set StructCount = StructCount-1
set AggroData[index] = AggroData[StructCount]
set AggroData[StructCount] = 0
call Data.destroy()
endfunction
function AddUnitAggro takes unit Target, unit Source, real Amount returns nothing
local integer index = GetAggroIndex(Target)
local integer i = 0
local integer j = 0 //An index to find the maximum aggro
//If a new struct is created j is "0" because the first attacking unit
//is saved in "0" while it's the only one.
//If the struct already exists and the unit is found in the loop, the maximum aggro-check
//has to compare the old "leader" with the unit on the index.
//If the struct already exists but the unit wasn't found, the index of the unit will
//be the amount of saved attackers, so j will have this value.
local AggroStruct Data
//Can be read in the IndexSaving trigger. Since 0 is a possible value for the index, uninitialized indexes start at -1
if index == -1 then
set Data = AggroStruct.create(Target, Source, Amount)
call SetAggroIndex(Target, StructCount)
set AggroData[StructCount] = Data
set StructCount = StructCount+1
else
set Data = AggroData[index]
set i = 0
//Damn, still found no way to get the attacker faster then looping through.
//The index system would be quite slow in this case, since I would need to
//create an array inside the struct with a size of more then 10.000
//(like in the AggroStorage lib, 65536). This would be possible, but slows down the struct.
loop
exitwhen Data.AggroUnit[i] == Source or Data.AggroUnit[i] == null
set i = i+1
endloop
//All units are initialized with "null", so we can check for it
//to find the first free slot.
if Data.AggroUnit[i] == null then
set j = Data.UnitsSaved
set Data.AggroUnit[Data.UnitsSaved] = Source
set Data.AggroAmount[Data.UnitsSaved] = Amount
set Data.UnitsSaved = Data.UnitsSaved+1
else
set j = i
set Data.AggroAmount[i] = Data.AggroAmount[i]+Amount
endif
endif
//Oh, we shouldn't forget to check if all units are alive.
//An attack order on a dead unit is quite pointless.
set i = 0
loop
exitwhen i >= Data.UnitsSaved
if GetWidgetLife(Data.AggroUnit[i]) <= 0.405 then
set Data.AggroUnit[i] = Data.AggroUnit[Data.UnitsSaved]
set Data.AggroAmount[i] = Data.AggroAmount[Data.UnitsSaved]
set Data.AggroUnit[Data.UnitsSaved] = null
set Data.AggroAmount[Data.UnitsSaved] = 0
set Data.UnitsSaved = Data.UnitsSaved-1
endif
set i = i+1
endloop
//In the end we have the most important part of the system although this codepiece is so short.
//We need to check if the last attacker "created more aggro" then the "leader" before.
if Data.AggroAmount[j] >= Data.AggroAmount[Data.MaxAggro]*AGGROCHANGE then
set Data.MaxAggro = j
call IssueTargetOrder(Target, "attack", Source)
endif
endfunction
endlibrary
//TESH.scrollpos=147
//TESH.alwaysfold=0
//! runtextmacro Indexes("OOC")
scope OOCSystem initializer InitSystem
private keyword OOCStruct
globals
private constant player Enemy = Player(PLAYER_NEUTRAL_AGGRESSIVE) //The player for that the system should work
private constant real Period = 0.2 //Period of the timer that runs the whole system
private constant real OOCTime = 10. //Time in seconds until the unit is sent back
private timer T = CreateTimer() //This timer loops through all units an increased their ooc-time
private OOCStruct array OOCData //A struct arrax to keep track of the times
private integer StructCount = 0 //An integer to count the struct
endglobals
private struct OOCStruct
unit Unit
real Dur
real X
real Y
trigger UnitDeath
triggeraction TrigAction
static method create takes unit u returns OOCStruct
local OOCStruct Data = OOCStruct.allocate()
set Data.Unit = u
set Data.Dur = 0
set Data.X = GetUnitX(u)
set Data.Y = GetUnitY(u)
set Data.UnitDeath = CreateTrigger()
call TriggerRegisterUnitEvent(Data.UnitDeath, u, EVENT_UNIT_DEATH)
set Data.TrigAction = TriggerAddAction(Data.UnitDeath, function OOCStruct.Destroy)
if StructCount == 0 then
call TimerStart(T, Period, true, function OOCStruct.Duration)
endif
return Data
endmethod
//This method check if the given player is a) not a computer and b) playing
static method IsPlayer takes player p returns boolean
local boolean b = false
local integer i = 0
loop
exitwhen i >= bj_MAX_PLAYER_SLOTS
if p == Player(i) and GetPlayerSlotState(p) == PLAYER_SLOT_STATE_PLAYING then
set b = true
endif
set i = i+1
endloop
return b
endmethod
static method OOCCondition takes nothing returns boolean
local boolean b = false
local player p
local player p2
set p = GetOwningPlayer(GetTriggerUnit())
if GetTriggerEventId() == EVENT_PLAYER_UNIT_SPELL_EFFECT then
set p2 = GetOwningPlayer(GetSpellTargetUnit())
else
set p2 = GetOwningPlayer(GetAttacker())
endif
if (p == Enemy and OOCStruct.IsPlayer(p2)) or (p2 == Enemy and OOCStruct.IsPlayer(p)) then
set b = true
endif
set p = null
set p2 = null
return b
endmethod
//Struct destroy function for the UnitDeath trigger
static method Destroy takes nothing returns nothing
local integer index = GetOOCIndex(GetTriggerUnit())
local OOCStruct Data = OOCData[index]
set StructCount = StructCount-1
set OOCData[index] = OOCData[StructCount]
set OOCData[StructCount] = 0
call Data.destroy()
endmethod
//Manual destruction of the structs
static method Reset takes unit Death returns nothing
local integer index = GetOOCIndex(Death)
local OOCStruct Data = OOCData[index]
set StructCount = StructCount-1
set OOCData[index] = OOCData[StructCount]
set OOCData[StructCount] = 0
call Data.destroy()
endmethod
//Increased a real value that counts the out of combat time
static method Duration takes nothing returns nothing
local OOCStruct Data
local integer i = 0
loop
exitwhen i >= StructCount
set Data = OOCData[i]
set Data.Dur = Data.Dur+Period
if Data.Dur>= OOCTime then
call OOCStruct.Reset(Data.Unit)
call ResetUnitAggro(Data.Unit)
endif
set i = i+1
endloop
endmethod
static method Add takes nothing returns nothing
local unit u = GetTriggerUnit()
local player p = GetOwningPlayer(u)
local OOCStruct Data
local integer id = GetOOCIndex(u)
local boolean b = OOCStruct.IsPlayer(p)
set p = null
//GetSpellTargetUnit() will return "null" if the event was an attack, you need to check this
if b then
if GetTriggerEventId() == EVENT_PLAYER_UNIT_SPELL_EFFECT then
set u = GetSpellTargetUnit()
else
set u = GetAttacker()
endif
endif
if id == -1 then
set Data = OOCStruct.create(u)
set OOCData[StructCount] = Data
set StructCount = StructCount+1
else
set Data = OOCData[id]
set Data.Dur = 0
endif
set u = null
endmethod
method onDestroy takes nothing returns nothing
call IssuePointOrder(.Unit, "move", .X, .Y)
set .Unit = null
call TriggerRemoveAction(.UnitDeath, .TrigAction)
set .TrigAction = null
call DestroyTrigger(.UnitDeath)
set .UnitDeath = null
set StructCount = StructCount-1
if StructCount == 0 then
call PauseTimer(T)
endif
endmethod
endstruct
private function InitSystem takes nothing returns nothing
local trigger t = CreateTrigger()
local integer i = 0
loop
exitwhen i >= bj_MAX_PLAYER_SLOTS
if GetPlayerSlotState(Player(i)) == PLAYER_SLOT_STATE_PLAYING then
call TriggerRegisterPlayerUnitEvent(t, Player(i), EVENT_PLAYER_UNIT_ATTACKED, null)
call TriggerRegisterPlayerUnitEvent(t, Player(i), EVENT_PLAYER_UNIT_SPELL_EFFECT, null)
endif
set i = i + 1
endloop
call TriggerAddCondition(t, Condition(function OOCStruct.OOCCondition))
call TriggerAddAction(t, function OOCStruct.Add)
set t = null
endfunction
endscope