Name | Type | is_array | initial_value |
//TESH.scrollpos=179
//TESH.alwaysfold=0
[center]
[B][SIZE="4"][COLOR="Yellow"]DESCRIPTION:[/COLOR][/SIZE][/B]
[IMG]http://www.hiveworkshop.com/forums/members/167625-albums4356-picture91796.jpg[/IMG]
[B][SIZE="4"][COLOR="Yellow"]PREVIEW:[/COLOR][/SIZE][/B]
[IMG]http://www.hiveworkshop.com/forums/members/167625-albums4356-picture91751.gif[/IMG]
[/center]
[simpletable=Healing Arcs v1.5]
[jass]
/***************************************************************
Healing Arcs v1.5
by mckill2009
****************************************************************
REQUIRED LIBRARIES:
- SpellEffectEvent by Bribe
- Table by Bribe
- DamageEvent by looking_for_help
****************************************************************/
library HealingArcs initializer Init uses SpellEffectEvent, Table, DamageEvent
globals
/***********************************************************
* RAW CODES: Press CTRL+D view and change raw codes from
* object editor accordingly
************************************************************/
private constant integer SPELL_ID = 'A000' //unit target spell to ally
private constant integer DUMMY_ID = 'h000' //unit who cast the healing arc
private constant integer BOLT_SPELL_ID = 'A001' //firebolt ID with arc
private constant integer BOLT_ORDER_ID = 852231 //firebolt order ID, must match with BOLT_SPELL_ID
/***********************************************************
* CONFIGURABLE GLOBALS
************************************************************/
private constant string HEAL_SFX = "Abilities\\Spells\\Human\\HolyBolt\\HolyBoltSpecialArt.mdl"
private constant string DAMAGE_SFX = "Abilities\\Spells\\Undead\\DeathCoil\\DeathCoilSpecialArt.mdl"
private constant string HEAL_ATTACHMENT = "overhead"
private constant string DAMAGE_ATTACHMENT = "chest"
private constant player NEUTRAL_PASSIVE = Player(15)
private constant attacktype ATK = ATTACK_TYPE_CHAOS
private constant damagetype DMG = DAMAGE_TYPE_DEATH
/***********************************************************
* NON-CONFIGURABLE GLOBALS
************************************************************/
private group g = CreateGroup()
private unit dummy
private player owner
private integer uID
private Table heal
endglobals
/***********************************************************
* CONFIGURABLE FUNCTIONS
************************************************************/
//Area of effect when searching allies to heal
private function GetAoE takes integer level returns real
return 200. * level + 300 //500, 700, 900, 1100, 1300
endfunction
//Targets only 1 enemy
private function GetDamageAmount takes integer level returns real
return 25. * level + 25 //50, 75, 100, 125, 150
endfunction
//Heals the main ally target
private function GetHealAmount takes integer level returns real
return 50. * level + 50 //100, 150, 200, 250, 300
endfunction
//Heal Amount of the allies nearby
private function GetHealAmountArc takes integer level returns real
return 50. * level + 50 //100, 150, 200, 250, 300
endfunction
private function UnitFilter takes unit u returns boolean
return not (IsUnitType(u, UNIT_TYPE_STRUCTURE) or IsUnitType(u, UNIT_TYPE_MECHANICAL))
endfunction
/***********************************************************
* NON-CONFIGURABLE FUNCTIONS
************************************************************/
private function UnitAlive takes unit u returns boolean
return not (IsUnitType(u, UNIT_TYPE_DEAD) or GetUnitTypeId(u)==0 or u==null)
endfunction
private function IsAllyUnitDamaged takes unit u returns boolean
return GetWidgetLife(u) < GetUnitState(u, UNIT_STATE_MAX_LIFE)
endfunction
private function ArcLands takes nothing returns nothing
if PDDS.source==dummy then
if UnitFilter(PDDS.target) then
set uID = GetHandleId(PDDS.target)
set owner = heal.player[uID]
if IsUnitEnemy(PDDS.target, owner) then
call UnitDamageTarget(dummy, PDDS.target, GetDamageAmount(heal[uID]), false, false, ATK, DMG, null)
call DestroyEffect(AddSpecialEffectTarget(DAMAGE_SFX, PDDS.target, DAMAGE_ATTACHMENT))
else
call SetWidgetLife(PDDS.target, GetWidgetLife(PDDS.target) + heal.real[uID])
call DestroyEffect(AddSpecialEffectTarget(HEAL_SFX, PDDS.target, HEAL_ATTACHMENT))
endif
endif
set heal.player[uID] = null
call heal.remove(uID)
endif
endfunction
private function Cast takes nothing returns nothing
local unit first
local unit u = GetSpellTargetUnit()
local real x = GetUnitX(u)
local real y = GetUnitY(u)
local integer level = GetUnitAbilityLevel(GetTriggerUnit(), SPELL_ID)
local real healAmt = GetHealAmount(level)
local real healAmtArc = GetHealAmountArc(level)
call SetUnitX(dummy, x)
call SetUnitY(dummy, y)
call SetUnitOwner(dummy, GetTriggerPlayer(), false)
call SetUnitFlyHeight(dummy, GetUnitFlyHeight(u), 0)
if IsUnitEnemy(u, GetTriggerPlayer()) then
call UnitDamageTarget(GetTriggerUnit(), u, GetDamageAmount(level), false, false, ATK, DMG, null)
call DestroyEffect(AddSpecialEffectTarget(DAMAGE_SFX, u, DAMAGE_ATTACHMENT))
else
call SetWidgetLife(u, GetWidgetLife(u) + healAmt)
call DestroyEffect(AddSpecialEffectTarget(HEAL_SFX, u, HEAL_ATTACHMENT))
endif
call GroupEnumUnitsInRange(g, x, y, GetAoE(level), null)
loop
set first = FirstOfGroup(g)
exitwhen first==null
if UnitAlive(first) and first!=u then
if (IsAllyUnitDamaged(first) or IsUnitEnemy(first, GetTriggerPlayer())) and UnitFilter(first) then
set uID = GetHandleId(first)
set heal[uID] = level
set heal.real[uID] = healAmtArc
set heal.player[uID] = GetTriggerPlayer()
call IssueTargetOrderById(dummy, BOLT_ORDER_ID, first)
endif
endif
call GroupRemoveUnit(g, first)
endloop
call SetUnitOwner(dummy, NEUTRAL_PASSIVE, false)
set u = null
endfunction
private function OnDeath takes nothing returns nothing
if heal.has(GetHandleId(GetTriggerUnit())) then
set heal.player[GetHandleId(GetTriggerUnit())] = null
call heal.remove(GetHandleId(GetTriggerUnit()))
endif
endfunction
private function Init takes nothing returns nothing
call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_DEATH, function OnDeath)
call RegisterSpellEffectEvent(SPELL_ID, function Cast)
set dummy = CreateUnit(NEUTRAL_PASSIVE, DUMMY_ID, 0,0,0)
call UnitRemoveAbility(dummy, 'Amov')
call UnitAddAbility(dummy, BOLT_SPELL_ID)
call AddDamageHandler(function ArcLands)
set heal = Table.create()
endfunction
endlibrary
[/jass]
[Tabs]
[tab=Instructions]
[box=Importing Objects]
- Go to object editor and copy ALL the custom buff and abilities to your map
- If your map doesnt have a dummy unit, you need to copy it as well
- Make sure that the dummy unit has locust ability, movement type is fly and;
[box=]
Art - Animation - Cast Backswing = 0
Art - Animation - Cast Point = 0
since this spell is only using ONE dummy
[/box]
- Copy ALL the required library and spell triggers to your map
[/box]
[box=Setting the Codes]
- Hold CTRL and press D while viewing object editor to view raw codes
- Raw codes are the first 4 digits of any object in the object editor
- Replace the ID's of the integer with the correct raw codes in the global block
- For some reasons this spell doesnt work if DamageEvent is changed to the correct raw codes, so set it like this;
[ljass]private constant integer DAMAGE_TYPE_DETECTOR = 0[/ljass]
[ljass]private constant integer SET_MAX_LIFE = 0[/ljass]
- I've contacted looking_for_help for this long ago but no reply
[/box]
[/tab]
[tab=Credits]
SpellEffectEvent by Bribe
Table by Bribe
DamageEvent by looking_for_help
[/tab]
[tab=Issues]
- Doesnt work if [B]DamageEvent[/B] raw code is correctly filled, so do like this;
[ljass]private constant integer DAMAGE_TYPE_DETECTOR = 0[/ljass]
[ljass]private constant integer SET_MAX_LIFE = 0[/ljass]
- Overwrites the previous cast since it used the handle of the target unit as ID which i think not a big deal coz
the damage event is fast if not instantaneous
[/tab]
[tab=Changelogs]
v1.5
- Most suggestions is added or included
- Instead of buf effect, trigger effect is added
- Player(15) is now a variable so that people can change it
v1.4
- Added filter to ArcLands
- Added safety when target is dead
v1.3
- Some changes in code as suggested by the moderator
v1.2
- Some code improvements
- Some names are now verbose
v1.1
- AOE globals moved to function
[/tab]
[/tabs]
[/simpletable]
//TESH.scrollpos=0
//TESH.alwaysfold=0
library Table /* made by Bribe, special thanks to Vexorian & Nestharus, version 3.1.1.0
One map, one hashtable. Welcome to NewTable 3.1
This library was originally called NewTable so it didn't conflict with
the API of Table by Vexorian. However, the damage is done and it's too
late to change the library name now. To help with damage control, I
have provided an extension library called TableBC, which bridges all
the functionality of Vexorian's Table except for 2-D string arrays &
the ".flush(integer)" method. I use ".flush()" to flush a child hash-
table, because I wanted the API in NewTable to reflect the API of real
hashtables (I thought this would be more intuitive).
API
------------
struct Table
| static method create takes nothing returns Table
| create a new Table
|
| method destroy takes nothing returns nothing
| destroy it
|
| method flush takes nothing returns nothing
| flush all stored values inside of it
|
| method remove takes integer key returns nothing
| remove the value at index "key"
|
| method operator []= takes integer key, $TYPE$ value returns nothing
| assign "value" to index "key"
|
| method operator [] takes integer key returns $TYPE$
| load the value at index "key"
|
| method has takes integer key returns boolean
| whether or not the key was assigned
|
----------------
struct TableArray
| static method operator [] takes integer array_size returns TableArray
| create a new array of Tables of size "array_size"
|
| method destroy takes nothing returns nothing
| destroy it
|
| method flush takes nothing returns nothing
| flush and destroy it
|
| method operator size takes nothing returns integer
| returns the size of the TableArray
|
| method operator [] takes integer key returns Table
| returns a Table accessible exclusively to index "key"
*/
globals
private integer less = 0 //Index generation for TableArrays (below 0).
private integer more = 8190 //Index generation for Tables.
//Configure it if you use more than 8190 "key" variables in your map (this will never happen though).
private hashtable ht = InitHashtable()
private key sizeK
private key listK
endglobals
private struct dex extends array
static method operator size takes nothing returns Table
return sizeK
endmethod
static method operator list takes nothing returns Table
return listK
endmethod
endstruct
private struct handles extends array
method has takes integer key returns boolean
return HaveSavedHandle(ht, this, key)
endmethod
method remove takes integer key returns nothing
call RemoveSavedHandle(ht, this, key)
endmethod
endstruct
private struct agents extends array
method operator []= takes integer key, agent value returns nothing
call SaveAgentHandle(ht, this, key, value)
endmethod
endstruct
//! textmacro NEW_ARRAY_BASIC takes SUPER, FUNC, TYPE
private struct $TYPE$s extends array
method operator [] takes integer key returns $TYPE$
return Load$FUNC$(ht, this, key)
endmethod
method operator []= takes integer key, $TYPE$ value returns nothing
call Save$FUNC$(ht, this, key, value)
endmethod
method has takes integer key returns boolean
return HaveSaved$SUPER$(ht, this, key)
endmethod
method remove takes integer key returns nothing
call RemoveSaved$SUPER$(ht, this, key)
endmethod
endstruct
private module $TYPE$m
method operator $TYPE$ takes nothing returns $TYPE$s
return this
endmethod
endmodule
//! endtextmacro
//! textmacro NEW_ARRAY takes FUNC, TYPE
private struct $TYPE$s extends array
method operator [] takes integer key returns $TYPE$
return Load$FUNC$Handle(ht, this, key)
endmethod
method operator []= takes integer key, $TYPE$ value returns nothing
call Save$FUNC$Handle(ht, this, key, value)
endmethod
method has takes integer key returns boolean
return HaveSavedHandle(ht, this, key)
endmethod
method remove takes integer key returns nothing
call RemoveSavedHandle(ht, this, key)
endmethod
endstruct
private module $TYPE$m
method operator $TYPE$ takes nothing returns $TYPE$s
return this
endmethod
endmodule
//! endtextmacro
//Run these textmacros to include the entire hashtable API as wrappers.
//Don't be intimidated by the number of macros - Vexorian's map optimizer is
//supposed to kill functions which inline (all of these functions inline).
//! runtextmacro NEW_ARRAY_BASIC("Real", "Real", "real")
//! runtextmacro NEW_ARRAY_BASIC("Boolean", "Boolean", "boolean")
//! runtextmacro NEW_ARRAY_BASIC("String", "Str", "string")
//New textmacro to allow table.integer[] syntax for compatibility with textmacros that might desire it.
//! runtextmacro NEW_ARRAY_BASIC("Integer", "Integer", "integer")
//! runtextmacro NEW_ARRAY("Player", "player")
//! runtextmacro NEW_ARRAY("Widget", "widget")
//! runtextmacro NEW_ARRAY("Destructable", "destructable")
//! runtextmacro NEW_ARRAY("Item", "item")
//! runtextmacro NEW_ARRAY("Unit", "unit")
//! runtextmacro NEW_ARRAY("Ability", "ability")
//! runtextmacro NEW_ARRAY("Timer", "timer")
//! runtextmacro NEW_ARRAY("Trigger", "trigger")
//! runtextmacro NEW_ARRAY("TriggerCondition", "triggercondition")
//! runtextmacro NEW_ARRAY("TriggerAction", "triggeraction")
//! runtextmacro NEW_ARRAY("TriggerEvent", "event")
//! runtextmacro NEW_ARRAY("Force", "force")
//! runtextmacro NEW_ARRAY("Group", "group")
//! runtextmacro NEW_ARRAY("Location", "location")
//! runtextmacro NEW_ARRAY("Rect", "rect")
//! runtextmacro NEW_ARRAY("BooleanExpr", "boolexpr")
//! runtextmacro NEW_ARRAY("Sound", "sound")
//! runtextmacro NEW_ARRAY("Effect", "effect")
//! runtextmacro NEW_ARRAY("UnitPool", "unitpool")
//! runtextmacro NEW_ARRAY("ItemPool", "itempool")
//! runtextmacro NEW_ARRAY("Quest", "quest")
//! runtextmacro NEW_ARRAY("QuestItem", "questitem")
//! runtextmacro NEW_ARRAY("DefeatCondition", "defeatcondition")
//! runtextmacro NEW_ARRAY("TimerDialog", "timerdialog")
//! runtextmacro NEW_ARRAY("Leaderboard", "leaderboard")
//! runtextmacro NEW_ARRAY("Multiboard", "multiboard")
//! runtextmacro NEW_ARRAY("MultiboardItem", "multiboarditem")
//! runtextmacro NEW_ARRAY("Trackable", "trackable")
//! runtextmacro NEW_ARRAY("Dialog", "dialog")
//! runtextmacro NEW_ARRAY("Button", "button")
//! runtextmacro NEW_ARRAY("TextTag", "texttag")
//! runtextmacro NEW_ARRAY("Lightning", "lightning")
//! runtextmacro NEW_ARRAY("Image", "image")
//! runtextmacro NEW_ARRAY("Ubersplat", "ubersplat")
//! runtextmacro NEW_ARRAY("Region", "region")
//! runtextmacro NEW_ARRAY("FogState", "fogstate")
//! runtextmacro NEW_ARRAY("FogModifier", "fogmodifier")
//! runtextmacro NEW_ARRAY("Hashtable", "hashtable")
struct Table extends array
// Implement modules for intuitive syntax (tb.handle; tb.unit; etc.)
implement realm
implement integerm
implement booleanm
implement stringm
implement playerm
implement widgetm
implement destructablem
implement itemm
implement unitm
implement abilitym
implement timerm
implement triggerm
implement triggerconditionm
implement triggeractionm
implement eventm
implement forcem
implement groupm
implement locationm
implement rectm
implement boolexprm
implement soundm
implement effectm
implement unitpoolm
implement itempoolm
implement questm
implement questitemm
implement defeatconditionm
implement timerdialogm
implement leaderboardm
implement multiboardm
implement multiboarditemm
implement trackablem
implement dialogm
implement buttonm
implement texttagm
implement lightningm
implement imagem
implement ubersplatm
implement regionm
implement fogstatem
implement fogmodifierm
implement hashtablem
method operator handle takes nothing returns handles
return this
endmethod
method operator agent takes nothing returns agents
return this
endmethod
//set this = tb[GetSpellAbilityId()]
method operator [] takes integer key returns Table
return LoadInteger(ht, this, key) //return this.integer[key]
endmethod
//set tb[389034] = 8192
method operator []= takes integer key, Table tb returns nothing
call SaveInteger(ht, this, key, tb) //set this.integer[key] = tb
endmethod
//set b = tb.has(2493223)
method has takes integer key returns boolean
return HaveSavedInteger(ht, this, key) //return this.integer.has(key)
endmethod
//call tb.remove(294080)
method remove takes integer key returns nothing
call RemoveSavedInteger(ht, this, key) //call this.integer.remove(key)
endmethod
//Remove all data from a Table instance
method flush takes nothing returns nothing
call FlushChildHashtable(ht, this)
endmethod
//local Table tb = Table.create()
static method create takes nothing returns Table
local Table this = dex.list[0]
if this == 0 then
set this = more + 1
set more = this
else
set dex.list[0] = dex.list[this]
call dex.list.remove(this) //Clear hashed memory
endif
debug set dex.list[this] = -1
return this
endmethod
// Removes all data from a Table instance and recycles its index.
//
// call tb.destroy()
//
method destroy takes nothing returns nothing
debug if dex.list[this] != -1 then
debug call BJDebugMsg("Table Error: Tried to double-free instance: " + I2S(this))
debug return
debug endif
call this.flush()
set dex.list[this] = dex.list[0]
set dex.list[0] = this
endmethod
//! runtextmacro optional TABLE_BC_METHODS()
endstruct
//! runtextmacro optional TABLE_BC_STRUCTS()
struct TableArray extends array
//Returns a new TableArray to do your bidding. Simply use:
//
// local TableArray ta = TableArray[array_size]
//
static method operator [] takes integer array_size returns TableArray
local Table tb = dex.size[array_size] //Get the unique recycle list for this array size
local TableArray this = tb[0] //The last-destroyed TableArray that had this array size
debug if array_size <= 0 then
debug call BJDebugMsg("TypeError: Invalid specified TableArray size: " + I2S(array_size))
debug return 0
debug endif
if this == 0 then
set this = less - array_size
set less = this
else
set tb[0] = tb[this] //Set the last destroyed to the last-last destroyed
call tb.remove(this) //Clear hashed memory
endif
set dex.size[this] = array_size //This remembers the array size
return this
endmethod
//Returns the size of the TableArray
method operator size takes nothing returns integer
return dex.size[this]
endmethod
//This magic method enables two-dimensional[array][syntax] for Tables,
//similar to the two-dimensional utility provided by hashtables them-
//selves.
//
//ta[integer a].unit[integer b] = unit u
//ta[integer a][integer c] = integer d
//
//Inline-friendly when not running in debug mode
//
method operator [] takes integer key returns Table
static if DEBUG_MODE then
local integer i = this.size
if i == 0 then
call BJDebugMsg("IndexError: Tried to get key from invalid TableArray instance: " + I2S(this))
return 0
elseif key < 0 or key >= i then
call BJDebugMsg("IndexError: Tried to get key [" + I2S(key) + "] from outside TableArray bounds: " + I2S(i))
return 0
endif
endif
return this + key
endmethod
//Destroys a TableArray without flushing it; I assume you call .flush()
//if you want it flushed too. This is a public method so that you don't
//have to loop through all TableArray indices to flush them if you don't
//need to (ie. if you were flushing all child-keys as you used them).
//
method destroy takes nothing returns nothing
local Table tb = dex.size[this.size]
debug if this.size == 0 then
debug call BJDebugMsg("TypeError: Tried to destroy an invalid TableArray: " + I2S(this))
debug return
debug endif
if tb == 0 then
//Create a Table to index recycled instances with their array size
set tb = Table.create()
set dex.size[this.size] = tb
endif
call dex.size.remove(this) //Clear the array size from hash memory
set tb[this] = tb[0]
set tb[0] = this
endmethod
private static Table tempTable
private static integer tempEnd
//Avoids hitting the op limit
private static method clean takes nothing returns nothing
local Table tb = .tempTable
local integer end = tb + 0x1000
if end < .tempEnd then
set .tempTable = end
call ForForce(bj_FORCE_PLAYER[0], function thistype.clean)
else
set end = .tempEnd
endif
loop
call tb.flush()
set tb = tb + 1
exitwhen tb == end
endloop
endmethod
//Flushes the TableArray and also destroys it. Doesn't get any more
//similar to the FlushParentHashtable native than this.
//
method flush takes nothing returns nothing
debug if this.size == 0 then
debug call BJDebugMsg("TypeError: Tried to flush an invalid TableArray instance: " + I2S(this))
debug return
debug endif
set .tempTable = this
set .tempEnd = this + this.size
call ForForce(bj_FORCE_PLAYER[0], function thistype.clean)
call this.destroy()
endmethod
endstruct
endlibrary
//TESH.scrollpos=366
//TESH.alwaysfold=0
library DamageEvent/* v 1.3.0.0
**********************************************************************************
*
* Physical Damage Detection Engine
* --------------------------------
* By looking_for_help aka eey
*
* This system is able to detect, modify and deal damage. It can differentiate
* between physical and spell damage, as well as block, reduce or increase the
* applied damage to a desired value. You can also allocate damage events from
* running damage events.
*
**********************************************************************************
*
* Implementation
* --------------
* 1. Copy this trigger to your map. With the AddDamageHandler function you can
* add as many handlers as you like (compare the OnDamage scope).
* 2. Copy the two custom abilities to your map and make sure they have the
* correct ID in the globals variable block.
* 3. Go to the locust swarm ability and invert its damage return portion
* from (default) 0.75 to -0.75.
* 4. Remove the spell damage reduction ability from the spell damage reduction
* items you use (runed bracers). You can configure the resistance of this
* item in the globals block, modifying BRACERS_SPELL_DAMAGE_REDUCTION.
*
**********************************************************************************
*
* Important Notes
* ---------------
* 1. Life Drain does not work with this system, so you should use a triggered
* version of this spell if you want to use it.
* 2. Same for Finger of Death, if you want to use this spell bug free with this
* system, you should use a triggered version of it.
* 3. If you use damage modifiers by setting the damage amount variable, you have
* to use GetUnitLife, GetUnitMaxLife and SetUnitLife instead of GetWidgetLife,
* GetUnitState for UNIT_STATE_MAX_LIFE and SetWidgetLife in your whole map to
* ensure there occure no bugs.
* 4. The boolean USE_SPELL_RESISTANCE_AUTO_DETECT is only neccessary set to true
* if you want to use a customized damage table with spell damage resistance
* above 100%. If this is not the case, it should be set to false, as the
* system is faster then and still works correct.
* 5. As already mentioned you can't use the spell reduction ability when using this
* system (runed bracers and elunes grace). If you want to use them, you can
* trigger them by using the damage modifiers. Runed bracers is already considered
* in this system, so you don't have to code it.
*
**********************************************************************************
*
* System API
* ----------
* unit PDDS.target
* - In this global unit variable, the damaged unit is saved. Don't write any-
* thing into this variable, use it as readonly.
*
* unit PDDS.source
* - In this global unit variable, the damage source is saved. Don't write any-
* thing into this variable, use it as readonly.
*
* real PDDS.amount
* - In this global real variable, the amount of damage is saved. This amount
* can be modified by simply setting it to the desired amount that should be
* applied to the target. Set the amount to 0.0 to block the damage completly.
* Negative values will result in heal.
*
* integer PDDS.damageType
* - In this global integer variable, the damage type of the current damage is
* saved. Use it to differentiate between physical, spell and code damage. The
* variable can takes the values PHYSICAL == 0, SPELL == 1 and CODE == 2. Don't
* write anything into this variable, use it as readonly.
*
* function UnitDamageTargetEx takes unit localSource, unit localTarget, real localAmount,
* boolean attack, boolean ranged, attacktype localAttackType,
* damagetype localDamageType, weapontype localWeaponType
* returns boolean
* - Use this function to allocate attacks from a running damage event. You can use
* it in exactly the same way as the standard native UnitDamageTarget. The function
* is designed recursive, so you can allocate as many attacks as you want. Do not
* use this function outside of a damage event.
*
* function GetUnitLife takes unit u returns real
* - Use this function instead of the GetWidgetLife native. It ensures that you
* get the correct health value, even when damage modifiers are applied. If
* you don't use damage modifiers at all, you don't need this function.
*
* function GetUnitMaxLife takes unit u returns real
* - Use this function instead of the GetUnitState(u, UNIT_STATE_MAX_LIFE) native.
* It will return the correct value, even when damage modifiers are applied. If
* you don't use damage modifiers at all, you don't need this function.
*
* function SetUnitLife takes unit u, real newLife returns nothing
* - Use this function instead of the SetWidgetLife(u, newLife) native if you use
* damage modifiers in your map. Same rules as for the GetUnitMaxLife and the
* GetUnitMaxLife functions.
*
* function AddDamageHandler takes code damageHandler returns nothing
* - Allows you to add a damage handler function to the system. Compare the
* OnDamage scope for an example usage.
*
* function RemoveDamageHandler takes code damageHandler returns nothing
* - Allows you to remove a damage handler function from the system dynamic-
* ally. If you added the same handler function multiple times to the sys-
* tem, this function will remove all of these equal functions.
*
*********************************************************************************/
globals
/*************************************************************************
* Customizable globals
*************************************************************************/
private constant integer DAMAGE_TYPE_DETECTOR = 0 //'A002'
private constant integer SET_MAX_LIFE = 0 //'A003'
private constant integer SPELL_DAMAGE_REDUCTION_ITEM = 'brac'
private constant boolean USE_SPELL_RESISTANCE_AUTO_DETECT = false
private constant real ETHEREAL_DAMAGE_FACTOR = 1.66
private constant real BRACERS_SPELL_DAMAGE_REDUCTION = 0.33
private constant real TRIGGER_CLEANUP_PERIOD = 10.0
/*************************************************************************
* End of Customizable globals
*************************************************************************/
constant integer PHYSICAL = 0
constant integer SPELL = 1
constant integer CODE = 2
constant real UNIT_MIN_LIFE = 0.406
private constant attacktype ATTACK_TYPE_UNIVERSAL = ConvertAttackType(7)
private hashtable h
private real pureAmount
private trigger damageEvent
private trigger damageHandler
private trigger runAllocatedAttacks
private integer allocatedAttacks
private integer totalAllocs
private integer allocCounter
private real damageEventTrigger
endglobals
struct PDDS extends array
static unit source
static unit target
static real amount
static integer damageType
endstruct
/******************************************************************************
* User functions
******************************************************************************/
function AddDamageHandler takes code func returns nothing
local integer id = GetHandleId(Condition(func))
local integer index = 0
// Loop to manage equal damage handlers
loop
exitwhen ( LoadTriggerConditionHandle(h, id, index) == null )
set index = index + 1
endloop
// Store the desired damage handler function
call SaveTriggerConditionHandle(h, id, index, TriggerAddCondition(damageHandler, Filter(func)))
endfunction
function RemoveDamageHandler takes code func returns nothing
local integer id = GetHandleId(Condition(func))
local integer index = 0
// Loop through all equal damage handlers
loop
exitwhen ( LoadTriggerConditionHandle(h, id, index) == null )
call TriggerRemoveCondition(damageHandler, LoadTriggerConditionHandle(h, id, index))
set index = index + 1
endloop
// Clean things up
call FlushChildHashtable(h, id)
endfunction
function GetUnitLife takes unit u returns real
local boolean duringModification
local integer uId = GetHandleId(u)
local real health
local real storedHealth = LoadReal(h, uId, 2)
local real storedDamage = LoadReal(h, uId, 1)
// Check if the unit is being rescued from damage
set duringModification = GetUnitAbilityLevel(u, SET_MAX_LIFE) > 0
if duringModification then
call UnitRemoveAbility(u, SET_MAX_LIFE)
endif
// Get the correct health value of the unit
if storedHealth != 0.0 then
set health = storedHealth - storedDamage
else
set health = GetWidgetLife(u) - storedDamage
endif
// Restore the rescue ability and return
if duringModification then
call UnitAddAbility(u, SET_MAX_LIFE)
endif
return health
endfunction
function GetUnitMaxLife takes unit u returns real
local real maxHealth
// Check if the unit is being rescued from damage
if GetUnitAbilityLevel(u, SET_MAX_LIFE) > 0 then
call UnitRemoveAbility(u, SET_MAX_LIFE)
set maxHealth = GetUnitState(u, UNIT_STATE_MAX_LIFE)
call UnitAddAbility(u, SET_MAX_LIFE)
return maxHealth
endif
// If the unit isn't being rescued, use the standard native
return GetUnitState(u, UNIT_STATE_MAX_LIFE)
endfunction
function SetUnitLife takes unit u, real newLife returns nothing
local integer targetId
local integer oldTimerId
local real oldHealth
// Check if the unit is being rescued from damage
if GetUnitAbilityLevel(u, SET_MAX_LIFE) > 0 then
call UnitRemoveAbility(u, SET_MAX_LIFE)
call SetWidgetLife(u, newLife)
call UnitAddAbility(u, SET_MAX_LIFE)
// Get the unit specific timer information
set targetId = GetHandleId(u)
set oldHealth = LoadReal(h, targetId, 0)
// Update the unit specific timer information
if oldHealth != 0.0 then
set oldTimerId = LoadInteger(h, targetId, 3)
call SaveReal(h, targetId, 2, newLife)
call SaveReal(h, targetId, 0, newLife)
call SaveReal(h, oldTimerId, 4, newLife)
endif
return
endif
// If the unit isn't being rescued, use the standard native
call SetWidgetLife(u, newLife)
endfunction
function UnitDamageTargetEx takes unit localSource, unit localTarget, real localAmount, boolean attack, /*
*/boolean ranged, attacktype localAttackType, damagetype localDamageType, /*
*/weapontype localWeaponType returns boolean
// Avoid infinite loop due to recursion
if PDDS.damageType == CODE then
return false
endif
// Avoid allocating attacks on units that are about to be killed
if ( localTarget == PDDS.target and GetUnitLife(localTarget) - PDDS.amount < UNIT_MIN_LIFE ) then
return false
endif
// Store all damage parameters determined by the user
set allocatedAttacks = allocatedAttacks + 1
call SaveUnitHandle(h, allocatedAttacks, 0, localSource)
call SaveUnitHandle(h, allocatedAttacks, 1, localTarget)
call SaveReal(h, allocatedAttacks, 2, localAmount)
call SaveBoolean(h, allocatedAttacks, 3, attack)
call SaveBoolean(h, allocatedAttacks, 4, ranged)
call SaveInteger(h, allocatedAttacks, 5, GetHandleId(localAttackType))
call SaveInteger(h, allocatedAttacks, 6, GetHandleId(localDamageType))
call SaveInteger(h, allocatedAttacks, 7, GetHandleId(localWeaponType))
// Return true if the damage was allocated
return true
endfunction
/******************************************************************************
* Sub functions
******************************************************************************/
private function DealFixDamage takes unit source, unit target, real pureAmount returns nothing
local real MAX_DAMAGE = 1000000.0
local real beforeHitpoints
local real newHitpoints
// Ensure the amount is positive
if pureAmount < 0 then
set pureAmount = -pureAmount
endif
// Save the targets hitpoints
set beforeHitpoints = GetWidgetLife(target)
set newHitpoints = beforeHitpoints - pureAmount
// Apply the desired, fixed amount
if newHitpoints >= UNIT_MIN_LIFE then
call SetUnitState(target, UNIT_STATE_LIFE, newHitpoints)
else
if ( IsUnitType(target, UNIT_TYPE_ETHEREAL) == false ) then
call SetWidgetLife(target, 1.0)
call UnitDamageTarget(source, target, MAX_DAMAGE, true, false, ATTACK_TYPE_UNIVERSAL, DAMAGE_TYPE_UNIVERSAL, null)
else
call UnitRemoveAbility(target, DAMAGE_TYPE_DETECTOR)
call SetWidgetLife(target, 1.0)
call UnitDamageTarget(source, target, MAX_DAMAGE, true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_UNIVERSAL, null)
endif
endif
endfunction
private function GetUnitSpellResistance takes unit u returns real
local real originalHP
local real beforeHP
local real afterHP
local real resistance
local real DUMMY_DAMAGE = 100
local real DUMMY_FACTOR = 0.01
// Deal spell damage in order to get the units resistance
call UnitRemoveAbility(u, DAMAGE_TYPE_DETECTOR)
set originalHP = GetWidgetLife(u)
call UnitAddAbility(u, SET_MAX_LIFE)
call SetWidgetLife(u, 20000.0)
set beforeHP = GetWidgetLife(u)
call DisableTrigger(damageEvent)
call UnitDamageTarget(PDDS.source, u, DUMMY_DAMAGE, true, false, null, DAMAGE_TYPE_UNIVERSAL, null)
call EnableTrigger(damageEvent)
set afterHP = GetWidgetLife(u)
call UnitRemoveAbility(u, SET_MAX_LIFE)
call SetWidgetLife(u, originalHP)
call UnitAddAbility(u, DAMAGE_TYPE_DETECTOR)
call UnitMakeAbilityPermanent(u, true, DAMAGE_TYPE_DETECTOR)
// Calculate this resistance
set resistance = DUMMY_FACTOR*(beforeHP - afterHP)
// If the resistance was greater than 100%, return it
if resistance > 1.0 then
return resistance
else
return 1.0
endif
endfunction
private function UnitHasItemOfType takes unit u, integer itemId returns integer
local integer index = 0
local item indexItem
// Check if the target has a spell damage reducing item
loop
set indexItem = UnitItemInSlot(u, index)
if ( indexItem != null ) and ( GetItemTypeId(indexItem) == itemId ) then
set indexItem = null
return index + 1
endif
set index = index + 1
exitwhen index >= bj_MAX_INVENTORY
endloop
set indexItem = null
return 0
endfunction
/******************************************************************************
* Damage Engine
******************************************************************************/
private function AfterDamage takes nothing returns nothing
local timer time = GetExpiredTimer()
local integer id = GetHandleId(time)
local unit localSource = LoadUnitHandle(h, id, 0)
local unit localTarget = LoadUnitHandle(h, id, 1)
local real amount = LoadReal(h, id, 3)
local real originalHealth = LoadReal(h, id, 4)
// If the damage was modified, restore units health
if originalHealth != 0.0 then
call UnitRemoveAbility(localTarget, SET_MAX_LIFE)
call SetWidgetLife(localTarget, originalHealth)
endif
// Apply the desired amount of damage
if amount > 0.0 then
call DisableTrigger(damageEvent)
call DealFixDamage(localSource, localTarget, amount)
call EnableTrigger(damageEvent)
else
call SetWidgetLife(localTarget, originalHealth - amount)
endif
// Clean things up
call FlushChildHashtable(h, id)
call FlushChildHashtable(h, GetHandleId(localTarget))
call DestroyTimer(time)
set time = null
set localSource = null
set localTarget = null
endfunction
private function DamageEngine takes nothing returns nothing
local timer time
local integer id
local real rawAmount
local real originalHealth
local integer targetId
local integer oldTimerId
local real oldHealth
local real oldOriginalHealth
local real oldAmount
// Set damage variables
set rawAmount = GetEventDamage()
if rawAmount == 0.0 then
return
endif
set PDDS.source = GetEventDamageSource()
set PDDS.target = GetTriggerUnit()
// Determine the damage type
if rawAmount > 0.0 then
if PDDS.damageType != CODE then
set PDDS.damageType = PHYSICAL
endif
set PDDS.amount = rawAmount
else
if PDDS.damageType != CODE then
set PDDS.damageType = SPELL
endif
set PDDS.amount = -rawAmount
endif
// Register spell reduction above 100%
static if USE_SPELL_RESISTANCE_AUTO_DETECT then
set PDDS.amount = GetUnitSpellResistance(PDDS.target)*PDDS.amount
else
if ( IsUnitType(PDDS.target, UNIT_TYPE_ETHEREAL) and IsUnitEnemy(PDDS.target, GetOwningPlayer(PDDS.source)) and rawAmount < 0.0 ) then
set PDDS.amount = ETHEREAL_DAMAGE_FACTOR*PDDS.amount
endif
endif
// Register spell damage reducing items like runed bracers
if ( IsUnitType(PDDS.target, UNIT_TYPE_HERO) and UnitHasItemOfType(PDDS.target, SPELL_DAMAGE_REDUCTION_ITEM) > 0 ) and rawAmount < 0.0 then
set PDDS.amount = (1 - BRACERS_SPELL_DAMAGE_REDUCTION)*PDDS.amount
endif
// Save health and damage variables
if PDDS.damageType != CODE then
call UnitRemoveAbility(PDDS.target, SET_MAX_LIFE)
endif
set pureAmount = PDDS.amount
set originalHealth = GetWidgetLife(PDDS.target)
set oldTimerId = 0
// Call damage handlers
set damageEventTrigger = 0.0
set damageEventTrigger = 1.0
set damageEventTrigger = 0.0
// If the damage was modified, apply the rescue ability
if PDDS.amount != pureAmount then
call UnitAddAbility(PDDS.target, SET_MAX_LIFE)
call SetWidgetLife(PDDS.target, GetWidgetLife(PDDS.target) + pureAmount)
endif
// Check if a previous timer didn't yet expire
set targetId = GetHandleId(PDDS.target)
set oldHealth = LoadReal(h, targetId, 0)
// If this is the case, update the timer information
if oldHealth != 0.0 then
set oldTimerId = LoadInteger(h, targetId, 3)
set oldOriginalHealth = LoadReal(h, targetId, 2)
set oldAmount = LoadReal(h, targetId, 1)
set originalHealth = oldOriginalHealth - oldAmount
call SaveReal(h, oldTimerId, 4, oldOriginalHealth)
endif
// Call after damage event if damage was spell, modified, code or parallel
if ( rawAmount < 0.0 or pureAmount != PDDS.amount or oldTimerId != 0 or allocatedAttacks > 0 ) then
set time = CreateTimer()
set id = GetHandleId(time)
// Save handles for after damage event
call SaveUnitHandle(h, id, 0, PDDS.source)
call SaveUnitHandle(h, id, 1, PDDS.target)
call SaveReal(h, id, 2, pureAmount)
call SaveReal(h, id, 3, PDDS.amount)
call SaveReal(h, id, 4, originalHealth)
// Save this extra to manage parallel damage instances
call SaveReal(h, targetId, 0, GetWidgetLife(PDDS.target))
call SaveReal(h, targetId, 1, PDDS.amount)
call SaveReal(h, targetId, 2, originalHealth)
call SaveInteger(h, targetId, 3, id)
// Avoid healing of negative spell damage
if rawAmount < 0.0 then
call DisableTrigger(damageEvent)
call DealFixDamage(PDDS.source, PDDS.target, -rawAmount)
if ( originalHealth - PDDS.amount < UNIT_MIN_LIFE ) then
call UnitRemoveAbility(PDDS.target, SET_MAX_LIFE)
call DealFixDamage(PDDS.source, PDDS.target, PDDS.amount)
endif
call EnableTrigger(damageEvent)
endif
// Guarantee unit exploding
if originalHealth - PDDS.amount < UNIT_MIN_LIFE then
if rawAmount > 0.0 then
call UnitRemoveAbility(PDDS.target, SET_MAX_LIFE)
call SetWidgetLife(PDDS.target, UNIT_MIN_LIFE)
endif
endif
// Start the after damage event
call TimerStart(time, 0.0, false, function AfterDamage)
endif
// Handle allocated attacks from UnitDamageTargetEx
if totalAllocs == 0 then
set totalAllocs = allocatedAttacks
endif
if allocatedAttacks > 0 then
set allocatedAttacks = allocatedAttacks - 1
set allocCounter = allocCounter + 1
call TriggerEvaluate(runAllocatedAttacks)
endif
// Reset all required variables
set totalAllocs = 0
set allocCounter = -1
set PDDS.damageType = -1
endfunction
/******************************************************************************
* Initialization
******************************************************************************/
private function RestoreTriggers takes nothing returns nothing
local unit enumUnit = GetEnumUnit()
// Re-register units that are alive
if GetUnitTypeId(enumUnit) != 0 then
call TriggerRegisterUnitEvent(damageEvent, enumUnit, EVENT_UNIT_DAMAGED)
endif
set enumUnit = null
endfunction
private function ClearMemory_Actions takes nothing returns nothing
local group g = CreateGroup()
local code c = function DamageEngine
// Reset the damage event
call GroupEnumUnitsInRect(g, GetWorldBounds(), null)
call ResetTrigger(damageEvent)
call DestroyTrigger(damageEvent)
set damageEvent = null
// Rebuild it then
set damageEvent = CreateTrigger()
call TriggerAddCondition(damageEvent, Filter(c))
call ForGroup(g, function RestoreTriggers)
// Clean things up
call DestroyGroup(g)
set g = null
set c = null
endfunction
private function MapInit takes nothing returns nothing
local unit enumUnit = GetEnumUnit()
// Register units on map initialization
call UnitAddAbility(enumUnit, DAMAGE_TYPE_DETECTOR)
call UnitMakeAbilityPermanent(enumUnit, true, DAMAGE_TYPE_DETECTOR)
call TriggerRegisterUnitEvent(damageEvent, enumUnit, EVENT_UNIT_DAMAGED)
set enumUnit = null
endfunction
private function UnitEntersMap takes nothing returns nothing
local unit triggerUnit = GetTriggerUnit()
// Register units that enter the map
if ( GetUnitAbilityLevel(triggerUnit, DAMAGE_TYPE_DETECTOR) < 1 ) then
call UnitAddAbility(triggerUnit, DAMAGE_TYPE_DETECTOR)
call UnitMakeAbilityPermanent(triggerUnit, true, DAMAGE_TYPE_DETECTOR)
call TriggerRegisterUnitEvent(damageEvent, triggerUnit, EVENT_UNIT_DAMAGED)
endif
set triggerUnit = null
endfunction
private function RunAllocatedAttacks takes nothing returns nothing
local integer localAllocAttacks = allocatedAttacks + 1
// Calculate the correct sequence of allocated attacks
set localAllocAttacks = localAllocAttacks - totalAllocs + 1 + 2*allocCounter
// Deal code damage if the unit isn't exploding
set PDDS.damageType = CODE
if GetUnitLife(LoadUnitHandle(h, localAllocAttacks, 1)) >= UNIT_MIN_LIFE then
call UnitDamageTarget(LoadUnitHandle(h, localAllocAttacks, 0), LoadUnitHandle(h, localAllocAttacks, 1), /*
*/LoadReal(h, localAllocAttacks, 2), LoadBoolean(h, localAllocAttacks, 3), /*
*/LoadBoolean(h, localAllocAttacks, 4), ConvertAttackType(LoadInteger(h, /*
*/localAllocAttacks, 5)), ConvertDamageType(LoadInteger(h, localAllocAttacks, 6)), /*
*/ConvertWeaponType(LoadInteger(h, localAllocAttacks, 7)))
else
call FlushChildHashtable(h, localAllocAttacks - 1)
endif
// Clean things up
call FlushChildHashtable(h, localAllocAttacks)
endfunction
private module Inits
private static method onInit takes nothing returns nothing
local group g = CreateGroup()
local region r = CreateRegion()
local trigger UnitEnters = CreateTrigger()
local trigger ClearMemory = CreateTrigger()
local code cDamageEngine = function DamageEngine
local code cUnitEnters = function UnitEntersMap
local code cClearMemory = function ClearMemory_Actions
local code cRunAllocatedAttacks = function RunAllocatedAttacks
// Initialize variables
set h = InitHashtable()
set damageEvent = CreateTrigger()
set damageHandler = CreateTrigger()
set PDDS.damageType = -1
set allocatedAttacks = 0
set runAllocatedAttacks = CreateTrigger()
set totalAllocs = 0
set allocCounter = -1
// Register units on map initialization
call TriggerRegisterVariableEvent(damageHandler, SCOPE_PRIVATE + "damageEventTrigger", EQUAL, 1.0)
call TriggerAddCondition(damageEvent, Filter(cDamageEngine))
call GroupEnumUnitsInRect(g, GetWorldBounds(), null)
call ForGroup(g, function MapInit)
// Register units that enter the map
call RegionAddRect(r, GetWorldBounds())
call TriggerRegisterEnterRegion(UnitEnters, r, null)
call TriggerAddCondition(UnitEnters, Filter(cUnitEnters))
// Register trigger for allocated attacks
call TriggerAddCondition(runAllocatedAttacks, Filter(cRunAllocatedAttacks))
// Clear memory leaks
call TriggerRegisterTimerEvent(ClearMemory, TRIGGER_CLEANUP_PERIOD, true)
call TriggerAddCondition(ClearMemory, Filter(cClearMemory))
// Clean things up
call DestroyGroup(g)
set UnitEnters = null
set ClearMemory = null
set cDamageEngine = null
set cUnitEnters = null
set cClearMemory = null
set cRunAllocatedAttacks = null
set g = null
set r = null
endmethod
endmodule
private struct Init extends array
implement Inits
endstruct
endlibrary
//TESH.scrollpos=35
//TESH.alwaysfold=0
//============================================================================
// SpellEffectEvent
// - Version 1.1.0.0
//
// API
// ---
// RegisterSpellEffectEvent(integer abil, code onCast)
//
// Requires
// --------
// RegisterPlayerUnitEvent: hiveworkshop.com/forums/showthread.php?t=203338
//
// Optional
// --------
// Table: hiveworkshop.com/forums/showthread.php?t=188084
//
library SpellEffectEvent requires RegisterPlayerUnitEvent, optional Table
//============================================================================
private module M
static if LIBRARY_Table then
static Table tb
else
static hashtable ht = InitHashtable()
endif
static method onCast takes nothing returns nothing
static if LIBRARY_Table then
call TriggerEvaluate(.tb.trigger[GetSpellAbilityId()])
else
call TriggerEvaluate(LoadTriggerHandle(.ht, 0, GetSpellAbilityId()))
endif
endmethod
private static method onInit takes nothing returns nothing
static if LIBRARY_Table then
set .tb = Table.create()
endif
call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_SPELL_EFFECT, function thistype.onCast)
endmethod
endmodule
//============================================================================
private struct S extends array
implement M
endstruct
//============================================================================
function RegisterSpellEffectEvent takes integer abil, code onCast returns nothing
static if LIBRARY_Table then
if not S.tb.handle.has(abil) then
set S.tb.trigger[abil] = CreateTrigger()
endif
call TriggerAddCondition(S.tb.trigger[abil], Filter(onCast))
else
if not HaveSavedHandle(S.ht, 0, abil) then
call SaveTriggerHandle(S.ht, 0, abil, CreateTrigger())
endif
call TriggerAddCondition(LoadTriggerHandle(S.ht, 0, abil), Filter(onCast))
endif
endfunction
endlibrary
//TESH.scrollpos=29
//TESH.alwaysfold=0
/**************************************************************
*
* RegisterPlayerUnitEvent
* v5.1.0.1
* By Magtheridon96
*
* I would like to give a special thanks to Bribe, azlier
* and BBQ for improving this library. For modularity, it only
* supports player unit events.
*
* Functions passed to RegisterPlayerUnitEvent must either
* return a boolean (false) or nothing. (Which is a Pro)
*
* Warning:
* --------
*
* - Don't use TriggerSleepAction inside registered code.
* - Don't destroy a trigger unless you really know what you're doing.
*
* API:
* ----
*
* - function RegisterPlayerUnitEvent takes playerunitevent whichEvent, code whichFunction returns nothing
* - Registers code that will execute when an event fires.
* - function RegisterPlayerUnitEventForPlayer takes playerunitevent whichEvent, code whichFunction, player whichPlayer returns nothing
* - Registers code that will execute when an event fires for a certain player.
* - function GetPlayerUnitEventTrigger takes playerunitevent whichEvent returns trigger
* - Returns the trigger corresponding to ALL functions of a playerunitevent.
*
**************************************************************/
library RegisterPlayerUnitEvent // Special Thanks to Bribe and azlier
globals
private trigger array t
endglobals
function RegisterPlayerUnitEvent takes playerunitevent p, code c returns nothing
local integer i = GetHandleId(p)
local integer k = 15
if t[i] == null then
set t[i] = CreateTrigger()
loop
call TriggerRegisterPlayerUnitEvent(t[i], Player(k), p, null)
exitwhen k == 0
set k = k - 1
endloop
endif
call TriggerAddCondition(t[i], Filter(c))
endfunction
function RegisterPlayerUnitEventForPlayer takes playerunitevent p, code c, player pl returns nothing
local integer i = 16 * GetHandleId(p) + GetPlayerId(pl)
if t[i] == null then
set t[i] = CreateTrigger()
call TriggerRegisterPlayerUnitEvent(t[i], pl, p, null)
endif
call TriggerAddCondition(t[i], Filter(c))
endfunction
function GetPlayerUnitEventTrigger takes playerunitevent p returns trigger
return t[GetHandleId(p)]
endfunction
endlibrary
//TESH.scrollpos=6
//TESH.alwaysfold=0
/***************************************************************
Healing Arcs v1.5
by mckill2009
****************************************************************
REQUIRED LIBRARIES:
- SpellEffectEvent by Bribe
- Table by Bribe
- DamageEvent by looking_for_help
****************************************************************/
library HealingArcs initializer Init uses SpellEffectEvent, Table, DamageEvent
globals
/***********************************************************
* RAW CODES: Press CTRL+D view and change raw codes from
* object editor accordingly
************************************************************/
private constant integer SPELL_ID = 'A000' //unit target spell
private constant integer DUMMY_ID = 'h000' //unit who cast the healing arc
private constant integer BOLT_SPELL_ID = 'A001' //firebolt ID with arc
private constant integer BOLT_ORDER_ID = 852231 //firebolt order ID, must match with BOLT_SPELL_ID
/***********************************************************
* CONFIGURABLE GLOBALS
************************************************************/
private constant string HEAL_SFX = "Abilities\\Spells\\Human\\HolyBolt\\HolyBoltSpecialArt.mdl"
private constant string DAMAGE_SFX = "Abilities\\Spells\\Undead\\DeathCoil\\DeathCoilSpecialArt.mdl"
private constant string HEAL_ATTACHMENT = "overhead"
private constant string DAMAGE_ATTACHMENT = "chest"
private constant player NEUTRAL_PASSIVE = Player(15)
private constant attacktype ATK = ATTACK_TYPE_CHAOS
private constant damagetype DMG = DAMAGE_TYPE_DEATH
/***********************************************************
* NON-CONFIGURABLE GLOBALS
************************************************************/
private group g = CreateGroup()
private unit dummy
private player owner
private integer uID
private Table heal
endglobals
/***********************************************************
* CONFIGURABLE FUNCTIONS
************************************************************/
//Area of effect when searching allies to heal
private function GetAoE takes integer level returns real
return 200. * level + 300 //500, 700, 900, 1100, 1300
endfunction
//Targets only 1 enemy
private function GetDamageAmount takes integer level returns real
return 25. * level + 25 //50, 75, 100, 125, 150
endfunction
//Heals the main ally target
private function GetHealAmount takes integer level returns real
return 50. * level + 50 //100, 150, 200, 250, 300
endfunction
//Heal Amount of the allies nearby
private function GetHealAmountArc takes integer level returns real
return 50. * level + 50 //100, 150, 200, 250, 300
endfunction
private function UnitFilter takes unit u returns boolean
return not (IsUnitType(u, UNIT_TYPE_STRUCTURE) or IsUnitType(u, UNIT_TYPE_MECHANICAL))
endfunction
/***********************************************************
* NON-CONFIGURABLE FUNCTIONS
************************************************************/
private function UnitAlive takes unit u returns boolean
return not (IsUnitType(u, UNIT_TYPE_DEAD) or GetUnitTypeId(u)==0 or u==null)
endfunction
private function IsAllyUnitDamaged takes unit u returns boolean
return GetWidgetLife(u) < GetUnitState(u, UNIT_STATE_MAX_LIFE)
endfunction
private function ArcLands takes nothing returns nothing
if PDDS.source==dummy then
if UnitFilter(PDDS.target) then
set uID = GetHandleId(PDDS.target)
set owner = heal.player[uID]
if IsUnitEnemy(PDDS.target, owner) then
call UnitDamageTarget(dummy, PDDS.target, GetDamageAmount(heal[uID]), false, false, ATK, DMG, null)
call DestroyEffect(AddSpecialEffectTarget(DAMAGE_SFX, PDDS.target, DAMAGE_ATTACHMENT))
else
call SetWidgetLife(PDDS.target, GetWidgetLife(PDDS.target) + heal.real[uID])
call DestroyEffect(AddSpecialEffectTarget(HEAL_SFX, PDDS.target, HEAL_ATTACHMENT))
endif
endif
set heal.player[uID] = null
call heal.remove(uID)
endif
endfunction
private function Cast takes nothing returns nothing
local unit first
local unit u = GetSpellTargetUnit()
local real x = GetUnitX(u)
local real y = GetUnitY(u)
local integer level = GetUnitAbilityLevel(GetTriggerUnit(), SPELL_ID)
local real healAmt = GetHealAmount(level)
local real healAmtArc = GetHealAmountArc(level)
call SetUnitX(dummy, x)
call SetUnitY(dummy, y)
call SetUnitOwner(dummy, GetTriggerPlayer(), false)
call SetUnitFlyHeight(dummy, GetUnitFlyHeight(u), 0)
if IsUnitEnemy(u, GetTriggerPlayer()) then
call UnitDamageTarget(GetTriggerUnit(), u, GetDamageAmount(level), false, false, ATK, DMG, null)
call DestroyEffect(AddSpecialEffectTarget(DAMAGE_SFX, u, DAMAGE_ATTACHMENT))
else
call SetWidgetLife(u, GetWidgetLife(u) + healAmt)
call DestroyEffect(AddSpecialEffectTarget(HEAL_SFX, u, HEAL_ATTACHMENT))
endif
call GroupEnumUnitsInRange(g, x, y, GetAoE(level), null)
loop
set first = FirstOfGroup(g)
exitwhen first==null
if UnitAlive(first) and first!=u then
if (IsAllyUnitDamaged(first) or IsUnitEnemy(first, GetTriggerPlayer())) and UnitFilter(first) then
set uID = GetHandleId(first)
set heal[uID] = level
set heal.real[uID] = healAmtArc
set heal.player[uID] = GetTriggerPlayer()
call IssueTargetOrderById(dummy, BOLT_ORDER_ID, first)
endif
endif
call GroupRemoveUnit(g, first)
endloop
call SetUnitOwner(dummy, NEUTRAL_PASSIVE, false)
set u = null
endfunction
private function OnDeath takes nothing returns nothing
if heal.has(GetHandleId(GetTriggerUnit())) then
set heal.player[GetHandleId(GetTriggerUnit())] = null
call heal.remove(GetHandleId(GetTriggerUnit()))
endif
endfunction
private function Init takes nothing returns nothing
call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_DEATH, function OnDeath)
call RegisterSpellEffectEvent(SPELL_ID, function Cast)
set dummy = CreateUnit(NEUTRAL_PASSIVE, DUMMY_ID, 0,0,0)
call UnitRemoveAbility(dummy, 'Amov')
call UnitAddAbility(dummy, BOLT_SPELL_ID)
call AddDamageHandler(function ArcLands)
set heal = Table.create()
endfunction
endlibrary