Name | Type | is_array | initial_value |
Harvester | unit | No | |
Harvesterloc | location | No | |
Ka_Ability | abilcode | No | |
Ka_AbilityLvl | integer | Yes | |
Ka_Angle | real | Yes | |
Ka_Caster | unit | Yes | |
Ka_Count | real | Yes | |
Ka_Damage | real | Yes | |
Ka_DestroyTrees | boolean | No | |
Ka_Dummy | unit | Yes | |
Ka_DummyTimer | real | No | |
Ka_DummyUnit | unitcode | No | |
Ka_Index | integer | No | |
Ka_IndexX | integer | Yes | |
Ka_Loop | integer | No | |
Ka_MaxRange | real | Yes | |
Ka_MaxSize | integer | No | |
Ka_Offset | real | No | |
Ka_Radius | real | No | |
Ka_Range | real | Yes | |
Ka_Size | integer | No | |
Ka_Speed | real | Yes | |
Ka_UnitDamageGroup | group | Yes | |
Ka_UnitGroup | group | Yes | |
TempInt | integer | No | |
Tempoint1 | location | No | |
Tempoint2 | location | No | |
Tempoint3 | location | No |
//TESH.scrollpos=0
//TESH.alwaysfold=0
library TimerUtils initializer init
//*********************************************************************
//* TimerUtils (red+blue+orange flavors for 1.24b+) 2.0
//* ----------
//*
//* To implement it , create a custom text trigger called TimerUtils
//* and paste the contents of this script there.
//*
//* To copy from a map to another, copy the trigger holding this
//* library to your map.
//*
//* (requires vJass) More scripts: htt://www.wc3c.net
//*
//* For your timer needs:
//* * Attaching
//* * Recycling (with double-free protection)
//*
//* set t=NewTimer() : Get a timer (alternative to CreateTimer)
//* set t=NewTimerEx(x) : Get a timer (alternative to CreateTimer), call
//* Initialize timer data as x, instead of 0.
//*
//* ReleaseTimer(t) : Relese a timer (alt to DestroyTimer)
//* SetTimerData(t,2) : Attach value 2 to timer
//* GetTimerData(t) : Get the timer's value.
//* You can assume a timer's value is 0
//* after NewTimer.
//*
//* Multi-flavor:
//* Set USE_HASH_TABLE to true if you don't want to complicate your life.
//*
//* If you like speed and giberish try learning about the other flavors.
//*
//********************************************************************
//================================================================
globals
//How to tweak timer utils:
// USE_HASH_TABLE = true (new blue)
// * SAFEST
// * SLOWEST (though hash tables are kind of fast)
//
// USE_HASH_TABLE = false, USE_FLEXIBLE_OFFSET = true (orange)
// * kinda safe (except there is a limit in the number of timers)
// * ALMOST FAST
//
// USE_HASH_TABLE = false, USE_FLEXIBLE_OFFSET = false (red)
// * THE FASTEST (though is only faster than the previous method
// after using the optimizer on the map)
// * THE LEAST SAFE ( you may have to tweak OFSSET manually for it to
// work)
//
private constant boolean USE_HASH_TABLE = true
private constant boolean USE_FLEXIBLE_OFFSET = false
private constant integer OFFSET = 0x100000
private integer VOFFSET = OFFSET
//Timers to preload at map init:
private constant integer QUANTITY = 256
//Changing this to something big will allow you to keep recycling
// timers even when there are already AN INCREDIBLE AMOUNT of timers in
// the stack. But it will make things far slower so that's probably a bad idea...
private constant integer ARRAY_SIZE = 8190
endglobals
//==================================================================================================
globals
private integer array data[ARRAY_SIZE]
private hashtable ht
endglobals
//It is dependent on jasshelper's recent inlining optimization in order to perform correctly.
function SetTimerData takes timer t, integer value returns nothing
static if(USE_HASH_TABLE) then
// new blue
call SaveInteger(ht,0,GetHandleId(t), value)
elseif (USE_FLEXIBLE_OFFSET) then
// orange
static if (DEBUG_MODE) then
if(GetHandleId(t)-VOFFSET<0) then
call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
endif
endif
set data[GetHandleId(t)-VOFFSET]=value
else
// new red
static if (DEBUG_MODE) then
if(GetHandleId(t)-OFFSET<0) then
call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
endif
endif
set data[GetHandleId(t)-OFFSET]=value
endif
endfunction
function GetTimerData takes timer t returns integer
static if(USE_HASH_TABLE) then
// new blue
return LoadInteger(ht,0,GetHandleId(t) )
elseif (USE_FLEXIBLE_OFFSET) then
// orange
static if (DEBUG_MODE) then
if(GetHandleId(t)-VOFFSET<0) then
call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
endif
endif
return data[GetHandleId(t)-VOFFSET]
else
// new red
static if (DEBUG_MODE) then
if(GetHandleId(t)-OFFSET<0) then
call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
endif
endif
return data[GetHandleId(t)-OFFSET]
endif
endfunction
//==========================================================================================
globals
private timer array tT[ARRAY_SIZE]
private integer tN = 0
private constant integer HELD=0x28829022
//use a totally random number here, the more improbable someone uses it, the better.
private boolean didinit = false
endglobals
private keyword init
//==========================================================================================
// I needed to decide between duplicating code ignoring the "Once and only once" rule
// and using the ugly textmacros. I guess textmacros won.
//
//! textmacro TIMERUTIS_PRIVATE_NewTimerCommon takes VALUE
// On second thought, no.
//! endtextmacro
function NewTimerEx takes integer value returns timer
if (tN==0) then
if (not didinit) then
//This extra if shouldn't represent a major performance drawback
//because QUANTITY rule is not supposed to be broken every day.
call init.evaluate()
set tN = tN - 1
else
//If this happens then the QUANTITY rule has already been broken, try to fix the
// issue, else fail.
debug call BJDebugMsg("NewTimer: Warning, Exceeding TimerUtils_QUANTITY, make sure all timers are getting recycled correctly")
set tT[0]=CreateTimer()
static if( not USE_HASH_TABLE) then
debug call BJDebugMsg("In case of errors, please increase it accordingly, or set TimerUtils_USE_HASH_TABLE to true")
static if( USE_FLEXIBLE_OFFSET) then
if (GetHandleId(tT[0])-VOFFSET<0) or (GetHandleId(tT[0])-VOFFSET>=ARRAY_SIZE) then
//all right, couldn't fix it
call BJDebugMsg("NewTimer: Unable to allocate a timer, you should probably set TimerUtils_USE_HASH_TABLE to true or fix timer leaks.")
return null
endif
else
if (GetHandleId(tT[0])-OFFSET<0) or (GetHandleId(tT[0])-OFFSET>=ARRAY_SIZE) then
//all right, couldn't fix it
call BJDebugMsg("NewTimer: Unable to allocate a timer, you should probably set TimerUtils_USE_HASH_TABLE to true or fix timer leaks.")
return null
endif
endif
endif
endif
else
set tN=tN-1
endif
call SetTimerData(tT[tN],value)
return tT[tN]
endfunction
function NewTimer takes nothing returns timer
return NewTimerEx(0)
endfunction
//==========================================================================================
function ReleaseTimer takes timer t returns nothing
if(t==null) then
debug call BJDebugMsg("Warning: attempt to release a null timer")
return
endif
if (tN==ARRAY_SIZE) then
debug call BJDebugMsg("Warning: Timer stack is full, destroying timer!!")
//stack is full, the map already has much more troubles than the chance of bug
call DestroyTimer(t)
else
call PauseTimer(t)
if(GetTimerData(t)==HELD) then
debug call BJDebugMsg("Warning: ReleaseTimer: Double free!")
return
endif
call SetTimerData(t,HELD)
set tT[tN]=t
set tN=tN+1
endif
endfunction
private function init takes nothing returns nothing
local integer i=0
local integer o=-1
local boolean oops = false
if ( didinit ) then
return
else
set didinit = true
endif
static if( USE_HASH_TABLE ) then
set ht = InitHashtable()
loop
exitwhen(i==QUANTITY)
set tT[i]=CreateTimer()
call SetTimerData(tT[i], HELD)
set i=i+1
endloop
set tN = QUANTITY
else
loop
set i=0
loop
exitwhen (i==QUANTITY)
set tT[i] = CreateTimer()
if(i==0) then
set VOFFSET = GetHandleId(tT[i])
static if(USE_FLEXIBLE_OFFSET) then
set o=VOFFSET
else
set o=OFFSET
endif
endif
if (GetHandleId(tT[i])-o>=ARRAY_SIZE) then
exitwhen true
endif
if (GetHandleId(tT[i])-o>=0) then
set i=i+1
endif
endloop
set tN = i
exitwhen(tN == QUANTITY)
set oops = true
exitwhen not USE_FLEXIBLE_OFFSET
debug call BJDebugMsg("TimerUtils_init: Failed a initialization attempt, will try again")
endloop
if(oops) then
static if ( USE_FLEXIBLE_OFFSET) then
debug call BJDebugMsg("The problem has been fixed.")
//If this message doesn't appear then there is so much
//handle id fragmentation that it was impossible to preload
//so many timers and the thread crashed! Therefore this
//debug message is useful.
elseif(DEBUG_MODE) then
call BJDebugMsg("There were problems and the new timer limit is "+I2S(i))
call BJDebugMsg("This is a rare ocurrence, if the timer limit is too low:")
call BJDebugMsg("a) Change USE_FLEXIBLE_OFFSET to true (reduces performance a little)")
call BJDebugMsg("b) or try changing OFFSET to "+I2S(VOFFSET) )
endif
endif
endif
endfunction
endlibrary
//TESH.scrollpos=10
//TESH.alwaysfold=0
//============================================================================
// SpellEffectEvent
// - Version 1.1.0.0
//
// API
// ---
// RegisterSpellEffectEvent(integer abil, code onCast)
//
// Requires
// --------
// RegisterPlayerUnitEvent: hiveworkshop.com/forums/showthread.php?t=203338
//
// Optional
// --------
// Table: hiveworkshop.com/forums/showthread.php?t=188084
//
library SpellEffectEvent requires RegisterPlayerUnitEvent, optional Table
//============================================================================
private module M
static if LIBRARY_Table then
static Table tb
else
static hashtable ht = InitHashtable()
endif
static method onCast takes nothing returns nothing
static if LIBRARY_Table then
call TriggerEvaluate(.tb.trigger[GetSpellAbilityId()])
else
call TriggerEvaluate(LoadTriggerHandle(.ht, 0, GetSpellAbilityId()))
endif
endmethod
private static method onInit takes nothing returns nothing
static if LIBRARY_Table then
set .tb = Table.create()
endif
call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_SPELL_EFFECT, function thistype.onCast)
endmethod
endmodule
//============================================================================
private struct S extends array
implement M
endstruct
//============================================================================
function RegisterSpellEffectEvent takes integer abil, code onCast returns nothing
static if LIBRARY_Table then
if not S.tb.handle.has(abil) then
set S.tb.trigger[abil] = CreateTrigger()
endif
call TriggerAddCondition(S.tb.trigger[abil], Filter(onCast))
else
if not HaveSavedHandle(S.ht, 0, abil) then
call SaveTriggerHandle(S.ht, 0, abil, CreateTrigger())
endif
call TriggerAddCondition(LoadTriggerHandle(S.ht, 0, abil), Filter(onCast))
endif
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
/**************************************************************
*
* RegisterPlayerUnitEvent
* v5.1.0.1
* By Magtheridon96
*
* I would like to give a special thanks to Bribe, azlier
* and BBQ for improving this library. For modularity, it only
* supports player unit events.
*
* Functions passed to RegisterPlayerUnitEvent must either
* return a boolean (false) or nothing. (Which is a Pro)
*
* Warning:
* --------
*
* - Don't use TriggerSleepAction inside registered code.
* - Don't destroy a trigger unless you really know what you're doing.
*
* API:
* ----
*
* - function RegisterPlayerUnitEvent takes playerunitevent whichEvent, code whichFunction returns nothing
* - Registers code that will execute when an event fires.
* - function RegisterPlayerUnitEventForPlayer takes playerunitevent whichEvent, code whichFunction, player whichPlayer returns nothing
* - Registers code that will execute when an event fires for a certain player.
* - function GetPlayerUnitEventTrigger takes playerunitevent whichEvent returns trigger
* - Returns the trigger corresponding to ALL functions of a playerunitevent.
*
**************************************************************/
library RegisterPlayerUnitEvent // Special Thanks to Bribe and azlier
globals
private trigger array t
endglobals
function RegisterPlayerUnitEvent takes playerunitevent p, code c returns nothing
local integer i = GetHandleId(p)
local integer k = 15
if t[i] == null then
set t[i] = CreateTrigger()
loop
call TriggerRegisterPlayerUnitEvent(t[i], Player(k), p, null)
exitwhen k == 0
set k = k - 1
endloop
endif
call TriggerAddCondition(t[i], Filter(c))
endfunction
function RegisterPlayerUnitEventForPlayer takes playerunitevent p, code c, player pl returns nothing
local integer i = 16 * GetHandleId(p) + GetPlayerId(pl)
if t[i] == null then
set t[i] = CreateTrigger()
call TriggerRegisterPlayerUnitEvent(t[i], pl, p, null)
endif
call TriggerAddCondition(t[i], Filter(c))
endfunction
function GetPlayerUnitEventTrigger takes playerunitevent p returns trigger
return t[GetHandleId(p)]
endfunction
endlibrary
//TESH.scrollpos=23
//TESH.alwaysfold=0
library IsDestructableTree initializer init /* v1.1.
*************************************************************************************
*
* Detect whether a destructable is a tree or not.
*
***************************************************************************
*
* Credits
*
* To PitzerMike
* -----------------------
*
* for IsDestructableTree
*
*************************************************************************************
*
* Functions
*
* function IsDestructableTree takes destructable which returns boolean
*
* function IsDestructableAlive takes destructable which returns boolean
*
* function IsDestructableDead takes destructable which returns boolean
*
* function IsTreeAlive takes destructable which returns boolean
* - May only return true for trees
*
* function KillTree takes destructable which returns nothing
* - May only kill trees
*
*/
globals
private constant integer UNIT_DUMMY_HARVESTER = 'hpea'//human peasant
private constant integer HARVEST_ABILITY = 'Ahrl'//ghoul harvest
private constant integer HARVEST = 0xD0032//harvest order, could also be 852018
private constant player PLAYER_OWNER = Player(PLAYER_NEUTRAL_PASSIVE)
private unit h = null
endglobals
function IsDestructableTree takes destructable d returns boolean
debug if (d == null) then
debug call BJDebugMsg("ERROR: library IsDestructableTree, function IsDestructableTree, INVALID DESTRUCTABLE")
debug return false
debug endif
return (IssueTargetOrderById(h, HARVEST, d)) and (IssueImmediateOrderById(h, 851973))
endfunction
function IsDestructableAlive takes destructable d returns boolean
return (GetWidgetLife(d) > 0.405)
endfunction
function IsTreeAlive takes destructable d returns boolean
return IsDestructableAlive(d) and IsDestructableTree(d)
endfunction
function KillTree takes destructable d returns nothing
if IsTreeAlive(d) then
call KillDestructable(d)
endif
endfunction
function IsDestructableDead takes destructable d returns boolean
return (GetWidgetLife(d) <= 0.405)
endfunction
private function init takes nothing returns nothing
set h=CreateUnit(PLAYER_OWNER,UNIT_DUMMY_HARVESTER,0,0,0)
call UnitAddAbility(h, HARVEST_ABILITY)
call UnitAddAbility(h, 'Aloc')
call ShowUnit(h, false)
endfunction
endlibrary
//TESH.scrollpos=105
//TESH.alwaysfold=0
/************************************************************************************************** *
* This spell requires TimerUtils, RegisterPlayerUnitEvent, SpellEffectEvent and IsDestructableTree*
***************************************************************************************************/
scope Kamehameha
globals
private constant integer AID = 'A000' //Set AID = your ability id. To get a spells id go into the objekt editor and press ctrl+d.
private constant real AOE = 200 //How large area around the spell that will take damage.
private constant real OFFSET = 50. //How long infront of the caster the unit will spawn.
private constant integer DUMMY = 'h000' //Set this to the Unit it of your dummy. Press ctrl+d in the objekt editor to find it.
private constant attacktype ATTACK_TYPE = ATTACK_TYPE_NORMAL
private constant damagetype DAMAGE_TYPE = DAMAGE_TYPE_NORMAL
private constant real INTERVAL = 0.0312500 //How often the spell will run, this is the same as GUIs Time - Every 0.03 seconds of game time
private constant real DUMMYTIMER = 0.45 //This is for how long the ''line'' will last
private constant boolean DESTROYTREE = true
endglobals
//This is the formula for the damage. You can make your own formula if you want.
private constant function GetDamage takes integer lvl returns real
return 20.*lvl
endfunction
//This is the range of the spell, it will multiply by the speed, check the WARNING below.
private constant function GetRange takes integer lvl returns real
return 200.*lvl
endfunction
//This is how fast the spell will move.
/*********************************************************************
* WARNING!! *
* The speed of the spell changes how long the range of the spell is! *
* If the speed is 20 then is will multiply the range with 2*range. *
**********************************************************************/
private constant function GetSpeed takes integer lvl returns real
return 20. + 0*lvl
endfunction
private function GetTree takes nothing returns nothing
if IsDestructableTree(GetEnumDestructable()) then
call KillTree(GetEnumDestructable())
endif
endfunction
//This checks if a unit is alive so we can deal damage to it or not.
native UnitAlive takes unit id returns boolean
//This is the formula that filters out which units that should take damage.
private function FilterUnits takes unit u, player p returns boolean
return (UnitAlive(u)) and (IsUnitEnemy(u, p)) and (not IsUnitType(u, UNIT_TYPE_MAGIC_IMMUNE))
endfunction
//DON'T EDIT ANYTHING BELOW
private struct Kamehameha extends array
unit caster
unit dummy
real damage
real angle
player owner
real range
real inRange
real speed
integer lvl
group g
static integer array rn
static integer ic = 0
private static method line takes nothing returns nothing
local timer t = GetExpiredTimer()
local thistype this = GetTimerData(t)
local unit u
local location l = GetUnitLoc(.dummy)
local real x = GetUnitX(.dummy)
local real y = GetUnitY(.dummy)
local unit u2
set .g = CreateGroup()
if UnitAlive(.caster) and .inRange < .range then
//This checks if the DestroyTree globals is true, and if it is then if will destroy all trees withing "aoe" range.
if DESTROYTREE == true then
call EnumDestructablesInCircleBJ( AOE, l, function GetTree)
endif
//Picks all units within "AOE" range and deals damage to them.
call GroupEnumUnitsInRange( .g, x, y, AOE, null)
set u2 = CreateUnit( .owner, DUMMY, x, y, GetUnitFacing(.dummy))
loop
set u = FirstOfGroup(.g)
exitwhen u == null
call GroupRemoveUnit(.g, u)
if FilterUnits(u, .owner) then
call UnitDamageTarget(.caster, u, .damage, false, false, ATTACK_TYPE, DAMAGE_TYPE, null)
endif
endloop
call SetUnitX(.dummy, GetUnitX(.dummy)+.speed*Cos(.angle))
call SetUnitY(.dummy, GetUnitY(.dummy)+.speed*Sin(.angle))
call UnitApplyTimedLife(u2, 'BTLF', DUMMYTIMER)
set u2 = null
set .inRange = .inRange + 10.
else
call ReleaseTimer(t)
call KillUnit(.dummy)
//Deindex the spell
set rn[this] = rn[0]
set rn[0] = this
//Null some of the variables
set caster = null
set owner = null
endif
//Clear the leaks
call RemoveLocation(l)
set l = null
call DestroyGroup(.g)
set u2 = null
set t = null
set u = null
set g = null
endmethod
private static method run takes nothing returns nothing
local thistype this
local integer lvl
local real x = GetUnitX(GetTriggerUnit())
local real y = GetUnitY(GetTriggerUnit())
//Index the spell
set this = rn[0]
if this == 0 then
set ic = ic + 1
set this = ic
else
set rn[0] = rn[this]
endif
set .owner = GetTriggerPlayer()
set .caster = GetTriggerUnit()
set lvl = GetUnitAbilityLevel(.caster, AID)
set .damage = GetDamage(lvl)
set .range = GetRange(lvl)
set .angle = Atan2(GetSpellTargetY()-y,GetSpellTargetX()-x)
set .dummy = CreateUnit(.owner,DUMMY,x + OFFSET*Cos(.angle),y + OFFSET*Sin(.angle),.angle*bj_RADTODEG)
set .inRange = 0
set .speed = GetSpeed(lvl)
call TimerStart(NewTimerEx(this), INTERVAL, true, function thistype.line)
endmethod
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(AID, function thistype.run)
endmethod
endstruct
endscope