- Joined
- Sep 26, 2009
- Messages
- 9,534
DamageEngine GUI has been constantly hacked at to make it what it is. Today, I was trying to locate a bug and was re-writing the script as I went through it, just to make sure my logic is sound. The result is a series of minor changes (I fixed the bug, that's what matters) but now contains full JASS efficiency and readability.
JASS:
library DamageEngineGUI initializer Init requires RealEvent //and GUI Unit Indexer
/*
API
CreateDamageEventTrigger(string variableName, code whichCodeToRun) -> trigger
- Provided by the library RealEvent
A shorter amount of code so that you don't have to type everything out.
I have provided constant strings for easier syntax:
DAMAGE_EVENT - damage before HP was reduced. Will not fire for 0 damage.
ZERO_DAMAGE_EVENT - if GetEventDamage() was equal to 0
AFTER_DAMAGE_EVENT - after unit's HP has been reduced (if it had been)
DAMAGE_MODIFIER_EVENT - modify damage amount directly
DAMAGE_ABSORB_EVENT - runs if damage was converted to a heal
DAMAGE_SHIELD_EVENT - subtract damage amount from a potential shield.
- Unlike with the modification event, fires after DamageEvent has run.
AfterDamageEvent will not fire if the shield completely blocked the damage.
DAMAGE_BLOCK_EVENT - fires after ShieldDamageEvent has run to identify blocks.
Damage.overrideNext()
- Prevents internal spell modification and modifier events 1-3 from firing for
the next UnitDamageTarget call
Damage[real value] -> real
- For use in a modifier event to convert values based on previous modification
so as to remove the need for modification priorities aside from shielding.
Damage.addAmount(real amount)
- Add a value to the damage based on the factor of previous modification
writeonly
Damage.enabled = boolean -------- dis/enables DamageEventTrigger
Damage.nextType = integer -------- set type before dealing triggered damage
readonly
Damage.source -> unit -------- source unit of damage dealt
Damage.target -> unit -------- target unit of damage dealt
Damage.isSpell -> boolean -------- true for spell damage, false for physical
Damage.prevAmount -> real -------- absolute amount of GetEventDamage()
read/write
Damage.amount -> real -------- the amount of damage a unit will receive
Damage.type -> integer -------- negative values cause the unit to explode
Damage.override -> boolean -------- prevent additional modifiers from running
*/
globals
private constant integer SPELL_ID = 'A000' // rawcode of Detect Spell Damage
private constant integer BLOCK_ID = 'A001' // rawcode of Cheat Death Ability
private constant integer DUMMY_ID = 'uloc' // a dummy created just for preloading abilities
private constant boolean HAVE_VARIABLES = true // true if you already have DamageEvent's GUI variables in your map
private constant boolean BRACERS_ABIL = false // true if you have removed Spell Reduction ability from Runed Bracers
endglobals
//What criteria must be met for a unit to have a damage event?
private function UnitFilter takes unit u returns boolean
return GetUnitAbilityLevel(u, 'Aloc') == 0
endfunction
//Applied to spell damage before events are run.
private function GetSpellDamageMultiplier takes unit u returns real
local real r = 1.00
local boolean hero = IsUnitType(u, UNIT_TYPE_HERO)
if udg_DamageEventOverride then
return r
endif
if IsUnitType(u, UNIT_TYPE_ETHEREAL) and not hero then
set r = r*1.67
endif
if GetUnitAbilityLevel(u, 'Aegr') > 0 then
set r = r*0.80
endif
static if not BRACERS_ABIL then
if hero and UnitHasItemOfTypeBJ(u, 'brac') then
set r = r*0.67
endif
endif
return r
endfunction
//The only GUI variables that you need for this to work:
static if not HAVE_VARIABLES then
globals
unit udg_DamageEventSource = null
unit udg_DamageEventTarget = null
real udg_DamageEventAmount = 0.00
real udg_DamageEventPrevAmount = 0.00
real udg_DamageEvent = 0.00
real udg_DamageModifierEvent = 0.00
real udg_AfterDamageEvent = 0.00
real udg_ZeroDamageEvent = 0.00
real udg_DamageShieldEvent = 0.00
real udg_DamageBlockEvent = 0.00
real udg_DamageAbsorbEvent = 0.00
trigger udg_DamageEventTrigger = null
trigger udg_ClearDamageEvent = null
integer udg_DamageEventType = 0
integer udg_NextDamageType = 0
boolean udg_NextDamageOverride = false
boolean udg_DamageEventOverride = false
boolean udg_IsDamageSpell = false
endglobals
endif
globals
//convenient syntax
constant string DAMAGE_EVENT = "udg_DamageEvent"
constant string AFTER_DAMAGE_EVENT = "udg_AfterDamageEvent"
constant string DAMAGE_MODIFIER_EVENT = "udg_DamageModifierEvent"
constant string ZERO_DAMAGE_EVENT = "udg_ZeroDamageEvent"
constant string DAMAGE_SHIELD_EVENT = "udg_DamageShieldEvent"
constant string DAMAGE_BLOCK_EVENT = "udg_DamageBlockEvent"
constant string DAMAGE_ABSORB_EVENT = "udg_DamageAbsorbEvent"
private keyword onDamage
private boolexpr checkEvent
endglobals
struct Damage extends array
//writeonly
static method operator enabled= takes boolean flag returns nothing
if flag then
call EnableTrigger(udg_DamageEventTrigger)
else
call DisableTrigger(udg_DamageEventTrigger)
endif
endmethod
static method operator nextType= takes integer nt returns nothing
set udg_NextDamageType = nt
endmethod
static method overrideNext takes nothing returns nothing
set udg_NextDamageOverride = true
endmethod
static method operator [] takes real value returns real
if udg_DamageEventPrevAmount == 0.00 then
debug call BJDebugMsg("DamageEngine error - don't try to modify value from a Zero damage event!")
return 0.00
endif
return value*udg_DamageEventAmount/udg_DamageEventPrevAmount
endmethod
static method addValue takes real value returns nothing
set udg_DamageEventAmount = udg_DamageEventAmount + Damage[value]
endmethod
//readonly
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 prevAmount takes nothing returns real
return udg_DamageEventPrevAmount
endmethod
static method operator isSpell takes nothing returns boolean
return udg_IsDamageSpell
endmethod
//read/write
static method operator amount takes nothing returns real
return udg_DamageEventAmount
endmethod
static method operator amount= takes real value returns nothing
set udg_DamageEventAmount = value
endmethod
//read/write
static method operator type takes nothing returns integer
return udg_DamageEventType
endmethod
static method operator type= takes integer value returns nothing
set udg_DamageEventType = value
endmethod
//read/write
static method operator override takes nothing returns boolean
return udg_DamageEventOverride
endmethod
static method operator override= takes boolean flag returns nothing
set udg_DamageEventOverride = flag
endmethod
private static integer recursionN = 0
private static integer recursionMax = 0
private static trigger trig = null
private static real fixedLife = 0.00
private unit rsource
private unit rtarget
private real ramount
private real rprevAmt
private real rtype
private boolean rspell
static method reset takes nothing returns nothing
set .recursionN = .recursionN - 1
set udg_DamageEventSource = Damage(.recursionN).rsource
set udg_DamageEventTarget = Damage(.recursionN).rtarget
set udg_DamageEventAmount = Damage(.recursionN).ramount
set udg_DamageEventPrevAmount = Damage(.recursionN).rprevAmt
set udg_DamageEventType = Damage(.recursionN).rtype
set udg_IsDamageSpell = Damage(.recursionN).rspell
endmethod
private static boolean resetNext = true
static method checkEvent takes nothing returns boolean
if .resetNext then
set udg_NextDamageType = 0
set udg_NextDamageOverride = false
else
set .resetNext = true
endif
if .trig != null then
call DestroyTrigger(.trig)
set .trig = null
if udg_IsDamageSpell then
if .fixedLife <= 0.405 then
call SetUnitExploded(udg_DamageEventTarget, udg_DamageEventType < 0)
call SetWidgetLife(udg_DamageEventTarget, 0.41)
call DisableTrigger(udg_DamageEventTrigger)
call UnitDamageTarget(udg_DamageEventSource, udg_DamageEventTarget, -10.00, false, false, null, DAMAGE_TYPE_UNIVERSAL, null)
call EnableTrigger(udg_DamageEventTrigger)
else
call SetWidgetLife(udg_DamageEventTarget, .fixedLife)
endif
elseif UnitRemoveAbility(udg_DamageEventTarget, BLOCK_ID) then
call SetWidgetLife(udg_DamageEventTarget, .fixedLife)
endif
if udg_DamageEventAmount > 0.001 then
set udg_AfterDamageEvent = 0.00
set udg_AfterDamageEvent = 1.00
set udg_AfterDamageEvent = 0.00
set .resetNext = false
call .checkEvent()
endif
if .recursionN > 0 then
call .reset()
endif
endif
return false
endmethod
private static timer zeroHour = CreateTimer()
private static boolean started = false
private static method onExpire takes nothing returns nothing
set .started = .checkEvent()
endmethod
/*internal*/ static method onDamage takes nothing returns boolean
local boolean override = udg_DamageEventOverride
local boolean clear = false
local real life
local real maxLife
set .resetNext = false
call .checkEvent()
if .running then
set .recursionMax = .recursionMax + 1
if .recursionMax < 16 then
set Damage(.recursionN).rsource = udg_DamageEventSource
set Damage(.recursionN).rtarget = udg_DamageEventTarget
set Damage(.recursionN).ramount = udg_DamageEventAmount
set Damage(.recursionN).rprevAmt = udg_DamageEventPrevAmount
set Damage(.recursionN).rtype = udg_DamageEventType
set Damage(.recursionN).rspell = udg_IsDamageSpell
set .recursionN = .recursionN + 1
else
debug call BJDebugMsg("DAMAGE ENGINE INFINITE LOOP!")
return false
endif
else
set .running = true
set clear = true
endif
set udg_DamageEventSource = GetEventDamageSource()
set udg_DamageEventTarget = GetTriggerUnit()
set udg_DamageEventAmount = GetEventDamage()
set udg_DamageEventPrevAmount = udg_DamageEventAmount
set udg_DamageEventType = udg_NextDamageType
set udg_NextDamageType = 0
set udg_DamageEventOverride = udg_NextDamageOverride
set udg_NextDamageOverride = false
if udg_DamageEventAmount == 0.00 then
set udg_ZeroDamageEvent = 0.00
set udg_ZeroDamageEvent = 1.00
set udg_ZeroDamageEvent = 0.00
else
if not .started then
set .started = true
call TimerStart(.zeroHour, 0.00, false, function thistype.onExpire)
endif
set udg_IsDamageSpell = udg_DamageEventAmount < 0.00
if udg_IsDamageSpell then
set udg_DamageEventPrevAmount = -udg_DamageEventAmount
set udg_DamageEventAmount = udg_DamageEventPrevAmount*GetSpellDamageMultiplier(udg_DamageEventTarget)
endif
if not udg_DamageEventOverride then
set udg_DamageModifierEvent = 0.00
set udg_DamageModifierEvent = 1.00
set udg_DamageModifierEvent = 0.00
endif
set udg_DamageEventOverride = override
if udg_DamageEventAmount != 0.00 then
if udg_DamageEventAmount > 0 then
set udg_DamageEvent = 0.00
set udg_DamageEvent = 1.00
set udg_DamageEvent = 0.00
set udg_DamageShieldEvent = 0.00
set udg_DamageShieldEvent = 1.00
set udg_DamageShieldEvent = 0.00
else
set udg_DamageAbsorbEvent = 0.00
set udg_DamageAbsorbEvent = 1.00
set udg_DamageAbsorbEvent = 0.00
endif
else
set udg_DamageBlockEvent = 0.00
set udg_DamageBlockEvent = 1.00
set udg_DamageBlockEvent = 0.00
endif
set .trig = CreateTrigger()
call TriggerAddCondition(.trig, checkEvent)
set life = GetWidgetLife(udg_DamageEventTarget)
set maxLife = GetUnitState(udg_DamageEventTarget, UNIT_STATE_MAX_LIFE)
if udg_IsDamageSpell then
set .fixedLife = life - (DamageEventPrevAmt - (DamageEventPrevAmt - udg_DamageEventAmount))
if life + udg_DamageEventAmount*0.75 < maxLife then
set life = life + udg_DamageEventPrevAmount*0.50
else
set life = RMaxBJ(1.00, maxLife - udg_DamageEventPrevAmount*0.50)
call SetWidgetLife(udg_DamageEventTarget, life)
set life = (life + maxLife)*0.50
endif
else
if actualDamage != udg_DamageEventPrevAmount then
set life = life + udg_DamageEventPrevAmount - udg_DamageEventAmount
if life > maxLife then
set .fixedLife = life - udg_DamageEventPrevAmount
call UnitAddAbility(udg_DamageEventTarget, BLOCK_ID)
endif
call SetWidgetLife(udg_DamageEventTarget, RMaxBJ(0.42, life))
endif
set life = RMaxBJ(0.41, life - udg_DamageEventPrevAmount*0.50)
endif
call TriggerRegisterUnitStateEvent(.trig, udg_DamageEventTarget, UNIT_STATE_LIFE, ConvertLimitOp(IntegerTertiaryOp(udg_IsDamageSpell, 4, 0)), life)
endif
if clear then
set .recursionMax = 0
set .running = false
elseif udg_DamageEventPrevAmount == 0.00 then
call .reset()
endif
return false
endmethod
endstruct
private function NewDamageTrigger takes nothing returns nothing
set udg_DamageEventTrigger = CreateTrigger()
call TriggerAddCondition(udg_DamageEventTrigger, Filter(function Damage.onDamage))
endfunction
globals
private integer array stackRef
private unit array stack
private integer count = 0
private integer wasted = 0
endglobals
private function OnCreate takes nothing returns nothing
local integer id = udg_UDex
local unit u = udg_UDexUnits[id]
if UnitFilter(u) then
set stackRef[id] = idN
set stack[idN] = u
set count = count + 1
call UnitAddAbility(u, SPELL_ID)
call UnitMakeAbilityPermanent(u, SPELL_ID, true)
call TriggerRegisterUnitEvent(udg_DamageEventTrigger, u, EVENT_UNIT_DAMAGED)
endif
set u = null
endfunction
private function OnRemove takes nothing returns nothing
local integer index = stackRef[udg_UDex]
if n >= 0 then
set count = count - 1
set stackRef[GetUnitUserData(stack[count])] = index
set stack[index] = stack[count]
set stackRef[udg_UDex] = -1
set wasted = wasted + 1
if wasted == 32 then
set wasted = 0
call DestroyTrigger(udg_DamageEventTrigger)
call NewDamageTrigger()
set index = count
loop
exitwhen index == 0
set index = index - 1
call TriggerRegisterUnitEvent(udg_DamageEventTrigger, stack[index], EVENT_UNIT_DAMAGED)
endloop
endif
endif
endfunction
private function Init takes nothing returns nothing
local unit u
local integer i = 15
loop
call SetPlayerAbilityAvailable(Player(i), SPELL_ID, false)
exitwhen i == 0
set i = i - 1
endloop
set checkEvent = Filter(function Damage.checkEvent)
set udg_ClearDamageEvent = CreateTrigger()
call TriggerAddCondition(udg_ClearDamageEvent, checkEvent)
call NewDamageTrigger()
set u = CreateUnit(Player(15), DUMMY_ID, 0, 0, 0)
call UnitAddAbility(u, BLOCK_ID)
call UnitAddAbility(u, SPELL_ID)
call RemoveUnit(u)
call CreateRealEventTrigger("udg_UnitIndexEvent", 1.00, function OnCreate)
call CreateRealEventTrigger("udg_UnitIndexEvent", 2.00, function OnRemove)
set u = null
endfunction
function CreateDamageEventTrigger takes string whichEvent, code toRun returns trigger
return CreateRealEventTrigger(whichEvent, 1.00, toRun)
endfunction
endlibrary
library_once RealEvent
globals
private trigger tempTrig
endglobals
function CreateRealEventTrigger takes string variable, real value, code run returns trigger
set tempTrig = CreateTrigger()
call TriggerRegisterVariableEvent(tempTrig, variable, EQUAL, value)
if run != null then
call TriggerAddCondition(tempTrig, Filter(run))
endif
return tempTrig
endfunction
endlibrary
Last edited: