//TESH.scrollpos=0
//TESH.alwaysfold=0
Name | Type | is_array | initial_value |
TempPoint | location | No | |
TempReal | real | No | |
TempUnit | unit | No | |
Timer | timer | No |
//TESH.scrollpos=6
//TESH.alwaysfold=0
//==============================================================================
// GRAVITY SPELLPACK v1.2
// BY Ayanami
//==============================================================================
//==============================================================================
// REQUIREMENTS
//==============================================================================
// - JNGP
// - Damage
// * Event
// * AIDS
// - Timer32
// - GTrigger
// - GroupUtils
//==============================================================================
//==============================================================================
// IMPLEMENTATION
//==============================================================================
// 1) Copy the whole "Required Systems" Trigger folder
// 2) Save the map, it will take a bit longer than usual
// 3) Close the map and re-open it, then disable or delete the trigger "Objects"
// 4) Go to the Import Manager and export all resources, then import into map
// 5) Copy all 4 abilities under "Night Elf"
// 6) Copy the whole "Gravity Spellpack" Trigger folder
//==============================================================================
//TESH.scrollpos=233
//TESH.alwaysfold=0
scope InversionField // requires Damage, Timer32, GTrigger, GroupUtils
//===========================================================================
// CONFIGURABLES
//===========================================================================
globals
private constant integer ABILID = 'ABIF' // raw code of ability "Inversion Field"
private constant integer ABILDUMID = 'AIF0' // raw code of ability "Inversion Field (Buff)"
private constant integer BUFFID = 'BIF0' // raw code of buff "Inversion Field"
private constant integer PRELOADID = 'prel' // raw code of unit "Preloader"
private constant string WALLART = "Abilities\\Spells\\Undead\\DarkSummoning\\DarkSummonMissile.mdl" // model used as field wall
private constant string DAMAGEART = "Abilities\\Weapons\\WingedSerpentMissile\\WingedSerpentMissile.mdl" // model used when damaged
private constant string DAMAGEATTACH = "chest" // attachment point of model
private constant real THICKNESS = 100.0 // thickness of the field wall
private constant boolean GFBOOLEAN = true // set to true if Gravity Flux will be used
private constant boolean GABOOLEAN = true // set to true if Graviton Acceleration will be used
endglobals
private function GetCastArea takes integer level returns real
return 500.0 // area of cast
endfunction
private function GetArea takes integer level returns real
return 550.0 - (50.0 * level) // area of field
endfunction
private function GetDuration takes integer level returns real
return 4.0 // duration of spell
endfunction
private function GetReduction takes integer level returns real
return 0.9 - (0.1 * level) // reduction in damage taken where, 1.0 == 100%
endfunction
private function GetWallCount takes integer level returns integer
return 30 - (3 * level) // number of effect created
endfunction
//===========================================================================
// END CONFIGURABLES
//===========================================================================
// Do not touch below unless you know what you're doing
native UnitAlive takes unit id returns boolean
private struct Data
real duration
real aoe
real castaoe
real x
real y
integer level
group tgroup
static thistype tempData
static unit tempCaster
static hashtable Hash = InitHashtable()
private static method staticCheck takes unit u returns boolean
local boolean check = true
static if GFBOOLEAN then
set check = GetUnitTypeId(u) != GravityFlux_DUMMYID
endif
static if GABOOLEAN then
if not check then
set check = not IsUnitInGroup(u, GravitonAcceleration_Checkgroup)
endif
endif
return check
endmethod
private static method groupFilter takes nothing returns boolean
local thistype this = tempData
local unit u = GetFilterUnit()
local real a
local real dx
local real dy
if IsUnitEnemy(u, GetOwningPlayer(tempCaster)) and UnitAlive(u) then
if GetUnitAbilityLevel(u, ABILDUMID) == 0 then
call UnitAddAbility(u, ABILDUMID)
endif
call SetUnitAbilityLevel(u, ABILDUMID, this.level)
set u = null
return true
else
set dx = GetUnitX(u) - this.x
set dy = GetUnitY(u) - this.y
if dx * dx + dy * dy < this.aoe * this.aoe then
set a = Atan2(dy, dx)
set dx = this.x + (this.aoe + THICKNESS) * Cos(a)
set dy = this.y + (this.aoe + THICKNESS) * Sin(a)
call SetUnitX(u, dx)
call SetUnitY(u, dy)
endif
set u = null
endif
return false
endmethod
private static method groupFunc takes nothing returns nothing
local thistype this = tempData
local unit u = GetEnumUnit()
local real dx = GetUnitX(u) - this.x
local real dy = GetUnitY(u) - this.y
local real a
if GetUnitAbilityLevel(u, ABILDUMID) == 0 then
call UnitAddAbility(u, ABILDUMID)
call SetUnitAbilityLevel(u, ABILDUMID, this.level)
endif
if SquareRoot(dx * dx + dy * dy) > this.aoe - THICKNESS and thistype.staticCheck(u) then
set a = Atan2(dy, dx)
set dx = this.x + (this.aoe - THICKNESS - 50) * Cos(a)
set dy = this.y + (this.aoe - THICKNESS - 50) * Sin(a)
call SetUnitX(u, dx)
call SetUnitY(u, dy)
endif
set u = null
endmethod
private static method groupAction takes nothing returns boolean
local thistype this = tempData
local unit u = GetFilterUnit()
local real dx
local real dy
local real a
if not IsUnitInGroup(u, this.tgroup) and thistype.staticCheck(u) then
set a = Atan2(GetUnitY(u) - this.y, GetUnitX(u) - this.x)
set dx = this.x + (this.aoe + THICKNESS + 50) * Cos(a)
set dy = this.y + (this.aoe + THICKNESS + 50) * Sin(a)
call SetUnitX(u, dx)
call SetUnitY(u, dy)
endif
set u = null
return false
endmethod
private static method removeGroup takes nothing returns nothing
call UnitRemoveAbility(GetEnumUnit(), ABILDUMID)
call UnitRemoveAbility(GetEnumUnit(), BUFFID)
endmethod
private method periodic takes nothing returns nothing
local integer i
local real dx
local real dy
if this.duration <= 0 then
set i = LoadInteger(Hash, this, StringHash("count"))
loop
exitwhen i < 0
call DestroyEffect(LoadEffectHandle(Hash, this, i))
set i = i - 1
endloop
call ForGroup(this.tgroup, function thistype.removeGroup)
call ReleaseGroup(this.tgroup)
call FlushChildHashtable(Hash, this)
call this.stopPeriodic()
call this.deallocate()
else
set tempData = this
call ForGroup(this.tgroup, function thistype.groupFunc)
call GroupEnumUnitsInArea(ENUM_GROUP, this.x, this.y, this.aoe + THICKNESS, Filter(function thistype.groupAction))
set this.duration = this.duration - T32_PERIOD
endif
endmethod
implement T32x
static method actions takes nothing returns nothing
local thistype this = thistype.allocate()
local unit u = GetTriggerUnit()
local integer i
local integer count
local real a
local real x
local real y
set this.level = GetUnitAbilityLevel(u, ABILID)
set this.duration = GetDuration(this.level)
set this.aoe = GetArea(this.level)
set this.castaoe = GetCastArea(this.level)
set this.x = GetSpellTargetX()
set this.y = GetSpellTargetY()
set this.tgroup = NewGroup()
set tempCaster = u
set tempData = this
call GroupEnumUnitsInArea(this.tgroup, this.x, this.y, this.castaoe, Filter(function thistype.groupFilter))
set count = GetWallCount(this.level)
set i = 0
loop
exitwhen i == count
set a = ((bj_PI * 2) / count) * i
set x = this.x + this.aoe * Cos(a)
set y = this.y + this.aoe * Sin(a)
call SaveEffectHandle(Hash, this, i, AddSpecialEffect(WALLART, x, y))
set i = i + 1
endloop
call SaveInteger(Hash, this, StringHash("count"), i - 1)
call this.startPeriodic()
set u = null
endmethod
private static method damageReduction takes nothing returns boolean
local unit u = GetEventDamageSource()
local unit t = GetTriggerUnit()
local integer sourcelevel = GetUnitAbilityLevel(u, ABILDUMID)
local integer targetlevel = GetUnitAbilityLevel(t, ABILDUMID)
if sourcelevel > 0 and targetlevel > 0 then
elseif sourcelevel > 0 then
call Damage_Block(GetEventDamage() * GetReduction(sourcelevel))
call DestroyEffect(AddSpecialEffectTarget(DAMAGEART, t, DAMAGEATTACH))
elseif targetlevel > 0 then
call Damage_Block(GetEventDamage() * GetReduction(targetlevel))
call DestroyEffect(AddSpecialEffectTarget(DAMAGEART, t, DAMAGEATTACH))
endif
set u = null
set t = null
return false
endmethod
private static method onInit takes nothing returns nothing
local trigger t = CreateTrigger()
local unit u = CreateUnit(Player(13), PRELOADID, 0, 0, 0)
call GT_RegisterStartsEffectEvent(t, ABILID)
call TriggerAddAction(t, function thistype.actions)
set t = CreateTrigger()
call Damage_RegisterEvent(t)
call TriggerAddCondition(t, Condition(function thistype.damageReduction))
set t = null
//preload
call UnitAddAbility(u, ABILDUMID)
call RemoveUnit(u)
set u = null
endmethod
endstruct
endscope
//TESH.scrollpos=0
//TESH.alwaysfold=0
scope GravityMatrix initializer OnInit // requires Damage, GTrigger, GroupUtils
//===========================================================================
// CONFIGURABLES
//===========================================================================
globals
private constant integer ABILID = 'ABGM' // raw code of ability "Gravity Matrix"
private constant string DAMAGEART = "Abilities\\Weapons\\WingedSerpentMissile\\WingedSerpentMissile.mdl" // model used when damaged
private constant string DAMAGEATTACH = "chest" // attachment point of model
private constant string MANAART = "Abilities\\Weapons\\Bolt\\BoltImpact.mdl" // model used when damaging enemy units
private constant string MANAATTACH = "origin" // attachment point of model
private constant attacktype ATK = ATTACK_TYPE_NORMAL // attack type of damage
private constant damagetype DMG = DAMAGE_TYPE_MAGIC // damage type of damage
private constant weapontype WEP = WEAPON_TYPE_WHOKNOWS // weapon type of damage
endglobals
private function GetDistance takes integer level returns real
return 1000.0 // distance where damage reduction becomes maximum
endfunction
private function GetReduction takes integer level returns real
return 0.06 + (0.11 * level) // maximum damage reduction where, 1.00 = 100%
endfunction
private function GetManaInterval takes integer level returns real
return 100.0 // every interval of mana where damage is unleashed
endfunction
private function GetArea takes integer level returns real
return 500.0 // area of damage
endfunction
//===========================================================================
// END CONFIGURABLES
//===========================================================================
// Do not touch below unless you know what you're doing
native UnitAlive takes unit id returns boolean
globals
private hashtable Hash = InitHashtable()
private trigger Trig
private unit Tempunit
private real Tempreal
endglobals
private function GroupFunc takes nothing returns boolean
local unit u = GetFilterUnit()
if IsUnitEnemy(u, GetOwningPlayer(Tempunit)) and UnitAlive(u) then
call DisableTrigger(Trig)
call UnitDamageTargetEx(Tempunit, u, Tempreal, true, false, ATK, DMG, WEP)
call DestroyEffect(AddSpecialEffectTarget(MANAART, u, MANAATTACH))
call EnableTrigger(Trig)
endif
set u = null
return false
endfunction
private function Learn takes nothing returns boolean
local unit u = GetTriggerUnit()
if GetUnitAbilityLevel(u, ABILID) == 1 then
call SaveReal(Hash, GetHandleId(u), 0, 0.0)
endif
set u = null
return false
endfunction
private function Actions takes nothing returns boolean
local unit u
local integer i
local integer count
local integer id
local real r
local real x
local real y
local real dist
local real maxdist
set Tempunit = GetTriggerUnit()
set i = GetUnitAbilityLevel(Tempunit, ABILID)
set id = GetHandleId(Tempunit)
set maxdist = GetDistance(i)
if i > 0 then
set u = GetEventDamageSource()
set x = GetUnitX(u) - GetUnitX(Tempunit)
set y = GetUnitY(u) - GetUnitY(Tempunit)
set dist = SquareRoot(x * x + y * y)
if dist > maxdist then
set dist = maxdist
endif
set r = GetEventDamage() * GetReduction(i) * (dist / maxdist)
call Damage_Block(r)
call DestroyEffect(AddSpecialEffectTarget(DAMAGEART, Tempunit, DAMAGEATTACH))
call SetUnitState(Tempunit, UNIT_STATE_MANA, GetUnitState(Tempunit, UNIT_STATE_MANA) + r)
set r = r + LoadReal(Hash, id, 0)
if r >= GetManaInterval(i) then
set count = R2I(r / GetManaInterval(i))
call SaveReal(Hash, id, 0, r - (GetManaInterval(i) * count))
if GetUnitState(Tempunit, UNIT_STATE_MANA) >= GetManaInterval(i) then
set Tempreal = GetManaInterval(i)
loop
exitwhen count == 0
call GroupEnumUnitsInArea(ENUM_GROUP, GetUnitX(Tempunit), GetUnitY(Tempunit), GetArea(i), Filter(function GroupFunc))
call SetUnitState(Tempunit, UNIT_STATE_MANA, GetUnitState(Tempunit, UNIT_STATE_MANA) - GetManaInterval(i))
set count = count - 1
endloop
endif
else
call SaveReal(Hash, id, 0, r)
endif
endif
set u = null
return false
endfunction
private function OnInit takes nothing returns nothing
local trigger t = CreateTrigger()
call GT_RegisterLearnsAbilityEvent(t, ABILID)
call TriggerAddCondition(t, Condition(function Learn))
set Trig = CreateTrigger()
call Damage_RegisterEvent(Trig)
call TriggerAddCondition(Trig, Condition(function Actions))
set t = null
endfunction
endscope
//TESH.scrollpos=0
//TESH.alwaysfold=0
scope GravityFlux // requires Damage, Timer32, GTrigger, GroupUtils
native UnitAlive takes unit id returns boolean
//===========================================================================
// CONFIGURABLES
//===========================================================================
globals
private constant integer ABILID = 'ABGF' // raw code of ability "Gravity Flux"
public constant integer DUMMYID = 'grFl' // raw code of unit "Gravity Flux Dummy"
private constant boolean SELF = true // true if includes mana cost
private constant real TIME = 1.0 // time taken for balls to reach target point
private constant string ART = "Objects\\Spawnmodels\\NightElf\\NEDeathMedium\\NEDeath.mdl" // effect upon enemy damage
private constant string ARTPOINT = "origin" // attachment point of damage effect
private constant string DEATHART = "Objects\\Spawnmodels\\NightElf\\NEDeathMedium\\NEDeath.mdl" // effect upon projectile death
private constant attacktype ATK = ATTACK_TYPE_NORMAL // attack type of damage
private constant damagetype DMG = DAMAGE_TYPE_MAGIC // damage type of damage
private constant weapontype WEP = WEAPON_TYPE_WHOKNOWS // weapon type of damage
endglobals
private function GetMana takes integer level returns real
return 25.0 + (25.0 * level) // mana absorbed from unit
endfunction
private function GetTargetAoe takes integer level returns real
return 400.0 // target area of effect
endfunction
private function GetAoe takes integer level returns real
return 400.0 // area of effect of damage
endfunction
private function FilterTarget takes unit owner, unit target returns boolean
return UnitAlive(target) or IsUnitEnemy(target, GetOwningPlayer(owner)) // targets allowed for damage
endfunction
//===========================================================================
// END CONFIGURABLES
//===========================================================================
private struct Data
real duration
real mana
real x
real y
integer level
integer count = 0
group ballgroup
group damagegroup
unit caster
static Data tempData
static hashtable Hash = InitHashtable()
static unit tempUnit
static real tempX
static real tempY
static real tempReal
static integer tempInt
private static method damageFunc takes nothing returns nothing
local thistype this = tempData
local unit u = GetEnumUnit()
call UnitDamageTargetEx(this.caster, u, this.mana, true, false, ATK, DMG, WEP)
call DestroyEffect(AddSpecialEffectTarget(ART, u, ARTPOINT))
set u = null
endmethod
private static method countFunc takes nothing returns boolean
local thistype this = tempData
local unit u = GetFilterUnit()
if IsUnitEnemy(u, GetOwningPlayer(this.caster)) and UnitAlive(u) then
set this.count = this.count + 1
call GroupAddUnit(this.damagegroup, u)
endif
set u = null
return false
endmethod
private static method groupFunc takes nothing returns nothing
local thistype this = tempData
local unit u = GetEnumUnit()
local real x = GetUnitX(u)
local real y = GetUnitY(u)
local real a = Atan2(this.y - y, this.x - x)
local real dist = LoadReal(Hash, GetHandleId(u), 0)
set x = x + dist * Cos(a)
set y = y + dist * Sin(a)
call SetUnitPosition(u, x, y)
set u = null
endmethod
private static method filterFunc takes nothing returns boolean
local thistype this = tempData
local unit u = GetFilterUnit()
local real x
local real y
local real mana
local real unitmana
if FilterTarget(this.caster, u) then
set x = GetUnitX(u)
set y = GetUnitY(u)
set mana = GetMana(this.level)
set unitmana = GetUnitState(u, UNIT_STATE_MANA)
if unitmana < mana then
set mana = unitmana
endif
set this.mana = this.mana + mana
set tempUnit = CreateUnit(GetOwningPlayer(this.caster), DUMMYID, x, y, Atan2(this.y - y, this.x - x) * bj_RADTODEG)
call GroupAddUnit(this.ballgroup, tempUnit)
call SetUnitState(u, UNIT_STATE_MANA, unitmana - mana)
set x = x - this.x
set y = y - this.y
call SaveReal(Hash, GetHandleId(tempUnit), 0, (SquareRoot(x * x + y * y) / TIME) * T32_PERIOD)
endif
set u = null
return false
endmethod
private static method GroupFlush takes nothing returns nothing
local unit u = GetEnumUnit()
local thistype this = tempData
call FlushChildHashtable(Hash, GetHandleId(u))
call GroupRemoveUnit(this.ballgroup, u)
call RemoveUnit(u)
set u = null
endmethod
private method periodic takes nothing returns nothing
if this.duration <= 0 then
set tempData = this
call GroupEnumUnitsInArea(ENUM_GROUP, this.x, this.y, GetAoe(this.level), Filter(function thistype.countFunc))
if this.count > 0 then
set this.mana = this.mana / this.count
call ForGroup(this.damagegroup, function thistype.damageFunc)
endif
call DestroyEffect(AddSpecialEffect(DEATHART, this.x, this.y))
call ForGroup(this.ballgroup, function thistype.GroupFlush)
call ReleaseGroup(this.ballgroup)
call this.stopPeriodic()
call this.deallocate()
else
set tempData = this
call ForGroup(this.ballgroup, function thistype.groupFunc)
set this.duration = this.duration - T32_PERIOD
endif
endmethod
implement T32x
private static method create takes unit caster, integer level, real x, real y, real mana returns thistype
local thistype this = thistype.allocate()
set this.duration = TIME
set this.mana = mana
set this.x = x
set this.y = y
set this.level = level
set this.count = 0
set this.ballgroup = NewGroup()
set this.damagegroup = NewGroup()
set this.caster = caster
static if SELF then
set tempX = GetUnitX(this.caster)
set tempY = GetUnitY(this.caster)
set tempUnit = CreateUnit(GetOwningPlayer(this.caster), DUMMYID, tempX, tempY, Atan2(y - tempY, x - tempX) * bj_RADTODEG)
set tempX = tempX - this.x
set tempY = tempY - this.y
call GroupAddUnit(this.ballgroup, tempUnit)
call SaveReal(Hash, GetHandleId(tempUnit), 0, (SquareRoot(tempX * tempX + tempY * tempY) / TIME) * T32_PERIOD)
endif
set tempData = this
call GroupEnumUnitsInArea(ENUM_GROUP, this.x, this.y, GetTargetAoe(this.level), Filter(function thistype.filterFunc))
call this.startPeriodic()
return this
endmethod
private static method manaTimeOut takes nothing returns nothing
local timer t = GetExpiredTimer()
call thistype.create(tempUnit, tempInt, tempX, tempY, tempReal - GetUnitState(tempUnit, UNIT_STATE_MANA))
call PauseTimer(t)
call DestroyTimer(t)
set t = null
endmethod
private static method actions takes nothing returns nothing
local timer t
set tempUnit = GetTriggerUnit()
set tempInt = GetUnitAbilityLevel(tempUnit, ABILID)
set tempX = GetSpellTargetX()
set tempY = GetSpellTargetY()
static if SELF then
set t = CreateTimer()
set tempReal = GetUnitState(tempUnit, UNIT_STATE_MANA)
call TimerStart(t, 0.00, false, function thistype.manaTimeOut)
else
call thistype.create(tempUnit, tempInt, tempX, tempY, 0)
endif
set t = null
endmethod
private static method onInit takes nothing returns nothing
local trigger t = CreateTrigger()
call GT_RegisterStartsEffectEvent(t, ABILID)
call TriggerAddAction(t, function thistype.actions)
set t = null
//preload
call RemoveUnit(CreateUnit(Player(13), DUMMYID, 0, 0, 0))
endmethod
endstruct
endscope
//TESH.scrollpos=0
//TESH.alwaysfold=0
scope GravitonAcceleration // requires Damage, Timer32, GTrigger, GroupUtils
//===========================================================================
// CONFIGURABLES
//===========================================================================
globals
private constant integer ABILID = 'ABGA' // raw code of ability "Graviton Acceleration"
private constant integer DUMMYID = 'grAc' // raw code of unit "Graviton Acceleration Dummy"
private constant real MAXSPEED = 200.0 // maximum drag speed
private constant real SPINRATE = 2.0 // number of spins in the center area per second
private constant string LIGHTNING = "DRAM" // lightning effect used, small note that lightning effect
// can generate lag when dealing with mass units
private constant boolean ALLOWLIGHT = true // true if lightning effect is used
private constant attacktype ATK = ATTACK_TYPE_NORMAL // attack type of damage
private constant damagetype DMG = DAMAGE_TYPE_MAGIC // damage type of damage
private constant weapontype WEP = WEAPON_TYPE_WHOKNOWS // weapon type of damage
endglobals
private function GetCenterAoe takes integer level returns real
return 100.0 // radius of center area
endfunction
private function GetAoe takes integer level returns real
return 600.0 // area of effect of spell
endfunction
private function GetAbsorbAmount takes integer level returns real
return 10.0 + (10.0 * level) // amount of mana absorbed per second
endfunction
private function GetDuration takes integer level returns real
return 5.0 // duration of spell
endfunction
//===========================================================================
// END CONFIGURABLES
//===========================================================================
native UnitAlive takes unit id returns boolean
globals
public group Checkgroup = CreateGroup()
endglobals
private struct Data
integer level
real x
real y
real duration
real amount
real angle
real center
real aoe
group track
unit caster
unit dummy
static Data tempData
static hashtable Hash = InitHashtable()
private static method groupFunc takes nothing returns nothing
local thistype this = tempData
local unit u = GetEnumUnit()
local real x = GetUnitX(u)
local real y = GetUnitY(u)
local real dx = x - this.x
local real dy = y - this.y
local real a
local real mana
local real dist = SquareRoot(dx * dx + dy * dy)
local integer id = GetHandleId(u)
local boolean check
if dist > this.aoe or not UnitAlive(u) then
call DestroyLightning(LoadLightningHandle(Hash, this, id))
call GroupRemoveUnit(this.track, u)
call GroupRemoveUnit(Checkgroup, u)
else
set mana = GetUnitState(u, UNIT_STATE_MANA)
if mana >= this.amount then
call SetUnitState(u, UNIT_STATE_MANA, mana - this.amount)
else
call UnitDamageTargetEx(this.caster, u, this.amount, true, false, ATK, DMG, WEP)
call SetUnitState(u, UNIT_STATE_MANA, 0)
endif
set check = mana >= 1
set a = LoadReal(Hash, id, 0) + (this.angle)
set dx = this.x + this.center * Cos(a)
set dy = this.y + this.center * Sin(a)
call MoveLightningEx(LoadLightningHandle(Hash, this, id), true, dx, dy, 0, x, y, GetUnitFlyHeight(u) + 50.0)
call SaveReal(Hash, id, 0, a)
if dist > this.center then
set a = Atan2(this.y - y, this.x - x)
if check then
set dist = MAXSPEED * T32_PERIOD
else
set dist = MAXSPEED * (1.0 - (mana / GetUnitState(u, UNIT_STATE_MAX_MANA))) * T32_PERIOD
endif
set dx = x + dist * Cos(a)
set dy = y + dist * Sin(a)
endif
if check then
call SetUnitX(u, dx)
call SetUnitY(u, dy)
else
call SetUnitPosition(u, dx, dy)
endif
endif
set u = null
endmethod
private static method filterFunc takes nothing returns boolean
local thistype this = tempData
local unit u = GetFilterUnit()
local real a
local real x
local real y
if IsUnitEnemy(u, GetOwningPlayer(this.caster)) and UnitAlive(u) and not IsUnitInGroup(u, this.track) then
set x = GetUnitX(u)
set y = GetUnitY(u)
set a = Atan2(y - this.y, x - this.x)
static if ALLOWLIGHT then
call SaveLightningHandle(Hash, this, GetHandleId(u), AddLightning(LIGHTNING, true, this.x + this.center + Cos(a), this.y + this.center + Sin(a), x, y))
endif
call SaveReal(Hash, GetHandleId(u), 0, a)
call GroupAddUnit(this.track, u)
call GroupAddUnit(Checkgroup, u)
endif
set u = null
return false
endmethod
private static method groupFlush takes nothing returns nothing
local thistype this = tempData
local unit u = GetEnumUnit()
local integer id = GetHandleId(u)
static if ALLOWLIGHT then
call DestroyLightning(LoadLightningHandle(Hash, this, id))
endif
call GroupRemoveUnit(Checkgroup, u)
set u = null
endmethod
private method periodic takes nothing returns nothing
set tempData = this
if this.duration <= 0 then
call KillUnit(this.dummy)
call ForGroup(this.track, function thistype.groupFlush)
call FlushChildHashtable(Hash, this)
call ReleaseGroup(this.track)
call this.stopPeriodic()
call this.deallocate()
else
call GroupEnumUnitsInArea(ENUM_GROUP, this.x, this.y, this.aoe, Filter(function thistype.filterFunc))
call ForGroup(this.track, function thistype.groupFunc)
set this.duration = this.duration - T32_PERIOD
endif
endmethod
implement T32x
static method actions takes nothing returns nothing
local thistype this = thistype.allocate()
set this.caster = GetTriggerUnit()
set this.level = GetUnitAbilityLevel(this.caster, ABILID)
set this.x = GetSpellTargetX()
set this.y = GetSpellTargetY()
set this.duration = GetDuration(this.level)
set this.amount = GetAbsorbAmount(this.level) * T32_PERIOD
set this.angle = bj_PI * 2 * (T32_PERIOD / SPINRATE)
set this.center = GetCenterAoe(this.level)
set this.aoe = GetAoe(this.level)
set this.track = NewGroup()
set this.dummy = CreateUnit(GetOwningPlayer(this.caster), DUMMYID, this.x, this.y, 0)
call this.startPeriodic()
endmethod
private static method onInit takes nothing returns nothing
local trigger t = CreateTrigger()
call GT_RegisterStartsEffectEvent(t, ABILID)
call TriggerAddAction(t, function thistype.actions)
set t = null
//preload
call RemoveUnit(CreateUnit(Player(13), DUMMYID, 0, 0, 0))
endmethod
endstruct
endscope
//TESH.scrollpos=12
//TESH.alwaysfold=0
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// ~~ Event ~~ By Jesus4Lyf ~~ Version 1.04 ~~
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// What is Event?
// - Event simulates Warcraft III events. They can be created,
// registered for, fired and also destroyed.
// - Event, therefore, can also be used like a trigger "group".
// - This was created when there was an influx of event style systems
// emerging that could really benefit from a standardised custom
// events snippet. Many users were trying to achieve the same thing
// and making the same kind of errors. This snippet aims to solve that.
//
// Functions:
// - Event.create() --> Creates a new Event.
// - .destroy() --> Destroys an Event.
// - .fire() --> Fires all triggers which have been
// registered on this Event.
// - .register(trigger) --> Registers another trigger on this Event.
// - .unregister(trigger) --> Unregisters a trigger from this Event.
//
// Details:
// - Event is extremely efficient and lightweight.
// - It is safe to use with dynamic triggers.
// - Internally, it is just a linked list. Very simple.
//
// How to import:
// - Create a trigger named Event.
// - Convert it to custom text and replace the whole trigger text with this.
//
// Thanks:
// - Builder Bob for the trigger destroy detection method.
// - Azlier for inspiring this by ripping off my dodgier code.
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
library Event
///////////////
// EventRegs //
////////////////////////////////////////////////////////////////////////////
// For reading this far, you can learn one thing more.
// Unlike normal Warcraft III events, you can attach to Event registries.
//
// Event Registries are registrations of one trigger on one event.
// These cannot be created or destroyed, just attached to.
//
// It is VERY efficient for loading and saving data.
//
// Functions:
// - set eventReg.data = someStruct --> Store data.
// - eventReg.data --> Retreive data.
// - Event.getTriggeringEventReg() --> Get the triggering EventReg.
// - eventReg.destroy() --> Undo this registration.
//
private keyword destroyNode
struct EventReg extends array
integer data
method clear takes nothing returns nothing
set this.data=0
endmethod
method destroy takes nothing returns nothing
call Event(this).destroyNode()
endmethod
endstruct
private module Stack
static thistype top=0
static method increment takes nothing returns nothing
set thistype.top=thistype(thistype.top+1)
endmethod
static method decrement takes nothing returns nothing
set thistype.top=thistype(thistype.top-1)
endmethod
endmodule
private struct EventStack extends array
implement Stack
Event current
endstruct
struct Event
private trigger trig
private thistype next
private thistype prev
static method getTriggeringEventReg takes nothing returns EventReg
return EventStack.top.current
endmethod
static method create takes nothing returns Event
local Event this=Event.allocate()
set this.next=this
set this.prev=this
return this
endmethod
private static trigger currentTrigger
method fire takes nothing returns nothing
local thistype curr=this.next
call EventStack.increment()
loop
exitwhen curr==this
set thistype.currentTrigger=curr.trig
if IsTriggerEnabled(thistype.currentTrigger) then
set EventStack.top.current=curr
if TriggerEvaluate(thistype.currentTrigger) then
call TriggerExecute(thistype.currentTrigger)
endif
else
call EnableTrigger(thistype.currentTrigger) // Was trigger destroyed?
if IsTriggerEnabled(thistype.currentTrigger) then
call DisableTrigger(thistype.currentTrigger)
else // If trigger destroyed...
set curr.next.prev=curr.prev
set curr.prev.next=curr.next
call curr.deallocate()
endif
endif
set curr=curr.next
endloop
call EventStack.decrement()
endmethod
method register takes trigger t returns EventReg
local Event new=Event.allocate()
set new.prev=this.prev
set this.prev.next=new
set this.prev=new
set new.next=this
set new.trig=t
call EventReg(new).clear()
return new
endmethod
method destroyNode takes nothing returns nothing // called on EventReg
set this.prev.next=this.next
set this.next.prev=this.prev
call this.deallocate()
endmethod
method unregister takes trigger t returns nothing
local thistype curr=this.next
loop
exitwhen curr==this
if curr.trig==t then
set curr.next.prev=curr.prev
set curr.prev.next=curr.next
call curr.deallocate()
return
endif
set curr=curr.next
endloop
endmethod
method destroy takes nothing returns nothing
local thistype curr=this.next
loop
call curr.deallocate()
exitwhen curr==this
set curr=curr.next
endloop
endmethod
method chainDestroy takes nothing returns nothing
call this.destroy() // backwards compatability.
endmethod
endstruct
/////////////////////////////////////////////////////
// Demonstration Functions & Alternative Interface //
////////////////////////////////////////////////////////////////////////////
// What this would look like in normal WC3 style JASS (should all inline).
//
function CreateEvent takes nothing returns Event
return Event.create()
endfunction
function DestroyEvent takes Event whichEvent returns nothing
call whichEvent.chainDestroy()
endfunction
function FireEvent takes Event whichEvent returns nothing
call whichEvent.fire()
endfunction
function TriggerRegisterEvent takes trigger whichTrigger, Event whichEvent returns EventReg
return whichEvent.register(whichTrigger)
endfunction
// And for EventRegs...
function SetEventRegData takes EventReg whichEventReg, integer data returns nothing
set whichEventReg.data=data
endfunction
function GetEventRegData takes EventReg whichEventReg returns integer
return whichEventReg.data
endfunction
function GetTriggeringEventReg takes nothing returns integer
return Event.getTriggeringEventReg()
endfunction
endlibrary
//TESH.scrollpos=18
//TESH.alwaysfold=0
//
// _ ___ ___ ___ _______________________________________________
// /_\ |_ _| \/ __| || A D V A N C E D I N D E X I N G ||
// / _ \ | || |) \__ \ || A N D ||
// /_/ \_\___|___/|___/ || D A T A S T O R A G E ||
// By Jesus4Lyf ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
// v 1.0.5
// What is AIDS?
// ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
// AIDS assigns unique integers between 1 and 8191 to units which enter
// the map. These can be used for arrays and data attaching.
//
// AIDS also allows you to define structs which are created automatically
// when units enter the map, and filtering which units should be indexed
// as well as for which units these structs should be created.
//
// How to implement?
// ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
// Simply create a new trigger object called AIDS, go to 'Edit -> Convert
// to Custom Text', and replace everything that's there with this script.
//
// Save the map, close it, reopen it, and then delete the "!" from the
// FAR left side of the next lines (so "external" will line up with this line):
// external ObjectMerger w3a Adef AIDS anam "State Detection" ansf "(AIDS)" aart "" arac 0
//
// At the top of the script, there is a 'UnitIndexingFilter' constant
// function. If the function returns true for the unit, then that unit
// will be automatically indexed. Setting this to true will automatically
// index all units. Setting it to false will disable automatic indexing.
//
// Functions:
// ¯¯¯¯¯¯¯¯¯¯¯¯
// function GetUnitId takes unit u returns integer
// - This returns the index of an indexed unit. This will return 0
// if the unit has not been indexed.
// - This function inlines. It does not check if the unit needs an
// index. This function is for the speed freaks.
// - Always use this if 'UnitIndexingFilter' simply returns true.
//
// function GetUnitIndex takes unit u returns integer
// - This will return the index of a unit if it has one, or assign
// an index if the unit doesn't have one (and return the new index).
// - Use this if 'UnitIndexingFilter' doesn't return true.
//
// function GetIndexUnit takes integer index returns unit
// - This returns the unit which has been assigned the 'index'.
//
// AIDS Structs:
// ¯¯¯¯¯¯¯¯¯¯¯¯¯¯
// - Insert: //! runtextmacro AIDS() at the top of a struct to make it
// an AIDS struct.
// - AIDS structs cannot be created or destroyed manually. Instead, they
// are automatically created when an appropriate unit enters the map.
// - You cannot give members default values in their declaration.
// (eg: private integer i=5 is not allowed)
// - You cannot use array members.
// - AIDS structs must "extend array". This will remove some unused
// functions and enforce the above so there will be no mistakes.
// - There are four optional methods you can use in AIDS structs:
// - AIDS_onCreate takes nothing returns nothing
// - This is called when the struct is 'created' for the unit.
// - In here you can assign members their default values, which
// you would usually assign in their declarations.
// (eg: set this.i=5)
// - AIDS_onDestroy takes nothing returns nothing
// - This is called when the struct is 'destroyed' for the unit.
// - This is your substitute to the normal onDestroy method.
// - AIDS_filter takes unit u returns boolean
// - This is similar to the constant filter in the main system.
// - Each unit that enters the map will be tested by each AIDS
// struct filter. If it returns true for that unit, that unit
// will be indexed if it was not already, the AIDS struct will
// have its AIDS_onCreate method called, and later have its
// AIDS_onDestroy method called when the index is recycled.
// - Not declaring this will use the default AIDS filter instead.
// - AIDS_onInit takes nothing returns nothing
// - This is because I stole your onInit function with my textmacro.
// - You can use '.unit' from any AIDS struct to get the unit for which
// the struct is for.
// - The structs id will be the units index, so getting the struct for
// a unit inlines to a single native call, and you can typecast between
// different AIDS structs. This is the premise of AIDS.
// - Never create or destroy AIDS structs directly.
// - You can call .AIDS_addLock() and AIDS_removeLock() to increase or
// decrease the lock level on the struct. If a struct's lock level is
// not 0, it will not be destroyed until it is reduced to 0. Locks just
// put off AIDS struct destruction in case you wish to attach to a timer
// or something which must expire before the struct data disappears.
// Hence, not freeing all locks will leak the struct (and index).
//
// PUI and AutoIndex:
// ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
// - AIDS includes the PUI textmacros and the AutoIndex module, because
// these systems are not compatible with AIDS but have valid and distinct
// uses.
// - The PUI textmacros are better to use for spells than AIDS structs,
// because they are not created for all units, just those targetted by
// the spell (or whatever else is necessary).
// - The AutoData module is good for very simple array syntax for data
// attachment (although I don't recommend that people actually use it,
// it's here mostly for compatability). Note that unlike the PUI textmacros,
// units must pass the AIDS filter in order for this module to work with
// them. This is exactly as the same as in AutoIndex itself (AutoIndex
// has a filter too).
//
// Thanks:
// ¯¯¯¯¯¯¯¯¯
// - Romek, for writing 90% of this user documentation, challenging my
// interface, doing some testing, suggesting improvements and inspiring
// me to re-do my code to include GetUnitIndex as non-inlining.
// - grim001, for writing the AutoData module, and AutoIndex. I used the
// on-enter-map method that he used. Full credits for the AutoData module.
// - Cohadar, for writing his PUI textmacros. Full credits to him for these,
// except for my slight optimisations for this system.
// Also, I have used an optimised version of his PeriodicRecycler from
// PUI in this system to avoid needing a RemoveUnitEx function.
// - Vexorian, for helping Cohadar on the PUI textmacro.
// - Larcenist, for suggesting the AIDS acronym. Originally he suggested
// 'Alternative Index Detection System', but obviously I came up with
// something better. In fact, I'd say it looks like the acronym was
// an accident. Kinda neat, don't you think? :P
//
// Final Notes:
// ¯¯¯¯¯¯¯¯¯¯¯¯¯¯
// - With most systems, I wouldn't usually add substitutes for alternative
// systems. However, UnitData systems are an exception, because they
// are incompatible with eachother. Since using this system forbids
// people from using the real PUI or AutoIndex, and a lot of resources
// use either of these, it made sense not to break them all.
//
// - If this documentation confused you as to how to use the system, just
// leave everything as default and use GetUnitId everywhere.
//
// - To use this like PUI (if you don't like spamming indices) simply
// make the AIDS filter return false, and use GetUnitIndex.
//
library AIDS initializer InitAIDS
//==============================================================================
// Configurables
//
globals
private constant boolean USE_PERIODIC_RECYCLER = false
private constant real PERIOD = 0.03125 // Recycles 32 units/second max.
// Lower to be able to recycle faster.
// Only used if USE_PERIODIC_RECYCLER
// is set to true.
private constant integer LEAVE_DETECTION_ABILITY = 'AIDS'
endglobals
private function UnitIndexingFilter takes unit u returns boolean
return true
endfunction
//==============================================================================
// System code
//
globals
// The unit stored at an index.
private unit array IndexUnit
private integer array LockLevel
endglobals
//==============================================================================
globals
// Recycle stack
private integer array RecycledIndex
private integer MaxRecycledIndex = 0
// Previous highest index
private integer MaxIndex = 0
endglobals
//==============================================================================
globals
private integer array DecayingIndex
private integer MaxDecayingIndex=0
private integer DecayChecker=0
endglobals
globals
private timer UndefendTimer=CreateTimer()
private integer array UndefendIndex
private integer UndefendStackIndex=0
endglobals
globals
private integer array UndefendExpiringIndex
private integer UndefendExpiringIndexLevel=0
endglobals
//==============================================================================
globals
// The Add/Remove stack (or assign/recycle stack).
//
// Indexing can become recusive since units can be created on index
// assignment or deallocation.
// To support this, a stack is used to store the event response results.
private integer ARStackLevel=0
private integer array ARStackIndex
private unit array ARStackUnit
// A later discovery revealed that the Add/Remove stack did not need to be
// used for deallocation. The alternative used works fine...
endglobals
public constant function GetEnteringIndexUnit takes nothing returns unit
return ARStackUnit[ARStackLevel]
endfunction
public function GetIndexOfEnteringUnit takes nothing returns integer
// Called in AIDS structs when units do not pass the initial AIDS filter.
if ARStackIndex[ARStackLevel]==0 then
// Get new index, from recycler first, else new.
// Store the current index on the (new) top level of the AR stack.
if MaxRecycledIndex==0 then // Get new.
set MaxIndex=MaxIndex+1
set ARStackIndex[ARStackLevel]=MaxIndex
else // Get from recycle stack.
set ARStackIndex[ARStackLevel]=RecycledIndex[MaxRecycledIndex]
set MaxRecycledIndex=MaxRecycledIndex-1
endif
// Store index on unit.
call SetUnitUserData(ARStackUnit[ARStackLevel],ARStackIndex[ARStackLevel])
set IndexUnit[ARStackIndex[ARStackLevel]]=ARStackUnit[ARStackLevel]
// Add index to recycle list.
set MaxDecayingIndex=MaxDecayingIndex+1
set DecayingIndex[MaxDecayingIndex]=ARStackIndex[ARStackLevel]
endif
return ARStackIndex[ARStackLevel]
endfunction
public constant function GetIndexOfEnteringUnitAllocated takes nothing returns integer
// Called in AIDS structs when units have passed the initial AIDS filter.
return ARStackIndex[ARStackLevel]
endfunction
public constant function GetDecayingIndex takes nothing returns integer
static if USE_PERIODIC_RECYCLER then
return DecayingIndex[DecayChecker]
else
return UndefendExpiringIndex[UndefendExpiringIndexLevel]
endif
endfunction
//==============================================================================
globals
// For structs and such which need to do things on unit index assignment.
private trigger OnEnter=CreateTrigger()
// The same, but for when units pass the initial filter anyway.
private trigger OnEnterAllocated=CreateTrigger()
// For structs and such which need to do things on unit index deallocation.
private trigger OnDeallocate=CreateTrigger()
endglobals
public function RegisterOnEnter takes boolexpr b returns triggercondition
return TriggerAddCondition(OnEnter,b)
endfunction
public function RegisterOnEnterAllocated takes boolexpr b returns triggercondition
return TriggerAddCondition(OnEnterAllocated,b)
endfunction
public function RegisterOnDeallocate takes boolexpr b returns triggercondition
return TriggerAddCondition(OnDeallocate,b)
endfunction
//==============================================================================
function GetIndexUnit takes integer index returns unit
debug if index==0 then
debug call BJDebugMsg("|cFFFF0000Error using AIDS:|r Trying to get the unit of index 0.")
debug elseif IndexUnit[index]==null then
debug call BJDebugMsg("|cFFFF0000Error using AIDS:|r Trying to get the unit of unassigned index.")
debug endif
return IndexUnit[index]
endfunction
function GetUnitId takes unit u returns integer
debug if u==null then
debug call BJDebugMsg("|cFFFF0000Error using AIDS:|r Trying to get the id (inlines) of null unit.")
debug elseif GetUnitUserData(u)==0 then
debug call BJDebugMsg("|cFFFF0000Error using AIDS:|r Trying to use GetUnitId (inlines) when you should be using GetUnitIndex (unit didn't pass filter).")
debug endif
return GetUnitUserData(u)
endfunction
globals//locals
private integer getindex
endglobals
function GetUnitIndex takes unit u returns integer // Cannot be recursive.
debug if u==null then
debug call BJDebugMsg("|cFFFF0000Error using AIDS:|r Trying to get the index of null unit.")
debug endif
set getindex=GetUnitUserData(u)
if getindex==0 then
// Get new index, from recycler first, else new.
// Store the current index in getindex.
if MaxRecycledIndex==0 then // Get new.
set MaxIndex=MaxIndex+1
set getindex=MaxIndex
else // Get from recycle stack.
set getindex=RecycledIndex[MaxRecycledIndex]
set MaxRecycledIndex=MaxRecycledIndex-1
endif
// Store index on unit.
call SetUnitUserData(u,getindex)
set IndexUnit[getindex]=u
static if USE_PERIODIC_RECYCLER then
// Add index to recycle list.
set MaxDecayingIndex=MaxDecayingIndex+1
set DecayingIndex[MaxDecayingIndex]=getindex
else
// Add leave detection ability.
call UnitAddAbility(ARStackUnit[ARStackLevel],LEAVE_DETECTION_ABILITY)
call UnitMakeAbilityPermanent(ARStackUnit[ARStackLevel],true,LEAVE_DETECTION_ABILITY)
endif
// Do not fire things here. No AIDS structs will be made at this point.
endif
return getindex
endfunction
//==============================================================================
public function AddLock takes integer index returns nothing
set LockLevel[index]=LockLevel[index]+1
endfunction
public function RemoveLock takes integer index returns nothing
set LockLevel[index]=LockLevel[index]-1
static if not USE_PERIODIC_RECYCLER then
if GetUnitUserData(IndexUnit[index])==0 and LockLevel[index]==0 then
// Increment stack for recursion.
set UndefendExpiringIndexLevel=UndefendExpiringIndexLevel+1
set UndefendExpiringIndex[UndefendExpiringIndexLevel]=index
// Fire things.
call TriggerEvaluate(OnDeallocate)
// Decrement stack for recursion.
set UndefendExpiringIndexLevel=UndefendExpiringIndexLevel-1
// Add the index to the recycler stack.
set MaxRecycledIndex=MaxRecycledIndex+1
set RecycledIndex[MaxRecycledIndex]=index
// Null the unit.
set IndexUnit[index]=null
endif
endif
endfunction
//==============================================================================
static if USE_PERIODIC_RECYCLER then
private function PeriodicRecycler takes nothing returns nothing
if MaxDecayingIndex>0 then
set DecayChecker=DecayChecker+1
if DecayChecker>MaxDecayingIndex then
set DecayChecker=1
endif
if GetUnitUserData(IndexUnit[DecayingIndex[DecayChecker]])==0 then
if LockLevel[DecayingIndex[DecayChecker]]==0 then
// Fire things.
call TriggerEvaluate(OnDeallocate)
// Add the index to the recycler stack.
set MaxRecycledIndex=MaxRecycledIndex+1
set RecycledIndex[MaxRecycledIndex]=DecayingIndex[DecayChecker]
// Null the unit.
set IndexUnit[DecayingIndex[DecayChecker]]=null
// Remove index from decay list.
set DecayingIndex[DecayChecker]=DecayingIndex[MaxDecayingIndex]
set MaxDecayingIndex=MaxDecayingIndex-1
endif
endif
endif
endfunction
else
private function UndefendFilter takes nothing returns boolean
return IsUnitType(GetFilterUnit(),UNIT_TYPE_DEAD)
endfunction
private function OnUndefendTimer takes nothing returns nothing
loop
exitwhen UndefendStackIndex==0
set UndefendStackIndex=UndefendStackIndex-1
set UndefendExpiringIndex[0]=UndefendIndex[UndefendStackIndex]
if IndexUnit[UndefendExpiringIndex[0]]!=null then
if GetUnitUserData(IndexUnit[UndefendExpiringIndex[0]])==0 then
if LockLevel[UndefendExpiringIndex[0]]==0 then
// Fire things.
call TriggerEvaluate(OnDeallocate)
// Add the index to the recycler stack.
set MaxRecycledIndex=MaxRecycledIndex+1
set RecycledIndex[MaxRecycledIndex]=UndefendExpiringIndex[0]
// Null the unit.
set IndexUnit[UndefendExpiringIndex[0]]=null
endif
endif
endif
endloop
endfunction
globals//locals
private integer UndefendFilterIndex
endglobals
private function OnUndefend takes nothing returns boolean
if GetIssuedOrderId()==852056 then // If undefend then...
set UndefendFilterIndex=GetUnitUserData(GetOrderedUnit())
if UndefendIndex[UndefendStackIndex-1]!=UndefendFilterIndex then // Efficiency perk.
set UndefendIndex[UndefendStackIndex]=UndefendFilterIndex
set UndefendStackIndex=UndefendStackIndex+1
call TimerStart(UndefendTimer,0,false,function OnUndefendTimer)
endif
endif
return false
endfunction
endif
//==============================================================================
public function IndexEnum takes nothing returns boolean // Can be recursive...
// Start by adding another level on the AR stack (for recursion's sake).
set ARStackLevel=ARStackLevel+1
// Store the current unit on the (new) top level of the AR stack.
set ARStackUnit[ARStackLevel]=GetFilterUnit()
if GetUnitUserData(ARStackUnit[ARStackLevel])==0 then // Has not been indexed.
if UnitIndexingFilter(ARStackUnit[ARStackLevel]) then
// Get new index, from recycler first, else new.
// Store the current index on the (new) top level of the AR stack.
if MaxRecycledIndex==0 then // Get new.
set MaxIndex=MaxIndex+1
set ARStackIndex[ARStackLevel]=MaxIndex
else // Get from recycle stack.
set ARStackIndex[ARStackLevel]=RecycledIndex[MaxRecycledIndex]
set MaxRecycledIndex=MaxRecycledIndex-1
endif
// Store index on unit.
call SetUnitUserData(ARStackUnit[ARStackLevel],ARStackIndex[ARStackLevel])
set IndexUnit[ARStackIndex[ARStackLevel]]=ARStackUnit[ARStackLevel]
static if USE_PERIODIC_RECYCLER then
// Add index to recycle list.
set MaxDecayingIndex=MaxDecayingIndex+1
set DecayingIndex[MaxDecayingIndex]=ARStackIndex[ARStackLevel]
else
// Add leave detection ability.
call UnitAddAbility(ARStackUnit[ARStackLevel],LEAVE_DETECTION_ABILITY)
call UnitMakeAbilityPermanent(ARStackUnit[ARStackLevel],true,LEAVE_DETECTION_ABILITY)
endif
// Fire things.
call TriggerEvaluate(OnEnter)
else
// The unit did not pass the filters, so does not need to be auto indexed.
// However, for certain AIDS structs, it may still require indexing.
// These structs may index the unit on their creation.
// We flag that an index must be assigned by setting the current index to 0.
set ARStackIndex[ARStackLevel]=0
// Fire things.
call TriggerEvaluate(OnEnter)
endif
endif
// Decrement the stack.
set ARStackLevel=ARStackLevel-1
return false
endfunction
//==============================================================================
private function InitAIDS takes nothing returns nothing
local region r=CreateRegion()
local group g=CreateGroup()
local integer n=15
static if USE_PERIODIC_RECYCLER then
call TimerStart(UndefendTimer,PERIOD,true,function PeriodicRecycler)
else
local trigger t=CreateTrigger()
loop
call TriggerRegisterPlayerUnitEvent(t,Player(n),EVENT_PLAYER_UNIT_ISSUED_ORDER,Filter(function UndefendFilter))
call SetPlayerAbilityAvailable(Player(n),LEAVE_DETECTION_ABILITY,false)
// Capture "undefend" orders.
exitwhen n==0
set n=n-1
endloop
set n=15
call TriggerAddCondition(t,Filter(function OnUndefend))
set t=null
endif
// This must be done first, due to recursion. :)
call RegionAddRect(r,GetWorldBounds())
call TriggerRegisterEnterRegion(CreateTrigger(),r,Filter(function IndexEnum))
set r=null
loop
call GroupEnumUnitsOfPlayer(g,Player(n),Filter(function IndexEnum))
//Enum every non-filtered unit on the map during initialization and assign it a unique
//index. By using GroupEnumUnitsOfPlayer, even units with Locust can be detected.
exitwhen n==0
set n=n-1
endloop
call DestroyGroup(g)
set g=null
endfunction
//==============================================================================
public struct DEFAULT extends array
method AIDS_onCreate takes nothing returns nothing
endmethod
method AIDS_onDestroy takes nothing returns nothing
endmethod
static method AIDS_filter takes unit u returns boolean
return UnitIndexingFilter(u)
endmethod
static method AIDS_onInit takes nothing returns nothing
endmethod
endstruct
//===========================================================================
// Never create or destroy AIDS structs directly.
// Also, do not initialise members except by using the AIDS_onCreate method.
//===========================================================================
//! textmacro AIDS
// This magic line makes default methods get called which do nothing
// if the methods are otherwise undefined.
private static delegate AIDS_DEFAULT AIDS_DELEGATE=0
//-----------------------------------------------------------------------
// Gotta know whether or not to destroy on deallocation...
private boolean AIDS_instanciated
//-----------------------------------------------------------------------
static method operator[] takes unit whichUnit returns thistype
return GetUnitId(whichUnit)
endmethod
method operator unit takes nothing returns unit
// Allows structVar.unit to return the unit.
return GetIndexUnit(this)
endmethod
//-----------------------------------------------------------------------
method AIDS_addLock takes nothing returns nothing
call AIDS_AddLock(this)
endmethod
method AIDS_removeLock takes nothing returns nothing
call AIDS_RemoveLock(this)
endmethod
//-----------------------------------------------------------------------
private static method AIDS_onEnter takes nothing returns boolean
// At this point, the unit might not have been assigned an index.
if thistype.AIDS_filter(AIDS_GetEnteringIndexUnit()) then
// Flag it for destruction on deallocation.
set thistype(AIDS_GetIndexOfEnteringUnit()).AIDS_instanciated=true
// Can use inlining "Assigned" function now, as it must be assigned.
call thistype(AIDS_GetIndexOfEnteringUnitAllocated()).AIDS_onCreate()
endif
return false
endmethod
private static method AIDS_onEnterAllocated takes nothing returns boolean
// At this point, the unit must have been assigned an index.
if thistype.AIDS_filter(AIDS_GetEnteringIndexUnit()) then
// Flag it for destruction on deallocation. Slightly faster!
set thistype(AIDS_GetIndexOfEnteringUnitAllocated()).AIDS_instanciated=true
// Can use inlining "Assigned" function now, as it must be assigned.
call thistype(AIDS_GetIndexOfEnteringUnitAllocated()).AIDS_onCreate()
endif
return false
endmethod
private static method AIDS_onDeallocate takes nothing returns boolean
if thistype(AIDS_GetDecayingIndex()).AIDS_instanciated then
call thistype(AIDS_GetDecayingIndex()).AIDS_onDestroy()
// Unflag destruction on deallocation.
set thistype(AIDS_GetDecayingIndex()).AIDS_instanciated=false
endif
return false
endmethod
//-----------------------------------------------------------------------
private static method onInit takes nothing returns nothing
call AIDS_RegisterOnEnter(Filter(function thistype.AIDS_onEnter))
call AIDS_RegisterOnEnterAllocated(Filter(function thistype.AIDS_onEnterAllocated))
call AIDS_RegisterOnDeallocate(Filter(function thistype.AIDS_onDeallocate))
// Because I robbed you of your struct's onInit method.
call thistype.AIDS_onInit()
endmethod
//! endtextmacro
endlibrary
library PUI uses AIDS
//===========================================================================
// Allowed PUI_PROPERTY TYPES are: unit, integer, real, boolean, string
// Do NOT put handles that need to be destroyed here (timer, trigger, ...)
// Instead put them in a struct and use PUI textmacro
//===========================================================================
//! textmacro PUI_PROPERTY takes VISIBILITY, TYPE, NAME, DEFAULT
$VISIBILITY$ struct $NAME$
private static unit array pui_unit
private static $TYPE$ array pui_data
//-----------------------------------------------------------------------
// Returns default value when first time used
//-----------------------------------------------------------------------
static method operator[] takes unit whichUnit returns $TYPE$
local integer pui = GetUnitId(whichUnit) // Changed from GetUnitIndex.
if .pui_unit[pui] != whichUnit then
set .pui_unit[pui] = whichUnit
set .pui_data[pui] = $DEFAULT$
endif
return .pui_data[pui]
endmethod
//-----------------------------------------------------------------------
static method operator[]= takes unit whichUnit, $TYPE$ whichData returns nothing
local integer pui = GetUnitIndex(whichUnit)
set .pui_unit[pui] = whichUnit
set .pui_data[pui] = whichData
endmethod
endstruct
//! endtextmacro
//===========================================================================
// Never destroy PUI structs directly.
// Use .release() instead, will call .destroy()
//===========================================================================
//! textmacro PUI
private static unit array pui_unit
private static integer array pui_data
private static integer array pui_id
//-----------------------------------------------------------------------
// Returns zero if no struct is attached to unit
//-----------------------------------------------------------------------
static method operator[] takes unit whichUnit returns integer
local integer pui = GetUnitId(whichUnit) // Changed from GetUnitIndex.
// Switched the next two lines for optimisation.
if .pui_unit[pui] != whichUnit then
if .pui_data[pui] != 0 then
// recycled index detected
call .destroy(.pui_data[pui])
set .pui_unit[pui] = null
set .pui_data[pui] = 0
endif
endif
return .pui_data[pui]
endmethod
//-----------------------------------------------------------------------
// This will overwrite already attached struct if any
//-----------------------------------------------------------------------
static method operator[]= takes unit whichUnit, integer whichData returns nothing
local integer pui = GetUnitIndex(whichUnit)
if .pui_data[pui] != 0 then
call .destroy(.pui_data[pui])
endif
set .pui_unit[pui] = whichUnit
set .pui_data[pui] = whichData
set .pui_id[whichData] = pui
endmethod
//-----------------------------------------------------------------------
// If you do not call release struct will be destroyed when unit handle gets recycled
//-----------------------------------------------------------------------
method release takes nothing returns nothing
local integer pui= .pui_id[integer(this)]
call .destroy()
set .pui_unit[pui] = null
set .pui_data[pui] = 0
endmethod
//! endtextmacro
endlibrary
library AutoIndex uses AIDS
module AutoData
private static thistype array data
// Fixed up the below to use thsitype instead of integer.
static method operator []= takes unit u, thistype i returns nothing
set .data[GetUnitId(u)] = i //Just attaching a struct to the unit
endmethod //using the module's thistype array.
static method operator [] takes unit u returns thistype
return .data[GetUnitId(u)] //Just returning the attached struct.
endmethod
endmodule
endlibrary
//TESH.scrollpos=85
//TESH.alwaysfold=0
//
// ___ _ __ __ _ ___ ____ _______________________________
// | \ /_\ / |/ | /_\ / _\| __| || D E A L I T , ||
// | |) / _ \ / / | / |/ _ \| |/|| __| || D E T E C T I T , ||
// |___/_/ \_/_/|__/|_|_/ \_\___/|____| || B L O C K I T . ||
// By Jesus4Lyf ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
// v 1.0.5
// What is Damage?
// ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
// Damage is a damage dealing, detection and blocking system. It implements
// all such functionality. It also provides a means to detect what type
// of damage was dealt, so long as all damage in your map is dealt using
// this system's deal damage functions (except for basic attacks).
//
// It is completely recursively defined, meaning if you deal damage on
// taking damage, the type detection and other features like blocking
// will not malfunction.
//
// How to implement?
// ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
// Create a new trigger object called Damage, go to 'Edit -> Convert to
// Custom Text', and replace everything that's there with this script.
//
// At the top of the script, there is a '//! external ObjectMerger' line.
// Save your map, close your map, reopen your map, and then comment out this
// line. Damage is now implemented. This line creates a dummy ability used
// in the system in some circumstances with damage blocking.
//
// Functions:
// ¯¯¯¯¯¯¯¯¯¯¯¯
// function Damage_RegisterEvent takes trigger whichTrigger returns nothing
// - This registers a special "any unit takes damage" event.
// - This event supports dynamic trigger use.
// - Only triggers registered on this event may block damage.
// - Only fires when the damage dealt is not 0.
//
// function Damage_RegisterZeroEvent takes trigger whichTrigger returns nothing
// - The same as Damage_RegisterEvent, but for only zero damage events
// (which are excluded from Damage_RegisterEvent).
// - Note that getting the damage type may be unreliable, since spells
// like faerie fire trigger 0 damage, but it will count as an attack.
//
// function Damage_GetType takes nothing returns damagetype
// - This will get the type of damage dealt, like an event response,
// for when using a unit takes damage event (or the special event above).
//
// function Damage_IsPhysical takes nothing returns boolean
// function Damage_IsSpell takes nothing returns boolean
// function Damage_IsPure takes nothing returns boolean
// - Wrappers to simply check if Damage_GetType is certain types.
//
// function Damage_IsAttack takes nothing returns boolean
// - Checks if the damage is from a physical attack (so you can deal
// physical damage without it being registered as an actual attack).
//
// function Damage_Block takes real amount returns nothing
// function Damage_BlockAll takes nothing returns nothing
// - For use only with Damage_RegisterEvent.
// - Blocks 'amount' of the damage dealt.
// - Multiple blocks at once work correctly.
// - Blocking more than 100% of the damage will block 100% instead.
// - Damage_BlockAll blocks 100% of the damage being dealt.
//
// function Damage_EnableEvent takes boolean enable returns nothing
// - For disabling and re-enabling the special event.
// - Use it to deal damage which you do not want to be detected by
// the special event.
//
// function UnitDamageTargetEx takes lots of things returns boolean
// - Replaces UnitDamageTarget in your map, with the same arguments.
//
// function Damage_Physical takes unit source, unit target, real amount,
// attacktype whichType, boolean attack, boolean ranged returns boolean
// - A clean wrapper for physical damage.
// - 'attack' determines if this is to be treated as a real physical
// attack or just physical type damage.
// - 'ranged' determines if this is to be treated as a ranged or melee
// attack.
//
// function Damage_Spell takes unit source, unit target, real amount returns boolean
// - A clean wrapper for spell damage.
//
// function Damage_Pure takes unit source, unit target, real amount returns boolean
// - A clean wrapper for pure type damage (universal type, 100% damage).
//
// Thanks:
// ¯¯¯¯¯¯¯¯¯
// - Romek, for helping me find a better way to think about damage blocking.
//
library Damage uses AIDS, Event
//============================================================
globals
private constant integer LIFE_BONUS_ABIL='dprv'
endglobals
//============================================================
globals
private Event OnDamageEvent
private Event OnZeroDamageEvent
private boolean EventEnabled=true
endglobals
public function RegisterEvent takes trigger whichTrigger returns nothing
call OnDamageEvent.register(whichTrigger)
endfunction
public function RegisterZeroEvent takes trigger whichTrigger returns nothing
call OnZeroDamageEvent.register(whichTrigger)
endfunction
public function EnableEvent takes boolean enable returns nothing
set EventEnabled=enable
endfunction
//============================================================
globals
private integer TypeStackLevel=0
private damagetype array TypeStackValue
private boolean array TypeStackAttack
private real array ToBlock
endglobals
public function GetType takes nothing returns damagetype
return TypeStackValue[TypeStackLevel]
endfunction
public function IsAttack takes nothing returns boolean
return TypeStackAttack[TypeStackLevel]
endfunction
public function Block takes real amount returns nothing
set ToBlock[TypeStackLevel]=ToBlock[TypeStackLevel]+amount
endfunction
public function BlockAll takes nothing returns nothing
set ToBlock[TypeStackLevel]=ToBlock[TypeStackLevel]+GetEventDamage()
endfunction
//============================================================
globals
private integer BlockNum=0
private unit array BlockUnit
private real array BlockUnitLife
private real array BlockRedamage
private unit array BlockDamageSource
private timer BlockTimer=CreateTimer()
endglobals
//============================================================
globals
private unit array RemoveBoosted
private integer RemoveBoostedMax=0
private timer RemoveBoostedTimer=CreateTimer()
endglobals
globals//locals
private real BoostedLifeTemp
private unit BoostedLifeUnit
endglobals
private function RemoveBoostedTimerFunc takes nothing returns nothing
loop
exitwhen RemoveBoostedMax==0
set BoostedLifeUnit=RemoveBoosted[RemoveBoostedMax]
set BoostedLifeTemp=GetWidgetLife(BoostedLifeUnit)
call UnitRemoveAbility(BoostedLifeUnit,LIFE_BONUS_ABIL)
if BoostedLifeTemp>0.405 then
call SetWidgetLife(BoostedLifeUnit,BoostedLifeTemp)
endif
set RemoveBoostedMax=RemoveBoostedMax-1
endloop
endfunction
//============================================================
private keyword Detector // Darn, I actually had to do this. XD
globals//locals
private unit ForUnit
private real NextHealth
endglobals
private function OnDamageActions takes nothing returns boolean
if EventEnabled then
if GetEventDamage()==0. then
call OnZeroDamageEvent.fire()
else
call OnDamageEvent.fire()
endif
if ToBlock[TypeStackLevel]!=0. then
//====================================================
// Blocking
set ForUnit=GetTriggerUnit()
set NextHealth=GetEventDamage()
if ToBlock[TypeStackLevel]>=NextHealth then
set NextHealth=GetWidgetLife(ForUnit)+NextHealth
else
set NextHealth=GetWidgetLife(ForUnit)+ToBlock[TypeStackLevel]
endif
call SetWidgetLife(ForUnit,NextHealth)
if GetWidgetLife(ForUnit)<NextHealth then
// NextHealth is over max health.
call UnitAddAbility(ForUnit,LIFE_BONUS_ABIL)
call SetWidgetLife(ForUnit,NextHealth)
set RemoveBoostedMax=RemoveBoostedMax+1
set RemoveBoosted[RemoveBoostedMax]=ForUnit
call ResumeTimer(RemoveBoostedTimer)
endif
//====================================================
set ToBlock[TypeStackLevel]=0.
endif
endif
return false
endfunction
//============================================================
function UnitDamageTargetEx takes unit whichUnit, widget target, real amount, boolean attack, boolean ranged, attacktype attackType, damagetype damageType, weapontype weaponType returns boolean
local boolean result
set TypeStackLevel=TypeStackLevel+1
set TypeStackValue[TypeStackLevel]=damageType
set TypeStackAttack[TypeStackLevel]=attack
set result=UnitDamageTarget(whichUnit,target,amount,attack,ranged,attackType,damageType,weaponType)
set TypeStackLevel=TypeStackLevel-1
return result
endfunction
//! textmacro Damage__DealTypeFunc takes NAME, TYPE
public function $NAME$ takes unit source, unit target, real amount returns boolean
return UnitDamageTargetEx(source,target,amount,false,false,ATTACK_TYPE_NORMAL,$TYPE$,WEAPON_TYPE_WHOKNOWS)
endfunction
public function Is$NAME$ takes nothing returns boolean
return GetType()==$TYPE$
endfunction
//! endtextmacro
//! runtextmacro Damage__DealTypeFunc("Pure","DAMAGE_TYPE_UNIVERSAL")
//! runtextmacro Damage__DealTypeFunc("Spell","DAMAGE_TYPE_MAGIC")
// Uses different stuff, but works much the same way.
public function Physical takes unit source, unit target, real amount, attacktype whichType, boolean attack, boolean ranged returns boolean
return UnitDamageTargetEx(source,target,amount,attack,ranged,whichType,DAMAGE_TYPE_NORMAL,WEAPON_TYPE_WHOKNOWS)
endfunction
public function IsPhysical takes nothing returns boolean
return GetType()==DAMAGE_TYPE_NORMAL
endfunction
//============================================================
private struct Detector extends array // Uses AIDS.
//! runtextmacro AIDS()
private static conditionfunc ACTIONS_COND
private trigger t
private method AIDS_onCreate takes nothing returns nothing
set this.t=CreateTrigger()
call TriggerAddCondition(this.t,thistype.ACTIONS_COND)
call TriggerRegisterUnitEvent(this.t,this.unit,EVENT_UNIT_DAMAGED)
endmethod
private method AIDS_onDestroy takes nothing returns nothing
call DestroyTrigger(this.t)
endmethod
private static method AIDS_onInit takes nothing returns nothing
set thistype.ACTIONS_COND=Condition(function OnDamageActions)
endmethod
endstruct
//============================================================
private module InitModule
private static method onInit takes nothing returns nothing
local unit abilpreload=CreateUnit(Player(15),'uloc',0,0,0)
call UnitAddAbility(abilpreload,LIFE_BONUS_ABIL)
call RemoveUnit(abilpreload)
set abilpreload=null
set OnDamageEvent=Event.create()
set OnZeroDamageEvent=Event.create()
set TypeStackValue[TypeStackLevel]=DAMAGE_TYPE_NORMAL
set TypeStackAttack[TypeStackLevel]=true
call TimerStart(RemoveBoostedTimer,0.0,false,function RemoveBoostedTimerFunc)
endmethod
endmodule
private struct InitStruct extends array
implement InitModule
endstruct
endlibrary
//TESH.scrollpos=53
//TESH.alwaysfold=0
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//~~ Timer32 ~~ By Jesus4Lyf ~~ Version 1.06 ~~
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// What is Timer32?
// - Timer32 implements a fully optimised timer loop for a struct.
// - Instances can be added to the loop, which will call .periodic every
// PERIOD until .stopPeriodic() is called.
//
// =Pros=
// - Efficient.
// - Simple.
//
// =Cons=
// - Only allows one period.
// - The called method must be named ".periodic".
//
// Methods:
// - struct.startPeriodic()
// - struct.stopPeriodic()
//
// - private method periodic takes nothing returns nothing
//
// This must be defined in structs that implement Periodic Module.
// It will be executed by the module every PERIOD until .stopPeriodic() is called.
// Put "implement T32x" BELOW this method.
//
// Modules:
// - T32x
// Has no safety on .stopPeriodic or .startPeriodic (except debug messages
// to warn).
//
// - T32xs
// Has safety on .stopPeriodic and .startPeriodic so if they are called
// multiple times, or while otherwise are already stopped/started respectively,
// no error will occur, the call will be ignored.
//
// - T32
// The original, old version of the T32 module. This remains for backwards
// compatability, and is deprecated. The periodic method must return a boolean,
// false to continue running or true to stop.
//
// Details:
// - Uses one timer.
//
// - Do not, within a .periodic method, follow a .stopPeriodic call with a
// .startPeriodic call.
//
// How to import:
// - Create a trigger named T32.
// - Convert it to custom text and replace the whole trigger text with this.
//
// Thanks:
// - Infinitegde for finding a bug in the debug message that actually altered
// system operation (when in debug mode).
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
library T32 initializer OnInit
globals
public constant real PERIOD=0.03125
public constant integer FPS=R2I(1/PERIOD)
public integer Tick=0 // very useful.
//==============================================================================
private trigger Trig=CreateTrigger()
endglobals
//==============================================================================
// The standard T32 module, T32x.
//
module T32x
private thistype next
private thistype prev
private static method PeriodicLoop takes nothing returns boolean
local thistype this=thistype(0).next
loop
exitwhen this==0
call this.periodic()
set this=this.next
endloop
return false
endmethod
method startPeriodic takes nothing returns nothing
debug if this.prev!=0 or thistype(0).next==this then
debug call BJDebugMsg("T32 ERROR: Struct #"+I2S(this)+" had startPeriodic called while already running!")
debug endif
set thistype(0).next.prev=this
set this.next=thistype(0).next
set thistype(0).next=this
set this.prev=thistype(0)
endmethod
method stopPeriodic takes nothing returns nothing
debug if this.prev==0 and thistype(0).next!=this then
debug call BJDebugMsg("T32 ERROR: Struct #"+I2S(this)+" had stopPeriodic called while not running!")
debug endif
// This is some real magic.
set this.prev.next=this.next
set this.next.prev=this.prev
// This will even work for the starting element.
debug set this.prev=0
endmethod
private static method onInit takes nothing returns nothing
call TriggerAddCondition(Trig,Condition(function thistype.PeriodicLoop))
endmethod
endmodule
//==============================================================================
// The standard T32 module with added safety checks on .startPeriodic() and
// .stopPeriodic(), T32xs.
//
module T32xs
private thistype next
private thistype prev
private boolean runningPeriodic
private static method PeriodicLoop takes nothing returns boolean
local thistype this=thistype(0).next
loop
exitwhen this==0
call this.periodic()
set this=this.next
endloop
return false
endmethod
method startPeriodic takes nothing returns nothing
if not this.runningPeriodic then
set thistype(0).next.prev=this
set this.next=thistype(0).next
set thistype(0).next=this
set this.prev=thistype(0)
set this.runningPeriodic=true
endif
endmethod
method stopPeriodic takes nothing returns nothing
if this.runningPeriodic then
// This is some real magic.
set this.prev.next=this.next
set this.next.prev=this.prev
// This will even work for the starting element.
set this.runningPeriodic=false
endif
endmethod
private static method onInit takes nothing returns nothing
call TriggerAddCondition(Trig,Condition(function thistype.PeriodicLoop))
endmethod
endmodule
//==============================================================================
// The original T32 module, for backwards compatability only.
//
module T32 // deprecated.
private thistype next
private thistype prev
private static method PeriodicLoop takes nothing returns boolean
local thistype this=thistype(0).next
loop
exitwhen this==0
if this.periodic() then
// This is some real magic.
set this.prev.next=this.next
set this.next.prev=this.prev
// This will even work for the starting element.
debug set this.prev=0
endif
set this=this.next
endloop
return false
endmethod
method startPeriodic takes nothing returns nothing
debug if this.prev!=0 or thistype(0).next==this then
debug call BJDebugMsg("T32 ERROR: Struct #"+I2S(this)+" had startPeriodic called while already running!")
debug endif
set thistype(0).next.prev=this
set this.next=thistype(0).next
set thistype(0).next=this
set this.prev=thistype(0)
endmethod
private static method onInit takes nothing returns nothing
call TriggerAddCondition(Trig,Condition(function thistype.PeriodicLoop))
endmethod
endmodule
//==============================================================================
// System Core.
//
private function OnExpire takes nothing returns nothing
set Tick=Tick+1
call TriggerEvaluate(Trig)
endfunction
private function OnInit takes nothing returns nothing
call TimerStart(CreateTimer(),PERIOD,true,function OnExpire)
endfunction
endlibrary
//TESH.scrollpos=18
//TESH.alwaysfold=0
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//~~ GT ~~ GTrigger ~~ By Jesus4Lyf ~~ Version 1.05 ~~
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// What is GTrigger?
// - GTrigger is an event system that replaces the cumbersome WC3
// event system.
// - GTrigger only launches the necessary threads instead of x threads,
// where x is the number of times the event type occurs in the map.
//
// =Pros=
// - Instead of having 16 events (for "16" players) per use of an,
// event type, you have 0 per use and 16 total for that event type.
// - If you have 100 events of one type in your map, instead of firing
// 100 triggers each time any spell is cast, you fire only what's needed.
// - GTrigger is faster to code with, more efficient to execute, and just
// better programming practises and nicer code all round.
//
// =Cons=
// - If a trigger with a GTrigger event is destroyed, it must have its
// event unregistered first or it will leak an event (slows firing down).
// - Shouldn't use "wait" actions anywhere in the triggers.
//
// Functions:
// // General
// - GT_UnregisterTriggeringEvent()
//
// // Ability events
// - GT_RegisterStartsEffectEvent(trigger, abilityid) (returns the trigger passed in)
// - GT_RegisterBeginsChanellingEvent(trigger, abilityid) (returns the trigger passed in)
// - GT_RegisterBeginsCastingEvent(trigger, abilityid) (returns the trigger passed in)
// - GT_RegisterStopsCastingEvent(trigger, abilityid) (returns the trigger passed in)
// - GT_RegisterFinishesCastingEvent(trigger, abilityid) (returns the trigger passed in)
// - GT_RegisterLearnsAbilityEvent(trigger, abilityid) (returns the trigger passed in)
// // Order events // (can use String2OrderIdBJ("OrderString") for orderid
// - GT_RegisterTargetOrderEvent(trigger, orderid) (returns the trigger passed in)
// - GT_RegisterPointOrderEvent(trigger, orderid) (returns the trigger passed in)
// - GT_RegisterNoTargetOrderEvent(trigger, orderid) (returns the trigger passed in)
// // Item events
// - GT_RegisterItemUsedEvent(trigger, itemtypeid) (returns the trigger passed in)
// - GT_RegisterItemAcquiredEvent(trigger, itemtypeid) (returns the trigger passed in)
// - GT_RegisterItemDroppedEvent(trigger, itemtypeid) (returns the trigger passed in)
// // Unit events
// - GT_RegisterUnitDiesEvent(trigger, unittypeid) (returns the trigger passed in)
//
// // Ability Events
// - GT_UnregisterSpellEffectEvent(trigger, abilityid) (returns the trigger passed in)
// - GT_UnregisterBeginsChanellingEvent(trigger, abilityid) (returns the trigger passed in)
// - GT_UnregisterBeginsCastingEvent(trigger, abilityid) (returns the trigger passed in)
// - GT_UnregisterStopsCastingEvent(trigger, abilityid) (returns the trigger passed in)
// - GT_UnregisterFinishesCastingEvent(trigger, abilityid) (returns the trigger passed in)
// - GT_UnregisterLearnsAbilityEvent(trigger, abilityid) (returns the trigger passed in)
// // Order events // (can use String2OrderIdBJ("OrderString") for orderid
// - GT_UnregisterTargetOrderEvent(trigger, orderid) (returns the trigger passed in)
// - GT_UnregisterPointOrderEvent(trigger, orderid) (returns the trigger passed in)
// - GT_UnregisterNoTargetOrderEvent(trigger, orderid) (returns the trigger passed in)
// // Item events
// - GT_UnregisterItemUsedEvent(trigger, itemtypeid) (returns the trigger passed in)
// - GT_UnregisterItemAcquiredEvent(trigger, itemtypeid) (returns the trigger passed in)
// - GT_UnregisterItemDroppedEvent(trigger, itemtypeid) (returns the trigger passed in)
// // Unit events
// - GT_UnregisterUnitDiesEvent(trigger, unittypeid) (returns the trigger passed in)
//
// Alternative interface (not recommended):
// If you aren't familiar with how this works, you shouldn't use it.
// All funcs must return false. (That is the only reason it isn't recommended.)
// // General
// - GT_RemoveTriggeringAction() // Use this to remove actions.
// // Ability Events
// - GT_AddStartsEffectAction(func, abilityid)
// - GT_AddBeginsChanellingActon(func, abilityid)
// - GT_AddBeginsCastingAction(func, abilityid)
// - GT_AddStopsCastingAction(func, abilityid)
// - GT_AddFinishesCastingAction(func, abilityid)
// - GT_AddLearnsAbilityAction(func, abilityid)
// // Order events // (can use String2OrderIdBJ("OrderString") for orderid
// - GT_AddTargetOrderAction(func, orderid)
// - GT_AddPointOrderAction(func, orderid)
// - GT_AddNoTargetOrderAction(func, orderid)
// // Item events
// - GT_AddItemUsedAction(func, itemtypeid)
// - GT_AddItemAcquiredAction(func, itemtypeid)
// - GT_AddItemDroppedAction(func, itemtypeid)
// // Unit events
// - GT_AddUnitDiesAction(func, unittypeid)
//
// Details:
// - Due to the storage method, only 8191 GTrigger events are possible at any one time.
//
// Thanks:
// - Daxtreme: For voluntarily testing this system and the UnitDies event idea.
// - kenny!: For the Order and Learns Ability event ideas.
//
// How to import:
// - Create a trigger named GT.
// - Convert it to custom text and replace the whole trigger text with this.
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
library GT initializer Init
//////////////
// Pointers //
////////////////////////////////////////////////////////////////////////////
// Assigned to abilities, and point to trigger grouping linked lists.
//
// Use:
// GetPointer --> int (pointer)
// FreePointer(int (pointer))
// set PointerTarget[int (pointer)]=int (list link)
// PointerTarget[int (pointer)] --> int (list link)
globals
// Pointer
private integer array PointerTarget
private integer PointerMax=0
// Spare Pointer Stack
private integer array NextPointer
private integer NextPointerMaxPlusOne=1
endglobals
private function GetPointer takes nothing returns integer
if NextPointerMaxPlusOne==1 then
set PointerMax=PointerMax+1
return PointerMax
endif
set NextPointerMaxPlusOne=NextPointerMaxPlusOne-1
return NextPointer[NextPointerMaxPlusOne]
endfunction
private function FreePointer takes integer pointer returns nothing
set PointerTarget[pointer]=0
set NextPointer[NextPointerMaxPlusOne]=pointer
set NextPointerMaxPlusOne=NextPointerMaxPlusOne+1
endfunction
///////////////////////////////////
// Trigger Grouping Linked Lists //
////////////////////////////////////////////////////////////////////////////
// Contains a chain of triggers to be executed together.
//
// Use:
// GetMem() --> int (mem)
// FreeMem(int (mem))
// Link(int (pointer), int (mem))
// Unlink(int (pointer), int (mem))
globals
// Spare Link Stack
private integer array NextMem
private integer NextMemMaxPlusOne=1
// Linked list
private trigger array Trig
private integer array Next
private integer array Prev
private integer TrigMax=0
endglobals
private function GetMem takes nothing returns integer
if NextMemMaxPlusOne==1 then
set TrigMax=TrigMax+1
return TrigMax
endif
set NextMemMaxPlusOne=NextMemMaxPlusOne-1
return NextMem[NextMemMaxPlusOne]
endfunction
private function FreeMem takes integer i returns nothing
set Trig[i]=null
set NextMem[NextMemMaxPlusOne]=i
set NextMemMaxPlusOne=NextMemMaxPlusOne+1
endfunction
// Linked list functionality
// NOTE: This means "Next" must be loaded BEFORE executing the trigger, which could delete the current link.
private function Link takes integer pointer, integer new returns nothing
set Prev[new]=0
set Next[new]=PointerTarget[pointer]
set Prev[PointerTarget[pointer]]=new
set PointerTarget[pointer]=new
endfunction
private function Unlink takes integer pointer, integer rem returns nothing
if Prev[rem]==0 then
set PointerTarget[pointer]=Next[rem]
set Prev[Next[rem]]=0
endif
set Next[Prev[rem]]=Next[rem]
set Prev[Next[rem]]=Prev[rem]
endfunction
//////////////////////
// GTrigger General //
////////////////////////////////////////////////////////////////////////////
// Only contains the UnregisterTriggeringEvent action for public use.
globals
boolean UnregisterLastEvent=false
endglobals
public function UnregisterTriggeringEvent takes nothing returns nothing
set UnregisterLastEvent=true
endfunction
/////////////////////////////////////
// GTrigger Ability Implementation //
////////////////////////////////////////////////////////////////////////////
// The nasty textmacro implementation of special "All Players" events.
//! textmacro SetupSpecialAllPlayersEvent takes NAME, EVENT, GETSPECIAL
globals
private trigger $NAME$Trigger=CreateTrigger()
// Extendable arrays
private integer array $NAME$AbilityIdA
private integer array $NAME$ListPointerA
private integer array $NAME$AbilityIdB
private integer array $NAME$ListPointerB
private integer array $NAME$AbilityIdC
private integer array $NAME$ListPointerC
private integer array $NAME$AbilityIdD
private integer array $NAME$ListPointerD
private integer array $NAME$AbilityIdE
private integer array $NAME$ListPointerE
endglobals
globals//locals
private integer GetOrCreateListPointer$NAME$AbilHashed
endglobals
private function GetOrCreate$NAME$ListPointer takes integer abil returns integer
set GetOrCreateListPointer$NAME$AbilHashed=abil-(abil/8191)*8191
if $NAME$AbilityIdA[GetOrCreateListPointer$NAME$AbilHashed]==abil then // Correct
return $NAME$ListPointerA[GetOrCreateListPointer$NAME$AbilHashed]
elseif $NAME$AbilityIdA[GetOrCreateListPointer$NAME$AbilHashed]<1 then // Empty
set $NAME$AbilityIdA[GetOrCreateListPointer$NAME$AbilHashed]=abil
set $NAME$ListPointerA[GetOrCreateListPointer$NAME$AbilHashed]=GetPointer()
return $NAME$ListPointerA[GetOrCreateListPointer$NAME$AbilHashed]
endif
if $NAME$AbilityIdB[GetOrCreateListPointer$NAME$AbilHashed]==abil then // Correct
return $NAME$ListPointerB[GetOrCreateListPointer$NAME$AbilHashed]
elseif $NAME$AbilityIdB[GetOrCreateListPointer$NAME$AbilHashed]<1 then // Empty
set $NAME$AbilityIdB[GetOrCreateListPointer$NAME$AbilHashed]=abil
set $NAME$ListPointerB[GetOrCreateListPointer$NAME$AbilHashed]=GetPointer()
return $NAME$ListPointerB[GetOrCreateListPointer$NAME$AbilHashed]
endif
if $NAME$AbilityIdC[GetOrCreateListPointer$NAME$AbilHashed]==abil then // Correct
return $NAME$ListPointerC[GetOrCreateListPointer$NAME$AbilHashed]
elseif $NAME$AbilityIdC[GetOrCreateListPointer$NAME$AbilHashed]<1 then // Empty
set $NAME$AbilityIdC[GetOrCreateListPointer$NAME$AbilHashed]=abil
set $NAME$ListPointerC[GetOrCreateListPointer$NAME$AbilHashed]=GetPointer()
return $NAME$ListPointerC[GetOrCreateListPointer$NAME$AbilHashed]
endif
if $NAME$AbilityIdD[GetOrCreateListPointer$NAME$AbilHashed]==abil then // Correct
return $NAME$ListPointerD[GetOrCreateListPointer$NAME$AbilHashed]
elseif $NAME$AbilityIdD[GetOrCreateListPointer$NAME$AbilHashed]<1 then // Empty
set $NAME$AbilityIdD[GetOrCreateListPointer$NAME$AbilHashed]=abil
set $NAME$ListPointerD[GetOrCreateListPointer$NAME$AbilHashed]=GetPointer()
return $NAME$ListPointerD[GetOrCreateListPointer$NAME$AbilHashed]
endif
if $NAME$AbilityIdE[GetOrCreateListPointer$NAME$AbilHashed]==abil then // Correct
return $NAME$ListPointerE[GetOrCreateListPointer$NAME$AbilHashed]
elseif $NAME$AbilityIdE[GetOrCreateListPointer$NAME$AbilHashed]<1 then // Empty
set $NAME$AbilityIdE[GetOrCreateListPointer$NAME$AbilHashed]=abil
set $NAME$ListPointerE[GetOrCreateListPointer$NAME$AbilHashed]=GetPointer()
return $NAME$ListPointerE[GetOrCreateListPointer$NAME$AbilHashed]
endif
call BJDebugMsg("GTrigger Error: Ran out of storage locations for pointers on object "+GetObjectName(abil)+"!")
set PointerTarget[0]=0
return 0
endfunction
globals//locals
private integer GetListPointer$NAME$AbilHashed
endglobals
private function Get$NAME$ListPointer takes integer abil returns integer
set GetListPointer$NAME$AbilHashed=abil-(abil/8191)*8191
if $NAME$AbilityIdA[GetListPointer$NAME$AbilHashed]==abil then // Correct
return $NAME$ListPointerA[GetListPointer$NAME$AbilHashed]
elseif $NAME$AbilityIdA[GetListPointer$NAME$AbilHashed]<1 then // Empty
set PointerTarget[0]=0 // Make sure.
return 0
endif
if $NAME$AbilityIdB[GetListPointer$NAME$AbilHashed]==abil then // Correct
return $NAME$ListPointerB[GetListPointer$NAME$AbilHashed]
elseif $NAME$AbilityIdB[GetListPointer$NAME$AbilHashed]<1 then // Empty
set PointerTarget[0]=0 // Make sure.
return 0
endif
if $NAME$AbilityIdC[GetListPointer$NAME$AbilHashed]==abil then // Correct
return $NAME$ListPointerC[GetListPointer$NAME$AbilHashed]
elseif $NAME$AbilityIdC[GetListPointer$NAME$AbilHashed]<1 then // Empty
set PointerTarget[0]=0 // Make sure.
return 0
endif
if $NAME$AbilityIdD[GetListPointer$NAME$AbilHashed]==abil then // Correct
return $NAME$ListPointerD[GetListPointer$NAME$AbilHashed]
elseif $NAME$AbilityIdD[GetListPointer$NAME$AbilHashed]<1 then // Empty
set PointerTarget[0]=0 // Make sure.
return 0
endif
if $NAME$AbilityIdE[GetListPointer$NAME$AbilHashed]==abil then // Correct
return $NAME$ListPointerE[GetListPointer$NAME$AbilHashed]
elseif $NAME$AbilityIdE[GetListPointer$NAME$AbilHashed]<1 then // Empty
set PointerTarget[0]=0 // Make sure.
return 0
endif
call BJDebugMsg("GTrigger Error: Ran out of storage locations for pointers at ability "+GetObjectName(abil)+"!")
set PointerTarget[0]=0
return 0
endfunction
globals//locals
private integer Register$NAME$Mem
endglobals
public function Register$NAME$Event takes trigger t, integer abil returns trigger
set Register$NAME$Mem=GetMem()
set Trig[Register$NAME$Mem]=t
call Link(GetOrCreate$NAME$ListPointer(abil),Register$NAME$Mem)
return t
endfunction
globals//locals
private integer Unregister$NAME$Pointer
private integer Unregister$NAME$Mem
endglobals
public function Unregister$NAME$Event takes trigger t, integer abil returns trigger
set Unregister$NAME$Pointer=Get$NAME$ListPointer(abil)
set Unregister$NAME$Mem=PointerTarget[Unregister$NAME$Pointer]
loop
exitwhen Trig[Unregister$NAME$Mem]==t
if Unregister$NAME$Mem==0 then
return t // Not found.
endif
set Unregister$NAME$Mem=Next[Unregister$NAME$Mem]
endloop
call Unlink(Unregister$NAME$Pointer,Unregister$NAME$Mem)
call FreeMem(Unregister$NAME$Mem)
return t
endfunction
private function Trigger$NAME$Event takes nothing returns boolean
local integer Trigger$NAME$Pointer=Get$NAME$ListPointer($GETSPECIAL$)
local integer Trigger$NAME$Mem=PointerTarget[Trigger$NAME$Pointer]
local integer Trigger$NAME$NextMem
set UnregisterLastEvent=false
loop
exitwhen Trigger$NAME$Mem<1
set Trigger$NAME$NextMem=Next[Trigger$NAME$Mem]
if TriggerEvaluate(Trig[Trigger$NAME$Mem]) then
call TriggerExecute(Trig[Trigger$NAME$Mem])
endif
if UnregisterLastEvent then
set UnregisterLastEvent=false
call Unlink(Trigger$NAME$Pointer,Trigger$NAME$Mem)
call FreeMem(Trigger$NAME$Mem)
endif
set Trigger$NAME$Mem=Trigger$NAME$NextMem
endloop
return false
endfunction
private function Init$NAME$ takes nothing returns nothing
local integer i=bj_MAX_PLAYER_SLOTS
call TriggerAddCondition($NAME$Trigger,Condition(function Trigger$NAME$Event))
loop
set i=i-1
call TriggerRegisterPlayerUnitEvent($NAME$Trigger,Player(i),EVENT_PLAYER_$EVENT$,null)
exitwhen i==0
endloop
endfunction
//! endtextmacro
//! runtextmacro SetupSpecialAllPlayersEvent("StartsEffect", "UNIT_SPELL_EFFECT", "GetSpellAbilityId()")
//! runtextmacro SetupSpecialAllPlayersEvent("BeginsChanelling", "UNIT_SPELL_CHANNEL", "GetSpellAbilityId()")
//! runtextmacro SetupSpecialAllPlayersEvent("BeginsCasting", "UNIT_SPELL_CAST", "GetSpellAbilityId()")
//! runtextmacro SetupSpecialAllPlayersEvent("StopsCasting", "UNIT_SPELL_ENDCAST", "GetSpellAbilityId()")
//! runtextmacro SetupSpecialAllPlayersEvent("FinishesCasting", "UNIT_SPELL_FINISH", "GetSpellAbilityId()")
//! runtextmacro SetupSpecialAllPlayersEvent("TargetOrder", "UNIT_ISSUED_TARGET_ORDER", "GetIssuedOrderId()")
//! runtextmacro SetupSpecialAllPlayersEvent("PointOrder", "UNIT_ISSUED_POINT_ORDER", "GetIssuedOrderId()")
//! runtextmacro SetupSpecialAllPlayersEvent("NoTargetOrder", "UNIT_ISSUED_ORDER", "GetIssuedOrderId()")
//! runtextmacro SetupSpecialAllPlayersEvent("ItemUsed", "UNIT_USE_ITEM", "GetItemTypeId(GetManipulatedItem())")
//! runtextmacro SetupSpecialAllPlayersEvent("ItemAcquired", "UNIT_PICKUP_ITEM", "GetItemTypeId(GetManipulatedItem())")
//! runtextmacro SetupSpecialAllPlayersEvent("ItemDropped", "UNIT_DROP_ITEM", "GetItemTypeId(GetManipulatedItem())")
//! runtextmacro SetupSpecialAllPlayersEvent("UnitDies", "UNIT_DEATH", "GetUnitTypeId(GetTriggerUnit())")
//! runtextmacro SetupSpecialAllPlayersEvent("LearnsAbility", "HERO_SKILL", "GetLearnedSkill()")
// Note to self: Remember to update the Init function.
/////////////////////////////////////////
// GTrigger All Players Implementation //
////////////////////////////////////////////////////////////////////////////
// The textmacro implementation of other "All Players" events.
//! textmacro SetupAllPlayersEvent takes NAME, EVENT
globals
private trigger $NAME$Trigger=CreateTrigger()
private integer $NAME$ListPointer=0
endglobals
globals//locals
private integer Register$NAME$Mem
endglobals
public function Register$NAME$Event takes trigger t returns trigger
set Register$NAME$Mem=GetMem()
set Trig[Register$NAME$Mem]=t
call Link($NAME$ListPointer,Register$NAME$Mem)
return t
endfunction
globals//locals
private integer Unregister$NAME$Pointer
private integer Unregister$NAME$Mem
endglobals
public function Unregister$NAME$Event takes trigger t returns trigger
set Unregister$NAME$Mem=PointerTarget[$NAME$ListPointer]
loop
exitwhen Trig[Unregister$NAME$Mem]==t
if Unregister$NAME$Mem==0 then
return t // Not found.
endif
set Unregister$NAME$Mem=Next[Unregister$NAME$Mem]
endloop
call Unlink($NAME$ListPointer,Unregister$NAME$Mem)
call FreeMem(Unregister$NAME$Mem)
return t
endfunction
private function Trigger$NAME$Event takes nothing returns boolean
local integer Trigger$NAME$Mem=PointerTarget[$NAME$ListPointer]
local integer Trigger$NAME$NextMem
set UnregisterLastEvent=false
loop
exitwhen Trigger$NAME$Mem<1
set Trigger$NAME$NextMem=Next[Trigger$NAME$Mem]
if TriggerEvaluate(Trig[Trigger$NAME$Mem]) then
call TriggerExecute(Trig[Trigger$NAME$Mem])
endif
if UnregisterLastEvent then
set UnregisterLastEvent=false
call Unlink($NAME$ListPointer,Trigger$NAME$Mem)
call FreeMem(Trigger$NAME$Mem)
endif
set Trigger$NAME$Mem=Trigger$NAME$NextMem
endloop
return false
endfunction
private function Init$NAME$ takes nothing returns nothing
local integer i=bj_MAX_PLAYER_SLOTS
call TriggerAddCondition($NAME$Trigger,Condition(function Trigger$NAME$Event))
loop
set i=i-1
call TriggerRegisterPlayerUnitEvent($NAME$Trigger,Player(i),EVENT_PLAYER_UNIT_$EVENT$,null)
exitwhen i==0
endloop
// Initialise the pointer.
set $NAME$ListPointer=GetPointer()
endfunction
//! endtextmacro
// Old: //! runtextmacro SetupAllPlayersEvent("AnyUnitDies", "DEATH")
private function Init takes nothing returns nothing
// Ability events
call InitStartsEffect()
call InitBeginsChanelling()
call InitBeginsCasting()
call InitStopsCasting()
call InitFinishesCasting()
call InitLearnsAbility()
// Order events
call InitTargetOrder()
call InitPointOrder()
call InitNoTargetOrder()
// Item events
call InitItemUsed()
call InitItemAcquired()
call InitItemDropped()
// Unit events
call InitUnitDies()
endfunction
//////////////
// Wrappers //
////////////////////////////////////////////////////////////////////////////
// Wraps it up, for those who really want this interface.
// General
public function RemoveTriggeringAction takes nothing returns nothing
call UnregisterTriggeringEvent()
call DestroyTrigger(GetTriggeringTrigger())
endfunction
// Special All Player Events
//! textmacro AddSpecialAllPlayersWrapper takes EVENT
public function Add$EVENT$Action takes code func, integer special returns nothing
call TriggerAddCondition(Register$EVENT$Event(CreateTrigger(),special),Condition(func))
endfunction
//! endtextmacro
//! runtextmacro AddSpecialAllPlayersWrapper("StartsEffect")
//! runtextmacro AddSpecialAllPlayersWrapper("BeginsChanelling")
//! runtextmacro AddSpecialAllPlayersWrapper("BeginsCasting")
//! runtextmacro AddSpecialAllPlayersWrapper("StopsCasting")
//! runtextmacro AddSpecialAllPlayersWrapper("FinishesCasting")
//! runtextmacro AddSpecialAllPlayersWrapper("TargetOrder")
//! runtextmacro AddSpecialAllPlayersWrapper("PointOrder")
//! runtextmacro AddSpecialAllPlayersWrapper("NoTargetOrder")
//! runtextmacro AddSpecialAllPlayersWrapper("ItemUsed")
//! runtextmacro AddSpecialAllPlayersWrapper("ItemAcquired")
//! runtextmacro AddSpecialAllPlayersWrapper("ItemDropped")
//! runtextmacro AddSpecialAllPlayersWrapper("UnitDies")
//! runtextmacro AddSpecialAllPlayersWrapper("LearnsAbility")
// Note to self: Remember to update the Init function.
// All Player Events
//! textmacro AddAllPlayersWrapper takes EVENT
public function Add$EVENT$Action takes code func returns nothing
call TriggerAddCondition(Register$EVENT$Event(CreateTrigger()),Condition(func))
endfunction
//! endtextmacro
// Old: //! runtextmacro AddAllPlayersWrapper("AnyUnitDies")
endlibrary
//TESH.scrollpos=41
//TESH.alwaysfold=0
library GroupUtils initializer Init requires optional xebasic
//******************************************************************************
//* BY: Rising_Dusk
//*
//* This library is a combination of several features relevant to groups. First
//* and foremost, it contains a group stack that you can access dynamic groups
//* from. It also provides means to refresh groups and clear any shadow
//* references within them. The included boolexprs are there for backwards
//* compatibility with maps that happen to use them. Since the 1.24c patch,
//* null boolexprs used in GroupEnumUnits* calls no longer leak, so there is no
//* performance gain to using the BOOLEXPR_TRUE constant.
//*
//* Instead of creating/destroying groups, we have moved on to recycling them.
//* NewGroup pulls a group from the stack and ReleaseGroup adds it back. Always
//* remember to call ReleaseGroup on a group when you are done using it. If you
//* fail to do so enough times, the stack will overflow and no longer work.
//*
//* GroupRefresh cleans a group of any shadow references which may be clogging
//* its hashtable. If you remove a unit from the game who is a member of a unit
//* group, it will 'effectively' remove the unit from the group, but leave a
//* shadow in its place. Calling GroupRefresh on a group will clean up any
//* shadow references that may exist within it. It is only worth doing this on
//* groups that you plan to have around for awhile.
//*
//* Constants that can be used from the library:
//* [group] ENUM_GROUP As you might expect, this group is good for
//* when you need a group just for enumeration.
//* [boolexpr] BOOLEXPR_TRUE This is a true boolexpr, which is important
//* because a 'null' boolexpr in enumeration
//* calls results in a leak. Use this instead.
//* [boolexpr] BOOLEXPR_FALSE This exists mostly for completeness.
//*
//* This library also includes a simple implementation of a group enumeration
//* call that factors collision of units in a given area of effect. This is
//* particularly useful because GroupEnumUnitsInRange doesn't factor collision.
//*
//* In your map, you can just replace all instances of GroupEnumUnitsInRange
//* with GroupEnumUnitsInArea with identical arguments and your spells will
//* consider all units colliding with the area of effect. After calling this
//* function as you would normally call GroupEnumUnitsInRange, you are free to
//* do anything with the group that you would normally do.
//*
//* If you don't use xebasic in your map, you may edit the MAX_COLLISION_SIZE
//* variable below and the library will use that as the added radius to check.
//* If you use xebasic, however, the script will automatically use xe's
//* collision size variable.
//*
//* You are also able to use GroupUnitsInArea. This function returns all units
//* within the area, no matter what they are, which can be convenient for those
//* instances where you actually want that.
//*
//* Example usage:
//* local group MyGroup = NewGroup()
//* call GroupRefresh(MyGroup)
//* call ReleaseGroup(MyGroup)
//* call GroupEnumUnitsInArea(ENUM_GROUP, x, y, 350., BOOLEXPR_TRUE)
//* call GroupUnitsInArea(ENUM_GROUP, x, y, 350.)
//*
globals
//If you don't have xebasic in your map, this value will be used instead.
//This value corresponds to the max collision size of a unit in your map.
private constant real MAX_COLLISION_SIZE = 197.
//If you are insane and don't care about any of the protection involved in
//this library, but want this script to be really fast, set this to true.
private constant boolean LESS_SAFETY = false
endglobals
globals
//* Constants that are available to the user
group ENUM_GROUP = CreateGroup()
boolexpr BOOLEXPR_TRUE = null
boolexpr BOOLEXPR_FALSE = null
endglobals
globals
//* Hashtable for debug purposes
private hashtable ht = InitHashtable()
//* Temporary references for GroupRefresh
private boolean Flag = false
private group Refr = null
//* Arrays and counter for the group stack
private group array Groups
private integer Count = 0
//* Variables for use with the GroupUnitsInArea function
private real X = 0.
private real Y = 0.
private real R = 0.
private hashtable H = InitHashtable()
endglobals
private function HookDestroyGroup takes group g returns nothing
if g == ENUM_GROUP then
call BJDebugMsg(SCOPE_PREFIX+"Warning: ENUM_GROUP destroyed")
endif
endfunction
debug hook DestroyGroup HookDestroyGroup
private function AddEx takes nothing returns nothing
if Flag then
call GroupClear(Refr)
set Flag = false
endif
call GroupAddUnit(Refr, GetEnumUnit())
endfunction
function GroupRefresh takes group g returns nothing
set Flag = true
set Refr = g
call ForGroup(Refr, function AddEx)
if Flag then
call GroupClear(g)
endif
endfunction
function NewGroup takes nothing returns group
if Count == 0 then
set Groups[0] = CreateGroup()
else
set Count = Count - 1
endif
static if not LESS_SAFETY then
call SaveInteger(ht, 0, GetHandleId(Groups[Count]), 1)
endif
return Groups[Count]
endfunction
function ReleaseGroup takes group g returns boolean
local integer id = GetHandleId(g)
static if LESS_SAFETY then
if g == null then
debug call BJDebugMsg(SCOPE_PREFIX+"Error: Null groups cannot be released")
return false
elseif Count == 8191 then
debug call BJDebugMsg(SCOPE_PREFIX+"Error: Max groups achieved, destroying group")
call DestroyGroup(g)
return false
endif
else
if g == null then
debug call BJDebugMsg(SCOPE_PREFIX+"Error: Null groups cannot be released")
return false
elseif not HaveSavedInteger(ht, 0, id) then
debug call BJDebugMsg(SCOPE_PREFIX+"Error: Group not part of stack")
return false
elseif LoadInteger(ht, 0, id) == 2 then
debug call BJDebugMsg(SCOPE_PREFIX+"Error: Groups cannot be multiply released")
return false
elseif Count == 8191 then
debug call BJDebugMsg(SCOPE_PREFIX+"Error: Max groups achieved, destroying group")
call DestroyGroup(g)
return false
endif
call SaveInteger(ht, 0, id, 2)
endif
call GroupClear(g)
set Groups[Count] = g
set Count = Count + 1
return true
endfunction
private function Filter takes nothing returns boolean
return IsUnitInRangeXY(GetFilterUnit(), X, Y, R)
endfunction
private function HookDestroyBoolExpr takes boolexpr b returns nothing
local integer bid = GetHandleId(b)
if HaveSavedHandle(H, 0, bid) then
//Clear the saved boolexpr
call DestroyBoolExpr(LoadBooleanExprHandle(H, 0, bid))
call RemoveSavedHandle(H, 0, bid)
endif
endfunction
hook DestroyBoolExpr HookDestroyBoolExpr
private constant function GetRadius takes real radius returns real
static if LIBRARY_xebasic then
return radius+XE_MAX_COLLISION_SIZE
else
return radius+MAX_COLLISION_SIZE
endif
endfunction
function GroupEnumUnitsInArea takes group whichGroup, real x, real y, real radius, boolexpr filter returns nothing
local real prevX = X
local real prevY = Y
local real prevR = R
local integer bid = 0
//Set variables to new values
set X = x
set Y = y
set R = radius
if filter == null then
//Adjusts for null boolexprs passed to the function
set filter = Condition(function Filter)
else
//Check for a saved boolexpr
set bid = GetHandleId(filter)
if HaveSavedHandle(H, 0, bid) then
//Set the filter to use to the saved one
set filter = LoadBooleanExprHandle(H, 0, bid)
else
//Create a new And() boolexpr for this filter
set filter = And(Condition(function Filter), filter)
call SaveBooleanExprHandle(H, 0, bid, filter)
endif
endif
//Enumerate, if they want to use the boolexpr, this lets them
call GroupEnumUnitsInRange(whichGroup, x, y, GetRadius(radius), filter)
//Give back original settings so nested enumerations work
set X = prevX
set Y = prevY
set R = prevR
endfunction
function GroupUnitsInArea takes group whichGroup, real x, real y, real radius returns nothing
local real prevX = X
local real prevY = Y
local real prevR = R
//Set variables to new values
set X = x
set Y = y
set R = radius
//Enumerate
call GroupEnumUnitsInRange(whichGroup, x, y, GetRadius(radius), Condition(function Filter))
//Give back original settings so nested enumerations work
set X = prevX
set Y = prevY
set R = prevR
endfunction
private function True takes nothing returns boolean
return true
endfunction
private function False takes nothing returns boolean
return false
endfunction
private function Init takes nothing returns nothing
set BOOLEXPR_TRUE = Condition(function True)
set BOOLEXPR_FALSE = Condition(function False)
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
//! external ObjectMerger w3a Aasl AIF0 Slo1 1 0 aare 1 0 abuf 1 "BIF0" arac "other" atar 1 "self" ansf "(Buff)" anam "Inversion Field"
//! external ObjectMerger w3h Basl BIF0 fart "ReplaceableTextures\CommandButtons\BTNBanish.blp" ftat "" frac "other" fnsf "" fnam "Inversion Field" ftip "Inversion Field" fube "This unit is caught within the Inversion Field; it cannot escape the field temporarily."
//! external ObjectMerger w3h BOae BGM0 fart "ReplaceableTextures\CommandButtons\BTNGravityMatrix.blp" ftat "" frac "other" fnsf "" fnam "Gravity Matrix" ftip "Gravity Matrix" fube "This unit is under the effects of Gravity Matrix; it receives less damage depending on distance, and absorbs it into mana."
//! external ObjectMerger w3u nalb grFl uabi Aloc ucbs 0 udtm 0 ushr 0 umdl "Abilities\Spells\Undead\AbsorbMana\AbsorbManaBirthMissile.mdl" usca 1.5 ushu "None" util " " umvh 50 umvf 50 umvs 522 umas 522 umis 522 ucol 0 urac "human" unam "Gravity Flux Dummy" utip " " utub " " usid 0 usin 0
//! external ObjectMerger w3u nalb grAc uabi Aloc ucbs 0 udtm 0 ushr 0 umdl "war3mapImported\BlackHole.mdx" usca 2 ushu "None" util " " umvh -200 umvf -200 umvs 522 umas 522 umis 522 ucol 0 urac "human" unam "Graviton Acceleration Dummy" utip " " utub " " usid 0 usin 0
//! external ObjectMerger w3a Adef AIDS anam "State Detection" ansf "(AIDS)" aart "" arac 0
//! external ObjectMerger w3a AIlz dprv anam "Life Bonus" ansf "(Damage System)" Ilif 1 500000 aite 0
//! external ObjectMerger w3u nalb prel uabi Aloc ucbs 0 udtm 0 ushr 0 umdl "none.mdl" usca 0.1 ushu "None" util " " umvh 0 umvf 0 umvs 0 umas 0 umis 0 ucol 0 urac "human" unam "Preloader" utip " " utub " " usid 0 usin 0