Dirac
22710180
- Reaction score
- 147
JASS:
library Damage /* v2.0.7
____ _ _ _____ _____
| _ \ /\ | \ / | /\ / ___\ | ___|
| | | | / \ | \/ | / \ | / _ | __|
| |_| | / /\ \ | |\/| | / /\ \ | |_\ \ | |___
|____/ /_/ \_\ |_| |_| /_/ \_\ \_____/ |_____|
*/uses/* one of the following
*/ optional AIDS /* thehelper.net/forums/showthread.php/130752-Advanced-Indexing-Data-Storage
*/ optional UnitIndexer /* hiveworkshop.com/forums/jass-functions-413/system-unit-indexer-172090/
and
*/ LinkedListModule /* thehelper.net/forums/showthread.php/168775-LinkedListModule
Description:
Damage management, detection and manipulation. Allows full control of damage done
by units
Extensions:
DamageMod v1.0.7 - thehelper.net/forums/showthread.php/168198-Damage-Mod
DamageType v1.0.1 - thehelper.net/forums/showthread.php/168364-DamageType
DamageId v1.0.1 - thehelper.net/forums/showthread.php/168970-DamageId
Armor v1.0.0 - thehelper.net/forums/showthread.php/168282-Armor
RawDamage v1.0.5 - thehelper.net/forums/showthread.php/168257-RawDamage
AmplifyDamage v1.0.4 - thehelper.net/forums/showthread.php/168255-AmplifyDamage
DampenDamage v1.0.4 - thehelper.net/forums/showthread.php/168256-DampenDamage
Shield v1.0.6 - thehelper.net/forums/showthread.php/168203-Shield
Demo map:
mediafire.com/?o8hxybolawjeun5
Special thanks to:
-Nestharus (Damage library concept)
-PurgeandFire111 (original idea)
Important note:
If there is another damage system in your map, replace it with this one, otherwise
it will cause collision issues. This system provides all the useful damage tools
you'll ever need.
**********************************************************************/
globals
//*********************************************************************
// For damage event recycling upon deindexing. Resets the main damage
// event recognition trigger whenever that amount of units are
// deindexed.
private constant integer MAX_WASTED_UNITS = 20
//*********************************************************************
// An ability based off item life bonus that should add a very high
// amount of hp to the unit, the higher the better
constant integer ABILITY_LIFE_BONUS = 'DcLB'
endglobals
/**********************************************************************
*
* function RegisterGlobalDamage takes code func returns triggercondition
* - Runs after damage mods and unit damage events.
* function UnregisterGlobalDamage takes triggercondition whichCondition returns nothing
* - Unregister the function from handling damage
* function RegisterUnitDamageDealtEvent takes unit whichUnit, code func returns DamageDealtEvent
* function RegisterUnitDamageTakenEvent takes unit whichUnit, code func returns DamageTakenEvent
* - Wrappers for the DamageDealtEvent and DamageTakenEvent register methods
* function UnregisterUnitDamageDealtEvent takes DamageDealtEvent whichEvent returns nothing
* function UnregisterUnitDamageTakenEvent takes DamageTakenEvent whichEvent returns nothing
* - Wrappers for the DamageDealtEvent and DamageTakenEvent unregister methods
* function GetUnitDamageReduction takes unit whichUnit, attacktype attackType, damagetype damageType returns real
* - Returns the amount of damage the unit takes from a specific type
* - of damage. 1.0 means it takes 100% of the damage.
* function GetUnitArmor takes unit whichUnit returns real
* - Returns the unit's armor, losses it's accuracy when dealing with
* - negative armor.
*
***********************************************************************
*
* struct Damage
*
* - This struct manages all damage done and taken.
*
* readonly static real amount
* - Total damage dealt.
* readonly static real dealt
* - Actual damage received by the unit:
* - Takes into account prevented and added damage.
* readonly static real recount
* - Recount damage done by all units.
* readonly static unit target
* readonly static unit source
* - Event units.
* readonly static integer targetId
* readonly static integer sourceId
* - The custom value of those units.
* readonly integer data
* - The damage event attached data.
* static boolean enabled
* - If false the system stops recording damage.
* static method prevent takes real howMuch returns real
* - Prevents damage done, call only inside damage handling method.
* - Note: after this call the unit's max health changes.
* static method add takes real howMuch returns nothing
* - Increases damage done, damage dealt this way is pure, call only
* - inside damage handling methods.
*
**********************************************************************
*
* struct DamageDealtEvent and DamageTakenEvent
*
* - Both structs have the same API, they record the unit's damage dealt
* - or taken.
*
* integer data
* - You're able to attach and retreive data from instances.
* static method register takes integer unitId, code func returns thistype
* - Registers unit's damage through the given code.
* method unregister takes nothing returns nothing
* - Unregisters the given damage event.
*
**********************************************************************
*
* struct UnitDamage
*
* - This struct manages damage done by units, uses unit custom value as
* - instances.
*
* readonly real dealt
* readonly real taken
* - Recount of unit damage, doesn't take into account current damage.
* boolean enabled
* - If false then damage events by this unit wont fire.
* method reset takes nothing returns nothing
* - Clears all the damage done by the given unit.
*
*********************************************************************/
globals
private trigger globalTrig =CreateTrigger()
private integer currentData =0
private real array unitDamageDealt
private real array unitDamageTaken
endglobals
/*********************************************************************/
//! runtextmacro optional DAMAGE_TYPE_EX1()
//! runtextmacro optional DAMAGE_MOD_EX1()
//! runtextmacro optional DAMAGE_ID_EX1()
/*********************************************************************/
function RegisterGlobalDamage takes code func returns triggercondition
return TriggerAddCondition(globalTrig,Filter(func))
endfunction
function UnregisterGlobalDamage takes triggercondition whichCondition returns nothing
call TriggerRemoveCondition(globalTrig,whichCondition)
endfunction
/*********************************************************************/
struct UnitDamage extends array
boolean enabled
method operator dealt takes nothing returns real
return unitDamageDealt[this]
endmethod
method operator taken takes nothing returns real
return unitDamageTaken[this]
endmethod
method reset takes nothing returns nothing
set unitDamageDealt[this]=0
endmethod
endstruct
/*********************************************************************/
//! runtextmacro DAMAGE_EVENT_REGISTRATION_SETUP("Dealt")
//! runtextmacro DAMAGE_EVENT_REGISTRATION_SETUP("Taken")
//! textmacro DAMAGE_EVENT_REGISTRATION_SETUP takes NAME
struct Damage$NAME$Event extends array
implement LinkedList
//*********************************************************************
// The instance's corresponding unit id.
private thistype root
//*********************************************************************
// First instance from the list attached to an unit.
private thistype node
//*********************************************************************
// The instance's trigger to be evaluated.
private trigger trig
//*********************************************************************
// If false the event wont be fired.
boolean enabled
//*********************************************************************
// Value assigned by the user.
integer data
static method fire takes thistype unitId returns nothing
local thistype exit = unitId.node
local thistype this = exit
//*********************************************************************
// If node points to nowhere then the list is empty.
if this==0 then
return
endif
//*********************************************************************
// Iterate through the list firing all the triggers.
loop
set currentData=data
if enabled then
call TriggerEvaluate(trig)
endif
set this=next
exitwhen this==exit
endloop
endmethod
static method register takes thistype unitId, code func returns thistype
local thistype this
if unitId.node==0 then
set this=createNode()
set unitId.node=this
else
set this=allocate()
call unitId.node.insertNode(this)
endif
set root=unitId
if this.trig==null then
set trig=CreateTrigger()
else
call TriggerClearConditions(trig)
endif
set enabled=true
call TriggerAddCondition(trig,Filter(func))
return this
endmethod
method unregister takes nothing returns nothing
call this.removeNode()
if next==this then
set root.node=0
elseif this==root.node then
set root.node=next
endif
call this.deallocate()
endmethod
static method clear takes thistype unitId returns nothing
call unitId.node.flushNode()
set unitId.node=0
endmethod
endstruct
//! endtextmacro
/*********************************************************************/
private module DamageModule
readonly static real recount =0
readonly static real dealt =0
readonly static real amount =0
readonly static unit source =null
readonly static unit target =null
readonly static integer sourceId=0
readonly static integer targetId=0
//*********************************************************************
// Keeps track of the amount of wasted unit indexes.
private static integer count =0
//*********************************************************************
// Keeps track of the additional damage dealt through the Damage.add
// method, this damage is applied at the end of the evaluation to
// prevent the unit from dying.
private static real toAdd =0
//*********************************************************************
// The main trigger that detects all damage done ingame
private static trigger dmgTrig =CreateTrigger()
//*********************************************************************
// Timer used to remove the life bonus ability from units who had
// damage prevented to them.
private static timer after =CreateTimer()
//*********************************************************************
// Stack used for simultaneous damage prevention, stores all the units
// inside it and then it gets cleared.
private static boolean array stackIs
private static integer array stack
//*********************************************************************
// Linked list of all units indexed.
private static integer array next
private static integer array prev
private static method handleDamage takes nothing returns nothing
local real hp
local unit u
//*********************************************************************
// Loops through the stack removing the life bonus ability from units,
// after that the unit gets removed from the stack.
loop
exitwhen stack[0]==0
static if LIBRARY_UnitIndexer then
set u=GetUnitById(stack[0])
else
set u=GetIndexUnit(stack[0])
endif
set hp=GetWidgetLife(u)
call UnitRemoveAbility(u,ABILITY_LIFE_BONUS)
call SetWidgetLife(u,hp)
set stackIs[stack[0]]=false
set stack[0]=stack[stack[0]]
endloop
endmethod
static method prevent takes real howMuch returns real
local real hp=GetWidgetLife(target)
set howMuch=RMinBJ(howMuch,amount)
set dealt=RMaxBJ(dealt-howMuch,0)
if dealt>hp or howMuch==0 then
return howMuch
endif
//*********************************************************************
// This is where the stack for damage prevention gets created. Only
// adds the life bonus ability if needed (remember that the damage
// prevention comes before the damage is actually done, so if the unit
// has it's max hp then adding life to it would bug.)
if not(stackIs[targetId]) and (hp+howMuch>=GetUnitState(target,UNIT_STATE_MAX_LIFE)) then
set stack[targetId]=stack[0]
set stack[0]=targetId
set stackIs[targetId]=true
call UnitAddAbility(target,ABILITY_LIFE_BONUS)
endif
call SetWidgetLife(target,hp+howMuch)
return howMuch
endmethod
static method operator enabled= takes boolean state returns nothing
if state then
call EnableTrigger(dmgTrig)
else
call DisableTrigger(dmgTrig)
endif
endmethod
static method operator data takes nothing returns integer
return currentData
endmethod
//! runtextmacro optional DAMAGE_ID_EX2()
static method add takes real howMuch returns nothing
set dealt=dealt+howMuch
set toAdd=toAdd+howMuch
endmethod
//*********************************************************************
// First damage handler method of the entire system.
private static method onDamage takes nothing returns boolean
//*********************************************************************
// Stores the previous damage instance inside locals, at the end of
// the execution the values are reverted.
local real prevAmount =amount
local real prevDealt =dealt
local real prevAdd =toAdd
local unit prevSource =source
local unit prevTarget =target
local integer prevSourceId =sourceId
local integer prevTargetId =targetId
//! runtextmacro optional DAMAGE_ID_EX3()
set amount=GetEventDamage()
set source=GetEventDamageSource()
set target=GetTriggerUnit()
set sourceId=GetUnitUserData(source)
set targetId=GetUnitUserData(target)
set toAdd=0
//! runtextmacro optional DAMAGE_ID_EX4()
static if OVERKILL then
set dealt=amount
else
set dealt=RMinBJ(amount,GetWidgetLife(target))
endif
//! runtextmacro optional DAMAGE_TYPE_EX2()
//! runtextmacro optional DAMAGE_MOD_EX2()
//*********************************************************************
// Fires the trigger attached to the unit's custom value, each trigger
// with a number of conditions that are added on the Register calls.
if UnitDamage[sourceId].enabled then
call DamageDealtEvent.fire(sourceId)
endif
if UnitDamage[targetId].enabled then
call DamageTakenEvent.fire(targetId)
endif
call TriggerEvaluate(globalTrig)
//*********************************************************************
// Keeping track of all damage dealt and taken...
set unitDamageDealt[sourceId]=unitDamageDealt[sourceId]+dealt
set unitDamageTaken[targetId]=unitDamageTaken[targetId]+dealt
set recount=recount+dealt
//*********************************************************************
// Applies all missing damage left to be dealt.
if 0<toAdd then
set enabled=false
call UnitDamageTarget(Damage.source,Damage.target,toAdd,true,false,ATTACK_TYPE_CHAOS,DAMAGE_TYPE_UNIVERSAL,null)
set enabled=true
endif
//*********************************************************************
// Reverts the variables to their previous values
set amount=prevAmount
set dealt=prevDealt
set source=prevSource
set target=prevTarget
set sourceId=prevSourceId
set targetId=prevTargetId
set toAdd=prevAdd
//! runtextmacro optional DAMAGE_ID_EX5()
//*********************************************************************
// When amount equals to 0 as well as any other variable it means that
// no more damage is left to be done so it starts the prevention timer
// if stack isn't empty
if not(stack[0]==0) and amount==0 then
call TimerStart(after,0,false,function thistype.handleDamage)
endif
set prevSource=null
set prevTarget=null
return false
endmethod
private static method onIndex takes nothing returns boolean
local integer index
local unit indexUnit
static if LIBRARY_UnitIndexer then
set index=GetIndexedUnitId()
set indexUnit=GetIndexedUnit()
else
set index=AIDS_GetIndexOfEnteringUnit()
set indexUnit=AIDS_GetEnteringIndexUnit()
endif
//*********************************************************************
// Adds the unit to the list.
set prev[next[0]]=index
set next[index]=next[0]
set next[0]=index
set prev[index]=0
//! runtextmacro optional DAMAGE_ID_EX6()
set UnitDamage[index].enabled=true
call TriggerRegisterUnitEvent(dmgTrig,indexUnit,EVENT_UNIT_DAMAGED)
set indexUnit=null
return false
endmethod
private static method onDeIndex takes nothing returns boolean
local integer i = 0
local integer index
static if LIBRARY_UnitIndexer then
set index=GetIndexedUnitId()
else
set index=AIDS_GetDecayingIndex()
endif
//*********************************************************************
// Deletes the given unit from the list.
set next[prev[index]]=next[index]
set prev[next[index]]=prev[index]
//*********************************************************************
// In order to prevent a high amount of unused evets added to the
// trigger whenever plenty of units are deindexed it refreshes the
// trigger's events using a linked list of indexed units.
set count=count+1
if count==MAX_WASTED_UNITS then
call DestroyTrigger(dmgTrig)
set dmgTrig=CreateTrigger()
call TriggerAddCondition(dmgTrig,Filter(function thistype.onDamage))
loop
set i=next<i>
exitwhen i==0
static if LIBRARY_UnitIndexer then
call TriggerRegisterUnitEvent(dmgTrig,GetUnitById(i),EVENT_UNIT_DAMAGED)
else
call TriggerRegisterUnitEvent(dmgTrig,GetIndexUnit(i),EVENT_UNIT_DAMAGED)
endif
endloop
set count=0
endif
//*********************************************************************
// Prepares the triggers for the next unit.
call DamageDealtEvent.clear(index)
call DamageTakenEvent.clear(index)
return false
endmethod
private static method onInit takes nothing returns nothing
debug if GetObjectName(ABILITY_LIFE_BONUS)=="" then
debug call BJDebugMsg("ERROR: Life Bonus Ability doesn't exist")
debug return
debug endif
call TriggerAddCondition(dmgTrig,Filter(function thistype.onDamage))
//*********************************************************************
// Detection of units that enter the map.
static if LIBRARY_UnitIndexer then
call RegisterUnitIndexEvent(Condition(function thistype.onIndex),UnitIndexer.INDEX)
call RegisterUnitIndexEvent(Condition(function thistype.onDeIndex),UnitIndexer.DEINDEX)
else
call AIDS_RegisterOnEnter(Condition(function thistype.onIndex))
call AIDS_RegisterOnDeallocate(Condition(function thistype.onDeIndex))
endif
endmethod
endmodule
struct Damage extends array
//*********************************************************************
// Module initialization calls perform before any other.
implement DamageModule
endstruct
/*********************************************************************/
function RegisterUnitDamageDealtEvent takes unit whichUnit, code func returns DamageDealtEvent
return DamageDealtEvent.register(GetUnitUserData(whichUnit),func)
endfunction
function RegisterUnitDamageTakenEvent takes unit whichUnit, code func returns DamageTakenEvent
return DamageTakenEvent.register(GetUnitUserData(whichUnit),func)
endfunction
function UnregisterUnitDamageDealtEvent takes DamageDealtEvent whichEvent returns nothing
call whichEvent.unregister()
endfunction
function UnregisterUnitDamageTakenEvent takes DamageTakenEvent whichEvent returns nothing
call whichEvent.unregister()
endfunction
/*********************************************************************/
endlibrary</i>