//TESH.scrollpos=-1
//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
Name | Type | is_array | initial_value |
Arthas | unit | Yes | |
GDD__Integers | integer | Yes | |
GDD__LeftMapGroup | group | No | |
GDD__TriggerArray | trigger | Yes | |
GDD__UnitArray | unit | Yes | |
GDD_Damage | real | No | |
GDD_DamagedUnit | unit | No | |
GDD_DamageSource | unit | No | |
GDD_Event | real | No | |
Loc | location | No | |
Proudmoore | unit | No | |
Rush_Hash | hashtable | No |
//TESH.scrollpos=-1
//TESH.alwaysfold=0
//This part is where you can configure the spell.
globals
//Configurables
integer Rush = 'A000' //This is the ability id the spell uses. Change it after copying the trigger.
real Speed = 1150 //This is the speed of the caster when he uses the spell.
real Interval = .03 //How often you want the timer to run.
real Range = 175 //How close units should be to be damaged by the spell.
boolean UnitOrSfx = false //This decides if you want to use a dummy unit or sfx as the well...sfx of the spell. False for sfx, true for dummy unit.
integer Transparency = 80 //How transparent you want the dummy to be.
integer RushDummy = 'e001' // This is the id of the dummy unit created.
real Lifespan = 0.5 // Life span of the dummy.
string Sfx = "Abilities\\Spells\\Orc\\MirrorImage\\MirrorImageCaster.mdl" //The sfx that appears when the caster dashes.
string Animation = "stand ready" //What animation plays when the caster dashes.
real DamageBase = 75 //This is the base damage which will be multiplied by the ability level.
integer R = 255 //This is the red value for the dummy unit.
integer G = 255 //This is the blue value for the dummy unit.
integer B = 255 //This is the green value for the dummy unit.
real AbilDist = 200 //This is the distance that scales per level of ability. Set BonusDist to 0 if you want a pure level based distance.
real BonusDist = 200 // This is the bonus distance. Set AbilDist to 0 if you want constant distant.
attacktype AttackType = ATTACK_TYPE_CHAOS //The attack type.
damagetype DamageType = DAMAGE_TYPE_UNIVERSAL //The damage type. They're both currently set to these configurations to allow easy testing.
//Non-configurables
group RushGroup = CreateGroup() //Don't touch this. Don't null or destroy this.
real Offset = Speed * Interval //How far the caster moves per interval.
endglobals
//------Configurable functions------//
//Configure the conditions for group check here
function UnitCheck takes unit caster, unit u returns boolean
return IsUnitEnemy(u, GetOwningPlayer(caster)) and not (IsUnitType(u, UNIT_TYPE_DEAD) and GetUnitTypeId(u) != 0) and IsUnitType(u, UNIT_TYPE_GROUND) and not IsUnitType(u, UNIT_TYPE_STRUCTURE)
endfunction
//The Damage formula for the ability.
function Damage takes unit caster, integer Rush, real BaseDamage returns real
return GetUnitAbilityLevel(caster, Rush) * BaseDamage
endfunction
//Formula for the maximum distance. If you only want the ability to affect distance, set BonusDist to 0.
function MaximumDistance takes unit caster, integer Rush, real AbilDist, real BonusDist returns real
return GetUnitAbilityLevel(caster, Rush) * AbilDist + BonusDist
endfunction
//---End of Configurable functions---//
function Rush_Periodic takes nothing returns nothing
//Local Variable Setup
local timer t = GetExpiredTimer()
local integer id = GetHandleId(t)
local unit caster = LoadUnitHandle(udg_Rush_Hash, id, 0)
local real facing = LoadReal(udg_Rush_Hash, id, 1)
local real cur_dist = LoadReal(udg_Rush_Hash, id, 2)
local group g = LoadGroupHandle(udg_Rush_Hash, id, 3)
local real x = GetUnitX(caster)
local real y = GetUnitY(caster)
local real x1 = x + Offset * Cos(facing)
local real y1 = y + Offset * Sin(facing)
local player owner = GetOwningPlayer(caster)
local unit dummy
local unit u
local real damage = Damage(caster, Rush, DamageBase) //You can change this to whatever you want.
local real MaxDistance = MaximumDistance(caster, Rush, AbilDist, BonusDist) //This makes the distance you dash scale with the level.
if cur_dist < MaxDistance then
call SetUnitAnimation(caster, Animation)
if not UnitOrSfx then
call DestroyEffect(AddSpecialEffect(Sfx, x, y))
else
set dummy = CreateUnit(owner, RushDummy, x, y, facing)
call SetUnitAnimation(dummy, Animation)
call SetUnitVertexColor(dummy, R, G, B, Transparency)
call UnitApplyTimedLife(dummy, 'BTLF', Lifespan)
set dummy = null
endif
call GroupEnumUnitsInRange(RushGroup, x, y, Range, null)
loop //What we do here is check if the target is in g. If not, then we damage it and add it to g to prevent it from being damaged again.
set u = FirstOfGroup(RushGroup)
exitwhen u == null
if not IsUnitInGroup(u, g) and UnitCheck(caster, u) then
call UnitDamageTarget(caster, u, damage, false, false, AttackType, DamageType, null)
call GroupAddUnit(g, u)
endif
call GroupRemoveUnit(RushGroup, u)
endloop
if IsTerrainWalkable(x1, y1) then //If the terrain is passable the you'll dash, if not then trigger ends.
call SetUnitX(caster, x1)
call SetUnitY(caster, y1)
set cur_dist = cur_dist + Offset //This counts how many times you've moved.
call SaveReal(udg_Rush_Hash, id, 2, cur_dist)
else
call PauseTimer(t)
call DestroyTimer(t)
call DestroyGroup(g)
call SetUnitAnimation(caster, "stand") //Resets the animation.
call FlushChildHashtable(udg_Rush_Hash, id)
endif
else
call PauseTimer(t)
call DestroyTimer(t)
call DestroyGroup(g)
call SetUnitAnimation(caster, "stand") //Resets the animation.
call FlushChildHashtable(udg_Rush_Hash, id)
endif
//Nulling
set t = null
set caster = null
set g = null
endfunction
function Rush_Actions takes nothing returns nothing
//Local Variable Setup
local timer t = CreateTimer()
local integer id = GetHandleId(t)
local unit caster = GetTriggerUnit()
local real dx = GetSpellTargetX() - GetUnitX(caster)
local real dy = GetSpellTargetY() - GetUnitY(caster)
local real dist_check = (dx*dx) + (dy*dy)
local real facing
//A little distance check to avoid bugs when you cast the spell in your current position.
if dist_check <= 100 * 100 then
set facing = GetUnitFacing(caster) * bj_DEGTORAD
else
set facing = (Atan2(dy, dx))
endif
//Hashtable Setup
call SaveUnitHandle(udg_Rush_Hash, id, 0, caster)
call SaveReal(udg_Rush_Hash, id, 1, facing)
call SaveReal(udg_Rush_Hash, id, 2, 0)
call SaveGroupHandle(udg_Rush_Hash, id, 3, CreateGroup())
//End Hashtable Setup
call TimerStart(t, Interval, true, function Rush_Periodic)
//Nulling
set t = null
set caster = null
endfunction
function Rush_Conditions takes nothing returns boolean
if GetSpellAbilityId() == Rush then
call Rush_Actions()
endif
return false
endfunction
//===========================================================================
function InitTrig_Rush takes nothing returns nothing
local trigger t = CreateTrigger( )
call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_SPELL_EFFECT )
call TriggerAddCondition( t, Condition( function Rush_Conditions) )
set udg_Rush_Hash = InitHashtable()
set t = null
endfunction
//TESH.scrollpos=0
//TESH.alwaysfold=0
// GUI-Friendly Damage Detection -- v1.2.1 -- by Weep
// http:// www.thehelper.net/forums/showthread.php?t=137957
//
// Requires: only this trigger and its variables.
//
// -- What? --
// This snippet provides a leak-free, GUI-friendly implementation of an "any unit takes
// damage" event. It requires no JASS knowledge to use.
//
// It uses the Game - Value Of Real Variable event as its method of activating other
// triggers, and passes the event responses through a few globals.
//
// -- Why? --
// The traditional GUI method of setting up a trigger than runs when any unit is damaged
// leaks trigger events. This snippet is easy to implement and removes the need to do
// you own GUI damage detection setup.
//
// -- How To Implement --
// 0. Before you copy triggers that use GDD into a new map, you need to copy over GDD
// with its GDD Variable Creator trigger, or there will be a problem: the variables
// won't be automatically created correctly.
//
// 1. Be sure "Automatically create unknown variables while pasting trigger data" is
// enabled in the World Editor general preferences.
// 2. Copy this trigger category ("GDD") and paste it into your map.
// (Alternately: create the variables listed in the globals block below, create a
// trigger named "GUI Friendly Damage Detection", and paste in this entire text.)
// 3. Create your damage triggers using Game - Value Of Real Variable as the event,
// select GDD_Event as the variable, and leave the rest of the settings to the default
// "becomes Equal to 0.00".
// The event responses are the following variables:
// GDD_Damage is the amount of damage, replacing Event Response - Damage Taken.
// GDD_DamagedUnit is the damaged unit, replacing Event Response - Triggering Unit.
// Triggering Unit can still be used, if you need to use waits.
// Read the -- Notes -- section below for more info.
// GDD_DamageSource is the damaging unit, replacing Event Response - Damage Source.
//
// -- Notes --
// GDD's event response variables are not wait-safe; you can't use them after a wait in
// a trigger. If you need to use waits, Triggering Unit (a.k.a. GetTriggerUnit()) can
// be used in place of GDD_DamageSource. There is no usable wait-safe equivalent to
// Event Damage or Damage Source; you'll need to save the values yourself.
//
// Don't write any values to the variables used as the event responses, or it will mess
// up any other triggers using this snippet for their triggering. Only use their values.
//
// This uses arrays, so can detect damage for a maximum of 8190 units at a time, and
// cleans up data at a rate of 33.33 per second, by default. This should be enough for
// most maps, but if you want to change the rate, change the value returned in the
// GDD_RecycleRate function at the top of the code, below.
//
// By default, GDD will not register units that have Locust at the moment of their
// entering the game, and will not recognize when they take damage (which can only
// happen if the Locust ability is later removed from the unit.) To allow a unit to have
// Locust yet still cause GDD damage events if Locust is removed, you can either design
// the unit to not have Locust by default and add it via triggers after creation, or
// edit the GDD_Filter function at the top of the code, below.
//
// -- Credits --
// Captain Griffin on wc3c.net for the research and concept of GroupRefresh.
//
// Credit in your map not needed, but please include this README.
//
// -- Version History --
// 1.2.1: Minor code cleaning. Added configuration functions. Updated documentation.
// 1.2.0: Made this snippet work properly with recursive damage.
// 1.1.1: Added a check in order to not index units with the Locust ability (dummy units).
// If you wish to check for damage taken by a unit that is unselectable, do not
// give the unit-type Locust in the object editor; instead, add the Locust ability
// 'Aloc' via a trigger after its creation, then remove it.
// 1.1.0: Added a check in case a unit gets moved out of the map and back.
// 1.0.0: First release.
//===================================================================
// Configurables.
function GDD_RecycleRate takes nothing returns real //The rate at which the system checks units to see if they've been removed from the game
return 0.03
endfunction
function GDD_Filter takes unit u returns boolean //The condition a unit has to pass to have it registered for damage detection
return GetUnitAbilityLevel(u, 'Aloc') == 0 //By default, the system ignores Locust units, because they normally can't take damage anyway
endfunction
//===================================================================
// This is just for reference.
// If you use JassHelper, you could uncomment this section instead of creating the variables in the trigger editor.
// globals
// real udg_GDD_Event = 0.
// real udg_GDD_Damage = 0.
// unit udg_GDD_DamagedUnit
// unit udg_GDD_DamageSource
// trigger array udg_GDD__TriggerArray
// integer array udg_GDD__Integers
// unit array udg_GDD__UnitArray
// group udg_GDD__LeftMapGroup = CreateGroup()
// endglobals
//===================================================================
// System code follows. Don't touch!
function GDD_Event takes nothing returns boolean
local unit damagedcache = udg_GDD_DamagedUnit
local unit damagingcache = udg_GDD_DamageSource
local real damagecache = udg_GDD_Damage
set udg_GDD_DamagedUnit = GetTriggerUnit()
set udg_GDD_DamageSource = GetEventDamageSource()
set udg_GDD_Damage = GetEventDamage()
set udg_GDD_Event = 1.
set udg_GDD_Event = 0.
set udg_GDD_DamagedUnit = damagedcache
set udg_GDD_DamageSource = damagingcache
set udg_GDD_Damage = damagecache
set damagedcache = null
set damagingcache = null
return false
endfunction
function GDD_AddDetection takes nothing returns boolean
// if(udg_GDD__Integers[0] > 8190) then
// call BJDebugMsg("GDD: Too many damage events! Decrease number of units present in the map or increase recycle rate.")
// ***Recycle rate is specified in the GDD_RecycleRate function at the top of the code. Smaller is faster.***
// return
// endif
if(IsUnitInGroup(GetFilterUnit(), udg_GDD__LeftMapGroup)) then
call GroupRemoveUnit(udg_GDD__LeftMapGroup, GetFilterUnit())
elseif(GDD_Filter(GetFilterUnit())) then
set udg_GDD__Integers[0] = udg_GDD__Integers[0]+1
set udg_GDD__UnitArray[udg_GDD__Integers[0]] = GetFilterUnit()
set udg_GDD__TriggerArray[udg_GDD__Integers[0]] = CreateTrigger()
call TriggerRegisterUnitEvent(udg_GDD__TriggerArray[udg_GDD__Integers[0]], udg_GDD__UnitArray[udg_GDD__Integers[0]], EVENT_UNIT_DAMAGED)
call TriggerAddCondition(udg_GDD__TriggerArray[udg_GDD__Integers[0]], Condition(function GDD_Event))
endif
return false
endfunction
function GDD_PreplacedDetection takes nothing returns nothing
local group g = CreateGroup()
local integer i = 0
loop
call GroupEnumUnitsOfPlayer(g, Player(i), Condition(function GDD_AddDetection))
set i = i+1
exitwhen i == bj_MAX_PLAYER_SLOTS
endloop
call DestroyGroup(g)
set g = null
endfunction
function GDD_GroupRefresh takes nothing returns nothing
// Based on GroupRefresh by Captain Griffen on wc3c.net
if (bj_slotControlUsed[5063] == true) then
call GroupClear(udg_GDD__LeftMapGroup)
set bj_slotControlUsed[5063] = false
endif
call GroupAddUnit(udg_GDD__LeftMapGroup, GetEnumUnit())
endfunction
function GDD_Recycle takes nothing returns nothing
if(udg_GDD__Integers[0] <= 0) then
return
elseif(udg_GDD__Integers[1] <= 0) then
set udg_GDD__Integers[1] = udg_GDD__Integers[0]
endif
if(GetUnitTypeId(udg_GDD__UnitArray[udg_GDD__Integers[1]]) == 0) then
call DestroyTrigger(udg_GDD__TriggerArray[udg_GDD__Integers[1]])
set udg_GDD__TriggerArray[udg_GDD__Integers[1]] = null
set udg_GDD__TriggerArray[udg_GDD__Integers[1]] = udg_GDD__TriggerArray[udg_GDD__Integers[0]]
set udg_GDD__UnitArray[udg_GDD__Integers[1]] = udg_GDD__UnitArray[udg_GDD__Integers[0]]
set udg_GDD__UnitArray[udg_GDD__Integers[0]] = null
set udg_GDD__Integers[0] = udg_GDD__Integers[0]-1
endif
set udg_GDD__Integers[1] = udg_GDD__Integers[1]-1
endfunction
function GDD_LeaveMap takes nothing returns boolean
local boolean cached = bj_slotControlUsed[5063]
if(udg_GDD__Integers[2] < 64) then
set udg_GDD__Integers[2] = udg_GDD__Integers[2]+1
else
set bj_slotControlUsed[5063] = true
call ForGroup(udg_GDD__LeftMapGroup, function GDD_GroupRefresh)
set udg_GDD__Integers[2] = 0
endif
call GroupAddUnit(udg_GDD__LeftMapGroup, GetFilterUnit())
set bj_slotControlUsed[5063] = cached
return false
endfunction
// ===========================================================================
function InitTrig_GUI_Friendly_Damage_Detection takes nothing returns nothing
local region r = CreateRegion()
call RegionAddRect(r, GetWorldBounds())
call TriggerRegisterEnterRegion(CreateTrigger(), r, Condition(function GDD_AddDetection))
call TriggerRegisterLeaveRegion(CreateTrigger(), r, Condition(function GDD_LeaveMap))
call GDD_PreplacedDetection()
call TimerStart(CreateTimer(), GDD_RecycleRate(), true, function GDD_Recycle)
set r = null
endfunction