Name | Type | is_array | initial_value |
//TESH.scrollpos=0
//TESH.alwaysfold=0
library InitMap initializer Init
globals
unit Hero
endglobals
private function Init takes nothing returns nothing
call SetFloatGameState(GAME_STATE_TIME_OF_DAY, 12.)
set Hero = CreateUnit(Player(0),'Nbrn',0,0,270)
call SelectUnit(Hero,true)
call DisplayTextToPlayer(Player(0),0,0,"Wrath by watermelon_1234\nType -lvl x to set hero's level to x\nPress Esc to refresh your hero.")
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
scope ReviveUnits initializer Init
globals
private hashtable Revive = InitHashtable()
private constant real DELAY = 30.
private constant real HERO_DELAY = 5.
endglobals
private function SaveData takes unit u returns nothing
call SaveReal(Revive,0,GetHandleId(u),GetUnitX(u))
call SaveReal(Revive,1,GetHandleId(u),GetUnitY(u))
call SaveReal(Revive,2,GetHandleId(u),GetUnitFacing(u))
endfunction
private function ReviveFilter takes nothing returns boolean
local unit u = GetFilterUnit()
call SaveData(u)
set u = null
return true
endfunction
private struct Data
unit d
static method create takes unit d returns Data
local Data D = Data.allocate()
set D.d = d
return D
endmethod
static method onRevive takes nothing returns nothing
local timer t = GetExpiredTimer()
local Data D = Data(GetTimerData(t))
local real x = LoadReal(Revive,0,GetHandleId(D.d))
local real y = LoadReal(Revive,1,GetHandleId(D.d))
local real ang = LoadReal(Revive,2,GetHandleId(D.d))
if IsUnitType(D.d,UNIT_TYPE_HERO) == true then
call ReviveHero(D.d,x,y,true)
call SetUnitFacing(D.d,ang)
if GetLocalPlayer() == GetOwningPlayer(D.d) then
call PanCameraTo(x,y)
endif
call SetUnitState( D.d,UNIT_STATE_MANA,GetUnitState(D.d,UNIT_STATE_MAX_MANA))
else
call SaveData(CreateUnit(GetOwningPlayer(D.d),GetUnitTypeId(D.d),x,y,ang))
endif
set D.d = null
call D.destroy()
call ReleaseTimer(t)
set t = null
endmethod
endstruct
private function Actions takes nothing returns boolean
local timer t = NewTimer()
local unit d = GetTriggerUnit()
local real delay = DELAY
if IsUnitType(d,UNIT_TYPE_STRUCTURE) == false and IsUnitType(d,UNIT_TYPE_SUMMONED) == false then
call SetTimerData(t,Data.create(d))
if IsUnitType(d,UNIT_TYPE_HERO) == true then
set delay = HERO_DELAY
endif
call TimerStart(t,delay,false,function Data.onRevive)
set t = null
endif
return false
endfunction
private function Init takes nothing returns nothing
local trigger t = CreateTrigger( )
local group g = CreateGroup()
call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_DEATH )
call TriggerAddCondition( t, Condition(function Actions))
call GroupEnumUnitsInRect(g, bj_mapInitialPlayableArea, Condition(function ReviveFilter))
call DestroyGroup(g)
set g = null
endfunction
endscope
//TESH.scrollpos=0
//TESH.alwaysfold=0
function Trig_Refresh_Actions takes nothing returns nothing
call SetWidgetLife( Hero,GetUnitState(Hero,UNIT_STATE_MAX_LIFE))
call SetUnitState( Hero,UNIT_STATE_MANA,GetUnitState(Hero,UNIT_STATE_MAX_MANA))
call UnitResetCooldown( Hero )
endfunction
//===========================================================================
function InitTrig_Refresh takes nothing returns nothing
set gg_trg_Refresh = CreateTrigger( )
call TriggerRegisterPlayerEventEndCinematic( gg_trg_Refresh, Player(0) )
call TriggerAddAction( gg_trg_Refresh, function Trig_Refresh_Actions )
endfunction
//TESH.scrollpos=0
//TESH.alwaysfold=0
function Trig_Level_Actions takes nothing returns nothing
call SetHeroLevel( Hero, S2I(SubString(GetEventPlayerChatString(), 5, StringLength(GetEventPlayerChatString()))), false )
endfunction
function InitTrig_LvlUp takes nothing returns nothing
set gg_trg_LvlUp = CreateTrigger()
call TriggerRegisterPlayerChatEvent( gg_trg_LvlUp, Player(0), "-lvl", false )
call TriggerAddAction( gg_trg_LvlUp, function Trig_Level_Actions )
endfunction
//TESH.scrollpos=9
//TESH.alwaysfold=0
library Table // made by Bribe, special thanks to Nestharus, version 3.0.0.0
/*
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 hashtable ht = InitHashtable() //The last hashtable you need
private integer more = 2 //Index generation for Tables (above 2)
private integer less = 0 //Index generation for TableArrays (below 0)
endglobals
private struct dex extends array
static method operator size takes nothing returns Table
return 1
endmethod
static method operator list takes nothing returns Table
return 2
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
endstruct
private module $TYPE$m
method operator $TYPE$ takes nothing returns $TYPE$s
return this
endmethod
endmodule
//! endtextmacro
//! runtextmacro NEW_ARRAY_BASIC("Real", "Real", "real")
//! runtextmacro NEW_ARRAY_BASIC("Boolean", "Boolean", "boolean")
//! runtextmacro NEW_ARRAY_BASIC("String", "Str", "string")
//! 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 type-syntax
implement realm
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 = a[GetSpellAbilityId()]
method operator [] takes integer key returns Table
return LoadInteger(ht, this, key)
endmethod
// set a[389034] = 8192
method operator []= takes integer key, Table a returns nothing
call SaveInteger(ht, this, key, a)
endmethod
// set b = a.has(2493223)
method has takes integer key returns boolean
return HaveSavedInteger(ht, this, key)
endmethod
// call a.remove(294080)
method remove takes integer key returns nothing
call RemoveSavedInteger(ht, this, key)
endmethod
// Remove all data from a Table instance
method flush takes nothing returns nothing
call FlushChildHashtable(ht, this)
endmethod
// local Table a = Table.create()
static method create takes nothing returns Table
local Table this = dex.list[0]
if this == 0 then
set more = more + 1
set this = more
else
set dex.list[0] = dex.list[this]
call dex.list.remove(this)
endif
debug set dex.list[this] = -1
return this
endmethod
// Removes all data from a Table instance and recycles its index.
//
// call a.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
endstruct
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 a = dex.size[array_size] //Get the unique recycle list for this array size
local TableArray this = a[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 less = less - array_size
set this = less
else
set a[0] = a[this] //Set the last destroyed to the last-last destroyed
call a.remove(this) //Clear hash 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
//da[integer a].unit[integer b] = unit u
//da[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; assumed you'd call .flush()
//if you want it flushed too. This is public so that if you are flushing
//instances the whole time you don't waste efficiency when disposing the
//TableArray.
//
method destroy takes nothing returns nothing
local Table a = 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 a == 0 then
//Create an array to index recycled instances with their array size
set a = Table.create()
set dex.size[this.size] = a
endif
call dex.size.remove(this) //Clear the array size from hash memory
set a[this] = a[0]
set a[0] = this
endmethod
//All you need to know about this one is that it won't hit the op limit.
private static method clean takes Table a, integer end returns nothing
local integer i = a + 5000
if i < end then
call clean.evaluate(i, end)
set end = i
endif
loop
call a.flush()
set a = a + 1
exitwhen a == 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
local integer end = this.size + this
debug if this == end then
debug call BJDebugMsg("TypeError: Tried to flush an invalid TableArray instance: " + I2S(this))
debug return
debug endif
call clean.evaluate(this, end)
call this.destroy()
endmethod
endstruct
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library TimerUtils initializer init
//*********************************************************************
//* TimerUtils (red+blue+orange flavors for 1.24b+)
//* ----------
//*
//* 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)
//* ReleaseTimer(t) : Relese a timer (alt to DestroyTimer)
//* SetTimerData(t,2) : Attach value 2 to timer
//* GetTimerData(t) : Get the timer's value.
//* You can assume a timer's value is 0
//* after NewTimer.
//*
//* Multi-flavor:
//* Set USE_HASH_TABLE to true if you don't want to complicate your life.
//*
//* If you like speed and giberish try learning about the other flavors.
//*
//********************************************************************
//================================================================
globals
//How to tweak timer utils:
// USE_HASH_TABLE = true (new blue)
// * SAFEST
// * SLOWEST (though hash tables are kind of fast)
//
// USE_HASH_TABLE = false, USE_FLEXIBLE_OFFSET = true (orange)
// * kinda safe (except there is a limit in the number of timers)
// * ALMOST FAST
//
// USE_HASH_TABLE = false, USE_FLEXIBLE_OFFSET = false (red)
// * THE FASTEST (though is only faster than the previous method
// after using the optimizer on the map)
// * THE LEAST SAFE ( you may have to tweak OFSSET manually for it to
// work)
//
private constant boolean USE_HASH_TABLE = true
private constant boolean USE_FLEXIBLE_OFFSET = false
private constant integer OFFSET = 0x100000
private integer VOFFSET = OFFSET
//Timers to preload at map init:
private constant integer QUANTITY = 256
//Changing this to something big will allow you to keep recycling
// timers even when there are already AN INCREDIBLE AMOUNT of timers in
// the stack. But it will make things far slower so that's probably a bad idea...
private constant integer ARRAY_SIZE = 8190
endglobals
//==================================================================================================
globals
private integer array data[ARRAY_SIZE]
private hashtable ht
endglobals
//It is dependent on jasshelper's recent inlining optimization in order to perform correctly.
function SetTimerData takes timer t, integer value returns nothing
static if(USE_HASH_TABLE) then
// new blue
call SaveInteger(ht,0,GetHandleId(t), value)
elseif (USE_FLEXIBLE_OFFSET) then
// orange
static if (DEBUG_MODE) then
if(GetHandleId(t)-VOFFSET<0) then
call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
endif
endif
set data[GetHandleId(t)-VOFFSET]=value
else
// new red
static if (DEBUG_MODE) then
if(GetHandleId(t)-OFFSET<0) then
call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
endif
endif
set data[GetHandleId(t)-OFFSET]=value
endif
endfunction
function GetTimerData takes timer t returns integer
static if(USE_HASH_TABLE) then
// new blue
return LoadInteger(ht,0,GetHandleId(t) )
elseif (USE_FLEXIBLE_OFFSET) then
// orange
static if (DEBUG_MODE) then
if(GetHandleId(t)-VOFFSET<0) then
call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
endif
endif
return data[GetHandleId(t)-VOFFSET]
else
// new red
static if (DEBUG_MODE) then
if(GetHandleId(t)-OFFSET<0) then
call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
endif
endif
return data[GetHandleId(t)-OFFSET]
endif
endfunction
//==========================================================================================
globals
private timer array tT[ARRAY_SIZE]
private integer tN = 0
private constant integer HELD=0x28829022
//use a totally random number here, the more improbable someone uses it, the better.
endglobals
//==========================================================================================
function NewTimer takes nothing returns timer
if (tN==0) then
//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")
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")
set tT[0]=CreateTimer()
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
else
set tN=tN-1
endif
call SetTimerData(tT[tN],0)
return tT[tN]
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
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=28
//TESH.alwaysfold=0
library xebasic
//**************************************************************************
//
// xebasic 0.4
// =======
// XE_DUMMY_UNITID : Rawcode of the dummy unit in your map. It should
// use the dummy.mdx model, so remember to import it as
// well, just use copy&paste to copy the dummy from the
// xe map to yours, then change the rawcode.
//
// XE_HEIGHT_ENABLER: Medivh's raven form ability, you may need to change
// this rawcode to another spell that morphs into a flier
// in case you modified medivh's spell in your map.
//
// XE_TREE_RECOGNITION: The ancients' Eat tree ability, same as with medivh
// raven form, you might have to change it.
//
// XE_ANIMATION_PERIOD: The global period of animation used by whatever
// timer that depends on it, if you put a low value
// the movement will look good but it may hurt your
// performance, if instead you use a high value it
// will not lag but will be fast.
//
// XE_MAX_COLLISION_SIZE: The maximum unit collision size in your map, if
// you got a unit bigger than 197.0 it would be
// a good idea to update this constant, since some
// enums will not find it. Likewise, if none of
// your units can go bellow X and X is much smaller
// than 197.0, it would be a good idea to update
// as well, since it will improve the performance
// those enums.
//
// Notice you probably don't have to update this library, unless I specify
// there are new constants which would be unlikely.
//
//**************************************************************************
//===========================================================================
globals
constant integer XE_DUMMY_UNITID = 'e000'
constant integer XE_HEIGHT_ENABLER = 'Amrf'
constant integer XE_TREE_RECOGNITION = 'Aeat'
constant real XE_ANIMATION_PERIOD = 0.025
constant real XE_MAX_COLLISION_SIZE = 197.0
endglobals
endlibrary
//TESH.scrollpos=45
//TESH.alwaysfold=0
library xefx initializer init requires xebasic
//**************************************************
// xefx 0.7
// --------
// Recommended: ARGB (adds ARGBrecolor method)
// For your movable fx needs
//
//**************************************************
//==================================================
globals
private constant integer MAX_INSTANCES = 8190 //change accordingly.
private constant real RECYCLE_DELAY = 4.0
//recycling, in order to show the effect correctly, must wait some time before
//removing the unit.
private timer recycler
private timer NOW
endglobals
private struct recyclebin extends array
unit u
real schedule
static recyclebin end=0
static recyclebin begin=0
static method Recycle takes nothing returns nothing
call RemoveUnit(.begin.u) //this unit is private, systems shouldn't mess with it.
set .begin.u=null
set .begin=recyclebin(integer(.begin)+1)
if(.begin==.end) then
set .begin=0
set .end=0
else
call TimerStart(recycler, .begin.schedule-TimerGetElapsed(NOW), false, function recyclebin.Recycle)
endif
endmethod
endstruct
private function init takes nothing returns nothing
set recycler=CreateTimer()
set NOW=CreateTimer()
call TimerStart(NOW,43200,true,null)
endfunction
struct xefx[MAX_INSTANCES]
public integer tag=0
private unit dummy
private effect fx=null
private real zang=0.0
private integer r=255
private integer g=255
private integer b=255
private integer a=255
private integer abil=0
static method create takes real x, real y, real facing returns xefx
local xefx this=xefx.allocate()
set this.dummy= CreateUnit(Player(15), XE_DUMMY_UNITID, x,y, facing*bj_RADTODEG)
call UnitAddAbility(this.dummy,XE_HEIGHT_ENABLER)
call UnitAddAbility(this.dummy,'Aloc')
call UnitRemoveAbility(this.dummy,XE_HEIGHT_ENABLER)
call SetUnitX(this.dummy,x)
call SetUnitY(this.dummy,y)
return this
endmethod
method operator owner takes nothing returns player
return GetOwningPlayer(this.dummy)
endmethod
method operator owner= takes player p returns nothing
call SetUnitOwner(this.dummy,p,false)
endmethod
method operator teamcolor= takes playercolor c returns nothing
call SetUnitColor(this.dummy,c)
endmethod
method operator scale= takes real value returns nothing
call SetUnitScale(this.dummy,value,value,value)
endmethod
//! textmacro XEFX_colorstuff takes colorname, colorvar
method operator $colorname$ takes nothing returns integer
return this.$colorvar$
endmethod
method operator $colorname$= takes integer value returns nothing
set this.$colorvar$=value
call SetUnitVertexColor(this.dummy,this.r,this.g,this.b,this.a)
endmethod
//! endtextmacro
//! runtextmacro XEFX_colorstuff("red","r")
//! runtextmacro XEFX_colorstuff("green","g")
//! runtextmacro XEFX_colorstuff("blue","b")
//! runtextmacro XEFX_colorstuff("alpha","a")
method recolor takes integer r, integer g , integer b, integer a returns nothing
set this.r=r
set this.g=g
set this.b=b
set this.a=a
call SetUnitVertexColor(this.dummy,this.r,this.g,this.b,this.a)
endmethod
implement optional ARGBrecolor
method operator abilityid takes nothing returns integer
return this.abil
endmethod
method operator abilityid= takes integer a returns nothing
if(this.abil!=0) then
call UnitRemoveAbility(this.dummy,this.abil)
endif
if(a!=0) then
call UnitAddAbility(this.dummy,a)
endif
set this.abil=a
endmethod
method operator abilityLevel takes nothing returns integer
return GetUnitAbilityLevel( this.dummy, this.abil)
endmethod
method operator abilityLevel= takes integer newLevel returns nothing
call SetUnitAbilityLevel(this.dummy, this.abil, newLevel)
endmethod
method flash takes string fx returns nothing
call DestroyEffect(AddSpecialEffectTarget(fx,this.dummy,"origin"))
endmethod
method operator xyangle takes nothing returns real
return GetUnitFacing(this.dummy)*bj_DEGTORAD
endmethod
method operator xyangle= takes real value returns nothing
call SetUnitFacing(this.dummy,value*bj_RADTODEG)
endmethod
method operator zangle takes nothing returns real
return this.zang
endmethod
method operator zangle= takes real value returns nothing
local integer i=R2I(value*bj_RADTODEG+90.5)
set this.zang=value
if(i>=180) then
set i=179
elseif(i<0) then
set i=0
endif
call SetUnitAnimationByIndex(this.dummy, i )
endmethod
method operator x takes nothing returns real
return GetUnitX(this.dummy)
endmethod
method operator y takes nothing returns real
return GetUnitY(this.dummy)
endmethod
method operator z takes nothing returns real
return GetUnitFlyHeight(this.dummy)
endmethod
method operator z= takes real value returns nothing
call SetUnitFlyHeight(this.dummy,value,0)
endmethod
method operator x= takes real value returns nothing
call SetUnitX(this.dummy,value)
endmethod
method operator y= takes real value returns nothing
call SetUnitY(this.dummy,value)
endmethod
method operator fxpath= takes string newpath returns nothing
if (this.fx!=null) then
call DestroyEffect(this.fx)
endif
if (newpath=="") then
set this.fx=null
else
set this.fx=AddSpecialEffectTarget(newpath,this.dummy,"origin")
endif
endmethod
method hiddenReset takes string newfxpath, real newfacing returns nothing
local real x = GetUnitX(this.dummy)
local real y = GetUnitY(this.dummy)
local real z = this.z
local real za = this.zangle
local integer level = this.abilityLevel
set fxpath=null
call RemoveUnit(this.dummy)
set this.dummy= CreateUnit(Player(15), XE_DUMMY_UNITID, x,y, newfacing*bj_RADTODEG)
if(level != 0) then
call UnitAddAbility(this.dummy, abilityid)
endif
call UnitAddAbility(this.dummy,XE_HEIGHT_ENABLER)
call UnitAddAbility(this.dummy,'Aloc')
call UnitRemoveAbility(this.dummy,XE_HEIGHT_ENABLER)
call SetUnitX(this.dummy,x)
call SetUnitY(this.dummy,y)
set this.z = z
set zangle = za
endmethod
private method onDestroy takes nothing returns nothing
if(this.abil!=0) then
call UnitRemoveAbility(this.dummy,this.abil)
endif
if(this.fx!=null) then
call DestroyEffect(this.fx)
set this.fx=null
endif
if (recyclebin.end==MAX_INSTANCES) then
//I'd like to see this happen...
call TimerStart(recycler,0,false,function recyclebin.Recycle)
call ExplodeUnitBJ(this.dummy)
else
set recyclebin.end.u=this.dummy
set recyclebin.end.schedule=TimerGetElapsed(NOW)+RECYCLE_DELAY
set recyclebin.end= recyclebin( integer(recyclebin.end)+1)
if( recyclebin.end==1) then
call TimerStart(recycler, RECYCLE_DELAY, false, function recyclebin.Recycle)
endif
call SetUnitOwner(this.dummy,Player(15),false)
endif
set this.dummy=null
endmethod
method hiddenDestroy takes nothing returns nothing
call ShowUnit(dummy,false)
call destroy()
endmethod
endstruct
endlibrary
//TESH.scrollpos=36
//TESH.alwaysfold=0
library GroupUtils initializer Init requires optional xebasic
//******************************************************************************
//* BY: Rising_Dusk
//*
//* This library is a combination of several features relevant to groups. First
//* and foremost, it contains a group stack that you can access dynamic groups
//* from. It also provides means to refresh groups and clear any shadow
//* references within them. The included boolexprs are there for backwards
//* compatibility with maps that happen to use them. Since the 1.24c patch,
//* null boolexprs used in GroupEnumUnits* calls no longer leak, so there is no
//* performance gain to using the BOOLEXPR_TRUE constant.
//*
//* Instead of creating/destroying groups, we have moved on to recycling them.
//* NewGroup pulls a group from the stack and ReleaseGroup adds it back. Always
//* remember to call ReleaseGroup on a group when you are done using it. If you
//* fail to do so enough times, the stack will overflow and no longer work.
//*
//* GroupRefresh cleans a group of any shadow references which may be clogging
//* its hashtable. If you remove a unit from the game who is a member of a unit
//* group, it will 'effectively' remove the unit from the group, but leave a
//* shadow in its place. Calling GroupRefresh on a group will clean up any
//* shadow references that may exist within it. It is only worth doing this on
//* groups that you plan to have around for awhile.
//*
//* Constants that can be used from the library:
//* [group] ENUM_GROUP As you might expect, this group is good for
//* when you need a group just for enumeration.
//* [boolexpr] BOOLEXPR_TRUE This is a true boolexpr, which is important
//* because a 'null' boolexpr in enumeration
//* calls results in a leak. Use this instead.
//* [boolexpr] BOOLEXPR_FALSE This exists mostly for completeness.
//*
//* This library also includes a simple implementation of a group enumeration
//* call that factors collision of units in a given area of effect. This is
//* particularly useful because GroupEnumUnitsInRange doesn't factor collision.
//*
//* In your map, you can just replace all instances of GroupEnumUnitsInRange
//* with GroupEnumUnitsInArea with identical arguments and your spells will
//* consider all units colliding with the area of effect. After calling this
//* function as you would normally call GroupEnumUnitsInRange, you are free to
//* do anything with the group that you would normally do.
//*
//* If you don't use xebasic in your map, you may edit the MAX_COLLISION_SIZE
//* variable below and the library will use that as the added radius to check.
//* If you use xebasic, however, the script will automatically use xe's
//* collision size variable.
//*
//* You are also able to use GroupUnitsInArea. This function returns all units
//* within the area, no matter what they are, which can be convenient for those
//* instances where you actually want that.
//*
//* Example usage:
//* local group MyGroup = NewGroup()
//* call GroupRefresh(MyGroup)
//* call ReleaseGroup(MyGroup)
//* call GroupEnumUnitsInArea(ENUM_GROUP, x, y, 350., BOOLEXPR_TRUE)
//* call GroupUnitsInArea(ENUM_GROUP, x, y, 350.)
//*
globals
//If you don't have xebasic in your map, this value will be used instead.
//This value corresponds to the max collision size of a unit in your map.
private constant real MAX_COLLISION_SIZE = 197.
//If you are insane and don't care about any of the protection involved in
//this library, but want this script to be really fast, set this to true.
private constant boolean LESS_SAFETY = false
endglobals
globals
//* Constants that are available to the user
group ENUM_GROUP = CreateGroup()
boolexpr BOOLEXPR_TRUE = null
boolexpr BOOLEXPR_FALSE = null
endglobals
globals
//* Hashtable for debug purposes
private hashtable ht = InitHashtable()
//* Temporary references for GroupRefresh
private boolean Flag = false
private group Refr = null
//* Arrays and counter for the group stack
private group array Groups
private integer Count = 0
//* Variables for use with the GroupUnitsInArea function
private real X = 0.
private real Y = 0.
private real R = 0.
private hashtable H = InitHashtable()
endglobals
private function HookDestroyGroup takes group g returns nothing
if g == ENUM_GROUP then
call BJDebugMsg(SCOPE_PREFIX+"Warning: ENUM_GROUP destroyed")
endif
endfunction
debug hook DestroyGroup HookDestroyGroup
private function AddEx takes nothing returns nothing
if Flag then
call GroupClear(Refr)
set Flag = false
endif
call GroupAddUnit(Refr, GetEnumUnit())
endfunction
function GroupRefresh takes group g returns nothing
set Flag = true
set Refr = g
call ForGroup(Refr, function AddEx)
if Flag then
call GroupClear(g)
endif
endfunction
function NewGroup takes nothing returns group
if Count == 0 then
set Groups[0] = CreateGroup()
else
set Count = Count - 1
endif
static if not LESS_SAFETY then
call SaveInteger(ht, 0, GetHandleId(Groups[Count]), 1)
endif
return Groups[Count]
endfunction
function ReleaseGroup takes group g returns boolean
local integer id = GetHandleId(g)
static if LESS_SAFETY then
if g == null then
debug call BJDebugMsg(SCOPE_PREFIX+"Error: Null groups cannot be released")
return false
elseif Count == 8191 then
debug call BJDebugMsg(SCOPE_PREFIX+"Error: Max groups achieved, destroying group")
call DestroyGroup(g)
return false
endif
else
if g == null then
debug call BJDebugMsg(SCOPE_PREFIX+"Error: Null groups cannot be released")
return false
elseif not HaveSavedInteger(ht, 0, id) then
debug call BJDebugMsg(SCOPE_PREFIX+"Error: Group not part of stack")
return false
elseif LoadInteger(ht, 0, id) == 2 then
debug call BJDebugMsg(SCOPE_PREFIX+"Error: Groups cannot be multiply released")
return false
elseif Count == 8191 then
debug call BJDebugMsg(SCOPE_PREFIX+"Error: Max groups achieved, destroying group")
call DestroyGroup(g)
return false
endif
call SaveInteger(ht, 0, id, 2)
endif
call GroupClear(g)
set Groups[Count] = g
set Count = Count + 1
return true
endfunction
private function Filter takes nothing returns boolean
return IsUnitInRangeXY(GetFilterUnit(), X, Y, R)
endfunction
private function HookDestroyBoolExpr takes boolexpr b returns nothing
local integer bid = GetHandleId(b)
if HaveSavedHandle(H, 0, bid) then
//Clear the saved boolexpr
call DestroyBoolExpr(LoadBooleanExprHandle(H, 0, bid))
call RemoveSavedHandle(H, 0, bid)
endif
endfunction
hook DestroyBoolExpr HookDestroyBoolExpr
private constant function GetRadius takes real radius returns real
static if LIBRARY_xebasic then
return radius+XE_MAX_COLLISION_SIZE
else
return radius+MAX_COLLISION_SIZE
endif
endfunction
function GroupEnumUnitsInArea takes group whichGroup, real x, real y, real radius, boolexpr filter returns nothing
local real prevX = X
local real prevY = Y
local real prevR = R
local integer bid = 0
//Set variables to new values
set X = x
set Y = y
set R = radius
if filter == null then
//Adjusts for null boolexprs passed to the function
set filter = Condition(function Filter)
else
//Check for a saved boolexpr
set bid = GetHandleId(filter)
if HaveSavedHandle(H, 0, bid) then
//Set the filter to use to the saved one
set filter = LoadBooleanExprHandle(H, 0, bid)
else
//Create a new And() boolexpr for this filter
set filter = And(Condition(function Filter), filter)
call SaveBooleanExprHandle(H, 0, bid, filter)
endif
endif
//Enumerate, if they want to use the boolexpr, this lets them
call GroupEnumUnitsInRange(whichGroup, x, y, GetRadius(radius), filter)
//Give back original settings so nested enumerations work
set X = prevX
set Y = prevY
set R = prevR
endfunction
function GroupUnitsInArea takes group whichGroup, real x, real y, real radius returns nothing
local real prevX = X
local real prevY = Y
local real prevR = R
//Set variables to new values
set X = x
set Y = y
set R = radius
//Enumerate
call GroupEnumUnitsInRange(whichGroup, x, y, GetRadius(radius), Condition(function Filter))
//Give back original settings so nested enumerations work
set X = prevX
set Y = prevY
set R = prevR
endfunction
private function True takes nothing returns boolean
return true
endfunction
private function False takes nothing returns boolean
return false
endfunction
private function Init takes nothing returns nothing
set BOOLEXPR_TRUE = Condition(function True)
set BOOLEXPR_FALSE = Condition(function False)
endfunction
endlibrary
//TESH.scrollpos=30
//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=189
//TESH.alwaysfold=0
//==========================================================================================
// Wrath v1.02b by watermelon_1234
//******************************************************************************************
// Libraries required: (Libraries with * are optional)
// - New Table http://www.hiveworkshop.com/forums/jass-functions-413/snippet-new-table-188084/
// - TimerUtils http://www.wc3c.net/showthread.php?t=101322
// - xe system http://www.wc3c.net/showthread.php?t=101150
// * BoundSentinel http://www.wc3c.net/showthread.php?t=102576
// * GroupUtils http://www.wc3c.net/showthread.php?t=104464
//##########################################################################################
// Importing:
// 1. Copy the ability, Wrath
// 2. Copy the unit, Wrath (Dummy)
// 3. Implement the required libraries
// 4. Copy this trigger
// 5. Configure the spell
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Notes:
// - N/A
//==========================================================================================
scope Wrath
//==========================================================================================
// CONSTANTS
//==========================================================================================
globals
//------------------------------------------------------------------------------------------
// General spell settings
//------------------------------------------------------------------------------------------
private constant integer SPELL_ID = 'A000' // Raw id of the Wish ability
private constant integer DUMMY_ID = 'h000' // Raw id of the Wrath dummy unit
private constant real TIMER_LOOP = 0.03 // How often the timer loops, affects the smoothness of the spell
//------------------------------------------------------------------------------------------
// Wrath-specific settings
//------------------------------------------------------------------------------------------
private constant boolean RECYCLE_DUMMY = true // Determines whether or not to recylce the wrath unit; good idea if the spell is used frequently
private constant integer ANIM_SUMMON = 2 // Animation index the wrath plays while being summoned
private constant string ANIM_REPEAT = "attack" // Animation the wrath plays every ANIM_REPEAT_INTERVAL until MISSILE_TIME seconds has elapsed
private constant real ANIM_REPEAT_INT = 0.5 // Determines how often the wrath plays ANIM_REPEAT and when DAMAGE_TIME_SFX will be played
private constant string ANIM_FIN = "spell" // Animation the wrath plays when the spell deals the delayed damage
private constant real WRATH_EXPIRE = 1.4 // Determines how long the wrath will remain after playing ANIM_FIN
//------------------------------------------------------------------------------------------
// Missile-specfic settings
//------------------------------------------------------------------------------------------
private constant string MISSILE_SFX = "Abilities\\Weapons\\ZigguratMissile\\ZigguratMissile.mdl" // SFX path for the missile
private constant real MISSILE_ROTATE = bj_PI/2 // Determines how many radians the missiles will orbit per second
private constant integer MAX_MISSILES = 8 // The maximum number of missiles that would be created
//------------------------------------------------------------------------------------------
// Shared Settings for the Wrath and Missiles
//------------------------------------------------------------------------------------------
// Values with an INIT and FIN are affected by how long the spell was channeled for
// Vertex Colors
private constant integer RED = 255 // Red value
private constant integer GREEN = 255 // Green value
private constant integer BLUE = 255 // Blue value
private constant integer ALPHA_INIT = 50 // Initial alpha
private constant integer ALPHA_FIN = 150 // Final alpha
// Scaling
private constant real SCALE_INIT = 0.65 // Initial scaling
private constant real SCALE_FIN = 1.35 // Final scaling
// Height
private constant real HEIGHT_INIT = 50. // Initial height
private constant real HEIGHT_FIN = 200. // Final height
//------------------------------------------------------------------------------------------
// Other SFX settings
//------------------------------------------------------------------------------------------
private constant boolean PRELOAD = true // Determines if the spell should preload the sfxs and Wrath dummy unit
private constant string DMG_SFX = "Abilities\\Spells\\Undead\\Possession\\PossessionMissile.mdl" //The sfx that will be played on units damaged by the Delayed damage of the spell
private constant string DMG_ATTACH = "chest" // Attachment point for DMG_SFX
private constant string DMG_TIME_SFX = "Abilities\\Spells\\Undead\\DeathandDecay\\DeathandDecayDamage.mdl" //The sfx that will play on units affected by the DoT filter. Note that this sfx will be played as often as MISSILE_TIME_LOOP
private constant string DMG_TIME_ATTACH = "origin" //Attachment point for DMG_TIME_SFX
private constant string POINT_SFX = "Objects\\Spawnmodels\\Undead\\UndeadDissipate\\UndeadDissipate.mdl" //The sfx that will be played on the wrath unit
//------------------------------------------------------------------------------------------
// Damage Settings
//------------------------------------------------------------------------------------------
private constant attacktype ATK_TYPE = ATTACK_TYPE_NORMAL
private constant damagetype DMG_TYPE = DAMAGE_TYPE_UNIVERSAL
private constant weapontype WPN_TYPE = null
//------------------------------------------------------------------------------------------
// Texttag settings
//------------------------------------------------------------------------------------------
private constant real FONT_SIZE = .038 // Font size for the texttag
private constant real TEXT_VELOCITY = 0.04 // Velocity for texttag
private constant real TEXT_FADE = 1.5 // Fading age for texttag
private constant real TEXT_LIFE = 2. // Lifespan for texttag
// Vertex color
private constant integer TEXT_R = 150 // Red value
private constant integer TEXT_G = 0 // Green value
private constant integer TEXT_B = 255 // Blue value
private constant integer TEXT_A = 255 // Alpha value
endglobals
//==========================================================================================
// OTHER CONFIGURATION
//==========================================================================================
// The target area of the spell
private constant function Area takes integer lvl returns real
return 225. + 75*lvl
endfunction
// Determines which units are damaged by the spell
private function AffectedTargets takes unit target, player owner returns boolean
return not IsUnitType(target, UNIT_TYPE_DEAD) and /*
*/ IsUnitEnemy(target, owner) and /*
*/ not IsUnitType(target, UNIT_TYPE_MAGIC_IMMUNE) and /*
*/ not IsUnitType(target, UNIT_TYPE_STRUCTURE)
endfunction
// The casting time for the spell
private constant function CastTime takes integer lvl returns real
return 3.
endfunction
// The number of missiles that will orbit around the wrath; change MAX_MISSILES accordingly
private constant function MissileNumber takes integer lvl returns integer
return 8
endfunction
// How long it should take for the missile to reach the wrath
private constant function MissileTime takes integer lvl returns real
return 2.5
endfunction
// Damage done to units every second for MissileTime seconds
private constant function PeriodicDamage takes integer lvl returns real
return 10. + 10*lvl
endfunction
// Damage done to enemy units MissileTime seconds after the wrath has been summoned
private constant function DelayedDamage takes integer lvl returns real
return 100. + 75*lvl
endfunction
//==========================================================================================
// END OF CONFIGURATION
//==========================================================================================
globals
private Table info // Passes info for spell channeling purposes
endglobals
private struct Data
unit cast // Caster of the spell
unit wrath // Wrath dummy unit
integer lvl // The level of the ability of the caster when it was casted
integer missileNum // Number of missiles created
real x // The x of the spell target point
real y // The y of the spell target point
real area // spell target area
real missileTime // How long the missiles will take to ascend
real percent // Stores ratio of how long the wrath was summoned to CastTime
real count = 0 // Count duration for the spell
real animCount = 0 // A more specific count that determines when the wrath should play ANIM_REPEAT
real ang = 0 // Angle for the missiles to rotate around the wrath
timer t // Main timer used by the spell to carry out actions
xefx array fx[MAX_MISSILES] // Stores missiles created
static thistype temp // Passes information from a struct to the filter method
static real Total // Stores total amount of damage done by the delayed damage for the texttag
// Cleans up the final parts of the spell
static method timedDestroy takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
static if RECYCLE_DUMMY then
call ShowUnit(.wrath, false)
else
call RemoveUnit(.wrath)
endif
call ReleaseTimer(.t)
call .destroy()
endmethod
// Damages units by the delayed portion of the spell
static method dealDelayedDmg takes nothing returns boolean
local unit u = GetFilterUnit()
local real life
if AffectedTargets(u, GetOwningPlayer(temp.cast)) then
set life = GetWidgetLife(u)
call DestroyEffect(AddSpecialEffectTarget(DMG_SFX, u, DMG_ATTACH))
call UnitDamageTarget(temp.cast, u, DelayedDamage(temp.lvl)*temp.percent, false, true, ATK_TYPE, DMG_TYPE, WPN_TYPE)
set Total = Total + life - GetWidgetLife(u)
endif
set u = null
return false
endmethod
// Damages units in the periodic portion of the spell
static method dealPeriodicDamage takes nothing returns boolean
local unit u = GetFilterUnit()
if AffectedTargets(u, GetOwningPlayer(temp.cast)) then
// Play sfx about the same time the wrath plays its animation
if temp.animCount == 0 then
call DestroyEffect(AddSpecialEffectTarget(DMG_TIME_SFX, u, DMG_TIME_ATTACH))
endif
call UnitDamageTarget(temp.cast, u, PeriodicDamage(temp.lvl)*temp.percent*TIMER_LOOP, false, true, ATK_TYPE, DMG_TYPE, WPN_TYPE)
endif
set u = null
return false
endmethod
// Takes care of most of the spell's bulk:
// - playing the wrath's animation
// - setting the missiles
// - dealing damage over time
// - dealing delayed damage
static method onEffectLoop takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
local integer i = 0
local real offset
local texttag t
set temp = this // For group enumeration
if .count + TIMER_LOOP < .missileTime then
set .count = .count + TIMER_LOOP
static if LIBRARY_GroupUtils then
call GroupEnumUnitsInArea(ENUM_GROUP, .x, .y, .area, Condition(function thistype.dealPeriodicDamage))
else
call GroupEnumUnitsInRange(bj_lastCreatedGroup, .x, .y, .area, Condition(function thistype.dealPeriodicDamage))
endif
// Done this way so wrath will play its animation immediately after being summoned
if .animCount == 0 then
call SetUnitAnimation(.wrath,ANIM_REPEAT)
endif
set .animCount = .animCount + TIMER_LOOP
if .animCount >= ANIM_REPEAT_INT then
set .animCount = 0
endif
// Missile movement
set .ang = .ang + MISSILE_ROTATE * TIMER_LOOP
set offset = .area - .area / .missileTime * .count
loop
set .fx[i].x = .x + offset * Cos(.ang + bj_PI * 2 / .missileNum * i)
set .fx[i].y = .y + offset * Sin(.ang + bj_PI * 2 / .missileNum * i)
set .fx[i].z = (HEIGHT_INIT + (HEIGHT_FIN - HEIGHT_INIT) * .percent) * .count / .missileTime
set i = i + 1
exitwhen i == MissileNumber(.lvl)
endloop
else
call DestroyEffect(AddSpecialEffect(POINT_SFX, .x, .y))
set Total = 0
static if LIBRARY_GroupUtils then
call GroupEnumUnitsInArea(ENUM_GROUP, .x, .y, .area, Condition(function thistype.dealDelayedDmg))
else
call GroupEnumUnitsInRange(bj_lastCreatedGroup, .x, .y, .area, Condition(function thistype.dealDelayedDmg))
endif
// Create a texttag if damage was dealt
if Total > 0 then
set t = CreateTextTag()
call SetTextTagPos(t, .x, .y, HEIGHT_INIT + (HEIGHT_FIN - HEIGHT_INIT) * .percent)
call SetTextTagText(t, "-"+I2S(R2I(Total))+"!", FONT_SIZE)
call SetTextTagColor(t, TEXT_R, TEXT_G, TEXT_B, TEXT_A)
call SetTextTagVelocity(t, 0, TEXT_VELOCITY)
call SetTextTagFadepoint(t, TEXT_FADE)
call SetTextTagLifespan(t, TEXT_LIFE)
call SetTextTagPermanent(t, false)
set t = null
endif
// Clean up the missiles
loop
call .fx[i].destroy()
set i = i + 1
exitwhen i == .missileNum
endloop
// Allow wrath to play ANIM_FIN before getting removed
call SetUnitAnimation(.wrath, ANIM_FIN)
call TimerStart(.t, WRATH_EXPIRE, false, function thistype.timedDestroy)
endif
endmethod
// Takes effect when the caster stops channeling the spell for any reason and then starts the effects
static method spellStop takes nothing returns boolean
local thistype this
local integer i = 0
local integer alpha
local real mx
local real my
local real scale
if GetSpellAbilityId() == SPELL_ID then
set this = info[GetHandleId(GetTriggerUnit())]
set .percent = .count / CastTime(.lvl)
// For precision, since Jass is stupid
if .percent == 1. then
set .percent = 1
endif
set alpha = R2I(ALPHA_INIT + (ALPHA_FIN-ALPHA_INIT)*.percent)
set scale = SCALE_INIT + (SCALE_FIN-SCALE_INIT) * .percent
loop
set mx = .x + .area*Cos(bj_PI * 2 / missileNum*i)
set my = .y + .area*Sin(bj_PI * 2 / missileNum*i)
set .fx[i] = xefx.create(mx, my, 0)
set .fx[i].fxpath = MISSILE_SFX
call .fx[i].recolor(RED, GREEN, BLUE, alpha)
set .fx[i].scale = scale
set i = i + 1
exitwhen i == .missileNum
endloop
set .count = 0
call TimerStart(.t, TIMER_LOOP, true, function thistype.onEffectLoop)
call info.remove(GetHandleId(.cast)) // No longer need to store this info since spell channeling is done
endif
return false
endmethod
// Sets the visuals of the wrath as it's being summoned
static method onChannel takes nothing returns nothing
local thistype this = thistype(GetTimerData(GetExpiredTimer()))
set .count = .count + TIMER_LOOP
set .percent = .count / CastTime(.lvl)
call SetUnitScale(.wrath, SCALE_INIT +(SCALE_FIN-SCALE_INIT) * .percent, 0, 0)
call SetUnitVertexColor(.wrath, RED, GREEN, BLUE, R2I(ALPHA_INIT + (ALPHA_FIN-ALPHA_INIT) * .percent))
call SetUnitFlyHeight(.wrath, HEIGHT_INIT + (HEIGHT_FIN-HEIGHT_INIT) * .percent, 0)
endmethod
static method startSpell takes unit c, real x, real y returns nothing
local thistype this = thistype.allocate()
local real ang
set .cast = c
set .x = x
set .y = y
set .lvl = GetUnitAbilityLevel(.cast, SPELL_ID)
// Variables that are set as they're used a lot in calculations
set .area = Area(.lvl)
set .missileNum = MissileNumber(.lvl)
set .missileTime = MissileTime(.lvl)
// Ensures that the ghost will have a proper facing angle if the spell is cast directly on the caster
if y - GetUnitY(c) > 0 and x - GetUnitX(c) > 0 then
set ang = bj_RADTODEG*Atan2(y - GetUnitY(c), x - GetUnitX(c))
else
set ang = GetUnitFacing(c)
endif
// Wrath settings
static if RECYCLE_DUMMY then
if .wrath == null then
set .wrath = CreateUnit(Player(15), DUMMY_ID, x, y, ang)
else
call SetUnitX(.wrath, x)
call SetUnitY(.wrath, y)
call SetUnitFacing(.wrath, ang)
call ShowUnit(.wrath, true)
call UnitRemoveAbility(.wrath, 'Aloc')
call UnitAddAbility(.wrath, 'Aloc')
endif
else
set .wrath = CreateUnit(Player(15), DUMMY_ID, x, y, ang)
endif
call SetUnitScale(.wrath, SCALE_INIT, 0, 0)
call SetUnitVertexColor(.wrath, RED, GREEN, BLUE, ALPHA_INIT)
call SetUnitFlyHeight(.wrath, HEIGHT_INIT, 0)
call SetUnitAnimationByIndex(.wrath, ANIM_SUMMON)
set info[GetHandleId(.cast)] = this
set .t = NewTimer()
call SetTimerData(.t, this)
call TimerStart(.t, TIMER_LOOP, true, function thistype.onChannel)
endmethod
static method spellActions takes nothing returns boolean
if GetSpellAbilityId() == SPELL_ID then
call thistype.startSpell(GetTriggerUnit(), GetSpellTargetX(), GetSpellTargetY())
endif
return false
endmethod
static method onInit takes nothing returns nothing
local trigger t = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(t, Condition(function thistype.spellActions))
set t = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_ENDCAST)
call TriggerAddCondition(t, Condition(function thistype.spellStop))
set info = Table.create()
// Preloading
static if PRELOAD then
call RemoveUnit(CreateUnit(Player(15),DUMMY_ID,0,0,0))
call Preload(MISSILE_SFX)
call Preload(DMG_SFX)
call Preload(DMG_TIME_SFX)
call Preload(POINT_SFX)
endif
set t = null
endmethod
endstruct
endscope