- Joined
- Apr 24, 2012
- Messages
- 5,111
JASS:
library AttackIndexer /* v1.1
**************************************************************************************
*
* !!! THIS SYSTEM CANNOT DETECT IF AN ATTACK IS LAUNCHED !!!
*
* A system for indexing attacks and attaching data on Wc3 attack projectiles
*
* The system indexes an attack whenever it is attempted.
* An attack is deindexed whenever the unit deals physical damage.
* Take note that the damage is modified to zero damage whenever the
* the PDD detects a physical damage from an attack that haven't been indexed.
*
* If you are going to use the AttackIndexer, just register your code to the
* index/deindex events (see API)
* Take note that the events won't fire if an attempted attack came from a unit
* that is not indexed by the unit indexer and if the amount of damage dealt
* is equal to 0.
*
*************************************************************************************
*
* */requires /*
*
* */DamageEvent /*
* - hiveworkshop.com/forums/jass-resources-412/system-physical-damage-detection-228456/
*
* Please use either of the two :
*
* */optional UnitDex /*
* - hiveworkshop.com/forums/submissions-414/system-unitdex-unit-indexer-248209/
* */optional UnitIndexerGUI /*
* - hiveworkshop.com/forums/jass-resources-412/snippet-gui-unit-indexer-vjass-plugin-268592/
*
*************************************************************************************
*
* API
*
* struct Attack extends array
*
* readonly static integer indexed
* - index of the recently indexed attack. Use this inside your onIndex event.
* readonly static integer deindexed
* - inex of the deindexed attack.
*
* static method register takes code c, boolean onIndex returns nothing
* - register code to the index events.
* - if onIndex = true, the code is fired whenever an attack is indexed
*
* static method isValid takes unit u, integer attackId returns boolean
* - check if the attack came from the unit
*
* static method isEmpty takes unit u returns boolean
* - check if the unit has indexed attacks
*
* static method pop takes unit u returns integer
* - remove the oldest indexed attack of the unit.
*
*
*************************************************************************************/
globals
/*
* How long does an attack last?
* This is made for the purpose of removing attacks that are either:
* - stopped in the middle of launching
* - if the unit missed the attack
* - if the attack has been evaded
*/
private constant real ATTACK_DURATION = 90
/*
* Allow to null the damage if the attack is invalid?
*/
private constant boolean NULL_INVALID_ATTACK = true
endglobals
private module Init
private static method onInit takes nothing returns nothing
call init()
endmethod
endmodule
private struct AttackQueue extends array
/*
* Used for checking whenever the unit is indexed
*/
private boolean indexed
/*
* the time the attack has been attempted
*/
private real time
/*
* the owner of the attack
*/
private thistype owner
/*
* The global queue
* all attacks, regardless of the unit, is enqueued here
*/
private thistype g_front
private thistype g_back
private thistype g_next
private thistype g_prev
/*
* The unit's attack queue
*/
private thistype front
private thistype back
private thistype next
private static thistype array recycler
private static method allocate takes nothing returns thistype
local thistype this = recycler[0]
if recycler[this] == 0 then
set recycler[0] = this + 1
else
set recycler[0] = recycler[this]
endif
return this
endmethod
private method deallocate takes nothing returns nothing
set recycler[this] = recycler[0]
set recycler[0] = this
endmethod
private static method init takes nothing returns nothing
set recycler[0] = 1
endmethod
implement Init
static method operator [] takes unit u returns thistype
local thistype this = GetUnitId(u)
if not indexed and this != 0 then
set indexed = true
endif
return this
endmethod
method push takes real gameTime returns thistype
local thistype newNode = 0
if indexed then
/*
* Check if the oldest node has expired
*/
if front != 0 and gameTime - front.time > ATTACK_DURATION then
/*
* If true, use the front node as the new node
*/
set newNode = front
set front = front.next
/*
* Check if the oldest attack indexed has expired
*/
elseif g_front != 0 and gameTime - g_front.time > ATTACK_DURATION then
set newNode = g_front
set g_front = g_front.g_next
set g_front.g_prev = 0
else
/*
* if not, allocate an attack
*/
set newNode = allocate()
endif
/*
* If the global queue is empty, set the newNode as the front
*/
if g_front == 0 then
set g_front = newNode
endif
/*
* Push the newNode to the back
*/
set newNode.g_prev = g_back
set g_back.g_next = newNode
set newNode.g_next = 0
set g_back = newNode
/*
* save the inex of the owner
*/
set newNode.owner = this
/*
* if the queue has empty, we make sure that the new node
* also becomes the front node
*/
if front == 0 then
set front = newNode
endif
/*
* push the node to the back
*/
set back.next = newNode
set newNode.next = 0
set back = newNode
/*
* set the current game time
*/
set newNode.time = gameTime
endif
return newNode
endmethod
method pop takes nothing returns thistype
local thistype node = 0
if indexed then
/*
* recycle the node
*/
call front.deallocate()
set node = front
/*
* Remove the node from the global queue
*/
set node.g_prev.g_next = node.g_next
set node.g_next.g_prev = node.g_prev
/*
* Update the front and back of the global queue if it happens to be
* the front or back being popped
*/
if node == g_front then
set g_front = g_front.g_next
endif
if node == g_back then
set g_back = g_back.g_prev
endif
/*
* set the new front node of the unit's queue
*/
set front = front.next
/*
* clear data
*/
set node.time = 0
set node.owner = 0
endif
return node
endmethod
method has takes thistype node returns boolean
return indexed and node.owner == this
endmethod
method empty takes nothing returns boolean
return indexed and front == 0
endmethod
method destroy takes nothing returns nothing
if indexed then
loop
exitwhen 0 == front
call pop()
endloop
set indexed = false
endif
endmethod
endstruct
struct Attack extends array
readonly static integer indexed
readonly static integer deindexed
private static trigger indexHandler = CreateTrigger()
private static trigger deindexHandler = CreateTrigger()
private static constant timer gameTime = CreateTimer()
static method register takes code c, boolean onIndex returns nothing
if onIndex then
call TriggerAddCondition(indexHandler, Condition(c))
else
call TriggerAddCondition(deindexHandler, Condition(c))
endif
endmethod
private static method fire takes boolean onIndex returns nothing
if onIndex then
call TriggerEvaluate(indexHandler)
else
call TriggerEvaluate(deindexHandler)
endif
endmethod
static method isValid takes unit u, integer attackId returns boolean
return AttackQueue[u].has(attackId)
endmethod
static method isEmpty takes unit u returns boolean
return AttackQueue[u].empty()
endmethod
static method pop takes unit u returns integer
return AttackQueue[u].pop()
endmethod
private static method attack takes nothing returns boolean
local unit u = GetAttacker()
if GetUnitId(u) != 0 then
/*
* index the attack then fire the index event
*/
set indexed = AttackQueue[u].push(TimerGetElapsed(gameTime))
call fire(true)
endif
set u = null
return false
endmethod
private static method damaged takes nothing returns boolean
/*
* We only want physical damage :)
*/
if PDDS.damageType == PHYSICAL then
/*
* check if the unit has indexed attacks
*/
if AttackQueue[PDDS.source].empty() then
/*
* if no, the damage is invalid
*/
if NULL_INVALID_ATTACK then
set PDDS.amount = 0
endif
else
/*
* Deindex the attack then fire the deindex event
*/
set deindexed = AttackQueue[PDDS.source].pop()
call fire(false)
endif
endif
return false
endmethod
private static method onDeindexUnits takes nothing returns boolean
/*
* Attacks of units that are deindexed should be removed
* to give space for new attacks
*/
call AttackQueue[GetIndexedUnit()].destroy()
return false
endmethod
private static method init takes nothing returns nothing
/*
* Register attack event
*/
local trigger t = CreateTrigger()
call TriggerAddCondition(t, Condition(function thistype.attack))
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_ATTACKED)
set t = null
/*
* Register to the damage event
*/
call AddDamageHandler(function thistype.damaged)
/*
* Register to the deindex event
*/
call OnUnitDeindex(function thistype.onDeindexUnits)
/*
* Run the timer for one year :D
*/
call TimerStart(gameTime, 31557600, false, null)
endmethod
implement Init
endstruct
endlibrary
Demo:
JASS:
library LifeSteal requires AttackIndexer
/*
* Instead of using PDD, we are going to use Attack Indexer instead
* so we can handle the damage modification much better
*/
private struct T extends array
private static real array factor
static method attacked takes nothing returns boolean
/*
* Check if unit has Mask of Death
*/
if UnitHasItemOfTypeBJ(GetAttacker(), 'I000') then
/*
* Get a random factor then attach it to the indexed attack
*/
set factor[Attack.indexed] = GetRandomReal(0, 1)
call BJDebugMsg("Attack indexed! :" + I2S(Attack.indexed))
call BJDebugMsg("Attached lifesteal factor: " + R2S(factor[Attack.indexed]))
endif
return false
endmethod
static method damaged takes nothing returns boolean
/*
* Heal the unit by getting the attached data
*/
call SetWidgetLife(PDDS.source, GetWidgetLife(PDDS.source) + PDDS.amount*factor[Attack.deindexed])
call BJDebugMsg("Attack deindexed! :" + I2S(Attack.deindexed))
call BJDebugMsg("Attached lifesteal factor: " + R2S(factor[Attack.deindexed]))
return false
endmethod
private static method onInit takes nothing returns nothing
/*
* Register to the events
*/
call Attack.register(function thistype.attacked, true) // onIndex
call Attack.register(function thistype.damaged, false) // onDeindex
endmethod
endstruct
endlibrary
Changelogs:
1.1
- Fixed a bug where the push method starts allocating 0 node and allocating the same old node to all attacks.
Attachments
Last edited: