Name | Type | is_array | initial_value |
Test_AI_Engager | unit | No | |
Test_AI_Hero | unit | No | |
Test_AI_Location | location | No | |
Test_AI_Player | player | No | |
Test_Altar_Type | unitcode | Yes | |
Test_Barracks_Type | unitcode | Yes | |
Test_Blacksmith_Type | unitcode | Yes | |
Test_Breaker_Type | unitcode | Yes | |
Test_Castle_Type | unitcode | Yes | |
Test_Dialog | dialog | No | |
Test_Dialog_Race | dialog | No | |
Test_DialogButton | button | Yes | |
Test_Elemental_Count | integer | No | |
Test_Elemental_Group | group | No | |
Test_Farm_Type | unitcode | Yes | |
Test_Flying_Type | unitcode | Yes | |
Test_Footman_Type | unitcode | Yes | |
Test_Frostbite_Count | integer | No | |
Test_Frostbite_Group | group | No | |
Test_Frostbite_Radius | real | No | |
Test_Gryphon_Type | unitcode | Yes | |
Test_Guard_Type | unitcode | Yes | |
Test_Hero | unit | No | |
Test_Hero_Condition | unit | No | |
Test_Hero_Life | real | No | |
Test_Hero_Location | location | No | |
Test_Hero_Mana | real | No | |
Test_Hero_Type | unitcode | Yes | |
Test_Keep_Type | unitcode | Yes | |
Test_Knight_Type | unitcode | Yes | |
Test_Lives | integer | No | |
Test_Location | location | No | |
Test_Lumber_Type | unitcode | Yes | |
Test_Modified_Flag | boolean | Yes | |
Test_Mortar_Type | unitcode | Yes | |
Test_Peasant_Type | unitcode | Yes | |
Test_Priest_Type | unitcode | Yes | |
Test_Quest_Flag | boolean | No | |
Test_Quest_Player | player | No | |
Test_Race_Facing | real | No | |
Test_Race_Hero | unit | No | |
Test_Race_Integer | integer | No | |
Test_Race_Picked | unit | No | |
Test_Race_Type | unitcode | No | |
Test_Region | rect | Yes | |
Test_Reinforcements_Timer | timer | No | |
Test_Reinforcements_Window | timerdialog | No | |
Test_Rifleman_Type | unitcode | Yes | |
Test_Sanctum_Type | unitcode | Yes | |
Test_Scout_Type | unitcode | Yes | |
Test_Siege_Type | unitcode | Yes | |
Test_Sorceress_Type | unitcode | Yes | |
Test_Town_Type | unitcode | Yes | |
Test_Vault_Type | unitcode | Yes | |
Test_Workshop_Type | unitcode | Yes |
library Alloc /* v1.1.0 https://www.hiveworkshop.com/threads/324937/
*/uses /*
*/Table /* https://www.hiveworkshop.com/threads/188084/
*/optional ErrorMessage /* https://github.com/nestharus/JASS/blob/master/jass/Systems/ErrorMessage
*///! novjass
/*
Coded by AGD, based on MyPad's allocation algorithm
An allocator module using a single global indexed stack. Allocated values are
within the JASS_MAX_ARRAY_SIZE. No need to worry about code bloats behind
the module implementation as it generates only six lines of code without DEBUG_MODE.
*/
|-----------|
| INTERFACE |
|-----------|
/*
*/module GlobalAlloc/*
- Uses a single stack globally.
*/module Alloc/*
- Uses a unique stack per struct.
*/debug readonly boolean allocated/* Is node allocated?
*/static method allocate takes nothing returns thistype/*
*/method deallocate takes nothing returns nothing/*
*///! endnovjass
/*===========================================================================*/
globals
private key stack
endglobals
static if DEBUG_MODE then
private function AssertError takes boolean condition, string methodName, string structName, integer node, string message returns nothing
static if LIBRARY_ErrorMessage then
call ThrowError(condition, SCOPE_PREFIX, methodName, structName, node, message)
else
if condition then
call BJDebugMsg("[Library: " + SCOPE_PREFIX + "] [Struct: " + structName + "] [Method: " + methodName + "] [Instance: " + I2S(node) + "] : |cffff0000" + message + "|r")
endif
endif
endfunction
public function IsAllocated takes integer typeId, integer node returns boolean
return node > 0 and Table(stack)[typeId*JASS_MAX_ARRAY_SIZE + node] == 0
endfunction
endif
public function Allocate takes integer typeId returns integer
local integer offset = typeId*JASS_MAX_ARRAY_SIZE
local integer node = Table(stack)[offset]
local integer stackNext = Table(stack)[offset + node]
debug call AssertError(typeId < 0, "allocate()", Table(stack).string[-typeId], 0, "Invalid struct ID (" + I2S(typeId) + ")")
if stackNext == 0 then
debug call AssertError(node == (JASS_MAX_ARRAY_SIZE - 1), "allocate()", Table(stack).string[-typeId], node, "Overflow")
set node = node + 1
set Table(stack)[offset] = node
else
set Table(stack)[offset] = stackNext
set Table(stack)[offset + node] = 0
endif
return node
endfunction
public function Deallocate takes integer typeId, integer node returns nothing
local integer offset = typeId*JASS_MAX_ARRAY_SIZE
debug call AssertError(node == 0, "deallocate()", Table(stack).string[-typeId], 0, "Null node")
debug call AssertError(Table(stack)[offset + node] > 0, "deallocate()", Table(stack).string[-typeId], node, "Double-free")
set Table(stack)[offset + node] = Table(stack)[offset]
set Table(stack)[offset] = node
endfunction
module Alloc
debug method operator allocated takes nothing returns boolean
debug return IsAllocated(thistype.typeid, this)
debug endmethod
static method allocate takes nothing returns thistype
return Allocate(thistype.typeid)
endmethod
method deallocate takes nothing returns nothing
call Deallocate(thistype.typeid, this)
endmethod
debug private static method onInit takes nothing returns nothing
debug set Table(stack).string[-thistype.typeid] = "thistype"
debug endmethod
endmodule
module GlobalAlloc
debug method operator allocated takes nothing returns boolean
debug return IsAllocated(0, this)
debug endmethod
static method allocate takes nothing returns thistype
debug call AssertError(Table(stack)[0] == (JASS_MAX_ARRAY_SIZE - 1), "allocate()", "thistype", JASS_MAX_ARRAY_SIZE - 1, "Overflow")
return Allocate(0)
endmethod
method deallocate takes nothing returns nothing
debug call AssertError(this == 0, "deallocate()", "thistype", 0, "Null node")
debug call AssertError(Table(stack)[this] > 0, "deallocate()", "thistype", this, "Double-free")
call Deallocate(0, this)
endmethod
endmodule
endlibrary
library Buff /*
Buff v1.30
by Flux
Handles all interactions of self-defined buffs.
Features:
- Can dispel positive/negative/both/all buffs.
- Supports 3 types of buff stacking.
- Buffs with duration.
- Pick all Buffs of a unit easily.
*/ requires /*
(nothing)
*/ optional TimerUtils /*
*/ optional RegisterPlayerUnitEvent /*
******************
CREDITS
******************
muzzel - For BuffHandler which this resource is heavily based upon.
Vexorian - For the optional TimerUtils.
Magtheridon96 - For the optional RegisterPlayerUnitEvent.
*/
globals
//-----------------------------//
//--------- BUFF TYPES --------//
//-----------------------------//
constant integer BUFF_NONE = 0
constant integer BUFF_POSITIVE = 1
constant integer BUFF_NEGATIVE = 2
//-----------------------------//
//------ BUFF STACK TYPES -----//
//-----------------------------//
//Applying the same buff only refreshes the duration
//If the buff is reapplied but from a different source, the Buff unit source gets replaced.
constant integer BUFF_STACK_NONE = 0
//Each buff from different source stacks.
//Re-applying the same buff from the same source only refreshes the duration
constant integer BUFF_STACK_PARTIAL = 1
//Each buff applied fully stacks.
constant integer BUFF_STACK_FULL = 2
//Determines the automatic Buff rawcode based on the Ability rawcode
//If BUFF_OFFSET = 0x01000000, then Ability rawcode of 'AXXX' will have Buff rawcode of 'BXXX'
//If BUFF_OFFSET = 0x20000000, then Ability rawcode of 'AXXX' will have Buff rawcode of 'aXXX'
private constant integer BUFF_OFFSET = 0x01000000
//Automatically Preloads all Buff abilities defined in "method rawcode"
//but will generate a lot of scripts in the process
private constant boolean PRELOAD_BUFFS = true
//Automatically initialize a Buff type.
//If false, initialize it using <MyBuff>.initialize()
private constant boolean AUTO_INITIALIZE = true
endglobals
struct Buff
//Buff properties
readonly boolean exist
readonly unit target
readonly unit source
readonly integer rawcode
readonly integer buffId
readonly integer stackType
readonly integer dispelType
//For duration
private timer t
//Buff Enumeration
private thistype bnext
private thistype bprev
//Events
private static thistype callback
private static trigger array onApply
private static trigger array onRemove
method operator name takes nothing returns string
return GetObjectName(this.rawcode)
endmethod
private static hashtable hash = InitHashtable()
//===============================================================
//======================== BUFF CORE ============================
//===============================================================
static method get takes unit source, unit target, integer typeid returns thistype
local integer id = GetHandleId(target)
local thistype this
local thistype head
if HaveSavedInteger(thistype.hash, id, 0) then
set head = LoadInteger(thistype.hash, id, 0)
set this = head
loop
if this.getType() == typeid and (source == null or source == this.source) then
return this
endif
exitwhen this == head.bprev
set this = this.bnext
endloop
endif
return 0
endmethod
static method has takes unit source, unit target, integer typeid returns boolean
return thistype.get(source, target, typeid) > 0
endmethod
method remove takes nothing returns nothing
local boolean remove = false
local integer id
local thistype head
local integer count
if this.exist then
set id = GetHandleId(this.target)
set thistype.callback = this
call TriggerEvaluate(thistype.onRemove[this.getType()])
if this.t != null then
static if LIBRARY_TimerUtils then
call ReleaseTimer(this.t)
else
call RemoveSavedInteger(thistype.hash, GetHandleId(this.t), 0)
call DestroyTimer(this.t)
endif
set this.t = null
endif
if this.stackType == BUFF_STACK_FULL or this.stackType == BUFF_STACK_PARTIAL then
//Update Buff count
set count = LoadInteger(thistype.hash, this.getType(), id) - 1
call SaveInteger(thistype.hash, this.getType(), id, count)
if count == 0 then
set remove = true
endif
elseif this.stackType == BUFF_STACK_NONE then
set remove = true
endif
if remove then
call UnitRemoveAbility(this.target, this.rawcode)
call UnitRemoveAbility(this.target, this.buffId)
endif
//Remove from the BuffList
set head = LoadInteger(thistype.hash, id, 0)
if this == head and this.bnext == head then //If this is the only Buff of the unit
call RemoveSavedInteger(thistype.hash, id, 0)
else
//If this is the head of the BuffList
if this == head then
//Change this unit's BuffList head
call SaveInteger(thistype.hash, id, 0, this.bnext)
endif
set this.bnext.bprev = this.bprev
set this.bprev.bnext = this.bnext
endif
set this.exist = false
set this.target = null
set this.source = null
call this.destroy()
debug else
debug call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, "[Buff]: Attempted to remove non-existing Buff instance.")
endif
endmethod
private static method expires takes nothing returns nothing
static if LIBRARY_TimerUtils then
local thistype this = GetTimerData(GetExpiredTimer())
call ReleaseTimer(GetExpiredTimer())
else
local integer id = GetHandleId(GetExpiredTimer())
local thistype this = LoadInteger(thistype.hash, id, 0)
call RemoveSavedInteger(thistype.hash, id, 0)
call DestroyTimer(GetExpiredTimer())
endif
if this.t != null then
set this.t = null
call this.remove()
endif
endmethod
method operator duration takes nothing returns real
if this.t != null then
return TimerGetRemaining(this.t)
endif
return 0.0
endmethod
method operator duration= takes real time returns nothing
if this.t == null then
static if LIBRARY_TimerUtils then
set this.t = NewTimerEx(this)
else
set this.t = CreateTimer()
call SaveInteger(thistype.hash, GetHandleId(this.t), 0, this)
endif
endif
call TimerStart(this.t, time, false, function thistype.expires)
endmethod
method check takes unit source, unit target returns thistype
local boolean apply = false
local integer id = GetHandleId(target)
local thistype head
local thistype temp
static if not LIBRARY_TimerUtils then
local timer t
endif
if this.stackType == BUFF_STACK_FULL then
//Count how many buffs are stored in a certain unit
call SaveInteger(thistype.hash, this.getType(), id, LoadInteger(thistype.hash, this.getType(), id) + 1)
set apply = true
elseif this.stackType == BUFF_STACK_PARTIAL then
//Check if a similar buff type with the same target and source exist
set temp = thistype.get(source, target, this.getType())
if temp == 0 then //None is found
set apply = true
//Count how many buffs of this type are stored in this certain unit
call SaveInteger(thistype.hash, this.getType(), id, LoadInteger(thistype.hash, this.getType(), id) + 1)
else //Buff is found, use the previous Buff as the newly applied Buff
call this.destroy()
set this = temp
endif
elseif this.stackType == BUFF_STACK_NONE then
//Check if a similar buff type with the same target exist
set temp = thistype.get(null, target, this.getType())
if temp == 0 then //None is found
set apply = true
else //Buff is found, use the previous Buff as the newly applied Buff
call this.destroy()
set this = temp
endif
endif
set this.source = source
set this.target = target
set this.exist = true
set this.buffId = this.rawcode + BUFF_OFFSET
if apply then
if GetUnitAbilityLevel(target, this.rawcode) == 0 then
call UnitAddAbility(target, this.rawcode)
call UnitMakeAbilityPermanent(target, true, this.rawcode)
endif
//Add the Buff to a BuffList of this unit
//If BuffList already exist
if HaveSavedInteger(thistype.hash, id, 0) then
set head = LoadInteger(thistype.hash, id, 0)
set this.bnext = head
set this.bprev = head.bprev
set this.bnext.bprev = this
set this.bprev.bnext = this
else
//Set this as the unit's BuffList head
call SaveInteger(thistype.hash, id, 0, this)
set this.bnext = this
set this.bprev = this
endif
set thistype.callback = this
call TriggerEvaluate(thistype.onApply[this.getType()])
endif
static if LIBRARY_BuffEvent then
static if LIBRARY_TimerUtils then
call TimerStart(NewTimerEx(this), 0.0, false, function BuffEvent.pickAll)
else
set t = CreateTimer()
call SaveInteger(thistype.hash, GetHandleId(t), 0, this)
call TimerStart(t, 0.0, false, function BuffEvent.pickAll)
endif
endif
return this
endmethod
//===============================================================
//======================== BUFF ENUM ============================
//===============================================================
readonly static thistype buffHead
readonly static thistype picked
static method pickBuffs takes unit u returns nothing
local integer id = GetHandleId(u)
if HaveSavedInteger(thistype.hash, id, 0) then
set thistype.buffHead = LoadInteger(thistype.hash, id, 0)
else
set thistype.buffHead = 0
endif
endmethod
//===============================================================
//======================= BUFF DISPEL ===========================
//===============================================================
static method dispel takes unit u, integer dispelType returns nothing
local integer id = GetHandleId(u)
local thistype head
local thistype this
if HaveSavedInteger(thistype.hash, id, 0) then
set head = LoadInteger(thistype.hash, id, 0)
set this = head.bnext
loop
if this.dispelType == dispelType then
call this.remove()
endif
exitwhen this == head
set this = this.bnext
endloop
endif
endmethod
static method dispelBoth takes unit u returns nothing
local integer id = GetHandleId(u)
local thistype head
local thistype this
if HaveSavedInteger(thistype.hash, id, 0) then
set head = LoadInteger(thistype.hash, id, 0)
set this = head.bnext
loop
if this.dispelType == BUFF_POSITIVE or this.dispelType == BUFF_NEGATIVE then
call this.remove()
endif
exitwhen this == head
set this = this.bnext
endloop
endif
endmethod
static method dispelAll takes unit u returns nothing
local integer id = GetHandleId(u)
local thistype head
local thistype this
if HaveSavedInteger(thistype.hash, id, 0) then
set head = LoadInteger(thistype.hash, id, 0)
set this = head.bnext
loop
call this.remove()
exitwhen this == head
set this = this.bnext
endloop
endif
endmethod
private static method onDeath takes nothing returns nothing
call thistype.dispelAll(GetTriggerUnit())
endmethod
implement optional BuffInit
private static method onInit takes nothing returns nothing
static if LIBRARY_RegisterPlayerUnitEvent then
call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_DEATH, function thistype.onDeath)
else
local trigger core = CreateTrigger()
local code death = function thistype.onDeath
call TriggerRegisterAnyUnitEventBJ(core, EVENT_PLAYER_UNIT_DEATH)
call TriggerAddCondition(core, Condition(death))
endif
endmethod
endstruct
static if PRELOAD_BUFFS then
module BuffInit
readonly static unit preloader
private static method onInit takes nothing returns nothing
set thistype.preloader = CreateUnit(Player(14), 'ushd', GetRectMaxX(bj_mapInitialPlayableArea), GetRectMaxY(bj_mapInitialPlayableArea), 0)
call UnitApplyTimedLife(thistype.preloader, 'BTLF', 1.0)
endmethod
endmodule
endif
module BuffApply
static method add takes unit source, unit target returns thistype
local thistype this = thistype.create()
//Write into readonly attributes
set s__Buff_rawcode[this] = thistype.RAWCODE
set s__Buff_stackType[this] = thistype.STACK_TYPE
set s__Buff_dispelType[this] = thistype.DISPEL_TYPE
set this = this.check(source, target)
return this
endmethod
private static method onApplyInit takes nothing returns boolean
call thistype(s__Buff_callback).onApply()
return false
endmethod
private static method onRemoveInit takes nothing returns boolean
call thistype(s__Buff_callback).onRemove()
return false
endmethod
static method initialize takes nothing returns nothing
static if thistype.onApply.exists then
set s__Buff_onApply[thistype.typeid] = CreateTrigger()
call TriggerAddCondition(s__Buff_onApply[thistype.typeid], function thistype.onApplyInit)
endif
static if thistype.onRemove.exists then
set s__Buff_onRemove[thistype.typeid] = CreateTrigger()
call TriggerAddCondition(s__Buff_onRemove[thistype.typeid], function thistype.onRemoveInit)
endif
endmethod
static if PRELOAD_BUFFS then
private static method onInit takes nothing returns nothing
local thistype this = thistype.create()
call UnitAddAbility(Buff.preloader, thistype.RAWCODE)
call UnitRemoveAbility(Buff.preloader, thistype.RAWCODE)
call this.destroy()
static if AUTO_INITIALIZE then
call thistype.initialize()
endif
endmethod
elseif AUTO_INITIALIZE then
private static method onInit takes nothing returns nothing
call thistype.initialize()
endmethod
endif
endmodule
module BuffListStart
if Buff.buffHead > 0 then
set s__Buff_picked = s__Buff_buffHead
loop
endmodule
module BuffListEnd
exitwhen Buff.picked == s__Buff_bprev[s__Buff_buffHead]
set s__Buff_picked = s__Buff_bnext[s__Buff_picked]
endloop
endif
endmodule
endlibrary
library Table /* made by Bribe, special thanks to Vexorian & Nestharus, version 4.1.0.1.0
One map, one hashtable. Welcome to NewTable 4.1.0.1.0
This newest iteration of Table introduces the new HashTable struct.
You can now instantiate HashTables which enables the use of large
parent and large child keys, just like a standard hashtable. Previously,
the user would have to instantiate a Table to do this on their own which -
while doable - is something the user should not have to do if I can add it
to this resource myself (especially if they are inexperienced).
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
//NEW: in Table 4.0. A fairly simple struct but allows you to do more
//than that which was previously possible.
struct HashTable extends array
//Enables myHash[parentKey][childKey] syntax.
//Basically, it creates a Table in the place of the parent key if
//it didn't already get created earlier.
method operator [] takes integer index returns Table
local Table t = Table(this)[index]
if t == 0 then
set t = Table.create()
set Table(this)[index] = t //whoops! Forgot that line. I'm out of practice!
endif
return t
endmethod
//You need to call this on each parent key that you used if you
//intend to destroy the HashTable or simply no longer need that key.
method remove takes integer index returns nothing
local Table t = Table(this)[index]
if t != 0 then
call t.destroy()
call Table(this).remove(index)
endif
endmethod
method has takes integer index returns boolean
return Table(this).has(index)
endmethod
method destroy takes nothing returns nothing
call Table(this).destroy()
endmethod
static method create takes nothing returns thistype
return Table.create()
endmethod
endstruct
endlibrary
library SpellEvent /* v2.0.3.0 https://www.hiveworkshop.com/threads/301895/
*/uses /*
*/Table /* https://www.hiveworkshop.com/threads/188084/
*/optional RegisterPlayerUnitEvent /* https://www.hiveworkshop.com/threads/250266/
*/optional ResourcePreloader /* https://www.hiveworkshop.com/threads/287358/
*/optional ErrorMessage /* https://github.com/nestharus/JASS/blob/master/jass/Systems/ErrorMessage
*///! novjass
/*
A library that eases and expands the possibilities of custom spells development.
Core Features:
1. Two-phase spell event handlers
2. Manual spell event invocation
3. Event parameters overriding and event cancelling
4. Spell development template
1.) Spell event handlers are grouped into two: generic handlers that runs for every spell, and ability-specific
handlers. All generic handlers run first. Within them, you can do things such as changing the event parameters
(caster, target, ...) as well as preventing the ability-specific handlers from running. The second phase are
the specific handlers which are for the spell developers to define the mechanics of the spell.
Note: Generic handlers only run for spells that have existing ability-specific handlers. This is because
generic handlers are intended as custom-spell modifier, not an ability handler. If you want to catch an event
that runs for just any ability (including normal OE abilities), you can easily use (in fact, you should) the
blizzard native events instead.
2.) You can invoke a spell event to run and define the parameters manually. This removes the need for dummy
casters in most cases.
3.) As mentioned in no. 1, within the generic event handlers, you can override the event parameters. The change
will only affect the ability-specific handlers. You can also stop the ability-specific handlers from running.
(known as event cancelling)
Note: Event cancelling is currently incompatible with Reforged. (crashes the game)
4.) This library provides a framework for the flow of spell through the use of modules. This removes from the
spell developers the additional work of manual spell event registration, spell instance allocation, and other
minute tasks such as storing and looping through each active spell instance.
*/
|=========|
| Credits |
|=========|
/*
- AGD (Author)
- Bribe , Nestharus (SpellEffectEvent Concept)
- Anitarf (Original SpellEvent Idea)
*/
|=========|
| Structs |
|=========|
/*
*/struct Spell extends array/*
*/static constant thistype GENERIC /* You can also use this like 'Spell.GENERIC.registerEventHandlers()'
Event Responses:
*/readonly static integer abilityId /*
*/readonly static integer eventType /*
*/readonly static integer orderType /*
*/readonly static integer level /*
*/readonly static player triggerPlayer /*
*/readonly static unit triggerUnit /*
*/readonly static unit targetUnit /* Fixed a mistake in the native event responses where target is set to caster for 'No-target' abilities based on channel (Is set to null instead)
*/readonly static item targetItem /*
*/readonly static destructable targetDest /*
*/readonly static real targetX /* Returns the x-coordinate of the caster if the spell is a 'No-target' ability
*/readonly static real targetY /* Returns the y-coordinate of the caster if the spell is a 'No-target' ability
Fields:
*/readonly integer abilId/*
- Rawcode of the activation ability for the spell
*/boolean handlersDisabled/*
- (incompatible with Reforged versions - crashes the game)
- Value is always reset to false before running the generic spell handlers
- Set to <false> to increment the internal disabler counter or <true> to decrement counter
- If counter > 0, the ability-specific handlers won't run
Methods:
*/static method operator [] takes integer abilId returns Spell/*
- Returns a Spell instance based on the given activation-ability rawcode which can be used for event handler registrations
*/method setEventFlag takes integer eventType, boolean flag returns nothing/*
*/method getEventFlag takes integer eventType returns boolean/*
- Disables/Enables certain event types from running for a Spell (these flags are <true> by default)
*/method invokeNoTargetEvent takes integer eventType, integer level, unit caster returns nothing/*
*/method invokePointTargetEvent takes integer eventType, integer level, unit caster, real targetX, real targetY returns nothing/*
*/method invokeSingleTargetEvent takes integer eventType, integer level, unit caster, widget target returns nothing/*
- Manually invokes a spell event
*/static method overrideNoTargetParams takes integer level, unit caster returns nothing/*
*/static method overridePointTargetParams takes integer level, unit caster, real targetX, real targetY returns nothing/*
*/static method overrideSingleTargetParams takes integer level, unit caster, widget target returns nothing/*
- Overrides the values of the event response variables (only effective when called inside a generic event handler)
- The values are only overriden in the ability-specific spell event handlers
*/method registerEventHandler takes integer eventType, code handler returns nothing/*
*/method unregisterEventHandler takes integer eventType, code handler returns nothing/*
*/method clearEventHandlers takes integer eventType returns nothing/*
*/method clearHandlers takes nothing returns nothing/*
- Manages ability-specific spell event handlers
*/static method registerGenericEventHandler takes integer eventType, code handler returns nothing/*
*/static method unregisterGenericEventHandler takes integer eventType, code handler returns nothing/*
*/static method clearGenericEventHandlers takes integer eventType returns nothing/*
*/static method clearGenericHandlers takes nothing returns nothing/*
- Manages generic spell event handlers
*/
|===========|
| Variables |
|===========|
/*
Spell Event Types
*/constant integer EVENT_SPELL_CAST/*
*/constant integer EVENT_SPELL_CHANNEL/*
*/constant integer EVENT_SPELL_EFFECT/*
*/constant integer EVENT_SPELL_ENDCAST/*
*/constant integer EVENT_SPELL_FINISH/*
Spell Order Types
*/constant integer SPELL_ORDER_TYPE_SINGLE_TARGET/*
*/constant integer SPELL_ORDER_TYPE_POINT_TARGET/*
*/constant integer SPELL_ORDER_TYPE_NO_TARGET/*
*/
|===========|
| Functions |
|===========|
/*
Equivalent functions for the methods above
(Event Responses)
*/constant function GetEventSpellAbilityId takes nothing returns integer/*
*/constant function GetEventSpellEventType takes nothing returns integer/*
*/constant function GetEventSpellOrderType takes nothing returns integer/*
*/constant function GetEventSpellLevel takes nothing returns integer/*
*/constant function GetEventSpellPlayer takes nothing returns player/*
*/constant function GetEventSpellCaster takes nothing returns unit/*
*/constant function GetEventSpellTargetUnit takes nothing returns unit/*
*/constant function GetEventSpellTargetItem takes nothing returns item/*
*/constant function GetEventSpellTargetDest takes nothing returns destructable/*
*/constant function GetEventSpellTargetX takes nothing returns real/*
*/constant function GetEventSpellTargetY takes nothing returns real/*
*/function SetSpellEventFlag takes integer abilId, integer eventType, boolean flag returns nothing/*
*/function GetSpellEventFlag takes integer abilId, integer eventType returns boolean/*
*/function SpellCancelEventHandlers takes boolean cancel returns nothing/*
- This function is incompatible with Reforged versions
*/function SpellInvokeNoTargetEvent takes integer abilId, integer eventType, integer level, unit caster returns nothing/*
*/function SpellInvokePointTargetEvent takes integer abilId, integer eventType, integer level, unit caster, real targetX, real targetY returns nothing/*
*/function SpellInvokeSingleTargetEvent takes integer abilId, integer eventType, integer level, unit caster, widget target returns nothing/*
*/function SpellOverrideNoTargetParams takes integer level, unit caster returns nothing/*
*/function SpellOverridePointTargetParams takes integer level, unit caster, real targetX, real targetY returns nothing/*
*/function SpellOverrideSingleTargetParams takes integer level, unit caster, widget target returns nothing/*
*/function SpellRegisterEventHandler takes integer abilId, integer eventType, code handler returns nothing/*
*/function SpellUnregisterEventHandler takes integer abilId, integer eventType, code handler returns nothing/*
*/function SpellClearEventHandlers takes integer abilId, integer eventType returns nothing/*
*/function SpellClearHandlers takes integer abilId returns nothing/*
*/function SpellRegisterGenericEventHandler takes integer eventType, code handler returns nothing/*
*/function SpellUnregisterGenericEventHandler takes integer eventType, code handler returns nothing/*
*/function SpellClearGenericEventHandlers takes integer eventType returns nothing/*
*/function SpellClearGenericHandlers takes nothing returns nothing/*
*/
|=========|
| Modules |
|=========|
/*
Automates spell event handler registration at map initialization
Modules <SpellEvent> and <SpellEventEx> cannot both be implemented in the same struct
*/module SpellEvent/*
> Uses a single timer (per struct) for all active spell instances. Standard module designed for
periodic spells with high-frequency timeout (< 0.5 seconds)
Fields:
*/readonly thistype prev/*
*/readonly thistype next/*
- Spell instances links
- Readonly attribute is only effective outside the implementing struct, though users are
also not supposed to change these values from inside the struct. But if you insist in
using this, only do so for manually inserting nodes. Only do it if really needed
Public methods:
*/static method registerSpellEvent takes integer abilId, integer eventType returns nothing/*
- Manually registers an ability rawcode to trigger spell events
- Can be used for spells that involve more than one activation ability IDs
Member interfaces:
- Should be declared above the module implementation
*/interface static integer SPELL_ABILITY_ID /* Ability rawcode
*/interface static integer SPELL_EVENT_TYPE /* Spell event type
*/interface static real SPELL_PERIOD /* Spell periodic actions execution period
*/interface method onSpellStart takes nothing returns thistype/*
- Runs right after the spell event fires
- Returning zero or a negative value will not run the periodic operations for that instance
- You can return a different value or transmute 'this', provided that all your nodes/values
comes from the same node/value stack. Also remember to always deallocate what you manually
allocated.
- The value returned will be added to the list of instances that will run onSpellPeriodic()
*/optional interface method onSpellPeriodic takes nothing returns boolean/*
- Runs periodically after the spell event fires until it returns false
*/optional interface method onSpellEnd takes nothing returns nothing/*
- Runs after method onSpellPeriodic() returns false
- If onSpellPeriodic() is not present, this will be called after onSpellStart() returns a valid instance
*/module SpellEventEx/*
> Uses 1 timer for each active spell instance. A module specifically designed for
periodic spells with low-frequency timeout (> 0.5 seconds) as it does not affect
the accuracy of the first 'tick' of the periodic operations. Here, you always
need to manually allocate/deallocate you spell instances
Public methods:
*/static method registerSpellEvent takes integer abilId, integer eventType returns nothing/*
- Manually registers a spell rawcode to trigger spell events
- Can be used for spells that involves more than one abilityId
Member interfaces:
- Should be declared above the module implementation
*/interface static integer SPELL_ABILITY_ID /* Ability rawcode
*/interface static integer SPELL_EVENT_TYPE /* Spell event type
*/interface static real SPELL_PERIOD /* Spell periodic actions execution period
*/interface static method onSpellStart takes nothing returns thistype/*
- Runs right after the spell event fires
- User should manually allocate the spell instance and use it as a return value of this method
- Returning zero or a negative value will not run the periodic operations for that instance
*/optional interface method onSpellPeriodic takes nothing returns boolean/*
- Runs periodically after the spell event fires until it returns false
*/optional interface method onSpellEnd takes nothing returns nothing/*
- Runs after method onSpellPeriodic() returns false
- If onSpellPeriodic() is not present, this will be called after onSpellStart() returns a valid instance
- User must manually deallocate the spell instance inside this method
*/module SpellEventGeneric/*
Member interfaces:
- Should be declared above the module implementation
*/optional interface static method onSpellEvent takes nothing returns nothing/*
- Runs on any generic spell event
*/optional interface static method onSpellCast takes nothing returns nothing/*
*/optional interface static method onSpellChannel takes nothing returns nothing/*
*/optional interface static method onSpellEffect takes nothing returns nothing/*
*/optional interface static method onSpellEndcast takes nothing returns nothing/*
*/optional interface static method onSpellFinish takes nothing returns nothing/*
- Runs on certain generic spell events
*///! endnovjass
/*=================================== SYSTEM CODE ===================================*/
globals
constant integer EVENT_SPELL_CAST = 0x1
constant integer EVENT_SPELL_CHANNEL = 0x2
constant integer EVENT_SPELL_EFFECT = 0x4
constant integer EVENT_SPELL_ENDCAST = 0x8
constant integer EVENT_SPELL_FINISH = 0x10
constant integer SPELL_ORDER_TYPE_SINGLE_TARGET = 0x12
constant integer SPELL_ORDER_TYPE_POINT_TARGET = 0x123
constant integer SPELL_ORDER_TYPE_NO_TARGET = 0x1234
endglobals
globals
private integer eventAbilityId = 0
private integer eventEventType = 0
private integer eventOrderType = 0
private integer eventLevel = 0
private player eventTriggerPlayer = null
private unit eventTriggerUnit = null
private unit eventTargetUnit = null
private item eventTargetItem = null
private destructable eventTargetDest = null
private real eventTargetX = 0.00
private real eventTargetY = 0.00
private integer tempOrderType = 0
private integer tempLevel = 0
private player tempTriggerPlayer = null
private unit tempTriggerUnit = null
private widget tempTarget = null
private real tempTargetX = 0.00
private real tempTargetY = 0.00
private boolexpr bridgeExpr
private TableArray table
private integer array eventTypeId
private integer array eventIndex
endglobals
private keyword Init
static if DEBUG_MODE then
private function IsValidEventType takes integer eventType returns boolean
return eventType > 0 and eventType <= (EVENT_SPELL_CAST + EVENT_SPELL_CHANNEL + EVENT_SPELL_EFFECT + EVENT_SPELL_ENDCAST + EVENT_SPELL_FINISH)
endfunction
private function IsEventSingleFlag takes integer eventType returns boolean
return eventType == EVENT_SPELL_CAST or/*
*/ eventType == EVENT_SPELL_CHANNEL or/*
*/ eventType == EVENT_SPELL_EFFECT or/*
*/ eventType == EVENT_SPELL_ENDCAST or/*
*/ eventType == EVENT_SPELL_FINISH
endfunction
private function AssertError takes boolean condition, string methodName, string structName, integer instance, string message returns nothing
static if LIBRARY_ErrorMessage then
call ThrowError(condition, SCOPE_PREFIX, methodName, structName, instance, message)
endif
endfunction
endif
/*===================================================================================*/
private function OnOverrideParams takes nothing returns nothing
set eventOrderType = tempOrderType
set eventLevel = tempLevel
set eventTriggerPlayer = GetOwningPlayer(tempTriggerUnit)
set eventTriggerUnit = tempTriggerUnit
set eventTargetX = tempTargetX
set eventTargetY = tempTargetY
if tempTarget == null then
set eventTargetUnit = null
set eventTargetItem = null
set eventTargetDest = null
else
set table[0].widget[0] = tempTarget
set eventTargetUnit = table[0].unit[0]
set eventTargetItem = table[0].item[0]
set eventTargetDest = table[0].destructable[0]
call table[0].handle.remove(0)
endif
set tempOrderType = 0
set tempLevel = 0
set tempTriggerUnit = null
set tempTargetX = 0.00
set tempTargetY = 0.00
set tempTarget = null
endfunction
/*===================================================================================*/
/*
* One Allocator for the whole library. It would be unlikely for this system to
* reach JASS_MAX_ARRAY_SIZE instances of allocated nodes at a single time
*
* Need to use custom Alloc because of the updated value for JASS_MAX_ARRAY_SIZE
* Credits to MyPad for the allocation algorithm
*/
private struct Node extends array
private static thistype array stack
static method allocate takes nothing returns thistype
local thistype node = stack[0]
if stack[node] == 0 then
debug call AssertError(node == (JASS_MAX_ARRAY_SIZE - 1), "allocate()", "thistype", node, "Overflow")
set node = node + 1
set stack[0] = node
else
set stack[0] = stack[node]
set stack[node] = 0
endif
return node
endmethod
method deallocate takes nothing returns nothing
debug call AssertError(this == 0, "deallocate()", "thistype", 0, "Null node")
debug call AssertError(stack[this] > 0, "deallocate()", "thistype", this, "Double-free")
set stack[this] = stack[0]
set stack[0] = this
endmethod
endstruct
private module List
readonly thistype prev
readonly thistype next
method makeHead takes nothing returns nothing
set this.prev = this
set this.next = this
endmethod
method insert takes thistype node returns nothing
local thistype next = this.next
set node.prev = this
set node.next = next
set next.prev = node
set this.next = node
endmethod
method delete takes nothing returns nothing
set this.next.prev = this.prev
set this.prev.next = this.next
set this.handle = null
call Node(this).deallocate()
endmethod
endmodule
private struct ConditionList extends array
triggercondition handle
implement List
endstruct
private struct ExprList extends array
boolexpr handle
implement List
method operator conditionList takes nothing returns ConditionList
return this
endmethod
method operator empty takes nothing returns boolean
return this.next == this
endmethod
method insertExpr takes boolexpr expr returns thistype
local thistype node = Node.allocate()
set node.handle = expr
call this.insert(node)
return node
endmethod
endstruct
private struct Handler extends array
readonly trigger trigger
boolean overrideParams
integer disablerCounter
private integer index
private static ExprList array genericList
private method operator exprList takes nothing returns ExprList
return this
endmethod
/*
* You might think that the process of registering handlers are expensive in performance
* due to constant rebuilding of triggerconditions each time, but setting up proper spell
* handlers are seldom done (often only once per spell) and a large part of them are done
* at map initialization
*/
method updateHandlers takes nothing returns nothing
local ExprList exprNode = genericList[this.index].next
local ConditionList conditionNode
call TriggerClearConditions(this.trigger)
if exprNode != genericList[this.index].prev then
loop
exitwhen exprNode == genericList[this.index]
set conditionNode = exprNode.conditionList.next
loop
exitwhen conditionNode == exprNode.conditionList
call TriggerAddCondition(this.trigger, exprNode.handle)
set conditionNode = conditionNode.next
endloop
set exprNode = exprNode.next
endloop
endif
set exprNode = this.exprList.next
loop
exitwhen exprNode == this.exprList
set conditionNode = exprNode.conditionList.next
loop
exitwhen conditionNode == exprNode.conditionList
set conditionNode.handle = TriggerAddCondition(this.trigger, exprNode.handle)
set conditionNode = conditionNode.next
endloop
set exprNode = exprNode.next
endloop
endmethod
/*
* This method is registered in position after all the generic handlers and before the
* ability-specific handlers. Its position allows it to unlink all the ability-specific
* handlers positioned after it or to change the event parameters, should the user goto
*/
private static method bridge takes nothing returns nothing
local integer triggerId = GetHandleId(GetTriggeringTrigger())
local thistype node = table[0][triggerId]
local trigger tempTrig
if node.disablerCounter > 0 then
if node.exprList.next != node.exprList then
set tempTrig = node.trigger
set node.trigger = CreateTrigger()
set table[0][GetHandleId(node.trigger)] = node
call node.updateHandlers()
call table[0].remove(triggerId)
call TriggerClearConditions(tempTrig)
call DestroyTrigger(tempTrig)
set tempTrig = null
endif
return
endif
if node.overrideParams and node.exprList.next != node.exprList then
call OnOverrideParams()
endif
endmethod
static method registerGeneric takes integer eventIndex, boolexpr expr returns nothing
local integer exprId = GetHandleId(expr)
local ExprList exprNode = table[genericList[eventIndex]][exprId]
local ConditionList conditionNode = Node.allocate()
if exprNode == 0 then
set exprNode = genericList[eventIndex].prev.prev.insertExpr(expr)
call exprNode.conditionList.makeHead()
set table[genericList[eventIndex]][exprId] = exprNode
endif
call exprNode.conditionList.prev.insert(conditionNode)
endmethod
static method unregisterGeneric takes integer eventIndex, integer exprId returns nothing
local ExprList exprNode = table[genericList[eventIndex]][exprId]
local ConditionList conditionNode = exprNode.conditionList.next
loop
exitwhen conditionNode == exprNode.conditionList
call conditionNode.delete()
set conditionNode = conditionNode.next
endloop
call table[genericList[eventIndex]].remove(exprId)
call exprNode.delete()
endmethod
static method clearGeneric takes integer eventIndex returns nothing
local ExprList exprNode = genericList[eventIndex].next
loop
exitwhen exprNode == genericList[eventIndex].prev
call unregisterGeneric(eventIndex, GetHandleId(exprNode.handle))
set exprNode = exprNode.next
endloop
endmethod
method register takes boolexpr expr returns nothing
local integer exprId = GetHandleId(expr)
local ExprList exprNode = table[this][exprId]
local ConditionList conditionNode = Node.allocate()
if this.exprList.empty then
set this.trigger = CreateTrigger()
set table[0][GetHandleId(this.trigger)] = this
endif
if exprNode == 0 then
set exprNode = this.exprList.prev.insertExpr(expr)
call exprNode.conditionList.makeHead()
set table[this][exprId] = exprNode
call exprNode.conditionList.insert(conditionNode)
call this.updateHandlers()
else
call exprNode.conditionList.prev.insert(conditionNode)
if exprNode.next == this.exprList then
set conditionNode.handle = TriggerAddCondition(this.trigger, expr)
else
call this.updateHandlers()
endif
endif
endmethod
method unregister takes integer exprId returns nothing
local ExprList exprNode = table[this][exprId]
local ConditionList conditionNode = exprNode.conditionList.next
loop
exitwhen conditionNode == exprNode.conditionList
call TriggerRemoveCondition(this.trigger, conditionNode.handle)
call conditionNode.delete()
set conditionNode = conditionNode.next
endloop
call exprNode.delete()
call table[this].remove(exprId)
if this.exprList.empty then
call table[0].remove(GetHandleId(this.trigger))
call DestroyTrigger(this.trigger)
set this.trigger = null
endif
endmethod
method clear takes nothing returns nothing
local ExprList exprNode = this.exprList.next
loop
exitwhen exprNode == this.exprList
call this.unregister(GetHandleId(exprNode.handle))
set exprNode = exprNode.next
endloop
endmethod
static method create takes integer eventIndex returns thistype
local thistype node = Node.allocate()
call ExprList(node).makeHead()
set node.index = eventIndex
set node.disablerCounter = 0
return node
endmethod
method destroy takes nothing returns nothing
call this.clear()
call Node(this).deallocate()
endmethod
debug static method hasGenericExpr takes integer eventIndex, boolexpr expr returns boolean
debug return table[genericList[eventIndex]][GetHandleId(expr)] != 0
debug endmethod
debug method hasExpr takes boolexpr expr returns boolean
debug return table[this][GetHandleId(expr)] != 0
debug endmethod
method operator enabled= takes boolean flag returns nothing
if flag then
call EnableTrigger(this.trigger)
else
call DisableTrigger(this.trigger)
endif
endmethod
method operator enabled takes nothing returns boolean
return IsTriggerEnabled(this.trigger)
endmethod
private static method initGenericList takes integer eventIndex returns nothing
local ExprList list = create(eventIndex)
local ExprList exprNode = list.insertExpr(bridgeExpr)
call exprNode.conditionList.makeHead()
call exprNode.conditionList.insert(Node.allocate())
set genericList[eventIndex] = list
endmethod
static method init takes nothing returns nothing
/*
* This bridge boolexpr executes in after all the generic spell handlers
* before transitioning into the ability-specific spell handlers.
* This boolexpr is responsible for disabling the ability-specific handlers
* (if requested) as well as implementing the change/overriding of the
* event parameters
*/
local code bridgeFunc = function thistype.bridge
set bridgeExpr = Filter(bridgeFunc)
call initGenericList(eventIndex[EVENT_SPELL_CAST])
call initGenericList(eventIndex[EVENT_SPELL_CHANNEL])
call initGenericList(eventIndex[EVENT_SPELL_EFFECT])
call initGenericList(eventIndex[EVENT_SPELL_ENDCAST])
call initGenericList(eventIndex[EVENT_SPELL_FINISH])
endmethod
endstruct
/*===================================================================================*/
struct Spell extends array
readonly integer abilId
private static integer spellCount = 0
private static Node spellKey
private static Handler array eventHandler
static if not LIBRARY_ResourcePreloader then
private static unit preloadDummy
endif
static constant method operator abilityId takes nothing returns integer
return eventAbilityId
endmethod
static constant method operator eventType takes nothing returns integer
return eventEventType
endmethod
static constant method operator orderType takes nothing returns integer
return eventOrderType
endmethod
static constant method operator level takes nothing returns integer
return eventLevel
endmethod
static constant method operator triggerPlayer takes nothing returns player
return eventTriggerPlayer
endmethod
static constant method operator triggerUnit takes nothing returns unit
return eventTriggerUnit
endmethod
static constant method operator targetUnit takes nothing returns unit
return eventTargetUnit
endmethod
static constant method operator targetItem takes nothing returns item
return eventTargetItem
endmethod
static constant method operator targetDest takes nothing returns destructable
return eventTargetDest
endmethod
static constant method operator target takes nothing returns widget
if eventTargetUnit != null then
return eventTargetUnit
elseif eventTargetItem != null then
return eventTargetItem
elseif eventTargetDest != null then
return eventTargetDest
endif
return null
endmethod
static constant method operator targetX takes nothing returns real
return eventTargetX
endmethod
static constant method operator targetY takes nothing returns real
return eventTargetY
endmethod
static method operator [] takes integer abilId returns thistype
local thistype this = table[spellKey][abilId]
local integer offset
if this == 0 then
debug call AssertError(spellCount > R2I(JASS_MAX_ARRAY_SIZE/5), "Spell[]", "thistype", 0, "Overflow")
static if LIBRARY_ResourcePreloader then
call PreloadAbility(abilId)
else
if UnitAddAbility(preloadDummy, abilId) then
call UnitRemoveAbility(preloadDummy, abilId)
endif
endif
set spellCount = spellCount + 1
set thistype(spellCount).abilId = abilId
set table[spellKey][abilId] = spellCount
set offset = (spellCount - 1)*5
set eventHandler[offset + eventIndex[EVENT_SPELL_CAST]] = Handler.create(eventIndex[EVENT_SPELL_CAST])
set eventHandler[offset + eventIndex[EVENT_SPELL_CHANNEL]] = Handler.create(eventIndex[EVENT_SPELL_CHANNEL])
set eventHandler[offset + eventIndex[EVENT_SPELL_EFFECT]] = Handler.create(eventIndex[EVENT_SPELL_EFFECT])
set eventHandler[offset + eventIndex[EVENT_SPELL_ENDCAST]] = Handler.create(eventIndex[EVENT_SPELL_ENDCAST])
set eventHandler[offset + eventIndex[EVENT_SPELL_FINISH]] = Handler.create(eventIndex[EVENT_SPELL_FINISH])
return spellCount
endif
return this
endmethod
static method operator GENERIC takes nothing returns thistype
return thistype[0]
endmethod
static method registerGenericEventHandler takes integer eventType, code handler returns nothing
local boolexpr expr = Filter(handler)
local integer eventId = 0x10
local integer node
debug call AssertError(not IsValidEventType(eventType), "registerGenericEventHandler()", "thistype", 0, "Invalid Spell Event Type (" + I2S(eventType) + ")")
loop
exitwhen eventId == 0
if eventType >= eventId then
set eventType = eventType - eventId
call Handler.registerGeneric(eventIndex[eventId], expr)
set node = spellCount
loop
exitwhen node == 0
set node = node - 1
call eventHandler[node*5 + eventIndex[eventId]].updateHandlers()
endloop
endif
set eventId = eventId/2
endloop
set expr = null
endmethod
static method unregisterGenericEventHandler takes integer eventType, code handler returns nothing
local boolexpr expr = Filter(handler)
local integer eventId = 0x10
local integer node
debug call AssertError(not IsValidEventType(eventType), "unregisterGenericEventHandler()", "thistype", 0, "Invalid Spell Event Type (" + I2S(eventType) + ")")
loop
exitwhen eventId == 0
if eventType >= eventId then
set eventType = eventType - eventId
debug call AssertError(not Handler.hasGenericExpr(eventIndex[eventId], expr), "unregisterGenericEventHandler()", "thistype", 0, "EventType(" + I2S(eventType) + "): Code is not registered")
call Handler.unregisterGeneric(eventIndex[eventId], GetHandleId(expr))
set node = spellCount
loop
exitwhen node == 0
set node = node - 1
call eventHandler[node*5 + eventIndex[eventId]].updateHandlers()
endloop
endif
set eventId = eventId/2
endloop
set expr = null
endmethod
static method clearGenericEventHandlers takes integer eventType returns nothing
local integer eventId = 0x10
local integer node
debug call AssertError(not IsValidEventType(eventType), "clearGenericEventHandlers()", "thistype", 0, "Invalid Spell Event Type (" + I2S(eventType) + ")")
loop
exitwhen eventId == 0
if eventType >= eventId then
set eventType = eventType - eventId
call Handler.clearGeneric(eventIndex[eventId])
set node = spellCount
loop
exitwhen node == 0
set node = node - 1
call eventHandler[node*5 + eventIndex[eventId]].updateHandlers()
endloop
endif
set eventId = eventId/2
endloop
endmethod
static method clearGenericHandlers takes nothing returns nothing
call clearGenericEventHandlers(EVENT_SPELL_CAST + EVENT_SPELL_CHANNEL + EVENT_SPELL_EFFECT + EVENT_SPELL_ENDCAST + EVENT_SPELL_FINISH)
endmethod
method registerEventHandler takes integer eventType, code handler returns nothing
local boolexpr expr = Filter(handler)
local integer offset = (this - 1)*5
local integer eventId = 0x10
debug call AssertError((this) < 1 or (this) > spellCount, "registerEventHandler()", "thistype", this, "Invalid Spell instance")
debug call AssertError(not IsValidEventType(eventType), "registerEventHandler()", "thistype", this, "Invalid Spell Event Type (" + I2S(eventType) + ")")
if this == GENERIC then
call registerGenericEventHandler(eventType, handler)
else
loop
exitwhen eventId == 0
if eventType >= eventId then
set eventType = eventType - eventId
call eventHandler[offset + eventIndex[eventId]].register(expr)
endif
set eventId = eventId/2
endloop
endif
set expr = null
endmethod
method unregisterEventHandler takes integer eventType, code handler returns nothing
local boolexpr expr = Filter(handler)
local integer offset = (this - 1)*5
local integer eventId = 0x10
debug call AssertError((this) < 1 or (this) > spellCount, "unregisterEventHandler()", "thistype", this, "Invalid Spell instance")
debug call AssertError(not IsValidEventType(eventType), "unregisterEventHandler()", "thistype", this, "Invalid Spell Event Type (" + I2S(eventType) + ")")
if this == GENERIC then
call unregisterGenericEventHandler(eventType, handler)
else
loop
exitwhen eventId == 0
if eventType >= eventId then
set eventType = eventType - eventId
debug call AssertError(not eventHandler[offset + eventIndex[eventId]].hasExpr(expr), "registerEventHandler()", "thistype", this, "EventType(" + I2S(eventType) + "): Code is already unregistered")
call eventHandler[offset + eventIndex[eventId]].unregister(GetHandleId(expr))
endif
set eventId = eventId/2
endloop
endif
set expr = null
endmethod
method clearEventHandlers takes integer eventType returns nothing
local integer offset = (this - 1)*5
local integer eventId = 0x10
debug call AssertError((this) < 1 or (this) > spellCount, "SpellEvent", "clearEventHandlers()", this, "Invalid Spell instance")
debug call AssertError(not IsValidEventType(eventType), "SpellEvent", "clearEventHandlers()", this, "Invalid Spell Event Type (" + I2S(eventType) + ")")
if this == GENERIC then
call clearGenericEventHandlers(eventType)
else
loop
exitwhen eventId == 0
if eventType >= eventId then
set eventType = eventType - eventId
call eventHandler[offset + eventIndex[eventId]].clear()
endif
set eventId = eventId/2
endloop
endif
endmethod
method clearHandlers takes nothing returns nothing
debug call AssertError((this) < 1 or (this) > spellCount, "clearHandlers()", "thistype", this, "Invalid Spell instance")
if this == GENERIC then
call this.clearGenericHandlers()
else
call this.clearEventHandlers(EVENT_SPELL_CAST + EVENT_SPELL_CHANNEL + EVENT_SPELL_EFFECT + EVENT_SPELL_ENDCAST + EVENT_SPELL_FINISH)
endif
endmethod
method setEventFlag takes integer eventType, boolean flag returns nothing
debug call AssertError(not IsEventSingleFlag(eventType), "setEventFlag()", "thistype", this, "Spell Event Type does not contain a single flag (" + I2S(eventType) + ")")
set eventHandler[(this - 1)*5 + eventIndex[eventType]].enabled = flag
endmethod
method getEventFlag takes integer eventType returns boolean
debug call AssertError(not IsEventSingleFlag(eventType), "getEventFlag()", "thistype", this, "Spell Event Type does not contain a single flag (" + I2S(eventType) + ")")
return eventHandler[(this - 1)*5 + eventIndex[eventType]].enabled
endmethod
method operator handlersDisabled= takes boolean disabled returns nothing
local Handler handler
if eventAbilityId == this.abilId then
set handler = eventHandler[(this - 1)*5 + eventIndex[eventEventType]]
if disabled then
set handler.disablerCounter = handler.disablerCounter + 1
else
set handler.disablerCounter = handler.disablerCounter - 1
endif
endif
endmethod
method operator handlersDisabled takes nothing returns boolean
if eventAbilityId != this.abilId then
return false
endif
return eventHandler[(this - 1)*5 + eventIndex[eventEventType]].disablerCounter > 0
endmethod
private static method overrideParams takes integer orderType, integer level, unit triggerUnit, widget target, real targetX, real targetY returns nothing
if eventAbilityId != 0 then
set Handler(table[0][GetHandleId(GetTriggeringTrigger())]).overrideParams = true
set tempOrderType = orderType
set tempLevel = level
set tempTriggerPlayer = GetOwningPlayer(triggerUnit)
set tempTriggerUnit = triggerUnit
set tempTargetX = targetX
set tempTargetY = targetY
set tempTarget = target
endif
endmethod
static method overrideNoTargetParams takes integer level, unit triggerUnit returns nothing
call overrideParams(SPELL_ORDER_TYPE_NO_TARGET, level, triggerUnit, null, GetUnitX(triggerUnit), GetUnitY(triggerUnit))
endmethod
static method overridePointTargetParams takes integer level, unit triggerUnit, real targetX, real targetY returns nothing
call overrideParams(SPELL_ORDER_TYPE_POINT_TARGET, level, triggerUnit, null, targetX, targetY)
endmethod
static method overrideSingleTargetParams takes integer level, unit triggerUnit, widget target returns nothing
call overrideParams(SPELL_ORDER_TYPE_SINGLE_TARGET, level, triggerUnit, target, GetWidgetX(target), GetWidgetY(target))
endmethod
private static method executeEventHandler takes Handler eventHandler, integer currentId, boolean manualInvoke, integer eventFlag, integer orderType, integer level, unit triggerUnit, widget target, real targetX, real targetY returns nothing
local integer disablerCounter = eventHandler.disablerCounter
local boolean overrideParams = eventHandler.overrideParams
local integer prevAbilityId = eventAbilityId
local integer prevEventType = eventEventType
local integer prevOrderType = eventOrderType
local integer prevLevel = eventLevel
local player prevTriggerPlayer = eventTriggerPlayer
local unit prevTriggerUnit = eventTriggerUnit
local real prevTargetX = eventTargetX
local real prevTargetY = eventTargetY
local unit prevTargetUnit = eventTargetUnit
local item prevTargetItem = eventTargetItem
local destructable prevTargetDest = eventTargetDest
local location tempLoc
set eventAbilityId = currentId
if manualInvoke then
set eventEventType = eventFlag
set eventOrderType = orderType
set eventLevel = level
set eventTriggerPlayer = GetOwningPlayer(triggerUnit)
set eventTriggerUnit = triggerUnit
set eventTargetX = targetX
set eventTargetY = targetY
set table[0].widget[0] = target
set eventTargetUnit = table[0].unit[0]
set eventTargetItem = table[0].item[0]
set eventTargetDest = table[0].destructable[0]
else
set eventEventType = eventTypeId[GetHandleId(GetTriggerEventId())]
set eventTriggerPlayer = GetTriggerPlayer()
set eventTriggerUnit = GetTriggerUnit()
set eventLevel = GetUnitAbilityLevel(eventTriggerUnit, eventAbilityId)
set eventTargetUnit = GetSpellTargetUnit()
set eventTargetItem = GetSpellTargetItem()
set eventTargetDest = GetSpellTargetDestructable()
if eventTargetUnit != null then
if eventTargetUnit == eventTriggerUnit and /*
*/ not (GetSpellTargetX() != 0) and /*
*/ not (GetSpellTargetY() != 0) then
set eventTargetX = GetUnitX(eventTriggerUnit)
set eventTargetY = GetUnitY(eventTriggerUnit)
set eventTargetUnit = null
set eventOrderType = SPELL_ORDER_TYPE_NO_TARGET
else
set eventTargetX = GetUnitX(eventTargetUnit)
set eventTargetY = GetUnitY(eventTargetUnit)
set eventOrderType = SPELL_ORDER_TYPE_SINGLE_TARGET
endif
elseif eventTargetItem != null then
set eventTargetX = GetItemX(eventTargetItem)
set eventTargetY = GetItemY(eventTargetItem)
set eventOrderType = SPELL_ORDER_TYPE_SINGLE_TARGET
elseif eventTargetDest != null then
set eventTargetX = GetWidgetX(eventTargetDest)
set eventTargetY = GetWidgetY(eventTargetDest)
set eventOrderType = SPELL_ORDER_TYPE_SINGLE_TARGET
else
set tempLoc = GetSpellTargetLoc()
if tempLoc == null then
set eventTargetX = GetUnitX(eventTriggerUnit)
set eventTargetY = GetUnitY(eventTriggerUnit)
set eventOrderType = SPELL_ORDER_TYPE_NO_TARGET
else
call RemoveLocation(tempLoc)
set tempLoc = null
set eventTargetX = GetSpellTargetX()
set eventTargetY = GetSpellTargetY()
set eventOrderType = SPELL_ORDER_TYPE_POINT_TARGET
endif
endif
endif
set eventHandler.disablerCounter = 0
set eventHandler.overrideParams = false
call TriggerEvaluate(eventHandler.trigger)
set eventHandler.disablerCounter = disablerCounter
set eventHandler.overrideParams = overrideParams
set eventAbilityId = prevAbilityId
set eventEventType = prevEventType
set eventOrderType = prevOrderType
set eventLevel = prevLevel
set eventTriggerPlayer = prevTriggerPlayer
set eventTriggerUnit = prevTriggerUnit
set eventTargetX = prevTargetX
set eventTargetY = prevTargetY
set eventTargetUnit = prevTargetUnit
set eventTargetItem = prevTargetItem
set eventTargetDest = prevTargetDest
set prevTriggerPlayer = null
set prevTriggerUnit = null
set prevTargetUnit = null
set prevTargetItem = null
set prevTargetDest = null
endmethod
private method invokeEvent takes integer eventType, integer orderType, integer level, unit triggerUnit, widget target, real targetX, real targetY returns nothing
local Handler handler = eventHandler[(this - 1)*5 + eventIndex[eventType]]
if handler != 0 and handler.enabled then
call executeEventHandler(handler, this.abilId, true, eventType, orderType, level, triggerUnit, target, targetX, targetY)
endif
endmethod
method invokeNoTargetEvent takes integer eventType, integer level, unit triggerUnit returns nothing
debug call AssertError(not IsEventSingleFlag(eventType), "executeNoTargetEvent()", "thistype", this, "Spell Event Type does not contain a single flag (" + I2S(eventType) + ")")
call this.invokeEvent(eventType, SPELL_ORDER_TYPE_NO_TARGET, level, triggerUnit, null, GetUnitX(triggerUnit), GetUnitY(triggerUnit))
endmethod
method invokePointTargetEvent takes integer eventType, integer level, unit triggerUnit, real targetX, real targetY returns nothing
debug call AssertError(not IsEventSingleFlag(eventType), "executePointTargetEvent()", "thistype", this, "Spell Event Type does not contain a single flag (" + I2S(eventType) + ")")
call this.invokeEvent(eventType, SPELL_ORDER_TYPE_POINT_TARGET, level, triggerUnit, null, targetX, targetY)
endmethod
method invokeSingleTargetEvent takes integer eventType, integer level, unit triggerUnit, widget target returns nothing
debug call AssertError(not IsEventSingleFlag(eventType), "executeSingleTargetEvent()", "thistype", this, "Spell Event Type does not contain a single flag (" + I2S(eventType) + ")")
call this.invokeEvent(eventType, SPELL_ORDER_TYPE_SINGLE_TARGET, level, triggerUnit, target, GetWidgetX(target), GetWidgetY(target))
endmethod
private static method onSpellEvent takes integer eventIndex returns nothing
local integer id = GetSpellAbilityId()
local Handler handler = eventHandler[(table[spellKey][id] - 1)*5 + eventIndex]
if handler != 0 and handler.enabled then
call executeEventHandler(handler, id, false, 0, 0, 0, null, null, 0.00, 0.00)
endif
endmethod
private static method onSpellCast takes nothing returns nothing
call onSpellEvent(eventIndex[EVENT_SPELL_CAST])
endmethod
private static method onSpellChannel takes nothing returns nothing
call onSpellEvent(eventIndex[EVENT_SPELL_CHANNEL])
endmethod
private static method onSpellEffect takes nothing returns nothing
call onSpellEvent(eventIndex[EVENT_SPELL_EFFECT])
endmethod
private static method onSpellEndcast takes nothing returns nothing
call onSpellEvent(eventIndex[EVENT_SPELL_ENDCAST])
endmethod
private static method onSpellFinish takes nothing returns nothing
call onSpellEvent(eventIndex[EVENT_SPELL_FINISH])
endmethod
private static method registerEvent takes playerunitevent whichEvent, code handler returns nothing
static if LIBRARY_RegisterPlayerUnitEvent then
call RegisterAnyPlayerUnitEvent(whichEvent, handler)
else
local trigger core = CreateTrigger()
local integer index = 0
loop
call TriggerRegisterPlayerUnitEvent(core, Player(index), whichEvent, null)
set index = index + 1
exitwhen index == bj_MAX_PLAYER_SLOTS
endloop
call TriggerAddCondition(core, Filter(handler) )
endif
endmethod
static if not LIBRARY_ResourcePreloader then
private static method initPreloadDummy takes nothing returns nothing
local rect world = GetWorldBounds()
set preloadDummy = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE), 'hpea', 0.00, 0.00, 0.00)
call UnitAddAbility(preloadDummy, 'AInv')
call UnitAddAbility(preloadDummy, 'Avul')
call UnitRemoveAbility(preloadDummy, 'Amov')
call SetUnitY(preloadDummy, GetRectMaxY(world) + 1000.00)
call RemoveRect(world)
set world = null
endmethod
endif
private static method init takes nothing returns nothing
set spellKey = Node.allocate()
set spellCount = spellCount + 1
set table[spellKey][0] = spellCount
set eventIndex[EVENT_SPELL_CAST] = 1
set eventIndex[EVENT_SPELL_CHANNEL] = 2
set eventIndex[EVENT_SPELL_EFFECT] = 3
set eventIndex[EVENT_SPELL_ENDCAST] = 4
set eventIndex[EVENT_SPELL_FINISH] = 5
set eventTypeId[GetHandleId(EVENT_PLAYER_UNIT_SPELL_CAST)] = EVENT_SPELL_CAST
set eventTypeId[GetHandleId(EVENT_PLAYER_UNIT_SPELL_CHANNEL)] = EVENT_SPELL_CHANNEL
set eventTypeId[GetHandleId(EVENT_PLAYER_UNIT_SPELL_EFFECT)] = EVENT_SPELL_EFFECT
set eventTypeId[GetHandleId(EVENT_PLAYER_UNIT_SPELL_ENDCAST)] = EVENT_SPELL_ENDCAST
set eventTypeId[GetHandleId(EVENT_PLAYER_UNIT_SPELL_FINISH)] = EVENT_SPELL_FINISH
call registerEvent(EVENT_PLAYER_UNIT_SPELL_CAST, function thistype.onSpellCast)
call registerEvent(EVENT_PLAYER_UNIT_SPELL_CHANNEL, function thistype.onSpellChannel)
call registerEvent(EVENT_PLAYER_UNIT_SPELL_EFFECT, function thistype.onSpellEffect)
call registerEvent(EVENT_PLAYER_UNIT_SPELL_ENDCAST, function thistype.onSpellEndcast)
call registerEvent(EVENT_PLAYER_UNIT_SPELL_FINISH, function thistype.onSpellFinish)
static if not LIBRARY_ResourcePreloader then
call initPreloadDummy()
endif
endmethod
implement Init
endstruct
private module Init
private static method onInit takes nothing returns nothing
set table = TableArray[JASS_MAX_ARRAY_SIZE]
call init()
call Handler.init()
endmethod
endmodule
/*===================================================================================*/
constant function GetEventSpellAbilityId takes nothing returns integer
return Spell.abilityId
endfunction
constant function GetEventSpellEventType takes nothing returns integer
return Spell.eventType
endfunction
constant function GetEventSpellOrderType takes nothing returns integer
return Spell.orderType
endfunction
constant function GetEventSpellLevel takes nothing returns integer
return Spell.level
endfunction
constant function GetEventSpellPlayer takes nothing returns player
return Spell.triggerPlayer
endfunction
constant function GetEventSpellCaster takes nothing returns unit
return Spell.triggerUnit
endfunction
constant function GetEventSpellTargetUnit takes nothing returns unit
return Spell.targetUnit
endfunction
constant function GetEventSpellTargetItem takes nothing returns item
return Spell.targetItem
endfunction
constant function GetEventSpellTargetDest takes nothing returns destructable
return Spell.targetDest
endfunction
constant function GetEventSpellTarget takes nothing returns widget
return Spell.target
endfunction
constant function GetEventSpellTargetX takes nothing returns real
return Spell.targetX
endfunction
constant function GetEventSpellTargetY takes nothing returns real
return Spell.targetY
endfunction
function SetSpellEventFlag takes integer abilId, integer eventType, boolean flag returns nothing
debug call AssertError(not IsEventSingleFlag(eventType), "SetSpellEventFlag()", "", 0, "Spell(" + I2S(abilId) + "): Invalid Spell Event Type (" + I2S(eventType) + ")")
call Spell[abilId].setEventFlag(eventType, flag)
endfunction
function GetSpellEventFlag takes integer abilId, integer eventType returns boolean
debug call AssertError(not IsEventSingleFlag(eventType), "GetSpellEventFlag()", "", 0, "Spell(" + I2S(abilId) + "): Invalid Spell Event Type (" + I2S(eventType) + ")")
return Spell[abilId].getEventFlag(eventType)
endfunction
function SpellCancelEventHandlers takes boolean cancel returns nothing
set Spell[GetEventSpellAbilityId()].handlersDisabled = cancel
endfunction
function SpellInvokeNoTargetEvent takes integer abilId, integer eventType, integer level, unit caster returns nothing
call Spell[abilId].invokeNoTargetEvent(eventType, level, caster)
endfunction
function SpellInvokePointTargetEvent takes integer abilId, integer eventType, integer level, unit caster, real targetX, real targetY returns nothing
call Spell[abilId].invokePointTargetEvent(eventType, level, caster, targetX, targetY)
endfunction
function SpellInvokeSingleTargetEvent takes integer abilId, integer eventType, integer level, unit caster, widget target returns nothing
call Spell[abilId].invokeSingleTargetEvent(eventType, level, caster, target)
endfunction
function SpellOverrideNoTargetParams takes integer level, unit caster returns nothing
call Spell.overrideNoTargetParams(level, caster)
endfunction
function SpellOverridePointTargetParams takes integer level, unit caster, real targetX, real targetY returns nothing
call Spell.overridePointTargetParams(level, caster, targetX, targetY)
endfunction
function SpellOverrideSingleTargetParams takes integer level, unit caster, widget target returns nothing
call Spell.overrideSingleTargetParams(level, caster, target)
endfunction
function SpellRegisterEventHandler takes integer abilId, integer eventType, code handler returns nothing
debug call AssertError(not IsValidEventType(eventType), "SpellRegisterEventHandler()", "", 0, "Spell(" + I2S(abilId) + "): Invalid Spell Event Type (" + I2S(eventType) + ")")
call Spell[abilId].registerEventHandler(eventType, handler)
endfunction
function SpellUnregisterEventHandler takes integer abilId, integer eventType, code handler returns nothing
debug call AssertError(not IsValidEventType(eventType), "SpellUnregisterEventHandler()", "", 0, "Spell(" + I2S(abilId) + "): Invalid Spell Event Type (" + I2S(eventType) + ")")
call Spell[abilId].unregisterEventHandler(eventType, handler)
endfunction
function SpellClearEventHandlers takes integer abilId, integer eventType returns nothing
debug call AssertError(not IsValidEventType(eventType), "SpellClearEventHandler()", "", 0, "Spell(" + I2S(abilId) + "): Invalid Spell Event Type (" + I2S(eventType) + ")")
call Spell[abilId].clearEventHandlers(eventType)
endfunction
function SpellClearHandlers takes integer abilId returns nothing
call Spell[abilId].clearHandlers()
endfunction
function SpellRegisterGenericEventHandler takes integer eventType, code handler returns nothing
debug call AssertError(not IsValidEventType(eventType), "SpellRegisterGenericEventHandler()", "", 0, "Invalid Spell Event Type (" + I2S(eventType) + ")")
call Spell.registerGenericEventHandler(eventType, handler)
endfunction
function SpellUnregisterGenericEventHandler takes integer eventType, code handler returns nothing
debug call AssertError(not IsValidEventType(eventType), "SpellUnregisterGenericEventHandler()", "", 0, "Invalid Spell Event Type (" + I2S(eventType) + ")")
call Spell.unregisterGenericEventHandler(eventType, handler)
endfunction
function SpellClearGenericEventHandlers takes integer eventType returns nothing
debug call AssertError(not IsValidEventType(eventType), "SpellClearGenericEventHandlers()", "", 0, "Invalid Spell Event Type (" + I2S(eventType) + ")")
call Spell.clearGenericEventHandlers(eventType)
endfunction
function SpellClearGenericHandlers takes nothing returns nothing
call Spell.clearGenericHandlers()
endfunction
/*===================================================================================*/
private function DestroyTimerEx takes timer whichTimer returns nothing
call PauseTimer(whichTimer)
call DestroyTimer(whichTimer)
endfunction
private function OnSpellEventEx takes integer node, real period, code callback returns nothing
local timer periodicTimer
if node > 0 then
set periodicTimer = CreateTimer()
set table[0][GetHandleId(periodicTimer)] = node
call TimerStart(periodicTimer, period, true, callback)
set periodicTimer = null
endif
endfunction
private function RegisterSpell takes integer abilId, integer eventType, code onSpellEvent returns nothing
if abilId != 0 then
call SpellRegisterEventHandler(abilId, eventType, onSpellEvent)
endif
endfunction
module SpellEvent
static if thistype.onSpellPeriodic.exists then
private static method enqueue takes thistype node returns nothing
local thistype last = thistype(0).prev
set thistype(0).prev = node
set last.next = node
set node.prev = last
set node.next = 0
endmethod
private static method onPeriodic takes nothing returns nothing
local thistype node = thistype(0).next
local thistype next
if node == 0 then
/*
* For some reason, someone tried to manually remove his node from the supposed
* readonly linked-list, without realizing that he's almost breaking the system
*/
call DestroyTimerEx(GetExpiredTimer())
return
endif
loop
exitwhen node == 0
set next = node.next
if not node.onSpellPeriodic() then
set node.next.prev = node.prev
set node.prev.next = node.next
static if thistype.onSpellEnd.exists then
call node.onSpellEnd()
endif
if node.internal then
set node.internal = false
call Node(node).deallocate()
endif
if thistype(0).next == 0 then
call DestroyTimerEx(GetExpiredTimer())
endif
endif
set node = next
endloop
endmethod
endif
static if not thistype.onSpellPeriodic.exists and not thistype.onSpellEnd.exists then
private static method onSpellEvent takes nothing returns nothing
call thistype(0).onSpellStart()
endmethod
else
readonly thistype prev
readonly thistype next
private boolean internal
private static method onSpellEvent takes nothing returns nothing
local thistype node = Node.allocate()
local boolean prevEmpty = thistype(0).next == 0
local thistype used = node.onSpellStart()
/*
* Add the new node into the list
*/
if used == node then
debug call AssertError(used.next.prev == used, "[internal] onSpellStart()", "thistype", used, "[Node Already Exists] : Make sure your nodes are all from the same stack..")
static if thistype.onSpellPeriodic.exists then
set used.internal = true
call enqueue(used)
elseif thistype.onSpellEnd.exists then
call used.onSpellEnd()
call used.deallocate()
endif
else
/*
* If the user returned a different node than the one he was given,
* deallocate the earlier node and replace it with the new node from the user
*/
call Node(node).deallocate()
if used > 0 then
debug call AssertError(used.next.prev == used, "[internal] onSpellStart()", "thistype", used, "[Node Already Exists] : Make sure your nodes are all from the same stack..")
static if thistype.onSpellPeriodic.exists then
call enqueue(used)
elseif thistype.onSpellEnd.exists then
call used.onSpellEnd()
endif
endif
endif
/*
* We need to use this kind of check in case the user returned 0
* but manually added some node in the list inside onSpellStart()
*/
static if thistype.onSpellPeriodic.exists then
if prevEmpty and thistype(0).next != 0 then
call TimerStart(CreateTimer(), SPELL_PERIOD, true, function thistype.onPeriodic)
endif
endif
endmethod
endif
private static method onInit takes nothing returns nothing
call RegisterSpell(SPELL_ABILITY_ID, SPELL_EVENT_TYPE, function thistype.onSpellEvent)
static if thistype.onSpellEventModuleInit.exists then
call onSpellEventModuleInit()
endif
endmethod
static method registerSpellEvent takes integer abilId, integer eventType returns nothing
call SpellRegisterEventHandler(abilId, eventType, function thistype.onSpellEvent)
endmethod
endmodule
module SpellEventEx
static if thistype.onSpellPeriodic.exists then
private static method onPeriodic takes nothing returns nothing
local timer expired = GetExpiredTimer()
local integer handleId = GetHandleId(expired)
local thistype node = table[0][handleId]
if not node.onSpellPeriodic() then
static if thistype.onSpellEnd.exists then
call node.onSpellEnd()
endif
call table[0].remove(handleId)
call DestroyTimerEx(expired)
endif
set expired = null
endmethod
endif
private static method onSpellEvent takes nothing returns nothing
static if thistype.onSpellPeriodic.exists then
call OnSpellEventEx(onSpellStart(), SPELL_PERIOD, function thistype.onPeriodic)
elseif thistype.onSpellEnd.exists then
local thistype node = onSpellStart()
if node > 0 then
call node.onSpellEnd()
endif
else
call onSpellStart()
endif
endmethod
private static method onInit takes nothing returns nothing
call RegisterSpell(SPELL_ABILITY_ID, SPELL_EVENT_TYPE, function thistype.onSpellEvent)
static if thistype.onSpellEventModuleInit.exists then
call onSpellEventModuleInit()
endif
endmethod
static method registerSpellEvent takes integer abilId, integer eventType returns nothing
call SpellRegisterEventHandler(abilId, eventType, function thistype.onSpellEvent)
endmethod
endmodule
module SpellEventGeneric
private static method onSpellResponse takes nothing returns nothing
static if thistype.onSpellEvent.exists then
call onSpellEvent()
endif
static if thistype.onSpellCast.exists then
if GetEventSpellEventType() == EVENT_SPELL_CAST then
call onSpellCast()
endif
endif
static if thistype.onSpellChannel.exists then
if GetEventSpellEventType() == EVENT_SPELL_CHANNEL then
call onSpellChannel()
endif
endif
static if thistype.onSpellEffect.exists then
if GetEventSpellEventType() == EVENT_SPELL_EFFECT then
call onSpellEffect()
endif
endif
static if thistype.onSpellEndcast.exists then
if GetEventSpellEventType() == EVENT_SPELL_ENDCAST then
call onSpellEndcast()
endif
endif
static if thistype.onSpellFinish.exists then
if GetEventSpellEventType() == EVENT_SPELL_FINISH then
call onSpellFinish()
endif
endif
endmethod
private static method onInit takes nothing returns nothing
call SpellRegisterGenericEventHandler(EVENT_SPELL_CAST + EVENT_SPELL_CHANNEL + EVENT_SPELL_EFFECT + EVENT_SPELL_ENDCAST + EVENT_SPELL_FINISH, function thistype.onSpellResponse)
static if thistype.onSpellEventGenericModuleInit.exists then
call onSpellEventGenericModuleInit()
endif
endmethod
endmodule
endlibrary
library SpellCloner /* v2.0.0 https://www.hiveworkshop.com/threads/324157/
*/uses /*
*/SpellEvent /* https://www.hiveworkshop.com/threads/301895/
*/Table /* https://www.hiveworkshop.com/threads/188084/
*///! novjass
/*
CREDITS:
- AGD (Author)
- JAKEZINC (Feedbacks and Suggestions, which helped bring the system into its current form)
*/
|=====|
| API |
|=====|
readonly static SpellCloner.configuredInstance/*
- Use this variable inside the configuration function to refer to the spell
instance being configured
*/module SpellClonerHeader/*
- Implement this module at the top of your spell struct
*/method initSpellConfiguration takes integer abilId returns integer/*
- Call this method at the top of you onSpellStart() method to initialize the
correct local configuration of your spell instance based on the activation
ability id
- Returns the struct type id of the struct containing the configuration
*/method loadSpellConfiguration takes integer configStructId returns nothing/*
- Call this method with the value returned by initSpellConfiguration() as the
parameter
- Like initSpellConfiguration(), loads the correct local configuration of the
spell, but based on the typeid of the configuration struct
*/module SpellClonerFooter/*
- Implement this module at the bottom of your spell struct, below your SpellEvent implementation
*/static method create takes integer configStructId, integer abilId, integer spellEventType, code configurationFunc returns thistype/*
- Creates a new local configuration instance for the spell (Return value is obsolete)
*/module SpellCloner/*
- A supplement to using both SpellClonerHeader and SpellClonerFooter. Implement this
module at the bottom of your spell struct, no need to implement the SpellEvent module
*/interface method onClonedSpellStart takes nothing returns thistype/*
*/interface method onClonedSpellPeriodic takes nothing returns boolean/*
*/interface method onClonedSpellEnd takes nothing returns nothing/*
- Supplement to the onSpellStart(), onSpellPeriodic(), and onSpellEnd() methods from
SpellEvent module
- All these interface methods follow the same rules as their SpellEvent interface methods
counterpart
- You no longer need to call this.initSpellConfiguration(ABILITY_ID) on onClonedSpellStart()
to run your configuration function, this is already done internally by the system
*///! endnovjass
private keyword SpellConfigList
globals
private trigger evaluator = CreateTrigger()
private integer array eventIndex
private SpellConfigList array configStructNode
private integer configuredSpellInstance = 0
private TableArray table
endglobals
private module Init
private static method onInit takes nothing returns nothing
set table = TableArray[JASS_MAX_ARRAY_SIZE]
set eventIndex[EVENT_SPELL_CAST] = 1
set eventIndex[EVENT_SPELL_CHANNEL] = 2
set eventIndex[EVENT_SPELL_EFFECT] = 3
set eventIndex[EVENT_SPELL_ENDCAST] = 4
set eventIndex[EVENT_SPELL_FINISH] = 5
endmethod
endmodule
struct SpellCloner extends array
static method operator configuredInstance takes nothing returns thistype
return configuredSpellInstance
endmethod
implement Init
endstruct
private struct SpellConfigList extends array
thistype current
readonly thistype prev
readonly thistype next
readonly integer structId
readonly boolexpr configExpr
private static thistype node = 0
method evaluateExpr takes integer spellInstance returns nothing
local integer prevInstance = configuredSpellInstance
set configuredSpellInstance = spellInstance
call TriggerAddCondition(evaluator, this.configExpr)
call TriggerEvaluate(evaluator)
call TriggerClearConditions(evaluator)
set configuredSpellInstance = prevInstance
endmethod
method insert takes integer id, boolexpr expr returns thistype
local thistype next = this.next
set node = node + 1
set node.structId = id
set node.configExpr = expr
set node.prev = this
set node.next = next
set next.prev = node
set this.next = node
return node
endmethod
static method create takes nothing returns thistype
set node = node + 1
set node.prev = node
set node.next = node
set node.current = node
return node
endmethod
endstruct
private function InitSpellConfiguration takes integer spellStructId, integer spellInstance, integer abilId returns integer
local SpellConfigList configList = table[spellStructId*5 + eventIndex[GetEventSpellEventType()]][abilId]
local integer configStructId
set configList.current = configList.current.next
set configStructId = configList.current.structId
call configList.current.evaluateExpr(spellInstance)
if configList.current.next == configList then
set configList.current = configList
endif
return configStructId
endfunction
private function CloneSpell takes integer spellStructId, integer configStructId, integer abilId, integer eventType, code configFunc returns nothing
local SpellConfigList configList
local integer eventId = 0x10
loop
exitwhen eventId == 0
if eventType >= eventId then
set eventType = eventType - eventId
set configList = table[spellStructId*5 + eventIndex[eventId]][abilId]
if configList == 0 then
set configList = SpellConfigList.create()
set table[spellStructId*5 + eventIndex[eventId]][abilId] = configList
endif
set configStructNode[configStructId] = configList.prev.insert(configStructId, Filter(configFunc))
endif
set eventId = eventId/2
endloop
endfunction
module SpellClonerHeader
static constant integer SPELL_ABILITY_ID = 0
static constant integer SPELL_EVENT_TYPE = 0
method initSpellConfiguration takes integer abilId returns integer
return InitSpellConfiguration(thistype.typeid, this, abilId)
endmethod
method loadSpellConfiguration takes integer configStructId returns nothing
call SpellConfigList(configStructNode[configStructId]).evaluateExpr(this)
endmethod
endmodule
module SpellClonerFooter
static method create takes integer configStructId, integer abilId, integer spellEventType, code configurationFunc returns thistype
call CloneSpell(thistype.typeid, configStructId, abilId, spellEventType, configurationFunc)
call registerSpellEvent(abilId, spellEventType)
return 0
endmethod
endmodule
module SpellCloner
static constant integer SPELL_ABILITY_ID = 0
static constant integer SPELL_EVENT_TYPE = 0
method onSpellStart takes nothing returns thistype
local integer configStructId = InitSpellConfiguration(thistype.typeid, this, GetEventSpellAbilityId())
local thistype node = this.onClonedSpellStart()
if node > 0 then
if node != this then
call SpellConfigList(configStructNode[configStructId]).evaluateExpr(node)
endif
return node
endif
return 0
endmethod
method onSpellPeriodic takes nothing returns boolean
return this.onClonedSpellPeriodic()
endmethod
method onSpellEnd takes nothing returns nothing
call this.onClonedSpellEnd()
endmethod
implement SpellEvent
implement SpellClonerFooter
endmodule
endlibrary
library LinkedList /* v1.0.0
*/uses /*
*/optional ErrorMessage /* https://github.com/nestharus/JASS/blob/master/jass/Systems/ErrorMessage
*///! novjass
/*
Author:
- AGD
Credits:
- Nestharus , Dirac , Bribe (for their scripts and discussions which I used as reference)
Pros:
- Modular
- Feature-Rich (Can Be)
- Safety-Oriented (On DEBUG_MODE)
- Extensible (Provides Interfaces)
- Flexible (Does not enforce a built-in allocator - enables user to use a custom Alloc or the default allocator)
*/
|-----|
| API |
|-----|
/*
Note: All the fields except from 'prev' and 'next' are actually operators, so you might want to
avoid using them from the interface methods that would be declared above them.
=====================================================================================================
List Fields Modules (for those who want to write or inline the core linked-list operations themselves)
*/module LinkedListFields/*
*/readonly thistype prev/*
*/readonly thistype next/*
*/module StaticListFields extends LinkedListFields/*
*/readonly static constant thistype head/*
*/readonly static thistype front/*
*/readonly static thistype back/*
*/readonly static boolean empty/*
*/module ListFields extends LinkedListFields/*
*/readonly thistype front/*
*/readonly thistype back/*
*/readonly boolean empty/*
=====================================================================================================
Lite List Modules (should be enough for most cases)
*/module LinkedListLite extends LinkedListFields/*
*/optional interface static method onInsert takes thistype node returns nothing/*
*/optional interface static method onRemove takes thistype node returns nothing/*
*/optional interface method onTraverse takes thistype node returns nothing/*
*/static method insert takes thistype prev, thistype node returns nothing/*
*/static method remove takes thistype node returns nothing/*
*/method traverseForwards takes nothing returns nothing/*
*/method traverseBackwards takes nothing returns nothing/*
- Only present if onTraverse() is also present
*/module StaticListLite extends StaticListFields, LinkedListLite/*
*/static method pushFront takes thistype node returns nothing/*
*/static method popFront takes nothing returns thistype/*
*/static method pushBack takes thistype node returns nothing/*
*/static method popBack takes nothing returns thistype/*
*/module ListLite extends ListFields, LinkedListLite/*
*/method pushFront takes thistype node returns nothing/*
*/method popFront takes nothing returns thistype/*
*/method pushBack takes thistype node returns nothing/*
*/method popBack takes nothing returns thistype/*
*/module InstantiatedListLite extends ListLite/*
*/interface static method allocate takes nothing returns thistype/*
*/interface method deallocate takes nothing returns nothing/*
*/optional interface method onConstruct takes nothing returns nothing/*
*/optional interface method onDestruct takes nothing returns nothing/*
*/static method create takes nothing returns thistype/*
*/method destroy takes nothing returns nothing/*
=====================================================================================================
Standard List Modules
*/module LinkedList extends LinkedListLite/*
*/static method isLinked takes thistype node returns boolean/*
*/module StaticList extends StaticListLite, LinkedList/*
*/static method clear takes nothing returns nothing/*
*/static method flush takes nothing returns nothing/*
*/module List extends ListLite, LinkedList/*
*/method clear takes nothing returns nothing/*
*/method flush takes nothing returns nothing/*
*/module InstantiatedList extends InstantiatedListLite, List/*
=====================================================================================================
Feature-Rich List Modules (for those who somehow need exotic linked-list operations)
*/module LinkedListEx extends LinkedList/*
*/static method swap takes thistype nodeA, thistype nodeB returns nothing/*
*/module StaticListEx extends StaticList, LinkedListEx/*
*/static method contains takes thistype node returns boolean/*
*/static method getSize takes nothing returns integer/*
*/static method rotateLeft takes nothing returns nothing/*
*/static method rotateRight takes nothing returns nothing/*
*/module ListEx extends List, LinkedListEx/*
*/method contains takes thistype node returns boolean/*
*/method getSize takes nothing returns integer/*
*/method rotateLeft takes nothing returns nothing/*
*/method rotateRight takes nothing returns nothing/*
*/module InstantiatedListEx extends InstantiatedList, ListEx/*
*///! endnovjass
/*========================================= CONFIGURATION ===========================================
* Only affects DEBUG_MODE
* If false, throws warnings instead (Errors pauses the game while warnings do not)
*/
globals
private constant boolean THROW_ERRORS = true
endglobals
/*====================================== END OF CONFIGURATION =====================================*/
static if DEBUG_MODE then
private function AssertError takes boolean condition, string methodName, string structName, integer node, string message returns nothing
static if LIBRARY_ErrorMessage then
static if THROW_ERRORS then
call ThrowError(condition, SCOPE_PREFIX, methodName, structName, node, message)
else
call ThrowWarning(condition, SCOPE_PREFIX, methodName, structName, node, message)
endif
endif
endfunction
endif
private module LinkedListUtils
method p_clear takes nothing returns nothing
set this.next.prev = 0
set this.prev.next = 0
set this.prev = this
set this.next = this
endmethod
method p_flush takes nothing returns nothing
local thistype node = this.prev
loop
exitwhen node == this
call remove(node)
set node = node.prev
endloop
endmethod
endmodule
private module LinkedListUtilsEx
implement LinkedListUtils
method p_contains takes thistype toFind returns boolean
local thistype node = this.next
loop
exitwhen node == this
if node == toFind then
return true
endif
set node = node.next
endloop
return false
endmethod
method p_getSize takes nothing returns integer
local integer count = 0
local thistype node = this.next
loop
exitwhen node == this
set count = count + 1
set node = node.next
endloop
return count
endmethod
endmodule
module LinkedListFields
readonly thistype prev
readonly thistype next
endmodule
module LinkedListLite
implement LinkedListFields
static method p_insert takes thistype this, thistype node returns nothing
local thistype next = this.next
set node.prev = this
set node.next = next
set next.prev = node
set this.next = node
endmethod
static method p_remove takes thistype node returns nothing
set node.next.prev = node.prev
set node.prev.next = node.next
endmethod
static method insert takes thistype this, thistype node returns nothing
debug call AssertError(node == 0, "insert()", "thistype", 0, "Cannot insert null node")
debug call AssertError(node.next.prev == node or node.prev.next == node, "insert()", "thistype", 0, "Already linked node [" + I2S(node) + "]")
call p_insert(this, node)
static if thistype.onInsert.exists then
call onInsert(node)
endif
endmethod
static method remove takes thistype node returns nothing
debug call AssertError(node == 0, "remove()", "thistype", 0, "Cannot remove null node")
debug call AssertError(node.next.prev != node and node.prev.next != node, "remove()", "thistype", 0, "Invalid node [" + I2S(node) + "]")
static if thistype.onRemove.exists then
call onRemove(node)
endif
call p_remove(node)
endmethod
static if thistype.onTraverse.exists then
method p_traverse takes boolean forward returns nothing
local thistype node
if forward then
set node = this.next
loop
exitwhen node == this or node.prev.next != node
call this.onTraverse(node)
set node = node.next
endloop
else
set node = this.prev
loop
exitwhen node == this or node.next.prev != node
call this.onTraverse(node)
set node = node.prev
endloop
endif
endmethod
method traverseForwards takes nothing returns nothing
call this.p_traverse(true)
endmethod
method traverseBackwards takes nothing returns nothing
call this.p_traverse(false)
endmethod
endif
endmodule
module LinkedList
implement LinkedListLite
static method isLinked takes thistype node returns boolean
return node.next.prev == node or node.prev.next == node
endmethod
endmodule
module LinkedListEx
implement LinkedList
static method swap takes thistype this, thistype node returns nothing
local thistype thisPrev = this.prev
local thistype thisNext = this.next
debug call AssertError(this == 0, "swap()", "thistype", 0, "Cannot swap null node")
debug call AssertError(node == 0, "swap()", "thistype", 0, "Cannot swap null node")
debug call AssertError(not isLinked(this), "swap()", "thistype", 0, "Cannot use unlinked node [" + I2S(this) + "]")
debug call AssertError(not isLinked(node), "swap()", "thistype", 0, "Cannot use unlinked node [" + I2S(node) + "]")
call p_remove(this)
call p_insert(node, this)
if thisNext != node then
call p_remove(node)
call p_insert(thisPrev, node)
endif
endmethod
endmodule
module StaticListFields
implement LinkedListFields
static constant method operator head takes nothing returns thistype
return 0
endmethod
static method operator back takes nothing returns thistype
return head.prev
endmethod
static method operator front takes nothing returns thistype
return head.next
endmethod
static method operator empty takes nothing returns boolean
return front == head
endmethod
endmodule
module StaticListLite
implement StaticListFields
implement LinkedListLite
static method pushFront takes thistype node returns nothing
debug call AssertError(node == 0, "pushFront()", "thistype", 0, "Cannot use null node")
debug call AssertError(node.next.prev == node, "pushFront()", "thistype", 0, "Already linked node [" + I2S(node) + "]")
call insert(head, node)
endmethod
static method popFront takes nothing returns thistype
local thistype node = front
debug call AssertError(node.prev != head, "popFront()", "thistype", 0, "Invalid list")
call remove(node)
return node
endmethod
static method pushBack takes thistype node returns nothing
debug call AssertError(node == 0, "pushBack()", "thistype", 0, "Cannot use null node")
debug call AssertError(node.next.prev == node, "pushBack()", "thistype", 0, "Already linked node [" + I2S(node) + "]")
call insert(back, node)
endmethod
static method popBack takes nothing returns thistype
local thistype node = back
debug call AssertError(node.next != head, "popBack()", "thistype", 0, "Invalid list")
call remove(node)
return node
endmethod
endmodule
module StaticList
implement StaticListLite
implement LinkedList
implement LinkedListUtils
static method clear takes nothing returns nothing
call head.p_clear()
endmethod
static method flush takes nothing returns nothing
call head.p_flush()
endmethod
endmodule
module StaticListEx
implement StaticList
implement LinkedListEx
implement LinkedListUtilsEx
static method contains takes thistype node returns boolean
return head.p_contains(node)
endmethod
static method getSize takes nothing returns integer
return head.p_getSize()
endmethod
static method rotateLeft takes nothing returns nothing
call pushBack(popFront())
endmethod
static method rotateRight takes nothing returns nothing
call pushFront(popBack())
endmethod
endmodule
module ListFields
implement LinkedListFields
method operator back takes nothing returns thistype
return this.prev
endmethod
method operator front takes nothing returns thistype
return this.next
endmethod
method operator empty takes nothing returns boolean
return this.next == this
endmethod
endmodule
module ListLite
implement ListFields
implement LinkedListLite
method pushFront takes thistype node returns nothing
debug call AssertError(this == 0, "pushFront()", "thistype", 0, "Null list")
debug call AssertError(this.next.prev != this, "pushFront()", "thistype", this, "Invalid list")
debug call AssertError(node == 0, "pushFront()", "thistype", this, "Cannot insert null node")
debug call AssertError(node.next.prev == node, "pushFront()", "thistype", this, "Already linked node [" + I2S(node) + "]")
call insert(this, node)
endmethod
method popFront takes nothing returns thistype
local thistype node = this.next
debug call AssertError(this == 0, "popFront()", "thistype", 0, "Null list")
debug call AssertError(node.prev != this, "popFront()", "thistype", this, "Invalid list")
call remove(node)
return node
endmethod
method pushBack takes thistype node returns nothing
debug call AssertError(this == 0, "pushBack()", "thistype", 0, "Null list")
debug call AssertError(this.next.prev != this, "pushBack()", "thistype", this, "Invalid list")
debug call AssertError(node == 0, "pushBack()", "thistype", this, "Cannot insert null node")
debug call AssertError(node.next.prev == node, "pushBack()", "thistype", this, "Already linked node [" + I2S(node) + "]")
call insert(this.prev, node)
endmethod
method popBack takes nothing returns thistype
local thistype node = this.prev
debug call AssertError(this == 0, "popBack()", "thistype", 0, "Null list")
debug call AssertError(node.next != this, "pushFront()", "thistype", this, "Invalid list")
call remove(node)
return node
endmethod
endmodule
module List
implement ListLite
implement LinkedList
implement LinkedListUtils
method clear takes nothing returns nothing
debug call AssertError(this == 0, "clear()", "thistype", 0, "Null list")
debug call AssertError(this.next.prev != this, "clear()", "thistype", this, "Invalid list")
call this.p_clear()
endmethod
method flush takes nothing returns nothing
debug call AssertError(this == 0, "flush()", "thistype", 0, "Null list")
debug call AssertError(this.next.prev != this, "flush()", "thistype", this, "Invalid list")
call this.p_flush()
endmethod
endmodule
module ListEx
implement List
implement LinkedListEx
implement LinkedListUtilsEx
method contains takes thistype node returns boolean
debug call AssertError(this == 0, "contains()", "thistype", 0, "Null list")
debug call AssertError(this.next.prev != this, "contains()", "thistype", this, "Invalid list")
return this.p_contains(node)
endmethod
method getSize takes nothing returns integer
debug call AssertError(this == 0, "getSize()", "thistype", 0, "Null list")
debug call AssertError(this.next.prev != this, "getSize()", "thistype", this, "Invalid list")
return this.p_getSize()
endmethod
method rotateLeft takes nothing returns nothing
debug call AssertError(this == 0, "rotateLeft()", "thistype", 0, "Null list")
debug call AssertError(this.next.prev != this, "rotateLeft()", "thistype", this, "Invalid list")
call this.pushBack(this.popFront())
endmethod
method rotateRight takes nothing returns nothing
debug call AssertError(this == 0, "rotateRight()", "thistype", 0, "Null list")
debug call AssertError(this.next.prev == this, "rotateRight()", "thistype", this, "Invalid list")
call this.pushFront(this.popBack())
endmethod
endmodule
module InstantiatedListLite
implement ListLite
debug private boolean valid
static method create takes nothing returns thistype
local thistype node = allocate()
set node.prev = node
set node.next = node
debug set node.valid = true
static if thistype.onConstruct.exists then
call node.onConstruct()
endif
return node
endmethod
method destroy takes nothing returns nothing
debug call AssertError(this == 0, "destroy()", "thistype", 0, "Null list")
debug call AssertError(this.next.prev != this, "destroy()", "thistype", this, "Invalid list")
debug call AssertError(not this.valid, "destroy()", "thistype", this, "Double-free")
debug set this.valid = false
static if thistype.flush.exists then
call this.flush()
endif
static if thistype.onDestruct.exists then
call this.onDestruct()
endif
debug set this.prev = 0
debug set this.next = 0
call this.deallocate()
endmethod
endmodule
module InstantiatedList
implement List
implement InstantiatedListLite
endmodule
module InstantiatedListEx
implement ListEx
implement InstantiatedList
endmodule
endlibrary
library UnitDex uses optional WorldBounds, optional GroupUtils
/***************************************************************
*
* v1.2.2.0, by TriggerHappy
* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* UnitDex assigns every unit an unique integer. It attempts to make that number within the
* maximum array limit so you can associate it with one.
* _________________________________________________________________________
* 1. Installation
* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* Copy the script to your map, save it, then restart the editor and comment out the line below.
*/
// external ObjectMerger w3a Adef uDex anam "Detect Leave" ansf "(UnitDex)" aart "" acat "" arac 0
/* _________________________________________________________________________
* 2. Configuration
* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
*/
private module UnitDexConfig
// The raw code of the leave detection ability.
static constant integer DETECT_LEAVE_ABILITY = 'uDex'
// Allow debug messages (debug mode must also be on)
static constant boolean ALLOW_DEBUGGING = true
// Uncomment the lines below to define a global filter for units being indexed
/*static method onFilter takes unit u returns boolean
return true
endmethod*/
endmodule
/* _________________________________________________________________________
* 3. Function API
* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* Every function inlines.
*
* function GetUnitId takes unit whichUnit returns integer
* function GetUnitById takes integer index returns unit
*
* function UnitDexEnable takes boolean flag returns nothing
* function UnitDexRemove takes unit u, boolean runEvents returns boolean
*
* function IsUnitIndexed takes unit u returns boolean
* function IsIndexingEnabled takes nothing returns boolean
*
* function GetIndexedUnit takes nothing returns unit
* function GetIndexedUnitId takes nothing returns integer
*
* function RegisterUnitIndexEvent takes boolexpr func, integer eventtype returns indexevent
* function RemoveUnitIndexEvent takes triggercondition c, integer eventtype returns nothing
* function TriggerRegisterUnitIndexEvent takes trigger t, integer eventtype returns nothing
*
* function OnUnitIndex takes code func returns triggercondition
* function OnUnitDeidex takes code func returns triggercondition
* _________________________________________________________________________
* 4. Struct API
* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* UnitDex.Enabled = false // toggle the indexer
* UnitDex.Initialized // returns true if the preload timer has finished
* UnitDex.Count // returns the amount of units indexed
* UnitDex.Unit[0] // access the UnitDex array directly
* UnitDex.Group // a unit group containing every indexed unit (for enumeration)
* UnitDex.LastIndex // returns the last indexed unit's id
* _________________________________________________________________________
* 5. Public Variables
* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* These are to be used with the "eventtype" argument of the event API:
*
* constant integer EVENT_UNIT_INDEX = 0
* constant integer EVENT_UNIT_DEINDEX = 1
* _________________________________________________________________________
* 6. Examples
* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* 1. Associate a unit with a variable
*
* set UnitFlag[GetUnitId(yourUnit)] = true
*
* 2. Allocate a struct instance using a units assigned ID
*
* local somestruct data = GetUnitId(yourUnit)
*
* 3. Detect when a unit leaves the map
*
* function Exit takes nothing returns nothing
* call BJDebugMsg("The unit " + GetUnitName(GetIndexedUnit()) + " has left the map.")
* endfunction
*
* call OnUnitDeindex(function Exit)
* // or
* call RegisterUnitIndexEvent(Filter(function Exit), EVENT_UNIT_DEINDEX)
*
*
* _________________________________________________________________________
* 7. How it works
* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* 1. Enumerate all preplaced units
* 2. TriggerRegisterEnterRegion native to detect when a unit enters the map
* 3. Assign each unit that enters the map a unique integer
* 4. Give every unit an ability based off of defend. There is no native to accurately
* detect when a unit leaves the map but when a unit dies or is removed from the game
* they are issued the "undefend" order
* 5. Catch the "undefend" order and remove unit from the queue
* _________________________________________________________________________
* 8. Notes
* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* - This system is compatable with GUI because it utilizes UnitUserData (custom values for units).
* - The object merger line should be commented out after saving and restarting.
*
* -http://www.hiveworkshop.com/forums/submissions-414/unitdex-lightweight-unit-indexer-248209/
*
* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
*/
globals
// Event types
constant integer EVENT_UNIT_INDEX = 0
constant integer EVENT_UNIT_DEINDEX = 1
// System variables
private trigger array IndexTrig
private integer Index = 0
private real E=-1
private boolexpr FilterEnter
endglobals
function GetUnitId takes unit whichUnit returns integer
return GetUnitUserData(whichUnit)
endfunction
function GetUnitById takes integer index returns unit
return UnitDex.Unit[index]
endfunction
function GetIndexedUnit takes nothing returns unit
return UnitDex.Unit[UnitDex.LastIndex]
endfunction
function GetIndexedUnitId takes nothing returns integer
return UnitDex.LastIndex
endfunction
function IsUnitIndexed takes unit u returns boolean
return (GetUnitById(GetUnitId(u)) != null)
endfunction
function UnitDexEnable takes boolean flag returns nothing
set UnitDex.Enabled = flag
endfunction
function IsIndexingEnabled takes nothing returns boolean
return UnitDex.Enabled
endfunction
function RegisterUnitIndexEvent takes boolexpr func, integer eventtype returns triggercondition
return TriggerAddCondition(IndexTrig[eventtype], func)
endfunction
function RemoveUnitIndexEvent takes triggercondition c, integer eventtype returns nothing
call TriggerRemoveCondition(IndexTrig[eventtype], c)
endfunction
function TriggerRegisterUnitIndexEvent takes trigger t, integer eventtype returns nothing
call TriggerRegisterVariableEvent(t, SCOPE_PRIVATE + "E", EQUAL, eventtype)
endfunction
function OnUnitIndex takes code func returns triggercondition
return TriggerAddCondition(IndexTrig[EVENT_UNIT_INDEX], Filter(func))
endfunction
function OnUnitDeindex takes code func returns triggercondition
return TriggerAddCondition(IndexTrig[EVENT_UNIT_DEINDEX], Filter(func))
endfunction
/****************************************************************/
private keyword UnitDexCore
struct UnitDex extends array
static boolean Enabled = true
readonly static integer LastIndex
readonly static boolean Initialized=false
readonly static group Group=CreateGroup()
readonly static unit array Unit
readonly static integer Count = 0
readonly static integer array List
implement UnitDexConfig
private static integer Counter = 0
implement UnitDexCore
endstruct
function UnitDexRemove takes unit u, boolean runEvents returns boolean
return UnitDex.Remove(u, runEvents)
endfunction
/****************************************************************/
private module UnitDexCore
static method Remove takes unit u, boolean runEvents returns boolean
local integer i
if (IsUnitIndexed(u)) then
set i = GetUnitId(u)
set UnitDex.List[i] = Index
set Index = i
call GroupRemoveUnit(UnitDex.Group, u)
call SetUnitUserData(u, 0)
if (runEvents) then
set UnitDex.LastIndex = i
set E = EVENT_UNIT_DEINDEX
call TriggerEvaluate(IndexTrig[EVENT_UNIT_DEINDEX])
set E = -1
endif
set UnitDex.Unit[i] = null
set UnitDex.Count = UnitDex.Count - 1
return true
endif
return false
endmethod
private static method onGameStart takes nothing returns nothing
local integer i = 1
loop
exitwhen i > Counter
set LastIndex = i
call TriggerEvaluate(IndexTrig[EVENT_UNIT_INDEX])
set E = EVENT_UNIT_INDEX
set E = -1
set i = i + 1
endloop
set LastIndex = Counter
set Initialized = true
set FilterEnter = null
call DestroyTimer(GetExpiredTimer())
endmethod
private static method onEnter takes nothing returns boolean
local unit u = GetFilterUnit()
local integer i = GetUnitId(u)
local integer t = Index
if (i == 0 and thistype.Enabled) then
// If a filter was defined pass the unit through it.
static if (thistype.onFilter.exists) then
if (not thistype.onFilter(u)) then
set u = null
return false // check failed
endif
endif
// Handle debugging
static if (thistype.DEBUG_MODE and thistype.ALLOW_DEBUGGING) then
if (t == 0 and Counter+1 >= JASS_MAX_ARRAY_SIZE) then
call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, "UnitDex: Maximum number of units reached!")
set u = null
return false
endif
endif
// Add to group of indexed units
call GroupAddUnit(thistype.Group, u)
// Give unit the leave detection ability
call UnitAddAbility(u, thistype.DETECT_LEAVE_ABILITY)
call UnitMakeAbilityPermanent(u, true, thistype.DETECT_LEAVE_ABILITY)
// Allocate index
if (Index != 0) then
set Index = List[t]
else
set Counter = Counter + 1
set t = Counter
endif
set List[t] = -1
set LastIndex = t
set thistype.Unit[t] = u
set Count = Count + 1
// Store the index
call SetUnitUserData(u, t)
if (thistype.Initialized) then
// Execute custom events registered with RegisterUnitIndexEvent
call TriggerEvaluate(IndexTrig[EVENT_UNIT_INDEX])
// Handle TriggerRegisterUnitIndexEvent
set E = EVENT_UNIT_INDEX
// Reset so the event can occur again
set E = -1
endif
endif
set u = null
return false
endmethod
private static method onLeave takes nothing returns boolean
local unit u
local integer i
// Check if order is undefend.
if (thistype.Enabled and GetIssuedOrderId() == 852056) then
set u = GetTriggerUnit()
// If unit was killed (not removed) then don't continue
if (GetUnitAbilityLevel(u, thistype.DETECT_LEAVE_ABILITY) != 0) then
set u = null
return false
endif
set i = GetUnitId(u)
// If unit has been indexed then deindex it
if (i > 0 and i <= Counter and u == GetUnitById(i)) then
// Recycle the index
set List[i] = Index
set Index = i
set LastIndex = i
// Remove to group of indexed units
call GroupRemoveUnit(thistype.Group, u)
// Execute custom events without any associated triggers
call TriggerEvaluate(IndexTrig[EVENT_UNIT_DEINDEX])
// Handle TriggerRegisterUnitIndexEvent
set E = EVENT_UNIT_DEINDEX
// Remove entry
call SetUnitUserData(u, 0)
set thistype.Unit[i] = null
// Decrement unit count
set Count = Count - 1
// Reset so the event can occur again
set E = -1
endif
set u = null
endif
return false
endmethod
private static method onInit takes nothing returns nothing
local trigger t = CreateTrigger()
local integer i = 0
local player p
local unit u
static if (not LIBRARY_WorldBounds) then // Check if WorldBounts exists, if not then define the necessary vars
local region reg = CreateRegion() // If WorldBounds wasn't found, create the region manually
local rect world = GetWorldBounds()
endif
static if (not LIBRARY_GroupUtils) then // Check if GroupUtils exists so we can use it's enumeration group.
local group ENUM_GROUP = CreateGroup() // If not, create the group.
endif
set FilterEnter = Filter(function thistype.onEnter)
// Begin to index units when they enter the map
static if (LIBRARY_WorldBounds) then
call TriggerRegisterEnterRegion(CreateTrigger(), WorldBounds.worldRegion, FilterEnter)
else
call RegionAddRect(reg, world)
call TriggerRegisterEnterRegion(CreateTrigger(), reg, FilterEnter)
call RemoveRect(world)
set world = null
endif
call TriggerAddCondition(t, Filter(function thistype.onLeave))
set IndexTrig[EVENT_UNIT_INDEX] = CreateTrigger()
set IndexTrig[EVENT_UNIT_DEINDEX] = CreateTrigger()
loop
set p = Player(i)
// Detect "undefend"
call TriggerRegisterPlayerUnitEvent(t, p, EVENT_PLAYER_UNIT_ISSUED_ORDER, null)
// Hide the detect ability from players
call SetPlayerAbilityAvailable(p, thistype.DETECT_LEAVE_ABILITY, false)
set i = i + 1
exitwhen i == bj_MAX_PLAYER_SLOTS
endloop
// Index preplaced units
set i = 0
loop
call GroupEnumUnitsOfPlayer(ENUM_GROUP, Player(i), FilterEnter)
set i = i + 1
exitwhen i == bj_MAX_PLAYER_SLOTS
endloop
static if (not LIBRARY_GroupUtils) then
call DestroyGroup(ENUM_GROUP)
set ENUM_GROUP = null
endif
set LastIndex = Counter
// run init triggers
call TimerStart(CreateTimer(), 0.00, false, function thistype.onGameStart)
endmethod
endmodule
endlibrary
library DummyRecycler /*
// DummyRecycler v1.25
// by Flux
//
// A system that recycles dummy units while considering their facing angle.
// It can be used as attachment dummies for visual effects or as dummy caster.
//
// Why is recycling a unit important?
// Because creating a unit is is one of the slowest function in the game
// and there are reports that will always leave a permanent tiny bit of
// memory (0.04 KB).
// On average, retrieving a pending Dummy is approximately 4x faster compared
// to creating a new one and recycling a Dummy compared to removing it is
// approximately 1.3x faster.
// Furthermore, if you're using a lot of "Unit has entered map" events,
// using this system will even result to even more better performance
// because retrieving Dummy units does not cause that event to run.
*/ requires /*
nothing
*/ optional Table/*
if not found, this system will use a hashtable. Hashtables are limited to
255 per map.
*/ optional WorldBounds /*
if not found, this system will initialize its own Map Boundaries.
//
//
// Features:
//
// -- Dummy Sharing
// When a Dummy List gets low on unit count, it will borrow Dummy Units
// from the Dummy List with the highest unit count. The transfer is not
// instant because the shared Dummy Unit has to turn to the appropriate
// angle of its new Dummy List before it can be recycled.
// See BORROW_REQUEST.
//
// -- Self-balancing recycling algorithm
// Recycled Dummy Units will be thrown to the List having the least number
// of Dummy Units.
//
// -- Recycling least used
// Allows recycling a Dummy from the Dummy List with the highest
// unit count. It is useful when the facing angle of the Dummy Unit
// does not matter.
// See GetRecycledDummyAnyAngle.
//
// -- Self-adaptation
// When there are no free Dummy Units from a Dummy List, it will end up creating
// a new unit instead but that unit will be permanently added as a Dummy
// Unit to be recycled increasing the overall total Dummy Unit count.
//
// -- Count control
// Allows limiting the overall number of Dummy Units.
// See MAX_DUMMY_COUNT.
//
// -- Delayed Recycle
// Allows recycling Dummy Units after some delay to allocate time for the
// death animation of Special Effects to be seen.
// See DummyAddRecycleTimer.
//
// ******************************************************************
// ***************************** API: *******************************
// ******************************************************************
//
// function GetRecycledDummy takes real x, real y, real z, real facing returns unit
// - Retrieve an unused Dummy Unit from the List.
// - The equivalent of CreateUnit.
// - To use as a Dummy Caster, follow it with PauseUnit(dummy, false).
//
// function GetRecycledDummyAnyAngle takes real x, real y, real z returns unit
// - Use this function if the facing angle of the Dummy doesn't matter to you.
// - It will return a unit from the list having the highest number of unused Dummy Units.
// - To use as a Dummy Caster, follow it with PauseUnit(dummy, false).
//
// function RecycleDummy takes unit u returns nothing
// - Recycle the Dummy unit for it to be used again later.
// - The equivalent of RemoveUnit.
//
// function DummyAddRecycleTimer takes unit u, real time returns nothing
// - Recycle the Dummy unit after a certain time.
// - Use this to allocate time for the the death animation of an effect attached to the
// Dummy Unit to finish..
// - The equivalent of UnitApplyTimedLife.
//
// function ShowDummy takes unit u, boolean flag returns nothing
// - Shows/hides Dummy Unit without conflicting with the Locust ability.
//
//--------------------
// CREDITS
//--------------------
// Bribe - for the MissileRecycler (vJASS) where I got this concept from
// http://www.hiveworkshop.com/forums/jass-resources-412/system-missilerecycler-206086/
// - for the optional Table
// http://www.hiveworkshop.com/forums/jass-resources-412/snippet-new-table-188084/
// Vexorian - for the Attachable and Pitch Animation Model (dummy.mdx)
// http://www.wc3c.net/showthread.php?t=101150
// Maker and IcemanBo - for the unit permanent 0.04 KB memory leak of units.
// http://www.hiveworkshop.com/forums/trigger-gui-editor-tutorials-279/memory-leaks-263410/
// Nestharus - for the data structure
// http://www.hiveworkshop.com/forums/2809461-post7.html
// - for the optional WorldBounds
// http://githubusercontent.com/nestharus/JASS/master/jass/Systems/WorldBounds/script.j
// =============================================================== //
// ====================== CONFIGURATION ========================== //
// =============================================================== */
globals
//The rawcode of the Dummy Unit
private constant integer DUMMY_ID = 'dumi'
//The owner of the Dummy Unit
private constant player OWNER = Player(bj_PLAYER_NEUTRAL_EXTRA)
//The number of indexed angle. The higher the value the:
// - Lesser the turning time for the Dummy Units.
// - Higher the total number of Dummy Units created at Map Initialization.
// Recommended Value: 10 (Max difference of 18 degrees)
private constant integer ANGLES_COUNT = 10
//The number of Dummy units per ANGLES_COUNT. The higher the value the:
// - Higher the number of units that can be recycled per angle, when
// no more units are in queue, the system will resort to use CreateUnit.
// - Higher the total number of Dummy Units created at Map Initialization.
// Recommended Value: 3 to 5 (for less overhead in Map Loading Screen)
private constant integer STORED_UNIT_COUNT = 3
//The maximum number of Dummy units that can exist. When the system resort
//to using CreateUnit, the unit will be permanently added to the Dummy
//List. To avoid spamming Dummy Units and having too much free Dummy
//Units to allocate, the maximum number of Dummy Units is capped.
// Recommended Value: 80 to 120
private constant integer MAX_DUMMY_COUNT = 100
//When a certain angle have less than BORROW_REQUEST units in its list,
//it will start to borrow Dummy Units from the list with the highest
//Dummy Unit count.
// Recommended Value: Half of maximum STORED_UNIT_COUNT
private constant integer BORROW_REQUEST = 5
//It will only return a Dummy if the current dummy is close
//to it's appropriate facing angle. This is to avoid returning
//a Dummy which is still turning to face it's list angle
private constant real ANGLE_TOLERANCE = 10.0
//An additional option to automatically hide recycled dummy units in the
//corner of the map camera bounds
private constant boolean HIDE_ON_MAP_CORNER = true
endglobals
//Every time a new dummy unit is retrieved, it will apply this resets
//If it is redundant/you dont need it, remove it.
//! textmacro DUMMY_UNIT_RESET
call SetUnitScale(bj_lastCreatedUnit, 1, 0, 0)
call SetUnitVertexColor(bj_lastCreatedUnit, 255, 255, 255, 255)
call SetUnitAnimationByIndex(bj_lastCreatedUnit, 90)
call ShowDummy(bj_lastCreatedUnit, true)
//! endtextmacro
// =============================================================== //
// ==================== END CONFIGURATION ======================== //
// =============================================================== //
globals
private integer dummyCount = ANGLES_COUNT*STORED_UNIT_COUNT
private real array angle
private integer array count
private integer array countHead
private integer array countNext
private integer array countPrev
private integer array next
private integer array prev
private unit array dummy
private integer upper
private integer lower
private integer lastInstance
private constant real FACING_OFFSET = 180.0/ANGLES_COUNT
endglobals
static if HIDE_ON_MAP_CORNER and not LIBRARY_WorldBounds then
private module BoundsInit
readonly static real x
readonly static real y
private static method onInit takes nothing returns nothing
local rect map = GetWorldBounds()
set thistype.x = GetRectMaxX(map)
set thistype.y = GetRectMaxY(map)
call RemoveRect(map)
set map = null
endmethod
endmodule
private struct Bounds extends array
implement BoundsInit
endstruct
endif
private module M
static if LIBRARY_Table then
static Table tb
else
static hashtable hash = InitHashtable()
endif
private static method onInit takes nothing returns nothing
local real add = 360.0/ANGLES_COUNT
local real a = 0
local integer this = ANGLES_COUNT
local integer head = 0
local integer cHead = JASS_MAX_ARRAY_SIZE - 1 //avoid allocation collision
local integer i = R2I(MAX_DUMMY_COUNT/ANGLES_COUNT + 0.5)
set upper = STORED_UNIT_COUNT
set lower = STORED_UNIT_COUNT
static if LIBRARY_Table then
set tb = Table.create()
endif
//Initialize countHeads
loop
exitwhen i < 0
set countNext[cHead] = cHead
set countPrev[cHead] = cHead
set countHead[i] = cHead
set cHead = cHead - 1
set i = i - 1
endloop
set cHead = countHead[STORED_UNIT_COUNT] //All heads will be inserted here initially
//Create the Dummy units
loop
exitwhen a >= 360
//Initialize head
set next[head] = head
set prev[head] = head
set count[head] = STORED_UNIT_COUNT
set angle[head] = a
//Insert head in the Count List
set countNext[head] = cHead
set countPrev[head] = countPrev[cHead]
set countNext[countPrev[head]] = head
set countPrev[countNext[head]] = head
set i = 0
loop
exitwhen i >= STORED_UNIT_COUNT
//Queued Linked List
set next[this] = head
set prev[this] = prev[head]
set next[prev[this]] = this
set prev[next[this]] = this
static if HIDE_ON_MAP_CORNER then
static if LIBRARY_WorldBounds then
set dummy[this] = CreateUnit(OWNER, DUMMY_ID, WorldBounds.maxX, WorldBounds.maxY, a)
else
set dummy[this] = CreateUnit(OWNER, DUMMY_ID, Bounds.x, Bounds.y, a)
endif
else
set dummy[this] = CreateUnit(OWNER, DUMMY_ID, 0, 0, a)
endif
call PauseUnit(dummy[this], true)
static if LIBRARY_Table then
set tb[GetHandleId(dummy[this])] = this
else
call SaveInteger(hash, GetHandleId(dummy[this]), 0, this)
endif
set this = this + 1
set i = i + 1
endloop
set head = head + 1
set a = a + add
endloop
set lastInstance = this
endmethod
endmodule
private struct S extends array
implement M
endstruct
private function GetHead takes integer facing returns integer
if facing < 0 or facing >= 360 then
set facing = facing - (facing/360)*360
if facing < 0 then
set facing = facing + 360
endif
endif
return R2I((facing*ANGLES_COUNT/360.0))
endfunction
function ShowDummy takes unit u, boolean flag returns nothing
if IsUnitHidden(u) == flag then
call ShowUnit(u, flag)
if flag and GetUnitTypeId(u) == DUMMY_ID then
call UnitRemoveAbility(u, 'Aloc')
call UnitAddAbility(u, 'Aloc')
endif
endif
endfunction
function GetRecycledDummy takes real x, real y, real z, real facing returns unit
local integer head = GetHead(R2I(facing + FACING_OFFSET))
local integer this = next[head]
local integer cHead
//If there are Dummy Units in the Queue List already facing close to the appropriate angle
if this != head and RAbsBJ(GetUnitFacing(dummy[this]) - angle[head]) <= ANGLE_TOLERANCE then
//Remove from the Queue List
set next[prev[this]] = next[this]
set prev[next[this]] = prev[this]
//For double free protection
set next[this] = -1
//Unit Properties
set bj_lastCreatedUnit = dummy[this]
call SetUnitX(bj_lastCreatedUnit, x)
call SetUnitY(bj_lastCreatedUnit, y)
call SetUnitFacing(bj_lastCreatedUnit, facing)
call SetUnitFlyHeight(bj_lastCreatedUnit, z, 0)
//! runtextmacro DUMMY_UNIT_RESET()
//Update Count and Bounds
set count[head] = count[head] - 1
//------------------------------------------------
// Unit Sharing
//------------------------------------------------
if count[head] < BORROW_REQUEST and count[countNext[countHead[upper]]] > count[head] then
set count[head] = count[head] + 1
set this = next[countNext[countHead[upper]]]
call SetUnitFacing(dummy[this], angle[head])
//Remove
set next[prev[this]] = next[this]
set prev[next[this]] = prev[this]
//Add to the Current List
set next[this] = head
set prev[this] = prev[head]
set next[prev[this]] = this
set prev[next[this]] = this
set head = countNext[countHead[upper]]
set count[head] = count[head] - 1
endif
//---------------------------
//Update Count Lists
//---------------------------
//Remove from the current Count List
set countNext[countPrev[head]] = countNext[head]
set countPrev[countNext[head]] = countPrev[head]
//Add to the new Count List
set cHead = countHead[count[head]]
set countNext[head] = cHead
set countPrev[head] = countPrev[cHead]
set countNext[countPrev[head]] = head
set countPrev[countNext[head]] = head
//---------------------------
// Update Bounds
//---------------------------
set cHead = countHead[upper]
if countNext[cHead] == cHead then
set upper = upper - 1
endif
if count[head] < lower then
set lower = count[head]
endif
else
set bj_lastCreatedUnit = CreateUnit(OWNER, DUMMY_ID, x, y, facing)
call PauseUnit(bj_lastCreatedUnit, true)
call SetUnitFlyHeight(bj_lastCreatedUnit, z, 0)
if dummyCount < MAX_DUMMY_COUNT then
set this = lastInstance
//For double free protection
set next[this] = -1
set dummy[this] = bj_lastCreatedUnit
static if LIBRARY_Table then
set S.tb[GetHandleId(bj_lastCreatedUnit)] = this
else
call SaveInteger(S.hash, GetHandleId(bj_lastCreatedUnit), 0, this)
endif
set lastInstance = lastInstance + 1
endif
set dummyCount = dummyCount + 1
endif
return bj_lastCreatedUnit
endfunction
function RecycleDummy takes unit u returns nothing
static if LIBRARY_Table then
local integer this = S.tb[GetHandleId(u)]
else
local integer this = LoadInteger(S.hash, GetHandleId(u), 0)
endif
local integer head
local integer cHead
//If the unit is a legit Dummy Unit
if this > 0 and next[this] == -1 then
//Find where to insert based on the list having the least number of units
set head = countNext[countHead[lower]]
set next[this] = head
set prev[this] = prev[head]
set next[prev[this]] = this
set prev[next[this]] = this
//Update Status
call SetUnitFacing(u, angle[head])
call PauseUnit(u, true)
call SetUnitOwner(u, OWNER, false)
static if HIDE_ON_MAP_CORNER then
static if LIBRARY_WorldBounds then
call SetUnitX(u, WorldBounds.maxX)
call SetUnitY(u, WorldBounds.maxY)
else
call SetUnitX(u, Bounds.x)
call SetUnitY(u, Bounds.y)
endif
else
call SetUnitScale(u, 0, 0, 0)
call SetUnitVertexColor(u, 0, 0, 0, 0)
endif
set count[head] = count[head] + 1
//---------------------------
// Update Count Lists
//---------------------------
//Remove
set countNext[countPrev[head]] = countNext[head]
set countPrev[countNext[head]] = countPrev[head]
//Add to the new Count List
set cHead = countHead[count[head]]
set countNext[head] = cHead
set countPrev[head] = countPrev[cHead]
set countNext[countPrev[head]] = head
set countPrev[countNext[head]] = head
//---------------------------
// Update Bounds
//---------------------------
set cHead = countHead[lower]
if countNext[cHead] == cHead then
set lower = lower + 1
endif
if count[head] > upper then
set upper = count[head]
endif
elseif this == 0 then
call RemoveUnit(u)
debug elseif next[this] != -1 then
debug call BJDebugMsg("|cffffcc00[DummyRecycler]:|r Attempted to recycle a pending/free Dummy Unit.")
endif
endfunction
private function Expires takes nothing returns nothing
local timer t = GetExpiredTimer()
local integer id = GetHandleId(t)
static if LIBRARY_Table then
call RecycleDummy(S.tb.unit[id])
call S.tb.unit.remove(id)
else
call RecycleDummy(LoadUnitHandle(S.hash, id, 0))
call FlushChildHashtable(S.hash, id)
endif
call DestroyTimer(t)
set t = null
endfunction
function DummyAddRecycleTimer takes unit u, real time returns nothing
local timer t = CreateTimer()
static if LIBRARY_Table then
set S.tb.unit[GetHandleId(t)] = u
else
call SaveUnitHandle(S.hash, GetHandleId(t), 0, u)
endif
call TimerStart(t, time, false, function Expires)
set t = null
endfunction
function GetRecycledDummyAnyAngle takes real x, real y, real z returns unit
return GetRecycledDummy(x, y, z, angle[countNext[countHead[upper]]])
endfunction
// runtextmacro DUMMY_DEBUG_TOOLS()
endlibrary
library TimerUtils initializer init
//*********************************************************************
//* TimerUtils (red+blue+orange flavors for 1.24b+) 2.0
//* ----------
//*
//* To implement it , create a custom text trigger called TimerUtils
//* and paste the contents of this script there.
//*
//* To copy from a map to another, copy the trigger holding this
//* library to your map.
//*
//* (requires vJass) More scripts: htt://www.wc3c.net
//*
//* For your timer needs:
//* * Attaching
//* * Recycling (with double-free protection)
//*
//* set t=NewTimer() : Get a timer (alternative to CreateTimer)
//* set t=NewTimerEx(x) : Get a timer (alternative to CreateTimer), call
//* Initialize timer data as x, instead of 0.
//*
//* ReleaseTimer(t) : Relese a timer (alt to DestroyTimer)
//* SetTimerData(t,2) : Attach value 2 to timer
//* GetTimerData(t) : Get the timer's value.
//* You can assume a timer's value is 0
//* after NewTimer.
//*
//* Multi-flavor:
//* Set USE_HASH_TABLE to true if you don't want to complicate your life.
//*
//* If you like speed and giberish try learning about the other flavors.
//*
//********************************************************************
//================================================================
globals
//How to tweak timer utils:
// USE_HASH_TABLE = true (new blue)
// * SAFEST
// * SLOWEST (though hash tables are kind of fast)
//
// USE_HASH_TABLE = false, USE_FLEXIBLE_OFFSET = true (orange)
// * kinda safe (except there is a limit in the number of timers)
// * ALMOST FAST
//
// USE_HASH_TABLE = false, USE_FLEXIBLE_OFFSET = false (red)
// * THE FASTEST (though is only faster than the previous method
// after using the optimizer on the map)
// * THE LEAST SAFE ( you may have to tweak OFSSET manually for it to
// work)
//
private constant boolean USE_HASH_TABLE = true
private constant boolean USE_FLEXIBLE_OFFSET = false
private constant integer OFFSET = 0x100000
private integer VOFFSET = OFFSET
//Timers to preload at map init:
private constant integer QUANTITY = 256
//Changing this to something big will allow you to keep recycling
// timers even when there are already AN INCREDIBLE AMOUNT of timers in
// the stack. But it will make things far slower so that's probably a bad idea...
private constant integer ARRAY_SIZE = 8190
endglobals
//==================================================================================================
globals
private integer array data[ARRAY_SIZE]
private hashtable ht
endglobals
//It is dependent on jasshelper's recent inlining optimization in order to perform correctly.
function SetTimerData takes timer t, integer value returns nothing
static if(USE_HASH_TABLE) then
// new blue
call SaveInteger(ht,0,GetHandleId(t), value)
elseif (USE_FLEXIBLE_OFFSET) then
// orange
static if (DEBUG_MODE) then
if(GetHandleId(t)-VOFFSET<0) then
call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
endif
endif
set data[GetHandleId(t)-VOFFSET]=value
else
// new red
static if (DEBUG_MODE) then
if(GetHandleId(t)-OFFSET<0) then
call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
endif
endif
set data[GetHandleId(t)-OFFSET]=value
endif
endfunction
function GetTimerData takes timer t returns integer
static if(USE_HASH_TABLE) then
// new blue
return LoadInteger(ht,0,GetHandleId(t) )
elseif (USE_FLEXIBLE_OFFSET) then
// orange
static if (DEBUG_MODE) then
if(GetHandleId(t)-VOFFSET<0) then
call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
endif
endif
return data[GetHandleId(t)-VOFFSET]
else
// new red
static if (DEBUG_MODE) then
if(GetHandleId(t)-OFFSET<0) then
call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
endif
endif
return data[GetHandleId(t)-OFFSET]
endif
endfunction
//==========================================================================================
globals
private timer array tT[ARRAY_SIZE]
private integer tN = 0
private constant integer HELD=0x28829022
//use a totally random number here, the more improbable someone uses it, the better.
private boolean didinit = false
endglobals
private keyword init
//==========================================================================================
// I needed to decide between duplicating code ignoring the "Once and only once" rule
// and using the ugly textmacros. I guess textmacros won.
//
//! textmacro TIMERUTIS_PRIVATE_NewTimerCommon takes VALUE
// On second thought, no.
//! endtextmacro
function NewTimerEx takes integer value returns timer
if (tN==0) then
if (not didinit) then
//This extra if shouldn't represent a major performance drawback
//because QUANTITY rule is not supposed to be broken every day.
call init.evaluate()
set tN = tN - 1
else
//If this happens then the QUANTITY rule has already been broken, try to fix the
// issue, else fail.
debug call BJDebugMsg("NewTimer: Warning, Exceeding TimerUtils_QUANTITY, make sure all timers are getting recycled correctly")
set tT[0]=CreateTimer()
static if( not USE_HASH_TABLE) then
debug call BJDebugMsg("In case of errors, please increase it accordingly, or set TimerUtils_USE_HASH_TABLE to true")
static if( USE_FLEXIBLE_OFFSET) then
if (GetHandleId(tT[0])-VOFFSET<0) or (GetHandleId(tT[0])-VOFFSET>=ARRAY_SIZE) then
//all right, couldn't fix it
call BJDebugMsg("NewTimer: Unable to allocate a timer, you should probably set TimerUtils_USE_HASH_TABLE to true or fix timer leaks.")
return null
endif
else
if (GetHandleId(tT[0])-OFFSET<0) or (GetHandleId(tT[0])-OFFSET>=ARRAY_SIZE) then
//all right, couldn't fix it
call BJDebugMsg("NewTimer: Unable to allocate a timer, you should probably set TimerUtils_USE_HASH_TABLE to true or fix timer leaks.")
return null
endif
endif
endif
endif
else
set tN=tN-1
endif
call SetTimerData(tT[tN],value)
return tT[tN]
endfunction
function NewTimer takes nothing returns timer
return NewTimerEx(0)
endfunction
//==========================================================================================
function ReleaseTimer takes timer t returns nothing
if(t==null) then
debug call BJDebugMsg("Warning: attempt to release a null timer")
return
endif
if (tN==ARRAY_SIZE) then
debug call BJDebugMsg("Warning: Timer stack is full, destroying timer!!")
//stack is full, the map already has much more troubles than the chance of bug
call DestroyTimer(t)
else
call PauseTimer(t)
if(GetTimerData(t)==HELD) then
debug call BJDebugMsg("Warning: ReleaseTimer: Double free!")
return
endif
call SetTimerData(t,HELD)
set tT[tN]=t
set tN=tN+1
endif
endfunction
private function init takes nothing returns nothing
local integer i=0
local integer o=-1
local boolean oops = false
if ( didinit ) then
return
else
set didinit = true
endif
static if( USE_HASH_TABLE ) then
set ht = InitHashtable()
loop
exitwhen(i==QUANTITY)
set tT[i]=CreateTimer()
call SetTimerData(tT[i], HELD)
set i=i+1
endloop
set tN = QUANTITY
else
loop
set i=0
loop
exitwhen (i==QUANTITY)
set tT[i] = CreateTimer()
if(i==0) then
set VOFFSET = GetHandleId(tT[i])
static if(USE_FLEXIBLE_OFFSET) then
set o=VOFFSET
else
set o=OFFSET
endif
endif
if (GetHandleId(tT[i])-o>=ARRAY_SIZE) then
exitwhen true
endif
if (GetHandleId(tT[i])-o>=0) then
set i=i+1
endif
endloop
set tN = i
exitwhen(tN == QUANTITY)
set oops = true
exitwhen not USE_FLEXIBLE_OFFSET
debug call BJDebugMsg("TimerUtils_init: Failed a initialization attempt, will try again")
endloop
if(oops) then
static if ( USE_FLEXIBLE_OFFSET) then
debug call BJDebugMsg("The problem has been fixed.")
//If this message doesn't appear then there is so much
//handle id fragmentation that it was impossible to preload
//so many timers and the thread crashed! Therefore this
//debug message is useful.
elseif(DEBUG_MODE) then
call BJDebugMsg("There were problems and the new timer limit is "+I2S(i))
call BJDebugMsg("This is a rare ocurrence, if the timer limit is too low:")
call BJDebugMsg("a) Change USE_FLEXIBLE_OFFSET to true (reduces performance a little)")
call BJDebugMsg("b) or try changing OFFSET to "+I2S(VOFFSET) )
endif
endif
endif
endfunction
endlibrary
library DispelConfiguration uses DispelLibrary
/* If you want to include more dispel abilities in the list
* Just copy & paste the default format of the structs here
*/
private struct DispelMagic extends array
// Dispel Magic (Priest) Ability ID
private static constant integer ABILITY_ID = 'Adis'
private static constant method radius takes integer level returns real
return 200.00 + (0.00*level) // radius of the ability (set to 0 to target unit dispel)
endmethod
private static constant method dispel takes integer level returns integer
/* DISPEL TYPES:
* BUFF_POSITIVE
* BUFF_NEGATIVE
*/
return 0 // dispel type of the ability (set to 0 to dispel both)
endmethod
private static constant integer SPELL_EVENT_TYPE = EVENT_SPELL_EFFECT
implement DispelConfigurationCloner
endstruct
private struct Disenchant extends array
// Disenchant (Spirit Walker) Ability ID
private static constant integer ABILITY_ID = 'Adcn'
private static constant method radius takes integer level returns real
return 250.00 + (0.00*level) // radius of the ability (set to 0 to target unit dispel)
endmethod
private static constant method dispel takes integer level returns integer
/* DISPEL TYPES:
* BUFF_POSITIVE
* BUFF_NEGATIVE
*/
return 0 // dispel type of the ability (set to 0 to dispel both)
endmethod
private static constant integer SPELL_EVENT_TYPE = EVENT_SPELL_EFFECT
implement DispelConfigurationCloner
endstruct
private struct Purge extends array
// Purge (Shaman) Ability ID
private static constant integer ABILITY_ID = 'Aprg'
private static constant method radius takes integer level returns real
return 0.00 + (0.00*level) // radius of the ability (set to 0 to target unit dispel)
endmethod
private static constant method dispel takes integer level returns integer
/* DISPEL TYPES:
* BUFF_POSITIVE
* BUFF_NEGATIVE
*/
return 0 // dispel type of the ability (set to 0 to dispel both)
endmethod
private static constant integer SPELL_EVENT_TYPE = EVENT_SPELL_EFFECT
implement DispelConfigurationCloner
endstruct
private struct DevourMagic extends array
// Devour Magic (Destroyer) Ability ID
private static constant integer ABILITY_ID = 'Advm'
private static constant method radius takes integer level returns real
return 200.00 + (0.00*level) // radius of the ability (set to 0 to target unit dispel)
endmethod
private static constant method dispel takes integer level returns integer
/* DISPEL TYPES:
* BUFF_POSITIVE
* BUFF_NEGATIVE
*/
return 0 // dispel type of the ability (set to 0 to dispel both)
endmethod
private static constant integer SPELL_EVENT_TYPE = EVENT_SPELL_EFFECT
implement DispelConfigurationCloner
endstruct
private struct AbolishMagic extends array
// Abolish Magic (Dryad) Ability ID
private static constant integer ABILITY_ID = 'Aadm'
private static constant method radius takes integer level returns real
return 0.00 + (0.00*level) // radius of the ability (set to 0 to target unit dispel)
endmethod
private static constant method dispel takes integer level returns integer
/* DISPEL TYPES:
* BUFF_POSITIVE
* BUFF_NEGATIVE
*/
return 0 // dispel type of the ability (set to 0 to dispel both)
endmethod
private static constant integer SPELL_EVENT_TYPE = EVENT_SPELL_EFFECT
implement DispelConfigurationCloner
endstruct
private struct Detonate extends array
// Detonate (Wisp) Ability ID
private static constant integer ABILITY_ID = 'Adtn'
private static constant method radius takes integer level returns real
return 300.00 + (0.00*level) // radius of the ability (set to 0 to target unit dispel)
endmethod
private static constant method dispel takes integer level returns integer
/* DISPEL TYPES:
* BUFF_POSITIVE
* BUFF_NEGATIVE
*/
return 0 // dispel type of the ability (set to 0 to dispel both)
endmethod
private static constant integer SPELL_EVENT_TYPE = EVENT_SPELL_EFFECT
implement DispelConfigurationCloner
endstruct
endlibrary
library DispelLibrary /* by JAKEZINC, extra library to dispel self-defined buffs created by Buff library
*/uses /*
*/Buff /* https://www.hiveworkshop.com/threads/288640/
*/SpellEvent /* https://www.hiveworkshop.com/threads/301895/
*/SpellCloner /* https://www.hiveworkshop.com/threads/292751/
*/
globals
private group Group = CreateGroup()
endglobals
native UnitAlive takes unit id returns boolean
// Filtration of the units that will be dispelled
private function Filtration takes unit picked returns boolean
return UnitAlive(picked)
endfunction
private struct Clone extends array
static real radius
static integer dispel
endstruct
private struct Dispel extends array
private method onClonedSpellStart takes nothing returns thistype
local real x
local real y
local unit picked
if Clone.radius > 0. then
set x = GetEventSpellTargetX()
set y = GetEventSpellTargetY()
call GroupEnumUnitsInRange(Group,x,y,Clone.radius,null)
loop
set picked = FirstOfGroup(Group)
exitwhen picked == null
if Filtration(picked) then
if Clone.dispel > 0 then
call Buff.dispel(picked,Clone.dispel)
else
call Buff.dispelBoth(picked)
endif
endif
call GroupRemoveUnit(Group,picked)
endloop
else
if Clone.dispel > 0 then
call Buff.dispel(GetEventSpellTargetUnit(),Clone.dispel)
else
call Buff.dispelBoth(GetEventSpellTargetUnit())
endif
endif
return 0
endmethod
/*
* locust methods because of the SpellEvent interface
*/
private static constant real SPELL_PERIOD = 0.0
private method onClonedSpellPeriodic takes nothing returns boolean
return false
endmethod
private method onClonedSpellEnd takes nothing returns nothing
endmethod
implement SpellCloner
endstruct
module DispelConfigurationCloner
private static method configHandler takes nothing returns nothing
set Clone.radius = radius(GetEventSpellLevel())
set Clone.dispel = dispel(GetEventSpellLevel())
endmethod
private static method onInit takes nothing returns nothing
call Dispel.create(thistype.typeid,ABILITY_ID,SPELL_EVENT_TYPE,function thistype.configHandler)
endmethod
endmodule
endlibrary
library PauseLibrary // by JAKEZINC, extra library to adapt pause/unpause methods for a unit safely
globals
// This serves as the main setup for pausing/unpausing a unit.
// Pause Ability ID
public constant integer PAUSE_ID = 'A00A' // Based on 'Infinite Channel Ability'
public constant integer PAUSE_ORDER_ID = 852055
/*
OBJECT DATA IMPORTS ARE OPTIONAL BELOW
I've discovered that infinite channel ability alone is not enough for some rare cases.
So if you don't care about these rare cases you can simply set the booleans to FALSE. (<-NO IMPORT REQUIRED)
If the infinite channel ability doesn't get implemented to a unit, the pause method adapts to a new method.
These are some of the rare cases where infinite channel ability method breaks: */
/*
Case #1: Militia - for unknown reason, we can only put aura abilities to them,
so we can't implement this method to militia units as it's considered using an active ability...
In order to pause them, we can DISARM them. */
public constant boolean DISARM = true
// Disarm Ability ID
public constant integer DISARM_ID = 'A001' // Based on 'Cargo Hold Ability'
/*
Case #2: Same as #1 but the unit has abilities, therefore DISARM is not enough.
In order to pause them, we can DISABLE (DISARM + SILENCE) them.
So you should set DISARM to false and set DISABLE to true instead.
Note that this DISABLE method requires and creates a global dummy unit to
cast the Disable Ability on a unit. It also shows on the unit's status bar.
Make sure you set the 'Stats - Buffs' field of the Disable Ability to Disable <- (Buff) in OE. */
public constant boolean DISABLE = false
// Dummy Unit ID
public constant integer DISABLE_CASTER = 'dumi' // Based on 'DummyRecycler Library'
// Disable Ability ID
public constant integer DISABLE_ID = 'A004' // Based on 'Drunken Haze Ability'
public constant integer DISABLE_ORDER_ID = 852585
// Disable Buff ID
public constant integer DISABLE_BUFF = 'B000' // Based on 'Drunken Haze Buff'
/* If you have Breath Of Fire ability that ignites the units having the Drunken Haze Buff in your map,
You must specify the Drunken Haze buff and the Breath of Fire buff that is related to each other.
In this way, Breath Of Fire ability will not ignite the units that are paused using the DISABLE method. */
// Drunken Haze Buff ID
public constant integer DRUNKEN_HAZE_BUFF = 'BNdh'
// Breath Of Fire Buff ID
public constant integer BREATH_OF_FIRE_BUFF = 'BNbf'
/*
Case #3: Silenced - for obvious reason, we can't implement the method to a silenced unit.
In order to pause them, the silence must be removed from the unit first before pausing it. */
public constant boolean SILENCE = true
// Silence Buff ID
public constant integer SILENCE_BUFF = 'BNsi'
/*
Case #4: Polymorphed - for obvious reason, we can't implement the method to a polymorphed unit.
In order to pause them, periodic polymorph checking, disabling its movement, and re-enable pause. */
public constant boolean POLYMORPH = true
/*
Case #5: Sleeping - for obvious reason, we can't implement the method to a sleeping unit.
In order to pause them, the unit must wake up first so we can order it to execute the pause. */
public constant boolean SLEEPING = true
public constant real PERIODIC_INTERVAL = 0.05
endglobals
struct PauseSystem extends array
private static hashtable table = InitHashtable()
private static method periodic takes nothing returns nothing
local unit target = LoadUnitHandle(table,0,GetHandleId(GetExpiredTimer()))
local thistype this = GetHandleId(target)
static if DISABLE then
if GetUnitAbilityLevel(target,BREATH_OF_FIRE_BUFF) > 0 and GetUnitAbilityLevel(target,DRUNKEN_HAZE_BUFF) == 0 then
call UnitRemoveAbility(target,BREATH_OF_FIRE_BUFF)
endif
endif
static if SILENCE then
if GetUnitAbilityLevel(target,SILENCE_BUFF) > 0 then
static if DISARM then
if GetUnitAbilityLevel (target,DISARM_ID) == 0 then
call UnitAddAbility(target,DISARM_ID)
endif
elseif DISABLE then
if GetUnitAbilityLevel(target,DISABLE_BUFF) == 0 then
call SetUnitX( disabler,GetUnitX(target) )
call SetUnitY( disabler,GetUnitY(target) )
call UnitAddAbility(disabler,DISABLE_ID)
call IssueTargetOrderById(disabler,DISABLE_ORDER_ID,target)
call UnitRemoveAbility(disabler,DISABLE_ID)
endif
else
call UnitRemoveAbility(target,SILENCE_BUFF)
endif
endif
endif
if GetUnitTypeId(target) != LoadInteger(table,this,2) then
call SaveInteger( table,this,2,GetUnitTypeId(target) )
call UnitRemoveAbility(target,PAUSE_ID)
call SetUnitTurnSpeed(target,0)
call SetUnitPropWindow(target,0)
if UnitAddAbility(target,PAUSE_ID) and IssueImmediateOrderById(target,PAUSE_ORDER_ID) then
endif
if GetUnitAbilityLevel(target,PAUSE_ID) == 0 or GetUnitCurrentOrder(target) != PAUSE_ORDER_ID then
static if DISARM then
if GetUnitAbilityLevel (target,DISARM_ID) == 0 then
call UnitAddAbility(target,DISARM_ID)
endif
elseif DISABLE then
if GetUnitAbilityLevel(target,DISABLE_BUFF) == 0 then
call SetUnitX( disabler,GetUnitX(target) )
call SetUnitY( disabler,GetUnitY(target) )
call UnitAddAbility(disabler,DISABLE_ID)
call IssueTargetOrderById(disabler,DISABLE_ORDER_ID,target)
call UnitRemoveAbility(disabler,DISABLE_ID)
endif
endif
endif
endif
static if POLYMORPH then
if not IsUnitType(target,UNIT_TYPE_POLYMORPHED) and LoadBoolean(table,this,3) then
call SetUnitTurnSpeed(target,0)
call SetUnitPropWindow(target,0)
static if SILENCE then
if GetUnitAbilityLevel(target,SILENCE_BUFF) > 0 then
call UnitRemoveAbility(target,SILENCE_BUFF)
endif
endif
call IssueImmediateOrderById(target,PAUSE_ORDER_ID)
call RemoveSavedBoolean(table,this,3)
elseif IsUnitType(target,UNIT_TYPE_POLYMORPHED) then
call SetUnitTurnSpeed(target,0)
call SetUnitPropWindow(target,0)
call SaveBoolean( table,this,3,true )
endif
endif
set target = null
endmethod
static method pause takes unit target returns nothing
local thistype this = GetHandleId(target)
local integer count = LoadInteger(table,this,0)
local timer time
if count == 0 then
set time = CreateTimer()
call SaveUnitHandle(table,0,GetHandleId(time),target)
call TimerStart(time,PERIODIC_INTERVAL,true,function thistype.periodic)
call SaveTimerHandle(table,this,1,time)
call SaveInteger( table,this,2,GetUnitTypeId(target) )
static if SLEEPING then
if IsUnitType(target,UNIT_TYPE_SLEEPING) then
call UnitWakeUp(target)
endif
endif
static if SILENCE then
if GetUnitAbilityLevel(target,SILENCE_BUFF) > 0 then
call UnitRemoveAbility(target,SILENCE_BUFF)
endif
endif
call SetUnitTurnSpeed(target,0)
call SetUnitPropWindow(target,0)
if UnitAddAbility(target,PAUSE_ID) and IssueImmediateOrderById(target,PAUSE_ORDER_ID) then
endif
if GetUnitAbilityLevel(target,PAUSE_ID) == 0 or GetUnitCurrentOrder(target) != PAUSE_ORDER_ID then
static if DISARM then
call UnitAddAbility(target,DISARM_ID)
elseif DISABLE then
call SetUnitX( disabler,GetUnitX(target) )
call SetUnitY( disabler,GetUnitY(target) )
call UnitAddAbility(disabler,DISABLE_ID)
call IssueTargetOrderById(disabler,DISABLE_ORDER_ID,target)
call UnitRemoveAbility(disabler,DISABLE_ID)
endif
endif
static if POLYMORPH then
call SaveBoolean( table,this,3,IsUnitType(target,UNIT_TYPE_POLYMORPHED) )
endif
set time = null
endif
call SaveInteger(table,this,0,count + 1)
endmethod
static method unpause takes unit target returns nothing
local timer time
local thistype this = GetHandleId(target)
local integer count = LoadInteger(table,this,0)
set count = count - 1
call SaveInteger(table,this,0,count)
if count == 0 then
static if DISARM then
call UnitRemoveAbility(target,DISARM_ID)
endif
static if DISABLE then
call UnitRemoveAbility(target,DISABLE_BUFF)
endif
call UnitRemoveAbility(target,PAUSE_ID)
call SetUnitTurnSpeed(target,GetUnitDefaultTurnSpeed(target) )
call SetUnitPropWindow(target,GetUnitDefaultPropWindow(target)*bj_RADTODEG)
set time = LoadTimerHandle(table,this,1)
call DestroyTimer(time)
call RemoveSavedHandle( table,0,GetHandleId(time) )
call FlushChildHashtable(table,this)
set time = null
endif
endmethod
static if DISABLE then
private static unit disabler = null
private static method onInit takes nothing returns nothing
set disabler = CreateUnit(Player(bj_PLAYER_NEUTRAL_EXTRA),DISABLE_CASTER,0,0,0)
endmethod
endif
endstruct
endlibrary
library WorldBounds /* v2.0.0.0
************************************************************************************
*
* struct WorldBounds extends array
*
* Fields
* ------
*
* readonly static integer maxX
* readonly static integer maxY
* readonly static integer minX
* readonly static integer minY
*
* readonly static integer centerX
* readonly static integer centerY
*
* readonly static rect world
* readonly static region worldRegion
*
************************************************************************************/
private module WorldBoundInit
private static method onInit takes nothing returns nothing
set world=GetWorldBounds()
set maxX = R2I(GetRectMaxX(world))
set maxY = R2I(GetRectMaxY(world))
set minX = R2I(GetRectMinX(world))
set minY = R2I(GetRectMinY(world))
set centerX = R2I((maxX + minX)/2)
set centerY = R2I((minY + maxY)/2)
set worldRegion = CreateRegion()
call RegionAddRect(worldRegion, world)
endmethod
endmodule
struct WorldBounds extends array
readonly static integer maxX
readonly static integer maxY
readonly static integer minX
readonly static integer minY
readonly static integer centerX
readonly static integer centerY
readonly static rect world
readonly static region worldRegion
implement WorldBoundInit
endstruct
endlibrary
library GetTerrainHeight /* not required to import if you already have GetTerrainZ(X,Y) function in your map
********************************************************************
* function GetTerrainZ takes real X, real Y returns real *
********************************************************************/
globals
private constant location Terrain = Location(0,0)
endglobals
function GetTerrainZ takes real X, real Y returns real
call MoveLocation(Terrain,X,Y)
return GetLocationZ(Terrain)
endfunction
endlibrary
library AutoFly initializer onInit requires UnitDex
/***************************************************************
*
* v1.0 by TriggerHappy
* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* AutoFly implements & removes crow form ability on any unit entering the map.
* This enables modifying the units custom fly height without having to do it manually.
*
* Credits to Azlier for the original idea
*
****************************************************************/
globals
private constant integer FLYABILITY = 'Amrf'
endglobals
private function Fly takes nothing returns boolean
return UnitAddAbility(GetIndexedUnit(),FLYABILITY) and UnitRemoveAbility(GetIndexedUnit(),FLYABILITY)
endfunction
private function onInit takes nothing returns nothing
call RegisterUnitIndexEvent(Filter(function Fly),EVENT_UNIT_INDEX)
endfunction
endlibrary
library RiseAndFall requires TimerUtils
/* RiseAndFall v1.05
by Spellbound
Special Thanks to Wareditor for the math and re-organizing my code for script optimization.
This simple library can be used to simulate an airborne effect by causing units to bob up and
down. It can also be used to make a unit fall from a certain height or rise in the air.
You can manipulate the height of the unit using this library or for your other purposes easily.
------------
REQUIREMENTS
------------
TimerUtils: http://www.wc3c.net/showthread.php?t=101322
If you want units with movement types (no other than hover or fly) to be able to use fly height,
you may put Crow Form ability on them and then remove it at the same time. Alternatively, just get
AutoFly library and make your life easier. One is available in the test map. Requires UnitDex.
------------
INTERFACE
------------
Operator:
RiseAndFall[whichUnit] will return the RiseAndFall instance of the unit.
Static Methods:
RiseAndFall.create(unit whichUnit, real currentZ, real endZ, real duration, boolean flag)
^ This will cause a unit to changed its height from currentZ to endZ over duration.
The flag determines if the unit should accelerate/decelerate as they reach destination height.
This function returns the struct instance if you wish to store it.
RiseAndFall.levitate(unit whichUnit, real currentZ, real endZ, real duration)
^ This will make your unit appear to levitate between currentZ and endZ over duration.
This function returns the struct instance if you wish to store it.
RiseAndFall.isUnitAirborne(unit whichUnit)
^ This returns true/false if a unit is levitating/rising/falling.
Instance Methods:
RiseAndFall[whichUnit].end(real endZ, real speed)
^ Ends the Rise/Fall/Airborne. real endZ determines the height at which you
want the unit to end at and the speed determines how fast the unit will get there.
(calculated as: speed = PERIODIC_INTERVAL / ( (currentHeight - endHeight)/speed )
Set to the current height of the unit to terminate instantly.
*/
globals
private constant real PERIODIC_INTERVAL = 0.03125
endglobals
private module Init
private static method onInit takes nothing returns nothing
set instance_storage = InitHashtable()
endmethod
endmodule
struct RiseAndFall
private unit unit
private real heightStart
private real heightEnd
private real speed
private real progress
private boolean useSmoothstep
private boolean isAirborne
private static timer clock = CreateTimer()
private static hashtable instance_storage
static method operator [] takes unit whichUnit returns thistype
return LoadInteger(instance_storage,GetHandleId(whichUnit),0)
endmethod
static method isUnitAirborne takes unit whichUnit returns boolean
return LoadInteger(instance_storage,GetHandleId(whichUnit),0) != 0
endmethod
method destroy takes nothing returns nothing
call FlushChildHashtable( instance_storage,GetHandleId(.unit) )
call .deallocate()
set .unit = null
endmethod
private static method update takes nothing returns nothing
local timer time = GetExpiredTimer()
local thistype this = GetTimerData(time)
local real x
local real y
set .progress = .progress + .speed
if .progress >= 1.0 then
call SetUnitFlyHeight(.unit,.heightEnd,0)
if .isAirborne then
set y = .heightStart
set .heightStart = .heightEnd
set .heightEnd = y
set .progress = 0.
else
call ReleaseTimer(time)
if isUnitAirborne(.unit) then
call .destroy()
endif
endif
else
set x = .progress
if .useSmoothstep then
call SetUnitFlyHeight(.unit,.heightStart + (.heightEnd - .heightStart)*(x*x*x*(x*(x*6.0-15)+10) ),0)
else
call SetUnitFlyHeight(.unit,.heightStart + (.heightEnd - .heightStart)*(x*x),0)
endif
endif
set time = null
endmethod
method end takes real endZ, real speed returns nothing
local real height = GetUnitFlyHeight(.unit)
if endZ != height then
set .heightStart = height
set .heightEnd = endZ
set .progress = 0.
set .isAirborne = false
set .useSmoothstep = false
if speed == 0. then // divide by zero prevention
set speed = 500.
endif
set .speed = PERIODIC_INTERVAL / ( (.heightStart - .heightEnd)/speed )
elseif isUnitAirborne(.unit) then
set .heightStart = height
set .heightEnd = endZ
set .progress = 1.0
set .isAirborne = false
set .useSmoothstep = false
call .destroy()
endif
endmethod
private static method createAirborne takes unit whichUnit, real currentZ, real endZ, real duration, boolean airborne, boolean smooth returns thistype
local thistype this = RiseAndFall[whichUnit]
if this != 0 then
call this.end(GetUnitFlyHeight(whichUnit),1.0)
endif
set this = allocate()
set this.unit = whichUnit
set this.heightStart = currentZ
set this.heightEnd = endZ
set this.progress = 0.
set this.speed = PERIODIC_INTERVAL/duration
set this.isAirborne = airborne
set this.useSmoothstep = smooth
call SaveInteger(instance_storage,GetHandleId(whichUnit),0,this)
call TimerStart(NewTimerEx(this),PERIODIC_INTERVAL,true,function thistype.update)
return this
endmethod
static method create takes unit whichUnit, real currentZ, real endZ, real duration, boolean flag returns thistype
if RiseAndFall[whichUnit] == 0 then
return createAirborne(whichUnit,currentZ,endZ,duration,false,flag)
endif
return 0
endmethod
static method levitate takes unit whichUnit, real currentZ, real endZ, real duration returns thistype
if RiseAndFall[whichUnit] == 0 then
return createAirborne(whichUnit,currentZ,endZ,duration,true,true)
endif
return 0
endmethod
implement Init
endstruct
endlibrary
library RapidSound requires optional TimerUtils
globals
// Actually, just leave this value
private constant real MIN_DELAY_FACTOR = 4.0
endglobals
/* v1.6.0
Description
¯¯¯¯¯¯¯¯¯¯¯
Allows you to play sounds rapidly and flawlessly without limit.
Remember one sound file can only have one RSound instance.
External Dependencies
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
(Optional)
TimerUtils by Vexorian
wc3c.net/showthread.php?t=101322
User API
¯¯¯¯¯¯¯¯
struct RSound
Instantiate an RapidSound instance
| static method create takes string fileName, boolean is3D, boolean autoStop, integer inRate, integer outRate returns thistype
autoStop => stop when sound is out of range
inRate => fade in rate
outRate => fade out rate
Play the sound at given coordinate
| method play takes real x, real y, real z, integer volume returns nothing
Stop sound
| method stop takes boolean fadeOut returns nothing
Destroy RapidSound instance
| method kill takes nothing returns nothing
Sound file duration (in second)
| method operator duration takes nothing returns real
Resource Link
¯¯¯¯¯¯¯¯¯¯¯¯¯
hiveworkshop.com/threads/snippet-rapidsound.258991/
*/
struct RSound
private static constant integer MAX_COUNT = 4
private static integer Counter = -1
private static string array StrLib
private static thistype array StrDex
private integer ct
private integer lib
private integer dex
private real dur
private sound array snd[thistype.MAX_COUNT]
private timer array tmr[thistype.MAX_COUNT]
method operator duration takes nothing returns real
return .dur*MIN_DELAY_FACTOR
endmethod
method kill takes nothing returns nothing
local integer i
set .ct = .ct - 1
if .ct == 0 then
set i = 0
loop
exitwhen i == MAX_COUNT
call StopSound(.snd[i], true, false)
static if LIBRARY_TimerUtils then
call ReleaseTimer(.tmr[i])
else
call DestroyTimer(.tmr[i])
endif
set .snd[i] = null
set .tmr[i] = null
set i = i + 1
endloop
set StrLib[.lib] = StrLib[Counter]
set StrDex[.lib] = StrDex[Counter]
set Counter = Counter - 1
call deallocate()
endif
endmethod
method stop takes boolean fadeOut returns nothing
local integer i = 0
loop
exitwhen i == MAX_COUNT
call StopSound(.snd[i], false, fadeOut)
set i = i + 1
endloop
endmethod
method play takes real x, real y, real z, integer volume returns nothing
set .dex = .dex + 1
if .dex == MAX_COUNT then
set .dex = 0
endif
if TimerGetRemaining(.tmr[.dex]) == 0 then
call StopSound(.snd[.dex], false, false)
call SetSoundPosition(.snd[.dex], x, y, z)
call SetSoundVolume(.snd[.dex], volume)
call StartSound(.snd[.dex])
call TimerStart(.tmr[.dex], .dur, false, null)
endif
endmethod
static method create takes string fileName, boolean is3D, boolean autoStop, integer inRate, integer outRate returns thistype
local thistype this
local integer i = 0
local boolean b = true
loop
exitwhen i > Counter
if fileName == StrLib[i] then
set b = false
exitwhen true
endif
set i = i + 1
endloop
if b then
set this = allocate()
set Counter = Counter + 1
set StrLib[Counter] = fileName
set StrDex[Counter] = this
set .ct = 1
set .dex = -1
set .lib = Counter
set .dur = I2R(GetSoundFileDuration(fileName))/(1000.*MIN_DELAY_FACTOR)
set i = 0
loop
exitwhen i == MAX_COUNT
set .snd[i] = CreateSound(fileName, false, is3D, autoStop, inRate, outRate, "")
static if LIBRARY_TimerUtils then
set .tmr[i] = NewTimer()
call TimerStart(.tmr[i], 0, false, null)
call PauseTimer(.tmr[i])
else
set .tmr[i] = CreateTimer()
endif
set i = i + 1
endloop
else
set this = StrDex[i]
set .ct = .ct + 1
endif
return this
endmethod
endstruct
endlibrary
library CameraShake initializer Init
/*
* ---- Camera Shake System ----
* by rulerofiron
*
* Functions:
* CameraShake_NewEvent takes real TargetX, real TargetY, real Magnitude, real MaxRange
* TargetX/Y location of the shake origin
* Magnitude the magnitude of the camera shake at origin point
* MaxRange the maximum range at which players are shaken
*
* CameraShake_NewEventPlayer takes player TargetPlayer, real Magnitude
* TargetPlayer the player to be shaken
* Magnitude the magnitude of the camera shake to the player
*
*/
globals
//Configurables
private constant real PERIODIC_INTERVAL = 0.10
private constant real SHAKE_REDUCE_FLAT = 0.50
private constant real SHAKE_REDUCE_PERCENT = 0.05
private constant real SHAKE_THRESHOLD = 0.50 // the shaking stops on this threshold
private constant real RICHTER_MIN = 2.5 // the minimum magnitude of the shaking
private constant real RICHTER_MAX = 5.0 // the maximum magnitude of the shaking
//System Variables
private force shakingPlayers = CreateForce()
private real X
private real Y
private real magnitude
private real range
private real array shake
endglobals
// shaking the players included per period
private function PeriodPerPlayer takes nothing returns nothing
local player picked = GetEnumPlayer()
local integer id = GetPlayerId(picked)
local real richter = shake[id]
local real temprichter
if richter > RICHTER_MAX then
set richter = RICHTER_MAX
endif
if richter < RICHTER_MIN then
set richter = RICHTER_MIN
endif
if GetLocalPlayer() == picked then
set temprichter = shake[id]*Pow(10,richter)
call CameraSetTargetNoiseEx(shake[id]*2.0,temprichter,true)
call CameraSetSourceNoiseEx(shake[id]*2.0,temprichter,true)
endif
set shake[id] = shake[id] - (shake[id]*SHAKE_REDUCE_PERCENT)
set shake[id] = shake[id] - SHAKE_REDUCE_FLAT
if shake[id] < SHAKE_THRESHOLD then
if GetLocalPlayer() == picked then
call CameraSetSourceNoise(0,0)
call CameraSetTargetNoise(0,0)
endif
call ForceRemovePlayer(shakingPlayers,picked)
endif
endfunction
// including players to be shaken for location event
private function NewEventPerPlayer takes nothing returns nothing
local real dis
local real cameraX
local real cameraY
local player picked = GetEnumPlayer()
local integer id = GetPlayerId(picked)
if GetLocalPlayer() == picked then
set cameraX = GetCameraTargetPositionX()
set cameraY = GetCameraTargetPositionY()
endif
set dis = SquareRoot((cameraX-X)*(cameraX-X)+(cameraY-Y)*(cameraY-Y))
if dis <= range then
set shake[id] = shake[id]+(magnitude*((range-dis)/(range+dis)))
call ForceAddPlayer(shakingPlayers,picked)
endif
endfunction
//Register new location shake event
public function NewEvent takes real x, real y, real mag, real ran returns nothing
set X = x
set Y = y
set magnitude = mag
set range = ran
call ForForce(bj_FORCE_ALL_PLAYERS,function NewEventPerPlayer)
endfunction
//Register new player shake event
public function NewEventPlayer takes player target, real magnitude returns nothing
set shake[GetPlayerId(target)] = shake[GetPlayerId(target)] + magnitude
call ForceAddPlayer(shakingPlayers,target)
endfunction
private function Periodic takes nothing returns nothing
call ForForce(shakingPlayers,function PeriodPerPlayer)
endfunction
private function Init takes nothing returns nothing
call TimerStart(CreateTimer(), PERIODIC_INTERVAL, true, function Periodic)
endfunction
endlibrary
/*
* Knockback3D by Cokemonkey and revised by JAKEZINC.
*
* Requirements:
* AutoFly
* https://www.hiveworkshop.com/threads/249422/
*
* GetTerrainHeight
* To make this work with terrain height
* You must have a library that contains GetTerrainZ(X,Y) function
*
* IsTerrainWalkable or TerrainPathability
* To make this detect pathing collision,
* You must have a library either IsTerrainWalkable by Anitarf & Vexorian or TerrainPathability by Rising_Dusk
*
* Optional IsDestructableTree
* To make this detect if the destructable is a tree or not
*
* Interface:
* Knockback3D.New(unit whichUnit, real velocity, real angle, real alpha, string whichModel)
* Make a new knockback vector to a unit.
* If the unit is already in the system, this new vector will be emulated as a secondary knockback source.
*
* Knockback3D.Set(unit whichUnit, real velocity, real angle, real alpha, string whichModel)
* Set a new knockback vector to a unit.
* If the unit is already in the system, this new vector will replace the old one.
*
* Knockback3D.UpdateMapArea(rect whichRect)
* For updating the valid map coordinates.
* In case the playable map area changes dynamically.
*
* Parameters:
* whichUnit : unit to knockback.
* velocity : speed in units per second at which to knockback the unit.
* angle : angle of force in X & Y plane to knockback the unit, in radians.
* alpha : angle of force in Z plane to knockback the unit, in radians.
* whichModel : model to spawn on the unit's ground in friction, doesn't show for flying unit type.
*
* Configurations:
* constant boolean USE_MOVESPEED_MODIFIERS - enable/disable the knockbacked unit's movement speed
* constant boolean USE_TREE_CHECKER - check the picked destructable if it's a tree or not
* constant boolean DESTROY_DESTRUCTABLES_ONHIT - destroy destructable hit by knockbacked unit
* constant real CLOCK_PERIOD - how often to iterate through knockbacked unit
* constant real VELOCITY_RESTITUTION_GROUND - fraction of velocity to keep after hitting ground
* constant real VELOCITY_RESTITUTION_DESTRUCTABLE - fraction of velocity to keep after hitting destructable
* constant real VELOCITY_LOST_MULTIPLIER - fraction of velocity to lose while sliding per iteration
* constant real GRAVITY - acceleration rate in units per second
* constant real MAX_Z_VELOCITY_TO_BOUNCE - necessary z-velocity to bounce off ground
* constant real MIN_Z_VELOCITY_TO_BECOME_AIRBORNE - necessay z-velocity to stop sliding
* constant real MIN_FLY_HEIGHT - minimum height threshold
* constant real MIN_FOR_KNOCKBACK - minimum velocity to maintain knockback
* constant real MIN_VELOCITY_FRICTION_MODEL - minimum velocity to draw friction model
* constant real DESTRUCTABLE_PICK_RADIUS - size of square to enumerate destructables
* constant real MIN_VELOCITY_DESTROY_DESTRUCTABLE - minimum velocity to destroy destructable
* constant real MAX_HEIGHT_DESTROY_DESTRUCTABLE - flying height at which destructable is destroyed
*
*/
library Knockback3D uses AutoFly, GetTerrainHeight, /*
*/ optional IsDestructableTree, /*
*/ optional IsTerrainWalkable, /*
*/ optional TerrainPathability
// =========================================================================
// Begin Customizable Section
// =========================================================================
globals
// Defines whether units should have their movement speed enabled/disabled
// while in motion, and then returns back to their default movement speed.
// True = Enable , False = Disable.
private constant boolean USE_MOVESPEED_MODIFIERS=true
// Defines whether the script should check enumerated destructables as
// being trees or not. If enabled, will only work if IsDestructableTree
// library is available.
private constant boolean USE_TREE_CHECKER=true
// Defines whether to enumerate and destroy destructables in contact
// with knockbacked unit.
private constant boolean DESTROY_DESTRUCTABLES_ONHIT=true
endglobals
struct Knockback3D
// A parameter for controlling the system clock, in seconds. 1/60 runs
// 60 times per second.
private static constant real CLOCK_PERIOD=1.0/60.
// How much velocity should be retained after hitting a ground.
// .40 = 40% retention.
private static constant real VELOCITY_RESTITUTION_GROUND=.40
// How much velocity should be retained after hitting a destructable.
// .30 = 30% retention.
private static constant real VELOCITY_RESTITUTION_DESTRUCTABLE=.30
// Fraction of velocity should be lost with every iteration of
// ground friction. Note that simulating an abstraction of friction in
// units per second overflows real precision numbers. Thus, you must
// adjust this according to your clock period.
private static constant real VELOCITY_LOST_MULTIPLIER=.15
// The downward acceleration of units in motion. A value of
// CLOCK_PERIOD*45. means they accelerate downwards by 45. units per second.
private static constant real GRAVITY=CLOCK_PERIOD*45.
// The minimum fall-speed for a unit to bounce. CLOCK_PERIOD*-300. means
// that the a unit must be falling at 300 units per second to bounce.
private static constant real MAX_Z_VELOCITY_TO_BOUNCE=CLOCK_PERIOD*-300.
// The minimum z-velocity of a unit to have it's flying height changed,
// instead of simply sliding.
private static constant real MIN_Z_VELOCITY_TO_BECOME_AIRBORNE=CLOCK_PERIOD*150.
// This is the minimum height a unit can be at before friction is
// applied. A value greater than 0 is recommended as some units have a
// small non-zero flying height.
private static constant real MIN_FLY_HEIGHT=5.0
// The minimum horizontal velocity a unit can be sliding before the
// system ignores it. A value of CLOCK_PERIOD*30 means the unit will
// stop sliding when its slide speed reduces past 30 units per second.
private static constant real MIN_FOR_KNOCKBACK=CLOCK_PERIOD*30.
// The minimum speed a sliding unit must be moving to spawn a friction
// model. A value of CLOCK_PERIOD*180 means the effect is applied while units
// are moving faster than 180 units per second. Ignores flying unit type.
private static constant real MIN_VELOCITY_FRICTION_MODEL=CLOCK_PERIOD*180.
// The square size to search for destructables when destroying them.
// Note that a square's diagonal is SquareRoot(2) times bigger than this.
private static constant real DESTRUCTABLE_PICK_RADIUS=130.
// The minimum horizontal velocity a unit must have to destroy a
// destructable. You can set this to a very high number to disable the
// feature. A value of CLOCK_PERIOD*300 means the unit must travel at
// 300 units per second on the XY plane, to destroy obstacles.
private static constant real MIN_VELOCITY_DESTROY_DESTRUCTABLE=CLOCK_PERIOD*300.
// The height below which a flying unit is elligible to destroy
// destructables. It could be the maximum height of destructables.
private static constant real MAX_HEIGHT_DESTROY_DESTRUCTABLE=150.
// =====================================================================
// End Customizable Section
// =====================================================================
private real x
private real y
private real z
private unit unit
private string model
private static boolean hitDestructable
// A stack size counter.
private static integer stack=-1
// A stack index of knockback data id.
private static thistype array id
private static real mapMinX
private static real mapMaxX
private static real mapMinY
private static real mapMaxY
private static rect destructableRect
private static timer clock=CreateTimer()
private static method destructableCallback takes nothing returns nothing
local destructable des=GetEnumDestructable()
if GetDestructableLife(des)>0. then
static if DESTROY_DESTRUCTABLES_ONHIT then
static if USE_TREE_CHECKER and LIBRARY_IsDestructableTree then
if IsDestructableTree(des) then
call KillDestructable(des)
endif
else
call KillDestructable(des)
endif
endif
set hitDestructable=true
endif
set des=null
endmethod
private static method periodic takes nothing returns nothing
local real x1
local real y1
local real x2
local real y2
local real z
local real curHeight
local real realHeight
local real propWindow
local real distance
local boolean newInMap
local integer index=0
local thistype data
loop
exitwhen index>stack
set data=thistype.id[index]
set x1=GetUnitX(data.unit)
set y1=GetUnitY(data.unit)
set x2=x1+data.x
set y2=y1+data.y
set newInMap=x2>mapMinX and x2<mapMaxX and y2>mapMinY and y2<mapMaxY
set curHeight=GetUnitFlyHeight(data.unit)
set realHeight=GetUnitDefaultFlyHeight(data.unit)+MIN_FLY_HEIGHT
set distance=(data.x*data.x+data.y*data.y)
if curHeight<realHeight then
set propWindow = GetUnitDefaultPropWindow(data.unit)*bj_DEGTORAD
if IsTerrainWalkable(x2,y2) and newInMap then
call SetUnitX(data.unit,x1+data.x)
call SetUnitY(data.unit,y1+data.y)
if data.z<=realHeight then
set data.x=data.x*(1.0-VELOCITY_LOST_MULTIPLIER)
set data.y=data.y*(1.0-VELOCITY_LOST_MULTIPLIER)
if distance>MIN_VELOCITY_FRICTION_MODEL and not IsUnitType(data.unit,UNIT_TYPE_FLYING) then
call DestroyEffect( AddSpecialEffect(data.model,x1,y1) )
endif
endif
static if USE_MOVESPEED_MODIFIERS then
call SetUnitPropWindow(data.unit,propWindow)
endif
else
set data.x=0
set data.y=0
endif
if data.z<MAX_Z_VELOCITY_TO_BOUNCE then
set data.z=data.z*-1.*VELOCITY_RESTITUTION_GROUND
endif
if data.z>MIN_Z_VELOCITY_TO_BECOME_AIRBORNE then
call SetUnitFlyHeight(data.unit,curHeight+data.z,0)
set data.z=data.z-GRAVITY
endif
elseif newInMap then
set data.z=data.z-GRAVITY
set z=GetTerrainZ(x2,y2)-GetTerrainZ(x1,y1)
call SetUnitFlyHeight(data.unit,curHeight+data.z-z,0)
call SetUnitX(data.unit,x2)
call SetUnitY(data.unit,y2)
static if USE_MOVESPEED_MODIFIERS then
if GetUnitPropWindow(data.unit) != 0 then
call SetUnitPropWindow(data.unit,0)
endif
endif
else
set data.x=0
set data.y=0
endif
if distance<MIN_FOR_KNOCKBACK and data.z>MAX_Z_VELOCITY_TO_BOUNCE and data.z<-1*MAX_Z_VELOCITY_TO_BOUNCE and curHeight<realHeight then
set id[index]=id[stack]
set stack=stack-1
call SetUnitFlyHeight(data.unit,0.00010+GetUnitDefaultFlyHeight(data.unit),0)
static if USE_MOVESPEED_MODIFIERS then
call SetUnitPropWindow(data.unit,propWindow)
endif
call data.destroy()
set index=index-1
if stack<0 then
call PauseTimer(clock)
endif
endif
if distance>MIN_VELOCITY_DESTROY_DESTRUCTABLE and curHeight<MAX_HEIGHT_DESTROY_DESTRUCTABLE then
set hitDestructable=false
call MoveRectTo(destructableRect,x2,y2)
call EnumDestructablesInRect(destructableRect,null,function thistype.destructableCallback)
if hitDestructable then
set data.x=data.x*VELOCITY_RESTITUTION_DESTRUCTABLE
set data.y=data.y*VELOCITY_RESTITUTION_DESTRUCTABLE
endif
endif
set index=index+1
endloop
endmethod
private static method getUnitIndexFromStack takes unit whichUnit returns integer
local integer index=0
local integer returner=-1
local thistype data
loop
exitwhen index>stack or returner!=-1
set data=id[index]
if data.unit==whichUnit then
set returner=index
endif
set index=index+1
endloop
return returner
endmethod
private static method onInit takes nothing returns nothing
set destructableRect=Rect(-1*DESTRUCTABLE_PICK_RADIUS,-1*DESTRUCTABLE_PICK_RADIUS,DESTRUCTABLE_PICK_RADIUS,DESTRUCTABLE_PICK_RADIUS)
set mapMinX=GetRectMinX(bj_mapInitialPlayableArea)
set mapMaxX=GetRectMaxX(bj_mapInitialPlayableArea)
set mapMinY=GetRectMinY(bj_mapInitialPlayableArea)
set mapMaxY=GetRectMaxY(bj_mapInitialPlayableArea)
endmethod
public static method New takes unit whichUnit, real velocity, real angle, real alpha, string whichModel returns nothing
local integer index=getUnitIndexFromStack(whichUnit)
local thistype data
local real realVelocity=velocity*CLOCK_PERIOD
if index==-1 then
set data=thistype.create()
set data.unit=whichUnit
set data.model=whichModel
set data.x=realVelocity*Cos(angle)*Cos(alpha)
set data.y=realVelocity*Sin(angle)*Cos(alpha)
set data.z=realVelocity*Sin(alpha)
set stack=stack+1
set id[stack]=data
if stack==0 then
call TimerStart(clock,CLOCK_PERIOD,true,function thistype.periodic)
endif
else
set data=id[index]
set data.x=data.x+realVelocity*Cos(angle)*Cos(alpha)
set data.y=data.y+realVelocity*Sin(angle)*Cos(alpha)
set data.z=data.z+realVelocity*Sin(alpha)
endif
endmethod
public static method Set takes unit whichUnit, real velocity, real angle, real alpha, string whichModel returns nothing
local integer index=getUnitIndexFromStack(whichUnit)
local thistype data
local real realVelocity=velocity*CLOCK_PERIOD
if index==-1 then
set data=thistype.create()
set data.unit=whichUnit
set data.model=whichModel
set data.x=realVelocity*Cos(angle)*Cos(alpha)
set data.y=realVelocity*Sin(angle)*Cos(alpha)
set data.z=realVelocity*Sin(alpha)
set stack=stack+1
set id[stack]=data
if stack==0 then
call TimerStart(clock,CLOCK_PERIOD,true,function thistype.periodic)
endif
else
set data=id[index]
set data.x=realVelocity*Cos(angle)*Cos(alpha)
set data.y=realVelocity*Sin(angle)*Cos(alpha)
set data.z=realVelocity*Sin(alpha)
endif
endmethod
public static method UpdateMapArea takes rect whichRect returns nothing
set thistype.mapMinX=GetRectMinX(whichRect)
set thistype.mapMinY=GetRectMinY(whichRect)
set thistype.mapMaxX=GetRectMaxX(whichRect)
set thistype.mapMaxY=GetRectMaxY(whichRect)
endmethod
endstruct
endlibrary
library TerrainPathability initializer Init
//******************************************************************************
//* BY: Rising_Dusk
//*
//* This script can be used to detect the type of pathing at a specific point.
//* It is valuable to do it this way because the IsTerrainPathable is very
//* counterintuitive and returns in odd ways and aren't always as you would
//* expect. This library, however, facilitates detecting those things reliably
//* and easily.
//*
//******************************************************************************
//*
//* > function IsTerrainDeepWater takes real x, real y returns boolean
//* > function IsTerrainShallowWater takes real x, real y returns boolean
//* > function IsTerrainLand takes real x, real y returns boolean
//* > function IsTerrainPlatform takes real x, real y returns boolean
//* > function IsTerrainWalkable takes real x, real y returns boolean
//*
//* These functions return true if the given point is of the type specified
//* in the function's name and false if it is not. For the IsTerrainWalkable
//* function, the MAX_RANGE constant below is the maximum deviation range from
//* the supplied coordinates that will still return true.
//*
//* The IsTerrainPlatform works for any preplaced walkable destructable. It will
//* return true over bridges, destructable ramps, elevators, and invisible
//* platforms. Walkable destructables created at runtime do not create the same
//* pathing hole as preplaced ones do, so this will return false for them. All
//* other functions except IsTerrainWalkable return false for platforms, because
//* the platform itself erases their pathing when the map is saved.
//*
//* After calling IsTerrainWalkable(x, y), the following two global variables
//* gain meaning. They return the X and Y coordinates of the nearest walkable
//* point to the specified coordinates. These will only deviate from the
//* IsTerrainWalkable function arguments if the function returned false.
//*
//* Variables that can be used from the library:
//* [real] TerrainPathability_X
//* [real] TerrainPathability_Y
//*
globals
private constant real MAX_RANGE = 10.
private constant integer DUMMY_ITEM_ID = 'wolg'
endglobals
globals
private item Item = null
private rect Find = null
private item array Hid
private integer HidMax = 0
public real X = 0.
public real Y = 0.
endglobals
function IsTerrainDeepWater takes real x, real y returns boolean
return not IsTerrainPathable(x, y, PATHING_TYPE_FLOATABILITY) and IsTerrainPathable(x, y, PATHING_TYPE_WALKABILITY)
endfunction
function IsTerrainShallowWater takes real x, real y returns boolean
return not IsTerrainPathable(x, y, PATHING_TYPE_FLOATABILITY) and not IsTerrainPathable(x, y, PATHING_TYPE_WALKABILITY) and IsTerrainPathable(x, y, PATHING_TYPE_BUILDABILITY)
endfunction
function IsTerrainLand takes real x, real y returns boolean
return IsTerrainPathable(x, y, PATHING_TYPE_FLOATABILITY)
endfunction
function IsTerrainPlatform takes real x, real y returns boolean
return not IsTerrainPathable(x, y, PATHING_TYPE_FLOATABILITY) and not IsTerrainPathable(x, y, PATHING_TYPE_WALKABILITY) and not IsTerrainPathable(x, y, PATHING_TYPE_BUILDABILITY)
endfunction
private function HideItem takes nothing returns nothing
if IsItemVisible(GetEnumItem()) then
set Hid[HidMax] = GetEnumItem()
call SetItemVisible(Hid[HidMax], false)
set HidMax = HidMax + 1
endif
endfunction
function IsTerrainWalkable takes real x, real y returns boolean
//Hide any items in the area to avoid conflicts with our item
call MoveRectTo(Find, x, y)
call EnumItemsInRect(Find ,null, function HideItem)
//Try to move the test item and get its coords
call SetItemPosition(Item, x, y) //Unhides the item
set X = GetItemX(Item)
set Y = GetItemY(Item)
static if LIBRARY_IsTerrainWalkable then
//This is for compatibility with the IsTerrainWalkable library
set IsTerrainWalkable_X = X
set IsTerrainWalkable_Y = Y
endif
call SetItemVisible(Item, false)//Hide it again
//Unhide any items hidden at the start
loop
exitwhen HidMax <= 0
set HidMax = HidMax - 1
call SetItemVisible(Hid[HidMax], true)
set Hid[HidMax] = null
endloop
//Return walkability
return (X-x)*(X-x)+(Y-y)*(Y-y) <= MAX_RANGE*MAX_RANGE and not IsTerrainPathable(x, y, PATHING_TYPE_WALKABILITY)
endfunction
private function Init takes nothing returns nothing
set Find = Rect(0., 0., 128., 128.)
set Item = CreateItem(DUMMY_ITEM_ID, 0, 0)
call SetItemVisible(Item, false)
endfunction
endlibrary
library IsDestructableTree /* v1.3.2.0 revised by JAKEZINC.
****************************************************************
*
* Detect whether a destructable is a tree or not.
*
****************************************************************
*
* Credits
*
* PitzerMike for his IsDestructableTree library
*
****************************************************************
*
* Functions
*
* function IsDestructableTree takes destructable des returns boolean
*
* function IsDestructableAlive takes destructable des returns boolean
*
* function IsDestructableDead takes destructable des returns boolean
*
* function IsTreeAlive takes destructable tree returns boolean
*
* function KillTree takes destructable tree returns boolean
*
*/
globals
// Ability ID of the harvest ability used for detecting if a destructable is a tree or not
private constant integer HARVEST_ABILITY_ID = 'Ahar'
// Order ID of the harvest ability
private constant integer HARVEST_ORDER_ID = 852018
// Order ID of the stop command used for preventing the following harvest order
private constant integer STOP_ORDER_ID = 851972
// Unit ID of the harvester unit that will use the harvest ability
private constant integer HARVESTER_UNIT_ID = 'hpea'
// Player ID of the harvester unit
private constant player HARVESTER_PLAYER_ID = Player(bj_PLAYER_NEUTRAL_EXTRA)
endglobals
globals
public rect Region
private unit Harvester
endglobals
function IsDestructableTree takes destructable des returns boolean
return IssueTargetOrderById(Harvester,HARVEST_ORDER_ID,des) and IssueImmediateOrderById(Harvester,STOP_ORDER_ID)
endfunction
function IsDestructableAlive takes destructable des returns boolean
return (GetWidgetLife(des) > .405)
endfunction
function IsDestructableDead takes destructable des returns boolean
return (GetWidgetLife(des) <= .405)
endfunction
function IsTreeAlive takes destructable tree returns boolean
return IsDestructableAlive(tree) and IsDestructableTree(tree)
endfunction
function KillTree takes destructable tree returns boolean
if ( IsTreeAlive(tree) ) then
call KillDestructable(tree)
return true
endif
return false
endfunction
private function Init takes nothing returns nothing
set Region = Rect(0,0,0,0)
set Harvester = CreateUnit(HARVESTER_PLAYER_ID,HARVESTER_UNIT_ID,0,0,0)
call UnitAddAbility(Harvester,HARVEST_ABILITY_ID)
call UnitAddAbility(Harvester,'Aloc')
call ShowUnit(Harvester,false)
endfunction
private module ModuleInitializer
private static method onInit takes nothing returns nothing
call Init()
endmethod
endmodule
private struct StructInitializer extends array
implement ModuleInitializer
endstruct
endlibrary
library FelInfusionBuffLibrary /* is using optional custom models
war3mapImported\Soul Armor Lime.mdx
*/uses /*
*/Buff /* https://www.hiveworkshop.com/threads/288640/
*/
public struct FelInfusionBuff extends Buff
/**************************************************
* GLOBAL CONFIGURATION
***************************************************/
/*
* Note that when importing, make sure the two objects (<AbilityName> Buff (Creator) and <AbilityName> Buff)
* In Object Editor (OE) have the same rawcode except for their first characters: 'A' & 'B'
* Examples: 'A000' & 'B000' , 'A00A' & 'B00A' , 'A00B' & 'B00B' , 'A005' & 'B005' , 'AXYZ' & 'BXYZ'
* You can customize rawcodes using an Object Merger script or Grimoire extension (JassNewGenPack)
* Import the optional DispelLibrary for dispel feature!
*/
// Fel Infusion Ability Buff Creator ID
private static constant integer RAWCODE = 'AINF'
// The model of the Fel Infusion buff
private static constant string BUFF_MODEL = "war3mapImported\\Soul Armor Lime.mdx"
private static constant string BUFF_MODEL_ORIGIN = "chest"
// BUFF_POSITIVE , BUFF_NEGATIVE (set to 0 to not dispel)
private static constant integer DISPEL_TYPE = BUFF_POSITIVE
/* BUFF_STACK_NONE = does not stack with any source
* BUFF_STACK_PARTIAL = does not stack with same source
* BUFF_STACK_FULL = does stack with any source or same source
* Note that it refreshes duration if the buff doesn't stack
*/
private static constant integer STACK_TYPE = BUFF_STACK_NONE
/**************************************************
* END OF GLOBAL CONFIGURATION
***************************************************/
/*============================= CORE CODE =============================*/
private effect model
method onRemove takes nothing returns nothing
call DestroyEffect(.model)
set .model = null
endmethod
method onApply takes nothing returns nothing
set .model = AddSpecialEffectTarget(BUFF_MODEL,.target,BUFF_MODEL_ORIGIN)
endmethod
implement BuffApply
endstruct
endlibrary
library LivingBombBuffLibrary /* is using optional custom models
war3mapImported\Living Bomb.mdx
war3mapImported\Living Bomb Fel.mdx
*/uses /*
*/Buff /* https://www.hiveworkshop.com/threads/288640/
*/TimerUtils /* http://www.wc3c.net/showthread.php?t=101322
*/
public struct LivingBombBuff extends Buff
/**************************************************
* GLOBAL CONFIGURATION
***************************************************/
readonly static constant real INTERVAL = 0.05
/*
* Note that when importing, make sure the two objects (<AbilityName> Buff (Creator) and <AbilityName> Buff)
* In Object Editor (OE) have the same rawcode except for their first characters: 'A' & 'B'
* Examples: 'A000' & 'B000' , 'A00A' & 'B00A' , 'A00B' & 'B00B' , 'A005' & 'B005' , 'AXYZ' & 'BXYZ'
* You can customize rawcodes using an Object Merger script or Grimoire extension (JassNewGenPack)
* Import the optional DispelLibrary for dispel feature!
*/
// Living Bomb Ability Buff Creator ID
private static constant integer RAWCODE = 'ALVB'
// The radius of the explosion
private static constant real EXPLOSION_RADIUS = 200.00
// The model of the Living Bomb buff
private static constant string LIVINGBOMB_BUFF_MODEL = "war3mapImported\\Living Bomb.mdx"
private static constant string LIVINGBOMB_BUFF_MODEL_ORIGIN = "origin"
// The model of the Chain Bomb buff
private static constant string CHAINBOMB_BUFF_MODEL = "war3mapImported\\Living Bomb Fel.mdx"
private static constant string CHAINBOMB_BUFF_MODEL_ORIGIN = "origin"
// The model of the Living Bomb explosion
private static constant string LIVINGBOMB_EXPLOSION_MODEL = ""
private static constant string LIVINGBOMB_EXPLOSION_MODEL_ORIGIN = ""
// The model of the Chain Bomb explosion
private static constant string CHAINBOMB_EXPLOSION_MODEL = ""
private static constant string CHAINBOMB_EXPLOSION_MODEL_ORIGIN = ""
// The attack type of inflicting damage
private static constant attacktype ATTACK_TYPE = ATTACK_TYPE_NORMAL
// The damage type of inflicting damage
private static constant damagetype DAMAGE_TYPE = DAMAGE_TYPE_UNIVERSAL
// The weapon type of inflicting damage
private static constant weapontype WEAPON_TYPE = WEAPON_TYPE_WHOKNOWS
// Filtration of the units that will be picked and damaged on explosion
private static method Filtration takes unit target, unit picked, player owner returns boolean
return IsUnitEnemy(picked,owner) and UnitAlive(picked) and picked != target
endmethod
// BUFF_POSITIVE , BUFF_NEGATIVE (set to 0 to not dispel)
private static constant integer DISPEL_TYPE = BUFF_NEGATIVE
/* BUFF_STACK_NONE = does not stack with any source
* BUFF_STACK_PARTIAL = does not stack with same source
* BUFF_STACK_FULL = does stack with any source or same source
* Note that it refreshes duration if the buff doesn't stack
*/
private static constant integer STACK_TYPE = BUFF_STACK_FULL
/**************************************************
* END OF GLOBAL CONFIGURATION
***************************************************/
/*============================= CORE CODE =============================*/
real realDuration
real periodicDamage
real explosionDamage
boolean spread
readonly integer id
readonly boolean polymorphed
private effect model
private timer time
private static group Group = CreateGroup()
method onRemove takes nothing returns nothing
local unit picked
local thistype bomb
call ReleaseTimer(.time)
call DestroyEffect(.model)
if not UnitAlive(.target) and .source != null and .target != null then
if .spread then
call DestroyEffect( AddSpecialEffectTarget(CHAINBOMB_EXPLOSION_MODEL,.target,CHAINBOMB_EXPLOSION_MODEL_ORIGIN) )
else
call DestroyEffect( AddSpecialEffectTarget(LIVINGBOMB_EXPLOSION_MODEL,.target,LIVINGBOMB_EXPLOSION_MODEL_ORIGIN) )
endif
call GroupEnumUnitsInRange(Group,GetUnitX(.target),GetUnitY(.target),EXPLOSION_RADIUS,null)
loop
set picked = FirstOfGroup(Group)
exitwhen picked == null
if Filtration( .target,picked,GetOwningPlayer(.source) ) then
if .spread and Buff.get(.source,picked,thistype.typeid) == 0 then
if .source != null and .target != null then
set bomb = thistype.add(.source,picked)
set bomb.duration = .realDuration
set bomb.realDuration = .realDuration
set bomb.periodicDamage = .periodicDamage
set bomb.explosionDamage = .explosionDamage
set bomb.spread = true
endif
endif
call UnitDamageTarget(.source,picked,.explosionDamage,false,false,ATTACK_TYPE,DAMAGE_TYPE,WEAPON_TYPE)
endif
call GroupRemoveUnit(Group,picked)
endloop
endif
set .time = null
set .model = null
endmethod
private static method onPeriod takes nothing returns nothing
local unit picked
local thistype bomb
local thistype this = GetTimerData(GetExpiredTimer())
if .source == null or .target == null then
call .remove()
endif
if .model == null then
if .spread then
set .model = AddSpecialEffectTarget(CHAINBOMB_BUFF_MODEL,.target,CHAINBOMB_BUFF_MODEL_ORIGIN)
else
set .model = AddSpecialEffectTarget(LIVINGBOMB_BUFF_MODEL,.target,LIVINGBOMB_BUFF_MODEL_ORIGIN)
endif
endif
call UnitDamageTarget(.source,.target,.periodicDamage,false,false,ATTACK_TYPE,DAMAGE_TYPE,WEAPON_TYPE)
if not IsUnitType(.target,UNIT_TYPE_POLYMORPHED) and .polymorphed then
call DestroyEffect(.model)
set .model = null
if .spread then
set .model = AddSpecialEffectTarget(CHAINBOMB_BUFF_MODEL,.target,CHAINBOMB_BUFF_MODEL_ORIGIN)
else
set .model = AddSpecialEffectTarget(LIVINGBOMB_BUFF_MODEL,.target,LIVINGBOMB_BUFF_MODEL_ORIGIN)
endif
set .polymorphed = false
elseif IsUnitType(.target,UNIT_TYPE_POLYMORPHED) and not .polymorphed then
set .polymorphed = true
endif
if GetUnitTypeId(.target) != .id then
call DestroyEffect(.model)
set .model = null
if .spread then
set .model = AddSpecialEffectTarget(CHAINBOMB_BUFF_MODEL,.target,CHAINBOMB_BUFF_MODEL_ORIGIN)
else
set .model = AddSpecialEffectTarget(LIVINGBOMB_BUFF_MODEL,.target,LIVINGBOMB_BUFF_MODEL_ORIGIN)
endif
set .id = GetUnitTypeId(.target)
endif
if .duration <= INTERVAL then
if .spread then
call DestroyEffect( AddSpecialEffectTarget(CHAINBOMB_EXPLOSION_MODEL,.target,CHAINBOMB_EXPLOSION_MODEL_ORIGIN) )
else
call DestroyEffect( AddSpecialEffectTarget(LIVINGBOMB_EXPLOSION_MODEL,.target,LIVINGBOMB_EXPLOSION_MODEL_ORIGIN) )
endif
call GroupEnumUnitsInRange(Group,GetUnitX(.target),GetUnitY(.target),EXPLOSION_RADIUS,null)
loop
set picked = FirstOfGroup(Group)
exitwhen picked == null
if Filtration( .target,picked,GetOwningPlayer(.source) ) then
if .spread and Buff.get(.source,picked,thistype.typeid) == 0 then
set bomb = thistype.add(.source,picked)
set bomb.duration = .realDuration
set bomb.realDuration = .realDuration
set bomb.periodicDamage = .periodicDamage
set bomb.explosionDamage = .explosionDamage
set bomb.spread = true
endif
call UnitDamageTarget(.source,picked,.explosionDamage,false,false,ATTACK_TYPE,DAMAGE_TYPE,WEAPON_TYPE)
endif
call GroupRemoveUnit(Group,picked)
endloop
call UnitDamageTarget(.source,.target,.explosionDamage,false,false,ATTACK_TYPE,DAMAGE_TYPE,WEAPON_TYPE)
endif
endmethod
method onApply takes nothing returns nothing
set .id = GetUnitTypeId(.target)
set .polymorphed = IsUnitType(.target,UNIT_TYPE_POLYMORPHED)
set .time = NewTimerEx(this)
call TimerStart(.time,INTERVAL,true,function thistype.onPeriod)
endmethod
implement BuffApply
endstruct
endlibrary
library FelInfusionConfiguration uses FelInfusion
private struct Configuration extends array
// Fel Infusion Ability ID
private static constant integer ABILITY_ID = 'A00H'
// The model spawned on the caster
private static constant string CASTER_MODEL = "Abilities\\Weapons\\GreenDragonMissile\\GreenDragonMissile.mdl"
private static constant string CASTER_MODEL_ORIGIN = "overhead"
private static constant method duration takes integer level returns real
return 10.00 - (0.00*level) // duration of the ability (set to 0 to permanent)
endmethod
private static constant method lifeCostFlat takes integer level returns real
return 0.00 + (0.00*level) // lifecost of the ability based on flat (set to 0 to disable)
endmethod
private static constant method lifeCostPercentage takes integer level returns real
return 0.10 + (0.00*level) // lifecost of the ability based on percentage (set to 0 to disable)
endmethod
private static constant method manaCost takes integer level returns real
return 0.00 + (0.00*level) // reverted manacost when the ability fails (set to 0 to disable)
endmethod
private static constant integer SPELL_EVENT_TYPE = EVENT_SPELL_EFFECT
implement FelInfusionConfigurationCloner
endstruct
endlibrary
library FelInfusion /*
*/uses /*
*/SpellEvent /* https://www.hiveworkshop.com/threads/301895/
*/SpellCloner /* https://www.hiveworkshop.com/threads/292751/
*/FelInfusionBuffLibrary /* library is used to make this work on Fel Infusion buff
*/
/**************************************************
* GLOBAL CONFIGURATION
***************************************************/
private module Configuration
// Order ID to cancel the ability cooldown (set to 0 to disable)
static constant integer STOP_ID = 851972
// Displays timed text when ability fails (there's already active)
static constant string CASTFAIL_DISPLAY = "There's already active Fel Infusion on the caster."
static constant string CASTFAIL_COLOR = "|cffffcc00"
static constant real CASTFAIL_DURATION = 1.0 // (set to 0 to disable)
// Displays timed text when ability fails (there's not enough life)
static constant string LIFEFAIL_DISPLAY = "Not enough life to cast Fel Infusion on the caster."
// Displays timed text when ability fails (there's no picked unit)
static constant string LIFEFAIL_COLOR = "|cffffcc00"
static constant real LIFEFAIL_DURATION = 1.0 // (set to 0 to disable)
endmodule
/**************************************************
* END OF GLOBAL CONFIGURATION
***************************************************/
/*============================= CORE CODE =============================*/
globals
private sound Sound
endglobals
struct FelInfusion extends array
implement Configuration
static real duration
static real lifeCostFlat
static real lifeCostPercentage
static real manaCost
static string model
static string modelOrigin
private method onClonedSpellStart takes nothing returns thistype
local real mana
local real curLife
local real maxLife
local unit caster = GetEventSpellCaster()
local player owner = GetEventSpellPlayer()
local FelInfusionBuffLibrary_FelInfusionBuff infuse
if Buff.get(caster,caster,FelInfusionBuffLibrary_FelInfusionBuff.typeid) > 0 then
if STOP_ID != 0 then
call IssueImmediateOrderById(caster,STOP_ID)
endif
if manaCost > 0. then
set mana = GetUnitState(caster,UNIT_STATE_MANA)
call SetUnitState(caster,UNIT_STATE_MANA,mana+manaCost)
endif
if CASTFAIL_DURATION > 0. then
if (GetLocalPlayer() == owner) then
call StartSound(Sound)
endif
call DisplayTimedTextToPlayer(owner,0,0,CASTFAIL_DURATION,CASTFAIL_COLOR+CASTFAIL_DISPLAY+"|r")
endif
else
set curLife = GetWidgetLife(caster)
set maxLife = (GetUnitState(caster,UNIT_STATE_MAX_LIFE)*lifeCostPercentage)
if curLife > maxLife + lifeCostFlat then
call DestroyEffect( AddSpecialEffectTarget(model,caster,modelOrigin) )
call SetWidgetLife(caster,curLife-maxLife+lifeCostFlat)
set infuse = FelInfusionBuffLibrary_FelInfusionBuff.add(caster,caster)
if duration > 0. then
set infuse.duration = duration
endif
else
if STOP_ID != 0 then
call IssueImmediateOrderById(caster,STOP_ID)
endif
if manaCost > 0. then
set mana = GetUnitState(caster,UNIT_STATE_MANA)
call SetUnitState(caster,UNIT_STATE_MANA,mana+manaCost)
endif
if LIFEFAIL_DURATION > 0. then
if (GetLocalPlayer() == owner) then
call StartSound(Sound)
endif
call DisplayTimedTextToPlayer(owner,0,0,LIFEFAIL_DURATION,LIFEFAIL_COLOR+LIFEFAIL_DISPLAY+"|r")
endif
endif
endif
return 0
endmethod
private static method onInit takes nothing returns nothing
set Sound = CreateSoundFromLabel("InterfaceError",false,false,false,10,10)
endmethod
/*
* locust methods because of the SpellEvent interface
*/
private static constant real SPELL_PERIOD = 0.0
private method onClonedSpellPeriodic takes nothing returns boolean
return false
endmethod
private method onClonedSpellEnd takes nothing returns nothing
endmethod
implement SpellCloner
endstruct
module FelInfusionConfigurationCloner
private static method configHandler takes nothing returns nothing
// Configuration by constants
set FelInfusion.model = CASTER_MODEL
set FelInfusion.modelOrigin = CASTER_MODEL_ORIGIN
// Configuration with levels
set FelInfusion.duration = duration(GetEventSpellLevel())
set FelInfusion.lifeCostFlat = lifeCostFlat(GetEventSpellLevel())
set FelInfusion.lifeCostPercentage = lifeCostPercentage(GetEventSpellLevel())
set FelInfusion.manaCost = manaCost(GetEventSpellLevel())
endmethod
private static method onInit takes nothing returns nothing
call FelInfusion.create(thistype.typeid,ABILITY_ID,SPELL_EVENT_TYPE,function thistype.configHandler)
endmethod
endmodule
endlibrary
library FlameStrikeConfiguration uses FlameStrike optional FelInfusionBuffLibrary optional LivingBombBuffLibrary
/* is using optional custom models
war3mapImported\Flamestrike Indicator.mdx
war3mapImported\Flamestrike.mdx
war3mapImported\Flamestrike Indicator Fel.mdx
war3mapImported\Flamestrike Fel.mdx
*/
private struct Configuration extends array
// Flame Strike Ability ID
private static constant integer ABILITY_ID = 'A00I'
// The model of the indicator
private static constant string FLAME_INDICATOR_MODEL = "war3mapImported\\Flamestrike Indicator.mdx"
private static constant string FLAME_INDICATOR_MODEL_ORIGIN = "origin"
// The model of the explosion
private static constant string FLAME_STRIKE_MODEL = "war3mapImported\\Flamestrike.mdx"
private static constant string FLAME_STRIKE_MODEL_ORIGIN = "origin"
// The model spawned on picked unit when explosion
private static constant string FLAME_PICKED_MODEL = "Abilities\\Weapons\\FireBallMissile\\FireBallMissile.mdl"
private static constant string FLAME_PICKED_MODEL_ORIGIN = "origin"
// The attack type of inflicting damage
private static constant attacktype FLAME_ATTACK_TYPE = ATTACK_TYPE_NORMAL
// The damage type of inflicting damage
private static constant damagetype FLAME_DAMAGE_TYPE = DAMAGE_TYPE_MAGIC
// The weapon type of inflicting damage
private static constant weapontype FLAME_WEAPON_TYPE = WEAPON_TYPE_WHOKNOWS
private static constant method flameIndicatorSize takes integer level returns real
return 1.50 + (0.00*level) // size of the indicator
endmethod
private static constant method flameDelay takes integer level returns real
return 2.75 - (0.25*level) // delay of the explosion
endmethod
private static constant method flameSize takes integer level returns real
return 1.40 + (0.00*level) // size of the explosion
endmethod
private static constant method flameRadius takes integer level returns real
return 300.00 + (0.00*level) // radius of the explosion
endmethod
private static constant method flameDamage takes integer level returns real
return 0.00 + (100.00*level) // damage of the explosion
endmethod
static if LIBRARY_LivingBombBuffLibrary then
private static constant method flameLivingBomb takes integer level returns boolean
return false // explosion inflicts living bomb on picked unit
endmethod
private static constant method flameChainBomb takes integer level returns boolean
return false // explosion inflicts chain bomb on picked unit
endmethod
private static constant method flameBombDuration takes integer level returns real
return 0.00 - (0.00*level) // duration of the inflicted bomb on picked unit
endmethod
private static constant method flameBombPeriodicDamage takes integer level returns real
return 0.00 + (0.00*level) // periodic damage of the inflicted bomb on picked unit
endmethod
private static constant method flameBombExplosionDamage takes integer level returns real
return 0.00 + (0.00*level) // explosion damage of the inflicted bomb on picked unit
endmethod
endif
static if LIBRARY_FelInfusionBuffLibrary then
// Make Flame Strike empowered by Fel Infusion?
private static constant boolean EMPOWERMENT = true
// The model of the indicator
private static constant string FEL_INDICATOR_MODEL = "war3mapImported\\Flamestrike Indicator Fel.mdx"
private static constant string FEL_INDICATOR_MODEL_ORIGIN = "origin"
// The model of the explosion
private static constant string FEL_STRIKE_MODEL = "war3mapImported\\Flamestrike Fel.mdx"
private static constant string FEL_STRIKE_MODEL_ORIGIN = "origin"
// The model spawned on picked unit when explosion
private static constant string FEL_PICKED_MODEL = "Abilities\\Weapons\\DemonHunterMissile\\DemonHunterMissile.mdl"
private static constant string FEL_PICKED_MODEL_ORIGIN = "origin"
// The attack type of inflicting damage
private static constant attacktype FEL_ATTACK_TYPE = ATTACK_TYPE_CHAOS
// The damage type of inflicting damage
private static constant damagetype FEL_DAMAGE_TYPE = DAMAGE_TYPE_UNIVERSAL
// The weapon type of inflicting damage
private static constant weapontype FEL_WEAPON_TYPE = WEAPON_TYPE_WHOKNOWS
private static constant method felIndicatorSize takes integer level returns real
return 1.50 + (0.00*level) // size of the indicator
endmethod
private static constant method felDelay takes integer level returns real
return 2.75 - (0.25*level) // delay of the explosion
endmethod
private static constant method felSize takes integer level returns real
return 1.40 + (0.00*level) // size of the explosion
endmethod
private static constant method felRadius takes integer level returns real
return 300.00 + (0.00*level) // radius of the explosion
endmethod
private static constant method felDamage takes integer level returns real
return 0.00 + (100.00*level) // damage of the explosion
endmethod
static if LIBRARY_LivingBombBuffLibrary then
private static constant method felLivingBomb takes integer level returns boolean
return true // explosion inflicts living bomb on picked unit
endmethod
private static constant method felChainBomb takes integer level returns boolean
return false // explosion inflicts chain bomb on picked unit
endmethod
private static constant method felBombDuration takes integer level returns real
return 8.00 - (1.00*level) // duration of the inflicted bomb on picked unit
endmethod
private static constant method felBombPeriodicDamage takes integer level returns real
return 0.00 + (1.00*level) // periodic damage of the inflicted bomb on picked unit
endmethod
private static constant method felBombExplosionDamage takes integer level returns real
return 50.00 + (50.00*level) // explosion damage of the inflicted bomb on picked unit
endmethod
endif
endif
private static constant integer SPELL_EVENT_TYPE = EVENT_SPELL_EFFECT
implement FlameStrikeConfigurationCloner
endstruct
endlibrary
library FlameStrike /*
*/uses /*
*/SpellEvent /* https://www.hiveworkshop.com/threads/301895/
*/SpellCloner /* https://www.hiveworkshop.com/threads/292751/
*/DummyRecycler /* https://www.hiveworkshop.com/threads/277659/
*/optional IsDestructableTree /* https://www.hiveworkshop.com/threads/248054/
*/optional FelInfusionBuffLibrary /* library is used to make this work on Fel Infusion buff
*/optional LivingBombBuffLibrary /* library is used to make this work on Living Bomb buff
*/
/**************************************************
* GLOBAL CONFIGURATION
***************************************************/
globals
private constant real PERIODIC_INTERVAL = 0.05
private constant real MODEL_DEATH_TIME = 2.5
// A flag to enable/disable destroy trees on explosion
private constant boolean DESTROY_TREES = true
// ONLY configure if IsDestructableTree library is present in the map
endglobals
// Filtration of the units that will be damaged on Flame Strike
private function FlameFiltration takes unit picked, player owner returns boolean
return IsUnitEnemy(picked,owner) and /*
*/ not IsUnitType(picked,UNIT_TYPE_MAGIC_IMMUNE) and /*
*/ UnitAlive(picked)
endfunction
static if LIBRARY_FelInfusionBuffLibrary then
// Filtration of the units that will be damaged on Fel Strike
private function FelFiltration takes unit picked, player owner returns boolean
return IsUnitEnemy(picked,owner) and UnitAlive(picked)
endfunction
endif
/**************************************************
* END OF GLOBAL CONFIGURATION
***************************************************/
/*============================= CORE CODE =============================*/
native UnitAlive takes unit id returns boolean
globals
private group Group = CreateGroup()
endglobals
struct FlameStrike extends array
real flameIndicatorSize
string flameIndicatorModel
string flameIndicatorModelOrigin
real flameDelay
real flameSize
real flameRadius
real flameDamage
string flameStrikeModel
string flameStrikeModelOrigin
string flamePickedModel
string flamePickedModelOrigin
attacktype flameAttackType
damagetype flameDamageType
weapontype flameWeaponType
static if LIBRARY_LivingBombBuffLibrary then
boolean flameLivingBomb
boolean flameChainBomb
real flameBombDuration
real flameBombPeriodicDamage
real flameBombExplosionDamage
endif
static if LIBRARY_FelInfusionBuffLibrary then
boolean empowerment
real felIndicatorSize
string felIndicatorModel
string felIndicatorModelOrigin
real felDelay
real felSize
real felRadius
real felDamage
string felStrikeModel
string felStrikeModelOrigin
string felPickedModel
string felPickedModelOrigin
attacktype felAttackType
damagetype felDamageType
weapontype felWeaponType
static if LIBRARY_LivingBombBuffLibrary then
boolean felLivingBomb
boolean felChainBomb
real felBombDuration
real felBombPeriodicDamage
real felBombExplosionDamage
endif
readonly boolean empowered
readonly boolean filtration
endif
readonly real x
readonly real y
readonly real delay
readonly real size
readonly real radius
readonly real damage
readonly string strikeModel
readonly string strikeModelOrigin
readonly string pickedModel
readonly string pickedModelOrigin
readonly unit caster
readonly player owner
readonly attacktype attackType
readonly damagetype damageType
readonly weapontype weaponType
static if LIBRARY_LivingBombBuffLibrary then
readonly boolean livingBomb
readonly boolean chainBomb
readonly real duration
readonly real periodicDamage
readonly real explosionDamage
endif
private unit indicator
private effect indicatorModel
static if LIBRARY_IsDestructableTree and DESTROY_TREES then
private static real X
private static real Y
private static real Radius
private static method destroyTree takes nothing returns nothing
local destructable des = GetEnumDestructable()
local real x = GetWidgetX(des) - X
local real y = GetWidgetY(des) - Y
if x*x + y*y <= Radius then
call KillTree(des)
endif
set des = null
endmethod
endif
readonly static constant real SPELL_PERIOD = PERIODIC_INTERVAL
private method onClonedSpellStart takes nothing returns thistype
static if LIBRARY_FelInfusionBuffLibrary then
local FelInfusionBuffLibrary_FelInfusionBuff infuse = Buff.get(GetEventSpellCaster(),GetEventSpellCaster(),FelInfusionBuffLibrary_FelInfusionBuff.typeid)
endif
set .caster = GetEventSpellCaster()
set .owner = GetEventSpellPlayer()
set .x = GetEventSpellTargetX()
set .y = GetEventSpellTargetY()
set .indicator = GetRecycledDummyAnyAngle(.x,.y,0)
static if LIBRARY_FelInfusionBuffLibrary then
if infuse > 0 and .empowerment then
set .empowered = true
call infuse.remove()
set .delay = .felDelay
set .size = .felSize
set .radius = .felRadius
set .damage = .felDamage
set .strikeModel = .felStrikeModel
set .strikeModelOrigin = .felStrikeModelOrigin
set .pickedModel = .felPickedModel
set .pickedModelOrigin = .felPickedModelOrigin
set .attackType = .felAttackType
set .damageType = .felDamageType
set .weaponType = .felWeaponType
static if LIBRARY_LivingBombBuffLibrary then
set .livingBomb = .felLivingBomb
set .chainBomb = .felChainBomb
set .duration = .felBombDuration
set .periodicDamage = .felBombPeriodicDamage
set .explosionDamage = .felBombExplosionDamage
endif
call SetUnitScale(.indicator,.felIndicatorSize,0,0)
set .indicatorModel = AddSpecialEffectTarget(.felIndicatorModel,.indicator,.felIndicatorModelOrigin)
else
set .empowered = false
set .delay = .flameDelay
set .size = .flameSize
set .radius = .flameRadius
set .damage = .flameDamage
set .strikeModel = .flameStrikeModel
set .strikeModelOrigin = .flameStrikeModelOrigin
set .pickedModel = .flamePickedModel
set .pickedModelOrigin = .flamePickedModelOrigin
set .attackType = .flameAttackType
set .damageType = .flameDamageType
set .weaponType = .flameWeaponType
static if LIBRARY_LivingBombBuffLibrary then
set .livingBomb = .flameLivingBomb
set .chainBomb = .flameChainBomb
set .duration = .flameBombDuration
set .periodicDamage = .flameBombPeriodicDamage
set .explosionDamage = .flameBombExplosionDamage
endif
call SetUnitScale(.indicator,.flameIndicatorSize,0,0)
set .indicatorModel = AddSpecialEffectTarget(.flameIndicatorModel,.indicator,.flameIndicatorModelOrigin)
endif
else
set .delay = .flameDelay
set .size = .flameSize
set .radius = .flameRadius
set .damage = .flameDamage
set .strikeModel = .flameStrikeModel
set .strikeModelOrigin = .flameStrikeModelOrigin
set .pickedModel = .flamePickedModel
set .pickedModelOrigin = .flamePickedModelOrigin
set .attackType = .flameAttackType
set .damageType = .flameDamageType
set .weaponType = .flameWeaponType
static if LIBRARY_LivingBombBuffLibrary then
set .livingBomb = .flameLivingBomb
set .chainBomb = .flameChainBomb
set .duration = .flameBombDuration
set .periodicDamage = .flameBombPeriodicDamage
set .explosionDamage = .flameBombExplosionDamage
endif
call SetUnitScale(.indicator,.flameIndicatorSize,0,0)
set .indicatorModel = AddSpecialEffectTarget(.flameIndicatorModel,.indicator,.flameIndicatorModelOrigin)
endif
return this
endmethod
private method onClonedSpellPeriodic takes nothing returns boolean
set .delay = .delay - PERIODIC_INTERVAL
return .delay > 0.
endmethod
private method onClonedSpellEnd takes nothing returns nothing
static if LIBRARY_LivingBombBuffLibrary then
local LivingBombBuffLibrary_LivingBombBuff bomb
endif
local unit picked
local unit strike = GetRecycledDummyAnyAngle(.x,.y,0)
call SetUnitScale(strike,.size,0,0)
call DestroyEffect( AddSpecialEffectTarget(.strikeModel,strike,.strikeModelOrigin) )
call DummyAddRecycleTimer(strike,MODEL_DEATH_TIME)
call DestroyEffect(.indicatorModel)
call DummyAddRecycleTimer(.indicator,MODEL_DEATH_TIME)
set strike = null
call GroupEnumUnitsInRange(Group,.x,.y,.radius,null)
loop
set picked = FirstOfGroup(Group)
exitwhen picked == null
static if LIBRARY_FelInfusionBuffLibrary then
if .empowered then
set .filtration = FelFiltration(picked,.owner)
else
set .filtration = FlameFiltration(picked,.owner)
endif
if .filtration then
call DestroyEffect( AddSpecialEffectTarget(.pickedModel,picked,.pickedModelOrigin) )
call UnitDamageTarget(.caster,picked,.damage,false,false,.attackType,.damageType,.weaponType)
static if LIBRARY_LivingBombBuffLibrary then
if UnitAlive(picked) then
if .livingBomb or .chainBomb then
set bomb = Buff.get(.caster,picked,LivingBombBuffLibrary_LivingBombBuff.typeid)
if bomb > 0 then
set bomb.duration = LivingBombBuffLibrary_LivingBombBuff.INTERVAL
endif
if UnitAlive(picked) then
if .livingBomb then
set bomb = LivingBombBuffLibrary_LivingBombBuff.add(.caster,picked)
set bomb.duration = .duration
set bomb.realDuration = .duration
set bomb.periodicDamage = .periodicDamage
set bomb.explosionDamage = .explosionDamage
set bomb.spread = false
endif
if .chainBomb then
set bomb = LivingBombBuffLibrary_LivingBombBuff.add(.caster,picked)
set bomb.duration = .duration
set bomb.realDuration = .duration
set bomb.periodicDamage = .periodicDamage
set bomb.explosionDamage = .explosionDamage
set bomb.spread = true
endif
endif
endif
endif
endif
endif
else
if FlameFiltration(picked,.owner) then
call DestroyEffect( AddSpecialEffectTarget(.pickedModel,picked,.pickedModelOrigin) )
call UnitDamageTarget(.caster,picked,.damage,false,false,.attackType,.damageType,.weaponType)
static if LIBRARY_LivingBombBuffLibrary then
if UnitAlive(picked) then
if .livingBomb or .chainBomb then
set bomb = Buff.get(.caster,picked,LivingBombBuffLibrary_LivingBombBuff.typeid)
if bomb > 0 then
set bomb.duration = LivingBombBuffLibrary_LivingBombBuff.INTERVAL
endif
if UnitAlive(picked) then
if .livingBomb then
set bomb = LivingBombBuffLibrary_LivingBombBuff.add(.caster,picked)
set bomb.duration = .duration
set bomb.realDuration = .duration
set bomb.periodicDamage = .periodicDamage
set bomb.explosionDamage = .explosionDamage
set bomb.spread = false
endif
if .chainBomb then
set bomb = LivingBombBuffLibrary_LivingBombBuff.add(.caster,picked)
set bomb.duration = .duration
set bomb.realDuration = .duration
set bomb.periodicDamage = .periodicDamage
set bomb.explosionDamage = .explosionDamage
set bomb.spread = true
endif
endif
endif
endif
endif
endif
endif
call GroupRemoveUnit(Group,picked)
endloop
static if LIBRARY_IsDestructableTree and DESTROY_TREES then
set X = .x
set Y = .y
set Radius = .radius*.radius
call SetRect(IsDestructableTree_Region,X - .radius,Y - .radius,X + .radius,Y + .radius)
call EnumDestructablesInRect(IsDestructableTree_Region,null,function thistype.destroyTree)
endif
set .indicator = null
set .indicatorModel = null
set .caster = null
endmethod
implement SpellCloner
endstruct
module FlameStrikeConfigurationCloner
private static method configHandler takes nothing returns nothing
local FlameStrike clone = SpellCloner.configuredInstance
// Configuration by constants
set clone.flameIndicatorModel = FLAME_INDICATOR_MODEL
set clone.flameIndicatorModelOrigin = FLAME_INDICATOR_MODEL_ORIGIN
set clone.flameStrikeModel = FLAME_STRIKE_MODEL
set clone.flameStrikeModelOrigin = FLAME_STRIKE_MODEL_ORIGIN
set clone.flamePickedModel = FLAME_PICKED_MODEL
set clone.flamePickedModelOrigin = FLAME_PICKED_MODEL_ORIGIN
set clone.flameAttackType = FLAME_ATTACK_TYPE
set clone.flameDamageType = FLAME_DAMAGE_TYPE
set clone.flameWeaponType = FLAME_WEAPON_TYPE
// Configuration with levels
set clone.flameIndicatorSize = flameIndicatorSize(GetEventSpellLevel())
set clone.flameDelay = flameDelay(GetEventSpellLevel())
set clone.flameSize = flameSize(GetEventSpellLevel())
set clone.flameRadius = flameRadius(GetEventSpellLevel())
set clone.flameDamage = flameDamage(GetEventSpellLevel())
static if LIBRARY_LivingBombBuffLibrary then
set clone.flameLivingBomb = flameLivingBomb(GetEventSpellLevel())
set clone.flameChainBomb = flameChainBomb(GetEventSpellLevel())
set clone.flameBombDuration = flameBombDuration(GetEventSpellLevel())
set clone.flameBombPeriodicDamage = flameBombPeriodicDamage(GetEventSpellLevel())
set clone.flameBombExplosionDamage = flameBombExplosionDamage(GetEventSpellLevel())
endif
static if LIBRARY_FelInfusionBuffLibrary then
set clone.empowerment = EMPOWERMENT
// Configuration by constants
set clone.felIndicatorModel = FEL_INDICATOR_MODEL
set clone.felIndicatorModelOrigin = FEL_INDICATOR_MODEL_ORIGIN
set clone.felStrikeModel = FEL_STRIKE_MODEL
set clone.felStrikeModelOrigin = FEL_STRIKE_MODEL_ORIGIN
set clone.felPickedModel = FEL_PICKED_MODEL
set clone.felPickedModelOrigin = FEL_PICKED_MODEL_ORIGIN
set clone.felAttackType = FEL_ATTACK_TYPE
set clone.felDamageType = FEL_DAMAGE_TYPE
set clone.felWeaponType = FEL_WEAPON_TYPE
// Configuration with levels
set clone.felIndicatorSize = felIndicatorSize(GetEventSpellLevel())
set clone.felDelay = felDelay(GetEventSpellLevel())
set clone.felSize = felSize(GetEventSpellLevel())
set clone.felRadius = felRadius(GetEventSpellLevel())
set clone.felDamage = felDamage(GetEventSpellLevel())
static if LIBRARY_LivingBombBuffLibrary then
set clone.felLivingBomb = felLivingBomb(GetEventSpellLevel())
set clone.felChainBomb = felChainBomb(GetEventSpellLevel())
set clone.felBombDuration = felBombDuration(GetEventSpellLevel())
set clone.felBombPeriodicDamage = felBombPeriodicDamage(GetEventSpellLevel())
set clone.felBombExplosionDamage = felBombExplosionDamage(GetEventSpellLevel())
endif
endif
endmethod
private static method onInit takes nothing returns nothing
call FlameStrike.create(thistype.typeid,ABILITY_ID,SPELL_EVENT_TYPE,function thistype.configHandler)
endmethod
endmodule
endlibrary
library LivingBombConfiguration uses LivingBomb optional FelInfusionBuffLibrary
private struct Configuration extends array
// Living Bomb Ability ID
private static constant integer ABILITY_ID = 'A003'
private static constant method explode takes integer level returns boolean
return true // explodes existing bomb on target unit
endmethod
private static constant method flameLivingBomb takes integer level returns boolean
return true // inflicts living bomb on target unit
endmethod
private static constant method flameChainBomb takes integer level returns boolean
return false // inflicts chain bomb on target unit
endmethod
private static constant method flameBombDuration takes integer level returns real
return 8.00 - (1.00*level) // duration of the inflicted bomb on target unit
endmethod
private static constant method flameBombPeriodicDamage takes integer level returns real
return 0.00 + (1.00*level) // periodic damage of the inflicted bomb on target unit
endmethod
private static constant method flameBombExplosionDamage takes integer level returns real
return 50.00 + (50.00*level) // explosion damage of the inflicted bomb on target unit
endmethod
static if LIBRARY_FelInfusionBuffLibrary then
// Make Living Bomb empowered by Fel Infusion?
private static constant boolean EMPOWERMENT = true
private static constant method felLivingBomb takes integer level returns boolean
return false // inflicts living bomb on target unit
endmethod
private static constant method felChainBomb takes integer level returns boolean
return true // inflicts chain bomb on target unit
endmethod
private static constant method felBombDuration takes integer level returns real
return 8.00 - (1.00*level) // duration of the inflicted bomb on target unit
endmethod
private static constant method felBombPeriodicDamage takes integer level returns real
return 0.00 + (1.00*level) // periodic damage of the inflicted bomb on target unit
endmethod
private static constant method felBombExplosionDamage takes integer level returns real
return 25.00 + (25.00*level) // explosion damage of the inflicted bomb on target unit
endmethod
endif
private static constant integer SPELL_EVENT_TYPE = EVENT_SPELL_EFFECT
implement LivingBombConfigurationCloner
endstruct
endlibrary
library LivingBomb /*
*/uses /*
*/SpellEvent /* https://www.hiveworkshop.com/threads/301895/
*/SpellCloner /* https://www.hiveworkshop.com/threads/292751/
*/LivingBombBuffLibrary /* library is used to make this work on Living Bomb buff
*/optional FelInfusionBuffLibrary /* library is used to make this work on Fel Infusion buff
*/
/**************************************************
* GLOBAL CONFIGURATION
***************************************************/
// There's no global configuration available for it
/**************************************************
* END OF GLOBAL CONFIGURATION
***************************************************/
/*============================= CORE CODE =============================*/
native UnitAlive takes unit id returns boolean
struct LivingBomb extends array
static boolean flameLivingBomb
static boolean flameChainBomb
static real flameBombDuration
static real flameBombPeriodicDamage
static real flameBombExplosionDamage
static if LIBRARY_FelInfusionBuffLibrary then
static boolean empowerment
static boolean felLivingBomb
static boolean felChainBomb
static real felBombDuration
static real felBombPeriodicDamage
static real felBombExplosionDamage
endif
static boolean explode
private method onClonedSpellStart takes nothing returns thistype
local unit caster = GetEventSpellCaster()
local unit target = GetEventSpellTargetUnit()
static if LIBRARY_FelInfusionBuffLibrary then
local FelInfusionBuffLibrary_FelInfusionBuff infuse = Buff.get(caster,caster,FelInfusionBuffLibrary_FelInfusionBuff.typeid)
endif
local LivingBombBuffLibrary_LivingBombBuff bomb
set bomb = Buff.get(caster,target,LivingBombBuffLibrary_LivingBombBuff.typeid)
if bomb > 0 then
if explode then
set bomb.duration = LivingBombBuffLibrary_LivingBombBuff.INTERVAL
endif
endif
if UnitAlive(target) then
static if LIBRARY_FelInfusionBuffLibrary then
if infuse > 0 and empowerment then
call infuse.remove()
if felLivingBomb then
set bomb = LivingBombBuffLibrary_LivingBombBuff.add(caster,target)
set bomb.duration = felBombDuration
set bomb.realDuration = felBombDuration
set bomb.periodicDamage = felBombPeriodicDamage
set bomb.explosionDamage = felBombExplosionDamage
set bomb.spread = false
endif
if felChainBomb then
set bomb = LivingBombBuffLibrary_LivingBombBuff.add(caster,target)
set bomb.duration = felBombDuration
set bomb.realDuration = felBombDuration
set bomb.periodicDamage = felBombPeriodicDamage
set bomb.explosionDamage = felBombExplosionDamage
set bomb.spread = true
endif
else
if flameLivingBomb then
set bomb = LivingBombBuffLibrary_LivingBombBuff.add(caster,target)
set bomb.duration = flameBombDuration
set bomb.realDuration = flameBombDuration
set bomb.periodicDamage = flameBombPeriodicDamage
set bomb.explosionDamage = flameBombExplosionDamage
set bomb.spread = false
endif
if flameChainBomb then
set bomb = LivingBombBuffLibrary_LivingBombBuff.add(caster,target)
set bomb.duration = flameBombDuration
set bomb.realDuration = flameBombDuration
set bomb.periodicDamage = flameBombPeriodicDamage
set bomb.explosionDamage = flameBombExplosionDamage
set bomb.spread = true
endif
endif
else
if flameLivingBomb then
set bomb = LivingBombBuffLibrary_LivingBombBuff.add(caster,target)
set bomb.duration = flameBombDuration
set bomb.realDuration = flameBombDuration
set bomb.periodicDamage = flameBombPeriodicDamage
set bomb.explosionDamage = flameBombExplosionDamage
set bomb.spread = false
endif
if flameChainBomb then
set bomb = LivingBombBuffLibrary_LivingBombBuff.add(caster,target)
set bomb.duration = flameBombDuration
set bomb.realDuration = flameBombDuration
set bomb.periodicDamage = flameBombPeriodicDamage
set bomb.explosionDamage = flameBombExplosionDamage
set bomb.spread = true
endif
endif
endif
return 0
endmethod
/*
* locust methods because of the SpellEvent interface
*/
private static constant real SPELL_PERIOD = 0.0
private method onClonedSpellPeriodic takes nothing returns boolean
return false
endmethod
private method onClonedSpellEnd takes nothing returns nothing
endmethod
implement SpellCloner
endstruct
module LivingBombConfigurationCloner
private static method configHandler takes nothing returns nothing
// Configuration by constants
set LivingBomb.explode = explode(GetEventSpellLevel())
// Configuration with levels
set LivingBomb.flameLivingBomb = flameLivingBomb(GetEventSpellLevel())
set LivingBomb.flameChainBomb = flameChainBomb(GetEventSpellLevel())
set LivingBomb.flameBombDuration = flameBombDuration(GetEventSpellLevel())
set LivingBomb.flameBombPeriodicDamage = flameBombPeriodicDamage(GetEventSpellLevel())
set LivingBomb.flameBombExplosionDamage = flameBombExplosionDamage(GetEventSpellLevel())
static if LIBRARY_FelInfusionBuffLibrary then
// Configuration by constants
set LivingBomb.empowerment = EMPOWERMENT
// Configuration with levels
set LivingBomb.felLivingBomb = felLivingBomb(GetEventSpellLevel())
set LivingBomb.felChainBomb = felChainBomb(GetEventSpellLevel())
set LivingBomb.felBombDuration = felBombDuration(GetEventSpellLevel())
set LivingBomb.felBombPeriodicDamage = felBombPeriodicDamage(GetEventSpellLevel())
set LivingBomb.felBombExplosionDamage = felBombExplosionDamage(GetEventSpellLevel())
endif
endmethod
private static method onInit takes nothing returns nothing
call LivingBomb.create(thistype.typeid,ABILITY_ID,SPELL_EVENT_TYPE,function thistype.configHandler)
endmethod
endmodule
endlibrary
library GravityLapseConfiguration uses GravityLapse optional FelInfusionBuffLibrary
/* is using optional custom models
war3mapImported\\GravityLapseSummoning Circle.mdl
war3mapImported\\NetherBanishSummoning Circle.mdl
*/
private struct Configuration extends array
// Gravity Lapse Ability ID
private static constant integer ABILITY_ID = 'A002'
// Caster Ability ID casted on picked unit (set to 0 to disable)
private static constant integer GRAVITY_CASTER_ABILITY_ID = 0
// Buff ID created by Caster Ability ID
private static constant integer GRAVITY_CASTER_BUFF_ID = 0
// Order ID of Caster Ability ID
private static constant integer GRAVITY_CASTER_ORDER_ID = 0
// The model of the indicator
private static constant string GRAVITY_MODEL = "war3mapImported\\GravityLapseSummoning Circle.mdl"
private static constant string GRAVITY_MODEL_ORIGIN = "origin"
// The model spawned on picked unit
private static constant string GRAVITY_PICKED_MODEL = "Abilities\\Spells\\NightElf\\Blink\\BlinkCaster.mdl"
// The model spawned on picked unit when levitated
private static constant string GRAVITY_LEVITATED_MODEL = "Abilities\\Spells\\Human\\ManaFlare\\ManaFlareTarget.mdl"
private static constant string GRAVITY_LEVITATED_MODEL_ORIGIN = "origin"
// The model spawned on picked unit when landed
private static constant string GRAVITY_LANDED_MODEL = "Abilities\\Weapons\\AncientProtectorMissile\\AncientProtectorMissile.mdl"
private static constant string GRAVITY_LANDED_MODEL_ORIGIN = "origin"
// The attack type of inflicting damage
private static constant attacktype GRAVITY_ATTACK_TYPE = ATTACK_TYPE_NORMAL
// The damage type of inflicting damage
private static constant damagetype GRAVITY_DAMAGE_TYPE = DAMAGE_TYPE_MAGIC
// The weapon type of inflicting damage
private static constant weapontype GRAVITY_WEAPON_TYPE = WEAPON_TYPE_WHOKNOWS
private static constant method gravitySize takes integer level returns real
return 1.25 + (0.25*level) // size of the indicator
endmethod
private static constant method gravityHeight takes integer level returns real
return 300.00 + (0.00*level) // height to be reached of picked unit to disable its movement
endmethod
private static constant method gravityDelay takes integer level returns real
return 1.50 + (0.00*level) // delay to be reached of picked unit to disable its movement
endmethod
private static constant method gravityLevitateHeight takes integer level returns real
return -100.00 - (0.00*level) // certain height from the reached height where to levitate up and down.
endmethod
private static constant method gravityLevitateDuration takes integer level returns real
return 1.00 + (0.00*level) // duration of levitating from the reached height to the certain height.
endmethod
private static constant method gravityDuration takes integer level returns real
return 2.00 + (1.00*level) // duration of the ability
endmethod
private static constant method gravityRadius takes integer level returns real
return 175.00 + (50.00*level) // radius of the ability
endmethod
private static constant method gravityLandSpeed takes integer level returns real
return 550.00 + (0.00*level) // landing speed of picked unit after duration ends
endmethod
private static constant method gravityExposure takes integer level returns real
return 150.00 + (0.00*level) // required height of unit on air for eligible damage
endmethod
private static constant method gravityDamage takes integer level returns real
return 50.00 + (0.00*level) // inflicting damage on picked unit when landed
endmethod
static if LIBRARY_FelInfusionBuffLibrary then
// Make Gravity Lapse empowered by Fel Infusion?
private static constant boolean EMPOWERMENT = true
// Caster Ability ID casted on picked unit (set to 0 to disable)
private static constant integer NETHER_CASTER_ABILITY_ID = 'A000'
// Buff ID created by Caster Ability ID
private static constant integer NETHER_CASTER_BUFF_ID = 'B001'
// Order ID of Caster Ability ID
private static constant integer NETHER_CASTER_ORDER_ID = 852486
// The model of the indicator
private static constant string NETHER_MODEL = "war3mapImported\\NetherBanishSummoning Circle.mdl"
private static constant string NETHER_MODEL_ORIGIN = "origin"
// The model spawned on picked unit
private static constant string NETHER_PICKED_MODEL = "Abilities\\Weapons\\GreenDragonMissile\\GreenDragonMissile.mdl"
// The model spawned on picked unit when levitated
private static constant string NETHER_LEVITATED_MODEL = "Abilities\\Spells\\Human\\ManaFlare\\ManaFlareBase.mdl"
private static constant string NETHER_LEVITATED_MODEL_ORIGIN = "origin"
// The model spawned on picked unit when landed
private static constant string NETHER_LANDED_MODEL = "Abilities\\Weapons\\AncientProtectorMissile\\AncientProtectorMissile.mdl"
private static constant string NETHER_LANDED_MODEL_ORIGIN = "origin"
// The attack type of inflicting damage
private static constant attacktype NETHER_ATTACK_TYPE = ATTACK_TYPE_NORMAL
// The damage type of inflicting damage
private static constant damagetype NETHER_DAMAGE_TYPE = DAMAGE_TYPE_MAGIC
// The weapon type of inflicting damage
private static constant weapontype NETHER_WEAPON_TYPE = WEAPON_TYPE_WHOKNOWS
private static constant method netherSize takes integer level returns real
return 1.25 + (0.25*level) // size of the indicator
endmethod
private static constant method netherHeight takes integer level returns real
return 300.00 + (0.00*level) // height to be reached of picked unit to disable its movement
endmethod
private static constant method netherDelay takes integer level returns real
return 1.50 + (0.00*level) // delay to be reached of picked unit to disable its movement
endmethod
private static constant method netherLevitateHeight takes integer level returns real
return -100.00 - (0.00*level) // certain height from the reached height where to levitate up and down.
endmethod
private static constant method netherLevitateDuration takes integer level returns real
return 1.00 + (0.00*level) // duration of levitating from the reached height to the certain height.
endmethod
private static constant method netherDuration takes integer level returns real
return 5.00 + (1.00*level) // duration of the ability
endmethod
private static constant method netherRadius takes integer level returns real
return 175.00 + (50.00*level) // radius of the ability
endmethod
private static constant method netherLandSpeed takes integer level returns real
return 550.00 + (0.00*level) // landing speed of picked unit after duration ends
endmethod
private static constant method netherExposure takes integer level returns real
return 150.00 + (0.00*level) // required height of unit on air for eligible damage
endmethod
private static constant method netherDamage takes integer level returns real
return 50.00 + (0.00*level) // inflicting damage on picked unit when landed
endmethod
endif
private static constant integer SPELL_EVENT_TYPE = EVENT_SPELL_EFFECT
implement GravityLapseConfigurationCloner
endstruct
endlibrary
library GravityLapse /*
*/uses /*
*/Alloc /* library is used to make this work on instances efficiently
*/LinkedList /* library is used to make this work on a list data structure
*/SpellEvent /* https://www.hiveworkshop.com/threads/301895/
*/SpellCloner /* https://www.hiveworkshop.com/threads/292751/
*/DummyRecycler /* https://www.hiveworkshop.com/threads/277659/
*/AutoFly /* https://www.hiveworkshop.com/threads/249422/
*/RiseAndFall /* https://www.hiveworkshop.com/threads/306703/
*/optional FelInfusionBuffLibrary /* library is used to make this work on Fel Infusion buff
*/
/**************************************************
* GLOBAL CONFIGURATION
***************************************************/
globals
private constant real PERIODIC_INTERVAL = 0.05
private constant real MODEL_DEATH_TIME = 5.0
private constant boolean MODEL_DEATH_SHOW = false
// A fail safe time in case the unit is stucked on air to ensure 100% safety
private constant real LANDING_FAILSAFE = 1.0
// A flag to damage flying unit when landed for logical and realistic purpose
private constant boolean DAMAGE_FLYING = false
// Caster Unit ID
private constant integer CASTER_UNIT_ID = 'dumi' // Based on 'DummyRecycler Library'
// Disarm Picked Unit
// Disarm (Gravity Lapse) Ability ID
private constant integer DISARM_ID = 'A005' // Based on 'Cargo Hold Ability'
private constant boolean DISARM_MELEE = true
private constant boolean DISARM_RANGED = false
// Walkable Picked Unit
// Ghost (Visible) Ability ID
private constant integer GHOST_ID = 'Aeth'
endglobals
// Filtration of the units that will be levitated on Gravity Lapse
private function GravityFiltration takes unit picked, player owner returns boolean
return IsUnitEnemy(picked,owner) and /*
*/ not IsUnitType(picked,UNIT_TYPE_STRUCTURE) and /*
*/ not IsUnitType(picked,UNIT_TYPE_MAGIC_IMMUNE) and /*
*/ UnitAlive(picked)
endfunction
static if LIBRARY_FelInfusionBuffLibrary then
// Filtration of the units that will be levitated on Nether Banish
private function NetherFiltration takes unit picked, player owner returns boolean
return IsUnitEnemy(picked,owner) and /*
*/ not IsUnitType(picked,UNIT_TYPE_STRUCTURE) and /*
*/ not IsUnitType(picked,UNIT_TYPE_MAGIC_IMMUNE) and /*
*/ UnitAlive(picked)
endfunction
endif
/**************************************************
* END OF GLOBAL CONFIGURATION
***************************************************/
/*============================= CORE CODE =============================*/
native UnitAlive takes unit id returns boolean
globals
private group Group = CreateGroup()
endglobals
private struct List extends array
implement Alloc
real time
unit picked
effect model
boolean eligible
private static method onRemove takes thistype node returns nothing
local real height = GetUnitFlyHeight(node.picked)
if RiseAndFall[node.picked].isUnitAirborne(node.picked) then
call RiseAndFall[node.picked].end(height,1.0)
endif
call SetUnitFlyHeight(node.picked,0.00010+GetUnitDefaultFlyHeight(node.picked),0)
call node.deallocate()
set node.eligible = false
if node.model != null then
call DestroyEffect(node.model)
set node.model = null
endif
set node.picked = null
set node.time = 0.
endmethod
implement InstantiatedListLite
endstruct
struct GravityLapse extends array
real gravitySize
real gravityHeight
real gravityDelay
real gravityLevitateHeight
real gravityLevitateDuration
real gravityDuration
real gravityRadius
real gravityLandSpeed
real gravityExposure
real gravityDamage
integer gravityAbilityId
integer gravityBuffId
integer gravityOrderId
string gravityModel
string gravityModelOrigin
string gravityPickedModel
string gravityLevitatedModel
string gravityLevitatedModelOrigin
string gravityLandedModel
string gravityLandedModelOrigin
attacktype gravityAttackType
damagetype gravityDamageType
weapontype gravityWeaponType
static if LIBRARY_FelInfusionBuffLibrary then
boolean empowerment
real netherSize
real netherHeight
real netherDelay
real netherLevitateHeight
real netherLevitateDuration
real netherDuration
real netherRadius
real netherLandSpeed
real netherExposure
real netherDamage
integer netherAbilityId
integer netherBuffId
integer netherOrderId
string netherModel
string netherModelOrigin
string netherPickedModel
string netherLevitatedModel
string netherLevitatedModelOrigin
string netherLandedModel
string netherLandedModelOrigin
attacktype netherAttackType
damagetype netherDamageType
weapontype netherWeaponType
readonly boolean empowered
readonly boolean filtration
endif
readonly real x
readonly real y
readonly real height
readonly real delay
readonly real levitateHeight
readonly real levitateDuration
readonly real duration
readonly real radius
readonly real landSpeed
readonly real exposure
readonly real damage
readonly integer abilityId
readonly integer buffId
readonly integer orderId
readonly string pickedModel
readonly string levitatedModel
readonly string levitatedModelOrigin
readonly string landedModel
readonly string landedModelOrigin
readonly attacktype attackType
readonly damagetype damageType
readonly weapontype weaponType
readonly unit caster
readonly player owner
private unit indicator
private effect indicatorModel
private List list
readonly static constant real SPELL_PERIOD = PERIODIC_INTERVAL
private method onClonedSpellStart takes nothing returns thistype
static if LIBRARY_FelInfusionBuffLibrary then
local FelInfusionBuffLibrary_FelInfusionBuff infuse = Buff.get(GetEventSpellCaster(),GetEventSpellCaster(),FelInfusionBuffLibrary_FelInfusionBuff.typeid)
endif
set .caster = GetEventSpellCaster()
set .owner = GetEventSpellPlayer()
set .x = GetEventSpellTargetX()
set .y = GetEventSpellTargetY()
set .indicator = GetRecycledDummyAnyAngle(.x,.y,0)
static if LIBRARY_FelInfusionBuffLibrary then
if infuse > 0 and .empowerment then
set .empowered = true
call infuse.remove()
set .height = .netherHeight
set .delay = .netherDelay
set .levitateHeight = .netherLevitateHeight
set .levitateDuration = .netherLevitateDuration
set .duration = .netherDuration
set .radius = .netherRadius
set .landSpeed = .netherLandSpeed
set .exposure = .netherExposure
set .damage = .netherDamage
set .abilityId = .netherAbilityId
set .buffId = .netherBuffId
set .orderId = .netherOrderId
set .pickedModel = .netherPickedModel
set .levitatedModel = .netherLevitatedModel
set .levitatedModelOrigin = .netherLevitatedModelOrigin
set .landedModel = .netherLandedModel
set .landedModelOrigin = .netherLandedModelOrigin
set .attackType = .netherAttackType
set .damageType = .netherDamageType
set .weaponType = .netherWeaponType
call SetUnitScale(.indicator,.netherSize,0,0)
set .indicatorModel = AddSpecialEffectTarget(.netherModel,.indicator,.netherModelOrigin)
else
set .empowered = false
set .height = .gravityHeight
set .delay = .gravityDelay
set .levitateHeight = .gravityLevitateHeight
set .levitateDuration = .gravityLevitateDuration
set .duration = .gravityDuration
set .radius = .gravityRadius
set .landSpeed = .gravityLandSpeed
set .exposure = .gravityExposure
set .damage = .gravityDamage
set .abilityId = .gravityAbilityId
set .buffId = .gravityBuffId
set .orderId = .gravityOrderId
set .pickedModel = .gravityPickedModel
set .levitatedModel = .gravityLevitatedModel
set .levitatedModelOrigin = .gravityLevitatedModelOrigin
set .landedModel = .gravityLandedModel
set .landedModelOrigin = .gravityLandedModelOrigin
set .attackType = .gravityAttackType
set .damageType = .gravityDamageType
set .weaponType = .gravityWeaponType
call SetUnitScale(.indicator,.gravitySize,0,0)
set .indicatorModel = AddSpecialEffectTarget(.gravityModel,.indicator,.gravityModelOrigin)
endif
else
set .height = .gravityHeight
set .delay = .gravityDelay
set .levitateHeight = .gravityLevitateHeight
set .levitateDuration = .gravityLevitateDuration
set .duration = .gravityDuration
set .radius = .gravityRadius
set .landSpeed = .gravityLandSpeed
set .exposure = .gravityExposure
set .damage = .gravityDamage
set .abilityId = .gravityAbilityId
set .buffId = .gravityBuffId
set .orderId = .gravityOrderId
set .pickedModel = .gravityPickedModel
set .levitatedModel = .gravityLevitatedModel
set .levitatedModelOrigin = .gravityLevitatedModelOrigin
set .landedModel = .gravityLandedModel
set .landedModelOrigin = .gravityLandedModelOrigin
set .attackType = .gravityAttackType
set .damageType = .gravityDamageType
set .weaponType = .gravityWeaponType
call SetUnitScale(.indicator,.gravitySize,0,0)
set .indicatorModel = AddSpecialEffectTarget(.gravityModel,.indicator,.gravityModelOrigin)
endif
set .list = List.create()
return this
endmethod
private method onClonedSpellPeriodic takes nothing returns boolean
local real x
local real y
local real curHeight
local real realHeight
local unit picked
local List node = this.list.next
set .duration = .duration - PERIODIC_INTERVAL
loop
exitwhen node == this.list
set node.time = node.time - PERIODIC_INTERVAL
set curHeight = GetUnitFlyHeight(node.picked)
if UnitAlive(node.picked) and IsUnitInRangeXY(node.picked,.x,.y,.radius) and .duration > 0. then
if not RiseAndFall.isUnitAirborne(node.picked) and (node.time <= 0. or curHeight >= .height) then
call RiseAndFall.levitate(node.picked,curHeight,curHeight+.levitateHeight,.levitateDuration)
endif
if GetUnitPropWindow(node.picked) != 0 and (node.time <= 0. or curHeight >= .height) then
call SetUnitPropWindow(node.picked,0)
endif
else
set realHeight = GetUnitDefaultFlyHeight(node.picked)
if node.model != null then
if curHeight >= realHeight + .exposure then
set node.eligible = true
else
set node.eligible = false
endif
call RiseAndFall[node.picked].end(realHeight,.landSpeed)
call DestroyEffect(node.model)
set node.model = null
endif
if not RiseAndFall.isUnitAirborne(node.picked) or .duration <= -LANDING_FAILSAFE then
set x = GetUnitX(node.picked)
set y = GetUnitY(node.picked)
call SetUnitPosition(node.picked,x,y)
if .abilityId != 0 and .buffId != 0 then
call UnitRemoveAbility(node.picked,.buffId)
endif
call UnitRemoveAbility(node.picked,GHOST_ID)
call SetUnitPathing(node.picked,true)
if DISARM_MELEE or DISARM_RANGED then
call UnitRemoveAbility(node.picked,DISARM_ID)
endif
call SetUnitPropWindow(node.picked,GetUnitDefaultPropWindow(node.picked)*bj_DEGTORAD)
if node.eligible and (not IsUnitType(node.picked,UNIT_TYPE_FLYING) or DAMAGE_FLYING) then
call DestroyEffect( AddSpecialEffectTarget(.landedModel,node.picked,.landedModelOrigin) )
call UnitDamageTarget(.caster,node.picked,.damage,false,false,.attackType,.damageType,.weaponType)
endif
call List.remove(node)
endif
endif
set node = node.next
endloop
if .duration > 0. then
call GroupEnumUnitsInRange(Group,.x,.y,.radius,null)
loop
set picked = FirstOfGroup(Group)
exitwhen picked == null
set curHeight = GetUnitFlyHeight(picked)
static if LIBRARY_FelInfusionBuffLibrary then
if .empowered then
set .filtration = NetherFiltration(picked,.owner)
else
set .filtration = GravityFiltration(picked,.owner)
endif
if .filtration and not RiseAndFall.isUnitAirborne(picked) then
set x = GetUnitX(picked)
set y = GetUnitY(picked)
call DestroyEffect( AddSpecialEffect(.pickedModel,x,y) )
if .abilityId != 0 then
call SetUnitX(CASTER,x)
call SetUnitY(CASTER,y)
call UnitAddAbility(CASTER,.abilityId)
call IssueTargetOrderById(CASTER,.orderId,picked)
call UnitRemoveAbility(CASTER,.abilityId)
endif
call UnitAddAbility(picked,GHOST_ID)
call SetUnitPathing(picked,false)
if DISARM_MELEE or DISARM_RANGED then
if IsUnitType(picked,UNIT_TYPE_MELEE_ATTACKER) and DISARM_MELEE then
call UnitAddAbility(picked,DISARM_ID)
elseif IsUnitType(picked,UNIT_TYPE_RANGED_ATTACKER) and DISARM_RANGED then
call UnitAddAbility(picked,DISARM_ID)
endif
endif
set node = List.allocate()
set node.picked = picked
set node.model = AddSpecialEffectTarget(.levitatedModel,picked,.levitatedModelOrigin)
set node.time = .delay
set node.eligible = false
call RiseAndFall.create(picked,curHeight,curHeight+.height,.delay,false)
call this.list.pushBack(node)
endif
else
if GravityFiltration(picked,.owner) and not RiseAndFall.isUnitAirborne(picked) then
set x = GetUnitX(picked)
set y = GetUnitY(picked)
call DestroyEffect( AddSpecialEffect(.pickedModel,x,y) )
if .abilityId != 0 then
call SetUnitX(CASTER,x)
call SetUnitY(CASTER,y)
call UnitAddAbility(CASTER,.abilityId)
call IssueTargetOrderById(CASTER,.orderId,picked)
call UnitRemoveAbility(CASTER,.abilityId)
endif
call UnitAddAbility(picked,GHOST_ID)
call SetUnitPathing(picked,false)
if DISARM_MELEE or DISARM_RANGED then
if IsUnitType(picked,UNIT_TYPE_MELEE_ATTACKER) and DISARM_MELEE then
call UnitAddAbility(picked,DISARM_ID)
elseif IsUnitType(picked,UNIT_TYPE_RANGED_ATTACKER) and DISARM_RANGED then
call UnitAddAbility(picked,DISARM_ID)
endif
endif
set node = List.allocate()
set node.picked = picked
set node.model = AddSpecialEffectTarget(.levitatedModel,picked,.levitatedModelOrigin)
set node.time = .delay
set node.eligible = false
call RiseAndFall.create(picked,curHeight,curHeight+.height,.delay,false)
call this.list.pushBack(node)
endif
endif
call GroupRemoveUnit(Group,picked)
endloop
return true
else
return not this.list.empty
endif
endmethod
private method onClonedSpellEnd takes nothing returns nothing
call this.list.destroy()
call DestroyEffect(.indicatorModel)
call DummyAddRecycleTimer(.indicator,MODEL_DEATH_TIME)
if not MODEL_DEATH_SHOW then
call ShowDummy(.indicator,false)
endif
set .indicatorModel = null
set .indicator = null
set .caster = null
endmethod
private static unit CASTER = null
private static method onInit takes nothing returns nothing
set CASTER = CreateUnit(Player(bj_PLAYER_NEUTRAL_EXTRA),CASTER_UNIT_ID,0,0,0)
endmethod
implement SpellCloner
endstruct
module GravityLapseConfigurationCloner
private static method configHandler takes nothing returns nothing
local GravityLapse clone = SpellCloner.configuredInstance
// Configuration by constants
set clone.gravityAbilityId = GRAVITY_CASTER_ABILITY_ID
set clone.gravityBuffId = GRAVITY_CASTER_BUFF_ID
set clone.gravityOrderId = GRAVITY_CASTER_ORDER_ID
set clone.gravityModel = GRAVITY_MODEL
set clone.gravityModelOrigin = GRAVITY_MODEL_ORIGIN
set clone.gravityPickedModel = GRAVITY_PICKED_MODEL
set clone.gravityLevitatedModel = GRAVITY_LEVITATED_MODEL
set clone.gravityLevitatedModelOrigin = GRAVITY_LEVITATED_MODEL_ORIGIN
set clone.gravityLandedModel = GRAVITY_LANDED_MODEL
set clone.gravityLandedModelOrigin = GRAVITY_LANDED_MODEL_ORIGIN
set clone.gravityAttackType = GRAVITY_ATTACK_TYPE
set clone.gravityDamageType = GRAVITY_DAMAGE_TYPE
set clone.gravityWeaponType = GRAVITY_WEAPON_TYPE
// Configuration with levels
set clone.gravitySize = gravitySize(GetEventSpellLevel())
set clone.gravityHeight = gravityHeight(GetEventSpellLevel())
set clone.gravityDelay = gravityDelay(GetEventSpellLevel())
set clone.gravityLevitateHeight = gravityLevitateHeight(GetEventSpellLevel())
set clone.gravityLevitateDuration = gravityLevitateDuration(GetEventSpellLevel())
set clone.gravityDuration = gravityDuration(GetEventSpellLevel())
set clone.gravityRadius = gravityRadius(GetEventSpellLevel())
set clone.gravityLandSpeed = gravityLandSpeed(GetEventSpellLevel())
set clone.gravityExposure = gravityExposure(GetEventSpellLevel())
set clone.gravityDamage = gravityDamage(GetEventSpellLevel())
static if LIBRARY_FelInfusionBuffLibrary then
set clone.empowerment = EMPOWERMENT
// Configuration by constants
set clone.netherAbilityId = NETHER_CASTER_ABILITY_ID
set clone.netherBuffId = NETHER_CASTER_BUFF_ID
set clone.netherOrderId = NETHER_CASTER_ORDER_ID
set clone.netherModel = NETHER_MODEL
set clone.netherModelOrigin = NETHER_MODEL_ORIGIN
set clone.netherPickedModel = NETHER_PICKED_MODEL
set clone.netherLevitatedModel = NETHER_LEVITATED_MODEL
set clone.netherLevitatedModelOrigin = NETHER_LEVITATED_MODEL_ORIGIN
set clone.netherLandedModel = NETHER_LANDED_MODEL
set clone.netherLandedModelOrigin = NETHER_LANDED_MODEL_ORIGIN
set clone.netherAttackType = NETHER_ATTACK_TYPE
set clone.netherDamageType = NETHER_DAMAGE_TYPE
set clone.netherWeaponType = NETHER_WEAPON_TYPE
// Configuration with levels
set clone.netherSize = netherSize(GetEventSpellLevel())
set clone.netherHeight = netherHeight(GetEventSpellLevel())
set clone.netherDelay = netherDelay(GetEventSpellLevel())
set clone.netherLevitateHeight = netherLevitateHeight(GetEventSpellLevel())
set clone.netherLevitateDuration = netherLevitateDuration(GetEventSpellLevel())
set clone.netherDuration = netherDuration(GetEventSpellLevel())
set clone.netherRadius = netherRadius(GetEventSpellLevel())
set clone.netherLandSpeed = netherLandSpeed(GetEventSpellLevel())
set clone.netherExposure = netherExposure(GetEventSpellLevel())
set clone.netherDamage = netherDamage(GetEventSpellLevel())
endif
endmethod
private static method onInit takes nothing returns nothing
call GravityLapse.create(thistype.typeid,ABILITY_ID,SPELL_EVENT_TYPE,function thistype.configHandler)
endmethod
endmodule
endlibrary
library PyroblastConfiguration uses Pyroblast
/* is using optional custom models
war3mapImported\Pyroblast.mdx
*/
private struct Configuration extends array
// Pyroblast Ability ID
private static constant integer ABILITY_ID = 'A006'
// The height of the projectile
private static constant real PROJECTILE_HEIGHT = 125.00
// The distance of the projectile
private static constant real PROJECTILE_DISTANCE = 75.00
// The detection range of the projectile
private static constant real PROJECTILE_DETECT_RANGE = 50.00
// The animation played of the caster to launch the projectile (set to null to disable)
private static constant string LAUNCH_ANIMATION = "spell"
// The model of the projectile
private static constant string PROJECTILE_MODEL = "war3mapImported\\Pyroblast.mdx"
private static constant string PROJECTILE_MODEL_ORIGIN = "origin"
// The model spawned on picked unit when projectile hits
private static constant string PICKED_MODEL = "Abilities\\Weapons\\FireBallMissile\\FireBallMissile.mdl"
private static constant string PICKED_MODEL_ORIGIN = "head"
// The attack type of inflicting damage
private static constant attacktype ATTACK_TYPE = ATTACK_TYPE_NORMAL
// The damage type of inflicting damage
private static constant damagetype DAMAGE_TYPE = DAMAGE_TYPE_MAGIC
// The weapon type of inflicting damage
private static constant weapontype WEAPON_TYPE = WEAPON_TYPE_WHOKNOWS
private static constant method duration takes integer level returns real
return 3.00 + (0.00*level) // duration of the channeling time
endmethod
private static constant method animation takes integer level returns real
return 0.75 + (0.00*level) // remaining duration to play launch animation
endmethod
private static constant method startSize takes integer level returns real
return 0.00 + (0.00*level) // starting size of the projectile
endmethod
private static constant method endSize takes integer level returns real
return 1.00 + (0.00*level) // ending size of the projectile
endmethod
private static constant method velocity takes integer level returns real
return 5.00 + (0.00*level) // speed of the projectile
endmethod
private static constant method collision takes integer level returns real
return 150.00 + (0.00*level) // collision of the projectile
endmethod
private static constant method radius takes integer level returns real
return 300.00 + (0.00*level) // radius of the explosion
endmethod
private static constant method periodicDamage takes integer level returns real
return 0.00 + (2.50*level) // periodic damage of the projectile on collision
endmethod
private static constant method explosionDamage takes integer level returns real
return 0.00 + (200.00*level) // explosion damage of the projectile on radius
endmethod
private static constant integer SPELL_EVENT_TYPE = EVENT_SPELL_CHANNEL + EVENT_SPELL_ENDCAST
implement PyroblastConfigurationCloner
endstruct
endlibrary
library Pyroblast /*
*/uses /*
*/UnitDex /* https://www.hiveworkshop.com/threads/248209/
*/SpellEvent /* https://www.hiveworkshop.com/threads/301895/
*/SpellCloner /* https://www.hiveworkshop.com/threads/292751/
*/DummyRecycler /* https://www.hiveworkshop.com/threads/277659/
*/GetTerrainHeight /* library is used to make this work with terrain height
*/optional RapidSound /* https://www.hiveworkshop.com/threads/258991/
*/
/**************************************************
* GLOBAL CONFIGURATION
***************************************************/
private module Configuration
static constant real PERIODIC_INTERVAL = 0.015625
static constant real MODEL_DEATH_TIME = 2.5
// The height detection range for the units to be picked based on projectile's height
static constant real HEIGHT_DETECT_RANGE = 200.
// A flag to enable/disable sound to be played on cast
static constant boolean PLAY_SOUND = true
// ONLY configure if RapidSound library is present in the map and PLAY_SOUND is true
static if LIBRARY_RapidSound and thistype.PLAY_SOUND then
static constant string CAST_SOUND = "war3mapImported\\Pyroblast Cast Sound.wav"
static constant integer CAST_SOUND_VOLUME = 100
static constant boolean CAST_SOUND_3D = false
endif
endmodule
// Filtration of the units that will be damaged
private function Filtration takes unit target, unit picked, player owner, real pickedHeight, real projectileHeight returns boolean
return IsUnitEnemy(picked,owner) and /*
*/ not IsUnitType(picked,UNIT_TYPE_MAGIC_IMMUNE) and /*
*/ pickedHeight <= projectileHeight + Pyroblast.HEIGHT_DETECT_RANGE and /*
*/ pickedHeight >= projectileHeight - Pyroblast.HEIGHT_DETECT_RANGE and /*
*/ UnitAlive(picked)
endfunction
/**************************************************
* END OF GLOBAL CONFIGURATION
***************************************************/
/*============================= CORE CODE =============================*/
native UnitAlive takes unit id returns boolean
globals
private constant integer STOP_ID = 851972
private constant integer DEFAULT_PITCH = 90
private group Group = CreateGroup()
endglobals
private struct Projectile extends array
implement Alloc
implement StaticList
readonly unit projectile
private effect model
readonly unit source
readonly unit target
readonly player owner
readonly Pyroblast main
private static timer time = CreateTimer()
method destroy takes nothing returns nothing
call DestroyEffect(.model)
call DummyAddRecycleTimer(.projectile,Pyroblast.MODEL_DEATH_TIME)
set .model = null
set .projectile = null
set .source = null
set .target = null
call this.deallocate()
call remove(this)
if empty then
call PauseTimer(time)
endif
endmethod
static method periodic takes nothing returns nothing
local real x1
local real x2
local real y1
local real y2
local real z1
local real z2
local real ang
local real dis
local real phi
local unit picked
local thistype this = front
loop
exitwhen this == 0
set x1 = GetUnitX(.projectile)
set x2 = GetUnitX(.target)
set y1 = GetUnitY(.projectile)
set y2 = GetUnitY(.target)
set z1 = GetUnitFlyHeight(.projectile) + GetTerrainZ(x1,y1)
set z2 = GetUnitFlyHeight(.target) + .main.height + GetTerrainZ(x2,y2)
set ang = Atan2(y2-y1,x2-x1)
set dis = SquareRoot( (x2-x1)*(x2-x1)+(y2-y1)*(y2-y1) )
set phi = Atan2(z2-z1,dis)
call SetUnitX(.projectile,x1 + .main.velocity*Cos(ang)*Cos(phi) )
call SetUnitY(.projectile,y1 + .main.velocity*Sin(ang)*Cos(phi) )
call SetUnitFlyHeight(.projectile,z1 + .main.velocity*Sin(phi)-GetTerrainZ(x1,y1),0)
call SetUnitAnimationByIndex(.projectile,R2I(phi*bj_RADTODEG+0.5)+DEFAULT_PITCH)
call SetUnitFacing(.projectile,ang*bj_RADTODEG)
call GroupEnumUnitsInRange(Group,x1,y1,.main.collision,null)
loop
set picked = FirstOfGroup(Group)
exitwhen picked == null
if Filtration( .target,picked,.owner,GetUnitFlyHeight(picked),GetUnitFlyHeight(.projectile) ) then
call DestroyEffect( AddSpecialEffectTarget(.main.pickedModel,picked,.main.pickedModelOrigin) )
call UnitDamageTarget(.source,picked,.main.periodicDamage,false,false,.main.attackType,.main.damageType,.main.weaponType)
endif
call GroupRemoveUnit(Group,picked)
endloop
set dis = (x2-x1)*(x2-x1)+(y2-y1)*(y2-y1)+(z2-z1)*(z2-z1)
if (dis <= .main.range*.main.range) or not UnitAlive(.target) then
call GroupEnumUnitsInRange(Group,x1,y1,.main.radius,null)
loop
set picked = FirstOfGroup(Group)
exitwhen picked == null
if Filtration( .target,picked,.owner,GetUnitFlyHeight(picked),GetUnitFlyHeight(.projectile) ) then
call DestroyEffect( AddSpecialEffectTarget(.main.pickedModel,picked,.main.pickedModelOrigin) )
call UnitDamageTarget(.source,picked,.main.explosionDamage,false,false,.main.attackType,.main.damageType,.main.weaponType)
endif
call GroupRemoveUnit(Group,picked)
endloop
call this.destroy()
endif
set this = next
endloop
endmethod
static method create takes Pyroblast source returns thistype
local thistype this = allocate()
if empty then
call TimerStart(time,Pyroblast.PERIODIC_INTERVAL,true,function thistype.periodic)
endif
call pushBack(this)
set .main = source
set .source = source.caster
set .target = source.target
set .owner = source.owner
set .projectile = source.projectile
set .model = source.model
return this
endmethod
endstruct
struct Pyroblast extends array
implement Configuration
implement SpellClonerHeader
real height
real distance
real range
real duration
real animation
real startSize
real endSize
real velocity
real collision
real radius
real periodicDamage
real explosionDamage
string launchAnimation
string projectileModel
string projectileModelOrigin
string pickedModel
string pickedModelOrigin
attacktype attackType
damagetype damageType
weapontype weaponType
readonly real size
readonly real rate
readonly real remaining
readonly unit projectile
readonly effect model
readonly unit caster
readonly unit target
readonly player owner
readonly boolean play
readonly boolean ended
static if LIBRARY_RapidSound and thistype.PLAY_SOUND then
private RSound castSound
endif
readonly static constant real SPELL_PERIOD = PERIODIC_INTERVAL
private method onSpellStart takes nothing returns thistype
local real x
local real y
local real angle
set this = GetUnitUserData( GetEventSpellCaster() )
if GetEventSpellEventType() == EVENT_SPELL_ENDCAST then
set .ended = true
return 0
endif
call this.initSpellConfiguration( GetEventSpellAbilityId() )
set thistype(0).caster = GetEventSpellCaster()
if .caster == thistype(0).caster then
static if LIBRARY_RapidSound and thistype.PLAY_SOUND then
call .castSound.stop(true)
call .castSound.kill()
endif
call DestroyEffect(.model)
call RecycleDummy(.projectile)
set .model = null
set .projectile = null
endif
set .target = GetEventSpellTargetUnit()
set .owner = GetEventSpellPlayer()
set .size = .startSize
set .rate = (.startSize - .endSize)/(.duration/PERIODIC_INTERVAL)
set .remaining = .duration
set .play = false
set .ended = false
set x = GetUnitX(thistype(0).caster)
set y = GetUnitY(thistype(0).caster)
static if LIBRARY_RapidSound and thistype.PLAY_SOUND then
set .castSound = RSound.create(CAST_SOUND,CAST_SOUND_3D,false,10000,10000)
call .castSound.play(x,y,GetUnitFlyHeight(thistype(0).caster),CAST_SOUND_VOLUME)
endif
set angle = Atan2(GetEventSpellTargetY()-y,GetEventSpellTargetX()-x)
set x = x + .distance*Cos(angle)
set y = y + .distance*Sin(angle)
set .projectile = GetRecycledDummy(x,y,.height,angle*bj_RADTODEG)
call SetUnitScale(.projectile,.size,0,0)
set .model = AddSpecialEffectTarget(.projectileModel,.projectile,.projectileModelOrigin)
if .caster != thistype(0).caster then
set .caster = thistype(0).caster
return this
else
return 0
endif
endmethod
private method onSpellPeriodic takes nothing returns boolean
set .size = .size - .rate
call SetUnitScale(.projectile,.size,0,0)
// prevents ongoing channel animation if the target dies while channeling
if not UnitAlive(.target) then
call IssueImmediateOrderById(.caster,STOP_ID)
return false
endif
set .remaining = .remaining - PERIODIC_INTERVAL
if (.remaining <= .animation and not .play) then
call SetUnitAnimation(.caster,.launchAnimation)
set .play = true
endif
return not .ended and .remaining > 0. and UnitAlive(.caster)
endmethod
private method onSpellEnd takes nothing returns nothing
local Projectile node
if .remaining <= 0. and not. ended then
set node = Projectile.create(this)
else
call DestroyEffect(.model)
call RecycleDummy(.projectile)
endif
static if LIBRARY_RapidSound and thistype.PLAY_SOUND then
call .castSound.stop(true)
call .castSound.kill()
endif
set .model = null
set .projectile = null
set .caster = null
set .target = null
endmethod
implement SpellEvent
implement SpellClonerFooter
endstruct
module PyroblastConfigurationCloner
private static method configHandler takes nothing returns nothing
local Pyroblast clone = SpellCloner.configuredInstance
// Configuration by constants
set clone.height = PROJECTILE_HEIGHT
set clone.distance = PROJECTILE_DISTANCE
set clone.range = PROJECTILE_DETECT_RANGE
set clone.launchAnimation = LAUNCH_ANIMATION
set clone.projectileModel = PROJECTILE_MODEL
set clone.projectileModelOrigin = PROJECTILE_MODEL_ORIGIN
set clone.pickedModel = PICKED_MODEL
set clone.pickedModelOrigin = PICKED_MODEL_ORIGIN
set clone.attackType = ATTACK_TYPE
set clone.damageType = DAMAGE_TYPE
set clone.weaponType = WEAPON_TYPE
// Configuration with levels
set clone.duration = duration(GetEventSpellLevel())
set clone.animation = animation(GetEventSpellLevel())
set clone.startSize = startSize(GetEventSpellLevel())
set clone.endSize = endSize(GetEventSpellLevel())
set clone.velocity = velocity(GetEventSpellLevel())
set clone.collision = collision(GetEventSpellLevel())
set clone.radius = radius(GetEventSpellLevel())
set clone.periodicDamage = periodicDamage(GetEventSpellLevel())
set clone.explosionDamage = explosionDamage(GetEventSpellLevel())
endmethod
private static method onInit takes nothing returns nothing
call Pyroblast.create(thistype.typeid,ABILITY_ID,SPELL_EVENT_TYPE,function thistype.configHandler)
endmethod
endmodule
endlibrary
library AlarNormalDiveConfiguration uses Alar
private struct Configuration extends array
// Al'ar (Normal Dive) Ability ID
private static constant integer ABILITY_ID = 'A007'
// Caster Ability ID casted on picked unit (set to 0 to disable)
private static constant integer CASTER_ABILITY_ID = 'A009'
// Buff ID created by Caster Ability ID
// (set to 0 to no buff or ignore buff-check to cast the CASTER_ABILITY_ID per damage inflicted on trail)
private static constant integer CASTER_BUFF_ID = 'B002'
// Order ID of Caster Ability ID
private static constant integer CASTER_ORDER_ID = 852662
// The release height of the phoenix
private static constant real PHOENIX_HEIGHT = 100.00
// The arc of the dive on trail release
private static constant real PHOENIX_ARC = 0.65
// The animation played of the phoenix to release the trail (set to null to disable)
private static constant string PHOENIX_SPIN_ANIMATION = null
// The model spawned on phoenix position on cast
private static constant string PHOENIX_CREATION_MODEL = "Abilities\\Spells\\Other\\Doom\\DoomDeath.mdl"
// The model spawned on phoenix body on cast
private static constant string PHOENIX_BLAZE_MODEL = "Abilities\\Weapons\\FireBallMissile\\FireBallMissile.mdl"
private static constant method blazeModelOrigin takes integer level, integer index returns string
local string array origin
set origin[1] = "head"
set origin[2] = "chest"
set origin[3] = "hand,left"
set origin[4] = "hand,right"
set origin[5] = "origin"
return origin[index]
endmethod
// The maximum number of index of origin specified in blazeModelOrigin (the minimum index should be 1)
private static constant integer PHOENIX_BLAZE_MODEL_ORIGIN_COUNT = 5
// The model spawned on picked unit when phoenix hits
private static constant string PICKED_MODEL = ""
private static constant string PICKED_MODEL_ORIGIN = ""
// The model of the trail
private static constant string TRAIL_MODEL = "Abilities\\Spells\\Other\\BreathOfFire\\BreathOfFireDamage.mdl"
private static constant string TRAIL_MODEL_ORIGIN = "origin"
// The attack type of inflicting damage
private static constant attacktype ATTACK_TYPE = ATTACK_TYPE_NORMAL
// The damage type of inflicting damage
private static constant damagetype DAMAGE_TYPE = DAMAGE_TYPE_MAGIC
// The weapon type of inflicting damage
private static constant weapontype WEAPON_TYPE = WEAPON_TYPE_WHOKNOWS
private static constant method phoenixID takes integer level returns integer
return 'h000' // the rawcode of the phoenix unit
endmethod
// Normal vertex coloring of phoenix on return (0-255), 255 is the general value
private static constant method normalRed takes integer level returns integer
return 255
endmethod
private static constant method normalGreen takes integer level returns integer
return 255
endmethod
private static constant method normalBlue takes integer level returns integer
return 255
endmethod
private static constant method normalAlpha takes integer level returns integer
return 255
endmethod
// Alternate vertex coloring of phoenix on arc dive (0-255), 255 is the general value
private static constant method alternateRed takes integer level returns integer
return 255
endmethod
private static constant method alternateGreen takes integer level returns integer
return 255
endmethod
private static constant method alternateBlue takes integer level returns integer
return 255
endmethod
private static constant method alternateAlpha takes integer level returns integer
return 255
endmethod
private static constant method invulnerable takes integer level returns boolean
return true // invulnerable phoenix on arc dive
endmethod
private static constant method duration takes integer level returns real
return 5.00 + (0.00*level) // time it takes for phoenix to complete the arc dive
endmethod
private static constant method range takes integer level returns real
return 1000.00 + (0.00*level) // range of the arc dive
endmethod
private static constant method collision takes integer level returns real
return 100.00 + (0.00*level) // collision of phoenix on arc dive
endmethod
private static constant method damage takes integer level returns real
return 0.00 + (0.00*level) // damage of phoenix collision on arc dive
endmethod
private static constant method delay takes integer level returns real
return 1.00 + (0.00*level) // time it takes for phoenix to return on default height
endmethod
private static constant method trailSize takes integer level returns real
return 3.50 + (0.00*level) // size of the trail
endmethod
private static constant method trailDuration takes integer level returns real
return 5.00 + (5.00*level) // duration of the trail
endmethod
private static constant method trailReleaseInterval takes integer level returns real
return 0.10 + (0.00*level) // interval of releasing trail on arc dive
endmethod
private static constant method trailRadius takes integer level returns real
return 100.00 + (0.00*level) // radius of the trail
endmethod
private static constant method trailDamageInterval takes integer level returns real
return 0.10 + (0.00*level) // interval of damage on each trail
endmethod
private static constant method trailDamage takes integer level returns real
return 0.00 + (10.00*level) // inflicted damage per interval on each trail
endmethod
private static constant integer SPELL_EVENT_TYPE = EVENT_SPELL_EFFECT
implement AlarConfigurationCloner
endstruct
endlibrary
library Alar /*
*/uses /*
*/Alloc /* library is used to make this work on instances efficiently
*/LinkedList /* library is used to make this work on a list data structure
*/SpellEvent /* https://www.hiveworkshop.com/threads/301895/
*/SpellCloner /* https://www.hiveworkshop.com/threads/292751/
*/DummyRecycler /* https://www.hiveworkshop.com/threads/277659/
*/AutoFly /* https://www.hiveworkshop.com/threads/249422/
*/RiseAndFall /* https://www.hiveworkshop.com/threads/306703/
*/PauseLibrary /* library is used for pausing/unpausing a unit safely
*/optional IsDestructableTree /* https://www.hiveworkshop.com/threads/248054/
*/optional RapidSound /* https://www.hiveworkshop.com/threads/258991/
*/
/**************************************************
* GLOBAL CONFIGURATION
***************************************************/
private module Configuration
static constant real PERIODIC_INTERVAL = 0.03125
static constant real MODEL_DEATH_TIME = 2.5
// Caster Unit ID
static constant integer CASTER_UNIT_ID = 'dumi' // Based on 'DummyRecycler Library'
// A flag to enable/disable sound to be played on cast
static constant boolean PLAY_SOUND = true
// ONLY configure if RapidSound library is present in the map and PLAY_SOUND is true
static if LIBRARY_RapidSound and thistype.PLAY_SOUND then
static constant string CAST_SOUND = "war3mapImported\\Al'ar Cast Sound.wav"
static constant integer CAST_SOUND_VOLUME = 200
static constant boolean CAST_SOUND_3D = false
endif
// A flag to enable/disable destroy trees on collision
static constant boolean DESTROY_TREES = true
// ONLY configure if IsDestructableTree library is present in the map
endmodule
// Filtration of the units that will be damaged
private function Filtration takes unit picked, player owner returns boolean
return IsUnitEnemy(picked,owner) and /*
*/ not IsUnitType(picked,UNIT_TYPE_MAGIC_IMMUNE) and /*
*/ UnitAlive(picked)
endfunction
/**************************************************
* END OF GLOBAL CONFIGURATION
***************************************************/
/*============================= CORE CODE =============================*/
native UnitAlive takes unit id returns boolean
globals
private constant string DEFAULT_ANIMATION = "stand"
private group Group = CreateGroup()
endglobals
public struct Trail extends array
implement Alloc
implement StaticList
real size
real duration
real interval
real radius
real damage
integer abilityId
integer abilityLevel
integer buffId
integer orderId
attacktype attackType
damagetype damageType
weapontype weaponType
readonly real remainingDuration
readonly real remainingInterval
readonly unit trail
readonly unit source
readonly player owner
private effect model
private static timer time = CreateTimer()
method destroy takes nothing returns nothing
call DestroyEffect(.model)
call DummyAddRecycleTimer(.trail,Alar.MODEL_DEATH_TIME)
set .model = null
set .trail = null
set .source = null
call this.deallocate()
call remove(this)
if empty then
call PauseTimer(time)
endif
endmethod
static method periodic takes nothing returns nothing
local real x
local real y
local unit picked
local thistype this = front
loop
exitwhen this == 0
if .remainingDuration > 0. then
set .remainingDuration = .remainingDuration - Alar.PERIODIC_INTERVAL
if .remainingInterval <= 0. then
set x = GetUnitX(.trail)
set y = GetUnitY(.trail)
call GroupEnumUnitsInRange(Group,x,y,.radius,null)
loop
set picked = FirstOfGroup(Group)
exitwhen picked == null
if Filtration(picked,.owner) then
if .abilityId != 0 and (GetUnitAbilityLevel(picked,.buffId) == 0 or .buffId == 0) then
call SetUnitX( CASTER,GetUnitX(picked) )
call SetUnitY( CASTER,GetUnitY(picked) )
call UnitAddAbility(CASTER,.abilityId)
call SetUnitAbilityLevel(CASTER,.abilityId,.abilityLevel)
call IssueTargetOrderById(CASTER,.orderId,picked)
call UnitRemoveAbility(CASTER,.abilityId)
endif
call UnitDamageTarget(.source,picked,.damage,false,false,.attackType,.damageType,.weaponType)
endif
call GroupRemoveUnit(Group,picked)
endloop
set .remainingInterval = .interval
else
set .remainingInterval = .remainingInterval - Alar.PERIODIC_INTERVAL
endif
else
call this.destroy()
endif
set this = next
endloop
endmethod
static method create takes unit caster, player owner, real x, real y, real z, real size, string model, string modelOrigin, /*
*/ real duration, real interval, real radius, real damage, attacktype attackType, damagetype damageType, weapontype weaponType, /*
*/ integer abilityId, integer abilityLevel, integer buffId, integer orderId returns thistype
local thistype this = allocate()
if empty then
call TimerStart(time,Alar.PERIODIC_INTERVAL,true,function thistype.periodic)
endif
call pushBack(this)
set .source = caster
set .owner = owner
set .trail = GetRecycledDummyAnyAngle(x,y,z)
call SetUnitScale(.trail,size,0,0)
set .model = AddSpecialEffectTarget(model,.trail,modelOrigin)
set .size = size
set .duration = duration
set .interval = interval
set .radius = radius
set .damage = damage
set .abilityId = abilityId
set .abilityLevel = abilityLevel
set .buffId = buffId
set .orderId = orderId
set .attackType = attackType
set .damageType = damageType
set .weaponType = weaponType
set .remainingDuration = .duration
set .remainingInterval = .interval
return this
endmethod
private static unit CASTER = null
private static method onInit takes nothing returns nothing
set CASTER = CreateUnit(Player(bj_PLAYER_NEUTRAL_EXTRA),Alar.CASTER_UNIT_ID,0,0,0)
endmethod
endstruct
struct Alar extends array
implement Configuration
implement SpellClonerHeader
real height
real arc
real duration
real range
real collision
real damage
real delay
real interval
real trailSize
real trailDuration
real trailReleaseInterval
real trailRadius
real trailDamageInterval
real trailDamage
integer unitId
integer abilityId
integer buffId
integer orderId
integer normalRed
integer normalGreen
integer normalBlue
integer normalAlpha
integer alternateRed
integer alternateGreen
integer alternateBlue
integer alternateAlpha
string animation
string creationModel
string blazeModel
string pickedModel
string pickedModelOrigin
string trailModel
string trailModelOrigin
boolean invulnerable
attacktype attackType
damagetype damageType
weapontype weaponType
static string array blazeModelOrigin
integer blazeModelOriginCount
readonly real x1
readonly real x2
readonly real y1
readonly real y2
readonly real travel
readonly real distance
readonly real facing
readonly unit caster
readonly unit phoenix
readonly player owner
readonly integer level
readonly boolean direction
readonly boolean ended
private group container
private static real RATE = 2.0
private static real VERTEX = bj_PI/2.0
static if LIBRARY_IsDestructableTree and thistype.DESTROY_TREES then
private static real X
private static real Y
private static real Radius
private static method destroyTree takes nothing returns nothing
local destructable des = GetEnumDestructable()
local real x = GetWidgetX(des) - X
local real y = GetWidgetY(des) - Y
if x*x + y*y <= Radius then
call KillTree(des)
endif
set des = null
endmethod
endif
static if LIBRARY_RapidSound and thistype.PLAY_SOUND then
private static RSound castSound
endif
readonly static constant real SPELL_PERIOD = PERIODIC_INTERVAL
private method onSpellStart takes nothing returns thistype
local real height
local real angle
local integer count
set this = GetUnitUserData( GetEventSpellCaster() )
call this.initSpellConfiguration( GetEventSpellAbilityId() )
if .caster == GetEventSpellCaster() and UnitAlive(.phoenix) and .phoenix != null then
call DestroyGroup(.container)
call RemoveUnit(.phoenix)
set .container = null
set .phoenix = null
set .direction = false
endif
set .caster = GetEventSpellCaster()
set .owner = GetEventSpellPlayer()
set .level = GetEventSpellLevel()
set .container = CreateGroup()
set .x1 = GetUnitX(.caster)
set .y1 = GetUnitY(.caster)
set height = GetUnitFlyHeight(.caster)
set .facing = GetUnitFacing(.caster)*bj_DEGTORAD
set .x2 = .x1 + .range *Cos(.facing)
set .y2 = .y1 + .range *Sin(.facing)
static if LIBRARY_RapidSound and thistype.PLAY_SOUND then
call castSound.play(.x1,.y1,height,CAST_SOUND_VOLUME)
endif
if GetRandomReal(0,1.0) <= .5 then
set .direction = true
endif
if .direction then
set angle = .facing-(VERTEX)
else
set angle = .facing+(VERTEX)
endif
set .phoenix = CreateUnit(.owner,.unitId,.x1,.y1,angle*bj_RADTODEG)
call SetUnitFlyHeight(.phoenix,.height,0)
call SetUnitVertexColor(.phoenix,.alternateRed,.alternateGreen,.alternateBlue,.alternateAlpha)
if .invulnerable then
call SetUnitInvulnerable(.phoenix,true)
endif
call PauseSystem.pause(.phoenix)
if .animation != null then
call SetUnitAnimation(.phoenix,.animation)
endif
call DestroyEffect( AddSpecialEffect(.creationModel,GetUnitX(.phoenix),GetUnitY(.phoenix) ) )
set count = .blazeModelOriginCount
loop
call DestroyEffect( AddSpecialEffectTarget(.blazeModel,.phoenix,blazeModelOrigin[count]) )
set count = count - 1
exitwhen count == 0
endloop
set .interval = .trailReleaseInterval
set .travel = 0.
set .distance = SquareRoot( (.x2-.x1)*(.x2-.x1) + (.y2-.y1)*(.y2-.y1) )
if .distance == 0. then
set .distance = 1.0
endif
set .ended = false
return this
endmethod
private method onSpellPeriodic takes nothing returns boolean
local real x
local real y
local real x1
local real y1
local real x2
local real y2
local unit picked
local Trail node
if .travel <= bj_PI then
set x = GetUnitX(.phoenix)
set y = GetUnitY(.phoenix)
set x1 = .x2 - .x1
set y1 = .y2 - .y1
if .direction then
set x2 = x1*Sin(.travel) + .arc*y1*Sin(RATE*.travel) + .x1
set y2 = y1*Sin(.travel) + .arc*(.x1-.x2)*Sin(RATE*.travel) + .y1
else
set x2 = x1*Sin(.travel) - .arc*y1*Sin(RATE*.travel) + .x1
set y2 = y1*Sin(.travel) - .arc*(.x1-.x2)*Sin(RATE*.travel) + .y1
endif
call SetUnitX(.phoenix,x2)
call SetUnitY(.phoenix,y2)
call SetUnitFacing(.phoenix,Atan2(y2-y,x2-x)*bj_RADTODEG)
if .interval <= 0. then
set node = Trail.create(.caster,.owner,x,y,0,.trailSize,.trailModel,.trailModelOrigin,.trailDuration,.trailDamageInterval,.trailRadius,.trailDamage,.attackType,.damageType,.weaponType,.abilityId,.level,.buffId,.orderId)
set .interval = .trailReleaseInterval
else
set .interval = .interval - PERIODIC_INTERVAL
endif
call GroupEnumUnitsInRange(Group,x,y,.collision,null)
loop
set picked = FirstOfGroup(Group)
exitwhen picked == null
if Filtration(picked,.owner) and not IsUnitInGroup(picked,.container) then
call DestroyEffect( AddSpecialEffectTarget(.pickedModel,picked,.pickedModelOrigin) )
call UnitDamageTarget(.caster,picked,.damage,false,false,.attackType,.damageType,.weaponType)
call GroupAddUnit(.container,picked)
endif
call GroupRemoveUnit(Group,picked)
endloop
static if LIBRARY_IsDestructableTree and thistype.DESTROY_TREES then
set X = x
set Y = y
set Radius = .collision*.collision
call SetRect(IsDestructableTree_Region,X - .collision,Y - .collision,X + .collision,Y + .collision)
call EnumDestructablesInRect(IsDestructableTree_Region,null,function thistype.destroyTree)
endif
set .travel = .travel + (.distance/( (.duration/PERIODIC_INTERVAL)*.distance) )*bj_PI
elseif not .ended then
call SetUnitPosition(.phoenix,.x1,.y1)
call SetUnitFacing(.phoenix,.facing*bj_RADTODEG)
call SetUnitVertexColor(.phoenix,.normalRed,.normalGreen,.normalBlue,.normalAlpha)
if .invulnerable then
call SetUnitInvulnerable(.phoenix,false)
endif
call PauseSystem.unpause(.phoenix)
if .animation != null then
call SetUnitAnimation(.phoenix,DEFAULT_ANIMATION)
endif
call RiseAndFall.create(.phoenix,GetUnitFlyHeight(.phoenix),GetUnitDefaultFlyHeight(.phoenix),.delay,true)
if (GetLocalPlayer() == .owner) then
call SelectUnit(.phoenix,true)
endif
set .ended = true
endif
return UnitAlive(.phoenix) and .phoenix != null
endmethod
private method onSpellEnd takes nothing returns nothing
set .phoenix = null
set .caster = null
endmethod
static if LIBRARY_RapidSound and thistype.PLAY_SOUND then
private static method onInit takes nothing returns nothing
set castSound = RSound.create(CAST_SOUND,CAST_SOUND_3D,false,10000,10000)
endmethod
endif
implement SpellEvent
implement SpellClonerFooter
endstruct
module AlarConfigurationCloner
private static method configHandler takes nothing returns nothing
local integer index
local Alar clone = SpellCloner.configuredInstance
// Configuration by constants
set clone.abilityId = CASTER_ABILITY_ID
set clone.buffId = CASTER_BUFF_ID
set clone.orderId = CASTER_ORDER_ID
set clone.height = PHOENIX_HEIGHT
set clone.arc = PHOENIX_ARC
set clone.animation = PHOENIX_SPIN_ANIMATION
set clone.creationModel = PHOENIX_CREATION_MODEL
set clone.blazeModel = PHOENIX_BLAZE_MODEL
set clone.blazeModelOriginCount = PHOENIX_BLAZE_MODEL_ORIGIN_COUNT
set clone.pickedModel = PICKED_MODEL
set clone.pickedModelOrigin = PICKED_MODEL_ORIGIN
set clone.trailModel = TRAIL_MODEL
set clone.trailModelOrigin = TRAIL_MODEL_ORIGIN
set clone.attackType = ATTACK_TYPE
set clone.damageType = DAMAGE_TYPE
set clone.weaponType = WEAPON_TYPE
// Configuration with levels
set clone.unitId = phoenixID(GetEventSpellLevel())
set clone.normalRed = normalRed(GetEventSpellLevel())
set clone.normalGreen = normalGreen(GetEventSpellLevel())
set clone.normalBlue = normalBlue(GetEventSpellLevel())
set clone.normalAlpha = normalAlpha(GetEventSpellLevel())
set clone.alternateRed = alternateRed(GetEventSpellLevel())
set clone.alternateGreen = alternateGreen(GetEventSpellLevel())
set clone.alternateBlue = alternateBlue(GetEventSpellLevel())
set clone.alternateAlpha = alternateAlpha(GetEventSpellLevel())
set clone.invulnerable = invulnerable(GetEventSpellLevel())
set clone.duration = duration(GetEventSpellLevel())
set clone.range = range(GetEventSpellLevel())
set clone.collision = collision(GetEventSpellLevel())
set clone.damage = damage(GetEventSpellLevel())
set clone.delay = delay(GetEventSpellLevel())
set clone.trailSize = trailSize(GetEventSpellLevel())
set clone.trailDuration = trailDuration(GetEventSpellLevel())
set clone.trailReleaseInterval = trailReleaseInterval(GetEventSpellLevel())
set clone.trailRadius = trailRadius(GetEventSpellLevel())
set clone.trailDamageInterval = trailDamageInterval(GetEventSpellLevel())
set clone.trailDamage = trailDamage(GetEventSpellLevel())
set index = clone.blazeModelOriginCount
loop
set Alar.blazeModelOrigin[index] = blazeModelOrigin(GetEventSpellLevel(),index)
set index = index - 1
exitwhen index == 0
endloop
endmethod
private static method onInit takes nothing returns nothing
call Alar.create(thistype.typeid,ABILITY_ID,SPELL_EVENT_TYPE,function thistype.configHandler)
endmethod
endmodule
endlibrary
library DiveBombConfiguration uses DiveBomb optional Knockback3D
/* is using optional custom models
war3mapImported\Phoenix Spinning.mdx
*/
private struct Configuration extends array
// Dive Bomb Ability ID
private static constant integer ABILITY_ID = 'A00B'
// The pitch of the diver on rising
private static constant integer RISE_PITCH = 180
// The pitch of the diver on falling
private static constant integer FALL_PITCH = 30
// The model spawned on the diver's ground on cast
private static constant string CAST_MODEL = "Abilities\\Spells\\Other\\Doom\\DoomDeath.mdl"
// The model of the diver
private static constant string DIVER_MODEL = "war3mapImported\\Phoenix Spinning.mdx"
private static constant string DIVER_MODEL_ORIGIN = "chest"
// The model of the crack on crash (radius-sized)
private static constant string CRACK_MODEL = "Abilities\\Spells\\Orc\\WarStomp\\WarStompCaster.mdl"
private static constant string CRACK_MODEL_ORIGIN = "origin"
// The model of the nova on crash (radius-sized)
private static constant string NOVA_MODEL = "Abilities\\Spells\\Other\\Doom\\DoomDeath.mdl"
private static constant string NOVA_MODEL_ORIGIN = "origin"
// The model of the explosion on crash (radius-sized)
private static constant string EXPLOSION_MODEL = "Objects\\Spawnmodels\\Other\\NeutralBuildingExplosion\\NeutralBuildingExplosion.mdl"
private static constant string EXPLOSION_MODEL_ORIGIN = "origin"
// The model spawned on picked unit on explosion
private static constant string PICKED_MODEL = "Abilities\\Weapons\\FireBallMissile\\FireBallMissile.mdl"
private static constant string PICKED_MODEL_ORIGIN = "origin"
// The attack type of inflicting damage
private static constant attacktype ATTACK_TYPE = ATTACK_TYPE_NORMAL
// The damage type of inflicting damage
private static constant damagetype DAMAGE_TYPE = DAMAGE_TYPE_MAGIC
// The weapon type of inflicting damage
private static constant weapontype WEAPON_TYPE = WEAPON_TYPE_WHOKNOWS
private static constant method rebirth takes integer level returns boolean
return true // rebirth on crash
endmethod
private static constant method delay takes integer level returns real
return 1.00 + (0.00*level) // time it takes for phoenix to return on default height if rebirth is false
endmethod
private static constant method height takes integer level returns real
return 750.00 + (0.00*level) // rising height of the diver
endmethod
private static constant method riseDelay takes integer level returns real
return 2.00 - (0.00*level) // delay of the rising
endmethod
private static constant method fallDelay takes integer level returns real
return 1.00 - (0.00*level) // delay of the falling
endmethod
private static constant method radius takes integer level returns real
return 500.00 + (0.00*level) // radius of the explosion
endmethod
private static constant method damage takes integer level returns real
return 500.00 + (0.00*level) // damage of the explosion
endmethod
static if LIBRARY_Knockback3D then
// The model spawned on knockbacked unit on friction
private static constant string KNOCKBACK_MODEL = "Abilities\\Weapons\\AncientProtectorMissile\\AncientProtectorMissile.mdl"
private static constant method knockbackPower takes integer level returns real
return 1500.00 + (0.00*level) // power of the knockback (set to 0 to disable)
endmethod
private static constant method knockbackPitch takes integer level returns real
return bj_PI/1.75 + (0.00*level) // pitch of the knockback
endmethod
endif
private static constant integer SPELL_EVENT_TYPE = EVENT_SPELL_EFFECT
implement DiveBombConfigurationCloner
endstruct
endlibrary
library DiveBomb /*
*/uses /*
*/SpellEvent /* https://www.hiveworkshop.com/threads/301895/
*/SpellCloner /* https://www.hiveworkshop.com/threads/292751/
*/DummyRecycler /* https://www.hiveworkshop.com/threads/277659/
*/AutoFly /* https://www.hiveworkshop.com/threads/249422/
*/RiseAndFall /* https://www.hiveworkshop.com/threads/306703/
*/PauseLibrary /* library is used for pausing/unpausing a unit safely
*/optional IsDestructableTree /* https://www.hiveworkshop.com/threads/248054/
*/optional Knockback3D /* https://www.hiveworkshop.com/threads/217056/
*/optional CameraShake /* library is used to make this simulate a camera shaking feature
*/
/**************************************************
* GLOBAL CONFIGURATION
***************************************************/
private module Configuration
static constant real PERIODIC_INTERVAL = 0.03125
static constant real MODEL_DEATH_TIME = 2.5
// A flag to enable/disable camera shake on crash
static constant boolean CAMERA_SHAKE = true
// ONLY configure if CameraShake library is present in the map and CAMERA_SHAKE is true
static if LIBRARY_CameraShake and thistype.CAMERA_SHAKE then
static constant real SHAKE_MAGNITUDE = 50.00
static constant real SHAKE_RANGE = 1000.00
endif
// A flag to enable/disable destroy trees on crash
static constant boolean DESTROY_TREES = true
// ONLY configure if IsDestructableTree library is present in the map
endmodule
// Filtration of the units that will be damaged or knockbacked on crash
private function Filtration takes unit picked, player owner returns boolean
return IsUnitEnemy(picked,owner) and /*
*/ not IsUnitType(picked,UNIT_TYPE_MAGIC_IMMUNE) and /*
*/ UnitAlive(picked)
endfunction
/**************************************************
* END OF GLOBAL CONFIGURATION
***************************************************/
/*============================= CORE CODE =============================*/
native UnitAlive takes unit id returns boolean
globals
private constant real RADIUS_SIZE_MULTIPLIER = .005
private constant string DEFAULT_ANIMATION = "stand"
private group Group = CreateGroup()
endglobals
struct DiveBomb extends array
implement Configuration
real height
real delay
real riseDelay
real fallDelay
real radius
real damage
integer risePitch
integer fallPitch
string castModel
string diverModel
string diverModelOrigin
string crackModel
string crackModelOrigin
string novaModel
string novaModelOrigin
string explosionModel
string explosionModelOrigin
string pickedModel
string pickedModelOrigin
boolean rebirth
attacktype attackType
damagetype damageType
weapontype weaponType
static if LIBRARY_Knockback3D then
real knockbackPower
real knockbackPitch
string knockbackModel
endif
readonly real x
readonly real y
readonly real velocity
readonly unit caster
readonly unit diver
readonly player owner
readonly boolean crash
private effect model
static if LIBRARY_IsDestructableTree and thistype.DESTROY_TREES then
private static real X
private static real Y
private static real Radius
private static method destroyTree takes nothing returns nothing
local destructable des = GetEnumDestructable()
local real x = GetWidgetX(des) - X
local real y = GetWidgetY(des) - Y
if x*x + y*y <= Radius then
call KillTree(des)
endif
set des = null
endmethod
endif
readonly static constant real SPELL_PERIOD = PERIODIC_INTERVAL
private method onClonedSpellStart takes nothing returns thistype
local real x
local real y
local real height
local real distance
set .caster = GetEventSpellCaster()
set .owner = GetEventSpellPlayer()
set .x = GetEventSpellTargetX()
set .y = GetEventSpellTargetY()
call PauseSystem.pause(.caster)
call SetUnitInvulnerable(.caster,true)
call ShowUnit(.caster,false)
set height = GetUnitFlyHeight(.caster)
set .diver = GetRecycledDummy( GetUnitX(.caster),GetUnitY(.caster),height,GetUnitFacing(.caster) )
call SetUnitAnimationByIndex(.diver,.risePitch)
set .model = AddSpecialEffectTarget(.diverModel,.diver,.diverModelOrigin)
set x = GetUnitX(.diver)
set y = GetUnitY(.diver)
call DestroyEffect( AddSpecialEffect(.castModel,x,y) )
call RiseAndFall.create(.diver,height,height+.height,.riseDelay,true)
set distance = SquareRoot( (.x-x)*(.x-x) + (.y-y)*(.y-y) )
set .velocity = distance/(.fallDelay/PERIODIC_INTERVAL)
set .crash = false
return this
endmethod
private method onClonedSpellPeriodic takes nothing returns boolean
local real x
local real y
local real angle
if not RiseAndFall.isUnitAirborne(.diver) and not .crash then
call SetUnitAnimationByIndex(.diver,.fallPitch)
call RiseAndFall.create(.diver,GetUnitFlyHeight(.diver),0,.fallDelay,false)
set .crash = true
endif
if .crash then
set x = GetUnitX(.diver)
set y = GetUnitY(.diver)
set angle = Atan2(.y-y,.x-x)
call SetUnitX(.diver,x + .velocity*Cos(angle) )
call SetUnitY(.diver,y + .velocity*Sin(angle) )
call SetUnitFacing(.diver,angle*bj_RADTODEG)
return RiseAndFall.isUnitAirborne(.diver)
endif
return UnitAlive(.caster) and GetUnitTypeId(.caster) != 0
endmethod
private method onClonedSpellEnd takes nothing returns nothing
local unit crash
local unit picked
if UnitAlive(.caster) and GetUnitTypeId(.caster) != 0 then
set crash = GetRecycledDummyAnyAngle(.x,.y,0)
call SetUnitScale(crash,.radius*RADIUS_SIZE_MULTIPLIER,0,0)
call DestroyEffect( AddSpecialEffectTarget(.crackModel,crash,.crackModelOrigin) )
call DestroyEffect( AddSpecialEffectTarget(.novaModel,crash,.novaModelOrigin) )
call DestroyEffect( AddSpecialEffectTarget(.explosionModel,crash,.explosionModelOrigin) )
call DummyAddRecycleTimer(crash,MODEL_DEATH_TIME)
set crash = null
static if LIBRARY_CameraShake and thistype.CAMERA_SHAKE then
call CameraShake_NewEvent(.x,.y,SHAKE_MAGNITUDE,SHAKE_RANGE)
endif
call GroupEnumUnitsInRange(Group,.x,.y,.radius,null)
loop
set picked = FirstOfGroup(Group)
exitwhen picked == null
if Filtration(picked,.owner) then
call DestroyEffect( AddSpecialEffectTarget(.pickedModel,picked,.pickedModelOrigin) )
static if LIBRARY_Knockback3D then
if not IsUnitType(picked,UNIT_TYPE_STRUCTURE) and .knockbackPower > 0. then
call Knockback3D.New(picked,.knockbackPower,Atan2(GetUnitY(picked)-.y,GetUnitX(picked)-.x),.knockbackPitch,.knockbackModel)
endif
endif
call UnitDamageTarget(.caster,picked,.damage,false,false,.attackType,.damageType,.weaponType)
endif
call GroupRemoveUnit(Group,picked)
endloop
static if LIBRARY_IsDestructableTree and thistype.DESTROY_TREES then
set X = .x
set Y = .y
set Radius = .radius*.radius
call SetRect(IsDestructableTree_Region,X - .radius,Y - .radius,X + .radius,Y + .radius)
call EnumDestructablesInRect(IsDestructableTree_Region,null,function thistype.destroyTree)
endif
call SetUnitX( .caster,GetUnitX(.diver) )
call SetUnitY( .caster,GetUnitY(.diver) )
call SetUnitFlyHeight(.caster,GetUnitFlyHeight(.diver),0)
call PauseSystem.unpause(.diver)
call DestroyEffect(.model)
call RecycleDummy(.diver)
call ShowUnit(.caster,true)
call SetUnitInvulnerable(.caster,false)
call PauseSystem.unpause(.caster)
if .rebirth then
call KillUnit(.caster)
endif
else
call PauseSystem.unpause(.diver)
call DestroyEffect(.model)
call RecycleDummy(.diver)
endif
set .model = null
set .diver = null
set .caster = null
endmethod
implement SpellCloner
endstruct
module DiveBombConfigurationCloner
private static method configHandler takes nothing returns nothing
local DiveBomb clone = SpellCloner.configuredInstance
// Configuration by constants
set clone.risePitch = RISE_PITCH
set clone.fallPitch = FALL_PITCH
set clone.castModel = CAST_MODEL
set clone.diverModel = DIVER_MODEL
set clone.diverModelOrigin = DIVER_MODEL_ORIGIN
set clone.crackModel = CRACK_MODEL
set clone.crackModelOrigin = CRACK_MODEL_ORIGIN
set clone.novaModel = NOVA_MODEL
set clone.novaModelOrigin = NOVA_MODEL_ORIGIN
set clone.explosionModel = EXPLOSION_MODEL
set clone.explosionModelOrigin = EXPLOSION_MODEL_ORIGIN
set clone.pickedModel = PICKED_MODEL
set clone.pickedModelOrigin = PICKED_MODEL_ORIGIN
set clone.attackType = ATTACK_TYPE
set clone.damageType = DAMAGE_TYPE
set clone.weaponType = WEAPON_TYPE
// Configuration with levels
set clone.rebirth = rebirth(GetEventSpellLevel())
set clone.height = height(GetEventSpellLevel())
set clone.delay = delay(GetEventSpellLevel())
set clone.riseDelay = riseDelay(GetEventSpellLevel())
set clone.fallDelay = fallDelay(GetEventSpellLevel())
set clone.radius = radius(GetEventSpellLevel())
set clone.damage = damage(GetEventSpellLevel())
static if LIBRARY_Knockback3D then
set clone.knockbackModel = KNOCKBACK_MODEL
set clone.knockbackPower = knockbackPower(GetEventSpellLevel())
set clone.knockbackPitch = knockbackPitch(GetEventSpellLevel())
endif
endmethod
private static method onInit takes nothing returns nothing
call DiveBomb.create(thistype.typeid,ABILITY_ID,SPELL_EVENT_TYPE,function thistype.configHandler)
endmethod
endmodule
endlibrary
library AlarBalletDiveConfiguration uses Alar
private struct Configuration extends array
// Al'ar (Ballet Dive) Ability ID
private static constant integer ABILITY_ID = 'A00C'
// Caster Ability ID casted on picked unit (set to 0 to disable)
private static constant integer CASTER_ABILITY_ID = 'A009'
// Buff ID created by Caster Ability ID
// (set to 0 to no buff or ignore buff-check to cast the CASTER_ABILITY_ID per damage inflicted on trail)
private static constant integer CASTER_BUFF_ID = 'B002'
// Order ID of Caster Ability ID
private static constant integer CASTER_ORDER_ID = 852662
// The release height of the phoenix
private static constant real PHOENIX_HEIGHT = 100.00
// The arc of the dive on trail release
private static constant real PHOENIX_ARC = 0.65
// The animation played of the phoenix to release the trail (set to null to disable)
private static constant string PHOENIX_SPIN_ANIMATION = "spin"
// The model spawned on phoenix position on cast
private static constant string PHOENIX_CREATION_MODEL = "Abilities\\Spells\\Other\\Doom\\DoomDeath.mdl"
// The model spawned on phoenix body on cast
private static constant string PHOENIX_BLAZE_MODEL = "Abilities\\Weapons\\FireBallMissile\\FireBallMissile.mdl"
private static constant method blazeModelOrigin takes integer level, integer index returns string
local string array origin
set origin[1] = "head"
set origin[2] = "chest"
set origin[3] = "hand,left"
set origin[4] = "hand,right"
set origin[5] = "origin"
return origin[index]
endmethod
// The maximum number of index of origin specified in blazeModelOrigin (the minimum index should be 1)
private static constant integer PHOENIX_BLAZE_MODEL_ORIGIN_COUNT = 5
// The model spawned on picked unit when phoenix hits
private static constant string PICKED_MODEL = "Abilities\\Weapons\\FireBallMissile\\FireBallMissile.mdl"
private static constant string PICKED_MODEL_ORIGIN = "chest"
// The model of the trail
private static constant string TRAIL_MODEL = "Abilities\\Spells\\Other\\BreathOfFire\\BreathOfFireDamage.mdl"
private static constant string TRAIL_MODEL_ORIGIN = "origin"
// The attack type of inflicting damage
private static constant attacktype ATTACK_TYPE = ATTACK_TYPE_NORMAL
// The damage type of inflicting damage
private static constant damagetype DAMAGE_TYPE = DAMAGE_TYPE_MAGIC
// The weapon type of inflicting damage
private static constant weapontype WEAPON_TYPE = WEAPON_TYPE_WHOKNOWS
private static constant method phoenixID takes integer level returns integer
return 'h000' // the rawcode of the phoenix unit
endmethod
// Normal vertex coloring of phoenix on return (0-255), 255 is the general value
private static constant method normalRed takes integer level returns integer
return 255
endmethod
private static constant method normalGreen takes integer level returns integer
return 255
endmethod
private static constant method normalBlue takes integer level returns integer
return 255
endmethod
private static constant method normalAlpha takes integer level returns integer
return 255
endmethod
// Alternate vertex coloring of phoenix on arc dive (0-255), 255 is the general value
private static constant method alternateRed takes integer level returns integer
return 255
endmethod
private static constant method alternateGreen takes integer level returns integer
return 255
endmethod
private static constant method alternateBlue takes integer level returns integer
return 255
endmethod
private static constant method alternateAlpha takes integer level returns integer
return 255
endmethod
private static constant method invulnerable takes integer level returns boolean
return true // invulnerable phoenix on arc dive
endmethod
private static constant method duration takes integer level returns real
return 3.00 + (0.00*level) // time it takes for phoenix to complete the arc dive
endmethod
private static constant method range takes integer level returns real
return 1000.00 + (0.00*level) // range of the arc dive
endmethod
private static constant method collision takes integer level returns real
return 100.00 + (0.00*level) // collision of phoenix on arc dive
endmethod
private static constant method damage takes integer level returns real
return 500.00 + (0.00*level) // damage of phoenix collision on arc dive
endmethod
private static constant method delay takes integer level returns real
return 1.00 + (0.00*level) // time it takes for phoenix to return on default height
endmethod
private static constant method trailSize takes integer level returns real
return 3.50 + (0.00*level) // size of the trail
endmethod
private static constant method trailDuration takes integer level returns real
return 15.00 + (0.00*level) // duration of the trail
endmethod
private static constant method trailReleaseInterval takes integer level returns real
return 0.05 + (0.00*level) // interval of releasing trail on arc dive
endmethod
private static constant method trailRadius takes integer level returns real
return 100.00 + (0.00*level) // radius of the trail
endmethod
private static constant method trailDamageInterval takes integer level returns real
return 0.10 + (0.00*level) // interval of damage on each trail
endmethod
private static constant method trailDamage takes integer level returns real
return 30.00 + (0.00*level) // inflicted damage per interval on each trail
endmethod
private static constant integer SPELL_EVENT_TYPE = EVENT_SPELL_EFFECT
implement AlarConfigurationCloner
endstruct
endlibrary
library MegaPyroblastConfiguration uses Pyroblast
/* is using optional custom models
war3mapImported\Pyroblast.mdx
*/
private struct Configuration extends array
// Mega Pyroblast Ability ID
private static constant integer ABILITY_ID = 'A00D'
// The height of the projectile
private static constant real PROJECTILE_HEIGHT = 175.00
// The distance of the projectile
private static constant real PROJECTILE_DISTANCE = 125.00
// The detection range of the projectile
private static constant real PROJECTILE_DETECT_RANGE = 25.00
// The animation played of the caster to launch the projectile (set to null to disable)
private static constant string LAUNCH_ANIMATION = "spell"
// The model of the projectile
private static constant string PROJECTILE_MODEL = "war3mapImported\\Pyroblast.mdx"
private static constant string PROJECTILE_MODEL_ORIGIN = "origin"
// The model spawned on picked unit when projectile hits
private static constant string PICKED_MODEL = "Abilities\\Weapons\\FireBallMissile\\FireBallMissile.mdl"
private static constant string PICKED_MODEL_ORIGIN = "head"
// The attack type of inflicting damage
private static constant attacktype ATTACK_TYPE = ATTACK_TYPE_NORMAL
// The damage type of inflicting damage
private static constant damagetype DAMAGE_TYPE = DAMAGE_TYPE_MAGIC
// The weapon type of inflicting damage
private static constant weapontype WEAPON_TYPE = WEAPON_TYPE_WHOKNOWS
private static constant method duration takes integer level returns real
return 3.00 + (0.00*level) // duration of the channeling time
endmethod
private static constant method animation takes integer level returns real
return 0.75 + (0.00*level) // remaining duration to play launch animation
endmethod
private static constant method startSize takes integer level returns real
return 0.00 + (0.00*level) // starting size of the projectile
endmethod
private static constant method endSize takes integer level returns real
return 1.75 + (0.00*level) // ending size of the projectile
endmethod
private static constant method velocity takes integer level returns real
return 5.00 + (0.00*level) // speed of the projectile
endmethod
private static constant method collision takes integer level returns real
return 300.00 + (0.00*level) // collision of the projectile
endmethod
private static constant method radius takes integer level returns real
return 500.00 + (0.00*level) // radius of the explosion
endmethod
private static constant method periodicDamage takes integer level returns real
return 7.50 + (0.00*level) // periodic damage of the projectile on collision
endmethod
private static constant method explosionDamage takes integer level returns real
return 600.00 + (0.00*level) // explosion damage of the projectile on radius
endmethod
private static constant integer SPELL_EVENT_TYPE = EVENT_SPELL_CHANNEL + EVENT_SPELL_ENDCAST
implement PyroblastConfigurationCloner
endstruct
endlibrary
library PillarBombConfiguration uses FlameStrike optional FelInfusionBuffLibrary optional LivingBombBuffLibrary
/* is using optional custom models
war3mapImported\Flamestrike Indicator.mdx
war3mapImported\Flamestrike.mdx
war3mapImported\Flamestrike Indicator Fel.mdx
war3mapImported\Flamestrike Fel.mdx
*/
private struct Configuration extends array
// Pillar Bomb Ability ID
private static constant integer ABILITY_ID = 'A00E'
// The model of the indicator
private static constant string FLAME_INDICATOR_MODEL = "war3mapImported\\Flamestrike Indicator.mdx"
private static constant string FLAME_INDICATOR_MODEL_ORIGIN = "origin"
// The model of the explosion
private static constant string FLAME_STRIKE_MODEL = "war3mapImported\\Flamestrike.mdx"
private static constant string FLAME_STRIKE_MODEL_ORIGIN = "origin"
// The model spawned on picked unit when explosion
private static constant string FLAME_PICKED_MODEL = "Abilities\\Weapons\\FireBallMissile\\FireBallMissile.mdl"
private static constant string FLAME_PICKED_MODEL_ORIGIN = "origin"
// The attack type of inflicting damage
private static constant attacktype FLAME_ATTACK_TYPE = ATTACK_TYPE_NORMAL
// The damage type of inflicting damage
private static constant damagetype FLAME_DAMAGE_TYPE = DAMAGE_TYPE_MAGIC
// The weapon type of inflicting damage
private static constant weapontype FLAME_WEAPON_TYPE = WEAPON_TYPE_WHOKNOWS
private static constant method flameIndicatorSize takes integer level returns real
return 1.50 + (0.00*level) // size of the indicator
endmethod
private static constant method flameDelay takes integer level returns real
return 2.00 - (0.00*level) // delay of the explosion
endmethod
private static constant method flameSize takes integer level returns real
return 1.40 + (0.00*level) // size of the explosion
endmethod
private static constant method flameRadius takes integer level returns real
return 300.00 + (0.00*level) // radius of the explosion
endmethod
private static constant method flameDamage takes integer level returns real
return 300.00 + (0.00*level) // damage of the explosion
endmethod
static if LIBRARY_LivingBombBuffLibrary then
private static constant method flameLivingBomb takes integer level returns boolean
return true // explosion inflicts living bomb on picked unit
endmethod
private static constant method flameChainBomb takes integer level returns boolean
return false // explosion inflicts chain bomb on picked unit
endmethod
private static constant method flameBombDuration takes integer level returns real
return 5.00 - (0.00*level) // duration of the inflicted bomb on picked unit
endmethod
private static constant method flameBombPeriodicDamage takes integer level returns real
return 3.00 + (0.00*level) // periodic damage of the inflicted bomb on picked unit
endmethod
private static constant method flameBombExplosionDamage takes integer level returns real
return 200.00 + (0.00*level) // explosion damage of the inflicted bomb on picked unit
endmethod
endif
static if LIBRARY_FelInfusionBuffLibrary then
// Make Pillar Bomb empowered by Fel Infusion?
private static constant boolean EMPOWERMENT = true
// The model of the indicator
private static constant string FEL_INDICATOR_MODEL = "war3mapImported\\Flamestrike Indicator Fel.mdx"
private static constant string FEL_INDICATOR_MODEL_ORIGIN = "origin"
// The model of the explosion
private static constant string FEL_STRIKE_MODEL = "war3mapImported\\Flamestrike Fel.mdx"
private static constant string FEL_STRIKE_MODEL_ORIGIN = "origin"
// The model spawned on picked unit when explosion
private static constant string FEL_PICKED_MODEL = "Abilities\\Weapons\\DemonHunterMissile\\DemonHunterMissile.mdl"
private static constant string FEL_PICKED_MODEL_ORIGIN = "origin"
// The attack type of inflicting damage
private static constant attacktype FEL_ATTACK_TYPE = ATTACK_TYPE_CHAOS
// The damage type of inflicting damage
private static constant damagetype FEL_DAMAGE_TYPE = DAMAGE_TYPE_UNIVERSAL
// The weapon type of inflicting damage
private static constant weapontype FEL_WEAPON_TYPE = WEAPON_TYPE_WHOKNOWS
private static constant method felIndicatorSize takes integer level returns real
return 1.50 + (0.00*level) // size of the indicator
endmethod
private static constant method felDelay takes integer level returns real
return 2.00 - (0.00*level) // delay of the explosion
endmethod
private static constant method felSize takes integer level returns real
return 1.40 + (0.00*level) // size of the explosion
endmethod
private static constant method felRadius takes integer level returns real
return 300.00 + (0.00*level) // radius of the explosion
endmethod
private static constant method felDamage takes integer level returns real
return 300.00 + (0.00*level) // damage of the explosion
endmethod
static if LIBRARY_LivingBombBuffLibrary then
private static constant method felLivingBomb takes integer level returns boolean
return false // explosion inflicts living bomb on picked unit
endmethod
private static constant method felChainBomb takes integer level returns boolean
return true // explosion inflicts chain bomb on picked unit
endmethod
private static constant method felBombDuration takes integer level returns real
return 5.00 - (0.00*level) // duration of the inflicted bomb on picked unit
endmethod
private static constant method felBombPeriodicDamage takes integer level returns real
return 3.00 + (0.00*level) // periodic damage of the inflicted bomb on picked unit
endmethod
private static constant method felBombExplosionDamage takes integer level returns real
return 100.00 + (0.00*level) // explosion damage of the inflicted bomb on picked unit
endmethod
endif
endif
private static constant integer SPELL_EVENT_TYPE = EVENT_SPELL_EFFECT
implement FlameStrikeConfigurationCloner
endstruct
endlibrary
library GravityLapsePolymorphConfiguration uses GravityLapse optional FelInfusionBuffLibrary
/* is using optional custom models
war3mapImported\\GravityLapseSummoning Circle.mdl
war3mapImported\\NetherBanishSummoning Circle.mdl
*/
private struct Configuration extends array
// Gravity Lapse (Polymorph) Ability ID
private static constant integer ABILITY_ID = 'A00G'
// Caster Ability ID casted on picked unit (set to 0 to disable)
private static constant integer GRAVITY_CASTER_ABILITY_ID = 'A00J'
// Buff ID created by Caster Ability ID
private static constant integer GRAVITY_CASTER_BUFF_ID = 'B003'
// Order ID of Caster Ability ID
private static constant integer GRAVITY_CASTER_ORDER_ID = 852074
// The model of the indicator
private static constant string GRAVITY_MODEL = "war3mapImported\\GravityLapseSummoning Circle.mdl"
private static constant string GRAVITY_MODEL_ORIGIN = "origin"
// The model spawned on picked unit
private static constant string GRAVITY_PICKED_MODEL = "Abilities\\Spells\\NightElf\\Blink\\BlinkCaster.mdl"
// The model spawned on picked unit when levitated
private static constant string GRAVITY_LEVITATED_MODEL = "Abilities\\Spells\\Human\\ManaFlare\\ManaFlareTarget.mdl"
private static constant string GRAVITY_LEVITATED_MODEL_ORIGIN = "origin"
// The model spawned on picked unit when landed
private static constant string GRAVITY_LANDED_MODEL = "Abilities\\Weapons\\AncientProtectorMissile\\AncientProtectorMissile.mdl"
private static constant string GRAVITY_LANDED_MODEL_ORIGIN = "origin"
// The attack type of inflicting damage
private static constant attacktype GRAVITY_ATTACK_TYPE = ATTACK_TYPE_NORMAL
// The damage type of inflicting damage
private static constant damagetype GRAVITY_DAMAGE_TYPE = DAMAGE_TYPE_MAGIC
// The weapon type of inflicting damage
private static constant weapontype GRAVITY_WEAPON_TYPE = WEAPON_TYPE_WHOKNOWS
private static constant method gravitySize takes integer level returns real
return 2.00 + (0.00*level) // size of the indicator
endmethod
private static constant method gravityHeight takes integer level returns real
return 300.00 + (0.00*level) // height to be reached of picked unit to disable its movement
endmethod
private static constant method gravityDelay takes integer level returns real
return 1.50 + (0.00*level) // delay to be reached of picked unit to disable its movement
endmethod
private static constant method gravityLevitateHeight takes integer level returns real
return -100.00 - (0.00*level) // certain height from the reached height where to levitate up and down.
endmethod
private static constant method gravityLevitateDuration takes integer level returns real
return 1.00 + (0.00*level) // duration of levitating from the reached height to the certain height.
endmethod
private static constant method gravityDuration takes integer level returns real
return 5.00 + (0.00*level) // duration of the ability
endmethod
private static constant method gravityRadius takes integer level returns real
return 325.00 + (0.00*level) // radius of the ability
endmethod
private static constant method gravityLandSpeed takes integer level returns real
return 550.00 + (0.00*level) // landing speed of picked unit after duration ends
endmethod
private static constant method gravityExposure takes integer level returns real
return 150.00 + (0.00*level) // required height of unit on air for eligible damage
endmethod
private static constant method gravityDamage takes integer level returns real
return 50.00 + (0.00*level) // inflicting damage on picked unit when landed
endmethod
static if LIBRARY_FelInfusionBuffLibrary then
// Make Gravity Lapse empowered by Fel Infusion?
private static constant boolean EMPOWERMENT = true
// Caster Ability ID casted on picked unit (set to 0 to disable)
private static constant integer NETHER_CASTER_ABILITY_ID = 'A000'
// Buff ID created by Caster Ability ID
private static constant integer NETHER_CASTER_BUFF_ID = 'B001'
// Order ID of Caster Ability ID
private static constant integer NETHER_CASTER_ORDER_ID = 852486
// The model of the indicator
private static constant string NETHER_MODEL = "war3mapImported\\NetherBanishSummoning Circle.mdl"
private static constant string NETHER_MODEL_ORIGIN = "origin"
// The model spawned on picked unit
private static constant string NETHER_PICKED_MODEL = "Abilities\\Weapons\\GreenDragonMissile\\GreenDragonMissile.mdl"
// The model spawned on picked unit when levitated
private static constant string NETHER_LEVITATED_MODEL = "Abilities\\Spells\\Human\\ManaFlare\\ManaFlareBase.mdl"
private static constant string NETHER_LEVITATED_MODEL_ORIGIN = "origin"
// The model spawned on picked unit when landed
private static constant string NETHER_LANDED_MODEL = "Abilities\\Weapons\\AncientProtectorMissile\\AncientProtectorMissile.mdl"
private static constant string NETHER_LANDED_MODEL_ORIGIN = "origin"
// The attack type of inflicting damage
private static constant attacktype NETHER_ATTACK_TYPE = ATTACK_TYPE_NORMAL
// The damage type of inflicting damage
private static constant damagetype NETHER_DAMAGE_TYPE = DAMAGE_TYPE_MAGIC
// The weapon type of inflicting damage
private static constant weapontype NETHER_WEAPON_TYPE = WEAPON_TYPE_WHOKNOWS
private static constant method netherSize takes integer level returns real
return 2.00 + (0.00*level) // size of the indicator
endmethod
private static constant method netherHeight takes integer level returns real
return 300.00 + (0.00*level) // height to be reached of picked unit to disable its movement
endmethod
private static constant method netherDelay takes integer level returns real
return 1.50 + (0.00*level) // delay to be reached of picked unit to disable its movement
endmethod
private static constant method netherLevitateHeight takes integer level returns real
return -100.00 - (0.00*level) // certain height from the reached height where to levitate up and down.
endmethod
private static constant method netherLevitateDuration takes integer level returns real
return 1.00 + (0.00*level) // duration of levitating from the reached height to the certain height.
endmethod
private static constant method netherDuration takes integer level returns real
return 8.00 + (0.00*level) // duration of the ability
endmethod
private static constant method netherRadius takes integer level returns real
return 325.00 + (0.00*level) // radius of the ability
endmethod
private static constant method netherLandSpeed takes integer level returns real
return 550.00 + (0.00*level) // landing speed of picked unit after duration ends
endmethod
private static constant method netherExposure takes integer level returns real
return 150.00 + (0.00*level) // required height of unit on air for eligible damage
endmethod
private static constant method netherDamage takes integer level returns real
return 50.00 + (0.00*level) // inflicting damage on picked unit when landed
endmethod
endif
private static constant integer SPELL_EVENT_TYPE = EVENT_SPELL_EFFECT
implement GravityLapseConfigurationCloner
endstruct
endlibrary
library InstantBombConfiguration uses LivingBomb optional FelInfusionBuffLibrary
private struct Configuration extends array
// Instant Bomb Ability ID
private static constant integer ABILITY_ID = 'A00F'
private static constant method explode takes integer level returns boolean
return false // explodes existing bomb on target unit
endmethod
private static constant method flameLivingBomb takes integer level returns boolean
return true // inflicts living bomb on target unit
endmethod
private static constant method flameChainBomb takes integer level returns boolean
return false // inflicts chain bomb on target unit
endmethod
private static constant method flameBombDuration takes integer level returns real
return LivingBombBuffLibrary_LivingBombBuff.INTERVAL // duration of the inflicted bomb on target unit
endmethod
private static constant method flameBombPeriodicDamage takes integer level returns real
return 0.00 + (0.00*level) // periodic damage of the inflicted bomb on target unit
endmethod
private static constant method flameBombExplosionDamage takes integer level returns real
return 200.00 + (0.00*level) // explosion damage of the inflicted bomb on target unit
endmethod
static if LIBRARY_FelInfusionBuffLibrary then
// Make Living Bomb empowered by Fel Infusion?
private static constant boolean EMPOWERMENT = true
private static constant method felLivingBomb takes integer level returns boolean
return false // inflicts living bomb on target unit
endmethod
private static constant method felChainBomb takes integer level returns boolean
return true // inflicts chain bomb on target unit
endmethod
private static constant method felBombDuration takes integer level returns real
return LivingBombBuffLibrary_LivingBombBuff.INTERVAL // duration of the inflicted bomb on target unit
endmethod
private static constant method felBombPeriodicDamage takes integer level returns real
return 0.00 + (0.00*level) // periodic damage of the inflicted bomb on target unit
endmethod
private static constant method felBombExplosionDamage takes integer level returns real
return 100.00 + (0.00*level) // explosion damage of the inflicted bomb on target unit
endmethod
endif
private static constant integer SPELL_EVENT_TYPE = EVENT_SPELL_EFFECT
implement LivingBombConfigurationCloner
endstruct
endlibrary