Name | Type | is_array | initial_value |
//TESH.scrollpos=36
//TESH.alwaysfold=0
library_once TimerUtils initializer redInit
//*********************************************************************
//* TimerUtils (Red flavor)
//* ----------
//*
//* 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.wc3campaigns.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.
//*
//* Red flavor: Fastest, method in existence for timer attaching,
//* only takes an array lookup, H2I and subtraction.
//* However, all the code in your map requires extra care
//* not to forget to call ReleaseTimer. It also requires
//* to preload a lot of timers at map init, they use
//* memory and handle ids.
//*
//* I recommend you run your map in debug mode the first
//* time after adding it, make sure you can see map init messages
//* if nothing appears, all is done, if an error appears, it might
//* suggest you a value with OFFSET, in that case, update that value
//* if it still does not work after updating (rare), try a bigger
//* OFFSET by 1000 for example. (Sounds hard? Then use blue or purple
//* timerutils that are friendlier though not as fast)
//*
//********************************************************************
//================================================================
globals
private constant integer OFFSET = 0x100000
private constant integer QUANTITY = 256
private constant integer ARRAY_SIZE = 8191 //changing this to a higher value would effectively
//cripple the performance making this thing irrelevant
endglobals
//=================================================================================================
private function H2I takes handle h returns integer
return GetHandleId( h )
endfunction
//==================================================================================================
globals
private integer array data[ARRAY_SIZE]
endglobals
//It is dependent on jasshelper's recent inlining optimization in order to perform correctly.
function SetTimerData takes timer t, integer value returns nothing
debug if(H2I(t)-OFFSET<0) then
debug call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
debug endif
set data[H2I(t)-OFFSET]=value
endfunction
function GetTimerData takes timer t returns integer
debug if(H2I(t)-OFFSET<0) then
debug call BJDebugMsg("GetTimerData: Wrong handle id, only use GetTimerData on timers created by NewTimer")
debug endif
return data[H2I(t)-OFFSET]
endfunction
//==========================================================================================
globals
private timer array tT
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, please increase it for your map, fix your map's timer leaks or switch to blue flavor when applicable")
set tT[0]=CreateTimer()
if (H2I(tT[0])-OFFSET<0) or (H2I(tT[0])-OFFSET>=ARRAY_SIZE) then
//all right, couldn't fix it
call BJDebugMsg("NewTimer: Unable to allocate a timer, you should probably switch to the blue flavor or fix timer leaks.")
return null
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==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(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 redInit takes nothing returns nothing
local integer i=0
local integer bestoffset=-1
loop
exitwhen (i==QUANTITY)
set tT[i] = CreateTimer()
if(i==0) then
set bestoffset=H2I(tT[i])
endif
if (H2I(tT[i])-OFFSET>=ARRAY_SIZE) then
debug call BJDebugMsg("TimerUtils_redInit: Failed a initializing attempt")
debug call BJDebugMsg("The timer limit is "+I2S(i))
debug call BJDebugMsg("This is a rare ocurrence, if the timer limit is too low, to change OFFSET to "+I2S(bestoffset) )
exitwhen true
endif
if (H2I(tT[i])-OFFSET>=0) then
set i=i+1
endif
endloop
set tN=i
endfunction
endlibrary
//TESH.scrollpos=36
//TESH.alwaysfold=0
library IsTerrainWalkable initializer Init
//*****************************************************************
//* IsTerrainWalkable
//*
//* rewritten in vJass by: Anitarf
//* original implementation: Vexorian
//*
//* A function for checking if a point is pathable for ground
//* units (it does so by attempting to move an item there and
//* checking where it ended up), typically used to stop sliding
//* units before they end up stuck in trees. If the point is not
//* pathable, the function will also determine the nearest point
//* that is (the point where the item ends up).
//*****************************************************************
globals
// this value is how far from a point the item may end up for the point to be considered pathable
private constant real MAX_RANGE = 10.0
// the following two variables are set to the position of the item after each pathing check
// that way, if a point isn't pathable, these will be the coordinates of the nearest point that is
public real X = 0.0
public real Y = 0.0
// END OF CALIBRATION SECTION
// ================================================================
private rect r
private item check
private item array hidden
private integer hiddenMax = 0
endglobals
private function Init takes nothing returns nothing
set check = CreateItem('ciri',0,0)
call SetItemVisible(check,false)
set r = Rect(0.0,0.0,128.0,128.0)
endfunction
private function HideBothersomeItem takes nothing returns nothing
if IsItemVisible(GetEnumItem()) then
set hidden[hiddenMax]=GetEnumItem()
call SetItemVisible(hidden[hiddenMax],false)
set hiddenMax=hiddenMax+1
endif
endfunction
// ================================================================
function IsTerrainWalkable takes real x, real y returns boolean
// first, hide any items in the area so they don't get in the way of our item
call MoveRectTo(r, x,y)
call EnumItemsInRect(r,null,function HideBothersomeItem)
// try to move the check item and get it's coordinates
call SetItemPosition(check,x,y)//this unhides the item...
set X = GetItemX(check)
set Y = GetItemY(check)
call SetItemVisible(check,false)//...so we must hide it again
// before returning, unhide any items that got hidden at the start
loop
exitwhen hiddenMax<=0
set hiddenMax=hiddenMax-1
call SetItemVisible(hidden[hiddenMax],true)
set hidden[hiddenMax]=null
endloop
// return pathability status
return (x-X)*(x-X)+(y-Y)*(y-Y) < MAX_RANGE*MAX_RANGE
endfunction
endlibrary
//TESH.scrollpos=44
//TESH.alwaysfold=0
library BoundSentinel initializer init
//*************************************************
//* BoundSentinel
//* -------------
//* Don't leave your units unsupervised, naughty
//* them may try to get out of the map bounds and
//* crash your game.
//*
//* To implement, just get a vJass compiler and
//* copy this library/trigger to your map.
//*
//*************************************************
//==================================================
globals
// High enough so the unit is no longer visible, low enough so the
// game doesn't crash...
//
// I think you need 0.0 or soemthing negative prior to patch 1.22
//
private constant real EXTRA = 500.0
endglobals
//=========================================================================================
globals
private real maxx
private real maxy
private real minx
private real miny
endglobals
//=======================================================================
private function dis takes nothing returns nothing
local unit u=GetTriggerUnit()
local real x=GetUnitX(u)
local real y=GetUnitY(u)
if(x>maxx) then
set x=maxx
elseif(x<minx) then
set x=minx
endif
if(y>maxy) then
set y=maxy
elseif(y<miny) then
set y=miny
endif
call SetUnitX(u,x)
call SetUnitY(u,y)
set u=null
endfunction
private function init takes nothing returns nothing
local trigger t=CreateTrigger()
local region r=CreateRegion()
local rect rc
set minx=GetCameraBoundMinX() - EXTRA
set miny=GetCameraBoundMinY() - EXTRA
set maxx=GetCameraBoundMaxX() + EXTRA
set maxy=GetCameraBoundMaxY() + EXTRA
set rc=Rect(minx,miny,maxx,maxy)
call RegionAddRect(r, rc)
call RemoveRect(rc)
call TriggerRegisterLeaveRegion(t,r, null)
call TriggerAddAction(t, function dis)
//this is not necessary but I'll do it anyway:
set t=null
set r=null
set rc=null
endfunction
endlibrary
//TESH.scrollpos=150
//TESH.alwaysfold=0
scope HolyBarrage initializer init
//*************************************************************************************************************//
// Holy Barrage //
// by //
// cedi //
// //
// needs: TimerUtils by Vexorian //
// Bound Sentinel by Vexorian //
// IsTerrainWalkable by Antitarf //
// Dummy Model by //
//*************************************************************************************************************//
//For use, copy the trigger to your map, copy the dummy create a spell and adjust the values below.
globals
//ID of the dummy spell
private constant integer SPELL_ID = 'A000'
//ID of the dummy
private constant integer DUMMY_ID = 'h000'
//How many levels does your spell have?
private constant integer SPELL_LEVEL_COUNT = 4
//The move timer interval
private constant real TIMER_INTERVAL = 0.02
//The spawn interval ( for the missiles )
private constant real SPAWN_INTERVAL = 0.2
//Should the spell create the first missile with a delay?
private constant real FIRST_SPAWN_DELAY = 0.00
//The spawn isn't always the same. This value is the max difference
private constant real MAX_SPAWNINT_DIF = 0.05
//Each x seconds the system picks all units around ( higher numbers are better for the performance )
private constant real PICK_INTERVAL = 0.10
//How fast should the missiles be? In wc3 units
private constant real SPEED = 700.00
//The distance to the caster on create. BEWARE!! Must be bigger then the COLLISION_SIZE!!
private constant real START_DISTANCE = 105.00
//The collisions size of the missiles.
private constant real COLLISION_SIZE = 75.00
//Max drift to the sides.
private constant real MAX_DRIFT_PERSEC = 550.00
//Size of the missile. 1 == 100%. 1.5 == 150%
private constant real SIZE = 2.00
//Height of the missiles.
private constant real FLYHEIGHT = 50.00
//Model of the missiles,
private constant string DUMMY_MODEL = "Abilities\\Weapons\\ProcMissile\\ProcMissile.mdl"
//Effect on collision.
private constant string HIT_MODEL = "Abilities\\Spells\\Other\\Incinerate\\FireLordDeathExplode.mdl"
//Effect when a unit takes damage because this spell.
private constant string DAMAGE_MODEL = "Abilities\\Spells\\Demon\\DarkPortal\\DarkPortalTarget.mdl"
//Effect when a unit gets healed by this spell
private constant string HEAL_MODEL = "Abilities\\Spells\\Human\\Heal\\HealTarget.mdl"
private integer array BARRAGE_COUNT[SPELL_LEVEL_COUNT] //SYSTEM
private real array BARRAGE_DAMAGE[SPELL_LEVEL_COUNT] //SYSTEM
private real array BARRAGE_HEAL[SPELL_LEVEL_COUNT] //SYSTEM
private real array BARRAGE_RANGE[SPELL_LEVEL_COUNT] //SYSTEM
private real array BARRAGE_AOE[SPELL_LEVEL_COUNT] //SYSTEM
private real REAL_MOVE = SPEED * TIMER_INTERVAL //SYSTEM
private real REAL_DRIFT = MAX_DRIFT_PERSEC * TIMER_INTERVAL //SYSTEM
private group TEMPGROUP = CreateGroup()//SYSTEM
endglobals
//When you want mor then 4 level c'n'p one line and adjust the numbers
//These functions sets the missiles amount for each level.
private function SET_BARRAGE_COUNT takes nothing returns nothing
set BARRAGE_COUNT[1] = 5
set BARRAGE_COUNT[2] = 7
set BARRAGE_COUNT[3] = 9
set BARRAGE_COUNT[4] = 11
endfunction
//These functions sets the missiles damage for each level.
private function SET_BARRAGE_DAMAGE takes nothing returns nothing
set BARRAGE_DAMAGE[1] = 20.00
set BARRAGE_DAMAGE[2] = 25.00
set BARRAGE_DAMAGE[3] = 30.00
set BARRAGE_DAMAGE[4] = 35.00
endfunction
//These functions sets the missiles heal amount for each level.
private function SET_BARRAGE_HEAL takes nothing returns nothing
set BARRAGE_HEAL[1] = 20.00
set BARRAGE_HEAL[2] = 25.00
set BARRAGE_HEAL[3] = 30.00
set BARRAGE_HEAL[4] = 35.00
endfunction
//These functions sets the missiles range for each level.
private function SET_BARRAGE_RANGE takes nothing returns nothing
set BARRAGE_RANGE[1] = 2000.00
set BARRAGE_RANGE[2] = 2000.00
set BARRAGE_RANGE[3] = 2000.00
set BARRAGE_RANGE[4] = 2000.00
endfunction
//These functions sets the missiles aoe for each level.
private function SET_BARRAGE_AOE takes nothing returns nothing
set BARRAGE_AOE[1] = 150.00
set BARRAGE_AOE[2] = 160.00
set BARRAGE_AOE[3] = 170.00
set BARRAGE_AOE[4] = 180.00
endfunction
//*************************************************************************************************************//
// !SYSTEM! //
//*************************************************************************************************************//
private function IsAliveAndUnit takes nothing returns boolean
return GetUnitState( GetFilterUnit(), UNIT_STATE_LIFE ) > 0.405 and IsUnitType(GetFilterUnit(), UNIT_TYPE_STRUCTURE) == false
endfunction
private function IsAliveAndUnitAndNotMagicImmune takes nothing returns boolean
return GetUnitState( GetFilterUnit(), UNIT_STATE_LIFE ) > 0.405 and IsUnitType(GetFilterUnit(), UNIT_TYPE_STRUCTURE) == false and IsUnitType(GetFilterUnit(), UNIT_TYPE_MAGIC_IMMUNE) == false
endfunction
private function Move takes nothing returns nothing
local timer t = GetExpiredTimer()
local Missile data = GetTimerData( t )
set data.pick = data.pick + TIMER_INTERVAL
call data.MoveMissile()
if data.pick >= PICK_INTERVAL then
call data.Pick()
endif
set t = null
endfunction
struct Missile
unit missile
unit caster
real x
real y
real range
real pick = 0.00
integer level
effect sfx
timer time
private method HealDamage takes nothing returns nothing
local unit u
call GroupEnumUnitsInRange( TEMPGROUP, .x, .y, BARRAGE_AOE[.level], Condition( function IsAliveAndUnitAndNotMagicImmune ) )
call DestroyEffect( AddSpecialEffect( HIT_MODEL, .x, .y ) )
loop
set u = FirstOfGroup( TEMPGROUP )
exitwhen u == null
if IsUnitEnemy( u, GetOwningPlayer( .caster ) ) == true then
call DestroyEffect( AddSpecialEffectTarget( DAMAGE_MODEL, u, "origin" ) )
call UnitDamageTarget( .caster, u, BARRAGE_DAMAGE[.level], true, false, ATTACK_TYPE_MAGIC, DAMAGE_TYPE_NORMAL, WEAPON_TYPE_WHOKNOWS )
else
call DestroyEffect( AddSpecialEffectTarget( HEAL_MODEL, u, "origin" ) )
call SetUnitState( u, UNIT_STATE_LIFE, GetUnitState( u, UNIT_STATE_LIFE ) + BARRAGE_HEAL[.level] )
endif
call GroupRemoveUnit( TEMPGROUP, u )
endloop
endmethod
method MoveMissile takes nothing returns nothing
local real angle = GetUnitFacing( .caster )
local real r
set .range = .range - REAL_MOVE
if .range <= 0.00 then
call .destroy()
endif
set .x = .x + Cos( angle * bj_DEGTORAD ) * REAL_MOVE
set .y = .y + Sin( angle * bj_DEGTORAD ) * REAL_MOVE
call SetUnitFacing( .missile, angle )
set angle = ( angle + 90.00 ) * bj_DEGTORAD
set r = GetRandomReal( -REAL_DRIFT, REAL_DRIFT )
set .x = .x + Cos( angle ) * r
set .y = .y + Sin( angle ) * r
if IsTerrainWalkable( .x, .y ) == false then
call .destroy()
endif
call SetUnitX( .missile, .x )
call SetUnitY( .missile, .y )
endmethod
method Pick takes nothing returns nothing
local unit u
call GroupEnumUnitsInRange( TEMPGROUP, .x, .y, COLLISION_SIZE, Condition( function IsAliveAndUnit ) )
set u = FirstOfGroup( TEMPGROUP )
call GroupClear( TEMPGROUP )
if u != null then
call .HealDamage()
call .destroy()
endif
endmethod
method onDestroy takes nothing returns nothing
call PauseTimer( .time )
call ReleaseTimer( .time )
call DestroyEffect( .sfx )
call KillUnit( .missile )
endmethod
static method create takes unit caster, integer level returns Missile
local Missile data = Missile.allocate()
local real angle = GetUnitFacing( caster )
set data.time = NewTimer()
call SetTimerData( data.time, data )
set data.caster = caster
set data.x = GetUnitX( caster ) + Cos( angle * bj_DEGTORAD ) * START_DISTANCE
set data.y = GetUnitY( caster ) + Sin( angle * bj_DEGTORAD ) * START_DISTANCE
set data.level = level
set data.range = BARRAGE_RANGE[level]
set data.missile = CreateUnit( GetOwningPlayer( caster ), DUMMY_ID, data.x, data.y, angle )
call SetUnitFlyHeight( data.missile, FLYHEIGHT, 0.00 )
call SetUnitScale( data.missile, SIZE, SIZE, SIZE )
set data.sfx = AddSpecialEffectTarget( DUMMY_MODEL, data.missile, "origin" )
set caster = null
call TimerStart( data.time, TIMER_INTERVAL, true, function Move )
return data
endmethod
endstruct
struct SpawnController
unit caster
integer count
integer level
static method create takes unit caster returns SpawnController
local SpawnController data = SpawnController.allocate()
set data.caster = caster
set data.level = GetUnitAbilityLevel( caster, SPELL_ID )
set data.count = BARRAGE_COUNT[data.level]
set caster = null
return data
endmethod
endstruct
private function GetSpawnInterval takes nothing returns real
local integer i = GetRandomInt( 0, 1 )
local real r = GetRandomReal( 0.00, MAX_SPAWNINT_DIF )
if i == 0 then
return SPAWN_INTERVAL - r
else
return SPAWN_INTERVAL + r
endif
endfunction
private function Spawn takes nothing returns nothing
local timer t = GetExpiredTimer()
local SpawnController data = GetTimerData( t )
local Missile m
set data.count = data.count - 1
set m = Missile.create( data.caster, data.level )
if data.count <= 0 then
call data.destroy()
call ReleaseTimer( t )
else
call TimerStart( t, GetSpawnInterval(), false, function Spawn )
endif
set t = null
endfunction
private function Action takes nothing returns nothing
local unit u = GetTriggerUnit()
local timer t = NewTimer()
local integer lvl = GetUnitAbilityLevel( u, SPELL_ID )
local SpawnController data = SpawnController.create( u )
call SetTimerData( t, data )
call TimerStart( t, FIRST_SPAWN_DELAY, false, function Spawn )
set t = null
set u = null
endfunction
private function IsSpell takes nothing returns boolean
return GetSpellAbilityId() == SPELL_ID
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 IsSpell ) )
call TriggerAddAction( t, function Action )
call SET_BARRAGE_COUNT()
call SET_BARRAGE_DAMAGE()
call SET_BARRAGE_HEAL()
call SET_BARRAGE_RANGE()
call SET_BARRAGE_AOE()
set t = null
endfunction
endscope
//TESH.scrollpos=0
//TESH.alwaysfold=0
function Trig_Revive_Units_Conditions takes nothing returns boolean
return IsUnitType(GetTriggerUnit(), UNIT_TYPE_HERO) == false
endfunction
function Trig_Revive_Units_Actions takes nothing returns nothing
call CreateUnit( GetOwningPlayer( GetTriggerUnit() ), GetUnitTypeId( GetTriggerUnit() ), GetUnitX( GetTriggerUnit() ), GetUnitY( GetTriggerUnit() ), GetUnitFacing( GetTriggerUnit() ) )
endfunction
//===========================================================================
function InitTrig_Revive_Units takes nothing returns nothing
set gg_trg_Revive_Units = CreateTrigger( )
call TriggerRegisterAnyUnitEventBJ( gg_trg_Revive_Units, EVENT_PLAYER_UNIT_DEATH )
call TriggerAddCondition( gg_trg_Revive_Units, Condition( function Trig_Revive_Units_Conditions ) )
call TriggerAddAction( gg_trg_Revive_Units, function Trig_Revive_Units_Actions )
endfunction