Name | Type | is_array | initial_value |
HERO | unit | No | UnitNull |
HeroLoc | location | No | |
RespawnLoc | location | Yes | |
ReviveTimer | timer | No | |
ReviveWindow | timerdialog | No | |
TempInt | integer | No |
//TESH.scrollpos=154
//TESH.alwaysfold=0
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
//~ //
//~ Retardation v1.1 //
//~ //
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
//~ //
//~ Provides the unit with a restricting //
//~ protection, limiting the damage //
//~ he can suffer in a period of time. //
//~ //
//~ Level 1: 500 damage in 2 seconds. //
//~ Level 2: 450 damage in 3 seconds. //
//~ Level 3: 400 damage in 4 seconds. //
//~ //
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
//~ //
//~ It's what the tooltip says. No matter what happens (okay... bugs occur) //
//~ it's impossible that the unit takes more then 500/450/400 damage //
//~ in 2/3/4 seconds. If there are very mighty neutrals, care that they can't //
//~ deal such an amount of damage, you'd be invulnurable. //
//~ //
//~ I though of it as a nuke-protection :) //
//~ //
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
//~ //
//~ Utilities: //
//~ -TimerUtils //
//~ -InlinedHashtable //
//~ //
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
scope Retardation initializer InitRetard //No comment on this pls :D
globals
//The rawcode of "Retardation" [Spell]
private constant integer SPELLID = 'A000'
//The sfx is played whenever damage is absorbed (cap broken)
private constant string SFX = "Abilities\\Spells\\Undead\\ReplenishMana\\SpiritTouchTarget.mdl"
private constant string ATTACH = "chest"
//Please, don't touch these globals, they're needed for the spell
private trigger DAMAGED
private integer KEY
private unit TEMPUNIT
private real TEMPREAL
//The next two are combinations: if b_HEROCAP is true, only the hero damage will be counted
//for the cap. If false, creepdamage is included.
//If b_HEROABSORB is true, only the hero damage will be absorbed after reaching the cap.
//This means, that you could set b_HEROCAP to false but b_HEROABSORB to true.
//Now creeps would reach the cap to, but their damage isn't absorbed, making the spell
//against heros very strong if you're surrounded by creeps.
private constant boolean HEROCAP = false
private constant boolean HEROABSORB = false
endglobals
//The duration in which the damage must be done to reach the cap
private constant function GetDuration takes integer i_lvl returns real
return 2.+i_lvl
endfunction
//The cap. If more damage then this is done in the time from above, the damage is prevented
private constant function GetDamageCap takes integer i_lvl returns real
return 550.-i_lvl*50.
endfunction
//---------NO MORE TOUCHING PLS, I'M SHY! :P
//This struct is used to store all the damage that's done to the "caster"
//Each single piece of damage will be stored and reset after some time
private struct TempDamage
unit u_hero
real r_damage
static method Create takes unit u_hero, real r_damage returns TempDamage
local TempDamage s_dat = TempDamage.allocate()
set s_dat.u_hero = u_hero
set s_dat.r_damage = r_damage
return s_dat
endmethod
endstruct
//This function removes the damage out of the entire damage (from the cap)
//and releases the timer+struct instance
private function DecreaseDamage takes nothing returns nothing
local timer t_decrease = GetExpiredTimer()
local TempDamage s_dat = GetTimerData(t_decrease)
call SetHandleReal(s_dat.u_hero, KEY, GetHandleReal(s_dat.u_hero, KEY)-s_dat.r_damage)
call ReleaseTimer(t_decrease)
set s_dat.u_hero = null
call s_dat.destroy()
endfunction
//For the null timer
private function HealEx takes nothing returns nothing
call SetWidgetLife(TEMPUNIT, GetWidgetLife(TEMPUNIT)+TEMPREAL)
call ReleaseTimer(GetExpiredTimer())
endfunction
//Heal function, since normal "heal" with SetWidgetLife
//is done before the damage, causing damage at full life not to
//be healed -> null-timer.
private function Heal takes unit u_target, real r_amount returns nothing
local timer t_null
call DestroyEffect(AddSpecialEffectTarget(SFX, u_target, ATTACH))
if GetUnitState(u_target, UNIT_STATE_MAX_LIFE) - GetWidgetLife(u_target) < r_amount then
set t_null = NewTimer()
set TEMPUNIT = u_target
set TEMPREAL = r_amount
call TimerStart(t_null, 0., false, function HealEx)
else
call SetWidgetLife(u_target, GetWidgetLife(u_target)+r_amount)
endif
endfunction
//The function of the damage trigger, this registers every piece of damage
private function PreventDamage takes nothing returns boolean
local unit u_damaged
local timer t_decrease
local real r_damage
local real r_taken = GetEventDamage()
local integer i_level
local TempDamage s_dat
//Just to filter the millions of sources out that don't deal damage
//like AI-stuff and buffs
if r_taken <= 0 then
return false
endif
set u_damaged = GetTriggerUnit()
set t_decrease = NewTimer()
set r_damage = GetHandleReal(u_damaged, KEY)
set i_level = GetUnitAbilityLevel(u_damaged, SPELLID)
set s_dat = TempDamage.Create(u_damaged, r_taken)
static if HEROABSORB then
if IsUnitType(GetEventDamageSource(), UNIT_TYPE_HERO) then
//If the cap is already reached, just heal
if r_damage >= GetDamageCap(i_level) then
call Heal(u_damaged, r_taken)
//If the cap will be reached with this damage,
//heal the cap-overflow
elseif r_damage+r_taken >= GetDamageCap(i_level) then
call Heal(u_damaged, r_damage+r_taken-GetDamageCap(i_level))
endif
endif
else
//If the cap is already reached, just heal
if r_damage >= GetDamageCap(i_level) then
call Heal(u_damaged, r_taken)
//If the cap will be reached with this damage,
//heal the cap-overflow
elseif r_damage+r_taken >= GetDamageCap(i_level) then
call Heal(u_damaged, r_damage+r_taken-GetDamageCap(i_level))
endif
endif
//Saving the damage and attaching it to the timer
static if HEROCAP then
if IsUnitType(GetEventDamageSource(), UNIT_TYPE_HERO) then
call SetHandleReal(u_damaged, KEY, r_damage+r_taken)
call SetTimerData(t_decrease, s_dat)
call TimerStart(t_decrease, GetDuration(i_level), false, function DecreaseDamage)
endif
else
call SetHandleReal(u_damaged, KEY, r_damage+r_taken)
call SetTimerData(t_decrease, s_dat)
call TimerStart(t_decrease, GetDuration(i_level), false, function DecreaseDamage)
endif
set u_damaged = null
return false
endfunction
//Registers all units that get the skill
private function AddToDAMAGED takes nothing returns boolean
if GetLearnedSkill() == SPELLID and GetLearnedSkillLevel() == 1 then
call TriggerRegisterUnitEvent(DAMAGED, GetTriggerUnit(), EVENT_UNIT_DAMAGED)
endif
return false
endfunction
private function InitRetard takes nothing returns nothing
local trigger t_init = CreateTrigger()
set DAMAGED = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t_init, EVENT_PLAYER_HERO_SKILL)
call TriggerAddCondition(t_init, Condition(function AddToDAMAGED))
call TriggerAddCondition(DAMAGED, Condition(function PreventDamage))
set KEY = StringHash("Retardation")
call RegisterStringHash("Retardation")
call Preload(SFX)
endfunction
endscope
//TESH.scrollpos=75
//TESH.alwaysfold=0
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
//~ //
//~ Protective Barrier v1.0 //
//~ //
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
//~ //
//~ Justify creates several rocks //
//~ in front of him, blocking his enemies. //
//~ To ensure his safety, he is pushed backwards. //
//~ Rocks last 5 seconds. //
//~ //
//~ Level 1: 300 range behind 3 rocks. //
//~ Level 2: 500 range behind 4 rocks. //
//~ Level 3: 700 range behind 5 rocks. //
//~ //
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
//~ //
//~ Another protective spell. The rocks aren't that big, //
//~ but you can block small paths. Even if the block isn't //
//~ fully blocking, the pushback can save your life :) //
//~ //
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
//~ //
//~ Utilities: //
//~ - TimerUtils //
//~ - Pathability //
//~ //
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
scope ProtectiveBarrier initializer InitBarrier
globals
//Rawcode of "Protective Barrier" [Spell]
private constant integer SPELLID = 'A001'
//Rawcode of "Barrier(Protective Barrier)" [Unit]
private constant integer BARRIERID = 'h000'
//Sfx shown at the caster
private constant string CASTERSFX = "Abilities\\Spells\\Items\\SpellShieldAmulet\\SpellShieldCaster.mdl"
//Sfx attachment point
private constant string CASTERATTACH = "chest"
//Sfx fpr the barriers
private constant string BARRIERSFX = "Abilities\\Spells\\Undead\\AnimateDead\\AnimateDeadTarget.mdl"
//Duration of the barrier
private constant real BARDUR = 5.
//Distance away in front of the caster
private constant real BARDIST = 150.
//Sidedistance between the middle of each barrier
private constant real SIDEDIST = 100.
//Knockback speed
private constant real SPEED = 1000.
//Timerperiod (unimportant)
private constant real PERIOD = 0.03
//If false, units will be dodged (uses SetUnitPosition instead of SetUnitX/Y)
//This looks realy silly, however, someone might find it usefull.
private constant boolean IGNOREUNITS = true
endglobals
//Overall distance knocked back
private constant function GetDistance takes integer i_lvl returns real
return 100.+i_lvl*200.
endfunction
//Number of barriers
private constant function GetNumber takes integer i_lvl returns integer
return 2+i_lvl
endfunction
private struct PushBack
//Caster
unit u_caster
//His position
real r_x
real r_y
//Movementvalues
real r_cos
real r_sin
//Saves distance values to find the end
real r_step
real r_done
real r_max
//Movemethod, callback of the timer
static method Move takes nothing returns nothing
local timer t_push = GetExpiredTimer()
local PushBack s_dat = GetTimerData(t_push)
//Moving backwards
set s_dat.r_x = s_dat.r_x-s_dat.r_cos
set s_dat.r_y = s_dat.r_y-s_dat.r_sin
if IsTerrainWalkable(s_dat.r_x, s_dat.r_y) then
static if b_IGNOREUNITS then
call SetUnitX(s_dat.u_caster, s_dat.r_x)
call SetUnitY(s_dat.u_caster, s_dat.r_y)
else
call SetUnitPosition(s_dat.u_caster, s_dat.r_x, s_dat.r_y)
endif
else
//In this case, collision hit something that's NOT a unit-> will never move away... just end it
set s_dat.r_done = s_dat.r_max
endif
set s_dat.r_done = s_dat.r_done+s_dat.r_step
if s_dat.r_done >= s_dat.r_max then
call ReleaseTimer(t_push)
set s_dat.u_caster = null
call s_dat.destroy()
endif
endmethod
static method Create takes nothing returns boolean
local PushBack s_dat
local real r_x
local real r_y
local real r_angle
local unit u_temp
local player p_temp
local timer t_push
local integer i_number
local integer i_loop
if GetSpellAbilityId() == SPELLID then
//Allocating, caster coordinates and facing and the rest of the struct
set s_dat = PushBack.allocate()
set s_dat.u_caster = GetTriggerUnit()
set r_angle = GetUnitFacing(s_dat.u_caster)*bj_DEGTORAD
set s_dat.r_x = GetUnitX(s_dat.u_caster)
set s_dat.r_y = GetUnitY(s_dat.u_caster)
set s_dat.r_max = GetDistance(GetUnitAbilityLevel(s_dat.u_caster, SPELLID))
set s_dat.r_step = SPEED*PERIOD
set s_dat.r_cos = s_dat.r_step*Cos(r_angle)
set s_dat.r_sin = s_dat.r_step*Sin(r_angle)
set s_dat.r_done = 0.
//SFX :D
call DestroyEffect(AddSpecialEffectTarget(CASTERSFX, s_dat.u_caster, CASTERATTACH))
//Getting the first rocks point and the amount of rocks
//The point is the "most left one" regarding the casters facing
set i_number = GetNumber(GetUnitAbilityLevel(s_dat.u_caster, SPELLID))
set r_x = s_dat.r_x+BARDIST*Cos(r_angle)+(i_number-1)/2*SIDEDIST*Cos(r_angle-bj_PI/2)
set r_y = s_dat.r_y+BARDIST*Sin(r_angle)+(i_number-1)/2*SIDEDIST*Sin(r_angle-bj_PI/2)
//We're starting at one side, going to the other (barrier-creation)
set r_angle = r_angle+bj_PI/2
//Tempplayer and the rocks
set p_temp = GetOwningPlayer(s_dat.u_caster)
set i_loop = 0
loop
exitwhen i_loop >= i_number
//SFX :D
call DestroyEffect(AddSpecialEffect(BARRIERSFX, r_x, r_y))
set u_temp = CreateUnit(p_temp, BARRIERID, r_x, r_y, GetRandomReal(0., 359.9))
//CreateUnit cares for collision, SetUnitX/Y not :)
call SetUnitX(u_temp, r_x)
call SetUnitY(u_temp, r_y)
call UnitApplyTimedLife(u_temp, 'BTLF', BARDUR)
set r_x = r_x+SIDEDIST*Cos(r_angle)
set r_y = r_y+SIDEDIST*Sin(r_angle)
set i_loop = i_loop+1
endloop
//And the timer
set t_push = NewTimer()
call SetTimerData(t_push, s_dat)
call TimerStart(t_push, PERIOD, true, function PushBack.Move)
endif
return false
endmethod
endstruct
private function InitBarrier takes nothing returns nothing
local trigger t_init = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t_init, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(t_init, Condition(function PushBack.Create))
call Preload(CASTERSFX)
call Preload(BARRIERSFX)
endfunction
endscope
//TESH.scrollpos=171
//TESH.alwaysfold=0
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
//~ //
//~ Soil Totem v1.2 //
//~ //
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
//~ //
//~ Summons an immobile Soil Totem which //
//~ calls a wave of explosions after a random //
//~ amount of time, dealing damage to each //
//~ enemy within a certain range. //
//~ Damage increases the lower Justifys //
//~ hitpoints are, while low hitpoints causing //
//~ the speed of the explosions to increase. //
//~ //
//~ Level 1: 60 maxdamage in 550 range every 2-6 seconds. //
//~ Level 2: 120 maxdamage in 600 range every 1-3 seconds. //
//~ Level 3: 180 maxdamage in 650 range every 0.7-2 seconds. //
//~ //
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
//~ //
//~ The aggressiv way of protection. The //
//~ damage at low life is heavy but weak //
//~ with a lot of hitpoints. //
//~ //
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
//~ //
//~ Utilities: //
//~ -TimerUtils //
//~ -GroupUtils //
//~ //
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
scope SoilTotem initializer InitTotem
globals
//The rawcode of "SoilTotem" [Spell]
private constant integer SPELLID = 'A002'
//The rawcode of "Totem (SoilTotem)" [Unit]
private constant integer TOTEMID = 'o000'
//Sfx path of the effect at the totem while ticking (ground)
private constant string TOTEMSFX = "Abilities\\Spells\\Human\\Flare\\FlareCaster.mdl"
//Sfx path of the effect at hit units
private constant string HITSFX = "Abilities\\Weapons\\SteamTank\\SteamTankImpact.mdl"
//Attachmentpoint of HITSFX
private constant string HITATTACH = "chest"
//Speed-increase hitpoint cap. 1=100%, 0.5 = 50% ...
private constant real HPCAP = 0.5
//Speed-increase of the cap ;) 2 = double, 3 = triple ...
private constant real CAPINCREASE = 2
private player TEMPPLAYER
private integer TEMPINT
private real FACTOR
endglobals
//Feel free to change to formulas below this comment (just the stuff behind the "return")
//How long the totem will live
private function GetTotemDuration takes integer i_lvl returns real
return 10.*i_lvl
endfunction
//How many ticks a totem can do at maximum
private function GetTotemTicks takes integer i_lvl returns integer
return 2*i_lvl
endfunction
//Time until the next tick, GetRandomReal(4.,8.) is a random value between 4 and 8
private function GetNextTick takes integer i_lvl returns real
return GetRandomReal(2., 6.)/I2R(i_lvl)
endfunction
//The damage done per tick
private function GetSpellDamage takes integer i_lvl returns real
return 120.*i_lvl
endfunction
//The radius of the totem ticks
private function GetTotemRadius takes integer i_lvl returns real
return 500.+50.*i_lvl
endfunction
//Don't touch the stuff below THIS comment ;) it is the spell core and you can't change any values
//here. Only change things if you want to change the way the spell works and you have enough knowledge of vJass.
private struct TotemStruct
//The caster
unit u_caster
//The totem
unit u_totem
//The totems position
real r_totemx
real r_totemy
//The casters owner
player p_owner
//Level of SoilTotem
integer i_abillvl
//Damage done by the totem
real r_damage
//AOE for the damage
real r_radius
//Duration of the totem
real r_maxtime
//Max ticks of the totem. If this or maxtime is reached, the spell is finished
integer r_maxcount
//Counts the time/ticks
real r_acttime
integer r_actcount
//Timer for the ticks
timer t_tick
//Enemy filter method, filters non-enemies, dead ones and magic-immunes
private static method Enemys takes nothing returns boolean
return IsUnitEnemy(GetFilterUnit(), TEMPPLAYER) and GetUnitState(GetFilterUnit(), UNIT_STATE_LIFE) > 0.405 and not IsUnitType(GetFilterUnit(),UNIT_TYPE_MAGIC_IMMUNE)
endmethod
//Damage method for the ForGroup function
private static method Damage takes nothing returns nothing
local TotemStruct s_dat = TEMPINT
call UnitDamageTarget(s_dat.u_caster, GetEnumUnit(), s_dat.r_damage*FACTOR, false, true, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_NORMAL, WEAPON_TYPE_WHOKNOWS)
//SFX :D
call DestroyEffect(AddSpecialEffectTarget(HITSFX, GetEnumUnit(), HITATTACH))
endmethod
//Function called by the timer (callback)
private static method RunAbility takes nothing returns nothing
local TotemStruct s_dat = GetTimerData(GetExpiredTimer())
local real r_tick
local real r_life
//Alive-check of the totem
if GetUnitState(s_dat.u_totem, UNIT_STATE_LIFE) > 0.405 then
//SFX :D
call DestroyEffect(AddSpecialEffect(TOTEMSFX, s_dat.r_totemx, s_dat.r_totemy))
set TEMPPLAYER = s_dat.p_owner
//Pick units in AOE
call GroupEnumUnitsInRange(ENUM_GROUP, s_dat.r_totemx, s_dat.r_totemy, s_dat.r_radius, Condition(function TotemStruct.Enemys))
set TEMPINT = s_dat
//A factor between 0 and which is multiplied with the damage
//So, if you got full hitpoints, this won't deal ANY damage!
set r_life = GetUnitState(s_dat.u_caster, UNIT_STATE_MAX_LIFE)
set FACTOR = (r_life-GetWidgetLife(s_dat.u_caster))/r_life
//Damage units with ForGroup
call ForGroup(ENUM_GROUP, function TotemStruct.Damage)
elseif not (s_dat.t_tick == null) then
//The totem is dead, reset the timer ....
call s_dat.destroy()
return
endif
//If this if is true, either maxticks or maxtime is reached
if s_dat.r_acttime >= s_dat.r_maxtime or s_dat.r_actcount >= s_dat.r_maxcount then
call s_dat.destroy()
else
//If not, we'll deal with the next tick in a random time
if GetWidgetLife(s_dat.u_caster)/r_life <= HPCAP then
set FACTOR = 1/CAPINCREASE
else
set FACTOR = 1.
endif
set r_tick = GetNextTick(s_dat.i_abillvl)*FACTOR
set s_dat.r_acttime = s_dat.r_acttime+r_tick
set s_dat.r_actcount = s_dat.r_actcount+1
call TimerStart(s_dat.t_tick, r_tick, false, function TotemStruct.RunAbility)
endif
endmethod
//Simple create method that gets all important values
static method Create takes nothing returns boolean
local TotemStruct s_dat
local integer i_lvl
if GetSpellAbilityId() == SPELLID then
set s_dat = TotemStruct.allocate()
set s_dat.u_caster = GetTriggerUnit()
set s_dat.p_owner = GetOwningPlayer(s_dat.u_caster)
set s_dat.u_totem = CreateUnit(s_dat.p_owner, TOTEMID, GetSpellTargetX(), GetSpellTargetY(), GetRandomReal(0., 360.))
//To avoid that the sfx isn't at the totem in the case
//that the totem is missplaced tue to unpathability
set s_dat.r_totemx = GetUnitX(s_dat.u_totem)
set s_dat.r_totemy = GetUnitY(s_dat.u_totem)
set s_dat.i_abillvl = GetUnitAbilityLevel(s_dat.u_caster, SPELLID)
set s_dat.r_damage = GetSpellDamage(s_dat.i_abillvl)
set s_dat.r_radius = GetTotemRadius(s_dat.i_abillvl)
set s_dat.r_maxtime = GetTotemDuration(s_dat.i_abillvl)
set s_dat.r_maxcount = GetTotemTicks(s_dat.i_abillvl)
set s_dat.r_acttime = GetNextTick(s_dat.i_abillvl)
set s_dat.r_actcount = 1
set s_dat.t_tick = NewTimer()
call SetTimerData(s_dat.t_tick, s_dat)
call TimerStart(s_dat.t_tick, s_dat.r_acttime, false, function TotemStruct.RunAbility)
endif
return false
endmethod
private method onDestroy takes nothing returns nothing
//Leak and timer removal
set .u_caster = null
call KillUnit(.u_totem)
set .u_totem = null
call ReleaseTimer(.t_tick)
endmethod
endstruct
private function InitTotem takes nothing returns nothing
local trigger t_init = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t_init, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(t_init, Condition(function TotemStruct.Create))
call Preload(TOTEMSFX)
call Preload(HITSFX)
endfunction
endscope
//TESH.scrollpos=112
//TESH.alwaysfold=0
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
//~ //
//~ Last Stand v1.0 //
//~ //
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
//~ //
//~ Releasing his inner will to survive, //
//~ Justify fully heals himself instantly. //
//~ The extra amount of hitpoints //
//~ don't last very long, damaging //
//~ him as soon as his will tends to break, //
//~ causing him to stop a moment. //
//~ Justify can't die due to this damage. //
//~ //
//~ Level 1: 3.0x healed life as damage after 8 seconds. //
//~ Level 2: 2.5x healed life as damage after 11 seconds. //
//~ Level 3: 2.0x healed life as damage after 14 seconds. //
//~ //
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
//~ //
//~ A rather simple spell which is rather strong. //
//~ Even 8 seconds should be enough to head back from //
//~ the enemies and rescue yourself. //
//~ Also, when the damage option is enabled, it is a //
//~ pretty decent combo with "Retardation". //
//~ //
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
//~ //
//~ Utilities: //
//~ -TimerUtils //
//~ //
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
scope LastStand initializer InitStand
globals
//Rawcode of "Last Stand" [Spell]
private constant integer SPELLID = 'A003'
//SFX that's attached to the caster until the spell is over
private constant string SFX = "Abilities\\Spells\\Orc\\Voodoo\\VoodooAuraTarget.mdl"
//Point for SFX
private constant string ATTACH = "chest"
//SFX that's palyed when the spells finished (and the damage done)
private constant string ENDSFX = "Abilities\\Spells\\Orc\\WarStomp\\WarStompCaster.mdl"
//Damage constants for the UnitDamageFunction (only important when b_DAMAGE is true)
//Like this it will always deal full damage, to regard armor and types you need
//to change a_ATTACK and d_DAMAGE
private constant boolean ISATTACK = true
private constant boolean ISRANGED = true
private constant attacktype ATTACK = ATTACK_TYPE_CHAOS
private constant damagetype DAMAGE = DAMAGE_TYPE_UNIVERSAL
private constant weapontype WEAPON = WEAPON_TYPE_WHOKNOWS
//If true, the loss of life is damage, causing damage detection systems
//to fire. If CANTDIE is false, he can kill himself with kill credits
//of him. If not, it's "SetWidgetLife" without any references to the damage source.
private constant boolean USEDAMAGE = true
//If true, the caster cant die due to this damage and will remain with 1hp if the damage
//kills him.
private constant boolean CANTDIE = true
endglobals
//Formula for the received damage
private constant function GetDamageFactor takes integer i_lvl returns real
return 3.5-i_lvl/2.
endfunction
//Function for the duration until the damage is done
private constant function GetDuration takes integer i_lvl returns real
return 5.+i_lvl*3.
endfunction
private struct LifeHolder
//The caster
unit u_caster
//The damage he'll suffer
real r_damage
//Timer to end the spell
timer t_end
//Sfx anyone?
effect e_sfx
static method End takes nothing returns nothing
local LifeHolder s_dat = GetTimerData(GetExpiredTimer())
local real r_life
//Reducing the damage, so the caster will survive it
static if CANTDIE then
set r_life = GetWidgetLife(s_dat.u_caster)
if GetWidgetLife(s_dat.u_caster) - s_dat.r_damage < 1 then
set s_dat.r_damage = GetWidgetLife(s_dat.u_caster)-1
endif
endif
//Choosing between the damage types
static if USEDAMAGE then
call UnitDamageTarget(s_dat.u_caster, s_dat.u_caster, s_dat.r_damage, ISATTACK, ISRANGED, ATTACK, DAMAGE, WEAPON)
else
call SetWidgetLife(s_dat.u_caster, GetWidgetLife(s_dat.u_caster)-s_dat.r_damage)
endif
//Stopping the caster. Interrupts channelings, order chains... care ;)
call IssueImmediateOrder(s_dat.u_caster, "stop")
//SFX again :D
call DestroyEffect(AddSpecialEffect(ENDSFX, GetUnitX(s_dat.u_caster), GetUnitY(s_dat.u_caster)))
//Leak removal
call DestroyEffect(s_dat.e_sfx)
set s_dat.e_sfx = null
set s_dat.u_caster = null
call ReleaseTimer(s_dat.t_end)
call s_dat.destroy()
endmethod
static method Create takes nothing returns boolean
local LifeHolder s_dat
local real r_life
local integer i_lvl
if GetSpellAbilityId() == SPELLID then
//Allocation, sfx, variable sets
set s_dat = LifeHolder.allocate()
set s_dat.u_caster = GetTriggerUnit()
set s_dat.e_sfx = AddSpecialEffectTarget(SFX, s_dat.u_caster, ATTACH)
set r_life = GetUnitState(s_dat.u_caster, UNIT_STATE_MAX_LIFE)
set i_lvl = GetUnitAbilityLevel(s_dat.u_caster, SPELLID)
//His new life is mirrored into the negativ: The life he already lost
//(MaxLife-ActualLife) will be substracted again after the spell,
//and this three times with the normal options
set s_dat.r_damage = (r_life-GetWidgetLife(s_dat.u_caster))*GetDamageFactor(i_lvl)
//Setting his life to max!
call SetWidgetLife(s_dat.u_caster, r_life)
set s_dat.t_end = NewTimer()
call SetTimerData(s_dat.t_end, s_dat)
call TimerStart(s_dat.t_end, GetDuration(i_lvl), false, function LifeHolder.End)
endif
return false
endmethod
endstruct
private function InitStand takes nothing returns nothing
local trigger t_init = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t_init, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(t_init, Condition(function LifeHolder.Create))
endfunction
endscope
//TESH.scrollpos=1
//TESH.alwaysfold=0
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//~
//~ InlinedHashtable (IHT) v1.0
//~
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//~
//~ Like the name of this system might tell you,
//~ these are only wrapper-functions for a hashtable.
//~ In addition, they're kept short enough to be inlined
//~ by jasshelper.
//~
//~ You might also notice that the functions have the same
//~ name like the very old "Katanas Handle Vars".
//~ Due to the two keys the syntax changed, but I like these
//~ function names. Another thing is, that many names are
//~ already used by Blizzard (damn gamecache).
//~
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
library InlinedHashtable initializer InitTable
globals
private hashtable HASH
endglobals
//! textmacro IHT_CreateFunction takes NAME, TYPE, ORIGNAME, HANDLE
function SetHandle$NAME$ takes handle h, integer key, $TYPE$ t returns nothing
call Save$ORIGNAME$(HASH, GetHandleId(h), key, t)
endfunction
function GetHandle$NAME$ takes handle h, integer key returns $TYPE$
return Load$ORIGNAME$(HASH, GetHandleId(h), key)
endfunction
function SavedHandle$NAME$ takes handle h, integer key returns boolean
static if $HANDLE$ then
return HaveSavedHandle(HASH, GetHandleId(h), key)
else
return HaveSaved$NAME$(HASH, GetHandleId(h), key)
endif
endfunction
function DeleteHandle$NAME$ takes handle h, integer key returns nothing
static if $HANDLE$ then
call RemoveSavedHandle(HASH, GetHandleId(h), key)
else
call RemoveSaved$NAME$(HASH, GetHandleId(h), key)
endif
endfunction
//! endtextmacro
//! runtextmacro IHT_CreateFunction("Integer", "integer", "Integer", "false")
//! runtextmacro IHT_CreateFunction("Real", "real", "Real", "false")
//! runtextmacro IHT_CreateFunction("Boolean", "boolean", "Boolean", "false")
//! runtextmacro IHT_CreateFunction("String", "string", "Str", "false")
//! runtextmacro IHT_CreateFunction("Unit", "unit", "UnitHandle", "true")
//! runtextmacro IHT_CreateFunction("Timer", "timer", "TimerHandle", "true")
//! runtextmacro IHT_CreateFunction("Group", "group", "GroupHandle", "true")
//! runtextmacro IHT_CreateFunction("Effect", "effect", "EffectHandle", "true")
//! runtextmacro IHT_CreateFunction("Trigger", "trigger", "TriggerHandle", "true")
//Added this for safety usage. The first key, -1, is normaly the HandleId of handles, so
//-1 will never be used. This function saves all strings that're used to attach values
//from different spells to units. The chance for a collision of StringHash is very small
//but this chance can be set to 0 if you control them and change a key in case of collission.
function RegisterStringHash takes string s_key returns nothing
if HaveSavedString(HASH, -1, StringHash(s_key)) then
call BJDebugMsg("ERROR: StringHash for "+s_key+" is the same like the one for "+LoadStr(HASH, -1, StringHash(s_key))+".")
else
call SaveStr(HASH, -1, StringHash(s_key), s_key)
endif
endfunction
private function InitTable takes nothing returns nothing
set HASH = InitHashtable()
endfunction
endlibrary
//TESH.scrollpos=1
//TESH.alwaysfold=0
library GroupUtils
//******************************************************************************
//* BY: Rising_Dusk
//*
//* This library is a simple implementation of a stack for groups that need to
//* be in the user's control for greater than an instant of time. Additionally,
//* this library provides a single, global group variable for use with user-end
//* enumerations. It is important to note that users should not be calling
//* DestroyGroup() on the global group, since then it may not exist for when it
//* it is next needed.
//*
//* The group stack removes the need for destroying groups and replaces it with
//* a recycling method.
//* function NewGroup takes nothing returns group
//* function ReleaseGroup takes group g returns boolean
//* function GroupRefresh takes group g returns nothing
//*
//* NewGroup grabs a currently unused group from the stack or creates one if the
//* stack is empty. You can use this group however you'd like, but always
//* remember to call ReleaseGroup on it when you are done with it. If you don't
//* release it, it will 'leak' and your stack may eventually overflow if you
//* keep doing that.
//*
//* GroupRefresh cleans a group of any shadow references which may be clogging
//* its hash table. If you remove a unit from the game who is a member of a unit
//* group, it will 'effectively' remove the unit from the group, but leave a
//* shadow in its place. Calling GroupRefresh on a group will clean up any
//* shadow references that may exist within it.
//*
globals
//* Group for use with all instant enumerations
group ENUM_GROUP = CreateGroup()
//* Temporary references for GroupRefresh
private boolean Flag = false
private group Refr = null
//* Assorted constants
private constant integer MAX_HANDLE_COUNT = 408000
private constant integer MIN_HANDLE_ID = 0x100000
//* Arrays and counter for the group stack
private group array Groups
private integer array Status[MAX_HANDLE_COUNT]
private integer Count = 0
endglobals
private function AddEx takes nothing returns nothing
if Flag then
call GroupClear(Refr)
set Flag = false
endif
call GroupAddUnit(Refr, GetEnumUnit())
endfunction
function GroupRefresh takes group g returns nothing
set Flag = true
set Refr = g
call ForGroup(Refr, function AddEx)
if Flag then
call GroupClear(g)
endif
endfunction
function NewGroup takes nothing returns group
if Count == 0 then
set Groups[0] = CreateGroup()
else
set Count = Count - 1
endif
set Status[GetHandleId(Groups[Count])-MIN_HANDLE_ID] = 1
return Groups[Count]
endfunction
function ReleaseGroup takes group g returns boolean
local integer stat = Status[GetHandleId(g)-MIN_HANDLE_ID]
if g == null then
debug call BJDebugMsg(SCOPE_PREFIX+"Error: Null groups cannot be released")
return false
elseif stat == 0 then
debug call BJDebugMsg(SCOPE_PREFIX+"Error: Group not part of stack")
return false
elseif stat == 2 then
debug call BJDebugMsg(SCOPE_PREFIX+"Error: Groups cannot be multiply released")
return false
elseif Count == 8191 then
debug call BJDebugMsg(SCOPE_PREFIX+"Error: Max groups achieved, destroying group")
call DestroyGroup(g)
return false
endif
call GroupClear(g)
set Groups[Count] = g
set Count = Count + 1
set Status[GetHandleId(g)-MIN_HANDLE_ID] = 2
return true
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library TimerUtils initializer init
//*********************************************************************
//* TimerUtils (red+blue+orange flavors for 1.24b+)
//* ----------
//*
//* To implement it , create a custom text trigger called TimerUtils
//* and paste the contents of this script there.
//*
//* To copy from a map to another, copy the trigger holding this
//* library to your map.
//*
//* (requires vJass) More scripts: htt://www.wc3c.net
//*
//* For your timer needs:
//* * Attaching
//* * Recycling (with double-free protection)
//*
//* set t=NewTimer() : Get a timer (alternative to CreateTimer)
//* ReleaseTimer(t) : Relese a timer (alt to DestroyTimer)
//* SetTimerData(t,2) : Attach value 2 to timer
//* GetTimerData(t) : Get the timer's value.
//* You can assume a timer's value is 0
//* after NewTimer.
//*
//* Multi-flavor:
//* Set USE_HASH_TABLE to true if you don't want to complicate your life.
//*
//* If you like speed and giberish try learning about the other flavors.
//*
//********************************************************************
//================================================================
globals
//How to tweak timer utils:
// USE_HASH_TABLE = true (new blue)
// * SAFEST
// * SLOWEST (though hash tables are kind of fast)
//
// USE_HASH_TABLE = false, USE_FLEXIBLE_OFFSET = true (orange)
// * kinda safe (except there is a limit in the number of timers)
// * ALMOST FAST
//
// USE_HASH_TABLE = false, USE_FLEXIBLE_OFFSET = false (red)
// * THE FASTEST (though is only faster than the previous method
// after using the optimizer on the map)
// * THE LEAST SAFE ( you may have to tweak OFSSET manually for it to
// work)
//
private constant boolean USE_HASH_TABLE = true
private constant boolean USE_FLEXIBLE_OFFSET = false
private constant integer OFFSET = 0x100000
private integer VOFFSET = OFFSET
//Timers to preload at map init:
private constant integer QUANTITY = 256
//Changing this to something big will allow you to keep recycling
// timers even when there are already AN INCREDIBLE AMOUNT of timers in
// the stack. But it will make things far slower so that's probably a bad idea...
private constant integer ARRAY_SIZE = 8190
endglobals
//==================================================================================================
globals
private integer array data[ARRAY_SIZE]
private hashtable ht
endglobals
//It is dependent on jasshelper's recent inlining optimization in order to perform correctly.
function SetTimerData takes timer t, integer value returns nothing
static if(USE_HASH_TABLE) then
// new blue
call SaveInteger(ht,0,GetHandleId(t), value)
elseif (USE_FLEXIBLE_OFFSET) then
// orange
static if (DEBUG_MODE) then
if(GetHandleId(t)-VOFFSET<0) then
call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
endif
endif
set data[GetHandleId(t)-VOFFSET]=value
else
// new red
static if (DEBUG_MODE) then
if(GetHandleId(t)-OFFSET<0) then
call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
endif
endif
set data[GetHandleId(t)-OFFSET]=value
endif
endfunction
function GetTimerData takes timer t returns integer
static if(USE_HASH_TABLE) then
// new blue
return LoadInteger(ht,0,GetHandleId(t) )
elseif (USE_FLEXIBLE_OFFSET) then
// orange
static if (DEBUG_MODE) then
if(GetHandleId(t)-VOFFSET<0) then
call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
endif
endif
return data[GetHandleId(t)-VOFFSET]
else
// new red
static if (DEBUG_MODE) then
if(GetHandleId(t)-OFFSET<0) then
call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
endif
endif
return data[GetHandleId(t)-OFFSET]
endif
endfunction
//==========================================================================================
globals
private timer array tT[ARRAY_SIZE]
private integer tN = 0
private constant integer HELD=0x28829022
//use a totally random number here, the more improbable someone uses it, the better.
endglobals
//==========================================================================================
function NewTimer takes nothing returns timer
if (tN==0) then
//If this happens then the QUANTITY rule has already been broken, try to fix the
// issue, else fail.
debug call BJDebugMsg("NewTimer: Warning, Exceeding TimerUtils_QUANTITY, make sure all timers are getting recycled correctly")
static if( not USE_HASH_TABLE) then
debug call BJDebugMsg("In case of errors, please increase it accordingly, or set TimerUtils_USE_HASH_TABLE to true")
set tT[0]=CreateTimer()
static if( USE_FLEXIBLE_OFFSET) then
if (GetHandleId(tT[0])-VOFFSET<0) or (GetHandleId(tT[0])-VOFFSET>=ARRAY_SIZE) then
//all right, couldn't fix it
call BJDebugMsg("NewTimer: Unable to allocate a timer, you should probably set TimerUtils_USE_HASH_TABLE to true or fix timer leaks.")
return null
endif
else
if (GetHandleId(tT[0])-OFFSET<0) or (GetHandleId(tT[0])-OFFSET>=ARRAY_SIZE) then
//all right, couldn't fix it
call BJDebugMsg("NewTimer: Unable to allocate a timer, you should probably set TimerUtils_USE_HASH_TABLE to true or fix timer leaks.")
return null
endif
endif
endif
else
set tN=tN-1
endif
call SetTimerData(tT[tN],0)
return tT[tN]
endfunction
//==========================================================================================
function ReleaseTimer takes timer t returns nothing
if(t==null) then
debug call BJDebugMsg("Warning: attempt to release a null timer")
return
endif
if (tN==ARRAY_SIZE) then
debug call BJDebugMsg("Warning: Timer stack is full, destroying timer!!")
//stack is full, the map already has much more troubles than the chance of bug
call DestroyTimer(t)
else
call PauseTimer(t)
if(GetTimerData(t)==HELD) then
debug call BJDebugMsg("Warning: ReleaseTimer: Double free!")
return
endif
call SetTimerData(t,HELD)
set tT[tN]=t
set tN=tN+1
endif
endfunction
private function init takes nothing returns nothing
local integer i=0
local integer o=-1
local boolean oops = false
static if( USE_HASH_TABLE ) then
set ht = InitHashtable()
loop
exitwhen(i==QUANTITY)
set tT[i]=CreateTimer()
call SetTimerData(tT[i], HELD)
set i=i+1
endloop
set tN = QUANTITY
else
loop
set i=0
loop
exitwhen (i==QUANTITY)
set tT[i] = CreateTimer()
if(i==0) then
set VOFFSET = GetHandleId(tT[i])
static if(USE_FLEXIBLE_OFFSET) then
set o=VOFFSET
else
set o=OFFSET
endif
endif
if (GetHandleId(tT[i])-o>=ARRAY_SIZE) then
exitwhen true
endif
if (GetHandleId(tT[i])-o>=0) then
set i=i+1
endif
endloop
set tN = i
exitwhen(tN == QUANTITY)
set oops = true
exitwhen not USE_FLEXIBLE_OFFSET
debug call BJDebugMsg("TimerUtils_init: Failed a initialization attempt, will try again")
endloop
if(oops) then
static if ( USE_FLEXIBLE_OFFSET) then
debug call BJDebugMsg("The problem has been fixed.")
//If this message doesn't appear then there is so much
//handle id fragmentation that it was impossible to preload
//so many timers and the thread crashed! Therefore this
//debug message is useful.
elseif(DEBUG_MODE) then
call BJDebugMsg("There were problems and the new timer limit is "+I2S(i))
call BJDebugMsg("This is a rare ocurrence, if the timer limit is too low:")
call BJDebugMsg("a) Change USE_FLEXIBLE_OFFSET to true (reduces performance a little)")
call BJDebugMsg("b) or try changing OFFSET to "+I2S(VOFFSET) )
endif
endif
endif
endfunction
endlibrary
//TESH.scrollpos=12
//TESH.alwaysfold=0
library TerrainPathability initializer Init
//******************************************************************************
//* BY: Rising_Dusk
//*
//* This script can be used to detect the type of pathing at a specific point.
//* It is valuable to do it this way because the IsTerrainPathable is very
//* counterintuitive and returns in odd ways and aren't always as you would
//* expect. This library, however, facilitates detecting those things reliably
//* and easily.
//*
//******************************************************************************
//*
//* > function IsTerrainDeepWater takes real x, real y returns boolean
//* > function IsTerrainShallowWater takes real x, real y returns boolean
//* > function IsTerrainLand takes real x, real y returns boolean
//* > function IsTerrainPlatform takes real x, real y returns boolean
//* > function IsTerrainWalkable takes real x, real y returns boolean
//*
//* These functions return true if the given point is of the type specified
//* in the function's name and false if it is not. For the IsTerrainWalkable
//* function, the MAX_RANGE constant below is the maximum deviation range from
//* the supplied coordinates that will still return true.
//*
//* The IsTerrainPlatform works for any preplaced walkable destructable. It will
//* return true over bridges, destructable ramps, elevators, and invisible
//* platforms. Walkable destructables created at runtime do not create the same
//* pathing hole as preplaced ones do, so this will return false for them. All
//* other functions except IsTerrainWalkable return false for platforms, because
//* the platform itself erases their pathing when the map is saved.
//*
//* After calling IsTerrainWalkable(x, y), the following two global variables
//* gain meaning. They return the X and Y coordinates of the nearest walkable
//* point to the specified coordinates. These will only deviate from the
//* IsTerrainWalkable function arguments if the function returned false.
//*
//* Variables that can be used from the library:
//* [real] TerrainPathability_X
//* [real] TerrainPathability_Y
//*
globals
private constant real MAX_RANGE = 10.
private constant integer DUMMY_ITEM_ID = 'wolg'
endglobals
globals
private item Item = null
private rect Find = null
private item array Hid
private integer HidMax = 0
public real X = 0.
public real Y = 0.
endglobals
function IsTerrainDeepWater takes real x, real y returns boolean
return not IsTerrainPathable(x, y, PATHING_TYPE_FLOATABILITY) and IsTerrainPathable(x, y, PATHING_TYPE_WALKABILITY)
endfunction
function IsTerrainShallowWater takes real x, real y returns boolean
return not IsTerrainPathable(x, y, PATHING_TYPE_FLOATABILITY) and not IsTerrainPathable(x, y, PATHING_TYPE_WALKABILITY) and IsTerrainPathable(x, y, PATHING_TYPE_BUILDABILITY)
endfunction
function IsTerrainLand takes real x, real y returns boolean
return IsTerrainPathable(x, y, PATHING_TYPE_FLOATABILITY)
endfunction
function IsTerrainPlatform takes real x, real y returns boolean
return not IsTerrainPathable(x, y, PATHING_TYPE_FLOATABILITY) and not IsTerrainPathable(x, y, PATHING_TYPE_WALKABILITY) and not IsTerrainPathable(x, y, PATHING_TYPE_BUILDABILITY)
endfunction
private function HideItem takes nothing returns nothing
if IsItemVisible(GetEnumItem()) then
set Hid[HidMax] = GetEnumItem()
call SetItemVisible(Hid[HidMax], false)
set HidMax = HidMax + 1
endif
endfunction
function IsTerrainWalkable takes real x, real y returns boolean
//Hide any items in the area to avoid conflicts with our item
call MoveRectTo(Find, x, y)
call EnumItemsInRect(Find ,null, function HideItem)
//Try to move the test item and get its coords
call SetItemPosition(Item, x, y) //Unhides the item
set X = GetItemX(Item)
set Y = GetItemY(Item)
static if LIBRARY_IsTerrainWalkable then
//This is for compatibility with the IsTerrainWalkable library
set IsTerrainWalkable_X = X
set IsTerrainWalkable_Y = Y
endif
call SetItemVisible(Item, false)//Hide it again
//Unhide any items hidden at the start
loop
exitwhen HidMax <= 0
set HidMax = HidMax - 1
call SetItemVisible(Hid[HidMax], true)
set Hid[HidMax] = null
endloop
//Return walkability
return (X-x)*(X-x)+(Y-y)*(Y-y) <= MAX_RANGE*MAX_RANGE and not IsTerrainPathable(x, y, PATHING_TYPE_WALKABILITY)
endfunction
private function Init takes nothing returns nothing
set Find = Rect(0., 0., 128., 128.)
set Item = CreateItem(DUMMY_ITEM_ID, 0, 0)
call SetItemVisible(Item, false)
endfunction
endlibrary