Name | Type | is_array | initial_value |
hero | unit | No |
//TESH.scrollpos=270
//TESH.alwaysfold=0
library EnergyFissure /*
****************************************************************************************************
*
* E N E R G Y F I S S U R E
*
* ~ version 2.0.2
* ~ coded by Mr_Bean
* ~ idea by noobieatmaps
*
****************************************************************************************************
*
* */ requires /*
* ¯¯¯¯¯¯¯¯
* */ SpellEffectEvent /* http://www.hiveworkshop.com/forums/jass-resources-412/snippet-spelleffectevent-187193
* */ T32 /* http://www.thehelper.net/threads/timer32.118245/
* */ TimerUtils /* http://www.wc3c.net/showthread.php?t=101322
* */ xefx /* http://www.wc3c.net/showthread.php?t=101150
*
****************************************************************************************************
*
* Installation
* ¯¯¯¯¯¯¯¯¯¯¯¯
* - Make sure you've got (and installed properly) the above systems.
* - Copy the "Energy Fissure" hero ability. Update SPELL_ID to its raw ID.
* - Go through the configurables section below and change what you want to.
*
****************************************************************************************************
*
* Configuration
* ¯¯¯¯¯¯¯¯¯¯¯¯¯
*/
globals
//==================================================
//=== RAW IDS
private constant integer SPELL_ID = 'A000' // Raw ID of the ability.
//==================================================
//=== VOID CONFIGURATION
private constant real VOID_HEIGHT = 100.0 // Height of void.
private constant real VOID_VISION = 300.0 // Vision area you get of the void. Set to 0.0 to disable.
private constant real DESTROY_DELAY = 2.0 // Delay between units being zapped and the void being removed.
// Model of void:
private constant string VOID_EFFECT = "DarkVoid.mdx"
//==================================================
//=== WAVE CONFIGURATION
private constant integer NUM_WAVES = 8 // Number of waves created.
private constant real WAVE_DELAY = 1.0 // Delay between void created and waves created.
private constant real WAVE_SPEED = 300.0 // Speed of waves.
// Model of waves:
private constant string WAVE_EFFECT = "DeathWave.mdx"
//==================================================
//=== LIGHTNING CONFIGURATION
private constant real BOLT_DURATION = 0.5 // Duration of lightning.
private constant real BOLT_HEIGHT = 50.0 // Height of lightning.
private constant string BOLT_CODE = "MBUR" // Lightning code.
// Effect created on zapped units:
private constant string BOLT_EFFECT = "Abilities\\Spells\\NightElf\\ManaBurn\\ManaBurnTarget.mdl"
// Attachment point for above effect:
private constant string BOLT_FX_ATTACH = "origin"
//==================================================
//=== FLOATING TEXT CONFIGURATION
private constant boolean DISPLAY_TEXT = true // Display floating text showing damage dealt?
private constant real TEXT_ANGLE = 90.0 // Movement angle.
private constant real TEXT_SPEED = 64.0 // Movement speed.
private constant real TEXT_OFFSET = 50.0 // Height above target.
private constant real TEXT_DURATION = 2.0 // Duration.
private constant real TEXT_FADEPOINT = 1.0 // Fade time.
private constant real TEXT_HEIGHT = 0.023 // Size.
// Colour:
private constant string TEXT_COLOUR = "|cff8000FF"
endglobals
//==================================================
//=== DISTANCE OF WAVES PER LEVEL
private function GetWaveDistance takes integer level returns real
return (100.0 * level) + 200.0
endfunction
//==================================================
//=== MANA BURNED PER LEVEL
private function GetManaBurned takes integer level returns real
return 100.0 * level
endfunction
//==================================================
//=== DAMAGE FUNCTION SETUP
// This is if you want to change attack/damage types or if you use a custom
// function for damage detection or something similar.
private function DealDamage takes unit source, unit target, real amount returns nothing
call UnitDamageTarget(source, target, amount, false, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_NORMAL, null)
endfunction
/***************************************************************************************************
*
* END OF CONFIGURATION AND DOCUMENTATION
*
***************************************************************************************************/
private struct ManaBolt
static constant real TEXT_X_VELOCITY = (Cos(TEXT_ANGLE * bj_DEGTORAD) * TEXT_SPEED) * 0.071 / 128.0
static constant real TEXT_Y_VELOCITY = (Sin(TEXT_ANGLE * bj_DEGTORAD) * TEXT_SPEED) * 0.071 / 128.0
//-----
static location loc = Location(0.0, 0.0)
//-----
unit source
unit target
lightning bolt
real sx
real sy
real sz
real time = BOLT_DURATION
real manaBurn
//-----
/**
* Destroys the lightning and cleans up.
*/
private method destroy takes nothing returns nothing
call DestroyLightning(bolt)
set source = null
set target = null
set bolt = null
call deallocate()
endmethod
static if (DISPLAY_TEXT) then
/**
* Creates text above the target showing the damage dealt.
* Only created if DISPLAY_TEXT is true.
*/
private method displayDamage takes real amount returns nothing
local texttag tt
if (IsVisibleToPlayer(GetUnitX(target), GetUnitY(target), GetLocalPlayer())) then
set tt = CreateTextTag()
call SetTextTagText(tt, TEXT_COLOUR + "-" + I2S(R2I(amount)) + "|r", TEXT_HEIGHT)
call SetTextTagPosUnit(tt, target, TEXT_OFFSET)
call SetTextTagVelocity(tt, TEXT_X_VELOCITY, TEXT_Y_VELOCITY)
call SetTextTagPermanent(tt, false)
call SetTextTagLifespan(tt, TEXT_DURATION)
call SetTextTagFadepoint(tt, TEXT_FADEPOINT)
call SetTextTagVisibility(tt, true)
set tt = null
endif
endmethod
endif
/**
* Drains the target's mana (if it has) and deals damage accordingly.
* Displays text if it's enabled.
*/
private method zap takes nothing returns nothing
local real curr
local real damage = 0.0
if (GetWidgetLife(target) > 0.405) then
set curr = GetUnitState(target, UNIT_STATE_MANA)
if (curr >= manaBurn) then
call SetUnitState(target, UNIT_STATE_MANA, curr - manaBurn)
set damage = manaBurn
elseif (curr > 0.0) then
call SetUnitState(target, UNIT_STATE_MANA, 0.0)
set damage = curr
endif
if (damage != 0.0) then
call DealDamage(source, target, damage)
endif
static if (DISPLAY_TEXT) then
call displayDamage(damage)
endif
endif
endmethod
/**
* Moves the lightning. If the duration expires, zaps the target and
* destroys the bolt.
*/
private method periodic takes nothing returns nothing
call MoveLocation(loc, GetUnitX(target), GetUnitY(target))
call MoveLightningEx(bolt, true, sx, sy, BOLT_HEIGHT + GetLocationZ(loc), /*
*/ GetUnitX(target), GetUnitY(target), BOLT_HEIGHT)
set time = time - T32_PERIOD
if (time <= 0.0) then
call stopPeriodic()
call zap()
call destroy()
endif
endmethod
implement T32x
/**
* Creates a new instance. Creates BOLT_EFFECT on the target and starts
* counting down until the damage is dealt.
*/
static method create takes unit s, real x, real y, unit t, real amt returns thistype
local thistype this = allocate()
set source = s
set target = t
set sx = x
set sy = y
call MoveLocation(loc, sx, sy)
set sz = BOLT_HEIGHT + GetLocationZ(loc)
set manaBurn = amt
set bolt = AddLightningEx(BOLT_CODE, true, sx, sy, sz, GetUnitX(target), GetUnitY(target), BOLT_HEIGHT)
call DestroyEffect(AddSpecialEffectTarget(BOLT_EFFECT, target, BOLT_FX_ATTACH))
call startPeriodic()
return this
endmethod
endstruct
private struct EnergyFissure
static constant real ANGLE_DIV = (2.0 * bj_PI) / NUM_WAVES
static constant real INCREMENT = WAVE_SPEED * T32_PERIOD
//-----
static real array X_VELOCITY[NUM_WAVES]
static real array Y_VELOCITY[NUM_WAVES]
static group enumG = CreateGroup()
static unit temp
//-----
unit caster
player owner
timer tim
real tx
real ty
real time
real aoe
real manaBurn
xefx pointFx
fogmodifier fog
//-----
xefx array waves[NUM_WAVES]
//-----
/**
* Destroys the void and each wave. Cleans up then deallocates.
*/
private method destroy takes nothing returns nothing
if (VOID_VISION > 0.0) then
call DestroyFogModifier(fog)
set fog = null
endif
call pointFx.destroy()
set caster = null
set owner = null
set tim = null
call deallocate()
endmethod
private static method cleanUp takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
call ReleaseTimer(GetExpiredTimer())
call destroy()
endmethod
/**
* The filter applied while enumerating units in range.
*/
private method filterUnit takes unit u returns boolean
return IsUnitEnemy(u, owner) /* Enemy of caster.
*/ and not IsUnitType(u, UNIT_TYPE_DEAD) /* Alive.
*/ and GetUnitTypeId(u) != 0 /* Still alive.
*/ and not IsUnitType(u, UNIT_TYPE_MAGIC_IMMUNE) /* Not magic immune.
*/ and not IsUnitType(u, UNIT_TYPE_STRUCTURE) /* Not a structure. */
endmethod
/**
* Zaps all units in range of the void, burning their mana and
* dealing damage equal to the amount burned.
*/
private method zapUnits takes nothing returns nothing
// First, destroy wave effects:
local integer i = 0
loop
exitwhen i == NUM_WAVES
call waves[i].destroy()
set i = i + 1
endloop
// Then zap units:
call GroupEnumUnitsInRange(enumG, tx, ty, aoe, null)
loop
set temp = FirstOfGroup(enumG)
exitwhen temp == null
call GroupRemoveUnit(enumG, temp)
if (filterUnit(temp)) then
call ManaBolt.create(caster, pointFx.x, pointFx.y, temp, manaBurn)
endif
endloop
endmethod
/**
* Moves each wave. If they reach their max distance, units within
* range get zapped, and the spell ends.
*/
private method periodic takes nothing returns nothing
local integer i = 0
loop
exitwhen i == NUM_WAVES
set waves[i].x = waves[i].x + X_VELOCITY[i]
set waves[i].y = waves[i].y + Y_VELOCITY[i]
set i = i + 1
endloop
set time = time - T32_PERIOD
if (time <= 0.0) then
call stopPeriodic()
call zapUnits()
call TimerStart(tim, DESTROY_DELAY, false, function thistype.cleanUp)
endif
endmethod
implement T32x
/**
* Creates each wave effect. Also starts moving them.
*/
private static method createPulses takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
local integer i = 0
loop
exitwhen i == NUM_WAVES
set waves[i] = xefx.create(tx, ty, i * ANGLE_DIV)
set waves[i].fxpath = WAVE_EFFECT
set i = i + 1
endloop
call startPeriodic()
endmethod
/**
* Creates a new instance. Creates the void effect at the target point
* and waits before creating the waves.
*/
private static method create takes nothing returns thistype
local thistype this = allocate()
local integer level
set caster = GetTriggerUnit()
set owner = GetTriggerPlayer()
set tx = GetSpellTargetX()
set ty = GetSpellTargetY()
set level = GetUnitAbilityLevel(caster, SPELL_ID)
set manaBurn = GetManaBurned(level)
set aoe = GetWaveDistance(level)
set time = aoe / WAVE_SPEED
set pointFx = xefx.create(tx, ty, 0.0)
set pointFx.z = VOID_HEIGHT
set pointFx.fxpath = VOID_EFFECT
if (VOID_VISION > 0.0) then
set fog = CreateFogModifierRadius(owner, FOG_OF_WAR_VISIBLE, tx, ty, VOID_VISION, true, false)
call FogModifierStart(fog)
endif
set tim = NewTimerEx(this)
call TimerStart(tim, WAVE_DELAY, false, function thistype.createPulses)
return this
endmethod
/**
* Stores the x and y velocities for each wave. Registers the spell
* and preloads the effects.
*/
private static method onInit takes nothing returns nothing
local integer i = 0
loop
exitwhen i == NUM_WAVES
set X_VELOCITY[i] = INCREMENT * Cos(i * ANGLE_DIV)
set Y_VELOCITY[i] = INCREMENT * Sin(i * ANGLE_DIV)
set i = i + 1
endloop
call RegisterSpellEffectEvent(SPELL_ID, function thistype.create)
call Preload(VOID_EFFECT)
call Preload(WAVE_EFFECT)
call Preload(BOLT_EFFECT)
endmethod
endstruct
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
/**************************************************************
*
* RegisterPlayerUnitEvent
* v4.2.0.0
* By Magtheridon96
*
* I would like to give a special thanks to Bribe, azlier
* and BBQ for improving this library. For modularity, it only
* supports player unit events.
*
* Functions passed to RegisterPlayerUnitEvent must
* return false. They can return nothing as well.
*
* Disclaimer:
* -----------
*
* - Don't use TriggerSleepAction inside registered code.
*
* API:
* ----
*
* function RegisterPlayerUnitEvent
* takes
* playerunitevent whichEvent : The event you would like to register
* code whichFunction : The code you would like to register
* returns
* nothing
*
* - Registers code that will execute when an event fires.
*
**************************************************************/
library RegisterPlayerUnitEvent // Special Thanks to Bribe and azlier
globals
private trigger array t
endglobals
function RegisterPlayerUnitEvent takes playerunitevent p, code c returns nothing
local integer i = GetHandleId(p)
local integer k = 15
if t[i] == null then
set t[i] = CreateTrigger()
loop
call TriggerRegisterPlayerUnitEvent(t[i], Player(k), p, null)
exitwhen k == 0
set k = k - 1
endloop
endif
call TriggerAddCondition(t[i], Filter(c))
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
//============================================================================
// SpellEffectEvent by Bribe
// - Version 1.1.0.0
//
// API
// ---
// RegisterSpellEffectEvent(integer abil, code onCast)
//
// Requires
// --------
// RegisterPlayerUnitEvent: hiveworkshop.com/forums/showthread.php?t=203338
//
// Optional
// --------
// Table: hiveworkshop.com/forums/showthread.php?t=188084
//
library SpellEffectEvent requires RegisterPlayerUnitEvent, optional Table
//============================================================================
private module M
static if LIBRARY_Table then
static Table tb
else
static hashtable ht = InitHashtable()
endif
static method onCast takes nothing returns nothing
static if LIBRARY_Table then
call TriggerEvaluate(.tb.trigger[GetSpellAbilityId()])
else
call TriggerEvaluate(LoadTriggerHandle(.ht, 0, GetSpellAbilityId()))
endif
endmethod
private static method onInit takes nothing returns nothing
static if LIBRARY_Table then
set .tb = Table.create()
endif
call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_SPELL_EFFECT, function thistype.onCast)
endmethod
endmodule
//============================================================================
private struct S extends array
implement M
endstruct
//============================================================================
function RegisterSpellEffectEvent takes integer abil, code onCast returns nothing
static if LIBRARY_Table then
if not S.tb.handle.has(abil) then
set S.tb.trigger[abil] = CreateTrigger()
endif
call TriggerAddCondition(S.tb.trigger[abil], Filter(onCast))
else
if not HaveSavedHandle(S.ht, 0, abil) then
call SaveTriggerHandle(S.ht, 0, abil, CreateTrigger())
endif
call TriggerAddCondition(LoadTriggerHandle(S.ht, 0, abil), Filter(onCast))
endif
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//~~ Timer32 ~~ By Jesus4Lyf ~~ Version 1.06 ~~
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// What is Timer32?
// - Timer32 implements a fully optimised timer loop for a struct.
// - Instances can be added to the loop, which will call .periodic every
// PERIOD until .stopPeriodic() is called.
//
// =Pros=
// - Efficient.
// - Simple.
//
// =Cons=
// - Only allows one period.
// - The called method must be named ".periodic".
//
// Methods:
// - struct.startPeriodic()
// - struct.stopPeriodic()
//
// - private method periodic takes nothing returns nothing
//
// This must be defined in structs that implement Periodic Module.
// It will be executed by the module every PERIOD until .stopPeriodic() is called.
// Put "implement T32x" BELOW this method.
//
// Modules:
// - T32x
// Has no safety on .stopPeriodic or .startPeriodic (except debug messages
// to warn).
//
// - T32xs
// Has safety on .stopPeriodic and .startPeriodic so if they are called
// multiple times, or while otherwise are already stopped/started respectively,
// no error will occur, the call will be ignored.
//
// - T32
// The original, old version of the T32 module. This remains for backwards
// compatability, and is deprecated. The periodic method must return a boolean,
// false to continue running or true to stop.
//
// Details:
// - Uses one timer.
//
// - Do not, within a .periodic method, follow a .stopPeriodic call with a
// .startPeriodic call.
//
// How to import:
// - Create a trigger named T32.
// - Convert it to custom text and replace the whole trigger text with this.
//
// Thanks:
// - Infinitegde for finding a bug in the debug message that actually altered
// system operation (when in debug mode).
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
library T32 initializer OnInit
globals
public constant real PERIOD=0.03125
public constant integer FPS=R2I(1/PERIOD)
public integer Tick=0 // very useful.
//==============================================================================
private trigger Trig=CreateTrigger()
endglobals
//==============================================================================
// The standard T32 module, T32x.
//
module T32x
private thistype next
private thistype prev
private static method PeriodicLoop takes nothing returns boolean
local thistype this=thistype(0).next
loop
exitwhen this==0
call this.periodic()
set this=this.next
endloop
return false
endmethod
method startPeriodic takes nothing returns nothing
debug if this.prev!=0 or thistype(0).next==this then
debug call BJDebugMsg("T32 ERROR: Struct #"+I2S(this)+" had startPeriodic called while already running!")
debug endif
set thistype(0).next.prev=this
set this.next=thistype(0).next
set thistype(0).next=this
set this.prev=thistype(0)
endmethod
method stopPeriodic takes nothing returns nothing
debug if this.prev==0 and thistype(0).next!=this then
debug call BJDebugMsg("T32 ERROR: Struct #"+I2S(this)+" had stopPeriodic called while not running!")
debug endif
// This is some real magic.
set this.prev.next=this.next
set this.next.prev=this.prev
// This will even work for the starting element.
debug set this.prev=0
endmethod
private static method onInit takes nothing returns nothing
call TriggerAddCondition(Trig,Condition(function thistype.PeriodicLoop))
endmethod
endmodule
//==============================================================================
// The standard T32 module with added safety checks on .startPeriodic() and
// .stopPeriodic(), T32xs.
//
module T32xs
private thistype next
private thistype prev
private boolean runningPeriodic
private static method PeriodicLoop takes nothing returns boolean
local thistype this=thistype(0).next
loop
exitwhen this==0
call this.periodic()
set this=this.next
endloop
return false
endmethod
method startPeriodic takes nothing returns nothing
if not this.runningPeriodic then
set thistype(0).next.prev=this
set this.next=thistype(0).next
set thistype(0).next=this
set this.prev=thistype(0)
set this.runningPeriodic=true
endif
endmethod
method stopPeriodic takes nothing returns nothing
if this.runningPeriodic then
// This is some real magic.
set this.prev.next=this.next
set this.next.prev=this.prev
// This will even work for the starting element.
set this.runningPeriodic=false
endif
endmethod
private static method onInit takes nothing returns nothing
call TriggerAddCondition(Trig,Condition(function thistype.PeriodicLoop))
endmethod
endmodule
//==============================================================================
// The original T32 module, for backwards compatability only.
//
module T32 // deprecated.
private thistype next
private thistype prev
private static method PeriodicLoop takes nothing returns boolean
local thistype this=thistype(0).next
loop
exitwhen this==0
if this.periodic() then
// This is some real magic.
set this.prev.next=this.next
set this.next.prev=this.prev
// This will even work for the starting element.
debug set this.prev=0
endif
set this=this.next
endloop
return false
endmethod
method startPeriodic takes nothing returns nothing
debug if this.prev!=0 or thistype(0).next==this then
debug call BJDebugMsg("T32 ERROR: Struct #"+I2S(this)+" had startPeriodic called while already running!")
debug endif
set thistype(0).next.prev=this
set this.next=thistype(0).next
set thistype(0).next=this
set this.prev=thistype(0)
endmethod
private static method onInit takes nothing returns nothing
call TriggerAddCondition(Trig,Condition(function thistype.PeriodicLoop))
endmethod
endmodule
//==============================================================================
// System Core.
//
private function OnExpire takes nothing returns nothing
set Tick=Tick+1
call TriggerEvaluate(Trig)
endfunction
private function OnInit takes nothing returns nothing
call TimerStart(CreateTimer(),PERIOD,true,function OnExpire)
endfunction
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 = true
private constant boolean USE_FLEXIBLE_OFFSET = false
private constant integer OFFSET = 0x100000
private integer VOFFSET = OFFSET
//Timers to preload at map init:
private constant integer QUANTITY = 256
//Changing this to something big will allow you to keep recycling
// timers even when there are already AN INCREDIBLE AMOUNT of timers in
// the stack. But it will make things far slower so that's probably a bad idea...
private constant integer ARRAY_SIZE = 8190
endglobals
//==================================================================================================
globals
private integer array data[ARRAY_SIZE]
private hashtable ht
endglobals
//It is dependent on jasshelper's recent inlining optimization in order to perform correctly.
function SetTimerData takes timer t, integer value returns nothing
static if(USE_HASH_TABLE) then
// new blue
call SaveInteger(ht,0,GetHandleId(t), value)
elseif (USE_FLEXIBLE_OFFSET) then
// orange
static if (DEBUG_MODE) then
if(GetHandleId(t)-VOFFSET<0) then
call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
endif
endif
set data[GetHandleId(t)-VOFFSET]=value
else
// new red
static if (DEBUG_MODE) then
if(GetHandleId(t)-OFFSET<0) then
call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
endif
endif
set data[GetHandleId(t)-OFFSET]=value
endif
endfunction
function GetTimerData takes timer t returns integer
static if(USE_HASH_TABLE) then
// new blue
return LoadInteger(ht,0,GetHandleId(t) )
elseif (USE_FLEXIBLE_OFFSET) then
// orange
static if (DEBUG_MODE) then
if(GetHandleId(t)-VOFFSET<0) then
call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
endif
endif
return data[GetHandleId(t)-VOFFSET]
else
// new red
static if (DEBUG_MODE) then
if(GetHandleId(t)-OFFSET<0) then
call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
endif
endif
return data[GetHandleId(t)-OFFSET]
endif
endfunction
//==========================================================================================
globals
private timer array tT[ARRAY_SIZE]
private integer tN = 0
private constant integer HELD=0x28829022
//use a totally random number here, the more improbable someone uses it, the better.
private boolean didinit = false
endglobals
private keyword init
//==========================================================================================
// I needed to decide between duplicating code ignoring the "Once and only once" rule
// and using the ugly textmacros. I guess textmacros won.
//
//! textmacro TIMERUTIS_PRIVATE_NewTimerCommon takes VALUE
// On second thought, no.
//! endtextmacro
function NewTimerEx takes integer value returns timer
if (tN==0) then
if (not didinit) then
//This extra if shouldn't represent a major performance drawback
//because QUANTITY rule is not supposed to be broken every day.
call init.evaluate()
set tN = tN - 1
else
//If this happens then the QUANTITY rule has already been broken, try to fix the
// issue, else fail.
debug call BJDebugMsg("NewTimer: Warning, Exceeding TimerUtils_QUANTITY, make sure all timers are getting recycled correctly")
set tT[0]=CreateTimer()
static if( not USE_HASH_TABLE) then
debug call BJDebugMsg("In case of errors, please increase it accordingly, or set TimerUtils_USE_HASH_TABLE to true")
static if( USE_FLEXIBLE_OFFSET) then
if (GetHandleId(tT[0])-VOFFSET<0) or (GetHandleId(tT[0])-VOFFSET>=ARRAY_SIZE) then
//all right, couldn't fix it
call BJDebugMsg("NewTimer: Unable to allocate a timer, you should probably set TimerUtils_USE_HASH_TABLE to true or fix timer leaks.")
return null
endif
else
if (GetHandleId(tT[0])-OFFSET<0) or (GetHandleId(tT[0])-OFFSET>=ARRAY_SIZE) then
//all right, couldn't fix it
call BJDebugMsg("NewTimer: Unable to allocate a timer, you should probably set TimerUtils_USE_HASH_TABLE to true or fix timer leaks.")
return null
endif
endif
endif
endif
else
set tN=tN-1
endif
call SetTimerData(tT[tN],value)
return tT[tN]
endfunction
function NewTimer takes nothing returns timer
return NewTimerEx(0)
endfunction
//==========================================================================================
function ReleaseTimer takes timer t returns nothing
if(t==null) then
debug call BJDebugMsg("Warning: attempt to release a null timer")
return
endif
if (tN==ARRAY_SIZE) then
debug call BJDebugMsg("Warning: Timer stack is full, destroying timer!!")
//stack is full, the map already has much more troubles than the chance of bug
call DestroyTimer(t)
else
call PauseTimer(t)
if(GetTimerData(t)==HELD) then
debug call BJDebugMsg("Warning: ReleaseTimer: Double free!")
return
endif
call SetTimerData(t,HELD)
set tT[tN]=t
set tN=tN+1
endif
endfunction
private function init takes nothing returns nothing
local integer i=0
local integer o=-1
local boolean oops = false
if ( didinit ) then
return
else
set didinit = true
endif
static if( USE_HASH_TABLE ) then
set ht = InitHashtable()
loop
exitwhen(i==QUANTITY)
set tT[i]=CreateTimer()
call SetTimerData(tT[i], HELD)
set i=i+1
endloop
set tN = QUANTITY
else
loop
set i=0
loop
exitwhen (i==QUANTITY)
set tT[i] = CreateTimer()
if(i==0) then
set VOFFSET = GetHandleId(tT[i])
static if(USE_FLEXIBLE_OFFSET) then
set o=VOFFSET
else
set o=OFFSET
endif
endif
if (GetHandleId(tT[i])-o>=ARRAY_SIZE) then
exitwhen true
endif
if (GetHandleId(tT[i])-o>=0) then
set i=i+1
endif
endloop
set tN = i
exitwhen(tN == QUANTITY)
set oops = true
exitwhen not USE_FLEXIBLE_OFFSET
debug call BJDebugMsg("TimerUtils_init: Failed a initialization attempt, will try again")
endloop
if(oops) then
static if ( USE_FLEXIBLE_OFFSET) then
debug call BJDebugMsg("The problem has been fixed.")
//If this message doesn't appear then there is so much
//handle id fragmentation that it was impossible to preload
//so many timers and the thread crashed! Therefore this
//debug message is useful.
elseif(DEBUG_MODE) then
call BJDebugMsg("There were problems and the new timer limit is "+I2S(i))
call BJDebugMsg("This is a rare ocurrence, if the timer limit is too low:")
call BJDebugMsg("a) Change USE_FLEXIBLE_OFFSET to true (reduces performance a little)")
call BJDebugMsg("b) or try changing OFFSET to "+I2S(VOFFSET) )
endif
endif
endif
endfunction
endlibrary
//TESH.scrollpos=0
//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=0
//TESH.alwaysfold=0
library xefx initializer init requires xebasic, optional xedummy
//**************************************************
// xefx 0.9
// --------
// Recommended: ARGB (adds ARGBrecolor method)
// For your movable fx needs
//
//**************************************************
//==================================================
globals
private constant integer MAX_INSTANCES = 8190 //change accordingly.
//Delay in order to show the death animation of the effect correctly when xefx is destroyed.
//You may need to increase this if you are using effects with longer death animations.
private constant real MIN_RECYCLE_DELAY = 4.0
//The delay does not need to be exact so we do cleanup in batches instead of individually.
//This determines how often the recycler runs, should be less than MIN_RECYCLE_DELAY.
private constant real RECYCLE_INTERVAL = 0.5
//if this is true and the xedummy library is present, units will be recycled instead of removed.
private constant boolean RECYCLE_DUMMY_UNITS = true
private timer recycler
endglobals
private struct recyclebin
unit u
private recyclebin next=0
private static recyclebin array list
private static integer readindex=1
private static integer writeindex=0
private static integer count=0
static method Recycle takes nothing returns nothing
local recyclebin this = .list[readindex]
loop
exitwhen this==0
static if RECYCLE_DUMMY_UNITS and LIBRARY_xedummy then
call XE_ReleaseDummyUnit(this.u)
else
call RemoveUnit(this.u)
endif
set this.u=null
set .count=.count-1
call this.destroy()
set this=this.next
endloop
set .list[readindex]=0
set .writeindex=.readindex
set .readindex=.readindex+1
if .readindex>R2I(MIN_RECYCLE_DELAY/RECYCLE_INTERVAL+1.0) then
set .readindex=0
endif
if count!=0 then
call TimerStart(recycler, RECYCLE_INTERVAL, false, function recyclebin.Recycle)
endif
endmethod
static method create takes unit u returns recyclebin
local recyclebin this=recyclebin.allocate()
if .count==0 then
call TimerStart(recycler, RECYCLE_INTERVAL, false, function recyclebin.Recycle)
endif
set .count=.count+1
set .u=u
call SetUnitOwner(u,Player(15),false)
set .next=.list[.writeindex]
set .list[.writeindex]=this
return this
endmethod
endstruct
private function init takes nothing returns nothing
set recycler=CreateTimer()
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()
static if RECYCLE_DUMMY_UNITS and LIBRARY_xedummy then
set this.dummy= XE_NewDummyUnit(Player(15), x,y, facing*bj_RADTODEG)
else
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)
endif
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
static if RECYCLE_DUMMY_UNITS and LIBRARY_xedummy then
if(this.abil!=0) then
call UnitRemoveAbility(this.dummy,this.abil)
endif
call recyclebin.create(this.dummy)
set this.dummy=XE_NewDummyUnit(Player(15), x,y, newfacing*bj_RADTODEG)
else
call RemoveUnit(this.dummy)
set this.dummy= CreateUnit(Player(15), XE_DUMMY_UNITID, x,y, newfacing*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)
endif
set .fxpath=newfxpath
if(level != 0) then
call UnitAddAbility(this.dummy, this.abil)
call SetUnitAbilityLevel(this.dummy, this.abil, level)
endif
set this.z = z
set zangle = za
endmethod
method destroy 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
call recyclebin.create(this.dummy)
set this.dummy=null
call this.deallocate()
endmethod
method hiddenDestroy takes nothing returns nothing
call ShowUnit(dummy,false)
call destroy()
endmethod
endstruct
endlibrary