Name | Type | is_array | initial_value |
Asw1_abilityLevel | integer | Yes | |
Asw1_angle | real | Yes | |
Asw1_casterUnit | unit | Yes | |
Asw1_currentDistance | real | Yes | |
Asw1_currentIndex | integer | No | |
Asw1_damage | real | Yes | |
Asw1_damagedGroup | group | Yes | |
Asw1_damageGroup | group | Yes | |
Asw1_dummyUnit | unit | Yes | |
Asw1_effect | effect | Yes | |
Asw1_indexContainer | integer | Yes | |
Asw1_indexCount | integer | No | |
Asw1_indexProcessor | integer | No | |
Asw1_maxDistance | real | Yes | |
Asw1_point1 | location | Yes | |
Asw1_point2 | location | Yes | |
Asw1_point3 | location | Yes | |
Asw1_recycleContainer | integer | Yes | |
Asw1_recycleCount | integer | No | |
Asw1_Speed | real | Yes | |
Asw1_totalIndexCount | integer | No | |
Asw1_x | real | Yes | |
Asw1_y | real | Yes | |
GDD__Integers | integer | Yes | |
GDD__LeftMapGroup | group | No | |
GDD__TriggerArray | trigger | Yes | |
GDD__UnitArray | unit | Yes | |
GDD_Damage | real | No | |
GDD_DamagedUnit | unit | No | |
GDD_DamageSource | unit | No | |
GDD_Event | real | No |
// GUI-Friendly Damage Detection -- v1.2.1 -- by Weep
// http:// www.thehelper.net/forums/showthread.php?t=137957
//
// Requires: only this trigger and its variables.
//
// -- What? --
// This snippet provides a leak-free, GUI-friendly implementation of an "any unit takes
// damage" event. It requires no JASS knowledge to use.
//
// It uses the Game - Value Of Real Variable event as its method of activating other
// triggers, and passes the event responses through a few globals.
//
// -- Why? --
// The traditional GUI method of setting up a trigger than runs when any unit is damaged
// leaks trigger events. This snippet is easy to implement and removes the need to do
// you own GUI damage detection setup.
//
// -- How To Implement --
// 0. Before you copy triggers that use GDD into a new map, you need to copy over GDD
// with its GDD Variable Creator trigger, or there will be a problem: the variables
// won't be automatically created correctly.
//
// 1. Be sure "Automatically create unknown variables while pasting trigger data" is
// enabled in the World Editor general preferences.
// 2. Copy this trigger category ("GDD") and paste it into your map.
// (Alternately: create the variables listed in the globals block below, create a
// trigger named "GUI Friendly Damage Detection", and paste in this entire text.)
// 3. Create your damage triggers using Game - Value Of Real Variable as the event,
// select GDD_Event as the variable, and leave the rest of the settings to the default
// "becomes Equal to 0.00".
// The event responses are the following variables:
// GDD_Damage is the amount of damage, replacing Event Response - Damage Taken.
// GDD_DamagedUnit is the damaged unit, replacing Event Response - Triggering Unit.
// Triggering Unit can still be used, if you need to use waits.
// Read the -- Notes -- section below for more info.
// GDD_DamageSource is the damaging unit, replacing Event Response - Damage Source.
//
// -- Notes --
// GDD's event response variables are not wait-safe; you can't use them after a wait in
// a trigger. If you need to use waits, Triggering Unit (a.k.a. GetTriggerUnit()) can
// be used in place of GDD_DamageSource. There is no usable wait-safe equivalent to
// Event Damage or Damage Source; you'll need to save the values yourself.
//
// Don't write any values to the variables used as the event responses, or it will mess
// up any other triggers using this snippet for their triggering. Only use their values.
//
// This uses arrays, so can detect damage for a maximum of 8190 units at a time, and
// cleans up data at a rate of 33.33 per second, by default. This should be enough for
// most maps, but if you want to change the rate, change the value returned in the
// GDD_RecycleRate function at the top of the code, below.
//
// By default, GDD will not register units that have Locust at the moment of their
// entering the game, and will not recognize when they take damage (which can only
// happen if the Locust ability is later removed from the unit.) To allow a unit to have
// Locust yet still cause GDD damage events if Locust is removed, you can either design
// the unit to not have Locust by default and add it via triggers after creation, or
// edit the GDD_Filter function at the top of the code, below.
//
// -- Credits --
// Captain Griffin on wc3c.net for the research and concept of GroupRefresh.
//
// Credit in your map not needed, but please include this README.
//
// -- Version History --
// 1.2.1: Minor code cleaning. Added configuration functions. Updated documentation.
// 1.2.0: Made this snippet work properly with recursive damage.
// 1.1.1: Added a check in order to not index units with the Locust ability (dummy units).
// If you wish to check for damage taken by a unit that is unselectable, do not
// give the unit-type Locust in the object editor; instead, add the Locust ability
// 'Aloc' via a trigger after its creation, then remove it.
// 1.1.0: Added a check in case a unit gets moved out of the map and back.
// 1.0.0: First release.
//===================================================================
// Configurables.
function GDD_RecycleRate takes nothing returns real //The rate at which the system checks units to see if they've been removed from the game
return 0.03
endfunction
function GDD_Filter takes unit u returns boolean //The condition a unit has to pass to have it registered for damage detection
return GetUnitAbilityLevel(u, 'Aloc') == 0 //By default, the system ignores Locust units, because they normally can't take damage anyway
endfunction
//===================================================================
// This is just for reference.
// If you use JassHelper, you could uncomment this section instead of creating the variables in the trigger editor.
// globals
// real udg_GDD_Event = 0.
// real udg_GDD_Damage = 0.
// unit udg_GDD_DamagedUnit
// unit udg_GDD_DamageSource
// trigger array udg_GDD__TriggerArray
// integer array udg_GDD__Integers
// unit array udg_GDD__UnitArray
// group udg_GDD__LeftMapGroup = CreateGroup()
// endglobals
//===================================================================
// System code follows. Don't touch!
function GDD_Event takes nothing returns boolean
local unit damagedcache = udg_GDD_DamagedUnit
local unit damagingcache = udg_GDD_DamageSource
local real damagecache = udg_GDD_Damage
set udg_GDD_DamagedUnit = GetTriggerUnit()
set udg_GDD_DamageSource = GetEventDamageSource()
set udg_GDD_Damage = GetEventDamage()
set udg_GDD_Event = 1.
set udg_GDD_Event = 0.
set udg_GDD_DamagedUnit = damagedcache
set udg_GDD_DamageSource = damagingcache
set udg_GDD_Damage = damagecache
set damagedcache = null
set damagingcache = null
return false
endfunction
function GDD_AddDetection takes nothing returns boolean
// if(udg_GDD__Integers[0] > 8190) then
// call BJDebugMsg("GDD: Too many damage events! Decrease number of units present in the map or increase recycle rate.")
// ***Recycle rate is specified in the GDD_RecycleRate function at the top of the code. Smaller is faster.***
// return
// endif
if(IsUnitInGroup(GetFilterUnit(), udg_GDD__LeftMapGroup)) then
call GroupRemoveUnit(udg_GDD__LeftMapGroup, GetFilterUnit())
elseif(GDD_Filter(GetFilterUnit())) then
set udg_GDD__Integers[0] = udg_GDD__Integers[0]+1
set udg_GDD__UnitArray[udg_GDD__Integers[0]] = GetFilterUnit()
set udg_GDD__TriggerArray[udg_GDD__Integers[0]] = CreateTrigger()
call TriggerRegisterUnitEvent(udg_GDD__TriggerArray[udg_GDD__Integers[0]], udg_GDD__UnitArray[udg_GDD__Integers[0]], EVENT_UNIT_DAMAGED)
call TriggerAddCondition(udg_GDD__TriggerArray[udg_GDD__Integers[0]], Condition(function GDD_Event))
endif
return false
endfunction
function GDD_PreplacedDetection takes nothing returns nothing
local group g = CreateGroup()
local integer i = 0
loop
call GroupEnumUnitsOfPlayer(g, Player(i), Condition(function GDD_AddDetection))
set i = i+1
exitwhen i == bj_MAX_PLAYER_SLOTS
endloop
call DestroyGroup(g)
set g = null
endfunction
function GDD_GroupRefresh takes nothing returns nothing
// Based on GroupRefresh by Captain Griffen on wc3c.net
if (bj_slotControlUsed[5063] == true) then
call GroupClear(udg_GDD__LeftMapGroup)
set bj_slotControlUsed[5063] = false
endif
call GroupAddUnit(udg_GDD__LeftMapGroup, GetEnumUnit())
endfunction
function GDD_Recycle takes nothing returns nothing
if(udg_GDD__Integers[0] <= 0) then
return
elseif(udg_GDD__Integers[1] <= 0) then
set udg_GDD__Integers[1] = udg_GDD__Integers[0]
endif
if(GetUnitTypeId(udg_GDD__UnitArray[udg_GDD__Integers[1]]) == 0) then
call DestroyTrigger(udg_GDD__TriggerArray[udg_GDD__Integers[1]])
set udg_GDD__TriggerArray[udg_GDD__Integers[1]] = null
set udg_GDD__TriggerArray[udg_GDD__Integers[1]] = udg_GDD__TriggerArray[udg_GDD__Integers[0]]
set udg_GDD__UnitArray[udg_GDD__Integers[1]] = udg_GDD__UnitArray[udg_GDD__Integers[0]]
set udg_GDD__UnitArray[udg_GDD__Integers[0]] = null
set udg_GDD__Integers[0] = udg_GDD__Integers[0]-1
endif
set udg_GDD__Integers[1] = udg_GDD__Integers[1]-1
endfunction
function GDD_LeaveMap takes nothing returns boolean
local boolean cached = bj_slotControlUsed[5063]
if(udg_GDD__Integers[2] < 64) then
set udg_GDD__Integers[2] = udg_GDD__Integers[2]+1
else
set bj_slotControlUsed[5063] = true
call ForGroup(udg_GDD__LeftMapGroup, function GDD_GroupRefresh)
set udg_GDD__Integers[2] = 0
endif
call GroupAddUnit(udg_GDD__LeftMapGroup, GetFilterUnit())
set bj_slotControlUsed[5063] = cached
return false
endfunction
// ===========================================================================
function InitTrig_GUI_Friendly_Damage_Detection takes nothing returns nothing
local region r = CreateRegion()
call RegionAddRect(r, GetWorldBounds())
call TriggerRegisterEnterRegion(CreateTrigger(), r, Condition(function GDD_AddDetection))
call TriggerRegisterLeaveRegion(CreateTrigger(), r, Condition(function GDD_LeaveMap))
call GDD_PreplacedDetection()
call TimerStart(CreateTimer(), GDD_RecycleRate(), true, function GDD_Recycle)
set r = null
endfunction
//===========================================================================
// Pseudo Illusions System (Jass)
//===========================================================================
globals
hashtable illusionData = null
// Constants for ability and spell IDs.
// If you ever change the ability/spell ID in the object editor, you only need to change it here.
constant integer abilityId = 'API0' // Inventory Hero
constant integer spellId = 'API1' // Spell ID of the ability that creates illusions.
constant string buffString = "metamorphosis" //
constant integer buffId = 'API2' // Spell ID of Metamorphosis with 'BIil' illusion buff.
constant real duration = 60.0 // How long illusions last.
constant real offset = 200.0 // How far away from the target the illusions are created.
endglobals
//===========================================================================
// Utility Functions
//===========================================================================
function OnUnitSpellCondition takes nothing returns boolean
return GetSpellAbilityId() == spellId
endfunction
//===========================================================================
// Timer Handlers
//===========================================================================
// Called when an illusion's expiration timer expires.
function OnExpirationTimer takes nothing returns nothing
local unit illusionUnit = LoadUnitHandle(illusionData, GetHandleId(GetExpiredTimer()), StringHash("illusionUnit"))
if illusionUnit != null then
call KillUnit(illusionUnit)
call FlushChildHashtable(illusionData, GetHandleId(GetExpiredTimer()))
endif
call DestroyTimer(GetExpiredTimer())
set illusionUnit = null
endfunction
// Called when the sync timer expires.
// It fixes some issues with the Metamorphosis ability without a normal form or an alternate form
// like units having incorrect stats (e.g., -100 HP) or doubled base damage.
// This function syncs the illusion's stats to match the target properly.
function OnSyncTimer takes nothing returns nothing
local unit target = LoadUnitHandle(illusionData, GetHandleId(GetExpiredTimer()), StringHash("targetUnit"))
local unit illusionUnit = LoadUnitHandle(illusionData, GetHandleId(GetExpiredTimer()), StringHash("illusionUnit"))
if illusionUnit != null and target != null then
call BlzSetUnitMaxHP(illusionUnit, BlzGetUnitMaxHP(target))
call SetUnitState(illusionUnit, UNIT_STATE_LIFE, GetUnitState(target, UNIT_STATE_LIFE))
call SetUnitState(illusionUnit, UNIT_STATE_MANA, GetUnitState(target, UNIT_STATE_MANA))
call BlzSetUnitBaseDamage(illusionUnit, BlzGetUnitBaseDamage(target, 0), 0)
else
call FlushChildHashtable(illusionData, GetHandleId(GetExpiredTimer()))
call DestroyTimer(GetExpiredTimer())
endif
set illusionUnit = null
set target = null
endfunction
//===========================================================================
// Event Handlers
//===========================================================================
// Called when a unit picks up an item.
function OnUnitPickupItem takes nothing returns nothing
local unit illusionUnit = GetManipulatingUnit()
local item pickedItem = GetManipulatedItem()
// Prevents an illusion from picking up an item by moving the item back to the illusion's position.
if illusionUnit != null and pickedItem != null then
call SetItemPosition(pickedItem, GetUnitX(illusionUnit), GetUnitY(illusionUnit))
endif
set pickedItem = null
set illusionUnit = null
endfunction
// Called when a unit dies.
function OnUnitDeath takes nothing returns nothing
local unit illusionUnit = GetDyingUnit()
if illusionUnit != null then
call DestroyEffect(AddSpecialEffect("Abilities\\Spells\\Orc\\MirrorImage\\MirrorImageDeathCaster.mdl", GetUnitX(illusionUnit), GetUnitY(illusionUnit)))
call RemoveUnit(illusionUnit)
endif
set illusionUnit = null
endfunction
// Called when a unit is damaged.
function OnUnitDamaging takes nothing returns nothing
local unit damageSource = GetEventDamageSource()
local unit damageTarget = BlzGetEventDamageTarget()
local real damage = GetEventDamage()
if damageSource != null and damageTarget != null then
if GetUnitAbilityLevel(damageSource, abilityId) > 0 then
call BlzSetEventDamage(damage * 0.25) // Illusions deal 25% damage
endif
if GetUnitAbilityLevel(damageTarget, abilityId) > 0 then
call BlzSetEventDamage(damage * 2.00) // Illusions take 200% damage
endif
endif
set damageSource = null
set damageTarget = null
endfunction
//===========================================================================
// Illusion Creation
//===========================================================================
// Called when a unit casts the illusion-creating spell.
function OnUnitSpellEffect takes nothing returns nothing
local unit caster = GetSpellAbilityUnit()
local player owner = GetOwningPlayer(caster)
local unit target = GetSpellTargetUnit()
local integer targetTypeId = GetUnitTypeId(target)
// Calculates the unit type ID for the illusion. The number 73 represents 'I'.
//Old Method: local integer illusionTypeId = 73 * 0x1000000 + ModuloInteger(targetTypeId / 0x10000, 0x100) * 0x10000 + ModuloInteger(targetTypeId / 0x100, 0x100) * 0x100 + ModuloInteger(targetTypeId, 0x100)
local integer illusionTypeId = 0x49000000 + (targetTypeId - (targetTypeId / 0x1000000) * 0x1000000)
// Create the illusion unit.
local unit illusionUnit = CreateUnit(owner, illusionTypeId, GetUnitX(target) + offset * Cos(GetRandomReal(0, 6.283)), GetUnitY(target) + offset * Sin(GetRandomReal(0, 6.283)), GetUnitFacing(target))
local integer targetHeroLevel = GetHeroLevel(target)
local integer i
local ability targetAbility
local item targetItem
local integer charges
local item illusionItem
local timer expirationTimer = CreateTimer() // Timer for illusion expiration.
local timer syncTimer = CreateTimer() // Timer to delay one frame for synchronizing Max HP and base damage.
local trigger itemTrigger = CreateTrigger() // Trigger for item pickup.
local trigger deathTrigger = CreateTrigger() // Trigger for unit death.
if caster == null and target == null then
set caster = null
set owner = null
set target = null
return
endif
// Store the created illusion unit in the hashtable, associated with the timer.
call SaveUnitHandle(illusionData, GetHandleId(expirationTimer), StringHash("illusionUnit"), illusionUnit)
call SaveUnitHandle(illusionData, GetHandleId(syncTimer), StringHash("illusionUnit"), illusionUnit)
call SaveUnitHandle(illusionData, GetHandleId(syncTimer), StringHash("targetUnit"), target)
// Start the timer. When it expires, the OnExpirationTimer function will be called.
if illusionData != null then
call TimerStart(expirationTimer, duration, false, function OnExpirationTimer)
else
call DestroyTimer(expirationTimer)
return
endif
// Visual effect for the illusion.
if GetLocalPlayer() == owner then
call SetUnitVertexColor(illusionUnit, 15, 63, 183, 223)
endif
call DestroyEffect(AddSpecialEffectTarget("Abilities\\Spells\\Items\\AIil\\AIilTarget.mdl", illusionUnit, "origin"))
// Hero setup (if the target was a hero).
if IsUnitType(target, UNIT_TYPE_HERO) then
if GetHeroLevel(illusionUnit) != targetHeroLevel then
call SetHeroLevel(illusionUnit, targetHeroLevel, false)
endif
call SuspendHeroXP(illusionUnit, true)
else
call SuspendHeroXP(illusionUnit, true)
endif
call UnitAddAbility(illusionUnit, buffId)
call IssueImmediateOrder(illusionUnit, buffString)
call BlzUnitDisableAbility(illusionUnit, buffId, true, true)
if GetUnitState(illusionUnit, UNIT_STATE_LIFE) > 0.405 and GetUnitState(target, UNIT_STATE_LIFE) > 0.405 then
call TimerStart(syncTimer, 0.0, false, function OnSyncTimer)
else
call DestroyTimer(syncTimer)
call FlushChildHashtable(illusionData, GetHandleId(syncTimer))
return
endif
// Copy abilities
set i = 1
loop
set targetAbility = BlzGetUnitAbilityByIndex(target, i - 1)
exitwhen targetAbility == null
if not BlzGetAbilityBooleanField(targetAbility, ABILITY_BF_ITEM_ABILITY) then
call UnitAddAbility(illusionUnit, BlzGetAbilityId(targetAbility))
call SetUnitAbilityLevel(illusionUnit, BlzGetAbilityId(targetAbility), GetUnitAbilityLevel(target, BlzGetAbilityId(targetAbility)))
endif
set targetAbility = null
set i = i + 1
endloop
// Copy items
set i = 1
loop
set targetItem = UnitItemInSlot(target, i - 1)
exitwhen targetItem == null or i >= bj_MAX_INVENTORY
set illusionItem = CreateItem(GetItemTypeId(targetItem), GetUnitX(illusionUnit), GetUnitY(illusionUnit))
set charges = GetItemCharges(targetItem)
if charges > 0 then
call SetItemCharges(illusionItem, charges)
endif
call UnitAddItem(illusionUnit, illusionItem)
set targetItem = null
set illusionItem = null
set i = i + 1
endloop
// Set up triggers for the illusion.
call TriggerRegisterUnitEvent(itemTrigger, illusionUnit, EVENT_UNIT_PICKUP_ITEM)
call TriggerRegisterUnitEvent(deathTrigger, illusionUnit, EVENT_UNIT_DEATH)
call TriggerAddAction(itemTrigger, function OnUnitPickupItem)
call TriggerAddAction(deathTrigger, function OnUnitDeath)
set caster = null
set owner = null
set target = null
set illusionUnit = null
endfunction
//===========================================================================
// Initialization
//===========================================================================
function InitTrig_Pseudo_Illusions_System takes nothing returns nothing
local trigger illusionTrigger = CreateTrigger()
local trigger damageTrigger = CreateTrigger()
local integer i = 1
if illusionData == null then
set illusionData = InitHashtable()
endif
loop
exitwhen i >= bj_MAX_PLAYER_SLOTS
call TriggerRegisterPlayerUnitEvent(damageTrigger, Player(i - 1), EVENT_PLAYER_UNIT_DAMAGING, null)
call TriggerRegisterPlayerUnitEvent(illusionTrigger, Player(i - 1), EVENT_PLAYER_UNIT_SPELL_EFFECT, null)
set i = i + 1
endloop
call TriggerAddAction(damageTrigger, function OnUnitDamaging)
call TriggerAddAction(illusionTrigger, function OnUnitSpellEffect)
call TriggerAddCondition(illusionTrigger, Condition(function OnUnitSpellCondition))
set damageTrigger = null
set illusionTrigger = null
endfunction