//TESH.scrollpos=0
//TESH.alwaysfold=0
Name | Type | is_array | initial_value |
GDD__Integers | integer | Yes | |
GDD__LeftMapGroup | group | No | |
GDD__TriggerArray | trigger | Yes | |
GDD__UnitArray | unit | Yes | |
GDD_Damage | real | No | |
GDD_DamagedUnit | unit | No | |
GDD_DamageSource | unit | No | |
GDD_Event | real | No |
/*
Library management
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
1. Declare a new unit
| static method add takes integer unitId returns nothing
2. Assign behavior datas
| static method operator (Aspect name)= takes type value returns nothing
3. Conclude declarations
| static method end takes nothing returns nothing
4. Get unit's index at library
| static method getIndex takes integer unitId returns integer
(check DemoLib trigger for example)
Aspects:
Name Type Description
¯¯¯¯ ¯¯¯¯ ¯¯¯¯¯¯¯¯¯¯¯
"maxHelp" "integer" Maximum number of helpers for the creep.
"helpType" "integer" 0 = doesn't help 1 = helps same id only 2 = helps any other creep
"seekHelp" "boolean" If true, it will look for nearby aid when low on health.
"callHelp" "boolean" If true, it can call for helps from nearby creeps while in combat. The response depends on "helpType" of nearby creeps.
"seekRadius" "real" Maximum seek help radius
"callRadius" "real" Maximum call help radius
"lowHealth" "real" Low health state parameter before the creep starts looking for nearby aid.
"canFlee" "boolean" Creep will flee when taking damage.
"fleeDist" "real" Maximum distance from nest area to flee.
"nestArea" "real" Nest area AoE (can be treated as "maximum wander distance").
"wanderDist" "real" Minimum wander distance.
"wanderDelay" "real" Basic delay before performing another wander.
"wanderVariant" "real" Vairant of wander delay (makes the delay random).
"isTamed" "boolean" Tamed creeps won't attack its opponents.
"sleepTime" "real" Time of day at which the creep will start sleeping.
"sightRadius" "real" Maximum sight radius (in degree)
"sleepDur" "real" Sleep duration (in-game hour). 3.5 = 3 hours 30 minutes (0.5*60 minutes = 30).
"acquisitionRange" "real" Target detection range.
"ambushDelay" "real" Delay before ambushing threat.
"combatDuration" "real" How long the creep will stay at combat mode.
"resetTimer" "boolean" Reset combat timer on each attack. If false, the creep may stop attacking when combat state timer expires (depends on "combatDuration").
API
¯¯¯
A. Trigger Events
function TriggerRegisterCreepEvent takes trigger t, integer whichEvent returns nothing
Events:
1. Fires when a creep starts sleeping
| EVENT_CREEP_SLEEP
2. Fires when a creep is awake
| EVENT_CREEP_AWAKE
3. Fires when a creep is ordered to wander
| EVENT_CREEP_WANDER
4. Fires when a creep is ordered to flee
| EVENT_CREEP_FLEE
5. Fires when a creep is threated
| EVENT_CREEP_THREAT
6. Fires when a creep is unthreated
| EVENT_CREEP_UNTHREAT
7. Fires when a creep is ordered to attack the threat
| EVENT_CREEP_ATTACK
8. Fires when a creep is ordered to stop attacking the threat
| EVENT_CREEP_UNATTACK
9. Fires when a creep is calling for helps
| EVENT_CREEP_CALL_HELP
10. Fires when a creep is seeking for helps
| EVENT_CREEP_SEEK_HELP
11. Fires when a creep is ordered to help the other
| EVENT_CREEP_GIVE_HELP
12. Fires when a creep is on low health state
| EVENT_CREEP_LOW_HEALTH
13. Fires when a creep deals any damage
| EVENT_CREEP_DEAL_DAMAGE
14. Fires when a creep takes any damage
| EVENT_CREEP_TAKE_DAMAGE
B. Event Responses
1. Returns wandering creep, attacking creep, helping creep, etc..
| function GetTriggerCreep takes nothing returns unit
2. Returns threating unit
| function GetTriggerThreat takes nothing returns unit
C. Miscs
1. Check if a creep is sleeping or not
| function IsCreepSleeping takes unit u returns boolean
2. Check if a creep is moving or not
| function IsCreepMoving takes unit u returns boolean
3. Get creeps nest x coordinate
| function GetCreepNestX takes unit u returns real
4. Get creeps nest y coordinate
| function GetCreepNestY takes unit u returns real
5. Register a generator units
| function SetGenUnit takes unit u, boolean b returns nothing
6. Register a unit to the engine
| function EnableCreepBehavior takes unit u, boolean b returns nothing
*/
library RPGTS initializer onInit uses GetClosestWidget, Table, UnitIndexer, optional TimerUtils
/************************************************************************************
* *
* RPG Threat System v3.2c *
* ***** *
* by: Quilnez *
* *
* This system generates special behavior to creep units. Gives some brains to *
* those shrimp-head creeps. They can flee, they have artificial sight area *
* (you can sneak behind them), they are able to help each other, they become *
* aggressive or not aggressive as well. And many more! *
* *
* External dependencies: *
* (required) *
* - Table *
* - UnitIndexer *
* - GetClosestWidget *
* - Any DDS *
* (optional) *
* - TimerUtils *
* *
* Implementation: *
* - Copy paste RPG Threat System folder into your map *
* - Give two player slots for passive and aggresive player, better just *
* leave'em as empty player slot *
* - Configure the system & start defining your creep behavior *
* - Done. *
* *
* Credits: *
* - GDD by Weep *
* - GetClosestWidget by Bannar *
* - UnitIndexer by Nestharus *
* - TimerUtils by Vexorian *
* - Table by Bribe *
* *
* (You can read more info at READ ME trigger) *
* *
*************************************************************************************
* *
* CONFIGURATION */
globals
// Owner of passive creeps
private constant player PASSIVE = Player(10)
// Owner of active creeps
private constant player AGGRESSIVE = Player(11)
// If true, creeps will use PASSIVE's player color
private constant boolean COLOR = true
// Behavior generation area (distance from generator units)
private constant real DISTANCE = 2400.0
// Created sfx when creeps are sleeping
private constant string SLEEP_SFX = "Abilities\\Spells\\Undead\\Sleep\\SleepTarget.mdl"
private constant string SLEEP_SFX_PT = "overhead"
// Order Ids
private constant integer ATTACK_ORDER_ID = 851983
private constant integer MOVE_ORDER_ID = 851986
private constant integer STOP_ORDER_ID = 851972
// Total seconds in a day (can be found in gameplay constants)
private constant real SECONDS_IN_A_DAY = 480
// If true, all units owned by PASSIVE and AGGRESSIVE will be
// registered automatically on unit indexing event
private constant boolean AUTO_REGISTER = true
// Just leave it alone
private constant real INTERVAL = 0.03125
// Don't touch this
private group CreepGroup = CreateGroup()
endglobals
// Damage event of your DDS
private function addEvent takes trigger t returns nothing
call TriggerRegisterVariableEvent(t, "udg_GDD_Event", EQUAL, 0.0)
endfunction
// Damage source of your DDS
private constant function getSource takes nothing returns unit
return udg_GDD_DamageSource
endfunction
// Damage target of your DDS
private constant function getTarget takes nothing returns unit
return udg_GDD_DamagedUnit
endfunction
// Set classifications of affected creeps
private function creepFilter takes unit u returns boolean
// For safety, just keep this format
if IsUnitType(u, UNIT_TYPE_HERO) or IsUnitType(u, UNIT_TYPE_STRUCTURE) then
return false
endif
/* END OF CONFIGURATION *
* *
************************************************************************************/
if IsUnitInGroup(u, CreepGroup) then
return false
endif
call GroupAddUnit(CreepGroup, u)
return true
endfunction
struct SCDataLib
private static thistype Dex
private static Table LibIndex
//! textmacro CREEP_DATA_TYPE takes VAR_NAME,VAL_TYPE
readonly $VAL_TYPE$ $VAR_NAME$Var
static method operator $VAR_NAME$= takes $VAL_TYPE$ value returns nothing
set Dex.$VAR_NAME$Var = value
endmethod
//! endtextmacro
//! runtextmacro CREEP_DATA_TYPE( "maxHelp", "integer" )
//! runtextmacro CREEP_DATA_TYPE( "helpType", "integer" )
//! runtextmacro CREEP_DATA_TYPE( "resetTimer", "boolean" )
//! runtextmacro CREEP_DATA_TYPE( "canFlee", "boolean" )
//! runtextmacro CREEP_DATA_TYPE( "isTamed", "boolean" )
//! runtextmacro CREEP_DATA_TYPE( "seekHelp", "boolean" )
//! runtextmacro CREEP_DATA_TYPE( "callHelp", "boolean" )
//! runtextmacro CREEP_DATA_TYPE( "seekRadius", "real" )
//! runtextmacro CREEP_DATA_TYPE( "callRadius", "real" )
//! runtextmacro CREEP_DATA_TYPE( "sightRadius", "real" )
//! runtextmacro CREEP_DATA_TYPE( "sleepTime", "real" )
//! runtextmacro CREEP_DATA_TYPE( "sleepDur", "real" )
//! runtextmacro CREEP_DATA_TYPE( "lowHealth", "real" )
//! runtextmacro CREEP_DATA_TYPE( "acquisitionRange","real" )
//! runtextmacro CREEP_DATA_TYPE( "ambushDelay", "real" )
//! runtextmacro CREEP_DATA_TYPE( "combatDuration", "real" )
//! runtextmacro CREEP_DATA_TYPE( "wanderDelay", "real" )
//! runtextmacro CREEP_DATA_TYPE( "wanderVariant", "real" )
//! runtextmacro CREEP_DATA_TYPE( "wanderDist", "real" )
//! runtextmacro CREEP_DATA_TYPE( "fleeDist", "real" )
//! runtextmacro CREEP_DATA_TYPE( "nestArea", "real" )
static method getIndex takes integer unitId returns integer
return LibIndex.integer[unitId]
endmethod
static method add takes integer unitId returns nothing
if LibIndex.integer[unitId] == 0 then
set Dex = allocate()
set LibIndex.integer[unitId] = Dex
endif
endmethod
static method end takes nothing returns nothing
set Dex = 0
endmethod
static method onInit takes nothing returns nothing
set LibIndex = Table.create()
endmethod
endstruct
// Container for creep data
private struct CreepData
boolean isSleep
integer helpCount
effect sleepSfx
unit target
timer sleepTimer
real wanderDelay
real nestX
real nestY
real combatDur
real combatDly
real unitX
real unitY
endstruct
globals
private boolean Move = false
private group PickGroup = CreateGroup()
private group TempGroup = CreateGroup()
private integer DummyType
private integer Total = -1
private integer GenTotal = 0
private integer array GenDex
private real array Real
private timer Timer = CreateTimer()
private trigger array Handler
private unit TempUnit
private unit EventUnit = null
private unit EventThreat = null
private unit array GenUnit
private CreepData array Data
// Real array indexes
private constant integer R_X = 0
private constant integer R_Y = 1
private constant integer R_TOD = 2
private constant integer R_WAKE = 3
private constant integer R_DISTANCE = 4
private constant integer R_FACING = 5
private constant integer R_DURATION = 6
// Event constants
constant integer EVENT_CREEP_SLEEP = 0
constant integer EVENT_CREEP_AWAKE = 1
constant integer EVENT_CREEP_WANDER = 2
constant integer EVENT_CREEP_FLEE = 3
constant integer EVENT_CREEP_THREAT = 4
constant integer EVENT_CREEP_UNTHREAT = 5
constant integer EVENT_CREEP_ATTACK = 6
constant integer EVENT_CREEP_CALL_HELP = 7
constant integer EVENT_CREEP_SEEK_HELP = 8
constant integer EVENT_CREEP_GIVE_HELP = 9
constant integer EVENT_CREEP_LOW_HEALTH = 10
constant integer EVENT_CREEP_DEAL_DAMAGE = 11
constant integer EVENT_CREEP_TAKE_DAMAGE = 12
constant integer EVENT_CREEP_UNATTACK = 13
endglobals
native UnitAlive takes unit id returns boolean
// API functions
function SetGenUnit takes unit u, boolean b returns nothing
local integer uDex = GetUnitUserData(u)
if b then
if GenDex[uDex] == 0 then
set GenTotal = GenTotal + 1
set GenUnit[GenTotal] = u
set GenDex[uDex] = GenTotal
endif
elseif GenDex[uDex] > 0 then
set GenDex[GetUnitUserData(GenUnit[GenTotal])] = GenDex[uDex]
set GenUnit[GenDex[uDex]] = GenUnit[GenTotal]
set GenUnit[GenTotal] = null
set GenTotal = GenTotal - 1
set GenDex[uDex] = 0
endif
endfunction
function AddCreepEventHandler takes integer whichEvent, boolexpr func returns nothing
call TriggerAddCondition(Handler[whichEvent], func)
endfunction
function GetTriggerCreep takes nothing returns unit
return EventUnit
endfunction
function GetTriggerThreat takes nothing returns unit
return EventThreat
endfunction
function GetCreepNestX takes unit u returns real
return Data[GetUnitUserData(u)].nestX
endfunction
function GetCreepNestY takes unit u returns real
return Data[GetUnitUserData(u)].nestY
endfunction
function IsCreepSleeping takes unit u returns boolean
return Data[GetUnitUserData(u)].isSleep
endfunction
function IsCreepMoving takes unit u returns boolean
local integer uDex = GetUnitUserData(u)
return GetUnitCurrentOrder(u) == MOVE_ORDER_ID or GetUnitX(u) != Data[uDex].unitX or GetUnitY(u) != Data[uDex].unitY
endfunction
// Get distance between points
private function getDistance takes real x, real y, real xt, real yt returns real
return SquareRoot((xt-x)*(xt-x)+(yt-y)*(yt-y))
endfunction
// Get angle between points
private function getAngle takes real x, real y, real xt, real yt returns real
return Atan2(yt-y, xt-x) * bj_RADTODEG
endfunction
// Get circular difference between two angles
private function circularDifference takes real a, real b returns real
local real r = RAbsBJ(a-b)
if r <= 180 then
return r
else
return 180 - r
endif
endfunction
// Filter enemy units around creep
private function targetFilter takes nothing returns boolean
local unit u = GetFilterUnit()
local player p = GetOwningPlayer(u)
local SCDataLib lDex = SCDataLib.getIndex(GetUnitTypeId(TempUnit))
local real a = getAngle(GetUnitX(TempUnit), GetUnitY(TempUnit), GetUnitX(u), GetUnitY(u))
local boolean b = circularDifference(GetUnitFacing(TempUnit), a) < lDex.sightRadiusVar/4
set b = b and (p != PASSIVE and p != AGGRESSIVE and UnitAlive(u))
set b = b and (IsUnitVisible(u, PASSIVE) or IsUnitVisible(u, AGGRESSIVE))
set u = null
return b
endfunction
// Filter possible helper for creep
private function helperFilter takes nothing returns boolean
local unit u = GetFilterUnit()
local integer id = GetUnitTypeId(u)
local integer uDex = GetUnitUserData(u)
local SCDataLib lDex = SCDataLib.getIndex(id)
local boolean b = (lDex.helpTypeVar != 1 or id == DummyType) and lDex.helpTypeVar >= 1
set b = b and (GetOwningPlayer(u) == PASSIVE and not lDex.isTamedVar)
set b = b and not IsCreepSleeping(u)
set u = null
return b
endfunction
private function fireEvent takes integer whichEvent, unit u, unit t returns nothing
local unit tu = EventUnit
local unit tt = EventThreat
set EventUnit = u
set EventThreat = t
call TriggerEvaluate(Handler[whichEvent])
set EventUnit = tu
set EventThreat = tt
set tu = null
set tt = null
endfunction
// Seek for nearby helpers
private function pickHelper takes unit u, unit t, integer uDex, SCDataLib lDex returns nothing
local integer i = 0
local SCDataLib lDex2
local integer uDex2
local unit h
loop
exitwhen Data[uDex].helpCount == lDex.maxHelpVar
set h = GetClosestUnitInRange(Data[uDex].unitX, Data[uDex].unitY, lDex.callRadiusVar, Filter(function helperFilter))
exitwhen h == null
call SetUnitOwner(h, AGGRESSIVE, not COLOR)
set uDex2 = GetUnitUserData(h)
set lDex2 = SCDataLib.getIndex(GetUnitTypeId(h))
set Data[uDex2].combatDur = lDex2.combatDurationVar
set Data[uDex2].target = t
call IssueTargetOrderById(h, ATTACK_ORDER_ID, t)
call fireEvent(EVENT_CREEP_GIVE_HELP, h, null)
set Data[uDex].helpCount = Data[uDex].helpCount + 1
endloop
set h = null
endfunction
// Filter creeps around generators
private function pickFilter takes nothing returns boolean
local unit u = GetFilterUnit()
if not IsUnitInGroup(u, PickGroup) and IsUnitInGroup(u, CreepGroup) then
call GroupAddUnit(PickGroup, u)
endif
set u = null
return false
endfunction
private function onLoop takes nothing returns nothing
local unit u
local unit t
local SCDataLib lDex
local integer uDex
local integer i = 1
// Collect creeps around generator units' positions
loop
exitwhen i > GenTotal
call GroupEnumUnitsInRange(TempGroup, GetUnitX(GenUnit[i]), GetUnitY(GenUnit[i]), DISTANCE, function pickFilter)
set i = i + 1
endloop
loop
set u = FirstOfGroup(PickGroup)
exitwhen u == null
call GroupRemoveUnit(PickGroup, u)
set uDex = GetUnitUserData(u)
if GetUnitTypeId(u) != 0 then
if UnitAlive(u) then
set lDex = SCDataLib.getIndex(GetUnitTypeId(u))
// Determine whether the creep is moving atm or not
set Move = IsCreepMoving(u)
if Move then
set Data[uDex].unitX = GetUnitX(u)
set Data[uDex].unitY = GetUnitY(u)
endif
if GetOwningPlayer(u) == PASSIVE then
if Data[uDex].combatDly < 0 then
// If the creep is able to sleep
if lDex.sleepDurVar > 0 then
// Gather sleep time datas
set Real[R_TOD] = GetFloatGameState(GAME_STATE_TIME_OF_DAY)
set Real[R_WAKE] = lDex.sleepTimeVar + lDex.sleepDurVar
if Real[R_TOD] <= Real[R_WAKE] - 24 then
set Real[R_TOD] = Real[R_TOD] + 24
endif
// Check sleep time
if Real[R_TOD] >= lDex.sleepTimeVar and Real[R_TOD] < Real[R_WAKE] and not Data[uDex].isSleep then
if not Move and not(IsUnitType(u, UNIT_TYPE_STUNNED) or IsUnitPaused(u)) then
set Data[uDex].isSleep = true
set Data[uDex].sleepSfx = AddSpecialEffectTarget(SLEEP_SFX, u, SLEEP_SFX_PT)
if Data[uDex].sleepTimer == null then
static if LIBRARY_TimerUtils then
set Data[uDex].sleepTimer = NewTimer()
else
set Data[uDex].sleepTimer = CreateTimer()
endif
// Calculate sleep duration
set Real[R_DURATION] = (SECONDS_IN_A_DAY/86400)*((lDex.sleepDurVar-(Real[R_TOD]-lDex.sleepTimeVar))*3600)/GetTimeOfDayScale()
call TimerStart(Data[uDex].sleepTimer, Real[R_DURATION], false, null)
endif
call fireEvent(EVENT_CREEP_SLEEP, u, null)
endif
endif
endif
if Data[uDex].isSleep then
// Wake up time
if TimerGetRemaining(Data[uDex].sleepTimer) <= 0 then
call DestroyEffect(Data[uDex].sleepSfx)
static if LIBRARY_TimerUtils then
call ReleaseTimer(Data[uDex].sleepTimer)
else
call DestroyTimer(Data[uDex].sleepTimer)
endif
set Data[uDex].isSleep = false
set Data[uDex].sleepTimer = null
set Data[uDex].sleepSfx = null
call fireEvent(EVENT_CREEP_AWAKE, u, null)
endif
else
// Check if wandering or not
if lDex.wanderDelayVar > 0 then
if Data[uDex].wanderDelay > INTERVAL then
set Data[uDex].wanderDelay = Data[uDex].wanderDelay - INTERVAL
else
set Real[R_DISTANCE] = GetRandomReal(lDex.wanderDistVar, lDex.nestAreaVar)
set Real[R_FACING] = GetRandomReal(0,bj_PI*2)
// Reset wander delay
set Data[uDex].wanderDelay = lDex.wanderDelayVar + GetRandomReal(-lDex.wanderVariantVar, lDex.wanderVariantVar)/2
set Real[R_X] = Data[uDex].nestX + Real[R_DISTANCE] * Cos(Real[R_FACING])
set Real[R_Y] = Data[uDex].nestY + Real[R_DISTANCE] * Sin(Real[R_FACING])
call IssuePointOrderById(u, MOVE_ORDER_ID, Real[R_X], Real[R_Y])
call fireEvent(EVENT_CREEP_WANDER, u, null)
endif
endif
// If the creep is aggressive
if lDex.acquisitionRangeVar > 0 then
// If is around it's nest area
if getDistance(Data[uDex].unitX, Data[uDex].unitY, Data[uDex].nestX, Data[uDex].nestY) <= lDex.nestAreaVar then
set TempUnit = u
set t = GetClosestUnitInRange(Data[uDex].unitX, Data[uDex].unitY, lDex.acquisitionRangeVar, Filter(function targetFilter))
if t != null then
set Data[uDex].target = t
set Data[uDex].combatDly = lDex.ambushDelayVar
call IssueImmediateOrderById(u, STOP_ORDER_ID)
call fireEvent(EVENT_CREEP_THREAT, u, t)
set t = null
endif
endif
endif
endif
else
set Real[R_X] = GetUnitX(Data[uDex].target)
set Real[R_Y] = GetUnitY(Data[uDex].target)
call SetUnitFacing(u, getAngle(Data[uDex].unitX, Data[uDex].unitY, Real[R_X], Real[R_Y]))
// If target has fleed
if getDistance(Data[uDex].unitX, Data[uDex].unitY, Real[R_X], Real[R_Y]) > lDex.acquisitionRangeVar then
set Data[uDex].combatDly = -1
set Data[uDex].target = null
call fireEvent(EVENT_CREEP_UNTHREAT, u, Data[uDex].target)
else
if Data[uDex].combatDly > INTERVAL then
set Data[uDex].combatDly = Data[uDex].combatDly - INTERVAL
else
set Data[uDex].combatDur = lDex.combatDurationVar
call SetUnitOwner(u, AGGRESSIVE, not COLOR)
call IssueTargetOrderById(u, ATTACK_ORDER_ID, Data[uDex].target)
call fireEvent(EVENT_CREEP_ATTACK, u, Data[uDex].target)
endif
endif
endif
else
// Calm down if the combat timer is up or if the target has died
if Data[uDex].combatDur > INTERVAL and UnitAlive(Data[uDex].target) then
set Data[uDex].combatDur = Data[uDex].combatDur - INTERVAL
else
call SetUnitOwner(u, PASSIVE, not COLOR)
set Data[uDex].helpCount = 0
// Order to move away
set Real[R_DISTANCE] = GetRandomReal(0, lDex.nestAreaVar)
set Real[R_FACING] = GetRandomReal(0, bj_PI*2)
set Real[R_X] = Data[uDex].nestX + Real[R_DISTANCE] * Cos(Real[R_FACING])
set Real[R_Y] = Data[uDex].nestY + Real[R_DISTANCE] * Sin(Real[R_FACING])
call IssuePointOrderById(u, MOVE_ORDER_ID, Real[R_X], Real[R_Y])
call fireEvent(EVENT_CREEP_UNATTACK, u, Data[uDex].target)
set Data[uDex].target = null
endif
endif
endif
else
call Data[uDex].destroy()
set Data[uDex].sleepTimer = null
set Data[uDex].sleepSfx = null
set Data[uDex].target = null
set Total = Total - 1
if Total < 0 then
call PauseTimer(Timer)
endif
endif
endloop
endfunction
private function onHit takes nothing returns boolean
local SCDataLib lDex
local integer uDex
local integer i
local integer ix
local unit t = getTarget()
local unit s = getSource()
local unit h
if GetOwningPlayer(s) == AGGRESSIVE and IsUnitInGroup(s, CreepGroup) then
set DummyType = GetUnitTypeId(s)
set uDex = GetUnitUserData(s)
set lDex = SCDataLib.getIndex(DummyType)
call fireEvent(EVENT_CREEP_DEAL_DAMAGE, s, t)
if lDex.resetTimerVar then
if getDistance(Data[uDex].unitX, Data[uDex].unitY, Data[uDex].nestX, Data[uDex].nestY) <= lDex.nestAreaVar then
set Data[uDex].combatDur = lDex.combatDurationVar
endif
endif
// If call for help when attacking
if lDex.maxHelpVar > 0 and lDex.callHelpVar then
call pickHelper(s, t, uDex, lDex)
call fireEvent(EVENT_CREEP_CALL_HELP, s, null)
endif
elseif IsUnitInGroup(t, CreepGroup) then
set DummyType = GetUnitTypeId(t)
set uDex = GetUnitUserData(t)
set lDex = SCDataLib.getIndex(DummyType)
set Data[uDex].combatDur = lDex.combatDurationVar
call fireEvent(EVENT_CREEP_TAKE_DAMAGE, t, s)
// If the creep is currently sleeping
if Data[uDex].isSleep then
call DestroyEffect(Data[uDex].sleepSfx)
set Data[uDex].sleepSfx = null
set Data[uDex].isSleep = false
call fireEvent(EVENT_CREEP_AWAKE, t, null)
endif
if lDex.canFleeVar and lDex.fleeDistVar > 0 then
// If still around it's nest area
if getDistance(Data[uDex].unitX, Data[uDex].unitY, Data[uDex].nestX, Data[uDex].nestY) < lDex.nestAreaVar then
set Real[R_FACING] = getAngle(GetUnitX(s), GetUnitY(s), Data[uDex].unitX, Data[uDex].unitY) * bj_DEGTORAD
set Real[R_X] = Data[uDex].unitX + lDex.fleeDistVar * Cos(Real[R_FACING])
set Real[R_Y] = Data[uDex].unitY + lDex.fleeDistVar * Sin(Real[R_FACING])
else
set Real[R_FACING] = GetRandomReal(0, bj_PI*2)
set Real[R_X] = Data[uDex].nestX + lDex.fleeDistVar * Cos(Real[R_FACING])
set Real[R_Y] = Data[uDex].nestY + lDex.fleeDistVar * Sin(Real[R_FACING])
endif
call IssuePointOrderById(t, MOVE_ORDER_ID, Real[R_X], Real[R_Y])
call fireEvent(EVENT_CREEP_FLEE, t, null)
endif
if GetOwningPlayer(t) == PASSIVE then
set Data[uDex].target = s
// If not tamed
if not lDex.isTamedVar then
call SetUnitOwner(t, AGGRESSIVE, not COLOR)
call IssueTargetOrderById(t, ATTACK_ORDER_ID, s)
endif
if lDex.maxHelpVar > 0 then
call pickHelper(t, s, uDex, lDex)
call fireEvent(EVENT_CREEP_CALL_HELP, t, s)
endif
endif
if GetWidgetLife(t) <= lDex.lowHealthVar then
call fireEvent(EVENT_CREEP_LOW_HEALTH, t, null)
// If able to seek help
if lDex.seekHelpVar and lDex.maxHelpVar > 0 then
set h = GetClosestUnitInRange(Data[uDex].unitX,Data[uDex].unitY,lDex.seekRadiusVar,Filter(function helperFilter))
// Order to move to helper's position
if h != null then
call IssuePointOrderById(t, MOVE_ORDER_ID, GetUnitX(h), GetUnitY(h))
set h = null
endif
call fireEvent(EVENT_CREEP_SEEK_HELP, t, null)
endif
endif
endif
set t = null
set s = null
return false
endfunction
private function onDeath takes nothing returns boolean
local integer uDex
local SCDataLib lDex
local player p = GetTriggerPlayer()
local unit u
if p == PASSIVE or p == AGGRESSIVE then
set u = GetTriggerUnit()
set uDex = GetUnitUserData(u)
set lDex = SCDataLib.getIndex(GetUnitTypeId(u))
call SetUnitOwner(u, PASSIVE, not COLOR)
set Data[uDex].wanderDelay = lDex.wanderDelayVar + GetRandomReal(-lDex.wanderVariantVar, lDex.wanderVariantVar)/2
set Data[uDex].combatDur = -1
set Data[uDex].combatDly = -1
set Data[uDex].helpCount = 0
set Data[uDex].isSleep = false
set u = null
endif
return false
endfunction
function EnableCreepBehavior takes unit u, boolean b returns nothing
local SCDataLib lDex
local integer uDex
local player p = GetOwningPlayer(u)
if b then
if p == PASSIVE or p == AGGRESSIVE then
if p == AGGRESSIVE then
call SetUnitOwner(u, PASSIVE, false)
static if COLOR then
call SetUnitColor(u, GetPlayerColor(PASSIVE))
endif
else
static if not COLOR then
call SetUnitColor(u, GetPlayerColor(AGGRESSIVE))
endif
endif
if creepFilter(u) then
set Total = Total + 1
set uDex = GetUnitUserData(u)
set lDex = SCDataLib.getIndex(GetUnitTypeId(u))
set Data[uDex] = CreepData.create()
set Data[uDex].helpCount = 0
set Data[uDex].isSleep = false
set Data[uDex].wanderDelay = GetRandomReal(0, lDex.wanderDelayVar)
set Data[uDex].nestX = GetUnitX(u)
set Data[uDex].nestY = GetUnitY(u)
set Data[uDex].combatDly = -1
set Data[uDex].unitX = 0
set Data[uDex].unitY = 0
if Total == 0 then
call TimerStart(Timer, INTERVAL, true, function onLoop)
endif
endif
endif
elseif IsUnitInGroup(u, CreepGroup) then
call GroupRemoveUnit(CreepGroup, u)
endif
endfunction
private function onIndex takes nothing returns boolean
call EnableCreepBehavior(GetIndexedUnit(), true)
return false
endfunction
private function setAlliance takes nothing returns nothing
local player p = GetEnumPlayer()
if p != PASSIVE and p != AGGRESSIVE then
// Aggressive player threats others as enemies
call SetPlayerAlliance(AGGRESSIVE, p, ALLIANCE_PASSIVE, false)
// Passive player threats other players as allies
call SetPlayerAlliance(PASSIVE, p, ALLIANCE_PASSIVE, true)
endif
endfunction
private function onInit takes nothing returns nothing
local player p = GetLocalPlayer()
local trigger t1 = CreateTrigger()
local trigger t2 = CreateTrigger()
static if AUTO_REGISTER then
call RegisterUnitIndexEvent(Condition(function onIndex), UnitIndexer.INDEX)
endif
call addEvent(t1)
call TriggerAddCondition(t1, Condition(function onHit))
call TriggerRegisterAnyUnitEventBJ(t2, EVENT_PLAYER_UNIT_DEATH)
call TriggerAddCondition(t2, Condition(function onDeath))
// Alliance setting
call SetPlayerAlliance(PASSIVE, AGGRESSIVE, ALLIANCE_PASSIVE, true)
call SetPlayerAlliance(AGGRESSIVE, PASSIVE, ALLIANCE_PASSIVE, true)
call ForForce(bj_FORCE_ALL_PLAYERS, function setAlliance)
// Give global sight for both player
if p == AGGRESSIVE or p == PASSIVE then
call FogEnable(false)
call FogMaskEnable(false)
endif
set t1 = null
set t2 = null
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
/*****************************************************************************
*
* Get Closest Widget (light version)
* by Spinnaker v2.0.0.0
*
* Special thanks to Troll-Brain
*
******************************************************************************
*
* This snippet contains several functions which returns closest
* widget to given coordinates and passed filter.
*
******************************************************************************
*
* Important:
* Performance drop depends on amount of units currently on the map
* Snippet functions are designed to reduce the search time as much as
* posible, although it's recommended to use non specific functions
* wisely, especialy if your map contains large numbers of units.
*
******************************************************************************
*
* Functions:
* function GetClosestUnit takes real x, real y, boolexpr filter returns unit
* - Returns closest unit matching specific filter
* function GetClosestUnitInRange takes real x, real y, real radius, boolexpr filter returns unit
* - Gets nearest unit in range matching specific filter
* function GetClosestUnitInGroup takes real x, real y, group g returns unit
* - Retrieves closest unit in given group
*
* function GetClosestNUnitsInRange takes real x, real y, real radius, integer n, group g, boolexpr filter returns nothing
* - Returns up to N nearest units in range of passed coordinates
* function GetClosestNUnitsInGroup takes real x, real y, integer n, group sourceGroup, group destGroup returns nothing
* - Retrieves up to N closest units in passed group
*
*****************************************************************************/
library GetClosestWidget
private keyword Init
globals
private unit array Q
private real array V
private integer C=0
endglobals
struct ClosestWidget extends array
readonly static real distance=0
readonly static real cX=0
readonly static real cY=0
readonly static unit cUnit=null
static method resetData takes real x, real y returns nothing
set distance=100000
set cUnit=null
set cX=x
set cY=y
endmethod
static method enumUnits takes nothing returns nothing
local unit u=GetEnumUnit()
local real dx=GetUnitX(u)-cX
local real dy=GetUnitY(u)-cY
set dx=(dx*dx+dy*dy)/10000.
if dx<distance then
set cUnit=u
set distance=dx
endif
set u=null
endmethod
static method sortUnits takes integer l, integer r returns nothing
local integer i=l
local integer j=r
local real v=V[(l+r)/2]
loop
loop
exitwhen V[i]>=v
set i=i+1
endloop
loop
exitwhen V[j]<=v
set j=j-1
endloop
if i<=j then
set V[0]=V[i]
set V[i]=V[j]
set V[j]=V[0]
set Q[0]=Q[i]
set Q[i]=Q[j]
set Q[j]=Q[0]
set i=i+1
set j=j-1
endif
exitwhen i>j
endloop
if l<j then
call sortUnits(l,j)
endif
if r>i then
call sortUnits(i,r)
endif
endmethod
static method saveGroup takes nothing returns nothing
local real dx
local real dy
set C=C+1
set Q[C]=GetEnumUnit()
set dx=GetUnitX(Q[C])-cX
set dy=GetUnitY(Q[C])-cY
set V[C]=(dx*dx+dy*dy)/10000.
endmethod
endstruct
function GetClosestUnit takes real x, real y, boolexpr filter returns unit
local real r=800.
call ClosestWidget.resetData(x,y)
loop
if r>3200. then
call GroupEnumUnitsInRect(bj_lastCreatedGroup, bj_mapInitialPlayableArea, filter)
exitwhen true
else
call GroupEnumUnitsInRange(bj_lastCreatedGroup, x, y, r, filter)
exitwhen FirstOfGroup(bj_lastCreatedGroup)!=null
endif
set r=2*r
endloop
call ForGroup(bj_lastCreatedGroup, function ClosestWidget.enumUnits)
return ClosestWidget.cUnit
endfunction
function GetClosestUnitInRange takes real x, real y, real radius, boolexpr filter returns unit
call ClosestWidget.resetData(x,y)
if radius>=0 then
call GroupEnumUnitsInRange(bj_lastCreatedGroup, x, y, radius, filter)
call ForGroup(bj_lastCreatedGroup, function ClosestWidget.enumUnits)
endif
return ClosestWidget.cUnit
endfunction
function GetClosestUnitInGroup takes real x, real y, group g returns unit
call ClosestWidget.resetData(x,y)
call ForGroup(g, function ClosestWidget.enumUnits)
return ClosestWidget.cUnit
endfunction
function GetClosestNUnitsInRange takes real x, real y, real radius, integer n, group g, boolexpr filter returns nothing
local integer q=n+1
call ClosestWidget.resetData(x,y)
if radius>=0 then
call GroupEnumUnitsInRange(bj_lastCreatedGroup, x, y, radius, filter)
call ForGroup(bj_lastCreatedGroup, function ClosestWidget.saveGroup)
call ClosestWidget.sortUnits(1,C)
loop
exitwhen n==0 or Q[-n+q]==null
call GroupAddUnit(g, Q[-n+q])
set n =n-1
endloop
endif
set C=0
endfunction
function GetClosestNUnitsInGroup takes real x, real y, integer n, group sourceGroup, group destGroup returns nothing
local integer q=n+1
call ClosestWidget.resetData(x,y)
call ForGroup(sourceGroup, function ClosestWidget.saveGroup)
call ClosestWidget.sortUnits(1,C)
loop
exitwhen n==0 or Q[-n+q]==null
call GroupAddUnit(destGroup, Q[-n+q])
set n=n-1
endloop
set C=0
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
// GUI-Friendly Damage Detection -- v1.2.1 -- by Weep
// http:// www.thehelper.net/forums/showthread.php?t=137957
//
// Requires: only this trigger and its variables.
//
// -- What? --
// This snippet provides a leak-free, GUI-friendly implementation of an "any unit takes
// damage" event. It requires no JASS knowledge to use.
//
// It uses the Game - Value Of Real Variable event as its method of activating other
// triggers, and passes the event responses through a few globals.
//
// -- Why? --
// The traditional GUI method of setting up a trigger than runs when any unit is damaged
// leaks trigger events. This snippet is easy to implement and removes the need to do
// you own GUI damage detection setup.
//
// -- How To Implement --
// 0. Before you copy triggers that use GDD into a new map, you need to copy over GDD
// with its GDD Variable Creator trigger, or there will be a problem: the variables
// won't be automatically created correctly.
//
// 1. Be sure "Automatically create unknown variables while pasting trigger data" is
// enabled in the World Editor general preferences.
// 2. Copy this trigger category ("GDD") and paste it into your map.
// (Alternately: create the variables listed in the globals block below, create a
// trigger named "GUI Friendly Damage Detection", and paste in this entire text.)
// 3. Create your damage triggers using Game - Value Of Real Variable as the event,
// select GDD_Event as the variable, and leave the rest of the settings to the default
// "becomes Equal to 0.00".
// The event responses are the following variables:
// GDD_Damage is the amount of damage, replacing Event Response - Damage Taken.
// GDD_DamagedUnit is the damaged unit, replacing Event Response - Triggering Unit.
// Triggering Unit can still be used, if you need to use waits.
// Read the -- Notes -- section below for more info.
// GDD_DamageSource is the damaging unit, replacing Event Response - Damage Source.
//
// -- Notes --
// GDD's event response variables are not wait-safe; you can't use them after a wait in
// a trigger. If you need to use waits, Triggering Unit (a.k.a. GetTriggerUnit()) can
// be used in place of GDD_DamageSource. There is no usable wait-safe equivalent to
// Event Damage or Damage Source; you'll need to save the values yourself.
//
// Don't write any values to the variables used as the event responses, or it will mess
// up any other triggers using this snippet for their triggering. Only use their values.
//
// This uses arrays, so can detect damage for a maximum of 8190 units at a time, and
// cleans up data at a rate of 33.33 per second, by default. This should be enough for
// most maps, but if you want to change the rate, change the value returned in the
// GDD_RecycleRate function at the top of the code, below.
//
// By default, GDD will not register units that have Locust at the moment of their
// entering the game, and will not recognize when they take damage (which can only
// happen if the Locust ability is later removed from the unit.) To allow a unit to have
// Locust yet still cause GDD damage events if Locust is removed, you can either design
// the unit to not have Locust by default and add it via triggers after creation, or
// edit the GDD_Filter function at the top of the code, below.
//
// -- Credits --
// Captain Griffin on wc3c.net for the research and concept of GroupRefresh.
//
// Credit in your map not needed, but please include this README.
//
// -- Version History --
// 1.2.1: Minor code cleaning. Added configuration functions. Updated documentation.
// 1.2.0: Made this snippet work properly with recursive damage.
// 1.1.1: Added a check in order to not index units with the Locust ability (dummy units).
// If you wish to check for damage taken by a unit that is unselectable, do not
// give the unit-type Locust in the object editor; instead, add the Locust ability
// 'Aloc' via a trigger after its creation, then remove it.
// 1.1.0: Added a check in case a unit gets moved out of the map and back.
// 1.0.0: First release.
//===================================================================
// Configurables.
function GDD_RecycleRate takes nothing returns real //The rate at which the system checks units to see if they've been removed from the game
return 0.03
endfunction
function GDD_Filter takes unit u returns boolean //The condition a unit has to pass to have it registered for damage detection
return GetUnitAbilityLevel(u, 'Aloc') == 0 //By default, the system ignores Locust units, because they normally can't take damage anyway
endfunction
//===================================================================
// This is just for reference.
// If you use JassHelper, you could uncomment this section instead of creating the variables in the trigger editor.
// globals
// real udg_GDD_Event = 0.
// real udg_GDD_Damage = 0.
// unit udg_GDD_DamagedUnit
// unit udg_GDD_DamageSource
// trigger array udg_GDD__TriggerArray
// integer array udg_GDD__Integers
// unit array udg_GDD__UnitArray
// group udg_GDD__LeftMapGroup = CreateGroup()
// endglobals
//===================================================================
// System code follows. Don't touch!
function GDD_Event takes nothing returns boolean
local unit damagedcache = udg_GDD_DamagedUnit
local unit damagingcache = udg_GDD_DamageSource
local real damagecache = udg_GDD_Damage
set udg_GDD_DamagedUnit = GetTriggerUnit()
set udg_GDD_DamageSource = GetEventDamageSource()
set udg_GDD_Damage = GetEventDamage()
set udg_GDD_Event = 1.
set udg_GDD_Event = 0.
set udg_GDD_DamagedUnit = damagedcache
set udg_GDD_DamageSource = damagingcache
set udg_GDD_Damage = damagecache
set damagedcache = null
set damagingcache = null
return false
endfunction
function GDD_AddDetection takes nothing returns boolean
// if(udg_GDD__Integers[0] > 8190) then
// call BJDebugMsg("GDD: Too many damage events! Decrease number of units present in the map or increase recycle rate.")
// ***Recycle rate is specified in the GDD_RecycleRate function at the top of the code. Smaller is faster.***
// return
// endif
if(IsUnitInGroup(GetFilterUnit(), udg_GDD__LeftMapGroup)) then
call GroupRemoveUnit(udg_GDD__LeftMapGroup, GetFilterUnit())
elseif(GDD_Filter(GetFilterUnit())) then
set udg_GDD__Integers[0] = udg_GDD__Integers[0]+1
set udg_GDD__UnitArray[udg_GDD__Integers[0]] = GetFilterUnit()
set udg_GDD__TriggerArray[udg_GDD__Integers[0]] = CreateTrigger()
call TriggerRegisterUnitEvent(udg_GDD__TriggerArray[udg_GDD__Integers[0]], udg_GDD__UnitArray[udg_GDD__Integers[0]], EVENT_UNIT_DAMAGED)
call TriggerAddCondition(udg_GDD__TriggerArray[udg_GDD__Integers[0]], Condition(function GDD_Event))
endif
return false
endfunction
function GDD_PreplacedDetection takes nothing returns nothing
local group g = CreateGroup()
local integer i = 0
loop
call GroupEnumUnitsOfPlayer(g, Player(i), Condition(function GDD_AddDetection))
set i = i+1
exitwhen i == bj_MAX_PLAYER_SLOTS
endloop
call DestroyGroup(g)
set g = null
endfunction
function GDD_GroupRefresh takes nothing returns nothing
// Based on GroupRefresh by Captain Griffen on wc3c.net
if (bj_slotControlUsed[5063] == true) then
call GroupClear(udg_GDD__LeftMapGroup)
set bj_slotControlUsed[5063] = false
endif
call GroupAddUnit(udg_GDD__LeftMapGroup, GetEnumUnit())
endfunction
function GDD_Recycle takes nothing returns nothing
if(udg_GDD__Integers[0] <= 0) then
return
elseif(udg_GDD__Integers[1] <= 0) then
set udg_GDD__Integers[1] = udg_GDD__Integers[0]
endif
if(GetUnitTypeId(udg_GDD__UnitArray[udg_GDD__Integers[1]]) == 0) then
call DestroyTrigger(udg_GDD__TriggerArray[udg_GDD__Integers[1]])
set udg_GDD__TriggerArray[udg_GDD__Integers[1]] = null
set udg_GDD__TriggerArray[udg_GDD__Integers[1]] = udg_GDD__TriggerArray[udg_GDD__Integers[0]]
set udg_GDD__UnitArray[udg_GDD__Integers[1]] = udg_GDD__UnitArray[udg_GDD__Integers[0]]
set udg_GDD__UnitArray[udg_GDD__Integers[0]] = null
set udg_GDD__Integers[0] = udg_GDD__Integers[0]-1
endif
set udg_GDD__Integers[1] = udg_GDD__Integers[1]-1
endfunction
function GDD_LeaveMap takes nothing returns boolean
local boolean cached = bj_slotControlUsed[5063]
if(udg_GDD__Integers[2] < 64) then
set udg_GDD__Integers[2] = udg_GDD__Integers[2]+1
else
set bj_slotControlUsed[5063] = true
call ForGroup(udg_GDD__LeftMapGroup, function GDD_GroupRefresh)
set udg_GDD__Integers[2] = 0
endif
call GroupAddUnit(udg_GDD__LeftMapGroup, GetFilterUnit())
set bj_slotControlUsed[5063] = cached
return false
endfunction
// ===========================================================================
function InitTrig_GUI_Friendly_Damage_Detection takes nothing returns nothing
local region r = CreateRegion()
call RegionAddRect(r, GetWorldBounds())
call TriggerRegisterEnterRegion(CreateTrigger(), r, Condition(function GDD_AddDetection))
call TriggerRegisterLeaveRegion(CreateTrigger(), r, Condition(function GDD_LeaveMap))
call GDD_PreplacedDetection()
call TimerStart(CreateTimer(), GDD_RecycleRate(), true, function GDD_Recycle)
set r = null
endfunction
//TESH.scrollpos=0
//TESH.alwaysfold=0
library Table /* made by Bribe, special thanks to Vexorian & Nestharus, version 3.1.0.3
One map, one hashtable. Welcome to NewTable 3.1
This library was originally called NewTable so it didn't conflict with
the API of Table by Vexorian. However, the damage is done and it's too
late to change the library name now. To help with damage control, I
have provided an extension library called TableBC, which bridges all
the functionality of Vexorian's Table except for 2-D string arrays &
the ".flush(integer)" method. I use ".flush()" to flush a child hash-
table, because I wanted the API in NewTable to reflect the API of real
hashtables (I thought this would be more intuitive).
API
------------
struct Table
| static method create takes nothing returns Table
| create a new Table
|
| method destroy takes nothing returns nothing
| destroy it
|
| method flush takes nothing returns nothing
| flush all stored values inside of it
|
| method remove takes integer key returns nothing
| remove the value at index "key"
|
| method operator []= takes integer key, $TYPE$ value returns nothing
| assign "value" to index "key"
|
| method operator [] takes integer key returns $TYPE$
| load the value at index "key"
|
| method has takes integer key returns boolean
| whether or not the key was assigned
|
----------------
struct TableArray
| static method operator [] takes integer array_size returns TableArray
| create a new array of Tables of size "array_size"
|
| method destroy takes nothing returns nothing
| destroy it
|
| method flush takes nothing returns nothing
| flush and destroy it
|
| method operator size takes nothing returns integer
| returns the size of the TableArray
|
| method operator [] takes integer key returns Table
| returns a Table accessible exclusively to index "key"
*/
globals
private integer less = 0 //Index generation for TableArrays (below 0).
private integer more = 8190 //Index generation for Tables.
//Configure it if you use more than 8190 "key" variables in your map (this will never happen though).
private hashtable ht = InitHashtable()
private key sizeK
private key listK
endglobals
private struct dex extends array
static method operator size takes nothing returns Table
return sizeK
endmethod
static method operator list takes nothing returns Table
return listK
endmethod
endstruct
private struct handles extends array
method has takes integer key returns boolean
return HaveSavedHandle(ht, this, key)
endmethod
method remove takes integer key returns nothing
call RemoveSavedHandle(ht, this, key)
endmethod
endstruct
private struct agents extends array
method operator []= takes integer key, agent value returns nothing
call SaveAgentHandle(ht, this, key, value)
endmethod
endstruct
//! textmacro NEW_ARRAY_BASIC takes SUPER, FUNC, TYPE
private struct $TYPE$s extends array
method operator [] takes integer key returns $TYPE$
return Load$FUNC$(ht, this, key)
endmethod
method operator []= takes integer key, $TYPE$ value returns nothing
call Save$FUNC$(ht, this, key, value)
endmethod
method has takes integer key returns boolean
return HaveSaved$SUPER$(ht, this, key)
endmethod
method remove takes integer key returns nothing
call RemoveSaved$SUPER$(ht, this, key)
endmethod
endstruct
private module $TYPE$m
method operator $TYPE$ takes nothing returns $TYPE$s
return this
endmethod
endmodule
//! endtextmacro
//! textmacro NEW_ARRAY takes FUNC, TYPE
private struct $TYPE$s extends array
method operator [] takes integer key returns $TYPE$
return Load$FUNC$Handle(ht, this, key)
endmethod
method operator []= takes integer key, $TYPE$ value returns nothing
call Save$FUNC$Handle(ht, this, key, value)
endmethod
endstruct
private module $TYPE$m
method operator $TYPE$ takes nothing returns $TYPE$s
return this
endmethod
endmodule
//! endtextmacro
//Run these textmacros to include the entire hashtable API as wrappers.
//Don't be intimidated by the number of macros - Vexorian's map optimizer is
//supposed to kill functions which inline (all of these functions inline).
//! runtextmacro NEW_ARRAY_BASIC("Real", "Real", "real")
//! runtextmacro NEW_ARRAY_BASIC("Boolean", "Boolean", "boolean")
//! runtextmacro NEW_ARRAY_BASIC("String", "Str", "string")
//New textmacro to allow table.integer[] syntax for compatibility with textmacros that might desire it.
//! runtextmacro NEW_ARRAY_BASIC("Integer", "Integer", "integer")
//! runtextmacro NEW_ARRAY("Player", "player")
//! runtextmacro NEW_ARRAY("Widget", "widget")
//! runtextmacro NEW_ARRAY("Destructable", "destructable")
//! runtextmacro NEW_ARRAY("Item", "item")
//! runtextmacro NEW_ARRAY("Unit", "unit")
//! runtextmacro NEW_ARRAY("Ability", "ability")
//! runtextmacro NEW_ARRAY("Timer", "timer")
//! runtextmacro NEW_ARRAY("Trigger", "trigger")
//! runtextmacro NEW_ARRAY("TriggerCondition", "triggercondition")
//! runtextmacro NEW_ARRAY("TriggerAction", "triggeraction")
//! runtextmacro NEW_ARRAY("TriggerEvent", "event")
//! runtextmacro NEW_ARRAY("Force", "force")
//! runtextmacro NEW_ARRAY("Group", "group")
//! runtextmacro NEW_ARRAY("Location", "location")
//! runtextmacro NEW_ARRAY("Rect", "rect")
//! runtextmacro NEW_ARRAY("BooleanExpr", "boolexpr")
//! runtextmacro NEW_ARRAY("Sound", "sound")
//! runtextmacro NEW_ARRAY("Effect", "effect")
//! runtextmacro NEW_ARRAY("UnitPool", "unitpool")
//! runtextmacro NEW_ARRAY("ItemPool", "itempool")
//! runtextmacro NEW_ARRAY("Quest", "quest")
//! runtextmacro NEW_ARRAY("QuestItem", "questitem")
//! runtextmacro NEW_ARRAY("DefeatCondition", "defeatcondition")
//! runtextmacro NEW_ARRAY("TimerDialog", "timerdialog")
//! runtextmacro NEW_ARRAY("Leaderboard", "leaderboard")
//! runtextmacro NEW_ARRAY("Multiboard", "multiboard")
//! runtextmacro NEW_ARRAY("MultiboardItem", "multiboarditem")
//! runtextmacro NEW_ARRAY("Trackable", "trackable")
//! runtextmacro NEW_ARRAY("Dialog", "dialog")
//! runtextmacro NEW_ARRAY("Button", "button")
//! runtextmacro NEW_ARRAY("TextTag", "texttag")
//! runtextmacro NEW_ARRAY("Lightning", "lightning")
//! runtextmacro NEW_ARRAY("Image", "image")
//! runtextmacro NEW_ARRAY("Ubersplat", "ubersplat")
//! runtextmacro NEW_ARRAY("Region", "region")
//! runtextmacro NEW_ARRAY("FogState", "fogstate")
//! runtextmacro NEW_ARRAY("FogModifier", "fogmodifier")
//! runtextmacro NEW_ARRAY("Hashtable", "hashtable")
struct Table extends array
// Implement modules for intuitive syntax (tb.handle; tb.unit; etc.)
implement realm
implement integerm
implement booleanm
implement stringm
implement playerm
implement widgetm
implement destructablem
implement itemm
implement unitm
implement abilitym
implement timerm
implement triggerm
implement triggerconditionm
implement triggeractionm
implement eventm
implement forcem
implement groupm
implement locationm
implement rectm
implement boolexprm
implement soundm
implement effectm
implement unitpoolm
implement itempoolm
implement questm
implement questitemm
implement defeatconditionm
implement timerdialogm
implement leaderboardm
implement multiboardm
implement multiboarditemm
implement trackablem
implement dialogm
implement buttonm
implement texttagm
implement lightningm
implement imagem
implement ubersplatm
implement regionm
implement fogstatem
implement fogmodifierm
implement hashtablem
method operator handle takes nothing returns handles
return this
endmethod
method operator agent takes nothing returns agents
return this
endmethod
//set this = tb[GetSpellAbilityId()]
method operator [] takes integer key returns Table
return LoadInteger(ht, this, key)
endmethod
//set tb[389034] = 8192
method operator []= takes integer key, Table tb returns nothing
call SaveInteger(ht, this, key, tb)
endmethod
//set b = tb.has(2493223)
method has takes integer key returns boolean
return HaveSavedInteger(ht, this, key)
endmethod
//call tb.remove(294080)
method remove takes integer key returns nothing
call RemoveSavedInteger(ht, this, key)
endmethod
//Remove all data from a Table instance
method flush takes nothing returns nothing
call FlushChildHashtable(ht, this)
endmethod
//local Table tb = Table.create()
static method create takes nothing returns Table
local Table this = dex.list[0]
if this == 0 then
set this = more + 1
set more = this
else
set dex.list[0] = dex.list[this]
call dex.list.remove(this) //Clear hashed memory
endif
debug set dex.list[this] = -1
return this
endmethod
// Removes all data from a Table instance and recycles its index.
//
// call tb.destroy()
//
method destroy takes nothing returns nothing
debug if dex.list[this] != -1 then
debug call BJDebugMsg("Table Error: Tried to double-free instance: " + I2S(this))
debug return
debug endif
call this.flush()
set dex.list[this] = dex.list[0]
set dex.list[0] = this
endmethod
//! runtextmacro optional TABLE_BC_METHODS()
endstruct
//! runtextmacro optional TABLE_BC_STRUCTS()
struct TableArray extends array
//Returns a new TableArray to do your bidding. Simply use:
//
// local TableArray ta = TableArray[array_size]
//
static method operator [] takes integer array_size returns TableArray
local Table tb = dex.size[array_size] //Get the unique recycle list for this array size
local TableArray this = tb[0] //The last-destroyed TableArray that had this array size
debug if array_size <= 0 then
debug call BJDebugMsg("TypeError: Invalid specified TableArray size: " + I2S(array_size))
debug return 0
debug endif
if this == 0 then
set this = less - array_size
set less = this
else
set tb[0] = tb[this] //Set the last destroyed to the last-last destroyed
call tb.remove(this) //Clear hashed memory
endif
set dex.size[this] = array_size //This remembers the array size
return this
endmethod
//Returns the size of the TableArray
method operator size takes nothing returns integer
return dex.size[this]
endmethod
//This magic method enables two-dimensional[array][syntax] for Tables,
//similar to the two-dimensional utility provided by hashtables them-
//selves.
//
//ta[integer a].unit[integer b] = unit u
//ta[integer a][integer c] = integer d
//
//Inline-friendly when not running in debug mode
//
method operator [] takes integer key returns Table
static if DEBUG_MODE then
local integer i = this.size
if i == 0 then
call BJDebugMsg("IndexError: Tried to get key from invalid TableArray instance: " + I2S(this))
return 0
elseif key < 0 or key >= i then
call BJDebugMsg("IndexError: Tried to get key [" + I2S(key) + "] from outside TableArray bounds: " + I2S(i))
return 0
endif
endif
return this + key
endmethod
//Destroys a TableArray without flushing it; I assume you call .flush()
//if you want it flushed too. This is a public method so that you don't
//have to loop through all TableArray indices to flush them if you don't
//need to (ie. if you were flushing all child-keys as you used them).
//
method destroy takes nothing returns nothing
local Table tb = dex.size[this.size]
debug if this.size == 0 then
debug call BJDebugMsg("TypeError: Tried to destroy an invalid TableArray: " + I2S(this))
debug return
debug endif
if tb == 0 then
//Create a Table to index recycled instances with their array size
set tb = Table.create()
set dex.size[this.size] = tb
endif
call dex.size.remove(this) //Clear the array size from hash memory
set tb[this] = tb[0]
set tb[0] = this
endmethod
private static Table tempTable
private static integer tempEnd
//Avoids hitting the op limit
private static method clean takes nothing returns nothing
local Table tb = .tempTable
local integer end = tb + 0x1000
if end < .tempEnd then
set .tempTable = end
call ForForce(bj_FORCE_PLAYER[0], function thistype.clean)
else
set end = .tempEnd
endif
loop
call tb.flush()
set tb = tb + 1
exitwhen tb == end
endloop
endmethod
//Flushes the TableArray and also destroys it. Doesn't get any more
//similar to the FlushParentHashtable native than this.
//
method flush takes nothing returns nothing
debug if this.size == 0 then
debug call BJDebugMsg("TypeError: Tried to flush an invalid TableArray instance: " + I2S(this))
debug return
debug endif
set .tempTable = this
set .tempEnd = this + this.size
call ForForce(bj_FORCE_PLAYER[0], function thistype.clean)
call this.destroy()
endmethod
endstruct
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library_once Event /* v2.0.0.1
************************************************************************************
*
* Functions
*
* function CreateEvent takes nothing returns integer
* function TriggerRegisterEvent takes trigger t, integer ev returns nothing
*
************************************************************************************
*
* struct Event extends array
*
* static method create takes nothing returns thistype
* method registerTrigger takes trigger t returns nothing
* method register takes boolexpr c returns nothing
* method fire takes nothing returns nothing
*
************************************************************************************/
globals
private real q=0
endglobals
struct Event extends array
private static integer w=0
private static trigger array e
static method create takes nothing returns thistype
set w=w+1
set e[w]=CreateTrigger()
return w
endmethod
method registerTrigger takes trigger t returns nothing
call TriggerRegisterVariableEvent(t,SCOPE_PRIVATE+"q",EQUAL,this)
endmethod
method register takes boolexpr c returns nothing
call TriggerAddCondition(e[this],c)
endmethod
method fire takes nothing returns nothing
set q=0
set q=this
call TriggerEvaluate(e[this])
endmethod
endstruct
function CreateEvent takes nothing returns Event
return Event.create()
endfunction
function TriggerRegisterEvent takes trigger t,Event ev returns nothing
call ev.registerTrigger(t)
endfunction
function RegisterEvent takes boolexpr c,Event ev returns nothing
call ev.register(c)
endfunction
function FireEvent takes Event ev returns nothing
call ev.fire()
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library_once WorldBounds /* v2.0.0.0
************************************************************************************
*
* struct WorldBounds extends array
* readonly static integer maxX
* readonly static integer maxY
* readonly static integer minX
* readonly static integer minY
* readonly static integer centerX
* readonly static integer centerY
* readonly static rect world
* readonly static region worldRegion
*
************************************************************************************/
private module WorldBoundInit
private static method onInit takes nothing returns nothing
set world=GetWorldBounds()
set maxX=R2I(GetRectMaxX(world))
set maxY=R2I(GetRectMaxY(world))
set minX=R2I(GetRectMinX(world))
set minY=R2I(GetRectMinY(world))
set centerX=R2I((maxX+minX)/2)
set centerY=R2I((minY+maxY)/2)
set worldRegion=CreateRegion()
call RegionAddRect(worldRegion,world)
endmethod
endmodule
struct WorldBounds extends array
readonly static integer maxX
readonly static integer maxY
readonly static integer minX
readonly static integer minY
readonly static integer centerX
readonly static integer centerY
readonly static rect world
readonly static region worldRegion
implement WorldBoundInit
endstruct
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library UnitIndexer /* v4.0.2.7
*************************************************************************************
*
* Assigns unique indexes to units via unit user data.
*
*************************************************************************************
*
* */uses/*
*
* */ WorldBounds /* hiveworkshop.com/forums/jass-functions-413/snippet-worldbounds-180494/
* */ Event /* hiveworkshop.com/forums/submissions-414/snippet-event-186555/
*
************************************************************************************
*
* SETTINGS
*/
globals
constant integer ABILITIES_UNIT_INDEXER = 'A001'
endglobals
/*
************************************************************************************
*
* Functions
*
* function RegisterUnitIndexEvent takes boolexpr codeToRegister, Event unitIndexEvent returns nothing
* function TriggerRegisterUnitIndexEvent takes trigger triggerToRegister, Event unitIndexEvent returns nothing
*
* function GetUnitById takes integer index returns unit
* - Returns unit given a unit index
* function GetUnitId takes unit u returns integer
* - Returns unit index given a unit
*
* function IsUnitIndexed takes unit u returns boolean
* function IsUnitDeindexing takes unit u returns boolean
*
* function GetIndexedUnitId takes nothing returns integer
* function GetIndexedUnit takes nothing returns unit
*
************************************************************************************
*
* module UnitIndexStructMethods
* static method operator [] takes unit u returns thistype
* - Return GetUnitUserData(u)
*
* readonly unit unit
* - The indexed unit of the struct
*
************************************************************************************
*
* module UnitIndexStruct extends UnitIndexStructMethods
*
* - A pseudo module interface that runs a set of methods if they exist and provides
* - a few fields and operators. Runs on static ifs to minimize code.
*
* readonly boolean allocated
* - Is unit allocated for the struct
*
* Interface:
*
* - These methods don't have to exist. If they don't exist, the code
* - that calls them won't even be in the module.
*
* private method index takes nothing returns nothing
* - called when a unit is indexed and passes the filter.
* -
* - thistype this: Unit's index
* private method deindex takes nothing returns nothing
* - called when a unit is deindexed and is allocated for struct
* -
* - thistype this: Unit's index
* private static method filter takes unit unitToIndex returns boolean
* - Determines whether or not to allocate struct for unit
* -
* - unit unitToIndex: Unit being filtered
*
************************************************************************************
*
* struct UnitIndexer extends array
*
* - Controls the unit indexer system.
*
* static constant Event UnitIndexer.INDEX
* static constant Event UnitIndexer.DEINDEX
* - Don't register functions and triggers directly to the events. Register them via
* - RegisterUnitIndexEvent and TriggerRegisterUnitIndexEvent.
*
* static boolean enabled
* - Enables and disables unit indexing. Useful for filtering out dummy units.
*
************************************************************************************
*
* struct UnitIndex extends UnitIndexStructMethods
*
* - Constrols specific unit indexes.
*
* method lock takes nothing returns nothing
* - Locks an index. When an index is locked, it will not be recycled
* - when the unit is deindexed until all locks are removed. Deindex
* - events still fire at the appropriate times, the index just doesn't
* - get thrown into the recycler.
* method unlock takes nothing returns nothing
* - Unlocks an index.
*
************************************************************************************/
globals
private trigger q=CreateTrigger()
private trigger l=CreateTrigger()
private unit array e
private integer r=0
private integer y=0
private integer o=0
private boolean a=false
private integer array n
private integer array p
private integer array lc
endglobals
function GetIndexedUnitId takes nothing returns integer
return o
endfunction
function GetIndexedUnit takes nothing returns unit
return e[o]
endfunction
//! runtextmacro optional UNIT_LIST_LIB()
private struct PreLoader extends array
public static method run takes nothing returns nothing
call DestroyTimer(GetExpiredTimer())
set a=true
endmethod
public static method eval takes trigger t returns nothing
local integer f=n[0]
local integer d=o
loop
exitwhen 0==f
if (IsTriggerEnabled(t)) then
set o=f
if (TriggerEvaluate(t)) then
call TriggerExecute(t)
endif
else
exitwhen true
endif
set f=n[f]
endloop
set o=d
endmethod
public static method evalb takes boolexpr c returns nothing
local trigger t=CreateTrigger()
local thistype f=n[0]
local integer d=o
call TriggerAddCondition(t,c)
loop
exitwhen 0==f
set o=f
call TriggerEvaluate(t)
set f=n[f]
endloop
call DestroyTrigger(t)
set t=null
set o=d
endmethod
endstruct
//! runtextmacro optional UNIT_EVENT_MACRO()
private module UnitIndexerInit
private static method onInit takes nothing returns nothing
local integer i=15
local boolexpr bc=Condition(function thistype.onLeave)
local boolexpr bc2=Condition(function thistype.onEnter)
local group g=CreateGroup()
local player p
set INDEX=CreateEvent()
set DEINDEX=CreateEvent()
call TriggerRegisterEnterRegion(q,WorldBounds.worldRegion,bc2)
loop
set p=Player(i)
call TriggerRegisterPlayerUnitEvent(l,p,EVENT_PLAYER_UNIT_ISSUED_ORDER,bc)
call SetPlayerAbilityAvailable(p,ABILITIES_UNIT_INDEXER,false)
call GroupEnumUnitsOfPlayer(g,p,bc2)
exitwhen 0==i
set i=i-1
endloop
call DestroyGroup(g)
set bc=null
set g=null
set bc2=null
set p=null
call TimerStart(CreateTimer(),0,false,function PreLoader.run)
endmethod
endmodule
struct UnitIndex extends array
method lock takes nothing returns nothing
debug if (null!=e[this]) then
set lc[this]=lc[this]+1
debug else
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"UNIT INDEXER ERROR: ATTEMPT TO LOCK NULL INDEX")
debug endif
endmethod
method unlock takes nothing returns nothing
debug if (0<lc[this]) then
set lc[this]=lc[this]-1
if (0==lc[this] and null==e[this]) then
set n[this]=y
set y=this
endif
debug else
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"UNIT INDEXER ERROR: ATTEMPT TO UNLOCK UNLOCKED INDEX")
debug endif
endmethod
method operator unit takes nothing returns unit
return e[this]
endmethod
static method operator [] takes unit whichUnit returns thistype
return GetUnitUserData(whichUnit)
endmethod
endstruct
struct UnitIndexer extends array
readonly static Event INDEX
readonly static Event DEINDEX
static boolean enabled=true
private static method onEnter takes nothing returns boolean
local unit Q=GetFilterUnit()
local integer i
local integer d=o
if (enabled and Q!=e[GetUnitUserData(Q)] and 0==GetUnitUserData(Q)) then
if (0==y) then
set r=r+1
set i=r
else
set i=y
set y=n[y]
endif
call UnitAddAbility(Q,ABILITIES_UNIT_INDEXER)
call UnitMakeAbilityPermanent(Q,true,ABILITIES_UNIT_INDEXER)
call SetUnitUserData(Q,i)
set e[i]=Q
static if not LIBRARY_UnitList then
if (not a)then
set p[i]=p[0]
set n[p[0]]=i
set n[i]=0
set p[0]=i
endif
else
set p[i]=p[0]
set n[p[0]]=i
set n[i]=0
set p[0]=i
call GroupAddUnit(g,e[i])
endif
set o=i
call FireEvent(INDEX)
set o=d
endif
set Q=null
return false
endmethod
private static method onLeave takes nothing returns boolean
static if LIBRARY_UnitEvent then
implement optional UnitEventModule
else
local unit u=GetFilterUnit()
local integer i=GetUnitUserData(u)
local integer d=o
if (0==GetUnitAbilityLevel(u,ABILITIES_UNIT_INDEXER) and u==e[i]) then
static if not LIBRARY_UnitList then
if (not a)then
set n[p[i]]=n[i]
set p[n[i]]=p[i]
endif
else
set n[p[i]]=n[i]
set p[n[i]]=p[i]
call GroupRemoveUnit(g,e[i])
endif
set o=i
call FireEvent(DEINDEX)
set o=d
if (0==lc[i]) then
set n[i]=y
set y=i
endif
set e[i]=null
endif
set u=null
endif
return false
endmethod
implement UnitIndexerInit
endstruct
//! runtextmacro optional UNIT_EVENT_MACRO_2()
function RegisterUnitIndexEvent takes boolexpr c,integer ev returns nothing
call RegisterEvent(c, ev)
if (not a and ev==UnitIndexer.INDEX and 0!=n[0]) then
call PreLoader.evalb(c)
endif
endfunction
function TriggerRegisterUnitIndexEvent takes trigger t,integer ev returns nothing
call TriggerRegisterEvent(t,ev)
if (not a and ev == UnitIndexer.INDEX and 0!=n[0]) then
call PreLoader.eval(t)
endif
endfunction
function GetUnitById takes integer W returns unit
return e[W]
endfunction
function GetUnitId takes unit u returns integer
return GetUnitUserData(u)
endfunction
function IsUnitIndexed takes unit u returns boolean
return u==e[GetUnitUserData(u)]
endfunction
function IsUnitDeindexing takes unit u returns boolean
return IsUnitIndexed(u) and 0==GetUnitAbilityLevel(u,ABILITIES_UNIT_INDEXER)
endfunction
module UnitIndexStructMethods
static method operator [] takes unit u returns thistype
return GetUnitUserData(u)
endmethod
method operator unit takes nothing returns unit
return e[this]
endmethod
endmodule
module UnitIndexStruct
implement UnitIndexStructMethods
static if thistype.filter.exists then
static if thistype.index.exists then
static if thistype.deindex.exists then
readonly boolean allocated
else
method operator allocated takes nothing returns boolean
return filter(e[this])
endmethod
endif
else
method operator allocated takes nothing returns boolean
return filter(e[this])
endmethod
endif
elseif (thistype.index.exists) then
static if thistype.deindex.exists then
readonly boolean allocated
else
method operator allocated takes nothing returns boolean
return this==GetUnitUserData(e[this])
endmethod
endif
else
method operator allocated takes nothing returns boolean
return this==GetUnitUserData(e[this])
endmethod
endif
static if thistype.index.exists then
private static method onIndexEvent takes nothing returns boolean
static if thistype.filter.exists then
if (filter(e[o])) then
static if thistype.deindex.exists then
set thistype(o).allocated=true
endif
call thistype(o).index()
endif
else
static if thistype.deindex.exists then
set thistype(o).allocated=true
endif
call thistype(o).index()
endif
return false
endmethod
endif
static if thistype.deindex.exists then
private static method onDeindexEvent takes nothing returns boolean
static if thistype.filter.exists then
static if thistype.index.exists then
if (thistype(o).allocated) then
set thistype(o).allocated=false
call thistype(o).deindex()
endif
else
if (filter(e[o])) then
call thistype(o).deindex()
endif
endif
else
static if thistype.index.exists then
set thistype(o).allocated=false
endif
call thistype(o).deindex()
endif
return false
endmethod
endif
static if thistype.index.exists then
static if thistype.deindex.exists then
private static method onInit takes nothing returns nothing
call RegisterUnitIndexEvent(Condition(function thistype.onIndexEvent),UnitIndexer.INDEX)
call RegisterUnitIndexEvent(Condition(function thistype.onDeindexEvent),UnitIndexer.DEINDEX)
endmethod
else
private static method onInit takes nothing returns nothing
call RegisterUnitIndexEvent(Condition(function thistype.onIndexEvent),UnitIndexer.INDEX)
endmethod
endif
elseif thistype.deindex.exists then
private static method onInit takes nothing returns nothing
call RegisterUnitIndexEvent(Condition(function thistype.onDeindexEvent),UnitIndexer.DEINDEX)
endmethod
endif
endmodule
endlibrary
library TimerUtils initializer init
//*********************************************************************
//* TimerUtils (red+blue+orange flavors for 1.24b+) 2.0
//* ----------
//*
//* To implement it , create a custom text trigger called TimerUtils
//* and paste the contents of this script there.
//*
//* To copy from a map to another, copy the trigger holding this
//* library to your map.
//*
//* (requires vJass) More scripts: htt://www.wc3c.net
//*
//* For your timer needs:
//* * Attaching
//* * Recycling (with double-free protection)
//*
//* set t=NewTimer() : Get a timer (alternative to CreateTimer)
//* set t=NewTimerEx(x) : Get a timer (alternative to CreateTimer), call
//* Initialize timer data as x, instead of 0.
//*
//* ReleaseTimer(t) : Relese a timer (alt to DestroyTimer)
//* SetTimerData(t,2) : Attach value 2 to timer
//* GetTimerData(t) : Get the timer's value.
//* You can assume a timer's value is 0
//* after NewTimer.
//*
//* Multi-flavor:
//* Set USE_HASH_TABLE to true if you don't want to complicate your life.
//*
//* If you like speed and giberish try learning about the other flavors.
//*
//********************************************************************
//================================================================
globals
//How to tweak timer utils:
// USE_HASH_TABLE = true (new blue)
// * SAFEST
// * SLOWEST (though hash tables are kind of fast)
//
// USE_HASH_TABLE = false, USE_FLEXIBLE_OFFSET = true (orange)
// * kinda safe (except there is a limit in the number of timers)
// * ALMOST FAST
//
// USE_HASH_TABLE = false, USE_FLEXIBLE_OFFSET = false (red)
// * THE FASTEST (though is only faster than the previous method
// after using the optimizer on the map)
// * THE LEAST SAFE ( you may have to tweak OFSSET manually for it to
// work)
//
private constant boolean USE_HASH_TABLE = true
private constant boolean USE_FLEXIBLE_OFFSET = false
private constant integer OFFSET = 0x100000
private integer VOFFSET = OFFSET
//Timers to preload at map init:
private constant integer QUANTITY = 256
//Changing this to something big will allow you to keep recycling
// timers even when there are already AN INCREDIBLE AMOUNT of timers in
// the stack. But it will make things far slower so that's probably a bad idea...
private constant integer ARRAY_SIZE = 8190
endglobals
//==================================================================================================
globals
private integer array data[ARRAY_SIZE]
private hashtable ht
endglobals
//It is dependent on jasshelper's recent inlining optimization in order to perform correctly.
function SetTimerData takes timer t, integer value returns nothing
static if(USE_HASH_TABLE) then
// new blue
call SaveInteger(ht,0,GetHandleId(t), value)
elseif (USE_FLEXIBLE_OFFSET) then
// orange
static if (DEBUG_MODE) then
if(GetHandleId(t)-VOFFSET<0) then
call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
endif
endif
set data[GetHandleId(t)-VOFFSET]=value
else
// new red
static if (DEBUG_MODE) then
if(GetHandleId(t)-OFFSET<0) then
call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
endif
endif
set data[GetHandleId(t)-OFFSET]=value
endif
endfunction
function GetTimerData takes timer t returns integer
static if(USE_HASH_TABLE) then
// new blue
return LoadInteger(ht,0,GetHandleId(t) )
elseif (USE_FLEXIBLE_OFFSET) then
// orange
static if (DEBUG_MODE) then
if(GetHandleId(t)-VOFFSET<0) then
call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
endif
endif
return data[GetHandleId(t)-VOFFSET]
else
// new red
static if (DEBUG_MODE) then
if(GetHandleId(t)-OFFSET<0) then
call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
endif
endif
return data[GetHandleId(t)-OFFSET]
endif
endfunction
//==========================================================================================
globals
private timer array tT[ARRAY_SIZE]
private integer tN = 0
private constant integer HELD=0x28829022
//use a totally random number here, the more improbable someone uses it, the better.
private boolean didinit = false
endglobals
private keyword init
//==========================================================================================
// I needed to decide between duplicating code ignoring the "Once and only once" rule
// and using the ugly textmacros. I guess textmacros won.
//
//! textmacro TIMERUTIS_PRIVATE_NewTimerCommon takes VALUE
// On second thought, no.
//! endtextmacro
function NewTimerEx takes integer value returns timer
if (tN==0) then
if (not didinit) then
//This extra if shouldn't represent a major performance drawback
//because QUANTITY rule is not supposed to be broken every day.
call init.evaluate()
set tN = tN - 1
else
//If this happens then the QUANTITY rule has already been broken, try to fix the
// issue, else fail.
debug call BJDebugMsg("NewTimer: Warning, Exceeding TimerUtils_QUANTITY, make sure all timers are getting recycled correctly")
set tT[0]=CreateTimer()
static if( not USE_HASH_TABLE) then
debug call BJDebugMsg("In case of errors, please increase it accordingly, or set TimerUtils_USE_HASH_TABLE to true")
static if( USE_FLEXIBLE_OFFSET) then
if (GetHandleId(tT[0])-VOFFSET<0) or (GetHandleId(tT[0])-VOFFSET>=ARRAY_SIZE) then
//all right, couldn't fix it
call BJDebugMsg("NewTimer: Unable to allocate a timer, you should probably set TimerUtils_USE_HASH_TABLE to true or fix timer leaks.")
return null
endif
else
if (GetHandleId(tT[0])-OFFSET<0) or (GetHandleId(tT[0])-OFFSET>=ARRAY_SIZE) then
//all right, couldn't fix it
call BJDebugMsg("NewTimer: Unable to allocate a timer, you should probably set TimerUtils_USE_HASH_TABLE to true or fix timer leaks.")
return null
endif
endif
endif
endif
else
set tN=tN-1
endif
call SetTimerData(tT[tN],value)
return tT[tN]
endfunction
function NewTimer takes nothing returns timer
return NewTimerEx(0)
endfunction
//==========================================================================================
function ReleaseTimer takes timer t returns nothing
if(t==null) then
debug call BJDebugMsg("Warning: attempt to release a null timer")
return
endif
if (tN==ARRAY_SIZE) then
debug call BJDebugMsg("Warning: Timer stack is full, destroying timer!!")
//stack is full, the map already has much more troubles than the chance of bug
call DestroyTimer(t)
else
call PauseTimer(t)
if(GetTimerData(t)==HELD) then
debug call BJDebugMsg("Warning: ReleaseTimer: Double free!")
return
endif
call SetTimerData(t,HELD)
set tT[tN]=t
set tN=tN+1
endif
endfunction
private function init takes nothing returns nothing
local integer i=0
local integer o=-1
local boolean oops = false
if ( didinit ) then
return
else
set didinit = true
endif
static if( USE_HASH_TABLE ) then
set ht = InitHashtable()
loop
exitwhen(i==QUANTITY)
set tT[i]=CreateTimer()
call SetTimerData(tT[i], HELD)
set i=i+1
endloop
set tN = QUANTITY
else
loop
set i=0
loop
exitwhen (i==QUANTITY)
set tT[i] = CreateTimer()
if(i==0) then
set VOFFSET = GetHandleId(tT[i])
static if(USE_FLEXIBLE_OFFSET) then
set o=VOFFSET
else
set o=OFFSET
endif
endif
if (GetHandleId(tT[i])-o>=ARRAY_SIZE) then
exitwhen true
endif
if (GetHandleId(tT[i])-o>=0) then
set i=i+1
endif
endloop
set tN = i
exitwhen(tN == QUANTITY)
set oops = true
exitwhen not USE_FLEXIBLE_OFFSET
debug call BJDebugMsg("TimerUtils_init: Failed a initialization attempt, will try again")
endloop
if(oops) then
static if ( USE_FLEXIBLE_OFFSET) then
debug call BJDebugMsg("The problem has been fixed.")
//If this message doesn't appear then there is so much
//handle id fragmentation that it was impossible to preload
//so many timers and the thread crashed! Therefore this
//debug message is useful.
elseif(DEBUG_MODE) then
call BJDebugMsg("There were problems and the new timer limit is "+I2S(i))
call BJDebugMsg("This is a rare ocurrence, if the timer limit is too low:")
call BJDebugMsg("a) Change USE_FLEXIBLE_OFFSET to true (reduces performance a little)")
call BJDebugMsg("b) or try changing OFFSET to "+I2S(VOFFSET) )
endif
endif
endif
endfunction
endlibrary
scope DemoLib
struct DemoLib
static method onInit takes nothing returns nothing
/* * * * * * * * * * */
/* Murloc Tiderunner */
/* * * * * * * * * * */
call SCDataLib.add('nmrl')
set SCDataLib.isTamed = false
set SCDataLib.canFlee = false
set SCDataLib.fleeDist = 0
set SCDataLib.sleepDur = 7.25
set SCDataLib.sleepTime = 21.0
set SCDataLib.acquisitionRange = 0.0
set SCDataLib.combatDuration = 10.0
set SCDataLib.ambushDelay = 0.0
set SCDataLib.sightRadius = 160.0
set SCDataLib.resetTimer = false
set SCDataLib.callHelp = false
set SCDataLib.callRadius = 0.0
set SCDataLib.maxHelp = 0
set SCDataLib.helpType = 0
set SCDataLib.seekHelp = false
set SCDataLib.seekRadius = 0.0
set SCDataLib.lowHealth = 60.0
set SCDataLib.wanderDelay = 12.0
set SCDataLib.wanderVariant = 7.0
set SCDataLib.wanderDist = 200.0
set SCDataLib.nestArea = 800.0
/* * * * * * * * * * */
/* Draenei Disciple */
/* * * * * * * * * * */
call SCDataLib.add('ndrm')
set SCDataLib.isTamed = false
set SCDataLib.canFlee = false
set SCDataLib.fleeDist = 0
set SCDataLib.sleepDur = 7.25
set SCDataLib.sleepTime = 21.0
set SCDataLib.acquisitionRange = 0.0
set SCDataLib.combatDuration = 10.0
set SCDataLib.ambushDelay = 0.0
set SCDataLib.sightRadius = 160.0
set SCDataLib.resetTimer = false
set SCDataLib.callHelp = false
set SCDataLib.callRadius = 0.0
set SCDataLib.maxHelp = 0
set SCDataLib.helpType = 0
set SCDataLib.seekHelp = false
set SCDataLib.seekRadius = 0.0
set SCDataLib.lowHealth = 75.0
set SCDataLib.wanderDelay = 12.0
set SCDataLib.wanderVariant = 7.0
set SCDataLib.wanderDist = 250.0
set SCDataLib.nestArea = 800.0
/* * * * * * * * * * * */
/* Warrior Arachnatid */
/* * * * * * * * * * * */
call SCDataLib.add('nanw')
set SCDataLib.isTamed = false
set SCDataLib.canFlee = false
set SCDataLib.fleeDist = 0
set SCDataLib.sleepDur = 3.55
set SCDataLib.sleepTime = 23.0
set SCDataLib.acquisitionRange = 400.0
set SCDataLib.combatDuration = 10.0
set SCDataLib.ambushDelay = 3.0
set SCDataLib.sightRadius = 160.0
set SCDataLib.resetTimer = false
set SCDataLib.callHelp = false
set SCDataLib.callRadius = 0.0
set SCDataLib.maxHelp = 0
set SCDataLib.helpType = 2
set SCDataLib.seekHelp = false
set SCDataLib.seekRadius = 0.0
set SCDataLib.lowHealth = 90.0
set SCDataLib.wanderDelay = 7.0
set SCDataLib.wanderVariant = 1.0
set SCDataLib.wanderDist = 170.0
set SCDataLib.nestArea = 800.0
/* * * * * * * * * * */
/* Enraged Elemental */
/* * * * * * * * * * */
call SCDataLib.add('nele')
set SCDataLib.isTamed = false
set SCDataLib.canFlee = false
set SCDataLib.fleeDist = 0
set SCDataLib.sleepDur = 0.0
set SCDataLib.sleepTime = 21.0
set SCDataLib.acquisitionRange = 400.0
set SCDataLib.combatDuration = 5.0
set SCDataLib.ambushDelay = 0.0
set SCDataLib.sightRadius = 300.0
set SCDataLib.resetTimer = true
set SCDataLib.callHelp = false
set SCDataLib.callRadius = 0.0
set SCDataLib.maxHelp = 0
set SCDataLib.helpType = 0
set SCDataLib.seekHelp = false
set SCDataLib.seekRadius = 0.0
set SCDataLib.lowHealth = 115.0
set SCDataLib.wanderDelay = 25.0
set SCDataLib.wanderVariant = 5.0
set SCDataLib.wanderDist = 150.0
set SCDataLib.nestArea = 700.0
/* * * * * * * * * * */
/* Kobold Taskmaster */
/* * * * * * * * * * */
call SCDataLib.add('nkol')
set SCDataLib.isTamed = false
set SCDataLib.canFlee = false
set SCDataLib.fleeDist = 0
set SCDataLib.sleepDur = 8.0
set SCDataLib.sleepTime = 21.0
set SCDataLib.acquisitionRange = 0.0
set SCDataLib.combatDuration = 10.0
set SCDataLib.ambushDelay = 0.0
set SCDataLib.sightRadius = 160.0
set SCDataLib.resetTimer = false
set SCDataLib.callHelp = false
set SCDataLib.callRadius = 0.0
set SCDataLib.maxHelp = 0
set SCDataLib.helpType = 1
set SCDataLib.seekHelp = true
set SCDataLib.seekRadius = 400.0
set SCDataLib.lowHealth = 150.0
set SCDataLib.wanderDelay = 15.0
set SCDataLib.wanderVariant = 10.0
set SCDataLib.wanderDist = 200.0
set SCDataLib.nestArea = 1000.0
/* * * * * * * * * * * */
/* Murgul Shadowcaster */
/* * * * * * * * * * * */
call SCDataLib.add('nmsc')
set SCDataLib.isTamed = false
set SCDataLib.canFlee = false
set SCDataLib.fleeDist = 0
set SCDataLib.sleepDur = 4.0
set SCDataLib.sleepTime = 23.0
set SCDataLib.acquisitionRange = 400.0
set SCDataLib.combatDuration = 10.0
set SCDataLib.ambushDelay = 5.0
set SCDataLib.sightRadius = 400.0
set SCDataLib.resetTimer = true
set SCDataLib.callHelp = true
set SCDataLib.callRadius = 800.0
set SCDataLib.maxHelp = 2
set SCDataLib.helpType = 0
set SCDataLib.seekHelp = true
set SCDataLib.seekRadius = 400.0
set SCDataLib.lowHealth = 180.0
set SCDataLib.wanderDelay = 17.0
set SCDataLib.wanderVariant = 10.0
set SCDataLib.wanderDist = 100.0
set SCDataLib.nestArea = 800.0
/* * * * * * * * * * * */
/* Murloc Nightcrawler */
/* * * * * * * * * * * */
call SCDataLib.add('nmrm')
set SCDataLib.isTamed = false
set SCDataLib.canFlee = false
set SCDataLib.fleeDist = 0
set SCDataLib.sleepDur = 4.0
set SCDataLib.sleepTime = 23.0
set SCDataLib.acquisitionRange = 0.0
set SCDataLib.combatDuration = 10.0
set SCDataLib.ambushDelay = 0.0
set SCDataLib.sightRadius = 200.0
set SCDataLib.resetTimer = false
set SCDataLib.callHelp = false
set SCDataLib.callRadius = 0.0
set SCDataLib.maxHelp = 0
set SCDataLib.helpType = 2
set SCDataLib.seekHelp = false
set SCDataLib.seekRadius = 0.0
set SCDataLib.lowHealth = 190.0
set SCDataLib.wanderDelay = 10.0
set SCDataLib.wanderVariant = 3.0
set SCDataLib.wanderDist = 400.0
set SCDataLib.nestArea = 800.0
call SCDataLib.end()
endmethod
endstruct
endscope
library SRS initializer ini
/*****************************************************************************************************/
/** **/
/** 'Safe Revive System v1.6' **/
/** ***** **/
/** by Dalvengyr aka ..... **/
/** **/
/* Why safe? This revive system allows you to revive any organic non-Hero units without recreating */
/* them. In the demo map there will be shown floating shits that shows units handle that is not */
/* changed after revived. What are the advantages of using this system? This method is faster than */
/* recreating new unit, and save spaces in memory. The only thing you need to do is set units death */
/* type to 'can raise, does decay'. */
/* */
/* Requirement: */
/* - JNGP */
/* */
/* Disadvantages: */
/* - None */
/* */
/* Implementation: */
/* 1. Copy SRS folder into your map */
/* 2. Copy dummy unit and spell into your map */
/* 3. Make sure dummy and spell id below is the same with their rawcode at OE */
/* 4. If you have dummy.mdx in your map, then open OE, set dummy units file model to that */
/* dummy.mdx is better */
/* 5. Nuff said. Done. */
/* */
/* APIs: */
/* 1. Revive any unit you want at their current position */
/* */
/* function ReviveUnit takes unit whichUnit returns boolean */
/* */
/* 2. Revive any unit you want at certain point */
/* Set boolean flag to true to check target point pathability */
/* Keep in mind that checking pathability is always slower than not */
/* */
/* function ReviveUnitAtPoint takes unit whichUnit, real x, real y, boolean flag returns boolean */
/** **/
/*****************************************************************************************************/
/*****************************************************************************************************/
globals
/* 'CONFIGURATIONS
1. Dummy Id */
private constant integer DUMMY_ID = 'h000'
/* 2. Spell Id */
private constant integer SPELL_ID = 'A002'
/* 3. Ressurection Order Id */
private constant integer ORDER_ID = 852094
/* 'END-OF-CONFIGURATIONS' */
private unit DUMMY
endglobals
private constant function FilterUnits takes unit u returns boolean
return not(IsUnitType(u, UNIT_TYPE_STRUCTURE) or IsUnitType(u, UNIT_TYPE_MECHANICAL)) and not IsUnitType(u, UNIT_TYPE_HERO)
endfunction
private function ini takes nothing returns nothing
set DUMMY = CreateUnit(Player(15), DUMMY_ID, 0.0, 0.0, 270.0)
call UnitAddAbility(DUMMY, SPELL_ID)
endfunction
/*' APIs '*/
function ReviveUnit takes unit whichUnit returns boolean
if FilterUnits(whichUnit) then
if IsUnitType(whichUnit, UNIT_TYPE_DEAD) and GetUnitTypeId(whichUnit) != 0 then
call SetUnitOwner(DUMMY, GetOwningPlayer(whichUnit), false)
call SetUnitX(DUMMY, GetUnitX(whichUnit))
call SetUnitY(DUMMY, GetUnitY(whichUnit))
call IssueImmediateOrderById(DUMMY, ORDER_ID)
return true
endif
endif
return false
endfunction
function ReviveUnitAtPoint takes unit whichUnit, real x, real y, boolean flag returns boolean
if ReviveUnit(whichUnit) then
if flag then
call SetUnitPosition(whichUnit, x, y)
else
call SetUnitX(whichUnit, x)
call SetUnitY(whichUnit, y)
endif
return true
endif
return false
endfunction
endlibrary