Name | Type | is_array | initial_value |
AfterDamageEvent | real | No | |
AOEDamageEvent | real | No | |
AOEDamageSource | unit | No | |
ARMOR_TYPE_ETHEREAL | integer | No | |
ARMOR_TYPE_FLESH | integer | No | |
ARMOR_TYPE_METAL | integer | No | |
ARMOR_TYPE_NONE | integer | No | |
ARMOR_TYPE_STONE | integer | No | |
ARMOR_TYPE_WOOD | integer | No | |
ArmorDamageEvent | real | No | |
ArmorTypeDebugStr | string | Yes | |
ATTACK_TYPE_CHAOS | integer | No | |
ATTACK_TYPE_HERO | integer | No | |
ATTACK_TYPE_MAGIC | integer | No | |
ATTACK_TYPE_NORMAL | integer | No | |
ATTACK_TYPE_PIERCE | integer | No | |
ATTACK_TYPE_SIEGE | integer | No | |
ATTACK_TYPE_SPELLS | integer | No | |
AttackTypeDebugStr | string | Yes | |
CargoEvent | real | No | |
CargoTransportGroup | group | Yes | |
CargoTransportUnit | unit | Yes | |
CheckDeathInList | boolean | Yes | |
CheckDeathList | integer | Yes | |
CheckDeathTimer | timer | No | |
CONVERTED_ATTACK_TYPE | attacktype | Yes | |
CONVERTED_DAMAGE_TYPE | damagetype | Yes | |
DAMAGE_TYPE_ACID | integer | No | |
DAMAGE_TYPE_COLD | integer | No | |
DAMAGE_TYPE_DEATH | integer | No | |
DAMAGE_TYPE_DEFENSIVE | integer | No | |
DAMAGE_TYPE_DEMOLITION | integer | No | |
DAMAGE_TYPE_DISEASE | integer | No | |
DAMAGE_TYPE_DIVINE | integer | No | |
DAMAGE_TYPE_ENHANCED | integer | No | |
DAMAGE_TYPE_FIRE | integer | No | |
DAMAGE_TYPE_FORCE | integer | No | |
DAMAGE_TYPE_LIGHTNING | integer | No | |
DAMAGE_TYPE_MAGIC | integer | No | |
DAMAGE_TYPE_MIND | integer | No | |
DAMAGE_TYPE_NORMAL | integer | No | |
DAMAGE_TYPE_PLANT | integer | No | |
DAMAGE_TYPE_POISON | integer | No | |
DAMAGE_TYPE_SHADOW_STRIKE | integer | No | |
DAMAGE_TYPE_SLOW_POISON | integer | No | |
DAMAGE_TYPE_SONIC | integer | No | |
DAMAGE_TYPE_SPIRIT_LINK | integer | No | |
DAMAGE_TYPE_UNIVERSAL | integer | No | |
DAMAGE_TYPE_UNKNOWN | integer | No | |
DamageEvent | real | No | |
DamageEventAmount | real | No | |
DamageEventAOE | integer | No | |
DamageEventAOEGroup | group | No | |
DamageEventArmorPierced | real | No | |
DamageEventArmorT | integer | No | |
DamageEventAttackT | integer | No | |
DamageEventDamageT | integer | No | |
DamageEventDefenseT | integer | No | |
DamageEventLevel | integer | No | |
DamageEventOverride | boolean | No | |
DamageEventPrevAmt | real | No | |
DamageEventSource | unit | No | |
DamageEventTarget | unit | No | |
DamageEventType | integer | No | |
DamageEventWeaponT | integer | No | |
DamageFilterAttackT | integer | No | |
DamageFilterDamageT | integer | No | |
DamageFilterFailChance | real | No | |
DamageFilterMinAmount | real | No | |
DamageFilterRunChance | real | No | |
DamageFilterSource | unit | No | |
DamageFilterSourceA | abilcode | No | |
DamageFilterSourceB | buffcode | No | |
DamageFilterSourceC | integer | No | |
DamageFilterSourceI | itemcode | No | |
DamageFilterSourceT | unitcode | No | |
DamageFilterTarget | unit | No | |
DamageFilterTargetA | abilcode | No | |
DamageFilterTargetB | buffcode | No | |
DamageFilterTargetC | integer | No | |
DamageFilterTargetI | itemcode | No | |
DamageFilterTargetT | unitcode | No | |
DamageFilterType | integer | No | |
DamageModifierEvent | real | No | |
DamageScalingUser | real | No | |
DamageScalingWC3 | real | No | |
DamageTypeBlocked | integer | No | |
DamageTypeCode | integer | No | |
DamageTypeCriticalStrike | integer | No | |
DamageTypeDebugStr | string | Yes | |
DamageTypeExplosive | integer | No | |
DamageTypeHeal | integer | No | |
DamageTypePure | integer | No | |
DamageTypePureExplosive | integer | No | |
DamageTypeReduced | integer | No | |
DeathEvent | real | No | |
DEFENSE_TYPE_DIVINE | integer | No | |
DEFENSE_TYPE_FORTIFIED | integer | No | |
DEFENSE_TYPE_HEAVY | integer | No | |
DEFENSE_TYPE_HERO | integer | No | |
DEFENSE_TYPE_LIGHT | integer | No | |
DEFENSE_TYPE_MEDIUM | integer | No | |
DEFENSE_TYPE_NORMAL | integer | No | |
DEFENSE_TYPE_UNARMORED | integer | No | |
DefenseTypeDebugStr | string | Yes | |
DetectRemoveAbility | abilcode | No | |
DetectTransformAbility | abilcode | No | |
EnhancedDamageTarget | unit | No | |
IsDamageAttack | boolean | No | |
IsDamageCode | boolean | No | |
IsDamageMelee | boolean | No | |
IsDamageRanged | boolean | No | |
IsDamageSpell | boolean | No | |
IsUnitAlive | boolean | Yes | |
IsUnitBeingUnloaded | boolean | Yes | |
IsUnitNew | boolean | Yes | |
IsUnitPreplaced | boolean | Yes | |
IsUnitReincarnating | boolean | Yes | |
IsUnitRemoved | boolean | Yes | |
IsUnitTransforming | boolean | Yes | |
KillerOfUnit | unit | Yes | |
LethalDamageEvent | real | No | |
LethalDamageHP | real | No | |
LocalTempUnit | unit | No | |
LocalTempUnit1 | unit | No | |
NextDamageIsAttack | boolean | No | |
NextDamageIsMelee | boolean | No | |
NextDamageIsRanged | boolean | No | |
NextDamageType | integer | No | |
NextDamageWeaponT | integer | No | |
NextShieldBreakAtZero | boolean | No | |
NextShieldCanRegen | boolean | No | |
NextShieldDuration | real | No | |
NextShieldHP | real | No | |
NextShieldHpType | integer | No | |
NextShieldIsStackable | boolean | No | |
NextShieldIsTimed | boolean | No | |
NextShieldName | string | No | |
NextShieldProcType | integer | No | |
NextShieldRegenTime | real | No | |
NextShieldSfxAttachPoint | string | No | |
NextShieldSfxPath | string | No | |
NextShieldSource | unit | No | |
NextShieldTarget | unit | No | |
NextShieldTimeBeforeRegen | real | No | |
NextShieldType | integer | No | |
OnDamageEvent | real | No | |
PDex | integer | No | |
PlayerSelection | group | Yes | |
PlayerSelectionEvent | real | No | |
PreDamageEvent | real | No | |
PreShieldCreationEvent | real | No | |
RemoveDamageEvent | boolean | No | |
SHIELD_HP_TYPE_ALL | integer | No | |
SHIELD_HP_TYPE_ATTACK | integer | No | 2 |
SHIELD_HP_TYPE_SPELL | integer | No | 1 |
SHIELD_PROC_POSTREDUC | integer | No | 1 |
SHIELD_PROC_PREREDUC | integer | No | |
SHIELD_TYPE_HP_BASED | integer | No | |
SHIELD_TYPE_INSTANCE | integer | No | 1 |
ShieldAbsorbedDamage | real | No | |
ShieldAbsorbEvent | real | No | |
ShieldBreakAtZero | boolean | No | |
ShieldBreakEvent | real | No | |
ShieldCanRegen | boolean | No | |
ShieldCreatedEvent | real | No | |
ShieldCurrentHP | real | No | |
ShieldDuration | real | No | |
ShieldExpiredEvent | real | No | |
ShieldHpType | integer | No | |
ShieldIsStackable | boolean | No | |
ShieldIsTimed | boolean | No | |
ShieldMaxHP | real | No | |
ShieldName | string | No | |
ShieldProcType | integer | No | |
ShieldRefreshedEvent | real | No | |
ShieldRegenTime | real | No | |
ShieldSource | unit | No | |
ShieldStackedEvent | real | No | |
ShieldTimeBeforeRegen | real | No | |
ShieldTimeLeft | real | No | |
ShieldType | integer | No | |
ShieldWielder | unit | No | |
SourceDamageEvent | real | No | |
SummonerOfUnit | unit | Yes | |
UDex | integer | No | |
UDexLastRecycled | integer | No | |
UDexMax | integer | No | |
UDexNext | integer | Yes | |
UDexPrev | integer | Yes | |
UDexUnits | unit | Yes | |
UNIT_CLASS_ANCIENT | integer | No | |
UNIT_CLASS_ATTACKS_FLYING | integer | No | |
UNIT_CLASS_ATTACKS_GROUND | integer | No | |
UNIT_CLASS_DEAD | integer | No | |
UNIT_CLASS_ETHEREAL | integer | No | |
UNIT_CLASS_FLYING | integer | No | |
UNIT_CLASS_GIANT | integer | No | |
UNIT_CLASS_GROUND | integer | No | |
UNIT_CLASS_HERO | integer | No | |
UNIT_CLASS_MAGIC_IMMUNE | integer | No | |
UNIT_CLASS_MECHANICAL | integer | No | |
UNIT_CLASS_MELEE | integer | No | |
UNIT_CLASS_PEON | integer | No | |
UNIT_CLASS_PLAGUED | integer | No | |
UNIT_CLASS_POISONED | integer | No | |
UNIT_CLASS_POLYMORPHED | integer | No | |
UNIT_CLASS_RANGED | integer | No | |
UNIT_CLASS_RESISTANT | integer | No | |
UNIT_CLASS_SAPPER | integer | No | |
UNIT_CLASS_SLEEPING | integer | No | |
UNIT_CLASS_SNARED | integer | No | |
UNIT_CLASS_STRUCTURE | integer | No | |
UNIT_CLASS_STUNNED | integer | No | |
UNIT_CLASS_SUMMONED | integer | No | |
UNIT_CLASS_TAUREN | integer | No | |
UNIT_CLASS_TOWNHALL | integer | No | |
UNIT_CLASS_UNDEAD | integer | No | |
UnitInAction | boolean | Yes | |
UnitInActionEvent | real | No | |
UnitIndexerEnabled | boolean | No | |
UnitIndexEvent | real | No | |
UnitTypeEvent | real | No | |
UnitTypeOf | unitcode | Yes | |
WEAPON_TYPE_AM_CHOP | integer | No | |
WEAPON_TYPE_CH_SLICE | integer | No | |
WEAPON_TYPE_CL_SLICE | integer | No | |
WEAPON_TYPE_CM_SLICE | integer | No | |
WEAPON_TYPE_MH_BASH | integer | No | |
WEAPON_TYPE_MH_CHOP | integer | No | |
WEAPON_TYPE_MH_SLICE | integer | No | |
WEAPON_TYPE_MH_STAB | integer | No | |
WEAPON_TYPE_ML_CHOP | integer | No | |
WEAPON_TYPE_ML_SLICE | integer | No | |
WEAPON_TYPE_MM_BASH | integer | No | |
WEAPON_TYPE_MM_CHOP | integer | No | |
WEAPON_TYPE_MM_SLICE | integer | No | |
WEAPON_TYPE_MM_STAB | integer | No | |
WEAPON_TYPE_NONE | integer | No | |
WEAPON_TYPE_RH_BASH | integer | No | |
WEAPON_TYPE_WH_BASH | integer | No | |
WEAPON_TYPE_WH_SLICE | integer | No | |
WEAPON_TYPE_WL_BASH | integer | No | |
WEAPON_TYPE_WL_SLICE | integer | No | |
WEAPON_TYPE_WL_STAB | integer | No | |
WEAPON_TYPE_WM_BASH | integer | No | |
WEAPON_TYPE_WM_SLICE | integer | No | |
WEAPON_TYPE_WM_STAB | integer | No | |
WeaponTypeDebugStr | string | Yes | |
WorldMaxX | real | No | |
WorldMaxY | real | No | |
ZeroDamageEvent | real | No |
//TESH.scrollpos=-1
//TESH.alwaysfold=0
library ARGB initializer init
//******************************************************************************
//*
//* ARGB 1.1
//* ====
//* For your color needs.
//*
//* An ARGB object is a by-value struct, this means that assigning copies the
//* contents of the struct and that you don't have to use .destroy(), the
//* downside is that you cannot assign its members (can't do set c.r= 123 )
//*
//* This library should have plenty of uses, for example, if your spell involves
//* some unit recoloring you can allow users to input the color in the config
//* section as 0xAARRGGBB and you can then use this to decode that stuff.
//*
//* You can also easily merge two colors and make fading effects using ARGB.mix
//*
//* There's ARGB.fromPlayer which gets an ARGB object containing the player's
//* color. Then you can use the previous utilities on it.
//*
//* The .str() instance method can recolor a string, and the recolorUnit method
//* will apply the ARGB on a unit
//*
//* For other uses, you can use the .red, .green, .blue and .alpha members to get
//* an ARGB object's color value (from 0 to 255).
//*
//* structs that have a recolor method that takes red,green,blue and alpha as 0.255
//* integers can implement the ARGBrecolor module to gain an ability to quickly
//* recolor using an ARGB object.
//*
//********************************************************************************
//=================================================================================
globals
private string array i2cc
endglobals
//this double naming stuff is beginning to make me insane, if only TriggerEvaluate() wasn't so slow...
struct ARGB extends array
static method create takes integer a, integer r, integer g, integer b returns ARGB
return ARGB(b + g*0x100 + r*0x10000 + a*0x1000000)
endmethod
static method fromPlayer takes player p returns ARGB
local playercolor pc=GetPlayerColor(p)
if(pc==PLAYER_COLOR_RED) then
return 0xFFFF0303
elseif(pc==PLAYER_COLOR_BLUE) then
return 0xFF0042FF
elseif(pc==PLAYER_COLOR_CYAN) then
return 0xFF1CB619
elseif(pc==PLAYER_COLOR_PURPLE) then
return 0xFF540081
elseif(pc==PLAYER_COLOR_YELLOW) then
return 0xFFFFFF01
elseif(pc==PLAYER_COLOR_ORANGE) then
return 0xFFFE8A0E
elseif(pc==PLAYER_COLOR_GREEN) then
return 0xFF20C000
elseif(pc==PLAYER_COLOR_PINK) then
return 0xFFE55BB0
elseif(pc==PLAYER_COLOR_LIGHT_GRAY) then
return 0xFF959697
elseif(pc==PLAYER_COLOR_LIGHT_BLUE) then
return 0xFF7EBFF1
elseif(pc==PLAYER_COLOR_AQUA) then
return 0xFF106246
elseif(pc==PLAYER_COLOR_BROWN) then
return 0xFF4E2A04
endif
return 0xFF111111
endmethod
method operator alpha takes nothing returns integer
if( integer(this) <0) then
return 0x80+(-(-integer(this)+0x80000000))/0x1000000
else
return (integer(this))/0x1000000
endif
endmethod
method operator alpha= takes integer na returns ARGB
local integer a
local integer r
local integer g
local integer b
local integer col=integer(this)
if (col<0) then
set col=-(-col+0x80000000)
set a=0x80+col/0x1000000
set col=col-(a-0x80)*0x1000000
else
set a=col/0x1000000
set col=col-a*0x1000000
endif
set r=col/0x10000
set col=col-r*0x10000
set g=col/0x100
set b=col-g*0x100
return ARGB(b + g*0x100 + r*0x10000 + na*0x1000000)
endmethod
method operator red takes nothing returns integer
local integer c=integer(this)*0x100
if(c<0) then
return 0x80+(-(-c+0x80000000))/0x1000000
else
return c/0x1000000
endif
endmethod
method operator red= takes integer nr returns ARGB
local integer a
local integer r
local integer g
local integer b
local integer col=integer(this)
if (col<0) then
set col=-(-col+0x80000000)
set a=0x80+col/0x1000000
set col=col-(a-0x80)*0x1000000
else
set a=col/0x1000000
set col=col-a*0x1000000
endif
set r=col/0x10000
set col=col-r*0x10000
set g=col/0x100
set b=col-g*0x100
return ARGB(b + g*0x100 + nr*0x10000 + a*0x1000000)
endmethod
method operator green takes nothing returns integer
local integer c=integer(this)*0x10000
if(c<0) then
return 0x80+(-(-c+0x80000000))/0x1000000
else
return c/0x1000000
endif
endmethod
method operator green= takes integer ng returns ARGB
local integer a
local integer r
local integer g
local integer b
local integer col=integer(this)
if (col<0) then
set col=-(-col+0x80000000)
set a=0x80+col/0x1000000
set col=col-(a-0x80)*0x1000000
else
set a=col/0x1000000
set col=col-a*0x1000000
endif
set r=col/0x10000
set col=col-r*0x10000
set g=col/0x100
set b=col-g*0x100
return ARGB(b + ng*0x100 + r*0x10000 + a*0x1000000)
endmethod
//=======================================================
//
//
method operator blue takes nothing returns integer
local integer c=integer(this)*0x1000000
if(c<0) then
return 0x80+(-(-c+0x80000000))/0x1000000
else
return c/0x1000000
endif
endmethod
method operator blue= takes integer nb returns ARGB
local integer a
local integer r
local integer g
local integer b
local integer col=integer(this)
if (col<0) then
set col=-(-col+0x80000000)
set a=0x80+col/0x1000000
set col=col-(a-0x80)*0x1000000
else
set a=col/0x1000000
set col=col-a*0x1000000
endif
set r=col/0x10000
set col=col-r*0x10000
set g=col/0x100
set b=col-g*0x100
return ARGB(nb + g*0x100 + r*0x10000 + a*0x1000000)
endmethod
//====================================================================
// Mixes two colors, s would be a number 0<=s<=1 that determines
// the weight given to color c2.
//
// mix(c1,c2,0) = c1
// mix(c1,c2,1) = c2
// mix(c1,c2,0.5) = Mixing the colors c1 and c2 in equal proportions.
//
static method mix takes ARGB c1, ARGB c2, real s returns ARGB
//widest function ever
return ARGB( R2I(c2.blue*s+c1.blue*(1-s)+0.5) + R2I(c2.green*s+c1.green*(1-s)+0.5)*0x100 + R2I(c2.red*s+c1.red*(1-s)+0.5)*0x10000 + R2I(c2.alpha*s+c1.alpha*(1-s)+0.5)*0x1000000)
endmethod
method str takes string s returns string
return "|c"+i2cc[.alpha]+i2cc[.red]+i2cc[.green]+i2cc[.blue]+s+"|r"
endmethod
method recolorUnit takes unit u returns nothing
local integer a
local integer r
local integer g
local integer b
local integer col=integer(this)
if (col<0) then
set col=-(-col+0x80000000)
set a=0x80+col/0x1000000
set col=col-(a-0x80)*0x1000000
else
set a=col/0x1000000
set col=col-a*0x1000000
endif
set r=col/0x10000
set col=col-r*0x10000
set g=col/0x100
set b=col-g*0x100
call SetUnitVertexColor(u,r,g,b,a)
endmethod
endstruct
module ARGBrecolor
method ARGBrecolor takes ARGB color returns nothing
local integer a
local integer r
local integer g
local integer b
local integer col=integer(this)
if (col<0) then
set col=-(-col+0x80000000)
set a=0x80+col/0x1000000
set col=col-(a-0x80)*0x1000000
else
set a=col/0x1000000
set col=col-a*0x1000000
endif
set r=col/0x10000
set col=col-r*0x10000
set g=col/0x100
set b=col-g*0x100
call this.recolor(r, g , b, a)
endmethod
endmodule
private function init takes nothing returns nothing
local integer i=0
// Don't run textmacros you don't own!
//! textmacro ARGB_CHAR takes int, chr
set i=0
loop
exitwhen i==16
set i2cc[$int$*16+i]="$chr$"+i2cc[$int$*16+i]
set i2cc[i*16+$int$]=i2cc[i*16+$int$]+"$chr$"
set i=i+1
endloop
//! endtextmacro
//! runtextmacro ARGB_CHAR( "0","0")
//! runtextmacro ARGB_CHAR( "1","1")
//! runtextmacro ARGB_CHAR( "2","2")
//! runtextmacro ARGB_CHAR( "3","3")
//! runtextmacro ARGB_CHAR( "4","4")
//! runtextmacro ARGB_CHAR( "5","5")
//! runtextmacro ARGB_CHAR( "6","6")
//! runtextmacro ARGB_CHAR( "7","7")
//! runtextmacro ARGB_CHAR( "8","8")
//! runtextmacro ARGB_CHAR( "9","9")
//! runtextmacro ARGB_CHAR("10","A")
//! runtextmacro ARGB_CHAR("11","B")
//! runtextmacro ARGB_CHAR("12","C")
//! runtextmacro ARGB_CHAR("13","D")
//! runtextmacro ARGB_CHAR("14","E")
//! runtextmacro ARGB_CHAR("15","F")
endfunction
endlibrary
//TESH.scrollpos=-1
//TESH.alwaysfold=0
//******************************************************************************
//*
//* CustomBar 1.0 (Initial Release)
//* ====
//* For your CustomBar needs. / Thanks Vex for this sentence <3
//*
//* This library is used to give a fully functional Bar System
//* that provides you interfaces for setting the bar state and
//* for letting you fade the bar, and even let you decide which
//* fade Condition to use.
//*
//* If a Bar is fading (out) and its condition becomes false, its
//* changing its fading (to in).
//*
//* You can also disable fading, if you want.
//*
//* PLEASE NOTE: MY FADING OF MY SYSTEM DOES !NOT! USE
//* WC3S FADE SYSTEM! IT SIMPLY CHANGES THE COLOR OF THE
//* TEXTTAG WITH LESS ALPHA! YOU CAN USE BOTH TOGETHER,
//* BUT I DOUBT ITS A GOOD IDEA!
//*
//*
//* by dhk_undead_lord / aka Anachron
//*
//******************************************************************************
library CustomBar initializer init requires ARGB
//=!#!#!#!#!#!#!#!#!#!#!#!#!#!#!#!#!#!#!#!#!#!#
//: Change anything below to what you need!
//=!#!#!#!#!#!#!#!#!#!#!#!#!#!#!#!#!#!#!#!#!#!#
//: The customizeable constants.
//: I hope the names are self-explaining.
//: (If not, check the CustomBar Documentation!
globals
//==-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
//: Texttag defaults.
//==-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
public constant string FADE_TYPE_IN = "IN"
public constant string FADE_TYPE_OUT = "OUT"
private constant real FADE_MIN = 0.
private constant real FADE_MAX = 1.00
private constant real TT_DEFAULT_AGE = 0.
private ARGB TT_DEFAULT_COLOR
private constant real TT_DEFAULT_FADEPOINT = 0.
private constant boolean TT_DEFAULT_FADES = true
private constant real TT_DEFAULT_FADE_STATE = FADE_MIN
private constant real TT_DEFAULT_FADE_STRENGH = 0.75
private constant string TT_DEFAULT_FADE_TYPE = FADE_TYPE_OUT
private constant boolean TT_DEFAULT_FADING = false
private constant texttag TT_DEFAULT_HANDLE = null
private constant real TT_DEFAULT_LIFESPAN = 0.
private constant boolean TT_DEFAULT_PERMANENT = true
private constant real TT_DEFAULT_OFFSETX = 0.
private constant real TT_DEFAULT_OFFSETY = 0.
private constant real TT_DEFAULT_OFFSETZ = 0.
private constant real TT_DEFAULT_POSX = 0.
private constant real TT_DEFAULT_POSY = 0.
private constant real TT_DEFAULT_POSZ = 0.
private constant real TT_DEFAULT_SIZE = 7.5 * 0.023 / 10
private constant boolean TT_DEFAULT_SUSPENDED = false
private constant string TT_DEFAULT_TEXT = ""
private constant real TT_DEFAULT_VELX = 0.
private constant real TT_DEFAULT_VELY = 0.
private constant boolean TT_DEFAULT_VISIBILITY = true
//==-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
//: TextBar defaults.
//==-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
private constant string TB_DEFAULT_CHAR = "I"
private constant integer TB_DEFAULT_CHAR_AMOUNT = 25
private constant string TB_DEFAULT_CHAR_EMPTY = " "
private ARGB TB_DEFAULT_CHAR_COLOR
private constant string TB_DEFAULT_FINISHEDBAR = ""
private ARGB TB_DEFAULT_LIMITER_COLOR
private constant string TB_DEFAULT_LIMITER_SYMBOL_LEFT = "["
private constant string TB_DEFAULT_LIMITER_SYMBOL_RIGHT = "]"
private constant boolean TB_DEFAULT_LIMITER_VISIBLE = true
private constant real TB_DEFAULT_PERCENTAGE = 100.
private constant integer TB_MAX_CHAR_AMOUNT = 100
//==-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
//: CustomBar defaults.
//==-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
private constant boolean CB_DEFAULT_LOCKED = true
private constant unit CB_DEFAULT_TARGET = null
private constant force CB_DEFAULT_SHOW_FORCE = bj_FORCE_ALL_PLAYERS
//==-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
//: TextBarStack defaults.
//==-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
private constant timer CBS_TIMER = CreateTimer()
private constant real CBS_TICK = 0.0375
//==-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
//: Information.
//==-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
//: For the ARGB declaration check the function init,
//: at the total bottom of this library.
endglobals
//: The interfaces for the system
public function interface iChange takes CustomBar cb returns nothing
public function interface iFade takes CustomBar cb returns boolean
public function interface iShow takes player owner, player cur returns boolean
//: Default functions for this system
public function defaultFadeCondition takes CustomBar cb returns boolean
return GetUnitState(cb.target, UNIT_STATE_LIFE) == 0.
endfunction
public function defaultShowCondition takes player owner, player cur returns boolean
return true
endfunction
//=!#!#!#!#!#!#!#!#!#!#!#!#!#!#!#!#!#!#!#!#!#!#
//: I don't recommend changing code below this!
//=!#!#!#!#!#!#!#!#!#!#!#!#!#!#!#!#!#!#!#!#!#!#
//==-=-=-=-=-=-=-
//: Textmacros
//==-=-=-=-=-=-=-
//! textmacro varOp takes name, fname, type, method
method operator $fname$ takes nothing returns $type$
return .$name$
endmethod
method operator $fname$= takes $type$ val returns nothing
set .$name$ = val
$method$
endmethod
//! endtextmacro
//==-=-=-=-=-=-=-
//: A struct which contains all
//: TextTag methods and members
private struct TextTag
//==-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//: TextTag members. Sorted by name.
//==-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
private real Age = TT_DEFAULT_AGE
private ARGB Color = TT_DEFAULT_COLOR
private boolean Fades = TT_DEFAULT_FADES
private real FadeState = TT_DEFAULT_FADE_STATE
private real FadeStrengh = TT_DEFAULT_FADE_STRENGH
private string FadeType = TT_DEFAULT_FADE_TYPE
private real Fadepoint = TT_DEFAULT_FADEPOINT
private boolean Fading = TT_DEFAULT_FADING
private iFade FadeCond = defaultFadeCondition
private real Lifespan = TT_DEFAULT_LIFESPAN
private texttag Handle = TT_DEFAULT_HANDLE
private boolean Permanent = TT_DEFAULT_PERMANENT
private real OffsetX = TT_DEFAULT_OFFSETX
private real OffsetY = TT_DEFAULT_OFFSETY
private real OffsetZ = TT_DEFAULT_OFFSETZ
private real PosX = TT_DEFAULT_POSX
private real PosY = TT_DEFAULT_POSY
private real PosZ = TT_DEFAULT_POSZ
private boolean Suspended = TT_DEFAULT_SUSPENDED
private string Text = TT_DEFAULT_TEXT
private real Size = TT_DEFAULT_SIZE
private real VelX = TT_DEFAULT_VELX
private real VelY = TT_DEFAULT_VELY
private boolean Visibility = TT_DEFAULT_VISIBILITY
//==-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//: TextTag methods.
//==-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
public static method create takes nothing returns thistype
local thistype this = thistype.allocate()
set .Handle = CreateTextTag()
call .updAge()
call .updColor()
call .updFadepoint()
call .updLifespan()
call .updPermanent()
call .updPos()
call .updSuspended()
call .updText()
call .updVelocity()
return this
endmethod
//==-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//: Update Methods.
//==-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
private method updAge takes nothing returns nothing
call SetTextTagAge(.Handle, .Age)
endmethod
private method updColor takes nothing returns nothing
if not .fading then
call SetTextTagColor(.Handle, .Color.red, .Color.green, .Color.blue, .Color.alpha)
endif
endmethod
private method updFadepoint takes nothing returns nothing
call SetTextTagFadepoint(.Handle, .Fadepoint)
endmethod
private method updLifespan takes nothing returns nothing
call SetTextTagLifespan(.Handle, .Lifespan)
endmethod
private method updPermanent takes nothing returns nothing
call SetTextTagPermanent(.Handle, .Permanent)
endmethod
private method updPos takes nothing returns nothing
call SetTextTagPos(.Handle, .PosX, .PosY, .PosZ)
endmethod
private method updSuspended takes nothing returns nothing
call SetTextTagSuspended(.Handle, .Suspended)
endmethod
private method updText takes nothing returns nothing
call SetTextTagText(.Handle, .Text, .Size)
endmethod
private method updVelocity takes nothing returns nothing
call SetTextTagVelocity(.Handle, .VelX, .VelY)
endmethod
//==-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//: Member setting / getting
//==-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//: Variables that should update the texttag
//==----------------------------------------
//! runtextmacro varOp("Age", "age", "real", "call .updAge()")
//! runtextmacro varOp("Color", "color", "ARGB", "call .updColor()")
//! runtextmacro varOp("Fadepoint", "fadepoint", "real", "call .updFadepoint()")
//! runtextmacro varOp("Lifespan", "lifespan", "real", "call .updLifespan()")
//! runtextmacro varOp("Permanent", "permanent", "boolean", "call .updPermanent()")
//! runtextmacro varOp("Suspended", "suspended", "boolean", "call .updSuspended()")
//! runtextmacro varOp("Visibility", "visiblity", "boolean", "")
//==--------------------------------------------
//: Variables that shouldn't update the texttag
//==--------------------------------------------
//! runtextmacro varOp("Fades", "fades", "boolean", "")
//! runtextmacro varOp("FadeCond", "fadeCond", "iFade", "")
//! runtextmacro varOp("FadeState", "fadeState", "real", "")
//! runtextmacro varOp("FadeStrengh", "fadeStrengh", "real", "")
//! runtextmacro varOp("FadeType", "fadeType", "string", "")
//! runtextmacro varOp("Fading", "fading", "boolean", "")
//! runtextmacro varOp("Handle", "handle", "texttag", "")
//! runtextmacro varOp("OffsetX", "offsetX", "real", "")
//! runtextmacro varOp("OffsetY", "offsetY", "real", "")
//! runtextmacro varOp("OffsetZ", "offsetZ", "real", "")
//==-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//: Advanced members and their settings.
//==-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
public method pos takes real x, real y, real z returns nothing
set .PosX = x + .OffsetX
set .PosY = y + .OffsetY
set .PosZ = z + .OffsetZ
call .updPos()
endmethod
public method posUnit takes unit u, real z returns nothing
call .pos(GetUnitX(u), GetUnitY(u), z)
endmethod
public method text takes string t, real s returns nothing
set .Text = t
set .Size = s * 0.023 / 10
call .updText()
endmethod
method operator textText takes nothing returns string
return .Text
endmethod
method operator textText= takes string t returns nothing
set .Text = t
call .updText()
endmethod
method operator textSize takes nothing returns real
return .Size
endmethod
method operator textSize= takes real s returns nothing
set .Size = s
call .updText()
endmethod
public method velocity takes real s, real angle returns nothing
local real vel = s * 0.071 / 128
set .VelX = vel * Cos(angle * bj_DEGTORAD)
set .VelY = vel * Sin(angle * bj_DEGTORAD)
call .updVelocity()
endmethod
//==-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//: Advanced Methods
//==-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
public method showPlayer takes player p returns nothing
if GetLocalPlayer() == p then
call SetTextTagVisibility(.Handle, .Visibility)
endif
endmethod
public method fade takes CustomBar cb returns nothing
local boolean c = false
local boolean r = false
local ARGB color = 0
if .Fades then
set c = .fadeCond.evaluate(cb)
if c and .FadeState < FADE_MAX then
set .FadeType = FADE_TYPE_OUT
set .Fading = true
set r = true
elseif not(c) and .FadeState > FADE_MIN then
set .FadeType = FADE_TYPE_IN
set .Fading = true
set r = true
endif
if .Fading then
//: Lets calculate the fading!
if .FadeType == FADE_TYPE_OUT then
set .FadeState = .FadeState + .FadeStrengh * CBS_TICK
if .FadeState >= FADE_MAX then
set .FadeState = FADE_MAX
set .Fading = false
endif
elseif .FadeType == FADE_TYPE_IN then
set .FadeState = .FadeState - .FadeStrengh * CBS_TICK
if .FadeState <= FADE_MIN then
set .FadeState = FADE_MIN
set .Fading = false
endif
endif
if .Fading then
call SetTextTagAge(.Handle, .FadeState)
endif
endif
if r then
if .Fading then
call SetTextTagAge(.Handle, .FadeState)
call SetTextTagFadepoint(.Handle, 0.)
call SetTextTagLifespan(.Handle, FADE_MAX + CBS_TICK * 2)
call SetTextTagPermanent(.Handle, false)
call cb.showCB()
else
call SetTextTagAge(.Handle, .Age)
call SetTextTagLifespan(.Handle, .Lifespan)
call SetTextTagFadepoint(.Handle, .Fadepoint)
call SetTextTagPermanent(.Handle, .Permanent)
if .FadeType == FADE_TYPE_OUT then
call SetTextTagVisibility(.Handle, false)
endif
endif
endif
endif
endmethod
private method onDestroy takes nothing returns nothing
call DestroyTextTag(.Handle)
endmethod
endstruct
//: A struct which contains all
//: Textbar methods and members
private struct TextBar
//==-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//: TextBar members. Sorted by name.
//==-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
private string Char = TB_DEFAULT_CHAR
private integer CharAmount = TB_DEFAULT_CHAR_AMOUNT
private ARGB CharColor = TB_DEFAULT_CHAR_COLOR
private ARGB array CharColors [TB_MAX_CHAR_AMOUNT]
private string CharEmpty = TB_DEFAULT_CHAR_EMPTY
private string FinishedBar = TB_DEFAULT_FINISHEDBAR
private ARGB LimiterColor = TB_DEFAULT_LIMITER_COLOR
private string LimiterSymbolLeft = TB_DEFAULT_LIMITER_SYMBOL_LEFT
private string LimiterSymbolRight = TB_DEFAULT_LIMITER_SYMBOL_RIGHT
private boolean LimiterVisible = TB_DEFAULT_LIMITER_VISIBLE
private real Percentage = TB_DEFAULT_PERCENTAGE
//==-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//: TextBar methods
//==-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
public static method create takes nothing returns thistype
local thistype this = thistype.allocate()
return this
endmethod
//==-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//: Member setting / getting
//==-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//: Variables that should'nt update the texttag
//==--------------------------------------------
//! runtextmacro varOp("Char", "char", "string", "")
//! runtextmacro varOp("CharAmount", "charAmount", "integer", "")
//! runtextmacro varOp("CharEmpty", "charEmpty", "string", "")
//! runtextmacro varOp("FinishedBar", "finishedBar", "string", "")
//! runtextmacro varOp("LimiterColor", "limiterColor", "ARGB", "")
//! runtextmacro varOp("LimiterSymbolLeft", "limiterSymbolLeft", "string", "")
//! runtextmacro varOp("LimiterSymbolRight", "limiterSymbolRight", "string", "")
//! runtextmacro varOp("LimiterVisible", "limiterVisible", "boolean", "")
//! runtextmacro varOp("Percentage", "percentage", "real", "")
//==-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//: Advanced Methods
//==-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
public method addGradient takes ARGB left, ARGB right, integer start, integer end returns nothing
local integer i = start - 1
local boolean e = false
local real s = 0.
local string test = ""
local real m = 100 / .CharAmount
set m = m / 100
loop
exitwhen e
if i < end then
set s = i * m
set test = "i :" + I2S(i) + "\n" + " 1 / " + I2S(.CharAmount) + " = " + R2S(s)
set .CharColors[i] = ARGB.mix(left, right, s)
set i = i + 1
else
set e = true
endif
endloop
endmethod
public method generateBar takes nothing returns nothing
local integer i = 0
local string char = ""
set .FinishedBar = ""
loop
exitwhen i >= .CharAmount
if i < (.Percentage / 100) * .CharAmount then
if .CharColors[i] != 0 then
set char = .CharColors[i].str(.Char)
else
set char = .CharColor.str(.Char)
endif
else
set char = .CharEmpty
endif
set .FinishedBar = .FinishedBar + char
set i = i + 1
endloop
if .LimiterVisible then
set .FinishedBar = .LimiterColor.str(.LimiterSymbolLeft) + .FinishedBar + .LimiterColor.str(.LimiterSymbolRight)
endif
endmethod
endstruct
//: A struct which contains all
//: Custom methods and members
struct CustomBar
//==-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//: CustomBar members
//==-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
private unit Target = CB_DEFAULT_TARGET
private boolean Locked = CB_DEFAULT_LOCKED
private iChange UpdateMethod
private iShow ShowCond = defaultShowCondition
private force ShowForce = CB_DEFAULT_SHOW_FORCE
private TextBar TxtBar
private TextTag TxtTag
//==-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//: CustomBar methods
//==-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
public static method create takes unit target, iChange updMeth returns thistype
local thistype this = thistype.allocate()
set .Target = target
set .TxtBar = TextBar.create()
set .TxtTag = TextTag.create()
set .UpdateMethod = updMeth
call CBStack.addCustomBar(this)
return this
endmethod
//==-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//: Member setting / getting
//==-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//: Variables that should'nt update the texttag
//==--------------------------------------------
//! runtextmacro varOp("Locked", "locked", "boolean", "")
//! runtextmacro varOp("ShowCond", "showCond", "iShow", "")
//! runtextmacro varOp("ShowForce", "showForce", "force", "")
//! runtextmacro varOp("Target", "target", "unit", "")
//! runtextmacro varOp("TxtTag", "txtTag", "TextTag", "")
//! runtextmacro varOp("TxtBar", "txtBar", "TextBar", "")
//! runtextmacro varOp("UpdateMethod", "updateMethod", "iChange", "")
//==-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//: Advanced methods.
//==-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
public method Show takes force f, iShow c returns nothing
set .ShowForce = f
set .ShowCond = c
endmethod
public method checkP takes player p returns nothing
if .ShowCond.evaluate(GetOwningPlayer(.Target), p) then
call .TxtTag.showPlayer(p)
endif
endmethod
public method showCB takes nothing returns nothing
local integer i = 0
local boolean e = false
loop
exitwhen e
if i < 15 then
if IsPlayerInForce(Player(i), .ShowForce) then
call .checkP(Player(i))
endif
set i = i + 1
else
set e = true
endif
endloop
endmethod
public method updateBar takes nothing returns nothing
if .Locked then
call .TxtTag.posUnit(.Target, 0.)
endif
call .UpdateMethod.execute(this)
call .TxtBar.generateBar()
set .TxtTag.textText = .TxtBar.finishedBar
endmethod
public method update takes nothing returns nothing
call .updateBar()
call .TxtTag.fade(this)
endmethod
private method onDestroy takes nothing returns nothing
call CBStack.remCustomBar(this)
call .TxtTag.destroy()
call .TxtBar.destroy()
endmethod
endstruct
//: A struct which is used to
//: update CustomBars
struct CBStack
static CustomBar array CBars
static integer index = 0
public static method addCustomBar takes CustomBar cb returns nothing
set thistype.CBars[thistype.index] = cb
set thistype.index = thistype.index + 1
if thistype.index == 1 then
call TimerStart(CBS_TIMER, CBS_TICK, true, function thistype.updateBars)
endif
endmethod
public static method remCBByIndex takes integer i returns nothing
set thistype.CBars[i] = thistype.CBars[thistype.index]
set thistype.index = thistype.index - 1
if thistype.index <= 0 then
call PauseTimer(CBS_TIMER)
endif
endmethod
public static method remCustomBar takes CustomBar cb returns nothing
local integer i = 0
local boolean e = false
loop
exitwhen e
if i < thistype.index then
if thistype.CBars[i] == cb then
call .remCBByIndex(i)
set e = true
else
set i = i + 1
endif
else
set e = true
endif
endloop
endmethod
public static method updateBars takes nothing returns nothing
local integer i = 0
local boolean e = false
loop
exitwhen e
if i < thistype.index then
if thistype.CBars[i] == 0 then
call thistype.remCBByIndex(i)
set i = i - 1
else
call thistype.CBars[i].update()
endif
set i = i + 1
else
set e = true
endif
endloop
endmethod
endstruct
//: The init method, initialisates
//: a few default objects
private function init takes nothing returns nothing
set TB_DEFAULT_CHAR_COLOR = ARGB.create(255, 255, 255, 255)
set TB_DEFAULT_LIMITER_COLOR = ARGB.create(255, 255, 255, 255)
set TT_DEFAULT_COLOR = ARGB.create(255, 255, 255, 255)
endfunction
endlibrary
//TESH.scrollpos=-1
//TESH.alwaysfold=0
//******************************************************************************
// =-=-=-=-=-=-=-
// THE CONSTANTS
// =-=-=-=-=-=-=-
/*
//: Sets the identifier
//: for the fade type in.
FADE_TYPE_IN = "IN"
//: Sets the identifier
//: for the fade type out.
FADE_TYPE_OUT = "OUT"
//: Sets the boundaries
//: for the fading
FADE_MIN = 0.
FADE_MAX = 1.00
//: Sets the default age
//: of the texttag.
TT_DEFAULT_AGE = 0.
//: Sets the default color
//: of the texttag.
//: (INITIALIZED IN INIT METHOD)
TT_DEFAULT_COLOR
//: Sets the default fadepoint
//: of the texttag.
TT_DEFAULT_FADEPOINT = 0.
//: Sets the default boolean
//: of the texttag if its able
//: to fade.
TT_DEFAULT_FADES = true
//: Sets the default fadestate
//: of the texttag.
TT_DEFAULT_FADE_STATE = FADE_MIN
//: Sets the default fadestrengh
//: of the texttag.
TT_DEFAULT_FADE_STRENGH = 0.75
//: Sets the default fade type
//: of the texttag.
TT_DEFAULT_FADE_TYPE = FADE_TYPE_OUT
//: Sets the default boolean
//: of the texttag if its fading
//: or not.
TT_DEFAULT_FADING = false
//: Sets the default handle
//: of the texttag.
TT_DEFAULT_HANDLE = null
//: Sets the default lifespan time
//: of the texttag.
TT_DEFAULT_LIFESPAN = 0.
//: Sets the default boolean
//: of the texttag if its permanent.
TT_DEFAULT_PERMANENT = true
//: Sets the default offsets
//: of the texttag.
TT_DEFAULT_OFFSETX = 0.
TT_DEFAULT_OFFSETY = 0.
TT_DEFAULT_OFFSETZ = 0.
//: Sets the default location
//: of the texttag.
TT_DEFAULT_POSX = 0.
TT_DEFAULT_POSY = 0.
TT_DEFAULT_POSZ = 0.
//: Sets the default textsize
//: of the texttag.
//: Note: This is the wc3 size,
//: not the normal word one,
//: so we recalculate it with
//: the formula.
TT_DEFAULT_SIZE = 7.5 * 0.023 / 10
//: Sets the default boolean
//: of the texttag if its suspended
//: or not.
TT_DEFAULT_SUSPENDED = false
//: Sets the default text
//: of the texttag.
TT_DEFAULT_TEXT = ""
//: Sets the default x velocity
//: of the texttag.
TT_DEFAULT_VELX = 0.
//: Sets the default y velocity
//: of the texttag.
TT_DEFAULT_VELY = 0.
//: Sets the default boolean
//: of the texttag if its shown
//: or not.
TT_DEFAULT_VISIBILITY = true
*/
/*
//: Sets the default char symbol
//: of the TextBar.
TB_DEFAULT_CHAR = "||"
//: Sets the default char amount
//: of the TextBar.
TB_DEFAULT_CHAR_AMOUNT = 25
//: Sets the default empty char symbol
//: of the TextBar.
TB_DEFAULT_CHAR_EMPTY = " "
//: Sets the default char color
//: of the TextBar.
//: (INITIALIZED IN INIT METHOD)
TB_DEFAULT_CHAR_COLOR
//: Sets the default bar string
//: of the TextBar.
TB_DEFAULT_FINISHEDBAR = ""
//: Sets the default limiter color
//: of the TextBar.
//: (INITIALIZED IN INIT METHOD)
TB_DEFAULT_LIMITER_COLOR
//: Sets the default limiter symbol
//: on the left of the TextBar.
TB_DEFAULT_LIMITER_SYMBOL_LEFT = "["
//: Sets the default limiter symbol
//: on the right of the TextBar.
TB_DEFAULT_LIMITER_SYMBOL_RIGHT = "]"
//: Sets the default boolean if limiters
//: are shown in the TextBar.
TB_DEFAULT_LIMITER_VISIBLE = true
//: Sets the default percentage
//: of the TextBar.
TB_DEFAULT_PERCENTAGE = 100.
//: Sets the maximum amount of chars
//: in a TextBar.
TB_MAX_CHAR_AMOUNT = 100
*/
/*
//: Sets the default boolean
//: of the CustomBar if its locked
//: to the unit or not.
CB_DEFAULT_LOCKED = true
//: Sets the default target
//: of the CustomBar.
CB_DEFAULT_TARGET = null
//: Sets the default force
//: of the CustomBar to which its
//: displayed.
CB_DEFAULT_SHOW_FORCE = bj_FORCE_ALL_PLAYERS
*/
/*
//: Sets the timer for the CBStack.
CBS_TIMER = CreateTimer()
//: Sets the update interval
//: for timer above.
CBS_TICK = 0.0375
*/
//******************************************************************************
//===========================================================================
function UnitEventInAction takes nothing returns nothing
if (not udg_UnitInAction[udg_UDex]) then
set udg_UnitInAction[udg_UDex] = true
set udg_UnitInActionEvent = 0.00
set udg_UnitInActionEvent = 1.00
set udg_UnitInActionEvent = 0.00
endif
endfunction
function UnitEventInaction takes nothing returns nothing
local integer i = bj_MAX_PLAYER_SLOTS
local unit u = udg_UDexUnits[udg_UDex]
local player p
local player p2
local boolean b
if udg_IsUnitAlive[udg_UDex] and udg_CargoTransportUnit[udg_UDex] != null then
set p2 = GetOwningPlayer(udg_CargoTransportUnit[udg_UDex])
endif
if (udg_UnitInAction[udg_UDex]) then
set udg_UnitInAction[udg_UDex] = false
set udg_UnitInActionEvent = 0.00
set udg_UnitInActionEvent = 2.00
set udg_UnitInActionEvent = 0.00
endif
// Check if selected by some players
loop
set i = i - 1
set p = Player(i)
if IsUnitInGroup(u, udg_PlayerSelection[i]) then
call GroupRemoveUnit(udg_PlayerSelection[i], u)
set udg_PDex = i + 1
set udg_PlayerSelectionEvent = 0.00
set udg_PlayerSelectionEvent = 2.00
set udg_PlayerSelectionEvent = 0.00
// Check if selected unit is loaded in a controlable transporter
set b = (p == p2) or GetPlayerAlliance(p2, p, ALLIANCE_SHARED_CONTROL) or GetPlayerAlliance(p2, p, ALLIANCE_SHARED_ADVANCED_CONTROL)
if (b) then
call GroupAddUnit(udg_PlayerSelection[i], udg_CargoTransportUnit[udg_UDex])
set udg_UDex = GetUnitUserData(udg_CargoTransportUnit[udg_UDex])
set udg_PDex = i + 1
set udg_PlayerSelectionEvent = 0.00
set udg_PlayerSelectionEvent = 1.00
set udg_PlayerSelectionEvent = 0.00
endif
endif
exitwhen i == 0
endloop
endfunction
function UnitEventOnSelect takes nothing returns boolean
local unit u = GetTriggerUnit()
local player p = GetTriggerPlayer()
local integer i = GetPlayerId(p)
local boolean b = IsUnitInGroup(u, udg_PlayerSelection[i])
if (not b) then
call GroupAddUnit(udg_PlayerSelection[i], u)
set udg_UDex = GetUnitUserData(u)
set udg_PDex = i + 1
set udg_PlayerSelectionEvent = 0.00
set udg_PlayerSelectionEvent = 1.00
set udg_PlayerSelectionEvent = 0.00
endif
return false
endfunction
function UnitEventOnDeselect takes nothing returns boolean
local unit u = GetTriggerUnit()
local player p = GetTriggerPlayer()
local integer i = GetPlayerId(p)
local boolean b = IsUnitInGroup(u, udg_PlayerSelection[i])
if (b) then
call GroupRemoveUnit(udg_PlayerSelection[i], u)
set udg_UDex = GetUnitUserData(u)
set udg_PDex = i + 1
set udg_PlayerSelectionEvent = 0.00
set udg_PlayerSelectionEvent = 2.00
set udg_PlayerSelectionEvent = 0.00
endif
return false
endfunction
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 id = 0
local unit u
loop
set id = udg_CheckDeathList[id]
exitwhen id == 0
set udg_UDex = id
set u = udg_UDexUnits[id]
if udg_IsUnitNew[id] then
//The unit was just created.
set udg_IsUnitNew[id] = false
set udg_UnitIndexEvent = 1.50 //New event requested by SpellBound to detect when unit fully enters scope.
set udg_UnitIndexEvent = 0.00
elseif udg_IsUnitTransforming[id] then
//Added 21 July 2017 to fix the issue re-adding this ability in the same instant
set udg_UnitTypeEvent = 0.00
set udg_UnitTypeEvent = 1.00
set udg_UnitTypeOf[id] = GetUnitTypeId(u) //Set this afterward as otherwise the user won't know what the previous unittype was.
set udg_IsUnitTransforming[id] = false
call UnitAddAbility(u, udg_DetectTransformAbility)
elseif udg_IsUnitAlive[id] then
//The unit has started reincarnating (=is a tombstone).
set udg_IsUnitReincarnating[id] = true
set udg_IsUnitAlive[id] = false
set udg_DeathEvent = 0.50
set udg_DeathEvent = 0.00
call UnitEventInaction()
elseif GetUnitTypeId(u) != 0 and not IsUnitType(u, UNIT_TYPE_DEAD) then //with vJass, could just use UnitAlive instead of both of these values.
//Moved this code to fire after a 0 second timer instead.
set udg_IsUnitAlive[id] = true
set udg_DeathEvent = 2.00
set udg_DeathEvent = 0.00
call UnitEventInAction()
set udg_IsUnitReincarnating[id] = false
endif
set udg_CheckDeathInList[id] = false
endloop
set u = null
//Empty the list
set udg_CheckDeathList[0] = 0
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 not udg_CheckDeathInList[i] then
set udg_CheckDeathList[i] = udg_CheckDeathList[0]
set udg_CheckDeathList[0] = i
set udg_CheckDeathInList[i] = true
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
call UnitEventInAction()
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
call UnitEventInaction()
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 an inaction event for UDex
call UnitEventInaction()
//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
call UnitEventCheckAfterProxy(i) //modified 22 Oct 2022 to ensure the unit is fully revived before firing the event.
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
call UnitEventInaction()
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
call UnitEventInAction()
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
call UnitEventInaction()
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_CheckDeathInList[i] = false
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
// Fire a in action event
call UnitEventInAction()
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 trigger select = CreateTrigger()
local trigger deselect = 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] = 0
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))
call TriggerAddCondition(select, Filter(function UnitEventOnSelect))
call TriggerAddCondition(deselect, Filter(function UnitEventOnDeselect))
loop
set i = i - 1
set p = Player(i)
set udg_PlayerSelection[i] = CreateGroup()
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 TriggerRegisterPlayerUnitEvent( select, p, EVENT_PLAYER_UNIT_SELECTED, null)
call TriggerRegisterPlayerUnitEvent( deselect, p, EVENT_PLAYER_UNIT_DESELECTED, null)
call GroupEnumUnitsOfPlayer(bj_lastCreatedGroup, p, enterB)
exitwhen i == 0
endloop
// TODO: just in case, a 2nd loop for units already selected by the init struct of another library ?
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
/*
vJass Damage Engine 5.A.0.0
This update enables compatibility with AttackIndexer.
*/
/*
JASS API:
struct Damage extends array
readonly static unit source // udg_DamageEventSource in real-time
readonly static unit target // udg_DamageEventTarget in real-time
static real amount // udg_DamageEventAmount in real-time
readonly unit sourceUnit // udg_DamageEventSource by index
readonly unit targetUnit // udg_DamageEventTarget by index
real damage // udg_DamageEventAmount by index
readonly real prevAmt // udg_DamageEventPrevAmt by index
attacktype attackType // udg_DamageEventAttackT by index
damagetype damageType // udg_DamageEventDamageT by index
weapontype weaponType // udg_DamageEventWeaponT by index
integer userType // udg_DamageEventType by index
readonly boolean isAttack // udg_IsDamageAttack by index
readonly boolean isCode // udg_IsDamageCode by index
readonly boolean isMelee // udg_IsDamageMelee by index
readonly boolean isRanged // udg_IsDamageRanged by index
readonly boolean isSpell // udg_IsDamageSpell by index
real armorPierced // udg_DamageEventArmorPierced by index
integer armorType // udg_DamageEventArmorT by index
integer defenseType // udg_DamageEventDefenseT by index
readonly integer eFilter
Set to false to disable the damage event triggers or to true to reverse that:
static boolean operator enabled
Same arguments as "UnitDamageTarget" but has the benefit of being performance-friendly during recursive events.
Will automatically cause the damage to be registered as Code damage.
static method apply takes
unit source,
unit target,
real amount,
boolean isAttack,
boolean isRanged,
attacktype at,
damagetype dt,
weapontype wt
returns Damage
A simplified version of the above function that autofills each boolean, attacktype and weapontype.
static method applySpell takes
unit src,
unit tgt,
real amt,
damagetype dt
returns Damage
A different variation of the above which autofills the "isAttack" boolean
and populates damagetype as DAMAGE_TYPE_NORMAL.
static method applyAttack takes
unit src,
unit tgt,
real amt,
boolean ranged,
attacktype at,
weapontype wt
returns Damage
struct DamageTrigger extends array
method operator filter= takes integer filter returns nothing
// Apply primary filters such as DamageEngine_FILTER_MELEE/RANGED/SPELL which are based off of limitop handles to enable easier access for GUI folks
// Full filter list:
- integer DamageEngine_FILTER_ATTACK
- integer DamageEngine_FILTER_MELEE
- integer DamageEngine_FILTER_OTHER
- integer DamageEngine_FILTER_RANGED
- integer DamageEngine_FILTER_SPELL
- integer DamageEngine_FILTER_CODE
boolean configured //set to True after configuring any filters listed below.
Apply custom filters after setting any desired udg_DamageFilter variables (for GUI).
Alternatively, vJass users can set these instead. Just be mindful to set the variable
"configured" to true after settings these:
unit source
unit target
integer sourceType
integer targetType
integer sourceBuff
integer targetBuff
real damageMin
integer attackType
integer damageType
integer userType
method configure takes nothing returns nothing
The string in the aruments below requires the following API:
"" for standard damage event
"Modifier(or Mod if you prefer)/After/Lethal/AOE" for the others
static method registerTrigger takes
trigger whichTrig,
string var,
real value
returns nothing
static method unregister takes
trigger whichTrig,
string eventName,
real value,
boolean reset
returns boolean
static method getIndex takes
trigger fromTrigger,
string eventName,
real value
returns integer
If you already have the index of the trigger you want to unregister:
method unregisterByIndex takes
boolean reset
returns boolean
Converts a code argument to a trigger, while checking if the same code had already been registered before.
Use it via DamageTrigger[function MyCallbackFunction]
static method operator [] takes
code callback
returns trigger
The accepted strings here use the same criteria as DamageTrigger.getIndex/registerTrigger/unregister:
function TriggerRegisterDamageEngineEx takes
trigger whichTrig,
string eventName,
real value,
integer opId
returns nothing
function TriggerRegisterDamageEngine takes
trigger whichTrig,
string eventName,
real value
returns nothing
function RegisterDamageEngineEx takes
code callback,
string eventName,
real value,
integer opId
returns nothing
function RegisterDamageEngine takes
code callback,
string eventName,
real value
returns nothing
*/
//===========================================================================
library DamageEngine requires optional AttackIndexer /*, TriggerDebugAutomation*/
globals
private constant boolean USE_GUI = true //If you don't use any of the GUI events, set to false to slightly improve performance
private constant boolean USE_SCALING = USE_GUI //If you don't need or want to use DamageScalingUser/WC3 then set this to false
private constant boolean USE_EXTRA = true //If you don't use DamageEventLevel or SourceDamageEvent, set this to false
private constant boolean USE_ARMOR_MOD = true //If you do not modify nor detect armor/defense, set this to false
private constant boolean USE_MELEE_RANGE = true //If you do not detect melee nor ranged damage, set this to false
private constant boolean USE_LETHAL = true //If you do not use LethalDamageEvent nor negative damage (explosive) types, set this to false
/*
When manually-enabled recursion is enabled via DamageEngine_inception,
the engine will never go deeper than MAX_RECURSIVE_TOLERANCE:
*/
private constant integer MAX_RECURSIVE_TOLERANCE = 16
public constant integer TYPE_CODE = 1 //Must be the same as udg_DamageTypeCode, or 0 if you prefer to disable the automatic flag.
public constant integer TYPE_PURE = 2 //Must be the same as udg_DamageTypePure
private constant real DEATH_VAL = 0.405 //In case M$ ever changes this, it'll be a quick fix here.
private timer async = null
private boolean timerStarted = false
//Values to track the original pre-spirit Link/defensive damage values
private Damage lastInstance = 0
private boolean isNotNativeRecursiveDamage = true
private boolean waitingForDamageEventToRun = false
private boolean array attacksImmune
private boolean array damagesImmune
//Primary triggers used to handle all damage events.
private trigger damagingTrigger = null
private trigger damagedTrigger = null
private trigger recursiveTrigger = null //Catches, stores recursive events
/*
These variables coincide with Blizzard's "limitop" type definitions
so as to enable GUI users with some performance perks - however,
these optimizations need to be tested
*/
public constant integer FILTER_ATTACK = 0 //LESS_THAN
public constant integer FILTER_MELEE = 1 //LESS_THAN_OR_EQUAL
public constant integer FILTER_OTHER = 2 //EQUAL
public constant integer FILTER_RANGED = 3 //GREATER_THAN_OR_EQUAL
public constant integer FILTER_SPELL = 4 //GREATER_THAN
public constant integer FILTER_CODE = 5 //NOT_EQUAL
public constant integer FILTER_MAX = 6
private integer eventFilter = FILTER_OTHER
/*
When true, it allows your trigger to go recursively up to
MAX_RECURSIVE_TOLERANCE (if needed). It must be set before dealing damage.
*/
public boolean inception = false
private boolean callbacksInProgress = false
private integer recursiveCallbackDepth = 0
private group recursionSources = null
private group recursionTargets = null
private boolean recursiveCallbaksInProgress = false
private boolean nativeEventsCompleted = false
private boolean atLeastOneLethalDamageEventRegistered = false
// Struct members made private to this library.
private keyword run
private keyword trigFrozen
private keyword ownRecursiveDepth
private keyword manualRecursionRequested
endglobals
native UnitAlive takes unit u returns boolean
//GUI Vars:
/*
Retained from 3.8 and prior:
----------------------------
unit udg_DamageEventSource
unit udg_DamageEventTarget
unit udg_EnhancedDamageTarget
group udg_DamageEventAOEGroup
integer udg_DamageEventAOE
integer udg_DamageEventLevel
real udg_DamageModifierEvent
real udg_DamageEvent
real udg_AfterDamageEvent
real udg_DamageEventAmount
real udg_DamageEventPrevAmt
real udg_AOEDamageEvent
boolean udg_DamageEventOverride
boolean udg_NextDamageType
boolean udg_DamageEventType
boolean udg_IsDamageSpell
//Added in 5.0:
boolean udg_IsDamageMelee
boolean udg_IsDamageRanged
unit udg_AOEDamageSource
real udg_LethalDamageEvent
real udg_LethalDamageHP
real udg_DamageScalingWC3
integer udg_DamageEventAttackT
integer udg_DamageEventDamageT
integer udg_DamageEventWeaponT
//Added in 5.1:
boolean udg_IsDamageCode
//Added in 5.2:
integer udg_DamageEventArmorT
integer udg_DamageEventDefenseT
//Addded in 5.3:
real DamageEventArmorPierced
real udg_DamageScalingUser
//Added in 5.4.2 to allow GUI users to re-issue the exact same attack and damage type at the attacker.
attacktype array udg_CONVERTED_ATTACK_TYPE
damagetype array udg_CONVERTED_DAMAGE_TYPE
//Added after Reforged introduced the new native BlzGetDamageIsAttack
boolean udg_IsDamageAttack
//Added in 5.6 to give GUI users control over the "IsDamageAttack", "IsDamageRanged" and "DamageEventWeaponT" field
boolean udg_NextDamageIsAttack //The first boolean value in the UnitDamageTarget native
boolean udg_NextDamageIsMelee //Flag the damage classification as melee
boolean udg_NextDamageIsRanged //The second boolean value in the UnitDamageTarget native
integer udg_NextDamageWeaponT //Allows control over damage sound effect
//Added in 5.7 to enable efficient, built-in filtering (see the below "checkConfig" method - I recommend commenting-out anything you don't need in your map)
integer udg_DamageFilterAttackT
integer udg_DamageFilterDamageT //filter for a specific attack/damage type
unit udg_DamageFilterSource
unit udg_DamageFilterTarget //filter for a specific source/target
integer udg_DamageFilterSourceT
integer udg_DamageFilterTargetT //unit type of source/target
integer udg_DamageFilterType //which DamageEventType was used
integer udg_DamageFilterSourceB
integer udg_DamageFilterTargetB //if source/target has a buff
real udg_DamageFilterMinAmount //only allow a minimum damage threshold
//Added in 5.8:
boolean udg_RemoveDamageEvent //Allow GUI users to more fully unregister a damage event trigger. Can only be used from within a damage event (of any kind).
integer udg_DamageFilterSourceA
integer udg_DamageFilterTargetA //Check if a source or target have a specific ability (will overwrite any source or target buff check, I need to use this because GUI differentiates ability ID and buff ID)
integer udg_DamageFilterSourceI
integer udg_DamageFilterTargetI //Check if a source or target have a specific type of item
integer udg_DamageFilterSourceC
integer udg_DamageFilterTargetC //Classification of source/target (e.g. hero, treant, ward)
//Added in 5.9
real udg_SourceDamageEvent //Like AOEDamageEvent, fires each time the source unit has finished dealing damage, but doesn't care if the damage hit multiple units.
real udg_PreDamageEvent //Like DamageModifierEvent 3.99 or less, except can be any real value.
real udg_ArmorDamageEvent //Like DamageModifierEvent 4.00 or more, except can be any real value.
real udg_OnDamageEvent //Like DamageEvent equal to 1.00 or some non-zero/non-2 value, except can be any real value.
real udg_ZeroDamageEvent //Like DamageEvent equal to 0.00 or 2.00, except can be any real value.
*/
struct DamageTrigger extends array
static method checkItem takes unit u, integer id returns boolean
local integer i
if IsUnitType(u, UNIT_TYPE_HERO) then
set i = UnitInventorySize(u)
loop
exitwhen i <= 0
set i = i - 1
if GetItemTypeId(UnitItemInSlot(u, i)) == id then
return true
endif
endloop
endif
return false
endmethod
/*
Map makers should probably not use these filters,
unless someone tests performance to see
if such an ugly hack is even worth it.
*/
method checkConfig takes nothing returns boolean
//call BJDebugMsg("Checking configuration")
if this.sourceType != 0 and GetUnitTypeId(udg_DamageEventSource) != this.sourceType then
elseif this.targetType != 0 and GetUnitTypeId(udg_DamageEventTarget) != this.targetType then
elseif this.sourceBuff != 0 and GetUnitAbilityLevel(udg_DamageEventSource, this.sourceBuff) == 0 then
elseif this.targetBuff != 0 and GetUnitAbilityLevel(udg_DamageEventTarget, this.targetBuff) == 0 then
elseif this.failChance > 0.00 and GetRandomReal(0.00, 1.00) <= this.failChance then
elseif this.userType != 0 and udg_DamageEventType != this.userType then
elseif this.source != null and this.source != udg_DamageEventSource then
elseif this.target != null and this.target != udg_DamageEventTarget then
elseif this.attackType >= 0 and this.attackType != udg_DamageEventAttackT then
elseif this.damageType >= 0 and this.damageType != udg_DamageEventDamageT then
elseif this.sourceItem != 0 and not .checkItem(udg_DamageEventSource, this.sourceItem) then
elseif this.targetItem != 0 and not .checkItem(udg_DamageEventTarget, this.targetItem) then
elseif this.sourceClass >= 0 and not IsUnitType(udg_DamageEventSource, ConvertUnitType(this.sourceClass)) then
elseif this.targetClass >= 0 and not IsUnitType(udg_DamageEventTarget, ConvertUnitType(this.targetClass)) then
elseif udg_DamageEventAmount >= this.damageMin then
//call BJDebugMsg("Configuration passed")
return true
endif
//call BJDebugMsg("Checking failed")
return false
endmethod
//The below variables are to be treated as constant
readonly static thistype MOD = 1
readonly static thistype SHIELD = 4
readonly static thistype DAMAGE = 5
readonly static thistype ZERO = 6
readonly static thistype AFTER = 7
readonly static thistype LETHAL = 8
readonly static thistype AOE = 9
private static integer count = 9
static thistype lastRegistered = 0
private static thistype array trigIndexStack
static thistype eventIndex = 0
static boolean array filters
readonly string eventStr
readonly real weight
boolean usingGUI
private thistype next
private trigger rootTrig
//The below variables are to be treated as private
boolean trigFrozen //Whether the trigger is currently disabled due to recursion
integer ownRecursiveDepth //How deep the user recursion currently is.
boolean manualRecursionRequested //Added in 5.4.2 to simplify the inception variable for very complex DamageEvent triggers.
//configuration variables:
boolean configured
unit source
unit target
integer sourceType
integer targetType
integer sourceBuff
integer targetBuff
integer sourceItem
integer targetItem
integer sourceClass
integer targetClass
real damageMin
real failChance
integer attackType
integer damageType
integer userType
// getter:
method operator runChance takes nothing returns real
return 1.00 - this.failChance
endmethod
// setter:
method operator runChance= takes real chance returns nothing
set this.failChance = 1.00 - chance
endmethod
method configure takes nothing returns nothing
set this.attackType = udg_DamageFilterAttackT
set this.damageType = udg_DamageFilterDamageT
set this.source = udg_DamageFilterSource
set this.target = udg_DamageFilterTarget
set this.sourceType = udg_DamageFilterSourceT
set this.targetType = udg_DamageFilterTargetT
set this.sourceItem = udg_DamageFilterSourceI
set this.targetItem = udg_DamageFilterTargetI
set this.sourceClass = udg_DamageFilterSourceC
set this.targetClass = udg_DamageFilterTargetC
set this.userType = udg_DamageFilterType
set this.damageMin = udg_DamageFilterMinAmount
set this.failChance = 1.00 - (udg_DamageFilterRunChance - udg_DamageFilterFailChance)
if udg_DamageFilterSourceA > 0 then
set this.sourceBuff = udg_DamageFilterSourceA
set udg_DamageFilterSourceA = 0
else
set this.sourceBuff = udg_DamageFilterSourceB
endif
if udg_DamageFilterTargetA > 0 then
set this.targetBuff = udg_DamageFilterTargetA
set udg_DamageFilterTargetA = 0
else
set this.targetBuff = udg_DamageFilterTargetB
endif
set udg_DamageFilterSource = null
set udg_DamageFilterTarget = null
//These handles can have a valid value of 0, so we need to distinguish them.
set udg_DamageFilterAttackT = -1
set udg_DamageFilterDamageT = -1
set udg_DamageFilterSourceC = -1
set udg_DamageFilterTargetC = -1
set udg_DamageFilterSourceT = 0
set udg_DamageFilterTargetT = 0
set udg_DamageFilterType = 0
set udg_DamageFilterSourceB = 0
set udg_DamageFilterTargetB = 0
set udg_DamageFilterSourceI = 0
set udg_DamageFilterTargetI = 0
set udg_DamageFilterMinAmount = 0.00
set udg_DamageFilterFailChance = 0.00
set udg_DamageFilterRunChance = 1.00
set this.configured = true
endmethod
static method setGUIFromStruct takes boolean full returns nothing
set udg_DamageEventAmount = Damage.index.damage
set udg_DamageEventAttackT = GetHandleId(Damage.index.attackType)
set udg_DamageEventDamageT = GetHandleId(Damage.index.damageType)
set udg_DamageEventWeaponT = GetHandleId(Damage.index.weaponType)
set udg_DamageEventType = Damage.index.userType
static if USE_ARMOR_MOD then
set udg_DamageEventArmorPierced = Damage.index.armorPierced
set udg_DamageEventArmorT = Damage.index.armorType
set udg_DamageEventDefenseT = Damage.index.defenseType
endif
if full then
set udg_DamageEventSource = Damage.index.sourceUnit
set udg_DamageEventTarget = Damage.index.targetUnit
set udg_DamageEventPrevAmt = Damage.index.prevAmt
set udg_IsDamageAttack = Damage.index.isAttack
set udg_IsDamageCode = Damage.index.isCode
set udg_IsDamageSpell = Damage.index.isSpell
//! runtextmacro optional ATTACK_INDEXER_GUI_VARS()
static if USE_MELEE_RANGE then
set udg_IsDamageMelee = Damage.index.isMelee
set udg_IsDamageRanged = Damage.index.isRanged
endif
endif
endmethod
static method setStructFromGUI takes nothing returns nothing
set Damage.index.damage = udg_DamageEventAmount
set Damage.index.attackType = ConvertAttackType(udg_DamageEventAttackT)
set Damage.index.damageType = ConvertDamageType(udg_DamageEventDamageT)
set Damage.index.weaponType = ConvertWeaponType(udg_DamageEventWeaponT)
set Damage.index.userType = udg_DamageEventType
static if USE_ARMOR_MOD then
set Damage.index.armorPierced = udg_DamageEventArmorPierced
set Damage.index.armorType = udg_DamageEventArmorT
set Damage.index.defenseType = udg_DamageEventDefenseT
endif
endmethod
static method getVerboseStr takes string eventName returns string
if eventName == "Modifier" or eventName == "Mod" then
return "udg_DamageModifierEvent"
endif
return "udg_" + eventName + "DamageEvent"
endmethod
private static method getStrIndex takes string var, real lbs returns thistype
local integer root = R2I(lbs)
if (var == "udg_DamageModifierEvent" and root < 4) or var == "udg_PreDamageEvent" then
set root = MOD
elseif var == "udg_DamageModifierEvent" or var == "udg_ArmorDamageEvent" then
set root = SHIELD
elseif (var == "udg_DamageEvent" and root == 2 or root == 0) or var == "udg_ZeroDamageEvent" then
set root = ZERO
elseif var == "udg_DamageEvent" or var == "udg_OnDamageEvent" then
set root = DAMAGE
elseif var == "udg_AfterDamageEvent" then
set root = AFTER
elseif var == "udg_LethalDamageEvent" then
set root = LETHAL
elseif var == "udg_AOEDamageEvent" or var == "udg_SourceDamageEvent" then
set root = AOE
else
set root = 0
//! runtextmacro optional DAMAGE_EVENT_REG_PLUGIN_GDD()
//! runtextmacro optional DAMAGE_EVENT_REG_PLUGIN_PDD()
//! runtextmacro optional DAMAGE_EVENT_REG_PLUGIN_01()
//! runtextmacro optional DAMAGE_EVENT_REG_PLUGIN_02()
//! runtextmacro optional DAMAGE_EVENT_REG_PLUGIN_03()
//! runtextmacro optional DAMAGE_EVENT_REG_PLUGIN_04()
//! runtextmacro optional DAMAGE_EVENT_REG_PLUGIN_05()
endif
return root
endmethod
private method toggleAllFilters takes boolean flag returns nothing
set filters[this + FILTER_ATTACK] = flag
set filters[this + FILTER_MELEE] = flag
set filters[this + FILTER_OTHER] = flag
set filters[this + FILTER_RANGED] = flag
set filters[this + FILTER_SPELL] = flag
set filters[this + FILTER_CODE] = flag
endmethod
method operator filter= takes integer opId returns nothing
set this = this * FILTER_MAX
if opId == FILTER_OTHER then
call this.toggleAllFilters(true)
else
if opId == FILTER_ATTACK then
set filters[this + FILTER_ATTACK] = true
set filters[this + FILTER_MELEE] = true
set filters[this + FILTER_RANGED] = true
else
set filters[this + opId] = true
endif
endif
endmethod
static method registerVerbose takes /*
*/ trigger whichTrig, /*
*/ string var, /*
*/ real lbs, /*
*/ boolean GUI, /*
*/ integer filt /*
*/ returns thistype
local thistype index = getStrIndex(var, lbs)
local thistype i = 0
local thistype id = 0
if index == 0 then
return 0
elseif lastRegistered.rootTrig == whichTrig and lastRegistered.usingGUI then
//allows GUI to register multiple different types of Damage filters to the same trigger
set filters[lastRegistered*FILTER_MAX + filt] = true
return 0
endif
set atLeastOneLethalDamageEventRegistered = /*
*/ atLeastOneLethalDamageEventRegistered or index == LETHAL
if trigIndexStack[0] == 0 then
set count = count + 1 //List runs from index 10 and up
set id = count
else
set id = trigIndexStack[0]
set trigIndexStack[0] = trigIndexStack[id]
endif
set lastRegistered = id
set id.filter = filt
set id.rootTrig = whichTrig
set id.usingGUI = GUI
set id.weight = lbs
set id.eventStr = var
//Next 2 lines added to fix a bug when using manual vJass configuration,
//discovered and solved by lolreported
set id.attackType = -1
set id.damageType = -1
//they will probably bug out with class types as well, so I should add them, just in case:
set id.sourceClass = -1
set id.targetClass = -1
loop
set i = index.next
exitwhen i == 0 or lbs < i.weight
set index = i
endloop
set index.next = id
set id.next = i
//call BJDebugMsg("Registered " + I2S(id) + " to " + I2S(index) + " and before " + I2S(i))
return lastRegistered
endmethod
static method registerTrigger takes trigger t, string var, real lbs returns thistype
return registerVerbose(t, DamageTrigger.getVerboseStr(var), lbs, false, FILTER_OTHER)
endmethod
private static thistype prev = 0
static method getIndex takes /*
*/ trigger t, /*
*/ string eventName, /*
*/ real lbs /*
*/ returns thistype
local thistype index = getStrIndex(getVerboseStr(eventName), lbs)
loop
set prev = index
set index = index.next
exitwhen index == 0 or index.rootTrig == t
endloop
return index
endmethod
method unregisterByIndex takes boolean reset returns boolean
if this == 0 then
return false
endif
set prev.next = this.next
set trigIndexStack[this] = trigIndexStack[0]
set trigIndexStack[0] = this
if reset then
call this.configure()
set this.configured = false
call thistype(this*FILTER_MAX).toggleAllFilters(false)
endif
return true
endmethod
static method unregister takes /*
*/ trigger t, /*
*/ string eventName, /*
*/ real lbs, /*
*/ boolean reset /*
*/ returns boolean
return getIndex(t, eventName, lbs).unregisterByIndex(reset)
endmethod
method run takes nothing returns nothing
local integer cat = this
local Damage d = Damage.index
static if USE_GUI then
local boolean structUnset = false
local boolean guiUnset = false
local boolean mod = cat <= DAMAGE
endif
if callbacksInProgress then
return
endif
set callbacksInProgress = true
call DisableTrigger(damagingTrigger)
call DisableTrigger(damagedTrigger)
call EnableTrigger(recursiveTrigger)
//call BJDebugMsg("Start of event running")
loop
set this = this.next
exitwhen this == 0
exitwhen cat == MOD and (udg_DamageEventOverride or udg_DamageEventType == TYPE_PURE)
exitwhen cat == SHIELD and udg_DamageEventAmount <= 0.00
static if USE_LETHAL then
exitwhen cat == LETHAL and udg_LethalDamageHP > DEATH_VAL
endif
set eventIndex = this
if (not this.trigFrozen) and /*
*/ filters[this*FILTER_MAX + d.eFilter] and /*
*/ IsTriggerEnabled(this.rootTrig) and /*
*/ ((not this.configured) or (this.checkConfig())) and /*
*/ (cat != AOE or udg_DamageEventAOE > 1 or this.eventStr == "udg_SourceDamageEvent") /*
*/ then
static if USE_GUI then
if mod then
if this.usingGUI then
if guiUnset then
set guiUnset = false
call setGUIFromStruct(false)
endif
//! runtextmacro optional DAMAGE_EVENT_FILTER_PLUGIN_PDD()
elseif structUnset then
set structUnset = false
call setStructFromGUI()
endif
endif
endif
//! runtextmacro optional DAMAGE_EVENT_FILTER_PLUGIN_01()
//! runtextmacro optional DAMAGE_EVENT_FILTER_PLUGIN_02()
//! runtextmacro optional DAMAGE_EVENT_FILTER_PLUGIN_03()
//! runtextmacro optional DAMAGE_EVENT_FILTER_PLUGIN_04()
//! runtextmacro optional DAMAGE_EVENT_FILTER_PLUGIN_05()
//JASS users who do not use actions can modify the below block to just evaluate.
//It should not make any perceptable difference in terms of performance.
if TriggerEvaluate(this.rootTrig) then
call TriggerExecute(this.rootTrig)
endif
//! runtextmacro optional DAMAGE_EVENT_MOD_PLUGIN_01()
//! runtextmacro optional DAMAGE_EVENT_MOD_PLUGIN_02()
//! runtextmacro optional DAMAGE_EVENT_MOD_PLUGIN_03()
//! runtextmacro optional DAMAGE_EVENT_MOD_PLUGIN_04()
//! runtextmacro optional DAMAGE_EVENT_MOD_PLUGIN_05()
static if USE_GUI then
if mod then
if this.usingGUI then
//! runtextmacro optional DAMAGE_EVENT_MOD_PLUGIN_PDD()
if cat != MOD then
set d.damage = udg_DamageEventAmount
else
set structUnset = true
endif
elseif cat != MOD then
set udg_DamageEventAmount = d.damage
else
set guiUnset = true
endif
endif
if udg_RemoveDamageEvent then
set udg_RemoveDamageEvent = false
call this.unregisterByIndex(true)
endif
endif
endif
endloop
static if USE_GUI then
if structUnset then
call setStructFromGUI()
endif
if guiUnset then
call setGUIFromStruct(false)
endif
else
call setGUIFromStruct(false)
endif
//call BJDebugMsg("End of event running")
call DisableTrigger(recursiveTrigger)
call EnableTrigger(damagingTrigger)
call EnableTrigger(damagedTrigger)
set callbacksInProgress = false
endmethod
/*
Used by RegisterDamageEngineEx to create triggers behind-the-scenes,
allowing the user to simply pass the function they want to execute.
*/
static trigger array autoTriggers
static boolexpr array autoFuncs
static integer autoN = 0
static method operator [] takes code callback returns trigger
local integer i = 0
local boolexpr b = Filter(callback)
loop
if i == autoN then
set autoTriggers[i] = CreateTrigger()
set autoFuncs[i] = b
call TriggerAddCondition(autoTriggers[i], b)
//call TriggerDebugAutomation_TriggerIsReady(autoTriggers[i], "autoTriggers_" + I2S(i))
exitwhen true
endif
set i = i + 1
exitwhen b == autoFuncs[i]
endloop
return autoTriggers[i]
endmethod
endstruct
//! runtextmacro optional DAMAGE_EVENT_USER_STRUCT_PLUGIN_01()
//! runtextmacro optional DAMAGE_EVENT_USER_STRUCT_PLUGIN_02()
//! runtextmacro optional DAMAGE_EVENT_USER_STRUCT_PLUGIN_03()
//! runtextmacro optional DAMAGE_EVENT_USER_STRUCT_PLUGIN_04()
//! runtextmacro optional DAMAGE_EVENT_USER_STRUCT_PLUGIN_05()
struct Damage extends array
readonly unit sourceUnit
readonly unit targetUnit
real damage
readonly real prevAmt
attacktype attackType
damagetype damageType
weapontype weaponType
integer userType
readonly boolean isAttack
readonly boolean isCode
readonly boolean isSpell
static if USE_MELEE_RANGE then
readonly boolean isMelee //stores udg_IsDamageMelee
endif
readonly boolean isRanged //stores udg_IsDamageRanged
readonly integer eFilter //stores the previous eventFilter variable
static if USE_ARMOR_MOD then
real armorPierced //stores udg_DamageEventArmorPierced
integer armorType //stores udg_DamageEventArmorT
integer defenseType //stores udg_DamageEventDefenseT
endif
readonly static Damage index = 0
private static Damage damageStack = 0
private static Damage prepped = 0
private static integer count = 0 //The number of currently-running queued or sequential damage instances
private Damage stackRef
private DamageTrigger recursiveTrig
private integer prevArmorT
private integer prevDefenseT
static method operator source takes nothing returns unit
return udg_DamageEventSource
endmethod
static method operator target takes nothing returns unit
return udg_DamageEventTarget
endmethod
static method operator amount takes nothing returns real
return Damage.index.damage
endmethod
static method operator amount= takes real r returns nothing
set Damage.index.damage = r
endmethod
static if USE_ARMOR_MOD then
private method setArmor takes boolean reset returns nothing
local real pierce
local integer at
local integer dt
if reset then
set pierce = udg_DamageEventArmorPierced
set at = Damage.index.prevArmorT
set dt = Damage.index.prevDefenseT
set udg_DamageEventArmorPierced = 0.00
set this.armorPierced = 0.00
else
set pierce = -udg_DamageEventArmorPierced
set at = udg_DamageEventArmorT
set dt = udg_DamageEventDefenseT
endif
if not (pierce == 0.00) then //Changed from != to not == due to bug reported by BLOKKADE
call BlzSetUnitArmor(udg_DamageEventTarget, BlzGetUnitArmor(udg_DamageEventTarget) + pierce)
endif
if Damage.index.prevArmorT != udg_DamageEventArmorT then
call BlzSetUnitIntegerField(udg_DamageEventTarget, UNIT_IF_ARMOR_TYPE, at)
endif
if Damage.index.prevDefenseT != udg_DamageEventDefenseT then
call BlzSetUnitIntegerField(udg_DamageEventTarget, UNIT_IF_DEFENSE_TYPE, dt)
endif
endmethod
endif
static if USE_EXTRA then
private static method onAOEEnd takes nothing returns nothing
call DamageTrigger.AOE.run()
set udg_DamageEventAOE = 1
set udg_DamageEventLevel = 1
set udg_EnhancedDamageTarget = null
set udg_AOEDamageSource = null
call GroupClear(udg_DamageEventAOEGroup)
endmethod
endif
private static method afterDamage takes nothing returns nothing
if udg_DamageEventDamageT != 0 and not (udg_DamageEventPrevAmt == 0.00) then
call DamageTrigger.AFTER.run()
set udg_DamageEventDamageT = 0
set udg_DamageEventPrevAmt = 0.00
endif
endmethod
private method runDamagingEvents takes boolean natural returns boolean
static if USE_ARMOR_MOD then
set this.armorType = BlzGetUnitIntegerField(this.targetUnit, UNIT_IF_ARMOR_TYPE)
set this.defenseType = BlzGetUnitIntegerField(this.targetUnit, UNIT_IF_DEFENSE_TYPE)
set this.prevArmorT = this.armorType
set this.prevDefenseT = this.defenseType
set this.armorPierced = 0.00
endif
set Damage.index = this
call DamageTrigger.setGUIFromStruct(true)
call GroupAddUnit(recursionSources, udg_DamageEventSource)
call GroupAddUnit(recursionTargets, udg_DamageEventTarget)
//! runtextmacro optional DAMAGE_EVENT_PRE_VARS_PLUGIN_01()
//! runtextmacro optional DAMAGE_EVENT_PRE_VARS_PLUGIN_02()
//! runtextmacro optional DAMAGE_EVENT_PRE_VARS_PLUGIN_03()
//! runtextmacro optional DAMAGE_EVENT_PRE_VARS_PLUGIN_04()
//! runtextmacro optional DAMAGE_EVENT_PRE_VARS_PLUGIN_05()
// Using not == instead of !=; the idea is to eliminate floating point bugs when two numbers are very close to 0,
// because JASS uses a less-strict comparison for checking if a number is equal than when it is unequal.
if not (udg_DamageEventAmount == 0.00) then
set udg_DamageEventOverride = udg_DamageEventDamageT == 0
call DamageTrigger.MOD.run()
static if not USE_GUI then
call DamageTrigger.setGUIFromStruct(false)
endif
if natural then
call BlzSetEventAttackType(this.attackType)
call BlzSetEventDamageType(this.damageType)
call BlzSetEventWeaponType(this.weaponType)
call BlzSetEventDamage(udg_DamageEventAmount)
endif
static if USE_ARMOR_MOD then
call this.setArmor(false)
endif
return false
endif
return true //return value is based on whether the event is a 0 damage event (true) or not (false).
endmethod
private static method unfreeze takes nothing returns nothing
local Damage i = damageStack
loop
exitwhen i == 0
set i = i - 1
set i.stackRef.recursiveTrig.trigFrozen = false
set i.stackRef.recursiveTrig.ownRecursiveDepth = 0
endloop
call EnableTrigger(damagingTrigger)
call EnableTrigger(damagedTrigger)
set recursiveCallbaksInProgress = false
set damageStack = 0
set prepped = 0
set callbacksInProgress = false
set recursiveCallbackDepth = 0
call GroupClear(recursionSources)
call GroupClear(recursionTargets)
//call BJDebugMsg("Cleared up the groups")
endmethod
static method runAfterDamageEvents takes nothing returns nothing
local Damage i = 0
local integer exit
if nativeEventsCompleted then
set nativeEventsCompleted = false
call afterDamage()
endif
if isNotNativeRecursiveDamage and not recursiveCallbaksInProgress then
if damageStack != 0 then
set recursiveCallbaksInProgress = true
loop
/*
Use two loops. The outer loop handles all normal event
execution, while the inner loop intelligently handles
recursive execution (when it's used).
*/
set recursiveCallbackDepth = recursiveCallbackDepth + 1
set exit = damageStack
loop
set prepped = i.stackRef
if UnitAlive(prepped.targetUnit) then
// We don't need to trigger `damagingTrigger` itself, so just call its handler directly.
call prepped.runDamagingEvents(false)
if prepped.damage > 0.00 then
call DisableTrigger(damagingTrigger) // Disallow `damagingTrigger` because we only want `damageTrigger` to run.
call EnableTrigger(damagedTrigger) // Re-enable `damagedTrigger` in case the user forgot to do so.
set waitingForDamageEventToRun = true
call UnitDamageTarget( /*
*/ prepped.sourceUnit, /*
*/ prepped.targetUnit, /*
*/ prepped.damage, /*
*/ prepped.isAttack, /*
*/ prepped.isRanged, /*
*/ prepped.attackType, /*
*/ prepped.damageType, /*
*/ prepped.weaponType /*
*/ )
else
if udg_DamageEventDamageT != 0 then
//No native events run at all in this case
call DamageTrigger.DAMAGE.run()
endif
if prepped.damage < 0.00 then
/*
No need for BlzSetEventDamage/UnitDamageTarget here,
because we can safely adjust the unit's life instead.
*/
call SetWidgetLife( /*
*/ prepped.targetUnit, /*
*/ GetWidgetLife(prepped.targetUnit) - prepped.damage /*
*/ )
endif
static if USE_ARMOR_MOD then
call prepped.setArmor(true)
endif
endif
call afterDamage()
endif
set i = i + 1
exitwhen i == exit
endloop
exitwhen i == damageStack
endloop
endif
call unfreeze()
endif
endmethod
private static method failsafeClear takes nothing returns nothing
static if USE_ARMOR_MOD then
call Damage.index.setArmor(true)
endif
set isNotNativeRecursiveDamage = true
set recursiveCallbaksInProgress = false
set waitingForDamageEventToRun = false
if udg_DamageEventDamageT != 0 then
call DamageTrigger.DAMAGE.run()
set nativeEventsCompleted = true
endif
call runAfterDamageEvents()
endmethod
static method operator enabled= takes boolean b returns nothing
if b then
if callbacksInProgress then
call EnableTrigger(recursiveTrigger)
else
call EnableTrigger(damagingTrigger)
call EnableTrigger(damagedTrigger)
endif
else
if callbacksInProgress then
call DisableTrigger(recursiveTrigger)
else
call DisableTrigger(damagingTrigger)
call DisableTrigger(damagedTrigger)
endif
endif
endmethod
static method operator enabled takes nothing returns boolean
return IsTriggerEnabled(damagingTrigger)
endmethod
private static boolean threadCompleted = false
private static method asyncCallbackSafeCallback takes nothing returns nothing
if waitingForDamageEventToRun then
/*
This means that WarCraft 3 didn't run the DAMAGED event despite running the DAMAGING event.
*/
call failsafeClear()
else
set isNotNativeRecursiveDamage = true
set recursiveCallbaksInProgress = false
call runAfterDamageEvents()
endif
static if USE_EXTRA then
call onAOEEnd()
endif
set threadCompleted = true
endmethod
private static method asyncCallback takes nothing returns nothing
set callbacksInProgress = false
set Damage.enabled = true
/*
Open a new thread in case of a thread crash during callback.
*/
call ForForce(bj_FORCE_PLAYER[0], function thistype.asyncCallbackSafeCallback)
if not threadCompleted then
//call BJDebugMsg("DamageEngine issue: thread crashed!")
call unfreeze()
else
set threadCompleted = false
endif
set Damage.count = 0
set Damage.index = 0
set timerStarted = false
//call BJDebugMsg("Timer wrapped up")
endmethod
private method addRecursive takes nothing returns nothing
local DamageTrigger currentIndex
if not (this.damage == 0.00) then
set currentIndex = DamageTrigger.eventIndex
set this.recursiveTrig = currentIndex
if not this.isCode then
/*
If the recursive damage trigger is executed, this can only
mean that the user has manually dealt damage from a trigger.
Hence flag the damage as being 'code' if they didn't already
manually do this.
*/
set this.isCode = true
set this.userType = TYPE_CODE
endif
set inception = inception or /*
*/ currentIndex.manualRecursionRequested
if recursiveCallbaksInProgress and /*
*/ IsUnitInGroup(this.sourceUnit, recursionSources) and /*
*/ IsUnitInGroup(this.targetUnit, recursionTargets) /*
*/ then
if not inception then
set currentIndex.trigFrozen = true
elseif not currentIndex.trigFrozen then
set currentIndex.manualRecursionRequested = true
if currentIndex.ownRecursiveDepth < recursiveCallbackDepth then
set currentIndex.ownRecursiveDepth = /*
*/ currentIndex.ownRecursiveDepth + 1
if currentIndex.ownRecursiveDepth >= MAX_RECURSIVE_TOLERANCE then
set currentIndex.trigFrozen = true
endif
endif
endif
endif
// push the reference to the top of the damage stack.
set damageStack.stackRef = this
set damageStack = damageStack + 1
//call BJDebugMsg("damageStack: " + I2S(damageStack) + " ownRecursiveDepth: " + I2S(currentIndex.ownRecursiveDepth) + " recursiveCallbackDepth: " + I2S(recursiveCallbackDepth))
endif
set inception = false
endmethod
private static method clearNexts takes nothing returns nothing
set udg_NextDamageIsAttack = false
set udg_NextDamageType = 0
set udg_NextDamageWeaponT = 0
static if USE_MELEE_RANGE then
set udg_NextDamageIsMelee = false
set udg_NextDamageIsRanged = false
endif
endmethod
static method create takes /*
*/ unit src, /*
*/ unit tgt, /*
*/ real amt, /*
*/ boolean isAttack, /*
*/ attacktype at, /*
*/ damagetype dt, /*
*/ weapontype wt /*
*/ returns Damage
local Damage d = Damage.count + 1
set Damage.count = d
set d.sourceUnit = src
set d.targetUnit = tgt
set d.damage = amt
set d.prevAmt = amt
set d.damageType = dt
set d.attackType = at
set d.weaponType = wt
set d.isAttack = udg_NextDamageIsAttack or isAttack
set d.isSpell = d.attackType == null and not d.isAttack
return d
endmethod
private static method createFromEvent takes nothing returns Damage
local Damage d = thistype.create( /*
*/ GetEventDamageSource(), /*
*/ GetTriggerUnit(), /*
*/ GetEventDamage(), /*
*/ BlzGetEventIsAttack(), /*
*/ BlzGetEventAttackType(), /*
*/ BlzGetEventDamageType(), /*
*/ BlzGetEventWeaponType() /*
*/ )
set d.isCode = udg_NextDamageType != 0 or /*
*/ udg_NextDamageIsAttack or /*
*/ udg_NextDamageIsRanged or /*
*/ udg_NextDamageIsMelee or /*
*/ d.damageType == DAMAGE_TYPE_MIND or /*
*/ udg_NextDamageWeaponT != 0 or /*
*/ (d.damageType == DAMAGE_TYPE_UNKNOWN and not (d.damage == 0.00))
if d.isCode then
if udg_NextDamageType != 0 then
set d.userType = udg_NextDamageType
else
set d.userType = TYPE_CODE
endif
static if USE_MELEE_RANGE then
set d.isMelee = udg_NextDamageIsMelee
set d.isRanged = udg_NextDamageIsRanged
endif
set d.eFilter = FILTER_CODE
if udg_NextDamageWeaponT != 0 then
set d.weaponType = ConvertWeaponType(udg_NextDamageWeaponT)
set udg_NextDamageWeaponT = 0
endif
else
set d.userType = 0
if d.damageType == DAMAGE_TYPE_NORMAL and d.isAttack then
// Added in version 5.A in order to allow an optional external
// Attack Indexer system to reset the event weapon type to normal.
//! runtextmacro optional ATTACK_INDEXER_ADJUSTMENTS()
static if USE_MELEE_RANGE then
set d.isMelee = IsUnitType(d.sourceUnit, UNIT_TYPE_MELEE_ATTACKER)
set d.isRanged = IsUnitType(d.sourceUnit, UNIT_TYPE_RANGED_ATTACKER)
if d.isMelee and d.isRanged then
// Melee units always play a sound when damaging in WC3,
// so this is an easy check.
set d.isMelee = d.weaponType != null
// In the case where a unit is both ranged and melee,
// the ranged attack plays no sound.
set d.isRanged = not d.isMelee
endif
if d.isMelee then
set d.eFilter = FILTER_MELEE
elseif d.isRanged then
set d.eFilter = FILTER_RANGED
else
set d.eFilter = FILTER_ATTACK
endif
else
set d.eFilter = FILTER_ATTACK
endif
else
if d.isSpell then
set d.eFilter = FILTER_SPELL
else
set d.eFilter = FILTER_OTHER
endif
static if USE_MELEE_RANGE then
// Spells are neither melee nor ranged.
set d.isMelee = false
set d.isRanged = false
endif
endif
endif
call clearNexts()
return d
endmethod
private static method onRecursiveDamageCallback takes nothing returns boolean
local Damage d = Damage.createFromEvent()
call d.addRecursive()
call BlzSetEventDamage(0.00)
return false
endmethod
private static method onDamagingCallback takes nothing returns boolean
local Damage d = Damage.createFromEvent()
//call BJDebugMsg("Pre-damage event running for " + GetUnitName(GetTriggerUnit()))
if timerStarted then
if waitingForDamageEventToRun then
//WarCraft 3 didn't run the DAMAGED event despite running the DAMAGING event.
if d.damageType == DAMAGE_TYPE_SPIRIT_LINK or /*
*/ d.damageType == DAMAGE_TYPE_DEFENSIVE or /*
*/ d.damageType == DAMAGE_TYPE_PLANT /*
*/ then
set waitingForDamageEventToRun = false
set lastInstance = Damage.index
set isNotNativeRecursiveDamage = false
else
call failsafeClear() //Not an overlapping event - just wrap it up
endif
else
call runAfterDamageEvents() //wrap up any previous damage index
endif
static if USE_EXTRA then
if d.sourceUnit != udg_AOEDamageSource then
call onAOEEnd()
set udg_AOEDamageSource = d.sourceUnit
set udg_EnhancedDamageTarget = d.targetUnit
elseif d.targetUnit == udg_EnhancedDamageTarget then
set udg_DamageEventLevel= udg_DamageEventLevel + 1
elseif not IsUnitInGroup(d.targetUnit, udg_DamageEventAOEGroup) then
set udg_DamageEventAOE = udg_DamageEventAOE + 1
endif
endif
else
call TimerStart(async, 0.00, false, function Damage.asyncCallback)
set timerStarted = true
static if USE_EXTRA then
set udg_AOEDamageSource = d.sourceUnit
set udg_EnhancedDamageTarget= d.targetUnit
endif
endif
static if USE_EXTRA then
call GroupAddUnit(udg_DamageEventAOEGroup, d.targetUnit)
endif
if d.runDamagingEvents(true) then
call DamageTrigger.ZERO.run()
set isNotNativeRecursiveDamage = true
call runAfterDamageEvents()
endif
set waitingForDamageEventToRun = lastInstance == 0 or /*
*/ attacksImmune[udg_DamageEventAttackT] or /*
*/ damagesImmune[udg_DamageEventDamageT] or /*
*/ not IsUnitType(udg_DamageEventTarget, UNIT_TYPE_MAGIC_IMMUNE)
return false
endmethod
private static method onDamagedCallback takes nothing returns boolean
local real r = GetEventDamage()
local Damage d = Damage.index
//call BJDebugMsg("Second damage event running for " + GetUnitName(GetTriggerUnit()))
if prepped > 0 then
set prepped = 0
elseif callbacksInProgress or d.prevAmt == 0.00 then
return false
elseif waitingForDamageEventToRun then
set waitingForDamageEventToRun = false
else
/*
This should only happen for native recursive WarCraft 3 damage
such as Spirit Link, Thorns Aura, or Spiked Carapace / Barricades.
*/
call afterDamage()
set Damage.index = lastInstance
set lastInstance = 0
set d = Damage.index
/*
Since the native recursive damage has now wrapped up, we can resume
handling events as normal at this point. This means that the original
target that the DAMAGING event was triggered for is now finally getting
its DAMAGED event.
*/
set isNotNativeRecursiveDamage = true
call DamageTrigger.setGUIFromStruct(true)
endif
static if USE_ARMOR_MOD then
call d.setArmor(true)
endif
static if USE_SCALING then
if not (udg_DamageEventAmount == 0.00) and not (r == 0.00) then
set udg_DamageScalingWC3 = r / udg_DamageEventAmount
elseif udg_DamageEventAmount > 0.00 then
set udg_DamageScalingWC3 = 0.00
else
set udg_DamageScalingWC3 = 1.00
if udg_DamageEventPrevAmt == 0.00 then
set udg_DamageScalingUser = 0.00
else
set udg_DamageScalingUser = /*
*/ udg_DamageEventAmount / udg_DamageEventPrevAmt
endif
endif
endif
set udg_DamageEventAmount = r
set d.damage = r
//! runtextmacro optional DAMAGE_EVENT_VARS_PLUGIN_GDD()
//! runtextmacro optional DAMAGE_EVENT_VARS_PLUGIN_PDD()
//! runtextmacro optional DAMAGE_EVENT_VARS_PLUGIN_01()
//! runtextmacro optional DAMAGE_EVENT_VARS_PLUGIN_02()
//! runtextmacro optional DAMAGE_EVENT_VARS_PLUGIN_03()
//! runtextmacro optional DAMAGE_EVENT_VARS_PLUGIN_04()
//! runtextmacro optional DAMAGE_EVENT_VARS_PLUGIN_05()
if udg_DamageEventAmount > 0.00 then
call DamageTrigger.SHIELD.run()
static if not USE_GUI then
set udg_DamageEventAmount = d.damage
endif
static if USE_LETHAL then
if atLeastOneLethalDamageEventRegistered or udg_DamageEventType < 0 then
set udg_LethalDamageHP = /*
*/ GetWidgetLife(udg_DamageEventTarget) - udg_DamageEventAmount
if udg_LethalDamageHP <= DEATH_VAL then
if atLeastOneLethalDamageEventRegistered then
call DamageTrigger.LETHAL.run()
set udg_DamageEventAmount = /*
*/ GetWidgetLife(udg_DamageEventTarget) - udg_LethalDamageHP
set d.damage = udg_DamageEventAmount
endif
if udg_DamageEventType < 0 and /*
*/ udg_LethalDamageHP <= DEATH_VAL /*
*/ then
call SetUnitExploded(udg_DamageEventTarget, true)
endif
endif
endif
endif
static if USE_SCALING then
if udg_DamageEventPrevAmt == 0.00 or /*
*/ udg_DamageScalingWC3 == 0.00 /*
*/ then
set udg_DamageScalingUser = 0.00
else
set udg_DamageScalingUser = /*
*/ udg_DamageEventAmount / udg_DamageEventPrevAmt / udg_DamageScalingWC3
endif
endif
endif
if udg_DamageEventDamageT != 0 then
call DamageTrigger.DAMAGE.run()
endif
call BlzSetEventDamage(udg_DamageEventAmount)
set nativeEventsCompleted = true
if udg_DamageEventAmount == 0.00 then
call runAfterDamageEvents()
endif
// This return statement was needed years ago to avoid potential crashes on Mac.
// I am not sure if that's still a thing.
return false
endmethod
static method apply takes /*
*/ unit src, /*
*/ unit tgt, /*
*/ real amt, /*
*/ boolean a, /*
*/ boolean r, /*
*/ attacktype at, /*
*/ damagetype dt, /*
*/ weapontype wt /*
*/ returns Damage
local Damage d
if udg_NextDamageType == 0 then
set udg_NextDamageType = TYPE_CODE
endif
if callbacksInProgress then
set d = create(src, tgt, amt, a, at, dt, wt)
set d.isCode = true
set d.eFilter = FILTER_CODE
set d.userType = udg_NextDamageType
static if USE_MELEE_RANGE then
if not d.isSpell then
set d.isRanged = udg_NextDamageIsRanged or r
set d.isMelee = not d.isRanged
endif
endif
call d.addRecursive()
else
call UnitDamageTarget(src, tgt, amt, a, r, at, dt, wt)
set d = Damage.index
call runAfterDamageEvents()
endif
call clearNexts()
return d
endmethod
static method applySpell takes /*
*/ unit src, /*
*/ unit tgt, /*
*/ real amt, /*
*/ damagetype dt /*
*/ returns Damage
return apply(src, tgt, amt, false, false, null, dt, null)
endmethod
static method applyAttack takes /*
*/ unit src, /*
*/ unit tgt, /*
*/ real amt, /*
*/ boolean ranged, /*
*/ attacktype at, /*
*/ weapontype wt /*
*/ returns Damage
return apply(src, tgt, amt, true, ranged, at, DAMAGE_TYPE_NORMAL, wt)
endmethod
/*
This part is the most critical to get things kicked off. All the code we've seen up until now
is related to event handling, trigger assignment, edge cases, etc. But it's the following that
is really quite esesntial for any damage engine - not just this one.
*/
private static method onInit takes nothing returns nothing
set async = CreateTimer()
set recursionSources = CreateGroup()
set recursionTargets = CreateGroup()
set damagingTrigger = CreateTrigger()
set damagedTrigger = CreateTrigger()
set recursiveTrigger = CreateTrigger() //Moved from globals block as per request of user Ricola3D
call TriggerRegisterAnyUnitEventBJ(damagingTrigger, EVENT_PLAYER_UNIT_DAMAGING)
call TriggerAddCondition(damagingTrigger, Filter(function Damage.onDamagingCallback))
//call TriggerDebugAutomation_TriggerIsReady(damagingTrigger, "damagingTrigger")
call TriggerRegisterAnyUnitEventBJ(damagedTrigger, EVENT_PLAYER_UNIT_DAMAGED)
call TriggerAddCondition(damagedTrigger, Filter(function Damage.onDamagedCallback))
//call TriggerDebugAutomation_TriggerIsReady(damagedTrigger, "damagedTrigger")
//For recursion
call TriggerRegisterAnyUnitEventBJ(recursiveTrigger, EVENT_PLAYER_UNIT_DAMAGING)
call TriggerAddCondition(recursiveTrigger, Filter(function Damage.onRecursiveDamageCallback))
call DisableTrigger(recursiveTrigger) //starts disabled. Will be enabled during recursive event handling.
//call TriggerDebugAutomation_TriggerIsReady(recursiveTrigger, "recursiveTrigger")
/*
For preventing Thorns/Defensive glitch.
Data gathered from https://www.hiveworkshop.com/threads/repo-in-progress-mapping-damage-types-to-their-abilities.316271/
*/
set attacksImmune[0] = false //ATTACK_TYPE_NORMAL
set attacksImmune[1] = true //ATTACK_TYPE_MELEE
set attacksImmune[2] = true //ATTACK_TYPE_PIERCE
set attacksImmune[3] = true //ATTACK_TYPE_SIEGE
set attacksImmune[4] = false //ATTACK_TYPE_MAGIC
set attacksImmune[5] = true //ATTACK_TYPE_CHAOS
set attacksImmune[6] = true //ATTACK_TYPE_HERO
set damagesImmune[0] = true //DAMAGE_TYPE_UNKNOWN
set damagesImmune[4] = true //DAMAGE_TYPE_NORMAL
set damagesImmune[5] = true //DAMAGE_TYPE_ENHANCED
set damagesImmune[8] = false //DAMAGE_TYPE_FIRE
set damagesImmune[9] = false //DAMAGE_TYPE_COLD
set damagesImmune[10] = false //DAMAGE_TYPE_LIGHTNING
set damagesImmune[11] = true //DAMAGE_TYPE_POISON
set damagesImmune[12] = true //DAMAGE_TYPE_DISEASE
set damagesImmune[13] = false //DAMAGE_TYPE_DIVINE
set damagesImmune[14] = false //DAMAGE_TYPE_MAGIC
set damagesImmune[15] = false //DAMAGE_TYPE_SONIC
set damagesImmune[16] = true //DAMAGE_TYPE_ACID
set damagesImmune[17] = false //DAMAGE_TYPE_FORCE
set damagesImmune[18] = false //DAMAGE_TYPE_DEATH
set damagesImmune[19] = false //DAMAGE_TYPE_MIND
set damagesImmune[20] = false //DAMAGE_TYPE_PLANT
set damagesImmune[21] = false //DAMAGE_TYPE_DEFENSIVE
set damagesImmune[22] = true //DAMAGE_TYPE_DEMOLITION
set damagesImmune[23] = true //DAMAGE_TYPE_SLOW_POISON
set damagesImmune[24] = false //DAMAGE_TYPE_SPIRIT_LINK
set damagesImmune[25] = false //DAMAGE_TYPE_SHADOW_STRIKE
set damagesImmune[26] = true //DAMAGE_TYPE_UNIVERSAL
endmethod
//! runtextmacro optional DAMAGE_EVENT_STRUCT_PLUGIN_DMGPKG()
//! runtextmacro optional DAMAGE_EVENT_STRUCT_PLUGIN_01()
//! runtextmacro optional DAMAGE_EVENT_STRUCT_PLUGIN_02()
//! runtextmacro optional DAMAGE_EVENT_STRUCT_PLUGIN_03()
//! runtextmacro optional DAMAGE_EVENT_STRUCT_PLUGIN_04()
//! runtextmacro optional DAMAGE_EVENT_STRUCT_PLUGIN_05()
endstruct
// Called from the GUI configuration trigger once the assignments are in place.
public function DebugStr takes nothing returns nothing
local integer i = 0
loop
set udg_CONVERTED_ATTACK_TYPE[i] = ConvertAttackType(i)
exitwhen i == 6
set i = i + 1
endloop
set i = 0
loop
set udg_CONVERTED_DAMAGE_TYPE[i] = ConvertDamageType(i)
exitwhen i == 26
set i = i + 1
endloop
set udg_AttackTypeDebugStr[0] = "SPELLS" //ATTACK_TYPE_NORMAL in JASS
set udg_AttackTypeDebugStr[1] = "NORMAL" //ATTACK_TYPE_MELEE in JASS
set udg_AttackTypeDebugStr[2] = "PIERCE"
set udg_AttackTypeDebugStr[3] = "SIEGE"
set udg_AttackTypeDebugStr[4] = "MAGIC"
set udg_AttackTypeDebugStr[5] = "CHAOS"
set udg_AttackTypeDebugStr[6] = "HERO"
set udg_DamageTypeDebugStr[0] = "UNKNOWN"
set udg_DamageTypeDebugStr[4] = "NORMAL"
set udg_DamageTypeDebugStr[5] = "ENHANCED"
set udg_DamageTypeDebugStr[8] = "FIRE"
set udg_DamageTypeDebugStr[9] = "COLD"
set udg_DamageTypeDebugStr[10] = "LIGHTNING"
set udg_DamageTypeDebugStr[11] = "POISON"
set udg_DamageTypeDebugStr[12] = "DISEASE"
set udg_DamageTypeDebugStr[13] = "DIVINE"
set udg_DamageTypeDebugStr[14] = "MAGIC"
set udg_DamageTypeDebugStr[15] = "SONIC"
set udg_DamageTypeDebugStr[16] = "ACID"
set udg_DamageTypeDebugStr[17] = "FORCE"
set udg_DamageTypeDebugStr[18] = "DEATH"
set udg_DamageTypeDebugStr[19] = "MIND"
set udg_DamageTypeDebugStr[20] = "PLANT"
set udg_DamageTypeDebugStr[21] = "DEFENSIVE"
set udg_DamageTypeDebugStr[22] = "DEMOLITION"
set udg_DamageTypeDebugStr[23] = "SLOW_POISON"
set udg_DamageTypeDebugStr[24] = "SPIRIT_LINK"
set udg_DamageTypeDebugStr[25] = "SHADOW_STRIKE"
set udg_DamageTypeDebugStr[26] = "UNIVERSAL"
set udg_WeaponTypeDebugStr[0] = "NONE" //WEAPON_TYPE_WHOKNOWS in JASS
set udg_WeaponTypeDebugStr[1] = "METAL_LIGHT_CHOP"
set udg_WeaponTypeDebugStr[2] = "METAL_MEDIUM_CHOP"
set udg_WeaponTypeDebugStr[3] = "METAL_HEAVY_CHOP"
set udg_WeaponTypeDebugStr[4] = "METAL_LIGHT_SLICE"
set udg_WeaponTypeDebugStr[5] = "METAL_MEDIUM_SLICE"
set udg_WeaponTypeDebugStr[6] = "METAL_HEAVY_SLICE"
set udg_WeaponTypeDebugStr[7] = "METAL_MEDIUM_BASH"
set udg_WeaponTypeDebugStr[8] = "METAL_HEAVY_BASH"
set udg_WeaponTypeDebugStr[9] = "METAL_MEDIUM_STAB"
set udg_WeaponTypeDebugStr[10] = "METAL_HEAVY_STAB"
set udg_WeaponTypeDebugStr[11] = "WOOD_LIGHT_SLICE"
set udg_WeaponTypeDebugStr[12] = "WOOD_MEDIUM_SLICE"
set udg_WeaponTypeDebugStr[13] = "WOOD_HEAVY_SLICE"
set udg_WeaponTypeDebugStr[14] = "WOOD_LIGHT_BASH"
set udg_WeaponTypeDebugStr[15] = "WOOD_MEDIUM_BASH"
set udg_WeaponTypeDebugStr[16] = "WOOD_HEAVY_BASH"
set udg_WeaponTypeDebugStr[17] = "WOOD_LIGHT_STAB"
set udg_WeaponTypeDebugStr[18] = "WOOD_MEDIUM_STAB"
set udg_WeaponTypeDebugStr[19] = "CLAW_LIGHT_SLICE"
set udg_WeaponTypeDebugStr[20] = "CLAW_MEDIUM_SLICE"
set udg_WeaponTypeDebugStr[21] = "CLAW_HEAVY_SLICE"
set udg_WeaponTypeDebugStr[22] = "AXE_MEDIUM_CHOP"
set udg_WeaponTypeDebugStr[23] = "ROCK_HEAVY_BASH"
set udg_DefenseTypeDebugStr[0] = "LIGHT"
set udg_DefenseTypeDebugStr[1] = "MEDIUM"
set udg_DefenseTypeDebugStr[2] = "HEAVY"
set udg_DefenseTypeDebugStr[3] = "FORTIFIED"
set udg_DefenseTypeDebugStr[4] = "NORMAL" //Typically deals flat damage to all armor types
set udg_DefenseTypeDebugStr[5] = "HERO"
set udg_DefenseTypeDebugStr[6] = "DIVINE"
set udg_DefenseTypeDebugStr[7] = "UNARMORED"
set udg_ArmorTypeDebugStr[0] = "NONE" //ARMOR_TYPE_WHOKNOWS in JASS, added in 1.31
set udg_ArmorTypeDebugStr[1] = "FLESH"
set udg_ArmorTypeDebugStr[2] = "METAL"
set udg_ArmorTypeDebugStr[3] = "WOOD"
set udg_ArmorTypeDebugStr[4] = "ETHEREAL"
set udg_ArmorTypeDebugStr[5] = "STONE"
// Added 25 July 2017 to allow detection of things like Bash or Pulverize or AOE spread
set udg_DamageEventAOE = 1
set udg_DamageEventLevel = 1
/*
In-game World Editor doesn't allow Attack Type and Damage Type comparisons.
Therefore, I need to code them as integers into GUI
*/
set udg_ATTACK_TYPE_SPELLS = 0
set udg_ATTACK_TYPE_NORMAL = 1
set udg_ATTACK_TYPE_PIERCE = 2
set udg_ATTACK_TYPE_SIEGE = 3
set udg_ATTACK_TYPE_MAGIC = 4
set udg_ATTACK_TYPE_CHAOS = 5
set udg_ATTACK_TYPE_HERO = 6
// -
set udg_DAMAGE_TYPE_UNKNOWN = 0
set udg_DAMAGE_TYPE_NORMAL = 4
set udg_DAMAGE_TYPE_ENHANCED = 5
set udg_DAMAGE_TYPE_FIRE = 8
set udg_DAMAGE_TYPE_COLD = 9
set udg_DAMAGE_TYPE_LIGHTNING = 10
set udg_DAMAGE_TYPE_POISON = 11
set udg_DAMAGE_TYPE_DISEASE = 12
set udg_DAMAGE_TYPE_DIVINE = 13
set udg_DAMAGE_TYPE_MAGIC = 14
set udg_DAMAGE_TYPE_SONIC = 15
set udg_DAMAGE_TYPE_ACID = 16
set udg_DAMAGE_TYPE_FORCE = 17
set udg_DAMAGE_TYPE_DEATH = 18
set udg_DAMAGE_TYPE_MIND = 19
set udg_DAMAGE_TYPE_PLANT = 20
set udg_DAMAGE_TYPE_DEFENSIVE = 21
set udg_DAMAGE_TYPE_DEMOLITION = 22
set udg_DAMAGE_TYPE_SLOW_POISON = 23
set udg_DAMAGE_TYPE_SPIRIT_LINK = 24
set udg_DAMAGE_TYPE_SHADOW_STRIKE = 25
set udg_DAMAGE_TYPE_UNIVERSAL = 26
/*
The below variables don't affect damage amount, but do affect the sound played
They also give important information about the type of attack used.
They can differentiate between ranged and melee for units who are both
*/
set udg_WEAPON_TYPE_NONE = 0
// Metal Light/Medium/Heavy
set udg_WEAPON_TYPE_ML_CHOP = 1
set udg_WEAPON_TYPE_MM_CHOP = 2
set udg_WEAPON_TYPE_MH_CHOP = 3
set udg_WEAPON_TYPE_ML_SLICE = 4
set udg_WEAPON_TYPE_MM_SLICE = 5
set udg_WEAPON_TYPE_MH_SLICE = 6
set udg_WEAPON_TYPE_MM_BASH = 7
set udg_WEAPON_TYPE_MH_BASH = 8
set udg_WEAPON_TYPE_MM_STAB = 9
set udg_WEAPON_TYPE_MH_STAB = 10
// Wood Light/Medium/Heavy
set udg_WEAPON_TYPE_WL_SLICE = 11
set udg_WEAPON_TYPE_WM_SLICE = 12
set udg_WEAPON_TYPE_WH_SLICE = 13
set udg_WEAPON_TYPE_WL_BASH = 14
set udg_WEAPON_TYPE_WM_BASH = 15
set udg_WEAPON_TYPE_WH_BASH = 16
set udg_WEAPON_TYPE_WL_STAB = 17
set udg_WEAPON_TYPE_WM_STAB = 18
// Claw Light/Medium/Heavy
set udg_WEAPON_TYPE_CL_SLICE = 19
set udg_WEAPON_TYPE_CM_SLICE = 20
set udg_WEAPON_TYPE_CH_SLICE = 21
// Axe Medium
set udg_WEAPON_TYPE_AM_CHOP = 22
// Rock Heavy
set udg_WEAPON_TYPE_RH_BASH = 23
/*
Since GUI still doesn't provide Defense Type and Armor Types,
I needed to include the below:
*/
set udg_ARMOR_TYPE_NONE = 0
set udg_ARMOR_TYPE_FLESH = 1
set udg_ARMOR_TYPE_METAL = 2
set udg_ARMOR_TYPE_WOOD = 3
set udg_ARMOR_TYPE_ETHEREAL = 4
set udg_ARMOR_TYPE_STONE = 5
set udg_DEFENSE_TYPE_LIGHT = 0
set udg_DEFENSE_TYPE_MEDIUM = 1
set udg_DEFENSE_TYPE_HEAVY = 2
set udg_DEFENSE_TYPE_FORTIFIED = 3
set udg_DEFENSE_TYPE_NORMAL = 4
set udg_DEFENSE_TYPE_HERO = 5
set udg_DEFENSE_TYPE_DIVINE = 6
set udg_DEFENSE_TYPE_UNARMORED = 7
/*
The remaining stuff is an ugly 'optimization' that I did a long
time ago, thinking that it would improve performance for GUI users
by not having so many different triggerconditions evaluating per
damage event. I am not sure if it even worked; in Lua it might
perform worse, but in vJass it remains to be tested.
*/
set udg_UNIT_CLASS_HERO = 0
set udg_UNIT_CLASS_DEAD = 1
set udg_UNIT_CLASS_STRUCTURE = 2
set udg_UNIT_CLASS_FLYING = 3
set udg_UNIT_CLASS_GROUND = 4
set udg_UNIT_CLASS_ATTACKS_FLYING = 5
set udg_UNIT_CLASS_ATTACKS_GROUND = 6
set udg_UNIT_CLASS_MELEE = 7
set udg_UNIT_CLASS_RANGED = 8
set udg_UNIT_CLASS_GIANT = 9
set udg_UNIT_CLASS_SUMMONED = 10
set udg_UNIT_CLASS_STUNNED = 11
set udg_UNIT_CLASS_PLAGUED = 12
set udg_UNIT_CLASS_SNARED = 13
set udg_UNIT_CLASS_UNDEAD = 14
set udg_UNIT_CLASS_MECHANICAL = 15
set udg_UNIT_CLASS_PEON = 16
set udg_UNIT_CLASS_SAPPER = 17
set udg_UNIT_CLASS_TOWNHALL = 18
set udg_UNIT_CLASS_ANCIENT = 19
set udg_UNIT_CLASS_TAUREN = 20
set udg_UNIT_CLASS_POISONED = 21
set udg_UNIT_CLASS_POLYMORPHED = 22
set udg_UNIT_CLASS_SLEEPING = 23
set udg_UNIT_CLASS_RESISTANT = 24
set udg_UNIT_CLASS_ETHEREAL = 25
set udg_UNIT_CLASS_MAGIC_IMMUNE = 26
set udg_DamageFilterAttackT = -1
set udg_DamageFilterDamageT = -1
set udg_DamageFilterSourceC = -1
set udg_DamageFilterTargetC = -1
set udg_DamageFilterRunChance = 1.00
endfunction
public function RegisterFromHook takes /*
*/ trigger whichTrig, /*
*/ string var, /*
*/ limitop op, /*
*/ real value /*
*/ returns nothing
call DamageTrigger.registerVerbose(whichTrig, var, value, true, GetHandleId(op))
endfunction
hook TriggerRegisterVariableEvent RegisterFromHook
function TriggerRegisterDamageEngineEx takes /*
*/ trigger whichTrig, /*
*/ string eventName, /*
*/ real value, /*
*/ integer opId /*
*/ returns DamageTrigger
return DamageTrigger.registerVerbose( /*
*/ whichTrig, /*
*/ DamageTrigger.getVerboseStr(eventName), /*
*/ value, /*
*/ false, /*
*/ opId /*
*/ )
endfunction
function TriggerRegisterDamageEngine takes /*
*/ trigger whichTrig, /*
*/ string eventName, /*
*/ real value /*
*/ returns DamageTrigger
return DamageTrigger.registerTrigger(whichTrig, eventName, value)
endfunction
function RegisterDamageEngineEx takes /*
*/ code callback, /*
*/ string eventName, /*
*/ real value, /*
*/ integer opId /*
*/ returns DamageTrigger
return TriggerRegisterDamageEngineEx(DamageTrigger[callback], eventName, value, opId)
endfunction
//Similar to TriggerRegisterDamageEvent, but takes code instead of trigger as the first argument.
function RegisterDamageEngine takes /*
*/ code callback, /*
*/ string eventName, /*
*/ real value /*
*/ returns DamageTrigger
return RegisterDamageEngineEx(callback, eventName, value, FILTER_OTHER)
endfunction
/*
The below macros are for GUI to tap into more powerful vJass event filtering:
*/
//! textmacro DAMAGE_TRIGGER_CONFIG
if not DamageTrigger.eventIndex.configured then
//! endtextmacro
//! textmacro DAMAGE_TRIGGER_CONFIG_END
call DamageTrigger.eventIndex.configure()
endif
if not DamageTrigger.eventIndex.checkConfig() then
return
endif
//! endtextmacro
endlibrary
/*
vJass ShieldEngine 1.0.0
by Marchombre
Requirements:
- GUI Unit Event (by Bribe)
- Damage Engine 5.A.0.0 (by Bribe)
- CustomBar 1.0 (by dhk_undead_lord / aka Anachron)
*/
/*
Apply shield in the following order:
1/ Shield that can only take specific damage (only spell/attack) before shields that can absorb all
2/ Shield that nullify one instance of damage before those blocking amount of damage
3/ Order by timeLeft
*/
/*
================================================
JASS API DESCRIPTION
================================================
*/
/*
These are the functions you can use to interact with the engine:.
================================================
SHIELD CREATION
================================================
ShieldEngine.CreateShield
|-- Parameters:
unit source,
unit target,
string name,
string sfxPath,
string attachPoint,
integer shieldProcType,
integer shieldType,
integer shieldHpType,
boolean isStackable,
boolean isTimed,
real duration,
real HP,
boolean breakAtZero,
boolean canRegen,
real timeBeforeRegen,
real regenTime
|-- Returns: nothing
ShieldEngine.CreateTimedShield
|-- Parameters:
unit source,
unit target,
string name,
real duration,
real HP
|-- Returns: nothing
================================================
SHIELD DESTRUCTION
================================================
There are two ways of destroying a shield manually. Methods with Remove will do it silently. Methods with destroy will fire a BreakEvent.
ShieldEngine.RemoveShieldByName
|-- Parameters:
unit u,
string name
|-- Returns: nothing
ShieldEngine.RemoveAllShields
|-- Parameters:
unit u
|-- Returns: nothing
ShieldEngine.RemoveShieldsByType
|-- Parameters:
unit u,
integer hpType
|-- Returns: nothing
ShieldEngine.DestroyShieldByName
|-- Parameters:
unit u,
string name
|-- Returns: nothing
ShieldEngine.DestroyShieldsByType
|-- Parameters:
unit u,
integer hpType
|-- Returns: nothing
ShieldEngine.DestroyAllShields
|-- Parameters:
unit u
|-- Returns: nothing
================================================
UNIT INFORMATION
================================================
ShieldEngine.IsUnitShielded
|-- Parameters:
unit u
|-- Returns: nothing
ShieldEngine.UnitHasShield
|-- Parameters:
unit u,
string name
|-- Returns: nothing
*/
library ShieldEngine requires DamageEngine, ARGB, CustomBar
globals
/*
================================================
CONFIG VARIABLES
================================================
*/
//
private constant real SHIELD_TICK_INTERVAL = 0.1 // Interval at which duration is checked and regen is applied.
private constant integer MAX_PRESHIELD_CREATION_EVENTS = 4
/*
Shield aesthetic
*/
private constant string SHIELD_DEFAULT_SFX_PATH = "Abilities\\Spells\\Human\\ManaShield\\ManaShieldCaster.mdl"
private constant string SHIELD_DEFAULT_ATTACH_POINT = "origin"
// Shield Bar colors config is done in ShieldWieder.OnInit (can't be done in globals)
// SHIELD_HP_TYPE_ALL => Green by default
private ARGB SHIELD_COLOR_LEFT
private ARGB SHIELD_COLOR_RIGHT
// SHIELD_HP_TYPE_SPELL => Purple by default
private ARGB SPELL_SHIELD_COLOR_LEFT
private ARGB SPELL_SHIELD_COLOR_RIGHT
// SHIELD_HP_TYPE_ATTACK => Brown by default
private ARGB ATTACK_SHIELD_COLOR_LEFT
private ARGB ATTACK_SHIELD_COLOR_RIGHT
/*
================================================
SHOULD NOT TOUCH THESE CONSTANTS
================================================
*/
private constant integer SHIELD_PROC_PREREDUC = 0 // Shield absorb damage before reductions like armor are applied.
private constant integer SHIELD_PROC_POSTREDUC = 1 // Shield absorb damage after reductions are applied.
private constant integer SHIELD_TYPE_HP_BASED = 0 // Shield that expire after set amount of damage.
private constant integer SHIELD_TYPE_INSTANCE = 1 // Shield that expires after a number of damage instance
private constant integer SHIELD_HP_TYPE_ALL = 0 // Absorb all type of damage
private constant integer SHIELD_HP_TYPE_SPELL = 1 // Absorb only spell damage
private constant integer SHIELD_HP_TYPE_ATTACK = 2 // Absorb only attack damage
// Used for Firing events
private constant integer SHIELD_EVENT_CREATED = 0
private constant integer SHIELD_EVENT_ABSORB = 1
private constant integer SHIELD_EVENT_BREAK = 2
private constant integer SHIELD_EVENT_EXPIRE = 3
private constant integer SHIELD_EVENT_REFRESHED = 4
private constant integer SHIELD_EVENT_STACKED = 5
/*
================================================
GUI VARIABLES
================================================
*/
/*
*/
endglobals
public function interface iChange takes CustomBar cb returns nothing
struct Shield
/*
Per instance variables
*/
public string name // Name of Shield, allows search/stack etc.
public unit source // Unit that casted the shield
public unit wielder // Unit that received the shield
private string sfxPath // Path to the model of visual effect
public effect visualEffect // Visual Effect attached to the unit
private string attachPoint // Visual Effect attach point to the wielder
public integer shieldProcType // When does the shield apply
public integer shieldType // How the shield block damage
public integer shieldHpType // What type of damage does the shield absorb
public boolean isStackable // Can this shield stack with itself
public boolean isTimed // Is the shield limited in duration
public real duration // If timed: number of seconds before expiration.
public real timeLeft // If timed: how much time is left before expiration.
public real maxHP // If shieldType = SHIELD_TYPE_HP_BASED: Amount of damage the shield can absorb. If shieldType = SHIELD_TYPE_INSTANCE: how many instance of damage does the shield absorb
public real currentHP // Remaining HP
public boolean breakAtZero // Does the shield break when its HPs reach 0
public real lastAbsorbedDamage // Store the last amount of damage blocked
public boolean canRegen // Can the shield regenerate itself
public real timeBeforeRegen // How long without taking damage before shield regen (in seconds)
public real regenTime // How long should it take for a full regen (in seconds)
public real regenPerTick // How much HP regen per tick
public Shield next
public Shield prev
public static method create takes /*
*/ unit source, /*
*/ unit target, /*
*/ string name, /*
*/ string sfxPath, /*
*/ string attachPoint, /*
*/ integer shieldProcType, /*
*/ integer shieldType, /*
*/ integer shieldHpType, /*
*/ boolean isStackable, /*
*/ boolean isTimed, /*
*/ real duration, /*
*/ real HP, /*
*/ boolean breakAtZero, /*
*/ boolean canRegen, /*
*/ real timeBeforeRegen, /*
*/ real regenTime /*
*/ returns Shield
local Shield this = Shield.allocate()
set this.source = source
set this.wielder = target
set this.name = name
set this.sfxPath = sfxPath
set this.attachPoint = attachPoint
set this.visualEffect = null
set this.shieldProcType = shieldProcType
set this.shieldType = shieldType
set this.shieldHpType = shieldHpType
set this.isStackable = isStackable
set this.isTimed = isTimed
set this.duration = duration
set this.timeLeft = this.duration
// Set times to a very high amount that will not change to make it easier when sorting shields by duration
if not this.isTimed then
set this.duration = 1000000.00
set this.timeLeft = 1000000.00
endif
set this.maxHP = HP
set this.currentHP = HP
set this.breakAtZero = breakAtZero
set this.canRegen = canRegen
set this.timeBeforeRegen = timeBeforeRegen
set this.regenTime = regenTime
if this.canRegen then
set this.regenPerTick = this.maxHP / (this.regenTime / SHIELD_TICK_INTERVAL)
else
set this.regenPerTick = 0.00
endif
set this.next = 0
set this.next = 0
return this
endmethod
public method destroy takes nothing returns nothing
call DestroyEffect(this.visualEffect)
call this.deallocate()
endmethod
public method CreateVisualEffet takes nothing returns nothing
if this.visualEffect == null then
set this.visualEffect = AddSpecialEffectTarget(this.sfxPath, this.wielder, this.attachPoint)
endif
endmethod
public method Refresh takes real duration, real HP returns nothing
local real prevMaxHP
local real prevCurrentHP
local ShieldWielder wielder = ShieldEngine.GetUnitWielder(this.wielder)
set this.duration = RMaxBJ(this.duration, duration)
set this.timeLeft = RMinBJ(this.duration, this.timeLeft + duration)
set prevMaxHP = this.maxHP
set prevCurrentHP = this.currentHP
set this.maxHP = RMaxBJ(this.maxHP, HP)
set this.currentHP = RMinBJ(this.maxHP, this.currentHP + HP)
if this.shieldHpType == SHIELD_HP_TYPE_ALL then
set wielder.totalMaxShield = wielder.totalMaxShield + (this.maxHP - prevMaxHP)
set wielder.totalCurrentShield = wielder.totalCurrentShield + (this.currentHP - prevCurrentHP)
elseif this.shieldHpType == SHIELD_HP_TYPE_SPELL then
set wielder.totalMaxSpellShield = wielder.totalMaxSpellShield + (this.maxHP - prevMaxHP)
set wielder.totalCurrentSpellShield = wielder.totalCurrentSpellShield + (this.currentHP - prevCurrentHP)
elseif this.shieldHpType == SHIELD_HP_TYPE_ATTACK then
set wielder.totalMaxAttackShield = wielder.totalMaxAttackShield + (this.maxHP - prevMaxHP)
set wielder.totalCurrentAttackShield = wielder.totalCurrentAttackShield + (this.currentHP - prevCurrentHP)
endif
call ShieldEvent.FireEvent(SHIELD_EVENT_REFRESHED, this)
endmethod
public method Stack takes real duration, real HP returns nothing
local ShieldWielder wielder = ShieldEngine.GetUnitWielder(this.wielder)
set this.duration = RMaxBJ(this.duration, duration)
set this.timeLeft = RMinBJ(this.duration, this.timeLeft + duration)
set this.maxHP = this.maxHP + HP
set this.currentHP = this.currentHP + HP
if this.shieldHpType == SHIELD_HP_TYPE_ALL then
set wielder.totalMaxShield = wielder.totalMaxShield + HP
set wielder.totalCurrentShield = wielder.totalCurrentShield + HP
elseif this.shieldHpType == SHIELD_HP_TYPE_SPELL then
set wielder.totalMaxSpellShield = wielder.totalMaxSpellShield + HP
set wielder.totalCurrentSpellShield = wielder.totalCurrentSpellShield + HP
elseif this.shieldHpType == SHIELD_HP_TYPE_ATTACK then
set wielder.totalMaxAttackShield = wielder.totalMaxAttackShield + HP
set wielder.totalCurrentAttackShield = wielder.totalCurrentAttackShield + HP
endif
call ShieldEvent.FireEvent(SHIELD_EVENT_STACKED, this)
endmethod
endstruct
struct ShieldWielder
/*
Per instance variables
*/
public unit wielder
private CustomBar shieldBar
private CustomBar spellShieldBar
private CustomBar attackShieldBar
public Shield firstPreReduc
public Shield lastPreReduc
public Shield firstPreReducSpell
public Shield lastPreReducSpell
public Shield firstPreReducAttack
public Shield lastPreReducAttack
public Shield firstPostReduc
public Shield lastPostReduc
public Shield firstPostReducSpell
public Shield lastPostReducSpell
public Shield firstPostReducAttack
public Shield lastPostReducAttack
public real lastDamageTaken
public real totalMaxShield
public real totalCurrentShield
public real totalMaxSpellShield
public real totalCurrentSpellShield
public real totalMaxAttackShield
public real totalCurrentAttackShield
public ShieldWielder prev
public ShieldWielder next
public static method create takes unit wielder returns ShieldWielder
local ShieldWielder this = ShieldWielder.allocate()
set this.wielder = wielder
set this.shieldBar = 0
set this.spellShieldBar = 0
set this.attackShieldBar = 0
set this.firstPreReduc = 0
set this.lastPreReduc = 0
set this.firstPreReducSpell = 0
set this.lastPreReducSpell = 0
set this.firstPreReducAttack = 0
set this.lastPreReducAttack = 0
set this.firstPostReduc = 0
set this.lastPostReduc = 0
set this.firstPostReducSpell = 0
set this.lastPostReducSpell = 0
set this.firstPostReducAttack = 0
set this.lastPostReducAttack = 0
set this.lastDamageTaken = 0.00
set this.totalMaxShield = 0.00
set this.totalCurrentShield = 0.00
set this.totalMaxSpellShield = 0.00
set this.totalCurrentSpellShield = 0.00
set this.totalMaxAttackShield = 0.00
set this.totalCurrentAttackShield = 0.00
set this.prev = 0
set this.next = 0
return this
endmethod
public method destroy takes nothing returns nothing
call this.deallocate()
endmethod
private static method UpdateShieldBar takes CustomBar cb returns nothing
local ShieldWielder wielder = ShieldEngine.GetUnitWielder(cb.target)
set cb.txtBar.percentage = 100.0 * wielder.totalCurrentShield / wielder.totalMaxShield
endmethod
private static method UpdateSpellShieldBar takes CustomBar cb returns nothing
local ShieldWielder wielder = ShieldEngine.GetUnitWielder(cb.target)
set cb.txtBar.percentage = 100.0 * wielder.totalCurrentSpellShield / wielder.totalMaxSpellShield
endmethod
private static method UpdateAttackShieldBar takes CustomBar cb returns nothing
local ShieldWielder wielder = ShieldEngine.GetUnitWielder(cb.target)
set cb.txtBar.percentage = 100.0 * wielder.totalCurrentAttackShield / wielder.totalMaxAttackShield
endmethod
private static method ShowBarCheck takes player owner, player cur returns boolean
return IsPlayerAlly(owner, cur) or owner == cur
endmethod
private method CreateShieldBar takes integer shieldHpType returns nothing
local CustomBar cb
local iChange updateFunction
local real offsetX
local real offsetY
local ARGB colorLeft
local ARGB colorRight
if shieldHpType == SHIELD_HP_TYPE_SPELL then
set cb = this.spellShieldBar
set updateFunction = ShieldWielder.UpdateSpellShieldBar
set offsetX = -90.0
set offsetY = -70.0
set colorLeft = SPELL_SHIELD_COLOR_LEFT
set colorRight = SPELL_SHIELD_COLOR_RIGHT
elseif shieldHpType == SHIELD_HP_TYPE_ATTACK then
set cb = this.attackShieldBar
set updateFunction = ShieldWielder.UpdateAttackShieldBar
set offsetX = -90.0
set offsetY = -40.0
set colorLeft = ATTACK_SHIELD_COLOR_LEFT
set colorRight = ATTACK_SHIELD_COLOR_RIGHT
else
set cb = this.shieldBar
set updateFunction = ShieldWielder.UpdateShieldBar
set offsetX = -90.0
set offsetY = -10.0
set colorLeft = SHIELD_COLOR_LEFT
set colorRight = SHIELD_COLOR_RIGHT
endif
// create new bar only if there isn't already one
if cb == 0 then
set cb = CustomBar.create(this.wielder, updateFunction)
set cb.txtTag.offsetX = offsetX
set cb.txtTag.offsetY = offsetY
call cb.txtBar.addGradient(colorLeft, colorRight, 1, 25)
call this.shieldBar.Show(bj_FORCE_ALL_PLAYERS, ShieldWielder.ShowBarCheck)
call this.shieldBar.showCB()
if shieldHpType == SHIELD_HP_TYPE_SPELL then
set this.spellShieldBar = cb
elseif shieldHpType == SHIELD_HP_TYPE_ATTACK then
set this.attackShieldBar = cb
else
set this.shieldBar = cb
endif
endif
endmethod
public method AdjustBarAfterDamage takes integer shieldHpType, real dmgAmount returns nothing
if shieldHpType == SHIELD_HP_TYPE_ALL then
set this.totalCurrentShield = this.totalCurrentShield - dmgAmount
elseif shieldHpType == SHIELD_HP_TYPE_SPELL then
set this.totalCurrentSpellShield = this.totalCurrentSpellShield - dmgAmount
elseif shieldHpType == SHIELD_HP_TYPE_ATTACK then
set this.totalCurrentAttackShield = this.totalCurrentAttackShield - dmgAmount
endif
endmethod
private method AdjustShieldBars takes string how, Shield shield returns nothing
local CustomBar cb
if shield.shieldHpType == SHIELD_HP_TYPE_SPELL then
if how =="add" then
set this.totalMaxSpellShield = this.totalMaxSpellShield + shield.maxHP
set this.totalCurrentSpellShield = this.totalCurrentSpellShield + shield.currentHP
if this.spellShieldBar == 0 then
call this.CreateShieldBar(shield.shieldHpType)
endif
else
set this.totalMaxSpellShield = this.totalMaxSpellShield - shield.maxHP
set this.totalCurrentSpellShield = this.totalCurrentSpellShield - shield.currentHP
if this.totalMaxSpellShield <= 0.00 then
call this.spellShieldBar.destroy()
set this.spellShieldBar = 0
endif
endif
elseif shield.shieldHpType == SHIELD_HP_TYPE_ATTACK then
if how =="add" then
set this.totalMaxAttackShield = this.totalMaxAttackShield + shield.maxHP
set this.totalCurrentAttackShield = this.totalCurrentAttackShield + shield.currentHP
if this.attackShieldBar <= 0 then
call this.CreateShieldBar(shield.shieldHpType)
endif
else
set this.totalMaxAttackShield = this.totalMaxAttackShield - shield.maxHP
set this.totalCurrentAttackShield = this.totalCurrentAttackShield - shield.currentHP
if this.totalMaxAttackShield <= 0.00 then
call this.attackShieldBar.destroy()
set this.attackShieldBar = 0
endif
endif
else
if how =="add" then
set this.totalMaxShield = this.totalMaxShield + shield.maxHP
set this.totalCurrentShield = this.totalCurrentShield + shield.currentHP
if this.shieldBar == 0 then
call this.CreateShieldBar(shield.shieldHpType)
endif
else
set this.totalMaxShield = this.totalMaxShield - shield.maxHP
set this.totalCurrentShield = this.totalCurrentShield - shield.currentHP
if this.totalMaxShield <= 0.00 then
call this.shieldBar.destroy()
set this.shieldBar = 0
endif
endif
endif
endmethod
private method AddFirstShield takes Shield shield returns nothing
if shield.shieldProcType == SHIELD_PROC_PREREDUC and shield.shieldHpType == SHIELD_HP_TYPE_ALL then
set this.firstPreReduc = shield
set this.lastPreReduc = shield
elseif shield.shieldProcType == SHIELD_PROC_PREREDUC and shield.shieldHpType == SHIELD_HP_TYPE_SPELL then
set this.firstPreReducSpell = shield
set this.lastPreReducSpell = shield
elseif shield.shieldProcType == SHIELD_PROC_PREREDUC and shield.shieldHpType == SHIELD_HP_TYPE_ATTACK then
set this.firstPreReducAttack = shield
set this.lastPreReducAttack = shield
elseif shield.shieldProcType == SHIELD_PROC_POSTREDUC and shield.shieldHpType == SHIELD_HP_TYPE_ALL then
set this.firstPostReduc = shield
set this.lastPostReduc = shield
elseif shield.shieldProcType == SHIELD_PROC_POSTREDUC and shield.shieldHpType == SHIELD_HP_TYPE_SPELL then
set this.firstPostReducSpell = shield
set this.lastPostReducSpell = shield
elseif shield.shieldProcType == SHIELD_PROC_POSTREDUC and shield.shieldHpType == SHIELD_HP_TYPE_ATTACK then
set this.firstPostReducAttack = shield
set this.lastPostReducAttack = shield
endif
endmethod
public method AddShield takes Shield shield returns nothing
local Shield current
local Shield last
local Shield array listOfFirst
local integer array listOfProcType
local integer array listOfHpType
local integer i
set listOfFirst[0] = this.firstPreReduc
set listOfFirst[1] = this.firstPostReduc
set listOfFirst[2] = this.firstPreReducAttack
set listOfFirst[3] = this.firstPostReducAttack
set listOfFirst[4] = this.firstPreReducSpell
set listOfFirst[5] = this.firstPostReducSpell
set listOfProcType[0] = SHIELD_PROC_PREREDUC
set listOfProcType[1] = SHIELD_PROC_POSTREDUC
set listOfProcType[2] = SHIELD_PROC_PREREDUC
set listOfProcType[3] = SHIELD_PROC_POSTREDUC
set listOfProcType[4] = SHIELD_PROC_PREREDUC
set listOfProcType[5] = SHIELD_PROC_POSTREDUC
set listOfHpType[0] = SHIELD_HP_TYPE_ALL
set listOfHpType[1] = SHIELD_HP_TYPE_ALL
set listOfHpType[2] = SHIELD_HP_TYPE_ATTACK
set listOfHpType[3] = SHIELD_HP_TYPE_ATTACK
set listOfHpType[4] = SHIELD_HP_TYPE_SPELL
set listOfHpType[5] = SHIELD_HP_TYPE_SPELL
call shield.CreateVisualEffet()
call this.AdjustShieldBars("add", shield)
set i = 0
loop
exitwhen i > 5
set current = listOfFirst[i]
// check if the correct list is empty
if current == 0 and shield.shieldProcType == listOfProcType[i] and shield.shieldHpType == listOfHpType[i] then
call this.AddFirstShield(shield)
return
endif
loop
exitwhen current == 0 or shield.shieldProcType != listOfProcType[i] or shield.shieldHpType != listOfHpType[i]
if shield.shieldType == current.shieldType then
if shield.timeLeft < current.timeLeft then
set shield.prev = current.prev
set current.prev.next = shield
set shield.next = current
set current.prev = shield
return
endif
elseif shield.shieldType == SHIELD_TYPE_INSTANCE and current.shieldType == SHIELD_TYPE_HP_BASED then
set shield.prev = current.prev
set current.prev.next = shield
set shield.next = current
set current.prev = shield
return
endif
set last = current
set current = current.next
endloop
// check if it should be inserted at the end of this list
if shield.shieldProcType == listOfProcType[i] and shield.shieldHpType == listOfHpType[i] then
set last.next = shield
endif
set i = i +1
endloop
endmethod
public method RemoveShield takes Shield shield returns nothing
if shield == this.firstPreReduc then
set this.firstPreReduc = shield.next
elseif shield == this.firstPostReduc then
set this.firstPostReduc = shield.next
elseif shield == this.firstPreReducAttack then
set this.firstPreReducAttack = shield.next
elseif shield == this.firstPostReducAttack then
set this.firstPostReducAttack = shield.next
elseif shield == this.firstPreReducSpell then
set this.firstPreReducSpell = shield.next
elseif shield == this.firstPostReducSpell then
set this.firstPostReducSpell = shield.next
endif
if shield == this.lastPreReduc then
set this.lastPreReduc = shield.prev
elseif shield == this.lastPostReduc then
set this.lastPostReduc = shield.prev
elseif shield == this.lastPreReducAttack then
set this.lastPreReducAttack = shield.prev
elseif shield == this.lastPostReducAttack then
set this.lastPostReducAttack = shield.prev
elseif shield == this.lastPreReducSpell then
set this.lastPreReducSpell = shield.prev
elseif shield == this.lastPostReducSpell then
set this.lastPostReduc = shield.prev
endif
if shield.prev != 0 then
set shield.prev.next = shield.next
endif
if shield.next != 0 then
set shield.next.prev = shield.prev
endif
call this.AdjustShieldBars("remove", shield)
call ShieldEngine.CheckRemoveWielder(this)
endmethod
public method FindShieldByNameAndTypes takes string name, integer shieldProcType, integer shieldType, integer shieldHpType returns Shield
local Shield shield = 0
if shieldHpType == SHIELD_HP_TYPE_ALL then
if shieldProcType == SHIELD_PROC_PREREDUC then
set shield = this.firstPreReduc
else
set shield = this.firstPostReduc
endif
elseif shieldHpType == SHIELD_HP_TYPE_SPELL then
if shieldProcType == SHIELD_PROC_PREREDUC then
set shield = this.firstPreReducSpell
else
set shield = this.firstPostReducSpell
endif
elseif shieldHpType == SHIELD_HP_TYPE_ATTACK then
if shieldProcType == SHIELD_PROC_PREREDUC then
set shield = this.firstPreReducAttack
else
set shield = this.firstPostReducAttack
endif
endif
loop
exitwhen shield == 0
if shield.name == name and shield.shieldType == shieldType then
return shield
endif
set shield = shield.next
endloop
return 0
endmethod
public method RemoveShieldsByType takes integer hpType returns nothing
local Shield shield
if hpType == SHIELD_HP_TYPE_ALL then
set shield = this.firstPreReduc
elseif hpType == SHIELD_HP_TYPE_ATTACK then
set shield = this.firstPreReducAttack
elseif hpType == SHIELD_HP_TYPE_SPELL then
set shield = this.firstPreReducSpell
endif
loop
exitwhen shield == 0
call this.RemoveShield(shield)
set shield = shield.next
endloop
if hpType == SHIELD_HP_TYPE_ALL then
set shield = this.firstPostReduc
elseif hpType == SHIELD_HP_TYPE_ATTACK then
set shield = this.firstPostReducAttack
elseif hpType == SHIELD_HP_TYPE_SPELL then
set shield = this.firstPostReducSpell
endif
loop
exitwhen shield == 0
call this.RemoveShield(shield)
set shield = shield.next
endloop
endmethod
public method DestroyShieldsByType takes integer hpType returns nothing
local Shield shield
if hpType == SHIELD_HP_TYPE_ALL then
set shield = this.firstPreReduc
elseif hpType == SHIELD_HP_TYPE_ATTACK then
set shield = this.firstPreReducAttack
elseif hpType == SHIELD_HP_TYPE_SPELL then
set shield = this.firstPreReducSpell
endif
loop
exitwhen shield == 0
call this.RemoveShield(shield)
call ShieldEvent.FireEvent(SHIELD_EVENT_BREAK, shield)
set shield = shield.next
endloop
if hpType == SHIELD_HP_TYPE_ALL then
set shield = this.firstPostReduc
elseif hpType == SHIELD_HP_TYPE_ATTACK then
set shield = this.firstPostReducAttack
elseif hpType == SHIELD_HP_TYPE_SPELL then
set shield = this.firstPostReducSpell
endif
loop
exitwhen shield == 0
call this.RemoveShield(shield)
call ShieldEvent.FireEvent(SHIELD_EVENT_BREAK, shield)
set shield = shield.next
endloop
endmethod
public method RemoveShieldByName takes string name returns nothing
local Shield array listOfFirst
local Shield shield
local integer i = 0
set listOfFirst[0] = this.firstPreReduc
set listOfFirst[1] = this.firstPostReduc
set listOfFirst[2] = this.firstPreReducAttack
set listOfFirst[3] = this.firstPostReducAttack
set listOfFirst[4] = this.firstPreReducSpell
set listOfFirst[5] = this.firstPostReducSpell
loop
exitwhen i > 5
set shield = listOfFirst[i]
loop
exitwhen shield == 0
if shield.name == name then
call this.RemoveShield(shield)
endif
set shield = shield.next
endloop
set i = i +1
endloop
endmethod
public method DestroyShieldByName takes string name returns nothing
local Shield array listOfFirst
local Shield shield
local integer i = 0
set listOfFirst[0] = this.firstPreReduc
set listOfFirst[1] = this.firstPostReduc
set listOfFirst[2] = this.firstPreReducAttack
set listOfFirst[3] = this.firstPostReducAttack
set listOfFirst[4] = this.firstPreReducSpell
set listOfFirst[5] = this.firstPostReducSpell
loop
exitwhen i > 5
set shield = listOfFirst[i]
loop
exitwhen shield == 0
if shield.name == name then
call this.RemoveShield(shield)
call ShieldEvent.FireEvent(SHIELD_EVENT_BREAK, shield)
endif
set shield = shield.next
endloop
set i = i +1
endloop
endmethod
public method HasShield takes string name returns boolean
local Shield array listOfFirst
local Shield shield
local integer i = 0
set listOfFirst[0] = this.firstPreReduc
set listOfFirst[1] = this.firstPostReduc
set listOfFirst[2] = this.firstPreReducAttack
set listOfFirst[3] = this.firstPostReducAttack
set listOfFirst[4] = this.firstPreReducSpell
set listOfFirst[5] = this.firstPostReducSpell
loop
exitwhen i > 5
set shield = listOfFirst[i]
loop
exitwhen shield == 0
if shield.name == name then
return true
endif
set shield = shield.next
endloop
set i = i +1
endloop
return false
endmethod
private static method onInit takes nothing returns nothing
// Shield Bar color, gradient from left to right. Parameters: Alpha, red, green, blue
// SHIELD_HP_TYPE_ALL => Green by default
set SHIELD_COLOR_LEFT = ARGB.create(255, 15, 75, 15)
set SHIELD_COLOR_RIGHT = ARGB.create(255, 15, 220, 15)
// SHIELD_HP_TYPE_SPELL => Purple by default
set SPELL_SHIELD_COLOR_LEFT = ARGB.create(255, 75, 10, 75)
set SPELL_SHIELD_COLOR_RIGHT = ARGB.create(255, 220, 30, 220)
// SHIELD_HP_TYPE_ATTACK => Brown by default
set ATTACK_SHIELD_COLOR_LEFT = ARGB.create(255, 220, 150, 25)
set ATTACK_SHIELD_COLOR_RIGHT = ARGB.create(255, 150, 75, 25)
endmethod
endstruct
struct ShieldEvent
/*
Per instance variables
*/
private integer eventType
private Shield shield
private ShieldEvent next
/*
Shared variables
*/
private static ShieldEvent first
private static ShieldEvent last
public static method create takes integer eventType, Shield shield returns ShieldEvent
local ShieldEvent this = ShieldEvent.allocate()
set this.eventType = eventType
set this.shield = shield
set this.next = 0
return this
endmethod
private method destroy takes nothing returns nothing
call this.deallocate()
endmethod
private static method resetEventVariables takes nothing returns nothing
set udg_ShieldName = null
set udg_ShieldSource = null
set udg_ShieldWielder = null
set udg_ShieldProcType = -1
set udg_ShieldType = -1
set udg_ShieldHpType = -1
set udg_ShieldIsStackable = false
set udg_ShieldIsTimed = false
set udg_ShieldDuration = 0.00
set udg_ShieldTimeLeft = 0.00
set udg_ShieldMaxHP = 0.00
set udg_ShieldCurrentHP = 0.00
set udg_ShieldBreakAtZero = false
set udg_ShieldAbsorbedDamage = 0.00
set udg_ShieldCanRegen = false
set udg_ShieldTimeBeforeRegen = 0.00
set udg_ShieldRegenTime = 0.00
endmethod
private method setEventVariables takes nothing returns nothing
set udg_ShieldName = this.shield.name
set udg_ShieldSource = this.shield.source
set udg_ShieldWielder = this.shield.wielder
set udg_ShieldProcType = this.shield.shieldProcType
set udg_ShieldType = this.shield.shieldType
set udg_ShieldHpType = this.shield.shieldHpType
set udg_ShieldIsStackable = this.shield.isStackable
set udg_ShieldIsTimed = this.shield.isTimed
set udg_ShieldDuration = this.shield.duration
set udg_ShieldTimeLeft = this.shield.timeLeft
set udg_ShieldMaxHP = this.shield.maxHP
set udg_ShieldCurrentHP = this.shield.currentHP
set udg_ShieldBreakAtZero = this.shield.breakAtZero
set udg_ShieldAbsorbedDamage = this.shield.lastAbsorbedDamage
set udg_ShieldCanRegen = this.shield.canRegen
set udg_ShieldTimeBeforeRegen = this.shield.timeBeforeRegen
set udg_ShieldRegenTime = this.shield.regenTime
endmethod
private method getEventVariables takes nothing returns nothing
set this.shield.source = udg_ShieldSource
set this.shield.wielder = udg_ShieldWielder
set this.shield.isStackable = udg_ShieldIsStackable
set this.shield.isTimed = udg_ShieldIsTimed
set this.shield.duration = udg_ShieldDuration
set this.shield.timeLeft = udg_ShieldTimeLeft
set this.shield.maxHP = udg_ShieldMaxHP
set this.shield.currentHP = udg_ShieldCurrentHP
set this.shield.breakAtZero = udg_ShieldBreakAtZero
set this.shield.canRegen = udg_ShieldCanRegen
set this.shield.timeBeforeRegen = udg_ShieldTimeBeforeRegen
set this.shield.regenTime = udg_ShieldRegenTime
if this.shield.canRegen then
set this.shield.regenPerTick = this.shield.maxHP / (this.shield.regenTime / SHIELD_TICK_INTERVAL)
else
set this.shield.regenPerTick = 0.00
endif
endmethod
public static method FireEvent takes integer eventType, Shield shield returns nothing
local ShieldEvent shieldEvent = ShieldEvent.create(eventType, shield)
if first == 0 then
set first = shieldEvent
set last = shieldEvent
call ShieldEvent.Run()
else
set last.next = shieldEvent
set last = last.next
endif
endmethod
private static method Run takes nothing returns nothing
local ShieldEvent shEvent
local real ev
local ShieldWielder wielder
loop
exitwhen first == 0
set shEvent = first
set wielder = ShieldEngine.GetUnitWielder(shEvent.shield.wielder)
if shEvent.eventType == SHIELD_EVENT_CREATED then
set ev = 1.00
loop
exitwhen ev > MAX_PRESHIELD_CREATION_EVENTS
// Set GUI vars
call shEvent.setEventVariables()
// Fire PreCreation event
set udg_PreShieldCreationEvent = 0.00
set udg_PreShieldCreationEvent = ev
set udg_PreShieldCreationEvent = 0.00
// Update fields in case event was caught and variables changed.
call shEvent.getEventVariables()
set ev = ev + 1.00
endloop
if (not shEvent.shield.isTimed or shEvent.shield.timeLeft > 0.00) /*
*/ and (not shEvent.shield.breakAtZero or shEvent.shield.currentHP > 0.00) /*
*/ then
// Attach shield to wielder
call wielder.AddShield(shEvent.shield)
// Set GUI vars
call shEvent.setEventVariables()
// Fire ShieldCreated event
set udg_ShieldCreatedEvent = 0.00
set udg_ShieldCreatedEvent = 1.00
set udg_ShieldCreatedEvent = 0.00
endif
elseif shEvent.eventType == SHIELD_EVENT_ABSORB then
// Set GUI vars
call shEvent.setEventVariables()
set udg_ShieldAbsorbEvent = 0.00
set udg_ShieldAbsorbEvent = 1.00
set udg_ShieldAbsorbEvent = 0.00
// Update fields in case event was caught and variables changed.
call shEvent.getEventVariables()
elseif shEvent.eventType == SHIELD_EVENT_EXPIRE then
// Set GUI vars
call shEvent.setEventVariables()
set udg_ShieldExpiredEvent = 0.00
set udg_ShieldExpiredEvent = 1.00
set udg_ShieldExpiredEvent = 0.00
call shEvent.shield.destroy()
elseif shEvent.eventType == SHIELD_EVENT_BREAK then
// Set GUI vars
call shEvent.setEventVariables()
set udg_ShieldBreakEvent = 0.00
set udg_ShieldBreakEvent = 1.00
set udg_ShieldBreakEvent = 0.00
call shEvent.shield.destroy()
elseif shEvent.eventType == SHIELD_EVENT_REFRESHED then
// Set GUI vars
call shEvent.setEventVariables()
set udg_ShieldRefreshedEvent = 0.00
set udg_ShieldRefreshedEvent = 1.00
set udg_ShieldRefreshedEvent = 0.00
// Update fields in case event was caught and variables changed.
call shEvent.getEventVariables()
elseif shEvent.eventType == SHIELD_EVENT_STACKED then
// Set GUI vars
call shEvent.setEventVariables()
set udg_ShieldStackedEvent = 0.00
set udg_ShieldStackedEvent = 1.00
set udg_ShieldStackedEvent = 0.00
// Update fields in case event was caught and variables changed.
call shEvent.getEventVariables()
endif
// Resets variables
call resetEventVariables()
// Set the next ShieldEvent that will be run
set first = shEvent.next
// Free resources
call shEvent.destroy()
endloop
endmethod
private static method onInit takes nothing returns nothing
set first = 0
set last = 0
endmethod
endstruct
struct ShieldEngine
/*
Shared variables
*/
private static timer clock
private static timer tickTimer
private static trigger preReducTrigger
private static trigger postReducTrigger
private static trigger tickTrigger
private static trigger unregisterTrigger
private static boolean array isInSystem
private static ShieldWielder array unitToWielder
private static ShieldWielder firstWielder
private static ShieldWielder lastWielder
private static method ApplyShields takes integer procType returns nothing
local Shield shield //= 0
local real blockedDmg
local unit dmgTarget = udg_DamageEventTarget
local integer uId = GetUnitUserData(dmgTarget)
local ShieldWielder wielder = unitToWielder[uId]
local boolean isDmgSpell = udg_IsDamageSpell
local boolean isDmgAtt = udg_IsDamageAttack
set wielder.lastDamageTaken = TimerGetElapsed(clock)
/*
Loop through shields, applying one by one until either no shield left or no damage left
Shields order criterias
1/ Shield that can only take specific damage (only spell/attack) before shields that can absorb all
2/ Shield that nullify one instance of damage before those blocking amount of damage
3/ Order by timeLeft
*/
if procType == SHIELD_PROC_PREREDUC then
if isDmgSpell then
set shield = wielder.firstPreReducSpell
elseif isDmgAtt then
set shield = wielder.firstPreReducAttack
endif
elseif procType == SHIELD_PROC_POSTREDUC then
if isDmgSpell then
set shield = wielder.firstPostReducSpell
elseif isDmgAtt then
set shield = wielder.firstPostReducAttack
endif
endif
// Loop through shields against specific damage
loop
exitwhen shield == 0 or udg_DamageEventAmount == 0.00
if shield.shieldType == SHIELD_TYPE_INSTANCE and shield.currentHP >= 1.00 then
set shield.lastAbsorbedDamage = udg_DamageEventAmount
set udg_DamageEventAmount = 0.00
set shield.currentHP = shield.currentHP - 1.00
call wielder.AdjustBarAfterDamage(shield.shieldHpType, 1.00)
call ShieldEvent.FireEvent(SHIELD_EVENT_ABSORB, shield)
if shield.currentHP == 0 and shield.breakAtZero then
call wielder.RemoveShield(shield)
call ShieldEvent.FireEvent(SHIELD_EVENT_BREAK, shield)
endif
elseif shield.shieldType == SHIELD_TYPE_HP_BASED and shield.currentHP >= 0.00 then
if shield.currentHP > udg_DamageEventAmount then
set shield.currentHP = shield.currentHP - udg_DamageEventAmount
call wielder.AdjustBarAfterDamage(shield.shieldHpType, udg_DamageEventAmount)
set shield.lastAbsorbedDamage = udg_DamageEventAmount
set udg_DamageEventAmount = 0.00
call ShieldEvent.FireEvent(SHIELD_EVENT_ABSORB, shield)
else
set udg_DamageEventAmount = udg_DamageEventAmount - shield.currentHP
call wielder.AdjustBarAfterDamage(shield.shieldHpType, shield.currentHP)
set shield.lastAbsorbedDamage = shield.currentHP
set shield.currentHP = 0.00
call ShieldEvent.FireEvent(SHIELD_EVENT_ABSORB, shield)
if shield.breakAtZero then
call wielder.RemoveShield(shield)
call ShieldEvent.FireEvent(SHIELD_EVENT_BREAK, shield)
endif
endif
endif
set shield = shield.next
endloop
if procType == SHIELD_PROC_PREREDUC then
set shield = wielder.firstPreReduc
elseif procType == SHIELD_PROC_POSTREDUC then
set shield = wielder.firstPostReduc
endif
// Loop through shields against all type of damage
loop
exitwhen shield == 0 or udg_DamageEventAmount == 0.00
if shield.shieldType == SHIELD_TYPE_INSTANCE and shield.currentHP >= 1.00 then
set shield.lastAbsorbedDamage = udg_DamageEventAmount
set udg_DamageEventAmount = 0.00
set shield.currentHP = shield.currentHP - 1.00
call wielder.AdjustBarAfterDamage(shield.shieldHpType, 1.00)
call ShieldEvent.FireEvent(SHIELD_EVENT_ABSORB, shield)
if shield.currentHP == 0 and shield.breakAtZero then
call wielder.RemoveShield(shield)
call ShieldEvent.FireEvent(SHIELD_EVENT_BREAK, shield)
endif
elseif shield.shieldType == SHIELD_TYPE_HP_BASED and shield.currentHP >= 0.00 then
if shield.currentHP > udg_DamageEventAmount then
set shield.currentHP = shield.currentHP - udg_DamageEventAmount
call wielder.AdjustBarAfterDamage(shield.shieldHpType, udg_DamageEventAmount)
set shield.lastAbsorbedDamage = udg_DamageEventAmount
set udg_DamageEventAmount = 0.00
call ShieldEvent.FireEvent(SHIELD_EVENT_ABSORB, shield)
else
set udg_DamageEventAmount = udg_DamageEventAmount - shield.currentHP
call wielder.AdjustBarAfterDamage(shield.shieldHpType, shield.currentHP)
set shield.lastAbsorbedDamage = shield.currentHP
set shield.currentHP = 0.00
call ShieldEvent.FireEvent(SHIELD_EVENT_ABSORB, shield)
if shield.breakAtZero then
call wielder.RemoveShield(shield)
call ShieldEvent.FireEvent(SHIELD_EVENT_BREAK, shield)
endif
endif
endif
set shield = shield.next
endloop
endmethod
private static method ApplyShieldPreReduc takes nothing returns nothing
call ShieldEngine.ApplyShields(SHIELD_PROC_PREREDUC)
endmethod
private static method ApplyShieldPostReduc takes nothing returns nothing
call ShieldEngine.ApplyShields(SHIELD_PROC_POSTREDUC)
endmethod
private static method ShieldTickLoop takes nothing returns nothing
local ShieldWielder wielder = firstWielder
local Shield shield
local Shield array listOfFirst
local integer i
// Update every shield of every shield wielder
loop
exitwhen wielder == 0
set listOfFirst[0] = wielder.firstPreReduc
set listOfFirst[1] = wielder.firstPostReduc
set listOfFirst[2] = wielder.firstPreReducAttack
set listOfFirst[3] = wielder.firstPostReducAttack
set listOfFirst[4] = wielder.firstPreReducSpell
set listOfFirst[5] = wielder.firstPostReducSpell
set i = 0
loop
exitwhen i > 5
set shield = listOfFirst[i]
loop
exitwhen shield == 0
if shield.canRegen and shield.currentHP != shield.maxHP and (TimerGetElapsed(clock) - wielder.lastDamageTaken > shield.timeBeforeRegen) then
set shield.currentHP = RMinBJ(shield.maxHP, shield.currentHP + shield.regenPerTick)
if shield.shieldHpType == SHIELD_HP_TYPE_ALL then
set wielder.totalCurrentShield = wielder.totalCurrentShield + shield.regenPerTick
elseif shield.shieldType == SHIELD_HP_TYPE_SPELL then
set wielder.totalCurrentSpellShield = wielder.totalCurrentSpellShield + shield.regenPerTick
elseif shield.shieldType == SHIELD_HP_TYPE_ATTACK then
set wielder.totalCurrentAttackShield = wielder.totalCurrentAttackShield + shield.regenPerTick
endif
endif
if shield.isTimed then
set shield.timeLeft = shield.timeLeft - SHIELD_TICK_INTERVAL
if shield.timeLeft <= 0.00 then
call wielder.RemoveShield(shield)
call ShieldEvent.FireEvent(SHIELD_EVENT_EXPIRE, shield)
endif
endif
set shield = shield.next
endloop
set i = i +1
endloop
set wielder = wielder.next
endloop
endmethod
public static method GetUnitWielder takes unit u returns ShieldWielder
local integer uId = GetUnitUserData(u)
if isInSystem[uId] then
return unitToWielder[uId]
endif
return 0
endmethod
private static method IsUnitInSytem takes nothing returns boolean
local integer uId = GetUnitUserData(udg_DamageEventTarget)
return isInSystem[uId]
endmethod
private static method onInit takes nothing returns nothing
/*
================================================
TRIGGERS
================================================
*/
set clock = CreateTimer()
set tickTimer = CreateTimer()
set preReducTrigger = CreateTrigger()
set postReducTrigger = CreateTrigger()
set tickTrigger = CreateTrigger()
set unregisterTrigger = CreateTrigger()
call StartTimerBJ(clock, true, 99999.00)
call StartTimerBJ(tickTimer, true, SHIELD_TICK_INTERVAL)
// Check shields before damage reduction
call TriggerRegisterVariableEvent(preReducTrigger, "udg_PreDamageEvent", EQUAL, 1.00)
call TriggerAddAction(preReducTrigger, function ShieldEngine.ApplyShieldPreReduc)
call TriggerAddCondition(preReducTrigger, function ShieldEngine.IsUnitInSytem)
// Check shields after damage reduction
call TriggerRegisterVariableEvent(postReducTrigger, "udg_ArmorDamageEvent", EQUAL, 1.00)
call TriggerAddAction(postReducTrigger, function ShieldEngine.ApplyShieldPostReduc)
call TriggerAddCondition(preReducTrigger, function ShieldEngine.IsUnitInSytem)
// Loop Trigger for duration and regeneration
call TriggerRegisterTimerExpireEvent(tickTrigger, tickTimer)
call TriggerAddAction(tickTrigger, function ShieldEngine.ShieldTickLoop)
// Remove a unit from system if it is unindex by UnitEvent
call TriggerRegisterVariableEvent(unregisterTrigger, "udg_UnitIndexEvent", EQUAL, 2.00)
call TriggerRegisterVariableEvent(unregisterTrigger, "udg_DeathEvent", EQUAL, 0.50)
call TriggerRegisterVariableEvent(unregisterTrigger, "udg_DeathEvent", EQUAL, 1.00)
call TriggerRegisterVariableEvent(unregisterTrigger, "udg_DeathEvent", EQUAL, 3.00)
call TriggerAddAction(unregisterTrigger, function ShieldEngine.RemoveUnitFromSystem)
// Disable triggers until a unit is registered
call DisableTrigger(preReducTrigger)
call DisableTrigger(postReducTrigger)
call DisableTrigger(tickTrigger)
/*
================================================
Struct members init
================================================
*/
set firstWielder = 0
set lastWielder = 0
/*
================================================
GUI VARIABLES
================================================
*/
set udg_SHIELD_PROC_PREREDUC = SHIELD_PROC_PREREDUC
set udg_SHIELD_PROC_POSTREDUC = SHIELD_PROC_POSTREDUC
set udg_SHIELD_TYPE_HP_BASED = SHIELD_TYPE_HP_BASED
set udg_SHIELD_TYPE_INSTANCE = SHIELD_TYPE_INSTANCE
set udg_SHIELD_HP_TYPE_ALL = SHIELD_HP_TYPE_ALL
set udg_SHIELD_HP_TYPE_SPELL = SHIELD_HP_TYPE_SPELL
set udg_SHIELD_HP_TYPE_ATTACK = SHIELD_HP_TYPE_ATTACK
endmethod
public static method RemoveUnitFromSystem takes nothing returns nothing
local integer uId = udg_UDex
local ShieldWielder wielder
if isInSystem[uId] then
set wielder = unitToWielder[uId]
call ShieldEngine.RemoveAllShields(wielder.wielder)
endif
endmethod
public static method CheckRemoveWielder takes ShieldWielder wielder returns nothing
if wielder.totalMaxShield == 0.00 /*
*/ and wielder.totalMaxSpellShield == 0.00 /*
*/ and wielder.totalMaxAttackShield == 0.00 /*
*/ then
if wielder == firstWielder then
set firstWielder = wielder.next
set isInSystem[GetUnitUserData(wielder.wielder)] = false
call wielder.destroy()
if firstWielder == 0 then
call DisableTrigger(preReducTrigger)
call DisableTrigger(postReducTrigger)
call DisableTrigger(tickTrigger)
endif
else
set wielder.prev.next = wielder.next
set wielder.next.prev = wielder.prev
set isInSystem[GetUnitUserData(wielder.wielder)] = false
call wielder.destroy()
endif
endif
endmethod
/*
================================================
SYSTEM API
================================================
*/
// Tells if a unit is shielded
public static method IsUnitShielded takes unit u returns boolean
local integer uId = GetUnitUserData(u)
return isInSystem[uId]
endmethod
// Tells if unit has a specific shield
public static method UnitHasShield takes unit u, string name returns boolean
local ShieldWielder wielder = ShieldEngine.GetUnitWielder(u)
return wielder.HasShield(name)
endmethod
/*
The following methods remove/destroy shields.
Removing: No Event if fired, shield disappear without any trace.
Destroying: Shield Break Event is fired when destroying a shield.
*/
public static method RemoveShieldByName takes unit u, string name returns nothing
local ShieldWielder wielder = ShieldEngine.GetUnitWielder(u)
call wielder.RemoveShieldByName(name)
endmethod
public static method DestroyShieldByName takes unit u, string name returns nothing
local ShieldWielder wielder = ShieldEngine.GetUnitWielder(u)
call wielder.DestroyShieldByName(name)
endmethod
public static method RemoveShieldsByType takes unit u, integer hpType returns nothing
local ShieldWielder wielder = ShieldEngine.GetUnitWielder(u)
call wielder.RemoveShieldsByType(hpType)
endmethod
public static method RemoveAllShields takes unit u returns nothing
local ShieldWielder wielder = ShieldEngine.GetUnitWielder(u)
call wielder.RemoveShieldsByType(SHIELD_HP_TYPE_ALL)
call wielder.RemoveShieldsByType(SHIELD_HP_TYPE_SPELL)
call wielder.RemoveShieldsByType(SHIELD_HP_TYPE_ATTACK)
endmethod
public static method DestroyShieldsByType takes unit u, integer hpType returns nothing
local ShieldWielder wielder = ShieldEngine.GetUnitWielder(u)
call wielder.DestroyShieldsByType(hpType)
endmethod
public static method DestroyAllShields takes unit u returns nothing
local ShieldWielder wielder = ShieldEngine.GetUnitWielder(u)
call wielder.DestroyShieldsByType(SHIELD_HP_TYPE_ALL)
call wielder.DestroyShieldsByType(SHIELD_HP_TYPE_SPELL)
call wielder.DestroyShieldsByType(SHIELD_HP_TYPE_ATTACK)
endmethod
/*
================================================
ENTRY POINT
================================================
*/
public static method CreateShield takes /*
*/ unit source, /*
*/ unit target, /*
*/ string name, /*
*/ string sfxPath, /*
*/ string attachPoint, /*
*/ integer shieldProcType, /*
*/ integer shieldType, /*
*/ integer shieldHpType, /*
*/ boolean isStackable, /*
*/ boolean isTimed, /*
*/ real duration, /*
*/ real HP, /*
*/ boolean breakAtZero, /*
*/ boolean canRegen, /*
*/ real timeBeforeRegen, /*
*/ real regenTime /*
*/ returns nothing
local integer uId = GetUnitUserData(target)
local ShieldWielder wielder
local Shield shield
if isInSystem[uId] then
set wielder = unitToWielder[uId]
else
set wielder = ShieldWielder.create(target)
set unitToWielder[uId] = wielder
set isInSystem[uId] = true
if firstWielder == 0 then
set firstWielder = wielder
set lastWielder = wielder
call EnableTrigger(preReducTrigger)
call EnableTrigger(postReducTrigger)
call EnableTrigger(tickTrigger)
else
set wielder.prev = lastWielder
set lastWielder.next = wielder
set lastWielder = lastWielder.next
endif
endif
// Check if shield with same name and types exist
set shield = wielder.FindShieldByNameAndTypes(name, shieldProcType, shieldType, shieldHpType)
// If didn't exist, create new one
if shield == 0 then
set shield = Shield.create(source, target, name, sfxPath, attachPoint, shieldProcType, shieldType, shieldHpType, isStackable, isTimed, duration, HP, breakAtZero, canRegen, timeBeforeRegen, regenTime)
call ShieldEvent.FireEvent(SHIELD_EVENT_CREATED, shield)
else
// if both previous shield and new are stackable, then stack, else refresh
if shield.isStackable and isStackable then
call shield.Stack(duration, HP)
else
call shield.Refresh(duration, HP)
endif
endif
endmethod
/*
================================================
SIMPLIFIED FUNCTIONS
================================================
*/
public static method CreateTimedShield takes unit source, unit target, string name, real duration, real HP returns nothing
call ShieldEngine.CreateShield(source, target, name, SHIELD_DEFAULT_SFX_PATH, SHIELD_DEFAULT_ATTACH_POINT, SHIELD_PROC_POSTREDUC, SHIELD_TYPE_HP_BASED, SHIELD_HP_TYPE_ALL, false, true, duration, HP, true, false, 0.0, 0.0)
endmethod
endstruct
endlibrary