Name | Type | is_array | initial_value |
AfterDamageEvent | real | No | |
AOEDamageEvent | real | No | |
ClearDamageEvent | trigger | No | |
DAMAGE_FACTOR_BRACERS | real | No | |
DAMAGE_FACTOR_ELUNES | real | No | |
DAMAGE_FACTOR_ETHEREAL | real | No | |
DamageBlockingAbility | abilcode | No | |
DamageEvent | real | No | |
DamageEventAmount | real | No | |
DamageEventAOE | integer | No | |
DamageEventAOEGroup | group | No | |
DamageEventLevel | integer | No | |
DamageEventOverride | boolean | No | |
DamageEventPrevAmt | real | No | |
DamageEventSource | unit | No | |
DamageEventsWasted | integer | No | |
DamageEventTarget | unit | No | |
DamageEventTrigger | trigger | No | |
DamageEventType | integer | No | |
DamageModifierEvent | real | No | |
DamageTypeBlocked | integer | No | |
DamageTypeCriticalStrike | integer | No | |
DamageTypeExplosive | integer | No | |
DamageTypeHeal | integer | No | |
DamageTypeReduced | integer | No | |
DmgEvBracers | itemcode | No | |
DmgEvRecursionN | integer | No | |
DmgEvRunning | boolean | No | |
DmgEvStarted | boolean | No | |
DmgEvTimer | timer | No | |
DmgEvTrig | trigger | No | |
EnhancedDamageTarget | unit | No | |
HideDamageFrom | boolean | Yes | |
IsDamageSpell | boolean | No | |
LastDamageHP | real | No | |
LastDmgPrevAmount | real | Yes | |
LastDmgPrevType | integer | Yes | |
LastDmgSource | unit | Yes | |
LastDmgTarget | unit | Yes | |
LastDmgValue | real | Yes | |
LastDmgWasSpell | boolean | Yes | |
NextDamageOverride | boolean | No | |
NextDamageType | integer | No | |
SpellDamageAbility | abilcode | No | |
UDex | integer | No | |
UDexGen | integer | No | |
UDexNext | integer | Yes | |
UDexPrev | integer | Yes | |
UDexRecycle | integer | No | |
UDexUnits | unit | Yes | |
UDexWasted | integer | No | |
UnitDamageRegistered | boolean | Yes | |
UnitIndexerEnabled | boolean | No | |
UnitIndexEvent | real | No | |
UnitIndexLock | integer | Yes |
//TESH.scrollpos=51
//TESH.alwaysfold=0
library UnitIndexerGUI //requires GUI Unit Indexer
globals
private trigger index = null
private trigger deindex = null
private trigger init = null
private trigger tempTrig
endglobals
private function DoTheThings takes trigger t, code c, real r returns trigger
if t == null then
set t = CreateTrigger()
call TriggerRegisterVariableEvent(t, "udg_UnitIndexEvent", EQUAL, r)
endif
call TriggerAddCondition(t, Filter(c))
set tempTrig = t
set t = null
return tempTrig
endfunction
//call RegisterUnitIndexEvent(Filter(function Blah), EVENT_UNIT_INDEXED)
//->
//call OnUnitIndex(function Blah)
function OnUnitIndex takes code c returns nothing
set index = DoTheThings(index, c, 1.)
endfunction
//call RegisterUnitIndexEvent(Filter(function Blah), EVENT_UNIT_DEINDEXED)
//->
//call OnUnitDeindex(function Blah)
function OnUnitDeindex takes code c returns nothing
set deindex = DoTheThings(deindex, c, 2.)
endfunction
private function DestroyInit takes nothing returns boolean
call DestroyTrigger(init) //will still allow consecutive triggerconditions to run.
set init = null
return false
endfunction
//GUI Unit Indexer will initialize after any vJass stuff, so you need to do your
//unit-indexer-requiring stuff after this event instead of a module/struct/library
//initializer.
function OnUnitIndexerInitialized takes code c returns nothing
if init == null then
set init = CreateTrigger()
call TriggerAddCondition(init, Filter(function DestroyInit))
call TriggerRegisterVariableEvent(init, "udg_UnitIndexEvent", EQUAL, 3.00)
endif
call TriggerAddCondition(init, Filter(c))
endfunction
function GetUnitId takes unit u returns integer
return GetUnitUserData(u)
endfunction
function GetUnitById takes integer id returns unit
return udg_UDexUnits[id]
endfunction
function GetIndexedUnit takes nothing returns unit
return udg_UDexUnits[udg_UDex]
endfunction
function GetIndexedUnitId takes nothing returns integer
return udg_UDex
endfunction
function IsUnitIndexed takes unit u returns boolean
return udg_UDexUnits[GetUnitUserData(u)] == u
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library Table /* made by Bribe, special thanks to Vexorian & Nestharus, version 4.1.0.1.
One map, one hashtable. Welcome to NewTable 4.1.0.1
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: Added 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
//Added in version 4.1
method has takes integer index returns boolean
return Table(this).has(index)
endmethod
//HashTables are just fancy Table indices.
method destroy takes nothing returns nothing
call Table(this).destroy()
endmethod
//Like I said above...
static method create takes nothing returns thistype
return Table.create()
endmethod
endstruct
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library Missiles requires WorldBounds, optional DummyRecycler
/* ----------------------- Missiles v1.5 by Chopinski ----------------------- */
// Thanks and Full Credits to BPower, Dirac and Vexorian for the Missile Library's at which i based
// this Missiles library. Credits to Vexorian for the dummy model.
// How to Import:
// 1 -First copy the Missile dummy unit into your map and then import the dummy.mdx
// model, setting the missile dummy model path to imported dummy.mdx model.
// Dummy model: https://www.hiveworkshop.com/threads/vexorians-dummy-model.149230/
// WorldBounds: https://github.com/nestharus/JASS/blob/master/jass/Systems/WorldBounds/script.j
// DummyRecycler: https://www.hiveworkshop.com/threads/dummy-recycler-v1-25.277659/ (Highly Recommended)
// 2 - Copy this library into your map and set the
// DUMMY_RAW_CODE to the raw code of the missile dummy (ctrl + d) and you
// are done
// How to Use:
// This system works almost identicaly to the Missile library by BPower but
// with a more user friendly interface in my opinion. Also this system
// allows you to create Arc/Curved Homing missiles like Dirac's system.
// Differently than both BPower and Dirac systems, you are not required
// to use the ImplementStruct at the end of your struct. To make your struct
// behave like a Missile simply make it extends Missiles and you are done.
// After that you will have access to the events in the MissileEvents Interface.
// Simply declare the event you want for your strcut to have and the system
// takes care of the rest. You can access the members and functions using
// the "this" or "." syntax which is a plus. to terminate a missile, simply
// return true or call terminate() within the method. Example:
// struct MySpell extends Missiles
// method onPeriod takes nothing returns boolean
// // will display the missile current position
// call ClearTextMessages()
// call BJDebugMsg(I2S(.x))
// call BJDebugMsg(I2S(.y))
// call BJDebugMsg(I2S(.z))
// return false
// endmethod
// endstruct
// function Spell takes nothing returns nothing
// // the create method takes the initial and the final coordinates
// // if the target member is set, the missile home
// local MySpell spell = MySpell.create(fromX, fromY, fromZ, toX, toY, toZ)
// set spell.source = GetTriggerUnit()
// set spell.target = GetSpellTargetUnit() -> when a target is specified the missile will be homing
// set spell.speed = 1000 -> warcraft 3 speed unit
// set spell.duration = 1.5 -> will set the speed to match the time passed
// set spell.model = "Some Model.dmx"
// set spell.scale = 1.3
// set spell.arc = 30 (degrees converted to radians internally)
// set spell.curve = GetRandomReal(-40, 40) (degrees converted to radians internally
// call spell.launch()
// endfunction
// Available members and methods:
// Coordinates impact -> the impact coordiantes (x, y, z, anngle, slope, alpha, distance)
// Coordinates origin -> same, but for origin
// //-------------------------------------------------------
// readonly real x -> current x position of the missile
// readonly real y -> current y position of the missile
// readonly real z -> current z position of the missile
// readonly real prevX -> last x position of the missile
// readonly real prevY -> last y position of the missile
// readonly real prevZ -> last z position of the missile
// readonly real height -> the arc of the missile (change it using the .arc operator)
// readonly real turn -> the turn rate of the missile
// readonly real open -> the curvature of the missile (change it using the .curve operator)
// readonly real veloc -> the missile speed (change it using the .speed or .duration operator)
// readonly real travel -> distance travelled
// readonly unit dummy -> the dummy unit
// readonly boolean launched -> true if the missile was already launched
// readonly boolean allocated -> true if the missile can still perform its movement operations
// //-------------------------------------------------------
// unit source -> the source unit (optional)
// unit target -> the target unit (optional and if set to a valid unit the missile will be homing)
// player owner -> the owner of the missile (optional)
// boolean collideZ -> set to true if you want the missile to consider z in collisions
// real collision -> the collision size of the missile (optional. The onHit and onDestructable events only works if this is greater than 0)
// real damage -> stores the damage you want the missile to deal (optional)
// real acceleration -> if different than 0 then the missile will change its speed with time (optional)
// integer data -> just in case you want to pass some information to the missile to retrieve it later (optional)
// (call)
// method deflect takes real x, real y returns nothing
// - Deflects the missile from the target point, changing
// - it's course to the new x and y.
// - If target is a valid unit, deflect will null the target
// - and then deflects the missile
// (call)
// method deflectZ takes real x, real y, real z returns nothing
// - Deflects the missile from the target point, changing
// - it's course to the new x, y and z.
// - If target is a valid unit, deflect will null the target
// - and then deflects the missile
// (call)
// method bounce takes nothing returns nothing
// - Bounces the missile from it's current Z position.
// - Useful when assigning targets to missiles that were
// - already active, it fixes the rough Z position change.
// (call)
// method flush takes widget w returns nothing
// - call this method to allow the missile to be able to hit
// - a unit or destructable again
// (call)
// method flushAll nothing returns nothing
// - flushes the hit table for the missile
// (call)
// method hitted takes widget w returns boolean
// - returns true if the missile has hitted the widget
// (optional)
// method onHit takes unit hit returns boolean
// - Runs every time the missile collides with a unit.
// - If returns true the missile is destroyed.
// (optional)
// method onDestructable takes destructable dest returns boolean
// - Runs every time the missile collides with a destructable.
// - If returns true the missile is destroyed.
// (optional)
// method onItem takes item i returns boolean
// - Runs every time the missile collides with an item.
// - If returns true the missile is destroyed.
// (optional)
// method onMissile takes Missiles missile returns boolean
// - Runs every time the missile collides with another missile.
// - By default, missiles collide only once
// - If returns true the missile is destroyed.
// - Please, be aware that this method can be very performance
// - intensive, so careful!
// (optional)
// method onPeriod takes nothing returns boolean
// - Runs every period.
// - If returns true the missile is destroyed.
// (optional)
// method onFinish takes nothing returns boolean
// - Runs whenever the missile finishes it's course.
// - If returns true the missile is destroyed.
// (optional)
// method onTerrain takes nothing returns boolean
// - Runs whenever the missile collides with terrain height
// - greater then the missile current z
// - If returns true the missile is destroyed.
// (optional)
// method onRemove takes nothing returns nothing
// - Runs whenever the missile is deallocated.
// (optional)
// method onBoundaries takes nothing returns boolean
// - Runs whenever the missile is trying to move
// - out of map bounds.
// /* ----------------------------------- END ---------------------------------- */
/* --------------------------------- System --------------------------------- */
globals
// The raw code of the dummy unit. Match it with the
// Object Editor id
private constant integer DUMMY_RAW_CODE = 'dum0'
// The update period of the system
public constant real PERIOD = 1./40.
// The max amount of Missiles processed in a PERIOD
// You can play around with both these values to find
// your sweet spot. If equal to 0, the system will
// process all missiles at once every period.
public constant real SWEET_SPOT = 150
// the avarage collision size compensation when detecting
// collisions
private constant real COLLISION_SIZE = 128.
// item size used in z collision
private constant real ITEM_SIZE = 16.
// uint size used in z collision
private constant real BODY_SIZE = 64.
// How long takes for the missile to be removed.
// This is necessary so the death animation of the
// effect can play through
private constant real RECYCLE_TIME = 2.
// Needed, dont touch. Seriously, dont touch!
private location LOC = Location(0., 0.)
private rect RECT = Rect(0., 0., 0., 0.)
private hashtable table = InitHashtable()
endglobals
// The available methods the user can implement in their structs
private interface MissileEvents
method onHit takes unit hit returns boolean defaults false
method onItem takes item i returns boolean defaults false
method onMissile takes Missiles missile returns boolean defaults false
method onTerrain takes nothing returns boolean defaults false
method onPeriod takes nothing returns boolean defaults false
method onFinish takes nothing returns boolean defaults false
method onDestructable takes destructable dest returns boolean defaults false
method onBoundaries takes nothing returns boolean defaults false
method onRemove takes nothing returns nothing defaults nothing
endinterface
function GetLocZ takes real x, real y returns real
call MoveLocation(LOC, x, y)
return GetLocationZ(LOC)
endfunction
//Little snippet to remove the dummy unit after a delay
//I'm not using MissileRecycler because of a leaking problem i discover
//This will only be used if you dont have DummyRecycler
private struct Timed
timer t
unit u
static method onPeriod takes nothing returns nothing
local thistype this = LoadInteger(table, GetHandleId(GetExpiredTimer()), 0)
call FlushChildHashtable(table, GetHandleId(.t))
call PauseTimer(.t)
call DestroyTimer(.t)
call RemoveUnit(.u)
set .u = null
set .t = null
call .deallocate()
endmethod
static method Recycle takes unit u, real timeout returns nothing
local thistype this = thistype.allocate()
set .u = u
set .t = CreateTimer()
call SaveInteger(table, GetHandleId(.t), 0, this)
call TimerStart(.t, timeout, false, function thistype.onPeriod)
endmethod
endstruct
//Credits to Dirac for AdvLoc and BPower for fixing an error in it
private struct Coordinates
readonly real x
readonly real y
readonly real z
readonly real angle
readonly real distance
readonly real square
readonly real slope
readonly real alpha
// Creates an origin - impact link.
private thistype ref
private static method math takes thistype a, thistype b returns nothing
local real dx
local real dy
loop
set dx = b.x - a.x
set dy = b.y - a.y
set dx = dx*dx + dy*dy
set dy = SquareRoot(dx)
exitwhen dx != 0. and dy != 0.
set b.x = b.x + .01
set b.z = b.z - GetLocZ(b.x -.01, b.y) + GetLocZ(b.x, b.y)
endloop
set a.square = dx
set a.distance = dy
set a.angle = Atan2(b.y - a.y, b.x - a.x)
set a.slope = (b.z - a.z)/dy
set a.alpha = Atan(a.slope)*bj_RADTODEG
// Set b.
if b.ref == a then
set b.angle = a.angle + bj_PI
set b.distance = dy
set b.slope = -a.slope
set b.alpha = -a.alpha
set b.square = dx
endif
endmethod
static method link takes thistype a, thistype b returns nothing
set a.ref = b
set b.ref = a
call math(a, b)
endmethod
method move takes real toX, real toY, real toZ returns nothing
set x = toX
set y = toY
set z = toZ + GetLocZ(toX, toY)
if ref != this then
call math(this, ref)
endif
endmethod
method destroy takes nothing returns nothing
call .deallocate()
endmethod
static method create takes real x, real y, real z returns Coordinates
local thistype this = thistype.allocate()
set ref = this
call move(x, y, z)
return this
endmethod
endstruct
/* ------------------------------ Missile Sytem ----------------------------- */
struct Missiles extends MissileEvents
private static thistype array missiles
private static integer didx = -1
private static integer last = 0
private static real dilation
private static timer t = CreateTimer()
private static group hitGroup = CreateGroup()
private static thistype temp = 0
//-------------------------------------------------------
private real cA // current angle
private effect sfx // effect
private string fxpath // model
private real size // scale
private real height // Arcs
private real open // Curves
private real toZ // Necessary to fix a bug for homming missiles
//-------------------------------------------------------
Coordinates impact
Coordinates origin
//-------------------------------------------------------
readonly real x
readonly real y
readonly real z
readonly real prevX
readonly real prevY
readonly real prevZ
readonly real turn
readonly real veloc
readonly real travel
readonly unit dummy
readonly boolean launched
readonly boolean allocated
//-------------------------------------------------------
unit source
unit target
player owner
boolean collideZ
real collision
real damage
real acceleration
integer data
//-------------------------------------------------------
/* -------------------------- Model of the missile -------------------------- */
method operator model= takes string path returns nothing
call DestroyEffect(sfx)
set fxpath = path
set sfx = AddSpecialEffectTarget(path, dummy, "origin")
endmethod
method operator model takes nothing returns string
return fxpath
endmethod
/* ----------------------------- Curved movement ---------------------------- */
method operator curve= takes real value returns nothing
set open = Tan(value*bj_DEGTORAD)*origin.distance
endmethod
method operator curve takes nothing returns real
return Atan(open/origin.distance)
endmethod
/* ----------------------------- Arced Movement ----------------------------- */
method operator arc= takes real value returns nothing
set height = Tan(value*bj_DEGTORAD)*origin.distance/4
endmethod
method operator arc takes nothing returns real
return Atan(4*height/origin.distance)
endmethod
/* ------------------------------ Effect scale ------------------------------ */
method operator scale= takes real v returns nothing
call SetUnitScale(dummy, v, v, v)
set size = v
endmethod
method operator scale takes nothing returns real
return size
endmethod
/* ------------------------------ Missile Speed ----------------------------- */
method operator speed= takes real newspeed returns nothing
set veloc = newspeed*PERIOD
endmethod
method operator speed takes nothing returns real
return veloc
endmethod
/* ------------------------------- Flight Time ------------------------------ */
method operator duration= takes real flightTime returns nothing
set veloc = RMaxBJ(0.00000001, (origin.distance - travel)*PERIOD/RMaxBJ(0.00000001, flightTime))
endmethod
/* ---------------------------- Bound and Deflect --------------------------- */
method bounce takes nothing returns nothing
local real locZ = GetLocZ(x, y)
// This is here just to avoid an infinite loop
// with a deflect being called within an onTerrain
// event
if z < locZ then
set z = locZ
call impact.move(impact.x, impact.y, origin.z - GetLocZ(impact.x, impact.y))
endif
call origin.move(x, y, origin.z - GetLocZ(origin.x, origin.y))
set travel = 0
endmethod
method deflect takes real tx, real ty returns nothing
if target != null then
set target = null
endif
call impact.move(tx, ty, impact.z - GetLocZ(impact.x, impact.y))
call bounce()
endmethod
method deflectZ takes real tx, real ty, real tz returns nothing
call impact.move(impact.x, impact.y, tz)
call deflect(tx, ty)
endmethod
/* ---------------------------- Flush hit targets --------------------------- */
method flushAll takes nothing returns nothing
call FlushChildHashtable(table, this)
endmethod
method flush takes widget w returns nothing
if w != null then
call RemoveSavedBoolean(table, this, GetHandleId(w))
endif
endmethod
method hitted takes widget w returns boolean
return HaveSavedBoolean(table, this, GetHandleId(w))
endmethod
/* ------------------------- Destructable hit method ------------------------ */
private static method onDest takes nothing returns nothing
local thistype this = temp
local destructable d = GetEnumDestructable()
local real dz
local real tz
if not HaveSavedBoolean(table, this, GetHandleId(d)) then
if collideZ then
set dz = GetLocZ(GetWidgetX(d), GetWidgetY(d)) - GetLocZ(x, y)
set tz = GetDestructableOccluderHeight(d)
if dz + tz >= z - collision and dz <= z + collision then
call SaveBoolean(table, this, GetHandleId(d), true)
if allocated and .onDestructable(d) then
set d = null
call terminate()
return
endif
endif
else
call SaveBoolean(table, this, GetHandleId(d), true)
if allocated and .onDestructable(d) then
set d = null
call terminate()
return
endif
endif
endif
set d = null
endmethod
/* -------------------------- Item collision method ------------------------- */
private static method onItems takes nothing returns nothing
local thistype this = temp
local item i = GetEnumItem()
local real dz
if not HaveSavedBoolean(table, this, GetHandleId(i)) then
if collideZ then
set dz = GetLocZ(GetItemX(i), GetItemY(i)) - GetLocZ(x, y)
if dz + ITEM_SIZE >= z - collision and dz <= z + collision then
call SaveBoolean(table, this, GetHandleId(i), true)
if allocated and .onItem(i) then
set i = null
call terminate()
return
endif
endif
else
call SaveBoolean(table, this, GetHandleId(i), true)
if allocated and .onItem(i) then
set i = null
call terminate()
return
endif
endif
endif
set i = null
endmethod
/* ------------------------------ Reset members ----------------------------- */
private method reset takes nothing returns nothing
set launched = false
set collideZ = false
set source = null
set target = null
set owner = null
set sfx = null
set dummy = null
set fxpath = ""
set open = 0.
set height = 0.
set veloc = 0.
set acceleration = 0.
set collision = 0.
set damage = 0.
set travel = 0.
set turn = 0.
set size = 0.
set data = 0
endmethod
/* -------------------------------- Terminate ------------------------------- */
method terminate takes nothing returns nothing
if allocated then
set allocated = false
// onRemove event
if .onRemove.exists then
call .onRemove()
endif
call FlushChildHashtable(table, this)
endif
endmethod
/* -------------------------- Destroys the missile -------------------------- */
method remove takes integer i returns integer
call terminate()
call DestroyEffect(sfx)
call origin.destroy()
call impact.destroy()
static if LIBRARY_DummyRecycler then
call DummyAddRecycleTimer(dummy, RECYCLE_TIME)
else
call Timed.Recycle(dummy, RECYCLE_TIME)
endif
call reset()
set missiles[i] = missiles[didx]
set didx = didx - 1
//Compensation for time dilation
if didx + 1 > SWEET_SPOT and SWEET_SPOT > 0 then
set dilation = (didx + 1)/SWEET_SPOT
else
set dilation = 1
endif
if didx == -1 then
call PauseTimer(t)
endif
call deallocate()
return i - 1
endmethod
/* --------------------------- Launch the Missile --------------------------- */
method launch takes nothing returns nothing
if not launched and allocated then
set launched = true
set didx = didx + 1
set missiles[didx] = this
//Compensation for time dilation
if didx + 1 > SWEET_SPOT and SWEET_SPOT > 0 then
set dilation = (didx + 1)/SWEET_SPOT
else
set dilation = 1.
endif
if didx == 0 then
call TimerStart(t, PERIOD, true, function thistype.move)
endif
endif
endmethod
/* ---------------------------- Missiles movement --------------------------- */
static method move takes nothing returns nothing
local integer j = 0
local integer i
local integer k
local unit u
local real a
local real d
local real s
local real h
local real c
local real dx
local real dy
local real vel
local real locZ
local real yaw
local real pitch
local Missiles missile
local Coordinates o
local thistype this
// SWEET_SPOOT used to enable or disable
// relativistic processing
if SWEET_SPOT > 0 then
set i = last
else
set i = 0
endif
loop
exitwhen ((j >= SWEET_SPOT and SWEET_SPOT > 0) or j > didx)
set this = missiles[i]
set temp = this
if allocated then
set o = origin
set h = height
set c = open
set d = o.distance
set locZ = GetLocZ(x, y)
//onPeriod Event
if .onPeriod.exists then
if allocated and .onPeriod() then
call terminate()
endif
endif
// onHit Event
if .onHit.exists then
if allocated and collision > 0 then
call GroupEnumUnitsInRange(hitGroup, x, y, collision + COLLISION_SIZE, null)
loop
set u = FirstOfGroup(hitGroup)
exitwhen u == null
if not HaveSavedBoolean(table, this, GetHandleId(u)) then
if IsUnitInRangeXY(u, x, y, collision) then
if collideZ then
set dx = GetLocZ(GetUnitX(u), GetUnitY(u)) + GetUnitFlyHeight(u) - locZ
set dy = BODY_SIZE
if dx + dy >= z - collision and dx <= z + collision then
call SaveBoolean(table, this, GetHandleId(u), true)
if allocated and .onHit(u) then
call terminate()
exitwhen true
endif
endif
else
call SaveBoolean(table, this, GetHandleId(u), true)
if allocated and .onHit(u) then
call terminate()
exitwhen true
endif
endif
endif
endif
call GroupRemoveUnit(hitGroup, u)
endloop
endif
endif
// onMissile Event
if .onMissile.exists then
if allocated and collision > 0 then
set k = 0
loop
exitwhen k > didx
set missile = missiles[k]
if missile != this then
if not HaveSavedBoolean(table, this, missile) then
set dx = missile.x - x
set dy = missile.y - y
if SquareRoot(dx*dx + dy*dy) <= collision then
call SaveBoolean(table, this, missile, true)
if allocated and .onMissile(missile) then
call terminate()
exitwhen true
endif
endif
endif
endif
set k = k + 1
endloop
endif
endif
// onDestructable Event
if .onDestructable.exists then
if allocated and collision > 0 then
set dx = collision
call SetRect(RECT, x - dx, y - dx, x + dx, y + dx)
call EnumDestructablesInRect(RECT, null, function thistype.onDest)
endif
endif
// onItem Event
if .onItem.exists then
if allocated and collision > 0 then
set dx = collision
call SetRect(RECT, x - dx, y - dx, x + dx, y + dx)
call EnumItemsInRect(RECT, null, function thistype.onItems)
endif
endif
// Homing or not
set u = target
if u != null and GetUnitTypeId(u) != 0 then
call impact.move(GetUnitX(u), GetUnitY(u), GetUnitFlyHeight(u) + toZ)
set dx = impact.x - prevX
set dy = impact.y - prevY
set a = Atan2(dy, dx)
set travel = o.distance - SquareRoot(dx*dx + dy*dy)
else
set a = o.angle
set target = null
endif
// turn rate
if turn != 0 and not (Cos(cA-a) >= Cos(turn)) then
if Sin(a-cA) >= 0 then
set cA = cA + turn
else
set cA = cA - turn
endif
else
set cA = a
endif
set vel = veloc*dilation
set x = prevX + vel*Cos(cA)
set y = prevY + vel*Sin(cA)
set s = travel + vel
set prevX = x
set prevY = y
set prevZ = z
set travel = s
set veloc = veloc + acceleration
set pitch = origin.alpha
set yaw = cA*bj_RADTODEG
// arc calculation
if h != 0 or o.slope != 0 then
set z = 4*h*s*(d-s)/(d*d) + o.slope*s + o.z
set pitch = pitch - Atan(((4*h)*(2*s - d))/(d*d))*bj_RADTODEG
call SetUnitFlyHeight(dummy, z - GetLocZ(x, y), 0)
endif
// curve calculation
if c != 0 then
set dx = 4*c*s*(d-s)/(d*d)
set a = cA + bj_PI/2
set x = x + dx*Cos(a)
set y = y + dx*Sin(a)
set yaw = (cA + Atan(-((4*c)*(2*s - d))/(d*d)))*bj_RADTODEG
endif
// onTerrain event
if .onTerrain.exists then
if GetLocZ(x, y) > z then
if allocated and .onTerrain() then
call terminate()
endif
endif
endif
if s >= d then
// onFinish event
if .onFinish.exists then
if allocated and .onFinish() then
call terminate()
else
// deflected onFinish
if travel > 0 then
call terminate()
endif
endif
else
call terminate()
endif
endif
// missile orientation and positioning
call SetUnitAnimationByIndex(dummy, R2I(pitch + 90.5))
call SetUnitFacing(dummy, yaw)
if not (x > WorldBounds.maxX or x < WorldBounds.minX or y > WorldBounds.maxY or y < WorldBounds.minY) then
call SetUnitX(dummy, x)
call SetUnitY(dummy, y)
else
// onBoundaries event
if .onBoundaries.exists then
if allocated and .onBoundaries() then
call terminate()
endif
endif
endif
else
set i = remove(i)
set j = j - 1
endif
set i = i + 1
set j = j + 1
if i > didx and SWEET_SPOT > 0 then
set i = 0
endif
endloop
set last = i
set u = null
endmethod
/* --------------------------- Main Creator method -------------------------- */
static method create takes real x, real y, real z, real toX, real toY, real toZ returns thistype
local thistype this = thistype.allocate()
local real angle = Atan2(toY - y, toX - x)*bj_RADTODEG
call .reset()
set .origin = Coordinates.create(x, y, z)
set .impact = Coordinates.create(toX, toY, toZ)
set .allocated = true
set .x = x
set .y = y
set .z = z
set .prevX = x
set .prevY = y
set .prevZ = z
set .toZ = toZ
static if LIBRARY_DummyRecycler then
set .dummy = GetRecycledDummy(x, y, z - GetLocZ(x, y), angle)
else
set .dummy = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE), DUMMY_RAW_CODE, x, y, angle)
call SetUnitFlyHeight(.dummy, z - GetLocZ(x, y), 0)
endif
call Coordinates.link(origin, impact)
set .cA = origin.angle
return this
endmethod
endstruct
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library DummyRecycler /*
// DummyRecycler v1.24
// by Flux
//
// A system that recycles dummy units while considering their facing angle.
// It can be used as attachment units 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 it will always leave a permanent tiny bit of memory (0.04 KB).
// Furthermore, if a Damage Detection System (DDS) or a Unit Indexer is
// imported to your map, using this system will even result to better
// performance because DDS and Unit Indexer process new created units.
//
//
*/ 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.
//
//--------------------
// 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 = 'dum0'
//The owner of the Dummy Unit
private constant player OWNER = Player(PLAYER_NEUTRAL_PASSIVE)
//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)
//! 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 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
//TESH.scrollpos=0
//TESH.alwaysfold=0
library RemoveAbilityTimed uses ListT optional DummyRecycler
//! novjass
// API
call RemoveAbilityTimed.create(whichUnit, whichAbility, whichTimeout, recycle)
whichAbility will be removed from whichUnit after whichTimeout. If recycle is true, the unit
will be recycled if you have DummyRecycler. Otherwise it will be killed.
//! endnovjass
private module RemoveAbilityTimedModule
private static constant real TIMEOUT = .1
private static constant integer DUMMY_ID = 'dum0'
private static IntegerList List
private static timer Clock = CreateTimer()
private unit u
private integer abil
private boolean recycle
private real time
private method destroy takes nothing returns nothing
set this.u = null
set this.abil = 0
set this.recycle = false
set this.time = 0.
call this.deallocate()
endmethod
private static method update takes nothing returns nothing
local IntegerListItem node = List.first
local IntegerListItem nodeNext
local thistype this
loop
exitwhen node == 0
set nodeNext = node.next
set this = node.data
set this.time = this.time - TIMEOUT
if this.time <= 0. then
call UnitRemoveAbility(this.u, this.abil)
static if LIBRARY_DummyRecycler then
if this.recycle then
if GetUnitTypeId(this.u) == DUMMY_ID then
call RecycleDummy(this.u)
else
call UnitApplyTimedLife(this.u, 'BTLF', .01)
endif
endif
else
if this.recycle then
call UnitApplyTimedLife(this.u, 'BTLF', .01)
endif
endif
call List.erase(node)
call this.destroy()
endif
set node = nodeNext
endloop
if List.empty() then
call PauseTimer(Clock)
endif
endmethod
static method create takes unit u, integer abil, real time, boolean recycle returns thistype
local thistype this = allocate()
set this.u = u
set this.abil = abil
set this.recycle = recycle
set this.time = time
if List.empty() then
call TimerStart(Clock, TIMEOUT, true, function thistype.update)
endif
call List.push(this)
return this
endmethod
private static method onInit takes nothing returns nothing
set List = IntegerList.create()
endmethod
endmodule
struct RemoveAbilityTimed
implement RemoveAbilityTimedModule
endstruct
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
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
//TESH.scrollpos=0
//TESH.alwaysfold=0
library BoundSentinel initializer init
//*************************************************
//* BoundSentinel
//* -------------
//* Don't leave your units unsupervised, naughty
//* them may try to get out of the map bounds and
//* crash your game.
//*
//* To implement, just get a vJass compiler and
//* copy this library/trigger to your map.
//*
//*************************************************
//==================================================
globals
// High enough so the unit is no longer visible, low enough so the
// game doesn't crash...
//
// I think you need 0.0 or soemthing negative prior to patch 1.22
//
private constant real EXTRA = 500.0
endglobals
//=========================================================================================
globals
private real maxx
private real maxy
private real minx
private real miny
endglobals
//=======================================================================
private function dis takes nothing returns nothing
local unit u=GetTriggerUnit()
local real x=GetUnitX(u)
local real y=GetUnitY(u)
if(x>maxx) then
set x=maxx
elseif(x<minx) then
set x=minx
endif
if(y>maxy) then
set y=maxy
elseif(y<miny) then
set y=miny
endif
call SetUnitX(u,x)
call SetUnitY(u,y)
set u=null
endfunction
private function init takes nothing returns nothing
local trigger t=CreateTrigger()
local region r=CreateRegion()
local rect rc
set minx=GetCameraBoundMinX() - EXTRA
set miny=GetCameraBoundMinY() - EXTRA
set maxx=GetCameraBoundMaxX() + EXTRA
set maxy=GetCameraBoundMaxY() + EXTRA
set rc=Rect(minx,miny,maxx,maxy)
call RegionAddRect(r, rc)
call RemoveRect(rc)
call TriggerRegisterLeaveRegion(t,r, null)
call TriggerAddAction(t, function dis)
//this is not necessary but I'll do it anyway:
set t=null
set r=null
set rc=null
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
/*****************************************************************************
*
* List<T> v2.1.2.0
* by Bannar
*
* Doubly-linked list.
*
******************************************************************************
*
* Requirements:
*
* Table by Bribe
* hiveworkshop.com/forums/jass-resources-412/snippet-new-table-188084/
*
* Alloc - choose whatever you like
* e.g.: by Sevion hiveworkshop.com/threads/snippet-alloc.192348/
*
******************************************************************************
*
* Implementation:
*
* macro DEFINE_STRUCT_LIST takes ACCESS, NAME, TYPE
*
* macro DEFINE_LIST takes ACCESS, NAME, TYPE
*
* ACCESS - encapsulation, choose restriction access
* NAME - name of list type
* TYPE - type of values stored
*
* Implementation notes:
*
* - DEFINE_STRUCT_LIST macro purpose is to provide natural typecasting syntax for struct types.
* - <NAME>Item structs inline directly into hashtable operations thus generate basically no code.
* - Lists defined with DEFINE_STRUCT_LIST are inlined nicely into single create method and single integer array.
*
******************************************************************************
*
* struct API:
*
* struct <NAME>Item:
*
* | <TYPE> data
* | <NAME>Item next
* | <NAME>Item prev
*
*
* General:
*
* | static method create takes nothing returns thistype
* | Default ctor.
* |
* | static method operator [] takes thistype other returns thistype
* | Copy ctor.
* |
* | method destroy takes nothing returns nothing
* | Default dctor.
* |
* | method empty takes nothing returns boolean
* | Checks whether the list is empty.
* |
* | method size takes nothing returns integer
* | Returns size of a list.
*
*
* Access:
*
* | readonly <NAME>Item first
* | readonly <NAME>Item last
* |
* | method front takes nothing returns $TYPE$
* | Retrieves first element.
* |
* | method back takes nothing returns $TYPE$
* | Retrieves last element.
*
*
* Modifiers:
*
* | method clear takes nothing returns nothing
* | Flushes list and recycles its nodes.
* |
* | method push takes $TYPE$ value returns thistype
* | Adds elements to the end.
* |
* | method unshift takes $TYPE$ value returns thistype
* | Adds elements to the front.
* |
* | method pop takes nothing returns thistype
* | Removes the last element.
* |
* | method shift takes nothing returns thistype
* | Removes the first element.
* |
* | method find takes $TYPE$ value returns $NAME$Item
* | Returns the first node which data equals value.
* |
* | method erase takes $NAME$Item node returns boolean
* | Removes node from the list, returns true on success.
* |
* | method removeElem takes $TYPE$ value returns thistype
* | Removes first element that equals value from the list.
*
*
*****************************************************************************/
library ListT requires Table, Alloc
// Run here any global list types you want to be defined.
//! runtextmacro DEFINE_LIST("", "IntegerList", "integer")
//! textmacro_once DEFINE_STRUCT_LIST takes ACCESS, NAME, TYPE
$ACCESS$ struct $NAME$Item extends array
// Cannot inherit methods via delegate due to limited array size
method operator data takes nothing returns $TYPE$
return IntegerListItem(this).data
endmethod
method operator data= takes $TYPE$ value returns nothing
set IntegerListItem(this).data = value
endmethod
method operator next takes nothing returns thistype
return IntegerListItem(this).next
endmethod
method operator next= takes thistype value returns nothing
set IntegerListItem(this).next = value
endmethod
method operator prev takes nothing returns thistype
return IntegerListItem(this).prev
endmethod
method operator prev= takes thistype value returns nothing
set IntegerListItem(this).prev = value
endmethod
endstruct
$ACCESS$ struct $NAME$ extends array
private delegate IntegerList parent
method front takes nothing returns $TYPE$
return parent.front()
endmethod
method back takes nothing returns $TYPE$
return parent.back()
endmethod
static method create takes nothing returns thistype
local thistype this = IntegerList.create()
set parent = this
return this
endmethod
endstruct
//! endtextmacro
//! textmacro_once DEFINE_LIST takes ACCESS, NAME, TYPE
$ACCESS$ struct $NAME$Item extends array
// No default ctor and dctor due to limited array size
method operator data takes nothing returns $TYPE$
return Table(this).$TYPE$[0] // hashtable[ node, 0 ] = data
endmethod
method operator data= takes $TYPE$ value returns nothing
set Table(this).$TYPE$[0] = value
endmethod
method operator next takes nothing returns thistype
return Table(this)[1] // hashtable[ node, 1 ] = next
endmethod
method operator next= takes thistype value returns nothing
set Table(this)[1] = value
endmethod
method operator prev takes nothing returns thistype
return Table(this)[-1] // hashtable[ node, -1 ] = prev
endmethod
method operator prev= takes thistype value returns nothing
set Table(this)[-1] = value
endmethod
endstruct
$ACCESS$ struct $NAME$ extends array
readonly $NAME$Item first
readonly $NAME$Item last
private integer count
implement Alloc
private static method setNodeOwner takes $NAME$Item node, $NAME$ owner returns nothing
set Table(node)[2] = owner
endmethod
private static method getNodeOwner takes $NAME$Item node returns thistype
return Table(node)[2]
endmethod
private method createNode takes $TYPE$ value returns $NAME$Item
local $NAME$Item node = Table.create()
set node.data = value
call setNodeOwner(node, this) // ownership
return node
endmethod
private method deleteNode takes $NAME$Item node returns nothing
call Table(node).destroy() // also removes ownership
endmethod
static method create takes nothing returns thistype
local thistype this = allocate()
set count = 0
return this
endmethod
method clear takes nothing returns nothing
local $NAME$Item node = first
local $NAME$Item temp
loop // recycle all Table indexes
exitwhen 0 == node
set temp = node.next
call deleteNode(node)
set node = temp
endloop
set first = 0
set last = 0
set count = 0
endmethod
method destroy takes nothing returns nothing
call clear()
call deallocate()
endmethod
method front takes nothing returns $TYPE$
return first.data
endmethod
method back takes nothing returns $TYPE$
return last.data
endmethod
method empty takes nothing returns boolean
return count == 0
endmethod
method size takes nothing returns integer
return count
endmethod
method push takes $TYPE$ value returns thistype
local $NAME$Item node = createNode(value)
if not empty() then
set last.next = node
set node.prev = last
else
set first = node
set node.prev = 0
endif
set last = node
set node.next = 0
set count = count + 1
return this
endmethod
method unshift takes $TYPE$ value returns thistype
local $NAME$Item node = createNode(value)
if not empty() then
set first.prev = node
set node.next = first
else
set last = node
set node.next = 0
endif
set first = node
set node.prev = 0
set count = count + 1
return this
endmethod
method pop takes nothing returns thistype
local $NAME$Item node
if not empty() then
set node = last
set last = last.prev
if last == 0 then
set first = 0
else
set last.next = 0
endif
call deleteNode(node)
set count = count - 1
debug else
debug call DisplayTimedTextFromPlayer(GetLocalPlayer(),0,0,60,"$NAME$::pop failed for instance "+I2S(this)+". List is empty.")
endif
return this
endmethod
method shift takes nothing returns thistype
local $NAME$Item node
if not empty() then
set node = first
set first = first.next
if first == 0 then
set last = 0
else
set first.prev = 0
endif
call deleteNode(node)
set count = count - 1
debug else
debug call DisplayTimedTextFromPlayer(GetLocalPlayer(),0,0,60,"$NAME$::shift failed for instance "+I2S(this)+". List is empty.")
endif
return this
endmethod
static method operator [] takes thistype other returns thistype
local thistype instance = create()
local $NAME$Item node = other.first
loop
exitwhen node == 0
call instance.push(node.data)
set node = node.next
endloop
return instance
endmethod
method find takes $TYPE$ value returns $NAME$Item
local $NAME$Item node = first
loop
exitwhen node == 0 or node.data == value
set node = node.next
endloop
return node
endmethod
method erase takes $NAME$Item node returns boolean
if getNodeOwner(node) == this then // match ownership
if node == first then
call shift()
elseif node == last then
call pop()
else
set node.prev.next = node.next
set node.next.prev = node.prev
call deleteNode(node)
set count = count - 1
endif
return true
debug else
debug call DisplayTimedTextFromPlayer(GetLocalPlayer(),0,0,60,"$NAME$::erase failed for instance "+I2S(this)+". Attempted to remove invalid node "+I2S(node)+".")
endif
return false
endmethod
method remove takes $NAME$Item node returns boolean
debug call DisplayTimedTextFromPlayer(GetLocalPlayer(),0,0,60,"Method $NAME$::remove is obsolete, use $NAME$::erase instead.")
return erase(node)
endmethod
method removeElem takes $TYPE$ value returns thistype
local $NAME$Item node = find(value)
if node != 0 then
call remove(node)
endif
return this
endmethod
endstruct
//! endtextmacro
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library Alloc /* v1.3.1.1
*************************************************************************************
*
* */ uses /*
*
* */ optional ErrorMessage /* github.com/nestharus/JASS/tree/master/jass/Systems/ErrorMessage
* */ optional MemoryAnalysis /*
*
*************************************************************************************
*
* Minimizes code generation and global variables while maintaining
* excellent performance.
*
* local thistype this = recycler[0]
*
* if (recycler[this] == 0) then
* set recycler[0] = this + 1
* else
* set recycler[0] = recycler[this]
* endif
*
************************************************************************************
*
* module Alloc
*
* static method allocate takes nothing returns thistype
* method deallocate takes nothing returns nothing
*
* The Following Require Error Message To Be In The Map
* --------------------------------------------------------
*
* debug readonly boolean allocated
*
* The Following Require Memory Analysis To Be In The Map
* --------------------------------------------------------
*
* debug readonly integer monitorCount
* - the amount of global memory being monitored by this
* debug readonly integer monitorString
* - gets a string representation of all global memory being monitored by this
* debug readonly integer address
* - global memory address for debugging
* - used with monitor and stopMonitor
*
* debug static method calculateMemoryUsage takes nothing returns integer
* debug static method getAllocatedMemoryAsString takes nothing returns string
*
* debug method monitor takes string label, integer address returns nothing
* - monitor a global memory address with a label
* - used to identify memory leaks
* - should be memory that ought to be destroyed by the time this is destroyed
* debug method stopMonitor takes integer address returns nothing
* - stops monitoring global memory
* debug method stopMonitorValue takes handle monitoredHandle returns nothing
* - stops monitoring handle values
*
* The Following Are Used To Monitor Handle Values
*
* debug method monitor_widget takes string label, widget handleToTrack returns nothing
* debug method monitor_destructable takes string label, destructable handleToTrack returns nothing
* debug method monitor_item takes string label, item handleToTrack returns nothing
* debug method monitor_unit takes string label, unit handleToTrack returns nothing
* debug method monitor_timer takes string label, timer handleToTrack returns nothing
* debug method monitor_trigger takes string label, trigger handleToTrack returns nothing
* debug method monitor_triggercondition takes string label, triggercondition handleToTrack returns nothing
* debug method monitor_triggeraction takes string label, triggeraction handleToTrack returns nothing
* debug method monitor_force takes string label, force handleToTrack returns nothing
* debug method monitor_group takes string label, group handleToTrack returns nothing
* debug method monitor_location takes string label, location handleToTrack returns nothing
* debug method monitor_rect takes string label, rect handleToTrack returns nothing
* debug method monitor_boolexpr takes string label, boolexpr handleToTrack returns nothing
* debug method monitor_effect takes string label, effect handleToTrack returns nothing
* debug method monitor_unitpool takes string label, unitpool handleToTrack returns nothing
* debug method monitor_itempool takes string label, itempool handleToTrack returns nothing
* debug method monitor_quest takes string label, quest handleToTrack returns nothing
* debug method monitor_defeatcondition takes string label, defeatcondition handleToTrack returns nothing
* debug method monitor_timerdialog takes string label, timerdialog handleToTrack returns nothing
* debug method monitor_leaderboard takes string label, leaderboard handleToTrack returns nothing
* debug method monitor_multiboard takes string label, multiboard handleToTrack returns nothing
* debug method monitor_multiboarditem takes string label, multiboarditem handleToTrack returns nothing
* debug method monitor_dialog takes string label, dialog handleToTrack returns nothing
* debug method monitor_button takes string label, button handleToTrack returns nothing
* debug method monitor_texttag takes string label, texttag handleToTrack returns nothing
* debug method monitor_lightning takes string label, lightning handleToTrack returns nothing
* debug method monitor_image takes string label, image handleToTrack returns nothing
* debug method monitor_ubersplat takes string label, ubersplat handleToTrack returns nothing
* debug method monitor_region takes string label, region handleToTrack returns nothing
* debug method monitor_fogmodifier takes string label, fogmodifier handleToTrack returns nothing
* debug method monitor_hashtable takes string label, hashtable handleToTrack returns nothing
*
*
* Thanks to Ruke for the algorithm
************************************************************************************/
module Alloc
/*
* stack
*/
private static integer array recycler
static if LIBRARY_MemoryAnalysis then
debug private MemoryMonitor globalAddress
debug method operator address takes nothing returns integer
debug call ThrowError(recycler[this] != -1, "Alloc", "address", "thistype", this, "Attempted To Access Null Instance.")
debug return globalAddress
debug endmethod
endif
/*
* allocation
*/
static method allocate takes nothing returns thistype
local thistype this = recycler[0]
static if LIBRARY_ErrorMessage then
debug call ThrowError(this == 8192, "Alloc", "allocate", "thistype", 0, "Overflow.")
endif
if (recycler[this] == 0) then
set recycler[0] = this + 1
else
set recycler[0] = recycler[this]
endif
static if LIBRARY_ErrorMessage then
debug set recycler[this] = -1
endif
static if LIBRARY_MemoryAnalysis then
debug set globalAddress = MemoryMonitor.allocate("thistype")
endif
return this
endmethod
method deallocate takes nothing returns nothing
static if LIBRARY_ErrorMessage then
debug call ThrowError(recycler[this] != -1, "Alloc", "deallocate", "thistype", this, "Attempted To Deallocate Null Instance.")
endif
static if LIBRARY_MemoryAnalysis then
debug call globalAddress.deallocate()
debug set globalAddress = 0
endif
set recycler[this] = recycler[0]
set recycler[0] = this
endmethod
static if LIBRARY_MemoryAnalysis then
debug method monitor takes string label, integer address returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor(label, address)
debug endmethod
debug method stopMonitor takes integer address returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "stopMonitor", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.stopMonitor(address)
debug endmethod
debug method stopMonitorValue takes handle monitoredHandle returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "stopMonitorValue", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.stopMonitorValue(monitoredHandle)
debug endmethod
debug method operator monitorCount takes nothing returns integer
debug call ThrowError(recycler[this] != -1, "Alloc", "monitorCount", "thistype", this, "Attempted To Access Null Instance.")
debug return globalAddress.monitorCount
debug endmethod
debug method operator monitorString takes nothing returns string
debug call ThrowError(recycler[this] != -1, "Alloc", "monitorString", "thistype", this, "Attempted To Access Null Instance.")
debug return globalAddress.monitorString
debug endmethod
debug method monitor_widget takes string label, widget handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_widget", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_widget(label, handleToTrack)
debug endmethod
debug method monitor_destructable takes string label, destructable handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_destructable", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_destructable(label, handleToTrack)
debug endmethod
debug method monitor_item takes string label, item handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_item", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_item(label, handleToTrack)
debug endmethod
debug method monitor_unit takes string label, unit handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_unit", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_unit(label, handleToTrack)
debug endmethod
debug method monitor_timer takes string label, timer handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_timer", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_timer(label, handleToTrack)
debug endmethod
debug method monitor_trigger takes string label, trigger handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_trigger", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_trigger(label, handleToTrack)
debug endmethod
debug method monitor_triggercondition takes string label, triggercondition handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_triggercondition", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_triggercondition(label, handleToTrack)
debug endmethod
debug method monitor_triggeraction takes string label, triggeraction handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_triggeraction", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_triggeraction(label, handleToTrack)
debug endmethod
debug method monitor_force takes string label, force handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_force", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_force(label, handleToTrack)
debug endmethod
debug method monitor_group takes string label, group handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_group", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_group(label, handleToTrack)
debug endmethod
debug method monitor_location takes string label, location handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_location", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_location(label, handleToTrack)
debug endmethod
debug method monitor_rect takes string label, rect handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_rect", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_rect(label, handleToTrack)
debug endmethod
debug method monitor_boolexpr takes string label, boolexpr handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_boolexpr", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_boolexpr(label, handleToTrack)
debug endmethod
debug method monitor_effect takes string label, effect handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_effect", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_effect(label, handleToTrack)
debug endmethod
debug method monitor_unitpool takes string label, unitpool handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_unitpool", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_unitpool(label, handleToTrack)
debug endmethod
debug method monitor_itempool takes string label, itempool handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_itempool", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_itempool(label, handleToTrack)
debug endmethod
debug method monitor_quest takes string label, quest handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_quest", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_quest(label, handleToTrack)
debug endmethod
debug method monitor_defeatcondition takes string label, defeatcondition handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_defeatcondition", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_defeatcondition(label, handleToTrack)
debug endmethod
debug method monitor_timerdialog takes string label, timerdialog handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_timerdialog", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_timerdialog(label, handleToTrack)
debug endmethod
debug method monitor_leaderboard takes string label, leaderboard handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_leaderboard", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_leaderboard(label, handleToTrack)
debug endmethod
debug method monitor_multiboard takes string label, multiboard handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_multiboard", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_multiboard(label, handleToTrack)
debug endmethod
debug method monitor_multiboarditem takes string label, multiboarditem handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_multiboarditem", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_multiboarditem(label, handleToTrack)
debug endmethod
debug method monitor_dialog takes string label, dialog handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_dialog", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_dialog(label, handleToTrack)
debug endmethod
debug method monitor_button takes string label, button handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_button", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_button(label, handleToTrack)
debug endmethod
debug method monitor_texttag takes string label, texttag handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_texttag", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_texttag(label, handleToTrack)
debug endmethod
debug method monitor_lightning takes string label, lightning handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_lightning", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_lightning(label, handleToTrack)
debug endmethod
debug method monitor_image takes string label, image handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_image", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_image(label, handleToTrack)
debug endmethod
debug method monitor_ubersplat takes string label, ubersplat handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_ubersplat", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_ubersplat(label, handleToTrack)
debug endmethod
debug method monitor_region takes string label, region handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_region", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_region(label, handleToTrack)
debug endmethod
debug method monitor_fogmodifier takes string label, fogmodifier handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_fogmodifier", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_fogmodifier(label, handleToTrack)
debug endmethod
debug method monitor_hashtable takes string label, hashtable handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_hashtable", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_hashtable(label, handleToTrack)
debug endmethod
static if DEBUG_MODE then
//! runtextmacro optional MEMORY_ANALYSIS_STATIC_FIELD_NEW("recycler")
static method calculateMemoryUsage takes nothing returns integer
return calculateAllocatedMemory__recycler()
endmethod
static method getAllocatedMemoryAsString takes nothing returns string
return allocatedMemoryString__recycler()
endmethod
endif
endif
/*
* analysis
*/
static if LIBRARY_ErrorMessage then
debug method operator allocated takes nothing returns boolean
debug return recycler[this] == -1
debug endmethod
endif
/*
* initialization
*/
private static method onInit takes nothing returns nothing
set recycler[0] = 1
endmethod
endmodule
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
/*****************************************************************************
*
* RegisterNativeEvent v1.1.1.2
* by Bannar
*
* Storage of trigger handles for native events.
*
******************************************************************************
*
* Optional requirements:
*
* Table by Bribe
* hiveworkshop.com/forums/jass-resources-412/snippet-new-table-188084/
*
******************************************************************************
*
* Important:
*
* Avoid using TriggerSleepAction within functions registered.
* Destroy native event trigger on your own responsibility.
*
******************************************************************************
*
* Core:
*
* function IsNativeEventRegistered takes integer whichIndex, integer whichEvent returns boolean
* Whether index whichIndex has already been attached to event whichEvent.
*
* function RegisterNativeEventTrigger takes integer whichIndex, integer eventId returns boolean
* Registers whichIndex within whichEvent scope and assigns new trigger handle for it.
*
* function GetIndexNativeEventTrigger takes integer whichIndex, integer whichEvent returns trigger
* Retrieves trigger handle for event whichEvent specific to provided index whichIndex.
*
* function GetNativeEventTrigger takes integer whichEvent returns trigger
* Retrieves trigger handle for event whichEvent.
*
*
* Custom events:
*
* function CreateNativeEvent takes nothing returns integer
* Returns unique id for new event and registers it with RegisterNativeEvent.
*
* function RegisterIndexNativeEvent takes integer whichIndex, integer whichEvent, code func returns nothing
* Registers new event handler func for event whichEvent specific to index whichIndex.
*
* function RegisterNativeEvent takes integer whichEvent, code func returns nothing
* Registers new event handler func for specified event whichEvent.
*
*****************************************************************************/
library RegisterNativeEvent uses optional Table
globals
private integer eventIndex = 500 // 0-499 reserved for Blizzard native events
endglobals
private module NativeEventInit
private static method onInit takes nothing returns nothing
static if LIBRARY_Table then
set table = TableArray[0x2000]
endif
endmethod
endmodule
private struct NativeEvent extends array
static if LIBRARY_Table then
static TableArray table
else
static hashtable table = InitHashtable()
endif
implement NativeEventInit
endstruct
function IsNativeEventRegistered takes integer whichIndex, integer whichEvent returns boolean
static if LIBRARY_Table then
return NativeEvent.table[whichEvent].trigger.has(whichIndex)
else
return HaveSavedHandle(NativeEvent.table, whichEvent, whichIndex)
endif
endfunction
function RegisterNativeEventTrigger takes integer whichIndex, integer whichEvent returns boolean
if not IsNativeEventRegistered(whichIndex, whichEvent) then
static if LIBRARY_Table then
set NativeEvent.table[whichEvent].trigger[whichIndex] = CreateTrigger()
else
call SaveTriggerHandle(NativeEvent.table, whichEvent, whichIndex, CreateTrigger())
endif
return true
endif
return false
endfunction
function GetIndexNativeEventTrigger takes integer whichIndex, integer whichEvent returns trigger
static if LIBRARY_Table then
return NativeEvent.table[whichEvent].trigger[whichIndex]
else
return LoadTriggerHandle(NativeEvent.table, whichEvent, whichIndex)
endif
endfunction
function GetNativeEventTrigger takes integer whichEvent returns trigger
return GetIndexNativeEventTrigger(bj_MAX_PLAYER_SLOTS, whichEvent)
endfunction
function CreateNativeEvent takes nothing returns integer
local integer eventId = eventIndex
call RegisterNativeEventTrigger(bj_MAX_PLAYER_SLOTS, eventId)
set eventIndex = eventIndex + 1
return eventId
endfunction
function RegisterIndexNativeEvent takes integer whichIndex, integer whichEvent, code func returns nothing
call RegisterNativeEventTrigger(whichIndex, whichEvent)
call TriggerAddCondition(GetIndexNativeEventTrigger(whichIndex, whichEvent), Condition(func))
endfunction
function RegisterNativeEvent takes integer whichEvent, code func returns nothing
call RegisterIndexNativeEvent(bj_MAX_PLAYER_SLOTS, whichEvent, func)
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
/*****************************************************************************
*
* RegisterPlayerUnitEvent v1.0.3.2
* by Bannar
*
* Register version of TriggerRegisterPlayerUnitEvent.
*
* Special thanks to Magtheridon96, Bribe, azlier and BBQ for the original library version.
*
******************************************************************************
*
* Requirements:
*
* RegisterNativeEvent by Bannar
* hiveworkshop.com/threads/snippet-registerevent-pack.250266/
*
******************************************************************************
*
* Functions:
*
* function GetAnyPlayerUnitEventTrigger takes playerunitevent whichEvent returns trigger
* Retrieves trigger handle for playerunitevent whichEvent.
*
* function GetPlayerUnitEventTrigger takes player whichPlayer, playerunitevent whichEvent returns trigger
* Retrieves trigger handle for playerunitevent whichEvent specific to player whichPlayer.
*
* function RegisterAnyPlayerUnitEvent takes playerunitevent whichEvent, code func returns nothing
* Registers generic playerunitevent whichEvent adding code func as callback.
*
* function RegisterPlayerUnitEvent takes player whichPlayer, playerunitevent whichEvent, code func returns nothing
* Registers playerunitevent whichEvent for player whichPlayer adding code func as callback.
*
*****************************************************************************/
library RegisterPlayerUnitEvent requires RegisterNativeEvent
function GetAnyPlayerUnitEventTrigger takes playerunitevent whichEvent returns trigger
return GetNativeEventTrigger(GetHandleId(whichEvent))
endfunction
function GetPlayerUnitEventTrigger takes player whichPlayer, playerunitevent whichEvent returns trigger
return GetIndexNativeEventTrigger(GetPlayerId(whichPlayer), GetHandleId(whichEvent))
endfunction
function RegisterAnyPlayerUnitEvent takes playerunitevent whichEvent, code func returns nothing
local integer eventId = GetHandleId(whichEvent)
local integer index = 0
local trigger t = null
if RegisterNativeEventTrigger(bj_MAX_PLAYER_SLOTS, eventId) then
set t = GetNativeEventTrigger(eventId)
loop
call TriggerRegisterPlayerUnitEvent(t, Player(index), whichEvent, null)
set index = index + 1
exitwhen index == bj_MAX_PLAYER_SLOTS
endloop
set t = null
endif
call RegisterNativeEvent(eventId, func)
endfunction
function RegisterPlayerUnitEvent takes player whichPlayer, playerunitevent whichEvent, code func returns nothing
local integer playerId = GetPlayerId(whichPlayer)
local integer eventId = GetHandleId(whichEvent)
if RegisterNativeEventTrigger(playerId, eventId) then
call TriggerRegisterPlayerUnitEvent(GetIndexNativeEventTrigger(playerId, eventId), whichPlayer, whichEvent, null)
endif
call RegisterIndexNativeEvent(playerId, eventId, func)
endfunction
endlibrary
/*****************************************************************************
*
* RegisterPlayerEvent v1.0.2.2
* by Bannar
*
* Register version of TriggerRegisterPlayerEvent.
*
******************************************************************************
*
* Requirements:
*
* RegisterNativeEvent by Bannar
* hiveworkshop.com/threads/snippet-registerevent-pack.250266/
*
******************************************************************************
*
* Functions:
*
* function GetAnyPlayerEventTrigger takes playerevent whichEvent returns trigger
* Retrieves trigger handle for playerevent whichEvent.
*
* function GetPlayerEventTrigger takes player whichPlayer, playerevent whichEvent returns trigger
* Retrieves trigger handle for playerevent whichEvent specific to player whichPlayer.
*
* function RegisterAnyPlayerEvent takes playerevent whichEvent, code func returns nothing
* Registers generic playerevent whichEvent adding code func as callback.
*
* function RegisterPlayerEvent takes player whichPlayer, playerevent whichEvent, code func returns nothing
* Registers playerevent whichEvent for player whichPlayer adding code func as callback.
*
*****************************************************************************/
library RegisterPlayerEvent requires RegisterNativeEvent
function GetAnyPlayerEventTrigger takes playerevent whichEvent returns trigger
return GetNativeEventTrigger(GetHandleId(whichEvent))
endfunction
function GetPlayerEventTrigger takes player whichPlayer, playerevent whichEvent returns trigger
return GetIndexNativeEventTrigger(GetPlayerId(whichPlayer), GetHandleId(whichEvent))
endfunction
function RegisterAnyPlayerEvent takes playerevent whichEvent, code func returns nothing
local integer eventId = GetHandleId(whichEvent)
local integer index = 0
local trigger t = null
if RegisterNativeEventTrigger(bj_MAX_PLAYER_SLOTS, eventId) then
set t = GetNativeEventTrigger(eventId)
loop
call TriggerRegisterPlayerEvent(t, Player(index), whichEvent)
set index = index + 1
exitwhen index == bj_MAX_PLAYER_SLOTS
endloop
set t = null
endif
call RegisterNativeEvent(eventId, func)
endfunction
function RegisterPlayerEvent takes player whichPlayer, playerevent whichEvent, code func returns nothing
local integer playerId = GetPlayerId(whichPlayer)
local integer eventId = GetHandleId(whichEvent)
if RegisterNativeEventTrigger(playerId, eventId) then
call TriggerRegisterPlayerEvent(GetIndexNativeEventTrigger(playerId, eventId), whichPlayer, whichEvent)
endif
call RegisterIndexNativeEvent(playerId, eventId, func)
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
//============================================================================
// SpellEffectEvent
// - Version 1.1.0.0
//
// API
// ---
// RegisterSpellEffectEvent(integer abil, code onCast)
//
// Requires
// --------
// RegisterPlayerUnitEvent: hiveworkshop.com/forums/showthread.php?t=203338
//
// Optional
// --------
// Table: hiveworkshop.com/forums/showthread.php?t=188084
//
library SpellEffectEvent requires RegisterPlayerUnitEvent, optional Table
//============================================================================
private module M
static if LIBRARY_Table then
static Table tb
else
static hashtable ht = InitHashtable()
endif
static method onCast takes nothing returns nothing
static if LIBRARY_Table then
call TriggerEvaluate(.tb.trigger[GetSpellAbilityId()])
else
call TriggerEvaluate(LoadTriggerHandle(.ht, 0, GetSpellAbilityId()))
endif
endmethod
private static method onInit takes nothing returns nothing
static if LIBRARY_Table then
set .tb = Table.create()
endif
call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_SPELL_EFFECT, function thistype.onCast)
endmethod
endmodule
//============================================================================
private struct S extends array
implement M
endstruct
//============================================================================
function RegisterSpellEffectEvent takes integer abil, code onCast returns nothing
static if LIBRARY_Table then
if not S.tb.handle.has(abil) then
set S.tb.trigger[abil] = CreateTrigger()
endif
call TriggerAddCondition(S.tb.trigger[abil], Filter(onCast))
else
if not HaveSavedHandle(S.ht, 0, abil) then
call SaveTriggerHandle(S.ht, 0, abil, CreateTrigger())
endif
call TriggerAddCondition(LoadTriggerHandle(S.ht, 0, abil), Filter(onCast))
endif
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
//============================================================================
// OrderEvent by Bribe, special thanks to Nestharus and Azlier, version 3.0.1.1
//
// API
// ---
// RegisterOrderEvent(integer orderId, code eventFunc)
// RegisterAnyOrderEvent(code eventFunc) //Runs for point/target/instant for any order
//
// Requires
// --------
// RegisterPlayerUnitEvent: http://www.hiveworkshop.com/threads/snippet-registerevent-pack.250266/
// Table: http://www.hiveworkshop.com/forums/showthread.php?t=188084
//
library OrderEvent requires RegisterPlayerUnitEvent, Table
globals
private Table t = 0
endglobals
//============================================================================
function RegisterAnyOrderEvent takes code c returns nothing
call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_ISSUED_ORDER, c)
call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER, c)
call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER, c)
endfunction
//============================================================================
private function OnOrder takes nothing returns nothing
call TriggerEvaluate(t.trigger[GetIssuedOrderId()])
endfunction
//============================================================================
function RegisterOrderEvent takes integer orderId, code c returns nothing
local trigger trig
if integer(t) == 0 then
set t = Table.create()
call RegisterAnyOrderEvent(function OnOrder)
endif
set trig = t.trigger[orderId]
if trig == null then
set trig = CreateTrigger()
set t.trigger[orderId] = trig
endif
call TriggerAddCondition(trig, Filter(c))
set trig = null
endfunction
endlibrary
library BlizzardMessage /* v1.0.0.4
*************************************************************************************
*
* For creating Blizzard-like messages with a variety of sounds.
*
*************************************************************************************
*
* Credits
*
* Vexorian
* -----------------------
*
* The original SimError
*
*
* Maker
* -----------------------
*
* Sound label help
*
************************************************************************************
*
* Function
* -----------------------
*
* function BlizzardMessage takes string msg, string colorCode, integer soundLabel, player forPlayer returns nothing
*
* Description
* -----------------------
*
* This function creates a Blizzard-like message with a sound for a player.
*
* Takes
* -----------------------
*
* string msg
* - The message to be displayed.
*
* string colorCode
* - Adds a color for the message through an ARBG color code. A null value creates a normal white text.
*
* integer soundLabel
* - The sound to be played.
*
* player forPlayer
* - The player that receives the message.
*
* Returns
* -----------------------
*
* nothing
*
*************************************************************************************
*
* Function
* -----------------------
*
* function BlizzardMessageTimed takes string msg, string colorCode, real dur, integer soundLabel, player forPlayer returns nothing
*
* Description
* -----------------------
*
* This function creates a timed Blizzard-like message with a sound for a player.
*
* Takes
* -----------------------
*
* string msg
* - The message to be displayed.
*
* string colorCode
* - Adds a color for the message through an ARBG color code. A null value creates a normal white text.
*
* real dur
* - The duration of the message.
*
* integer soundLabel
* - The sound to be played.
*
* player forPlayer
* - The player that receives the message.
*
* Returns
* -----------------------
*
* nothing
*
*************************************************************************************
*
* SETTINGS
* -----------------------
*
*/
globals
constant real MSG_DURATION = 2.00 //Message Duration
/*************************************************************************************
*
* Sound Labels
*
*************************************************************************************/
constant integer ALLY_HERO_DIES_HUMAN = 0
constant integer ALLY_HERO_DIES_NAGA = 1
constant integer ALLY_HERO_DIES_NIGHTELF = 2
constant integer ALLY_HERO_DIES_ORC = 3
constant integer ALLY_HERO_DIES_UNDEAD = 4
constant integer ALLY_TOWN_UNDER_ATTACK_HUMAN = 5
constant integer ALLY_TOWN_UNDER_ATTACK_NAGA = 6
constant integer ALLY_TOWN_UNDER_ATTACK_NIGHTELF = 7
constant integer ALLY_TOWN_UNDER_ATTACK_ORC = 8
constant integer ALLY_TOWN_UNDER_ATTACK_UNDEAD = 9
constant integer ALLY_UNDER_ATTACK_HUMAN = 10
constant integer ALLY_UNDER_ATTACK_NAGA = 11
constant integer ALLY_UNDER_ATTACK_NIGHTELF = 12
constant integer ALLY_UNDER_ATTACK_ORC = 13
constant integer ALLY_UNDER_ATTACK_UNDEAD = 14
constant integer ARRANGED_TEAM_INVITATION = 15
constant integer AUTO_CAST_BUTTON_CLICK = 16
constant integer CANT_PLACE_HUMAN = 17
constant integer CANT_PLACE_NAGA = 18
constant integer CANT_PLACE_NIGHTELF = 19
constant integer CANT_PLACE_ORC = 20
constant integer CANT_PLACE_UNDEAD = 21
constant integer CANT_ROOT_NIGHTELF = 22
constant integer CHATROOM_TIMER_TICK = 23
constant integer CLAN_INVITATION = 24
constant integer CONSTRUCTING_BUILDING_DEFAULT = 25
constant integer CONSTRUCTING_BUILDING_NAGA = 26
constant integer CONSTRUCTING_BUILDING_NIGHTELF = 27
constant integer CONSTRUCTING_BUILDING_ORC = 28
constant integer CONSTRUCTING_BUILDING_UNDEAD = 29
constant integer CREEP_AGGRO = 30
constant integer ERROR_MESSAGE = 31
constant integer GAME_FOUND = 32
constant integer GLUE_SCREEN_CLICK = 33
constant integer GOLD_MINE_COLLAPSE_HUMAN = 34
constant integer GOLD_MINE_COLLAPSE_NAGA = 35
constant integer GOLD_MINE_COLLAPSE_NIGHTELF = 36
constant integer GOLD_MINE_COLLAPSE_ORC = 37
constant integer GOLD_MINE_COLLAPSE_UNDEAD = 38
constant integer GOLD_MINE_LOW_GENERIC = 39
constant integer GOLD_MINE_LOW_HUMAN = 40
constant integer GOLD_MINE_LOW_NAGA = 41
constant integer GOLD_MINE_LOW_NIGHTELF = 42
constant integer GOLD_MINE_LOW_ORC = 43
constant integer GOLD_MINE_LOW_UNDEAD = 44
constant integer GOOD_JOB = 45
constant integer HERO_DIES_GENERIC = 46
constant integer HERO_DIES_HUMAN = 47
constant integer HERO_DIES_NAGA = 48
constant integer HERO_DIES_NIGHTELF = 49
constant integer HERO_DIES_ORC = 50
constant integer HERO_DIES_UNDEAD = 51
constant integer HINT = 52
constant integer IN_GAME_CHAT_WHAT = 53
constant integer INTERFACE_CLICK = 54
constant integer INTERFACE_ERROR = 55
constant integer INVENTORY_FULL_HUMAN = 56
constant integer INVENTORY_FULL_NAGA = 57
constant integer INVENTORY_FULL_NIGHTELF = 58
constant integer INVENTORY_FULL_ORC = 59
constant integer INVENTORY_FULL_UNDEAD = 60
constant integer ITEM_DROP = 61
constant integer ITEM_GET = 62
constant integer ITEM_REWARD = 63
constant integer JOB_DONE_SOUND_HUMAN = 64
constant integer JOB_DONE_SOUND_NAGA = 65
constant integer JOB_DONE_SOUND_NIGHTELF = 66
constant integer JOB_DONE_SOUND_ORC = 67
constant integer JOB_DONE_SOUND_UNDEAD = 68
constant integer MAP_PING = 69
constant integer MENU_BUTTON_CLICK = 70
constant integer NEW_TOURNAMENT = 71
constant integer NO_FOOD_HUMAN = 72
constant integer NO_FOOD_NAGA = 73
constant integer NO_FOOD_NIGHTELF = 74
constant integer NO_FOOD_ORC = 75
constant integer NO_FOOD_UNDEAD = 76
constant integer NO_GOLD_GENERIC = 77
constant integer NO_GOLD_HUMAN = 78
constant integer NO_GOLD_NAGA = 79
constant integer NO_GOLD_NIGHTELF = 80
constant integer NO_GOLD_ORC = 81
constant integer NO_GOLD_UNDEAD = 82
constant integer NO_LUMBER_HUMAN = 83
constant integer NO_LUMBER_NAGA = 84
constant integer NO_LUMBER_NIGHTELF = 85
constant integer NO_LUMBER_ORC = 86
constant integer NO_LUMBER_UNDEAD = 87
constant integer NO_MANA_GENERIC = 88
constant integer NO_MANA_HUMAN = 89
constant integer NO_MANA_NAGA = 90
constant integer NO_MANA_NIGHTELF = 91
constant integer NO_MANA_ORC = 92
constant integer NO_MANA_UNDEAD = 93
constant integer OFF_BLIGHT_UNDEAD = 94
constant integer PAUSE_GAME = 95
constant integer PLACE_BUILDING_DEFAULT = 96
constant integer QUEST_COMPLETED = 97
constant integer QUEST_FAILED = 98
constant integer QUEST_LOG_MODIFIED = 99
constant integer QUEST_NEW = 100
constant integer QUEST_UPDATE = 101
constant integer RALLY_POINT_PLACE = 102
constant integer RESCUE = 103
constant integer RESEARCH_COMPLETE_GENERIC = 104
constant integer RESEARCH_COMPLETE_HUMAN = 105
constant integer RESEARCH_COMPLETE_NAGA = 106
constant integer RESEARCH_COMPLETE_NIGHTELF = 107
constant integer RESEARCH_COMPLETE_ORC = 108
constant integer RESEARCH_COMPLETE_UNDEAD = 109
constant integer SCORE_SCREEN_TAB_CLICK = 110
constant integer SECRET_FOUND = 111
constant integer SUB_GROUP_SELECTION_CHANGE = 112
constant integer TOWN_ATTACK_GENERIC = 113
constant integer TOWN_ATTACK_HUMAN = 114
constant integer TOWN_ATTACK_NAGA = 115
constant integer TOWN_ATTACK_NIGHTELF = 116
constant integer TOWN_ATTACK_ORC = 117
constant integer TOWN_ATTACK_UNDEAD = 118
constant integer UNDER_ATTACK_HUMAN = 119
constant integer UNDER_ATTACK_NAGA = 120
constant integer UNDER_ATTACK_NIGHTELF = 121
constant integer UNDER_ATTACK_ORC = 122
constant integer UNDER_ATTACK_UNDEAD = 123
constant integer UPGRADE_COMPLETE_GENERIC = 124
constant integer UPGRADE_COMPLETE_HUMAN = 125
constant integer UPGRADE_COMPLETE_NAGA = 126
constant integer UPGRADE_COMPLETE_NIGHTELF = 127
constant integer UPGRADE_COMPLETE_ORC = 128
constant integer UPGRADE_COMPLETE_UNDEAD = 129
constant integer UPKEEP_LEVEL = 130
constant integer WARNING = 131
constant integer WAYPOINT = 132
/* private */ string array SOUND_LABEL
endglobals
/*
************************************************************************************
*/
function BlizzardMessage takes string msg, string colorCode, integer soundLabel, player forPlayer returns nothing
local sound snd
local string s
debug if soundLabel >= 0 and soundLabel <= 132 and forPlayer != null then
set snd = CreateSoundFromLabel (SOUND_LABEL[soundLabel], false, false, false, 10, 10)
if colorCode != null then
set s = "|r"
else
set colorCode = ""
set s = ""
endif
if GetLocalPlayer () == forPlayer then
call ClearTextMessages ()
call DisplayTimedTextToPlayer (forPlayer, 0.52, 0.96, MSG_DURATION, "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n" + colorCode + msg + s)
call StartSound (snd)
endif
if s != null then
set s = ""
endif
set snd = null
debug else
debug if msg == null
debug call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "[BlizzardMessage] [BlizzardMessage] Null message")
debug endif
debug if forPlayer == null
debug call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "[BlizzardMessage] [BlizzardMessage] Null forPlayer")
debug endif
debug if soundLabel < 0 or soundLabel > 132
debug call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "[BlizzardMessage] [BlizzardMessage] Invalid sound label")
debug endif
debug endif
endfunction
function BlizzardMessageTimed takes string msg, string colorCode, real dur, integer soundLabel, player forPlayer returns nothing
local sound snd
local string s
debug if soundLabel >= 0 and soundLabel <= 132 and forPlayer != null then
set snd = CreateSoundFromLabel (SOUND_LABEL[soundLabel], false, false, false, 10, 10)
if colorCode != null then
set s = "|r"
else
set colorCode = ""
set s = ""
endif
if GetLocalPlayer () == forPlayer then
call ClearTextMessages ()
call DisplayTimedTextToPlayer (forPlayer, 0.52, 0.96, dur, "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n" + colorCode + msg + s)
call StartSound (snd)
endif
if s != null then
set s = ""
endif
set snd = null
debug else
debug if msg == null
debug call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "[BlizzardMessage] [BlizzardMessage] Null message")
debug endif
debug if forPlayer == null
debug call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "[BlizzardMessage] [BlizzardMessage] Null forPlayer")
debug endif
debug if soundLabel < 0 or soundLabel > 132
debug call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "[BlizzardMessage] [BlizzardMessage] Invalid sound label")
debug endif
debug endif
endfunction
private module BMInit
private static method onInit takes nothing returns nothing
call init()
endmethod
endmodule
private struct InitStruct extends array
private static method init takes nothing returns nothing
set SOUND_LABEL[0] = "AllyHeroDiesHuman"
set SOUND_LABEL[1] = "AllyHeroDiesNaga"
set SOUND_LABEL[2] = "AllyHeroDiesNightElf"
set SOUND_LABEL[3] = "AllyHeroDiesOrc"
set SOUND_LABEL[4] = "AllyHeroDiesUndead"
set SOUND_LABEL[5] = "AllyTownUnderAttackHuman"
set SOUND_LABEL[6] = "AllyTownUnderAttackNaga"
set SOUND_LABEL[7] = "AllyTownUnderAttackNightElf"
set SOUND_LABEL[8] = "AllyTownUnderAttackOrc"
set SOUND_LABEL[9] = "AllyTownUnderAttackUndead"
set SOUND_LABEL[10] = "AllyUnderAttackHuman"
set SOUND_LABEL[11] = "AllyUnderAttackNaga"
set SOUND_LABEL[12] = "AllyUnderAttackNightElf"
set SOUND_LABEL[13] = "AllyUnderAttackOrc"
set SOUND_LABEL[14] = "AllyUnderAttackUndead"
set SOUND_LABEL[15] = "ArrangedTeamInvitation"
set SOUND_LABEL[16] = "AutoCastButtonClick"
set SOUND_LABEL[17] = "CantPlaceHuman"
set SOUND_LABEL[18] = "CantPlaceNaga"
set SOUND_LABEL[19] = "CantPlaceNightElf"
set SOUND_LABEL[20] = "CantPlaceOrc"
set SOUND_LABEL[21] = "CantPlaceUndead"
set SOUND_LABEL[22] = "CantRootNightElf"
set SOUND_LABEL[23] = "ChatroomTimerTick"
set SOUND_LABEL[24] = "ClanInvitation"
set SOUND_LABEL[25] = "ConstructingBuildingDefault"
set SOUND_LABEL[26] = "ConstructingBuildingNaga"
set SOUND_LABEL[27] = "ConstructingBuildingNightElf"
set SOUND_LABEL[28] = "ConstructingBuildingOrc"
set SOUND_LABEL[29] = "ConstructingBuildingUndead"
set SOUND_LABEL[30] = "CreepAggro"
set SOUND_LABEL[31] = "ErrorMessage"
set SOUND_LABEL[32] = "GameFound"
set SOUND_LABEL[33] = "GlueScreenClick"
set SOUND_LABEL[34] = "GoldMineCollapseHuman"
set SOUND_LABEL[35] = "GoldMineCollapseNaga"
set SOUND_LABEL[36] = "GoldMineCollapseNightElf"
set SOUND_LABEL[37] = "GoldMineCollapseOrc"
set SOUND_LABEL[38] = "GoldMineCollapseUndead"
set SOUND_LABEL[39] = "GoldMineLowGeneric"
set SOUND_LABEL[40] = "GoldMineLowHuman"
set SOUND_LABEL[41] = "GoldMineLowNaga"
set SOUND_LABEL[42] = "GoldMineLowNightElf"
set SOUND_LABEL[43] = "GoldMineLowOrc"
set SOUND_LABEL[44] = "GoldMineLowUndead"
set SOUND_LABEL[45] = "GoodJob"
set SOUND_LABEL[46] = "HeroDiesGeneric"
set SOUND_LABEL[47] = "HeroDiesHuman"
set SOUND_LABEL[48] = "HeroDiesNaga"
set SOUND_LABEL[49] = "HeroDiesNightElf"
set SOUND_LABEL[50] = "HeroDiesOrc"
set SOUND_LABEL[51] = "HeroDiesUndead"
set SOUND_LABEL[52] = "Hint"
set SOUND_LABEL[53] = "InGameChatWhat"
set SOUND_LABEL[54] = "InterfaceClick"
set SOUND_LABEL[55] = "InterfaceError"
set SOUND_LABEL[56] = "InventoryFullHuman"
set SOUND_LABEL[57] = "InventoryFullNaga"
set SOUND_LABEL[58] = "InventoryFullNightElf"
set SOUND_LABEL[59] = "InventoryFullOrc"
set SOUND_LABEL[60] = "InventoryFullUndead"
set SOUND_LABEL[61] = "ItemDrop"
set SOUND_LABEL[62] = "ItemGet"
set SOUND_LABEL[63] = "ItemReward"
set SOUND_LABEL[64] = "JobDoneSoundHuman"
set SOUND_LABEL[65] = "JobDoneSoundNaga"
set SOUND_LABEL[66] = "JobDoneSoundNightElf"
set SOUND_LABEL[67] = "JobDoneSoundOrc"
set SOUND_LABEL[68] = "JobDoneSoundUndead"
set SOUND_LABEL[69] = "MapPing"
set SOUND_LABEL[70] = "MenuButtonClick"
set SOUND_LABEL[71] = "NewTournament"
set SOUND_LABEL[72] = "NoFoodHuman"
set SOUND_LABEL[73] = "NoFoodNaga"
set SOUND_LABEL[74] = "NoFoodNightElf"
set SOUND_LABEL[75] = "NoFoodOrc"
set SOUND_LABEL[76] = "NoFoodUndead"
set SOUND_LABEL[77] = "NoGoldGeneric"
set SOUND_LABEL[78] = "NoGoldHuman"
set SOUND_LABEL[79] = "NoGoldNaga"
set SOUND_LABEL[80] = "NoGoldNightElf"
set SOUND_LABEL[81] = "NoGoldOrc"
set SOUND_LABEL[82] = "NoGoldUndead"
set SOUND_LABEL[83] = "NoLumberHuman"
set SOUND_LABEL[84] = "NoLumberNaga"
set SOUND_LABEL[85] = "NoLumberNightElf"
set SOUND_LABEL[86] = "NoLumberOrc"
set SOUND_LABEL[87] = "NoLumberUndead"
set SOUND_LABEL[88] = "NoManaGeneric"
set SOUND_LABEL[89] = "NoManaHuman"
set SOUND_LABEL[90] = "NoManaNaga"
set SOUND_LABEL[91] = "NoManaNightElf"
set SOUND_LABEL[92] = "NoManaOrc"
set SOUND_LABEL[93] = "NoManaUndead"
set SOUND_LABEL[94] = "OffBlightUndead"
set SOUND_LABEL[95] = "PauseGame"
set SOUND_LABEL[96] = "PlaceBuildingDefault"
set SOUND_LABEL[97] = "QuestCompleted"
set SOUND_LABEL[98] = "QuestFailed"
set SOUND_LABEL[99] = "QuestLogModified"
set SOUND_LABEL[100] = "QuestNew"
set SOUND_LABEL[101] = "QuestUpdate"
set SOUND_LABEL[102] = "RallyPointPlace"
set SOUND_LABEL[103] = "Rescue"
set SOUND_LABEL[104] = "ResearchCompleteGeneric"
set SOUND_LABEL[105] = "ResearchCompleteHuman"
set SOUND_LABEL[106] = "ResearchCompleteNaga"
set SOUND_LABEL[107] = "ResearchCompleteNightElf"
set SOUND_LABEL[108] = "ResearchCompleteOrc"
set SOUND_LABEL[109] = "ResearchCompleteUndead"
set SOUND_LABEL[110] = "ScoreScreenTabClick"
set SOUND_LABEL[111] = "SecretFound"
set SOUND_LABEL[112] = "SubGroupSelectionChange"
set SOUND_LABEL[113] = "TownAttackGeneric"
set SOUND_LABEL[114] = "TownAttackHuman"
set SOUND_LABEL[115] = "TownAttackNaga"
set SOUND_LABEL[116] = "TownAttackNightElf"
set SOUND_LABEL[117] = "TownAttackOrc"
set SOUND_LABEL[118] = "TownAttackUndead"
set SOUND_LABEL[119] = "UnderAttackHuman"
set SOUND_LABEL[120] = "UnderAttackNaga"
set SOUND_LABEL[121] = "UnderAttackNightElf"
set SOUND_LABEL[122] = "UnderAttackOrc"
set SOUND_LABEL[123] = "UnderAttackUndead"
set SOUND_LABEL[124] = "UpgradeCompleteGeneric"
set SOUND_LABEL[125] = "UpgradeCompleteHuman"
set SOUND_LABEL[126] = "UpgradeCompleteNaga"
set SOUND_LABEL[127] = "UpgradeCompleteNightElf"
set SOUND_LABEL[128] = "UpgradeCompleteOrc"
set SOUND_LABEL[129] = "UpgradeCompleteUndead"
set SOUND_LABEL[130] = "UpkeepLevel"
set SOUND_LABEL[131] = "Warning"
set SOUND_LABEL[132] = "WayPoint"
endmethod
implement BMInit
endstruct
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
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 = false
private constant boolean USE_FLEXIBLE_OFFSET = true
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
//TESH.scrollpos=0
//TESH.alwaysfold=0
library DummyUtils uses DummyRecycler
globals
private unit DUMMY = null
endglobals
function DummyCaster takes real x, real y, real z, player p returns unit
set DUMMY = GetRecycledDummyAnyAngle(x, y, z)
call PauseUnit(DUMMY, false)
call SetUnitOwner(DUMMY, p, true)
return DUMMY
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library SpellUtils
function HasAbility takes unit u, integer id returns boolean
return GetUnitAbilityLevel(u, id) > 0
endfunction
function HasChance takes real chance returns boolean
return GetRandomInt(1, 100) <= chance
endfunction
function IsAlive takes unit u returns boolean
return GetWidgetLife(u) > 0.41
endfunction
function UnitHasMana takes unit u returns boolean
return GetUnitState(u, UNIT_STATE_MAX_MANA) > 0
endfunction
function CreateEffectOnUnit takes string fx, unit u, string attach returns nothing
call DestroyEffect(AddSpecialEffectTarget(fx, u, attach))
endfunction
function CreateEffectOnLoc takes string fx, location loc returns nothing
call DestroyEffect(AddSpecialEffectLoc(fx, loc))
endfunction
function Distance takes real aX, real aY, real bX, real bY returns real
local real dx = bX - aX
local real dy = bY - aY
return SquareRoot(dx * dx + dy * dy)
endfunction
function IsPointWalkable takes real x, real y, real tollerance returns boolean
local unit u = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE), 'hfoo', x, y, 0)
local real dist = (GetUnitX(u) - x)*(GetUnitX(u) - x) + (GetUnitY(u) - y)*(GetUnitY(u) - y)
call RemoveUnit(u)
set u = null
return dist < tollerance*tollerance
endfunction
function NextWalkablePoint takes real x, real y returns location
local unit u = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE), 'hfoo', x, y, 0)
local location loc = GetUnitLoc(u)
call RemoveUnit(u)
set u = null
return loc
endfunction
function IsMechanical takes unit u returns boolean
return IsUnitType(u, UNIT_TYPE_MECHANICAL)
endfunction
function IsBuilding takes unit u returns boolean
return IsUnitType(u, UNIT_TYPE_STRUCTURE) and GetUnitTypeId(u) != 'ncop'
endfunction
function IsMagicImmune takes unit u returns boolean
return IsUnitType(u, UNIT_TYPE_MAGIC_IMMUNE)
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library GroupUtils requires SpellUtils
globals
private constant real MAX_COLLISION = 64.
private real tempX = 0.
private real tempY = 0.
private real tempRange = 0.
endglobals
function FilterInRange takes nothing returns boolean
return IsUnitInRangeXY(GetFilterUnit(), tempX, tempY, tempRange)
endfunction
function AliveFilter takes nothing returns boolean
return IsAlive(GetFilterUnit()) and not IsBuilding(GetFilterUnit()) and GetUnitTypeId(GetFilterUnit()) != 'npgr'
endfunction
function GroupEnumUnitsWithCollision takes group g, real x, real y, real radius returns group
set tempX = x
set tempY = y
set tempRange = radius
call GroupEnumUnitsInRange(g, x, y, radius+MAX_COLLISION, Filter(function FilterInRange))
return g
endfunction
function GroupEnumLivingUnitsOfPlayer takes group g, player p returns group
call GroupEnumUnitsOfPlayer(g, p, Filter(function AliveFilter))
return g
endfunction
function CopyGroup takes group g returns group
set bj_groupAddGroupDest = CreateGroup()
call ForGroup(g, function GroupAddGroupEnum)
return bj_groupAddGroupDest
endfunction
function GroupCountUnits takes group g returns integer
local integer count = 0
local group tempG = CopyGroup(g)
local unit u
loop
set u = FirstOfGroup(tempG)
exitwhen u == null
set count = count + 1
call GroupRemoveUnit(tempG, u)
set u = null
endloop
call DestroyGroup(tempG)
set tempG = null
return count
endfunction
function GroupRandomUnit takes group g returns unit
local integer t = 0
local integer curmax = 0
local unit chosen = null
local group tempG = CopyGroup(g)
local unit u
loop
set u = FirstOfGroup(tempG)
exitwhen u == null
if IsAlive(u) then
set t = GetRandomInt(1,100)
if t > curmax then
set curmax = t
set chosen = u
endif
endif
call GroupRemoveUnit(tempG, u)
set u = null
endloop
return chosen
endfunction
endlibrary
library CinematicUtils
function FadeIn takes real dur returns nothing
call EnableUserUI(false)
call SetCineFilterTexture("ReplaceableTextures\\CameraMasks\\White_mask.blp")
call SetCineFilterBlendMode(BLEND_MODE_BLEND)
call SetCineFilterTexMapFlags(TEXMAP_FLAG_NONE)
call SetCineFilterStartUV(0, 0, 1, 1)
call SetCineFilterEndUV(0, 0, 1, 1)
call SetCineFilterStartColor(0, 0, 0, 0)
call SetCineFilterEndColor(0, 0, 0, 255)
call SetCineFilterDuration(dur)
call DisplayCineFilter(true)
endfunction
function FadeOut takes real dur returns nothing
call EnableUserUI(false)
call SetCineFilterTexture("ReplaceableTextures\\CameraMasks\\White_mask.blp")
call SetCineFilterBlendMode(BLEND_MODE_BLEND)
call SetCineFilterTexMapFlags(TEXMAP_FLAG_NONE)
call SetCineFilterStartUV(0, 0, 1, 1)
call SetCineFilterEndUV(0, 0, 1, 1)
call SetCineFilterStartColor(0, 0, 0, 255)
call SetCineFilterEndColor(0, 0, 0, 0)
call SetCineFilterDuration(dur)
call DisplayCineFilter(true)
endfunction
endlibrary
library MissionUtils
function CreateMainQuest takes nothing returns quest
local quest q = CreateQuest()
call QuestSetTitle(q, "The Shrine of Duredhel")
call QuestSetDescription(q, "Queen Malice approaches to Az'lann, the arcane crystal where Kathra has been sealed. Hurry up and stop her before she can obtain such power.")
call QuestSetIconPath(q, "ReplaceableTextures\\CommandButtons\\BTNAbility_racial_arcaneaffinity.blp")
call QuestSetRequired(q, true)
call QuestSetDiscovered(q, true)
call QuestSetCompleted(q, false)
return q
endfunction
function CreateInfoQuest takes string title, string desc, string icon returns nothing
local quest q = CreateQuest()
call QuestSetTitle(q, title)
call QuestSetDescription(q, desc)
call QuestSetIconPath(q, icon)
call QuestSetRequired(q, false)
call QuestSetDiscovered(q, true)
call QuestSetCompleted(q, false)
endfunction
function QuestObjetive takes quest q, string s returns questitem
local questitem i = QuestCreateItem(q)
call QuestItemSetDescription(i, s)
call QuestItemSetCompleted(i, false)
return i
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
//===========================================================================
// Damage Engine lets you detect, amplify, block or nullify damage. It even
// lets you detect if the damage was physical or from a spell. Just reference
// DamageEventAmount/Source/Target or the boolean IsDamageSpell, to get the
// necessary damage event data.
//
// - Detect damage: use the event "DamageEvent Equal to 1.00"
// - To change damage before it's dealt: use the event "DamageModifierEvent Equal to 1.00"
// - Detect damage after it was applied, use the event "AfterDamageEvent Equal to 1.00"
// - Detect spell damage: use the condition "IsDamageSpell Equal to True"
// - Detect zero-damage: use the event "DamageEvent Equal to 2.00" (an AfterDamageEvent will not fire for this)
//
// You can specify the DamageEventType before dealing triggered damage. To prevent an already-improbable error, I recommend running the trigger "ClearDamageEvent (Checking Conditions)" after dealing triggered damage from within a damage event:
// - Set NextDamageType = DamageTypeWhatever
// - Unit - Cause...
// - Trigger - Run ClearDamageEvent (Checking Conditions)
//
// You can modify the DamageEventAmount and the DamageEventType from a "DamageModifierEvent Equal to 1.00" trigger.
// - If the amount is modified to negative, it will count as a heal.
// - If the amount is set to 0, no damage will be dealt.
//
// If you need to reference the original in-game damage, use the variable "DamageEventPrevAmt".
//
//===========================================================================
// Programming note about "integer i" and "udg_DmgEvRecursionN": integer i
// ranges from -1 upwards. "udg_DmgEvRecursionN" ranges from 0 upwards.
// "integer i" is always 1 less than "udg_DmgEvRecursionN"
//
function DmgEvResetVars takes nothing returns nothing
local integer i = udg_DmgEvRecursionN - 2
set udg_DmgEvRecursionN = i + 1
if i >= 0 then
set udg_DamageEventPrevAmt = udg_LastDmgPrevAmount[i]
set udg_DamageEventAmount = udg_LastDmgValue[i]
set udg_DamageEventSource = udg_LastDmgSource[i]
set udg_DamageEventTarget = udg_LastDmgTarget[i]
set udg_IsDamageSpell = udg_LastDmgWasSpell[i]
set udg_DamageEventType = udg_LastDmgPrevType[i]
endif
endfunction
function CheckDamagedLifeEvent takes boolean clear returns nothing
if clear then
set udg_NextDamageOverride = false
set udg_NextDamageType = 0
endif
if udg_DmgEvTrig != null then
call DestroyTrigger(udg_DmgEvTrig)
set udg_DmgEvTrig = null
if udg_IsDamageSpell then
call SetWidgetLife(udg_DamageEventTarget, RMaxBJ(udg_LastDamageHP, 0.41))
if udg_LastDamageHP <= 0.405 then
if udg_DamageEventType < 0 then
call SetUnitExploded(udg_DamageEventTarget, true)
endif
//Kill the unit
call DisableTrigger(udg_DamageEventTrigger)
call UnitDamageTarget(udg_DamageEventSource, udg_DamageEventTarget, -999, false, false, null, DAMAGE_TYPE_UNIVERSAL, null)
call EnableTrigger(udg_DamageEventTrigger)
endif
elseif GetUnitAbilityLevel(udg_DamageEventTarget, udg_DamageBlockingAbility) > 0 then
call UnitRemoveAbility(udg_DamageEventTarget, udg_DamageBlockingAbility)
call SetWidgetLife(udg_DamageEventTarget, udg_LastDamageHP)
endif
if udg_DamageEventAmount != 0.00 and not udg_HideDamageFrom[GetUnitUserData(udg_DamageEventSource)] then
set udg_AfterDamageEvent = 0.00
set udg_AfterDamageEvent = 1.00
set udg_AfterDamageEvent = 0.00
endif
call DmgEvResetVars()
endif
endfunction
function DmgEvOnAOEEnd takes nothing returns nothing
if udg_DamageEventAOE > 1 then
set udg_AOEDamageEvent = 0.00
set udg_AOEDamageEvent = 1.00
set udg_AOEDamageEvent = 0.00
set udg_DamageEventAOE = 1
endif
set udg_DamageEventLevel = 1
set udg_EnhancedDamageTarget = null
call GroupClear(udg_DamageEventAOEGroup)
endfunction
function DmgEvOnExpire takes nothing returns nothing
set udg_DmgEvStarted = false
call CheckDamagedLifeEvent(true)
//Reset things so they don't perpetuate for AoE/Level target detection
call DmgEvOnAOEEnd()
set udg_DamageEventTarget = null
set udg_DamageEventSource = null
endfunction
function PreCheckDamagedLifeEvent takes nothing returns boolean
call CheckDamagedLifeEvent(true)
return false
endfunction
function OnUnitDamage takes nothing returns boolean
local boolean override = udg_DamageEventOverride
local integer i
local integer e = udg_DamageEventLevel
local integer a = udg_DamageEventAOE
local string s
local real prevAmount
local real life
local real prevLife
local unit u
local unit f
call CheckDamagedLifeEvent(false) //in case the unit state event failed and the 0.00 second timer hasn't yet expired
set i = udg_DmgEvRecursionN - 1 //Had to be moved here due to false recursion tracking
if i < 0 then
//Added 25 July 2017 to detect AOE damage or multiple single-target damage
set u = udg_DamageEventTarget
set f = udg_DamageEventSource
elseif i < 16 then
set udg_LastDmgPrevAmount[i]= udg_DamageEventPrevAmt
set udg_LastDmgValue[i] = udg_DamageEventAmount
set udg_LastDmgSource[i] = udg_DamageEventSource
set udg_LastDmgTarget[i] = udg_DamageEventTarget
set udg_LastDmgWasSpell[i] = udg_IsDamageSpell
set udg_LastDmgPrevType[i] = udg_DamageEventType
else
set s = "WARNING: Recursion error when dealing damage! Make sure when you deal damage from within a DamageEvent trigger, do it like this:\n\n"
set s = s + "Trigger - Turn off (This Trigger)\n"
set s = s + "Unit - Cause...\n"
set s = s + "Trigger - Turn on (This Trigger)"
//Delete the next couple of lines to disable the in-game recursion crash warnings
call ClearTextMessages()
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0.00, 0.00, 999.00, s)
return false
endif
set udg_DmgEvRecursionN = i + 2
set prevAmount = GetEventDamage()
set udg_DamageEventTarget = GetTriggerUnit()
set udg_DamageEventSource = GetEventDamageSource()
set udg_DamageEventAmount = prevAmount
set udg_DamageEventType = udg_NextDamageType
set udg_NextDamageType = 0
set udg_DamageEventOverride = udg_NextDamageOverride
set udg_NextDamageOverride = false
if i < 0 then
//Added 25 July 2017 to detect AOE damage or multiple single-target damage
if udg_DamageEventType == 0 then
if f == udg_DamageEventSource then
//Source has damaged more than once
if IsUnitInGroup(udg_DamageEventTarget, udg_DamageEventAOEGroup) then
//Added 5 August 2017 to improve tracking of enhanced damage against, say, Pulverize
set udg_DamageEventLevel = udg_DamageEventLevel + 1
set udg_EnhancedDamageTarget = udg_DamageEventTarget
else
//Multiple targets hit by this source - flag as AOE
set udg_DamageEventAOE = udg_DamageEventAOE + 1
endif
else
//New damage source - unflag everything
set u = udg_DamageEventSource
set udg_DamageEventSource = f
call DmgEvOnAOEEnd()
set udg_DamageEventSource = u
endif
call GroupAddUnit(udg_DamageEventAOEGroup, udg_DamageEventTarget)
endif
if not udg_DmgEvStarted then
set udg_DmgEvStarted = true
call TimerStart(udg_DmgEvTimer, 0.00, false, function DmgEvOnExpire)
endif
endif
if prevAmount == 0.00 then
if not udg_HideDamageFrom[GetUnitUserData(udg_DamageEventSource)] then
set udg_DamageEventPrevAmt = 0.00
set udg_DamageEvent = 0.00
set udg_DamageEvent = 2.00
set udg_DamageEvent = 0.00
endif
call DmgEvResetVars()
else
set u = udg_DamageEventTarget
set udg_IsDamageSpell = prevAmount < 0.00
if udg_IsDamageSpell then
set prevAmount = -udg_DamageEventAmount
set life = 1.00
if IsUnitType(u, UNIT_TYPE_ETHEREAL) and not IsUnitType(u, UNIT_TYPE_HERO) then
set life = life*udg_DAMAGE_FACTOR_ETHEREAL //1.67
endif
if GetUnitAbilityLevel(u, 'Aegr') > 0 then
set life = life*udg_DAMAGE_FACTOR_ELUNES //0.80
endif
if udg_DmgEvBracers != 0 and IsUnitType(u, UNIT_TYPE_HERO) then
//Inline of UnitHasItemOfTypeBJ without the potential handle ID leak.
set i = 6
loop
set i = i - 1
if GetItemTypeId(UnitItemInSlot(u, i)) == udg_DmgEvBracers then
set life = life*udg_DAMAGE_FACTOR_BRACERS //0.67
exitwhen true
endif
exitwhen i == 0
endloop
endif
set udg_DamageEventAmount = prevAmount*life
endif
set udg_DamageEventPrevAmt = prevAmount
set udg_DamageModifierEvent = 0.00
if not udg_DamageEventOverride then
set udg_DamageModifierEvent = 1.00
if not udg_DamageEventOverride then
set udg_DamageModifierEvent = 2.00
set udg_DamageModifierEvent = 3.00
endif
endif
set udg_DamageEventOverride = override
if udg_DamageEventAmount > 0.00 then
set udg_DamageModifierEvent = 4.00
endif
set udg_DamageModifierEvent = 0.00
if not udg_HideDamageFrom[GetUnitUserData(udg_DamageEventSource)] then
set udg_DamageEvent = 0.00
set udg_DamageEvent = 1.00
set udg_DamageEvent = 0.00
endif
call CheckDamagedLifeEvent(true) //in case the unit state event failed from a recursive damage event
//All events have run and the damage amount is finalized.
set life = GetWidgetLife(u)
set udg_DmgEvTrig = CreateTrigger()
call TriggerAddCondition(udg_DmgEvTrig, Filter(function PreCheckDamagedLifeEvent))
if not udg_IsDamageSpell then
if udg_DamageEventAmount != prevAmount then
set life = life + prevAmount - udg_DamageEventAmount
if GetUnitState(u, UNIT_STATE_MAX_LIFE) < life then
set udg_LastDamageHP = life - prevAmount
call UnitAddAbility(u, udg_DamageBlockingAbility)
endif
call SetWidgetLife(u, RMaxBJ(life, 0.42))
endif
call TriggerRegisterUnitStateEvent(udg_DmgEvTrig, u, UNIT_STATE_LIFE, LESS_THAN, RMaxBJ(0.41, life - prevAmount/2.00))
else
set udg_LastDamageHP = GetUnitState(u, UNIT_STATE_MAX_LIFE)
set prevLife = life
if life + prevAmount*0.75 > udg_LastDamageHP then
set life = RMaxBJ(udg_LastDamageHP - prevAmount/2.00, 1.00)
call SetWidgetLife(u, life)
set life = (life + udg_LastDamageHP)/2.00
else
set life = life + prevAmount*0.50
endif
set udg_LastDamageHP = prevLife - (prevAmount - (prevAmount - udg_DamageEventAmount))
call TriggerRegisterUnitStateEvent(udg_DmgEvTrig, u, UNIT_STATE_LIFE, GREATER_THAN, life)
endif
endif
set u = null
set f = null
return false
endfunction
function CreateDmgEvTrg takes nothing returns nothing
set udg_DamageEventTrigger = CreateTrigger()
call TriggerAddCondition(udg_DamageEventTrigger, Filter(function OnUnitDamage))
endfunction
function SetupDmgEv takes nothing returns boolean
local integer i = udg_UDex
local unit u
if udg_UnitIndexEvent == 1.00 then
set u = udg_UDexUnits[i]
if GetUnitAbilityLevel(u, 'Aloc') == 0 and TriggerEvaluate(gg_trg_Damage_Engine_Config) then
set udg_UnitDamageRegistered[i] = true
call TriggerRegisterUnitEvent(udg_DamageEventTrigger, u, EVENT_UNIT_DAMAGED)
call UnitAddAbility(u, udg_SpellDamageAbility)
call UnitMakeAbilityPermanent(u, true, udg_SpellDamageAbility)
endif
set u = null
else
set udg_HideDamageFrom[i] = false
if udg_UnitDamageRegistered[i] then
set udg_UnitDamageRegistered[i] = false
set udg_DamageEventsWasted = udg_DamageEventsWasted + 1
if udg_DamageEventsWasted == 32 then //After 32 registered units have been removed...
set udg_DamageEventsWasted = 0
//Rebuild the mass EVENT_UNIT_DAMAGED trigger:
call DestroyTrigger(udg_DamageEventTrigger)
call CreateDmgEvTrg()
set i = udg_UDexNext[0]
loop
exitwhen i == 0
if udg_UnitDamageRegistered[i] then
call TriggerRegisterUnitEvent(udg_DamageEventTrigger, udg_UDexUnits[i], EVENT_UNIT_DAMAGED)
endif
set i = udg_UDexNext[i]
endloop
endif
endif
endif
return false
endfunction
//===========================================================================
function InitTrig_Damage_Engine takes nothing returns nothing
local unit u = CreateUnit(Player(bj_PLAYER_NEUTRAL_EXTRA), 'uloc', 0, 0, 0)
local integer i = bj_MAX_PLAYERS //Fixed in 3.8
//Create this trigger with UnitIndexEvents in order add and remove units
//as they are created or removed.
local trigger t = CreateTrigger()
call TriggerRegisterVariableEvent(t, "udg_UnitIndexEvent", EQUAL, 1.00)
call TriggerRegisterVariableEvent(t, "udg_UnitIndexEvent", EQUAL, 2.00)
call TriggerAddCondition(t, Filter(function SetupDmgEv))
set t = null
//Run the configuration trigger to set all configurables:
if gg_trg_Damage_Engine_Config == null then
//It's possible this InitTrig_ function ran first, in which case use ExecuteFunc.
call ExecuteFunc("Trig_Damage_Engine_Config_Actions")
else
call TriggerExecute(gg_trg_Damage_Engine_Config)
endif
//Create trigger for storing all EVENT_UNIT_DAMAGED events.
call CreateDmgEvTrg()
//Create GUI-friendly trigger for cleaning up after UnitDamageTarget.
set udg_ClearDamageEvent = CreateTrigger()
call TriggerAddCondition(udg_ClearDamageEvent, Filter(function PreCheckDamagedLifeEvent))
//Disable SpellDamageAbility for every player.
loop
set i = i - 1
call SetPlayerAbilityAvailable(Player(i), udg_SpellDamageAbility, false)
exitwhen i == 0
endloop
//Preload abilities.
call UnitAddAbility(u, udg_DamageBlockingAbility)
call UnitAddAbility(u, udg_SpellDamageAbility)
call RemoveUnit(u)
set u = null
endfunction
//TESH.scrollpos=0
//TESH.alwaysfold=0
scope onDamage initializer Init
private function SanctuaryFilter takes unit u returns boolean
return HasAbility(u, 'A006') and HasChance(16) and IsAlive(u)
endfunction
private function ShadowDanceFilter takes unit u returns boolean
return HasAbility(u, 'A00G') and HasChance(14) and IsAlive(u)
endfunction
private function FaithFilter takes unit attacker, unit target returns boolean
return HasAbility(attacker, 'B001') and IsAlive(target)
endfunction
private function JudgmentFilter takes unit u, boolean isSpell returns boolean
return HasAbility(u, 'B002') and not isSpell and IsAlive(u)
endfunction
private function BarrierFilter takes unit u returns boolean
return HasAbility(u, 'B004') and IsAlive(u)
endfunction
private function CrusadeFilter takes unit u returns boolean
return HasAbility(u, 'B005') and IsAlive(u)
endfunction
private function Actions takes nothing returns nothing
local unit attacker = udg_DamageEventSource
local unit target = udg_DamageEventTarget
local real damageAmount = udg_DamageEventAmount
local boolean isSpell = udg_IsDamageSpell
local real redirectedDmg = 0
if SanctuaryFilter(target) then
call SanctuaryOnHit(target)
endif
if ShadowDanceFilter(attacker) and IsUnitEnemy(attacker, GetOwningPlayer(target)) then
call ShadowDanceOnHit(attacker, target, true)
elseif ShadowDanceFilter(target) and IsUnitEnemy(target, GetOwningPlayer(attacker)) then
call ShadowDanceOnHit(attacker, target, false)
endif
if FaithFilter(attacker, target) then
set damageAmount = damageAmount * 1.35
endif
if JudgmentFilter(target, isSpell) then
set damageAmount = damageAmount * 1.25
endif
if BarrierFilter(target) then
set redirectedDmg = damageAmount * .7
set damageAmount = damageAmount * .3
call RedirectDmg(target, redirectedDmg)
endif
if CrusadeFilter(target) then
set damageAmount = 0
endif
set udg_DamageEventAmount = damageAmount
set attacker = null
set target = null
endfunction
private function Init takes nothing returns nothing
local trigger onDamage = CreateTrigger()
call TriggerRegisterVariableEvent(onDamage, "udg_DamageEvent", EQUAL, 1.0)
call TriggerAddAction(onDamage, function Actions)
endfunction
endscope
//TESH.scrollpos=0
//TESH.alwaysfold=0
scope onAttack initializer Init
private function HolyStrikeFilter takes unit attacker, unit target returns boolean
return HasAbility(attacker, 'A005') and HasChance(15) and IsAlive(target)
endfunction
private function ShadowDanceFilter takes unit attacker, unit target returns boolean
return HasAbility(attacker, 'A00G') and HasChance(25) and HasAbility(target, 'B006') and HasAbility(attacker, 'A00M')
endfunction
private function UnholyStrikeFilter takes unit attacker, unit target returns boolean
return HasAbility(attacker, 'A00Y') and HasChance(10) and IsAlive(target)
endfunction
private function Actions takes nothing returns nothing
local unit attacker = GetAttacker()
local unit target = GetTriggerUnit()
if HolyStrikeFilter(attacker, target) then
call HolyStrikeOnHit(attacker, target)
endif
if ShadowDanceFilter(attacker, target) then
call ShadowDanceIllusion(attacker)
endif
if UnholyStrikeFilter(attacker, target) then
call UnitDamageTarget(attacker, target, 15., true, false, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_UNIVERSAL, null)
call CreateEffectOnUnit("Abilities\\Spells\\Other\\Stampede\\StampedeMissileDeath.mdl", target, "chest")
endif
if attacker == Wq.hero or target == Wq.hero then
if MaliceDefeated then
call IssueImmediateOrder(Wq.hero, "frenzy")
else
call IssueImmediateOrder(Wq.hero, "frenzy")
endif
endif
set attacker = null
set target = null
endfunction
private function Init takes nothing returns nothing
call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_ATTACKED, function Actions)
endfunction
endscope
scope onCast initializer Init
private function Actions takes nothing returns nothing
local unit caster = GetSpellAbilityUnit()
local integer id = GetUnitId(caster)
local real targetX = GetSpellTargetX()
local real targetY = GetSpellTargetY()
if GetSpellAbilityId() == 'A00C' then
if RectContainsCoords(gg_rct_ShrineRegion01, targetX, targetY) /*
*/ or RectContainsCoords(gg_rct_ShrineRegion02, targetX, targetY) then
call BlizzardMessage("Impossible to teleport there.", "|cffffcc00", 31, GetOwningPlayer(caster))
call IssueImmediateOrder(caster, "stop")
elseif not IsPointWalkable(targetX, targetY, 20) then
set EagleDanceLoc[id] = NextWalkablePoint(targetX, targetY)
if Distance(targetX, targetY, GetLocationX(EagleDanceLoc[id]), GetLocationY(EagleDanceLoc[id])) > 80. then
call BlizzardMessage("Impossible to teleport there.", "|cffffcc00", 31, GetOwningPlayer(caster))
call IssueImmediateOrder(caster, "stop")
call RemoveLocation(EagleDanceLoc[id])
set EagleDanceLoc[id] = null
endif
else
set EagleDanceLoc[id] = GetSpellTargetLoc()
endif
endif
set caster = null
endfunction
private function Init takes nothing returns nothing
call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_SPELL_CAST, function Actions)
endfunction
endscope
scope onDeath initializer Init
private function Actions takes nothing returns nothing
local unit dying = GetDyingUnit()
if GetUnitTypeId(dying) == 'npgr' then
call TeleporterDestroy(dying)
endif
if dying == Lb.hero then
call Lightbringer.startRevive()
endif
if dying == Bd.hero then
call Bladedancer.startRevive()
endif
if dying == Wq.hero then
if not MaliceDefeated then
set MaliceDefeated = true
call Malice.replace()
call SecondCinematic()
else
set KathraDefeated = true
call FinalCinematic()
endif
endif
set dying = null
endfunction
private function Init takes nothing returns nothing
call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_DEATH, function Actions)
endfunction
endscope
//TESH.scrollpos=0
//TESH.alwaysfold=0
scope Absolution
globals
private constant integer SPELL_ID = 'A000'
private constant integer DUMMY_SPELL_ID = 'A007'
private constant string DUMMY_SPELL_ORDER = "rejuvination"
private constant string ON_CAST_FX = "Abilities\\Spells\\Items\\AIda\\AIdaCaster.mdl"
private constant string ON_TARGETS_FX = "Abilities\\Spells\\Human\\HolyBolt\\HolyBoltSpecialArt.mdl"
private constant real AOE = 750.
private constant real BASIC_HEAL = 100.
private constant real HP_MULTIPLIER = .25
private constant integer BUFF_ID = 'B000'
private constant real HEAL_PER_SEC = 25.
private constant real MANA_PER_SEC = 18.75
private constant real DURATION = 8.
private constant real INTERVAL = 1.
private constant real FAITH_MULTIPLIER = 1.35
endglobals
private function UnitFilter takes unit u, player p returns boolean
return IsAlive(u) and not IsMechanical(u) and not IsBuilding(u) and not IsMagicImmune(u) and IsUnitAlly(u, p)
endfunction
private function MissingHP takes unit u returns real
return GetUnitState(u, UNIT_STATE_MAX_LIFE) - GetUnitState(u, UNIT_STATE_LIFE)
endfunction
struct Absolution
private unit caster
private group targetGroup = CreateGroup()
private real remainingDuration
private timer loopTimer
private method onDestroy takes nothing returns nothing
call DestroyGroup(.targetGroup)
call ReleaseTimer(.loopTimer)
set .loopTimer = null
set .caster = null
set .targetGroup = null
endmethod
private static method onInterval takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
local group tempGroup = CopyGroup(.targetGroup)
local player owner = GetOwningPlayer(.caster)
local unit picked
local real pickedCurrentHP
local real pickedCurrentMana
local real healOnInterval
local real manaOnInterval
if .remainingDuration > 0 then
set .remainingDuration = .remainingDuration - INTERVAL
loop
set picked = FirstOfGroup(tempGroup)
exitwhen picked == null
if UnitFilter(picked, owner) and HasAbility(picked, BUFF_ID) then
set pickedCurrentHP = GetUnitState(picked, UNIT_STATE_LIFE)
set pickedCurrentMana = GetUnitState(picked, UNIT_STATE_MANA)
set healOnInterval = HEAL_PER_SEC
set manaOnInterval = MANA_PER_SEC
if HasAbility(.caster, 'B001') then
set healOnInterval = healOnInterval * FAITH_MULTIPLIER
set manaOnInterval = manaOnInterval * FAITH_MULTIPLIER
endif
call SetUnitState(picked, UNIT_STATE_LIFE, pickedCurrentHP + healOnInterval)
call SetUnitState(picked, UNIT_STATE_MANA, pickedCurrentMana + manaOnInterval)
endif
call GroupRemoveUnit(tempGroup, picked)
set picked = null
endloop
else
call .destroy()
endif
set picked = null
set owner = null
endmethod
private static method onCast takes nothing returns nothing
local thistype this
local unit casterU = GetSpellAbilityUnit()
local location casterLoc = GetUnitLoc(casterU)
local real casterX = GetUnitX(casterU)
local real casterY = GetUnitY(casterU)
local player casterOwner = GetOwningPlayer(casterU)
local group nearbyAllies = CreateGroup()
local unit picked
local real pickedX
local real pickedY
local real pickedCurrentHP
local real pickedMissingHP
local real healAmount
local unit dummy
call CreateEffectOnLoc(ON_CAST_FX, casterLoc)
call GroupEnumUnitsWithCollision(nearbyAllies, casterX, casterY, AOE)
set this = allocate()
set .caster = casterU
set .targetGroup = CopyGroup(nearbyAllies)
set .remainingDuration = DURATION
set .loopTimer = NewTimerEx(this)
loop
set picked = FirstOfGroup(nearbyAllies)
exitwhen picked == null
if UnitFilter(picked, casterOwner) then
set pickedX = GetUnitX(picked)
set pickedY = GetUnitY(picked)
set pickedCurrentHP = GetUnitState(picked, UNIT_STATE_LIFE)
set pickedMissingHP = MissingHP(picked)
set healAmount = BASIC_HEAL + pickedMissingHP * HP_MULTIPLIER
if HasAbility(casterU, 'B001') then
set healAmount = healAmount * FAITH_MULTIPLIER
endif
call SetUnitState(picked, UNIT_STATE_LIFE, pickedCurrentHP + healAmount)
call CreateEffectOnUnit(ON_TARGETS_FX, picked, "origin")
call UnitRemoveBuffs(picked, false, true)
set dummy = DummyCaster(pickedX, pickedY, 0., casterOwner)
call UnitAddAbility(dummy, DUMMY_SPELL_ID)
call IssueTargetOrder(dummy, DUMMY_SPELL_ORDER, picked)
call RemoveAbilityTimed.create(dummy, DUMMY_SPELL_ID, 1., true)
set dummy = null
endif
call GroupRemoveUnit(nearbyAllies, picked)
set picked = null
endloop
call RemoveLocation(casterLoc)
call DestroyGroup(nearbyAllies)
call TimerStart(.loopTimer, INTERVAL, true, function thistype.onInterval)
set casterU = null
set casterLoc = null
set casterOwner = null
set nearbyAllies = null
endmethod
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_ID, function thistype.onCast)
endmethod
endstruct
endscope
//TESH.scrollpos=0
//TESH.alwaysfold=0
scope SealOfJudgment
globals
private constant integer SPELL_ID = 'A002'
private constant integer DUMMY_SPELL_ID = 'A008'
private constant string DUMMY_SPELL_ORDER = "drunkenhaze"
private constant string ON_CAST_FX = "war3mapImported\\Judgment of Light.mdx"
private constant real BASIC_DMG = 120.
private constant real HP_MULTIPLIER = .02
private constant attacktype ATTACK_TYPE = ATTACK_TYPE_NORMAL
private constant damagetype DAMAGE_TYPE = DAMAGE_TYPE_MAGIC
endglobals
struct SealOfJudgment
private static method onCast takes nothing returns nothing
local unit caster = GetSpellAbilityUnit()
local player casterOwner = GetOwningPlayer(caster)
local unit target = GetSpellTargetUnit()
local real targetX = GetUnitX(target)
local real targetY = GetUnitY(target)
local real targetMaxHP = GetUnitState(target, UNIT_STATE_MAX_LIFE)
local real dmgAmount = BASIC_DMG + targetMaxHP * HP_MULTIPLIER
local unit dummy
call UnitDamageTarget(caster, target, dmgAmount, true, false, ATTACK_TYPE, DAMAGE_TYPE, null)
call CreateEffectOnUnit(ON_CAST_FX, target, "origin")
set dummy = DummyCaster(targetX, targetY, 0., casterOwner)
call UnitAddAbility(dummy, DUMMY_SPELL_ID)
call IssueTargetOrder(dummy, DUMMY_SPELL_ORDER, target)
call RemoveAbilityTimed.create(dummy, DUMMY_SPELL_ID, 1., true)
set dummy = null
set caster = null
set casterOwner = null
set target = null
endmethod
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_ID, function thistype.onCast)
endmethod
endstruct
endscope
//TESH.scrollpos=0
//TESH.alwaysfold=0
scope CrusaderVerdict
globals
private constant integer SPELL_ID = 'A004'
private constant integer DUMMY_SPELL_ID = 'A00B'
private constant string DUMMY_SPELL_ORDER = "unholyfrenzy"
private constant string ON_CASTER_FX = "Abilities\\Spells\\Items\\AIsm\\AIsmTarget.mdl"
private constant string ON_TARGET_FX = "Abilities\\Spells\\Other\\Incinerate\\FireLordDeathExplode.mdl"
private constant real HP_COST = .15
private constant real MIN_DMG = 52.
private constant real MAX_DMG = 62.
private constant real DMG_MULTIPLIER = 2.5
private constant attacktype ATTACK_TYPE = ATTACK_TYPE_NORMAL
private constant damagetype DAMAGE_TYPE = DAMAGE_TYPE_MAGIC
endglobals
struct CrusaderVerdict
private static method onCast takes nothing returns nothing
local unit caster = GetSpellAbilityUnit()
local real casterX = GetUnitX(caster)
local real casterY = GetUnitY(caster)
local player casterOwner = GetOwningPlayer(caster)
local real casterCurrentHP = GetUnitState(caster, UNIT_STATE_LIFE)
local unit target = GetSpellTargetUnit()
local real hpCost = casterCurrentHP * HP_COST
local real dmgAmount = GetRandomReal(MIN_DMG, MAX_DMG) * DMG_MULTIPLIER
local unit dummy
call UnitDamageTarget(caster, caster, hpCost, true, false, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_UNIVERSAL, null)
call UnitDamageTarget(caster, target, dmgAmount, true, false, ATTACK_TYPE, DAMAGE_TYPE, null)
call CreateEffectOnUnit(ON_CASTER_FX, caster, "origin")
call CreateEffectOnUnit(ON_TARGET_FX, target, "origin")
set dummy = DummyCaster(casterX, casterY, 0., casterOwner)
call UnitAddAbility(dummy, DUMMY_SPELL_ID)
call IssueTargetOrder(dummy, DUMMY_SPELL_ORDER, caster)
call RemoveAbilityTimed.create(dummy, DUMMY_SPELL_ID, 1., true)
set dummy = null
set caster = null
set casterOwner = null
set target = null
endmethod
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_ID, function thistype.onCast)
endmethod
endstruct
endscope
//TESH.scrollpos=0
//TESH.alwaysfold=0
library SacredBarrier requires SpellEffectEvent, SpellUtils, DummyUtils, TimerUtils, UnitIndexerGUI
globals
private constant integer SPELL_ID = 'A003'
private constant integer DUMMY_SPELL_ID = 'A00A'
private constant string DUMMY_SPELL_ORDER = "lightningshield"
private constant integer BUFF_ID = 'B004'
private constant real DURATION = 16.
private constant real INTERVAL = .5
private unit array CasterAr
private unit array TargetAr
endglobals
private function UnitFilter takes unit u returns boolean
return IsAlive(u) and HasAbility(u, BUFF_ID)
endfunction
function RedirectDmg takes unit u, real dmg returns nothing
local integer id = GetUnitId(u)
if CasterAr[id] != null then
call UnitDamageTarget(CasterAr[id], CasterAr[id], dmg, true, false, ATTACK_TYPE_MELEE, DAMAGE_TYPE_NORMAL, null)
endif
endfunction
struct SacredBarrier
private unit caster
private unit target
private real remainingDuration
private timer loopTimer
private method onDestroy takes nothing returns nothing
call ReleaseTimer(.loopTimer)
set .loopTimer = null
set .caster = null
set .target = null
endmethod
private static method onInterval takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
local unit casterU = .caster
local unit targetU = .target
local integer id = GetUnitId(targetU)
if .remainingDuration > 0 then
set .remainingDuration = .remainingDuration - INTERVAL
if not UnitFilter(targetU) then
set CasterAr[id] = null
set TargetAr[id] = null
set .remainingDuration = 0
endif
else
set CasterAr[id] = null
set TargetAr[id] = null
call .destroy()
endif
set casterU = null
set targetU = null
endmethod
private static method onCast takes nothing returns nothing
local thistype this
local unit casterU = GetSpellAbilityUnit()
local real casterX = GetUnitX(casterU)
local real casterY = GetUnitY(casterU)
local player casterOwner = GetOwningPlayer(casterU)
local unit targetU = GetSpellTargetUnit()
local real targetX = GetUnitX(targetU)
local real targetY = GetUnitY(targetU)
local integer id = GetUnitId(targetU)
local unit dummy
set dummy = DummyCaster(targetX, targetY, 0., casterOwner)
call UnitAddAbility(dummy, DUMMY_SPELL_ID)
call IssueTargetOrder(dummy, DUMMY_SPELL_ORDER, targetU)
call RemoveAbilityTimed.create(dummy, DUMMY_SPELL_ID, 1., true)
set dummy = null
set CasterAr[id] = casterU
set TargetAr[id] = targetU
set this = allocate()
set .caster = casterU
set .target = targetU
set .remainingDuration = DURATION
set .loopTimer = NewTimerEx(this)
call TimerStart(.loopTimer, INTERVAL, true, function thistype.onInterval)
set casterU = null
set casterOwner = null
set targetU = null
endmethod
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_ID, function thistype.onCast)
endmethod
endstruct
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library HolyStrike requires SpellUtils, DummyUtils
globals
private constant string ON_HIT_FX = "war3mapImported\\HolyBlast.mdx"
private constant string ON_HIT_FX_SEC = "Abilities\\Weapons\\LavaSpawnMissile\\LavaSpawnMissile.mdl"
private constant real DMG_AMOUNT = 70.
private constant real AOE = 200.
private constant attacktype ATTACK_TYPE = ATTACK_TYPE_NORMAL
private constant damagetype DAMAGE_TYPE = DAMAGE_TYPE_NORMAL
endglobals
private function UnitFilter takes unit u, player p returns boolean
return IsAlive(u) and IsUnitEnemy(u, p) and not IsBuilding(u)
endfunction
function HolyStrikeOnHit takes unit attacker, unit target returns nothing
local player attackerOwner = GetOwningPlayer(attacker)
local real targetX = GetUnitX(target)
local real targetY = GetUnitY(target)
local group enemies = CreateGroup()
local unit picked
call CreateEffectOnUnit(ON_HIT_FX, target, "origin")
call CreateEffectOnUnit(ON_HIT_FX_SEC, target, "chest")
call GroupEnumUnitsWithCollision(enemies, targetX, targetY, AOE)
loop
set picked = FirstOfGroup(enemies)
exitwhen picked == null
if UnitFilter(picked, attackerOwner) then
call UnitDamageTarget(attacker, picked, DMG_AMOUNT, true, false, ATTACK_TYPE, DAMAGE_TYPE, null)
endif
call GroupRemoveUnit(enemies, picked)
set picked = null
endloop
call DestroyGroup(enemies)
set attackerOwner = null
set enemies = null
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library Sanctuary requires Missiles, SpellUtils
globals
private constant string MISSILE_MODEL = "Abilities\\Weapons\\FaerieDragonMissile\\FaerieDragonMissile.mdl"
private constant string ON_HIT_FX = "Abilities\\Spells\\Undead\\VampiricAura\\VampiricAuraTarget.mdl"
private constant real HEAL_AMOUNT = 80.
private constant real AOE = 750.
private constant real FAITH_MULTIPLIER = 1.35
endglobals
private function UnitFilter takes unit picked, unit u, player p returns boolean
return IsAlive(picked) and IsUnitAlly(picked, p) and not IsBuilding(u) and not IsMechanical(u) and picked != u
endfunction
private struct Essence extends Missiles
method onHit takes unit hit returns boolean
if hit == .target then
if HasAbility(.source, 'B001') then
call SetUnitState(hit, UNIT_STATE_LIFE, GetUnitState(hit, UNIT_STATE_LIFE) + HEAL_AMOUNT * FAITH_MULTIPLIER)
else
call SetUnitState(hit, UNIT_STATE_LIFE, GetUnitState(hit, UNIT_STATE_LIFE) + HEAL_AMOUNT)
endif
call CreateEffectOnUnit(ON_HIT_FX, hit, "origin")
endif
return false
endmethod
endstruct
function SanctuaryOnHit takes unit u returns nothing
local real unitX = GetUnitX(u)
local real unitY = GetUnitY(u)
local player unitOwner = GetOwningPlayer(u)
local group allies = CreateGroup()
local unit picked
local real pickedX
local real pickedY
local Essence essence
call GroupEnumUnitsWithCollision(allies, unitX, unitY, AOE)
loop
set picked = FirstOfGroup(allies)
exitwhen picked == null
if UnitFilter(picked, u, unitOwner) then
set pickedX = GetUnitX(picked)
set pickedY = GetUnitY(picked)
set essence = Essence.create(unitX, unitY, 50, pickedX, pickedY, 50)
set essence.source = u
set essence.target = picked
set essence.model = MISSILE_MODEL
set essence.speed = 800.
set essence.collision = 10.
set essence.arc = GetRandomReal(10, 45)
set essence.curve = GetRandomReal(5, 20)
call essence.launch()
endif
call GroupRemoveUnit(allies, picked)
set picked = null
endloop
call DestroyGroup(allies)
set unitOwner = null
set allies = null
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
scope EagleDance
globals
private constant integer SPELL_ID = 'A00C'
private constant integer DISABLE_ATTACK = 'A00P'
private constant string ON_HIT_FX = "war3mapImported\\Culling Cleave Silver.mdx"
private constant string ON_CASTER_FX = "Abilities\\Weapons\\ZigguratMissile\\ZigguratMissile.mdl"//"Abilities\\Weapons\\PhoenixMissile\\Phoenix_Missile.mdl"
private constant integer OPACITY = 130
private constant real AOE = 700.
private constant integer NUMBER_OF_HITS = 5
private constant real FIRST_JUMP_DELAY = 0.05
private constant real JUMP_INTERVAL = 0.2
private constant real DMG_PER_HIT = 50.
private constant attacktype ATTACK_TYPE = ATTACK_TYPE_NORMAL
private constant damagetype DAMAGE_TYPE = DAMAGE_TYPE_NORMAL
private constant integer NUMBER_OF_HITS_BF = 6
private constant attacktype ATTACK_TYPE_SM = ATTACK_TYPE_CHAOS
private constant damagetype DAMAGE_TYPE_SM = DAMAGE_TYPE_UNIVERSAL
endglobals
private function UnitFilter takes unit u, player p returns boolean
return IsAlive(u) and not IsBuilding(u) and IsUnitEnemy(u, p)
endfunction
struct EagleDance
private unit caster
private player casterOwner
private location casterLoc
private effect casterFx1
private effect casterFx2
private real targetX
private real targetY
private real teleportX
private real teleportY
private group targetGroup = CreateGroup()
private integer remainingJumps
private integer id
private boolean firstTarget
private timer loopTimer
private method onDestroy takes nothing returns nothing
call RemoveLocation(.casterLoc)
call RemoveLocation(EagleDanceLoc[.id])
call DestroyGroup(.targetGroup)
call DestroyEffect(.casterFx1)
call DestroyEffect(.casterFx2)
call ReleaseTimer(.loopTimer)
set .loopTimer = null
set .caster = null
set .casterOwner = null
set .casterLoc = null
set EagleDanceLoc[.id] = null
set .casterFx1 = null
set .casterFx2 = null
set .targetGroup = null
endmethod
private static method onInterval takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
local unit picked
local real x
local real y
local real angle
local boolean exit = false
local fogmodifier fog
set .teleportX = GetLocationX(EagleDanceLoc[.id])
set .teleportY = GetLocationY(EagleDanceLoc[.id])
if .remainingJumps > 0 then
loop
set picked = GroupRandomUnit(.targetGroup)
if picked == null then
call UnitRemoveAbility(.caster, 'Avul')
call UnitRemoveAbility(.caster, DISABLE_ATTACK)
call SetUnitPathing(.caster, true)
call SetUnitVertexColor(.caster, 255, 255, 255, 255)
call SetUnitX(.caster, .teleportX)
call SetUnitY(.caster, .teleportY)
call .destroy()
else
set x = GetUnitX(picked)
set y = GetUnitY(picked)
set fog = CreateFogModifierRadius(.casterOwner, FOG_OF_WAR_VISIBLE, x, y, 150, false, false)
call FogModifierStart(fog)
if IsUnitVisible(picked, .casterOwner) and UnitFilter(picked, .casterOwner) then
set .remainingJumps = .remainingJumps - 1
call CreateEffectOnUnit(ON_HIT_FX, .caster, "chest")
set angle = -GetUnitFacing(.caster)*bj_DEGTORAD
call SetUnitX(.caster, x + 50*Cos(angle))
call SetUnitY(.caster, y + 50*Sin(angle))
if HasAbility(picked, 'B006') then
call UnitDamageTarget(.caster, picked, DMG_PER_HIT, true, false, ATTACK_TYPE_SM, DAMAGE_TYPE_SM, null)
else
call UnitDamageTarget(.caster, picked, DMG_PER_HIT, true, false, ATTACK_TYPE, DAMAGE_TYPE, null)
endif
if .firstTarget then
set .firstTarget = false
call TimerStart(.loopTimer, JUMP_INTERVAL, true, function thistype.onInterval)
endif
set exit = true
else
call GroupRemoveUnit(.targetGroup, picked)
endif
call DestroyFogModifier(fog)
set fog = null
endif
set picked = null
exitwhen exit
endloop
else
call UnitRemoveAbility(.caster, 'Avul')
call UnitRemoveAbility(.caster, DISABLE_ATTACK)
call SetUnitPathing(.caster, true)
call SetUnitVertexColor(.caster, 255, 255, 255, 255)
call SetUnitX(.caster, .teleportX)
call SetUnitY(.caster, .teleportY)
call .destroy()
endif
endmethod
private static method onlyTeleport takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
set .teleportX = GetLocationX(EagleDanceLoc[.id])
set .teleportY = GetLocationY(EagleDanceLoc[.id])
call SetUnitX(.caster, .teleportX)
call SetUnitY(.caster, .teleportY)
call .destroy()
endmethod
private static method onCast takes nothing returns nothing
local thistype this = allocate()
local boolean groupHasUnits = false
local group enemiesGroup = CreateGroup()
local unit picked
local integer id
local BfPoint bfpoint
set .caster = GetSpellAbilityUnit()
set .casterOwner = GetOwningPlayer(.caster)
set .casterLoc = GetUnitLoc(.caster)
set .id = GetUnitId(.caster)
set .targetX = GetSpellTargetX()
set .targetY = GetSpellTargetY()
if HasAbility(.caster, 'A00M') then
set .remainingJumps = NUMBER_OF_HITS_BF
else
set .remainingJumps = NUMBER_OF_HITS
endif
set .firstTarget = true
set bfpoint = BfPoint.create()
call bfpoint.setHero(.caster)
call bfpoint.action(true)
call GroupEnumUnitsWithCollision(enemiesGroup, .targetX, .targetY, AOE)
set .targetGroup = CopyGroup(enemiesGroup)
loop
set picked = FirstOfGroup(enemiesGroup)
exitwhen picked == null
if UnitFilter(picked, casterOwner) then
if not groupHasUnits then
set groupHasUnits = true
endif
endif
call GroupRemoveUnit(enemiesGroup, picked)
set picked = null
endloop
if groupHasUnits then
set .casterFx1 = AddSpecialEffectTarget(ON_CASTER_FX, .caster, "right hand")
set .casterFx2 = AddSpecialEffectTarget(ON_CASTER_FX, .caster, "left hand")
call UnitAddAbility(.caster, 'Avul')
call UnitAddAbility(.caster, DISABLE_ATTACK)
call SetUnitPathing(.caster, false)
call SetUnitVertexColor(.caster, 255, 255, 255, OPACITY)
set .loopTimer = NewTimerEx(this)
call TimerStart(.loopTimer, FIRST_JUMP_DELAY, true, function thistype.onInterval)
else
set .casterFx1 = AddSpecialEffectTarget(ON_CASTER_FX, .caster, "right hand")
set .casterFx2 = AddSpecialEffectTarget(ON_CASTER_FX, .caster, "left hand")
set .loopTimer = NewTimerEx(this)
call TimerStart(.loopTimer, 0.05, true, function thistype.onlyTeleport)
endif
call DestroyGroup(enemiesGroup)
set enemiesGroup = null
endmethod
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_ID, function thistype.onCast)
endmethod
endstruct
endscope
//TESH.scrollpos=0
//TESH.alwaysfold=0
scope SnakeDance
globals
private constant integer SPELL_ID = 'A00D'
private constant integer DUMMY_SPELL_ID = 'A00N'
private constant string DUMMY_SPELL_ORDER = "cripple"
private constant string ON_HIT_FX = "Abilities\\Weapons\\DemonHunterMissile\\DemonHunterMissile.mdl"
private constant string ON_CASTER_FX = "Abilities\\Weapons\\IllidanMissile\\IllidanMissile.mdl"
private constant real DMG_AMOUNT = 150.
private constant attacktype ATTACK_TYPE = ATTACK_TYPE_NORMAL
private constant damagetype DAMAGE_TYPE = DAMAGE_TYPE_NORMAL
private constant real LIFE_STEAL_BF = .5
private constant real MANA_STEAL_BF = .5
private constant attacktype ATTACK_TYPE_SM = ATTACK_TYPE_CHAOS
private constant damagetype DAMAGE_TYPE_SM = DAMAGE_TYPE_UNIVERSAL
endglobals
struct SnakeDance
private static method onCast takes nothing returns nothing
local unit caster = GetSpellAbilityUnit()
local real currentHP = GetUnitState(caster, UNIT_STATE_LIFE)
local real currentMana = GetUnitState(caster, UNIT_STATE_MANA)
local player casterOwner = GetOwningPlayer(caster)
local unit target = GetSpellTargetUnit()
local real targetX = GetUnitX(target)
local real targetY = GetUnitY(target)
local real dmgAmount = DMG_AMOUNT
local unit dummy
local BfPoint bfpoint
if HasAbility(target, 'B006') then
call UnitDamageTarget(caster, target, dmgAmount, true, false, ATTACK_TYPE_SM, DAMAGE_TYPE_SM, null)
else
call UnitDamageTarget(caster, target, dmgAmount, true, false, ATTACK_TYPE, DAMAGE_TYPE, null)
endif
call CreateEffectOnUnit(ON_HIT_FX, target, "chest")
call CreateEffectOnUnit(ON_CASTER_FX, caster, "right hand")
call CreateEffectOnUnit(ON_CASTER_FX, caster, "left hand")
set dummy = DummyCaster(targetX, targetY, 0., casterOwner)
call UnitAddAbility(dummy, DUMMY_SPELL_ID)
call IssueTargetOrder(dummy, DUMMY_SPELL_ORDER, target)
call RemoveAbilityTimed.create(dummy, DUMMY_SPELL_ID, 1., true)
set dummy = null
if HasAbility(caster, 'A00M') then
call SetUnitState(caster, UNIT_STATE_LIFE, currentHP + dmgAmount * LIFE_STEAL_BF)
call SetUnitState(caster, UNIT_STATE_MANA, currentMana + dmgAmount * MANA_STEAL_BF)
endif
set bfpoint = BfPoint.create()
call bfpoint.setHero(caster)
call bfpoint.action(true)
set caster = null
set casterOwner = null
set target = null
endmethod
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_ID, function thistype.onCast)
endmethod
endstruct
endscope
//TESH.scrollpos=0
//TESH.alwaysfold=0
scope TigerDance
globals
private constant integer SPELL_ID = 'A00E'
private constant integer DUMMY_SPELL_ID = 'A00O'
private constant string DUMMY_SPELL_ORDER = "thunderbolt"
private constant string ON_HIT_FX = "war3mapImported\\Storm Bolt.mdx"
private constant real DMG_AMOUNT = 180.
private constant attacktype ATTACK_TYPE = ATTACK_TYPE_NORMAL
private constant damagetype DAMAGE_TYPE = DAMAGE_TYPE_NORMAL
private constant attacktype ATTACK_TYPE_SM = ATTACK_TYPE_CHAOS
private constant damagetype DAMAGE_TYPE_SM = DAMAGE_TYPE_UNIVERSAL
endglobals
struct TigerDance
private static method onCast takes nothing returns nothing
local unit caster = GetSpellAbilityUnit()
local player casterOwner = GetOwningPlayer(caster)
local unit target = GetSpellTargetUnit()
local real targetX = GetUnitX(target)
local real targetY = GetUnitY(target)
local real dmgAmount = DMG_AMOUNT
local unit dummy
local BfPoint bfpoint
if HasAbility(target, 'B006') then
call UnitDamageTarget(caster, target, dmgAmount, true, false, ATTACK_TYPE_SM, DAMAGE_TYPE_SM, null)
else
call UnitDamageTarget(caster, target, dmgAmount, true, false, ATTACK_TYPE, DAMAGE_TYPE, null)
endif
call CreateEffectOnUnit(ON_HIT_FX, target, "chest")
set dummy = DummyCaster(targetX, targetY, 0., casterOwner)
call UnitAddAbility(dummy, DUMMY_SPELL_ID)
if HasAbility(caster, 'A00M') then
call SetUnitAbilityLevel(dummy, DUMMY_SPELL_ID, 2)
else
call SetUnitAbilityLevel(dummy, DUMMY_SPELL_ID, 1)
endif
call IssueTargetOrder(dummy, DUMMY_SPELL_ORDER, target)
call RemoveAbilityTimed.create(dummy, DUMMY_SPELL_ID, 1., true)
set dummy = null
set bfpoint = BfPoint.create()
call bfpoint.setHero(caster)
call bfpoint.action(true)
set caster = null
set casterOwner = null
set target = null
endmethod
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_ID, function thistype.onCast)
endmethod
endstruct
endscope
//TESH.scrollpos=0
//TESH.alwaysfold=0
scope PhoenixDance
globals
private constant integer SPELL_ID = 'A00F'
private constant string MISSILE_MODEL = "war3mapImported\\HolyPhoenix.mdx"
private constant string ON_CAST_FX = "Abilities\\Weapons\\PhoenixMissile\\Phoenix_Missile.mdl"
private constant real AOE = 250.
private constant real DMG_AMOUNT = 220.
private constant attacktype ATTACK_TYPE = ATTACK_TYPE_NORMAL
private constant damagetype DAMAGE_TYPE = DAMAGE_TYPE_NORMAL
private constant real AOE_BF = 350.
private constant attacktype ATTACK_TYPE_SM = ATTACK_TYPE_CHAOS
private constant damagetype DAMAGE_TYPE_SM = DAMAGE_TYPE_UNIVERSAL
endglobals
private function UnitFilter takes unit u, player p returns boolean
return IsAlive(u) and not IsBuilding(u) and IsUnitEnemy(u, p)
endfunction
private struct Phoenix extends Missiles
private static method onCast takes nothing returns nothing
local unit caster = GetSpellAbilityUnit()
local location casterLoc = GetUnitLoc(caster)
local real casterX = GetUnitX(caster)
local real casterY = GetUnitY(caster)
local player casterOwner = GetOwningPlayer(caster)
local real dmgAmount = DMG_AMOUNT
local group enemies = CreateGroup()
local thistype this = thistype.create(casterX, casterY, 0, casterX, casterY, 2000)
local real area
local unit picked
local BfPoint bfpoint
set source = caster
set model = MISSILE_MODEL
set scale = 2.
set speed = 800.
set duration = 2.
call launch()
if HasAbility(caster, 'A00M') then
set area = AOE_BF
else
set area = AOE
endif
call CreateEffectOnLoc(ON_CAST_FX, casterLoc)
call GroupEnumUnitsWithCollision(enemies, casterX, casterY, area)
loop
set picked = FirstOfGroup(enemies)
exitwhen picked == null
if UnitFilter(picked, casterOwner) then
if HasAbility(target, 'B006') then
call UnitDamageTarget(caster, picked, dmgAmount, true, false, ATTACK_TYPE_SM, DAMAGE_TYPE_SM, null)
else
call UnitDamageTarget(caster, picked, dmgAmount, true, false, ATTACK_TYPE, DAMAGE_TYPE, null)
endif
endif
call GroupRemoveUnit(enemies, picked)
set picked = null
endloop
set bfpoint = BfPoint.create()
call bfpoint.setHero(caster)
call bfpoint.action(true)
call RemoveLocation(casterLoc)
call DestroyGroup(enemies)
set caster = null
set casterLoc = null
set casterOwner = null
set enemies = null
endmethod
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_ID, function thistype.onCast)
endmethod
endstruct
endscope
//TESH.scrollpos=0
//TESH.alwaysfold=0
scope WyrmDance
globals
private constant integer SPELL_ID = 'A009'
private constant string MISSILE_MODEL = "war3mapImported\\FireworksDragonHead.mdx"
private constant real DMG_AMOUNT = 350.
private constant real DISTANCE = 600.
private constant attacktype ATTACK_TYPE = ATTACK_TYPE_CHAOS
private constant damagetype DAMAGE_TYPE = DAMAGE_TYPE_UNIVERSAL
private constant integer BOOK_ID = 'A00R'
private constant real DMG_PER_POINT = 40.
private constant real DISTANCE_PER_POINT = 80.
endglobals
private function UnitFilter takes unit u, player p returns boolean
return IsAlive(u) and not IsBuilding(u) and IsUnitEnemy(u, p)
endfunction
private function AngleBetweenCoordinates takes real x, real y, real x2, real y2 returns real
return Atan2(y2 - y, x2 - x)
endfunction
private function GetPoints takes unit u returns integer
return GetUnitAbilityLevel(u, BOOK_ID) - 1
endfunction
private struct WyrmDance extends Missiles
method onHit takes unit hit returns boolean
if UnitFilter(hit, GetOwningPlayer(source)) then
call UnitDamageTarget(source, hit, damage, true, false, ATTACK_TYPE, DAMAGE_TYPE, null)
endif
return false
endmethod
private static method onCast takes nothing returns nothing
local unit caster = GetSpellAbilityUnit()
local real casterX = GetUnitX(caster)
local real casterY = GetUnitY(caster)
local real points = GetPoints(caster)
local player casterOwner = GetOwningPlayer(caster)
local real totalDistance = DISTANCE + points * DISTANCE_PER_POINT
local real targetX = GetSpellTargetX()
local real targetY = GetSpellTargetY()
local real angle = AngleBetweenCoordinates(targetX, targetY, casterX, casterY)
local real toX = casterX - totalDistance * Cos(angle)
local real toY = casterY - totalDistance * Sin(angle)
local real dmgAmount = DMG_AMOUNT + points * DMG_PER_POINT
local thistype this = thistype.create(casterX, casterY, 0, toX, toY, 0)
local BfPoint bfpoint
set source = caster
set model = MISSILE_MODEL
set damage = dmgAmount
set scale = 2.
set speed = 1000.
set collision = 100.
call launch()
set bfpoint = BfPoint.create()
call bfpoint.setHero(caster)
call bfpoint.action(false)
set caster = null
set casterOwner = null
endmethod
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_ID, function thistype.onCast)
endmethod
endstruct
endscope
//TESH.scrollpos=0
//TESH.alwaysfold=0
library ShadowDance requires SpellUtils, DummyUtils
globals
private constant integer DUMMY_SPELL_MARK_ID = 'A00Q'
private constant string DUMMY_SPELL_MARK_ORDER = "slow"
private constant integer DUMMY_SPELL_ILL_ID = 'A00W'
private constant integer DUMMY_SPELL_ILL_ORDER = 852274
endglobals
private function IllusionFilter takes unit sum, unit ill returns boolean
return GetUnitTypeId(sum) == 'dum0' and IsUnitIllusion(ill) and HasAbility(ill, 'B007')
endfunction
function ShadowDanceOnHit takes unit attacker, unit target, boolean isHeroAttacker returns nothing
local player owner
local unit u
local real x
local real y
local unit dummy
if isHeroAttacker then
set owner = GetOwningPlayer(attacker)
set u = target
else
set owner = GetOwningPlayer(target)
set u = attacker
endif
set x = GetUnitX(u)
set y = GetUnitY(u)
set dummy = DummyCaster(x, y, 0., owner)
call UnitAddAbility(dummy, DUMMY_SPELL_MARK_ID)
call IssueTargetOrder(dummy, DUMMY_SPELL_MARK_ORDER, u)
call RemoveAbilityTimed.create(dummy, DUMMY_SPELL_MARK_ID, 1., true)
set dummy = null
set owner = null
set u = null
endfunction
function ShadowDanceIllusion takes unit u returns nothing
local real x = GetUnitX(u)
local real y = GetUnitY(u)
local player owner = GetOwningPlayer(u)
local unit dummy
set dummy = DummyCaster(x, y, 0., owner)
call UnitAddAbility(dummy, DUMMY_SPELL_ILL_ID)
call IssueTargetOrderById(dummy, DUMMY_SPELL_ILL_ORDER, u)
call RemoveAbilityTimed.create(dummy, DUMMY_SPELL_ILL_ID, 1., true)
set dummy = null
set owner = null
endfunction
struct ShadowDance
private static method onSummon takes nothing returns nothing
local unit summoner = GetSummoningUnit()
local unit illusion = GetSummonedUnit()
if IllusionFilter(summoner, illusion) then
call UnitAddAbility(illusion, 'Aloc')
call SetUnitMoveSpeed(illusion, 100000.)
endif
set summoner = null
set illusion = null
endmethod
private static method onInit takes nothing returns nothing
call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_SUMMON, function thistype.onSummon)
endmethod
endstruct
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library BladeFury requires TimerUtils
globals
private constant integer BOOK_ID = 'A00R'
private constant real DURATION = 20.
private constant real INTERVAL = .5
private integer array Charge
private integer array PointsAr
endglobals
function ChargeSwap takes unit u, integer n returns nothing
local integer i = 0
set Charge[0] = 'A00H'
set Charge[1] = 'A00I'
set Charge[2] = 'A00J'
set Charge[3] = 'A00K'
set Charge[4] = 'A00L'
set Charge[5] = 'A00M'
loop
exitwhen i > 5
call UnitRemoveAbility(u, Charge[i])
set i = i + 1
endloop
call SetUnitAbilityLevel(u, BOOK_ID, n + 1)
call UnitAddAbility(u, Charge[n])
endfunction
struct BfPoint
private unit hero
private real remainingDuration = 0.
private timer loopTimer
static method create takes nothing returns BfPoint
local BfPoint bfpoint = BfPoint.allocate()
return bfpoint
endmethod
method setHero takes unit u returns nothing
set .hero = u
endmethod
private method onDestroy takes nothing returns nothing
call ReleaseTimer(.loopTimer)
set .loopTimer = null
set .hero = null
endmethod
private static method onInterval takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
local integer id = GetUnitId(.hero)
if .remainingDuration > 0 then
if IsAlive(.hero) and PointsAr[id] > 0 then
set .remainingDuration = .remainingDuration - INTERVAL
else
set .remainingDuration = 0
endif
else
if PointsAr[id] > 0 then
set PointsAr[id] = PointsAr[id] - 1
endif
if IsAlive(.hero) then
if PointsAr[id] < 6 then
call ChargeSwap(.hero, PointsAr[id])
endif
endif
call .destroy()
endif
endmethod
method action takes boolean isGenerating returns nothing
local integer id = GetUnitId(.hero)
if isGenerating then
set PointsAr[id] = PointsAr[id] + 1
if PointsAr[id] < 6 then
call ChargeSwap(.hero, PointsAr[id])
endif
set .remainingDuration = DURATION
set .loopTimer = NewTimerEx(this)
call TimerStart(.loopTimer, INTERVAL, true, function thistype.onInterval)
else
set PointsAr[id] = 0
call ChargeSwap(.hero, 0)
call .destroy()
endif
endmethod
endstruct
endlibrary
scope MaliceSpells
globals
private constant integer SPELL_ID_INSTANT = 'A013'
private constant integer SPELL_ID_TARGET = 'A015'
private constant integer DISCHARGE_SPELL_ID = 'A016'
private constant string DISCHARGE_SPELL_ORDER = "acidbomb"
private constant string DISCHARGE_FX = "war3mapImported\\Soul Discharge.mdx"
private constant real AOE = 1100.
private constant string TELEPORT_FX = "Abilities\\Spells\\Human\\MassTeleport\\MassTeleportTarget.mdl"
private constant integer NOVA_SPELL_ID = 'A017'
private constant string NOVA_SPELL_ORDER = "frostnova"
private constant integer BOLT_SPELL_ID = 'A018'
private constant string BOLT_SPELL_ORDER = "thunderbolt"
private constant integer CURSE_SPELL_ID = 'A019'
private constant string CURSE_SPELL_ORDER = "curse"
endglobals
private function UnitFilter takes unit u, player p returns boolean
return IsAlive(u) and not IsBuilding(u) and IsUnitEnemy(u, p)
endfunction
struct MaliceSpells
private unit caster
private player casterOwner
private location casterLoc
private real casterX
private real casterY
private unit target
private real targetX
private real targetY
private integer spellId
private string spellOrder
private timer loopTimer
private method onDestroy takes nothing returns nothing
call RemoveLocation(.casterLoc)
call ReleaseTimer(.loopTimer)
set .loopTimer = null
set .caster = null
set .casterOwner = null
set .casterLoc = null
set .target = null
endmethod
private static method castDischarge takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
local boolean enemyPicked = false
local group enemiesGroup = CreateGroup()
local unit dummy
local unit picked
local real pickedX
local real pickedY
call GroupEnumUnitsWithCollision(enemiesGroup, .casterX, .casterY, AOE)
loop
set picked = GroupRandomUnit(enemiesGroup)
exitwhen picked == null or enemyPicked
if UnitFilter(picked, .casterOwner) then
set enemyPicked = true
set pickedX = GetUnitX(picked)
set pickedY = GetUnitY(picked)
call CreateEffectOnLoc(DISCHARGE_FX, GetUnitLoc(picked))
set dummy = DummyCaster(pickedX, pickedY, 0., .casterOwner)
call UnitAddAbility(dummy, DISCHARGE_SPELL_ID)
call IssueTargetOrder(dummy, DISCHARGE_SPELL_ORDER, picked)
call RemoveAbilityTimed.create(dummy, DISCHARGE_SPELL_ID, 1., true)
set dummy = null
endif
call GroupRemoveUnit(enemiesGroup, picked)
set picked = null
endloop
call StartSound(gg_snd_MaliceDischarge)
call DestroyGroup(enemiesGroup)
set enemiesGroup = null
call .destroy()
endmethod
private static method castPortal takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
local integer random = GetRandomInt(1, 4)
local real x = .casterX + 120. * Cos(GetUnitFacing(.caster) * bj_DEGTORAD)
local real y = .casterY + 120. * Sin(GetUnitFacing(.caster) * bj_DEGTORAD)
local location loc = Location(x, y)
local unit u
if random == 1 then
set u = CreateUnitAtLoc(Player(5), 'h000', loc, 270.)
call CreateEffectOnLoc(TELEPORT_FX, GetUnitLoc(u))
set u = CreateUnitAtLoc(Player(5), 'h001', loc, 270.)
call CreateEffectOnLoc(TELEPORT_FX, GetUnitLoc(u))
set u = CreateUnitAtLoc(Player(5), 'h001', loc, 270.)
call CreateEffectOnLoc(TELEPORT_FX, GetUnitLoc(u))
elseif random == 2 then
set u = CreateUnitAtLoc(Player(5), 'h000', loc, 270.)
call CreateEffectOnLoc(TELEPORT_FX, GetUnitLoc(u))
set u = CreateUnitAtLoc(Player(5), 'h000', loc, 270.)
call CreateEffectOnLoc(TELEPORT_FX, GetUnitLoc(u))
elseif random == 3 then
set u = CreateUnitAtLoc(Player(5), 'h001', loc, 270.)
call CreateEffectOnLoc(TELEPORT_FX, GetUnitLoc(u))
set u = CreateUnitAtLoc(Player(5), 'h001', loc, 270.)
call CreateEffectOnLoc(TELEPORT_FX, GetUnitLoc(u))
set u = CreateUnitAtLoc(Player(5), 'h003', loc, 270.)
call CreateEffectOnLoc(TELEPORT_FX, GetUnitLoc(u))
elseif random == 4 then
set u = CreateUnitAtLoc(Player(5), 'h002', loc, 270.)
call CreateEffectOnLoc(TELEPORT_FX, GetUnitLoc(u))
endif
call StartSound(gg_snd_MalicePortal)
call RemoveLocation(loc)
set loc = null
set u = null
call .destroy()
endmethod
private static method castMaze takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
local integer i = 1
local real x
local real y
local location loc
local unit u
loop
exitwhen i > 5
set x = .casterX + 400. * Cos((72 * i) * bj_DEGTORAD)
set y = .casterY + 400. * Sin((72 * i) * bj_DEGTORAD)
set loc = Location(x, y)
set u = CreateUnitAtLoc(.casterOwner, 'h009', loc, 270.)
call UnitApplyTimedLife(u, 'BTLF', 5.)
set u = null
call RemoveLocation(loc)
set loc = null
set i = i + 1
endloop
set i = 1
loop
exitwhen i > 9
set x = .casterX + 850. * Cos((40 * i) * bj_DEGTORAD)
set y = .casterY + 850. * Sin((40 * i) * bj_DEGTORAD)
set loc = Location(x, y)
set u = CreateUnitAtLoc(.casterOwner, 'h009', loc, 270.)
call UnitApplyTimedLife(u, 'BTLF', 5.)
set u = null
call RemoveLocation(loc)
set loc = null
set i = i + 1
endloop
call StartSound(gg_snd_MaliceMaze)
call .destroy()
endmethod
private static method castTargetSpell takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
local integer random = GetRandomInt(1, 2)
local unit dummy
set dummy = DummyCaster(.targetX, .targetY, 0., .casterOwner)
call UnitAddAbility(dummy, .spellId)
call IssueTargetOrder(dummy, .spellOrder, .target)
call RemoveAbilityTimed.create(dummy, .spellId, 1., true)
set dummy = null
if random == 1 then
call StartSound(gg_snd_MaliceSpell1)
elseif random == 2 then
call StartSound(gg_snd_MaliceSpell2)
endif
call .destroy()
endmethod
private static method onCastInstant takes nothing returns nothing
local thistype this = allocate()
local integer random = GetRandomInt(1, 5)
set .caster = GetSpellAbilityUnit()
set .casterOwner = GetOwningPlayer(.caster)
set .casterLoc = GetUnitLoc(.caster)
set .casterX = GetUnitX(.caster)
set .casterY = GetUnitY(.caster)
set .loopTimer = NewTimerEx(this)
if random == 1 or random == 2 then
call TimerStart(.loopTimer, 0.05, true, function thistype.castDischarge)
elseif random == 3 then
call TimerStart(.loopTimer, 0.05, true, function thistype.castPortal)
elseif random == 4 or random == 5 then
call TimerStart(.loopTimer, 0.05, true, function thistype.castMaze)
endif
endmethod
private static method onCastTarget takes nothing returns nothing
local thistype this = allocate()
local integer random = GetRandomInt(1, 3)
set .caster = GetSpellAbilityUnit()
set .casterOwner = GetOwningPlayer(.caster)
set .target = GetSpellTargetUnit()
set .targetX = GetUnitX(.target)
set .targetY = GetUnitY(.target)
set .loopTimer = NewTimerEx(this)
if random == 1 then
set .spellId = NOVA_SPELL_ID
set .spellOrder = NOVA_SPELL_ORDER
elseif random == 2 then
set .spellId = BOLT_SPELL_ID
set .spellOrder = BOLT_SPELL_ORDER
elseif random == 3 then
set .spellId = CURSE_SPELL_ID
set .spellOrder = CURSE_SPELL_ORDER
endif
call TimerStart(.loopTimer, 0.05, true, function thistype.castTargetSpell)
endmethod
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_ID_INSTANT, function thistype.onCastInstant)
call RegisterSpellEffectEvent(SPELL_ID_TARGET, function thistype.onCastTarget)
endmethod
endstruct
endscope
scope KathraSpells
globals
private constant integer SPELL_ID_INSTANT = 'A014'
private constant integer SPELL_ID_TARGET = 'A01A'
private constant integer RAZE_SPELL_ID = 'A01B'
private constant string RAZE_SPELL_ORDER = "flamestrike"
private constant string DRAIN_FX = "Abilities\\Spells\\Undead\\AnimateDead\\AnimateDeadTarget.mdl"
private constant real DRAIN_AMOUNT = 150.
private constant integer SPIRITS_SPELL_ID = 'A01C'
private constant string SPIRITS_SPELL_ORDER = "locustswarm"
private constant real AOE = 1100.
private constant integer IMPALE_SPELL_ID = 'A01D'
private constant string IMPALE_SPELL_ORDER = "impale"
private constant integer LIGHTNING_SPELL_ID = 'A01E'
private constant string LIGHTNING_SPELL_ORDER = "forkedlightning"
private constant integer TERRORIZE_SPELL_ID = 'A01F'
private constant string TERRORIZE_SPELL_ORDER = "drunkenhaze"
endglobals
private function UnitFilter takes unit u, player p returns boolean
return IsAlive(u) and not IsBuilding(u) and IsUnitEnemy(u, p)
endfunction
struct KathraSpells
private unit caster
private player casterOwner
private location casterLoc
private real casterX
private real casterY
private unit target
// private real targetX
// private real targetY
private integer spellId
private string spellOrder
private timer loopTimer
private method onDestroy takes nothing returns nothing
call RemoveLocation(.casterLoc)
call ReleaseTimer(.loopTimer)
set .loopTimer = null
set .caster = null
set .casterOwner = null
set .casterLoc = null
endmethod
private static method castRaze takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
local boolean enemyPicked = false
local group enemiesGroup = CreateGroup()
local unit dummy
local unit picked
local real pickedX
local real pickedY
call GroupEnumUnitsWithCollision(enemiesGroup, .casterX, .casterY, AOE)
loop
set picked = GroupRandomUnit(enemiesGroup)
exitwhen picked == null or enemyPicked
if UnitFilter(picked, .casterOwner) then
set enemyPicked = true
set pickedX = GetUnitX(picked)
set pickedY = GetUnitY(picked)
set dummy = DummyCaster(pickedX, pickedY, 0., .casterOwner)
call UnitAddAbility(dummy, RAZE_SPELL_ID)
call IssueTargetOrder(dummy, RAZE_SPELL_ORDER, picked)
call RemoveAbilityTimed.create(dummy, RAZE_SPELL_ID, 1., true)
set dummy = null
endif
call GroupRemoveUnit(enemiesGroup, picked)
set picked = null
endloop
call StartSound(gg_snd_KathraRaze)
call DestroyGroup(enemiesGroup)
set enemiesGroup = null
call .destroy()
endmethod
private static method castDrain takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
local group enemiesGroup = CreateGroup()
local real currentHP = GetUnitState(.caster, UNIT_STATE_LIFE)
local real drainedAmount = 0
local unit picked
call GroupEnumUnitsWithCollision(enemiesGroup, .casterX, .casterY, AOE)
loop
set picked = FirstOfGroup(enemiesGroup)
exitwhen picked == null
if UnitFilter(picked, .casterOwner) then
if GetWidgetLife(picked) >= DRAIN_AMOUNT then
set drainedAmount = drainedAmount + DRAIN_AMOUNT
else
set drainedAmount = drainedAmount + GetWidgetLife(picked)
endif
call CreateEffectOnLoc(DRAIN_FX, GetUnitLoc(picked))
call UnitDamageTarget(.caster, picked, DRAIN_AMOUNT, true, false, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_UNIVERSAL, null)
endif
call GroupRemoveUnit(enemiesGroup, picked)
set picked = null
endloop
call SetUnitState(.caster, UNIT_STATE_LIFE, currentHP + drainedAmount)
call StartSound(gg_snd_KathraDrain)
call DestroyGroup(enemiesGroup)
set enemiesGroup = null
call .destroy()
endmethod
private static method castSpirits takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
local unit dummy
set dummy = DummyCaster(.casterX, .casterY, 0., .casterOwner)
call UnitAddAbility(dummy, SPIRITS_SPELL_ID)
call IssueImmediateOrder(dummy, SPIRITS_SPELL_ORDER)
call RemoveAbilityTimed.create(dummy, SPIRITS_SPELL_ID, 8., true)
set dummy = null
call StartSound(gg_snd_KathraSpirits)
call .destroy()
endmethod
private static method castTargetSpell takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
local integer random = GetRandomInt(1, 2)
local unit dummy
set dummy = DummyCaster(.casterX, .casterY, 0., .casterOwner)
call UnitAddAbility(dummy, .spellId)
call IssueTargetOrder(dummy, .spellOrder, .target)
call RemoveAbilityTimed.create(dummy, .spellId, 1., true)
set dummy = null
if random == 1 then
call StartSound(gg_snd_KathraSpell1)
elseif random == 2 then
call StartSound(gg_snd_KathraSpell2)
endif
call .destroy()
endmethod
private static method onCastInstant takes nothing returns nothing
local thistype this = allocate()
local integer random = GetRandomInt(1, 3)
set .caster = GetSpellAbilityUnit()
set .casterOwner = GetOwningPlayer(.caster)
set .casterLoc = GetUnitLoc(.caster)
set .casterX = GetUnitX(.caster)
set .casterY = GetUnitY(.caster)
set .loopTimer = NewTimerEx(this)
if random == 1 then
call TimerStart(.loopTimer, 0.05, true, function thistype.castRaze)
elseif random == 2 then
call TimerStart(.loopTimer, 0.05, true, function thistype.castDrain)
elseif random == 3 then
call TimerStart(.loopTimer, 0.05, true, function thistype.castSpirits)
endif
endmethod
private static method onCastTarget takes nothing returns nothing
local thistype this = allocate()
local integer random = GetRandomInt(1, 3)
set .caster = GetSpellAbilityUnit()
set .casterOwner = GetOwningPlayer(.caster)
set .target = GetSpellTargetUnit()
set .casterX = GetUnitX(.caster)
set .casterY = GetUnitY(.caster)
set .loopTimer = NewTimerEx(this)
if random == 1 then
set .spellId = IMPALE_SPELL_ID
set .spellOrder = IMPALE_SPELL_ORDER
elseif random == 2 then
set .spellId = LIGHTNING_SPELL_ID
set .spellOrder = LIGHTNING_SPELL_ORDER
elseif random == 3 then
set .spellId = TERRORIZE_SPELL_ID
set .spellOrder = TERRORIZE_SPELL_ORDER
endif
call TimerStart(.loopTimer, 0.05, true, function thistype.castTargetSpell)
endmethod
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_ID_INSTANT, function thistype.onCastInstant)
call RegisterSpellEffectEvent(SPELL_ID_TARGET, function thistype.onCastTarget)
endmethod
endstruct
endscope
library MapSetup initializer Init requires Heroes, Bossfight, HereticsChannel, Cinematics, MissionUtils
globals
boolean EasyDif
player SingleP
quest MainQuest
questitem MainQFirstItem
questitem MainQSecondItem
questitem MainQThirdItem
Lightbringer Lb
Bladedancer Bd
Malice Wq
destructable Crystal
unit array Heretic
location array EagleDanceLoc
endglobals
private function HeroHaveNotController takes player p returns boolean
return GetPlayerController(p) != MAP_CONTROL_USER or GetPlayerSlotState(p) != PLAYER_SLOT_STATE_PLAYING
endfunction
private function Initialization takes nothing returns nothing
local item heroItem
set Lb = Lightbringer.create()
set Bd = Bladedancer.create()
set Wq = Malice.create()
set Crystal = gg_dest_B00G_1184
call Lb.maxLevel()
call Bd.maxLevel()
call Wq.maxLevel()
call Lb.learnSpells()
call Bd.learnSpells()
set heroItem = CreateItem('I000', GetUnitX(Lb.hero), GetUnitY(Lb.hero))
call UnitAddItem(Lb.hero, heroItem)
set heroItem = CreateItem('I001', GetUnitX(Bd.hero), GetUnitY(Bd.hero))
call UnitAddItem(Bd.hero, heroItem)
if HeroHaveNotController(Lb.owner) then
call Lb.changeOwner(Bd.owner)
set EasyDif = true
set SingleP = Bd.owner
elseif HeroHaveNotController(Bd.owner) then
call Bd.changeOwner(Lb.owner)
set EasyDif = true
set SingleP = Lb.owner
else
set EasyDif = false
endif
call FirstCinematic()
call SetupHeretics()
set MainQuest = CreateMainQuest()
set MainQFirstItem = QuestObjetive(MainQuest, "Reach the Shrine")
set MainQSecondItem = QuestObjetive(MainQuest, "Stop Malice")
call CreateInfoQuest("About the map", "|cffffcc00Entry for the 17th Mini Mapping Contest|r
The Shrine of Duredhel is a challenging bossfight map designed for 2 players, although it has support for single player games. This is not an easy map, instead it has an appropiate difficulty level to keep the action going during the fight.", "ReplaceableTextures\\CommandButtons\\BTNTomeOfRetraining.blp")
call CreateInfoQuest("Respawn System", "When a Hero dies, its soul is preserved and will respawn again after 10 seconds in front of the Holy Eagle Statue. Don't despair, this waiting time is designed to prevent players from exploiting potential advantages that make the battle easier.", "ReplaceableTextures\\CommandButtons\\BTNResurrection.blp")
call CreateInfoQuest("Damage & armor types", "A new Damage / Armor interaction has been created to encourage players to support each other. While one Hero's spells may be strong against some enemies, they can also be weak against others.
- |cffb92f28Physical|r attacks are strong against Magical resistant enemies but weak against Light, Medium and Heavy armor types.
- |cff227bceMagical|r attacks are strong against Light, Medium and Heavy armor but have a reduced effectivity against Magical resistance.
- Heroes have resistance to both |cffb92f28Physical|r and |cff227bceMagical|r damage.
- |cffa33ad4Pure|r damage affects equally any type of armor.", "ReplaceableTextures\\CommandButtons\\BTNHumanArmorUpThree.blp")
call CreateInfoQuest("Single Player", "If only one player is detected, the map will switch to single player mode. In this mode, you will have control over both Heroes and the difficulty level will be reduced, decreasing the hit points of enemy units.", "ReplaceableTextures\\CommandButtons\\BTNSelectHeroOn.blp")
call CreateInfoQuest("Bug report", "If you find a bug while playing this map, please let me know so I can resolve it as soon as possible.", "ReplaceableTextures\\CommandButtons\\BTNCarrionScarabs.blp")
if EasyDif then
call SetPlayerHandicap(Player(2), .8)
call SetPlayerHandicap(Player(3), .8)
call SetPlayerHandicap(Player(4), .8)
call SetPlayerHandicap(Player(5), .8)
endif
call SetPlayerOnScoreScreen(Player(3), false)
call SetPlayerOnScoreScreen(Player(4), false)
call SetPlayerOnScoreScreen(Player(5), false)
call SetPlayerColorBJ(Player(1), PLAYER_COLOR_ORANGE, true)
call SetPlayerColorBJ(Player(2), PLAYER_COLOR_BLUE, true)
call SetPlayerColorBJ(Player(4), PLAYER_COLOR_PURPLE, true)
call SetPlayerColorBJ(Player(5), PLAYER_COLOR_PURPLE, true)
call SetSkyModel("war3mapImported\\SummerSphereCT.mdx")
call SetPlayerAbilityAvailable(Player(0), 'A00R', false)
call SetPlayerAbilityAvailable(Player(1), 'A00R', false)
call SetFloatGameState(GAME_STATE_TIME_OF_DAY, 15.)
call SuspendTimeOfDay(true)
set heroItem = null
endfunction
private function Init takes nothing returns nothing
call Initialization()
endfunction
endlibrary
library Heroes requires TimerUtils
struct Hero
unit hero
player owner
static method create takes nothing returns Hero
local Hero new = Hero.allocate()
return new
endmethod
method changeOwner takes player p returns nothing
set .owner = p
call SetUnitOwner(.hero, .owner, true)
endmethod
method maxLevel takes nothing returns nothing
call SetHeroLevel(.hero, 10, false)
endmethod
endstruct
struct Lightbringer extends Hero
method learnSpells takes nothing returns nothing
call SelectHeroSkill(.hero, 'A000')
call SelectHeroSkill(.hero, 'A001')
call SelectHeroSkill(.hero, 'A002')
call SelectHeroSkill(.hero, 'A003')
call SelectHeroSkill(.hero, 'A004')
endmethod
private static method onRevive takes nothing returns nothing
local timer t = GetExpiredTimer()
if not IsAlive(Lb.hero) then
call ReviveHeroLoc(Lb.hero, GetRectCenter(gg_rct_RespawnArea), true)
endif
call ReleaseTimer(t)
set t = null
endmethod
static method startRevive takes nothing returns nothing
call TimerStart(NewTimer(), 10., false, function thistype.onRevive)
endmethod
static method cinematicRevive takes nothing returns nothing
call ReviveHeroLoc(Lb.hero, GetRectCenter(gg_rct_LightbringerScene2), false)
endmethod
static method finalRevive takes nothing returns nothing
call ReviveHeroLoc(Lb.hero, GetRectCenter(gg_rct_LightbringerScene3), false)
endmethod
static method create takes nothing returns Lightbringer
local Lightbringer new = Lightbringer.allocate()
set new.owner = Player(0)
set new.hero = CreateUnitAtLoc(new.owner, 'HER0', GetRectCenter(gg_rct_LightbringerInit), 95.)
return new
endmethod
endstruct
struct Bladedancer extends Hero
method learnSpells takes nothing returns nothing
call SelectHeroSkill(.hero, 'A00C')
call SelectHeroSkill(.hero, 'A00D')
call SelectHeroSkill(.hero, 'A00E')
call SelectHeroSkill(.hero, 'A00F')
call SelectHeroSkill(.hero, 'A009')
endmethod
private static method onRevive takes nothing returns nothing
local timer t = GetExpiredTimer()
if not IsAlive(Bd.hero) then
call ReviveHeroLoc(Bd.hero, GetRectCenter(gg_rct_RespawnArea), true)
call ChargeSwap(Bd.hero, 0)
endif
call ReleaseTimer(t)
set t = null
endmethod
static method startRevive takes nothing returns nothing
call TimerStart(NewTimer(), 10., false, function thistype.onRevive)
endmethod
static method cinematicRevive takes nothing returns nothing
call ReviveHeroLoc(Bd.hero, GetRectCenter(gg_rct_BladedancerScene2), false)
call ChargeSwap(Bd.hero, 0)
endmethod
static method finalRevive takes nothing returns nothing
call ReviveHeroLoc(Bd.hero, GetRectCenter(gg_rct_BladedancerScene3), false)
call ChargeSwap(Bd.hero, 0)
endmethod
static method create takes nothing returns Bladedancer
local Bladedancer new = Bladedancer.allocate()
set new.owner = Player(1)
set new.hero = CreateUnitAtLoc(new.owner, 'HER1', GetRectCenter(gg_rct_BladedancerInit), 95.)
return new
endmethod
endstruct
struct Malice extends Hero
static method create takes nothing returns Malice
local Malice new = Malice.allocate()
set new.owner = Player(2)
set new.hero = CreateUnitAtLoc(new.owner, 'BOS0', GetRectCenter(gg_rct_MaliceInit), 90.)
return new
endmethod
static method replace takes nothing returns nothing
set Wq.hero = ReplaceUnitBJ(Wq.hero, 'BOS0', bj_UNIT_STATE_METHOD_DEFAULTS)
call SetUnitInvulnerable(Wq.hero, true)
call SetUnitState(Wq.hero, UNIT_STATE_LIFE, GetUnitState(Wq.hero, UNIT_STATE_MAX_LIFE) * 0.01)
endmethod
static method transformOnKathra takes nothing returns nothing
set Wq.hero = ReplaceUnitBJ(Wq.hero, 'BOS2', bj_UNIT_STATE_METHOD_DEFAULTS)
call SetUnitInvulnerable(Wq.hero, true)
endmethod
endstruct
endlibrary
library Bossfight initializer Init requires RegisterPlayerUnitEvent, Waves
globals
private boolean FirstEncCheck = false
boolean MaliceDefeated = false
boolean KathraDefeated = false
endglobals
private function TeleportersHint takes nothing returns nothing
local timer t = GetExpiredTimer()
if not IsKathraRunning and not IsFinalRunning then
call QuestMessageBJ(GetPlayersAll(), bj_QUESTMESSAGE_HINT, "|cff32cd32HINT|r
Syndra can reach the generators that power the teleporters with her Eagle Dance. Destroy them to stop the enemy reinforcements.")
call PingMinimapForForce(GetPlayersAll(), GetUnitX(gg_unit_npgr_0018), GetUnitY(gg_unit_npgr_0018), 4.00)
call PingMinimapForForce(GetPlayersAll(), GetUnitX(gg_unit_npgr_0019), GetUnitY(gg_unit_npgr_0019), 4.00)
endif
call FogModifierStop(CreateFogModifierRadiusLocBJ(true, Lb.owner, FOG_OF_WAR_VISIBLE, GetRectCenter(gg_rct_Gen1), 120.))
call FogModifierStop(CreateFogModifierRadiusLocBJ(true, Lb.owner, FOG_OF_WAR_VISIBLE, GetRectCenter(gg_rct_Gen2), 120.))
call FogModifierStop(CreateFogModifierRadiusLocBJ(true, Bd.owner, FOG_OF_WAR_VISIBLE, GetRectCenter(gg_rct_Gen1), 120.))
call FogModifierStop(CreateFogModifierRadiusLocBJ(true, Bd.owner, FOG_OF_WAR_VISIBLE, GetRectCenter(gg_rct_Gen2), 120.))
call ReleaseTimer(t)
set t = null
endfunction
private function TeleportersMessage2 takes nothing returns nothing
local timer t = GetExpiredTimer()
if not IsKathraRunning and not IsFinalRunning then
call TransmissionFromUnitWithNameBJ(GetPlayersAll(), Bd.hero, "Syndra", null, "I can reach them. Hold on here while I return.", bj_TIMETYPE_ADD, .0, false)
endif
call TimerStart(NewTimer(), 7., false, function TeleportersHint)
call ReleaseTimer(t)
set t = null
endfunction
private function TeleportersMessage takes nothing returns nothing
local timer t = GetExpiredTimer()
if not IsKathraRunning and not IsFinalRunning then
call TransmissionFromUnitWithNameBJ(GetPlayersAll(), Lb.hero, "Arivan", null, "More enemy reinforcements are arriving from the teleporters. We must destroy the generators to shut them down.", bj_TIMETYPE_ADD, .0, false)
endif
call TimerStart(NewTimer(), 0.05, false, function NewWave)
call TimerStart(NewTimer(), 8., false, function TeleportersMessage2)
call ReleaseTimer(t)
set t = null
endfunction
private function FirstEncounter takes nothing returns nothing
if (GetTriggerUnit() == Wq.hero or GetAttacker() == Wq.hero) and not FirstEncCheck then
set FirstEncCheck = true
call QuestItemSetCompleted(MainQFirstItem, true)
call QuestMessageBJ(GetPlayersAll(), bj_QUESTMESSAGE_UPDATED, "|cffffcc00MAIN QUEST UPDATE|r
The Shrine of Duredhel
- |cff808080Reach the Shrine|r
- Stop Malice ")
call PingMinimapForForce(GetPlayersAll(), GetUnitX(Wq.hero), GetUnitY(Wq.hero), 4.00)
call TransmissionFromUnitWithNameBJ(GetPlayersAll(), Wq.hero, "Malice", null, "Ah Arivan, have you come to stop me? It seems that you have been late.", bj_TIMETYPE_ADD, .0, false)
call TimerStart(NewTimer(), 10., false, function TeleportersMessage)
endif
endfunction
private function Init takes nothing returns nothing
call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_ATTACKED, function FirstEncounter)
endfunction
endlibrary
library Waves requires TimerUtils, GroupUtils
globals
private constant string TELEPORT_FX = "Abilities\\Spells\\Human\\MassTeleport\\MassTeleportTarget.mdl"
private boolean IsLeftTeleporterDis = false
private boolean IsRightTeleporterDis = false
endglobals
private function AttackOrder takes nothing returns nothing
call IssuePointOrderLoc(GetEnumUnit(), "attack", GetUnitLoc(Wq.hero))
endfunction
private function CreateUnits takes integer r, player p, location loc returns nothing
local unit u
if r == 1 then
set u = CreateUnitAtLoc(p, 'h000', loc, 270.)
call CreateEffectOnLoc(TELEPORT_FX, GetUnitLoc(u))
call IssuePointOrderLoc(u, "attack", GetUnitLoc(Wq.hero))
set u = CreateUnitAtLoc(p, 'h001', loc, 270.)
call CreateEffectOnLoc(TELEPORT_FX, GetUnitLoc(u))
call IssuePointOrderLoc(u, "attack", GetUnitLoc(Wq.hero))
set u = CreateUnitAtLoc(p, 'h001', loc, 270.)
call CreateEffectOnLoc(TELEPORT_FX, GetUnitLoc(u))
call IssuePointOrderLoc(u, "attack", GetUnitLoc(Wq.hero))
elseif r == 2 then
set u = CreateUnitAtLoc(p, 'h000', loc, 270.)
call CreateEffectOnLoc(TELEPORT_FX, GetUnitLoc(u))
call IssuePointOrderLoc(u, "attack", GetUnitLoc(Wq.hero))
set u = CreateUnitAtLoc(p, 'h000', loc, 270.)
call CreateEffectOnLoc(TELEPORT_FX, GetUnitLoc(u))
call IssuePointOrderLoc(u, "attack", GetUnitLoc(Wq.hero))
set u = CreateUnitAtLoc(p, 'h003', loc, 270.)
call CreateEffectOnLoc(TELEPORT_FX, GetUnitLoc(u))
call IssuePointOrderLoc(u, "attack", GetUnitLoc(Wq.hero))
elseif r == 3 then
set u = CreateUnitAtLoc(p, 'h001', loc, 270.)
call CreateEffectOnLoc(TELEPORT_FX, GetUnitLoc(u))
call IssuePointOrderLoc(u, "attack", GetUnitLoc(Wq.hero))
set u = CreateUnitAtLoc(p, 'h001', loc, 270.)
call CreateEffectOnLoc(TELEPORT_FX, GetUnitLoc(u))
call IssuePointOrderLoc(u, "attack", GetUnitLoc(Wq.hero))
set u = CreateUnitAtLoc(p, 'h003', loc, 270.)
call CreateEffectOnLoc(TELEPORT_FX, GetUnitLoc(u))
call IssuePointOrderLoc(u, "attack", GetUnitLoc(Wq.hero))
elseif r == 4 then
set u = CreateUnitAtLoc(p, 'h002', loc, 270.)
call CreateEffectOnLoc(TELEPORT_FX, GetUnitLoc(u))
call IssuePointOrderLoc(u, "attack", GetUnitLoc(Wq.hero))
elseif r == 5 then
set u = CreateUnitAtLoc(p, 'h004', loc, 270.)
call CreateEffectOnLoc(TELEPORT_FX, GetUnitLoc(u))
call IssuePointOrderLoc(u, "attack", GetUnitLoc(Wq.hero))
set u = CreateUnitAtLoc(p, 'h004', loc, 270.)
call CreateEffectOnLoc(TELEPORT_FX, GetUnitLoc(u))
call IssuePointOrderLoc(u, "attack", GetUnitLoc(Wq.hero))
set u = CreateUnitAtLoc(p, 'h004', loc, 270.)
call CreateEffectOnLoc(TELEPORT_FX, GetUnitLoc(u))
call IssuePointOrderLoc(u, "attack", GetUnitLoc(Wq.hero))
set u = CreateUnitAtLoc(p, 'h004', loc, 270.)
call CreateEffectOnLoc(TELEPORT_FX, GetUnitLoc(u))
call IssuePointOrderLoc(u, "attack", GetUnitLoc(Wq.hero))
set u = CreateUnitAtLoc(p, 'h005', loc, 270.)
call CreateEffectOnLoc(TELEPORT_FX, GetUnitLoc(u))
call IssuePointOrderLoc(u, "attack", GetUnitLoc(Wq.hero))
elseif r == 6 then
set u = CreateUnitAtLoc(p, 'h004', loc, 270.)
call CreateEffectOnLoc(TELEPORT_FX, GetUnitLoc(u))
call IssuePointOrderLoc(u, "attack", GetUnitLoc(Wq.hero))
set u = CreateUnitAtLoc(p, 'h004', loc, 270.)
call CreateEffectOnLoc(TELEPORT_FX, GetUnitLoc(u))
call IssuePointOrderLoc(u, "attack", GetUnitLoc(Wq.hero))
set u = CreateUnitAtLoc(p, 'h004', loc, 270.)
call CreateEffectOnLoc(TELEPORT_FX, GetUnitLoc(u))
call IssuePointOrderLoc(u, "attack", GetUnitLoc(Wq.hero))
set u = CreateUnitAtLoc(p, 'h001', loc, 270.)
call CreateEffectOnLoc(TELEPORT_FX, GetUnitLoc(u))
call IssuePointOrderLoc(u, "attack", GetUnitLoc(Wq.hero))
set u = CreateUnitAtLoc(p, 'h001', loc, 270.)
call CreateEffectOnLoc(TELEPORT_FX, GetUnitLoc(u))
call IssuePointOrderLoc(u, "attack", GetUnitLoc(Wq.hero))
elseif r == 7 then
set u = CreateUnitAtLoc(p, 'h004', loc, 270.)
call CreateEffectOnLoc(TELEPORT_FX, GetUnitLoc(u))
call IssuePointOrderLoc(u, "attack", GetUnitLoc(Wq.hero))
set u = CreateUnitAtLoc(p, 'h004', loc, 270.)
call CreateEffectOnLoc(TELEPORT_FX, GetUnitLoc(u))
call IssuePointOrderLoc(u, "attack", GetUnitLoc(Wq.hero))
set u = CreateUnitAtLoc(p, 'h005', loc, 270.)
call CreateEffectOnLoc(TELEPORT_FX, GetUnitLoc(u))
call IssuePointOrderLoc(u, "attack", GetUnitLoc(Wq.hero))
set u = CreateUnitAtLoc(p, 'h003', loc, 270.)
call CreateEffectOnLoc(TELEPORT_FX, GetUnitLoc(u))
call IssuePointOrderLoc(u, "attack", GetUnitLoc(Wq.hero))
endif
set u = null
endfunction
function NewWave takes nothing returns nothing
local timer t = GetExpiredTimer()
local group group1 = CreateGroup()
local group group2 = CreateGroup()
local integer group1Count
local integer group2Count
local unit picked
local integer random
if not IsAlive(gg_unit_ncop_0016) then
set IsLeftTeleporterDis = true
endif
if not IsAlive(gg_unit_ncop_0017) then
set IsRightTeleporterDis = true
endif
call GroupEnumLivingUnitsOfPlayer(group1, Player(3))
call GroupEnumLivingUnitsOfPlayer(group2, Player(4))
set group1Count = GroupCountUnits(group1)
set group2Count = GroupCountUnits(group2)
if group1Count <= 2 and not IsLeftTeleporterDis and /*
*/ not IsKathraRunning and not IsFinalRunning then
set random = GetRandomInt(1, 7)
call CreateUnits(random, Player(3), GetUnitLoc(gg_unit_ncop_0016))
endif
if group2Count <= 2 and not IsRightTeleporterDis and /*
*/ not IsKathraRunning and not IsFinalRunning then
set random = GetRandomInt(1, 7)
call CreateUnits(random, Player(4), GetUnitLoc(gg_unit_ncop_0017))
endif
call ForGroup(group1, function AttackOrder)
call ForGroup(group2, function AttackOrder)
if not IsLeftTeleporterDis or not IsRightTeleporterDis then
call TimerStart(NewTimer(), 15., false, function NewWave)
endif
call DestroyGroup(group1)
call DestroyGroup(group2)
set group1 = null
set group2 = null
call ReleaseTimer(t)
set t = null
endfunction
endlibrary
library Teleporters initializer Init requires TimerUtils, UnitIndexerGUI
globals
private unit array Generator
private unit array Teleporter
endglobals
function TeleporterDestroy takes unit gen returns nothing
local integer id = GetUnitId(gen)
call QuestMessageBJ(GetPlayersAll(), bj_QUESTMESSAGE_HINT, "|cff32cd32HINT|r
A teleporter has been deactivated. No more enemies will come from this direction.")
call PingMinimapForForce(GetPlayersAll(), GetUnitX(Teleporter[id]), GetUnitY(Teleporter[id]), 4.00)
call UnitRemoveAbility(Teleporter[id], 'Avul')
call KillUnit(Teleporter[id])
set Generator[id] = null
set Teleporter[id] = null
endfunction
private function TeleporterRegister takes unit gen, unit tel returns nothing
local integer id = GetUnitId(gen)
set Generator[id] = gen
set Teleporter[id] = tel
endfunction
private function TeleporterSetup takes nothing returns nothing
local timer t = GetExpiredTimer()
call TeleporterRegister(gg_unit_npgr_0018, gg_unit_ncop_0017)
call TeleporterRegister(gg_unit_npgr_0019, gg_unit_ncop_0016)
call ReleaseTimer(t)
set t = null
endfunction
private function Init takes nothing returns nothing
call TimerStart(NewTimer(), .01, false, function TeleporterSetup)
endfunction
endlibrary
library HereticsChannel requires TimerUtils, SpellUtils
private function ChannelOrder takes nothing returns nothing
local timer t = GetExpiredTimer()
if IsAlive(Heretic[1]) and IsAlive(Heretic[2]) and IsAlive(Heretic[3]) then
call IssueImmediateOrder(Heretic[1], "channel")
call IssueImmediateOrder(Heretic[2], "channel")
call IssueImmediateOrder(Heretic[3], "channel")
call TimerStart(NewTimer(), 3., false, function ChannelOrder)
else
set Heretic[1] = null
set Heretic[2] = null
set Heretic[3] = null
set Crystal = null
endif
call ReleaseTimer(t)
set t = null
endfunction
function SetupHeretics takes nothing returns nothing
set Heretic[1] = gg_unit_h008_0053
set Heretic[2] = gg_unit_h008_0054
set Heretic[3] = gg_unit_h008_0055
call TimerStart(NewTimer(), .01, false, function ChannelOrder)
endfunction
endlibrary
library Cinematics initializer Init requires RegisterPlayerEvent, IntroScene, KathraScene, FinalScene
private function OnEsc takes nothing returns nothing
if not IntroSkipped and IsIntroRunning then
call Cin1_SkipLoop()
endif
if not KathraSkipped and IsKathraRunning then
call Cin2_SkipLoop()
endif
if not FinalSkipped and IsFinalRunning then
call Cin3_SkipLoop()
endif
endfunction
private function Init takes nothing returns nothing
call RegisterAnyPlayerEvent(EVENT_PLAYER_END_CINEMATIC, function OnEsc)
endfunction
endlibrary
library IntroScene requires TimerUtils, CinematicUtils
globals
boolean IntroSkipped = false
boolean IsIntroRunning = false
endglobals
private function SecondHint takes nothing returns nothing
local timer t = GetExpiredTimer()
call QuestMessageBJ(GetPlayersAll(), bj_QUESTMESSAGE_HINT, "|cff32cd32RESPAWN SYSTEM|r
When a Hero dies, its soul is preserved and will respawn again after 10 seconds in front of the Holy Eagle Statue.
For more information, check the Quests Panel.")
call PingMinimapForForce(GetPlayersAll(), GetRectCenterX(gg_rct_RespawnArea), GetRectCenterY(gg_rct_RespawnArea), 4.00)
call ReleaseTimer(t)
set t = null
endfunction
private function FirstHint takes nothing returns nothing
local timer t = GetExpiredTimer()
call QuestMessageBJ(GetPlayersAll(), bj_QUESTMESSAGE_HINT, "|cff32cd32HINT|r
Use the nearby Teleporter to reach the Arcane Quarters.")
call PingMinimapForForce(GetPlayersAll(), GetUnitX(gg_unit_ncop_0011), GetUnitY(gg_unit_ncop_0011), 4.00)
call TimerStart(NewTimer(), 15., false, function SecondHint)
call ReleaseTimer(t)
set t = null
endfunction
private function SkipLoop2 takes nothing returns nothing
local timer t = GetExpiredTimer()
call QuestMessageBJ(GetPlayersAll(), bj_QUESTMESSAGE_DISCOVERED, "|cffffcc00MAIN QUEST|r
The Shrine of Duredhel
- Reach the Shrine
- Stop Malice ")
call PingMinimapForForce(GetPlayersAll(), GetRectCenterX(gg_rct_Quarters), GetRectCenterY(gg_rct_Quarters), 4.00)
call FogModifierStop(CreateFogModifierRadiusLocBJ(true, Lb.owner, FOG_OF_WAR_VISIBLE, GetRectCenter(gg_rct_Quarters), 512.))
call FogModifierStop(CreateFogModifierRadiusLocBJ(true, Bd.owner, FOG_OF_WAR_VISIBLE, GetRectCenter(gg_rct_Quarters), 512.))
call TimerStart(NewTimer(), 15., false, function FirstHint)
call ReleaseTimer(t)
set t = null
endfunction
function Cin1_SkipLoop takes nothing returns nothing
set IntroSkipped = true
set IsIntroRunning = false
call EndThematicMusic()
call SetUnitPositionLoc(Lb.hero, GetRectCenter(gg_rct_LightbringerScene1))
call SetUnitPositionLoc(Bd.hero, GetRectCenter(gg_rct_BladedancerScene1))
call SetUnitFacing(Lb.hero, AngleBetweenPoints(GetUnitLoc(Lb.hero), GetUnitLoc(Bd.hero)))
call SetUnitFacing(Bd.hero, AngleBetweenPoints(GetUnitLoc(Bd.hero), GetUnitLoc(Lb.hero)))
call FadeOut(2.)
call CameraSetupApplyForceDuration(gg_cam_Init12, true, .0)
call CameraSetSmoothingFactor(0)
call EnableUserUI(true)
call DisplayCineFilter(false)
call FogMaskEnable(true)
call CinematicModeExBJ(false, GetPlayersAll(), bj_CINEMODE_INTERFACEFADE)
call TimerStart(NewTimer(), .5, false, function SkipLoop2)
endfunction
private function FinalLoop2 takes nothing returns nothing
local timer t = GetExpiredTimer()
call EndThematicMusic()
call ResetToGameCamera(.0)
call EnableUserUI(true)
call DisplayCineFilter(false)
call FogMaskEnable(true)
call CinematicModeExBJ(false, GetPlayersAll(), bj_CINEMODE_INTERFACEFADE)
call QuestMessageBJ(GetPlayersAll(), bj_QUESTMESSAGE_DISCOVERED, "|cffffcc00MAIN QUEST|r
The Shrine of Duredhel
- Reach the Shrine
- Stop Malice ")
call PingMinimapForForce(GetPlayersAll(), GetRectCenterX(gg_rct_Quarters), GetRectCenterY(gg_rct_Quarters), 4.)
call FogModifierStop(CreateFogModifierRadiusLocBJ(true, Lb.owner, FOG_OF_WAR_VISIBLE, GetRectCenter(gg_rct_Quarters), 512.))
call FogModifierStop(CreateFogModifierRadiusLocBJ(true, Bd.owner, FOG_OF_WAR_VISIBLE, GetRectCenter(gg_rct_Quarters), 512.))
call TimerStart(NewTimer(), 15., false, function FirstHint)
call ReleaseTimer(t)
set t = null
endfunction
private function FinalLoop takes nothing returns nothing
local timer t = GetExpiredTimer()
if not IntroSkipped then
set IsIntroRunning = false
call FadeOut(2.)
call CameraSetupApplyForceDuration(gg_cam_Init12, true, .0)
call TimerStart(NewTimer(), 2., false, function FinalLoop2)
endif
call ReleaseTimer(t)
set t = null
endfunction
private function TwentiethLoop takes nothing returns nothing
local timer t = GetExpiredTimer()
if not IntroSkipped then
call CameraSetupApplyForceDuration(gg_cam_Init11, true, 5.)
call TransmissionFromUnitWithNameBJ(GetPlayersAll(), Wq.hero, "Malice", null, "Defeating the pesky guardians of this site was pretty easy. Hurry up fools, we must destroy this stupid crystal before the High Elves can counterattack.", bj_TIMETYPE_ADD, .5, false)
call TimerStart(NewTimer(), 6., false, function FinalLoop)
endif
call ReleaseTimer(t)
set t = null
endfunction
private function NineteenthLoop takes nothing returns nothing
local timer t = GetExpiredTimer()
if not IntroSkipped then
call CameraSetupApplyForceDuration(gg_cam_Init10, true, 5.)
call TimerStart(NewTimer(), 5., false, function TwentiethLoop)
endif
call ReleaseTimer(t)
set t = null
endfunction
private function EighteenthLoop takes nothing returns nothing
local timer t = GetExpiredTimer()
if not IntroSkipped then
call FadeOut(3.)
call CameraSetupApplyForceDuration(gg_cam_Init09, true, .0)
call TimerStart(NewTimer(), 3., false, function NineteenthLoop)
endif
call ReleaseTimer(t)
set t = null
endfunction
private function SeventeenthLoop takes nothing returns nothing
local timer t = GetExpiredTimer()
if not IntroSkipped then
call FadeIn(1.)
call TimerStart(NewTimer(), 1., false, function EighteenthLoop)
endif
call ReleaseTimer(t)
set t = null
endfunction
private function SixteenthLoop takes nothing returns nothing
local timer t = GetExpiredTimer()
if not IntroSkipped then
call TransmissionFromUnitWithNameBJ(GetPlayersAll(), Lb.hero, "Arivan", null, "Right. You better get ready, this won't be an easy fight.", bj_TIMETYPE_ADD, .0, false)
call TimerStart(NewTimer(), 6., false, function SeventeenthLoop)
endif
call ReleaseTimer(t)
set t = null
endfunction
private function FifteenthLoop takes nothing returns nothing
local timer t = GetExpiredTimer()
if not IntroSkipped then
call CameraSetupApplyForceDuration(gg_cam_Init08, true, 8.)
call TransmissionFromUnitWithNameBJ(GetPlayersAll(), Bd.hero, "Syndra", null, "Then we must move on. There are probably Dark Elves awaiting for us.", bj_TIMETYPE_ADD, .0, false)
call TimerStart(NewTimer(), 7., false, function SixteenthLoop)
endif
call ReleaseTimer(t)
set t = null
endfunction
private function FourteenthLoop takes nothing returns nothing
local timer t = GetExpiredTimer()
if not IntroSkipped then
call TransmissionFromUnitWithNameBJ(GetPlayersAll(), Lb.hero, "Arivan", null, "She wants to destroy the crystal in order to unleash Kathra's power and claim it for herself. This land depends on us today, so we cannot fail.", bj_TIMETYPE_ADD, .0, false)
call TimerStart(NewTimer(), 7., false, function FifteenthLoop)
endif
call ReleaseTimer(t)
set t = null
endfunction
private function ThirteenthLoop takes nothing returns nothing
local timer t = GetExpiredTimer()
if not IntroSkipped then
call SetUnitFacingTimed(Lb.hero, AngleBetweenPoints(GetUnitLoc(Lb.hero), GetUnitLoc(Bd.hero)), .1)
call SetUnitFacingTimed(Bd.hero, AngleBetweenPoints(GetUnitLoc(Bd.hero), GetUnitLoc(Lb.hero)), .1)
call TransmissionFromUnitWithNameBJ(GetPlayersAll(), Bd.hero, "Syndra", null, "And why has Malice come here? What is the point of coming to such a distant and forgotten place now?", bj_TIMETYPE_ADD, .0, false)
call TimerStart(NewTimer(), 7., false, function FourteenthLoop)
endif
call ReleaseTimer(t)
set t = null
endfunction
private function TwelfthLoop takes nothing returns nothing
local timer t = GetExpiredTimer()
if not IntroSkipped then
call FadeOut(3.)
call CameraSetupApplyForceDuration(gg_cam_Init07, true, .0)
call TransmissionFromUnitWithNameBJ(GetPlayersAll(), Lb.hero, "Arivan", null, "Unfortunately, that battle only marked the beginning of the conflict. Our race was divided between the followers of each Queen and it was only a matter of time before hostilities began.", bj_TIMETYPE_ADD, 1., false)
call TimerStart(NewTimer(), 10., false, function ThirteenthLoop)
endif
call ReleaseTimer(t)
set t = null
endfunction
private function EleventhLoop takes nothing returns nothing
local timer t = GetExpiredTimer()
if not IntroSkipped then
call FadeIn(1.)
call TimerStart(NewTimer(), 1., false, function TwelfthLoop)
endif
call ReleaseTimer(t)
set t = null
endfunction
private function TenthLoop takes nothing returns nothing
local timer t = GetExpiredTimer()
if not IntroSkipped then
call TransmissionFromUnitWithNameBJ(GetPlayersAll(), Lb.hero, "Arivan", null, "To finally stop her evil sister, Queen Elyna sacrificed herself, using her own soul to seal Kathra within an arcane crystal. This sanctuary was built to contain and protect it.", bj_TIMETYPE_ADD, 1., false)
call TimerStart(NewTimer(), 9., false, function EleventhLoop)
endif
call ReleaseTimer(t)
set t = null
endfunction
private function NinethLoop takes nothing returns nothing
local timer t = GetExpiredTimer()
if not IntroSkipped then
call CameraSetSmoothingFactor(.0)
call CameraSetupApplyForceDuration(gg_cam_Init05, true, .0)
call CameraSetupApplyForceDuration(gg_cam_Init06, true, 15.)
call TransmissionFromUnitWithNameBJ(GetPlayersAll(), Lb.hero, "Arivan", null, "Yes, was in this place that Queen Kathra declared the war after succumbing to the dark magics she had become obsessed with. The subsequent battle between her and her sister Elyna destroyed most of the city.", bj_TIMETYPE_ADD, 1., false)
call TimerStart(NewTimer(), 10., false, function TenthLoop)
endif
call ReleaseTimer(t)
set t = null
endfunction
private function EighthLoop takes nothing returns nothing
local timer t = GetExpiredTimer()
if not IntroSkipped then
call IssuePointOrderLoc(Bd.hero, "move", GetRectCenter(gg_rct_BladedancerScene1))
call TimerStart(NewTimer(), 1., false, function NinethLoop)
endif
call ReleaseTimer(t)
set t = null
endfunction
private function SeventhLoop takes nothing returns nothing
local timer t = GetExpiredTimer()
if not IntroSkipped then
call IssuePointOrderLoc(Lb.hero, "move", GetRectCenter(gg_rct_LightbringerScene1))
call TimerStart(NewTimer(), 1., false, function EighthLoop)
endif
call ReleaseTimer(t)
set t = null
endfunction
private function SixthLoop takes nothing returns nothing
local timer t = GetExpiredTimer()
if not IntroSkipped then
call CameraSetupApplyForceDuration(gg_cam_Init03, true, .0)
call CameraSetupApplyForceDuration(gg_cam_Init04, true, 6.5)
call TransmissionFromUnitWithNameBJ(GetPlayersAll(), Bd.hero, "Syndra", null, "Legends say that on this site, the Twin Queens started the war between our races thousands of years ago.", bj_TIMETYPE_ADD, .0, false)
call TimerStart(NewTimer(), 8., false, function SeventhLoop)
endif
call ReleaseTimer(t)
set t = null
endfunction
private function FifthLoop takes nothing returns nothing
local timer t = GetExpiredTimer()
if not IntroSkipped then
call SetUnitFacingTimed(Lb.hero, AngleBetweenPoints(GetUnitLoc(Lb.hero), GetUnitLoc(Bd.hero)), .1)
call SetUnitFacingTimed(Bd.hero, AngleBetweenPoints(GetUnitLoc(Bd.hero), GetUnitLoc(Lb.hero)), .1)
call TransmissionFromUnitWithNameBJ(GetPlayersAll(), Lb.hero, "Arivan", null, "We must hurry to reach Az'lann, the Sacred Crystal, before than Malice.", bj_TIMETYPE_ADD, .0, false)
call TimerStart(NewTimer(), 6., false, function SixthLoop)
endif
call ReleaseTimer(t)
set t = null
endfunction
private function FourthLoop takes nothing returns nothing
local timer t = GetExpiredTimer()
if not IntroSkipped then
call IssueImmediateOrder(Lb.hero, "stop")
call IssueImmediateOrder(Bd.hero, "stop")
call TransmissionFromUnitWithNameBJ(GetPlayersAll(), Lb.hero, "Arivan", null, "Finally, we have entered the Arcane Quarters and are about to reach the Shrine of Az'lann.", bj_TIMETYPE_ADD, .0, false)
call TimerStart(NewTimer(), 8., false, function FifthLoop)
endif
call ReleaseTimer(t)
set t = null
endfunction
private function ThirdLoop takes nothing returns nothing
local timer t = GetExpiredTimer()
if not IntroSkipped then
call CameraSetupApplyForceDuration(gg_cam_Init02, true, 7.)
call TimerStart(NewTimer(), 2., false, function FourthLoop)
endif
call ReleaseTimer(t)
set t = null
endfunction
private function SecondLoop takes nothing returns nothing
local timer t = GetExpiredTimer()
if not IntroSkipped then
call IssuePointOrderLoc(Lb.hero, "move", GetRectCenter(gg_rct_LightbringerEnd))
call IssuePointOrderLoc(Bd.hero, "move", GetRectCenter(gg_rct_BladedancerEnd))
call TimerStart(NewTimer(), .5, false, function ThirdLoop)
endif
call ReleaseTimer(t)
set t = null
endfunction
private function FirstLoop takes nothing returns nothing
local timer t = GetExpiredTimer()
if not IntroSkipped then
call PlayThematicMusic("Sound\\Music\\mp3Music\\BloodElfTheme.mp3")
call SetMapMusic("music", false, 0)
call TimerStart(NewTimer(), .1, false, function SecondLoop)
endif
call ReleaseTimer(t)
set t = null
endfunction
function FirstCinematic takes nothing returns nothing
set IsIntroRunning = true
call FadeIn(.0)
call CameraSetupApplyForceDuration(gg_cam_Init01, true, .0)
call CinematicModeExBJ(true, GetPlayersAll(), bj_CINEMODE_INTERFACEFADE)
call CameraSetSmoothingFactor(2.)
call FadeOut(4.)
call ClearMapMusic()
call TimerStart(NewTimer(), .01, false, function FirstLoop)
endfunction
endlibrary
library KathraScene requires TimerUtils, CinematicUtils, MissionUtils
globals
private constant string HERETIC_FX = "Abilities\\Spells\\Demon\\DemonBoltImpact\\DemonBoltImpact.mdl"
private constant string CRYSTAL_FX = "Abilities\\Spells\\Undead\\Unsummon\\UnsummonTarget.mdl"
private constant string HERETIC_EXPLODE_FX = "Abilities\\Spells\\Human\\ThunderClap\\ThunderClapCaster.mdl"
private constant string CRYSTAL_EXPLODE_FX = "Objects\\Spawnmodels\\Human\\HCancelDeath\\HCancelDeath.mdl"
private constant string MALICE_FX = "Abilities\\Spells\\Undead\\DarkRitual\\DarkRitualTarget.mdl"
private constant string MALICE_FX_FINAL = "Abilities\\Spells\\Demon\\DarkPortal\\DarkPortalTarget.mdl"
private constant string MALICE_FX_FINAL_2 = "Abilities\\Spells\\Other\\Doom\\DoomDeath.mdl"
private constant string MISSILE_MODEL = "Abilities\\Spells\\Undead\\OrbOfDeath\\AnnihilationMissile.mdl"
private effect CrystalFx
boolean KathraSkipped = false
boolean IsKathraRunning = false
endglobals
private function UnhideUnit takes nothing returns nothing
call ShowUnit(GetEnumUnit(), true)
endfunction
private function HideUnit takes nothing returns nothing
call ShowUnit(GetEnumUnit(), false)
endfunction
private function SkipLoop2 takes nothing returns nothing
local timer t = GetExpiredTimer()
call QuestMessageBJ(GetPlayersAll(), bj_QUESTMESSAGE_DISCOVERED, "|cffffcc00MAIN QUEST UPDATE|r
The Shrine of Duredhel
- |cff808080Reach the Shrine|r
- |cff808080Stop Malice|r
- Kill Kathra ")
set MainQThirdItem = QuestObjetive(MainQuest, "Kill Kathra")
call SetUnitInvulnerable(Lb.hero, false)
call SetUnitInvulnerable(Bd.hero, false)
call SetUnitInvulnerable(Wq.hero, false)
call PauseAllUnitsBJ(false)
call ReleaseTimer(t)
set t = null
endfunction
function Cin2_SkipLoop takes nothing returns nothing
local group g = CreateGroup()
set KathraSkipped = true
set IsKathraRunning = false
call EndThematicMusic()
call Wq.transformOnKathra()
if not IsAlive(Lb.hero) then
call Lightbringer.cinematicRevive()
else
call SetUnitPositionLoc(Lb.hero, GetRectCenter(gg_rct_LightbringerScene2End))
endif
if not IsAlive(Bd.hero) then
call Bladedancer.cinematicRevive()
else
call SetUnitPositionLoc(Bd.hero, GetRectCenter(gg_rct_BladedancerScene2End))
endif
call SetUnitPositionLoc(Wq.hero, GetRectCenter(gg_rct_MaliceScene2))
call SetUnitFacing(Lb.hero, AngleBetweenPoints(GetUnitLoc(Lb.hero), GetUnitLoc(Wq.hero)))
call SetUnitFacing(Bd.hero, AngleBetweenPoints(GetUnitLoc(Bd.hero), GetUnitLoc(Wq.hero)))
call SetUnitFacing(Wq.hero, 225.)
call GroupEnumUnitsOfPlayer(g, Player(3), null)
call ForGroup(g, function UnhideUnit)
call DestroyGroup(g)
set g = CreateGroup()
call GroupEnumUnitsOfPlayer(g, Player(4), null)
call ForGroup(g, function UnhideUnit)
call DestroyGroup(g)
set g = CreateGroup()
call GroupEnumUnitsOfPlayer(g, Player(5), null)
call ForGroup(g, function UnhideUnit)
call DestroyGroup(g)
set g = null
if IsAlive(Heretic[1]) then
call ExplodeUnitBJ(Heretic[1])
call ExplodeUnitBJ(Heretic[2])
call ExplodeUnitBJ(Heretic[3])
call KillDestructable(Crystal)
endif
call FadeOut(2.)
call CameraSetupApplyForceDuration(gg_cam_Kathra07, true, .0)
call CameraSetSmoothingFactor(0)
call EnableUserUI(true)
call DisplayCineFilter(false)
call FogMaskEnable(true)
call CinematicModeExBJ(false, GetPlayersAll(), bj_CINEMODE_INTERFACEFADE)
call TimerStart(NewTimer(), .5, false, function SkipLoop2)
endfunction
private function FinalLoop2 takes nothing returns nothing
local timer t = GetExpiredTimer()
local group g = CreateGroup()
call EndThematicMusic()
call EnableUserUI(true)
call DisplayCineFilter(false)
call FogMaskEnable(true)
call PauseAllUnitsBJ(false)
call GroupEnumUnitsOfPlayer(g, Player(3), null)
call ForGroup(g, function UnhideUnit)
call DestroyGroup(g)
set g = CreateGroup()
call GroupEnumUnitsOfPlayer(g, Player(4), null)
call ForGroup(g, function UnhideUnit)
call DestroyGroup(g)
set g = CreateGroup()
call GroupEnumUnitsOfPlayer(g, Player(5), null)
call ForGroup(g, function UnhideUnit)
call DestroyGroup(g)
call SetUnitInvulnerable(Lb.hero, false)
call SetUnitInvulnerable(Bd.hero, false)
call SetUnitInvulnerable(Wq.hero, false)
call CinematicModeExBJ(false, GetPlayersAll(), bj_CINEMODE_INTERFACEFADE)
call QuestMessageBJ(GetPlayersAll(), bj_QUESTMESSAGE_DISCOVERED, "|cffffcc00MAIN QUEST UPDATE|r
The Shrine of Duredhel
- |cff808080Reach the Shrine|r
- |cff808080Stop Malice|r
- Kill Kathra ")
set MainQThirdItem = QuestObjetive(MainQuest, "Kill Kathra")
call ReleaseTimer(t)
set g = null
set t = null
endfunction
private function FinalLoop takes nothing returns nothing
local timer t = GetExpiredTimer()
if not KathraSkipped then
set IsKathraRunning = false
call FadeOut(2.)
call CameraSetupApplyForceDuration(gg_cam_Kathra07, true, .0)
call TimerStart(NewTimer(), 2., false, function FinalLoop2)
endif
call ReleaseTimer(t)
set t = null
endfunction
private function TwentySixthLoop takes nothing returns nothing
local timer t = GetExpiredTimer()
if not KathraSkipped then
call TransmissionFromUnitWithNameBJ(GetPlayersAll(), Lb.hero, "Arivan", null, "Get ready Syndra, we must stop this right here!", bj_TIMETYPE_ADD, .0, false)
call TimerStart(NewTimer(), 6., false, function FinalLoop)
endif
call ReleaseTimer(t)
set t = null
endfunction
private function TwentyFifthLoop takes nothing returns nothing
local timer t = GetExpiredTimer()
if not KathraSkipped then
call TransmissionFromUnitWithNameBJ(GetPlayersAll(), Bd.hero, "Syndra", null, "This is really bad!", bj_TIMETYPE_ADD, .0, false)
call TimerStart(NewTimer(), 6., false, function TwentySixthLoop)
endif
call ReleaseTimer(t)
set t = null
endfunction
private function TwentyFourthLoop takes nothing returns nothing
local timer t = GetExpiredTimer()
if not KathraSkipped then
call TransmissionFromUnitWithNameBJ(GetPlayersAll(), Wq.hero, "Kathra", null, "Finally! I am free once again. Now this world will burn under my fury.", bj_TIMETYPE_ADD, .0, false)
call TimerStart(NewTimer(), 7., false, function TwentyFifthLoop)
endif
call ReleaseTimer(t)
set t = null
endfunction
private function TwentyThirdLoop takes nothing returns nothing
local timer t = GetExpiredTimer()
if not KathraSkipped then
call CreateEffectOnLoc(MALICE_FX_FINAL_2, GetUnitLoc(Wq.hero))
call Wq.transformOnKathra()
call TimerStart(NewTimer(), 2., false, function TwentyFourthLoop)
endif
call ReleaseTimer(t)
set t = null
endfunction
private function TwentySecondLoop takes nothing returns nothing
local timer t = GetExpiredTimer()
if not KathraSkipped then
call CreateEffectOnLoc(MALICE_FX_FINAL, GetUnitLoc(Wq.hero))
call TimerStart(NewTimer(), 1., false, function TwentyThirdLoop)
endif
call ReleaseTimer(t)
set t = null
endfunction
private function TwentyFirstLoop takes nothing returns nothing
local timer t = GetExpiredTimer()
if not KathraSkipped then
call PauseAllUnitsBJ(true)
call SetUnitFacing(Lb.hero, AngleBetweenPoints(GetUnitLoc(Lb.hero), GetUnitLoc(Wq.hero)))
call SetUnitFacing(Bd.hero, AngleBetweenPoints(GetUnitLoc(Bd.hero), GetUnitLoc(Wq.hero)))
call CreateEffectOnLoc(MALICE_FX, GetUnitLoc(Wq.hero))
call TimerStart(NewTimer(), 1., false, function TwentySecondLoop)
endif
call ReleaseTimer(t)
set t = null
endfunction
private function TwentiethLoop takes nothing returns nothing
local timer t = GetExpiredTimer()
if not KathraSkipped then
call CreateEffectOnLoc(MALICE_FX, GetUnitLoc(Wq.hero))
call TimerStart(NewTimer(), 1., false, function TwentyFirstLoop)
endif
call ReleaseTimer(t)
set t = null
endfunction
private function NineteenthLoop takes nothing returns nothing
local timer t = GetExpiredTimer()
if not KathraSkipped then
call CreateEffectOnLoc(MALICE_FX, GetUnitLoc(Wq.hero))
call IssuePointOrderLoc(Lb.hero, "move", GetRectCenter(gg_rct_LightbringerScene2End))
call IssuePointOrderLoc(Bd.hero, "move", GetRectCenter(gg_rct_BladedancerScene2End))
call TimerStart(NewTimer(), 1., false, function TwentiethLoop)
endif
call ReleaseTimer(t)
set t = null
endfunction
private function EighteenthLoop takes nothing returns nothing
local timer t = GetExpiredTimer()
if not KathraSkipped then
call PauseAllUnitsBJ(false)
call CreateEffectOnLoc(MALICE_FX, GetUnitLoc(Wq.hero))
call TransmissionFromUnitWithNameBJ(GetPlayersAll(), Lb.hero, "Arivan", null, "Oh no... Get away from her, Syndra!", bj_TIMETYPE_ADD, .0, false)
call TimerStart(NewTimer(), 1., false, function NineteenthLoop)
endif
call ReleaseTimer(t)
set t = null
endfunction
private function SeventeenthLoop takes nothing returns nothing
local timer t = GetExpiredTimer()
if not KathraSkipped then
call CreateEffectOnLoc(MALICE_FX, GetUnitLoc(Wq.hero))
call TimerStart(NewTimer(), 1., false, function EighteenthLoop)
endif
call ReleaseTimer(t)
set t = null
endfunction
private function SixteenthLoop takes nothing returns nothing
local timer t = GetExpiredTimer()
if not KathraSkipped then
call CreateEffectOnLoc(MALICE_FX, GetUnitLoc(Wq.hero))
call TimerStart(NewTimer(), 1., false, function SeventeenthLoop)
endif
call ReleaseTimer(t)
set t = null
endfunction
private function FifteenthLoop takes nothing returns nothing
local timer t = GetExpiredTimer()
if not KathraSkipped then
call CreateEffectOnLoc(MALICE_FX, GetUnitLoc(Wq.hero))
call TimerStart(NewTimer(), 1., false, function SixteenthLoop)
endif
call ReleaseTimer(t)
set t = null
endfunction
private function FourteenthLoop takes nothing returns nothing
local timer t = GetExpiredTimer()
if not KathraSkipped then
call EndThematicMusic()
call PlayThematicMusic("Sound\\Music\\mp3Music\\Tension.mp3")
call CreateEffectOnLoc(MALICE_FX, GetUnitLoc(Wq.hero))
call TransmissionFromUnitWithNameBJ(GetPlayersAll(), Wq.hero, "Malice", null, "Did you really think you had won? Now, you both will fall before her...", bj_TIMETYPE_ADD, .0, false)
call TimerStart(NewTimer(), 1., false, function FifteenthLoop)
endif
call ReleaseTimer(t)
set t = null
endfunction
private function ThirteenthLoop takes nothing returns nothing
local timer t = GetExpiredTimer()
if not KathraSkipped then
call CreateEffectOnLoc(MALICE_FX, GetUnitLoc(Wq.hero))
call SetUnitFacing(Lb.hero, AngleBetweenPoints(GetUnitLoc(Lb.hero), GetUnitLoc(Wq.hero)))
call SetUnitFacing(Bd.hero, AngleBetweenPoints(GetUnitLoc(Bd.hero), GetUnitLoc(Wq.hero)))
call TimerStart(NewTimer(), 2., false, function FourteenthLoop)
endif
call ReleaseTimer(t)
set t = null
endfunction
private function TwelfthLoop takes nothing returns nothing
local timer t = GetExpiredTimer()
if not KathraSkipped then
call TransmissionFromUnitWithNameBJ(GetPlayersAll(), Lb.hero, "Arivan", null, "What was that?... The crystal...", bj_TIMETYPE_ADD, .0, false)
call TimerStart(NewTimer(), 6., false, function ThirteenthLoop)
endif
call ReleaseTimer(t)
set t = null
endfunction
private function EleventhLoop takes nothing returns nothing
local timer t = GetExpiredTimer()
if not KathraSkipped then
call CameraSetupApplyForceDuration(gg_cam_Kathra05, true, .0)
call TimerStart(NewTimer(), 1., false, function TwelfthLoop)
endif
call ReleaseTimer(t)
set t = null
endfunction
private function TenthLoop takes nothing returns nothing
local timer t = GetExpiredTimer()
if not KathraSkipped then
call CreateEffectOnLoc(HERETIC_EXPLODE_FX, GetUnitLoc(Heretic[1]))
call CreateEffectOnLoc(HERETIC_EXPLODE_FX, GetUnitLoc(Heretic[2]))
call CreateEffectOnLoc(HERETIC_EXPLODE_FX, GetUnitLoc(Heretic[3]))
call ExplodeUnitBJ(Heretic[1])
call ExplodeUnitBJ(Heretic[2])
call ExplodeUnitBJ(Heretic[3])
call KillDestructable(Crystal)
call CreateEffectOnLoc(CRYSTAL_EXPLODE_FX, GetDestructableLoc(Crystal))
call TimerStart(NewTimer(), 2., false, function EleventhLoop)
endif
call DestroyEffect(CrystalFx)
set CrystalFx = null
call ReleaseTimer(t)
set t = null
endfunction
private function NinethLoop takes nothing returns nothing
local timer t = GetExpiredTimer()
if not KathraSkipped then
call CreateEffectOnUnit(HERETIC_FX, Heretic[1], "chest")
call CreateEffectOnUnit(HERETIC_FX, Heretic[2], "chest")
call CreateEffectOnUnit(HERETIC_FX, Heretic[3], "chest")
call SetUnitFacing(Lb.hero, AngleBetweenPoints(GetUnitLoc(Lb.hero), GetDestructableLoc(Crystal)))
call SetUnitFacing(Bd.hero, AngleBetweenPoints(GetUnitLoc(Bd.hero), GetDestructableLoc(Crystal)))
call TimerStart(NewTimer(), 1., false, function TenthLoop)
endif
call ReleaseTimer(t)
set t = null
endfunction
private function EighthLoop takes nothing returns nothing
local timer t = GetExpiredTimer()
if not KathraSkipped then
call CreateEffectOnUnit(HERETIC_FX, Heretic[1], "chest")
call CreateEffectOnUnit(HERETIC_FX, Heretic[2], "chest")
call CreateEffectOnUnit(HERETIC_FX, Heretic[3], "chest")
call TimerStart(NewTimer(), 1., false, function NinethLoop)
endif
call ReleaseTimer(t)
set t = null
endfunction
private function SeventhLoop takes nothing returns nothing
local timer t = GetExpiredTimer()
if not KathraSkipped then
set CrystalFx = AddSpecialEffectLoc(CRYSTAL_FX, GetDestructableLoc(Crystal))
call VolumeGroupSetVolume(SOUND_VOLUMEGROUP_SPELLS, 1.)
call CameraSetupApplyForceDuration(gg_cam_Kathra03, true, .0)
call CameraSetupApplyForceDuration(gg_cam_Kathra04, true, 8.)
call TimerStart(NewTimer(), .5, false, function EighthLoop)
endif
call ReleaseTimer(t)
set t = null
endfunction
private function SixthLoop takes nothing returns nothing
local timer t = GetExpiredTimer()
if not KathraSkipped then
call SetUnitFacing(Lb.hero, AngleBetweenPoints(GetUnitLoc(Lb.hero), GetUnitLoc(Bd.hero)))
call SetUnitFacing(Bd.hero, AngleBetweenPoints(GetUnitLoc(Bd.hero), GetUnitLoc(Lb.hero)))
call TimerStart(NewTimer(), 4., false, function SeventhLoop)
endif
call ReleaseTimer(t)
set t = null
endfunction
private function FifthLoop takes nothing returns nothing
local timer t = GetExpiredTimer()
if not KathraSkipped then
call TransmissionFromUnitWithNameBJ(GetPlayersAll(), Lb.hero, "Arivan", null, "It's over Malice, now we must reach the crystal and...", bj_TIMETYPE_ADD, .0, false)
call TimerStart(NewTimer(), 2., false, function SixthLoop)
endif
call ReleaseTimer(t)
set t = null
endfunction
private function FourthLoop takes nothing returns nothing
local timer t = GetExpiredTimer()
if not KathraSkipped then
call SetUnitTimeScale(Wq.hero, 0.005)
call CameraSetupApplyForceDuration(gg_cam_Kathra02, true, 10.)
call TransmissionFromUnitWithNameBJ(GetPlayersAll(), Wq.hero, "Malice", null, "This... can't be... I... I almost...", bj_TIMETYPE_ADD, .0, false)
call TimerStart(NewTimer(), 7., false, function FifthLoop)
endif
call ReleaseTimer(t)
set t = null
endfunction
private function ThirdLoop takes nothing returns nothing
local timer t = GetExpiredTimer()
local group g = CreateGroup()
if not KathraSkipped then
call GroupEnumUnitsOfPlayer(g, Player(3), null)
call ForGroup(g, function HideUnit)
call DestroyGroup(g)
set g = CreateGroup()
call GroupEnumUnitsOfPlayer(g, Player(4), null)
call ForGroup(g, function HideUnit)
call DestroyGroup(g)
set g = CreateGroup()
call GroupEnumUnitsOfPlayer(g, Player(5), null)
call ForGroup(g, function HideUnit)
call DestroyGroup(g)
call SetUnitPositionLoc(Wq.hero, GetRectCenter(gg_rct_MaliceScene2))
if not IsAlive(Lb.hero) then
call Lightbringer.cinematicRevive()
else
call SetUnitPositionLoc(Lb.hero, GetRectCenter(gg_rct_LightbringerScene2))
endif
if not IsAlive(Bd.hero) then
call Bladedancer.cinematicRevive()
else
call SetUnitPositionLoc(Bd.hero, GetRectCenter(gg_rct_BladedancerScene2))
endif
call SetUnitInvulnerable(Lb.hero, true)
call SetUnitInvulnerable(Bd.hero, true)
call UnitRemoveBuffs(Lb.hero, true, true)
call UnitRemoveBuffs(Bd.hero, true, true)
call UnitRemoveBuffs(Wq.hero, true, true)
call ShowUnit(Heretic[1], true)
call ShowUnit(Heretic[2], true)
call ShowUnit(Heretic[3], true)
call SetUnitFacing(Lb.hero, AngleBetweenPoints(GetUnitLoc(Lb.hero), GetUnitLoc(Wq.hero)))
call SetUnitFacing(Bd.hero, AngleBetweenPoints(GetUnitLoc(Bd.hero), GetUnitLoc(Wq.hero)))
call SetUnitFacing(Wq.hero, 225.)
call SetUnitAnimation(Wq.hero, "death")
call PauseAllUnitsBJ(true)
call CameraSetupApplyForceDuration(gg_cam_Kathra01, true, .0)
call FadeOut(1.)
call TimerStart(NewTimer(), 1., false, function FourthLoop)
endif
call ReleaseTimer(t)
set g = null
set t = null
endfunction
private function SecondLoop takes nothing returns nothing
local timer t = GetExpiredTimer()
set IsKathraRunning = true
call FogMaskEnable(false)
call PlayThematicMusic("Sound\\Music\\mp3Music\\TragicConfrontation.mp3")
call SetMapMusic("music", false, 0)
call ClearSelection()
call TimerStart(NewTimer(), .01, false, function ThirdLoop)
call ReleaseTimer(t)
set t = null
endfunction
private function FirstLoop takes nothing returns nothing
local timer t = GetExpiredTimer()
call CinematicModeExBJ(true, GetPlayersAll(), bj_CINEMODE_INTERFACEFADE)
call FadeIn(1.)
call TimerStart(NewTimer(), 1., false, function SecondLoop)
call ReleaseTimer(t)
set t = null
endfunction
function SecondCinematic takes nothing returns nothing
call PauseAllUnitsBJ(true)
call QuestItemSetCompleted(MainQSecondItem, true)
call QuestMessageBJ(GetPlayersAll(), bj_QUESTMESSAGE_UPDATED, "|cffffcc00MAIN QUEST UPDATE|r
The Shrine of Duredhel
- |cff808080Reach the Shrine|r
- |cff808080Stop Malice|r ")
call TimerStart(NewTimer(), 5., false, function FirstLoop)
endfunction
endlibrary
library FinalScene requires TimerUtils, CinematicUtils
globals
boolean FinalSkipped = false
boolean IsFinalRunning = false
endglobals
private function RemoveUnits takes nothing returns nothing
call RemoveUnit(GetEnumUnit())
endfunction
private function SkipLoop2 takes nothing returns nothing
local timer t = GetExpiredTimer()
call EndThematicMusic()
if EasyDif then
call CustomVictoryBJ(SingleP, true, true)
else
call CustomVictoryBJ(Player(0), true, true)
call CustomVictoryBJ(Player(1), true, true)
endif
call ReleaseTimer(t)
set t = null
endfunction
function Cin3_SkipLoop takes nothing returns nothing
set FinalSkipped = true
set IsFinalRunning = false
call FadeIn(2.)
call TimerStart(NewTimer(), 2., false, function SkipLoop2)
endfunction
private function FinalLoop2 takes nothing returns nothing
local timer t = GetExpiredTimer()
call EndThematicMusic()
if EasyDif then
call CustomVictoryBJ(SingleP, true, true)
else
call CustomVictoryBJ(Player(0), true, true)
call CustomVictoryBJ(Player(1), true, true)
endif
call ReleaseTimer(t)
set t = null
endfunction
private function FinalLoop takes nothing returns nothing
local timer t = GetExpiredTimer()
if not FinalSkipped then
set IsFinalRunning = false
call FadeIn(2.)
call TimerStart(NewTimer(), 2., false, function FinalLoop2)
endif
call ReleaseTimer(t)
set t = null
endfunction
private function TenthLoop takes nothing returns nothing
local timer t = GetExpiredTimer()
if not FinalSkipped then
call TransmissionFromUnitWithNameBJ(GetPlayersAll(), Lb.hero, "Arivan", null, "Come on Syndra, it's time to reunite again with our forces.", bj_TIMETYPE_ADD, .0, false)
call TimerStart(NewTimer(), 6., false, function FinalLoop)
endif
call ReleaseTimer(t)
set t = null
endfunction
private function NinethLoop takes nothing returns nothing
local timer t = GetExpiredTimer()
if not FinalSkipped then
call CameraSetupApplyForceDuration(gg_cam_Final05, true, 10.)
call TransmissionFromUnitWithNameBJ(GetPlayersAll(), Lb.hero, "Arivan", null, "And I as well. At least this victory will give us a break for a time.", bj_TIMETYPE_ADD, .0, false)
call TimerStart(NewTimer(), 6., false, function TenthLoop)
endif
call ReleaseTimer(t)
set t = null
endfunction
private function EighthLoop takes nothing returns nothing
local timer t = GetExpiredTimer()
if not FinalSkipped then
call SetUnitFacing(Lb.hero, AngleBetweenPoints(GetUnitLoc(Lb.hero), GetUnitLoc(Bd.hero)))
call SetUnitFacing(Bd.hero, AngleBetweenPoints(GetUnitLoc(Bd.hero), GetUnitLoc(Lb.hero)))
call TransmissionFromUnitWithNameBJ(GetPlayersAll(), Bd.hero, "Syndra", null, "I'm so exhausted of this conflict.", bj_TIMETYPE_ADD, .0, false)
call TimerStart(NewTimer(), 4., false, function NinethLoop)
endif
call ReleaseTimer(t)
set t = null
endfunction
private function SeventhLoop takes nothing returns nothing
local timer t = GetExpiredTimer()
if not FinalSkipped then
call TransmissionFromUnitWithNameBJ(GetPlayersAll(), Lb.hero, "Arivan", null, "And Kathra has been defeated. Today we have won a crucial victory for the war.", bj_TIMETYPE_ADD, .0, false)
call TimerStart(NewTimer(), 6., false, function EighthLoop)
endif
call ReleaseTimer(t)
set t = null
endfunction
private function SixthLoop takes nothing returns nothing
local timer t = GetExpiredTimer()
if not FinalSkipped then
call CameraSetupApplyForceDuration(gg_cam_Final03, true, .0)
call CameraSetupApplyForceDuration(gg_cam_Final04, true, 11.)
call FadeOut(1.)
call TimerStart(NewTimer(), 1., false, function SeventhLoop)
endif
call ReleaseTimer(t)
set t = null
endfunction
private function FifthLoop takes nothing returns nothing
local timer t = GetExpiredTimer()
local group g = CreateGroup()
if not FinalSkipped then
call GroupEnumUnitsOfPlayer(g, Player(3), null)
call ForGroup(g, function RemoveUnits)
call DestroyGroup(g)
call FadeIn(1.)
call TimerStart(NewTimer(), 1., false, function SixthLoop)
endif
set g = null
call ReleaseTimer(t)
set t = null
endfunction
private function FourthLoop takes nothing returns nothing
local timer t = GetExpiredTimer()
if not FinalSkipped then
call CameraSetupApplyForceDuration(gg_cam_Final02, true, 7.)
call TransmissionFromUnitWithNameBJ(GetPlayersAll(), Bd.hero, "Syndra", null, "It's finally over. The Twilight Queen is dead and the Dark Elves are running away!", bj_TIMETYPE_ADD, 1., false)
call TimerStart(NewTimer(), 7., false, function FifthLoop)
endif
call ReleaseTimer(t)
set t = null
endfunction
private function ThirdLoop takes nothing returns nothing
local timer t = GetExpiredTimer()
local group g = CreateGroup()
local unit u
if not FinalSkipped then
call GroupEnumUnitsOfPlayer(g, Player(3), null)
call ForGroup(g, function RemoveUnits)
call DestroyGroup(g)
set g = CreateGroup()
call GroupEnumUnitsOfPlayer(g, Player(4), null)
call ForGroup(g, function RemoveUnits)
call DestroyGroup(g)
set g = CreateGroup()
call GroupEnumUnitsOfPlayer(g, Player(5), null)
call ForGroup(g, function RemoveUnits)
call DestroyGroup(g)
if not IsAlive(Lb.hero) then
call Lightbringer.finalRevive()
else
call SetUnitPositionLoc(Lb.hero, GetRectCenter(gg_rct_LightbringerScene3))
endif
if not IsAlive(Bd.hero) then
call Bladedancer.finalRevive()
else
call SetUnitPositionLoc(Bd.hero, GetRectCenter(gg_rct_BladedancerScene3))
endif
call SetUnitInvulnerable(Lb.hero, true)
call SetUnitInvulnerable(Bd.hero, true)
call UnitRemoveBuffs(Lb.hero, true, true)
call UnitRemoveBuffs(Bd.hero, true, true)
call SetUnitFacing(Lb.hero, 150.)
call SetUnitFacing(Bd.hero, 145.)
set u = CreateUnitAtLoc(Player(3), 'h000', GetRectCenter(gg_rct_Reaver1Scene3), 180.)
call SetUnitPathing(u, false)
call IssuePointOrderLoc(u, "move", GetRectCenter(gg_rct_EnemiesScene3End))
call SetUnitMoveSpeed(u, 285.)
set u = CreateUnitAtLoc(Player(3), 'h000', GetRectCenter(gg_rct_Reaver2Scene3), 180.)
call SetUnitPathing(u, false)
call IssuePointOrderLoc(u, "move", GetRectCenter(gg_rct_EnemiesScene3End))
call SetUnitMoveSpeed(u, 285.)
set u = CreateUnitAtLoc(Player(3), 'h001', GetRectCenter(gg_rct_Harasser1Scene3), 180.)
call SetUnitPathing(u, false)
call IssuePointOrderLoc(u, "move", GetRectCenter(gg_rct_EnemiesScene3End))
call SetUnitMoveSpeed(u, 285.)
set u = CreateUnitAtLoc(Player(3), 'h001', GetRectCenter(gg_rct_Harasser2Scene3), 180.)
call SetUnitPathing(u, false)
call IssuePointOrderLoc(u, "move", GetRectCenter(gg_rct_EnemiesScene3End))
call SetUnitMoveSpeed(u, 285.)
set u = CreateUnitAtLoc(Player(3), 'h001', GetRectCenter(gg_rct_Harasser3Scene3), 180.)
call SetUnitPathing(u, false)
call IssuePointOrderLoc(u, "move", GetRectCenter(gg_rct_EnemiesScene3End))
call SetUnitMoveSpeed(u, 285.)
set u = CreateUnitAtLoc(Player(3), 'h003', GetRectCenter(gg_rct_HereticScene3), 180.)
call SetUnitPathing(u, false)
call IssuePointOrderLoc(u, "move", GetRectCenter(gg_rct_EnemiesScene3End))
call SetUnitMoveSpeed(u, 285.)
call CameraSetupApplyForceDuration(gg_cam_Final01, true, .0)
call FadeOut(1.)
call TimerStart(NewTimer(), 1., false, function FourthLoop)
endif
set g = null
set u = null
call ReleaseTimer(t)
set t = null
endfunction
private function SecondLoop takes nothing returns nothing
local timer t = GetExpiredTimer()
set IsFinalRunning = true
call FogMaskEnable(false)
call PlayThematicMusic("Sound\\Music\\mp3Music\\Comradeship.mp3")
call SetMapMusic("music", false, 0)
call ClearSelection()
call TimerStart(NewTimer(), .01, false, function ThirdLoop)
call ReleaseTimer(t)
set t = null
endfunction
private function FirstLoop takes nothing returns nothing
local timer t = GetExpiredTimer()
call CinematicModeExBJ(true, GetPlayersAll(), bj_CINEMODE_INTERFACEFADE)
call FadeIn(1.)
call TimerStart(NewTimer(), 1., false, function SecondLoop)
call ReleaseTimer(t)
set t = null
endfunction
function FinalCinematic takes nothing returns nothing
call QuestItemSetCompleted(MainQThirdItem, true)
call QuestMessageBJ(GetPlayersAll(), bj_QUESTMESSAGE_COMPLETED, "|cffffcc00MAIN QUEST COMPLETED|r
The Shrine of Duredhel
- |cff808080Reach the Shrine|r
- |cff808080Stop Malice|r
- |cff808080Kill Kathra|r ")
call QuestSetCompleted(MainQuest, true)
call TimerStart(NewTimer(), 5., false, function FirstLoop)
endfunction
endlibrary