Name | Type | is_array | initial_value |
//TESH.scrollpos=299
//TESH.alwaysfold=0
//==============================================================================
// PUI -- Perfect Unit Indexing by Cohadar -- v5.1
//==============================================================================
//
// PURPOUSE:
// * Extending UnitUserData()
// * This is basically perfect hashing algorithm for units
//
// HOW TO USE:
// * You have only one function at your disposal GetUnitIndex(unit)
// It will return a unique index for each unit in range 1..8190
//
// * What you will do with that index is all up to you
// Of course using global arrays is the most obvious choice
// Advanced jassers will think of a couple of more clever ones ofc.
//
// * There are also 2 textmacros available at the end of library code
// They can be used for easier attaching to units
// PUI for structs
// PUI_PROPERTY for unit, integer, real, boolean and string variables
//
// PROS:
// * You can use any number of systems that previously could not work together
// because they all required exclusive access of UnitUserData()
//
// * You can also use this to attach spell data structs to casters
//
// * There are no SetUnitIndex() or ClearUnitIndex() functions here
// Each unit gets assigned one index that cannot be changed
// That index will be automatically recycled when unit is removed from the game.
//
// CONS:
// * This system uses UnitUserData() itself
// That means that if you want to use PUI you must recode
// any other system that uses UnitUserData() to use GetUnitIndex() instead
//
// * If you use UnitIndex for arrays of non-native types (timers, effects and similar)
// you must check if timer on that index already exists before you create a new one.
// This can happen if GetUnitIndex() assigns a recycled index (index of some dead and removed unit)
// to the newly created unit for which you intended to use timer for
//
// * All in all this is not a sys for newbies, it gives great power,
// but it requires knowledge and carefull handling
//
// DETAILS:
// * System is using unit array to keep track of all units with an index.
// Array is periodically checked for removed units,
// when removed unit is found, index is recycled.
// Indexes have "decay time" to prevent bugs
// caused by attaching to "Can't Raise, Does not decay" type units,
// or by using RemoveUnit() function
//
// SYSTEM COMMANDS: (debug mode only, red player only)
//
// * type -pui to display indexes of currently selected units
// * type -puistats to display some stats
//
// THANKS TO:
// * Vexorian - for his help with PUI textmacro
//
// HOW TO IMPORT:
// * Just create a trigger named PUI
// * convert it to text and replace the whole trigger text with this one
//
//==============================================================================
library PUI initializer Init
//==============================================================================
globals
//-----------------------------------------------
private constant real INDEX_DECAY_TIME = 5. // seconds
//-----------------------------------------------
private constant real PERIOD = 0.03125 // 32 fps
//-----------------------------------------------
private constant integer DECAY_TICKS = R2I(INDEX_DECAY_TIME/PERIOD)
//-----------------------------------------------
private integer array Indexz
private unit array Unitz
private integer array Decayz
private integer array Tickz
private integer maxindex = 0
private integer topindex = 0
private integer decayindex = 0
private integer checker = 0
private integer decayer = 0
private integer tick = 0
endglobals
//==============================================================================
private function PeriodicRecycler takes nothing returns boolean
local integer temp
set tick = tick + 1
if topindex > decayindex then
set checker = checker + 1
if checker > topindex then
set checker = decayindex + 1
endif
if (GetUnitUserData(Unitz[checker])==0) then
set decayindex = decayindex + 1
set Unitz[checker] = Unitz[decayindex]
// swap(checker, decayindex)
set temp = Indexz[checker]
set Indexz[checker] = Indexz[decayindex]
set Indexz[decayindex] = temp
set Decayz[decayindex] = DECAY_TICKS
set Tickz[decayindex] = tick
endif
endif
if decayindex > 0 then
set decayer = decayer + 1
if decayer > decayindex then
set decayer = 1
endif
set Decayz[decayer] = Decayz[decayer] - (tick-Tickz[decayer])
if Decayz[decayer] > 0 then
set Tickz[decayer] = tick
else
// swap(decayer, decayindex)
set temp = Indexz[decayer]
set Indexz[decayer] = Indexz[decayindex]
set Indexz[decayindex] = temp
set Unitz[decayindex] = Unitz[topindex]
// swap(decayindex, topindex)
set temp = Indexz[decayindex]
set Indexz[decayindex] = Indexz[topindex]
set Indexz[topindex] = temp
set decayindex = decayindex - 1
set topindex = topindex - 1
endif
endif
return false
endfunction
//==============================================================================
// Main and only function exported by this library
//==============================================================================
function GetUnitIndex takes unit whichUnit returns integer
local integer index
debug if whichUnit == null then
debug call BJDebugMsg("|c00FF0000ERROR: PUI - Index requested for null unit")
debug return 0
debug endif
set index = GetUnitUserData(whichUnit)
if index == 0 then
set topindex = topindex + 1
if topindex > maxindex then
set maxindex = topindex
set Indexz[topindex] = topindex
endif
set index = Indexz[topindex]
set Unitz[topindex] = whichUnit
call SetUnitUserData(whichUnit, index)
set index = GetUnitUserData(whichUnit)
// this happens when requesting unit index for removed unit
debug if index == 0 then
debug call BJDebugMsg("|c00FFCC00WARNING: PUI - Bad unit handle")
debug endif
//debug call BJDebugMsg("|c00FFCC00PUI: Index assigned #" + I2S(index))
endif
return index
endfunction
//==============================================================================
private function DisplayStats takes nothing returns nothing
call BJDebugMsg("=============================================")
call BJDebugMsg("Biggest index ever = " + I2S(maxindex))
call BJDebugMsg("Indexes in use = " + I2S(topindex-decayindex))
call BJDebugMsg("Decaying indexes = " + I2S(decayindex))
call BJDebugMsg("Released indexes = " + I2S(maxindex-topindex))
call BJDebugMsg("=============================================")
endfunction
//===========================================================================
private function DisplaySelectedEnum takes nothing returns nothing
call BJDebugMsg( "PUI(" + ( GetUnitName(GetEnumUnit()) + ( ") = " + I2S(GetUnitUserData(GetEnumUnit())) ) ) )
endfunction
//===========================================================================
private function DisplaySelected takes nothing returns nothing
local group g = CreateGroup()
call SyncSelections()
call GroupEnumUnitsSelected(g, Player(0), null)
call ForGroup(g, function DisplaySelectedEnum)
call DestroyGroup(g)
set g = null
endfunction
//==============================================================================
private function Init takes nothing returns nothing
local trigger trig
set trig = CreateTrigger()
call TriggerRegisterTimerEvent( trig, PERIOD, true )
call TriggerAddCondition( trig, Condition(function PeriodicRecycler) )
debug set trig = CreateTrigger()
debug call TriggerRegisterPlayerChatEvent( trig, Player(0), "-pui", true )
debug call TriggerAddAction( trig, function DisplaySelected )
debug set trig = CreateTrigger()
debug call TriggerRegisterPlayerChatEvent( trig, Player(0), "-puistats", true )
debug call TriggerAddAction( trig, function DisplayStats )
endfunction
endlibrary
//===========================================================================
// Allowed PUI_PROPERTY TYPES are: unit, integer, real, boolean, string
// Do NOT put handles that need to be destroyed here (timer, trigger, ...)
// Instead put them in a struct and use PUI textmacro
//===========================================================================
//! textmacro PUI_PROPERTY takes VISIBILITY, TYPE, NAME, DEFAULT
$VISIBILITY$ struct $NAME$
private static unit array pui_unit
private static $TYPE$ array pui_data
//-----------------------------------------------------------------------
// Returns default value when first time used
//-----------------------------------------------------------------------
static method operator[] takes unit whichUnit returns $TYPE$
local integer pui = GetUnitIndex(whichUnit)
if .pui_unit[pui] != whichUnit then
set .pui_unit[pui] = whichUnit
set .pui_data[pui] = $DEFAULT$
endif
return .pui_data[pui]
endmethod
//-----------------------------------------------------------------------
static method operator[]= takes unit whichUnit, $TYPE$ whichData returns nothing
local integer pui = GetUnitIndex(whichUnit)
set .pui_unit[pui] = whichUnit
set .pui_data[pui] = whichData
endmethod
endstruct
//! endtextmacro
//===========================================================================
// Never destroy PUI structs directly.
// Use .release() instead, will call .destroy()
//===========================================================================
//! textmacro PUI
private static unit array pui_unit
private static integer array pui_data
private static integer array pui_id
//-----------------------------------------------------------------------
// Returns zero if no struct is attached to unit
//-----------------------------------------------------------------------
static method operator[] takes unit whichUnit returns integer
local integer pui = GetUnitIndex(whichUnit)
if .pui_data[pui] != 0 then
if .pui_unit[pui] != whichUnit then
// recycled handle detected
call .destroy(.pui_data[pui])
set .pui_unit[pui] = null
set .pui_data[pui] = 0
endif
endif
return .pui_data[pui]
endmethod
//-----------------------------------------------------------------------
// This will overwrite already attached struct if any
//-----------------------------------------------------------------------
static method operator[]= takes unit whichUnit, integer whichData returns nothing
local integer pui = GetUnitIndex(whichUnit)
if .pui_data[pui] != 0 then
call .destroy(.pui_data[pui])
endif
set .pui_unit[pui] = whichUnit
set .pui_data[pui] = whichData
set .pui_id[whichData] = pui
endmethod
//-----------------------------------------------------------------------
// If you do not call release struct will be destroyed when unit handle gets recycled
//-----------------------------------------------------------------------
method release takes nothing returns nothing
local integer pui= .pui_id[integer(this)]
call .destroy()
set .pui_unit[pui] = null
set .pui_data[pui] = 0
endmethod
//! endtextmacro
//TESH.scrollpos=155
//TESH.alwaysfold=0
// +------------------------------------------------------------+
// | |
// | -=-=- Armor Crush [v1.4] -=-=- |
// | -=-=- By Wolfie[NoCT] -=-=- |
// | Requires Jass NewGen and PUI |
// | |
// +------------------------------------------------------------+
// | |
// | Gives a 15% chance to deal a portion of your |
// | health back at the attacker. This blow is so |
// | shocking it also reduces the attackers armor. |
// | |
// +------------------------------------------------------------+
// | |
// | -=-=- How To Implement -=-=- |
// | 1. Copy this trigger into your map |
// | 2. Copy PUI into your map |
// | 3. Copy the buffs into your map |
// | 4. Copy the abilities into your map |
// | 5. Copy the dummies into your map |
// | 6. Make sure the 'Rawcodes' in the trigger match |
// | your buffs/abilities/units in Object Editor |
// | 7. Customize the spell |
// | 8. Enjoy! |
// | |
// +------------------------------------------------------------+
// | |
// | -=-=- Credits -=-=- |
// | Credits are not needed, but appreciated |
// | Just don't claim this as yours |
// | |
// +------------------------------------------------------------+
// | -=-=- Version History -=-=- |
// | |
// | Version 1.4 |
// | - Fixed a FirstOfGroup to ForGroup |
// | - Fixed a sound leak |
// | - Fixed texttag leak |
// | - Made floating text more efficient |
// | Credits to Artificial |
// | |
// | Version 1.3 |
// | - Fixed a major bug with Armor Crush not stacking |
// | - Made Armor Crush based off an aura |
// | Prevents the buff from being removed |
// | - Implemented PUI to save GetUnitUserData() |
// | - Fixed some BJ's |
// | - Made Armor Crush sound effect customizable |
// | - Various minor changes |
// | |
// | Version 1.2 |
// | - Code layout touchup |
// | - Fixed sound BJ's |
// | - Added formula configurable options |
// | - Added an extra implementation step |
// | - Removed a couple useless configurables |
// | - Removed GUI version from map |
// | Download v1.1 if you want GUI |
// | Note I'm not updating the GUI version anymore |
// | |
// | Version 1.1 |
// | - Fixed "Feedback" sound not sounding |
// | Although now it's unconfigurable |
// | - Greatly optimized code |
// | |
// | Version 1.0 |
// | - Initial release |
// | |
// +------------------------------------------------------------+
scope ArmorCrush initializer Init
// +-------------------------------------+
// | -=-=- MODIFY HERE -=-=- |
// | -=-=- MODIFY HERE -=-=- |
// | -=-=- MODIFY HERE -=-=- |
// +-------------------------------------+
globals
private constant integer ability_ACid = 'A00F'
// Rawcode of 'Armor Crush' ability
private constant integer ability_ACdid = 'A00E'
// Rawcode of 'Armor Crushed' ability
private constant integer buff_ACid = 'B001'
// Rawcode of 'Armor Crush' buff
private constant integer buff_ACdid = 'B002'
// Rawcode of 'Armor Crushed' buff
private constant integer dummyCaster = 'n007'
// Rawcode of 'DummyCaster' unit
private constant integer crushSoundVolume = 100
// Volume of the played sound
private constant string crushSFX = "Abilities\\Spells\\Items\\ResourceItems\\ResourceEffectTarget.mdl"
// Path for Armor Crush SFX
private constant string crushAttach = "overhead"
// Attachment point of Armor Crush SFX
private constant string crushSoundFX = "Abilities\\Spells\\Human\\Feedback\\Feedback.wav"
// Path for Armor Crush sound effect
private constant attacktype attackType = ATTACK_TYPE_HERO
// Attack type Armor Crush deals
private constant damagetype damageType = DAMAGE_TYPE_FIRE
// Damage type Armor Crush deals
private constant real tOffset = 0
// Floating text offset
private constant real tSize = 10
// Floating text font size
private constant integer tRed = 0
// Floating text red color amount (0-100)
private constant integer tGreen = 0
// Floating text green color amount (0-100)
private constant integer tBlue = 100
// Floating text blue color amount (0-100)
private constant integer tTrans = 0
// Floating text transparency (0-100)
private constant real tLife = 3
// Floating text life
private constant real tAge = 2
// Floating text age
private constant real tVelocity = 75
// Floating text velocity
private constant real tDegrees = 90
// Floating text degrees
private constant string buffCommand = "innerfire"
// 'Order String Use/Turn On' for 'Armor Crushed' ability
// Don't change the following constants
private sound crushSound = null // Don't change this!
private constant group crushedGroup = CreateGroup() // Don't change this!
private integer array unitCUSTOM // Don't change this!
// Don't change the above three(3) constants
endglobals
// Chance to cast Armor Crush
private function CHANCE takes integer level returns integer
return 40
endfunction
// How many times Armor Crush stacks
private function STACK takes integer level returns integer
return 40
endfunction
// Percentage of health Armor Crush returns
// Each 0.01 is equal to 1% (So 0.05 would be 5%, 0.25 would be 25%, and so on)
private function RETURN takes integer level returns real
return level * 0.05
endfunction
// +----------------------------------------------+
// | -=-=- NO TOUCHIE PAST HERE -=-=- |
// | -=-=- NO TOUCHIE PAST HERE -=-=- |
// | -=-=- NO TOUCHIE PAST HERE -=-=- |
// +----------------------------------------------+
private function ACConditions takes nothing returns boolean
return GetRandomInt(1, 100) <= CHANCE(GetUnitAbilityLevel(GetTriggerUnit(), ability_ACid)) and GetUnitAbilityLevel(GetTriggerUnit(), buff_ACid) > 0
endfunction
private function ACActions takes nothing returns nothing
local unit ar = GetAttacker()
local real x = GetUnitX(ar)
local real y = GetUnitY(ar)
local unit ad = GetTriggerUnit()
local integer damageAttacker
local texttag actt = CreateTextTag()
local unit u
local integer QT = GetUnitAbilityLevel(ad, ability_ACid)
if crushSound == null then
set crushSound = CreateSound(crushSoundFX, false, true, true, 10, 10, "SpellsEAX")
endif
call AttachSoundToUnit(crushSound, ar)
call SetSoundVolume(crushSound, crushSoundVolume)
call StartSound(crushSound)
call KillSoundWhenDone(crushSound)
set crushSound = null
call DestroyEffect(AddSpecialEffectTarget(crushSFX, ar, crushAttach))
set damageAttacker = R2I(GetWidgetLife(ar))
call UnitDamageTarget(ad, ar, (GetUnitState(ad, UNIT_STATE_LIFE) * (RETURN(QT))), true, true, attackType, damageType, WEAPON_TYPE_WHOKNOWS)
set damageAttacker = damageAttacker - R2I(GetWidgetLife(ar))
set u = CreateUnit(GetOwningPlayer(ad), dummyCaster, x, y, GetUnitFacing(ar))
call SetUnitAbilityLevel(u, ability_ACdid, (unitCUSTOM[GetUnitIndex(ar)] * QT))
call IssueTargetOrder(u, buffCommand, ar)
call UnitApplyTimedLife(u, 'BTLF', 2.00)
set actt = CreateTextTagUnitBJ((I2S(damageAttacker) + "!" ), ar, tOffset, tSize, tRed, tGreen, tBlue, tTrans)
call SetTextTagPermanent(actt, false)
call SetTextTagLifespan(actt, tLife)
call SetTextTagFadepoint(actt, tAge)
call SetTextTagVelocityBJ(actt, tVelocity, tDegrees)
set actt = null
if unitCUSTOM[GetUnitIndex(ar)] < STACK(QT) then
set unitCUSTOM[GetUnitIndex(ar)] = unitCUSTOM[GetUnitIndex(ar)] + 1
set u = CreateUnit(GetOwningPlayer(ad), dummyCaster, x, y, GetUnitFacing(ar))
call SetUnitAbilityLevel(u, ability_ACdid, (unitCUSTOM[GetUnitIndex(ar)] * QT))
call IssueTargetOrder(u, buffCommand, ar)
call UnitApplyTimedLife(u, 'BTLF', 2.00)
if (IsUnitInGroup(ar, crushedGroup) == false) then
call GroupAddUnit(crushedGroup, ar)
endif
endif
set ar = null
set ad = null
set u = null
endfunction
private function CGRConditions takes nothing returns boolean
return FirstOfGroup(crushedGroup) != null
endfunction
private function RemoveU takes nothing returns nothing
if GetUnitAbilityLevel(GetEnumUnit(), buff_ACdid) < 1 then
call GroupRemoveUnit(crushedGroup, GetEnumUnit())
set unitCUSTOM[GetUnitIndex(GetEnumUnit())] = 0
endif
endfunction
private function CGRActions takes nothing returns nothing
call ForGroup(crushedGroup, function RemoveU)
endfunction
//===========================================================================
private function Init takes nothing returns nothing
local trigger trig = CreateTrigger()
local integer i = 0
loop
call TriggerRegisterPlayerUnitEvent(trig, Player(i), EVENT_PLAYER_UNIT_ATTACKED, null)
set i = i + 1
exitwhen i == bj_MAX_PLAYER_SLOTS
endloop
call TriggerAddCondition(trig, Condition(function ACConditions))
call TriggerAddAction(trig, function ACActions)
set trig = CreateTrigger()
call TriggerRegisterTimerEvent(trig, 1.50, true)
call TriggerAddCondition(trig, Condition(function CGRConditions))
call TriggerAddAction(trig, function CGRActions)
endfunction
endscope
//TESH.scrollpos=73
//TESH.alwaysfold=0
* Casting functions
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
These were the original intention of the caster system, functions that save you the job of having
to make dummy caster triggers every time.
______________________________________________________________________________________________________________________________________________________________________
function CasterCastAbility takes player owner, integer abilid, string order, unit target, boolean instant returns unit
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* Calls a Caster to cast an ability for a player on an unit, abilid is the ability's
rawcode inside '' (ex: 'Aloc'), you must also give it the ability's orderstring, if the
ability doesn't have an order string use the Order Id betwen quotes (ex: "782042").
* The instant boolean determines the way of recicling the caster, when it is true it will
recycle the caster instantly, and when it is false will wait until the caster finishes
casting the ability.
* Returns the caster used, so if the spell is channeling you can choose when to order the caster
to stop the ability.
* The Rune of Bloodlust is an example of using this function, the Rune of Illusions shows
how to use special orderids.
______________________________________________________________________________________________________________________________________________________________________
function CasterCastAbilityPoint takes player owner, integer abilid, string order, real x, real y, boolean instant returns unit
function CasterCastAbilityPointLoc takes player owner, integer abilid, string order, location loc, boolean instant returns unit
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* Will make a caster cast a point order ability on a point, the difference betwen the first
one and the loc version is that CasterCastAbilityPoint uses reals for x and y,
CasterCastAbilityPointLoc uses a location (point) argumment that has to be saved / removed to
avoid memory leaks.
* It also works with no target orders like thunder clap, or Essence of blight.
* Returns the caster used, so if the spell is channeling you can choose when to order the caster
to stop the ability.
* See the Muradins Hammer and the Firewall sample triggers as an example of the usage of these
functions,
______________________________________________________________________________________________________________________________________________________________________
function CasterUseAbilityStatic takes player owner, string modelpath, integer abilityid, real duration, real x, real y returns unit
function CasterUseAbilityStaticLoc takes player owner, string modelpath, integer abilityid, real duration, location loc returns unit
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* Will leave a caster with a given special effect and a given passive ability on a point for a given
duration, good to use this with phoenix fire or auras. Leave modelpath as "" if you don't want the
caster to be visible.
* See the Poisonous Weeds sample trigger to see an usage example on this function.
______________________________________________________________________________________________________________________________________________________________________
function CasterCastAbilityAOE takes player owner, integer abilid, string order, real x, real y, real radius, boolean goodeffect, boolean instant returns nothing
function CasterCastAbilityAOELoc takes player owner, integer abilid, string order, location center, real radius, boolean goodeffect, boolean instant returns nothing
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* The AOE versions of CasterCastAbility, radius determines the area of effect, x and y or
center determine the center of the area of effect. The boolean goodeffect determines if
the spell will work on allies or enemies. (This is only for abilities with target orders)
* See the Sheep Staff sample trigger as an example of the usage of this function as a bad
effect, the Scroll of Rejuvenation sample trigger is an example of this function as a good
effect, the Thors Hammer sample trigger as an example of non instant abilities.
______________________________________________________________________________________________________________________________________________________________________
function CasterCastAbilityGroup takes player owner, integer abilid, string order, group targetgroup, boolean instant returns nothing
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* Will make casters to cast an ability on a Unit Group, It will affect every unit in the group
and remember that the set bj_wantDestroyGroup hack will work on this function.
* See the Cripple Everything sample trigger as an example of the usage of this function.
______________________________________________________________________________________________________________________________________________________________________________________________
function CasterCastAbilityLevel takes player owner, integer abilid, integer level, string order, unit target, boolean instant returns unit
function CasterCastAbilityLevelPoint takes player owner, integer abilid, integer level, string order, real x, real y, boolean instant returns unit
function CasterCastAbilityLevelPointLoc takes player owner, integer abilid, integer level, string order, location loc, boolean instant returns unit
function CasterUseAbilityLevelStatic takes player owner, string modelpath, integer abilityid, integer level, real duration, real x, real y returns unit
function CasterUseAbilityLevelStaticLoc takes player owner, string modelpath, integer abilityid, integer level, real duration, location loc returns unit
function CasterCastAbilityLevelAOE takes player owner, integer abilid, integer level, string order, real x, real y, real radius, boolean goodeffect, boolean instant returns nothing
function CasterCastAbilityLevelAOELoc takes player owner, integer abilid, integer level, string order, location center, real radius, boolean goodeffect, boolean instant returns nothing
function CasterCastAbilityLevelGroup takes player owner, integer abilid, integer level, string order, group targetgroup, boolean instant returns nothing
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* These are the same as the functions I already mentioned, but you can specify the level in the level
integer parameter, this is specially useful when making hero spells.
* The blinding Light trigger sample uses CasterCastAbilityLevelAOELoc .
______________________________________________________________________________________________________________________________
function CasterSetCastSource takes real x, real y returns nothing
function CasterSetCastSourceLoc takes real x, real y returns nothing
set udg_sourcehack = location (JASS) | set sourcehack = Point (GUI)
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* Will set the position where casters are moved before casting an ability, so you can make
them cast an ability from the point determined by this function, by default the point where
casters appear is the position of the target, use this to temporary change it.
* This will only affect the Caster System functions that are next to this call, after a wait
or in another instance of the trigger you'll have to set this again.
* You can get the same effect if you set the value of the sourcehack variable, but don't ever
forget to set it to null after calling the functions you want to affect.
* See the Blessed Sword and Thors Hammer sample triggers to see an usage example on this hack.
______________________________________________________________________________________________________________________________
function CasterSetRecycleDelay takes real Delay returns nothing
set udg_delayhack = Real (JASS) | Set delayhack = Real (GUI)
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* Will set the wait time before recicling a caster if you used false as instant argument, by
default the Caster System will wait till the caster is not casting that ability before
recycling it, use this to also add an extra delay before recycling it.
* This will only affect the Caster System functions that are next to this call, after a wait
or in another instance of the trigger you'll have to set this again.
* The delayhack global variable determines the time the system will wait before recicling
the casters used in a function when the ability is not instant. If you were experiencing
that damage abilities were not giving bounty/experience if the kill was after a while from
the time the casters cast the ability, set delayhack to the maximum time the damage can
take. Always set delayhack to 0.00 after calling the functions.
* See the Thor's hammer, the Blessed Sword and the Fire Wall sample triggers to see an
usage example on this hack.
___________________________________________________________________________________________________________________________________________________________________________________________________________
function CasterCastAbilityEx takes player owner, real x, real y, real z, integer abilid, integer level, string order, widget target, real delay returns unit
function CasterCastAbilityExLoc takes player owner, location source, real z, integer abilid, integer level, string order, widget target, real delay returns unit
function CasterCastAbilityPointEx takes player owner, real x1, real y1, real z1, integer abilid, integer level, string order, real x2, real y2, real delay returns unit
function CasterCastAbilityPointExLoc takes player owner, location loc, real z1, integer abilid, integer level, string order, real x2, real y2, real delay returns unit
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* These versions of CasterCastAbility and CasterCastAbilityPoint, allow you to choose everything,
including the level of the ability, the source point and ELEVATION and the recycle delay.
* They IGNORE CasterSetCastSource and CasterSetRecycleDelay , also udg_delayhack and udg_sourcehack
______________________________________________________________________________________________________________________________
function GetACaster takes nothing returns unit
function RecycleCaster takes unit caster returns nothing
function RecycleCasterAfterCast takes unit caster, integer abilid returns nothing
function RecycleCasterAfterCastEx takes unit caster, real delaytime, integer abilid, boolean activeability returns nothing
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* These are functions that you may find useful in certain situations.
* GetACaster will return an available caster or create one if necessary. (Will mark it as
unavailable )
* RecycleCaster will make the caster available again, and reset stuff from movement
speed to scale.
* RecycleCasterAfterCast will wait until the caster stops casting an ability, remove the
ability then wait udg_delayhack seconds, then recycle the caster, it uses its own thread.
* RecycleCasterAfterCastEx will wait until the caster stops casting the ability, then if the
argument activeability is false remove the ability, then wait delaytime seconds, then if
activeability was true remove the ability, then recycle the caster.
* Check the IceWave and the Shield of divinity Trigger samples to see usage examples on this
functions.
______________________________________________________________________________________________________________________________
function CreateCasters takes integer N returns nothing
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* Will Create a N number of casters ( ex: call CreateCasters(5) ) use it at map initialization.
______________________________________________________________________________________________________________________________
function PreloadAbility takes integer abilid returns nothing
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* Having lag the first time a caster uses an ability?, Use call PreloadAbility( 'Abil') at
map initialization, (Being 'Abil' the Ability's rawcode betwen '' (once again the 'Aloc'
example) (This command should be after CreateCasters()
* This Map's Caster Setup trigger is an example on the usage of CreateCasters and
PreloadAbility. JASS spells can have PreloadAbility in their InitFunction (Check IceWave
and Shield of divinity)
______________________________________________________________________________________________________________________________
function AddCaster takes nothing returns unit
function AddCasterFacing takes real fac returns unit
function CreateCaster takes real fac, real x , real y returns unit
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
Functions that Create a Caster you can then use for stuff like moving special effects. Sometimes you'd have to use
these instead of GetACaster , but note that then the Caster should not be recycled. AddCaster simply creates a caster you
can then move. AddCasterFacing creates a caster and makes sure it faces
a specific direction, this is important because of eye candy reasons. You can't change a unit facing instantly and the
only way to get a missile that points to an angle without having eye candy issues is to use AddCasterFacing.
CreateCaster is AddCasterFacing but allows you to set starting position by x and y.
Whenever you read damageoptions, remember that all the information related to damageoptions
is in the Appendix A , which is in the Damage and Target Options trigger.
* Damage and projectile functions
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
Someday I decided to add damaging functions to the caster system, that was in the times when there
wasn't any way to make flexible damage with triggers considering bounty and experience.
Later Blizzard made patch 1.17 and thanks to my suggestions in bnet forums, they added the damage
functions, so know the caster system functions play more of an auxiliar role to help blizzard's damage
natives to do what you want.
Projectile functions are a result of the lack of moving special effect options, they lack something
and it is that because of the unit movement speed limit they can't be faster than 522, but they can
be really useful.
___________________________________________________________________________________________________________________________________________________________________________________________________________
function GetDamageFactor takes unit u,attacktype a, damagetype d returns real
function GetDamageFactorByOptions takes unit hurter, unit target, integer DamageOptions returns real
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* GetDamageFactor Will return the factor for specific damage and attack types for the unit u.
* GetDamageFactorByOptions uses an unit argument that determines the unit that would do the
damage, and uses DamageOptions, DamageOptions are a conjunctions of flags that determine what
to do, see Appendix A for more information.
___________________________________________________________________________________________________________________________________________________________________________________________________________
function DamageUnitByTypes takes unit hurter, unit target, real dmg, attacktype attT, damagetype dmgT returns boolean
function DamageUnitByOptions takes unit hurter, unit target, real dmg, integer DamageOptions returns boolean
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* The normal target damage function , but calls the last functions.
* I had to create DamageUnitByTypes, Blizzard's native ignores the damage type, this function
won't ignore the damage type, and once blizzard fixes the bug it will be deprecated.
* Alternativelly DamageUnitByOptions, uses a DamageOptions argument, you have to see
Appendix A for more information.
___________________________________________________________________________________________________________________________________________________________________________________________________________
function UnitDamageUnitTimed takes unit hurter, real damageps, real damageperiod, real duration, unit target, string modelpath, string attachPointName, attacktype attT, damagetype dmgT returns nothing
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* Will add damage per second for an unit to a unit, hurter is the damaging unit, duration
is self explanatory, damageps is the damage per second, target is the unit to affect,
damageperiod is the time before the unit takes the damage again note that damageperiod
values lower than ~0.4 will be considered ~0.4
* You can set modelpath to a special effect and attachPointName is the attachment point,
but if you don't want it to have a special effect use "" for the modelpath and attachment.
* Last 2 options are the attacktype and damagetype values, check appendixex B and C for more
information.
* Also you can turn this function into a healing per second by using a negative value for damageps.
_____________________________________________________________________________________________________________________________________________________________________
function DamageUnitsInAOEEx takes unit hurter, real damage, real x, real y, real radius, boolean affectallied, integer DamageOptions returns nothing
function DamageUnitsInAOEExLoc takes unit hurter, real damage, location loc, real radius, boolean affectallied, integer DamageOptions returns nothing
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* Perform AOE damage on an area for an unit useful when UnitDamagePoint doesn't have enough
options for you.
* if affectallied is true will affect non-enemy
units too. x and y or loc determine the center of the circle, and radius the radius
* DamageOptions are a conjunctions of flags that determine what to do, see Appendix A for
more information.
* See the Light of Redemption Sample Trigger for an usage example.
______________________________________________________________________________________________________________________________________________________________________
function DamageUnitGroupEx takes unit hurter, real damage, group targetgroup, integer DamageOptions returns nothing
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* Performs damage on an unit group, it will affect every unit in the unit group, depending
on the damage options, and remember that the set bj_wantDestroyGroup = true will work for
this function.
* Keep in mind that DamageTrees() doesn't work with this.
______________________________________________________________________________________________________________________________________________________________________
function AddAreaDamagerForUnit takes unit hurter, string modelpath, string targetart, string targetattach, real x, real y, real damage , real damageperiod, real duration, real area, boolean affectallies, integer DamageOptions returns unit
function AddAreaDamagerForUnitLoc takes unit hurter, string modelpath, string targetart, string targetattach, location loc, real damage , real damageperiod, real duration, real area, boolean affectallies, integer DamageOptions returns unit
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* Will create a special effect at a point with a duration and each second will damage the units
in an area around the effect, the effect is a unit and that unit is returned by the function, you
can use the currentcaster variable to get the effect unit, you can move the unit or change its
size / color.
* targetart is the special effect that appears on the units affected by the damage,
targetattach is the attachment point for that effect and damageperiod is the period of time
before each moment the damage does effect
* You can also use a huge number (1000000) as duration and call RecicleCaster() on it
to stop it, instead of making it have a fixed duration.
* DamageOptions are a conjunctions of flags that determine what to do, see Appendix A for
more information.
* See the Poisonous Weeds and the The deadly fire star sample triggers to see usage
examples on this function.
* You can use 0 area, and 0 damage, to use this function for just the effect, it would allow
you to easily have effects with duration, the 'Deadly Fire Star Effect' sample does that.
_______________________________________________________________________________________________________________________________________________________________________________________________
function ProjectileLaunch takes string modelpath, real speed, real arc,real x1, real y1, real z1, real x2, real y2, real z2 returns nothing
function ProjectileLaunchLoc takes string modelpath, real speed, real arc, location loc1, real z1, location loc2, real z2 returns nothing
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* Will Create A missile effect from place1 to place2, x1 and y1 determine the starting
position of the missile, z1 determines its starting flying height, x2 and y2 the target
position and z2 the target height.
* Modelpath determines the model used, must be betweeen quotes and needs to have double '\',
example: "abilities\\weapons\\DemolisherMissile\\DemolisherMissile.mdl".
* Speed and Arc should work like the missiles in the object editor, Arc is a real number
between 1 and 0. ex: 0.15
IMPORTANT : This is valid for every projectile function, There are 2 modes for the projectile
functions, one looks really good and allows any positive speed value,
but is bad efficiency-wise, the other one looks ok, and will only happen with speed that are
less than or equal to 522 (and greater than 0).
Speed is a positive real number, when speed is less than or equal to 522, the projectile
function would use the normal unit movement, but if it is greater than 522 it will use a timer
with gamecache allowing high speeds, but the second mode has one problem: It is not too
efficient and it would cause problems if you use way too many of them at the same time.
* The ProjectileLaunchLoc uses location arguments instead of x and y coordinates, loc1
determines the starting position and loc2 the target position.
* Stops threads (The trigger that uses this function will wait until the missile effect
reaches to the target)
_____________________________________________________________________________________________________________________________________________________________________________________________________________________________
function ProjectileLaunchEx takes player owner, string modelpath, real scale, integer red, integer green, integer blue, integer alpha, real speed, real arc,real x1, real y1, real z1, real x2, real y2, real z2 returns nothing
function ProjectileLaunchExLoc takes player owner, string modelpath, real scale, integer red, integer green, integer blue, integer alpha, real speed, real arc, location loc1, real z1, location loc2, real z2 returns nothing
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* Extended versions of ProjectileLaunch, let you specify the scale and vertex coloring used
by the projectily, colors and alpha (opacity) are in integers from 0 to 255.
__________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________
function DamagingProjectileLaunchAOE takes unit hurter, string modelpath, real speed, real arc, real x1, real y1, real z1, real x2, real y2, real z2, real aoeradius, real damage, boolean affectallied, integer DamageOptions returns unit
function DamagingProjectileLaunchAOELoc takes unit hurter, string modelpath, real speed, real arc, location loc1, real z1, location loc2, real z2, real aoeradius, real damage, boolean affectallied, integer DamageOptions returns unit
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* Like the previous function but doesn't stop the thread and will deal damage for the
hurter unit to the units in the target area, will return the missile unit used, damage,
aoeradius, affectallied and DamageOptions work like the AOEDamage functions.
* See the Grenade and Stone of Lightning sample triggers to see usage examples of these functions.
* Returns the missile unit created, you can change its scale / vertex coloring with using
the returned unit, you can also use the unit variable currentcaster .
_______________________________________________________________________________________________________________________________________________________________________________________________
function ProjectileLaunchKill takes player owner, string modelpath, real speed, real arc, real x1, real y1, real z1, real x2, real y2, real z2 returns unit
function ProjectileLaunchKillLoc takes player owner, string modelpath, real speed, real arc, location loc1, real z1, location loc2, real z2 returns unit
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* Will create a missile effect, won't stop the thread and will kill the missile unit once it
reaches the target position, use a dead event to know when this kind of missile effect comes
to its target.
* Returns the missile unit created, you can change its scale / vertex coloring with using
the returned, unit you can also use the unit variable currentcaster.
______________________________________________________________________________________________________________________________________________________________________________________________________________________________________________
function ProjectileLaunchToUnit takes string modelpath, real speed, real arc,real x1, real y1, real z1, unit target, real zoffset returns nothing
function ProjectileLaunchToUnitLoc takes string modelpath, real speed, real arc, location loc1, real z1, unit target, real zoffset returns nothing
function ProjectileLaunchToUnitEx takes player owner, string modelpath, real scale, integer red, integer green, integer blue, integer alpha, real speed, real arc, real x1, real y1, real z1, unit target, real zoffset returns nothing
function ProjectileLaunchToUnitExLoc takes player owner, string modelpath, real scale, integer red, integer green, integer blue, integer alpha, real speed, real arc, location loc1, real z1, unit target, real zoffset returns nothing
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* The same as ProjectileLaunch but uses an unit argument instead of a second point, zoffset is
the height difference between the target flying height and the final height for the projectile,
like ProjectileLaunchEx, ProyectileLaunchToUnitEx is the extended version.
* The projectile is homing, if you don't want it to be homing use the normal projectile functions
and make them go to the position of the target unit.
______________________________________________________________________________________________________________________________________________________________________________________________________________________________________________
function DamagingProjectileLaunchTarget takes unit hurter, string modelpath, real speed, real arc, real x1, real y1, real z1, unit target, real zoffset, real damage, attacktype attT, damagetype dmgT returns unit
function DamagingProjectileLaunchTargetLoc takes unit hurter, string modelpath, real speed, real arc, location loc, real z1, unit target, real zoffset, real damage, attacktype attT, damagetype dmgT returns unit
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* Like the last functions but won't stop the thread and will hurt the target unit.
* attT and dmgT determine the attack and damage types, (like the ones that blizzard functions
take, for more info see appendixes B and C.
* Use the returned unit or the currentcaster variables to know the projectile.
* See the Green Fire balls trigger sample for an usage example for this function.
* Appendix E: Deprecated functions:
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
These functions were used in the pre patch 1.17 caster system versions, I left them
here for compatibility, and in case you find an use for them.
___________________________________________________________________________________________________________________________________________________________________________________________________________________________________________
function DamageUnit takes player hurter, real damage, unit victim returns boolean
function DamageUnitTimed takes player owner, real damageps, real duration, unit target, string modelpath, string attachPointName returns nothing
function DamageUnitTimedEx takes player owner, real damageps, real damageperiod, real duration, unit target, string modelpath, string attachPointName returns nothing
function DamageUnitsInAOE takes player hurter, real damage, real x, real y, real radius, boolean affectallied returns nothing
function DamageUnitsInAOELoc takes player hurter, real damage, location loc, real radius, boolean affectallied returns nothing
function DamageUnitGroup takes player hurter, real damage, group targetgroup returns nothing
function AddDamagingEffect takes player owner, string modelpath, real x, real y, real damageps , real duration, real area, boolean affectallies returns unit
function AddDamagingEffectLoc takes player owner, string modelpath, location loc, real damageps , real duration, real area, boolean affectallies returns unit
function AddDamagingEffectEx takes player owner, string modelpath, string targetart, string targetattach, real x, real y, real damage , real damageperiod, real duration, real area, boolean affectallies returns unit
function AddDamagingEffectExLoc takes player owner, string modelpath, string targetart, string targetattach, location loc, real damage , real damageperiod, real duration, real area, boolean affectallies returns unit
function ProjectileLaunchDamage takes player owner, string modelpath, real speed, real arc, real x1, real y1, real z1, real x2, real y2, real z2, real aoeradius, real damage, boolean affectallied returns unit
function ProjectileLaunchDamageLoc takes player owner, string modelpath, real speed, real arc, location loc1, real z1, location loc2, real z2, real aoeradius, real damage, boolean affectallied returns unit
function ProjectileLaunchToUnitDamage takes player owner, string modelpath, real speed, real arc, real x1, real y1, real z1, unit target, real zoffset, real damage returns unit
function ProjectileLaunchToUnitDamageLoc takes player owner, string modelpath, real speed, real arc, location loc1, real z1, unit target, real zoffset, real damage returns unit
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
A good thing about them is that they only need a player argument instead of an unit, they
will use the defaults on the top of the caster system functions.
Caster System comes with functions like AddAreaDamagerForUnit, that use a effect that
deals damage periodically to the area, It has some problems:
- The period of time is inaccurate and limited
- They use a big loop
- When used in groups they are imbalanced because 2 or more AreaDamagers can damage
the same unit, so you have to make sure their area is small enough so a unit is damaged
by only one of them.
- When used in groups they lag the game out.
I decided to add DamagerGroups as an alternative when you have these issues, specially
to be used in groups, they are also really flexible unlike that AddAreaDamagerForUnit and
derivated functions, and I was trying to simulate object oriented programming this time.
* DamagerGroup
¯¯¯¯¯¯¯¯¯¯¯¯¯¯
DamagerGroup is the main class here, it determines the damage, damage period, area and
damageoptions used.
We can pretend DamagerGroup inherits from Timer.
________________________________________________________________________________________________________________________________________________
function DamagerGroup_Create takes unit hurter, real damage, real damageperiod, real area, integer DamageOptions returns timer
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
Constructor of DamagerGroup.
Creates a DamagerGroup for unit hurter (this means the damage will credit that unit) ,
damage is the damage it will do, damageperiod the Period of time in seconds for each damage
wave, area the Area of effect of the damage (per Damager) , and it uses DamageOptions as well.
The area is used for each Damager in the group.
Note that this function will not make any damage by itself, you'd have to add Damagers
to the DamagerGroup before it has any effect.
The minimum damageperiod is 0.01, but note that a damageperiod of 0.01 means a periodic
timer of 0.01 seconds that is executed and executed every time, then picks a lot of units
uses gamecache and damage options, don't expect to use low values like 0.03, 0.02 and 0.01
without Lag Problems. I would actually recomend 0.2 as the minimum for DamagerGroups.
________________________________________________________________________________________________________________________________________________
function DamagerGroup_AddDamager takes timer DamagerGroup, string modelpath, real x, real y, real LifeSpan returns unit
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
Now this is the way to use DamagerGroups , with this function you create a damager for the
DamagerGroup, this created Damager is returned and you can use the Damager functions on it.
DamagerGroup is the timer that is returned by DamagerGroup_Create , in other words it is the
DamagerGroup that you want to have a new Damager
modelpath is the path of the model for the Damager .
real x and y are the coordinates of the point you want to locate the Damager
LifeSpan is the maximum duration of the Damager, after this time in seconds, the Damager
is self destructed, if you use 0 here it will last forever or until you change its lifespan,
remove the Damager, or the DamagerGroup is Destroyed.
________________________________________________________________________________________________________________________________________________
function DamagerGroup_AddDamagerLoc takes timer DamagerGroup, string modelpath, location loc, real LifeSpan returns unit
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
Does the same as DamagerGroup_AddDamager, but uses a location (point) as argument instead
of coordinates.
________________________________________________________________________________________________________________________________________________________
function DamagerGroup_Update takes timer DamagerGroup, unit hurter, real damage, real damageperiod, real area, integer DamageOptions returns nothing
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
You may want to change a DamagerGroup 's options, use this function for that, DamagerGroup
is the DamagerGroup you want to change (duh) , the rest works like the constructor.
This will affect the DamagerGroup and because of that it affects the Damagers used by the
DamagerGroup .
________________________________________________________________________________________________________________________________________________________
function DamagerGroup_Destroy takes timer DamagerGroup returns nothing
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
Deconstructor of DamagerGroup.
Destroys a DamagerGroup, meaning this will remove any memory used by the DamagerGroup,
will also Remove the Damagers used by the DamagerGroup.
________________________________________________________________________________________________________________________________________________________
function DamagerGroup_SetLifeSpan takes timer DamagerGroup, real lifespan returns nothing
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
Sets the Duration in seconds of the DamagerGroup, meaning that after (lifespan) seconds
the DamagerGroup will call DamagerGroup_Destroy over itself, Each time you use this function
the previous times you used it over that DamagerGroup will be ignored.
________________________________________________________________________________________________________________________________________________________
function DamagerGroup_AutoDestruct takes timer DamagerGroup, boolean auto returns nothing
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
Sets a DamagerGroup to destroy itself once it has no Damagers remaining, you should set this
to true only after the DamagerGroup got some Damagers that have lifespan durations.
* Damager
¯¯¯¯¯¯¯¯¯
Damager is a class that is used by DamagerGroup, Damagers are the effects that make the
DamagerGroup deal damage over the units around (area of DamagerGroup) of each Damager.
Let's pretend that Damager is a class that inherits from unit, well it actually inherits
from the Caster System's casters but are they a class ? Actually nothing here is a class
there is no Object Oriented Programming in JASS so I don't know what the hell I am talking
about. Its constructor is DamagerGroup_AddDamager , but how could it have a constructor
if it is not a class afterall ? Whatever is happening here is a great lie.
You can freely use unit natives on Damagers like changing colors, position, scale and
flying height.
________________________________________________________________________________________________________________________________________________________
function Damager_Remove takes unit Damager returns nothing
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
Deconstructor, will Destroy a Damager (it will actually recycle the caster) It will
destroy its effect, and it will no longer do damage nor be considered a part of a
DamagerGroup.
________________________________________________________________________________________________________________________________________________________
function Damager_SetAbility takes unit Damager, integer abilid, integer l returns nothing
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
I thought it might be useful to be able to add abilities to Damagers, why? I don't know
but I can think of at least two uses for this:
- Damagers have a disadvantage towards AddAreaDamagerForUnit and derivates, and it is that
they don't have target effects, I didn't add them because they would have been major anti
efficiency features, but you can use an ability to create the effects.
- You may also want to use an extra effect for the Damagers themselves.
Why use this instead of UnitAddAbility ? because whatever you add with UnitAddAbility
will not be removed when recycling the caster, and after that the caster might be used
for something else and it will have the ability you added to it, let's say it will screw
everything.
________________________________________________________________________________________________________________________________________________________
function Damager_SetLifeSpan takes unit Damager, real lifespan returns nothing
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
Will change a Damager's LifeSpan, in other words the Damager will be self destructed
after LifeSpan seconds, using this will make it ignore the LifeSpan argument you gave when
creating the Damager, and it will also ignore any previous usage of this function over that
Damager. Using 0 for lifespan means it will never be selfdestructed.
* Collision Missiles
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
Note: Collision missiles' creation is kind of blocked if done too close to the map bounds.
Collision Missiles are projectiles, but they don't use parabolic movement, they have collision
and you can detect when the projectiles hit an unit, and you can also destroy Collision Missiles
when you need to.
_______________________________________________________________________________________________________________________________________________________________________________________________________________________________________________
function CollisionMissile_Create takes string MissileModelPath, real x, real y, real dirangle, real speed, real AngleSpeed, real MaxDist, real height, boolean UseNewCaster, real Collision, code OnImpact returns unit
function CollisionMissile_CreateLoc takes string MissileModelPath, location loc, real dirangle, real speed, real AngleSpeed, real MaxDist, real height, boolean UseNewCaster, real Collision, code OnImpact returns unit
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
As always, CollisionMissile_CreateLoc is the version that uses point instead of coordinates,
MissileModelPath is the path of the model of the missile.
* real dirangle is the Angle of the Direction of the missile
* real speed is the movement speed of the missile
* real AngleSpeed should often be 0, is the angle increment per second for the direction, so you
can make Impact missiles that don't go in straight lines.
* real MaxDist is Seriously an important value and determines the duration of the missile, it
determines the maximum wc3 distance units the missile can travel.
* real height is just the flying height of the missile
* boolean UseNewCaster .- This is blizzard's fault, since for no reason It is impossible to
instantly change a unit's facing angle, and to have a unit facing an angle instantly you have to
create it, when UseNewCaster is true it will create a new caster, it will be slow in masses but
it will look right, if UseNewCaster is false it will be efficient in masses but the initial
facing angle of the missile won't be right most of the times.
Aboid to use true unless the model for the missile has a noticeable front.
* real Collision the collision size of the missile.
* code OnImpact , most important part of CollisionMissile is their collision and the ability they
give to you to know when they hit something, use this argument to specify a function to be called
when the projectile hits a unit.
Because of this, you would have to use Pure JASS if you want to call CollisionMissile_Create .
This function will be called whenever the missile encounters a unit, Use GetTriggerUnit()
(Triggering unit) to now the unit that was hit by the missile.
NOTE: It will ALSO be executed when the Missile is destroyed because of the Maximum distance,
Map Bounds or manually destroyed by user, in that case GetTriggerUnit() will be null . I made
it because I thought it would be good to be able to detect that.
* The returned unit is the CollisionMissile, use it to Attach Information on it, change team
color, scale or other stuff.
Check The Sample Trigger Inferno for an example about CollisionMissiles.
_____________________________________________________________________________________________________
function GetTriggerCollisionMissile takes nothing returns unit
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
You detected when the missile hit a unit, great, but you may need to know which Missile Hit
the unit, then you use this Event Response.
This function should be able to survive waits, but I won't recomend waits on functions called
with collisions, because the missile might be destroyed in the process and that would cause caos.
_____________________________________________________________________________________________________
function CollisionMissile_Destroy takes unit m returns nothing
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
This is useful when you want Non Homing projectiles that hit units and explode because of that.
You may also find it useful on other situations, What this function does is destroying a
CollisionMissile , remember that the OnImpact function will be called when this happens.
_____________________________________________________________________________________________________
function CollisionMissile_SetAngleSpeed takes unit m, real newAspeed returns nothing
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
Allows you to change a CollisionMissile's angle speed
_____________________________________________________________________________________________________
function CollisionMissile_SetSpeed takes unit m, real newspeed returns nothing
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
Allows you to change a CollisionMissile's Movement speed. As a matter of fact, negative speed
values are allowed and make the missiles go backwards
_____________________________________________________________________________________________________
function CollisionMissile_PeriodicFX takes unit m, string fx, real dur returns nothing
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
Adds an effect that spawns and is destroyed periodically to the effect, m is the collision
missile, fx is the path of the effect, and dur is the spawn delay.
* CollisionMissiles: Targets
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
CollisionMissiles may now have targets, when they have a target the collision missiles will
still advance to their speed, but will try to turn to the direction of the target, A
CollisionMissile must have a non zero AngleSpeed in order to be able to 'follow' targets.
_____________________________________________________________________________________________________
function CollisionMissile_SetTargetPoint takes unit m, real tx, real ty returns nothing
function CollisionMissile_SetTargetPointLoc takes unit m, location tloc returns nothing
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
Makes the CollisionMissile 'm' consider the given point as a target, the first one is the
coodinate version and the second one is the location version
_____________________________________________________________________________________________________
function CollisionMissile_SetTarget takes unit m, widget Target returns nothing
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
Makes the CollisionMissile consider the widget 'Target' as a its target, although you can use
destructables and items as widgets I suppose that this is only good for unit values.
_____________________________________________________________________________________________________
function CollisionMissile_ForgetTarget takes unit m returns nothing
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
Makes the CollisionMissile forget any target.
Of course, you can make a CollisionMissile change its target everytime you want.
//TESH.scrollpos=0
//TESH.alwaysfold=0
* Appendix A: Damage Options
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
Damage options are important when using aoe damage functions, they can help you specify a lot
of things, and are an addition for 1.17 , because the old functions got obsolette with 1.17
in case you didn't note.
Latelly I have been using them a lot to configure my spells too. Since they were invented for
the damage functions, they are called Damage Options, but they should have been called Target
options for their skill to help me configure immunity to spells.
One important note is that they can't really work in variables, you can't save Damage Options
in a variable and use it 6 seconds after you save them, each time you make some DamageOptions
they will keep the values till the next time you use damage options. Fortunally you can save
them in a variable if you use the variable INSTANTLY after saving it, no waits nor other functions
between the damage options and the functions please.
DamageOptions is often an integer, if you use 0 as value there, it works as a default gatherer
you can set the default attack type and damage type at the top of the caster system functions,
so if there are a pair of attack type and damage type you almost always use, you can set them to
whatever you want.
__________________________________________________________________________________________________________
function DamageTypes takes attacktype attT, damagetype dmgT returns integer
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
Specifies the Damage and Attack type (the ones used by blizzard's UnitDamage functions ) ,
they determine how the damage acts on different armors and ethereal / magic immune and that stuff,
for more info on damage and attack types see Appendixes B and C.
Example : DamageTypes(ATTACK_TYPE_CHAOS, DAMAGE_TYPE_UNIVERSAL)
Makes the attack type chaos and the damage universal.
* flags:
__________________________________________________________________________________________________________
function DamageException takes unittype Exception, real ExceptionFactor returns integer
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
Specifies a unit classification that uses a special factor for the damage, for more info about
unittypes (classifications) see appendix C, if factor is 0 then it doesn't harm the units with
that classification.
Example : DamageException(UNIT_TYPE_STRUCTURE,0.33)
Will Make structures take 33 percent of the given damage.
__________________________________________________________________________________________________________
function DamageOnlyTo takes unittype ThisUnitType returns integer
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
Makes the damage functions ignore any unit that doesn't have the given classification.
Example : DamageOnlyTo(UNIT_TYPE_GROUND)
Will make it so only ground units take the damage.
__________________________________________________________________________________________________________
function DamageIgnore takes unittype ThisUnitType returns integer
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
The opposite to DamageOnlyTo , units of this type will be ignored by the damage. (works the
same as using DamageException with 0 as factor)
Example : DamageIgnore(UNIT_TYPE_RESISTANT)
__________________________________________________________________________________________________________
function DontDamageSelf takes nothing returns integer
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
In the case the damage affects allies too, use this flag to avoid the damage to affect the
unit given as hurter
Example : DontDamageSelf()
__________________________________________________________________________________________________________
function DamageTrees takes nothing returns integer
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
The damage will also kill the trees in the area, this flag shouldn't be used by DamageGroupEx,
in that case it will surelly cause serious glitches, only use this on AOE damge functions.
Example : DamageTrees()
__________________________________________________________________________________________________________
function DamageOnlyVisibles takes nothing returns integer
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
Some spells shouldn't affect units that are invisible to the player,
use this flag in that case
Example : DamageOnlyVisibles()
__________________________________________________________________________________________________________
function DamageOnlyEnemies takes nothing returns integer
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
Will Force the function to ignore allied units, this makes it so it ignores even the affectallied
argument in the AOE damage functions.
Example : DamageOnlyEnemies()
You shouldn't it with DamageOnlyAllies() or DamageOnlyAllies(), it doesn't have any sense and
the bitflags would screw everything.
__________________________________________________________________________________________________________
function ForceDamageAllies takes nothing returns integer
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
Will Force the function to damage allied units, this makes it so it ignores even the affectallied
argument in the AOE damage functions.
Example : ForceDamageAllies()
You shouldn't it with DamageOnlyAllies() or DamageOnlyEnemies(), it doesn't have any sense and
the bitflags would screw everything.
__________________________________________________________________________________________________________
function DamageOnlyAllies takes nothing returns integer
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
Will Force the function to only affect units that are allied, damage allied units.
Example : DamageOnlyAllies()
You shouldn't combine it with ForceDamageAllies() or DamageOnlyEnemies(), it doesn't have any sense and
the bitflags would screw everything.
__________________________________________________________________________________________________________
function DamageAlliedFactor takes real fct returns integer
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
When a spell may damage allies as well as enemies, you can use this option so it performs
different damage to allied units, you can even give this option a negative factor so the spell
will heal allies but damage enemies.
Example : DamageAlliedFactor(0.5)
Would make allies take only 50% of the damage.
__________________________________________________________________________________________________________
function DamageFactorAbility1 takes integer spellid, real factor returns integer
function DamageFactorAbility2 takes integer spellid, real factor returns integer
function DamageFactorAbility3 takes integer spellid, real factor returns integer
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
Really useful damage options, with this you can make abilities that add damage factors for your
trigger spells, you can use 0 as factor so an ability gives immunity, 0.~ and you give the unit
resistances, or a number higher than 1 so you make the unit extra vulnerable, or a negative value
so the unit gets healed by the damage that uses that damage option.
Note that using a buff's rawcode would make it work with the buff instead of an ability, also
note that when an unit holds an item it counts as if it had the abilities of the item.
There are 3 functions, this is because using an option twice won't do any effect, it would
actually mess everything because I use bitflags for the damage options.
Example:
DamageFactorAbility1('A001',2)+DamageFactorAbility1('A002',-1)
IS TOTALLY WRONG, while
DamageFactorAbility1('A001',2) + DamageFactorAbility2('A002',-1)
Is right, it would mean that if the unit has A001, it will take double damage when effected with
that function, if it has A002 the unit will get healed by it. Note that if the unit had both
abilities, it would get healed with twice the damage value.
__________________________________________________________________________________________________________
function ConsiderOnlyDeadUnits takes nothing returns integer
function IgnoreDeadState takes nothing returns integer
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
Now this is a contradiction, these things are called DamageOptions but now there are DamageOptions
that allow you to pick dead units or even force you to pick dead units.
They are more useful when you use GetDamageFactorByOptions , thing is that this system went out
of context and besides of picking behaviour of damage functions it now servers for a lot of things
like making spells easier to configure.
Blade.dk was making a spell that was supposed to affect dead units. DamageOptions ignore dead units
automatically. So he asked me to do something to disable that.
The result, 2 new damage options, note that using the togetheri is a very stupid thing to do.
ConsiderOnlyDeadUnits makes the opposite to what damage options do by default- ignore dead units, if
you add this option the DamageOptions will ignore living units.
IgnoreDeadState will make it pick living or dead units without distinction
* Combining.
You can combine these flags in order to make the effect you want,
use the operator + for that, for example:
DamageOnlyTo(UNIT_TYPE_GROUND)+DamageException(UNIT_TYPE_STRUCTURE,0)
Will make it so the damage only affects ground units and doesn't affect buildings, will use the
default attack and damage types.
DamageTypes(ATTACK_TYPE_SIEGE,DAMAGE_TYPE_FORCE) + DamageTrees()
Will Do siege phisical damage and kill trees.
You CANNOT use a flag twice , for example:
DamageOnlyTo(UNIT_TYPE_UNDEAD)+DamageOnlyTo(UNIT_TYPE_STRUCTURE) WON'T WORK
I hope you understand this rather confusing stuff, if you need more help see samples that use
Damage Options and if that doesn't help pm Post your question in the Caster System forum.
The sample spells that use the options are:
- Grenade
- Stone of Lightning
- Deadly Fire Star
- Light of redemption
- Poisonous Weeds
* Saving and loading for later use:
As it was said, DamageOptions are temporal and can't be saved in a variable, however I made
some system to allow you to save them in variables or gamecache for later use.
_______________________________________________________________________________________________________
function CreateDamageOptions takes integer DamageOptions returns integer
function SetDamageOptions takes integer id, integer DamageOptions
function LoadDamageOptions takes integer id returns integer
function DestroyDamageOptions takes integer id returns nothing
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
Allows you to create saveable DamageOptions , CreateDamageOptions will create one of these
pseudo handles from some DamageOptions, likelly:
set somevar = CreateDamageOptions(DamageOnlyTo(UNIT_TYPE_UNDEAD)+DamageTrees())
So later when calling a caster system function that uses damage options and want to use the
saved value, use:
call DamageUnitsInAOEEx(GetTriggerUnit(),500,0,0,250,true,LoadDamageOptions(somevar))
These things work like handles so you have to destroy them if you will no longer use them
call DestroyDamageOptions(somevar)
You can also replace a saveable damage option with a new value
call SetDamageOptions(somevar,CreateDamageOptions(DamageOnlyTo(UNIT_TYPE_GROUND)+DontDamageSelf())
Strangelly enough, you could set a saveable damage option after destroying it before version 13.5 ,
I do not recommend doing so after that version.
Most of the times you won't need this at all, but for things like my templates system I
needed to create this.
* Damage Options functions:
_______________________________________________________________________________________________________________________
function IsDamageOptionIncluded takes integer DamageOptions, integer whichDamageOption returns boolean
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
If you are DamageOptions for a spell / trigger of yours and you might need to know if
some DamageOptions include a damageoption (for example DamageTrees() ) you might use this
function.
Example:
IsDamageOptionIncluded( -SomeDamageOptions- , DamageTrees() )
Will return true if the DamageOptions argument includes DamageTrees()
This will not work correctly with DamageOptions that use arguments. DamageOnlyEnemies(),
ForceDamageAllies() and DamageOnlyAllies() Will not work either.
In other words this function is only useful for DontDamageSelf(), DamageTrees()
and DamageOnlyVisibles()
* Appendix B : Attack Types
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
I'd rather suggest you to use variables for this, but unfortunally, blizzard screwed the
types disallowing us to make damage or attack type variables. So there are the instructions
for them in JASS, first thing is the thing you have to insert and second thing is the description
Attack types: (how they act depends on armor)
ATTACK_TYPE_NORMAL Spell
ATTACK_TYPE_MELEE Melee
ATTACK_TYPE_PIERCE Pierce
ATTACK_TYPE_SIEGE Siege
ATTACK_TYPE_MAGIC Magic
ATTACK_TYPE_CHAOS Chaos
ATTACK_TYPE_HERO Hero
I think that the attack type commonly used by spells is chaos, otherwise it is Magic-
¯¯¯¯¯
* Appendix C : Damage Types
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
I'd rather suggest you to use variables for this, but unfortunally, blizzard screwed the
types disallowing us to make damage or attack type variables. So there are the instructions
for them in JASS, first thing is the thing you have to insert and second thing is the description
Damage types: (how they act depends on tft magic things:
- magical damage is buffed by etheral and resisted by spell immunes (normal spells)
- phisical damage is resisted by ethereal and is able to affect spell immunes (passive spells)
- Universal is buffed by ethereal and can even affect spell immunes (ultimates)
DAMAGE_TYPE_FIRE Magical
DAMAGE_TYPE_COLD Magical
DAMAGE_TYPE_LIGHTNING Magical
DAMAGE_TYPE_DIVINE Magical
DAMAGE_TYPE_MAGIC Magical
DAMAGE_TYPE_SONIC Magical
DAMAGE_TYPE_FORCE Magical
DAMAGE_TYPE_DEATH Magical
DAMAGE_TYPE_MIND Magical
DAMAGE_TYPE_PLANT Magical
DAMAGE_TYPE_DEFENSIVE Magical
DAMAGE_TYPE_SPIRIT_LINK Magical
DAMAGE_TYPE_SHADOW_STRIKE Magical
DAMAGE_TYPE_NORMAL Phisical
DAMAGE_TYPE_ENHANCED Phisical
DAMAGE_TYPE_POISON Phisical
DAMAGE_TYPE_DISEASE Phisical
DAMAGE_TYPE_ACID Phisical
DAMAGE_TYPE_DEMOLITION Phisical
DAMAGE_TYPE_SLOW_POISON Phisical
DAMAGE_TYPE_UNKNOWN Universal
DAMAGE_TYPE_UNIVERSAL Universal
* Appendix D : UnitTypes (Unit classification)
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
UNIT_TYPE_HERO Hero
UNIT_TYPE_DEAD "Dead"
UNIT_TYPE_STRUCTURE Building
UNIT_TYPE_FLYING Unit is flying
UNIT_TYPE_GROUND Unit is ground *
UNIT_TYPE_ATTACKS_FLYING Unit can attack flying units
UNIT_TYPE_ATTACKS_GROUND Unit can attack ground units
UNIT_TYPE_MELEE_ATTACKER Unit has melee attack
UNIT_TYPE_RANGED_ATTACKER Unit has ranged attack
UNIT_TYPE_GIANT "Giant"
UNIT_TYPE_SUMMONED Unit is summoned (a.k.a takes damage from dispel)
UNIT_TYPE_STUNNED "Stunned" doesn't work as you might think though
UNIT_TYPE_PLAGUED "Plagued" doesn't work as you might think though
UNIT_TYPE_SNARED "Snared" doesn't work as you might think though
UNIT_TYPE_UNDEAD Undead
UNIT_TYPE_MECHANICAL Mechanical
UNIT_TYPE_PEON Worker
UNIT_TYPE_SAPPER Suicide
UNIT_TYPE_TOWNHALL Townhall
UNIT_TYPE_ANCIENT Ancient
UNIT_TYPE_POISONED 'Poisoned'
UNIT_TYPE_POLYMORPHED 'Polymorphed'
UNIT_TYPE_SLEEPING Sleeping
UNIT_TYPE_RESISTANT Resistant (has spell based of Resistant skin) *
UNIT_TYPE_ETHEREAL Etheral
UNIT_TYPE_MAGIC_IMMUNE Immune to magic
* Notes:
- Damage options read UNIT_TYPE_GROUND as if it meant ANY NON FLYING UNIT , this is to
fix a bug from blizzard that causes amphibious to do not be considered ground.
- Damage options read UNIT_TYPE_RESISTANT as units with a resistant skin based ability
OR Heroes , this is because HEROES ARE RESISTANT.
CSCache Module:
===============
* Attached Variables
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
Attached Variables allows you to put labeled variables on any handle (handles are units, items,
timers, triggers and a lot like those)
If you are familiar with Handle Variables, Attached variables are the same, but they allow things
like attached sets and tables. Tables are important because of their flezibility and because they
perform faster.
* Functions
¯¯¯¯¯¯¯¯¯¯¯
________________________________________________________________________________________________
function AttachInt takes handle h, string label, integer x returns nothing
function AttachReal takes handle h, string label, real x returns nothing
function AttachBoolean takes handle h, string label, boolean x returns nothing
function AttachString takes handle h, string label, string x returns nothing
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
So the idea is simple, an attached variable , is a variable that you can create in game, and
the best thing is that you can 'Attach' it to anything, you have this unit called Bob, you can
attach some stuff to it likelly:
call AttachInt(udg_bob, "age" , 345)
Or you have a trigger called "kill mephisto" , you can attach him a boolean:
call AttachBoolean(gg_trg_kill_mephisto,"already killed",true)
Anyways, just in case you don't know:
Integers (the AttachInt function uses them):
- numbers without decimal values ( 3 , 45 , -1 , 10993493)
- Wc3 Object 4 letter rawcodes ( 'Aloc' , 'h000' , 'S001' )
- Leet looking Hexadecimal values =) ( 0xffffffff , 0xccdecca , 0xFFFFFFFF , 0xEE32 , 0xA )
Reals:
- numbers with dots acting as decimal separators: 3.1416 , 0.3333 , 12536.35
- numbers without dots : 2 ,3 , 6, 1000 (they are actualyl integers, but if you save them as real they will be considered reals)
Booleans:
- true
- false
- null (equivalent to false)
Strings:
- Text between quotes : "hello" , "news flash" , "we are wasting time here"
- null
What is handle h?
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
A handle is an object, in wc3 a handle is a variable type that is not an integer, real, boolean,
string or 'code', for example, units are handles, items are handles too, triggers are handles,
points and unit groups are handles too.
So it means, that with these functions you can attach labels to virtually anything.
_________________________________________________________________________________________________________
function AttachObject takes handle h, string label, handle x returns nothing
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
This works exactly as attaching an integer or string or that stuff, but you can attach a handle
see above to know what can a handle be, ex:
call AttachObject( gg_trg_kill_mephisto,"mephisto",bj_lastCreatedUnit)
will attach the last created unit to the kill mephisto trigger, labeled as mephisto
call AttachObject( GetSummonedUnit() , "master", GetSummoningUnit() )
will attach the summoned unit to the summoning unit with the "master" label , so later you will be
able to know who summoned that unit.
_________________________________________________________________________________________________________
function GetAttachedInt takes handle h, string label returns integer
function GetAttachedReal takes handle h, string label returns real
function GetAttachedString takes handle h, string label returns string
function GetAttachedBoolean takes handle h, string label returns boolean
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
So, you attached something to something, you obviously did that for a reason, use these
functions to read stored values into something, a variable for instance.
set udg_bvar = GetAttachedBoolean( gg_trg_kill_mephisto, "already killed")
the bvar variable would be true.
Keep in mind that the LABELS ARE NOT CASE SENSITIVE , a label "A" will be the same as a label "a"
_________________________________________________________________________________________________________
function GetAttachedObject takes handle h, string label returns handle
function GetAttachedUnit takes handle h, string label returns unit
function GetAttachedItem takes handle h, string label returns item
function GetAttachedEffect takes handle h, string label returns effect
function GetAttachedLightning takes handle h, string label returns lightning
function GetAttachedImage takes handle h, string label returns image
function GetAttachedUbersplat takes handle h, string label returns ubersplat
function GetAttachedDestructable takes handle h, string label returns destructable
function GetAttachedTrigger takes handle h, string label returns trigger
function GetAttachedGroup takes handle h, string label returns group
function GetAttachedTimer takes handle h, string label returns timer
function GetAttachedTriggerAction takes handle h, string label returns triggeraction
function GetAttachedWidget takes handle h, string label returns widget
function GetAttachedRect takes handle h, string label returns rect
function GetAttachedRegion takes handle h, string label returns region
function GetAttachedTimerDialog takes handle h, string label returns timerdialog
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
When you use AttachObject, it is not as easy to recover the value as it would with the last
functions, you have to use a special function for each type, this is because while it is true that
you can store everything as a handle, you can use handle as everything (got it?) you have to first
make sure the parser is considering it what you want it to.
set udg_Mephisto = GetAttachedObject( gg_trg_kill_mephisto,"mephisto")
Will cause an ERROR
set udg_Mephisto = GetAttachedUnit( gg_trg_kill_mephisto,"mephisto")
Will work. (this is in case Mephisto is an unit variable)
According to me, the most used types for this are unit, item, effect (special effect),
destructable , trigger and group (unit group) , so I only made functions for them.
If you stored something weird, you'd have to make a typecasting function yourself! this is not
something difficult at all, actually.
Let's say you stored a rect for some reason , you'd have to do something like this
function GetRectFromTriggerMephisto takes nothing returns rect
return GetAttachedObject(gg_trg_kill_mephisto,"therect")
endfunction
it would work , anyways you can have whatever you like as function name, arguments and contents
the return is needed to typecast the handle value to the value you need.
______________________________________________________________________________________________________________
function CleanAttachedVars takes handle h returns nothing
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
I just forgot to tell you that these variables use memory , if you care about efficiency you
should clean them once you don't need them anymore there are two ways, one is by using
this function.
call CleanAttachedVars(gg_trg_kill_mephisto)
CleanAttachedVars will just remove whatever was attached to the object, I would only reccomend
to do this before destroying or removing the object. Note that I mean this would remove the
created variables, this won't remove the things that the variables are pointing out.
Another way to clean the memory used by these variables, is to just use the Attach functions,
I designed them to be intelligent, if you use variables like:
- For integers and reals use 0 to clean the variable
- For booleans use false to clean the variable
- For strings use "" to clean the variable
- For Objects use null to clean the variable
BTW whenever you read a variable that was cleaned it will return those values. ^
* Tables
¯¯¯¯¯¯¯¯
Table is a class (yeah right) that allows you to, well have tables, you generally use the
table to set a field to a value for example. They are actually Hash tables, but was crazy
when naming this.
Basically tables act the same as when using attached variables but instead of specyfing
a handle (object) you specify the table.
The magic stuff is that you can create / destroy tables in game and you can copy the
contents of a table to another. But this procedure requires you to first specify the fields
to copy and their types using a FieldData "object" (See bellow) Anyways, keep in mind that
copying stuff is optimized to the max. But it would still be lag friendly if you copy a
great quantity of data.
Also, you can access a handle's attached variables with a table. Have I said that I made
tables because I found out that Attached Variables / Handle Variables are slow because of
calling I2S(CS_H2I()) everytime? In other words, using tables is FASTER than using Attach
variables ( But the speed difference is only meaningful when you use a lot of them without
any wait)
The optimization a table can allow is good when you have a periodic timer that keeps
using Attached Variables, you can instead attach a table to the timer (Using AttachString
and GetAttachedString) and just use the table instead of reading attached stuff of the timer.
Without the need of attaching a table you can also read/write attached variables using
GetAttachmentTable, which is also faster.
Basically Use tables when you have to use tables, tables are great for having some non
declared variables if you just use labels between "(" ")" or "[" "]" for example:
SetTableInt("[Variables]","UnitId",'hfoo')
Tables are also good since you can create/
destroy them on the fly, you can attach a table to a unit and you just use the spell/system
specific name to the label where the table is attached and then just flush the table and
the label. This is helpful when a spell attach a lot of information to a public unit or
any handle, and you want to flush all that information when you don't need it.
Finally for intense operations that involve a periodic timer that runs a lot of times
using a table would be faster than using a lot of attached variables.
Note: Be careful with table functions: make sure you never send "" or null string as table
or field argument, that will cause crashes
__________________________________________________________________________________________________
function NewTable takes nothing returns string
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
First of all, tables ARE strings. NewTable will create a new string that you are free to
use as table (It would actually be a number) . But I must say, that you can use any string
with the other table functions, but please I recomend that any table name that is not
acquired with this function is used between "(" ")" or "[" "]"
__________________________________________________________________________________________________
function NewTableIndex takes nothing returns integer
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
Does the same as NewTable but it returns an integer (NewTable() just calls this function
and converts the returned integer to string) To store table pointers in gamecache / arrays
it would be better to use integers since strings are slower. But, obviously you will need to
use I2S() on the returned value in order to be able to use the table on other functions.
__________________________________________________________________________________________________
function GetAttachmentTable takes handle h returns string
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
As I said you can write/read attached variables with the table functions, use this to
get the table that is used by handle. This table is destroyed automatically whenever
CleanAttachedVars is used on the handle.
__________________________________________________________________________________________________
function DestroyTable takes string table returns nothing
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
This function should only be used on tables created by NewTable, although it is safe
to use it on other tables, it would only do the same thing ClearTable does, but will take
some extra execution time.
Basically cleans the contents of a table. And allows NewTable to return it again.
Storing stuff on the table after destroying it is possible but it is a BAD idea,
because NewTable could return the table that is supposed to be destroyed again, and it
could be used by other spell/system/trigger but with already set values and there is the
possibility of conflict.
IF you just want to clear a table that was created by NewTable but plan using it again
later, use ClearTable:
__________________________________________________________________________________________________
function ClearTable takes string table returns nothing
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
Cleans the contents of a table, if the table was created by NewTable, it WILL NOT, make
it available to be returned by NewTable.
You can also use it on non-created tables and Attachment Tables, consider that using
ClearTable on a handle's attachment table WILL NOT clear Attached Sets.
__________________________________________________________________________________________________
function SetTableBoolean takes string table, string field, boolean val returns nothing
function SetTableInt takes string table, string field, integer val returns nothing
function SetTableReal takes string table, string field, real val returns nothing
function SetTableString takes string table, string field, string val returns nothing
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
Set values of a table's field, false, 0 and "" will "flush" the field so it doesn't take
more memory. Yeah, they work like the Attached Variables stuff
__________________________________________________________________________________________________
function GetTableBoolean takes string table, string field returns boolean
function GetTableInt takes string table, string field returns integer
function GetTableReal takes string table, string field returns real
function GetTableString takes string table, string field returns string
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
Read table values, If that field was never set they return false, 0 or "" . Note:
Although you can use table functions to access Attached Variables, you can't Use
FromSetElement with GetTableInt.
__________________________________________________________________________________________________
function SetTableObject takes string table, string field, handle val returns nothing
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
You can set any object inside a table. Use null to "flush" the field so it doesn't take
memory. Like attached variables you can use it to set anything on that field, from units to
effects or whatever handle you like. But the number of preset GetTableXXXX functions is
limited like AttachableVariables.
__________________________________________________________________________________________________
function GetTableObject takes string table, string field returns handle
function GetTableUnit takes string table, string field returns unit
function GetTableEffect takes string table, string field returns effect
function GetTableLightning takes string table, string field returns lightning
function GetTableUbersplat takes string table, string field returns ubersplat
function GetTableImage takes string table, string field returns image
function GetTableItem takes string table, string field returns item
function GetTableDestructable takes string table, string field returns destructable
function GetTableWidget takes string table, string field returns widget
function GetTableGroup takes string table, string field returns group
function GetTableRect takes string table, string field returns rect
function GetTableRegion takes string table, string field returns region
function GetTableTimer takes string table, string field returns timer
function GetTableTrigger takes string table, string field returns trigger
function GetTableTimerDialog takes string table, string field returns timerdialog
function GetTableTriggerAction takes string table, string field returns triggeraction
function GetTableBoolExpr takes string table, string field returns boolexpr
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
These work the same as the Attached Variables' GetAttachedXXXX . Note: FromSetElement
won't work with table functions.
________________________________________________________________________________________________
function HaveSetField takes string table, string field, integer fieldType returns boolean
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
Returns true if the fiel contains a value different from 0, false, null, or ""
(depending on the type) it is worthless to use this with boolean, since it would be the
same as reading the boolean value.
fieldType is the type of the field:
bj_GAMECACHE_BOOLEAN : Boolean (Totally worthless)
bj_GAMECACHE_INTEGER : Integer or Object
bj_GAMECACHE_REAL : Real
bj_GAMECACHE_STRING : String
For Example: if (HaveSetField("(variables)","Unit",bj_GAMECACHE_INTEGER)) then
Would use the stuff inside the if then block only in the case a non-null value was stored
in the "Unit" field of the "(variables)" table.
__________________________________________________________________________________________________
function CopyTable takes integer FieldData, string sourceTable, string destTable returns nothing
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
Copies values of fields from a table to the fields of another table. You first need a
FieldData object to do so (See bellow) . The more Fields a FieldData object points, the more
time this operation will take (But this is way faster than doing so manually) And Yes, you
can use this in conjunction with GetAttachmentTable to copy values of the AttachedVariables
of a handle to the AttachedVariables of another handle.
* FieldData
¯¯¯¯¯¯¯¯¯¯¯
FieldData is an object that was created for the only purpose of allowing to copy tables,
to copy a table, CopyTable first has to know which fields of which type to copy. So a
FieldData object contains all that information.
_____________________________________________________________________________________________________
function FieldData_Create takes nothing returns integer
function FieldData_Destroy takes integer fielddata returns nothing
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
Create a FieldData Object or Destroy a FieldData Object, the integer returned by
FieldData_Create is the one that should be used with Destroy , AddField or CopyTable
______________________________________________________________________________________________________
function FieldData_AddField takes integer fielddata, string field, integer valueType returns nothing
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
This is the important function that allows you to determine which fields to copy.
valuetype is an integer, specifically:
bj_GAMECACHE_BOOLEAN
bj_GAMECACHE_INTEGER
bj_GAMECACHE_REAL
bj_GAMECACHE_UNIT
bj_GAMECACHE_STRING
Note: Objects are actually integers internally.
Example:
In this example, function First is executed on Map initialization , and function Second
is executed after a unit does something.
function First takes nothing returns nothing
local integer i=FieldData_Create()
local string T1=NewTable()
call FieldData_AddField(i,"xVel",bj_GAMECACHE_REAL)
call FieldData_AddField(i,"yVel",bj_GAMECACHE_REAL)
call FieldData_AddField(i,"Level",bj_GAMECACHE_INTEGER)
call SetTableReal(T1,"xVel" ,100.0)
call SetTableReal(T1,"yVel" ,200.0)
call SetTableInt( T1,"Level",1)
call SetTableString("[Example]","Table1",T1)
call SetTableInt("[Example]","TheFieldData",i)
endfunction
function Second takes nothing returns nothing
local string T1=GetTableString("[Example]","Table1")
local integer i =GetTableInt("[Example]","TheFieldData")
local string T2=GetAttachmentTable( GetTriggerUnit() )
call CopyTable(i,T1,T2)
endfunction
Lame example indeed, but I just managed to use as much of these functions as possible.
The First function will create a FieldData Object and a table. It will also use a table
"(Example)" to save values for later (Variables are not needed anymore=) , The Second
function will simply copy the values of the First table to the "attachment table" of the
Triggering Unit.
The result is that Triggering unit will have these attached variables:
"xVel" 100.0
"yVel" 200.0
"Level" 1
* Lower Level Table Handling
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
_____________________________________________________________________________________________________
function CSCache takes nothing returns gamecache
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
You can always use CSCache to get the gamecache where tables are stored and use it directly with
gamecache natives, probably for speed and that kind of things.
Calls to CSCache() will be converted to usage of a variable when you use the optimizer on your map
(Should use the clean useless code option)
This is important to know cause you can get rid of Table usage and use gamecache directly in some
situations, and it will be 6% faster (not really a lot)
For every table function but SetTableObject , GetTableObject and GetTable(SomeHandle) you can use
gamecache natives directly instead.
_____________________________________________________________________________________________________
variable udg_cscache
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
As long as you are sure that CSCache() was called already you can save some time and use
udg_csache instead
___________________________________________________________________________________________________________________
native StoreInteger takes gamecache cache, string missionKey, string key, integer value returns nothing
native StoreReal takes gamecache cache, string missionKey, string key, real value returns nothing
native StoreBoolean takes gamecache cache, string missionKey, string key, boolean value returns nothing
native StoreString takes gamecache cache, string missionKey, string key, string value returns boolean
native GetStoredInteger takes gamecache cache, string missionKey, string key returns integer
native GetStoredReal takes gamecache cache, string missionKey, string key returns real
native GetStoredBoolean takes gamecache cache, string missionKey, string key returns boolean
native GetStoredString takes gamecache cache, string missionKey, string key returns string
native FlushStoredMission takes gamecache cache, string missionKey returns nothing
native FlushStoredInteger takes gamecache cache, string missionKey, string key returns nothing
native FlushStoredReal takes gamecache cache, string missionKey, string key returns nothing
native FlushStoredBoolean takes gamecache cache, string missionKey, string key returns nothing
native FlushStoredUnit takes gamecache cache, string missionKey, string key returns nothing
native FlushStoredString takes gamecache cache, string missionKey, string key returns nothing
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
The only requisite is that you use the gamecache returned by CSCache() with these natives and you can access
the data used by tables faster. And use the table string in the missionKey argument
For example you can use 'call StoreInteger(CSCache(),"[Hello]","a",34)' instead of 'call SetTableInt("[Hello]","a",34)
It is gonna be kinda faster, notice that this should be used only on speed critical operations, and that the
difference itself is not that big unless there are plenty of calls.
When there are many calls you can save CSCache() in a variable, but notice that it is a worthless optimization if
you are going to use the optimizer on it.
For storing/retrieving objects it is better to use the tables' functions instead and even if you used your own
return bug exploiters the GetTableXXXXX functions would be as fast.
When using the Store natives they do not have auto flushing, so you'd have to use the Flush natives to clean
memory.
* CSArrays (Dynamic Arrays)
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
The fact is that gamecache is slow (and because of that tables and attacheable variables are slow as well)
That's the reason Dynamic Arrays had to be invented.
They simply use 2 global integer arrays to write/read information, in case both arrays are full it will use gamecache instead.
Dynamic Arrays are much faster than tables but they have their own disadvantages:
- Fixed size (you can't extend an array's size after it has been created)
- Use indexes instead of string fields (No GetTableInt(k,"fxpath") , instead GetArrayInt(k,4))
But they have their advantages:
- Really fast (check out the caster system's projectile system of 13.4 and compare it to 13.5 it has become incredibly fast)
- Fast Method to entirelly copy the contents of an array. (No need of fielddata auxiliar things)
______________________________________________________________________________
function NewArray takes integer size, boolean doinit returns integer
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
Creates an array, you just need to specify size which can be any number greater than 0 and lower than 145.
If the doinit argument is true it will initialize all the values of the array with zeroes, otherwise it
won't initialize the indexes so you could expect random numbers there.
If you are gonna set the values manually after creating the array you should use false in the doinit argument
If you might try to read a value without being sure it was initialized correctly, then better use doinit.
Notice that arrays are of type integer so it is easy to nest them.
There are 16382 available spaces of memory before it starts using gamecache. Of the 16382 spaces, 147 are taken
automatically when the map starts and are used for internal things of the CSCache module. Each Array takes
one space to store its size + X other indexes. X is lowest fibonacci number that is greater than or equal to the
size you give to the array.
So If you create an Array of size=100 , it will actually take 145 (1+144) values.
As long as you destroy arrays you no longer need you won't have problems, although if you have a lot of created
arrays and do not destroy them, they can start to use big indexes. Big indexes use gamecache instead of the global
arrays. So it will become slow. The good news is that the worst case scenario is that the arrays become as slow
as gamecache. But it is better to not abuse this system. Only use it for things that are temporary. You can use
global non-dynamic arrays for other things.
Is the 144 size limit too small? I could actually make the limit bigger but didn't feel like one would ever need
such a big array, you can always combine arrays by nesting them btw.
______________________________________________________________________________
function DestroyArray takes integer id returns nothing
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
Destroys an array, take a guess the integer you should use is the one that was returned by NewArray.
You shouldn't access an array after its destruction, although you can get the old values if you
try to access it immediayelly after destroying the array. DestroyArray marks the space the array
used as free so NewArray will eventually return that space so you can use it again.
______________________________________________________________________________
function GetArraySize takes integer id returns integer
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
Returns the size of the array. The size returned is the value you used with NewArray.
_____________________________________________________________________________________
function SetArrayInt takes integer id, integer index, integer val returns nothing
function GetArrayInt takes integer id, integer index returns integer
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
Integer values are the fastest for arrays, id is the value returned by NewArray, index
is the index of the value you have to Set/Get (start with 0) and val is the value
you want to place there.
GetArrayInt returns the value stored on index 'index' of the array.
If you didn't initialize the array with the doinit argument, and you didn't use
SetArrayInt to initialize the index, GetArrayInt will return a random,
umpredictable value.
And array of size 4 has these available indexes: 0,1,2,3
And array of size 9 has these available indexes: 0,1,2,3,4,5,6,7,8
Notice that an array of size 4 is actually an array of size 5 (fibonacci number) so
you *could* access index 4 , but it won't be initialized automatically by doinit.
It is possible to use any number as index , even negative numbers, that would be an
'access violation' you would be trying to access memory that is used by other
dynamic arrays or even their size values or other important things and cause chaos.
So please be careful, I didn't add bound checking because I wanted this to be fast.
____________________________________________________________________________________
function SetArrayReal takes integer id, integer index, real val returns nothing
function GetArrayReal takes integer id, integer index returns real
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
They do exactly the same as the Int ones but using Reals. Notice that you have to
Set the value using SetArrayReal before trying GetArrayReal. Else it might crash.
____________________________________________________________________________________
function SetArrayBoolean takes integer id, integer index, boolean val returns nothing
function GetArrayBoolean takes integer id, integer index returns boolean
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
I do not think I have to explain these...
_____________________________________________________________________________________
function SetArrayObject takes integer id, integer index, handle val returns nothing
function GetArrayObject takes integer id, integer index returns handle
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
The handle versions, since handles are integers these are 'safer' than reals. But
still make sure you used SetArrayObject on the index before using GetArrayObject.
If the index was initialized with 0 GetArrayObject will return null.
_________________________________________________________________________________________
function GetArrayWidget takes integer id, integer index returns widget
function GetArrayTimer takes integer id, integer index returns timer
function GetArrayUnit takes integer id, integer index returns unit
function GetArrayLoc takes integer id, integer index returns location
function GetArrayTrigger takes integer id, integer index returns trigger
function GetArrayTriggerAction takes integer id, integer index returns triggeraction
function GetArrayGroup takes integer id, integer index returns group
function GetArrayEffect takes integer id, integer index returns effect
function GetArrayItem takes integer id, integer index returns item
function GetArrayLightning takes integer id, integer index returns lightning
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
If you used SetArrayObject you can use these to retrieve the values, just as with tables
and attacheable variables, notice that you should use GetArrayUnit if you used SetArrayObject
to store an object.
Because of inheritance you can use GetArrayUnit and GetArrayWidget if you used
SetArrayObject to store a unit, the same happens with items and destructables.
_________________________________________________________________________________________
function GetArrayRect takes integer id, integer index returns rect
function GetArrayRegion takes integer id, integer index returns region
function GetArrayTimerDialog takes integer id, integer index returns timerdialog
function GetArrayDestructable takes integer id, integer index returns destructable
function GetArrayImage takes integer id, integer index returns image
function GetArrayUbersplat takes integer id, integer index returns ubersplat
function GetArraySound takes integer id, integer index returns sound
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
The difference between these functions and the previous ones is that these are not optimized
for speed but are optimized for script size. In other words GetArrayRect is slightly slower
than GetArrayUnit, but GetArrayRect is a much smaller function than GetArrayunit.
_________________________________________________________________________________________
function SetArrayString takes integer id, integer index, string val returns nothing
function GetArrayString takes integer id, integer index returns string
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
These are just dummy functions, they do not use the actual global arrays but gamecache
to store the values. And as a matter of fact you could use index i to store an integer
and an string and you could access both values. But these strings will also get cleaned
correctly if you use DestroyArray. So they are useful. The only bad things about these
functions is that CloneArray does not copy string values and that they are just as slow
as gamecache.
_________________________________________________________________________________________
function CloneArray takes integer id returns integer
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
Will return a new array that is the exact copy of the array that is specified by the id
argument.
It will NOT copy string values.
* Pools
¯¯¯¯¯¯¯
Pools and the functions involved with them and full with name issues, probably a better name
instead of 'pool' would have been 'set' they are sets. Also what we call pool's items are the
elements of the sets. It kind of conflicts with wc3's items that are widgets (little treasures
that your hero picks up != pool items)
I didn't bother to rename the function and cause conflicts.
What are pools? Well you probably know JASS' group type (unit groups) I made the pool class
to simulate "integer groups" so this is something that works like unit groups but using integers.
The number of times you add an element to a pool does not matter, the order does not matter
either.
They have a lot of uses, IsItemInPool is really helpful for configuration, then combining
PoolRemoveItem with PoolPickRandomItem also helps for randomizations, this is where the name pool
came from.
Pools can store a lot of elements, limited only by wc3's gamecache limit which is kind of
huge. But they are optimized for 33 items. This means that having less than 34 elements in
a pool is faster than having more than 33. Starting from the 33th element the pool class would
need to use more gamecache to store information, otherwise it uses mostly dynamic arrays,
You probably met pools before as a separate system, this is a variation designed to be included
as part of CSCache, apart from big optimizations, I also removed 2 functions, ForPool and ForPool2.
because they are too slow and it is much better to use PoolGetItem (see bellow)
The biggest difference is that DestroyPool will now destroy the pool instead of just clearing
it. Old systems may require you to replace calls to DestroyPool with ClearPool in order to
function correctly.
___________________________________________________________________________________________________
function CreatePool takes nothing returns integer
function DestroyPool takes integer poolid returns nothing
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* CreatePool returns an integer value that points to the created pool, it is useless to create
a pool without saving the pointer into an integer variable (Kinda like CreateGroup() ) From
now we will call this integer the pool's id.
* DestroyPool will clear the memory used by a pool like DestroyGroup() . Once you are not going
to use a pool anymore use DestroyPool on its id. Otherwise you would leak memory and add chances
to make the dynamic arrays use gamecache and become slow.
___________________________________________________________________________________________________
function ClearPool takes integer poolid returns nothing
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* Simply clears a pool, but you can still add items to the pool without problem.
___________________________________________________________________________________________________
function PoolAddItem takes integer poolid, integer value returns nothing
function PoolRemoveItem takes integer poolid, integer value returns nothing
function IsItemInPool takes integer poolid, integer it returns boolean
function CountItemsInPool takes integer poolid returns integer
function GetFirstOfPool takes integer poolid returns integer
function PoolPickRandomItem takes integer poolid returns integer
function PoolAddPool takes integer sourcepoolid, integer destpoolid returns nothing
function PoolRemovePool takes integer sourcepoolid, integer destpoolid returns nothing
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* The Functions above work exactly like their group counterparts.
* Keep in mind that like groups a pool cannot contain a value twice.
___________________________________________________________________________________________________
function PoolGetItem takes integer poolid, integer itemn returns integer
function GetItemPositionInPool takes integer poolid, integer it returns integer
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* Added these functions to make everything easier, specially PoolGetItem can be worthy when
making loops that pick every item of a pool without the problems ForPool can cause.
GetItemPositionInPool seems useless for an accessor user but it worked to help the other
functions,
* Keep in mind that pools aren't supposed to be sorted, and the order of the items
change when removing items to let things be faster.
* For technical issues, These functions use 1-based positions (a pool starts with item 1 not
item 0) that is because I made it so GetItemPositionInPool returns 0 when the item is not in
the Pool.
* To iterate through the contents of a pool p do something like this:
set n=CountItemsInPool(p)
set i=1
loop
exitwhen (i>n)
set a=PoolGetItem(p,i)
call BJDebugMsg(I2S(a)) //(for example)
set i=i+1
endloop
___________________________________________________________________________________________________
function S2Pool takes string s returns integer
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* Will Convert a tokenized string into a new pool
example: S2Pool( "1;2;3;4") will return a pool that has 1, 2, 3 and 4 inside
___________________________________________________________________________________________________
function PoolAddS takes integer poolid, string s returns nothing
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* The same as the last function, but it will add the numbers to a pool, instead of creating
a new one.
___________________________________________________________________________________________________
function Pool2S takes integer P returns string
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* Does the opposite of S2Pool if pool contains 1 ,2 ,3 and 4 then this function returns
"1;2;3;4" should be useful for debugging, you can also use this couple of functions to store
pools in string format, although this is unnecesarily slow.
___________________________________________________________________________________________________
function Rawcodes2Pool takes string s returns integer
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* Handy, it will do something similar to S2Pool , but it will do it with rawcodes (Object IDs)
Rawcodes2Pool("AHbz;AHfs;AHwe") will return a pool that has blizzard, flame strike and
water elemental inside
___________________________________________________________________________________________________
function PoolAddRawcodes takes integer poolid, string s returns nothing
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* The same as the last one, but this one will add the rawcodes to an existing pool instead of
creating a new one.
call PoolAddRawcodes( <a pool> , "AHbx;AHfs;AHwe")
* CSData
¯¯¯¯¯¯¯¯
CSData are like Item's and Unit's UserData (Custom value in GUI). But they work for every handle.
They are faster than using gamecache (attacheable variables/tables) to attach information to
a handle. But they only store one integer and they are global.
They have a limit, in case there are many (a lot of) handles in your map CSData will need to use
gamecache , and it will get as slow as gamecache. But "faster most of the times"
beats "always slow" . The handle limit is 8191 notice that many handles are created automatically
by blizzard.j or by default when the game starts the map.
Notice that for public spells or systems to be distributed you should only use these
for private objects (those who the mapper would never have access to) If you are making something
for your map you can use them wherever you want.
Best to be used in conjunction to CSArrays so you just specify an array id for a handle.
DO NOT USE THIS ON THESE HANDLE TYPES: lightning, ubersplat, image, texttag, player,
any 'argument' handle (like playerstate, damagetype, etc)
_________________________________________________________________________________
function SetCSData takes handle h, integer v returns nothing
function GetCSData takes handle h returns integer
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
Kind of obvious. NOTE: Don't ever try to GetCSData if SetCSData was not called
for that handle. It is not like it would explode on your face or anything, but
it would probably have npredictable data (don't assume it will be 0)
* Location based Linked List Kit
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
Locations can be used in conjunction of the return bug to make single linked lists that are
dynamic and fast (they can be faster than dynamic arrays but have more limitations, but they
don't use indexes so it is easier to add/remove things to them)
These functions are all what is needed to make linked lists that use locations, I won't really
explain how to use them right now. Maybe later
______________________________________________________________________________________________
function Location_IntLoc takes integer x , location y returns location
function Location_ObjectLoc takes handle x , location y returns location
function Location_LocLoc takes handle x , location y returns location
function Location_RealLoc takes real x , location y returns location
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
These are dummy constructors of locations that take other types of values instead of Location(x,y)
______________________________________________________________________________________________
function SetLocation_IntLoc takes location loc, integer x , location y returns nothing
function SetLocation_ObjectLoc takes location loc, handle x , location y returns nothing
function SetLocation_LocLoc takes location loc, handle x , location y returns nothing
function SetLocation_RealLoc takes location loc, real x , location y returns nothing
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
These are dummy functions that allow you to set both values of the location at the same time
using different types than MoveLocation(loc,x,y)
______________________________________________________________________________________________
function SetLocationX_Object takes location loc, handle val returns nothing
function SetLocationX_Loc takes location loc, location val returns nothing
function SetLocationX_Real takes location loc, real val returns nothing
function SetLocationX_Int takes location loc, integer val returns nothing
function SetLocationY_Loc takes location loc, location val returns nothing
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
There is no native way to set just one of the values, these use MoveLocation and GetLocationX/Y to
change one of the values and keep the other one. They allow you to use other types. Notice that
you could just use SetLocationX_Object instead of SetLocationX_Loc , SetLocationX_Loc was added
as eye candy.
______________________________________________________________________________________________
function GetLocationX_Loc takes location loc returns location
function GetLocationY_Loc takes location loc returns location
function GetLocationX_Int takes location loc returns integer
function GetLocationX_Unit takes location loc returns unit
function GetLocationX_Item takes location loc returns item
function GetLocationX_Effect takes location loc returns effect
function GetLocationX_Lightning takes location loc returns lightning
function GetLocationX_Widget takes location loc returns widget
function GetLocationX_Object takes location loc returns handle
function GetLocationX_Rect takes location loc returns rect
function GetLocationX_Region takes location loc returns region
function GetLocationX_TimerDialog takes location loc returns timerdialog
function GetLocationX_Destructable takes location loc returns destructable
function GetLocationX_Trigger takes location loc returns trigger
function GetLocationX_Timer takes location loc returns timer
function GetLocationX_Group takes location loc returns group
function GetLocationX_TriggerAction takes location loc returns triggeraction
function GetLocationX_Image takes location loc returns image
function GetLocationX_Ubersplat takes location loc returns ubersplat
function GetLocationX_Sound takes location loc returns sound
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
Get X/Y functions that allow you to use other types
________________________________________________________________________________________________
function CS_h2r takes handle h returns real
function CS_i2r takes integer h returns real
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
You can just use these functions in combination of Location() and MoveLocation() instead of the
dummy constructors and set functions, that would be faster.
You should not use return bug directly for the Get functions though, because of a weird bug with
real->int conversion. The Get functions I've included are safer and faster than using return bug
exploiter + GetLocationX/Y
Linked List?
¯¯¯¯¯¯¯¯¯¯¯¯
All right I am going to explain, this is a linked list:
__________ __________ __________ __________
| value |--->| value |--->| value |--->| value |--> null
¯¯¯¯¯¯¯¯¯¯ ¯¯¯¯¯¯¯¯¯¯ ¯¯¯¯¯¯¯¯¯¯ ¯¯¯¯¯¯¯¯¯¯
See? Node #1 has a value + a pointer to node #2 which has its value + pointer to node #4 and so
and so until the last node points to null instead.
If you need more explanations maybe linked lists aren't for you, keep using the other things instead.
Many people ask me about this topic, but well I can redirect them to :
http://en.wikipedia.org/wiki/Linked_lists
* Auxiliar functions
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
As a side effect of the pools' PoolAddRawcodes function, the CSCache engine is ready to help you convert
any rawcode in string format into integer format:
____________________________________________________________________________
function CS_Rawcode2Int takes string s returns integer
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
for example: CS_Rawcode2Int("Aloc") equals 'Aloc'
* Recommendation
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
In case CSCache is not used during map initialization for the first time, then it might go to the heavy
process of initializing CSCache() the time you need a system that uses CSCache. And it would cause
"first time cast lag"
In order to prevent this I recommend you to add this line to a trigger that is run at map initialization:
set udg_cscache=CSCache()
If you are going to implement the caster system, then don't worry, CreateCasters() will do this call.
Spell Events
¯¯¯¯¯¯¯¯¯¯¯¯
This module has 2 causes to exist:
- It will optimize a map that has plenty of enhanced spells, because there would only be one
global trigger with one check for every spell instead of one for each spell.
- In the case of Passive abilities it allows to detect them easily in the multiple situations
that they could be used (won't detect the action of a passive ability but the acquiring of it)
____________________________________________________________________________________________________________
function OnAbilityEffect takes integer abilid, string funcname returns nothing
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
This function registers a "starts the effect of an ability" event, directly for the abilid
spell. funcname is the name of a function to execute whenever that happens, this name is case
sensitive and it will cause crashes if you misspell it so make sure you are refferencing a true
function in the funcname argument.
Example: call OnAbilityEffect('AHbz',"FlashQuestDialogButton")
Will call FlashQuestDialogButton (the quest button will get a highlight)
whenever a unit casts blizzard (AHbz) . You can, in fact you are supposed to use your own
custom functions for the argument.
You can check out the samples on this map, IceWave and Inferno to see how it works.
It is safe to use waits inside the event function. And you can register the same function for
many abilities. Also the event response functions to use are the same that you use for the
"starts the effect of an ability" event, GetTriggerUnit() , GetSpellAbilityId(),
GetSpellTargetLoc() and GetSpellTargetUnit()
____________________________________________________________________________________________________________
function OnAbilityPreCast takes integer abilid, string funcname returns nothing
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
Does exactly the same as OnAbilityEffect but registers the "begins casting an ability"
event instead, the difference is that this event actually happens BEFORE the unit spends
mana / cooldown, so you can stop the spell if you have to without letting the unit spend
mana / cooldown. The event responses are the same of the OnAbilityEffect event
____________________________________________________________________________________________________________
function OnAbilityEndCast takes integer abilid, string funcname returns nothing
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
Is executed when the event EVENT_UNIT_SPELL_ENDCAST triggers.
____________________________________________________________________________________________________________
function OnAbilityGet takes integer abilid, string funcname returns nothing
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
Registers function called funcname to execute whenever a unit gets an ability, in fact the
function will execute whenever:
- A hero learns or increases the level of a skill.
- A unit is created/trainned/summoned and has that ability, but doesn't have 'Aloc'
- A preplaced unit has the ability
- You use UnitAddAbility_ConsiderEvent to add the ability to the unit (see bellow)
Because of the mixed kind of this event it has dummy event responses:
____________________________________________________________________________________________________________
function GetAbilityAcquiringUnit takes nothing returns unit
function GetAcquiredAbilityId takes nothing returns integer
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
GetAbilityAcquiringUnit() is the unit that just got the ability, GetAcquiredAbilityId() is the
spell id of the ability.
Note: These event responses behave like global variables, so it is not safe to call them after
waits, instead save their values in local variables.
--
This event does not detect an ability when it is added by a trigger or by an upgrade or by
morphing, you can use:
____________________________________________________________________________________________________________
function UnitAddAbility_ConsiderEvent takes unit whichUnit, integer abilid, integer level returns nothing
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
To add an ability with a level directly to a unit and make sure it is considered by the registered
event of the ability.
Note: This event requires some checks for every unit that is created every time, depending on
the number of passive abilities in the map in can get pretty bad for the performance side, if
you have just like 20 abilities registered with this event and a unit may only have a maximum
of 2 of these abilities, it won't be a really big problem, The engine adapts to each unit type
so after the first heavy check it will only remember which abilities the unit type had and just
check for them.
_____________________________________________________________________________________________________________
function OnAbilityLearn takes integer abilid, string funcname returns nothing
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
If you are sure that ability is just a hero skill and there are no other ways to get it than
learning, you can use this function to register its event and just use the normal "learns a skill"
event responses in the event function, note that OnAbilityGet automatically registers this event
to be used in conjunction with other events. As an exception, if you use UnitAddAbility_ConsiderEvent
it may also consider the spells registered with OnAbilityLearn.
NOTE: You can't register the same event for the same ability twice, and you should only use
these spells in map initialization.
NOTE: Only use OnAbilityCast, OnAbilityEffect or OnAbilityLearn in case that you have 3 or
more abilities registered with that function, otherwise it wouldn't really be worth it.
* Spell Helpers
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
These are miscelaneus functions that were added to Vexorian to give him more freedom
in his future spells, and they would be helpful to you in the future, they use casters to
retrieve what they want.
More shall be added with each version.
- Boolean Stuff
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
These are functions that help you recognize what is what. To use these boolean returning
functions, you'd rather have some JASS knowledge or if you want to keep it GUI-ish, use a
boolean variable in a custom script line:
Custom Script: set udg_Bvar = IsDestructableTree(GetDyingDestructable())
this would give true to the Bvar variable in case the dying destructable is a tree.
_______________________________________________________________________________________________
function IsDestructableTree takes destructable d returns boolean
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
Determines if a destructable is a tree, your spell shall want to avoid to affect doors
when you want to affect trees.
_______________________________________________________________________________________________
function IsPointWater takes real x, real y returns boolean
function IsPointWaterLoc takes location loc returns boolean
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
Determines if the given point is water, doesn't matter if it is deep or shallow, it
would return true.
_______________________________________________________________________________________________
function IsUnitSpellImmune takes unit u returns boolean
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
Determines if an unit is Immune to Magic damage, Since 1.18 it just uses IsUnitType with
UNIT_TYPE_SPELL_IMMUNE .
_______________________________________________________________________________________________
function IsUnitImmuneToPhisical takes unit u returns boolean
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
Determines if an unit is Immune to Phisical damage
_______________________________________________________________________________________________
function IsUnitInvulnerable takes unit u returns boolean
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
Determines if an unit is Invulnerable (immune to magic and phisical damage)
- Graphical
¯¯¯¯¯¯¯¯¯¯¯
_______________________________________________________________________________________________
function Caster_SetZAngle takes unit caster, real ang returns nothing
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
A moving special effect, projectile, Static ability caster or Area damager effect, or
just a Caster you gave a special effect, can now rotate at the Z axis to help you have some
extra possibilities when making special effects for your abilities.
Just use this function and use an angle from the ground and you are now able to rotate
any model on the Z plane.
This function requires you to have the latest version of the dummy.mdx model.
Note: For some odd reason the angle resets back to normal whenever you use this function
again, so you can't make loop movement.
Note: As a secondary effect, the facing angle of the caster will get locked
_______________________________________________________________________________________________
native ResetUnitLookAt takes unit whichUnit returns nothing
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
Use this when you want to cancel the effects of Caster_SetZAngle
_______________________________________________________________________________________________
function Caster_SetZAngle2 takes unit caster, integer ang returns nothing
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
This is a variation that uses another method to rotate the unit, it can only take integer
calues from -90 to 90 . The advantage is that it won't reset the rotation each time you use
this function. To restore things up use this function as 0 as ang argument
- Enuming Stuff
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
These functions are helpful to detect units that match certain conditions.
_______________________________________________________________________________________________________
function CS_EnumUnitsInAOE takes group g, real x, real y, real area, boolexpr bx returns nothing
function CS_EnumUnitsInAOELoc takes group g, location loc, real area, boolexpr bx returns nothing
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
CS_EnumUnitsInAOE does the same as GroupEnumUnitsInRange, but it has the advantage that it
is as accurate as a targetting circle, so if the targetting circle of a spell highlights a unit
you can be 100% sure that this function will detect that unit if you use the target point of
the ability for its point argument and the area of effect of the ability for area argument.
CS_EnumUnitsInAOELoc does the same thing but it is the version that takes a location argument.
- Angle Stuff:
¯¯¯¯¯¯¯¯¯¯¯¯¯
These are helpful when making spells and other stuff, all these functions use Deg angles, and
the values might be on every range, negative values, or values greater than 360 are automatically
translated into the right angles.
_______________________________________________________________________________________________________
function Angles_GetAngleDifference takes real a1, real a2 returns real
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
Returns the angle distance between angles a1 and a2:
____________________________
| a1 | a2 | return value |
|¯¯¯¯¯|¯¯¯¯¯|¯¯¯¯¯¯¯¯¯¯¯¯¯¯|
| 30 | 60 | 30 |
| -30 | 15 | 45 |
| 370 | 20 | 10 |
| -10 | -40 | 30 |
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
______________________________________________________________________________________________________
function Angles_GetMidAngle takes real a1, real a2 returns real
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
Returns the "mid angle" between a1 and a2 (here we call it bisectriz, I don't know how
it is called in english)
____________________________
| a1 | a2 | return value |
|¯¯¯¯¯|¯¯¯¯¯|¯¯¯¯¯¯¯¯¯¯¯¯¯¯|
| 30 | 60 | 45 |
| -30 | 15 | 352.5 |
| 370 | 20 | 15 |
| -10 | -40 | 335 |
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
____________________________________________________________________________________________________
function Angles_MoveAngleTowardsAngle takes real a1, real a2, real i returns real
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
Moves angle a1 towards angle a2 by i units, but if the distance between the angles is less than
i, it returns angle a2.
_________________________________
| a1 | a2 | i | return value |
|¯¯¯¯¯|¯¯¯¯¯|¯¯¯¯¯|¯¯¯¯¯¯¯¯¯¯¯¯¯¯|
| 30 | 60 | 10 | 40 |
| 60 | 30 | 10 | 50 |
| -10 | -15 | 5 | -15 |
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
____________________________________________________________________________________________________
function Angles_IsAngleBetweenAngles takes real angle, real a1, real a2 returns boolean
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
Returns true if the angle is inside the shortest range formed between the angle1 and the angle2
angles ? Keep in mind that if the angle distance between a1 and a2 happens to be exactly 180
degrees, the result will be random.
_________________________________
|angle| a1 | a2 | return value |
|¯¯¯¯¯|¯¯¯¯¯|¯¯¯¯¯|¯¯¯¯¯¯¯¯¯¯¯¯¯¯|
| 45 | 30 | 60 | true |
| 65 | 30 | 10 | false |
| 0 | -10 |355 | true |
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
- Movement
¯¯¯¯¯¯¯¯¯¯
____________________________________________________________________________________________________
function CS_MoveUnit takes unit u, real x, real y returns boolean
function CS_MoveUnitLoc takes unit u, location loc returns boolean
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
Moves a unit instantly to the point, this is different from The standard way, this is a faster
way, it won't stop the unit from casting spells/ attacking / or playing animations, and it won't
consider pathing at all.
The only thing it will prevent is moving the unit out of the map bounds (would cause a crash)
in that case it will move it to a point inside the map but as close to the target point as possible
and will return false.
- Interface
¯¯¯¯¯¯¯¯¯¯¯
____________________________________________________________________________________________________
function CS_Error takes player forWhichPlayer, string ErrorMessage
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
This function is the closest you can be to custom interface errors. Mimics an interface error
with sound and centered text. The text is a little higher than a normal interface error, it also
has the side effect of clearing the previous text messages, otherwise it would actually move them
and it would be worse.
- Other
¯¯¯¯¯¯¯
_______________________________________________________________________________________________
function DamageDestructablesInCircle takes real x, real y, real radius, real dmg returns nothing
function DamageDestructablesInCircleLoc takes location loc, real radius, real dmg returns nothing
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
Will deal damage to any noninvulnerable destructabe in the given circle
______________________________________________________________________________________________________________________________________________________________________
function DamageTreesInCircle takes real x, real y, real radius returns nothing
function DamageTreesInCircleLoc takes location loc, real radius returns nothing
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
Will KILL any noninvulnerable TREE in the given circle
Safe item hiding
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
After making Pocket Stash and InvX I note serious problems when hiding items by using
SetItemVisible :
- Computer controlled heroes can in fact pick them up.
- Units might be ordered to pick them up by using triggers because they are detected
when using pick every item in rect
- After saving a game and loading it, hidden items SUDDENLY BECOME VISIBLE AGAIN!
These problems were too bad and forced me to take action. I added these functions
to the caster system because they make use of dummy casters and because Pocket Stash
and InvX already require it.
Important Notes
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* Before using these functions make any trigger that uses item Acquire / Lose events
ignore units of the type Caster. Else you might have some problems (Like when you made
every trigger that used region enter/leave events ignore them too)
* Do not use any powerup (item that is auto consumed) with this function. It won't work.
* You need to have CS_InventoryId pointing to the rawcode of an inventory ability that
cannot use items, can drop/get items, and has 6 slots (in the rare case blizzard changes
the max inventory size to 9, then this ability should have 9 slots).
________________________________________________________________________________________
function CS_HideItem takes item i returns integer
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
*Hides* the 'i' item and returns an integer. This integer is the unique key that will
allow you to recover the item later.
It returns an integer as a way to enforce that you don't do anything directly the item
until you recover it. Avoid to do anything to the item while it is in hiden state, don't
change its life don't give it to a unit don't do anything.
Save the returned integer in a variable or other storage method so whenever you want
to recover the item you use that integer to do so.
________________________________________________________________________________________
function CS_RestoreItem takes integer hiddenindex, real x, real y returns item
function CS_RestoreItemLoc takes integer hiddenindex, location loc returns item
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
Recover the hidden item by giving the integer returned to CS_HideItem to this
function, also specify x and y - the point where you want the item to be placed.
CS_RestoreItemLoc is the version that uses locations and you should know what the
difference is already.
________________________________________________________________________________________
function CS_SetItemVisible takes item i, boolean flag returns nothing
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
This function works exactly the same as the native SetItemVisible but it is safe.
I think it is easier to use this function instead of the CS_HideItem/CS_RestoreItem
combo. Notice that if you hid an item using CS_SetItemVisible you cannot restore it
using CS_RestoreItem it is also not possible to restore an item hidden by CS_HideItem
using CS_SetItemVisible(item,true).
________________________________________________________________________________________
function CS_IsItemVisible takes item it returns boolean
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
This just allows you to recognize if an item has been hidden using CS_SetItemVisible.
//TESH.scrollpos=18
//TESH.alwaysfold=0
library CSData
//****************************************************************************************************
// CSData 15.2
// ¯¯¯¯¯¯¯¯¯¯¯
// CSDatas are like UserData in units and items, they are faster than gamecache unless you have more
// than 8191 handles in your map. In that case it would be a little slower but only for those
// handles. And if you have more than 8191 handles your map is too slow already anyways.
//
// Notice that for public spells or systems to be distributed you should only use these
// for private objects (those who the mapper would never have access to) If you are making something
// for your map you can use them wherever you want.
//
// Best to be used in conjunction to CSArrays so you just specify an array id for a handle.
//
// DO NOT USE THIS ON THESE HANDLE TYPES: -lightning, -ubersplat, -image, -texttag,
// -any 'argument' handle (like playerstate, damagetype, etc)
//
//****************************************************************************************************
//====================================================================================================
globals
private constant integer MAX_HANDLE_ID_COUNT = 408000
// values lower than 8191: very fast, but very unsafe.
// values bigger than 8191: not that fast, the bigger the number is the slower the function gets
// Most maps don't really need a value bigger than 50000 here, but if you are unsure, leave it
// as the rather inflated value of 408000
endglobals
//=================================================================================================
// a.k.a H2I, changed name to CS_H2I to prevent conflicts with other systems, it then stayed that
// instead of changing to a private or public function since many outside spells use it.
//
function CS_H2I takes handle h returns integer
return h
return 0
endfunction
//==================================================================================================
globals
private integer array csdata[MAX_HANDLE_ID_COUNT]
private constant integer MIN_HANDLE_ID=0x100000
endglobals
//It is dependent on jasshelper's recent inlining optimization in order to perform correctly.
function SetCSData takes handle h, integer v returns nothing
debug if(CS_H2I(h)-MIN_HANDLE_ID>=MAX_HANDLE_ID_COUNT) then
debug call BJDebugMsg("SetCSData: Handle id too big, increase the max handle id count or use gamecache instead")
debug endif
set csdata[CS_H2I(h)-MIN_HANDLE_ID]=v
endfunction
function GetCSData takes handle h returns integer
debug if(CS_H2I(h)-MIN_HANDLE_ID>=MAX_HANDLE_ID_COUNT) then
debug call BJDebugMsg("SetCSData: Handle id too big, increase the max handle id count or use gamecache instead")
debug endif
return csdata[CS_H2I(h)-MIN_HANDLE_ID]
endfunction
endlibrary
//TESH.scrollpos=1191
//TESH.alwaysfold=0
library CSSafeCache initializer init requires CSData
//*************************************************************************************************
//* *
//* CSCache 15.2 http://wc3campaigns.net/vexorian *
//* ¯¯¯¯¯¯¯ *
//* CSCache is evil now, use just CSSafeCache when possible as it now contains all functions *
//* that do not use I2H or similar functions that are considered to cause unstability in the map *
//* *
//* CSCache remains here for compatibility with previous work. *
//* Migration from CSCache to CSSafeCache+structs should be easy *
//* *
//*************************************************************************************************
//=================================================================================================
// CSSafeCache globals:
//
globals
gamecache cs_cache = null
integer array cs_array1
integer array cs_array3
integer array cs_array2
private integer array cs_freeindexes
private integer array cs_pairx
private integer array cs_pairy
private integer array cs_freepairs
endglobals
private function init takes nothing returns nothing
// CSSafeCache initializer :
call FlushGameCache(InitGameCache("cscache"))
set cs_cache=InitGameCache("cscache")
call ExecuteFunc("InitArrayIndexes")
call ExecuteFunc("Pool_SetupCharMap")
endfunction
//=================================================================================================================
// CS Pairs
// ¯¯¯¯¯¯¯¯
// This is a sub system to assist csarrays, you can use them but CSArrays of size 2 have the same functionality
// although cspairs are be faster their space is limited and will start using gamecache if abused
//
function NewPair takes integer x, integer y returns integer
local integer i
if (cs_freepairs[0]==0) then
set cs_freepairs[8190]=cs_freepairs[8190]+1
set i= cs_freepairs[8190]
else
set i= cs_freepairs[cs_freepairs[0]]
set cs_freepairs[0]=cs_freepairs[0]-1
endif
if (i>=8189) then //because we can only recycle up to 8189 (free pairs array uses indexes 0 and 8190 for other purposes)
call StoreInteger(cs_cache,"pairx",I2S(i),x)
call StoreInteger(cs_cache,"pairy",I2S(i),y)
else
set cs_pairx[i]=x
set cs_pairy[i]=y
endif
return i
endfunction
function DestroyPair takes integer id returns nothing
if (id>=8189) then
call FlushStoredInteger(cs_cache,"pairx",I2S(id))
call FlushStoredInteger(cs_cache,"pairy",I2S(id))
else
set cs_freepairs[0]=cs_freepairs[0]+1
set cs_freepairs[cs_freepairs[0]] = id
endif
endfunction
function SetPairXY takes integer id, integer x, integer y returns nothing
if (id>=8189) then
call StoreInteger(cs_cache,"pairx",I2S(id),x)
call StoreInteger(cs_cache,"pairy",I2S(id),y)
else
set cs_pairx[id]=x
set cs_pairy[id]=y
endif
endfunction
function SetPairX takes integer id, integer x returns nothing
if (id>=8189) then
call StoreInteger(cs_cache,"pairx",I2S(id),x)
else
set cs_pairx[id]=x
endif
endfunction
function SetPairY takes integer id, integer y returns nothing
if (id>=8189) then
call StoreInteger(cs_cache,"pairy",I2S(id),y)
else
set cs_pairy[id]=y
endif
endfunction
function GetPairX takes integer id returns integer
if (id>=8189) then
return GetStoredInteger(cs_cache,"pairy",I2S(id))
endif
return cs_pairx[id]
endfunction
function GetPairY takes integer id returns integer
if (id>=8189) then
return GetStoredInteger(cs_cache,"pairx",I2S(id))
endif
return cs_pairy[id]
endfunction
//=================================================================================================================
// CS Dynamic Arrays
// ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
// Thanks: Pipedream, Peppar
//
// We can now create arrays in game! , also pass them as arguments or return values!
// a 1 length array is a pointer!
//
function Array_TryFree takes nothing returns nothing
local integer i
local integer N=cs_array1[0]
local integer k
local boolean cleaned=false
local integer loc
local integer q
local integer r
set i=cs_array1[146]
if (i>144) then
call TimerStart(GetExpiredTimer(),60.,false,function Array_TryFree)
return
endif
set loc=cs_freeindexes[i]
set q=0
loop
exitwhen (loc==0)
// we could just have used:
//set k=GetPairX(loc)
//set r=GetPairY(loc) But it is slower than direct usage:
if (loc>=8192) then
set k=GetStoredInteger(cs_cache,"pairx",I2S(loc))
set r=GetStoredInteger(cs_cache,"pairy",I2S(loc))
else
set k=cs_pairx[loc]
set r=cs_pairy[loc]
endif
if (k+i-1==N) then
//we found one we can remove from the list
set cleaned=true
//decrement N
set N=k-2
//Remove from the list:
if (q==null) then
//That was the first, update the array as well
set cs_freeindexes[i]=r
else
//Update the next of the previous one
//We could use : call SetPairY(q,,r) but it is slower
if (q>=8189) then
call StoreInteger(cs_cache,"pairy",I2S(q),r)
else
set cs_pairy[q]=r
endif
endif
if (r==null) then
//This was the last one, update it in the array as well
set cs_freeindexes[i+4096]=q
endif
call DestroyPair(loc)
set loc=q
endif
set q=loc
set loc=r
endloop
if (cleaned) then
set cs_array1[0]=N
set cs_array1[146]=1
else
set cs_array1[146]=cs_array1[i+1]
endif
call TimerStart(GetExpiredTimer(),0.2,false,function Array_TryFree)
endfunction
function InitArrayIndexes2 takes nothing returns nothing
local integer i=0
loop
exitwhen (i==8191)
set cs_pairx[i]=777
set cs_pairy[i]=777
set i=i+1
endloop
endfunction
function InitArrayIndexes takes nothing returns nothing
local integer i=0
local integer a=1
local integer b=1
local integer c
//By placing 777 there instead of 0 it is easier to recognize non correctly initialized bugs
loop
exitwhen (i== 8191)
set cs_array1[i]=777
set cs_array2[i]=777
set cs_array3[i]=777
//set cs_pairx[i]=777
//set cs_pairy[i]=777
set i=i+1
endloop
call ExecuteFunc("InitArrayIndexes2")
set cs_freeindexes[0]=0 //The stack for the table indexes.
set cs_freepairs[0]=0
set cs_freepairs[8190]=0
set i=1
loop
set c=a+b
set a=b
set b=c
exitwhen (b>144) //max size is 144
set cs_freeindexes[b]=0 //the first of the list
set cs_freeindexes[b+4096]=0 //the last of the list
loop
exitwhen (i>b)
set cs_array1[i]=b
set i=i+1
endloop
endloop
set cs_array1[i]=b //i is 145
set cs_array1[146]=1
set cs_array1[147]=101 //initial table index is 101
set cs_array1[0]=147
//index 0: Last used index
// 1 to 145 : Fibonacci sequence
// 146 : last check
// 147 : Table indexes check
//This has a good chance to compress the thing when necesary
call TimerStart(CreateTimer(),60.,false,function Array_TryFree)
endfunction
//=============================================================================================
// Create an array of size, max size is 144, if doinit is true it will put a bunch of zeros
// in the indexes
//
function NewArray takes integer size, boolean doinit returns integer
local integer i
local integer rsize=cs_array1[size]
local integer loc
set loc=cs_freeindexes[rsize]
if (loc!=0) then
set cs_freeindexes[rsize]= GetPairY(loc)
if (cs_freeindexes[rsize]==0) then
set cs_freeindexes[4096+rsize]=0
endif
set i=GetPairX(loc)
call DestroyPair(loc)
if (i==0) then
//this code was probably a good idea when we used locations for the free indexes list, now we use pairs which should not ever
//do this unless someone modiffied the pair array incorrectly
call BJDebugMsg("Caster System: Unexpected error (5): corrupt stack, attempt to recover "+I2S(rsize))
// recovering involves forgetting about the stack which got corrupted and start again from zero, it will leak
// and probably get slow due to usage of gamecache but it is better than the problems that a corrupt stack might cause
set cs_freeindexes[rsize]=0
set cs_freeindexes[4096+rsize]=0
return NewArray(size,doinit)
endif
else
//sz i i+1 i+2
//[ ][ ][ ][ ]
set i=cs_array1[0]+2
set cs_array1[0]=i+rsize-1
endif
//It used to store size in the index equal to the array's id
// but that required the get/set functions to increment 1 in every index
// calculation. Instead, making size the previous index to the array works
if (i<=8191) then
set cs_array1[i-1]=size
elseif (i<=16382) then
set cs_array2[i-8192]=size
else
call StoreInteger(cs_cache,I2S(-i),"size",size)
endif
if (not doinit) then
return i
endif
// 3
//[i][i+1][i+2]
set size=i+size-1
if (size>=16382) then
set size=16381
endif
loop
exitwhen (size<i) or (size<8191)
set cs_array2[size-8191]=0
set size=size-1
endloop
loop
exitwhen (size<i)
set cs_array1[size]=0
set size=size-1
endloop
//call DisplayTextToPlayer(GetLocalPlayer(),0,0,I2S(i))
return i
endfunction
//===============================================================================================================
// Remember to destroy arrays when you no longer need them, else new arrays will get slower after a bunch of
// arrays are active
//
function DestroyArray takes integer id returns nothing
local integer L
local integer loc
// local string k=I2S(-id)
local integer lstpace
if (id<=8191) then
set L=cs_array1[cs_array1[id-1]]
elseif (id<=16382) then
set L=cs_array1[cs_array2[id-8191]]
else
set L=cs_array1[GetStoredInteger(cs_cache,I2S(-id),"size")]
//No way you are gonna call DestroyArray without calling
//NewArray first, so we can use the gamecache variable directly instead
endif
set lstpace=id+L-1
call FlushStoredMission(cs_cache,I2S(-id))
if (lstpace>16382) then
if (lstpace==cs_array1[0]) then
//We just destroyed the array that happens to be at the end of the heap.
//Just get it back
set cs_array1[0]=id-2
set cs_array1[146]=1
else
//Add to the last
set loc=cs_freeindexes[L+4096]
if (loc==0) then
set loc=NewPair(id,0)
set cs_freeindexes[L]=loc
set cs_freeindexes[L+4096]=loc
else
set cs_freeindexes[L+4096]= NewPair(id,0)
//we could just use: call SetPairY(loc, cs_freeindexes[L+4096] ) //but that's slower
if (loc>=8189) then
call StoreInteger(cs_cache,"pairy",I2S(loc), cs_freeindexes[L+4096] )
else
set cs_pairy[loc]=cs_freeindexes[L+4096]
endif
endif
endif
elseif (lstpace==cs_array1[0]) then
//We just destroyed the array that happens to be at the end of the heap.
//Just get it back
set cs_array1[0]=id-2
set cs_array1[146]=1
else
set loc=cs_freeindexes[L]
set cs_freeindexes[L]=NewPair(id,loc)
if (loc==0) then
set cs_freeindexes[L+4096]=cs_freeindexes[L]
endif
endif
endfunction
//================================================================================================================
// Int Set/Get array usage prototypes.
//
// These are the actual functions, the rest are just the result of copy paste, if these functions are updated
// the other ones should be updated as well (They are just return bugged variations)
//
function SetArrayInt takes integer id, integer index, integer val returns nothing
set index=id+index
if (index<8191) then
set cs_array1[index]=val
elseif (index<16382) then
set cs_array2[index-8191]=val
else
call StoreInteger(cs_cache,I2S(-id),I2S(index),val)
endif
endfunction
function GetArrayInt takes integer id, integer index returns integer
set index=id+index
if (index<8191) then
return cs_array1[index]
elseif (index<16382) then
return cs_array2[index-8191]
endif
return GetStoredInteger(cs_cache,I2S(-id),I2S(index))
endfunction
//================================================================================================================
// String Set/Get array
//
// Due String related return bug issues, these are forced to use gamecache
//
function SetArrayString takes integer id, integer index, string val returns nothing
call StoreString(cs_cache,I2S(-id),I2S(index),val)
endfunction
function GetArrayString takes integer id, integer index returns string
return GetStoredString(cs_cache,I2S(-id),I2S(index))
endfunction
//(Boolean is not needed)
//==========================================================================================================================
// Returns the size of an array (the specified by player one, not the actual size of it) should be useful.
//
function GetArraySize takes integer id returns integer
if (id<=8191) then
return cs_array1[id-1]
elseif (id<=16382) then
return cs_array2[id-8192]
endif
return GetStoredInteger(cs_cache,I2S(-id),"size")
endfunction
//===========================================================================================================================
// Returns an array that is an exact copy of the given array
//
function CloneArray takes integer id returns integer
local integer sz
local integer i
local integer sz2
local integer x
local integer y
if (id<=8191) then
set sz=cs_array1[id-1]
elseif (id<=16382) then
set sz=cs_array2[id-8192]
else
set sz=GetStoredInteger(cs_cache,I2S(-id),"size")
//No way you are gonna call DestroyArray without calling
//NewArray first, so we can use the gamecache variable directly instead
endif
set i=NewArray(sz,false)
set sz2=i+sz-1
set sz=id+sz-1
set x=i
set y=id
loop
exitwhen ((y>sz) or (y>=8191) or (x>=8191))
set cs_array1[x]=cs_array1[y]
set y=y+1
set x=x+1
endloop
loop
exitwhen ((y>sz) or (y>=8191) or (x>=16382))
set cs_array2[x-8191]=cs_array1[y]
set y=y+1
set x=x+1
endloop
loop
exitwhen ((y>sz) or (y>=8191))
call StoreInteger(cs_cache,I2S(-i),I2S(x-i),cs_array1[y])
set x=y+1
set y=y+1
endloop
//...
loop
exitwhen ((y>sz) or (y>=16382) or (x>=8191))
set cs_array1[x]=cs_array2[y-8191]
set y=y+1
set x=x+1
endloop
loop
exitwhen ((y>sz) or (y>=16382) or (x>=16382))
set cs_array2[x-8191]=cs_array2[y-8191]
set y=y+1
set x=x+1
endloop
loop
exitwhen ((y>sz) or (y>=16382))
call StoreInteger(cs_cache,I2S(-i),I2S(x-i),cs_array2[y-8191])
set y=y+1
set x=x+1
endloop
//...
loop
exitwhen ((y>sz) or (x>=8191))
set cs_array1[x]=GetStoredInteger(cs_cache,I2S(-id),I2S(y-id))
set y=y+1
set x=x+1
endloop
loop
exitwhen ((y>sz) or (x>=16382))
set cs_array2[x-8191]=GetStoredInteger(cs_cache,I2S(-id),I2S(y-id))
set y=y+1
set x=x+1
endloop
loop
exitwhen (y>sz)
call StoreInteger(cs_cache,I2S(-i),I2S(x-i),GetStoredInteger(cs_cache,I2S(-id),I2S(y-id)))
set y=y+1
set x=x+1
endloop
return i
endfunction
//==================================================================================================
// Attachable vars : Attacheable variables are what most other people call Handle Variables, they
// allow to relate data with any handle, using a label, and its value, the stuff auto flushes if
// the value is 0, false, "", or null .
//
// Differences between Attacheable variables and "Local Handle Variables" :
// - The names of the functions
// - The name of the function group does not cause confusion, it is difficult to say:
// "you should set local handle variables to null at the end of a function" since
// it sounds as if you were talking about the "Local Handle Variables"
// - And can work together with Tables.
//
// Gamecache stuff are NOT Case Sensitive, don't ever use "" for label (Crashes game!)
//
//
// Although locations and dynamic arrays are faster than gamecache, gamecache still keeps the flexibility
// Best thing to do in my opinion is to combine these options. By combining you can acquire gamecache
// flexibility and arrays/locs speed to solve a problem
//
//============================================================================================================
// For integers
//
function AttachInt takes handle h, string label, integer x returns nothing
if x==0 then
call FlushStoredInteger(cs_cache,I2S(CS_H2I(h)),label)
else
call StoreInteger(cs_cache,I2S(CS_H2I(h)),label,x)
endif
endfunction
function GetAttachedInt takes handle h, string label returns integer
return GetStoredInteger(cs_cache, I2S(CS_H2I(h)), label)
endfunction
//=============================================================================================================
function AttachReal takes handle h, string label, real x returns nothing
if x==0 then
call FlushStoredReal(cs_cache,I2S(CS_H2I(h)),label)
else
call StoreReal(cs_cache,I2S(CS_H2I(h)),label,x)
endif
endfunction
function GetAttachedReal takes handle h, string label returns real
return GetStoredReal(cs_cache,I2S(CS_H2I(h)),label)
endfunction
//=============================================================================================================
function AttachBoolean takes handle h, string label, boolean x returns nothing
if not x then
call FlushStoredBoolean(cs_cache,I2S(CS_H2I(h)),label)
else
call StoreBoolean(cs_cache,I2S(CS_H2I(h)),label,x)
endif
endfunction
function GetAttachedBoolean takes handle h, string label returns boolean
return GetStoredBoolean(cs_cache,I2S(CS_H2I(h)),label)
endfunction
//=============================================================================================================
function AttachString takes handle h, string label, string x returns nothing
if ((x=="") or (x==null)) then
call FlushStoredString(cs_cache,I2S(CS_H2I(h)),label)
else
call StoreString(cs_cache,I2S(CS_H2I(h)),label,x)
endif
endfunction
function GetAttachedString takes handle h, string label returns string
return GetStoredString(cs_cache,I2S(CS_H2I(h)),label)
endfunction
//============================================================================================================
function CleanAttachedVars takes handle h returns nothing
call FlushStoredMission(cs_cache,I2S(CS_H2I(h)))
endfunction
//============================================================================================================
// Left for compat
function CleanAttachedVars_NoSets takes handle h returns nothing
call FlushStoredMission(cs_cache,I2S(CS_H2I(h)))
endfunction
//=============================================================================================
// Tables
//
// Tables are lame, the real name would be hash tables, they are just abbreviated usage
// of gamecache natives with the addition that you can also Copy the values of a table to
// another one, but don't expect it to be automatic, it must use a FieldData object to know
// which fields and of wich types to copy, Copying a table to another, with a lot of Fields,
// should surelly be lag friendly.
//
// The other thing about tables is that I can say that the Attached variables of a handle work
// inside a table and GetAttachmentTable which is just return bug and I2S , works to allow you
// to manipulate a handle's attached variables through a table.
//
// NewTable and DestroyTable were created to allow to create tables in the fly, but you can
// simply use strings for tables, but place the table names should be between "("")" for example
// "(mytable)" to avoid conflicts with other caster system stuff.
//
function NewTableIndex takes nothing returns integer
local integer loc=cs_freeindexes[0]
local integer i
if (loc!=0) then
set i=GetPairX(loc)
set cs_freeindexes[0]=GetPairY(loc)
call DestroyPair(loc)
return i
endif
set i=cs_array1[147]+1
set cs_array1[147]=i
return i
endfunction
function NewTable takes nothing returns string
local integer loc=cs_freeindexes[0]
local integer i
if (loc!=0) then
set i=GetPairX(loc)
set cs_freeindexes[0]=GetPairY(loc)
call DestroyPair(loc)
return I2S(i)
endif
set i=cs_array1[147]+1
set cs_array1[147]=i
return I2S(i)
endfunction
function GetAttachmentTable takes handle h returns string
return I2S(CS_H2I(h))
endfunction
//============================================================================================================
function DestroyTable takes string table returns nothing
local integer i=S2I(table)
local integer n
call FlushStoredMission(cs_cache,table)
if ((i>100) and (i<1000000)) then //All right, more than 1000000 tables is just wrong.
if (i==cs_array1[147]) then
set cs_array1[147]=cs_array1[147]-1
else
set cs_freeindexes[0]= NewPair(i,cs_freeindexes[0])
endif
endif
endfunction
//============================================================================================================
function ClearTable takes string table returns nothing
call FlushStoredMission(cs_cache,table)
endfunction
//============================================================================================================
function SetTableInt takes string table, string field, integer val returns nothing
if (val==0) then
call FlushStoredInteger(cs_cache,table,field)
else
call StoreInteger(cs_cache,table,field,val)
endif
endfunction
function GetTableInt takes string table, string field returns integer
return GetStoredInteger(cs_cache,table,field)
endfunction
//============================================================================================================
function SetTableReal takes string table, string field, real val returns nothing
if (val==0) then
call FlushStoredReal(cs_cache,table,field)
else
call StoreReal(cs_cache,table,field,val)
endif
endfunction
function GetTableReal takes string table, string field returns real
return GetStoredReal(cs_cache,table,field)
endfunction
//============================================================================================================
function SetTableBoolean takes string table, string field, boolean val returns nothing
if (not(val)) then
call FlushStoredBoolean(cs_cache,table,field)
else
call StoreBoolean(cs_cache,table,field,val)
endif
endfunction
function GetTableBoolean takes string table, string field returns boolean
return GetStoredBoolean(cs_cache,table,field)
endfunction
//============================================================================================================
function SetTableString takes string table, string field, string val returns nothing
if (val=="") or (val==null) then
call FlushStoredString(cs_cache,table,field)
else
call StoreString(cs_cache,table,field,val)
endif
endfunction
function GetTableString takes string table, string field returns string
return GetStoredString(cs_cache,table,field)
endfunction
//============================================================================================================
// Returns true if the fiel contains a value different from 0, false, null, or "" (depending on the type)
// it is worthless to use this with boolean, since it would be the same as reading the boolean value
//
function HaveSetField takes string table, string field, integer fieldType returns boolean
if (fieldType == bj_GAMECACHE_BOOLEAN) then
return HaveStoredBoolean(cs_cache,table,field)
elseif (fieldType == bj_GAMECACHE_INTEGER) then
return HaveStoredInteger(cs_cache,table,field)
elseif (fieldType == bj_GAMECACHE_REAL) then
return HaveStoredReal(cs_cache,table,field)
elseif (fieldType == bj_GAMECACHE_STRING) then
return HaveStoredString(cs_cache,table,field)
endif
return false
endfunction
//============================================================================================================
// Allows to copy a table to another one, but it needs a FieldData object to know which fields of which type
// it is supposed to copy.
//
function CopyTable takes integer FieldData, string sourceTable, string destTable returns nothing
local integer i=1
local string k=I2S(FieldData)
local string k2
local string k3
local integer n=GetStoredInteger(cs_cache,k,"N")
local integer t
loop
exitwhen (i>n)
set k2=I2S(i)
set t=GetStoredInteger(cs_cache,k,k2)
set k3=GetStoredString(cs_cache,k,k2)
if (t==bj_GAMECACHE_BOOLEAN) then
if (HaveStoredBoolean(cs_cache,sourceTable,k3)) then
call StoreBoolean(cs_cache,destTable,k3,GetStoredBoolean(cs_cache,sourceTable,k3))
else
call FlushStoredBoolean(cs_cache,destTable,k3)
endif
elseif (t==bj_GAMECACHE_INTEGER) then
if (HaveStoredInteger(cs_cache,sourceTable,k3)) then
call StoreInteger(cs_cache,destTable,k3,GetStoredInteger(cs_cache,sourceTable,k3))
else
call FlushStoredInteger(cs_cache,destTable,k3)
endif
elseif (t==bj_GAMECACHE_REAL) then
if (HaveStoredReal(cs_cache,sourceTable,k3)) then
call StoreReal(cs_cache,destTable,k3,GetStoredReal(cs_cache,sourceTable,k3))
else
call FlushStoredReal(cs_cache,destTable,k3)
endif
elseif (t==bj_GAMECACHE_STRING) then
if (HaveStoredString(cs_cache,sourceTable,k3)) then
call StoreString(cs_cache,destTable,k3,GetStoredString(cs_cache,sourceTable,k3))
else
call FlushStoredString(cs_cache,destTable,k3)
endif
endif
set i=i+1
endloop
endfunction
//=============================================================================================
// FieldData inherits from Table, was just designed to be used by CopyTable.
//
function FieldData_Create takes nothing returns integer
return NewTableIndex()
endfunction
//============================================================================================================
// valueType uses the same integer variables from blizzard.j :
// bj_GAMECACHE_BOOLEAN, bj_GAMECACHE_INTEGER, bj_GAMECACHE_REAL and bj_GAMECACHE_STRING
//
function FieldData_AddField takes integer fielddata, string field, integer valueType returns nothing
local string k=I2S(fielddata)
local integer n=GetStoredInteger(cs_cache,k,"N")+1
local string k2=I2S(n)
call StoreString(cs_cache,k,k2,field)
call StoreInteger(cs_cache,k,k2,valueType)
call StoreInteger(cs_cache,k,"N",n)
endfunction
//=============================================================================================
// Destroys Field Data
function FieldData_Destroy takes integer fielddata returns nothing
call DestroyTable(I2S(fielddata))
endfunction
//=============================================================================================
// Pools
//
// A better name for pools would be sets, but by the time I made them I couldn't think of that
// name, besides the word set is already a JASS syntax word so it would have been problematic.
//
// Another naming failure is that values of a pool are called "items" but that conflicts with
// the word item that points to wc3 items, Pools can only store integer values, but if you want
// you can go and use the return bug on them.
//
function CreatePool takes nothing returns integer
local integer i=NewArray(34,false)
call SetArrayInt(i,0,0)
return i
endfunction
function ClearPool takes integer poolid returns nothing
call SetArrayInt(poolid,0,0) //[0:integer:n]
call FlushStoredMission(cs_cache,I2S(-poolid))
endfunction
function DestroyPool takes integer poolid returns nothing
call DestroyArray(poolid)
endfunction
function PoolAddItem takes integer poolid, integer value returns nothing
local integer n
local string k=I2S(-poolid)
local string vk="#"+I2S(value)
if not HaveStoredInteger(cs_cache,k,vk) then
set n=GetArrayInt(poolid,0)+1 //[0:integer:N]
call StoreInteger(cs_cache,k,vk,n)
if (n>33) then
call StoreInteger(cs_cache,k,I2S(n),value)
else
call SetArrayInt(poolid,n,value)
endif
call SetArrayInt(poolid,0,n) //[0:integer:N]
endif
endfunction
function PoolRemoveItem takes integer poolid, integer value returns nothing
local string k=I2S(-poolid)
local string vk="#"+I2S(value)
local integer p=GetStoredInteger(cs_cache,k,vk)
local integer n
if (p!=0) then
set n=GetArrayInt(poolid,0) //[0:integer:N]
call FlushStoredInteger( cs_cache, k, vk)
if (n>p) then
if (n>33) then
set vk=I2S(n)
set value=GetStoredInteger(cs_cache,k,vk)
call FlushStoredInteger(cs_cache,k,vk)
else
set value=GetArrayInt(poolid,n)
endif
call StoreInteger(cs_cache,k,"#"+I2S(value),p)
if (p>33) then
call StoreInteger(cs_cache,k,I2S(p),value)
else
call SetArrayInt(poolid,p,value)
endif
elseif (p>33) then
call FlushStoredInteger(cs_cache,k,I2S(p))
endif
call SetArrayInt( poolid,0,n - 1) //[0:integer:N]
endif
endfunction
//===================================================================================
function PoolGetItem takes integer poolid, integer itemn returns integer
if (itemn>33) then
return GetStoredInteger( cs_cache, I2S(-poolid), I2S(itemn))
endif
return GetArrayInt(poolid,itemn)
endfunction
//===================================================================================
function CountItemsInPool takes integer poolid returns integer
return GetArrayInt(poolid,0) //[0:integer:N]
endfunction
//===================================================================================
// Removed : GetEnumPoolItem , ForPool and ForPool2 they are much worse than just
// using CountItemsInPool and PoolGetItem to iterate the pool
//
//===================================================================================
function GetFirstOfPool takes integer poolid returns integer
return GetArrayInt(poolid,1) //[1 is just the first of the pool]
endfunction
//===================================================================================
function PoolPickRandomItem takes integer poolid returns integer
local integer p=GetRandomInt( 1, GetArrayInt(poolid,0) )
if (p>33) then
return GetStoredInteger( cs_cache, I2S(-poolid), I2S(p))
endif
return GetArrayInt(poolid,p)
endfunction
//===================================================================================
function GetItemPositionInPool takes integer poolid, integer it returns integer
return GetStoredInteger( cs_cache, I2S(-poolid), "#"+I2S(it))
endfunction
//===================================================================================
function IsItemInPool takes integer poolid, integer it returns boolean
return(HaveStoredInteger( cs_cache, I2S(-poolid), "#"+I2S(it)) )
endfunction
//===================================================================================
// This had to be optimized for speed, if it was just a loop using the above functions
// that would have been too slow to be worth keeping. That's a bad thing about JASS
// it is such an slow language that code reusability always has the cost of speed
//
function PoolAddPool takes integer sourcepoolid, integer destpoolid returns nothing
local integer a=1
local integer n=GetArrayInt( sourcepoolid,0) //[0:integer:N]
local integer dn=GetArrayInt( destpoolid,0) //[0:integer:N]
local string sk=I2S(-sourcepoolid)
local string k=I2S(-destpoolid)
local integer v
local string vk
loop
exitwhen (a>n)
if (a>33) then
set v=GetStoredInteger(cs_cache,sk,I2S(a))
else
set v=GetArrayInt(sourcepoolid,a)
endif
set vk="#"+I2S(v)
if not HaveStoredInteger(cs_cache,k,vk) then
set dn=dn+1
call StoreInteger(cs_cache,k,vk,dn)
if (dn>33) then
call StoreInteger(cs_cache,k,I2S(dn),v)
else
call SetArrayInt(destpoolid,dn,v)
endif
endif
set a=a+1
endloop
call SetArrayInt(destpoolid,0,dn) //[0:integer:N]
endfunction
//=============================================================================================
// Oh darn, After making PoolAddPool I don't feel like writting this one
// All right I am at least make the get code
//
function PoolRemovePool takes integer sourcepoolid, integer destpoolid returns nothing
local integer a=1
local integer n=GetArrayInt( sourcepoolid,0) //[0:integer:N]
local integer v
local string sk=I2S(-sourcepoolid)
loop
exitwhen a>n
if (a>33) then
set v=GetStoredInteger(cs_cache,sk,I2S(a) )
else
set v=GetArrayInt(sourcepoolid,a)
endif
call PoolRemoveItem( destpoolid, v)
set a=a+1
endloop
endfunction
//===================================================================================
// Adds a tokenized string to a pool,
// Example: PoolAddS(udg_p, "1;2;3;4") will add to the udg_p pool : 1,2,3 and 4
//
function PoolAddS takes integer poolid, string s returns nothing
local integer i=0
local integer st
local string c
set s=s+";"
set st=0
loop
set c=SubString(s, i, i+1)
exitwhen (c == "") or (c == null)
if (c == ";") then
call PoolAddItem( poolid, S2I(SubString(s, st, i)) )
set st=i+1
endif
set i=i+1
endloop
endfunction
//===================================================================================
// Converts a tokenized string into a pool,
// Example: S2Pool( "1;2;3;4") will return a pool that has 1,2,3 and 4 inside
//
function S2Pool takes string s returns integer
local integer spool= CreatePool()
call PoolAddS(spool,s)
return spool
endfunction
//===================================================================================
// Does the opposite of S2Pool, debugging is a good use for this function.
//
function Pool2S takes integer P returns string
local integer N=CountItemsInPool(P)
local integer i
local string s
if (N>=1) then
set s=I2S(PoolGetItem(P,1) )
set i=2
else
return ""
endif
loop
exitwhen (i>N)
set s=s+";"+I2S(PoolGetItem(P,i))
set i=i+1
endloop
return s
endfunction
//=============================================================================================================
// Fixes a lame bug by blizzard related to the custom script section (The condition of the if statement might
// actually be true.
//
function Pool_Percent takes nothing returns string
if ("%"=="") then
return "%%"
endif
return "%"
endfunction
function Pool_SetupCharMap takes nothing returns nothing
local string cm=".................................!.#$"+Pool_Percent()+"&'()*+,-./0123456789:;<=>.@ABCDEFGHIJKLMNOPQRSTUVWXYZ[.]^_`abcdefghijklmnopqrstuvwxyz{|}~................................................................................................................................."
local integer i=0
local string c
if HaveStoredInteger(cs_cache,"charmap_upper","A") then
return
endif
loop
set c=SubString(cm,i,i+1)
exitwhen (c==null) or (c=="")
if (c!=".") then
if c==StringCase(c,true) then
call StoreInteger(cs_cache,"charmap_upper",c,i)
else
call StoreInteger(cs_cache,"charmap_lower",c,i)
endif
endif
set i=i+1
endloop
endfunction
function Pool_Rawcode2Int takes string s returns integer
local string c
local integer i=0
local integer r=0
loop
exitwhen i>3
set c=SubString(s,i,i+1)
set r=r*256
if c==StringCase(c,true) then
set r=r+GetStoredInteger(cs_cache,"charmap_upper",c)
else
set r=r+GetStoredInteger(cs_cache,"charmap_lower",c)
endif
set i=i+1
endloop
return r
endfunction
function PoolAddRawcodes_thread takes nothing returns nothing
//Threaded because I don't want it to halt execution for no reason
//
local string s=bj_lastPlayedMusic
local integer poolid=bj_groupEnumTypeId
local string c
local integer i=0
local integer st=0
set s=s+";"
loop
set c=SubString(s, i, i+1)
exitwhen (c == "") or (c == null)
if c == ";" then
call PoolAddItem(poolid, Pool_Rawcode2Int(SubString(s,st,i) ))
set st=i+1
endif
set i=i+1
endloop
endfunction
//=====================================================================================================================
// Adds a string of tokenized rawcodes to a pool
// Example: PoolAddRawcodes(udg_p,"A000;A001") will add 'A000' and 'A001' to the pool
//
// (Saves some lines, but is not as good efficiency wise)
//
function PoolAddRawcodes takes integer poolid, string s returns nothing
local string b=bj_lastPlayedMusic
set bj_groupEnumTypeId=poolid
set bj_lastPlayedMusic=s
call ExecuteFunc("PoolAddRawcodes_thread")
set bj_lastPlayedMusic=b
endfunction
//===================================================================================================================
// Converts a tokenized string of rawcodes into a pool,
// Example: Rawcodes2Pool( "A000;A001;AHbz;S000") will return a pool that has 'A000,'A001','AHbx' and 'S000' inside
//
// (Saves some lines, but is not as good efficiency wise)
//
function Rawcodes2Pool takes string s returns integer
local integer spool= CreatePool()
call PoolAddRawcodes(spool,s)
return spool
endfunction
//===================================================================================================================
// A subproduct of the Pool's Rawcode support is that we can easily have this function so I am including it even if
// it has nothing to do with data storage.
//
// takes "Aloc" and converts it into 'Aloc'
// it is different to the Pool_Rawcode2Int function in that it is safe to use it when it is the first CSCache
// function ever used. But it is a little slower (wc3mapoptimizer should make it as fast though)
//
function CS_Rawcode2Int takes string s returns integer
local string c
local integer i=0
local integer r=0
loop
exitwhen i>3
set c=SubString(s,i,i+1)
set r=r*256
if c==StringCase(c,true) then
set r=r+GetStoredInteger(cs_cache,"charmap_upper",c)
else
set r=r+GetStoredInteger(cs_cache,"charmap_lower",c)
endif
set i=i+1
endloop
return r
endfunction
endlibrary
//TESH.scrollpos=45
//TESH.alwaysfold=0
library CSSafety requires CSData
//******************************************************************************************
//*
//* CSSafety 15.2
//* ¯¯¯¯¯¯¯¯
//* CSSafety was originally just a timer stack, take your map functions and replace calls
//* to CreateTimer with NewTimer and calls to DestroyTimer to ReleaseTimer and you can feel
//* at ease if your map's spells abuse dynamic triggers a lot since it prevents handle stack
//* corruptions caused by crazy reference counters, basically, wc3 will go crazy if you
//* detroy timers, so it is better to just recycle them.
//*
//* Eventually, we also added a group stack, the justification is different though, there
//* are just memory leak related issues when using DestroyGroup on groups that were handled
//* by certain natives, thus it is good to recycle them. Replace CreateGroup with NewGroup
//* and DestroyGroup with ReleaseGroup.
//*
//******************************************************************************************
//==========================================================================================
globals
private timer array tT
private integer tN = 0
private group array gT
private integer gN = 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
set tT[0]=CreateTimer()
else
set tN=tN-1
endif
call SetCSData(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==8191) 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(GetCSData(t)==HELD) then
debug call BJDebugMsg("Warning: ReleaseTimer: Double free!")
return
endif
call SetCSData(t,HELD)
set tT[tN]=t
set tN=tN+1
endif
endfunction
//==========================================================================================
function NewGroup takes nothing returns group
if (gN==0) then
set gT[0]=CreateGroup()
else
set gN=gN-1
endif
call SetCSData(gT[gN],0)
return gT[gN]
endfunction
//==========================================================================================
function ReleaseGroup takes group g returns nothing
if(g==null) then
debug call BJDebugMsg("Warning: attempt to release a null group")
return
endif
if (gN==8191) then
debug call BJDebugMsg("Warning: group stack is full, destroying group!!")
//stack is full, the map already has much more troubles than the chance of bug
call DestroyGroup(g)
else
call GroupClear(g)
if(GetCSData(g)==HELD) then
debug call BJDebugMsg("Warning: ReleaseGroup: Double free!")
return
endif
call SetCSData(g,HELD)
set gT[gN]=g
set gN=gN+1
endif
endfunction
endlibrary
//TESH.scrollpos=21
//TESH.alwaysfold=0
library CasterSystem initializer InitCasterSystem requires CSSafeCache, CSSafety
//***************************************************************************
//* *
//* Caster System 15.2 *
//* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯ http://wc3campaigns.net/vexorian *
//* Requires: *
//* ¯¯¯¯¯¯¯¯¯ *
//* - Jasshelper / Newgen pack *
//* *
//* - The CSSafeCache Module *
//* - The CSData Module *
//* - The CSSafety Module *
//* *
//* - The caster unit from this map Can be found in this map's unit editor *
//* ( Custom Units\Neutral Passive\Units\Caster ) *
//* *
//* ( Update the cs_CasterUnitId constant next:) *
//* *
//* - the war3mapImported\dummy.mdx imported file (find it in this map) *
//* *
//***************************************************************************
//====================================================================================================================================================================
// Caster System Configuration constants :
//
globals
//
// Caster Unit type rawcode (changes betwen maps, always use it inside '')
//
constant integer cs_CasterUnitId = 'e000'
// cs_TIMER_CYCLE : Cycle value for the projectile movement in seconds (Each 0.04 the projectiles get moved)
// 0.01 looks smooth but is lag friendly
// 0.025 looks smooth and probably matches wc3's frame rate (so in theory, lower values than it are not needed)
// 0.1 looks horrible but is not laggy
// 0.04 is decent for the human eye and very efficient.
// 0.035 is ... well, a little smoother than 0.04
// 0.05 would be an improvement in efficiency but probably doesn't look too well )
//
constant real cs_TIMER_CYCLE = 0.035
//
// The eat tree ability, don't need to change this rawcode unless you modiffied that ability in your map, in that case copy it, reset the copied one and use its rawcode here.
//
constant integer cs_DamageTreeDetectorId = 'Aeat'
//
// Medivh's Crow form ability, don't need to change this rawcode unless you
// modiffied that ability in your map, in that case copy it, reset the copied one and use its rawcode here:
//
constant integer cs_FlyingHeightHack = 'Amrf'
//
// This must point to an inventory ability with 6 slots that does not add bonuses, you don't need one unless
// the caster system item hiding functions are used in your map
//
constant integer cs_DummyInventory_Id = 'null'
//
//Maximum collision size in your map - Affects the caster system's area cast/damage functions
//
constant real cs_MaxUnitCollisionSize = 55.0
//
// Next are default attack and damage types for the old caster system functions or when using 0 as damageoptions
//
constant attacktype cs_DefaultAttackType = ATTACK_TYPE_CHAOS
constant damagetype cs_DefaultDamageType = DAMAGE_TYPE_UNIVERSAL
//
// cs_InitialCastersNumber: Number of casters to create on map init:
//
constant integer cs_InitialCastersNumber = 12
endglobals
//
//=================================================================================================
// Caster System script:
// ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
//=================================================================================================
// main Caster System global variables:
//
globals
//old ones (udg_ preffix forced for compat ( )
unit udg_currentcaster = null
real array udg_castervars
group udg_casters = null
integer udg_currentabi = 0
unit udg_currenthurter = null
real udg_delayhack = 0.
location udg_sourcehack = null
//--
integer array cs_missiles
integer cs_missilecount = 0
timer cs_movementtimer = CreateTimer()
boolean cs_movementtimer_active = false
endglobals
//=================================================================================================
function CS_Rawcode2Real takes integer i returns real
return i
return 0.
endfunction
function CS_LoadRawcodeFromReal takes integer n returns integer
return udg_castervars[n]
return 0
endfunction
function CS_CopyGroup takes group g returns group
set bj_groupAddGroupDest=NewGroup()
call ForGroup(g, function GroupAddGroupEnum)
return bj_groupAddGroupDest
endfunction
globals
constant real cs_RectLimitOffSet = 50.0
real cs_game_maxx
real cs_game_maxy
real cs_game_miny
real cs_game_minx
endglobals
function CS_SafeXY_Init takes nothing returns nothing
set cs_game_maxx=GetRectMaxX(bj_mapInitialPlayableArea)-cs_RectLimitOffSet
set cs_game_maxy=GetRectMaxY(bj_mapInitialPlayableArea)-cs_RectLimitOffSet
set cs_game_miny=GetRectMinY(bj_mapInitialPlayableArea)+cs_RectLimitOffSet
set cs_game_minx=GetRectMinX(bj_mapInitialPlayableArea)+cs_RectLimitOffSet
endfunction
function CS_SafeX takes real x returns real
if (x<cs_game_minx) then
return cs_game_minx
elseif (x>cs_game_maxx) then
return cs_game_maxx
endif
return(x)
endfunction
function CS_SafeY takes real y returns real
if (y<cs_game_miny) then
return cs_game_minx
elseif (y>cs_game_maxy) then
return cs_game_maxy
endif
return(y)
endfunction
//Deprecated, since patch 1.21 there is no crash anymore, it is possibly
// still mildly useful since it does bound checking.
function CS_MoveUnit takes unit u, real x, real y returns boolean
local boolean b=true
if (x<cs_game_minx) then
set b=false
elseif (x>cs_game_maxx) then
set b=false
elseif (y>cs_game_maxy) then
set b=false
elseif (y<cs_game_miny) then
set b=false
endif
if (b) then
call SetUnitX(u, x)
call SetUnitY(u, y)
endif
return b
endfunction
function CS_MoveUnitLoc takes unit u, location loc returns boolean
return CS_MoveUnit(u,GetLocationX(loc),GetLocationY(loc))
endfunction
//==================================================================================================
function CS_EnumUnitsInAOE_Filter takes nothing returns boolean
return IsUnitInRangeLoc(GetFilterUnit(), bj_enumDestructableCenter ,bj_enumDestructableRadius)
endfunction
//==================================================================================================
// Use this version when you only have coordinates of the point.
//
function CS_EnumUnitsInAOE takes group g, real x, real y, real area, boolexpr bx returns nothing
local boolexpr cond
local boolexpr aux=Condition(function CS_EnumUnitsInAOE_Filter)
if (bx==null) then
set cond=aux
else
set cond=And(aux,bx)
endif
set bj_enumDestructableCenter=Location(x,y)
set bj_enumDestructableRadius=area
call GroupEnumUnitsInRange(g,x,y,cs_MaxUnitCollisionSize+area,cond)
if (bx!=null) then
call DestroyBoolExpr(cond)
endif
call RemoveLocation(bj_enumDestructableCenter)
set aux=null
set cond=null
endfunction
//==================================================================================================
// Use this version whenever you already have a location for that point, to save some steps
//
function CS_EnumUnitsInAOELoc takes group g, location loc, real area, boolexpr bx returns nothing
local boolexpr cond
local boolexpr aux=Condition(function CS_EnumUnitsInAOE_Filter)
if (bx==null) then
set cond=aux
else
set cond=And(aux,bx)
endif
set bj_enumDestructableCenter=loc
set bj_enumDestructableRadius=area
call GroupEnumUnitsInRangeOfLoc(g,loc,cs_MaxUnitCollisionSize+area,cond)
call DestroyBoolExpr(cond)
if (bx!=null) then
call DestroyBoolExpr(aux)
endif
set aux=null
set cond=null
endfunction
//==================================================================================================
// Angle Calculations
//
// I decided to add them to the caster system, because I found myself using them everytime, I am
// trying to convert the caster system into a Spell Development Framework (hehe)
//
//=================================================================================================
// Returns the angle distance between angles a1 and a2 (For example: a1=30 , a2=60 , return= 30 )
//
function Angles_GetAngleDifference takes real a1, real a2 returns real
local real x
set a1=ModuloReal(a1,360)
set a2=ModuloReal(a2,360)
if a1>a2 then
set x=a1
set a1=a2
set a2=x
endif
set x=a2-360
if a2-a1 > a1-x then
set a2=x
endif
set x=a1-a2
if (x<0) then
return -x
endif
return x
endfunction
//=================================================================================================
// Returns the mid angle between a1 and a2 (For example: a1=30 , a2=60 , return= 45 )
//
function Angles_GetMidAngle takes real a1, real a2 returns real
local real x
set a1=ModuloReal(a1,360)
set a2=ModuloReal(a2,360)
if a1>a2 then
set x=a1
set a1=a2
set a2=x
endif
set x=a2-360
if a2-a1 > a1-x then
set a2=x
endif
return (a1+a2)/2
endfunction
//=================================================================================================
// Makes angle a1 advance i units towards angle a2 (For Example: a1=30, a2=60, i=10, return=40 )
//
function Angles_MoveAngleTowardsAngle takes real a1, real a2, real i returns real
local real x
set a1=ModuloReal(a1,360)
set a2=ModuloReal(a2,360)
if a1>a2 then
set x=a1-360
if a1-a2 > a2-x then
set a1=x
endif
else
set x=a2-360
if a2-a1 > a1-x then
set a2=x
endif
endif
if a1>a2 then
set x=a1-i
if x<=a2 then
return a2
endif
return x
endif
set x=a1+i
if x>=a2 then
return a2
endif
return x
endfunction
//=================================================================================================
// Returns true if the angle 'angle' is between 'angle1' and 'angle2'
//
function Angles_IsAngleBetweenAngles takes real angle, real angle1, real angle2 returns boolean
local real x
set angle=ModuloReal(angle,360)
set angle1=ModuloReal(angle1,360)
set angle2=ModuloReal(angle2,360)
if (angle1>angle2) then
set x=angle1
set angle1=angle2
set angle2=x
endif
if (angle2-angle1)>(angle1 - (angle2-360)) then
set angle2=angle2-360
if angle > 180 then
set angle=angle-360
endif
return angle>=angle2 and angle<=angle1
endif
return (angle>=angle1) and (angle<=angle2)
endfunction
//====================================================================================================================================================================
function CreateCaster takes real fac, real x , real y returns unit
local unit m
if (x!=0) then
set x=CS_SafeX(x)
endif
if (y!=0) then
set y=CS_SafeY(y)
endif
set m=CreateUnit( Player(15), cs_CasterUnitId, x ,y ,fac)
call UnitAddAbility(m, 'Aloc')
call UnitAddAbility(m, cs_FlyingHeightHack)
call UnitRemoveAbility(m, cs_FlyingHeightHack )
set udg_currentcaster=m
set m=null
return udg_currentcaster
endfunction
function AddCasterFacing takes real fac returns unit
return CreateCaster(fac,0,0)
endfunction
function AddCaster takes nothing returns unit
return CreateCaster(0,0,0)
endfunction
//====================================================================================================================================================================
function CreateCasters takes integer n returns nothing
local integer a=0
local unit c
loop
exitwhen a>=n
set c=CreateCaster(0,0,0)
call GroupAddUnit( udg_casters, c)
set a=a+1
endloop
set c=null
endfunction
private function InitCasterSystem takes nothing returns nothing
set udg_casters=NewGroup()
call CS_SafeXY_Init()
call CreateCasters(cs_InitialCastersNumber)
set udg_castervars[100]=-1
set udg_castervars[101]=-1
set udg_castervars[102]=-1
set udg_castervars[103]=-1
set udg_castervars[104]=-1
set cs_dmg_caster=CreateCaster(0.,0.,0.)
call UnitRemoveAbility(cs_dmg_caster,'Aloc') //Otherwise the units would flee like crazy
endfunction
//====================================================================================================================================================================
function GetACaster takes nothing returns unit
set udg_currentcaster=FirstOfGroup( udg_casters)
if udg_currentcaster == null then
set udg_currentcaster=CreateCaster(0,0,0)
endif
call GroupRemoveUnit( udg_casters,udg_currentcaster)
call SetUnitState( udg_currentcaster, UNIT_STATE_MANA, 1000)
return udg_currentcaster
endfunction
//====================================================================================================================================================================
function Caster_SetZAngle takes unit caster, real ang returns nothing
local real a=(ModuloReal(GetUnitFacing(caster),360)*bj_DEGTORAD)
local real x
set ang=ModuloReal(ang,360)
if ( ang == 90 ) then
set ang = 89
endif
if ( ang == 270 ) then
set ang = 271
endif
if (ang>90) and (ang<270) then
set x=-1
else
set x=1
endif
set ang=ang*bj_DEGTORAD
call SetUnitLookAt(caster,"Bone_Chest",caster, 10000.0*Cos(a)*Cos(ang), 10000.0*Sin(a)*Cos(ang), x*(10000.0*Tan(ang)+90.0) )
endfunction
//====================================================================================================================================================================
function Caster_SetZAngle2 takes unit caster, integer ang returns nothing
call SetUnitAnimationByIndex(caster,ang+90) //Thanks infrane!
endfunction
//====================================================================================================================================================================
function RecycleCaster takes unit caster returns nothing
if GetWidgetLife(caster)>=0.405 then
call ResetUnitLookAt(caster)
call SetUnitOwner( caster, Player(15), true)
call SetUnitVertexColor( caster, 255,255,255,255)
call SetUnitScale( caster, 1,1,1)
call SetUnitTimeScale( caster, 1)
call SetUnitMoveSpeed( caster, 522)
call SetUnitFlyHeight( caster, 0,0)
call UnitAddAbility(caster, 'Aloc')
call SetUnitTurnSpeed( caster, 0.6)
call GroupAddUnit( udg_casters, caster)
endif
endfunction
function RecycleCaster_Light takes unit caster returns nothing
if GetWidgetLife(caster)>=0.405 then
call SetUnitOwner( caster, Player(15), true)
call GroupAddUnit( udg_casters, caster)
endif
endfunction
function RecicleCaster takes unit caster returns nothing
call RecycleCaster(caster)
endfunction
function RecicleCaster_Light takes unit caster returns nothing
call RecycleCaster_Light(caster)
endfunction
private struct csrecy
unit caster
integer abil
endstruct
function CasterRecycleTimed_X takes nothing returns nothing
local timer t=GetExpiredTimer()
local csrecy k=GetCSData(t)
if (k.abil!=0) then
call UnitRemoveAbility(k.caster,k.abil)
endif
call RecycleCaster(k.caster)
call k.destroy()
call ReleaseTimer(t)
endfunction
function CasterRecycleTimed takes unit caster, integer abi, real delay returns nothing
local timer t=NewTimer()
local csrecy k= csrecy.create()
set k.caster=caster
set k.abil=abi
call SetCSData(t,k)
call TimerStart(t,delay,false,function CasterRecycleTimed_X)
endfunction
function CasterWaitForEndCast takes nothing returns nothing
local unit caster=udg_currentcaster
local integer abilid=udg_currentabi
local real delay=udg_castervars[0]
local boolean activeability=(udg_castervars[1]>0)
loop
exitwhen GetUnitCurrentOrder(caster) == 0
call TriggerSleepAction(0)
endloop
if (delay>0) then
if activeability then
call CasterRecycleTimed(caster,abilid,delay)
else
call UnitRemoveAbility( caster, abilid)
call CasterRecycleTimed(caster,0,delay)
endif
else
call UnitRemoveAbility( caster, abilid)
call RecycleCaster(caster)
endif
set caster=null
endfunction
function RecycleCasterAfterCastEx takes unit caster, real delaytime, integer abilid, boolean activeability returns nothing
set udg_castervars[0]=delaytime
set udg_castervars[1]=IntegerTertiaryOp(activeability,1,0)
set udg_currentabi=abilid
set udg_currentcaster=caster
call ExecuteFunc("CasterWaitForEndCast" )
endfunction
function RecicleCasterAfterCastEx takes unit caster, real delaytime, integer abilid, boolean activeability returns nothing
call RecycleCasterAfterCastEx(caster,delaytime,abilid,activeability)
endfunction
function CasterWaitForEndCast_Light takes nothing returns nothing
local unit caster=udg_currentcaster
local integer abilid=udg_currentabi
local real delay=udg_castervars[0]
local boolean activeability=(udg_castervars[1]>0)
loop
exitwhen GetUnitCurrentOrder(caster) == 0
call TriggerSleepAction(0)
endloop
if (delay>0) then
if activeability then
call CasterRecycleTimed(caster,abilid,delay)
else
call UnitRemoveAbility( caster, abilid)
call CasterRecycleTimed(caster,0,delay)
endif
else
call UnitRemoveAbility( caster, abilid)
call RecycleCaster_Light(caster)
endif
set caster=null
endfunction
function RecycleCasterAfterCastEx_Light takes unit caster, real delaytime, integer abilid, boolean activeability returns nothing
set udg_castervars[0]=delaytime
set udg_castervars[1]=IntegerTertiaryOp(activeability,1,0)
set udg_currentabi=abilid
set udg_currentcaster=caster
call ExecuteFunc("CasterWaitForEndCast_Light" )
endfunction
function RecicleCasterAfterCastEx_Light takes unit caster, real delaytime, integer abilid, boolean activeability returns nothing
call RecycleCasterAfterCastEx_Light(caster,delaytime,abilid,activeability)
endfunction
function RecicleCasterAfterCast takes unit caster, integer abilid returns nothing
call RecycleCasterAfterCastEx(caster,udg_delayhack,abilid,false)
endfunction
function RecycleCasterAfterCast takes unit caster, integer abilid returns nothing
call RecycleCasterAfterCastEx(caster,udg_delayhack,abilid,false)
endfunction
//====================================================================================================================================================================
function PreloadAbility takes integer abilid returns integer
local unit u=FirstOfGroup(udg_casters)
if u==null then
set u=GetACaster()
call UnitAddAbility(u, abilid)
call UnitRemoveAbility(u, abilid)
call RecycleCaster_Light( u)
else
call UnitAddAbility(u, abilid)
call UnitRemoveAbility(u, abilid)
endif
set u=null
return abilid
endfunction
//====================================================================================================================================================================
function CasterCastAbilityEx takes player owner, real x, real y, real z, integer abilid, integer level, string order, widget target, real delay returns unit
local unit caster=GetACaster()
local boolean done=false
call SetUnitOwner( caster, owner, false)
call UnitAddAbility( caster, abilid)
call SetUnitAbilityLevel(caster,abilid,level)
call SetUnitX(caster,x)
call SetUnitY(caster,y)
call SetUnitFlyHeight(caster,z,0)
if S2I(order) != 0 then
set done=IssueTargetOrderById( caster, S2I(order), target )
else
set done=IssueTargetOrder( caster, order, target )
endif
if (delay<=0) or not(done) then
call UnitRemoveAbility( caster, abilid)
call RecycleCaster_Light( caster)
else
call RecycleCasterAfterCastEx_Light(caster, delay, abilid, true)
endif
set udg_currentcaster=caster
set caster=null
return udg_currentcaster
endfunction
//====================================================================================================================================================================
function CasterCastAbilityExLoc takes player owner, location loc, real z, integer abilid, integer level, string order, widget target, real delay returns unit
return CasterCastAbilityEx(owner,GetLocationX(loc),GetLocationY(loc),z,abilid,level,order,target,delay)
endfunction
//====================================================================================================================================================================
function CasterCastAbilityLevel takes player owner, integer abilid, integer level, string order, widget target, boolean instant returns unit
local real x
local real y
local real d
if udg_sourcehack!=null then
set x=GetLocationX(udg_sourcehack)
set y=GetLocationY(udg_sourcehack)
else
set x=GetWidgetX(target)
set y=GetWidgetY(target)
endif
if not(instant) then
set d=udg_delayhack+0.01
else
set d=0
endif
return CasterCastAbilityEx(owner,x,y,0,abilid,level,order,target,d)
endfunction
//====================================================================================================================================================================
function CasterCastAbility takes player owner, integer abilid, string order, widget target, boolean instant returns unit
return CasterCastAbilityLevel( owner, abilid, 1, order, target, instant )
endfunction
//====================================================================================================================================================================
function CasterCastAbilityPointEx takes player owner, real x1, real y1, real z1, integer abilid, integer level, string order, real x2, real y2, real delay returns unit
local unit caster=GetACaster()
call SetUnitOwner( caster, owner, false)
call UnitAddAbility( caster, abilid)
call SetUnitAbilityLevel(caster,abilid,level)
call SetUnitX(caster,x1)
call SetUnitY(caster,y1)
call SetUnitFlyHeight(caster,z1,0)
if S2I(order) != 0 then
if not IssuePointOrderById( caster, S2I(order), x2,y2 ) then
call IssueImmediateOrderById( caster, S2I(order) )
endif
else
if not IssuePointOrder( caster, order, x2,y2 ) then
call IssueImmediateOrder( caster, order )
endif
endif
if (delay<=0) then
call UnitRemoveAbility( caster, abilid)
call RecycleCaster_Light( caster)
else
call RecicleCasterAfterCastEx_Light(caster, delay, abilid, true)
endif
set udg_currentcaster=caster
set caster=null
return udg_currentcaster
endfunction
//====================================================================================================================================================================
function CasterCastAbilityPointExLoc takes player owner, location loc1, real z1, integer abilid, integer level, string order, location loc2, real delay returns unit
return CasterCastAbilityPointEx(owner,GetLocationX(loc1),GetLocationY(loc1),z1,abilid,level,order,GetLocationX(loc2),GetLocationY(loc2),delay)
endfunction
//====================================================================================================================================================================
function CasterCastAbilityLevelPoint takes player owner, integer abilid, integer level, string order, real x, real y, boolean instant returns unit
local real sx
local real sy
local real d
if udg_sourcehack!=null then
set sx=GetLocationX(udg_sourcehack)
set sy=GetLocationY(udg_sourcehack)
else
set sx=x
set sy=y
endif
if instant then
set d=0
else
set d=udg_delayhack+0.01
endif
return CasterCastAbilityPointEx(owner,sx,sy,0,abilid,level,order,x,y,d)
endfunction
function CasterCastAbilityPoint takes player owner, integer abilid, string order, real x, real y, boolean instant returns unit
return CasterCastAbilityLevelPoint(owner,abilid,1,order,x,y,instant)
endfunction
function CasterCastAbilityPointLoc takes player owner, integer abilid, string order, location loc, boolean instant returns unit
return CasterCastAbilityLevelPoint( owner, abilid, 1,order, GetLocationX(loc), GetLocationY(loc), instant )
endfunction
function CasterCastAbilityLevelPointLoc takes player owner, integer abilid, integer level, string order, location loc, boolean instant returns unit
return CasterCastAbilityLevelPoint( owner, abilid, level,order, GetLocationX(loc), GetLocationY(loc), instant )
endfunction
//====================================================================================================================================================================
private struct cssabi
effect fx
unit caster
integer abil
endstruct
function CasterUseAbilityLevelStatic_Rec takes nothing returns nothing
local timer t=GetExpiredTimer()
local cssabi k=GetCSData(t)
call RecycleCaster(k.caster)
call k.destroy()
call ReleaseTimer(t)
endfunction
function CasterUseAbilityLevelStatic_X takes nothing returns nothing
local timer t=GetExpiredTimer()
local cssabi k=GetCSData(t)
call DestroyEffect(k.fx)
call UnitRemoveAbility( k.caster, k.abil)
call TimerStart(t,2,false, function CasterUseAbilityLevelStatic_Rec)
endfunction
function CasterUseAbilityLevelStatic takes player owner, string modelpath, integer abilityid, integer level, real duration, real x, real y returns unit
local timer t=NewTimer()
local unit c=GetACaster()
local cssabi k= cssabi.create()
call SetUnitPosition( c, x, y)
set k.fx= AddSpecialEffectTarget( modelpath, c,"origin" )
set k.caster = c
set k.abil=abilityid
call SetUnitOwner(c, owner, true)
call UnitAddAbility(c, abilityid)
call SetUnitAbilityLevel(c, abilityid, level)
call SetCSData(t,k)
call TimerStart(t,duration,false,function CasterUseAbilityLevelStatic_X)
set udg_currentcaster=c
set c=null
return udg_currentcaster
endfunction
function CasterUseAbilityStatic takes player owner, string modelpath, integer abilityid, real duration, real x, real y returns unit
return CasterUseAbilityLevelStatic(owner,modelpath,abilityid,1,duration,x,y)
endfunction
function CasterUseAbilityStaticLoc takes player owner, string modelpath, integer abilityid, real duration, location loc returns unit
return CasterUseAbilityLevelStatic(owner,modelpath,abilityid,1,duration, GetLocationX(loc), GetLocationY(loc))
endfunction
function CasterUseAbilityLevelStaticLoc takes player owner, string modelpath, integer abilityid, integer level,real duration, location loc returns unit
return CasterUseAbilityLevelStatic(owner,modelpath,abilityid,level,duration, GetLocationX(loc), GetLocationY(loc))
endfunction
//====================================================================================================================================================================
function CasterCastAbilityLevelGroup takes player owner, integer abilid, integer level,string order, group targetgroup, boolean instant returns nothing
local group affected
local unit tempunit
local unit caster=null
if bj_wantDestroyGroup then
set bj_wantDestroyGroup=false
set affected=targetgroup
else
set affected=NewGroup()
call GroupAddGroup( targetgroup, affected)
endif
loop
set tempunit=FirstOfGroup(affected)
exitwhen tempunit == null
if instant then
if caster==null then
set caster=GetACaster()
call SetUnitOwner( caster, owner, false)
call UnitAddAbility( caster, abilid)
call SetUnitAbilityLevel( caster, abilid,level)
endif
if udg_sourcehack != null then
call SetUnitX(caster,GetLocationX(udg_sourcehack))
call SetUnitY(caster,GetLocationY(udg_sourcehack))
else
call SetUnitX(caster,GetUnitX(tempunit))
call SetUnitY(caster,GetUnitY(tempunit))
endif
if S2I(order) != 0 then
call IssueTargetOrderById( caster, S2I(order), tempunit )
else
call IssueTargetOrder( caster, order, tempunit )
endif
else
call CasterCastAbilityLevel( owner, abilid,level, order, tempunit, false)
endif
call GroupRemoveUnit(affected, tempunit)
endloop
if caster != null then
call UnitRemoveAbility( caster, abilid)
call RecycleCaster(caster)
endif
call ReleaseGroup(affected)
set affected=null
set tempunit=null
set caster=null
endfunction
function CasterCastAbilityGroup takes player owner, integer abilid, string order, group targetgroup, boolean instant returns nothing
call CasterCastAbilityLevelGroup(owner,abilid,1,order,targetgroup,instant)
endfunction
//====================================================================================================================================================================
function CasterAOE_IsFilterEnemy takes nothing returns boolean
return IsUnitEnemy( GetFilterUnit(), bj_groupEnumOwningPlayer ) and ( GetWidgetLife(GetFilterUnit()) > 0.405)
endfunction
function CasterAOE_IsFilterAlly takes nothing returns boolean
return IsUnitAlly( GetFilterUnit(), bj_groupEnumOwningPlayer ) and ( GetWidgetLife(GetFilterUnit()) > 0.405)
endfunction
//====================================================================================================================================================================
function CasterCastAbilityLevelAOE takes player owner, integer abilid, integer level, string order, real x, real y, real radius, boolean goodeffect, boolean instant returns nothing
local boolexpr b
local group aoe=NewGroup()
set bj_groupEnumOwningPlayer=owner
if goodeffect then
set b=Condition(function CasterAOE_IsFilterAlly)
else
set b=Condition(function CasterAOE_IsFilterEnemy)
endif
call CS_EnumUnitsInAOE(aoe, x,y, radius, b)
set bj_wantDestroyGroup=true
call CasterCastAbilityLevelGroup( owner, abilid, level, order, aoe, instant)
call DestroyBoolExpr(b)
set b=null
set aoe=null
endfunction
function CasterCastAbilityAOE takes player owner, integer abilid, string order, real x, real y, real radius, boolean goodeffect, boolean instant returns nothing
call CasterCastAbilityLevelAOE(owner,abilid,1,order,x,y,radius,goodeffect,instant)
endfunction
function CasterCastAbilityAOELoc takes player owner, integer abilid, string order, location center, real radius, boolean goodeffect, boolean instant returns nothing
call CasterCastAbilityLevelAOE(owner, abilid,1, order, GetLocationX(center), GetLocationY(center), radius, goodeffect, instant)
endfunction
function CasterCastAbilityLevelAOELoc takes player owner, integer abilid, integer level, string order, location center, real radius, boolean goodeffect, boolean instant returns nothing
call CasterCastAbilityLevelAOE(owner, abilid,level, order, GetLocationX(center), GetLocationY(center), radius, goodeffect, instant)
endfunction
//====================================================================================================================================================================
globals
timer cs_sourcehacktimer = CreateTimer()
timer cs_delayhacktimer = CreateTimer()
endglobals
function ResetSourceHack takes nothing returns nothing
if (udg_sourcehack!=null) then
call RemoveLocation(udg_sourcehack)
set udg_sourcehack=null
endif
endfunction
function CasterSetCastSource takes real x, real y returns nothing
if (udg_sourcehack==null) then
set udg_sourcehack=Location(x,y)
else
call MoveLocation(udg_sourcehack,x,y)
endif
call TimerStart(cs_sourcehacktimer,0.,false,function ResetSourceHack)
endfunction
function CasterSetCastSourceLoc takes location loc returns nothing
call CasterSetCastSource( GetLocationX(loc), GetLocationY(loc) )
endfunction
function ResetDelayHack takes nothing returns nothing
set udg_delayhack=0.
endfunction
function CasterSetRecycleDelay takes real Delay returns nothing
set udg_delayhack=Delay
call TimerStart(cs_delayhacktimer,0.,false,function ResetDelayHack)
endfunction
//====================================================================================================================================================================
//Super DamageOptions structure!
globals
attacktype cs_dopt_Atype = null
damagetype cs_dopt_Dtype = null
unittype cs_dopt_ExceptionUtype = null
real cs_dopt_ExceptionFct = 1.0
unittype cs_dopt_OnlyUtype = null
unittype cs_dopt_IgnoreUtype = null
integer cs_dopt_EnemyAlly = 0 //[ 0 = onlyenemies, 1= forceall, 2=onlyallies]
integer cs_dopt_dfab1 = 'null'
real cs_dopt_dfab1_fc = 1.0
integer cs_dopt_dfab2 = 'null'
real cs_dopt_dfab2_fc = 1.
integer cs_dopt_dfab3 = 'null'
real cs_dopt_dfab3_fc = 1.
real cs_dopt_Ally_fc = 1.
endglobals
function DamageTypes takes attacktype attT, damagetype dmgT returns integer
// set udg_castervars[100] = CS_H2I(attT)
// set udg_castervars[101] = CS_H2I(dmgT)
set cs_dopt_Atype=attT
set cs_dopt_Dtype=dmgT
return 1
endfunction
function DamageException takes unittype Exception, real ExceptionFactor returns integer
set cs_dopt_ExceptionUtype=Exception
set cs_dopt_ExceptionFct = ExceptionFactor
// set udg_castervars[102] = CS_H2I(Exception)
// set udg_castervars[103] = ExceptionFactor
return 2
endfunction
function DamageOnlyTo takes unittype ThisUnitType returns integer
set cs_dopt_OnlyUtype = ThisUnitType
// set udg_castervars[104] = CS_H2I(ThisUnitType)
return 4
endfunction
constant function DontDamageSelf takes nothing returns integer
return 8
endfunction
constant function DamageTrees takes nothing returns integer
return 16
endfunction
constant function DamageOnlyVisibles takes nothing returns integer
return 32
endfunction
function DamageOnlyEnemies takes nothing returns integer
set cs_dopt_EnemyAlly=0
// ouch, forgot what it was
return 64
endfunction
function ForceDamageAllies takes nothing returns integer
set cs_dopt_EnemyAlly=1
return 64
endfunction
function DamageOnlyAllies takes nothing returns integer
set cs_dopt_EnemyAlly=2
return 64
endfunction
function DamageFactorAbility1 takes integer spellid, real factor returns integer
set cs_dopt_dfab1=spellid
set cs_dopt_dfab1_fc=factor
// set udg_castervars[106]=CS_Rawcode2Real(spellid)
// set udg_castervars[107]=factor
return 128
endfunction
function DamageFactorAbility2 takes integer spellid, real factor returns integer
set cs_dopt_dfab2=spellid
set cs_dopt_dfab2_fc=factor
// set udg_castervars[108]=CS_Rawcode2Real(spellid)
// set udg_castervars[109]=factor
return 256
endfunction
function DamageFactorAbility3 takes integer spellid, real factor returns integer
set cs_dopt_dfab3=spellid
set cs_dopt_dfab3_fc=factor
//set udg_castervars[110]=CS_Rawcode2Real(spellid)
// set udg_castervars[111]=factor
return 512
endfunction
function DamageIgnore takes unittype ThisUnitType returns integer
set cs_dopt_IgnoreUtype = ThisUnitType
// set udg_castervars[112] = CS_H2I(ThisUnitType)
return 1024
endfunction
function DamageAlliedFactor takes real fct returns integer
set cs_dopt_Ally_fc = fct
// set udg_castervars[113] = fct
return 2048
endfunction
constant function ConsiderOnlyDeadUnits takes nothing returns integer
return 4096
endfunction
constant function IgnoreDeadState takes nothing returns integer
return 8192
endfunction
//===============================================================================================
function IsDamageOptionIncluded takes integer DamageOptions, integer whichDamageOption returns boolean
local integer i=8192
if (DamageOptions==0) then
return false
endif
loop
exitwhen (i<=whichDamageOption)
if (DamageOptions>=i) then
set DamageOptions=DamageOptions-i
endif
set i=i/2
endloop
return (DamageOptions>=whichDamageOption)
endfunction
//=================================================================================================
globals
unit cs_dmg_caster=null
endglobals
function GetDamageFactor takes unit u,attacktype a, damagetype d returns real
local real hp=GetWidgetLife(u)
local real mana=GetUnitState(u,UNIT_STATE_MANA)
local real r
//Since a unit is in that point, we don't need checks.
call SetUnitX(cs_dmg_caster,GetUnitX(u))
call SetUnitY(cs_dmg_caster,GetUnitY(u))
call SetUnitOwner(cs_dmg_caster,GetOwningPlayer(u),false)
set r=hp
if (hp<1) then
call SetWidgetLife(u,1)
set r=1
endif
call UnitDamageTarget(cs_dmg_caster,u,0.01,true,false,a,d,null)
call SetUnitOwner(cs_dmg_caster,Player(15),false)
if (mana>GetUnitState(u,UNIT_STATE_MANA)) then
//Unit had mana shield, return 1 and restore mana too.
call SetUnitState(u,UNIT_STATE_MANA,mana)
set r=1
else
set r= (r-GetWidgetLife(u))*100
endif
call SetWidgetLife(u,hp)
return r
endfunction
//======================================================================================================
// Fix for the unit type bugs from blizzard, amphibious units aren't considered ground for some reason
// so this considers any non flying unit as ground.
//
// Also heroes are resistant too, so in case UNIT_TYPE_RESISTANT is used it will return true in case the
// unit is a hero too.
//
function CS_IsUnitType takes unit u, unittype ut returns boolean
if (ut==UNIT_TYPE_GROUND) then
return not(IsUnitType(u,UNIT_TYPE_FLYING))
elseif (ut==UNIT_TYPE_RESISTANT) then
return IsUnitType(u,ut) or IsUnitType(u,UNIT_TYPE_HERO)
endif
return IsUnitType(u,ut)
endfunction
function GetDamageFactorByOptions takes unit hurter, unit target, integer d returns real
local real r=1
if (d>=8192) then
set d=d-8192
elseif (d>=4096) then
if (GetWidgetLife(target)>0.405) then
return 0.0
endif
set d=d-4096
elseif (GetWidgetLife(target)<=0.405) then
return 0.0
endif
if d>=2048 then
if IsUnitAlly(target,GetOwningPlayer(hurter)) then
set r=r* cs_dopt_Ally_fc
endif
set d=d-2048
endif
if d>=1024 then
if CS_IsUnitType(target, cs_dopt_IgnoreUtype ) then
return 0.0
endif
set d=d-1024
endif
if d>=512 then
if GetUnitAbilityLevel(target, cs_dopt_dfab1 )>0 then
set r=r*cs_dopt_dfab1_fc
endif
set d=d-512
endif
if d>=256 then
if GetUnitAbilityLevel(target, cs_dopt_dfab2 )>0 then
set r=r*cs_dopt_dfab2_fc
endif
set d=d-256
endif
if d>=128 then
if GetUnitAbilityLevel(target, cs_dopt_dfab3 )>0 then
set r=r*cs_dopt_dfab3_fc
endif
set d=d-128
endif
if d>=64 then
if (cs_dopt_EnemyAlly==0) and IsUnitAlly(target,GetOwningPlayer(hurter)) then
return 0.0
elseif (cs_dopt_EnemyAlly==2) and IsUnitEnemy(target,GetOwningPlayer(hurter)) then
return 0.0
endif
set d=d-64
endif
if d>=32 then
set d=d-32
if not IsUnitVisible(target,GetOwningPlayer(hurter)) then
return 0.0
endif
endif
if d>=16 then
set d=d-16
endif
if d>=8 then
set d=d-8
if hurter==target then
return 0.0
endif
endif
if d>=4 then
set d=d-4
if not CS_IsUnitType( target,cs_dopt_OnlyUtype) then
return 0.0
endif
endif
if d>=2 then
set d=d-2
if CS_IsUnitType( target, cs_dopt_ExceptionUtype ) then
set r=r* cs_dopt_ExceptionFct
endif
endif
if d>=1 then
set d=d-1
set r=r*GetDamageFactor(target,cs_dopt_Atype,cs_dopt_Dtype)
endif
return r
endfunction
//======================================================================================================================
// This used to be needed because in 1.17 UnitDamageTarget didn't consider the damagetype argument, this bug
// was fixed in 1.18, and we no longer need this function, left for compatibility.
//
function DamageUnitByTypes takes unit hurter, unit target, real dmg, attacktype attT, damagetype dmgT returns boolean
return UnitDamageTarget(hurter,target,dmg,true,false,attT,dmgT,null)
endfunction
//=============================================================================================================================
function DamageUnitByOptions takes unit hurter, unit target, real dmg, integer DamageOptions returns boolean
local real f=GetDamageFactorByOptions(hurter,target,DamageOptions)
if (f==0) then
return false
endif
return UnitDamageTarget(hurter,target,dmg*f,true,false,null,null,null)
endfunction
//=============================================================================================================================
function DamageUnit takes player hurter, real damage, unit victim returns boolean
call SetUnitX(cs_dmg_caster,GetUnitX(victim))
call SetUnitY(cs_dmg_caster,GetUnitY(victim))
call SetUnitOwner(cs_dmg_caster,hurter,false)
call DamageUnitByTypes(cs_dmg_caster,victim,damage,cs_DefaultAttackType,cs_DefaultDamageType)
call SetUnitOwner(cs_dmg_caster,Player(15),false)
return GetWidgetLife(victim)<=0 // I thought UnitDamageTarget returned true when it killed the unit, but nope, it returns true when it was able to do the damage.
endfunction
//====================================================================================================================================================================
function UnitDamageUnitTimed_Child takes nothing returns nothing
local real damage = udg_castervars[0]
local real damageperiod= udg_castervars[2]
local effect fx=bj_lastCreatedEffect
local timer t=NewTimer()
local unit hurter=udg_currenthurter
local real next=0
local integer i=0
local real c
local unit target=udg_currentcaster
local damagetype dmgT=ConvertDamageType(R2I(udg_castervars[4]))
local attacktype attT=ConvertAttackType(R2I(udg_castervars[3]))
call TimerStart(t, udg_castervars[1]-0.01, false,null)
loop
if TimerGetElapsed(t) >= next then
exitwhen not UnitDamageTarget(hurter, target, damage,true,false, attT, dmgT,null)
exitwhen GetWidgetLife(target)<=0.405
set i=i+1
set next=i*damageperiod
endif
exitwhen (TimerGetRemaining(t) <= 0) or GetWidgetLife(target)<=0.405
call TriggerSleepAction(0)
endloop
call DestroyEffect(fx)
call ReleaseTimer(t)
set fx=null
set dmgT=null
set attT=null
endfunction
function UnitDamageUnitTimed takes unit hurter, real damageps, real damageperiod, real duration, unit target, string modelpath, string attachPointName, attacktype attT, damagetype dmgT returns nothing
local unit c=udg_currentcaster
set bj_lastCreatedEffect=AddSpecialEffectTarget( modelpath, target,attachPointName )
set udg_currentcaster=target
set udg_castervars[0]=damageps
set udg_castervars[1]=duration
set udg_castervars[2]=damageperiod
set udg_castervars[3]=CS_H2I(attT)
set udg_castervars[4]=CS_H2I(dmgT)
set udg_currenthurter=hurter
call ExecuteFunc("UnitDamageUnitTimed_Child")
set udg_currentcaster=c
set c=null
endfunction
//=============================================================================================================
// Left for compatibility
//
function DamageUnitTimedEx_Child takes nothing returns nothing
local real damage = udg_castervars[0]
local real damageperiod= udg_castervars[2]
local effect fx=bj_lastCreatedEffect
local timer t=NewTimer()
local integer id=udg_currentabi
local real next=0
local integer i=0
local real c
local unit target=udg_currentcaster
call TimerStart(t, udg_castervars[1]-0.01, false,null)
loop
if TimerGetElapsed(t) >= next then
exitwhen DamageUnit( Player(id), damage, target)
set i=i+1
set next=i*damageperiod
endif
exitwhen (TimerGetRemaining(t) <= 0) or GetWidgetLife(target)<=0.405
call TriggerSleepAction(0)
endloop
call DestroyEffect(fx)
call ReleaseTimer(t)
set fx=null
endfunction
function DamageUnitTimedEx takes player owner, real damageps, real damageperiod, real duration, unit target, string modelpath, string attachPointName returns nothing
local unit c=udg_currentcaster
set bj_lastCreatedEffect=AddSpecialEffectTarget( modelpath, target,attachPointName )
set udg_currentcaster=target
set udg_castervars[0]=damageps
set udg_castervars[1]=duration
set udg_castervars[2]=damageperiod
set udg_currentabi=GetPlayerId( owner )
call ExecuteFunc("DamageUnitTimedEx_Child")
set udg_currentcaster=c
set c=null
endfunction
function DamageUnitTimed takes player owner, real damageps, real duration, unit target, string modelpath, string attachPointName returns nothing
call DamageUnitTimedEx(owner , damageps, 1, duration, target, modelpath, attachPointName )
endfunction
//[
private struct dostruct
integer value
attacktype attt
damagetype dmgt
unittype excp
real excf
unittype only
integer allied
integer dfab1
real dfab1_fc
integer dfab2
real dfab2_fc
integer dfab3
real dfab3_fc
unittype ign
real ally_fc
endstruct
// integer k used to point to a table in gamecache, this is the reason I am not using dostruct directly
// the evil backwards compatibility... You shall find out why OOP guys like encapsullation so much...
//
// It's fun, it was first an integer pointing to a mission in gamecache, then it was a dynamic array
// and now it is a struct... so much history.
function SetDamageOptions_i takes integer k, integer DamageOptions returns nothing
local integer d=DamageOptions
set dostruct(k).value=d
if (d>=8192) then
set d=d-8192
endif
if (d>=4096) then
set d=d-4096
endif
if d>=2048 then
set dostruct(k).ally_fc=cs_dopt_Ally_fc
set d=d-2048
endif
if d>=1024 then
set dostruct(k).ign = cs_dopt_IgnoreUtype
set d=d-1024
endif
if d>=512 then
set dostruct(k).dfab3=cs_dopt_dfab3
set dostruct(k).dfab3_fc=cs_dopt_dfab3_fc
set d=d-512
endif
if d>=256 then
set dostruct(k).dfab2=cs_dopt_dfab2
set dostruct(k).dfab2_fc=cs_dopt_dfab2_fc
set d=d-256
endif
if d>=128 then
set dostruct(k).dfab1=cs_dopt_dfab1
set dostruct(k).dfab1_fc=cs_dopt_dfab1_fc
set d=d-128
endif
//[0 integer value][1 integer attT][2 integer dmgT][3 integer excp][4 real excf][5 integer only]
//[6 integer allied][7 integer ab1][8 real fc1][9 integer ab2][10 real fc2][11 integer ab3][12 real fc3]
//[13 integer ign][14 real allf]
if d >= 64 then
set d=d-64
set dostruct(k).allied=cs_dopt_EnemyAlly //[6=allied]
endif
if d >= 32 then
set d=d-32
endif
if d >= 16 then
set d=d-16
endif
if d >= 8 then
set d=d-8
endif
if d >= 4 then
set dostruct(k).only=cs_dopt_OnlyUtype
set d=d-4
endif
if d >= 2 then
set dostruct(k).excp= cs_dopt_ExceptionUtype
set dostruct(k).excf= cs_dopt_ExceptionFct
set d=d-2
endif
if d >= 1 then
set dostruct(k).attt=cs_dopt_Atype
set dostruct(k).dmgt=cs_dopt_Dtype
endif
endfunction
// inline friendly:)
function SetDamageOptions takes integer id, integer DamageOptions returns nothing
call SetDamageOptions_i(id,DamageOptions)
endfunction
function CreateDamageOptions takes integer DamageOptions returns integer
local integer n =integer(dostruct.create())
call SetDamageOptions_i(n,DamageOptions)
return n
endfunction
function DestroyDamageOptions takes integer id returns nothing
call dostruct(id).destroy()
endfunction
function LoadDamageOptions takes integer id returns integer
local integer opt=dostruct(id).value
local integer v=opt
if v>=8192 then
set v=v-8192
endif
if v>=4096 then
set v=v-4096
endif
if v>=2048 then
set cs_dopt_Ally_fc = dostruct(id).ally_fc
set v=v-2028
endif
if v>=1024 then
set cs_dopt_IgnoreUtype = dostruct(id).ign
set v=v-1024
endif
if v>=512 then
set cs_dopt_dfab3 =dostruct(id).dfab3
set cs_dopt_dfab3_fc=dostruct(id).dfab3_fc
set v=v-512
endif
if v>=256 then
set cs_dopt_dfab2 =dostruct(id).dfab2
set cs_dopt_dfab2_fc=dostruct(id).dfab2_fc
set v=v-256
endif
if v>=128 then
set cs_dopt_dfab1 =dostruct(id).dfab1
set cs_dopt_dfab1_fc=dostruct(id).dfab1_fc
set v=v-128
endif
if v >= 64 then
set v=v-64
set cs_dopt_EnemyAlly= dostruct(id).allied
endif
if v >= 32 then
set v=v-32
endif
if v >= 16 then
set v=v-16
endif
if v >= 8 then
set v=v-8
endif
if v >= 4 then
set cs_dopt_OnlyUtype = dostruct(id).only
set v=v-4
endif
if v >= 2 then
set cs_dopt_ExceptionUtype= dostruct(id).excp
set cs_dopt_ExceptionFct= dostruct(id).excf
set v=v-2
endif
if v >= 1 then
set cs_dopt_Atype= dostruct(id).attt
set cs_dopt_Dtype= dostruct(id).dmgt
endif
return opt
endfunction
//==================================================================================================
function IsDestructableTree_withcs takes destructable d returns boolean
local boolean b
local boolean i=IsDestructableInvulnerable(d)
call SetUnitX(cs_dmg_caster,GetWidgetX(d))
call SetUnitY(cs_dmg_caster,GetWidgetY(d))
if i then
call SetDestructableInvulnerable(d,false)
endif
call UnitAddAbility(cs_dmg_caster,cs_DamageTreeDetectorId)
set b=(IssueTargetOrder(cs_dmg_caster,"eattree",d))
call UnitRemoveAbility(cs_dmg_caster,cs_DamageTreeDetectorId)
if i then
call SetDestructableInvulnerable(d,true)
endif
return b
endfunction
function IsDestructableTree takes destructable d returns boolean
local string k=I2S(GetDestructableTypeId(d))
local boolean b
if HaveStoredBoolean(cs_cache,"trees",k) then
set b=GetStoredBoolean(cs_cache,"trees",k)
return b
else
set b=IsDestructableTree_withcs(d)
call StoreBoolean(cs_cache,"trees",k,b)
endif
return b
endfunction
//===============================================================================================
function DamageDestructablesInCircleEnum takes nothing returns nothing
local destructable d=GetEnumDestructable()
local unit u=udg_currentcaster
if (GetWidgetLife(d)>0) and not(IsDestructableInvulnerable(d)) and ((Pow(GetDestructableX(d)-udg_castervars[200],2)+Pow(GetDestructableY(d)-udg_castervars[201],2)) <= udg_castervars[202]) then
call SetWidgetLife(d,GetWidgetLife(d)-udg_castervars[203])
endif
set udg_currentcaster=u
set u=null
set d=null
endfunction
function DamageDestructablesInCircle takes real x, real y, real radius, real dmg returns nothing
local rect r=Rect(x - radius,y - radius,x + radius,y + radius)
set udg_castervars[200]=x
set udg_castervars[201]=y
set udg_castervars[202]=radius*radius
set udg_castervars[203]=dmg
call EnumDestructablesInRect(r,null,function DamageDestructablesInCircleEnum)
call RemoveRect(r)
set r=null
endfunction
function DamageDestructablesInCircleLoc takes location loc, real radius, real dmg returns nothing
call DamageDestructablesInCircle(GetLocationX(loc),GetLocationY(loc),radius,dmg)
endfunction
function DamageTreesInCircleEnum takes nothing returns nothing
local destructable d=GetEnumDestructable()
if (GetWidgetLife(d)>0) and not(IsDestructableInvulnerable(d)) and ((Pow(GetDestructableX(d)-udg_castervars[200],2)+Pow(GetDestructableY(d)-udg_castervars[201],2)) <= udg_castervars[202]) and (IsDestructableTree(d)) then
call KillDestructable(d)
endif
set d=null
endfunction
function DamageTreesInCircle takes real x, real y, real radius returns nothing
local rect r=Rect(x - radius,y - radius,x + radius,y + radius)
set udg_castervars[200]=x
set udg_castervars[201]=y
set udg_castervars[202]=radius*radius
call EnumDestructablesInRect(r,null,function DamageTreesInCircleEnum)
call RemoveRect(r)
set r=null
endfunction
function DamageTreesInCircleLoc takes location loc, real radius returns nothing
call DamageTreesInCircle(GetLocationX(loc),GetLocationY(loc),radius)
endfunction
function DamageUnitGroupEx takes unit hurter, real damage, group targetgroup, integer DamageOptions returns nothing
local group affected
local unit p
if bj_wantDestroyGroup then
set bj_wantDestroyGroup=false
set affected=targetgroup
else
set affected=NewGroup()
call GroupAddGroup( targetgroup, affected)
endif
loop
set p=FirstOfGroup(affected)
exitwhen p==null
call DamageUnitByOptions(hurter,p,damage,DamageOptions)
call GroupRemoveUnit(affected,p)
endloop
call ReleaseGroup(affected)
set affected=null
set p=null
endfunction
function DamageUnitsInAOEEx takes unit hurter, real damage, real x, real y, real radius, boolean affectallied, integer DamageOptions returns nothing
local boolexpr b=null
local group aoe=NewGroup()
local integer d=DamageOptions
set bj_groupEnumOwningPlayer=GetOwningPlayer(hurter)
if d>=8192 then
set d=d-8192
endif
if d>=4096 then
set d=d-4096
endif
if d>=2048 then
set d=d-2048
endif
if d>=1024 then
set d=d-1024
endif
if d>=512 then
set d=d-512
endif
if d>=256 then
set d=d-256
endif
if d>=128 then
set d=d-128
endif
if d>=64 then
if (cs_dopt_EnemyAlly==2) then
set b=Condition(function CasterAOE_IsFilterAlly)
elseif (cs_dopt_EnemyAlly==1) then
else
set b=Condition(function CasterAOE_IsFilterEnemy)
endif
set d=d-64
elseif not(affectallied) then
set b=Condition(function CasterAOE_IsFilterEnemy)
endif
if d>=32 then
set d=d-32
endif
if d>=16 then
call DamageTreesInCircle(x,y,radius)
endif
call CS_EnumUnitsInAOE(aoe, x,y, radius, b)
set bj_wantDestroyGroup=true
call DamageUnitGroupEx( hurter, damage, aoe,DamageOptions)
//call DestroyBoolExpr(b)
set b=null
set aoe=null
endfunction
function DamageUnitsInAOEExLoc takes unit hurter, real damage, location loc, real radius, boolean affectallied, integer DamageOptions returns nothing
call DamageUnitsInAOEEx(hurter,damage, GetLocationX(loc), GetLocationY(loc), radius, affectallied,DamageOptions)
endfunction
function DamageUnitGroup takes player hurter, real damage, group targetgroup returns nothing
call SetUnitOwner(cs_dmg_caster,hurter,false)
call DamageUnitGroupEx(cs_dmg_caster,damage,targetgroup,0)
call SetUnitOwner(cs_dmg_caster,Player(15),false)
endfunction
//====================================================================================================================================================================
function DamageUnitsInAOE takes player hurter, real damage, real x, real y, real radius, boolean affectallied returns nothing
call SetUnitOwner(cs_dmg_caster,hurter,false)
call DamageUnitsInAOEEx(cs_dmg_caster,damage,x,y,radius,affectallied,0)
call SetUnitOwner(cs_dmg_caster,Player(15),false)
endfunction
function DamageUnitsInAOELoc takes player hurter, real damage, location loc, real radius, boolean affectallied returns nothing
call DamageUnitsInAOE( hurter, damage, GetLocationX(loc), GetLocationY(loc), radius, affectallied)
endfunction
//====================================================================================================================================================================
function AddAreaDamagerForUnit_Child takes nothing returns nothing
local real D
local real damageps = udg_castervars[0]
local real area = udg_castervars[2]
local real damageperiod = udg_castervars[3]
local real excd=udg_castervars[8]
local boolean affectallies = (udg_castervars[4]>=1)
local boolean onlyallies = (udg_castervars[4]==2)
local boolean self = (udg_castervars[5]==1)
local unit hurter=udg_currenthurter
local unit fire = udg_currentcaster
local player owner = GetOwningPlayer(fire)
local timer t = NewTimer()
local real next = 0
local integer a = 0
local group inrange = NewGroup()
local string c
local string art=bj_lastPlayedMusic
local string attach=""
local unit picked
local boolean recicled=false
local unittype only=null
local unittype ign=null
local unittype exce=null
local attacktype attT
local damagetype dmgT
local boolean trees=(udg_castervars[11]==1)
local boolean inv=(udg_castervars[12]==1)
local integer a1=0
local integer a2=0
local integer a3=0
local real f1=udg_castervars[107]
local real f2=udg_castervars[109]
local real f3=udg_castervars[111]
local real allf=udg_castervars[113]
local effect array fx
local integer deadcond=R2I(udg_castervars[114])
local boolean deadeval=false
local integer fxn=0
set fx[0]=bj_lastCreatedEffect
if f1!=1 then
set a1=CS_LoadRawcodeFromReal(106)
endif
if f2!=1 then
set a2=CS_LoadRawcodeFromReal(108)
endif
if f3!=1 then
set a3=CS_LoadRawcodeFromReal(110)
endif
if udg_castervars[112]!=-1 then
set ign=ConvertUnitType(R2I(udg_castervars[112]))
endif
if udg_castervars[6]!=-1 then
set only=ConvertUnitType(R2I(udg_castervars[6]))
endif
if udg_castervars[7]!=-1 then
set exce=ConvertUnitType(R2I(udg_castervars[7]))
endif
if udg_castervars[9]!=-1 then
set attT=ConvertAttackType(R2I(udg_castervars[9]))
else
set attT=cs_DefaultAttackType
endif
if udg_castervars[10]!=-1 then
set dmgT=ConvertDamageType(R2I(udg_castervars[10]))
else
set dmgT=cs_DefaultDamageType
endif
loop
set c=SubString(art,a,a+1)
exitwhen c=="!" or c==""
set attach=attach+c
set a=a+1
endloop
set art=SubString(art,a+1,10000)
call TimerStart(t, udg_castervars[1]-0.01, false,null)
set a=0
loop
loop
exitwhen fxn<=0
call DestroyEffect(fx[fxn])
set fx[fxn]=null
set fxn=fxn-1
endloop
if IsUnitInGroup( fire, udg_casters) then
set recicled=true
call GroupRemoveUnit( udg_casters,fire)
endif
exitwhen recicled
if TimerGetElapsed(t) >= next then
set a=a+1
set next=a*damageperiod
call CS_EnumUnitsInAOE(inrange, GetUnitX(fire), GetUnitY(fire), area, null )
if trees then
call DamageTreesInCircle(GetUnitX(fire), GetUnitY(fire), area)
endif
loop
set picked=FirstOfGroup(inrange)
exitwhen picked==null
if (deadcond==0) then
set deadeval=(GetWidgetLife(picked)>0.405)
elseif(deadcond==1)then
set deadeval=(GetWidgetLife(picked)<=0.405)
else
set deadeval=true
endif
if (self or picked!=hurter) and not(GetWidgetLife(picked)<=0.405) and ( ((affectallies or onlyallies) and IsUnitAlly(picked, owner)) or (not(onlyallies) and IsUnitEnemy(picked, owner)) ) and (only==null or CS_IsUnitType(picked,only)) and (ign==null or not(CS_IsUnitType(picked,ign))) then
set D=damageps
if (allf!=1) and IsUnitAlly(picked, owner) then
set D=D*allf
endif
if (exce!=null) and CS_IsUnitType(picked,exce) then
set D=D*excd
endif
if inv and not(IsUnitVisible(picked,owner)) then
set D=0
endif
if (a1!=0) and (GetUnitAbilityLevel(picked,a1)>0) then
set D=D*f1
endif
if (a2!=0) and (GetUnitAbilityLevel(picked,a2)>0) then
set D=D*f2
endif
if (a3!=0) and (GetUnitAbilityLevel(picked,a3)>0) then
set D=D*f3
endif
if D!=0 then
call DamageUnitByTypes(hurter,picked,D,attT,dmgT )
if (art!="") and (art!=null) then
set fxn=fxn+1
set fx[fxn]=AddSpecialEffectTarget(art,picked,attach)
endif
endif
endif
call GroupRemoveUnit(inrange,picked)
endloop
endif
exitwhen TimerGetRemaining(t)<=0
call TriggerSleepAction(0)
endloop
call ReleaseGroup(inrange)
call DestroyEffect(fx[0])
call TriggerSleepAction(2)
call RecicleCaster(fire)
call ReleaseTimer(t)
set inrange=null
set fire=null
set owner=null
set fx[0]=null
set picked=null
set hurter=null
set only=null
set ign=null
set exce=null
set attT=null
set dmgT=null
endfunction
function AddAreaDamagerForUnit takes unit hurter, string modelpath, string targetart, string targetattach, real x, real y, real damage , real damageperiod, real duration, real area, boolean affectallies, integer DamageOptions returns unit
local string s=bj_lastPlayedMusic
local integer v=DamageOptions
set bj_lastPlayedMusic=targetattach+"!"+targetart
set udg_currentcaster=GetACaster()
call SetUnitPosition( udg_currentcaster, x, y)
set bj_lastCreatedEffect = AddSpecialEffectTarget( modelpath, udg_currentcaster,"origin" )
set udg_castervars[0]=damage
set udg_castervars[1]=duration
set udg_castervars[2]=area
set udg_castervars[3]=damageperiod
if(v>=8192)then
set udg_castervars[114]=2
set v=v-8192
elseif (v>=4096)then
set udg_castervars[114]=1
set v=v-4096
else
set udg_castervars[114]=0
endif
if v>=2048 then
set v=v-2048
else
set udg_castervars[113]=1
endif
if v >= 1024 then
set v=v-1024
else
set udg_castervars[112]=-1
endif
if v >= 512 then
set v=v-512
else
set udg_castervars[111]=0
endif
if v >= 256 then
set v=v-256
else
set udg_castervars[109]=0
endif
if v >= 128 then
set v=v-128
else
set udg_castervars[107]=0
endif
if v >= 64 then
set v=v-64
set udg_castervars[4]=cs_dopt_EnemyAlly
else
set udg_castervars[4]=IntegerTertiaryOp(affectallies,1,0)
endif
if v >= 32 then
set udg_castervars[12]=1
set v=v-32
else
set udg_castervars[12]=0
endif
if v >= 16 then
set udg_castervars[11]=1
set v=v-16
else
set udg_castervars[11]=0
endif
if v >= 8 then
set udg_castervars[5]=0
set v=v-8
else
set udg_castervars[5]=1
endif
if v >= 4 then
set udg_castervars[6]=CS_H2I(cs_dopt_OnlyUtype)
set v=v-4
else
set udg_castervars[6]=-1
endif
if v >= 2 then
set udg_castervars[7]=CS_H2I(cs_dopt_ExceptionUtype)
set udg_castervars[8]=damage*cs_dopt_ExceptionFct
set v=v-2
else
set udg_castervars[7]=-1
set udg_castervars[8]=-1
endif
if v >= 1 then
set udg_castervars[9]= CS_H2I(cs_dopt_Atype)
set udg_castervars[10]= CS_H2I(cs_dopt_Dtype)
else
set udg_castervars[9]=-1
set udg_castervars[10]=-1
endif
set udg_currenthurter=hurter
call SetUnitOwner( udg_currentcaster, GetOwningPlayer(hurter), true)
call ExecuteFunc("AddAreaDamagerForUnit_Child")
set bj_lastPlayedMusic=s
return udg_currentcaster
endfunction
function AddAreaDamagerForUnitLoc takes unit hurter, string modelpath, string targetart, string targetattach, location loc, real damage , real damageperiod, real duration, real area, boolean affectallies, integer DamageOptions returns unit
return AddAreaDamagerForUnit(hurter,modelpath,targetart,targetattach,GetLocationX(loc),GetLocationY(loc), damage , damageperiod, duration, area,affectallies, DamageOptions)
endfunction
function AddDamagingEffectEx takes player owner, string modelpath, string targetart, string targetattach, real x, real y, real damage , real damageperiod, real duration, real area, boolean affectallies returns unit
local string s=bj_lastPlayedMusic
set bj_lastPlayedMusic=targetattach+"!"+targetart
set udg_currentcaster=GetACaster()
call SetUnitPosition( udg_currentcaster, x, y)
set bj_lastCreatedEffect = AddSpecialEffectTarget( modelpath, udg_currentcaster,"origin" )
set udg_castervars[0]=damage
set udg_castervars[1]=duration
set udg_castervars[2]=area
set udg_castervars[3]=damageperiod
set udg_castervars[4]=IntegerTertiaryOp(affectallies,1,0)
set udg_castervars[5]=1
set udg_castervars[6]=-1
set udg_castervars[7]=-1
set udg_castervars[8]=-1
set udg_castervars[9]=-1
set udg_castervars[10]=-1
set udg_castervars[107]=0
set udg_castervars[109]=0
set udg_castervars[111]=0
set udg_castervars[112]=-1
set udg_castervars[113]=1
set udg_currenthurter=udg_currentcaster
call SetUnitOwner( udg_currentcaster, owner, true)
call ExecuteFunc("AddAreaDamagerForUnit_Child")
set bj_lastPlayedMusic=s
return udg_currentcaster
endfunction
function AddDamagingEffectExLoc takes player owner, string modelpath, string targetart, string targetattach, location loc, real damage , real damageperiod, real duration, real area, boolean affectallies returns unit
return AddDamagingEffectEx( owner, modelpath, targetart, targetattach, GetLocationX(loc), GetLocationY(loc), damage , damageperiod, duration, area, affectallies )
endfunction
function AddDamagingEffect takes player owner, string modelpath, real x, real y, real damageps , real duration, real area, boolean affectallies returns unit
return AddDamagingEffectEx( owner, modelpath, "", "", x, y, damageps , 1, duration, area, affectallies )
endfunction
function AddDamagingEffectLoc takes player owner, string modelpath, location loc, real damageps , real duration, real area, boolean affectallies returns unit
return AddDamagingEffectEx( owner, modelpath, "", "", GetLocationX(loc), GetLocationY(loc), damageps ,1, duration, area, affectallies)
endfunction
//============================================================================================================
function UnitMoveToAsProjectileAnySpeed_Move takes unit m, csprojectile k returns boolean
local boolean tounit = k.tounit
local unit tg
local real x2
local real y2
local real z2
local real x1=GetUnitX(m)
local real y1=GetUnitY(m)
local real z1=GetUnitFlyHeight(m)
local real g
local real d
local real od
local real v
local real time
local integer n
local boolean done=false
local effect fx
if tounit then
set tg=k.target
if (GetWidgetLife(tg)<=0.405) then
set tounit=false
set k.tounit=false
else
set x2=GetUnitX(tg)
set y2=GetUnitY(tg)
set z2=GetUnitFlyHeight(tg)+k.z2o
set n=k.N
if (n==0) then
//Using the counter prevents us to save z2,x2,y2 too much times and saves speed
set k.z2=z2
set k.x2=x2 // Backup stuff just in case
set k.y2=y2
elseif (n==25) then
set n=0
else
set n=n+1
endif
set k.N=n
endif
set tg=null
endif
if not(tounit) then
set z2=k.z2
set x2=k.x2
set y2=k.y2
endif
set g=Atan2(y2-y1,x2-x1)
call SetUnitFacing(m,g*bj_RADTODEG)
set v=k.speed
set d= v * cs_TIMER_CYCLE
set od=SquareRoot(Pow(x1-x2,2) + Pow(y1-y2,2))
if( od <=d )then
call SetUnitX(m,x2)
call SetUnitY(m,y2)
set done=true
else
call SetUnitX(m,x1+d*Cos(g))
call SetUnitY(m,y1+d*Sin(g))
endif
set g=k.acel
set time= od / v
set d=v
set v=(z2-z1+0.5*g*time*time)/time //z speed
if (GetUnitTypeId(m)==cs_CasterUnitId) then
call SetUnitAnimationByIndex(m,R2I(Atan2(v,d)* bj_RADTODEG)+90) //Thanks infrane!
endif
call SetUnitFlyHeight(m,z1+v*cs_TIMER_CYCLE,0)
set d=( Pow(GetUnitX(m)-x2,2) + Pow(GetUnitY(m)-y2,2) )
if (done or (d<=400)) then //So the actual distance is less than or equal to 20
set done=true
set k.done=true
set fx=k.fx
if (fx!=null) then
call SetUnitAnimationByIndex(m,91)
call DestroyEffect(fx)
set fx=null
endif
endif
return done
endfunction
interface csmissile
integer mtype=0
unit m //the missile
endinterface
globals
constant integer cs_TYPE_COLLISIONMISSILE=2
constant integer cs_TYPE_PROJECTILE=1
endglobals
struct cscollisionmissile extends csmissile
integer state //state of the collision missile
trigger t
triggeraction ac
real collision
real speed
real aspeed //angle speed
real f //facing of collisionmissile
real maxd
integer ttype=0 //target type
static constant integer ttype_widget=2
static constant integer ttype_point=1
static constant integer ttype_none=0
real tx //homing target x
real ty //homing target y
widget tw //homing target widget
boolean pfx=false //periodic fx mode
real pfx_current //current pfx (timer)
real pfx_dur //duration of a periodic effect
string pfx_path //path for the periodix fx
boolean new //Was it a new collision missile or must be recycled?
effect fx
integer tag=0 //custom tag for the user.
endstruct
function CollisionMissile_Destroyer takes unit m, cscollisionmissile k, trigger T returns nothing
local trigger tr = T
local triggeraction ta = k.ac
if (k.fx!=null) then
call DestroyEffect(k.fx)
endif
if (k.new) then
call ExplodeUnitBJ(m)
else
call RecicleCasterAfterCastEx(m,4,0,true)
endif
call TriggerRemoveAction(tr,ta)
//call TriggerRemoveAction(T,GetArrayTriggerAction(k,18)) //[18=triggeraction]
call k.destroy()
//call Hostage(CS_H2I(tr))
call DestroyTrigger(tr)
//call CleanAttachedVars(T)
//call DestroyTrigger(T)
set tr = null
set ta = null
endfunction
function GetTriggerCollisionMissile takes nothing returns unit
return s__csmissile_m[GetCSData(GetTriggeringTrigger()) ]
endfunction
function CollisionMissile_Move takes unit m, cscollisionmissile k returns boolean
local integer state= k.state
local boolean done
local real d
local real F
local real asp
local real x
local real nx
local real y
local real ny
local integer tt
local widget wd
local trigger TTT
if (state==2) then
set TTT=k.t
call TriggerExecute(TTT)
set TTT=null
set k.state=3
return false
elseif (state==3) then
call CollisionMissile_Destroyer(m,k,k.t)
return true
else
if (state==1) then
call TriggerRegisterUnitInRange(k.t,m,k.collision,null)
set k.state=0
endif
set d=k.speed * cs_TIMER_CYCLE
set F=k.f
set asp=k.aspeed
set x=GetUnitX(m)
set y=GetUnitY(m)
if (asp!=0) then
set tt=k.ttype
if (tt==cscollisionmissile.ttype_point) or (tt==cscollisionmissile.ttype_widget ) then
if (tt==cscollisionmissile.ttype_point) then
set nx=k.tx
set ny=k.ty
set F=Angles_MoveAngleTowardsAngle(F,Atan2(ny-y,nx-x)*bj_RADTODEG, asp * cs_TIMER_CYCLE)
else
set wd=k.tw
if (GetWidgetLife(wd)<=0.405) then
set k.ttype=cscollisionmissile.ttype_none
set nx=x+0.001
set ny=y+0.001
set F=F+ asp * cs_TIMER_CYCLE
else
set nx=GetWidgetX(wd)
set ny=GetWidgetY(wd)
set F=Angles_MoveAngleTowardsAngle(F,Atan2(ny-y,nx-x)*bj_RADTODEG, asp * cs_TIMER_CYCLE)
endif
set wd=null
endif
else
set F=F+ asp * cs_TIMER_CYCLE
endif
set k.f=F
call SetUnitFacing(m,F)
endif
set F=F*bj_DEGTORAD
set nx=x+d*Cos(F)
set ny=y+d*Sin(F)
set d=k.maxd-d
set k.maxd=d
set done=(d<=0)
if (not done) then
if not(CS_MoveUnit(m,nx,ny)) then
set done=true
elseif (k.pfx) then
set F= k.pfx_current+cs_TIMER_CYCLE
if (F>=k.pfx_dur) then
call DestroyEffect(AddSpecialEffectTarget(k.pfx_path, m, "origin" ))
set k.pfx_current=0.
else
set k.pfx_current=F
endif
endif
endif
endif
if done then
call DisableTrigger(k.t)
call DestroyEffect(k.fx)
set k.fx=null
set k.state=2
return false
endif
return false
endfunction
function CasterSystemMovementTimer takes nothing returns nothing
local timer ti
local integer n=cs_missilecount
local integer i=1
local unit p
local csmissile k
loop
exitwhen (i>n)
set k=cs_missiles[i]
if (k.mtype==cs_TYPE_COLLISIONMISSILE) then
set p=k.m
if not(CollisionMissile_Move(p,k)) then
set i=i+1
else
set cs_missiles[i]=cs_missiles[n]
set n=n-1
endif
elseif (k.mtype==cs_TYPE_PROJECTILE) then
set p=k.m
if not(UnitMoveToAsProjectileAnySpeed_Move(p,k )) then
set i=i+1
else
set cs_missiles[i]=cs_missiles[n]
set n=n-1
endif
else
set i=i+1
call BJDebugMsg("Caster System: Unexpected Error (1) Wrong Array:"+I2S(k)+" ; "+I2S(k.mtype))
endif
endloop
if (n==0) then
call PauseTimer(cs_movementtimer)
set cs_movementtimer_active=false
endif
set cs_missilecount=n
set p=null
endfunction
struct csprojectile extends csmissile
boolean tounit //Is it directed towards a unit?
unit target //if so, what target?
real z2
real x2
real y2
real speed
real acel
boolean done=false
effect fx
real z2o // offset for z2, useful for flyers
integer N=0
endstruct
function UnitMoveToAsProjectileAnySpeed_Effect takes unit m, effect fx, real speed, real arc, real x2, real y2, unit target, real z2 returns nothing
//
// The internal projectile system used by all the projectile functions
//
local timer t //=GetTableTimer("CasterSystem","MOVEMENT_TIMER")
local string km
local csprojectile k
set km=I2S(CS_H2I(m))
set k=GetTableInt("MOVEMENT_TABLES",km)
if (k>0) then
set k.done=true
endif
set k=csprojectile.create()
set k.m=m
call StoreInteger(cs_cache,"MOVEMENT_TABLES",km,k)
set cs_missilecount=cs_missilecount+1
set cs_missiles[cs_missilecount]=k
if (not cs_movementtimer_active) then
call TimerStart(cs_movementtimer,cs_TIMER_CYCLE,true,function CasterSystemMovementTimer)
set cs_movementtimer_active=true
endif
set k.mtype=cs_TYPE_PROJECTILE
if (target!=null) then
set k.tounit=true
set k.target=target
set k.x2=GetUnitX(target)
set k.y2=GetUnitY(target)
set k.z2o=z2
else
set k.tounit=false
set k.x2=x2
set k.y2=y2
endif
set k.z2=z2
set k.speed=speed
set k.acel=arc*8000
if (fx!=null) then
set k.fx=fx
set fx=null
endif
loop
exitwhen k.done
call TriggerSleepAction(0)
endloop
call FlushStoredInteger(cs_cache,"MOVEMENT_TABLES",km)
call k.destroy()
endfunction
function UnitMoveToAsProjectileAnySpeed takes unit m, real speed, real arc, real x2, real y2, unit target, real z2 returns nothing
//Left for compat
call UnitMoveToAsProjectileAnySpeed_Effect(m,null,speed,arc,x2,y2,target,z2)
endfunction
//========================================================================================================================
function UnitMoveToAsProjectileGen takes unit m, real arc, real x2, real y2, unit target, real z2 returns nothing
//
// The internal projectile system used by all the projectile functions
//
local real x1=GetUnitX(m)
local real y1=GetUnitY(m)
local real acel=arc*1600
local real speed=GetUnitMoveSpeed(m)
local real z1=GetUnitFlyHeight(m)
local real d
local real d1
local real d2
local real t
local real vel
local real dif=0
local boolean tounit= (target!=null)
local boolean b=false
local boolean mode=false
if tounit then
set x2=GetUnitX(target)
set y2=GetUnitY(target)
set z2=GetUnitFlyHeight(target)+z2
endif
set mode=(z2>z1)
set d=SquareRoot(Pow(x2-x1,2)+Pow(y2-y1,2))
set d1=1000000
set d2=0
set t=d/speed
if t==0 then
set t=0.001
endif
set vel=(z2-z1+0.5*acel*t*t)/t
call SetUnitFacing( m, Atan2BJ(y2 - y1, x2 - x2) )
call IssuePointOrder( m, "move", x2,y2)
set t=0
loop
set d2=d1
if tounit then
if (GetWidgetLife(target)<=0.405) then
set tounit=false
else
set x2=GetUnitX(target)
set y2=GetUnitY(target)
endif
endif
set d1=SquareRoot(Pow(x2-GetUnitX(m),2)+Pow(y2-GetUnitY(m),2))
exitwhen b or d1==0
set b=(d1<=speed*(t-dif))
exitwhen (mode and b) or (GetUnitCurrentOrder(m) != OrderId("move"))
if tounit then
call IssuePointOrder( m, "move", x2,y2)
endif
set dif=t
if dif==0.001 then
set t=0.1
else
set t= (d-d1)/speed
endif
set t= 2*t-dif
call SetUnitFlyHeight( m, z1+(vel*t-0.5*acel*t*t), RAbsBJ( vel-acel*(t+dif)/2) )
set t=(t+dif)/2
call TriggerSleepAction(0)
endloop
if tounit then
set x2=GetUnitX(target)
set y2=GetUnitY(target)
endif
call SetUnitFlyHeight( m,z2,0)
call SetUnitX(m,x2)
call SetUnitY(m,y2)
endfunction
function UnitMoveToAsProjectile takes unit m, real arc, real x2, real y2, real z2 returns nothing
call UnitMoveToAsProjectileGen(m, arc,x2,y2,null,z2)
endfunction
//============================================================================================================
function ProjectileLaunchEx takes player owner, string modelpath, real scale, integer red, integer green, integer blue, integer alpha, real speed, real arc,real x1, real y1, real z1, real x2, real y2, real z2 returns nothing
local unit m=CreateCaster( Atan2BJ(y2 - y1, x2 - x1),x1,y1 )
local effect fx=null
call SetUnitScale( m, scale, scale, scale)
call SetUnitVertexColor(m, red, green, blue, alpha)
call SetUnitFlyHeight( m, z1, 0)
set fx= AddSpecialEffectTarget( modelpath, m,"origin" )
call SetUnitOwner( m, owner, true)
if (speed<=522) then
call SetUnitMoveSpeed(m, speed)
call UnitMoveToAsProjectile(m, arc, x2, y2, z2)
call DestroyEffect(fx)
else
call UnitMoveToAsProjectileAnySpeed_Effect(m,fx,speed,arc,x2,y2,null,z2)
endif
call ExplodeUnitBJ(m)
set owner=null
set fx=null
set m=null
endfunction
function ProjectileLaunchExLoc takes player owner, string modelpath, real scale, integer red, integer green, integer blue, integer alpha, real speed, real arc, location loc1, real z1, location loc2, real z2 returns nothing
call ProjectileLaunchEx( owner, modelpath, scale, red, green, blue, alpha, speed, arc,GetLocationX(loc1), GetLocationY(loc1), z1, GetLocationX(loc2), GetLocationY(loc2), z2)
endfunction
//============================================================================================================
function ProjectileLaunch takes string modelpath, real speed, real arc,real x1, real y1, real z1, real x2, real y2, real z2 returns nothing
call ProjectileLaunchEx( Player(15), modelpath, 1, 255, 255, 255, 255, speed, arc,x1,y1,z1,x2,y2,z2)
endfunction
function ProjectileLaunchLoc takes string modelpath, real speed, real arc, location loc1, real z1, location loc2, real z2 returns nothing
call ProjectileLaunchExLoc( Player(15), modelpath, 1,255,255,255,255,speed,arc,loc1,z1,loc2,z2)
endfunction
//============================================================================================================
function DamagingProjectileLaunchAOE_Child takes nothing returns nothing
local unit m=udg_currentcaster
local effect fx=bj_lastCreatedEffect
local real x2=udg_castervars[0]
local real y2=udg_castervars[1]
local real aoeradius=udg_castervars[3]
local real damage=udg_castervars[4]
local boolean affectallied=bj_isUnitGroupInRectResult
local integer V=CreateDamageOptions(R2I(udg_castervars[5]))
local unit hurter=udg_currenthurter
local real speed=udg_castervars[6]
if (speed<=522) then
call SetUnitMoveSpeed(m, speed)
call UnitMoveToAsProjectile(m, bj_meleeNearestMineDist, udg_castervars[0], udg_castervars[1], udg_castervars[2])
call DestroyEffect(fx)
else
call UnitMoveToAsProjectileAnySpeed_Effect(m,fx,speed,bj_meleeNearestMineDist, udg_castervars[0], udg_castervars[1],null, udg_castervars[2])
endif
call DamageUnitsInAOEEx(hurter,damage,x2,y2,aoeradius,affectallied,LoadDamageOptions(V))
call DestroyDamageOptions(V)
call ExplodeUnitBJ(m)
set m=null
set fx=null
endfunction
function DamagingProjectileLaunchAOE takes unit hurter, string modelpath, real speed, real arc, real x1, real y1, real z1, real x2, real y2, real z2, real aoeradius, real damage, boolean affectallied, integer DamageOptions returns unit
local unit m=CreateCaster( Atan2BJ(y2 - y1, x2 - x1) ,x1,y1)
call SetUnitFlyHeight( m, z1, 0)
set udg_currentcaster=m
set bj_lastCreatedEffect=AddSpecialEffectTarget( modelpath, m,"origin" )
call SetUnitOwner( m, GetOwningPlayer(hurter), true)
set bj_meleeNearestMineDist = arc
set udg_castervars[0] = x2
set udg_castervars[1] = y2
set udg_castervars[2] = z2
set udg_castervars[3] =aoeradius
set udg_castervars[4] =damage
set udg_castervars[5] =DamageOptions
set udg_castervars[6] =speed
set udg_currenthurter=hurter
set bj_isUnitGroupInRectResult=affectallied
call ExecuteFunc("DamagingProjectileLaunchAOE_Child")
set m=null
return udg_currentcaster
endfunction
function DamagingProjectileLaunchAOELoc takes unit hurter, string modelpath, real speed, real arc, location loc1, real z1, location loc2, real z2, real aoeradius, real damage, boolean affectallied, integer DamageOptions returns unit
return DamagingProjectileLaunchAOE(hurter,modelpath,speed,arc,GetLocationX(loc1),GetLocationY(loc1),z1,GetLocationX(loc2),GetLocationY(loc2),z2, aoeradius, damage, affectallied, DamageOptions )
endfunction
function ProjectileLaunchDamage takes player owner, string modelpath, real speed, real arc, real x1, real y1, real z1, real x2, real y2, real z2, real aoeradius, real damage, boolean affectallied returns unit
local unit m=CreateCaster( Atan2BJ(y2 - y1, x2 - x1) ,x1,y1 )
call SetUnitFlyHeight( m, z1, 0)
set udg_currentcaster=m
set bj_lastCreatedEffect=AddSpecialEffectTarget( modelpath, m,"origin" )
call SetUnitOwner( m, owner, true)
set bj_meleeNearestMineDist = arc
set udg_castervars[0] = x2
set udg_castervars[1] = y2
set udg_castervars[2] = z2
set udg_castervars[3] =aoeradius
set udg_castervars[4] =damage
set udg_castervars[5] =0
set udg_castervars[6]= speed
set bj_isUnitGroupInRectResult=affectallied
set udg_currenthurter=m
call ExecuteFunc("DamagingProjectileLaunchAOE_Child")
set m=null
return udg_currentcaster
endfunction
function ProjectileLaunchDamageLoc takes player owner, string modelpath, real speed, real arc, location loc1, real z1, location loc2, real z2, real aoeradius, real damage, boolean affectallied returns unit
return ProjectileLaunchDamage( owner, modelpath, speed, arc, GetLocationX(loc1), GetLocationY(loc1), z1, GetLocationX(loc2), GetLocationY(loc2), z2, aoeradius, damage, affectallied)
endfunction
//============================================================================================================
function ProjectileLaunchKill_Child takes nothing returns nothing
local unit m=udg_currentcaster
local effect fx=bj_lastCreatedEffect
local real x2=udg_castervars[0]
local real y2=udg_castervars[1]
local real speed=udg_castervars[3]
if (speed<=522) then
call SetUnitMoveSpeed( m, speed)
call UnitMoveToAsProjectile(m, bj_meleeNearestMineDist, udg_castervars[0], udg_castervars[1], udg_castervars[2])
call DestroyEffect( fx)
else
call UnitMoveToAsProjectileAnySpeed_Effect(m,fx,speed, bj_meleeNearestMineDist, udg_castervars[0], udg_castervars[1], null, udg_castervars[2])
endif
call ExplodeUnitBJ(m)
set m=null
set fx=null
endfunction
function ProjectileLaunchKill takes player owner, string modelpath, real speed, real arc, real x1, real y1, real z1, real x2, real y2, real z2 returns unit
local unit m=CreateCaster( Atan2BJ(y2 - y1, x2 - x1),x1,y1 )
call SetUnitFlyHeight( m, z1, 0)
set udg_currentcaster=m
set bj_lastCreatedEffect=AddSpecialEffectTarget( modelpath, m,"origin" )
call SetUnitOwner( m, owner, true)
set bj_meleeNearestMineDist = arc
set udg_castervars[0] = x2
set udg_castervars[1] = y2
set udg_castervars[2] = z2
set udg_castervars[3] = speed
call ExecuteFunc("ProjectileLaunchKill_Child")
set m=null
return udg_currentcaster
endfunction
function ProjectileLaunchKillLoc takes player owner, string modelpath, real speed, real arc, location loc1, real z1, location loc2, real z2 returns unit
return ProjectileLaunchKill( owner, modelpath, speed, arc, GetLocationX(loc1), GetLocationY(loc1), z1, GetLocationX(loc2), GetLocationY(loc2), z2)
endfunction
//====================================================================================================================================================================
function UnitMoveToUnitAsProjectile takes unit m, real arc, unit target, real zoffset returns nothing
call UnitMoveToAsProjectileGen(m, arc,0,0,target,zoffset)
endfunction
//====================================================================================================================================================================
function ProjectileLaunchToUnitEx takes player owner, string modelpath, real scale, integer red, integer green, integer blue, integer alpha, real speed, real arc, real x1, real y1, real z1, unit target, real zoffset returns nothing
local unit m=CreateCaster( Atan2BJ(GetUnitY(target) - y1, GetUnitX(target) - x1),x1,y1 )
local effect fx=null
call SetUnitFlyHeight( m, z1, 0)
call SetUnitScale( m, scale, scale, scale)
call SetUnitVertexColor(m, red, green, blue, alpha)
set fx=AddSpecialEffectTarget( modelpath, m,"origin" )
call SetUnitOwner( m , owner, true)
if (speed<=522) then
call SetUnitMoveSpeed( m, speed)
call UnitMoveToUnitAsProjectile(m,arc,target, zoffset)
call DestroyEffect(fx)
else
call UnitMoveToAsProjectileAnySpeed_Effect(m,fx,speed, arc,0,0,target,zoffset)
endif
call ExplodeUnitBJ(m)
set m=null
set fx=null
endfunction
function ProjectileLaunchToUnitExLoc takes player owner, string modelpath, real scale, integer red, integer green, integer blue, integer alpha, real speed, real arc, location loc1, real z1, unit target, real zoffset returns nothing
call ProjectileLaunchToUnitEx( owner, modelpath, scale, red, green, blue, alpha, speed, arc, GetLocationX(loc1),GetLocationY(loc1), z1, target, zoffset)
endfunction
function ProjectileLaunchToUnit takes string modelpath, real speed, real arc,real x1, real y1, real z1, unit target, real zoffset returns nothing
call ProjectileLaunchToUnitEx( Player(15), modelpath, 1, 255,255,255,255,speed,arc,x1,y1,z1,target,zoffset)
endfunction
function ProjectileLaunchToUnitLoc takes string modelpath, real speed, real arc, location loc1, real z1, unit target, real zoffset returns nothing
call ProjectileLaunchToUnitExLoc( Player(15), modelpath, 1, 255,255,255,255, speed, arc, loc1, z1, target,zoffset)
endfunction
//====================================================================================================================================================================
function DamagingProjectileLaunchTarget_Child takes nothing returns nothing
local unit m=udg_currentcaster
local unit target=bj_meleeNearestMine
local effect fx=bj_lastCreatedEffect
local real damage=udg_castervars[4]
local damagetype dmgT=ConvertDamageType(R2I(udg_castervars[6]))
local attacktype attT=ConvertAttackType(R2I(udg_castervars[5]))
local unit hurter=udg_currenthurter
local real speed=udg_castervars[7]
if (speed<=522) then
call SetUnitMoveSpeed( m, speed)
call UnitMoveToUnitAsProjectile(m, bj_meleeNearestMineDist, target, udg_castervars[2])
call DestroyEffect( fx)
else
call UnitMoveToAsProjectileAnySpeed_Effect(m,fx,speed, bj_meleeNearestMineDist,0,0,target,udg_castervars[2])
endif
call DamageUnitByTypes(hurter,target,damage,attT,dmgT)
call ExplodeUnitBJ(m)
set m=null
set hurter=null
set target=null
set fx=null
set dmgT=null
set attT=null
endfunction
function DamagingProjectileLaunchTarget takes unit hurter, string modelpath, real speed, real arc, real x1, real y1, real z1, unit target, real zoffset, real damage, attacktype attT, damagetype dmgT returns unit
local unit m=CreateCaster( Atan2BJ(GetUnitY(target) - y1, GetUnitX(target) - x1),x1,y1 )
set udg_castervars[7]=speed
call SetUnitFlyHeight( m, z1, 0)
set udg_currentcaster=m
set bj_lastCreatedEffect=AddSpecialEffectTarget( modelpath, m,"origin" )
call SetUnitOwner( m, GetOwningPlayer(hurter), true)
set bj_meleeNearestMineDist = arc
set udg_castervars[2]= zoffset
set bj_meleeNearestMine=target
set udg_castervars[4]=damage
set udg_castervars[5]=CS_H2I(attT)
set udg_castervars[6]=CS_H2I(dmgT)
set udg_currenthurter=hurter
call ExecuteFunc("DamagingProjectileLaunchTarget_Child")
set m=null
return udg_currentcaster
endfunction
function DamagingProjectileLaunchTargetLoc takes unit hurter, string modelpath, real speed, real arc, location loc, real z1, unit target, real zoffset, real damage, attacktype attT, damagetype dmgT returns unit
return DamagingProjectileLaunchTarget(hurter,modelpath,speed,arc,GetLocationX(loc),GetLocationY(loc), z1, target, zoffset, damage, attT, dmgT)
endfunction
function ProjectileLaunchToUnitDamage takes player owner, string modelpath, real speed, real arc, real x1, real y1, real z1, unit target, real zoffset, real damage returns unit
local unit m=CreateCaster( Atan2BJ(GetUnitY(target) - y1, GetUnitX(target) - x1),x1,y1 )
set udg_castervars[7]=speed
call SetUnitFlyHeight( m, z1, 0)
set udg_currentcaster=m
set bj_lastCreatedEffect=AddSpecialEffectTarget( modelpath, m,"origin" )
call SetUnitOwner( m, owner, true)
set bj_meleeNearestMineDist = arc
set udg_castervars[2]= zoffset
set bj_meleeNearestMine=target
set udg_castervars[4]=damage
set udg_castervars[5]=CS_H2I(cs_DefaultAttackType)
set udg_castervars[6]=CS_H2I(cs_DefaultDamageType)
set udg_currenthurter=m
call ExecuteFunc("DamagingProjectileLaunchTarget_Child")
set m=null
return udg_currentcaster
endfunction
function ProjectileLaunchToUnitDamageLoc takes player owner, string modelpath, real speed, real arc, location loc1, real z1, unit target, real zoffset, real damage returns unit
return ProjectileLaunchToUnitDamage( owner, modelpath, speed, arc,GetLocationX(loc1),GetLocationY(loc1),z1,target,zoffset,damage)
endfunction
//==============================================================================================================================================================================
// Caster System Class: CollisionMissile
//
function CollisionMissile_Destroy takes unit m returns nothing
local cscollisionmissile k=GetUnitUserData(m)
if (k.fx!=null) then
call DestroyEffect(k.fx)
set k.fx=null
endif
//For convenience, always wait for next tic to destroy the thing. But destroy the effect so
//players don't ever notice it.
set k.state=2
call DisableTrigger(k.t)
endfunction
function CollisionMissile_Create takes string MissileModelPath, real x, real y, real dirangle, real speed, real AngleSpeed, real MaxDist, real height, boolean UseNewCaster, real Collision, code OnImpact returns unit
local timer t
local cscollisionmissile k
local trigger R
local unit m
set k=cscollisionmissile.create()
if UseNewCaster then
set m=CreateCaster(dirangle,x,y)
set k.new=true
else
set m=GetACaster()
call SetUnitFacing(m,dirangle)
call SetUnitPosition(m,CS_SafeX(x),CS_SafeY(y))
set k.new=false
endif
set cs_missilecount=cs_missilecount+1
set cs_missiles[cs_missilecount]=k
if (not cs_movementtimer_active) then
call TimerStart(cs_movementtimer,cs_TIMER_CYCLE,true,function CasterSystemMovementTimer)
set cs_movementtimer_active=true
endif
call SetUnitUserData(m,k)
set k.mtype=cs_TYPE_COLLISIONMISSILE
set k.speed=speed
set k.aspeed=AngleSpeed
set k.f=dirangle
set k.maxd=MaxDist
call SetUnitFlyHeight(m,height,0)
set R=CreateTrigger()
set k.collision=Collision
set k.state=1
set k.t=R
call SetCSData(R,k)
set k.fx = AddSpecialEffectTarget(MissileModelPath,m,"origin")
set k.m=m
set k.ac=TriggerAddAction(R,OnImpact)
set R=null
set udg_currentcaster=m
set m=null
return udg_currentcaster
endfunction
function CollisionMissile_CreateLoc takes string MissileModelPath, location loc, real dirangle, real speed, real AngleSpeed, real MaxDist, real height, boolean UseNewCaster, real Collision, code OnImpact returns unit
return CollisionMissile_Create(MissileModelPath,GetLocationX(loc),GetLocationY(loc),dirangle,speed,AngleSpeed,MaxDist,height,UseNewCaster,Collision,OnImpact)
endfunction
//! CollisionMissile_CreateLoc(MissileModelPath,loc,dirangle,speed,AngleSpeed,MaxDist,height,UseNewCaster,Collision,OnImpact) CollisionMissile_Create(MissileModelPath,GetLocationX(loc),GetLocationY(loc),dirangle,speed,AngleSpeed,MaxDist,height,UseNewCaster,Collision,OnImpact)
//=========================================================================================================================================================
function CollisionMissile_SetAngleSpeed takes unit m, real newAspeed returns nothing
local cscollisionmissile k=GetUnitUserData(m)
set k.aspeed=newAspeed
endfunction
//=========================================================================================================================================================
function CollisionMissile_SetSpeed takes unit m, real newspeed returns nothing
local cscollisionmissile k=GetUnitUserData(m)
set k.speed=newspeed
endfunction
//=========================================================================================================================================================
function CollisionMissile_SetTargetPoint takes unit m, real tx, real ty returns nothing
local cscollisionmissile k= GetUnitUserData(m)
set k.tx=tx
set k.ty=ty
set k.ttype=cscollisionmissile.ttype_point
endfunction
function CollisionMissile_SetTargetPointLoc takes unit m, location tloc returns nothing
call CollisionMissile_SetTargetPoint(m,GetLocationX(tloc),GetLocationY(tloc))
endfunction
//=========================================================================================================================================================
function CollisionMissile_SetTarget takes unit m, widget Target returns nothing
local cscollisionmissile k= GetUnitUserData(m)
set k.tw=Target
set k.ttype=cscollisionmissile.ttype_widget
endfunction
//=========================================================================================================================================================
function CollisionMissile_ForgetTarget takes unit m returns nothing
set cscollisionmissile(GetUnitUserData(m)).ttype=cscollisionmissile.ttype_none
endfunction
//=========================================================================================================================================================
function CollisionMissile_SetDirAngle takes unit m, real f returns nothing
set cscollisionmissile(GetUnitUserData(m)).f=f
call SetUnitFacing(m,f)
endfunction
//=========================================================================================================================================================
function CollisionMissile_ResetMaxDist takes unit m, real maxdist returns nothing
set cscollisionmissile(GetUnitUserData(m)).maxd=maxdist
endfunction
//=========================================================================================================================================================
function CollisionMissile_PeriodicFX takes unit m, string fx, real dur returns nothing
// [1 integer state][2 trigger T][3 real collision][4 real speed][5 real F][6 real aspeed][7 integer TType]
// [8 real Tx][9 real Ty][10 widget Tw][11 real F][12 real maxd][13 boolean pfx][14 real pfx_current][15 real pfx_dur][16 pfx_path]
// [17 fx][18 ac][19 boolean new][20 m]
local cscollisionmissile k= cscollisionmissile(GetUnitUserData(m))
set k.pfx=true
set k.pfx_path=fx
set k.pfx_dur=dur
set k.pfx_current=0.
endfunction
//=========================================================================================================================================================
function CollisionMissile_SetTag takes unit m, integer tag returns nothing
set cscollisionmissile(GetUnitUserData(m)).tag=tag
endfunction
//=========================================================================================================================================================
function CollisionMissile_GetTag takes unit m returns integer
return cscollisionmissile(GetUnitUserData(m)).tag
endfunction
//**************************************************************************************************
//*
//* Caster System Special Events:
//*
//*
//**************************************************************************************************
globals
private boolean eventhandlers_passives=false
private trigger events_probablytemp_learntrig=null
private triggeraction events_probablytemp_learntriga=null
endglobals
//==================================================================================================
// Event: OnAbilityLearn
//
function Event_OnLearn1 takes nothing returns nothing
local integer s=GetLearnedSkill()
local string k=I2S(s)
if HaveStoredString( cs_cache, "events_onlearn",k) then
call ExecuteFunc( GetStoredString( cs_cache, "events_onlearn",k) )
endif
endfunction
function Event_OnLearn2 takes nothing returns nothing
local integer s=GetLearnedSkill()
local string k=I2S(s)
if HaveStoredString( cs_cache, "events_onlearn",k) then
call StoreInteger(cs_cache,"events_variables","unit",CS_H2I(GetTriggerUnit()))
call StoreInteger(cs_cache,"events_variables","current",s)
call ExecuteFunc( GetStoredString( cs_cache, "events_onlearn",k) )
endif
endfunction
function InitLearnEvent takes gamecache g, integer i returns nothing
local trigger t=CreateTrigger()
local integer j=0
loop
call TriggerRegisterPlayerUnitEvent(t, Player(j),EVENT_PLAYER_HERO_SKILL, null)
set j=j+1
exitwhen j==bj_MAX_PLAYER_SLOTS
endloop
if (i==1) then
set events_probablytemp_learntrig=t
set events_probablytemp_learntriga=TriggerAddAction(t, function Event_OnLearn1)
else
call TriggerAddAction(t, function Event_OnLearn2)
endif
call StoreInteger(g,"eventhandlers","learn",i)
set t=null
endfunction
function OnAbilityLearn takes integer abilid, string funcname returns nothing
if (not HaveStoredInteger(cs_cache,"eventhandlers","learn")) then
call InitLearnEvent(cs_cache,1)
endif
call StoreString(cs_cache,"events_onlearn", I2S(abilid), funcname)
endfunction
//==================================================================================================
// Event: OnAbilityGet
//
function GetAbilityAcquiringUnit takes nothing returns unit
return GetStoredInteger(cs_cache,"events_variables","unit")
return null
endfunction
function GetAcquiredAbilityId takes nothing returns integer
return GetStoredInteger(cs_cache,"events_variables","current")
endfunction
function UnitAddAbility_ConsiderEvent takes unit whichUnit, integer abilid, integer level returns nothing
local string k=I2S(abilid)
call UnitAddAbility(whichUnit,abilid)
call SetUnitAbilityLevel(whichUnit,abilid,level)
if (HaveStoredString(cs_cache,"events_onlearn",k)) then
call StoreInteger(cs_cache,"events_variables","units",CS_H2I(whichUnit))
call StoreInteger(cs_cache,"events_variables","current",abilid)
call ExecuteFunc(GetStoredString(cs_cache,"events_onlearn",k))
endif
endfunction
function Event_OnPassive_Browse takes unit u, string k returns nothing
local integer n=GetStoredInteger(cs_cache,"events_passives","n")
local integer un=0
local integer i=1
local integer s
loop
exitwhen (i>n)
set s=GetStoredInteger(cs_cache,"events_passives",I2S(i))
if (GetUnitAbilityLevel(u,s)>0) then
if (un==0) then
set un=1
call StoreInteger(cs_cache,"events_variables","unit",CS_H2I(u))
else
set un=un+1
endif
call StoreInteger(cs_cache,"events_unit_passive"+I2S(un),k,s)
call StoreInteger(cs_cache,"events_variables","current",s)
call ExecuteFunc(GetStoredString(cs_cache,"events_onlearn",I2S(s)))
endif
set i=i+1
endloop
if (un==0) then
set un=-1
endif
call StoreInteger(cs_cache,"events_unit_passives",k,un)
endfunction
function Event_OnPassive_Do takes unit u, string k, integer n returns nothing
local integer i=1
local integer s
call StoreInteger(cs_cache,"events_variables","unit",CS_H2I(u))
loop
exitwhen (i>n)
set s=GetStoredInteger(cs_cache,"events_unit_passive"+I2S(i),k)
if (GetUnitAbilityLevel(u,s)>0) then
call StoreInteger(cs_cache,"events_variables","current",s)
call ExecuteFunc(GetStoredString(cs_cache,"events_onlearn",I2S(s)))
endif
set i=i+1
endloop
endfunction
function Event_OnPassive_EnterRect takes nothing returns nothing
local unit u=GetTriggerUnit()
local string k=I2S(GetUnitTypeId(u))
local integer n=GetStoredInteger(cs_cache,"events_unit_passives",k)
if (n>0) then
call Event_OnPassive_Do(u,k,n)
elseif (n==0) then
call Event_OnPassive_Browse(u,k)
endif
set u=null
endfunction
function Event_OnPassive_NoCasters takes nothing returns boolean
return (GetUnitTypeId(GetTriggerUnit())!=cs_CasterUnitId)
endfunction
function Event_OnPassive_InitEnum takes nothing returns nothing
local trigger t
local integer n=GetStoredInteger(cs_cache,"events_passives","n")
local integer i=1
local integer array p
local string array s
local unit u
local group a=NewGroup()
local boolean saved
call ReleaseTimer(GetExpiredTimer())
loop
exitwhen (i>n)
set p[i]=GetStoredInteger(cs_cache,"events_passives",I2S(i))
set s[i]=GetStoredString(cs_cache,"events_onlearn", I2S(p[i]))
set i=i+1
endloop
call GroupEnumUnitsInRect(a,bj_mapInitialPlayableArea,null)
loop
set u=FirstOfGroup(a)
exitwhen (u==null)
set i=1
set saved=false
loop
exitwhen (i>n)
if (GetUnitAbilityLevel(u,p[i])>0) then
if (not saved) then
set saved=true
call StoreInteger(cs_cache,"events_variables","unit",CS_H2I(u))
endif
call StoreInteger(cs_cache,"events_variables","current",p[i])
call ExecuteFunc(s[i])
endif
set i=i+1
endloop
call GroupRemoveUnit(a,u)
endloop
set t=CreateTrigger()
call TriggerRegisterEnterRectSimple(t,bj_mapInitialPlayableArea)
call TriggerAddAction(t,function Event_OnPassive_EnterRect)
call TriggerAddCondition(t,Condition(function Event_OnPassive_NoCasters))
call ReleaseGroup(a)
set t=null
set a=null
endfunction
function InitPassiveEvent takes nothing returns nothing
local trigger t
call TimerStart(NewTimer(),0,false,function Event_OnPassive_InitEnum)
set eventhandlers_passives=true
if (not HaveStoredInteger(cs_cache,"eventhandlers","learn")) then
call InitLearnEvent(cs_cache,2)
else
set t=events_probablytemp_learntrig
call TriggerRemoveAction(t,events_probablytemp_learntriga)
call TriggerAddAction(t, function Event_OnLearn2)
call StoreInteger(cs_cache,"eventhandlers","learn",2)
set t=null
endif
endfunction
function OnAbilityGet takes integer abilid, string funcname returns nothing
local integer n=GetStoredInteger(cs_cache,"events_passives","n")+1
if (not eventhandlers_passives) then
call InitPassiveEvent()
endif
call StoreString( cs_cache,"events_onlearn", I2S(abilid), funcname)
call StoreInteger(cs_cache,"events_passives","n",n)
call StoreInteger(cs_cache,"events_passives",I2S(n),abilid)
endfunction
//==================================================================================================
// Event: OnAbilityEffect
//
function Event_OnEffect takes nothing returns nothing
local string k=I2S(GetSpellAbilityId())
if HaveStoredString(cs_cache, "events_oneffect",k) then
call ExecuteFunc( GetStoredString(cs_cache, "events_oneffect",k))
endif
endfunction
function InitEffectEvent takes gamecache g returns nothing
local trigger t=CreateTrigger()
local integer i = 0
loop
call TriggerRegisterPlayerUnitEvent(t, Player(i),EVENT_PLAYER_UNIT_SPELL_EFFECT, null)
set i=i+1
exitwhen i==bj_MAX_PLAYER_SLOTS
endloop
call TriggerAddAction(t,function Event_OnEffect)
call StoreInteger(g,"eventhandlers","effect",1)
set t=null
endfunction
function OnAbilityEffect takes integer abilid, string funcname returns nothing
if (not HaveStoredInteger(cs_cache,"eventhandlers","effect")) then
call InitEffectEvent(cs_cache)
endif
call StoreString(cs_cache,"events_oneffect",I2S(abilid),funcname)
endfunction
//==================================================================================================
// Event: OnAbilityCast
//
function Event_OnCast takes nothing returns nothing
local string k=I2S(GetSpellAbilityId())
if HaveStoredString(cs_cache, "events_oncast",k) then
call ExecuteFunc( GetStoredString(cs_cache, "events_oncast",k))
endif
endfunction
function InitCastEvent takes nothing returns nothing
local trigger t=CreateTrigger()
local integer i = 0
loop
call TriggerRegisterPlayerUnitEvent(t, Player(i),EVENT_PLAYER_UNIT_SPELL_CAST, null)
set i=i+1
exitwhen i==bj_MAX_PLAYER_SLOTS
endloop
call TriggerAddAction(t,function Event_OnCast)
call StoreInteger(cs_cache,"eventhandlers","cast",1)
set t=null
endfunction
function OnAbilityPreCast takes integer abilid, string funcname returns nothing
if (not HaveStoredInteger(cs_cache,"eventhandlers","cast")) then
call InitCastEvent()
endif
call StoreString(cs_cache,"events_oncast",I2S(abilid),funcname)
endfunction
//==================================================================================================
// Event: OnAbilityEndCast
//
function Event_OnEndCast takes nothing returns nothing
local string k=I2S(GetSpellAbilityId())
if HaveStoredString(cs_cache, "events_onendcast",k) then
call ExecuteFunc( GetStoredString(cs_cache, "events_onendcast",k))
endif
endfunction
function InitEndCastEvent takes nothing returns nothing
local trigger t=CreateTrigger()
local integer i = 0
loop
call TriggerRegisterPlayerUnitEvent(t, Player(i),EVENT_PLAYER_UNIT_SPELL_ENDCAST, null)
set i=i+1
exitwhen i==bj_MAX_PLAYER_SLOTS
endloop
call TriggerAddAction(t,function Event_OnEndCast)
call StoreInteger(cs_cache,"eventhandlers","endcast",1)
set t=null
endfunction
function OnAbilityEndCast takes integer abilid, string funcname returns nothing
if (not HaveStoredInteger(cs_cache,"eventhandlers","endcast")) then
call InitEndCastEvent()
endif
call StoreString(cs_cache,"events_onendcast",I2S(abilid),funcname)
endfunction
//==================================================================================================
// Spell Helpers
//
function IsPointWater takes real x, real y returns boolean
return IsTerrainPathable(x,y,PATHING_TYPE_WALKABILITY) and not(IsTerrainPathable(x,y,PATHING_TYPE_AMPHIBIOUSPATHING))
endfunction
function IsPointWaterLoc takes location loc returns boolean
return IsPointWater(GetLocationX(loc),GetLocationY(loc))
endfunction
//==================================================================================================
function IsUnitSpellImmune takes unit u returns boolean
return IsUnitType(u,UNIT_TYPE_MAGIC_IMMUNE)
endfunction
function IsUnitImmuneToPhisical takes unit u returns boolean
return (GetDamageFactor(u,ATTACK_TYPE_CHAOS,DAMAGE_TYPE_DEMOLITION)==0)
endfunction
function IsUnitInvulnerable takes unit u returns boolean
return (GetDamageFactor(u,ATTACK_TYPE_CHAOS,DAMAGE_TYPE_UNIVERSAL)==0)
endfunction
//## Utility functions ##
//====================================================================================================
// Mimic an interface error message
// ForPlayer : The player to show the error
// msg : The error
//
function CS_Error takes player ForPlayer, string msg returns nothing
local sound error=CreateSoundFromLabel( "InterfaceError",false,false,false,10,10)
if (GetLocalPlayer() == ForPlayer) then
if (msg!="") and (msg!=null) then
call ClearTextMessages()
call DisplayTimedTextToPlayer( ForPlayer, 0.52, -1.00, 2.00, "|cffffcc00"+msg+"|r" )
endif
call StartSound( error )
endif
call KillSoundWhenDone( error)
set error=null
endfunction
//## Safe item hiding system ##
//===============================
// Hides an item, it keeps it safe while leaving other units unable to use it
// (unless an (attached) variable / table field points to it and user does
// something with it) - THIS IS HIGHLY DISCOURAGED
//
// * Don't use it with tomes that increase hero stats
// * Make triggers with item acquire/lose events ignore when a
// Caster is triggering unit.
//
globals
private group CSItemStorage_gr=null
endglobals
//If your map got more than 8191 items, you are crazy.
private struct itemholder
unit s
item it
endstruct
function CS_HideItem takes item i returns integer
local unit s
local group gr=CSItemStorage_gr
local itemholder r
if (gr==null) then
set gr=NewGroup()
set CSItemStorage_gr=gr
endif
set s=FirstOfGroup(gr)
if (s==null) then
set s=CreateCaster(0,0,0)
call UnitAddAbility(s,cs_DummyInventory_Id)
if (UnitInventorySize(s)<bj_MAX_INVENTORY) then
call BJDebugMsg("CASTER SYSTEM ERROR: Please use a correct ability for cs_DummyInventory_Id ")
loop //crash the thread
endloop
endif
call GroupAddUnit(gr,s)
endif
call UnitAddItem(s,i)
set r=itemholder.create()
set r.it=i
set r.s=s
if(UnitInventorySize(s)==UnitInventoryCount(s)) then
call GroupRemoveUnit(gr,s)
endif
set s=null
set gr=null
return integer(r)
endfunction
//===============================
// Restores an item
//
globals
private item returnitem
endglobals
function CS_RestoreItem takes integer hiddenindex, real x, real y returns item
local group gr=CSItemStorage_gr
local itemholder r=itemholder(hiddenindex)
local unit s=r.s
local item i=r.it
local integer c
call r.destroy()
call UnitRemoveItem(s,i)
set c=UnitInventoryCount(s)
if (c==0) then
call UnitRemoveAbility(s,cs_DummyInventory_Id)
call GroupRemoveUnit(gr,s)
call ExplodeUnitBJ(s)
else
call GroupAddUnit(gr,s)
endif
call SetItemPosition(i,x,y)
set returnitem=i
set i=null
set s=null
return returnitem
endfunction
function CS_RestoreItemLoc takes integer hiddenindex, location loc returns item
return CS_RestoreItem(hiddenindex,GetLocationX(loc),GetLocationY(loc))
endfunction
//=============================================================================================================
// More convenient than CS_HideItem not compatible with CS_HideItem / CS_RestoreItem(loc)
//
function CS_SetItemVisible takes item it, boolean flag returns nothing
local itemholder r
local unit s
local string k=I2S(CS_H2I(it))
local group gr
local integer x
if (flag) then
if (HaveStoredInteger(cs_cache,"CSItemStorage",k )) then
//DO SHOW:
set r=itemholder(GetStoredInteger(cs_cache,"CSItemStorage",k))
set s=r.s
set gr=CSItemStorage_gr
if (gr==null) then
set gr=NewGroup()
set CSItemStorage_gr=gr
endif
call FlushStoredInteger(cs_cache,"CSItemStorage",k)
call UnitRemoveItem(s,it)
call SetItemVisible(it,true) //Just in case
set x=GetUnitUserData(s)-1
if (x==0) then
call UnitRemoveAbility(s,cs_DummyInventory_Id)
call GroupRemoveUnit(gr,s)
call ExplodeUnitBJ(s)
else
call SetUnitUserData(s,x)
call GroupAddUnit(gr,s)
endif
call r.destroy()
set s=null
set gr=null
else //What? Trying to show an item that was not hidden using this method?
endif
elseif (HaveStoredInteger(cs_cache,"CSItemStorage",k )) then
//What? Trying to hide an item that was already hidden by this method?
else //DO HIDE:
set gr=CSItemStorage_gr
if (gr==null) then
set gr=NewGroup()
set CSItemStorage_gr=gr
endif
set s=FirstOfGroup(gr)
if (s==null) then
set s=CreateCaster(0,0,0)
call UnitAddAbility(s,cs_DummyInventory_Id)
if (UnitInventorySize(s)<bj_MAX_INVENTORY) then
call BJDebugMsg("CASTER SYSTEM ERROR: Please use a correct ability for cs_DummyInventory_Id ")
set x=x
endif
call SetUnitUserData(s,0) //UserData will now hold total items
call GroupAddUnit(gr,s)
endif
call UnitAddItem(s,it)
set r=itemholder.create()
set r.s=s
set r.it=it
call StoreInteger(cs_cache,"CSItemStorage",k,integer(r))
set x=GetUnitUserData(s)+1
call SetUnitUserData(s,x)
if (x==bj_MAX_INVENTORY) then
call GroupRemoveUnit(gr,s)
endif
set s=null
set gr=null
endif
endfunction
function CS_IsItemVisible takes item it returns boolean
return not HaveStoredInteger(cs_cache,"CSItemStorage",I2S(CS_H2I(it)) )
endfunction
//=============================================================================================================
// Obsolette functions: (Left for compatibility)
//
constant function WaterDetectorId takes nothing returns integer
return 'Asb2'
endfunction
function SpellEffectModelPath takes integer abilityid, effecttype t returns string
return GetAbilityEffectById(abilityid,t, 0)
endfunction
constant function Caster_DefaultAttackType takes nothing returns attacktype
return cs_DefaultAttackType
endfunction
constant function Caster_DefaultDamageType takes nothing returns damagetype
return cs_DefaultDamageType
endfunction
constant function Caster_UnitId takes nothing returns integer
return cs_CasterUnitId
endfunction
constant function DamageTreeDetectorId takes nothing returns integer
return cs_DamageTreeDetectorId
endfunction
constant function ChangeableFlyingHeightAllowerId takes nothing returns integer
return cs_FlyingHeightHack
endfunction
constant function CS_MaxCollisionSize takes nothing returns real
return cs_MaxUnitCollisionSize
endfunction
constant function CS_Cycle takes nothing returns real
return cs_TIMER_CYCLE
endfunction
constant function CS_InventoryId takes nothing returns integer
return cs_DummyInventory_Id
endfunction
constant function CS_RectLimitOffSet takes nothing returns real
return cs_RectLimitOffSet
endfunction
function CS_IsUnitVisible takes unit u, player p returns boolean
return IsUnitVisible(u,p)
endfunction
endlibrary
library CSDamagers initializer init requires CasterSystem, CSData, CSSafety
//*************************************************************************************
//* CSDamagers 15.2
//* ---------------
//* Rebuilt from scratch since 15.1, now they use CSData instead of CSCache
//*
//* A DamagerGroup is a bunch of 'damagers', only that it does not matter how many
//* 'damagers' are in range of the target unit, it is only affected by the damage once.
//*
//* A damager is a single special effect that every once in a while causes damage to
//* nearby units, these things are MUI, dynamic and can have expiration times.
//*
//*
//**************************************************************************************
//======================================================================================
globals
private constant real EPSILON=0.000001
endglobals
private keyword damagerdata
private struct data
boolean expired=false
boolean end=false
unit hurter
real damage
real area
integer DamageOptions
boolean trees=false
boolean selfdestruct=false
damagerdata damagers=0
timer thetimer
timer lifespan=null
endstruct
globals
private group damagerflag
endglobals
private struct damagerdata
unit u
effect fx
timer lifespan
integer abil=0
data parent
damagerdata prev=0
damagerdata next=0
method onDestroy takes nothing returns nothing
call GroupRemoveUnit(damagerflag, this.u)
if (.prev==this) then
set .parent.damagers=0
else
if(.parent.damagers==this) then
set .parent.damagers=.prev
endif
set .prev.next=.next
set .next.prev=.prev
endif
call ReleaseTimer(.lifespan)
if(this.abil!=0) then
call UnitRemoveAbility(this.u, this.abil)
endif
call RecicleCasterAfterCastEx(this.u,4,0,false)
call DestroyEffect(.fx)
endmethod
endstruct
//============================================================================================================
globals
private group enumgroup
private group loggroup
private boolexpr procfilter
private real radius
private real centerx
private real centery
private unit array inrange
private integer an
endglobals
private function processUnitsInRange takes nothing returns boolean
local unit e=GetFilterUnit()
if( IsUnitInRangeXY(e,centerx,centery,radius) and not IsUnitInGroup(e, loggroup)) then
call GroupAddUnit(loggroup,e)
set inrange[an]=e
set an=an+1
endif
set e=null
return false
endfunction
private function expire takes nothing returns nothing
local data d=data(GetCSData(GetExpiredTimer()) )
local damagerdata p=d.damagers
local damagerdata q
local integer do
local real x
set d.expired=true
set an=0
if(p!=0) then
set q=p
loop
set centerx=GetUnitX(q.u)
set centery=GetUnitY(q.u)
set radius=d.area
if d.trees then
call DamageTreesInCircle(centerx,centery,radius)
endif
call GroupEnumUnitsInRange(enumgroup, centerx,centery, radius+cs_MaxUnitCollisionSize, procfilter)
exitwhen (q.next==p)
set q=q.next
endloop
endif
if(an!=0) then
set do=LoadDamageOptions(d.DamageOptions)
loop
exitwhen (an==0)
set an=an-1
set x=GetDamageFactorByOptions(d.hurter,inrange[an],do)
if (x>=EPSILON) or (x<=-EPSILON) then
call UnitDamageTarget(d.hurter,inrange[an],d.damage*x,true,false,null,null,null)
endif
endloop
endif
call GroupClear(loggroup)
set d.expired=false
if(d.end or (d.selfdestruct and (p==0))) then
call DamagerGroup_Destroy.evaluate(GetExpiredTimer())
endif
endfunction
private function damagerExpires takes nothing returns nothing
call damagerdata(GetCSData(GetExpiredTimer() ) ).destroy()
endfunction
private function endOfDamagerGroup takes nothing returns nothing
local data d=data(GetCSData(GetExpiredTimer() ) )
call DamagerGroup_Destroy.evaluate(d.thetimer)
endfunction
//================================================================================================================================
//
//
function DamagerGroup_Create takes unit hurter, real damage, real damageperiod, real area, integer DamageOptions returns timer
local timer t=NewTimer()
local data d=data.create()
if IsDamageOptionIncluded(DamageOptions, DamageTrees() ) then
set d.trees=true
endif
set d.thetimer=t
set d.hurter=hurter
set d.damage=damage
set d.area=area
call SetCSData(t, integer(d) )
call TimerStart(t,damageperiod,true,function expire)
//since we use CSSafety, no need to null the timer...
return t
endfunction
function DamagerGroup_Destroy takes timer DamagerGroup returns nothing
local data d=data( GetCSData(DamagerGroup) )
if (d.expired) then
set d.end=true
return
endif
loop
exitwhen (d.damagers==0)
call d.damagers.destroy()
endloop
call ReleaseTimer(DamagerGroup)
call DestroyDamageOptions(d.DamageOptions)
if (d.lifespan!=null) then
call ReleaseTimer(d.lifespan)
endif
call d.destroy()
endfunction
//at least it is inlineable...
function DamagerGroup_AutoDestruct takes timer DamagerGroup, boolean auto returns nothing
set data( GetCSData(DamagerGroup) ).selfdestruct=auto
endfunction
function DamagerGroup_SetLifeSpan takes timer DamagerGroup, real LifeSpan returns nothing
local data d=data(GetCSData(DamagerGroup) )
if(d.lifespan==null) then
set d.lifespan=NewTimer()
call SetCSData(d.lifespan, integer(d) )
endif
call TimerStart(d.lifespan, LifeSpan, false, function endOfDamagerGroup)
endfunction
//
function DamagerGroup_Update takes timer DamagerGroup, unit hurter, real damage, real damageperiod, real area, integer DamageOptions returns nothing
local data d=data(GetCSData(DamagerGroup) )
set d.hurter=hurter
set d.damage=damage
call TimerStart(DamagerGroup,damageperiod,true,function expire)
call SetDamageOptions(d.DamageOptions,DamageOptions)
endfunction
function DamagerGroup_AddDamager takes timer DamagerGroup, string modelpath, real x, real y, real LifeSpan returns unit
local unit c=GetACaster()
local timer t=NewTimer()
local damagerdata dt=damagerdata.create()
local data d=data( GetCSData(DamagerGroup) )
set dt.u=c
call SetUnitUserData(c, integer(dt) )
set dt.lifespan=t
set dt.fx=AddSpecialEffectTarget(modelpath,c,"origin")
set dt.parent=d
if(d.damagers==0)then
set d.damagers=dt
set dt.next=dt
set dt.prev=dt
else
//brain hurts.
set dt.next=d.damagers
set dt.prev=d.damagers.prev
set d.damagers.prev.next=dt
set d.damagers.prev=dt
set d.damagers=dt
endif
call SetUnitX(c,x)
call SetUnitY(c,y)
call SetCSData(t, integer(dt) )
if (LifeSpan>=EPSILON) then
call TimerStart(t,LifeSpan,false, function damagerExpires)
endif
call SetUnitOwner(c,GetOwningPlayer(d.hurter),true)
call GroupAddUnit(damagerflag,c)
set c=null
return dt.u
endfunction
function DamagerGroup_AddDamagerLoc takes timer DamagerGroup, string modelpath, location loc, real LifeSpan returns unit
return DamagerGroup_AddDamager(DamagerGroup,modelpath,GetLocationX(loc),GetLocationY(loc),LifeSpan)
endfunction
//*************************************************
//* Damager functions
//*
//*************************************************
function Damager_SetLifeSpan takes unit Damager, real lifespan returns nothing
local damagerdata dt
if (not IsUnitInGroup(Damager,damagerflag)) then
return
endif
set dt=damagerdata( GetUnitUserData(Damager) )
if (lifespan<EPSILON) then
call PauseTimer(dt.lifespan)
else
call TimerStart(dt.lifespan,lifespan,false,function damagerExpires)
endif
endfunction
function Damager_Remove takes unit Damager returns nothing
if (not IsUnitInGroup(Damager,damagerflag)) then
return
endif
call damagerdata( GetUnitUserData(Damager) ).destroy()
endfunction
function Damager_SetAbility takes unit Damager, integer abilid, integer l returns nothing
local damagerdata dt
if (not IsUnitInGroup(Damager,damagerflag)) then
return
endif
set dt=damagerdata( GetUnitUserData(Damager) )
if(dt.abil!=abilid) then
if(dt.abil!=0) then
call UnitRemoveAbility(Damager,dt.abil)
endif
call UnitAddAbility(Damager,abilid)
set dt.abil=abilid
endif
call SetUnitAbilityLevel(Damager,abilid,l)
endfunction
//========================================================================================
private function init takes nothing returns nothing
set loggroup=CreateGroup()
set enumgroup=CreateGroup()
set damagerflag=CreateGroup()
set procfilter=Condition(function processUnitsInRange)
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library CSCache initializer InitCSCache requires CSData, CSSafeCache, CSDamagers
//*************************************************************************************************
//* *
//* CSCache 15.2 http://wc3campaigns.net/vexorian *
//* ¯¯¯¯¯¯¯ *
//* CSCache is evil now, use just CSSafeCache when possible as it now contains all functions *
//* that do not use I2H or similar functions that are considered to cause unstability in the map *
//* *
//* CSCache remains here for compatibility with previous work. *
//* Migration from CSCache to CSSafeCache+structs should be easy *
//* *
//* This library contains: *
//* - Location linked lists kit *
//* - SetArrayObject and GetArrayObject(And similar) *
//* - AttachObject and GetAttachedObject(And similar) *
//* - SetTableObject and GetTableObject(And similar) *
//* - udg_cscache and CSCache() , not unsafe but really lame, deprecated *
//* - CS_KillTrigger and CS_KillTimer (although they were in Caster System and not in CSCache *
//* *
//* CSCache is not required by the caster system, it instead requires CSSafeCache, you should *
//* Only include this library in your map if it is necessary, it is necessary if a spell *
//* or system uses the old unsafe functions *
//* *
//*************************************************************************************************
//=================================================================================================
// CSCache globals:
//
globals
gamecache udg_cscache = null //udg_ for compat!
endglobals
//==================================================================================================
// Bunch of other return bug exploiters
//
function CS_i2r takes integer i returns real
return i
return 0.
endfunction
function CS_h2r takes handle h returns real
return h
return 0.
endfunction
function CS_r2i takes real r returns integer
return r
return 0
endfunction
function CS_lx takes location l returns location
return GetLocationX(l)
return null
endfunction
function CS_ly takes location l returns location
return GetLocationY(l)
return null
endfunction
function CS_i2l takes integer l returns location
return l
return null
endfunction
//==================================================================================================
// location linked list kit
// ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
// Thanks: Pipedream | No thanks: Blizzard (for lame bug)
//
// It is a great irony, but locations are only useful when you don't use them as points but as
// structs thanks to the return bug. They only allow 2 values so they are limited in comparission
// to gamecache and arrays (see bellow) still useful for single linked lists.
//
// These functions are what I call enough to use locations as single list nodes, X holds values and
// Y can just hold a location (or real) . The Get functions use bj globals to fix an odd operator
// bug that can happen after converting pointer to real into pointer to integer.
//
// The Set functions can be replaced by the Location() constructor, the MoveLocation() native and
// the return bug exploiters up there.
//
//===================================================================================================
// Constructors
//
function Location_IntLoc takes integer x , location y returns location
return Location(CS_i2r(x),CS_h2r(y))
endfunction
function Location_ObjectLoc takes handle x , location y returns location
return Location(CS_h2r(x),CS_h2r(y))
endfunction
function Location_LocLoc takes handle x , location y returns location
return Location(CS_h2r(x),CS_h2r(y))
endfunction
function Location_RealLoc takes real x , location y returns location
return Location(x,CS_h2r(y))
endfunction
//====================================================================================================
// Combined assigments
//
function SetLocation_IntLoc takes location loc, integer x , location y returns nothing
call MoveLocation(loc,CS_i2r(x),CS_h2r(y))
endfunction
function SetLocation_ObjectLoc takes location loc, handle x , location y returns nothing
call MoveLocation(loc,CS_h2r(x),CS_h2r(y))
endfunction
function SetLocation_LocLoc takes location loc, handle x , location y returns nothing //Funny name
call MoveLocation(loc,CS_h2r(x),CS_h2r(y))
endfunction
function SetLocation_RealLoc takes location loc, real x , location y returns nothing
call MoveLocation(loc,x,CS_h2r(y))
endfunction
//===================================================================================================
// Lack of SetLocationX / SetLocationY natives is kind of lame
//
function SetLocationX_Object takes location loc, handle val returns nothing
call MoveLocation(loc,CS_h2r(val),GetLocationY(loc))
endfunction
// Notice how define SetLocationX_Object(loc,v) MoveLocation(loc,CS_h2r(v),GetLocationY(loc))
// would have a chance of messing sometimes since it has 2 loc in the result.
function SetLocationX_Loc takes location loc, location val returns nothing //just name candy
call MoveLocation(loc,CS_h2r(val),GetLocationY(loc))
endfunction
function SetLocationX_Real takes location loc, real val returns nothing
call MoveLocation(loc,val,GetLocationY(loc))
endfunction
function SetLocationX_Int takes location loc, integer val returns nothing
call MoveLocation(loc,CS_i2r(val),GetLocationY(loc))
endfunction
function SetLocationY_Loc takes location loc, location val returns nothing
call MoveLocation(loc,GetLocationX(loc),CS_h2r(val))
endfunction
//==================================================================================================
// All right, crazyness that started with Attacheable variables, and had to continue with everything
// from tables, arrays and now this. Sorry I can't help itç
//
// The good thing about these functions is that they are safe, you won't deal with real->(int/handle)
// conversion related bugs thanks to the use of bj variables these functions do
//
function GetLocationX_Loc takes location loc returns location
set bj_enumDestructableCenter=CS_lx(loc)
return bj_enumDestructableCenter
endfunction
function GetLocationY_Loc takes location loc returns location
set bj_enumDestructableCenter=CS_ly(loc)
return bj_enumDestructableCenter
endfunction
function GetLocationX_Int takes location loc returns integer
set bj_forLoopAIndex=CS_r2i(GetLocationX(loc))
return bj_forLoopAIndex
endfunction
function GetLocationX_Unit takes location loc returns unit
set bj_forLoopAIndex=CS_r2i(GetLocationX(loc))
return bj_forLoopAIndex
return null
endfunction
function GetLocationX_Item takes location loc returns item
set bj_forLoopAIndex=CS_r2i(GetLocationX(loc))
return bj_forLoopAIndex
return null
endfunction
function GetLocationX_Effect takes location loc returns effect
set bj_forLoopAIndex=CS_r2i(GetLocationX(loc))
return bj_forLoopAIndex
return null
endfunction
function GetLocationX_Lightning takes location loc returns lightning
set bj_forLoopAIndex=CS_r2i(GetLocationX(loc))
return bj_forLoopAIndex
return null
endfunction
function GetLocationX_Widget takes location loc returns widget
set bj_forLoopAIndex=CS_r2i(GetLocationX(loc))
return bj_forLoopAIndex
return null
endfunction
function GetLocationX_Object takes location loc returns handle
set bj_forLoopAIndex=CS_r2i(GetLocationX(loc))
return bj_forLoopAIndex
return null
endfunction
function GetLocationX_Rect takes location loc returns rect
set bj_forLoopAIndex=CS_r2i(GetLocationX(loc))
return bj_forLoopAIndex
return null
endfunction
function GetLocationX_Region takes location loc returns region
set bj_forLoopAIndex=CS_r2i(GetLocationX(loc))
return bj_forLoopAIndex
return null
endfunction
function GetLocationX_TimerDialog takes location loc returns timerdialog
set bj_forLoopAIndex=CS_r2i(GetLocationX(loc))
return bj_forLoopAIndex
return null
endfunction
function GetLocationX_Destructable takes location loc returns destructable
set bj_forLoopAIndex=CS_r2i(GetLocationX(loc))
return bj_forLoopAIndex
return null
endfunction
function GetLocationX_Trigger takes location loc returns trigger
set bj_forLoopAIndex=CS_r2i(GetLocationX(loc))
return bj_forLoopAIndex
return null
endfunction
function GetLocationX_Timer takes location loc returns timer
set bj_forLoopAIndex=CS_r2i(GetLocationX(loc))
return bj_forLoopAIndex
return null
endfunction
function GetLocationX_Group takes location loc returns group
set bj_forLoopAIndex=CS_r2i(GetLocationX(loc))
return bj_forLoopAIndex
return null
endfunction
function GetLocationX_TriggerAction takes location loc returns triggeraction
set bj_forLoopAIndex=CS_r2i(GetLocationX(loc))
return bj_forLoopAIndex
return null
endfunction
function GetLocationX_Image takes location loc returns image
set bj_forLoopAIndex=CS_r2i(GetLocationX(loc))
return bj_forLoopAIndex
return null
endfunction
function GetLocationX_Ubersplat takes location loc returns ubersplat
set bj_forLoopAIndex=CS_r2i(GetLocationX(loc))
return bj_forLoopAIndex
return null
endfunction
function GetLocationX_Sound takes location loc returns sound
set bj_forLoopAIndex=CS_r2i(GetLocationX(loc))
return bj_forLoopAIndex
return null
endfunction
//
//That should be all we needed to abuse Locations, Love lists.
//
// CSCache initializer :
function InitCSCache takes nothing returns nothing
set udg_cscache = cs_cache
endfunction
//=================================================================================================================
// CS Dynamic Arrays
// ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
// These are dynamic array functions that use the infamous I2H method, also included Real because it should be
// deprecated...
//
//===================================================================================================
// Indexes of real types. Let's say it is 'safe' to use real return bug, but I think it is still a
// bad idea.
//
function SetArrayReal takes integer id, integer index, real val returns nothing
set index=id+index
if (index<8191) then
set cs_array1[index]=CS_r2i(val)
elseif (index<16382) then
set cs_array2[index-8191]=CS_r2i(val)
else
call StoreInteger(cs_cache,I2S(-id),I2S(index),CS_r2i(val))
endif
endfunction
function GetArrayReal takes integer id, integer index returns real
set index=id+index
if (index<8191) then
return cs_array1[index]
elseif (index<16382) then
return cs_array2[index-8191]
endif
return GetStoredInteger(cs_cache,I2S(-id),I2S(index))
return 0.0
endfunction
//===================================================================================================
// <Indexes of handle types>
//
function SetArrayObject takes integer id, integer index, handle val returns nothing
debug call BJDebugMsg("Warning: SetArrayObject function in use (debug mode)")
set index=id+index
if (index<8191) then
set cs_array1[index]=CS_H2I(val)
elseif (index<16382) then
set cs_array2[index-8191]=CS_H2I(val)
else
call StoreInteger(cs_cache,I2S(-id),I2S(index),CS_H2I(val))
endif
endfunction
//! textmacro CS_GetArrayObject takes name, type
function GetArray$name$ takes integer id, integer index returns $type$
debug call BJDebugMsg("Warning: GetArray$name$ in use (debug mode)")
set index=id+index
if (index<8191) then
return cs_array1[index]
elseif (index<16382) then
return cs_array2[index-8191]
endif
return GetStoredInteger(cs_cache,I2S(-id),I2S(index))
return null
endfunction
//! endtextmacro
//
//
//Yep, I am crazy. But These save me a function call.
// This was much worse before the textmacro...
//! runtextmacro CS_GetArrayObject("Object" ,"handle")
//! runtextmacro CS_GetArrayObject("Widget" ,"widget")
//! runtextmacro CS_GetArrayObject("Timer" ,"timer")
//! runtextmacro CS_GetArrayObject("Unit" ,"unit")
//! runtextmacro CS_GetArrayObject("Loc" ,"location")
//! runtextmacro CS_GetArrayObject("Trigger" ,"trigger")
//! runtextmacro CS_GetArrayObject("TriggerAction","triggeraction")
//! runtextmacro CS_GetArrayObject("Group" ,"group")
//! runtextmacro CS_GetArrayObject("Effect" ,"effect")
//! runtextmacro CS_GetArrayObject("Item" ,"item")
//! runtextmacro CS_GetArrayObject("Lightning" ,"lightning")
//! runtextmacro CS_GetArrayObject("Rect" ,"rect")
//! runtextmacro CS_GetArrayObject("Region" ,"region")
//! runtextmacro CS_GetArrayObject("TimerDialog" ,"timerdialog")
//! runtextmacro CS_GetArrayObject("Destructable" ,"destructable")
//! runtextmacro CS_GetArrayObject("Image" ,"image")
//! runtextmacro CS_GetArrayObject("Ubersplat" ,"ubersplat")
//! runtextmacro CS_GetArrayObject("Sound" ,"sound")
// </Indexes of handle types>
// The horror!
//
//=============================================================================================================
// Follow AttachObject and GetAttachedObject* these functions require I2H and have therefore been banned.
//
function AttachObject takes handle h, string label, handle x returns nothing
debug call BJDebugMsg("Warning: AttachObject function is in use (debug mode)")
if (x==null) then
call FlushStoredInteger(cs_cache,I2S(CS_H2I(h)),label)
else
call StoreInteger(cs_cache,I2S(CS_H2I(h)),label,CS_H2I(x))
endif
endfunction
//! textmacro GetAttachedObject takes name, type
function GetAttached$name$ takes handle h, string label returns $type$
debug call BJDebugMsg("Warning: GetAttached$name$ function is in use (debug mode)")
return GetStoredInteger(cs_cache, I2S(CS_H2I(h)), label)
return null
endfunction
//! endtextmacro
//! runtextmacro GetAttachedObject("Object" ,"handle")
//! runtextmacro GetAttachedObject("Widget" ,"widget")
//! runtextmacro GetAttachedObject("Rect" ,"rect")
//! runtextmacro GetAttachedObject("Region" ,"region")
//! runtextmacro GetAttachedObject("TimerDialog" ,"timerdialog")
//! runtextmacro GetAttachedObject("Unit" ,"unit")
//! runtextmacro GetAttachedObject("Item" ,"item")
//! runtextmacro GetAttachedObject("Effect" ,"effect")
//! runtextmacro GetAttachedObject("Destructable" ,"destructable")
//! runtextmacro GetAttachedObject("Trigger" ,"trigger")
//! runtextmacro GetAttachedObject("Timer" ,"timer")
//! runtextmacro GetAttachedObject("Group" ,"group")
//! runtextmacro GetAttachedObject("TriggerAction","triggeraction")
//! runtextmacro GetAttachedObject("Lightning" ,"lightning")
//! runtextmacro GetAttachedObject("Image" ,"image")
//! runtextmacro GetAttachedObject("Ubersplat" ,"ubersplat")
//! runtextmacro GetAttachedObject("Sound" ,"sound")
//! runtextmacro GetAttachedObject("Loc" ,"location")
//============================================================================================================
// Same story with Tables:
//============================================================================================================
// You may ask why am I using thousands of functions instead of multi-use return bug exploiters? Well,
// these make the thing much easier to read (in my opinion) and it is also better in performance since we
// have less function calls (H2U(GetTableObject("table","unit"))) would be worse than GetTableUnit that is
// quite direct.
//
function SetTableObject takes string table, string field, handle val returns nothing
debug call BJDebugMsg("Warning: SetTableObject in use (debug mode)")
if (val==null) then
call FlushStoredInteger(cs_cache,table,field)
else
call StoreInteger(cs_cache,table,field,CS_H2I(val))
endif
endfunction
//! textmacro GetTableObject takes name,type
function GetTable$name$ takes string table, string field returns $type$
debug call BJDebugMsg("Warning: GetTable$name$ is in use (debug mode)")
return GetStoredInteger(cs_cache,table,field)
return null
endfunction
//! endtextmacro
//! runtextmacro GetTableObject("Object" ,"handle")
//! runtextmacro GetTableObject("Widget" ,"widget")
//! runtextmacro GetTableObject("Rect" ,"rect")
//! runtextmacro GetTableObject("Region" ,"region")
//! runtextmacro GetTableObject("TimerDialog" ,"timerdialog")
//! runtextmacro GetTableObject("Unit" ,"unit")
//! runtextmacro GetTableObject("Item" ,"item")
//! runtextmacro GetTableObject("Effect" ,"effect")
//! runtextmacro GetTableObject("Destructable" ,"destructable")
//! runtextmacro GetTableObject("Trigger" ,"trigger")
//! runtextmacro GetTableObject("Timer" ,"timer")
//! runtextmacro GetTableObject("Group" ,"group")
//! runtextmacro GetTableObject("TriggerAction","triggeraction")
//! runtextmacro GetTableObject("Lightning" ,"lightning")
//! runtextmacro GetTableObject("Image" ,"image")
//! runtextmacro GetTableObject("Ubersplat" ,"ubersplat")
//! runtextmacro GetTableObject("Sound" ,"sound")
//! runtextmacro GetTableObject("Loc" ,"location")
function CSCache takes nothing returns gamecache
return cs_cache //Left for compat.
endfunction
function CS_KillTrigger takes trigger t returns nothing
if (t!=null) then
call TriggerRemoveAction(t,GetAttachedTriggerAction(t,"ac"))
call CleanAttachedVars(t)
call DestroyTrigger(t)
endif
endfunction
function CS_KillTimer takes timer t returns nothing
if (t!=null) then
call PauseTimer(t)
call CleanAttachedVars(t)
call DestroyTimer(t)
endif
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
scope ArcticBlast initializer Init
globals
private integer ArcticBlastID = 'A00B' //RawId of the Arctic Blast ability
private integer ArcticBoltID = 'n006' //RawId of the Arctic Bolt dummy
private real ArcticBlastSpeed = 800 //speed for the Arctic Blast bolts
private integer IceSlowID = 'AUfn' //this ability is used for slow, modify this for the slow duration
private string IceSlowOrder = "frostnova" //this is order string of the ice slow ability, if you use a different ability (not Frost Nova), you should change this
private real ArcticBoltCollides = 125. //the range that the Arctic Blast bolts will hit enemy unit.
private integer BoltInterval = 2 //This is the amount of time between the fire of two bolts
//inteval = BoltInterval * 0.05 = 0.1. So every 0.1s a bolt will be fired
private real DamageInterval = BoltInterval * 0.05 // = 0.1
endglobals
//==========================================
private function TJSetUnitHeight takes unit c, real h returns nothing
call UnitAddAbility(c,'Amrf')
call UnitRemoveAbility(c,'Amrf')
call SetUnitFlyHeight(c,h,0)
endfunction
private function TJGetPPX takes real x, real dist, real angle returns real
return x + dist * Cos(angle * bj_DEGTORAD)
endfunction
private function TJGetPPY takes real y, real dist, real angle returns real
return y + dist * Sin(angle * bj_DEGTORAD)
endfunction
//===========================================
private function GetManaCost takes integer lvl returns real
return 10. //this is the mana cost per DamageInterval second
endfunction
private function GetMinDamage takes integer lvl returns real
return lvl * 5. //this is the minimum damage per DamageInterval second
endfunction
private function GetMaxDamage takes integer lvl returns real
return lvl * 10. //this is the maximum damage per DamageInterval second
endfunction
private function GetDistance takes integer lvl returns real
return 400.+100.*lvl
endfunction
//===========================================
private struct data
unit caster = null
trigger hit = null
triggeraction hitact = null
timer move = null
group blast = null
real cx = 0.0
real cy = 0.0
real cost = 0.0
real min = 0.0
real max = 0.0
real dis = 0.0
integer interval = 0
integer lvl = 0
method onDestroy takes nothing returns nothing
if .hit != null then
call TriggerRemoveAction(.hit, .hitact)
call DestroyTrigger(.hit)
endif
if .move != null then
call ReleaseTimer(.move)
endif
if .blast != null then
call ReleaseGroup(.blast)
endif
endmethod
endstruct
private function ArcticBlastH takes nothing returns nothing
local trigger t = GetTriggeringTrigger()
local data d = GetCSData(t)
local unit a = GetTriggerUnit()
if GetWidgetLife(a) == 0 or not IsUnitEnemy(a,GetOwningPlayer(d.caster)) then
return
endif
//when a is alive and a is an enemy, a takes damage (Still need more conditions)
call UnitDamageTarget(d.caster,a,GetRandomReal(d.min,d.max),false,true,ATTACK_TYPE_CHAOS,DAMAGE_TYPE_UNIVERSAL,WEAPON_TYPE_WHOKNOWS)
call CasterCastAbilityEx( Player(15), GetUnitX(a), GetUnitY(a), 0., IceSlowID, d.lvl, IceSlowOrder, a, 1. )
set a = null
endfunction
private function ArcticBlastGM takes nothing returns nothing
local unit d = GetEnumUnit()
if GetWidgetLife(d) > 0 then
call SetUnitPosition(d, TJGetPPX(GetUnitX(d),ArcticBlastSpeed/25,GetUnitFacing(d)), TJGetPPY(GetUnitY(d),ArcticBlastSpeed/25,GetUnitFacing(d)) )
endif
set d = null
endfunction
private function ArcticBlastE takes nothing returns nothing
local timer t = GetExpiredTimer()
local data d = GetCSData(t)
local boolean b = true
local string o
local unit db
if GetUnitState(d.caster,UNIT_STATE_MANA)<d.cost then
call IssueImmediateOrder(d.caster,"stop")
endif
set o = OrderId2String(GetUnitCurrentOrder(d.caster))
if o != "blizzard" or d.cx != GetUnitX(d.caster) or d.cy != GetUnitY(d.caster) then
set b = false
call SetCSData(d.caster,0)
endif
if b then
if d.interval == 0 and b then
set db = CreateUnit(GetOwningPlayer(d.caster),ArcticBoltID,TJGetPPX(GetUnitX(d.caster),50,GetUnitFacing(d.caster)), TJGetPPY(GetUnitY(d.caster),50,GetUnitFacing(d.caster)),GetUnitFacing(d.caster))
call SetUnitPathing(db,false)
call SetUnitExploded(db,true)
call TJSetUnitHeight(db,75)
call UnitApplyTimedLife(db,'BTLF',d.dis/ArcticBlastSpeed)
call GroupAddUnit(d.blast,db)
call TriggerRegisterUnitInRange(d.hit,db,ArcticBoltCollides,null)
set db = null
set d.interval = BoltInterval
call SetUnitState(d.caster,UNIT_STATE_MANA,GetUnitState(d.caster,UNIT_STATE_MANA)-d.cost)
endif
set d.interval = d.interval - 1
endif
if IsUnitGroupDeadBJ(d.blast) then
call SetCSData(d.caster,0)
call data.destroy(d)
else
call ForGroup(d.blast,function ArcticBlastGM)
endif
endfunction
private function ArcticBlast takes unit c, real min, real max, real dis, integer lvl,real cost returns nothing
local data d = GetCSData(c)
if d == 0 then
set d = data.create()
set d.hit = CreateTrigger()
set d.hitact = TriggerAddAction(d.hit,function ArcticBlastH )
set d.move = NewTimer()
call TimerStart(d.move,0.05,true, function ArcticBlastE)
set d.blast = NewGroup()
call SetCSData(d.move,d)
call SetCSData(d.hit,d)
call SetCSData(c,d)
endif
set d.cx = GetUnitX(c)
set d.cy = GetUnitY(c)
set d.caster = c
set d.cost = cost
set d.min = min
set d.max = max
set d.lvl = lvl
set d.dis = dis
endfunction
private function ArcticBlastC takes nothing returns boolean
return GetSpellAbilityId() == ArcticBlastID
endfunction
private function ArcticBlastA takes nothing returns nothing
local unit c = GetTriggerUnit()
local integer lvl = GetUnitAbilityLevel(c,ArcticBlastID)
call ArcticBlast(c,GetMinDamage( lvl ) , GetMaxDamage( lvl ) , GetDistance( lvl ) , lvl , GetManaCost( lvl ) )
set c = null
endfunction
//===========================================================================
private function Init takes nothing returns nothing
local trigger t = CreateTrigger( )
call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_SPELL_EFFECT )
call TriggerAddCondition( t, Condition( function ArcticBlastC ) )
call TriggerAddAction( t, function ArcticBlastA )
set t = null
endfunction
endscope