Name | Type | is_array | initial_value |
AbrasionRayDummy | unit | No | |
ActiveEffect | effect | No | |
Akama | unit | No | |
Archmage | unit | No | |
ArchMedGroup | group | No | |
ArchMediator | unit | No | |
Augur | unit | No | |
AugurAura | unit | No | |
AugurAuraGroup | group | No | |
AvengingExplosionUnit | unit | No | |
Base_regions | rect | Yes | |
BenedictionEffect | effect | No | |
BrightlanceGroup | group | No | |
BuildAurara | effect | No | |
CargoEvent | real | No | |
CargoTransportGroup | group | Yes | |
CargoTransportUnit | unit | Yes | |
CasterReplica | unit | No | |
Centurion | unit | No | |
CenturionGroup | group | No | |
CheckDeathList | integer | Yes | |
CheckDeathTimer | timer | No | |
Choice | dialog | Yes | |
ConvictionGroup | group | No | |
CourageAttacker | unit | No | |
CourageChance | integer | No | |
CourageousAttacked | unit | No | |
Crystal | unit | No | |
Crystal_Activation | integer | No | |
CrystalFinished | unit | No | |
CrystalFinishedGroup | group | No | |
CrystalGroup | group | No | |
Crystaline | effect | No | |
Crystaline_2 | effect | No | |
Deadly | unit | No | |
DeathEvent | real | No | |
DedicationDummy | unit | No | |
DedicationInt | integer | No | |
DetectRemoveAbility | abilcode | No | |
DetectTransformAbility | abilcode | No | |
Disperse_NonShock | unit | No | |
Disperse_Shock | unit | No | |
DivineBeaconAoE | unit | No | |
DivineCrest | integer | No | |
Draenei | button | Yes | |
DraeneiBuildings | unitcode | No | |
DraeneiBuildingsGroup | group | No | |
DragoonGroup | group | No | |
Dummy | unit | No | |
EchoBrilliance_Unit | unit | No | |
EchoCaster | unit | No | |
ElectricShackles_Non | unit | No | |
ElectricShackles_Shock | unit | No | |
Exarch | unit | No | |
FaerieDragonBindGroup | group | No | |
FaerieDragonL | unit | No | |
FaithfulGroup | group | No | |
FelcrusherGroup | group | No | |
Fog | unit | No | |
Gift_Of_Naruu_Int | integer | No | |
GiftofNaruu | unit | No | |
GoldenDebuffer | unit | No | |
GoldenStrikeChance | integer | No | |
Hallow | unit | No | |
HarmonizeInt | integer | No | |
HarmonizzeDummy | unit | No | |
HeroicBlow | unit | No | |
IreDummy | unit | No | |
IsUnitAlive | boolean | Yes | |
IsUnitBeingUnloaded | boolean | Yes | |
IsUnitNew | boolean | Yes | |
IsUnitPreplaced | boolean | Yes | |
IsUnitReincarnating | boolean | Yes | |
IsUnitRemoved | boolean | Yes | |
IsUnitTransforming | boolean | Yes | |
JustifierGroup | group | No | |
KillerOfUnit | unit | Yes | |
LightFaerieGroup | group | No | |
LightningEffect | lightning | No | |
MainStructureGroup | group | No | |
MasterAssassin | unit | No | |
Mend | unit | No | |
NovaFireUnit | unit | No | |
Other | button | No | |
ParagonAuraGroup | group | No | |
ParagonHero | unit | No | |
PartisanAttacked | unit | No | |
PartisanAttacker | unit | No | |
PartisanGroup | group | No | |
PickedCart | unit | No | |
PickedHall | unit | No | |
PickedMine | unit | No | |
PickedWorker | unit | No | |
PlayerColour | player | Yes | |
PlayerNum | integer | Yes | |
PlayerStructure | boolean | Yes | |
Prophet | unit | No | |
ProphetAura | unit | No | |
ProphetAuraGroup | group | No | |
ProphetMend | unit | No | |
RayTimer | timer | No | |
ReclaimedAttacked | unit | No | |
ReclaimedAttacker | unit | No | |
ReclaimedChance | integer | No | |
ReclaimedDummy | unit | No | |
ReclaimedDummy2 | unit | No | |
RectorGroup | group | No | |
RevivifyDummy | unit | No | |
RingOfCrystals | integer | No | |
RingUnit | unit | No | |
Rogue | unit | No | |
SalamderLGroup | group | No | |
SearcherLight | unit | No | |
SedateBuffed | boolean | Yes | |
SedateGroup | group | No | |
Sharpshooter | unit | No | |
ShiiningForce_Dummy | unit | No | |
ShiningShockTarget | unit | No | |
ShockBolt | effect | No | |
Sorcerer | unit | No | |
SpiritShackle | unit | No | |
SummonerOfUnit | unit | Yes | |
Sunshield | unit | No | |
SymbolBuff | boolean | Yes | |
SymbolGroup | group | No | |
Tech | unit | No | |
temp_loc | location | Yes | |
Temp_Unit | unit | No | |
TempEffect | effect | Yes | |
tempLoc | location | Yes | |
Test | group | No | |
TetherDummy | unit | No | |
ToggleInfusionUnit | unit | No | |
UDex | integer | No | |
UDexLastRecycled | integer | No | |
UDexMax | integer | No | |
UDexNext | integer | Yes | |
UDexPrev | integer | Yes | |
UDexUnits | unit | Yes | |
Unbroken | button | No | |
UnitIndexerEnabled | boolean | No | |
UnitIndexEvent | real | No | |
UnitTypeEvent | real | No | |
UnitTypeOf | unitcode | Yes | |
UnnerveAttacked | unit | No | |
UnnerveAttacker | unit | No | |
UnnerveDummy | unit | No | |
UnnerveHealChance | integer | No | |
UnnerveTargetUnit | unit | No | |
UnnerveTimer | unit | No | |
Upheaval | unit | No | |
ValiantChance | integer | No | |
VindicatorGroup | group | No | |
WarframeCaster | unit | No | |
WarframeUnit | unit | No | |
WorldMaxX | real | No | |
WorldMaxY | real | No | |
Zealot | unit | No |
//===========================================================================
function UnitEventDestroyGroup takes integer i returns nothing
if udg_CargoTransportGroup[i] != null then
call DestroyGroup(udg_CargoTransportGroup[i])
set udg_CargoTransportGroup[i] = null
endif
endfunction
function UnitEventCheckAfter takes nothing returns nothing
local integer i = udg_CheckDeathList[0]
local integer j
set udg_CheckDeathList[0] = -1
loop
exitwhen i == -1
if udg_IsUnitNew[i] then
//The unit was just created.
set udg_IsUnitNew[i] = false
elseif udg_IsUnitTransforming[i] then
//Added 21 July 2017 to fix the issue re-adding this ability in the same instant
set udg_UDex = i
set udg_UnitTypeEvent = 0.00
set udg_UnitTypeEvent = 1.00
set udg_UnitTypeOf[i] = GetUnitTypeId(udg_UDexUnits[i]) //Set this afterward to give the user extra reference
set udg_IsUnitTransforming[i] = false
call UnitAddAbility(udg_UDexUnits[i], udg_DetectTransformAbility)
elseif udg_IsUnitAlive[i] then
//The unit has started reincarnating.
set udg_IsUnitReincarnating[i] = true
set udg_IsUnitAlive[i] = false
set udg_UDex = i
set udg_DeathEvent = 0.50
set udg_DeathEvent = 0.00
endif
set j = udg_CheckDeathList[i]
set udg_CheckDeathList[i] = -1
set i = j
endloop
endfunction
function UnitEventCheckAfterProxy takes integer i returns nothing
if udg_CheckDeathList[0] == 0 then
call TimerStart(udg_CheckDeathTimer, 0.00, false, function UnitEventCheckAfter)
endif
if udg_CheckDeathList[i] == -1 then
set udg_CheckDeathList[i] = udg_CheckDeathList[0]
set udg_CheckDeathList[0] = i
endif
endfunction
function UnitEventOnUnload takes nothing returns nothing
local integer i = udg_UDex
call GroupRemoveUnit(udg_CargoTransportGroup[GetUnitUserData(udg_CargoTransportUnit[i])], udg_UDexUnits[i])
set udg_IsUnitBeingUnloaded[i] = true
set udg_CargoEvent = 0.00
set udg_CargoEvent = 2.00
set udg_CargoEvent = 0.00
set udg_IsUnitBeingUnloaded[i] = false
if not IsUnitLoaded(udg_UDexUnits[i]) or IsUnitType(udg_CargoTransportUnit[i], UNIT_TYPE_DEAD) or GetUnitTypeId(udg_CargoTransportUnit[i]) == 0 then
set udg_CargoTransportUnit[i] = null
endif
endfunction
function UnitEventOnDeath takes nothing returns boolean
local integer pdex = udg_UDex
set udg_UDex = GetUnitUserData(GetTriggerUnit())
if udg_UDex != 0 then
set udg_KillerOfUnit[udg_UDex] = GetKillingUnit() //Added 29 May 2017 for GIMLI_2
set udg_IsUnitAlive[udg_UDex] = false
set udg_DeathEvent = 0.00
set udg_DeathEvent = 1.00
set udg_DeathEvent = 0.00
set udg_KillerOfUnit[udg_UDex] = null
if udg_CargoTransportUnit[udg_UDex] != null then
call UnitEventOnUnload()
endif
endif
set udg_UDex = pdex
return false
endfunction
function UnitEventOnOrder takes nothing returns boolean
local integer pdex = udg_UDex
local unit u = GetFilterUnit()
local integer i = GetUnitUserData(u)
if i > 0 then
set udg_UDex = i
if GetUnitAbilityLevel(u, udg_DetectRemoveAbility) == 0 then
if not udg_IsUnitRemoved[i] then
set udg_IsUnitRemoved[i] = true
set udg_IsUnitAlive[i] = false
set udg_SummonerOfUnit[i] = null
//For backwards-compatibility:
set udg_DeathEvent = 0.00
set udg_DeathEvent = 3.00
set udg_DeathEvent = 0.00
//Fire deindex event for UDex:
set udg_UnitIndexEvent = 0.00
set udg_UnitIndexEvent = 2.00
set udg_UnitIndexEvent = 0.00
set udg_UDexNext[udg_UDexPrev[i]] = udg_UDexNext[i]
set udg_UDexPrev[udg_UDexNext[i]] = udg_UDexPrev[i]
// Recycle the index for later use
set udg_UDexUnits[i] = null
set udg_UDexPrev[i] = udg_UDexLastRecycled
set udg_UDexLastRecycled = i
call UnitEventDestroyGroup(i)
endif
elseif not udg_IsUnitAlive[i] then
if not IsUnitType(u, UNIT_TYPE_DEAD) then
set udg_IsUnitAlive[i] = true
set udg_DeathEvent = 0.00
set udg_DeathEvent = 2.00
set udg_DeathEvent = 0.00
set udg_IsUnitReincarnating[i] = false
endif
elseif IsUnitType(u, UNIT_TYPE_DEAD) then
if udg_IsUnitNew[i] then
//This unit was created as a corpse.
set udg_IsUnitAlive[i] = false
set udg_DeathEvent = 0.00
set udg_DeathEvent = 1.00
set udg_DeathEvent = 0.00
elseif udg_CargoTransportUnit[i] == null or not IsUnitType(u, UNIT_TYPE_HERO) then
//The unit may have just started reincarnating.
call UnitEventCheckAfterProxy(i)
endif
elseif GetUnitAbilityLevel(u, udg_DetectTransformAbility) == 0 and not udg_IsUnitTransforming[i] then
set udg_IsUnitTransforming[i] = true
call UnitEventCheckAfterProxy(i) //This block has been updated on 21 July 2017
endif
if udg_CargoTransportUnit[i] != null and not udg_IsUnitBeingUnloaded[i] and not IsUnitLoaded(u) or IsUnitType(u, UNIT_TYPE_DEAD) then
call UnitEventOnUnload()
endif
set udg_UDex = pdex
endif
set u = null
return false
endfunction
function UnitEventOnSummon takes nothing returns boolean
local integer pdex = udg_UDex
set udg_UDex = GetUnitUserData(GetTriggerUnit())
if udg_IsUnitNew[udg_UDex] then
set udg_SummonerOfUnit[udg_UDex] = GetSummoningUnit()
set udg_UnitIndexEvent = 0.00
set udg_UnitIndexEvent = 0.50
set udg_UnitIndexEvent = 0.00
endif
set udg_UDex = pdex
return false
endfunction
function UnitEventOnLoad takes nothing returns boolean
local integer pdex = udg_UDex
local integer i = GetUnitUserData(GetTriggerUnit())
local integer index
if i != 0 then
set udg_UDex = i
if udg_CargoTransportUnit[i] != null then
call UnitEventOnUnload()
endif
//Loaded corpses do not issue an order when unloaded, therefore must
//use the enter-region event method taken from Jesus4Lyf's Transport.
if not udg_IsUnitAlive[i] then
call SetUnitX(udg_UDexUnits[i], udg_WorldMaxX)
call SetUnitY(udg_UDexUnits[i], udg_WorldMaxY)
endif
set udg_CargoTransportUnit[i] = GetTransportUnit()
set index = GetUnitUserData(udg_CargoTransportUnit[i])
if udg_CargoTransportGroup[index] == null then
set udg_CargoTransportGroup[index] = CreateGroup()
endif
call GroupAddUnit(udg_CargoTransportGroup[index], udg_UDexUnits[i])
set udg_CargoEvent = 0.00
set udg_CargoEvent = 1.00
set udg_CargoEvent = 0.00
set udg_UDex = pdex
endif
return false
endfunction
function UnitEventEnter takes nothing returns boolean
local integer pdex = udg_UDex
local integer i = udg_UDexLastRecycled
local unit u = GetFilterUnit()
if udg_UnitIndexerEnabled and GetUnitAbilityLevel(u, udg_DetectRemoveAbility) == 0 then
//Generate a unique integer index for this unit
if i == 0 then
set i = udg_UDexMax + 1
set udg_UDexMax = i
else
set udg_UDexLastRecycled = udg_UDexPrev[i]
endif
//Link index to unit, unit to index
set udg_UDexUnits[i] = u
call SetUnitUserData(u, i)
//For backwards-compatibility, add the unit to a linked list
set udg_UDexNext[i] = udg_UDexNext[0]
set udg_UDexPrev[udg_UDexNext[0]] = i
set udg_UDexNext[0] = i
set udg_UDexPrev[i] = 0
set udg_CheckDeathList[i] = -1
call UnitAddAbility(u, udg_DetectRemoveAbility)
call UnitMakeAbilityPermanent(u, true, udg_DetectRemoveAbility)
call UnitAddAbility(u, udg_DetectTransformAbility)
set udg_UnitTypeOf[i] = GetUnitTypeId(u)
set udg_IsUnitNew[i] = true
set udg_IsUnitAlive[i] = true
set udg_IsUnitRemoved[i] = false
set udg_IsUnitReincarnating[i] = false
set udg_IsUnitPreplaced[i] = udg_IsUnitPreplaced[0] //Added 29 May 2017 for Spellbound
call UnitEventCheckAfterProxy(i)
//Fire index event for UDex
set udg_UDex = i
set udg_UnitIndexEvent = 0.00
set udg_UnitIndexEvent = 1.00
set udg_UnitIndexEvent = 0.00
else
set udg_UDex = GetUnitUserData(u)
if udg_CargoTransportUnit[udg_UDex] != null and not IsUnitLoaded(u) then
//The unit was dead, but has re-entered the map.
call UnitEventOnUnload()
endif
endif
set udg_UDex = pdex
set u = null
return false
endfunction
//===========================================================================
function UnitEventInit takes nothing returns nothing
local integer i = bj_MAX_PLAYER_SLOTS //update to make it work with 1.29
local player p
local trigger t = CreateTrigger()
local trigger load = CreateTrigger()
local trigger death = CreateTrigger()
local trigger summon = CreateTrigger()
local rect r = GetWorldBounds()
local region re = CreateRegion()
local boolexpr enterB = Filter(function UnitEventEnter)
local boolexpr orderB = Filter(function UnitEventOnOrder)
set udg_WorldMaxX = GetRectMaxX(r)
set udg_WorldMaxY = GetRectMaxY(r)
call RegionAddRect(re, r)
call RemoveRect(r)
call UnitEventDestroyGroup(0)
call UnitEventDestroyGroup(1)
set udg_CheckDeathList[0] = -1
set udg_UnitIndexerEnabled = true
call TriggerRegisterEnterRegion(CreateTrigger(), re, enterB)
call TriggerAddCondition(load, Filter(function UnitEventOnLoad))
call TriggerAddCondition(death, Filter(function UnitEventOnDeath))
call TriggerAddCondition(summon, Filter(function UnitEventOnSummon))
loop
set i = i - 1
set p = Player(i)
call SetPlayerAbilityAvailable(p, udg_DetectRemoveAbility, false)
call SetPlayerAbilityAvailable(p, udg_DetectTransformAbility, false)
call TriggerRegisterPlayerUnitEvent(summon, p, EVENT_PLAYER_UNIT_SUMMON, null)
call TriggerRegisterPlayerUnitEvent(t, p, EVENT_PLAYER_UNIT_ISSUED_ORDER, orderB)
call TriggerRegisterPlayerUnitEvent(death, p, EVENT_PLAYER_UNIT_DEATH, null)
call TriggerRegisterPlayerUnitEvent(load, p, EVENT_PLAYER_UNIT_LOADED, null)
call GroupEnumUnitsOfPlayer(bj_lastCreatedGroup, p, enterB)
exitwhen i == 0
endloop
set summon = null
set death = null
set load = null
set re = null
set enterB = null
set orderB = null
set p = null
set r = null
set t = null
endfunction
function InitTrig_Unit_Event takes nothing returns nothing
endfunction
library Fade requires TimerUtils
/*
Fade v1.00 by Spellbound
=== DESCRIPTION ================================================================================
This simply library will transition the opacity and colours of an effect or unit from one
starting value to the ending value. This library comes with 4 different structs that each
handle some aspect of fading or colour change over time.
Fading - simple opacity change.
Colour change - more complex colour and opacity change.
Key
Refer to this to see what the arguments mean.
rS - starting red colour value
gS - starting green colour value
bS - starting blue colour value
aS - starting alpha value
rE - final red colour value
gE - final green colour value
bE - final blue colour value
aE - final alpha value
=== API ========================================================================================
Struct
FadeEffect
STATIC
FadeEffect.create(effect, aS, aE, time)
This will change the opacity of an effect from alphaStart to alphaEnd over the
assigned period of time. RGB values for special effects are separate. use
ColourEffect() instead if you wish to also gradually change colour values.
Alternative, simply set special effect colour to whatever you want.
________________________________________________________________________________________________
FadeUnit
STATIC
FadeUnit.create(unit, aS, aE, time)
This will change the opacity of a unit from alphaStart to alphaEnd over the
assigned period of time. Colour values will default to
NON-STATIC
this.setColourChange(unit, red, green, blue, redEnd, greenEnd, blueEnd)
This parameter will switch colours to the assigned values and then switch them to
colourEnd when Fade is deallocated. The end colours are not gradual but instant.
If you wish to have a gradual colour change, use ColourUnit instead. If you
wish to have a gradual colour AND alpha change, ColourUnit will also be used.
Do not use ColourUnit and FadeUnit at the same time or the opacity of the
unit may flicker.
________________________________________________________________________________________________
ColourEffect
STATIC
ColourEffect.create(effect, rS, gS, bS, rE, gE, bE, time)
This will change the colour profile from colourS to colourE over time.
________________________________________________________________________________________________
ColourUnit
STATIC
ColourUnit.create(effect, rS, gS, bS, rE, gE, bE, aS, aE, time)
This will change the colour AND alpha values from colourS to colourE over
time. DO NOT USE with FadeUnit.create() as the values manipulated overlap
(alpha). Pick either one or the other. If you need to gradually
________________________________________________________________________________________________
*/
globals
private constant real TIMEOUT = 1./32.
endglobals
struct FadeEffect
effect fx
real rate
real alpha
integer alphaEnd
private boolean terminate
private static method fadeIn takes nothing returns nothing
local timer t = GetExpiredTimer()
local thistype this = GetTimerData(t)
set this.alpha = this.alpha + this.rate
if this.alpha >= this.alphaEnd or this.fx == null or this.terminate then
call BlzSetSpecialEffectAlpha(this.fx, this.alphaEnd)
set this.fx = null
call this.deallocate()
call ReleaseTimer(t)
else
call BlzSetSpecialEffectAlpha(this.fx, R2I(this.alpha))
endif
set t = null
endmethod
private static method fadeOut takes nothing returns nothing
local timer t = GetExpiredTimer()
local thistype this = GetTimerData(t)
set this.alpha = this.alpha + this.rate
if this.alpha <= this.alphaEnd or this.fx == null or this.terminate then
call BlzSetSpecialEffectAlpha(this.fx, this.alphaEnd)
set this.fx = null
call this.deallocate()
call ReleaseTimer(t)
else
call BlzSetSpecialEffectAlpha(this.fx, R2I(this.alpha))
endif
set t = null
endmethod
method endFade takes nothing returns nothing
set this.terminate = true
endmethod
static method create takes effect vfx, integer alphaStart, integer alphaEnd, real time returns thistype
local thistype this
// failsafe
if alphaStart != alphaEnd then
call BlzSetSpecialEffectAlpha(vfx, alphaStart)
set this = allocate()
set this.fx = vfx
set this.alpha = alphaStart
set this.alphaEnd = alphaEnd
set this.rate = ((alphaEnd - alphaStart) / time) * TIMEOUT
set this.terminate = false
if alphaStart < alphaEnd then
call TimerStart(NewTimerEx(this), TIMEOUT, true, function thistype.fadeIn)
else
call TimerStart(NewTimerEx(this), TIMEOUT, true, function thistype.fadeOut)
endif
return this
endif
return 0
endmethod
endstruct
struct FadeUnit
unit u
integer alphaEnd
integer r
integer g
integer b
integer rEnd
integer gEnd
integer bEnd
real rate
real alpha
boolean terminate
private static method fadeIn takes nothing returns nothing
local timer t = GetExpiredTimer()
local thistype this = GetTimerData(t)
set this.alpha = this.alpha + this.rate
if this.alpha >= this.alphaEnd or this.u == null or this.terminate then
call SetUnitVertexColor(this.u, this.rEnd, this.gEnd, this.bEnd, this.alphaEnd)
set this.u = null
call this.deallocate()
call ReleaseTimer(t)
else
call SetUnitVertexColor(this.u, this.r, this.g, this.b, R2I(this.alpha))
endif
set t = null
endmethod
private static method fadeOut takes nothing returns nothing
local timer t = GetExpiredTimer()
local thistype this = GetTimerData(t)
set this.alpha = this.alpha + this.rate
if this.alpha <= this.alphaEnd or this.u == null or this.terminate then
call SetUnitVertexColor(this.u, this.rEnd, this.gEnd, this.bEnd, this.alphaEnd)
set this.u = null
call this.deallocate()
call ReleaseTimer(t)
else
call SetUnitVertexColor(this.u, this.r, this.g, this.b, R2I(this.alpha))
endif
set t = null
endmethod
method setColourChange takes integer r, integer g, integer b, integer rEnd, integer gEnd, integer bEnd returns nothing
set this.r = r
set this.g = g
set this.b = b
set this.rEnd = rEnd
set this.gEnd = gEnd
set this.bEnd = bEnd
call SetUnitVertexColor(u, r, g, b, R2I(this.alpha))
endmethod
method endFade takes nothing returns nothing
set this.terminate = true
endmethod
static method create takes unit u, integer alphaStart, integer alphaEnd, real time returns thistype
local thistype this
// failsafe
if alphaStart != alphaEnd then
call SetUnitVertexColor(u, 255, 255, 255, alphaStart)
set this = allocate()
set this.u = u
set this.alpha = alphaStart
set this.alphaEnd = alphaEnd
set this.r = 255
set this.g = 255
set this.b = 255
set this.rEnd = 255
set this.gEnd = 255
set this.bEnd = 255
set this.rate = ((alphaEnd - alphaStart) / time) * TIMEOUT
set this.terminate = false
if alphaStart < alphaEnd then
if this.rate == 0 then
set this.rate = 1
endif
call TimerStart(NewTimerEx(this), TIMEOUT, true, function thistype.fadeIn)
else
if this.rate == 0 then
set this.rate = -1
endif
call TimerStart(NewTimerEx(this), TIMEOUT, true, function thistype.fadeOut)
endif
return this
endif
return 0
endmethod
endstruct
struct ColourEffect
effect fx
real r
real g
real b
real rRate
real gRate
real bRate
integer rEnd
integer gEnd
integer bEnd
boolean rDone
boolean gDone
boolean bDone
boolean terminate
private static method colourChange takes nothing returns nothing
local timer t = GetExpiredTimer()
local thistype this = GetTimerData(t)
// Check Red
if not this.rDone then
if this.rRate > 0. then
if this.r < this.rEnd then
set this.r = this.r + this.rRate
else
set this.rDone = true
endif
else
if this.r > this.rEnd then
set this.r = this.r + this.rRate
else
set this.rDone = true
endif
endif
endif
// Check Green
if not this.gDone then
if this.gRate > 0. then
if this.g < this.gEnd then
set this.g = this.g + this.gRate
else
set this.gDone = true
endif
else
if this.g > this.gEnd then
set this.g = this.g + this.gRate
else
set this.gDone = true
endif
endif
endif
// Check Blue
if not this.bDone then
if this.bRate > 0. then
if this.b < this.bEnd then
set this.b = this.b + this.bRate
else
set this.bDone = true
endif
else
if this.b > this.rEnd then
set this.b = this.b + this.bRate
else
set this.bDone = true
endif
endif
endif
if (this.rDone and this.gDone and this.bDone) or this.fx == null or this.terminate then
call BlzSetSpecialEffectColor(this.fx, this.rEnd, this.gEnd, this.bEnd)
set this.fx = null
call this.deallocate()
call ReleaseTimer(t)
else
call BlzSetSpecialEffectColor(this.fx, R2I(this.r), R2I(this.g), R2I(this.b))
endif
set t = null
endmethod
method endColour takes nothing returns nothing
set this.terminate = true
endmethod
static method create takes effect fx, real r, real g, real b, integer rEnd, integer gEnd, integer bEnd, real time returns thistype
local thistype this = allocate()
call BlzSetSpecialEffectColor(this.fx, R2I(this.r), R2I(this.g), R2I(this.b))
set this.fx = fx
set this.r = r
set this.g = g
set this.b = b
set this.rEnd = rEnd
set this.gEnd = gEnd
set this.bEnd = bEnd
set this.rRate = ((rEnd - r) / time) * TIMEOUT
set this.gRate = ((gEnd - g) / time) * TIMEOUT
set this.bRate = ((bEnd - b) / time) * TIMEOUT
set this.rDone = r == I2R(rEnd)
set this.gDone = g == I2R(gEnd)
set this.bDone = b == I2R(bEnd)
set this.terminate = false
call TimerStart(NewTimerEx(this), TIMEOUT, true, function thistype.colourChange)
return this
endmethod
endstruct
struct ColourUnit
unit u
real r
real g
real b
real a
real rRate
real gRate
real bRate
real aRate
integer rEnd
integer gEnd
integer bEnd
integer aEnd
boolean rDone
boolean gDone
boolean bDone
boolean aDone
boolean terminate
private static method colourChange takes nothing returns nothing
local timer t = GetExpiredTimer()
local thistype this = GetTimerData(t)
// Check Red
if not this.rDone then
if this.rRate > 0. then
if this.r < this.rEnd then
set this.r = this.r + this.rRate
else
set this.rDone = true
endif
else
if this.r > this.rEnd then
set this.r = this.r + this.rRate
else
set this.rDone = true
endif
endif
endif
// Check Green
if not this.gDone then
if this.gRate > 0. then
if this.g < this.gEnd then
set this.g = this.g + this.gRate
else
set this.gDone = true
endif
else
if this.g > this.gEnd then
set this.g = this.g + this.gRate
else
set this.gDone = true
endif
endif
endif
// Check Blue
if not this.bDone then
if this.bRate > 0. then
if this.b < this.bEnd then
set this.b = this.b + this.bRate
else
set this.bDone = true
endif
else
if this.b > this.rEnd then
set this.b = this.b + this.bRate
else
set this.bDone = true
endif
endif
endif
// Check Alpha
if not this.aDone then
if this.aRate > 0. then
if this.a < this.aEnd then
set this.a = this.a + this.aRate
else
set this.aDone = true
endif
else
if this.a > this.aEnd then
set this.a = this.a + this.aRate
else
set this.aDone = true
endif
endif
endif
if (this.rDone and this.gDone and this.bDone and this.aDone) or this.u == null or this.terminate then
call SetUnitVertexColor(this.u, this.rEnd, this.gEnd, this.bEnd, this.aEnd)
set this.u = null
call this.deallocate()
call ReleaseTimer(t)
else
call SetUnitVertexColor(this.u, R2I(this.r), R2I(this.g), R2I(this.b), R2I(this.a))
endif
set t = null
endmethod
method endColour takes nothing returns nothing
set this.terminate = true
endmethod
static method create takes unit u, real r, real g, real b, integer rEnd, integer gEnd, integer bEnd, integer a, integer aEnd, real time returns thistype
local thistype this = allocate()
call SetUnitVertexColor(this.u, R2I(r), R2I(g), R2I(b), R2I(a))
set this.u = u
set this.r = r
set this.g = g
set this.b = b
set this.a = a
set this.rEnd = rEnd
set this.gEnd = gEnd
set this.bEnd = bEnd
set this.aEnd = aEnd
set this.rRate = ((rEnd - r) / time) * TIMEOUT
set this.gRate = ((gEnd - g) / time) * TIMEOUT
set this.bRate = ((bEnd - b) / time) * TIMEOUT
set this.aRate = ((aEnd - a) / time) * TIMEOUT
set this.rDone = r == I2R(rEnd)
set this.gDone = g == I2R(gEnd)
set this.bDone = b == I2R(bEnd)
set this.aDone = a == I2R(aEnd)
set this.terminate = false
if not (this.rDone and this.gDone and this.bDone and this.aDone) then
call TimerStart(NewTimerEx(this), TIMEOUT, true, function thistype.colourChange)
else
set this.u = null
call this.deallocate()
endif
return this
endmethod
endstruct
endlibrary
/*****************************************************************************
*
* List<T> v2.1.2.0
* by Bannar
*
* Doubly-linked list.
*
******************************************************************************
*
* Requirements:
*
* Table by Bribe
* hiveworkshop.com/forums/jass-resources-412/snippet-new-table-188084/
*
* Alloc - choose whatever you like
* e.g.: by Sevion hiveworkshop.com/threads/snippet-alloc.192348/
*
******************************************************************************
*
* Implementation:
*
* macro DEFINE_STRUCT_LIST takes ACCESS, NAME, TYPE
*
* macro DEFINE_LIST takes ACCESS, NAME, TYPE
*
* ACCESS - encapsulation, choose restriction access
* NAME - name of list type
* TYPE - type of values stored
*
* Implementation notes:
*
* - DEFINE_STRUCT_LIST macro purpose is to provide natural typecasting syntax for struct types.
* - <NAME>Item structs inline directly into hashtable operations thus generate basically no code.
* - Lists defined with DEFINE_STRUCT_LIST are inlined nicely into single create method and single integer array.
*
******************************************************************************
*
* struct API:
*
* struct <NAME>Item:
*
* | <TYPE> data
* | <NAME>Item next
* | <NAME>Item prev
*
*
* General:
*
* | static method create takes nothing returns thistype
* | Default ctor.
* |
* | static method operator [] takes thistype other returns thistype
* | Copy ctor.
* |
* | method destroy takes nothing returns nothing
* | Default dctor.
* |
* | method empty takes nothing returns boolean
* | Checks whether the list is empty.
* |
* | method size takes nothing returns integer
* | Returns size of a list.
*
*
* Access:
*
* | readonly <NAME>Item first
* | readonly <NAME>Item last
* |
* | method front takes nothing returns $TYPE$
* | Retrieves first element.
* |
* | method back takes nothing returns $TYPE$
* | Retrieves last element.
*
*
* Modifiers:
*
* | method clear takes nothing returns nothing
* | Flushes list and recycles its nodes.
* |
* | method push takes $TYPE$ value returns thistype
* | Adds elements to the end.
* |
* | method unshift takes $TYPE$ value returns thistype
* | Adds elements to the front.
* |
* | method pop takes nothing returns thistype
* | Removes the last element.
* |
* | method shift takes nothing returns thistype
* | Removes the first element.
* |
* | method find takes $TYPE$ value returns $NAME$Item
* | Returns the first node which data equals value.
* |
* | method erase takes $NAME$Item node returns boolean
* | Removes node from the list, returns true on success.
* |
* | method removeElem takes $TYPE$ value returns thistype
* | Removes first element that equals value from the list.
*
*
*****************************************************************************/
library ListT requires Table, Alloc
// Run here any global list types you want to be defined.
//! runtextmacro DEFINE_LIST("", "IntegerList", "integer")
//! textmacro_once DEFINE_STRUCT_LIST takes ACCESS, NAME, TYPE
$ACCESS$ struct $NAME$Item extends array
// Cannot inherit methods via delegate due to limited array size
method operator data takes nothing returns $TYPE$
return IntegerListItem(this).data
endmethod
method operator data= takes $TYPE$ value returns nothing
set IntegerListItem(this).data = value
endmethod
method operator next takes nothing returns thistype
return IntegerListItem(this).next
endmethod
method operator next= takes thistype value returns nothing
set IntegerListItem(this).next = value
endmethod
method operator prev takes nothing returns thistype
return IntegerListItem(this).prev
endmethod
method operator prev= takes thistype value returns nothing
set IntegerListItem(this).prev = value
endmethod
endstruct
$ACCESS$ struct $NAME$ extends array
private delegate IntegerList parent
method front takes nothing returns $TYPE$
return parent.front()
endmethod
method back takes nothing returns $TYPE$
return parent.back()
endmethod
static method create takes nothing returns thistype
local thistype this = IntegerList.create()
set parent = this
return this
endmethod
endstruct
//! endtextmacro
//! textmacro_once DEFINE_LIST takes ACCESS, NAME, TYPE
$ACCESS$ struct $NAME$Item extends array
// No default ctor and dctor due to limited array size
method operator data takes nothing returns $TYPE$
return Table(this).$TYPE$[0] // hashtable[ node, 0 ] = data
endmethod
method operator data= takes $TYPE$ value returns nothing
set Table(this).$TYPE$[0] = value
endmethod
method operator next takes nothing returns thistype
return Table(this)[1] // hashtable[ node, 1 ] = next
endmethod
method operator next= takes thistype value returns nothing
set Table(this)[1] = value
endmethod
method operator prev takes nothing returns thistype
return Table(this)[-1] // hashtable[ node, -1 ] = prev
endmethod
method operator prev= takes thistype value returns nothing
set Table(this)[-1] = value
endmethod
endstruct
$ACCESS$ struct $NAME$ extends array
readonly $NAME$Item first
readonly $NAME$Item last
private integer count
implement Alloc
private static method setNodeOwner takes $NAME$Item node, $NAME$ owner returns nothing
set Table(node)[2] = owner
endmethod
private static method getNodeOwner takes $NAME$Item node returns thistype
return Table(node)[2]
endmethod
private method createNode takes $TYPE$ value returns $NAME$Item
local $NAME$Item node = Table.create()
set node.data = value
call setNodeOwner(node, this) // ownership
return node
endmethod
private method deleteNode takes $NAME$Item node returns nothing
call Table(node).destroy() // also removes ownership
endmethod
static method create takes nothing returns thistype
local thistype this = allocate()
set count = 0
return this
endmethod
method clear takes nothing returns nothing
local $NAME$Item node = first
local $NAME$Item temp
loop // recycle all Table indexes
exitwhen 0 == node
set temp = node.next
call deleteNode(node)
set node = temp
endloop
set first = 0
set last = 0
set count = 0
endmethod
method destroy takes nothing returns nothing
call clear()
call deallocate()
endmethod
method front takes nothing returns $TYPE$
return first.data
endmethod
method back takes nothing returns $TYPE$
return last.data
endmethod
method empty takes nothing returns boolean
return count == 0
endmethod
method size takes nothing returns integer
return count
endmethod
method push takes $TYPE$ value returns thistype
local $NAME$Item node = createNode(value)
if not empty() then
set last.next = node
set node.prev = last
else
set first = node
set node.prev = 0
endif
set last = node
set node.next = 0
set count = count + 1
return this
endmethod
method unshift takes $TYPE$ value returns thistype
local $NAME$Item node = createNode(value)
if not empty() then
set first.prev = node
set node.next = first
else
set last = node
set node.next = 0
endif
set first = node
set node.prev = 0
set count = count + 1
return this
endmethod
method pop takes nothing returns thistype
local $NAME$Item node
if not empty() then
set node = last
set last = last.prev
if last == 0 then
set first = 0
else
set last.next = 0
endif
call deleteNode(node)
set count = count - 1
debug else
debug call DisplayTimedTextFromPlayer(GetLocalPlayer(),0,0,60,"$NAME$::pop failed for instance "+I2S(this)+". List is empty.")
endif
return this
endmethod
method shift takes nothing returns thistype
local $NAME$Item node
if not empty() then
set node = first
set first = first.next
if first == 0 then
set last = 0
else
set first.prev = 0
endif
call deleteNode(node)
set count = count - 1
debug else
debug call DisplayTimedTextFromPlayer(GetLocalPlayer(),0,0,60,"$NAME$::shift failed for instance "+I2S(this)+". List is empty.")
endif
return this
endmethod
static method operator [] takes thistype other returns thistype
local thistype instance = create()
local $NAME$Item node = other.first
loop
exitwhen node == 0
call instance.push(node.data)
set node = node.next
endloop
return instance
endmethod
method find takes $TYPE$ value returns $NAME$Item
local $NAME$Item node = first
loop
exitwhen node == 0 or node.data == value
set node = node.next
endloop
return node
endmethod
method erase takes $NAME$Item node returns boolean
if getNodeOwner(node) == this then // match ownership
if node == first then
call shift()
elseif node == last then
call pop()
else
set node.prev.next = node.next
set node.next.prev = node.prev
call deleteNode(node)
set count = count - 1
endif
return true
debug else
debug call DisplayTimedTextFromPlayer(GetLocalPlayer(),0,0,60,"$NAME$::erase failed for instance "+I2S(this)+". Attempted to remove invalid node "+I2S(node)+".")
endif
return false
endmethod
method remove takes $NAME$Item node returns boolean
debug call DisplayTimedTextFromPlayer(GetLocalPlayer(),0,0,60,"Method $NAME$::remove is obsolete, use $NAME$::erase instead.")
return erase(node)
endmethod
method removeElem takes $TYPE$ value returns thistype
local $NAME$Item node = find(value)
if node != 0 then
call remove(node)
endif
return this
endmethod
endstruct
//! endtextmacro
endlibrary
library Table /* made by Bribe, special thanks to Vexorian & Nestharus, version 4.1.0.1.
One map, one hashtable. Welcome to NewTable 4.1.0.1
This newest iteration of Table introduces the new HashTable struct.
You can now instantiate HashTables which enables the use of large
parent and large child keys, just like a standard hashtable. Previously,
the user would have to instantiate a Table to do this on their own which -
while doable - is something the user should not have to do if I can add it
to this resource myself (especially if they are inexperienced).
This library was originally called NewTable so it didn't conflict with
the API of Table by Vexorian. However, the damage is done and it's too
late to change the library name now. To help with damage control, I
have provided an extension library called TableBC, which bridges all
the functionality of Vexorian's Table except for 2-D string arrays &
the ".flush(integer)" method. I use ".flush()" to flush a child hash-
table, because I wanted the API in NewTable to reflect the API of real
hashtables (I thought this would be more intuitive).
API
------------
struct Table
| static method create takes nothing returns Table
| create a new Table
|
| method destroy takes nothing returns nothing
| destroy it
|
| method flush takes nothing returns nothing
| flush all stored values inside of it
|
| method remove takes integer key returns nothing
| remove the value at index "key"
|
| method operator []= takes integer key, $TYPE$ value returns nothing
| assign "value" to index "key"
|
| method operator [] takes integer key returns $TYPE$
| load the value at index "key"
|
| method has takes integer key returns boolean
| whether or not the key was assigned
|
----------------
struct TableArray
| static method operator [] takes integer array_size returns TableArray
| create a new array of Tables of size "array_size"
|
| method destroy takes nothing returns nothing
| destroy it
|
| method flush takes nothing returns nothing
| flush and destroy it
|
| method operator size takes nothing returns integer
| returns the size of the TableArray
|
| method operator [] takes integer key returns Table
| returns a Table accessible exclusively to index "key"
*/
globals
private integer less = 0 //Index generation for TableArrays (below 0).
private integer more = 8190 //Index generation for Tables.
//Configure it if you use more than 8190 "key" variables in your map (this will never happen though).
private hashtable ht = InitHashtable()
private key sizeK
private key listK
endglobals
private struct dex extends array
static method operator size takes nothing returns Table
return sizeK
endmethod
static method operator list takes nothing returns Table
return listK
endmethod
endstruct
private struct handles extends array
method has takes integer key returns boolean
return HaveSavedHandle(ht, this, key)
endmethod
method remove takes integer key returns nothing
call RemoveSavedHandle(ht, this, key)
endmethod
endstruct
private struct agents extends array
method operator []= takes integer key, agent value returns nothing
call SaveAgentHandle(ht, this, key, value)
endmethod
endstruct
//! textmacro NEW_ARRAY_BASIC takes SUPER, FUNC, TYPE
private struct $TYPE$s extends array
method operator [] takes integer key returns $TYPE$
return Load$FUNC$(ht, this, key)
endmethod
method operator []= takes integer key, $TYPE$ value returns nothing
call Save$FUNC$(ht, this, key, value)
endmethod
method has takes integer key returns boolean
return HaveSaved$SUPER$(ht, this, key)
endmethod
method remove takes integer key returns nothing
call RemoveSaved$SUPER$(ht, this, key)
endmethod
endstruct
private module $TYPE$m
method operator $TYPE$ takes nothing returns $TYPE$s
return this
endmethod
endmodule
//! endtextmacro
//! textmacro NEW_ARRAY takes FUNC, TYPE
private struct $TYPE$s extends array
method operator [] takes integer key returns $TYPE$
return Load$FUNC$Handle(ht, this, key)
endmethod
method operator []= takes integer key, $TYPE$ value returns nothing
call Save$FUNC$Handle(ht, this, key, value)
endmethod
method has takes integer key returns boolean
return HaveSavedHandle(ht, this, key)
endmethod
method remove takes integer key returns nothing
call RemoveSavedHandle(ht, this, key)
endmethod
endstruct
private module $TYPE$m
method operator $TYPE$ takes nothing returns $TYPE$s
return this
endmethod
endmodule
//! endtextmacro
//Run these textmacros to include the entire hashtable API as wrappers.
//Don't be intimidated by the number of macros - Vexorian's map optimizer is
//supposed to kill functions which inline (all of these functions inline).
//! runtextmacro NEW_ARRAY_BASIC("Real", "Real", "real")
//! runtextmacro NEW_ARRAY_BASIC("Boolean", "Boolean", "boolean")
//! runtextmacro NEW_ARRAY_BASIC("String", "Str", "string")
//New textmacro to allow table.integer[] syntax for compatibility with textmacros that might desire it.
//! runtextmacro NEW_ARRAY_BASIC("Integer", "Integer", "integer")
//! runtextmacro NEW_ARRAY("Player", "player")
//! runtextmacro NEW_ARRAY("Widget", "widget")
//! runtextmacro NEW_ARRAY("Destructable", "destructable")
//! runtextmacro NEW_ARRAY("Item", "item")
//! runtextmacro NEW_ARRAY("Unit", "unit")
//! runtextmacro NEW_ARRAY("Ability", "ability")
//! runtextmacro NEW_ARRAY("Timer", "timer")
//! runtextmacro NEW_ARRAY("Trigger", "trigger")
//! runtextmacro NEW_ARRAY("TriggerCondition", "triggercondition")
//! runtextmacro NEW_ARRAY("TriggerAction", "triggeraction")
//! runtextmacro NEW_ARRAY("TriggerEvent", "event")
//! runtextmacro NEW_ARRAY("Force", "force")
//! runtextmacro NEW_ARRAY("Group", "group")
//! runtextmacro NEW_ARRAY("Location", "location")
//! runtextmacro NEW_ARRAY("Rect", "rect")
//! runtextmacro NEW_ARRAY("BooleanExpr", "boolexpr")
//! runtextmacro NEW_ARRAY("Sound", "sound")
//! runtextmacro NEW_ARRAY("Effect", "effect")
//! runtextmacro NEW_ARRAY("UnitPool", "unitpool")
//! runtextmacro NEW_ARRAY("ItemPool", "itempool")
//! runtextmacro NEW_ARRAY("Quest", "quest")
//! runtextmacro NEW_ARRAY("QuestItem", "questitem")
//! runtextmacro NEW_ARRAY("DefeatCondition", "defeatcondition")
//! runtextmacro NEW_ARRAY("TimerDialog", "timerdialog")
//! runtextmacro NEW_ARRAY("Leaderboard", "leaderboard")
//! runtextmacro NEW_ARRAY("Multiboard", "multiboard")
//! runtextmacro NEW_ARRAY("MultiboardItem", "multiboarditem")
//! runtextmacro NEW_ARRAY("Trackable", "trackable")
//! runtextmacro NEW_ARRAY("Dialog", "dialog")
//! runtextmacro NEW_ARRAY("Button", "button")
//! runtextmacro NEW_ARRAY("TextTag", "texttag")
//! runtextmacro NEW_ARRAY("Lightning", "lightning")
//! runtextmacro NEW_ARRAY("Image", "image")
//! runtextmacro NEW_ARRAY("Ubersplat", "ubersplat")
//! runtextmacro NEW_ARRAY("Region", "region")
//! runtextmacro NEW_ARRAY("FogState", "fogstate")
//! runtextmacro NEW_ARRAY("FogModifier", "fogmodifier")
//! runtextmacro NEW_ARRAY("Hashtable", "hashtable")
struct Table extends array
// Implement modules for intuitive syntax (tb.handle; tb.unit; etc.)
implement realm
implement integerm
implement booleanm
implement stringm
implement playerm
implement widgetm
implement destructablem
implement itemm
implement unitm
implement abilitym
implement timerm
implement triggerm
implement triggerconditionm
implement triggeractionm
implement eventm
implement forcem
implement groupm
implement locationm
implement rectm
implement boolexprm
implement soundm
implement effectm
implement unitpoolm
implement itempoolm
implement questm
implement questitemm
implement defeatconditionm
implement timerdialogm
implement leaderboardm
implement multiboardm
implement multiboarditemm
implement trackablem
implement dialogm
implement buttonm
implement texttagm
implement lightningm
implement imagem
implement ubersplatm
implement regionm
implement fogstatem
implement fogmodifierm
implement hashtablem
method operator handle takes nothing returns handles
return this
endmethod
method operator agent takes nothing returns agents
return this
endmethod
//set this = tb[GetSpellAbilityId()]
method operator [] takes integer key returns Table
return LoadInteger(ht, this, key) //return this.integer[key]
endmethod
//set tb[389034] = 8192
method operator []= takes integer key, Table tb returns nothing
call SaveInteger(ht, this, key, tb) //set this.integer[key] = tb
endmethod
//set b = tb.has(2493223)
method has takes integer key returns boolean
return HaveSavedInteger(ht, this, key) //return this.integer.has(key)
endmethod
//call tb.remove(294080)
method remove takes integer key returns nothing
call RemoveSavedInteger(ht, this, key) //call this.integer.remove(key)
endmethod
//Remove all data from a Table instance
method flush takes nothing returns nothing
call FlushChildHashtable(ht, this)
endmethod
//local Table tb = Table.create()
static method create takes nothing returns Table
local Table this = dex.list[0]
if this == 0 then
set this = more + 1
set more = this
else
set dex.list[0] = dex.list[this]
call dex.list.remove(this) //Clear hashed memory
endif
debug set dex.list[this] = -1
return this
endmethod
// Removes all data from a Table instance and recycles its index.
//
// call tb.destroy()
//
method destroy takes nothing returns nothing
debug if dex.list[this] != -1 then
debug call BJDebugMsg("Table Error: Tried to double-free instance: " + I2S(this))
debug return
debug endif
call this.flush()
set dex.list[this] = dex.list[0]
set dex.list[0] = this
endmethod
//! runtextmacro optional TABLE_BC_METHODS()
endstruct
//! runtextmacro optional TABLE_BC_STRUCTS()
struct TableArray extends array
//Returns a new TableArray to do your bidding. Simply use:
//
// local TableArray ta = TableArray[array_size]
//
static method operator [] takes integer array_size returns TableArray
local Table tb = dex.size[array_size] //Get the unique recycle list for this array size
local TableArray this = tb[0] //The last-destroyed TableArray that had this array size
debug if array_size <= 0 then
debug call BJDebugMsg("TypeError: Invalid specified TableArray size: " + I2S(array_size))
debug return 0
debug endif
if this == 0 then
set this = less - array_size
set less = this
else
set tb[0] = tb[this] //Set the last destroyed to the last-last destroyed
call tb.remove(this) //Clear hashed memory
endif
set dex.size[this] = array_size //This remembers the array size
return this
endmethod
//Returns the size of the TableArray
method operator size takes nothing returns integer
return dex.size[this]
endmethod
//This magic method enables two-dimensional[array][syntax] for Tables,
//similar to the two-dimensional utility provided by hashtables them-
//selves.
//
//ta[integer a].unit[integer b] = unit u
//ta[integer a][integer c] = integer d
//
//Inline-friendly when not running in debug mode
//
method operator [] takes integer key returns Table
static if DEBUG_MODE then
local integer i = this.size
if i == 0 then
call BJDebugMsg("IndexError: Tried to get key from invalid TableArray instance: " + I2S(this))
return 0
elseif key < 0 or key >= i then
call BJDebugMsg("IndexError: Tried to get key [" + I2S(key) + "] from outside TableArray bounds: " + I2S(i))
return 0
endif
endif
return this + key
endmethod
//Destroys a TableArray without flushing it; I assume you call .flush()
//if you want it flushed too. This is a public method so that you don't
//have to loop through all TableArray indices to flush them if you don't
//need to (ie. if you were flushing all child-keys as you used them).
//
method destroy takes nothing returns nothing
local Table tb = dex.size[this.size]
debug if this.size == 0 then
debug call BJDebugMsg("TypeError: Tried to destroy an invalid TableArray: " + I2S(this))
debug return
debug endif
if tb == 0 then
//Create a Table to index recycled instances with their array size
set tb = Table.create()
set dex.size[this.size] = tb
endif
call dex.size.remove(this) //Clear the array size from hash memory
set tb[this] = tb[0]
set tb[0] = this
endmethod
private static Table tempTable
private static integer tempEnd
//Avoids hitting the op limit
private static method clean takes nothing returns nothing
local Table tb = .tempTable
local integer end = tb + 0x1000
if end < .tempEnd then
set .tempTable = end
call ForForce(bj_FORCE_PLAYER[0], function thistype.clean)
else
set end = .tempEnd
endif
loop
call tb.flush()
set tb = tb + 1
exitwhen tb == end
endloop
endmethod
//Flushes the TableArray and also destroys it. Doesn't get any more
//similar to the FlushParentHashtable native than this.
//
method flush takes nothing returns nothing
debug if this.size == 0 then
debug call BJDebugMsg("TypeError: Tried to flush an invalid TableArray instance: " + I2S(this))
debug return
debug endif
set .tempTable = this
set .tempEnd = this + this.size
call ForForce(bj_FORCE_PLAYER[0], function thistype.clean)
call this.destroy()
endmethod
endstruct
//NEW: Added in Table 4.0. A fairly simple struct but allows you to do more
//than that which was previously possible.
struct HashTable extends array
//Enables myHash[parentKey][childKey] syntax.
//Basically, it creates a Table in the place of the parent key if
//it didn't already get created earlier.
method operator [] takes integer index returns Table
local Table t = Table(this)[index]
if t == 0 then
set t = Table.create()
set Table(this)[index] = t //whoops! Forgot that line. I'm out of practice!
endif
return t
endmethod
//You need to call this on each parent key that you used if you
//intend to destroy the HashTable or simply no longer need that key.
method remove takes integer index returns nothing
local Table t = Table(this)[index]
if t != 0 then
call t.destroy()
call Table(this).remove(index)
endif
endmethod
//Added in version 4.1
method has takes integer index returns boolean
return Table(this).has(index)
endmethod
//HashTables are just fancy Table indices.
method destroy takes nothing returns nothing
call Table(this).destroy()
endmethod
//Like I said above...
static method create takes nothing returns thistype
return Table.create()
endmethod
endstruct
endlibrary
library Alloc /* v1.3.1.1
*************************************************************************************
*
* */ uses /*
*
* */ optional ErrorMessage /* github.com/nestharus/JASS/tree/master/jass/Systems/ErrorMessage
* */ optional MemoryAnalysis /*
*
*************************************************************************************
*
* Minimizes code generation and global variables while maintaining
* excellent performance.
*
* local thistype this = recycler[0]
*
* if (recycler[this] == 0) then
* set recycler[0] = this + 1
* else
* set recycler[0] = recycler[this]
* endif
*
************************************************************************************
*
* module Alloc
*
* static method allocate takes nothing returns thistype
* method deallocate takes nothing returns nothing
*
* The Following Require Error Message To Be In The Map
* --------------------------------------------------------
*
* debug readonly boolean allocated
*
* The Following Require Memory Analysis To Be In The Map
* --------------------------------------------------------
*
* debug readonly integer monitorCount
* - the amount of global memory being monitored by this
* debug readonly integer monitorString
* - gets a string representation of all global memory being monitored by this
* debug readonly integer address
* - global memory address for debugging
* - used with monitor and stopMonitor
*
* debug static method calculateMemoryUsage takes nothing returns integer
* debug static method getAllocatedMemoryAsString takes nothing returns string
*
* debug method monitor takes string label, integer address returns nothing
* - monitor a global memory address with a label
* - used to identify memory leaks
* - should be memory that ought to be destroyed by the time this is destroyed
* debug method stopMonitor takes integer address returns nothing
* - stops monitoring global memory
* debug method stopMonitorValue takes handle monitoredHandle returns nothing
* - stops monitoring handle values
*
* The Following Are Used To Monitor Handle Values
*
* debug method monitor_widget takes string label, widget handleToTrack returns nothing
* debug method monitor_destructable takes string label, destructable handleToTrack returns nothing
* debug method monitor_item takes string label, item handleToTrack returns nothing
* debug method monitor_unit takes string label, unit handleToTrack returns nothing
* debug method monitor_timer takes string label, timer handleToTrack returns nothing
* debug method monitor_trigger takes string label, trigger handleToTrack returns nothing
* debug method monitor_triggercondition takes string label, triggercondition handleToTrack returns nothing
* debug method monitor_triggeraction takes string label, triggeraction handleToTrack returns nothing
* debug method monitor_force takes string label, force handleToTrack returns nothing
* debug method monitor_group takes string label, group handleToTrack returns nothing
* debug method monitor_location takes string label, location handleToTrack returns nothing
* debug method monitor_rect takes string label, rect handleToTrack returns nothing
* debug method monitor_boolexpr takes string label, boolexpr handleToTrack returns nothing
* debug method monitor_effect takes string label, effect handleToTrack returns nothing
* debug method monitor_unitpool takes string label, unitpool handleToTrack returns nothing
* debug method monitor_itempool takes string label, itempool handleToTrack returns nothing
* debug method monitor_quest takes string label, quest handleToTrack returns nothing
* debug method monitor_defeatcondition takes string label, defeatcondition handleToTrack returns nothing
* debug method monitor_timerdialog takes string label, timerdialog handleToTrack returns nothing
* debug method monitor_leaderboard takes string label, leaderboard handleToTrack returns nothing
* debug method monitor_multiboard takes string label, multiboard handleToTrack returns nothing
* debug method monitor_multiboarditem takes string label, multiboarditem handleToTrack returns nothing
* debug method monitor_dialog takes string label, dialog handleToTrack returns nothing
* debug method monitor_button takes string label, button handleToTrack returns nothing
* debug method monitor_texttag takes string label, texttag handleToTrack returns nothing
* debug method monitor_lightning takes string label, lightning handleToTrack returns nothing
* debug method monitor_image takes string label, image handleToTrack returns nothing
* debug method monitor_ubersplat takes string label, ubersplat handleToTrack returns nothing
* debug method monitor_region takes string label, region handleToTrack returns nothing
* debug method monitor_fogmodifier takes string label, fogmodifier handleToTrack returns nothing
* debug method monitor_hashtable takes string label, hashtable handleToTrack returns nothing
*
*
* Thanks to Ruke for the algorithm
************************************************************************************/
module Alloc
/*
* stack
*/
private static integer array recycler
static if LIBRARY_MemoryAnalysis then
debug private MemoryMonitor globalAddress
debug method operator address takes nothing returns integer
debug call ThrowError(recycler[this] != -1, "Alloc", "address", "thistype", this, "Attempted To Access Null Instance.")
debug return globalAddress
debug endmethod
endif
/*
* allocation
*/
static method allocate takes nothing returns thistype
local thistype this = recycler[0]
static if LIBRARY_ErrorMessage then
debug call ThrowError(this == 8192, "Alloc", "allocate", "thistype", 0, "Overflow.")
endif
if (recycler[this] == 0) then
set recycler[0] = this + 1
else
set recycler[0] = recycler[this]
endif
static if LIBRARY_ErrorMessage then
debug set recycler[this] = -1
endif
static if LIBRARY_MemoryAnalysis then
debug set globalAddress = MemoryMonitor.allocate("thistype")
endif
return this
endmethod
method deallocate takes nothing returns nothing
static if LIBRARY_ErrorMessage then
debug call ThrowError(recycler[this] != -1, "Alloc", "deallocate", "thistype", this, "Attempted To Deallocate Null Instance.")
endif
static if LIBRARY_MemoryAnalysis then
debug call globalAddress.deallocate()
debug set globalAddress = 0
endif
set recycler[this] = recycler[0]
set recycler[0] = this
endmethod
static if LIBRARY_MemoryAnalysis then
debug method monitor takes string label, integer address returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor(label, address)
debug endmethod
debug method stopMonitor takes integer address returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "stopMonitor", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.stopMonitor(address)
debug endmethod
debug method stopMonitorValue takes handle monitoredHandle returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "stopMonitorValue", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.stopMonitorValue(monitoredHandle)
debug endmethod
debug method operator monitorCount takes nothing returns integer
debug call ThrowError(recycler[this] != -1, "Alloc", "monitorCount", "thistype", this, "Attempted To Access Null Instance.")
debug return globalAddress.monitorCount
debug endmethod
debug method operator monitorString takes nothing returns string
debug call ThrowError(recycler[this] != -1, "Alloc", "monitorString", "thistype", this, "Attempted To Access Null Instance.")
debug return globalAddress.monitorString
debug endmethod
debug method monitor_widget takes string label, widget handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_widget", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_widget(label, handleToTrack)
debug endmethod
debug method monitor_destructable takes string label, destructable handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_destructable", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_destructable(label, handleToTrack)
debug endmethod
debug method monitor_item takes string label, item handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_item", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_item(label, handleToTrack)
debug endmethod
debug method monitor_unit takes string label, unit handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_unit", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_unit(label, handleToTrack)
debug endmethod
debug method monitor_timer takes string label, timer handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_timer", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_timer(label, handleToTrack)
debug endmethod
debug method monitor_trigger takes string label, trigger handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_trigger", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_trigger(label, handleToTrack)
debug endmethod
debug method monitor_triggercondition takes string label, triggercondition handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_triggercondition", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_triggercondition(label, handleToTrack)
debug endmethod
debug method monitor_triggeraction takes string label, triggeraction handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_triggeraction", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_triggeraction(label, handleToTrack)
debug endmethod
debug method monitor_force takes string label, force handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_force", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_force(label, handleToTrack)
debug endmethod
debug method monitor_group takes string label, group handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_group", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_group(label, handleToTrack)
debug endmethod
debug method monitor_location takes string label, location handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_location", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_location(label, handleToTrack)
debug endmethod
debug method monitor_rect takes string label, rect handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_rect", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_rect(label, handleToTrack)
debug endmethod
debug method monitor_boolexpr takes string label, boolexpr handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_boolexpr", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_boolexpr(label, handleToTrack)
debug endmethod
debug method monitor_effect takes string label, effect handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_effect", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_effect(label, handleToTrack)
debug endmethod
debug method monitor_unitpool takes string label, unitpool handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_unitpool", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_unitpool(label, handleToTrack)
debug endmethod
debug method monitor_itempool takes string label, itempool handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_itempool", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_itempool(label, handleToTrack)
debug endmethod
debug method monitor_quest takes string label, quest handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_quest", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_quest(label, handleToTrack)
debug endmethod
debug method monitor_defeatcondition takes string label, defeatcondition handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_defeatcondition", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_defeatcondition(label, handleToTrack)
debug endmethod
debug method monitor_timerdialog takes string label, timerdialog handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_timerdialog", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_timerdialog(label, handleToTrack)
debug endmethod
debug method monitor_leaderboard takes string label, leaderboard handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_leaderboard", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_leaderboard(label, handleToTrack)
debug endmethod
debug method monitor_multiboard takes string label, multiboard handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_multiboard", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_multiboard(label, handleToTrack)
debug endmethod
debug method monitor_multiboarditem takes string label, multiboarditem handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_multiboarditem", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_multiboarditem(label, handleToTrack)
debug endmethod
debug method monitor_dialog takes string label, dialog handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_dialog", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_dialog(label, handleToTrack)
debug endmethod
debug method monitor_button takes string label, button handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_button", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_button(label, handleToTrack)
debug endmethod
debug method monitor_texttag takes string label, texttag handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_texttag", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_texttag(label, handleToTrack)
debug endmethod
debug method monitor_lightning takes string label, lightning handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_lightning", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_lightning(label, handleToTrack)
debug endmethod
debug method monitor_image takes string label, image handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_image", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_image(label, handleToTrack)
debug endmethod
debug method monitor_ubersplat takes string label, ubersplat handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_ubersplat", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_ubersplat(label, handleToTrack)
debug endmethod
debug method monitor_region takes string label, region handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_region", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_region(label, handleToTrack)
debug endmethod
debug method monitor_fogmodifier takes string label, fogmodifier handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_fogmodifier", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_fogmodifier(label, handleToTrack)
debug endmethod
debug method monitor_hashtable takes string label, hashtable handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_hashtable", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_hashtable(label, handleToTrack)
debug endmethod
static if DEBUG_MODE then
//! runtextmacro optional MEMORY_ANALYSIS_STATIC_FIELD_NEW("recycler")
static method calculateMemoryUsage takes nothing returns integer
return calculateAllocatedMemory__recycler()
endmethod
static method getAllocatedMemoryAsString takes nothing returns string
return allocatedMemoryString__recycler()
endmethod
endif
endif
/*
* analysis
*/
static if LIBRARY_ErrorMessage then
debug method operator allocated takes nothing returns boolean
debug return recycler[this] == -1
debug endmethod
endif
/*
* initialization
*/
private static method onInit takes nothing returns nothing
set recycler[0] = 1
endmethod
endmodule
endlibrary
library TimerUtils initializer init
//*********************************************************************
//* TimerUtils (red+blue+orange flavors for 1.24b+) 2.0
//* ----------
//*
//* To implement it , create a custom text trigger called TimerUtils
//* and paste the contents of this script there.
//*
//* To copy from a map to another, copy the trigger holding this
//* library to your map.
//*
//* (requires vJass) More scripts: htt://www.wc3c.net
//*
//* For your timer needs:
//* * Attaching
//* * Recycling (with double-free protection)
//*
//* set t=NewTimer() : Get a timer (alternative to CreateTimer)
//* set t=NewTimerEx(x) : Get a timer (alternative to CreateTimer), call
//* Initialize timer data as x, instead of 0.
//*
//* ReleaseTimer(t) : Relese a timer (alt to DestroyTimer)
//* SetTimerData(t,2) : Attach value 2 to timer
//* GetTimerData(t) : Get the timer's value.
//* You can assume a timer's value is 0
//* after NewTimer.
//*
//* Multi-flavor:
//* Set USE_HASH_TABLE to true if you don't want to complicate your life.
//*
//* If you like speed and giberish try learning about the other flavors.
//*
//********************************************************************
//================================================================
globals
//How to tweak timer utils:
// USE_HASH_TABLE = true (new blue)
// * SAFEST
// * SLOWEST (though hash tables are kind of fast)
//
// USE_HASH_TABLE = false, USE_FLEXIBLE_OFFSET = true (orange)
// * kinda safe (except there is a limit in the number of timers)
// * ALMOST FAST
//
// USE_HASH_TABLE = false, USE_FLEXIBLE_OFFSET = false (red)
// * THE FASTEST (though is only faster than the previous method
// after using the optimizer on the map)
// * THE LEAST SAFE ( you may have to tweak OFSSET manually for it to
// work)
//
private constant boolean USE_HASH_TABLE = true
private constant boolean USE_FLEXIBLE_OFFSET = true
private constant integer OFFSET = 0x100000
private integer VOFFSET = OFFSET
//Timers to preload at map init:
private constant integer QUANTITY = 256
//Changing this to something big will allow you to keep recycling
// timers even when there are already AN INCREDIBLE AMOUNT of timers in
// the stack. But it will make things far slower so that's probably a bad idea...
private constant integer ARRAY_SIZE = 8190
endglobals
//==================================================================================================
globals
private integer array data[ARRAY_SIZE]
private hashtable ht
endglobals
//It is dependent on jasshelper's recent inlining optimization in order to perform correctly.
function SetTimerData takes timer t, integer value returns nothing
static if(USE_HASH_TABLE) then
// new blue
call SaveInteger(ht,0,GetHandleId(t), value)
elseif (USE_FLEXIBLE_OFFSET) then
// orange
static if (DEBUG_MODE) then
if(GetHandleId(t)-VOFFSET<0) then
call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
endif
endif
set data[GetHandleId(t)-VOFFSET]=value
else
// new red
static if (DEBUG_MODE) then
if(GetHandleId(t)-OFFSET<0) then
call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
endif
endif
set data[GetHandleId(t)-OFFSET]=value
endif
endfunction
function GetTimerData takes timer t returns integer
static if(USE_HASH_TABLE) then
// new blue
return LoadInteger(ht,0,GetHandleId(t) )
elseif (USE_FLEXIBLE_OFFSET) then
// orange
static if (DEBUG_MODE) then
if(GetHandleId(t)-VOFFSET<0) then
call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
endif
endif
return data[GetHandleId(t)-VOFFSET]
else
// new red
static if (DEBUG_MODE) then
if(GetHandleId(t)-OFFSET<0) then
call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
endif
endif
return data[GetHandleId(t)-OFFSET]
endif
endfunction
//==========================================================================================
globals
private timer array tT[ARRAY_SIZE]
private integer tN = 0
private constant integer HELD=0x28829022
//use a totally random number here, the more improbable someone uses it, the better.
private boolean didinit = false
endglobals
private keyword init
//==========================================================================================
// I needed to decide between duplicating code ignoring the "Once and only once" rule
// and using the ugly textmacros. I guess textmacros won.
//
//! textmacro TIMERUTIS_PRIVATE_NewTimerCommon takes VALUE
// On second thought, no.
//! endtextmacro
function NewTimerEx takes integer value returns timer
if (tN==0) then
if (not didinit) then
//This extra if shouldn't represent a major performance drawback
//because QUANTITY rule is not supposed to be broken every day.
call init.evaluate()
set tN = tN - 1
else
//If this happens then the QUANTITY rule has already been broken, try to fix the
// issue, else fail.
debug call BJDebugMsg("NewTimer: Warning, Exceeding TimerUtils_QUANTITY, make sure all timers are getting recycled correctly")
set tT[0]=CreateTimer()
static if( not USE_HASH_TABLE) then
debug call BJDebugMsg("In case of errors, please increase it accordingly, or set TimerUtils_USE_HASH_TABLE to true")
static if( USE_FLEXIBLE_OFFSET) then
if (GetHandleId(tT[0])-VOFFSET<0) or (GetHandleId(tT[0])-VOFFSET>=ARRAY_SIZE) then
//all right, couldn't fix it
call BJDebugMsg("NewTimer: Unable to allocate a timer, you should probably set TimerUtils_USE_HASH_TABLE to true or fix timer leaks.")
return null
endif
else
if (GetHandleId(tT[0])-OFFSET<0) or (GetHandleId(tT[0])-OFFSET>=ARRAY_SIZE) then
//all right, couldn't fix it
call BJDebugMsg("NewTimer: Unable to allocate a timer, you should probably set TimerUtils_USE_HASH_TABLE to true or fix timer leaks.")
return null
endif
endif
endif
endif
else
set tN=tN-1
endif
call SetTimerData(tT[tN],value)
return tT[tN]
endfunction
function NewTimer takes nothing returns timer
return NewTimerEx(0)
endfunction
//==========================================================================================
function ReleaseTimer takes timer t returns nothing
if(t==null) then
debug call BJDebugMsg("Warning: attempt to release a null timer")
return
endif
if (tN==ARRAY_SIZE) then
debug call BJDebugMsg("Warning: Timer stack is full, destroying timer!!")
//stack is full, the map already has much more troubles than the chance of bug
call DestroyTimer(t)
else
call PauseTimer(t)
if(GetTimerData(t)==HELD) then
debug call BJDebugMsg("Warning: ReleaseTimer: Double free!")
return
endif
call SetTimerData(t,HELD)
set tT[tN]=t
set tN=tN+1
endif
endfunction
private function init takes nothing returns nothing
local integer i=0
local integer o=-1
local boolean oops = false
if ( didinit ) then
return
else
set didinit = true
endif
static if( USE_HASH_TABLE ) then
set ht = InitHashtable()
loop
exitwhen(i==QUANTITY)
set tT[i]=CreateTimer()
call SetTimerData(tT[i], HELD)
set i=i+1
endloop
set tN = QUANTITY
else
loop
set i=0
loop
exitwhen (i==QUANTITY)
set tT[i] = CreateTimer()
if(i==0) then
set VOFFSET = GetHandleId(tT[i])
static if(USE_FLEXIBLE_OFFSET) then
set o=VOFFSET
else
set o=OFFSET
endif
endif
if (GetHandleId(tT[i])-o>=ARRAY_SIZE) then
exitwhen true
endif
if (GetHandleId(tT[i])-o>=0) then
set i=i+1
endif
endloop
set tN = i
exitwhen(tN == QUANTITY)
set oops = true
exitwhen not USE_FLEXIBLE_OFFSET
debug call BJDebugMsg("TimerUtils_init: Failed a initialization attempt, will try again")
endloop
if(oops) then
static if ( USE_FLEXIBLE_OFFSET) then
debug call BJDebugMsg("The problem has been fixed.")
//If this message doesn't appear then there is so much
//handle id fragmentation that it was impossible to preload
//so many timers and the thread crashed! Therefore this
//debug message is useful.
elseif(DEBUG_MODE) then
call BJDebugMsg("There were problems and the new timer limit is "+I2S(i))
call BJDebugMsg("This is a rare ocurrence, if the timer limit is too low:")
call BJDebugMsg("a) Change USE_FLEXIBLE_OFFSET to true (reduces performance a little)")
call BJDebugMsg("b) or try changing OFFSET to "+I2S(VOFFSET) )
endif
endif
endif
endfunction
endlibrary
library Beam requires TimerUtils optional WorldBounds
/*
Beam v1.00
by Spellbound
DESCRIPTION_________________________________
Beam is a lightning effect handler that uses a moving-point system to track where the beam
goes. It also has a hit-box feature that allows you to do unit-to-unit lightning effects
that strike coordinates offset from the core of the model, which gives the appearance that
the beam is hitting the surface of the model.
Similar to libraries like Missile and Buff System, every lightning effect generated by Beam
has its own code that it can run, allowing to tell the system what specific beam does when
it is cast, when and interval occures, when it begins to fade, or when it ends.
INSTALLATION________________________________
Beam requires TimerUtils. Get it from this map or at this link:
http://www.wc3c.net/showthread.php?t=101322
Then copy the system itself in your map and you're good to go.
USAGE_______________________________________
Beam requires a basic understanding or structs, but even if you don't have that knowledge,
don't let that scare you! It's actually quite simple once you wrap your head around it. If
you're already familiar with structs, you can move on to the API section. Otherwise, follow
this link for a simple tutorial:
[link]
*/
//! novjass
API_________________________________________
Beam.create()
//This will generate a beam instance. It doesn't do antyhing else. You must configure its
//various parameters individually.
Eg:
local Beam b = Beam.create()
set b.source = GetTriggerUnit()
set b.beamFX = "CLPB"
etc...
[StructName].cast(your beam)
//This is called after all necessary parameters have been set. It will create your beam.
//! endnovjass
globals
public constant real TIMEOUT = .03125
private constant location LOC = Location(0., 0.)
private effect FX = null
endglobals
public function GetLocZ takes real x, real y returns real
call MoveLocation(LOC, x, y)
return GetLocationZ(LOC)
endfunction
function GetLocalUnitZ takes unit u returns real
call MoveLocation(LOC, GetUnitX(u), GetUnitY(u))
return GetUnitFlyHeight(u) + GetLocationZ(LOC)
endfunction
public function AddEffect takes string s, real x, real y, real z, real scale returns effect
set FX = AddSpecialEffect(s, x, y)
call BlzSetSpecialEffectZ(FX, z)
call BlzSetSpecialEffectScale(FX, scale)
return FX
endfunction
// Beam getters
function GetBeamAngle takes Beam b returns real
return Atan2(b.targetpoint.y - b.sourcepoint.y, b.targetpoint.x - b.sourcepoint.x)
endfunction
function GetBeamLength takes Beam b returns real
return SquareRoot( (b.targetpoint.x - b.sourcepoint.x)*(b.targetpoint.x - b.sourcepoint.x) + (b.targetpoint.y - b.sourcepoint.y)*(b.targetpoint.y - b.sourcepoint.y) )
endfunction
function GetBeamAngleOrigin takes Beam b returns real
return Atan2(b.targetpointOrigin.y - b.sourcepointOrigin.y, b.targetpointOrigin.x - b.sourcepointOrigin.x)
endfunction
function GetBeamLengthOrigin takes Beam b returns real
return SquareRoot( (b.targetpointOrigin.x - b.sourcepointOrigin.x)*(b.targetpointOrigin.x - b.sourcepointOrigin.x) + (b.targetpointOrigin.y - b.sourcepointOrigin.y)*(b.targetpointOrigin.y - b.sourcepointOrigin.y) )
endfunction
// Beam setters
function SetBeamTime takes Beam b, real newTime returns nothing
set b.beamTime = newTime
endfunction
function FadeBeam takes Beam b returns nothing
set b.isFading = true
endfunction
module BeamControl
static method interval takes nothing returns nothing
local Beam b = GetTimerData(GetExpiredTimer())
local real x
local real y
local real z
local real a
// runs every TIMEOUT seconds
static if thistype.onPeriod.exists then
call thistype.onPeriod(b)
endif
if b.source != null then
if b.sourceFacing then
set a = b.sourceAngle + GetUnitFacing(b.source) * bj_DEGTORAD
else
set a = b.sourceAngle
endif
set x = GetUnitX(b.source) + Cos(a) * b.sourceDistance
set y = GetUnitY(b.source) + Sin(a) * b.sourceDistance
set z = BlzGetLocalUnitZ(b.source) + b.sourceZOffset
call Point.update(b.sourcepoint, x, y, z)
endif
if b.target != null then
if b.targetFacing then
set a = b.targetAngle + (GetUnitFacing(b.target) + 180.) * bj_DEGTORAD
else
set a = b.targetAngle
endif
set x = GetUnitX(b.target) + Cos(a) * b.targetDistance
set y = GetUnitY(b.target) + Sin(a) * b.targetDistance
set z = BlzGetLocalUnitZ(b.target) + b.targetZOffset
call Point.update(b.targetpoint, x, y, z)
endif
if b.sourceHitBox != 0. then
// calculate hitbox distance
set a = Atan2(b.targetpoint.y - b.sourcepoint.y, b.targetpoint.x - b.sourcepoint.x)
set x = b.sourcepoint.x + Cos(a) * b.sourceHitBox
set y = b.sourcepoint.y + Sin(a) * b.sourceHitBox
call Point.update(b.launch, x, y, b.sourcepoint.z)
else
call Point.update(b.launch, b.sourcepoint.x, b.sourcepoint.y, b.sourcepoint.z)
endif
if b.targetHitBox != 0. then
// calculate hitbox distance
set a = Atan2(b.sourcepoint.y - b.targetpoint.y, b.sourcepoint.x - b.targetpoint.x)
set x = b.targetpoint.x + Cos(a) * b.targetHitBox
set y = b.targetpoint.y + Sin(a) * b.targetHitBox
call Point.update(b.impact, x, y, b.targetpoint.z)
else
call Point.update(b.impact, b.targetpoint.x, b.targetpoint.y, b.targetpoint.z)
endif
// checks if the visual launch and impact points have moved (eg due to rotation) and if so,
// update the beam's coordinates.
if Point.hasMoved(b.launch) or Point.hasMoved(b.impact) then
call MoveLightningEx(b.beam, true, b.launch.x, b.launch.y, b.launch.z, b.impact.x, b.impact.y, b.impact.z)
endif
// if not in the fade phase
if not b.isFading then
if b.beamTime != 0 then // if zero, then beam lasts forever.
set b.beamTime = b.beamTime - TIMEOUT
if b.beamTime <= 0. then
set b.isFading = true
static if thistype.onFade.exists then
call thistype.onFade(b)
endif
endif
endif
// if in the fade phase
else
set b.alpha = b.alpha - b.alphaRate
call SetLightningColor(b.beam, b.red, b.green, b.blue, b.alpha)
if b.alpha <= 0. then
// runs when the beam has completely faded
static if thistype.onEnd.exists then
call thistype.onEnd(b)
endif
call b.destroy()
endif
endif
endmethod
static method cast takes Beam b returns nothing
local real xSource
local real ySource
local real xTarget
local real yTarget
local real x
local real y
local real z
local real a
local real aBeam
if not b.isCast then
set b.isCast = true
if b.source != null then
// if you have a source, calculate the angle and distance between the launch coordinates and the source.
// these values will then recalculate every interval to determine if the Point has moved or not.
set xSource = GetUnitX(b.source)
set ySource = GetUnitY(b.source)
set a = Atan2(b.yLaunch - ySource, b.xLaunch - xSource)
if b.sourceFacing then
set b.sourceAngle = a - GetUnitFacing(b.source) * bj_DEGTORAD
else
set b.sourceAngle = a
endif
set b.sourceDistance = SquareRoot( (b.xLaunch - xSource)*(b.xLaunch - xSource) + (b.yLaunch - ySource)*(b.yLaunch - ySource) )
set b.sourceZOffset = b.zLaunch - BlzGetLocalUnitZ(b.source)
endif
set b.sourcepoint = Point.create(b.xLaunch, b.yLaunch, b.zLaunch)
set b.sourcepointOrigin = Point.create(b.xLaunch, b.yLaunch, b.zLaunch)
if b.target != null then
// same as the above, but for targets
set xTarget = GetUnitX(b.target)
set yTarget = GetUnitY(b.target)
set a = Atan2(b.yImpact - yTarget, b.xImpact - xTarget)
if b.targetFacing then
set b.targetAngle = a - GetUnitFacing(b.target) * bj_DEGTORAD
else
set b.targetAngle = a
endif
set b.targetDistance = SquareRoot( (b.xImpact - xTarget)*(b.xImpact - xTarget) + (b.yImpact - yTarget)*(b.yImpact - yTarget) )
set b.targetZOffset = b.zImpact - BlzGetLocalUnitZ(b.target)
endif
set b.targetpoint = Point.create(b.xImpact, b.yImpact, b.zImpact)
set b.targetpointOrigin = Point.create(b.xImpact, b.yImpact, b.zImpact)
// calculate visual launch from hitbox
set aBeam = Atan2(b.yImpact - b.yLaunch, b.xImpact - b.xLaunch)
set x = b.xLaunch + Cos(aBeam) * b.sourceHitBox
set y = b.yLaunch + Sin(aBeam) * b.sourceHitBox
set b.launch = Point.create(x, y, b.zLaunch)
set aBeam = aBeam + 3.14159
set x = b.xImpact + Cos(aBeam) * b.targetHitBox
set y = b.yImpact + Sin(aBeam) * b.targetHitBox
set b.impact = Point.create(x, y, b.zImpact)
if b.alpha < 0. then // alpha cannot be lower than zero.
set b.alpha = 0.
endif
if b.fadeTime <= 0. then // fade time cannot be zero or lower.
set b.fadeTime = .25
endif
set b.alphaRate = (b.alpha/b.fadeTime) * TIMEOUT
if b.alphaRate == 0. then // alphaRate cannot be zero or lower.
set b.alphaRate = .1
endif
set b.beam = AddLightningEx(b.beamFX, true, b.launch.x, b.launch.y, b.launch.z, b.impact.x, b.impact.y, b.impact.z)
call SetLightningColor(b.beam, b.red, b.green, b.blue, b.alpha)
if b.beamTime < 0. then
set b.isFading = true
else
set b.isFading = false
endif
set b.t = NewTimerEx(b)
call TimerStart(b.t, TIMEOUT, true, function thistype.interval)
endif
endmethod
endmodule
static if not LIBRARY_WorldBounds then
private module InitMapBounds
static rect MapBounds = null
static real MaxX = 0.
static real MinX = 0.
static real MaxY = 0.
static real MinY = 0.
private static method onInit takes nothing returns nothing
set MapBounds = GetWorldBounds()
set MaxX = GetRectMaxX(MapBounds)
set MinX = GetRectMinX(MapBounds)
set MaxY = GetRectMaxY(MapBounds)
set MinY = GetRectMinY(MapBounds)
endmethod
endmodule
endif
struct Point
readonly real x
readonly real y
readonly real z
private real xOld
private real yOld
private real zOld
static method remove takes thistype point returns nothing
set point.x = 0.
set point.y = 0.
set point.z = 0.
set point.xOld = 0.
set point.yOld = 0.
set point.zOld = 0.
call point.deallocate()
endmethod
static method hasMoved takes thistype point returns boolean
if point.x != point.xOld or point.y != point.yOld or point.z != point.zOld then
return true
else
return false
endif
endmethod
static method update takes thistype point, real X, real Y, real Z returns nothing
// to avoid getting out of map bounds. Credits to Retera for pointing it out.
static if LIBRARY_WorldBounds then
if X > WorldBounds.maxX then
set X = WorldBounds.maxX
endif
if X < WorldBounds.minX then
set X = WorldBounds.minX
endif
if Y > WorldBounds.maxY then
set Y = WorldBounds.maxY
endif
if Y < WorldBounds.minY then
set Y = WorldBounds.minY
endif
else
if X > MaxX then
set X = MaxX
endif
if X < MinX then
set X = MinX
endif
if Y > MaxY then
set Y = MaxY
endif
if Y < MinY then
set Y = MinY
endif
endif
set point.xOld = point.x
set point.yOld = point.y
set point.zOld = point.z
set point.x = X
set point.y = Y
set point.z = Z
endmethod
static method create takes real X, real Y, real Z returns thistype
local thistype point = allocate()
set point.x = X
set point.y = Y
set point.z = Z
set point.xOld = X
set point.yOld = Y
set point.zOld = Z
return point
endmethod
static if not LIBRARY_WorldBounds then
implement InitMapBounds
endif
endstruct
struct Beam
timer t
unit source
unit target
// these reals will be used to recalculate the new coordiates of Points should the source or target move
real sourceAngle
real sourceDistance
real sourceZOffset
real targetAngle
real targetDistance
real targetZOffset
// these booleans will move the point around the source/target with relation to their facing angle
boolean sourceFacing
boolean targetFacing
// these values will either be the launch and impact coordinates, or offset the source and
// target location by these values. Refer to the demo for a exmaple of how to use those as offsets.
real xLaunch
real yLaunch
real zLaunch
real xImpact
real yImpact
real zImpact
// the hit box will act like a 'surface' and will offset the beam by the value it's set at.
real sourceHitBox
real targetHitBox
real beamTime
real fadeTime
// these variables store the launch and impact Points of the beam instance.
Point impact
Point launch
Point sourcepoint
Point targetpoint
Point sourcepointOrigin
Point targetpointOrigin
lightning beam
string beamFX
real red
real blue
real green
real alpha
real alphaRate
boolean isFading
boolean isCast
integer data
method destroy takes nothing returns nothing
if this.beam != null then
call DestroyLightning(this.beam)
set this.beam = null
endif
call ReleaseTimer(this.t)
set this.t = null
set this.source = null
set this.target = null
call Point.remove(this.impact)
call Point.remove(this.launch)
call Point.remove(this.sourcepoint)
call Point.remove(this.targetpoint)
call Point.remove(this.sourcepointOrigin)
call Point.remove(this.targetpointOrigin)
set this.isCast = false
call this.deallocate()
endmethod
static method create takes nothing returns thistype
local thistype b = allocate()
set b.t = null
set b.data = 0
set b.source = null
set b.target = null
set b.sourceAngle = 0.
set b.sourceDistance = 0.
set b.sourceZOffset = 0.
set b.targetAngle = 0.
set b.targetDistance = 0.
set b.targetZOffset = 0.
set b.sourceFacing = false
set b.targetFacing = false
set b.xLaunch = 0.
set b.yLaunch = 0.
set b.zLaunch = 0.
set b.xImpact = 0.
set b.yImpact = 0.
set b.zImpact = 0.
set b.sourceHitBox = 0.
set b.targetHitBox = 0.
set b.beamTime = 1.
set b.fadeTime = 0.25
set b.impact = 0
set b.launch = 0
set b.sourcepoint = 0
set b.targetpoint = 0
set b.sourcepointOrigin = 0
set b.targetpointOrigin = 0
set b.beam = null
set b.beamFX = null
set b.red = 1.
set b.blue = 1.
set b.green = 1.
set b.alpha = 1.
set b.alphaRate = 0.
set b.isFading = false
set b.isCast = false
return b
endmethod
endstruct
endlibrary
library EnumGroup
globals
constant real MAX_COLLISION_SIZE = 197.
group ENUM_GROUP = CreateGroup()
endglobals
function EnumGroup takes real x, real y, real radius, boolexpr c returns nothing
call GroupEnumUnitsInRange(ENUM_GROUP, x, y, radius + MAX_COLLISION_SIZE, c)
endfunction
endlibrary
//============================================================================
// SpellEffectEvent
// - Version 1.1.0.0
//
// API
// ---
// RegisterSpellEffectEvent(integer abil, code onCast)
//
// Requires
// --------
// RegisterPlayerUnitEvent: hiveworkshop.com/forums/showthread.php?t=203338
//
// Optional
// --------
// Table: hiveworkshop.com/forums/showthread.php?t=188084
//
library SpellEffectEvent requires RegisterPlayerUnitEvent, optional Table
//============================================================================
private module M
static if LIBRARY_Table then
static Table tb
else
static hashtable ht = InitHashtable()
endif
static method onCast takes nothing returns nothing
static if LIBRARY_Table then
call TriggerEvaluate(.tb.trigger[GetSpellAbilityId()])
else
call TriggerEvaluate(LoadTriggerHandle(.ht, 0, GetSpellAbilityId()))
endif
endmethod
private static method onInit takes nothing returns nothing
static if LIBRARY_Table then
set .tb = Table.create()
endif
call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_SPELL_EFFECT, function thistype.onCast)
endmethod
endmodule
//============================================================================
private struct S extends array
implement M
endstruct
//============================================================================
function RegisterSpellEffectEvent takes integer abil, code onCast returns nothing
static if LIBRARY_Table then
if not S.tb.handle.has(abil) then
set S.tb.trigger[abil] = CreateTrigger()
endif
call TriggerAddCondition(S.tb.trigger[abil], Filter(onCast))
else
if not HaveSavedHandle(S.ht, 0, abil) then
call SaveTriggerHandle(S.ht, 0, abil, CreateTrigger())
endif
call TriggerAddCondition(LoadTriggerHandle(S.ht, 0, abil), Filter(onCast))
endif
endfunction
endlibrary
/*****************************************************************************
*
* RegisterNativeEvent v1.1.1.2
* by Bannar
*
* Storage of trigger handles for native events.
*
******************************************************************************
*
* Optional requirements:
*
* Table by Bribe
* hiveworkshop.com/forums/jass-resources-412/snippet-new-table-188084/
*
******************************************************************************
*
* Important:
*
* Avoid using TriggerSleepAction within functions registered.
* Destroy native event trigger on your own responsibility.
*
******************************************************************************
*
* Core:
*
* function IsNativeEventRegistered takes integer whichIndex, integer whichEvent returns boolean
* Whether index whichIndex has already been attached to event whichEvent.
*
* function RegisterNativeEventTrigger takes integer whichIndex, integer eventId returns boolean
* Registers whichIndex within whichEvent scope and assigns new trigger handle for it.
*
* function GetIndexNativeEventTrigger takes integer whichIndex, integer whichEvent returns trigger
* Retrieves trigger handle for event whichEvent specific to provided index whichIndex.
*
* function GetNativeEventTrigger takes integer whichEvent returns trigger
* Retrieves trigger handle for event whichEvent.
*
*
* Custom events:
*
* function CreateNativeEvent takes nothing returns integer
* Returns unique id for new event and registers it with RegisterNativeEvent.
*
* function RegisterIndexNativeEvent takes integer whichIndex, integer whichEvent, code func returns nothing
* Registers new event handler func for event whichEvent specific to index whichIndex.
*
* function RegisterNativeEvent takes integer whichEvent, code func returns nothing
* Registers new event handler func for specified event whichEvent.
*
*****************************************************************************/
library RegisterNativeEvent uses optional Table
globals
private integer eventIndex = 500 // 0-499 reserved for Blizzard native events
endglobals
private module NativeEventInit
private static method onInit takes nothing returns nothing
static if LIBRARY_Table then
set table = TableArray[0x2000]
endif
endmethod
endmodule
private struct NativeEvent extends array
static if LIBRARY_Table then
static TableArray table
else
static hashtable table = InitHashtable()
endif
implement NativeEventInit
endstruct
function IsNativeEventRegistered takes integer whichIndex, integer whichEvent returns boolean
static if LIBRARY_Table then
return NativeEvent.table[whichEvent].trigger.has(whichIndex)
else
return HaveSavedHandle(NativeEvent.table, whichEvent, whichIndex)
endif
endfunction
function RegisterNativeEventTrigger takes integer whichIndex, integer whichEvent returns boolean
if not IsNativeEventRegistered(whichIndex, whichEvent) then
static if LIBRARY_Table then
set NativeEvent.table[whichEvent].trigger[whichIndex] = CreateTrigger()
else
call SaveTriggerHandle(NativeEvent.table, whichEvent, whichIndex, CreateTrigger())
endif
return true
endif
return false
endfunction
function GetIndexNativeEventTrigger takes integer whichIndex, integer whichEvent returns trigger
static if LIBRARY_Table then
return NativeEvent.table[whichEvent].trigger[whichIndex]
else
return LoadTriggerHandle(NativeEvent.table, whichEvent, whichIndex)
endif
endfunction
function GetNativeEventTrigger takes integer whichEvent returns trigger
return GetIndexNativeEventTrigger(bj_MAX_PLAYER_SLOTS, whichEvent)
endfunction
function CreateNativeEvent takes nothing returns integer
local integer eventId = eventIndex
call RegisterNativeEventTrigger(bj_MAX_PLAYER_SLOTS, eventId)
set eventIndex = eventIndex + 1
return eventId
endfunction
function RegisterIndexNativeEvent takes integer whichIndex, integer whichEvent, code func returns nothing
call RegisterNativeEventTrigger(whichIndex, whichEvent)
call TriggerAddCondition(GetIndexNativeEventTrigger(whichIndex, whichEvent), Condition(func))
endfunction
function RegisterNativeEvent takes integer whichEvent, code func returns nothing
call RegisterIndexNativeEvent(bj_MAX_PLAYER_SLOTS, whichEvent, func)
endfunction
endlibrary
/*****************************************************************************
*
* RegisterPlayerUnitEvent v1.0.3.2
* by Bannar
*
* Register version of TriggerRegisterPlayerUnitEvent.
*
* Special thanks to Magtheridon96, Bribe, azlier and BBQ for the original library version.
*
******************************************************************************
*
* Requirements:
*
* RegisterNativeEvent by Bannar
* hiveworkshop.com/threads/snippet-registerevent-pack.250266/
*
******************************************************************************
*
* Functions:
*
* function GetAnyPlayerUnitEventTrigger takes playerunitevent whichEvent returns trigger
* Retrieves trigger handle for playerunitevent whichEvent.
*
* function GetPlayerUnitEventTrigger takes player whichPlayer, playerunitevent whichEvent returns trigger
* Retrieves trigger handle for playerunitevent whichEvent specific to player whichPlayer.
*
* function RegisterAnyPlayerUnitEvent takes playerunitevent whichEvent, code func returns nothing
* Registers generic playerunitevent whichEvent adding code func as callback.
*
* function RegisterPlayerUnitEvent takes player whichPlayer, playerunitevent whichEvent, code func returns nothing
* Registers playerunitevent whichEvent for player whichPlayer adding code func as callback.
*
*****************************************************************************/
library RegisterPlayerUnitEvent requires RegisterNativeEvent
function GetAnyPlayerUnitEventTrigger takes playerunitevent whichEvent returns trigger
return GetNativeEventTrigger(GetHandleId(whichEvent))
endfunction
function GetPlayerUnitEventTrigger takes player whichPlayer, playerunitevent whichEvent returns trigger
return GetIndexNativeEventTrigger(GetPlayerId(whichPlayer), GetHandleId(whichEvent))
endfunction
function RegisterAnyPlayerUnitEvent takes playerunitevent whichEvent, code func returns nothing
local integer eventId = GetHandleId(whichEvent)
local integer index = 0
local trigger t = null
if RegisterNativeEventTrigger(bj_MAX_PLAYER_SLOTS, eventId) then
set t = GetNativeEventTrigger(eventId)
loop
call TriggerRegisterPlayerUnitEvent(t, Player(index), whichEvent, null)
set index = index + 1
exitwhen index == bj_MAX_PLAYER_SLOTS
endloop
set t = null
endif
call RegisterNativeEvent(eventId, func)
endfunction
function RegisterPlayerUnitEvent takes player whichPlayer, playerunitevent whichEvent, code func returns nothing
local integer playerId = GetPlayerId(whichPlayer)
local integer eventId = GetHandleId(whichEvent)
if RegisterNativeEventTrigger(playerId, eventId) then
call TriggerRegisterPlayerUnitEvent(GetIndexNativeEventTrigger(playerId, eventId), whichPlayer, whichEvent, null)
endif
call RegisterIndexNativeEvent(playerId, eventId, func)
endfunction
endlibrary
library IsUnitInvulnerable uses optional UnitIndexer /* v1.0
*************************************************************************************
*
* Detects whether a unit is invulnerable or not.
*
*************************************************************************************
*
* Installation
*
* 1. Copy the DetectInvulnerable spell from this map to your in the Object Editor
* 2. Create an empty trigger, call it IsUnitInvulnerable and convert it to custom text.
* 3. Copy/paste the contents of this trigger in the IsUnitIvulnrable trigger.
* 4. Change the ability code for the DUMMY_ABILITY variable in the globals block to 'ADIN'
*
*************************************************************************************
*
* Credits
*
* To PitzerMike
* -----------------------
*
* for IsDestructableTree, on which this small library is based
*
* and Retera
* -----------------------
*
* who gave me the idea and explanation of how to do it
*
*************************************************************************************
*
* Functions
*
* function IsUnitInvulnerable takes unit u returns boolean
*
*/
globals
private constant integer DUMMY_UNIT_ID = 'dumi'//* human peasant
private constant integer DUMMY_ABILITY = 'ADIN'//* detect invulnerable
private constant integer DUMMY_ORDER_ID = 852600//* channel order id
private constant player NEUTRAL_PLAYER = Player(PLAYER_NEUTRAL_PASSIVE)
private unit dummy = null
endglobals
function IsUnitInvulnerable takes unit u returns boolean
//* 851973 is the order id for stunned, it will interrupt the preceding harvest order.
return (IssueTargetOrderById(dummy, DUMMY_ORDER_ID, u)) and (IssueImmediateOrderById(dummy, 851973))
endfunction
private function Init takes nothing returns nothing
static if LIBRARY_UnitIndexer then//* You may adapt this to your own indexer.
set UnitIndexer.enabled = false
endif
set dummy = CreateUnit(NEUTRAL_PLAYER, DUMMY_UNIT_ID, 0, 0, 0)
static if LIBRARY_UnitIndexer then
set UnitIndexer.enabled = true
endif
call UnitAddAbility(dummy, DUMMY_ABILITY)
call UnitAddAbility(dummy, 'Aloc')
call ShowUnit(dummy, false)
endfunction
private module Inits
private static method onInit takes nothing returns nothing
call Init()
endmethod
endmodule
private struct IsUnitInv extends array
implement Inits
endstruct
endlibrary
library GetLocZ
globals
private location loc = Location(0., 0.)
endglobals
function GetLocZ takes real x, real y returns real
call MoveLocation(loc, x, y)
return GetLocationZ(loc)
endfunction
function GetUnitZ takes unit u returns real
return GetUnitFlyHeight(u) + GetLocZ(GetUnitX(u), GetUnitY(u))
endfunction
endlibrary
scope onDamage initializer Init
globals
// Mana shield fix
private constant real DMG_REDUC_PER_LEVEL = 0.
private constant real BASE_DMG_REDUC = 1.
private constant real MANA_MULT_PER_LEVEL = .5
private constant real BASE_MANA_MULT = .5
endglobals
private function OnDamage takes nothing returns nothing
local unit source = Damage.source
local unit target = Damage.target
local real xSource = GetUnitX(source)
local real ySource = GetUnitY(source)
local real zSource = BlzGetLocalUnitZ(source)
local real xTarget = GetUnitX(target)
local real yTarget = GetUnitY(target)
local real zTarget = BlzGetLocalUnitZ(target)
local player ownerSource = GetOwningPlayer(source)
local player ownerTarget = GetOwningPlayer(target)
local integer lvl
local integer dmgType = Damage.type
// Spectral Bleed
local real value
// Life Drain
local real healAmount
local integer lifeDrainHero
// Mana shield
local real mana
local real effectiveMana
local real manaMult
local real dmgReduction
local real dmgToMana
local real dmgToHP
local real dmgResult
local integer manaShieldHero
/*
DUMMY DAMAGE
This type of damage is meant only as a trigger and never designed to hurt the target.
Damage that is meant to trigger an effect regardless of whether it's blocked or not
also comes here.
*/
if dmgType == DAMAGE_TYPE_MAGICAL or DAMAGE_TYPE == DAMAGE_TYPE_SPELL then
/*
// Psychic Noise
if UnitReflectMana(source, target, Damage.amount) then
set Damage.amount = 0.
endif*/
elseif dmgType == DAMAGE_TYPE_PHYSICAL then
/*
// Chimeric Spawns
if HasChimericSpawns(source) then
set Damage.amount = 0.
call SpawnChimeras(source, target, GetUnitFacing(source) * bj_DEGTORAD)
endif
// Phantasmagoria
if GetApparitionChance(source) then
call SpawnApparition(target, ownerSource)
endif
// Confusion
if GetUnitAbilityLevel(source, 'B00G') > 0 then
if GetRandomInt(0, 100) <= 33 then
call UnitDamageTarget(source, source, Damage.amount * .8, true, false, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_UNIVERSAL, null)
call DestroyEffect(AddSpecialEffectTarget("Abilities\\Weapons\\SorceressMissile\\SorceressMissile.mdl", source, "head"))
endif
endif
// Limited Attacks
set lvl = GetUnitAbilityLevel(source, ATTACK_LIMITER)
if lvl > 0 then
set lvl = lvl - 1
call SetUnitAbilityLevel(source, ATTACK_LIMITER, lvl)
if lvl <= 0 then
call UnitApplyTimedLife(source, 'BTLF', .5)
call DestroyEffect(AddSpecialEffect(APPARITION_FX, xSource, ySource))
endif
endif
*/
endif
/*
PRE-DAMAGE REDUCTION
All effects that are meant to mitigate damage in any way (Mana Shield, Evasion, etc)
are handled here. They only occur if the Damage.amount is greater than 0., which means
it was not a dummy damage.
*/
if Damage.amount != 0. then
/*
// Spectral Bleed
set value = GetSpectralBleedValue(target, Damage.amount)
if value > 0. then
call SpectralBleedHeal(target, SPECTRAL_BLEED_RANGE, value)
endif
// Spirit Flare
set value = GetSpiritFlareValue(target, Damage.amount)
if value > 0. then
call SpiritFlareMana(target, SPRIIT_FLARE_RANGE, value)
endif
// Invocation
if HasInvocation(source) then
// Spectral Bleed
set value = GetSpectralBleedEvocationValue(source, Damage.amount)
if value > 0. then
call SpectralBleedHeal(source, SPECTRAL_BLEED_RANGE, value)
endif
// Spirit Flare
set value = GetSpiritFlareEvocationValue(source, Damage.amount)
if value > 0. then
call SpiritFlareMana(source, SPRIIT_FLARE_RANGE, value)
endif
endif
*/
if dmgType == DAMAGE_TYPE_MAGICAL or DAMAGE_TYPE == DAMAGE_TYPE_SPELL then
elseif dmgType == DAMAGE_TYPE_PHYSICAL then
// Blur
/*if GetUnitAbilityLevel(target, 'B002') > 0 then
set Damage.amount = Blur(target, Damage.amount)
endif*/
endif
// Mana Shield fix
if GetUnitAbilityLevel(target, 'BNms') > 0 then
set mana = GetUnitState(target, UNIT_STATE_MANA)
set manaShieldHero = GetUnitAbilityLevel(target, 'ANms')
if manaShieldHero == 0 then
set manaMult = 2.
set dmgReduction = 1.
else
// find the mana multiplier. If manaMult == 4. then it reduces 1 hp for every 4 points of mana.
set manaMult = BASE_MANA_MULT + MANA_MULT_PER_LEVEL * manaShieldHero
// This will mimic the Damage Absorbed field.
set dmgReduction = BASE_DMG_REDUC// - DMG_REDUC_PER_LEVEL * manaShieldHero
endif
set dmgToMana = Damage.amount * dmgReduction
set dmgToHP = Damage.amount - dmgToMana
set Damage.amount = dmgToHP
set effectiveMana = mana * manaMult
if dmgToMana <= effectiveMana then
set effectiveMana = effectiveMana - dmgToMana
set dmgResult = 0.
set mana = effectiveMana / manaMult
else
set dmgResult = dmgToMana - effectiveMana
call IssueImmediateOrderById(target, 852590) // order manashieldoff
set mana = 0.
endif
call SetUnitState(target, UNIT_STATE_MANA, mana)
set Damage.amount = Damage.amount + dmgResult
endif
endif
/*
POST-DAMAGE REDUCTION
If damage is still greater than zero at this point then move on to post-reduction
conditions like Life Drains and other stuff like that.
*/
if Damage.amount != 0. then
if IsUnitBonded(target) and not IsBondedDamage then
call MalignantChain.applyBondedDamage(source, target, Damage.amount)
endif
if dmgType == DAMAGE_TYPE_MAGICAL or DAMAGE_TYPE == DAMAGE_TYPE_SPELL then
//fix Runed Bracers here
// Life Drain fix
if GetUnitAbilityLevel(source, 'Bdcl') > 0 and GetUnitAbilityLevel(target, 'Bdtl') > 0 then
set lifeDrainHero = GetUnitAbilityLevel(source, 'ANdr')
if lifeDrainHero > 0 then
set healAmount = 10. + 15. * GetUnitAbilityLevel(source, 'ANdr')
else
set healAmount = 55.
endif
call SetUnitState(source, UNIT_STATE_LIFE, GetUnitState(source, UNIT_STATE_LIFE) + healAmount)
endif
elseif dmgType == DAMAGE_TYPE_PHYSICAL then
// Keeper of Nightmares attack
// Figment Barrage
endif
endif
set source = null
set target = null
endfunction
private function Init takes nothing returns nothing
call Damage.registerModifier(function OnDamage)
endfunction
endscope
//! novjass
/*
DamagePackage v1.44
Documentation
by Flux
Contains libraries for your damage detection, distinction and manipulation needs.
-----------
DamageEvent
-----------
A lightweight damage detection system that
detects when a unit takes damage.
Can distinguish physical and magical damage.
------------
DamageModify
------------
An add-on to DamageEvent that allows modification
of damage taken before it is applied.
-------------
DamageObjects
-------------
Automatically generates required Objects by DamageEvent and
DamageModify.
CONTENTS:
- API
- How to use DamagePackage
- Important Notes
- Credits
- Changelog
//==================================================================//
API:
//==================================================================//
*/
DamageEvent:
-----------------------------
DAMAGE PROPERTIES
-----------------------------
Damage.source
// Unit that dealt the damage.
Damage.target
// Unit that took the damage.
Damage.amount
// Amount of damage taken.
Damage.type
// The type of damage taken.
// Values can be DAMAGE_TYPE_PHYSICAL or DAMAGE_TYPE_MAGICAL.
-----------------------------
DAMAGE CALLBACKS
-----------------------------
Damage.register(code)
// Registers a code that will permanently run when a registered
// unit takes damage.
Damage.registerTrigger(trigger)
// Registers a trigger that will run when a registered unit takes
// damage. The trigger can be disabled to avoid an infinite loop.
// Triggers will execute depending on the order they are registered.
Damage.unregisterTrigger(trigger)
// Removes the event in the trigger, causing the trigger to no longer
// run when a registered unit takes damage.
Damage.add(unit)
// For manual registration of units to DamageEvent. You
// won't use this if AUTO_REGISTER is set to true.
-----------------------------
MISCELLANEOUS
-----------------------------
Damage.enabled
//Turns on/off the entire DamageEvent.
Damage.lockAmount()
//Prevents further modification of damage on this damage instance.
DamageModify
set Damage.amount = <new amount>
// Modify the damage taken before it is applied.
Damage.registerModifier(code)
// Registers a code that will permanently run when a
// registered unit takes damage executing before any callbacks
// registered via Damage.register(code)/Damage.registerTrigger(trigger).
Damage.registerModifierTrigger(trigger)
// Registers a trigger that will run when a registered unit
// takes damage executing before any callbacks registered
// via Damage.register(code)/Damage.registerTrigger(trigger).
// The trigger can be disabled to avoid an infinite loop.
Damage.unregisterModifierTrigger(trigger)
// Removes the event in the trigger, it will no longer evaluate
// and execute when a registered unit takes damage.
/*
//==================================================================//
HOW TO USE DAMAGE PACKAGE:
//==================================================================//
1. Decide whether you need to use DamageEvent or DamageEvent with DamageModify.
If you only want to detect the damage and the type of damage, DamageEvent
will suffice, but if you want to modify the Damage taken, then you need
DamageModify. Note that DamageEvent without DamageModify is designed to be
lightweight, therefore it is better not to have DamageModify if you do not
need it. Using DamageModify is 90 to 100 microseconds slower.
2. Define Basic configuration of DamageEvent
*/
private constant integer DAMAGE_TYPE_DETECTOR
//An ability based on Runed Bracer that is utilized by DamageEvent to distinguish
//PHYSICAL and MAGICAL damage.
private constant real ETHEREAL_FACTOR
//Using DamageEvent disables the ethereal factor configured in Gameplay Constants as
//a side effect of Runed Bracer. However, the system simulates ethereal amplification
//and this is the new ethereal factor for magic damage. The configured ethereal factor
//in Gameplay Constants will be completely ignored.
.
private constant boolean AUTO_REGISTER
//Determines whether units entering the map are automatically registered
//to the DamageEvent system
private constant boolean PREPLACE_INIT
//Auto registers units initially placed in World Editor.
private constant integer COUNT_LIMIT
//When the number of registered individual unit in the current DamageBucket
//reaches COUNT_LIMIT, the system will find a new DamageBucket with units less
//than COUNT_LIMIT and use it as the new current DamageBucket. If none is found,
//the system will create a new DamageBucket.
private constant real REFRESH_TIMEOUT
//Periodic Timeout of Trigger Refresh.
//Every REFRESH_TIMEOUT, the system will refresh a signle DamageBucket.
private constant integer SET_MAX_LIFE
//An ability based on Item Life Bonus that is utilized by DamageModify to
//manipulate damage taken.
/*
3. Register a code or a trigger that will run when a registered unit takes damage. Example:
*/
//USING CODE PARAMETER
library L initializer Init
private function OnDamage takes nothing returns nothing
//This will run whenever a unit registered takes damage.
//Do you thing here
endfunction
private function Init takes nothing returns nothing
call Damage.register(function OnDamage)
endfunction
endlibrary
//USING TRIGGER PARAMETER
library L initializer Init
globals
private trigger trg = CreateTrigger()
endglobals
private function OnDamage takes nothing returns boolean
//This will run whenever a unit registered takes damage.
return false
endfunction
private function Init takes nothing returns nothing
call Damage.registerTrigger(trg)
call TriggerAddCondition(trg, Condition(function OnDamage))
endfunction
endlibrary
// You would want to use Damage.registerTrigger when avoiding recursion loop
// because the trigger can be disabled unlike code.
/*
4. If you want to modify the damage taken, you need the DamageModify library.
Simply change Damage.amount to whatever you want the new damage value to be.
Example:
*/
library L initializer Init
private function OnDamage takes nothing returns nothing
//All damage taken will be amplied by two
set Damage.amount = 2*Damage.amount
endfunction
private function Init takes nothing returns nothing
call Damage.registerModifier(function OnDamage)
endfunction
endlibrary
/*
DamageModify callbacks and triggers runs first before DamageEvent callbacks
and triggers. Example:
*/
library L initializer Init
private function OnDamageModifier takes nothing returns nothing
set Damage.amount = 0 //This will cause all damage taken to be zero
endfunction
private function OnDamage takes nothing returns nothing
call BJDebugMsg(GetUnitName(Damage.target) " takes " + R2S(Damage.amount) + " damage")
//Will print:
//"<Target Name> takes 0 damage"
endfunction
private function Init takes nothing returns nothing
call Damage.registerModifier(function OnDamageModifier)
call Damage.register(function OnDamageModifier)
endfunction
endlibrary
/*
5. If you want to deal damage inside onDamage callback without causing infinite loops,
you can do so using Damage.registerTrigger(trigger) and disabling the trigger before
the new damage is applied then enabling it again after damage is applied. Example:
*/
library L initializer Init
globals
private trigger trg = CreateTrigger()
endglobals
private function OnDamage takes nothing returns boolean
call BJDebugMsg(GetUnitName(Damage.target) " takes " + R2S(Damage.amount) + " damage")
call DisableTrigger(thistype.trg)
call UnitDamageTarget(Damage.source, Damage.target, 42.0, false, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_MAGIC, null)
call EnableTrigger(thistype.trg)
call BJDebugMsg(GetUnitName(Damage.target) " takes an extra 42 damage.")
return false
endfunction
private function Init takes nothing returns nothing
call Damage.registerTrigger(trg)
call TriggerAddCondition(trg, Condition(function OnDamage))
endfunction
endlibrary
/*
//==================================================================//
IMPORTANT NOTES:
//==================================================================//
- Life Drain will not work with this system.
- Locust Swarm abilities will still work, but the "Data - Damage Return Factor"
defined in Object Editor must be multiplied to -1. Example, to fix the
default Locust Swarm ability, change the value from 0.75 to -0.75
- Runed Bracer items and abilities will not work with this system. But one
can easily make a trigger for that.
- Mana Shield works normally.
- Artillery attacks that causes unit to explode on death works normally.
- Finger of Death works normally.
- Spirit Link will not work with this system. However, it is possible to
recreate a triggered version of Spirit Link using this system.
- Magic Attacks are detected as DAMAGE_TYPE_PHYSICAL while Spells Attack
are detected as DAMAGE_TYPE_MAGICAL.
//==================================================================//
CREDITS:
//==================================================================//
looking_for_help
- for the Runed Bracer trick allowing this system to distinguish PHYSICAL
and MAGICAL damage.
- for Physical Damage Detection System which was used as a reference for
creating this system.
Bribe
- for the optional Table
Cokemonkey11 and PurplePoot
- for the bucket-based damage detection systems for less processes per refresh.
Aniki, Quilnez and Wietlol
- for finding bugs, giving feedbacks and suggestions.
//==================================================================//
CHANGELOG:
//==================================================================//
v1.00 - [3 Aug 2016]
- Initial Release
v1.10 - [7 Aug 2016]
- Fixed unremoved and unintentional BJDebug Messages.
- Fixed unremoved group in preplace.
- Fixed uncleaned Table/hashtable timer handle id.
- Fixed "Nonrecursive Damage bug".
- Fixed HP Bar flickering bug.
- Fixed a bug where revived units are not registered.
- Optimized the script, now only uses 1 static timer.
- Replaced UnitAlive by GetUnitTypeId as the condition for removal.
- Removed optional requirements TimerUtils and TimerUtilsEx.
- Implemented a periodic refresh mechanism.
- Implemented the bucket technique to limit the number of units per refresh.
v1.11 - [7 Aug 2016]
- Fixed a bug where it does not auto-register preplaced units when using Table.
- Fixed some functions compiling to trigger evaluation due to the order.
- Added a filter when using AUTO_REGISTER.
v1.12 - [8 August 2016]
- Fixed a bug when all DamageBuckets are removed.
- Fixed a bug where currentBucket points to a destroyed DamageBucket
when it is destroyed.
- Fixed unremoved saved boolean in Table/hashtable.
- AutoRegisterFilter is now also applied to preplaced units.
v1.20 - [17 August 2016]
- Fixed recursion bug.
- Added Damage.enabled to control whether DamageEvent callbacks are ON/OFF.
- Added Damage.registerPermanent(code).
- Renamed Damage.registerFirst(code) to Damage.registerModifier(code).
- Damage.registerModifier(code) only comes within DamageModify.
- SET_MAX_LIFE of DamageModify is now preloaded.
v1.30 - [15 October 2016]
- Added more detailed documentation with examples.
- Now uses life change event instead of a timer avoiding several bugs.
- Fixed recursion damage workaround.
- Optimized and shortened the code.
v1.40 - [29 March 2017]
- Implemented a stack to make Damage.source, Damage.target, Damage.amount and Damage.type behave like local variables in the callback.
- Fixed Damage.source and Damage.target changing unit value withing callback due to recursion.
- Fixed Damage.amount and Damage.type changing value within callback due to recursion.
- Improved documentation on how it affects default Warcraft 3 abilities.
v1.41 - [24 May 2017]
- Added Damage.lockAmount() feature.
- Fixed bug occuring when units with very high hp takes damage.
- Changing Damage.amount will no longer work on non-modifier codes/triggers.
v1.42 - [25 May 2017]
- Fixed bug occuring when units with very high hp takes very small damage.
v1.43 - [29 May 2017]
- Fixed bug when magic damage amount is between 0.125 to 0.2.
v1.44 - [3 June 2017]
- Fixed bug when magic damage exceeds target's max health.
*/
//! endnovjass
library DamageEvent /*
----------------------------------
DamageEvent v1.44
by Flux
----------------------------------
A lightweight damage detection system that
detects when a unit takes damage.
Can distinguish physical and magical damage.
*/ requires /*
(nothing)
*/ optional Table /*
If not found, DamageEvent will create 2 hashtables. Hashtables are limited to 255 per map.
*/
//Basic Configuration
//See documentation for details
globals
private constant integer DAMAGE_TYPE_DETECTOR = 'ADMG'
private constant real ETHEREAL_FACTOR = 1.6666
endglobals
//Advanced Configuration
//Default values are recommended, edit only if you understand how the system works (See documentation).
globals
private constant boolean AUTO_REGISTER = true
private constant boolean PREPLACE_INIT = true
private constant integer COUNT_LIMIT = 50
private constant real REFRESH_TIMEOUT = 30.0
endglobals
static if not AUTO_REGISTER and not PREPLACE_INIT then
//Equivalent to AUTO_REGISTER or PREPLACE_INIT
else
//Autoregister Filter
//If it returns true, it will be registered automatically
private function AutoRegisterFilter takes unit u returns boolean
local integer id = GetUnitTypeId(u)
return id != 'dumi'
endfunction
endif
//Globals not meant to be edited.
globals
constant integer DAMAGE_TYPE_PHYSICAL = 1
constant integer DAMAGE_TYPE_MAGICAL = 2
private constant real MIN_LIFE = 0.406
private DamageBucket pickedBucket = 0
private DamageBucket currentBucket = 0
endglobals
struct DamageBucket
readonly integer count
readonly trigger trg
readonly group grp
readonly thistype next
readonly thistype prev
private static timer t = CreateTimer()
method destroy takes nothing returns nothing
set this.next.prev = this.prev
set this.prev.next = this.next
if thistype(0).next == 0 then
call PauseTimer(thistype.t)
endif
if this == currentBucket then
set currentBucket = thistype(0).next
endif
call DestroyTrigger(this.trg)
call DestroyGroup(this.grp)
set this.trg = null
set this.grp = null
call this.deallocate()
endmethod
//Returns the DamageBucket where unit u belongs.
static method get takes unit u returns thistype
static if LIBRARY_Table then
return Damage.tb[GetHandleId(u)]
else
return LoadInteger(Damage.hash, GetHandleId(u), 0)
endif
endmethod
method remove takes unit u returns nothing
call GroupRemoveUnit(this.grp, u)
static if LIBRARY_Table then
call Damage.tb.remove(GetHandleId(u))
else
call RemoveSavedInteger(Damage.hash, GetHandleId(u), 0)
endif
endmethod
//Add unit u to this DamageBucket.
method add takes unit u returns nothing
call TriggerRegisterUnitEvent(this.trg, u, EVENT_UNIT_DAMAGED)
call GroupAddUnit(this.grp, u)
set this.count = this.count + 1
static if LIBRARY_Table then
set Damage.tb[GetHandleId(u)] = this
else
call SaveInteger(Damage.hash, GetHandleId(u), 0, this)
endif
endmethod
private static thistype temp
//Enumerate DamageBucket units, removing it if it is removed from the game
private static method cleanGroup takes nothing returns nothing
local unit u = GetEnumUnit()
local thistype this = temp
if GetUnitTypeId(u) != 0 then
call TriggerRegisterUnitEvent(this.trg, u, EVENT_UNIT_DAMAGED)
set this.count = this.count + 1
else
call GroupRemoveUnit(this.grp, u)
static if LIBRARY_Table then
call Damage.tb.remove(GetHandleId(u))
else
call RemoveSavedInteger(Damage.hash, GetHandleId(u), 0)
endif
endif
set u = null
endmethod
//Refreshes this DamageBucket
method refresh takes nothing returns nothing
local unit u
call DestroyTrigger(this.trg)
set this.trg = CreateTrigger()
call TriggerAddCondition(this.trg, Filter(function Damage.core))
set this.count = 0
set thistype.temp = this
call ForGroup(this.grp, function thistype.cleanGroup)
if this.count == 0 then
call this.destroy()
endif
endmethod
static method create takes nothing returns thistype
local thistype this = thistype.allocate()
set this.count = 0
set this.trg = CreateTrigger()
set this.grp = CreateGroup()
call TriggerAddCondition(this.trg, Filter(function Damage.core))
set this.next = thistype(0)
set this.prev = thistype(0).prev
set this.next.prev = this
set this.prev.next = this
if this.prev == 0 then
call TimerStart(thistype.t, REFRESH_TIMEOUT, true, function Damage.refresh)
endif
return this
endmethod
endstruct
struct DamageTrigger
private trigger trg
private thistype next
private thistype prev
method destroy takes nothing returns nothing
set this.next.prev = this.prev
set this.prev.next = this.next
static if LIBRARY_Table then
call Damage.tb.remove(GetHandleId(this.trg))
else
call RemoveSavedInteger(Damage.hash, GetHandleId(this.trg), 0)
endif
set this.trg = null
call this.deallocate()
endmethod
static method unregister takes trigger t returns nothing
local integer id = GetHandleId(t)
static if LIBRARY_Table then
if Damage.tb.has(id) then
call thistype(Damage.tb[id]).destroy()
endif
else
if HaveSavedInteger(Damage.hash, id, 0) then
call thistype(LoadInteger(Damage.hash, id, 0)).destroy()
endif
endif
endmethod
static method register takes trigger t returns nothing
local thistype this = thistype.allocate()
set this.trg = t
set this.next = thistype(0)
set this.prev = thistype(0).prev
set this.next.prev = this
set this.prev.next = this
static if LIBRARY_Table then
set Damage.tb[GetHandleId(t)] = this
else
call SaveInteger(Damage.hash, GetHandleId(t), 0, this)
endif
endmethod
static method executeAll takes nothing returns nothing
local thistype this = thistype(0).next
loop
exitwhen this == 0
if IsTriggerEnabled(this.trg) then
if TriggerEvaluate(this.trg) then
call TriggerExecute(this.trg)
endif
endif
set this = this.next
endloop
endmethod
endstruct
struct DamageTrigger2
implement optional DamageTrigger2Module
endstruct
struct Damage extends array
private static thistype stackTop = 0
private static thistype global
private static integer array allocator
private unit stackSource
private unit stackTarget
private real stackAmount
private integer stackType
private thistype stackNext
private static real hp
static if LIBRARY_Table then
readonly static Table tb
else
readonly static hashtable hash = InitHashtable()
endif
//Allows the DamageModify module to access the configuration
static if LIBRARY_DamageModify then
private static constant real S_ETHEREAL_FACTOR = ETHEREAL_FACTOR
private static constant real S_MIN_LIFE = MIN_LIFE
endif
static method remove takes unit u returns nothing
call DamageBucket.get(u).remove(u)
endmethod
//Add unit u to the current DamageBucket
static method add takes unit u returns nothing
local DamageBucket temp
local DamageBucket b
//If unit does not belong to any DamageBucket yet
if DamageBucket.get(u) == 0 then
call UnitAddAbility(u, DAMAGE_TYPE_DETECTOR)
call UnitMakeAbilityPermanent(u, true, DAMAGE_TYPE_DETECTOR)
if currentBucket != 0 then
//When the current DamageBucket exceeds the limit
if currentBucket.count >= COUNT_LIMIT then
set temp = DamageBucket(0).next
loop
exitwhen temp == 0
//Find a DamageBucket with few units
if temp.count < COUNT_LIMIT then
exitwhen true
endif
set temp = temp.next
endloop
if temp == 0 then //If none is found
set currentBucket = DamageBucket.create()
else //If a DamageBucket is found, use it
set currentBucket = temp
endif
endif
else
set currentBucket = DamageBucket.create()
set pickedBucket = currentBucket
endif
call currentBucket.add(u)
endif
endmethod
//Periodic Refresh only refreshing one DamageBucket per REFRESH_TIMEOUT
//to avoid lag spike.
static method refresh takes nothing returns nothing
call pickedBucket.refresh()
loop
set pickedBucket = pickedBucket.next
exitwhen pickedBucket != 0
endloop
endmethod
static method operator amount takes nothing returns real
return thistype.stackTop.stackAmount
endmethod
static method operator type takes nothing returns integer
return thistype.stackTop.stackType
endmethod
static method operator target takes nothing returns unit
return thistype.stackTop.stackTarget
endmethod
static method operator source takes nothing returns unit
return thistype.stackTop.stackSource
endmethod
private static boolean prevEnable = true
static method operator enabled takes nothing returns boolean
return thistype.prevEnable
endmethod
static method operator enabled= takes boolean b returns nothing
local DamageBucket bucket = DamageBucket(0).next
if b != thistype.prevEnable then
loop
exitwhen bucket == 0
if b then
call EnableTrigger(bucket.trg)
else
call DisableTrigger(bucket.trg)
endif
set bucket = bucket.next
endloop
endif
set thistype.prevEnable = b
endmethod
//All registered codes will go to this trigger
private static trigger registered
static method register takes code c returns boolean
call TriggerAddCondition(thistype.registered, Condition(c))
return false //Prevents inlining
endmethod
static method registerTrigger takes trigger trig returns nothing
call DamageTrigger.register(trig)
endmethod
static method unregisterTrigger takes trigger trig returns nothing
call DamageTrigger.unregister(trig)
endmethod
implement optional DamageModify
static if not LIBRARY_DamageModify then
private static method afterDamage takes nothing returns boolean
call SetWidgetLife(thistype.stackTop.stackTarget, thistype.hp - thistype.stackTop.stackAmount)
call DestroyTrigger(GetTriggeringTrigger())
if thistype.global > 0 then
set thistype.allocator[thistype.global] = thistype.allocator[0]
set thistype.allocator[0] = thistype.global
set thistype.stackTop = thistype.stackTop.stackNext
endif
return false
endmethod
static method core takes nothing returns boolean
local real amount = GetEventDamage()
local thistype this
local real newHp
local trigger trg
if amount == 0.0 then
return false
endif
set this = thistype.allocator[0]
if (thistype.allocator[this] == 0) then
set thistype.allocator[0] = this + 1
else
set thistype.allocator[0] = thistype.allocator[this]
endif
set this.stackSource = GetEventDamageSource()
set this.stackTarget = GetTriggerUnit()
set this.stackNext = thistype.stackTop
set thistype.stackTop = this
if amount > 0.0 then
set this.stackType = DAMAGE_TYPE_PHYSICAL
set this.stackAmount = amount
call DamageTrigger.executeAll()
set thistype.allocator[this] = thistype.allocator[0]
set thistype.allocator[0] = this
set thistype.stackTop = thistype.stackTop.stackNext
elseif amount < 0.0 then
set this.stackType = DAMAGE_TYPE_MAGICAL
if IsUnitType(this.stackTarget, UNIT_TYPE_ETHEREAL) then
set amount = amount*ETHEREAL_FACTOR
endif
set this.stackAmount = -amount
call DamageTrigger.executeAll()
set thistype.hp = GetWidgetLife(this.stackTarget)
set newHp = thistype.hp + amount
if newHp < MIN_LIFE then
set newHp = MIN_LIFE
endif
call SetWidgetLife(this.stackTarget, newHp)
set trg = CreateTrigger()
if amount < -1.0 then
call TriggerRegisterUnitStateEvent(trg, this.stackTarget, UNIT_STATE_LIFE, GREATER_THAN, newHp + 1.0)
elseif amount < -0.125 then
call TriggerRegisterUnitStateEvent(trg, this.stackTarget, UNIT_STATE_LIFE, GREATER_THAN, newHp + 0.125)
else
call TriggerRegisterUnitStateEvent(trg, this.stackTarget, UNIT_STATE_LIFE, GREATER_THAN, newHp + 0.01)
endif
call TriggerAddCondition(trg, Condition(function thistype.afterDamage))
set trg = null
set thistype.global = this
endif
return false
endmethod
endif
static if PREPLACE_INIT then
private static method preplace takes nothing returns nothing
local group g = CreateGroup()
local unit u
call GroupEnumUnitsInRect(g, bj_mapInitialPlayableArea, null)
loop
set u = FirstOfGroup(g)
exitwhen u == null
call GroupRemoveUnit(g, u)
if AutoRegisterFilter(u) then
call thistype.add(u)
endif
endloop
call DestroyGroup(g)
call DestroyTimer(GetExpiredTimer())
set g = null
endmethod
endif
static if AUTO_REGISTER then
private static method entered takes nothing returns boolean
local unit u = GetTriggerUnit()
if AutoRegisterFilter(u) then
call thistype.add(u)
endif
set u = null
return false
endmethod
endif
implement DamageInit
endstruct
module DamageInit
private static method onInit takes nothing returns nothing
static if AUTO_REGISTER then
local trigger t = CreateTrigger()
local region reg = CreateRegion()
call RegionAddRect(reg, bj_mapInitialPlayableArea)
call TriggerRegisterEnterRegion(t, reg, null)
call TriggerAddCondition(t, function thistype.entered)
endif
static if LIBRARY_Table then
set thistype.tb = Table.create()
endif
static if PREPLACE_INIT then
call TimerStart(CreateTimer(), 0.0000, false, function thistype.preplace)
endif
set thistype.registered = CreateTrigger()
call DamageTrigger.register(thistype.registered)
set thistype.allocator[0] = 1
set thistype(0).stackSource = null
set thistype(0).stackTarget = null
set thistype(0).stackAmount = 0.0
set thistype(0).stackType = 0
endmethod
endmodule
endlibrary
library DamageModify uses DamageEvent/*
---------------------------------
DamageModify v1.44
by Flux
---------------------------------
An add-on to DamageEvent that allows modification
of damage taken before it is applied.
*/
globals
private constant integer SET_MAX_LIFE = 'ASML'
endglobals
module DamageTrigger2Module
private trigger trg
private thistype next
private thistype prev
method destroy takes nothing returns nothing
set this.next.prev = this.prev
set this.prev.next = this.next
static if LIBRARY_Table then
call Damage.tb.remove(GetHandleId(this.trg))
else
call RemoveSavedInteger(Damage.hash, GetHandleId(this.trg), 0)
endif
set this.trg = null
call this.deallocate()
endmethod
static method unregister takes trigger t returns nothing
local integer id = GetHandleId(t)
static if LIBRARY_Table then
if Damage.tb.has(id) then
call thistype(Damage.tb[id]).destroy()
endif
else
if HaveSavedInteger(Damage.hash, id, 0) then
call thistype(LoadInteger(Damage.hash, id, 0)).destroy()
endif
endif
endmethod
static method register takes trigger t returns nothing
local thistype this = thistype.allocate()
set this.trg = t
set this.next = thistype(0)
set this.prev = thistype(0).prev
set this.next.prev = this
set this.prev.next = this
static if LIBRARY_Table then
set Damage.tb[GetHandleId(t)] = this
else
call SaveInteger(Damage.hash, GetHandleId(t), 0, this)
endif
endmethod
static method executeAll takes nothing returns nothing
local thistype this = thistype(0).next
loop
exitwhen this == 0
if IsTriggerEnabled(this.trg) then
if TriggerEvaluate(this.trg) then
call TriggerExecute(this.trg)
endif
endif
set this = this.next
endloop
endmethod
endmodule
module DamageModify
private static boolean changed = false
private static trigger registered2 = CreateTrigger()
private static boolean locked = false
static method registerModifier takes code c returns boolean
call TriggerAddCondition(thistype.registered2, Condition(c))
return false //Prevents inlining
endmethod
static method registerModifierTrigger takes trigger trg returns nothing
call DamageTrigger2.register(trg)
endmethod
static method unregisterModifierTrigger takes trigger trg returns nothing
call DamageTrigger2.unregister(trg)
endmethod
static method lockAmount takes nothing returns nothing
set thistype.locked = true
endmethod
private static method afterDamage takes nothing returns boolean
if GetUnitAbilityLevel(thistype.stackTop.stackTarget, SET_MAX_LIFE) > 0 then
call UnitRemoveAbility(thistype.stackTop.stackTarget, SET_MAX_LIFE)
endif
call SetWidgetLife(thistype.stackTop.stackTarget, thistype.hp - thistype.stackTop.stackAmount)
call DestroyTrigger(GetTriggeringTrigger())
if thistype.global > 0 then
set thistype.allocator[thistype.global] = thistype.allocator[0]
set thistype.allocator[0] = thistype.global
set thistype.stackTop = thistype.stackTop.stackNext
endif
return false
endmethod
static method core takes nothing returns boolean
local real amount = GetEventDamage()
local boolean changed = false
local thistype this
local trigger trg
local real newHp
if amount == 0.0 then
return false
endif
set this = thistype.allocator[0]
if (thistype.allocator[this] == 0) then
set thistype.allocator[0] = this + 1
else
set thistype.allocator[0] = thistype.allocator[this]
endif
set this.stackSource = GetEventDamageSource()
set this.stackTarget = GetTriggerUnit()
set this.stackNext = thistype.stackTop
set thistype.stackTop = this
if amount > 0.0 then
set this.stackType = DAMAGE_TYPE_PHYSICAL
set this.stackAmount = amount
call DamageTrigger2.executeAll()
set changed = thistype.changed
if changed then
set thistype.changed = false
endif
set thistype.locked = true
call DamageTrigger.executeAll()
set thistype.locked = false
elseif amount < 0.0 then
set this.stackType = DAMAGE_TYPE_MAGICAL
if IsUnitType(this.stackTarget, UNIT_TYPE_ETHEREAL) then
set amount = amount*S_ETHEREAL_FACTOR
endif
set this.stackAmount = -amount
call DamageTrigger2.executeAll()
set changed = thistype.changed
if changed then
set thistype.changed = false
endif
set thistype.locked = true
call DamageTrigger.executeAll()
set thistype.locked = false
endif
if amount < 0.0 or (changed and amount > 0.125) then
set thistype.hp = GetWidgetLife(this.stackTarget)
set trg = CreateTrigger()
if amount > 0.0 then
set newHp = thistype.hp + amount
if newHp > GetUnitState(this.stackTarget, UNIT_STATE_MAX_LIFE) then
call UnitAddAbility(this.stackTarget, SET_MAX_LIFE)
endif
call SetWidgetLife(this.stackTarget, newHp)
if amount > 1.0 then
call TriggerRegisterUnitStateEvent(trg, this.stackTarget, UNIT_STATE_LIFE, LESS_THAN, newHp - 1.0)
elseif amount > 0.125 then
call TriggerRegisterUnitStateEvent(trg, this.stackTarget, UNIT_STATE_LIFE, LESS_THAN, newHp - 0.125)
endif
else
set newHp = thistype.hp + amount
if newHp < S_MIN_LIFE then
set newHp = S_MIN_LIFE
endif
call SetWidgetLife(this.stackTarget, newHp)
if amount < -1.0 then
call TriggerRegisterUnitStateEvent(trg, this.stackTarget, UNIT_STATE_LIFE, GREATER_THAN, newHp + 1.0)
elseif amount < -0.125 then
call TriggerRegisterUnitStateEvent(trg, this.stackTarget, UNIT_STATE_LIFE, GREATER_THAN, newHp + 0.125)
else
call TriggerRegisterUnitStateEvent(trg, this.stackTarget, UNIT_STATE_LIFE, GREATER_THAN, newHp + 0.01)
endif
endif
call TriggerAddCondition(trg, Condition(function thistype.afterDamage))
set trg = null
set thistype.global = this
else
set thistype.allocator[this] = thistype.allocator[0]
set thistype.allocator[0] = this
set thistype.stackTop = thistype.stackTop.stackNext
endif
return false
endmethod
static method operator amount= takes real r returns nothing
if not thistype.locked then
set thistype.stackTop.stackAmount = r
set thistype.changed = true
endif
endmethod
private static method onInit takes nothing returns nothing
local unit u = CreateUnit(Player(14), 'hfoo', 0, 0, 0)
call UnitAddAbility(u, SET_MAX_LIFE)
call RemoveUnit(u)
set thistype.registered2 = CreateTrigger()
call DamageTrigger2.register(thistype.registered2)
set u = null
endmethod
endmodule
endlibrary
library DamageTypes uses DamageEvent
globals
constant integer DAMAGE_TYPE_SPELL = 3
constant integer DAMAGE_TYPE_PURE = 4
constant integer DAMAGE_TYPE_DARK = 5
constant integer DAMAGE_TYPE_LIGHT = 6
integer DAMAGE_TYPE = 0
endglobals
function ApplyDamage takes unit source, unit target, real dmg, boolean ranged, attacktype atk, integer dmgType returns nothing
set DAMAGE_TYPE = dmgType
call UnitDamageTarget(source, target, dmg, true, ranged, atk, DAMAGE_TYPE_UNKNOWN, null)
set DAMAGE_TYPE = 0
endfunction
endlibrary
library DamageModify uses DamageEvent/*
---------------------------------
DamageModify v1.44
by Flux
---------------------------------
An add-on to DamageEvent that allows modification
of damage taken before it is applied.
*/
globals
private constant integer SET_MAX_LIFE = 'ASML'
private constant boolean DEBUG_SYSTEM = true
endglobals
struct DamageTrigger2
private trigger trg
private thistype next
private thistype prev
method destroy takes nothing returns nothing
set this.next.prev = this.prev
set this.prev.next = this.next
static if LIBRARY_Table then
call Damage.tb.remove(GetHandleId(this.trg))
else
call RemoveSavedInteger(Damage.hash, GetHandleId(this.trg), 0)
endif
set this.trg = null
call this.deallocate()
endmethod
static method unregister takes trigger t returns nothing
local integer id = GetHandleId(t)
static if LIBRARY_Table then
if Damage.tb.has(id) then
call thistype(Damage.tb[id]).destroy()
endif
else
if HaveSavedInteger(Damage.hash, id, 0) then
call thistype(LoadInteger(Damage.hash, id, 0)).destroy()
endif
endif
endmethod
static method register takes trigger t returns nothing
local thistype this = thistype.allocate()
set this.trg = t
set this.next = thistype(0)
set this.prev = thistype(0).prev
set this.next.prev = this
set this.prev.next = this
static if LIBRARY_Table then
set Damage.tb[GetHandleId(t)] = this
else
call SaveInteger(Damage.hash, GetHandleId(t), 0, this)
endif
endmethod
static method executeAll takes nothing returns nothing
local thistype this = thistype(0).next
loop
exitwhen this == 0
if IsTriggerEnabled(this.trg) then
if TriggerEvaluate(this.trg) then
call TriggerExecute(this.trg)
endif
endif
set this = this.next
endloop
endmethod
endstruct
module DamageModify
private static boolean changed = false
private static trigger registered2 = CreateTrigger()
private static boolean locked = false
static if DEBUG_SYSTEM then
private static integer instanceCount = 0
endif
static method registerModifier takes code c returns boolean
call TriggerAddCondition(thistype.registered2, Condition(c))
return false //Prevents inlining
endmethod
static method registerModifierTrigger takes trigger trg returns nothing
call DamageTrigger2.register(trg)
endmethod
static method unregisterModifierTrigger takes trigger trg returns nothing
call DamageTrigger2.unregister(trg)
endmethod
static method lockAmount takes nothing returns nothing
set thistype.locked = true
endmethod
private static method afterDamage takes nothing returns boolean
if GetUnitAbilityLevel(thistype.stackTop.stackTarget, SET_MAX_LIFE) > 0 then
call UnitRemoveAbility(thistype.stackTop.stackTarget, SET_MAX_LIFE)
endif
call SetWidgetLife(thistype.stackTop.stackTarget, thistype.hp - thistype.stackTop.stackAmount)
call DestroyTrigger(GetTriggeringTrigger())
if thistype.global > 0 then
set thistype.allocator[thistype.global] = thistype.allocator[0]
set thistype.allocator[0] = thistype.global
set thistype.stackTop = thistype.stackTop.stackNext
endif
static if DEBUG_SYSTEM then
set thistype.instanceCount = thistype.instanceCount - 1
endif
return false
endmethod
static method core takes nothing returns boolean
local real amount = GetEventDamage()
local boolean changed = false
local thistype this
local trigger trg
local real newHp
if amount == 0.0 then
return false
endif
set this = thistype.allocator[0]
if (thistype.allocator[this] == 0) then
set thistype.allocator[0] = this + 1
else
set thistype.allocator[0] = thistype.allocator[this]
endif
set this.stackSource = GetEventDamageSource()
set this.stackTarget = GetTriggerUnit()
set this.stackNext = thistype.stackTop
set thistype.stackTop = this
static if DEBUG_SYSTEM then
set thistype.instanceCount = thistype.instanceCount + 1
endif
if amount > 0.0 then
set this.stackType = DAMAGE_TYPE_PHYSICAL
set this.stackAmount = amount
call DamageTrigger2.executeAll()
set changed = thistype.changed
if changed then
set thistype.changed = false
endif
set thistype.locked = true
call DamageTrigger.executeAll()
set thistype.locked = false
elseif amount < 0.0 then
set this.stackType = DAMAGE_TYPE_MAGICAL
if IsUnitType(this.stackTarget, UNIT_TYPE_ETHEREAL) then
set amount = amount*S_ETHEREAL_FACTOR
endif
set this.stackAmount = -amount
call DamageTrigger2.executeAll()
set changed = thistype.changed
if changed then
set thistype.changed = false
endif
set thistype.locked = true
call DamageTrigger.executeAll()
set thistype.locked = false
endif
if amount < 0.0 or (changed and amount > 0.125) then
set thistype.hp = GetWidgetLife(this.stackTarget)
set trg = CreateTrigger()
if amount > 0.0 then
set newHp = thistype.hp + amount
if newHp > GetUnitState(this.stackTarget, UNIT_STATE_MAX_LIFE) then
call UnitAddAbility(this.stackTarget, SET_MAX_LIFE)
endif
call SetWidgetLife(this.stackTarget, newHp)
if amount > 1.0 then
call TriggerRegisterUnitStateEvent(trg, this.stackTarget, UNIT_STATE_LIFE, LESS_THAN, newHp - 1.0)
elseif amount > 0.125 then
call TriggerRegisterUnitStateEvent(trg, this.stackTarget, UNIT_STATE_LIFE, LESS_THAN, newHp - 0.125)
endif
else
set newHp = thistype.hp + amount
if newHp < S_MIN_LIFE then
set newHp = S_MIN_LIFE
endif
call SetWidgetLife(this.stackTarget, newHp)
if amount < -1.0 then
call TriggerRegisterUnitStateEvent(trg, this.stackTarget, UNIT_STATE_LIFE, GREATER_THAN, newHp + 1)
elseif amount < -0.125 then
call TriggerRegisterUnitStateEvent(trg, this.stackTarget, UNIT_STATE_LIFE, GREATER_THAN, newHp + 0.125)
else
call TriggerRegisterUnitStateEvent(trg, this.stackTarget, UNIT_STATE_LIFE, GREATER_THAN, newHp + 0.01)
endif
endif
call TriggerAddCondition(trg, Condition(function thistype.afterDamage))
set trg = null
set thistype.global = this
else
set thistype.allocator[this] = thistype.allocator[0]
set thistype.allocator[0] = this
set thistype.stackTop = thistype.stackTop.stackNext
static if DEBUG_SYSTEM then
set thistype.instanceCount = thistype.instanceCount - 1
endif
endif
return false
endmethod
static method operator amount= takes real r returns nothing
if not thistype.locked then
set thistype.stackTop.stackAmount = r
set thistype.changed = true
endif
endmethod
static if DEBUG_SYSTEM then
private static method instancePrint takes nothing returns nothing
call BJDebugMsg("Damage.instances = " + I2S(thistype.instanceCount))
endmethod
endif
private static method onInit takes nothing returns nothing
local unit u = CreateUnit(Player(14), 'hfoo', 0, 0, 0)
call UnitAddAbility(u, SET_MAX_LIFE)
call RemoveUnit(u)
set thistype.registered2 = CreateTrigger()
call DamageTrigger2.register(thistype.registered2)
set u = null
static if DEBUG_SYSTEM then
call TimerStart(CreateTimer(), 1.5, true, function thistype.instancePrint)
endif
endmethod
endmodule
endlibrary
/*
----------------------------------
DamageObjects v1.00
by Flux and Aniki
----------------------------------
Automatically generates required Objects by DamageEvent and
DamageModify.
INSTRUCTIONS:
1. Enable DamageObjects trigger.
2. Save the map, pjass.exe should take time in "Executing external commands".
3. Close the map (CTRL + W).
4. Re-open the map, new abilities called Damage.DAMAGE_TYPE_DETECTOR and
Damage.SET_MAX_LIFE should appear.
5. Disable/Delete DamageObjects trigger.
*/
//! externalblock extension=lua ObjectMerger $FILENAME$
//! i setobjecttype("abilities")
//! i createobject("AIsr", "ADMG")
//! i makechange(current, "isr2", "1" , "2")
//! i makechange(current, "aart", "")
//! i makechange(current, "anam", "Damage.DAMAGE_TYPE_DETECTOR")
//! i createobject("AIl1", "ASML")
//! i makechange(current, "Ilif", "1" , "1000000")
//! i makechange(current, "ansf", "")
//! i makechange(current, "anam", "Damage.SET_MAX_LIFE")
//! endexternalblock
library MalignantChain uses Beam, ListT, SpellEffectEvent, EnumGroup, TimerUtils, IsUnitInvulnerable, GetLocZ
// NB: This version of MalignantChain is modified to heal and bounce to allies rather than enemies.
globals
private constant integer SPELL_ID = 'A05D'
private constant integer MAX_NUMBER = 5
private constant real RADIUS = 500.
private constant real BASE_PERCENT = .2
private constant real PERCENT_PER_LVL = .1
private constant real TIMEOUT = .05
private constant real DURATION = 15.
private constant string BONDED_DMG_FX = "Abilities\\Weapons\\SorceressMissile\\SorceressMissile.mdl"
private constant string LIGHTNING_FX = "HWPB"
private IntegerList array ListOfLists
boolean IsBondedDamage = false
endglobals
native UnitAlive takes unit whichUnit returns boolean
//! runtextmacro DEFINE_LIST("private", "BondedUnitList", "unit")
private function GetRandomUnit takes BondedUnitList list returns unit
local integer size = list.size()
local integer i
local BondedUnitListItem node = 0
debug if size == 0 then
debug call BJDebugMsg("the list is empty")
debug return 0
debug endif
// thanks to Wareditor for that bit of code
if size > 0 then
set i = GetRandomInt(1, size)
if i > size / 2 then
set i = size - i
set node = list.last
loop
exitwhen i == 0
set node = node.prev
set i = i - 1
endloop
else
set i = i - 1
set node = list.first
loop
exitwhen i == 0
set node = node.next
set i = i - 1
endloop
endif
endif
return node.data
endfunction
private struct ChainPulse
implement BeamControl
endstruct
private function Chain takes unit caster, unit mark returns nothing
local real xSource = GetUnitX(caster)
local real ySource = GetUnitY(caster)
local real xTarget = GetUnitX(mark)
local real yTarget = GetUnitY(mark)
local real cSource = BlzGetUnitCollisionSize(caster)
local real cTarget = BlzGetUnitCollisionSize(mark)
local Beam b = Beam.create()
set b.source = caster
set b.target = mark
set b.beamFX = LIGHTNING_FX
set b.xLaunch = xSource
set b.yLaunch = ySource
set b.zLaunch = GetUnitZ(caster) + cSource
set b.xImpact = xTarget
set b.yImpact = yTarget
set b.zImpact = GetUnitZ(mark) + cTarget
set b.beamTime = .1
set b.fadeTime = .1
set b.alpha = .5
call ChainPulse.cast(b)
endfunction
function IsUnitBonded takes unit u returns boolean
return ListOfLists[GetUnitUserData(u)] != 0
endfunction
struct MalignantChain
private static BondedUnitList EnumList = 0
private BondedUnitList bondedList
private real damageModifier
private real lifespan
private timer clock
private method destroy takes nothing returns nothing
call ReleaseTimer(this.clock)
set this.clock = null
set this.damageModifier = 0.
call this.bondedList.destroy()
call this.deallocate()
endmethod
private method remove takes unit mark returns nothing
local integer idMark = GetUnitUserData(mark)
call ListOfLists[idMark].removeElem(this.bondedList)
if ListOfLists[idMark].empty() then
call ListOfLists[idMark].destroy()
set ListOfLists[idMark] = 0
endif
call this.bondedList.removeElem(mark)
if this.bondedList.empty() then
call this.destroy()
endif
endmethod
private method add takes unit mark returns nothing
local integer idMark = GetUnitUserData(mark)
if ListOfLists[idMark] == 0 then
set ListOfLists[idMark] = IntegerList.create()
endif
if ListOfLists[idMark].find(this) == 0 then
call ListOfLists[idMark].push(this)
endif
call this.bondedList.push(mark)
call DestroyEffect(AddSpecialEffectTarget(BONDED_DMG_FX, mark, "chest"))
call DestroyEffect(AddSpecialEffectTarget(BONDED_DMG_FX, mark, "chest"))
call DestroyEffect(AddSpecialEffectTarget(BONDED_DMG_FX, mark, "head"))
endmethod
private method removeAll takes nothing returns nothing
local BondedUnitListItem node = this.bondedList.first
local BondedUnitListItem nodeNext
loop
exitwhen node == 0
set nodeNext = node.next
call this.remove(node.data)
set node = nodeNext
endloop
endmethod
private method checkDeadUnits takes nothing returns nothing
local BondedUnitListItem node = this.bondedList.first
local BondedUnitListItem nodeNext
local unit u
loop
exitwhen node == 0
set nodeNext = node.next
set u = node.data
if u != null and not UnitAlive(u) then
call this.remove(u)
endif
set node = nodeNext
endloop
set u = null
endmethod
private static method chainLife takes nothing returns nothing
local timer t = GetExpiredTimer()
local thistype this = GetTimerData(t)
call this.checkDeadUnits() // loops through the list and removes any units that are dead.
if this.bondedList.empty() then
call this.destroy() // if list is empty then there are no units to remove. Destroy it.
else
set this.lifespan = this.lifespan - TIMEOUT
if this.lifespan <= 0. then
call this.removeAll()
endif
endif
set t = null
endmethod
// chains a lightning effect between all linked units. Does it both ways, from first to last and then last to first.
private method pulseChains takes nothing returns nothing
local BondedUnitListItem node = this.bondedList.first
loop
exitwhen node == 0
if node.data != null and node.next.data != null then
call Chain(node.data, node.next.data)
endif
set node = node.next
endloop
set node = this.bondedList.last
loop
exitwhen node == 0
if node.data != null and node.prev.data != null then
call Chain(node.data, node.prev.data)
endif
set node = node.prev
endloop
endmethod
static method applyBondedDamage takes unit source, unit mark, real damage returns nothing
local IntegerListItem nodeMain = ListOfLists[GetUnitUserData(mark)].first
local BondedUnitListItem node
local thistype this
local unit u
local real dmg
loop
exitwhen nodeMain == 0
set this = nodeMain.data
set node = this.bondedList.first
loop
exitwhen node == 0
set u = node.data
if UnitAlive(u) and u != mark then
set dmg = damage * this.damageModifier
/*set IsBondedDamage = true
call UnitDamageTarget(source, u, dmg, false, true, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_UNIVERSAL, null)
set IsBondedDamage = false*/
call SetUnitState(u, UNIT_STATE_LIFE, GetUnitState(u, UNIT_STATE_LIFE) + dmg)
call DestroyEffect(AddSpecialEffectTarget(BONDED_DMG_FX, u, "chest"))
call DestroyEffect(AddSpecialEffectTarget(BONDED_DMG_FX, u, "chest"))
call DestroyEffect(AddSpecialEffectTarget(BONDED_DMG_FX, u, "head"))
endif
set node = node.next
endloop
call this.pulseChains()
set nodeMain = nodeMain.next
endloop
set u = null
endmethod
// Enumerate all units in RADIUS and return the random unit from GetRandomUnit
private method getNext takes real x, real y, player ownerCaster returns unit
local unit u
call EnumGroup(x, y, RADIUS, null)
loop
set u = FirstOfGroup(ENUM_GROUP)
call GroupRemoveUnit(ENUM_GROUP, u)
exitwhen u == null
if UnitAlive(u) and IsUnitAlly(u, ownerCaster) and not IsUnitInvulnerable(u) and this.bondedList.find(u) == 0 /*
*/ and not IsUnitType(u, UNIT_TYPE_MECHANICAL) and not IsUnitType(u, UNIT_TYPE_STRUCTURE) then
call EnumList.push(u)
endif
endloop
return GetRandomUnit(EnumList)
endmethod
static method create takes nothing returns thistype
local thistype this = allocate()
local unit caster = GetTriggerUnit()
local unit mark = GetSpellTargetUnit()
local integer i = MAX_NUMBER - 1
local player ownerCaster = GetOwningPlayer(caster)
local unit next
set this.bondedList = BondedUnitList.create()
set this.damageModifier = BASE_PERCENT + PERCENT_PER_LVL * GetUnitAbilityLevel(caster, SPELL_ID)
set this.lifespan = DURATION
// if the mark is the only unit in the chain, cancel the chain.
set next = this.getNext(GetUnitX(mark), GetUnitY(mark), ownerCaster)
call EnumList.clear()
if next == null then
call this.destroy()
else
call this.add(mark)
loop
call this.add(next)
set mark = next
set next = this.getNext(GetUnitX(mark), GetUnitY(mark), ownerCaster)
call EnumList.clear()
set i = i - 1
exitwhen next == null or i == 0
endloop
call this.pulseChains()
set this.clock = NewTimerEx(this)
call TimerStart(this.clock, TIMEOUT, true, function thistype.chainLife)
endif
set caster = null
set mark = null
set next = null
return 0
endmethod
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_ID, function thistype.create)
set EnumList = BondedUnitList.create()
endmethod
endstruct
endlibrary
library DefyingMartyr uses RegisterPlayerUnitEvent, TimerUtils
struct DefyingMartyr
// The spell id.
private static constant integer SPELL_ID = 'A04T'
// Base HP gain
private static constant real HP_GAIN_BASE = 5.
// Base HP gain per level
private static constant real HP_GAIN_LVL = 5.
// Base HP cap
private static constant real HP_CAP_BASE = 200.
// Base HP cap per level
private static constant real HP_CAP_LVL = 100.
// The delay after which you start losing bonus HP.
private static constant real TIMEOUT = 3.
// This is the micro-time for checking whether decaying bonus health should be refreshed
private static constant real INTERVAL = .1
// How much HP is lost after not attacking for TIMEOUT seconds.
private static constant real HP_LOSS = -15.
// How the effect that plays to indicate the unit has bonus health
private static constant string BONUS_FX = "Abilities\\Weapons\\ProcMissile\\ProcMissile.mdl"
// How the effect that plays to indicate the unit has bonus health
private static constant string ATTACHMENT_PT = "head"
private static integer array Instance
private real hpBonus
private real countdown
private unit source
private timer clock
private effect fx
private method destroy takes nothing returns nothing
set this.source = null
call DestroyEffect(this.fx)
set this.fx = null
call ReleaseTimer(this.clock)
set this.clock = null
call this.deallocate()
endmethod
private method modHp takes real bonus returns nothing
local real hp = GetUnitState(this.source, UNIT_STATE_LIFE)
local real hpMax = GetUnitState(this.source, UNIT_STATE_MAX_LIFE)
local real percentage = ((100. / hpMax) * hp) * .01
local integer newMaxHp = R2I(hpMax + bonus)
set this.hpBonus = this.hpBonus + bonus
call BlzSetUnitMaxHP(this.source, newMaxHp)
call SetUnitState(this.source, UNIT_STATE_LIFE, newMaxHp * percentage)
endmethod
private static method update takes nothing returns nothing
local timer t = GetExpiredTimer()
local thistype this = GetTimerData(t)
local real loss
set this.countdown = this.countdown - INTERVAL
if this.countdown <= 0. then
set this.countdown = TIMEOUT
if this.hpBonus < -HP_LOSS then
set loss = -this.hpBonus
else
set loss = HP_LOSS
endif
call this.modHp(loss)
endif
if this.hpBonus <= 0. then
set Instance[GetUnitUserData(this.source)] = 0
call this.destroy()
endif
set t = null
endmethod
private static method make takes nothing returns nothing
local unit caster = GetAttacker()
local integer spellLvl = GetUnitAbilityLevel(caster, SPELL_ID)
local integer id = GetUnitUserData(caster)
local thistype this = Instance[id]
local real cap
local real bonus
if spellLvl > 0 then
if this == 0 then
set this = allocate()
set this.source = caster
set this.hpBonus = 0.
set this.countdown = TIMEOUT
set this.clock = NewTimerEx(this)
set this.fx = AddSpecialEffectTarget(BONUS_FX, caster, ATTACHMENT_PT)
set Instance[id] = this
call TimerStart(this.clock, INTERVAL, true, function thistype.update)
endif
set this.countdown = TIMEOUT
set cap = HP_CAP_BASE + HP_CAP_LVL * spellLvl
if this.hpBonus < cap then
set bonus = HP_GAIN_BASE + HP_GAIN_LVL * spellLvl
if bonus + this.hpBonus > cap then
set bonus = cap - this.hpBonus
endif
call this.modHp(bonus)
endif
endif
set caster = null
endmethod
private static method onInit takes nothing returns nothing
call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_ATTACKED, function thistype.make)
endmethod
endstruct
endlibrary
library SetupHumans uses CustomVictoryDefeat
globals
// CONFIG
private race RACE = RACE_HUMAN
private string CHOICE_STRING = "Play as Draenei?"
private integer BASE_WORKER = 'hpea'
private integer NEW_WORKER = 'h03T'
private integer BASE_HALL = 'htow'
private integer NEW_HALL = 'h03U'
// END CONFIG
private dialog array Choice
private button array Yes
private button array No
private trigger array DialogTrigger
private hashtable Hash = InitHashtable()
endglobals
private function DialogClicked takes nothing returns nothing
local trigger trig = GetTriggeringTrigger()
local integer trigId = GetHandleId(trig)
local integer i = LoadInteger(Hash, trigId, 0)
local player play = Player(i)
local group g
local unit u
if GetClickedButton() == Yes[i] then
set g = CreateGroup()
call GroupEnumUnitsOfPlayer(g, play, null)
loop
set u = FirstOfGroup(g)
exitwhen u == null
call GroupRemoveUnit(g, u)
if GetUnitTypeId(u) == BASE_WORKER and GetOwningPlayer(u) == play then
call ReplaceUnitBJ(u, NEW_WORKER, bj_UNIT_STATE_METHOD_RELATIVE)
elseif GetUnitTypeId(u) == BASE_HALL and GetOwningPlayer(u) == play then
call RemoveUnit(u)
call CreateUnit(play, NEW_HALL, GetPlayerStartLocationX(play), GetPlayerStartLocationY(play), bj_UNIT_FACING)
endif
endloop
call DestroyGroup(g)
endif
call FlushChildHashtable(Hash, trigId)
call DialogDestroy(Choice[i])
endfunction
public function Actions takes nothing returns nothing
local integer i = 0 // player 1 starts at 0 in JASS
local player play
local dialog diag
loop
set play = Player(i)
if GetPlayerController(play) == MAP_CONTROL_USER and GetPlayerRace(play) == RACE then
set diag = DialogCreate()
set Choice[i] = diag
call DialogSetMessage(diag, CHOICE_STRING)
set Yes[i] = DialogAddButton(diag, "Yes", 0)
set No[i] = DialogAddButton(diag, "No", 0)
set DialogTrigger[i] = CreateTrigger()
call TriggerRegisterDialogEvent(DialogTrigger[i], diag)
call TriggerAddAction(DialogTrigger[i], function DialogClicked)
call SaveInteger(Hash, GetHandleId(DialogTrigger[i]), 0, i)
call DialogDisplay(play, diag, true)
endif
call SetPlayerMaxHeroesAllowed(3, play)
set i = i + 1
exitwhen i > bj_MAX_PLAYER_SLOTS
endloop
call MeleeInitVictoryDefeatCustomized()
call MeleeStartingAI()
call MeleeStartingVisibility()
set diag = null
endfunction
endlibrary
library SetupOrcs uses CustomVictoryDefeat
globals
// CONFIG
private race RACE = RACE_ORC
private string CHOICE_STRING = "Play as Lightforged?"
private integer BASE_WORKER = 'opeo'
private integer NEW_WORKER = 'e002'
private integer BASE_HALL = 'ogre'
private integer NEW_HALL = 'h00J'
// END CONFIG
private dialog array Choice
private button array Yes
private button array No
private trigger array DialogTrigger
private hashtable Hash = InitHashtable()
endglobals
private function DialogClicked takes nothing returns nothing
local trigger trig = GetTriggeringTrigger()
local integer trigId = GetHandleId(trig)
local integer i = LoadInteger(Hash, trigId, 0)
local player play = Player(i)
local group g
local unit u
if GetClickedButton() == Yes[i] then
set g = CreateGroup()
call GroupEnumUnitsOfPlayer(g, play, null)
loop
set u = FirstOfGroup(g)
exitwhen u == null
call GroupRemoveUnit(g, u)
if GetUnitTypeId(u) == BASE_WORKER and GetOwningPlayer(u) == play then
call ReplaceUnitBJ(u, NEW_WORKER, bj_UNIT_STATE_METHOD_RELATIVE)
elseif GetUnitTypeId(u) == BASE_HALL and GetOwningPlayer(u) == play then
call RemoveUnit(u)
call CreateUnit(play, NEW_HALL, GetPlayerStartLocationX(play), GetPlayerStartLocationY(play), bj_UNIT_FACING)
endif
endloop
call DestroyGroup(g)
endif
call FlushChildHashtable(Hash, trigId)
call DialogDestroy(Choice[i])
endfunction
public function Actions takes nothing returns nothing
local integer i = 0 // player 1 starts at 0 in JASS
local player play
local dialog diag
loop
set play = Player(i)
if GetPlayerController(play) == MAP_CONTROL_USER and GetPlayerRace(play) == RACE then
set diag = DialogCreate()
set Choice[i] = diag
call DialogSetMessage(diag, CHOICE_STRING)
set Yes[i] = DialogAddButton(diag, "Yes", 0)
set No[i] = DialogAddButton(diag, "No", 0)
set DialogTrigger[i] = CreateTrigger()
call TriggerRegisterDialogEvent(DialogTrigger[i], diag)
call TriggerAddAction(DialogTrigger[i], function DialogClicked)
call SaveInteger(Hash, GetHandleId(DialogTrigger[i]), 0, i)
call DialogDisplay(play, diag, true)
endif
call SetPlayerMaxHeroesAllowed(3, play)
set i = i + 1
exitwhen i > bj_MAX_PLAYER_SLOTS
endloop
call MeleeInitVictoryDefeatCustomized()
call MeleeStartingAI()
call MeleeStartingVisibility()
set diag = null
endfunction
endlibrary
scope PrimeSetup
private function Actions takes nothing returns nothing
call SetupHumans_Actions()
call SetupOrcs_Actions()
//call SetupUndead_Actions()
//call SetupNightElves_Actions()
endfunction
private module init
private static method onInit takes nothing returns nothing
local trigger trig = CreateTrigger()
call MeleeClearExcessUnits()
call MeleeStartingUnits()
call MeleeStartingHeroLimit()
call MeleeGrantHeroItems()
call MeleeStartingResources()
call TriggerRegisterTimerEventSingle(trig, 0.01 )
call TriggerAddAction(trig, function Actions)
endmethod
endmodule
private struct Setup
implement init
endstruct
endscope
library CustomVictoryDefeat
globals
private constant string RevealWarning = "You will be revealed to your opponents unless you build a town-hall type structure."
endglobals
//===========================================================================
// Counts key structures owned by a player and his or her allies, including
// structures currently upgrading or under construction.
//
// Key structures: Town Hall, Great Hall, Tree of Life, Necropolis
//
private function LivingPlayerHallsFilter takes nothing returns boolean
return (IsUnitAliveBJ(GetFilterUnit()) and IsUnitType(GetFilterUnit(),UNIT_TYPE_TOWNHALL))
endfunction
private function CountLivingPlayerTownHalls takes player whichPlayer returns integer
local group g
local integer matchedCount
local boolexpr b=Filter(function LivingPlayerHallsFilter)
set g = CreateGroup()
call GroupEnumUnitsOfPlayer(g, whichPlayer, b)
set matchedCount = CountUnitsInGroup(g)
call DestroyGroup(g)
call DestroyBoolExpr(b)
set b=null
set g=null
return matchedCount
endfunction
private function Custom_MeleeGetAllyKeyStructureCount takes player whichPlayer returns integer
local integer playerIndex
local player indexPlayer
local integer keyStructs
// Count the number of buildings controlled by all not-yet-defeated co-allies.
set keyStructs = 0
set playerIndex = 0
loop
set indexPlayer = Player(playerIndex)
if (PlayersAreCoAllied(whichPlayer, indexPlayer)) then
set keyStructs = keyStructs + CountLivingPlayerTownHalls(indexPlayer)
// set keyStructs = keyStructs + GetPlayerTypedUnitCount(indexPlayer, "townhall", true, true)
// set keyStructs = keyStructs + GetPlayerTypedUnitCount(indexPlayer, "greathall", true, true)
// set keyStructs = keyStructs + GetPlayerTypedUnitCount(indexPlayer, "treeoflife", true, true)
// set keyStructs = keyStructs + GetPlayerTypedUnitCount(indexPlayer, "necropolis", true, true)
// set keyStructs = keyStructs + GetPlayerTypedUnitCount(indexPlayer, "custom_h030", true, true)
// set keyStructs = keyStructs + GetPlayerTypedUnitCount(indexPlayer, "custom_h01C", true, true)
// set keyStructs = keyStructs + GetPlayerTypedUnitCount(indexPlayer, "custom_h012", true, true)
// set keyStructs = keyStructs + GetPlayerTypedUnitCount(indexPlayer, "custom_h013", true, true)
// set keyStructs = keyStructs + CountLivingPlayerUnitsOfTypeId('h013',indexPlayer)//Never use group functions for this, just count living units for player. Much better idea.
// set keyStructs = keyStructs + CountLivingPlayerUnitsOfTypeId('h014',indexPlayer)
// set keyStructs = keyStructs + CountLivingPlayerUnitsOfTypeId('h015',indexPlayer)
// set keyStructs = keyStructs + CountLivingPlayerUnitsOfTypeId('h01C',indexPlayer)
// set keyStructs = keyStructs + CountLivingPlayerUnitsOfTypeId('h01E',indexPlayer)
// set keyStructs = keyStructs + CountLivingPlayerUnitsOfTypeId('h01F',indexPlayer)
// set keyStructs = keyStructs + CountLivingPlayerUnitsOfTypeId('h012',indexPlayer)
// set keyStructs = keyStructs + CountLivingPlayerUnitsOfTypeId('h02F',indexPlayer)
// set keyStructs = keyStructs + CountLivingPlayerUnitsOfTypeId('h02J',indexPlayer)
// set keyStructs = keySructs + CountLivingPlayerUnitsOfTypeId('h030',indexPlayer)
// set keyStructs = keyStructs + CountLivingPlayerUnitsOfTypeId('h000',indexPlayer)//Non-modded human custom ids
// set keyStructs = keyStructs + CountLivingPlayerUnitsOfTypeId('h00D',indexPlayer)
// set keyStructs = keyStructs + CountLivingPlayerUnitsOfTypeId('h00E',indexPlayer)
// set keyStructs = keyStructs + CountLivingPlayerUnitsOfTypeId('o00C',indexPlayer)//Non-modded orc custom ids
// set keyStructs = keyStructs + CountLivingPlayerUnitsOfTypeId('o00D',indexPlayer)
// set keyStructs = keyStructs + CountLivingPlayerUnitsOfTypeId('o00E',indexPlayer)
// set keyStructs = keyStructs + CountLivingPlayerUnitsOfTypeId('e00M',indexPlayer)//Non-modded night elf custom ids
// set keyStructs = keyStructs + CountLivingPlayerUnitsOfTypeId('e00N',indexPlayer)
// set keyStructs = keyStructs + CountLivingPlayerUnitsOfTypeId('e00O',indexPlayer)
// set keyStructs = keyStructs + CountLivingPlayerUnitsOfTypeId('u00C',indexPlayer)//Non-modded undead custom ids
// set keyStructs = keyStructs + CountLivingPlayerUnitsOfTypeId('u00D',indexPlayer)
// set keyStructs = keyStructs + CountLivingPlayerUnitsOfTypeId('u00E',indexPlayer)
endif
set playerIndex = playerIndex + 1
exitwhen playerIndex == bj_MAX_PLAYERS
endloop
return keyStructs
endfunction
//===========================================================================
private function Custom_MeleePlayerIsCrippled takes player whichPlayer returns boolean
local integer allyStructures = MeleeGetAllyStructureCount(whichPlayer)
local integer allyKeyStructures = Custom_MeleeGetAllyKeyStructureCount(whichPlayer)
// Dead teams are not considered to be crippled.
return (allyStructures > 0) and (allyKeyStructures <= 0)
endfunction
//===========================================================================
// Test each player to determine if anyone has become crippled.
//
private function Custom_MeleeCheckForCrippledPlayers takes nothing returns nothing
local integer playerIndex
local player indexPlayer
local force crippledPlayers = CreateForce()
local boolean isNowCrippled
local race indexRace
// The "finish soon" exposure of all players overrides any "crippled" exposure
if bj_finishSoonAllExposed then
return
endif
// Check each player to see if he or she has been crippled or uncrippled.
set playerIndex = 0
loop
set indexPlayer = Player(playerIndex)
set isNowCrippled = Custom_MeleePlayerIsCrippled(indexPlayer)
if (not bj_playerIsCrippled[playerIndex] and isNowCrippled) then
// Player became crippled; start their cripple timer.
set bj_playerIsCrippled[playerIndex] = true
call TimerStart(bj_crippledTimer[playerIndex], bj_MELEE_CRIPPLE_TIMEOUT, false, function MeleeCrippledPlayerTimeout)
if (GetLocalPlayer() == indexPlayer) then
// Use only local code (no net traffic) within this block to avoid desyncs.
// Show the timer window.
call TimerDialogDisplay(bj_crippledTimerWindows[playerIndex], true)
// Display a warning message.
call DisplayTimedTextToPlayer(indexPlayer, 0, 0, bj_MELEE_CRIPPLE_MSG_DURATION, "|cffffcc00"+RevealWarning+"|r")
endif
elseif (bj_playerIsCrippled[playerIndex] and not isNowCrippled) then
// Player became uncrippled; stop their cripple timer.
set bj_playerIsCrippled[playerIndex] = false
call PauseTimer(bj_crippledTimer[playerIndex])
if (GetLocalPlayer() == indexPlayer) then
// Use only local code (no net traffic) within this block to avoid desyncs.
// Hide the timer window for this player.
call TimerDialogDisplay(bj_crippledTimerWindows[playerIndex], false)
// Display a confirmation message if the player's team is still alive.
if (MeleeGetAllyStructureCount(indexPlayer) > 0) then
if (bj_playerIsExposed[playerIndex]) then
call DisplayTimedTextToPlayer(indexPlayer, 0, 0, bj_MELEE_CRIPPLE_MSG_DURATION, GetLocalizedString("CRIPPLE_UNREVEALED"))
else
call DisplayTimedTextToPlayer(indexPlayer, 0, 0, bj_MELEE_CRIPPLE_MSG_DURATION, GetLocalizedString("CRIPPLE_UNCRIPPLED"))
endif
endif
endif
// If the player granted shared vision, deny that vision now.
call MeleeExposePlayer(indexPlayer, false)
endif
set playerIndex = playerIndex + 1
exitwhen playerIndex == bj_MAX_PLAYERS
endloop
endfunction
//===========================================================================
// Determine if the lost unit should result in any defeats or victories.
//
private function Custom_MeleeCheckLostUnit takes unit lostUnit returns nothing
local player lostUnitOwner = GetOwningPlayer(lostUnit)
// We only need to check for mortality if this was the last building.
if (GetPlayerStructureCount(lostUnitOwner, true) <= 0) then
call MeleeCheckForLosersAndVictors()
endif
// Check if the lost unit has crippled or uncrippled the player.
// (A team with 0 units is dead, and thus considered uncrippled.)
call Custom_MeleeCheckForCrippledPlayers()
endfunction
//===========================================================================
// Determine if the gained unit should result in any defeats, victories,
// or cripple-status changes.
//
private function Custom_MeleeCheckAddedUnit takes unit addedUnit returns nothing
local player addedUnitOwner = GetOwningPlayer(addedUnit)
// If the player was crippled, this unit may have uncrippled him/her.
if (bj_playerIsCrippled[GetPlayerId(addedUnitOwner)]) then
call Custom_MeleeCheckForCrippledPlayers()
endif
endfunction
//===========================================================================
private function Custom_MeleeTriggerActionConstructCancel takes nothing returns nothing
call Custom_MeleeCheckLostUnit(GetCancelledStructure())
endfunction
//===========================================================================
private function Custom_MeleeTriggerActionUnitDeath takes nothing returns nothing
if (IsUnitType(GetDyingUnit(), UNIT_TYPE_STRUCTURE)) then
call Custom_MeleeCheckLostUnit(GetDyingUnit())
endif
endfunction
//===========================================================================
private function Custom_MeleeTriggerActionUnitConstructionStart takes nothing returns nothing
call Custom_MeleeCheckAddedUnit(GetConstructingStructure())
endfunction
//===========================================================================
private function Custom_MeleeTriggerActionAllianceChange takes nothing returns nothing
call MeleeCheckForLosersAndVictors()
call Custom_MeleeCheckForCrippledPlayers()
endfunction
//===========================================================================
function MeleeInitVictoryDefeatCustomized takes nothing returns nothing
local trigger trig
local integer index
local player indexPlayer
// Create a timer window for the "finish soon" timeout period, it has no timer
// because it is driven by real time (outside of the game state to avoid desyncs)
set bj_finishSoonTimerDialog = CreateTimerDialog(null)
// Set a trigger to fire when we receive a "finish soon" game event
set trig = CreateTrigger()
call TriggerRegisterGameEvent(trig , EVENT_GAME_TOURNAMENT_FINISH_SOON)
call TriggerAddAction(trig , function MeleeTriggerTournamentFinishSoon)
// Set a trigger to fire when we receive a "finish now" game event
set trig = CreateTrigger()
call TriggerRegisterGameEvent(trig , EVENT_GAME_TOURNAMENT_FINISH_NOW)
call TriggerAddAction(trig , function MeleeTriggerTournamentFinishNow)
// Set up each player's mortality code.
set index = 0
loop
set indexPlayer = Player(index)
// Make sure this player slot is playing.
if ( GetPlayerSlotState(indexPlayer) == PLAYER_SLOT_STATE_PLAYING ) then
set bj_meleeDefeated[index]=false
set bj_meleeVictoried[index]=false
// Create a timer and timer window in case the player is crippled.
set bj_playerIsCrippled[index]=false
set bj_playerIsExposed[index]=false
set bj_crippledTimer[index]=CreateTimer()
set bj_crippledTimerWindows[index]=CreateTimerDialog(bj_crippledTimer[index])
call TimerDialogSetTitle(bj_crippledTimerWindows[index] , MeleeGetCrippledTimerMessage(indexPlayer))
// Set a trigger to fire whenever a building is cancelled for this player.
set trig = CreateTrigger()
call TriggerRegisterPlayerUnitEvent(trig , indexPlayer , EVENT_PLAYER_UNIT_CONSTRUCT_CANCEL , null)
call TriggerAddAction(trig , function Custom_MeleeTriggerActionConstructCancel)
// Set a trigger to fire whenever a unit dies for this player.
set trig = CreateTrigger()
call TriggerRegisterPlayerUnitEvent(trig , indexPlayer , EVENT_PLAYER_UNIT_DEATH , null)
call TriggerAddAction(trig , function Custom_MeleeTriggerActionUnitDeath)
// Set a trigger to fire whenever a unit begins construction for this player
set trig = CreateTrigger()
call TriggerRegisterPlayerUnitEvent(trig , indexPlayer , EVENT_PLAYER_UNIT_CONSTRUCT_START , null)
call TriggerAddAction(trig , function Custom_MeleeTriggerActionUnitConstructionStart)
// Set a trigger to fire whenever this player defeats-out
set trig = CreateTrigger()
call TriggerRegisterPlayerEvent(trig , indexPlayer , EVENT_PLAYER_DEFEAT)
call TriggerAddAction(trig , function MeleeTriggerActionPlayerDefeated)
// Set a trigger to fire whenever this player leaves
set trig = CreateTrigger()
call TriggerRegisterPlayerEvent(trig , indexPlayer , EVENT_PLAYER_LEAVE)
call TriggerAddAction(trig , function MeleeTriggerActionPlayerLeft)
// Set a trigger to fire whenever this player changes his/her alliances.
set trig = CreateTrigger()
call TriggerRegisterPlayerAllianceChange(trig , indexPlayer , ALLIANCE_PASSIVE)
call TriggerRegisterPlayerStateEvent(trig , indexPlayer , PLAYER_STATE_ALLIED_VICTORY , EQUAL , 1)
call TriggerAddAction(trig , function Custom_MeleeTriggerActionAllianceChange)
else
set bj_meleeDefeated[index]=true
set bj_meleeVictoried[index]=false
// Handle leave events for observers
if ( IsPlayerObserver(indexPlayer) ) then
// Set a trigger to fire whenever this player leaves
set trig = CreateTrigger()
call TriggerRegisterPlayerEvent(trig , indexPlayer , EVENT_PLAYER_LEAVE)
call TriggerAddAction(trig , function MeleeTriggerActionPlayerLeft)
endif
endif
set index = index + 1
exitwhen index == bj_MAX_PLAYERS
endloop
// Test for victory / defeat at startup, in case the user has already won / lost.
// Allow for a short time to pass first, so that the map can finish loading.
call TimerStart(CreateTimer() , 2.0 , false , function Custom_MeleeTriggerActionAllianceChange)
endfunction
endlibrary