Name | Type | is_array | initial_value |
hero | unit | No |
//TESH.scrollpos=0
//TESH.alwaysfold=0
library ShatteringFrost /*
****************************************************************************************************
*
* S H A T T E R I N G F R O S T
*
* ~ version 2.0.1
* ~ coded by Mr_Bean
*
****************************************************************************************************
*
* */ requires /*
* ¯¯¯¯¯¯¯¯
* */ BeanDummyCaster, /* (not uploaded; available in demo map)
* */ SpellEffectEvent, /* http://www.hiveworkshop.com/forums/jass-resources-412/snippet-spelleffectevent-187193
* */ TimerUtils /* http://www.wc3c.net/showthread.php?t=101322
* */ xemissile /* http://www.wc3c.net/showthread.php?t=101150
*
****************************************************************************************************
*
* Installation
* ¯¯¯¯¯¯¯¯¯¯¯¯
* - Make sure you've got (and installed properly) the above systems.
* - Copy the 4 custom abilities. Scroll down to the "RAW IDS" section and set their raw IDs.
* - Copy the 2 custom buffs (or make your own):
* > Set the buff fields of "Slow [Shattering Frost]" to the "Shattering Frost" buff
* > Set the buff fields of "Pause [Shattering Frost]" to the "Shattering Frost [paused]" buff.
* - Go through the configurables section below and change what you want to.
*
****************************************************************************************************
*
* Adding Ability Levels
* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* If you would like to add more levels to the ability, you will need to add the same amount of
* levels to the 3 dummy abilities (Pause, Stun and Slow). Configure the stun and slow values as
* desired. For the Stun and Pause dummy abilities, make sure the for each level you added:
* - Damage is set to 0.
* - Cast range is set to 99999.
* - Cooldown is set to 0.
* - Mana cost is set to 0.
* - Targets allowed is BLANK (don't worry, the code makes sure only valid targets are chosen).
*
* For the Slow dummy ability, make sure the for each level you added:
* - Cast range is set to 99999.
* - Cooldown is set to 0.
* - Mana cost is set to 0.
* - Targets allowed is BLANK (don't worry, the code makes sure only valid targets are chosen).
*
****************************************************************************************************
*
* Configuration
* ¯¯¯¯¯¯¯¯¯¯¯¯¯
*/
globals
//==================================================
//=== RAW IDS
private constant integer SPELL_ID = 'A002' // "Shattering Frost" ability.
private constant integer PAUSE_ID = 'A003' // "Pause [Shattering Frost]" ability.
private constant integer STUN_ID = 'A000' // "Stun [Shattering Frost]" ability.
private constant integer SLOW_ID = 'A001' // "Slow [Shattering Frost]" ability.
//==================================================
//=== PAUSE CONFIGURATION
private constant real PAUSE_DELAY = 1.0 // Delay between target effect creation and pause starting.
// Effect created on target while paused:
private constant string TARGET_EFFECT = "Abilities\\Spells\\Undead\\FreezingBreath\\FreezingBreathTargetArt.mdl"
private constant string TARGET_ATTACH = "origin"
//==================================================
//=== EXPLOSION CONFIGURATION
private constant real EXPLODE_AOE = 400.0 // All enemies within this range of the target get a frost shard.
// Effect created at target when ice explodes:
private constant string EXPLODE_EFFECT = "Abilities\\Spells\\Undead\\FrostNova\\FrostNovaTarget.mdl"
//==================================================
//=== ICE SHARD CONFIGURATION
private constant real SHARD_SCALE = 1.0 // Model scale.
private constant real SHARD_SPEED = 800.0 // Speed.
private constant real SHARD_ARC = 0.2 // Arc.
private constant real SHARD_HEIGHT = 50.0 // Height.
// Model:
private constant string SHARD_ART = "Abilities\\Spells\\Other\\FrostBolt\\FrostBoltMissile.mdl"
endglobals
//==================================================
//=== PRIMARY TARGET DAMAGE PER LEVEL
private function GetTargetDamage takes integer level returns real
return (50.0 * level) + 25.0
endfunction
//==================================================
//=== PRIMARY TARGET PAUSE PER LEVEL:
private function GetPauseDuration takes integer level returns real
return (0.25 * level) + 0.25
endfunction
//==================================================
//=== SHARD DAMAGE PER LEVEL:
private function GetShardDamage takes integer level returns real
return 50.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 IceShard extends xemissile
unit source
integer level
//-----
/**
* Damages the target and stuns it if it's still alive.
*/
private method onHit takes nothing returns nothing
call DealDamage(source, targetUnit, GetShardDamage(level))
if (not IsUnitType(targetUnit, UNIT_TYPE_DEAD)) then
call BeanDummyCaster_SetAbility(STUN_ID, level, "thunderbolt")
call BeanDummyCaster_TargetOrder(targetUnit)
endif
set source = null
endmethod
/**
* Creates a new instance and launches the missile.
*/
static method create takes unit s, real x, real y, unit t, integer l returns thistype
local thistype this = allocate(x, y, SHARD_HEIGHT, GetUnitX(t), GetUnitY(t), SHARD_HEIGHT)
set source = s
set level = l
set targetUnit = t
set fxpath = SHARD_ART
set scale = SHARD_SCALE
call launch(SHARD_SPEED, SHARD_ARC)
return this
endmethod
endstruct
private struct ShatteringFrost
static group enumG = CreateGroup()
static unit temp
//-----
unit caster
unit target
player owner
integer level
effect fx
//-----
/**
* Cleans up and dellocates the instance.
*/
private method destroy takes nothing returns nothing
set caster = null
set target = null
set owner = null
set fx = null
call deallocate()
endmethod
/**
* This is the filter applied to units that are in range of the target.
*/
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 /* Alive
*/ and not IsUnitType(u, UNIT_TYPE_MAGIC_IMMUNE) /* Not magic immune.
*/ and not IsUnitType(u, UNIT_TYPE_STRUCTURE) /* Not a structure. */
endmethod
/**
* Unpauses the target and sends ice shards to all enemies in range.
* Also slows the target and destroys the effect.
*/
private static method explode takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
local real x = GetUnitX(target)
local real y = GetUnitY(target)
call ReleaseTimer(GetExpiredTimer())
call DestroyEffect(fx)
call DestroyEffect(AddSpecialEffect(EXPLODE_EFFECT, x, y))
call BeanDummyCaster_SetAbility(SLOW_ID, level, "slow")
call BeanDummyCaster_TargetOrder(target)
call GroupEnumUnitsInRange(enumG, x, y, EXPLODE_AOE, null)
call GroupRemoveUnit(enumG, target)
loop
set temp = FirstOfGroup(enumG)
exitwhen temp == null
call GroupRemoveUnit(enumG, temp)
if (filterUnit(temp)) then
call IceShard.create(caster, x, y, temp, level)
endif
endloop
call destroy()
endmethod
/**
* Pauses the target and deals damage. Waits until the pause is
* over.
*/
private static method pause takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
call BeanDummyCaster_SetAbility(PAUSE_ID, level, "thunderbolt")
call BeanDummyCaster_TargetOrder(target)
call DealDamage(caster, target, GetTargetDamage(level))
call TimerStart(GetExpiredTimer(), GetPauseDuration(level), false, function thistype.explode)
endmethod
/**
* Creates a new instance. Creates the effect on the target and
* waits until PAUSE_DELAY expires.
*/
private static method create takes nothing returns thistype
local thistype this = allocate()
set caster = GetTriggerUnit()
set owner = GetTriggerPlayer()
set level = GetUnitAbilityLevel(caster, SPELL_ID)
set target = GetSpellTargetUnit()
set fx = AddSpecialEffectTarget(TARGET_EFFECT, target, TARGET_ATTACH)
call TimerStart(NewTimerEx(this), PAUSE_DELAY, false, function thistype.pause)
return this
endmethod
/**
* Registers the spell and preloads effects and abilities.
*/
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_ID, function thistype.create)
call Preload(TARGET_EFFECT)
call Preload(EXPLODE_EFFECT)
call Preload(SHARD_ART)
set temp = CreateUnit(Player(0), 'hpea', 0.0, 0.0, 0.0)
call UnitAddAbility(temp, PAUSE_ID)
call UnitAddAbility(temp, STUN_ID)
call UnitAddAbility(temp, SLOW_ID)
call RemoveUnit(temp)
endmethod
endstruct
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library BeanDummyCaster /*
****************************************************************************************************
*
* S I M P L E D U M M Y C A S T E R
*
* ~ coded by Mr_Bean
*
****************************************************************************************************
*
* Description
* ¯¯¯¯¯¯¯¯¯¯¯
* This simple library uses one dummy unit to cast the spells instead of creating a new dummy each
* time. This is efficient, however it will not work well with non-instant spells (like channelling
* ones).
*
* The dummy unit MUST have Animation - Cast Backswing AND Animation - Cast Point set to 0.0.
*
* All dummy spells MUST have 0 mana, 0 cast time and 0 seconds cooldown. I recommend that you also
* set their cast range to 99999.0, otherwise you will need to move the dummy so it's in range. For
* instant orders (such as War Stomp), you will need to move the dummy anyway (unless it's a global
* spell).
*
****************************************************************************************************
*
* Installation
* ¯¯¯¯¯¯¯¯¯¯¯¯
* - Copy this entire trigger.
* - Copy the "Dummy" unit if you don't have one already. Set DUMMY_ID below to its raw ID.
* - Read the "Requirements" section above so you know how to set up the dummy as well as dummy
* abilities.
*
****************************************************************************************************
*
* Usage
* ¯¯¯¯¯
* public function SetAbility takes integer abilId, integer level, string order returns nothing
* Adds an ability to the dummy. The dummy can only have 1 ability at a time.
* order is the order string of the ability, like "thunderbolt".
*
* public function SetAbilityLevel takes integer level returns nothing
* Changes the level of the dummy's current ability.
*
* public function RemoveAbility takes nothing returns nothing
* Removes the current ability from the dummy. This shouldn't really be needed.
*
* public function Move takes real x, real y returns nothing
* Moves the dummy caster to a point.
*
* public function InstantOrder takes nothing returns nothing
* Makes the dummy cast the current spell as an instant spell. Example: War Stomp.
*
* public function TargetOrder takes widget target returns nothing
* Makes the dummy cast the current spell as a target spell. Example: Frost Nova.
*
* public function PointOrder takes real x, real y returns nothing
* Makes the dummy cast the current spell as a point spell. Example: Silence.
*
****************************************************************************************************
*
* Configuration
* ¯¯¯¯¯¯¯¯¯¯¯¯¯
*/
globals
private constant integer DUMMY_ID = 'e000' // Raw ID of the dummy caster.
endglobals
/***************************************************************************************************
*
* END OF CONFIGURATION AND DOCUMENTATION
*
***************************************************************************************************/
globals
private unit dummy
private integer abilityId = 0
private integer orderId = 0
endglobals
public function PointOrder takes real x, real y returns nothing
if (orderId != 0) then
call IssuePointOrderById(dummy, orderId, x, y)
endif
endfunction
public function TargetOrder takes widget target returns nothing
if (orderId != 0 and target != null) then
call IssueTargetOrderById(dummy, orderId, target)
endif
endfunction
public function InstantOrder takes nothing returns nothing
if (orderId != 0) then
call IssueImmediateOrderById(dummy, orderId)
endif
endfunction
public function Move takes real x, real y returns nothing
call SetUnitPosition(dummy, x, y)
endfunction
public function RemoveAbility takes nothing returns nothing
if (abilityId != 0) then
call UnitRemoveAbility(dummy, abilityId)
set abilityId = 0
endif
endfunction
public function SetAbilityLevel takes integer level returns nothing
// Update level if dummy has an ability and
// new level is valid (greater than 0) and
// new level is different to current level:
if (abilityId != 0 /*
*/ and level > 0 /*
*/ and level != GetUnitAbilityLevel(dummy, abilityId)) then
call SetUnitAbilityLevel(dummy, abilityId, level)
endif
endfunction
public function SetAbility takes integer abilId, integer level, string order returns nothing
// Don't add the ability if the dummy has it already or
// if the ability is null (0):
if (abilityId == abilId or abilId == 0) then
return
endif
// Don't cast any abilities if the order string is bad:
if (order == "") then
set orderId = 0
else
set orderId = OrderId(order)
endif
call RemoveAbility()
set abilityId = abilId
call UnitAddAbility(dummy, abilityId)
call SetAbilityLevel(level)
endfunction
private struct InitStruct
private static method onInit takes nothing returns nothing
set dummy = CreateUnit(Player(PLAYER_NEUTRAL_AGGRESSIVE), DUMMY_ID, 0.0, 0.0, 0.0)
call UnitRemoveAbility(dummy, 'Amov')
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
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=15
//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
//TESH.scrollpos=60
//TESH.alwaysfold=0
library xemissile requires xefx, xebasic
//****************************************************************
//*
//* xemissile 0.9
//* -------------
//* A xemissile object is a special effect that moves like a
//* WC3's attack or spell missile.
//*
//* Please use .terminate() instead of .destroy() this ensures
//* that it will be safe to destroy it (else you would have to
//* worry about destroying it during the animation loop/etc.)
//*
//* This struct is used similarly to xecollider. Instead of just
//* creating the xemissile (which works, but it would only be a
//* xefx that can move like a missile) you probably need to make
//* it do something special when it reaches its target...
//* For this reason, you need to make a new struct extending
//* xemissile that declares an onHit method, you may also declare
//* a loopControl method.
//*
//****************************************************************
//===========================================================================
// So, this exists merely so you can declare your own event handler methods
// if interfaces make your brain blow out, please skip the next four lines.
//
private interface eventHandler
method onHit takes nothing returns nothing defaults nothing
method loopControl takes nothing returns nothing defaults nothing
endinterface
//===========================================================================
private struct missile extends eventHandler
private delegate xefx fx
// movement duration parameter.
private real time
// xy movement parameters.
private real mx
private real my
private real mvx
private real mvy
private real mvxy = 1.0
// z movement parameters.
private real mz
private real mvz
private real maz
private static location l = Location(0.0,0.0)
// target parameters.
private real tx = 0.0
private real ty = 0.0
private real tz = 0.0
private unit tu = null
public real zoffset = 0.0
private boolean update = true
private boolean launched = false
private boolean dead = false
public method operator x takes nothing returns real
return .mx
endmethod
public method operator y takes nothing returns real
return .my
endmethod
public method operator z takes nothing returns real
call MoveLocation(.l, .mx,.my)
return mz-GetLocationZ(.l)
endmethod
public method operator x= takes real r returns nothing
set .update=true
set .mx=r
endmethod
public method operator y= takes real r returns nothing
set .update=true
set .my=r
endmethod
public method operator z= takes real r returns nothing
set .update=true
call MoveLocation(.l, .mx,.my)
set .mz=r+GetLocationZ(.l)
endmethod
public method operator speed takes nothing returns real
return .mvxy/XE_ANIMATION_PERIOD
endmethod
public method operator speed= takes real newspeed returns nothing
local real factor=newspeed*XE_ANIMATION_PERIOD/.mvxy
if newspeed<=0.0 then
debug call BJDebugMsg("xemissile speed error: speed must be a non-zero positive value.")
return
endif
set .mvxy=newspeed*XE_ANIMATION_PERIOD
if .launched then
set .time=.time/factor
set .mvx=.mvx*factor
set .mvy=.mvy*factor
set .mvz=.mvz*factor
set .maz=.maz*factor*factor
endif
endmethod
public method operator targetUnit takes nothing returns unit
return this.tu
endmethod
public method operator targetUnit= takes unit u returns nothing
set .update=true
set .tu=u
set .tx=GetUnitX(u)
set .ty=GetUnitY(u)
call MoveLocation(.l, .tx,.ty)
set .tz=GetUnitFlyHeight(u)+GetLocationZ(.l)+.zoffset
endmethod
public method setTargetPoint takes real x, real y, real z returns nothing
set .update=true
set .tu=null
set .tx=x
set .ty=y
call MoveLocation(.l, .tx,.ty)
set .tz=z+GetLocationZ(.l)
endmethod
//-------- Missile launcher
public method launch takes real speed, real arc returns nothing
local real dx=.tx-.mx
local real dy=.ty-.my
local real d=SquareRoot(dx*dx+dy*dy)
local real a=Atan2(dy, dx)
local real dz=.tz-.mz
set .mvxy=speed*XE_ANIMATION_PERIOD
if speed<=0.0 then
debug call BJDebugMsg("xemissile launch error: speed must be a non-zero positive value.")
return
elseif d>0.0 then
set .time=d/speed
else
set .time=XE_ANIMATION_PERIOD
endif
set .mvz=( (d*arc)/(.time/4.0) + dz/.time )*XE_ANIMATION_PERIOD // Do some mathemagics to get a proper arc.
set .dead=.dead and not(.launched) // In case this is called from the onHit method to bounce the missile.
if not .dead and not .launched then
set .launched=true
set .V[.N]=this
set .N=.N+1
if(.N==1) then
call TimerStart(.T, XE_ANIMATION_PERIOD, true, xemissile.timerLoopFunction )
endif
endif
endmethod
//-------- Constructors and destructors
static method create takes real x, real y, real z, real a returns missile
local missile this=missile.allocate()
set .mx=x
set .my=y
call MoveLocation(.l, x,y)
set .mz=z+GetLocationZ(.l)
set .fx = xefx.create(x,y,a)
set .fx.z = z
return this
endmethod
private boolean silent=false
private method destroy takes nothing returns nothing
if(this.silent) then
call this.fx.hiddenDestroy()
else
call this.fx.destroy()
endif
call .deallocate()
endmethod
method terminate takes nothing returns nothing
set this.dead=true
set this.fx.zangle=0.0
set this.fxpath=""
endmethod
// declare hiddenDestroy so people don't call directly on the delegate xefx
method hiddenDestroy takes nothing returns nothing
set silent = true
call terminate()
endmethod
//-------- Main engine
private static timer T
private static integer N=0
private static xemissile array V
private static code timerLoopFunction //I use a code var so create can be above the timerloop function, more readable
private static method timerLoop takes nothing returns nothing
local integer i=0
local integer c=0
local thistype this
local real dx
local real dy
local real d
local real a
loop
exitwhen (i==.N )
set this=.V[i] //adopt-a-instance
if .dead then
set .launched=false
set this.fx.zangle=0.0
call .destroy()
else
if .tu!=null and GetUnitTypeId(.tu)!=0 then
set .update=true
set .tx=GetUnitX(.tu)
set .ty=GetUnitY(.tu)
call MoveLocation(.l, .tx,.ty)
set .tz=GetUnitFlyHeight(.tu)+GetLocationZ(.l)+.zoffset
endif
if .update then
set .update=false
set dx=.tx-.mx
set dy=.ty-.my
set d=SquareRoot(dx*dx+dy*dy)
set a=Atan2(dy,dx)
if d>0.0 then
set .time=d/.mvxy*XE_ANIMATION_PERIOD
else
set .time=XE_ANIMATION_PERIOD
endif
set .mvx=Cos(a)*.mvxy
set .mvy=Sin(a)*.mvxy
set .fx.xyangle=a
set .maz=2*((.tz-.mz)/.time/.time*XE_ANIMATION_PERIOD*XE_ANIMATION_PERIOD-(.mvz*XE_ANIMATION_PERIOD)/.time)
endif
set .mx=.mx+.mvx
set .my=.my+.mvy
set a=.maz/2.0
set .mvz=.mvz+a
set .mz=.mz+.mvz
set .mvz=.mvz+a
set .fx.x=.mx
set .fx.y=.my
call MoveLocation(.l, .mx,.my)
set .fx.z=.mz-GetLocationZ(.l)
set .fx.zangle=Atan2(.mvz, .mvxy)
set .time=.time-XE_ANIMATION_PERIOD
if .time<=0.0 then
set .dead=true
call this.onHit()
if .dead then
set .fxpath=""
endif
endif
set .V[c]=this
set c=c+1
if( this.loopControl.exists and not this.dead ) then
call this.loopControl()
endif
endif
set i=i+1
endloop
set .N=c
if(c==0) then
call PauseTimer(.T)
endif
endmethod
static method onInit takes nothing returns nothing
set .timerLoopFunction = (function missile.timerLoop)
set .T=CreateTimer()
endmethod
endstruct
//===========================================================================
struct xemissile extends missile
public static method create takes real x,real y,real z, real tx,real ty,real tz returns xemissile
local xemissile xm = xemissile.allocate(x,y,z, Atan2(ty-y,tx-x))
call xm.setTargetPoint(tx,ty,tz)
return xm
endmethod
endstruct
struct xehomingmissile extends xemissile
public static method create takes real x,real y,real z, unit target,real zoffset returns xehomingmissile
local xehomingmissile xm = xehomingmissile.allocate(x,y,z, GetUnitX(target), GetUnitY(target), 0.0)
set xm.zoffset=zoffset
set xm.targetUnit=target
return xm
endmethod
endstruct
endlibrary