- Joined
- Apr 5, 2011
- Messages
- 245
Updated.
Unit Indexer:
Damage Controller:
Template:
As you see in template every damage on the map should be reduced by 5 points.
There is only 1 tool (setDamageTotalAmount) yet.
You can test my demo map in attachments below.
Unit Indexer:
JASS:
//======================
//=======[ vGUI ]=======
//======= v0.201 =======
library vGUI requires IndexedData, NExt, WorldBounds, optional vGDC, Temple
//=== Settings ===
globals
private constant boolean REGISTER_START_UNITS = true
//----------------
private constant boolean TEST_MODE = false
endglobals
//================
globals
indexedUnit Unit
endglobals
struct vGUI
readonly static unit triggerUnit
readonly static boolean enabled = false
private static trigger t1
private static trigger t2
private static method register takes nothing returns boolean
set triggerUnit = GetFilterUnit()
call Unit.register(triggerUnit)
static if TEST_MODE then
call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, "Unit[" + I2S(Unit.indexOf(triggerUnit)) + "] has been registered.")
endif
static if LIBRARY_vGDC then
call vGDC.register()
endif
static if LIBRARY_Temple then
call onUnitRegister()
endif
return false
endmethod
private static method unregister takes nothing returns boolean
set triggerUnit = GetTriggerUnit()
static if TEST_MODE then
call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, "Unit[" + I2S(Unit.indexOf(triggerUnit)) + "] has been unregistered.")
endif
static if LIBRARY_vGDC then
call vGDC.unregister()
endif
static if LIBRARY_Temple then
call onUnitUnregister()
endif
call Unit.unregister(triggerUnit)
return false
endmethod
//=== Register units present at this moment ===
static method snapshot takes nothing returns nothing
local group g = CreateGroup()
call GroupEnumUnits(g, Condition(function thistype.register))
call DestroyGroup(g)
set g = null
endmethod
//=== Enable/disable triggers ===
static method enable takes boolean flag returns nothing
if flag then
call EnableTrigger(t1)
call EnableTrigger(t2)
set enabled = true
else
call DisableTrigger(t1)
call DisableTrigger(t2)
set enabled = false
endif
endmethod
//=== Starts work with it ===
static method start takes nothing returns nothing
static if REGISTER_START_UNITS then
call snapshot()
endif
call TriggerRegisterEnterRegion(t1, WorldBounds.worldRegion, Condition(function thistype.register))
call TriggerRegisterAnyUnitEvent(t2, EVENT_PLAYER_UNIT_DEATH, null)
call TriggerAddCondition(t2, Condition(function thistype.unregister))
set enabled = true
endmethod
private static method onInit takes nothing returns nothing
set Unit = Unit.create()
set t1 = CreateTrigger()
set t2 = CreateTrigger()
endmethod
endstruct
endlibrary
JASS:
//======================
//=======[ vGDC ]=======
//======= v0.203 =======
library vGDC requires optional Temple
//=== Settings ===
globals
private constant boolean DETECT_MAGIC = true
private constant integer MAX_INSTANCES = 24
private constant integer REFRESH_COUNT = 25
//When <REFRESH_COUNT> units are unregistered trigger refreshes itself.
private constant real SAFE_LIFE = 500000
//<SAFE_LIFE> should be out of game values.
//----------------
private constant integer SURVIVAL_ABILITY = 'Z000'
endglobals
static if DETECT_MAGIC then
globals
private constant integer DETECT_MAGIC_SPELLBOOK = 'Z001'
endglobals
endif
globals
private constant boolean TEST_MODE = true
endglobals
//================
//Damage type interface
static if DETECT_MAGIC then
globals
constant boolean PHYSICAL = false
constant boolean MAGIC = true
constant boolean SPELL = true
endglobals
endif
struct vGDC
//General
readonly static boolean enabled = true
private static trigger t
private static integer count = 0
private static boolean flag = false
private static timer tick
private static integer instances
//Damage instance's interface
readonly static unit damageTarget
readonly static real targetLife
readonly static integer damageSources
readonly static unit array damageSource[MAX_INSTANCES]
readonly static real array damageAmount[MAX_INSTANCES]
static if DETECT_MAGIC then
readonly static boolean array damageType[MAX_INSTANCES]
endif
readonly static real damageTotalAmount
readonly static boolean deathEvent
//Damage instances' data
private static unit array damaged[MAX_INSTANCES]
private static real array life[MAX_INSTANCES]
private static integer array sources[MAX_INSTANCES]
private static unit array damager[MAX_INSTANCES][MAX_INSTANCES]
private static real array damage[MAX_INSTANCES][MAX_INSTANCES]
static if DETECT_MAGIC then
private static boolean array type[MAX_INSTANCES][MAX_INSTANCES]
endif
private static real array damageTotal[MAX_INSTANCES]
private static boolean array death[MAX_INSTANCES]
static method register takes nothing returns nothing
static if DETECT_MAGIC then
call UnitAddAbility(vGUI.triggerUnit, DETECT_MAGIC_SPELLBOOK)
endif
call TriggerRegisterUnitEvent(t, vGUI.triggerUnit, EVENT_UNIT_DAMAGED)
endmethod
//It is called by vGUI register.
static method unregister takes nothing returns nothing
set count = count + 1
if count == REFRESH_COUNT then
set count = 0
call refresh()
endif
endmethod
//It is called by vGUI unregister.
//=== Refresh trigger ===
static method refresh takes nothing returns nothing
local integer i = 0
call DestroyTrigger(t)
set t = CreateTrigger()
call TriggerAddCondition(t, Condition(function thistype.beforeDamage))
loop
exitwhen i == Unit.size
if Unit[i] != null then
call TriggerRegisterUnitEvent(t, Unit[i], EVENT_UNIT_DAMAGED)
endif
set i = i + 1
endloop
static if TEST_MODE then
call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, "Damage trigger has been refreshed.")
endif
endmethod
//=== Enable/disable trigger ===
static method enable takes boolean flag returns nothing
if flag then
call EnableTrigger(t)
set enabled = true
else
call DisableTrigger(t)
set enabled = false
endif
endmethod
private static method beforeDamage takes nothing returns boolean
local integer i = 0
local boolean b = false
//First damage in session
if not flag then
set flag = true
call TimerStart(tick, 0, false, function thistype.afterDamage)
set instances = 0
endif
//Save data for instance
set damaged[instances] = GetTriggerUnit()
set life[instances] = GetWidgetLife(damaged[instances])
set sources[instances] = 1
set damager[instances][0] = GetEventDamageSource()
set damage[instances][0] = GetEventDamage()
static if DETECT_MAGIC then
//Determine damage type
if damage[instances][0] < 0 then
set damage[instances][0] = -damage[instances][0]
set type[instances][0] = MAGIC
else
set type[instances][0] = PHYSICAL
endif
endif
set damageTotal[instances] = damage[instances][0]
set death[instances] = false
//Prevent unit from early death
if life[instances] <= damageTotal[instances] + 1 then
set death[instances] = true
call UnitAddAbility(damaged[instances], SURVIVAL_ABILITY)
call SetWidgetLife(damaged[instances], SAFE_LIFE)
endif
//<SAFE_LIFE> is out of game values, thaty's why no need of check (<SURVIVAL_ABILITY> existense)/(death event).
//Merge this instance with another one with the same source
loop
exitwhen i == instances or b
if damaged[instances] == damaged[i] then
set b = true
set damager[i][sources[i]] = damager[instances][0]
set damage[i][sources[i]] = damage[instances][0]
static if DETECT_MAGIC then
set type[i][sources[i]] = type[instances][0]
endif
set damageTotal[i] = damageTotal[i] + damage[instances][0]
set sources[i] = sources[i] + 1
//Prevent unit from early death
if life[i] <= damageTotal[i] + 1 and not death[i] then
set death[i] = true
call UnitAddAbility(damaged[i], SURVIVAL_ABILITY)
call SetWidgetLife(damaged[i], SAFE_LIFE)
endif
endif
set i = i + 1
endloop
if not b then
set instances = instances + 1
endif
return false
endmethod
private static method afterDamage takes nothing returns nothing
local integer i = 0
local integer j
local real regen
set flag = false
loop
//Prepare interface
set damageTarget = damaged[i]
set targetLife = life[i]
set damageSources = sources[i]
set j = 0
loop
set damageSource[j] = damager[i][j]
set damageAmount[j] = damage[i][j]
static if DETECT_MAGIC then
set damageType[j] = type[i][j]
endif
set j = j + 1
exitwhen j == damageSources
endloop
set damageTotalAmount = damageTotal[i]
set deathEvent = death[i]
static if TEST_MODE then
if damageSources == 1 then
static if DETECT_MAGIC then
if damageType[0] == MAGIC then
call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, "Unit[" + I2S(Unit.indexOf(damageTarget)) + "] is gonna take " + R2S(damageTotalAmount) + " magic damage from Unit[" + I2S(Unit.indexOf(damageSource[0])) + "].")
else
call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, "Unit[" + I2S(Unit.indexOf(damageTarget)) + "] is gonna take " + R2S(damageTotalAmount) + " physical damage from Unit[" + I2S(Unit.indexOf(damageSource[0])) + "].")
endif
else
call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, "Unit[" + I2S(Unit.indexOf(damageTarget)) + "] is gonna take " + R2S(damageTotalAmount) + " damage from Unit[" + I2S(Unit.indexOf(damageSource[0])) + "].")
endif
else
call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, "Unit[" + I2S(Unit.indexOf(damageTarget)) + "] is gonna take " + R2S(damageTotalAmount) + " damage from " + I2S(damageSources) + " sources.")
endif
endif
//Nullify incoming damage
if deathEvent then
call UnitRemoveAbility(damageTarget, SURVIVAL_ABILITY)
endif
call SetWidgetLife(damageTarget, targetLife)
//Modify damage
static if LIBRARY_Temple then
call onUnitTakesDamage()
endif
static if TEST_MODE then
static if DETECT_MAGIC then
if damageSources == 1 then
if damageType[0] == MAGIC then
call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, "Unit[" + I2S(Unit.indexOf(damageTarget)) + "] takes " + R2S(damageTotalAmount) + " magic damage.")
else
call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, "Unit[" + I2S(Unit.indexOf(damageTarget)) + "] takes " + R2S(damageTotalAmount) + " physical damage.")
endif
else
call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, "Unit[" + I2S(Unit.indexOf(damageTarget)) + "] takes " + R2S(damageTotalAmount) + " damage.")
endif
else
call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, "Unit[" + I2S(Unit.indexOf(damageTarget)) + "] takes " + R2S(damageTotalAmount) + " damage.")
endif
endif
//Deal modified damage
call SetWidgetLife(damageTarget, GetWidgetLife(damageTarget) - damageTotalAmount)
set i = i + 1
exitwhen i == instances
endloop
endmethod
//=== Modify damage total amount ===
static method setDamageAmount takes integer i, real value returns nothing
static if LIBRARY_ErrorStater then
if CheckArrayIndexError(i, MAX_INSTANCES, damageSources) then
return
endif
endif
set damageTotalAmount = damageTotalAmount - damageAmount[i] + value
set damageAmount[i] = value
endmethod
static if DETECT_MAGIC then
//=== Set damage type ===
static method setDamageType takes integer i, boolean value returns nothing
set damageType[i] = value
endmethod
endif
private static method onInit takes nothing returns nothing
static if DETECT_MAGIC then
local integer i = 0
loop
call SetPlayerAbilityAvailable(Player(i), DETECT_MAGIC_SPELLBOOK, false)
exitwhen i == MAX_PLAYER_INDEX
set i = i + 1
endloop
endif
set t = CreateTrigger()
call TriggerAddCondition(t, Condition(function thistype.beforeDamage))
set tick = CreateTimer()
endmethod
endstruct
endlibrary
JASS:
//==============
//=== Temple ===
//==============
library Temple requires Bounty
globals
endglobals
function onUnitRegister takes nothing returns nothing
endfunction
function onUnitUnregister takes nothing returns nothing
endfunction
function onUnitTakesDamage takes nothing returns nothing
local player p = GetOwningPlayer(vGDC.damageSource[0])
if vGDC.deathEvent and IsUnitEnemy(vGDC.damageTarget, p) then
call Bounty(p, 10, GetWidgetX(vGDC.damageTarget), GetWidgetY(vGDC.damageTarget))
endif
//call vGDC.setDamageAmount(0, 1000)
endfunction
endlibrary
There is only 1 tool (setDamageTotalAmount) yet.
You can test my demo map in attachments below.
I noticed that more than 1 (but I have not seen more than 2) damage instances may take a place in single moment. So, I detect damage, run zero-timer to make right after damage event when target is attacked by multiple sources and have 2 instances side by side for it. Experienced here, tell me please how many instances can be (maybe I should do only with 2, it's so simple; but if no...) and how to deal with them the best? I have seen large arrays with damage data for every unit in some systems but it seems cumbersome for me.
There are few pieces of code where I could try to do it:
I don't use arguments for outer functions connected with damage events, I use address to struct with interface above instead.
There are few pieces of code where I could try to do it:
JASS:
globals
private constant integer MAX_INSTANCES = 4 //(?)
endglobals
...
//Damage instance's interface
readonly static unit damageTarget
readonly static unit damageSource
readonly static real damageAmount
readonly static boolean deathEvent
//Damage instances' data
private static unit array damaged[MAX_INSTANCES]
private static unit array damager[MAX_INSTANCES]
private static real array damage[MAX_INSTANCES]
private static boolean array death[MAX_INSTANCES]
...
//Save data for instance
set damaged[instances] = damageTarget
set damager[instances] = damageSource
set damage[instances] = damageAmount
set death[instances] = deathEvent
set instances = instances + 1
...
//Load data for interface
set damageTarget = damaged[i]
set damageSource = damager[i]
set damageAmount = damage[i]
set deathEvent = death[i]
Attachments
Last edited: