//TESH.scrollpos=-1
//TESH.alwaysfold=0
Name | Type | is_array | initial_value |
//TESH.scrollpos=-1
//TESH.alwaysfold=0
// *************************************************************
// * OrbStacking -- Version 1.8.1
// * by Deaod
// *************************************************************
// *
// * CREDITS:
// * - Anitarf (DamageEvent, DamageModifiers)
// * - grim001 (AutoIndex)
// * - Rising_Dusk (GroupUtils)
// * - Vexorian (JassHelper, Table)
// * - MindWorX and PitzerMike (JassNewGenPack)
// * - Pipedream (Grimoire)
// * - SFilip (TESH)
// *
// * HOW TO IMPORT:
// * * Copy over this library and save. Its that easy.
// *
// * HOW TO USE:
// * * use Orb.create(integer AID, integer Method, integer Priority, OrbCallback Func)
// * to create a new Orb.
// * - AID is the ID of the ability representing the orb
// * (in the case of an item representing the orb, pass
// * the rawcode of the item)
// * - Method is the type of ability triggerin the orb
// * (either ORB_TYPE_SKILL, if the ability is a
// * skill, or ORB_TYPE_ITEM, if an item triggers
// * the orb)
// * - Priority is the priority of the orb. When stacking
// * different orbs on the same unit, the orb with the
// * highest priority will get executed first.
// * If a unit has two orbs of equal priority, the one
// * acquired first will be executed first.
// * - Func is the function being run when the orb is
// * triggered. It has to follow the OrbCallback
// * interface defined below. A function following that
// * interface has to return a boolean, either
// * ORB_APPLIED or ORB_NOT_APPLIED.
// * If you return ORB_APPLIED, all Categories of type
// * WRITE will be written (their execution-count will
// * be increased)
// *
// * * add that Orb to a category using YourOrb.addToCategory(string Label, integer Type)
// * - Label is the label of the category you want to
// * add that orb to.
// * Every Orb can belong to up to MAX_CATEGORIES
// * categories
// * - Type defines the behaviour of the orb.
// * ORB_CATEGORY_TYPE_READ makes the orb only "read" from
// * the Category. That means it only checks whether
// * that category has been written in any orb before.
// * ORB_CATEGORY_TYPE_WRITE makes the orb only "write" to
// * the Category. That means it only marks the Category
// * as executed but does not depend on the Category not
// * being executed before.
// * ORB_CATEGORY_TYPE_READWRITE combines READ and WRITE.
// * That means, the orb will not be executed, if the
// * Category has been executed before and it will mark
// * the Category as executed when the orb is triggered
// *
// * * each Category has an execution limit and cant be executed
// * more often than that limit. You can change that limit
// * using this: set OrbCategory[label].execLimit=some_new_limit
// * - label is the label of the Category which's execution
// * limit you want to change
// *
// * * you can add a missile modifier to each Orb by using
// * YourOrb.setMissileModifier(integer Ability)
// * - Ability is the rawcode of an ability that places BID
// * on the attacked unit and modifies the missile.
// * You should base this ability off of AIcb for optimum
// * performance.
// * If a unit has an orb with a missile modifier, that
// * modifier gets used. If the unit has more than one, the
// * one of the orb with highest priority gets used.
// * Missile modifiers currently dont get added to units already
// * having the ability/item for the orb when the orb is created
// * in the code. Though i dont think this will ever be an issue
// * please tell me if you run into a situation where it is.
// * Note that you wont be able to change the missile if the
// * dummy ability isnt able to change the missile under the
// * given circumstances.
// *
// * * GetDamagingUnit(), GetDamagedUnit() and GetDamage() can be
// * used to access GetEventDamageSource(), GetTriggerUnit()
// * and GetEventDamage() inside OrbCallback functions
// * executed by this script
// *
// * * if you want to prevent damage or deal additional damage
// * through orbs, use the SetDamage(real new) function.
// * Pass the new amount of damage that attack should deal.
// * Using it outside Orb Callbacks wont have any effect.
// *
// * * by default, all units that get indexed by AutoIndex upon
// * entering the map, automatically get the dummy ability for
// * OrbStacking. If there are specific types you want to
// * exclude from acquiring orbs, filter them out using the
// * UnitFilter function right below the first globals block.
// * If you add the dummy ability to a unit, that unit wont
// * be able to use other orbs anymore. They also should not
// * be able to attack ground as that can cause crashes.
// *
// *************************************************************
library OrbStacking uses Table, DamageEvent, DamageModifiers, AutoIndex, optional GroupUtils
// these are the objects required for this library to function
// you can deactivate these after saving and immediately reopening the map (by removing the ! or adding a / in the front).
//! external ObjectMerger w3a AIcb AOrb anam "Generic Orb" aart "" arac "0" amat "" asat "" aspt "" atat "" ata0 "" Iarp 1 0 Idic 1 0 Iob5 1 0 abuf 1 "BORB" ahdu 1 "0.01" adur 1 "0.01" aite "0"
//! external ObjectMerger w3h Bfro BORB fnam "Proxy Orb" ftat "" ftip "Proxy Orb" fube ""
globals
constant key ORB_TYPE_SKILL //
constant key ORB_TYPE_ITEM //
constant key ORB_CATEGORY_TYPE_READ // categories of this type for the orb must not have been written before
constant key ORB_CATEGORY_TYPE_WRITE // whenever an orb belonging to this type of category is triggered that category becomes unusable
constant key ORB_CATEGORY_TYPE_READWRITE // combines READ and WRITE
constant boolean ORB_APPLIED = true
constant boolean ORB_NOT_APPLIED = false
private constant integer AID = 'AOrb' // Based off of AIcb, places BID on attacked unit.
private constant integer BID = 'BORB' // the buff placed by the dummy orb ability // based on Orb of Corruption
private constant integer MAX_CATEGORIES = 5 // the maximum categories an orb can belong to
private constant integer DEFAULT_CATEGORY_EXEC_LIMIT = 1 // the default execution limit of a newly created OrbCategory
private constant boolean REMOVE_BUFF_BEFORE = true // change to false, if you need the buff in one of your callbacks
private constant integer ORB_PRIORITY = 0x7FFFFFFF // highest possible value, meaning all orbs will get executed before any other damage modifiers get a chance
endglobals
// If theres any specific unit-type you want to exclude from using Orbs at all, filter it out here.
// Note that AutoIndex already filters out xe's dummies, and that units not indexed by AutoIndex wont be able to use Orbs
private function UnitFilter takes unit u returns boolean
return true
endfunction
// Dont touch it, its just here for you to see
function interface OrbCallback takes nothing returns boolean
// Dont touch anything below.
globals
private unit Damaging
private unit Damaged
private real Damage
endglobals
struct OrbCategory
private integer execlimit
private static StringTable CategoryTable
method operator execLimit takes nothing returns integer
return execlimit
endmethod
method operator execLimit= takes integer new returns nothing
debug if new<=0 then
debug call BJDebugMsg("OrbCategory: New execution limit too low!")
debug return
debug endif
set execlimit=new
endmethod
private static method Create takes string label returns thistype
local thistype s=allocate()
set s.execLimit=DEFAULT_CATEGORY_EXEC_LIMIT
set CategoryTable[label]=s
return s
endmethod
static method create takes string label returns thistype
if CategoryTable.exists(label) then
return CategoryTable[label]
else
return Create(label)
endif
endmethod
static method operator [] takes string label returns thistype // a cleaner wrapper
return create(label)
endmethod
private method destroy takes nothing returns nothing
// i hope this prevents idiots from destroying a Category
endmethod
private static method onInit takes nothing returns nothing
set OrbCategory.CategoryTable=StringTable.create()
endmethod
endstruct
private keyword UnitOrb
// the following are terrible hacks to create library-private struct members
private keyword aid
private keyword Method
private keyword Cat
private keyword CatType
private keyword CatCnt
private keyword cb
private keyword Priority
private keyword MissileModAbil
private struct OrbUnit extends DamageModifier
private unit self
private integer id
readonly UnitOrb missileMod
private static integer array OrbCount
private static thistype array UnitInstance
method destroy takes nothing returns nothing
set OrbCount[id]=OrbCount[id]-1
if OrbCount[id]==0 then
set self=null
set UnitInstance[id]=0
call deallocate()
endif
endmethod
method newMissileMod takes UnitOrb newmod returns nothing
call UnitRemoveAbility(self, missileMod.orb.MissileModAbil)
call UnitAddAbility(self, newmod.orb.MissileModAbil)
set missileMod=newmod
endmethod
method resetMissileMod takes nothing returns nothing
call UnitRemoveAbility(self, missileMod.orb.MissileModAbil)
call UnitAddAbility(self, AID)
set missileMod=UnitOrb[self]
endmethod
private method onDamageDealt takes unit target, real damage returns real
local integer i
local Orb s
local integer array b // counts the execution of each category
local boolean t
local UnitOrb o
if GetUnitAbilityLevel(target, BID)<=0 then
// not triggered by an attack.
return 0.
endif
static if REMOVE_BUFF_BEFORE then
call UnitRemoveAbility(target, BID)
endif
set Damaging=self
set Damaged=target
set Damage=damage
set o=UnitOrb[self]
loop
exitwhen o==0
set s=o.orb
set i=0
set t=true
loop
exitwhen i>=s.CatCnt
// lets see if were allowed to trigger the Orb
if (s.CatType[i]==ORB_CATEGORY_TYPE_READ or s.CatType[i]==ORB_CATEGORY_TYPE_READWRITE) and b[s.Cat[i]]>=s.Cat[i].execLimit then
set t=false // we arent
exitwhen true
endif
set i=i+1
endloop
if t and s.cb.evaluate()==ORB_APPLIED then // additionally check if the Orb actually triggered (for chance based Orbs)
set i=0
loop
exitwhen i>=s.CatCnt
// now, lets mark the categories as execute once more
if s.CatType[i]==ORB_CATEGORY_TYPE_WRITE or s.CatType[i]==ORB_CATEGORY_TYPE_READWRITE then
set b[s.Cat[i]]=b[s.Cat[i]]+1
endif
set i=i+1
endloop
endif
set o=o[o.index+1] // and loop through the Orbs of the unit
endloop
return Damage-damage // return newdamage-olddamage
endmethod
static method create takes unit u returns thistype
local integer id=GetUnitId(u)
if UnitInstance[id]==0 then
set UnitInstance[id]=allocate(u, ORB_PRIORITY)
set UnitInstance[id].id=id
set UnitInstance[id].self=u
set UnitInstance[id].missileMod=AID
endif
set OrbCount[id]=OrbCount[id]+1
return UnitInstance[id]
endmethod
endstruct
private struct UnitOrb
Orb orb
OrbUnit orbunit
implement AutoDestroy
static method clear takes unit u returns nothing
local thistype o
local thistype p
set o=UnitOrb[u]
loop
exitwhen o==0
set p=o[o.index+1]
call o.destroy()
set o=p
endloop
endmethod
private method onDestroy takes nothing returns nothing
local thistype o
if orbunit.missileMod==this then
set o=o[o.index+1] // start with the next orb in line
loop
exitwhen o==0
if o.orb.MissileModAbil!=AID then
call o.orbunit.newMissileMod(o)
endif
set o=o[o.index+1]
endloop
if o.orbunit.missileMod==this then
call o.orbunit.resetMissileMod()
endif
endif
call orbunit.destroy()
endmethod
static method create takes unit u, Orb orb returns thistype
local thistype s=allocate()
local thistype o
local boolean b // theres a missile modifier with higher priority or the orb does not have a missile modifier.
set s.orb=orb
set s.orbunit=OrbUnit.create(u)
set o=UnitOrb[u]
set b=s.orb.MissileModAbil==AID
// search the right place to insert
loop
exitwhen o==0 or o.orb.Priority<orb.Priority
if not b and o.orb.MissileModAbil!=AID then
set b=true
endif
set o=o[o.index+1]
endloop
set s.me=u // insert at end
if not b then
// apply new Missile Modifier
call s.orbunit.newMissileMod(s)
endif
loop // shift instances moving new instance to the right place
exitwhen o==0
set o.me=u
set o=o[o.index+1]
endloop
return s
endmethod
endstruct
struct Orb
integer aid
integer Method
OrbCategory array Cat[MAX_CATEGORIES]
integer array CatType[MAX_CATEGORIES]
integer CatCnt=0
OrbCallback cb
integer Priority=0
integer MissileModAbil=AID
private integer i
private static thistype array Structs
private static integer Count=0
private static Table OrbTable
private static rect WorldRect
private static group InitGroup
private static thistype TempOrb
private static method OnDestroyCheck takes nothing returns boolean
local UnitOrb o
local UnitOrb next
set o=UnitOrb[GetFilterUnit()]
loop
exitwhen o<=0
if o.orb==TempOrb then
set next=o[o.index+1]
call o.destroy()
set o=next
else
set o=o[o.index+1]
endif
endloop
return false
endmethod
method onDestroy takes nothing returns nothing
set TempOrb=this
static if LIBRARY_GroupUtils then
call GroupEnumUnitsInRect(ENUM_GROUP, WorldRect, function thistype.OnDestroyCheck)
else
call GroupEnumUnitsInRect(InitGroup, WorldRect, function thistype.OnDestroyCheck)
endif
if Method==ORB_TYPE_SKILL then
set Count=Count-1
set Structs[i]=Structs[Count]
set Structs[i].i=i
endif
call OrbTable.flush(.aid)
endmethod
private static method OnCreateCheck takes nothing returns boolean
local unit u=GetFilterUnit()
if UnitFilter(u) then
if TempOrb.Method==ORB_TYPE_SKILL and GetUnitAbilityLevel(u, TempOrb.aid)>0 then
call UnitOrb.create(u, TempOrb)
elseif TempOrb.Method==ORB_TYPE_ITEM and UnitHasItemOfTypeBJ(u, TempOrb.aid) then
call UnitOrb.create(u, TempOrb)
endif
endif
set u=null
return false
endmethod
static method create takes integer AID, integer Method, integer Priority, OrbCallback Func returns Orb
local thistype s=allocate()
set s.aid=AID
set s.cb=Func
if Method==ORB_TYPE_SKILL then
set Structs[Count]=s
set s.i=Count
set Count=Count+1
endif
set s.Method=Method
set s.Priority=Priority
set OrbTable[AID]=s
set TempOrb=s
static if LIBRARY_GroupUtils then
call GroupEnumUnitsInRect(ENUM_GROUP, WorldRect, function thistype.OnCreateCheck)
else
call GroupEnumUnitsInRect(InitGroup, WorldRect, function thistype.OnCreateCheck)
endif
return s
endmethod
method addToCategory takes string Label, integer Type returns nothing
debug if CatCnt>=MAX_CATEGORIES or Label=="" or Label==null or (Type!=ORB_CATEGORY_TYPE_READ and Type!=ORB_CATEGORY_TYPE_WRITE and Type!=ORB_CATEGORY_TYPE_READWRITE) then
debug call BJDebugMsg("OrbStacking: Can't add Orb["+I2S(this)+"] to another OrbCategory (wrong label, invalid type, or maximum categories exceeded)!")
debug return
debug endif
set Cat[CatCnt]=OrbCategory.create(Label)
set CatType[CatCnt]=Type
set CatCnt=CatCnt+1
endmethod
method setMissileModifier takes integer Ability returns nothing
set MissileModAbil=Ability
endmethod
private static method UnitCreated takes unit u returns nothing
local integer i=Orb.Count-1
local thistype s
if UnitFilter(u) then
call UnitAddAbility(u, AID)
loop
exitwhen i<0
set s=Structs[i]
if GetUnitAbilityLevel(u, s.aid)>0 then
call UnitOrb.create(u, s)
endif
set i=i-1
endloop
endif
endmethod
private static method OnUpgrade takes nothing returns nothing
local unit u=GetTriggerUnit()
call UnitOrb.clear(u)
call UnitCreated(u)
set u=null
endmethod
private static method OnSkill takes nothing returns nothing
local thistype s=OrbTable[GetLearnedSkill()]
local UnitOrb o
if UnitFilter(GetTriggerUnit()) and s>0 and GetLearnedSkillLevel()==1 then
set o=UnitOrb.create(GetTriggerUnit(), s)
endif
endmethod
private static method OnItemPickup takes nothing returns nothing
local thistype s=OrbTable[GetItemTypeId(GetManipulatedItem())]
local UnitOrb o
if UnitFilter(GetTriggerUnit()) and s>0 then
set o=UnitOrb.create(GetTriggerUnit(), s)
endif
endmethod
private static method OnItemDrop takes nothing returns nothing
local thistype s=OrbTable[GetItemTypeId(GetManipulatedItem())]
local UnitOrb o
if s>0 then
set o=UnitOrb[GetTriggerUnit()]
loop
exitwhen o<=0
if o.orb==s then
call o.destroy()
return
endif
set o=o[o.index+1]
endloop
endif
endmethod
private static method OnUnitAddAbility takes unit u, integer aid returns nothing
local thistype s=OrbTable[aid]
local UnitOrb o
if UnitFilter(u) and s>0 and GetUnitAbilityLevel(u, aid)<=0 then
set o=UnitOrb.create(u, s)
endif
endmethod
private static method OnUnitAddAbilityBJ takes integer aid, unit u returns nothing
call OnUnitAddAbility(u, aid)
endmethod
private static method OnUnitRemoveAbility takes unit u, integer aid returns nothing
local thistype s=OrbTable[aid]
local UnitOrb o
if s>0 and GetUnitAbilityLevel(u, aid)>0 then
set o=UnitOrb[u]
loop
exitwhen o<=0
if o.orb==s then
call o.destroy()
return
endif
set o=o[o.index+1]
endloop
endif
endmethod
private static method OnUnitRemoveAbilityBJ takes integer aid, unit u returns nothing
call OnUnitRemoveAbility(u, aid)
endmethod
private static method onInit takes nothing returns nothing
local trigger t
// Listener for units entering the map
call OnUnitIndexed(UnitCreated)
// Listener for upgrading a unit
set t=CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_UPGRADE_FINISH)
call TriggerAddAction(t, function thistype.OnUpgrade)
// Listener for learning an ability
set t=CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_HERO_SKILL)
call TriggerAddAction(t, function thistype.OnSkill)
// Listener for picking up and item
set t=CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_PICKUP_ITEM)
call TriggerAddAction(t, function thistype.OnItemPickup)
// Listener for dropping an item
set t=CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_DROP_ITEM)
call TriggerAddAction(t, function thistype.OnItemDrop)
set OrbTable=Table.create()
set WorldRect=GetWorldBounds() // sadly, you cant initialize rects using this function inside the globals block, unless you want the game to crash
static if not LIBRARY_GroupUtils then
set InitGroup=CreateGroup() // only initialize if necessary.
endif
endmethod
endstruct
hook UnitAddAbility Orb.OnUnitAddAbility
hook UnitAddAbilityBJ Orb.OnUnitAddAbilityBJ
hook UnitRemoveAbility Orb.OnUnitRemoveAbility
hook UnitRemoveAbilityBJ Orb.OnUnitRemoveAbilityBJ
hook UnitRemoveBuffBJ Orb.OnUnitRemoveAbilityBJ
function GetDamagingUnit takes nothing returns unit
return Damaging
endfunction
function GetDamagedUnit takes nothing returns unit
return Damaged
endfunction
function GetDamage takes nothing returns real
return Damage
endfunction
function SetDamage takes real new returns nothing
set Damage=new
endfunction
endlibrary
//TESH.scrollpos=-1
//TESH.alwaysfold=0
library LifeSteal initializer Init uses OrbStacking
globals
private constant integer AID = 'A000' //
private constant integer MISSILEMOD_AID = 'A003' //
private constant string FX = "Abilities\\Spells\\Undead\\VampiricAura\\VampiricAuraTarget.mdl"
private constant string FX_ATTPT = "origin"
private constant integer ORB_PRIORITY = 0
private real array LEECH_FACTOR //
endglobals
private function SetUpLEECH_FACTOR takes nothing returns nothing
set LEECH_FACTOR[1]=0.15
set LEECH_FACTOR[2]=0.25
set LEECH_FACTOR[3]=0.35
endfunction
//
private function Leech_Factor takes integer level returns real
return LEECH_FACTOR[level]
endfunction
private function Actions takes nothing returns boolean
local unit d=GetDamagedUnit()
local unit t=GetDamagingUnit()
local real r=GetDamage()
if IsUnitType(d, UNIT_TYPE_MECHANICAL)==false and IsUnitEnemy(d, GetOwningPlayer(t)) then
call SetWidgetLife(t, GetWidgetLife(t)+(r*Leech_Factor(GetUnitAbilityLevel(t, AID))))
call DestroyEffect(AddSpecialEffectTarget(FX, t, FX_ATTPT))
endif
set d=null
set t=null
return ORB_APPLIED
endfunction
private function Init takes nothing returns nothing
local Orb o=Orb.create(AID, ORB_TYPE_SKILL, ORB_PRIORITY, Actions)
call o.addToCategory("VampAura", ORB_CATEGORY_TYPE_READWRITE)
call o.setMissileModifier(MISSILEMOD_AID)
call SetUpLEECH_FACTOR()
endfunction
endlibrary
//TESH.scrollpos=-1
//TESH.alwaysfold=0
library ManaBurn initializer Init uses OrbStacking
globals
private constant integer AID = 'A001'
private constant string FX = "Abilities\\Spells\\Human\\Feedback\\ArcaneTowerAttack.mdl"
private constant string FX_ATTPT = "origin"
private constant boolean ABSOLUTE = true // is AMOUNT_BURNED treated as an absolute value or a relative?
private constant unitstate RELATIVE_ANCHOR = UNIT_STATE_MANA // if ABSOLUTE equals false, this is the anchor used for the relative calculations
private constant integer ORB_PRIORITY = 0
private real array AMOUNT_BURNED //
endglobals
private function SetUpAMOUNT_BURNED takes nothing returns nothing
set AMOUNT_BURNED[1]=24.
set AMOUNT_BURNED[2]=36.
set AMOUNT_BURNED[3]=48.
endfunction
//
private function Amount_Burned takes integer level returns real
return AMOUNT_BURNED[level]
endfunction
private function Actions takes nothing returns boolean
local unit t=GetDamagedUnit()
if GetUnitState(t, UNIT_STATE_MANA)>0 and (not IsUnitType(t, UNIT_TYPE_MECHANICAL)) and (not IsUnitType(t, UNIT_TYPE_STRUCTURE)) then
if ABSOLUTE then
call SetUnitState(t, UNIT_STATE_MANA, GetUnitState(t, UNIT_STATE_MANA)-Amount_Burned(GetUnitAbilityLevel(GetDamagingUnit(), AID)))
else
call SetUnitState(t, UNIT_STATE_MANA, GetUnitState(t, UNIT_STATE_MANA)-(Amount_Burned(GetUnitAbilityLevel(GetDamagingUnit(), AID))*GetUnitState(t, RELATIVE_ANCHOR)))
endif
call DestroyEffect(AddSpecialEffectTarget(FX, t, FX_ATTPT))
endif
set t=null
return ORB_APPLIED
endfunction
private function Init takes nothing returns nothing
call Orb.create(AID, ORB_TYPE_SKILL, ORB_PRIORITY, Actions).addToCategory("manaburn", ORB_CATEGORY_TYPE_READWRITE)
call SetUpAMOUNT_BURNED()
endfunction
endlibrary
//TESH.scrollpos=-1
//TESH.alwaysfold=0
library AutoIndex
//===========================================================================
// Information:
//==============
//
// AutoIndex is a very simple script to utilize. Just call GetUnitId(unit)
// to get get the unique value assigned to a particular unit. The GetUnitId
// function is extremely fast because it inlines directly to a GetUnitUserData
// call. AutoIndex automatically assigns an ID to each unit as it enters the
// map, and instantly frees that ID as the unit leaves the map. Detection of
// leaving units is accomplished in constant time without a periodic scan.
//
// AutoIndex uses UnitUserData by default. If something else in your map
// would conflict with that, you can set the UseUnitUserData configuration
// constant to false, and a hashtable will be used instead. Note that hash-
// tables are about 60% slower.
//
// If you turn on debug mode, AutoIndex will be able to display several
// helpful error messages. The following issues will be detected:
// -Passing a removed or decayed unit to GetUnitId
// -Code outside of AutoIndex has overwritten a unit's UserData value.
// -GetUnitId was used on a filtered unit (a unit you don't want indexed).
//
// AutoIndex provides events upon indexing or deindexing units. This
// effectively allows you to notice when units enter or leave the game. Also
// included are the AutoData, AutoCreate, and AutoDestroy modules, which allow
// you to fully utilize AutoIndex's enter/leave detection capabilities in
// conjunction with your structs.
//
//===========================================================================
// How to install AutoIndex:
//===========================
//
// 1.) Copy and paste this script into your map.
// 2.) Save it to allow the ObjectMerger macro to generate the "Leave Detect"
// ability for you. Close and re-open the map. After that, disable the macro
// to prevent the delay while saving.
//
//===========================================================================
// How to use AutoIndex:
//=======================
//
// So you can get a unique integer for each unit, but how do you use that to
// attach data to a unit? GetUnitId will always return a number in the range of
// 1-8190. This means it can be used as an array index, as demonstrated below:
/*
globals
integer array IntegerData
real array RealData
SomeStruct array SomeStructData
englobals
function Example takes nothing returns nothing
local unit u = CreateUnit(Player(0), 'hpea', 0., 0., 0.)
local integer id = GetUnitId(u)
//You now have a unique index for the unit, so you can
//attach or retrieve data about the unit using arrays.
set IntegerData[id] = 5
set RealData[id] = 25.0
set SomeStructData[id] = SomeStruct.create()
//If you have access to the same unit in another function, you can
//retrieve the data by using GetUnitId() and reading the arrays.
endfunction
*/
// The UnitFilter function in the configuration section is provided so that
// you can make AutoIndex completely ignore certain unit-types. Ignored units
// won't be indexed or fire indexed/deindexed events. You may want to filter out
// dummy casters or system-private units, especially ones that use UnitUserData
// internally. xe dummy units are automatically filtered.
//
//===========================================================================
// How to use OnUnitIndexed / OnUnitDeindexed:
//=============================================
//
// AutoIndex will fire the OnUnitIndexed event when a unit enters the map,
// and the OnUnitDeindexed event when a unit leaves the map. Functions used
// as events must take a unit and return nothing. An example is given below:
/*
function UnitEntersMap takes unit u returns nothing
call BJDebugMsg(GetUnitName(u)+" with ID "+I2S(GetUnitId(u))+" entered the map.")
endfunction //Using GetUnitId() during Indexed events works fine...
function UnitLeavesMap takes unit u returns nothing
call BJDebugMsg(GetUnitName(u)+" with ID "+I2S(GetUnitId(u))+" left the map.")
endfunction //So does using GetUnitId() during Deindexed events.
function Init takes nothing returns nothing
call OnUnitIndexed(UnitEntersMap)
call OnUnitDeindexed(UnitLeavesMap)
endfunction
*/
// If you call OnUnitIndexed during map initialization, every existing
// unit will be considered as entering the map. This saves you from the need
// to manually enumerate preplaced units (or units created by initialization
// code that ran before OnUnitIndexed was called).
//
// OnUnitDeindexed runs while a unit still exists, which means you can
// still do things such as destroy special effects attached to the unit.
// The unit will cease to exist immediately after the event is over.
//
//===========================================================================
// AutoIndex API:
//================
//
// GetUnitId(unit) -> integer
// This function returns a unique ID in the range of 1-8190 for the
// specified unit. Returns 0 if a null unit was passed. This function
// inlines directly to GetUnitUserData or LoadInteger if debug mode
// is disabled. If debug mode is enabled, this function will print
// an error message when passed a decayed or filtered unit.
//
// IsUnitIndexed(unit) -> boolean
// This function returns a boolean indicating whether the specified
// unit has been indexed. The only time this will return false is
// for units you have filtered using the UnitFilter function, or
// for xe dummy units. You can use this function to easily detect
// dummy units and avoid performing certain actions on them.
//
// OnUnitIndexed(IndexFunc)
// This function accepts an IndexFunc, which must take a unit and
// return nothing. The IndexFunc will be fired instantly whenever
// a unit enters the map. You may use GetUnitId on the unit. When
// you call this function during map initialization, every existing
// unit will be considered as entering the map.
//
// OnUnitDeindexed(IndexFunc)
// Same as above, but runs whenever a unit is leaving the map. When
// this event runs, the unit still exists, but it will cease to exist
// as soon as the event ends. You may use GetUnitId on the unit.
//
//===========================================================================
// How to use AutoData:
//======================
//
// The AutoData module allows you to associate one or more instances
// of the implementing struct with units, as well as iterate through all
// of the instances associated with each unit.
//
// This association is accomplished through the "me" instance member,
// which the module will place in the implementing struct. Whichever unit
// you assign to "me" becomes the owner of that instance. You may change
// ownership by reassigning "me" to another unit at any time, or you may
// make the instance unowned by assigning "me" to null.
//
// AutoData implements the static method operator [] in your struct
// to allow you to access instances from their owning units. For example,
// you may type: local StructName s = StructName[u]. If u has been set
// to own an instance of StructName, s will be set to that instance.
//
// So, what happens if you assign the same owning unit to multiple
// instances? You may use 2D array syntax to access instances assigned to
// the same unit: local StructName s = StructName[u][n], where u is the
// owning unit, and n is the index beginning with 0 for each unit. You
// can access the size of a unit's instance list (i.e. the number of
// instances belonging to the unit) by using the .size instance member.
/*
struct Example
implement AutoData
static method create takes unit u returns Example
local Example this = allocate()
set me = u //Assigning the "me" member from AutoData.
return this
endmethod
endstruct
function Test takes nothing returns nothing
local unit u = CreateUnit(Player(0), 'hpea', 0., 0., 0.)
local Example e1 = Example.create(u)
local Example e2 = Example.create(u)
local Example e3 = Example.create(u)
local Example e
call BJDebugMsg(I2S(Example[u].size)) //Prints 3 because u owns e1, e2, and e3.
set e = Example[u][GetRandomInt(0, Example[u].size - 1)] //Random instance belonging to u.
set e = Example[u] //This is the fastest way to iterate the instances belonging
loop //to a specific unit, starting with the first instance.
exitwhen e == 0 //e will be assigned to 0 when no instances remain.
call BJDebugMsg(I2S(e)) //Prints the values of e1, e2, e3.
set e = e[e.index + 1] //"e.index" refers to the e's position in u's instance list.
endloop //Thus, index + 1 is next, and index - 1 is previous.
endfunction //This trick allows you to avoid a local counter.
*/
// AutoData restrictions:
// -You may not implement AutoData in any struct which has already
// declared static or non-static method operator [].
// -AutoData will conflict with anything named "me", "size", or
// "index" in the implementing struct.
// -AutoData may not be implemented in structs that extend array.
// -You may not declare your own destroy method. (This restriction
// can be dropped as soon as JassHelper supports module onDestroy).
//
// AutoData information:
// -You do not need to null the "me" member when destroying an
// instance. That is done for you automatically during destroy().
// (But if you use deallocate(), you must null "me" manually.)
// -StructName[u] and StructName[u][0] refer to the same instance,
// which is the first instance that was associated with unit u.
// -StructName[u][StructName[u].size - 1] refers to the instance that
// was most recently associated with unit u.
// -Instances keep their relative order in the list when one is removed.
//
//===========================================================================
// How to use AutoCreate:
//=======================
//
// The AutoCreate module allows you to automatically create instances
// of the implementing struct for units as they enter the game. AutoCreate
// automatically implements AutoData into your struct. Any time an instance
// is automatically created for a unit, that instance's "me" member will be
// assigned to the entering unit.
//
// AutoCreate restrictions:
// -All of the same restrictions as AutoData.
// -If your struct's allocate() method takes parameters (i.e. the parent
// type's create method takes parameters), you must declare a create
// method and pass those extra parameters to allocate yourself.
//
// AutoCreate information:
// -You may optionally declare the createFilter method, which specifies
// which units should recieve an instance as they enter the game. If
// you do not declare it, all entering units will recieve an instance.
// -You may optionally declare the onCreate method, which will run when
// AutoCreate automatically creates an instance. (This is just a stand-
// in until JassHelper supports the onCreate method.)
// -You may declare your own create method, but it must take a single
// unit parameter (the entering unit) if you do so.
/*
struct Example
private static method createFilter takes unit u returns boolean
return GetUnitTypeId(u) == 'hfoo' //Created only for Footmen.
endmethod
private method onCreate takes nothing returns nothing
call BJDebugMsg(GetUnitName(me)+" entered the game!")
endmethod
implement AutoCreate
endstruct
*/
//===========================================================================
// How to use AutoDestroy:
//=========================
//
// The AutoDestroy module allows you to automatically destroy instances
// of the implementing struct when their "me" unit leaves the game. AutoDestroy
// automatically implements AutoData into your struct. You must assign a unit
// to the "me" member of an instance for this module to have any effect.
//
// AutoDestroy restrictions:
// -All of the same restrictions as AutoData.
//
// AutoDestroy information:
// -If you also implement AutoCreate in the same struct, remember that it
// assigns the "me" unit automatically. That means you can have fully
// automatic creation and destruction.
/*
struct Example
static method create takes unit u returns Example
local Example this = allocate()
set me = u //You should assign a unit to "me",
return this //otherwise AutoDestroy does nothing.
endmethod //Not necessary if using AutoCreate.
private method onDestroy takes nothing returns nothing
call BJDebugMsg(GetUnitName(me)+" left the game!")
endmethod
implement AutoDestroy
endstruct
*/
//===========================================================================
// Configuration:
//================
//! external ObjectMerger w3a Adef lvdt anam "Leave Detect" aart "" arac 0
//Save your map with this Object Merger call enabled, then close and reopen your
//map. Disable it by removing the exclamation to remove the delay while saving.
globals
private constant integer LeaveDetectAbilityID = 'lvdt'
//This rawcode must match the parameter after "Adef" in the
//ObjectMerger macro above. You may change both if you want.
private constant boolean UseUnitUserData = true
//If this is set to true, UnitUserData will be used. You should only set
//this to false if something else in your map already uses UnitUserData.
//A hashtable will be used instead, but it is about 60% slower.
private constant boolean SafeMode = true
//This is set to true by default so that GetUnitId() will ALWAYS work.
//If if this is set to false, GetUnitId() may fail to work in a very
//rare circumstance: creating a unit that has a default-on autocast
//ability, and using GetUnitId() on that unit as it enters the game,
//during a trigger that detects any order. Set this to true for a
//performance boost only if you think you can avoid this issue.
private constant boolean AutoDataFastMode = true
//If this is set to true, AutoData will utilize one hashtable per time
//it is implemented. If this is set to false, all AutoDatas will share
//a single hashtable, but iterating through the instances belonging to
//a unit will become about 12.5% slower. Your map will break if you
//use more than 255 hashtables simultaneously. Only set this to false
//if you suspect you will run out of hashtable instances.
endglobals
private function UnitFilter takes unit u returns boolean
return true
endfunction
//Make this function return false for any unit-types you want to ignore.
//Ignored units won't be indexed or fire OnUnitIndexed/OnUnitDeindexed
//events. The unit parameter "u" to refers to the unit being filtered.
//Do not filter out xe dummy units; they are automatically filtered.
//===========================================================================
// AutoData / AutoCreate / AutoDestroy modules:
//==============================================
function interface AutoCreator takes unit u returns nothing
function interface AutoDestroyer takes unit u returns nothing
globals
hashtable AutoData = null //If AutoDataFastMode is disabled, this hashtable will be
endglobals //initialized and shared between all AutoData implementations.
module AutoData
private static hashtable ht
private static thistype array data
private static integer array listsize
private static key typeid //Good thing keys exist to identify each implementing struct.
private unit meunit
private integer id
readonly integer index //The user can avoid using a local counter because this is accessable.
static method operator [] takes unit u returns thistype
return data[GetUnitId(u)]
endmethod //This is as fast as retrieving an instance from a unit gets.
method operator [] takes integer index returns thistype
static if AutoDataFastMode then //If fast mode is enabled...
return LoadInteger(ht, id, index)
else //Each instance has its own hashtable to associate unit and index.
return LoadInteger(AutoData, id, index*8190+typeid)
endif //Otherwise, simulate a 3D array associating unit, struct-type ID, and index.
endmethod //Somehow, this version is 12.5% slower just because of the math.
private method setIndex takes integer index, thistype data returns nothing
static if AutoDataFastMode then //Too bad you can't have a module-private operator []=.
call SaveInteger(ht, id, index, data)
else
call SaveInteger(AutoData, id, index*8190+typeid, data)
endif
endmethod
private method remove takes nothing returns nothing
if meunit == null then //If the struct doesn't have an owner...
return //Nothing needs to be done.
endif
loop
exitwhen index == listsize[id] //The last value gets overwritten by 0.
call setIndex(index, this[index + 1]) //Shift each element down by one.
set this[index].index = index //Update the shifted instance's index.
set index = index + 1
endloop
set listsize[id] = listsize[id] - 1
set data[id] = this[0] //Ensure thistype[u] returns the same value as thistype[u][0].
set meunit = null
endmethod
private method add takes unit u returns nothing
if meunit != null then //If the struct has an owner...
call remove() //remove it first.
endif
set meunit = u
set id = GetUnitId(u) //Cache GetUnitId for slight performance boost.
if data[id] == 0 then //If this is the first instance for this unit...
set data[id] = this //Update the value that thistype[u] returns.
endif
set index = listsize[id] //Remember the index for removal.
call setIndex(index, this) //Add to the array.
set listsize[id] = index + 1
endmethod
method operator me takes nothing returns unit
return meunit
endmethod
method operator me= takes unit u returns nothing
if u != null then //If assigning "me" a non-null value...
call add(u) //Add this instance to that unit's array.
else //If assigning "me" a null value...
call remove() //Remove this instance from that unit's array.
endif
endmethod
method operator size takes nothing returns integer
return listsize[id]
endmethod
method destroy takes nothing returns nothing
call deallocate()
call remove() //This makes removal automatic when an instance is destroyed.
endmethod
private static method onInit takes nothing returns nothing
static if AutoDataFastMode then //If fast mode is enabled...
set ht = InitHashtable() //Initialize one hashtable per instance.
else //If fast mode is disabled...
if AutoData == null then //If the hashtable hasn't been initialized yet...
set AutoData = InitHashtable() //Initialize the shared hashtable.
endif
endif
endmethod
endmodule
module AutoCreate
implement AutoData //AutoData is necessary for AutoCreate.
private static method creator takes unit u returns nothing
local thistype this
local boolean b = true //Assume that the instance will be created.
static if thistype.createFilter.exists then //If createFilter exists...
set b = createFilter(u) //evaluate it and update b.
endif
if b then //If the instance should be created...
static if thistype.create.exists then //If the create method exists...
set this = create(u) //Create the instance, passing the entering unit.
else //If the create method doesn't exist...
set this = allocate() //Just allocate the instance.
endif
set me = u //Assign the instance's owner as the entering unit.
static if thistype.onCreate.exists then //If onCreate exists...
call onCreate() //Call it, because JassHelper should do this anyway.
endif
endif
endmethod
private static method onInit takes nothing returns nothing
call AutoIndex.addAutoCreate(thistype.creator)
endmethod //During module initialization, pass the creator function to AutoIndex.
endmodule
module AutoDestroy
implement AutoData //AutoData is necessary for AutoDestroy.
static method destroyer takes unit u returns nothing
loop
exitwhen thistype[u] == 0
call thistype[u].destroy()
endloop
endmethod //Destroy each instance owned by the unit until none are left.
private static method onInit takes nothing returns nothing
call AutoIndex.addAutoDestroy(thistype.destroyer)
endmethod //During module initialization, pass the destroyer function to AutoIndex.
endmodule
//===========================================================================
// AutoIndex struct:
//===================
function interface IndexFunc takes unit u returns nothing
hook RemoveUnit AutoIndex.hook_RemoveUnit
hook ReplaceUnitBJ AutoIndex.hook_ReplaceUnitBJ
debug hook SetUnitUserData AutoIndex.hook_SetUnitUserData
private keyword getIndex
private keyword getIndexDebug
private keyword isUnitIndexed
private keyword onUnitIndexed
private keyword onUnitDeindexed
struct AutoIndex
private static trigger enter = CreateTrigger()
private static trigger order = CreateTrigger()
private static trigger creepdeath = CreateTrigger()
private static group preplaced = CreateGroup()
private static timer allowdecay = CreateTimer()
private static hashtable ht
private static boolean array dead
private static boolean array summoned
private static boolean array animated
private static boolean array nodecay
private static boolean array removing
private static IndexFunc array indexfuncs
private static integer indexfuncs_n = -1
private static IndexFunc array deindexfuncs
private static integer deindexfuncs_n = -1
private static IndexFunc indexfunc
private static AutoCreator array creators
private static integer creators_n = -1
private static AutoDestroyer array destroyers
private static integer destroyers_n = -1
private static unit array allowdecayunit
private static integer allowdecay_n = -1
private static boolean duringinit = true
private static boolean array altered
private static unit array idunit
//===========================================================================
static method getIndex takes unit u returns integer
static if UseUnitUserData then
return GetUnitUserData(u)
else
return LoadInteger(ht, 0, GetHandleId(u))
endif
endmethod //Resolves to an inlinable one-liner after the static if.
static method getIndexDebug takes unit u returns integer
if u == null then
return 0
elseif GetUnitTypeId(u) == 0 then
call BJDebugMsg("AutoIndex error: Removed or decayed unit passed to GetUnitId.")
elseif idunit[getIndex(u)] != u and GetIssuedOrderId() != 852056 then
call BJDebugMsg("AutoIndex error: "+GetUnitName(u)+" is a filtered unit.")
endif
return getIndex(u)
endmethod //If debug mode is enabled, use the getIndex method that shows errors.
private static method setIndex takes unit u, integer index returns nothing
static if UseUnitUserData then
call SetUnitUserData(u, index)
else
call SaveInteger(ht, 0, GetHandleId(u), index)
endif
endmethod //Resolves to an inlinable one-liner after the static if.
static method isUnitIndexed takes unit u returns boolean
return u != null and idunit[getIndex(u)] == u
endmethod
static method isUnitAnimateDead takes unit u returns boolean
return animated[getIndex(u)]
endmethod //Don't use this; use IsUnitAnimateDead from AutoEvents instead.
//===========================================================================
private static method onUnitIndexed_sub takes nothing returns nothing
call indexfunc.evaluate(GetEnumUnit())
endmethod
static method onUnitIndexed takes IndexFunc func returns nothing
set indexfuncs_n = indexfuncs_n + 1
set indexfuncs[indexfuncs_n] = func
if duringinit then //During initialization, evaluate the indexfunc for every preplaced unit.
set indexfunc = func
call ForGroup(preplaced, function AutoIndex.onUnitIndexed_sub)
endif
endmethod
static method onUnitDeindexed takes IndexFunc func returns nothing
set deindexfuncs_n = deindexfuncs_n + 1
set deindexfuncs[deindexfuncs_n] = func
endmethod
static method addAutoCreate takes AutoCreator func returns nothing
set creators_n = creators_n + 1
set creators[creators_n] = func
endmethod
static method addAutoDestroy takes AutoDestroyer func returns nothing
set destroyers_n = destroyers_n + 1
set destroyers[destroyers_n] = func
endmethod
//===========================================================================
private static method hook_RemoveUnit takes unit whichUnit returns nothing
set removing[getIndex(whichUnit)] = true
endmethod //Intercepts whenever RemoveUnit is called and sets a flag.
private static method hook_ReplaceUnitBJ takes unit whichUnit, integer newUnitId, integer unitStateMethod returns nothing
set removing[getIndex(whichUnit)] = true
endmethod //Intercepts whenever ReplaceUnitBJ is called and sets a flag.
private static method hook_SetUnitUserData takes unit whichUnit, integer data returns nothing
static if UseUnitUserData then
if idunit[getIndex(whichUnit)] == whichUnit then
if getIndex(whichUnit) == data then
call BJDebugMsg("AutoIndex error: Code outside AutoIndex attempted to alter "+GetUnitName(whichUnit)+"'s index.")
else
call BJDebugMsg("AutoIndex error: Code outside AutoIndex altered "+GetUnitName(whichUnit)+"'s index.")
if idunit[data] != null then
call BJDebugMsg("AutoIndex error: "+GetUnitName(whichUnit)+" and "+GetUnitName(idunit[data])+" now have the same index.")
endif
set altered[data] = true
endif
endif
endif //In debug mode, intercepts whenever SetUnitUserData is used on an indexed unit.
endmethod //Displays an error message if outside code tries to alter a unit's index.
//===========================================================================
private static method allowDecay takes nothing returns nothing
local integer n = allowdecay_n
loop
exitwhen n < 0
set nodecay[getIndex(allowdecayunit[n])] = false
set allowdecayunit[n] = null
set n = n - 1
endloop
set allowdecay_n = -1
endmethod //Iterate through all the units in the stack and allow them to decay again.
private static method detectStatus takes nothing returns boolean
local unit u = GetTriggerUnit()
local integer index = getIndex(u)
local integer n
if idunit[index] == u then //Ignore non-indexed units.
if not IsUnitType(u, UNIT_TYPE_DEAD) then
if dead[index] then //The unit was dead, but now it's alive.
set dead[index] = false //The unit has been resurrected.
//! runtextmacro optional RunAutoEvent("Resurrect")
//If AutoEvents is in the map, run the resurrection events.
if IsUnitType(u, UNIT_TYPE_SUMMONED) and not summoned[index] then
set summoned[index] = true //If the unit gained the summoned flag,
set animated[index] = true //it's been raised with Animate Dead.
//! runtextmacro optional RunAutoEvent("AnimateDead")
//If AutoEvents is in the map, run the Animate Dead events.
endif
endif
else
if not removing[index] and not dead[index] and not animated[index] then
set dead[index] = true //The unit was alive, but now it's dead.
set nodecay[index] = true //A dead unit can't decay for at least 0. seconds.
set allowdecay_n = allowdecay_n + 1 //Add the unit to a stack. After the timer
set allowdecayunit[allowdecay_n] = u //expires, allow the unit to decay again.
call TimerStart(allowdecay, 0., false, function AutoIndex.allowDecay)
//! runtextmacro optional RunAutoEvent("Death")
//If AutoEvents is in the map, run the Death events.
elseif removing[index] or (dead[index] and not nodecay[index]) or (not dead[index] and animated[index]) then
//If .nodecay was false and the unit is dead and was previously dead, the unit decayed.
//If .animated was true and the unit is dead, the unit died and exploded.
//If .removing was true, the unit is being removed or replaced.
set n = deindexfuncs_n
loop //Run the OnUnitDeindexed events.
exitwhen n < 0
call deindexfuncs[n].evaluate(u)
set n = n - 1
endloop
set n = destroyers_n
loop //Destroy AutoDestroy structs for the leaving unit.
exitwhen n < 0
call destroyers[n].evaluate(u)
set n = n - 1
endloop
call AutoIndex(index).destroy() //Free the index by destroying the AutoIndex struct.
set idunit[index] = null //Null this unit reference to prevent a leak.
endif
endif
endif
set u = null
return false
endmethod
//===========================================================================
private static method unitEntersMap takes unit u returns nothing
local integer index
local integer n = 0
if getIndex(u) != 0 then
return //Don't index a unit that already has an ID.
endif
static if LIBRARY_xebasic then
if GetUnitTypeId(u) == XE_DUMMY_UNITID then
return //Don't index xe dummy units.
endif
endif
if not UnitFilter(u) then
return //Don't index units that fail the unit filter.
endif
set index = create()
call setIndex(u, index) //Assign an index to the entering unit.
call UnitAddAbility(u, LeaveDetectAbilityID) //Add the leave detect ability to the entering unit.
call UnitMakeAbilityPermanent(u, true, LeaveDetectAbilityID) //Prevent it from disappearing on morph.
set dead[index] = IsUnitType(u, UNIT_TYPE_DEAD) //Reset all of the flags for the entering unit.
set summoned[index] = IsUnitType(u, UNIT_TYPE_SUMMONED) //Each of these flags are necessary to detect
set animated[index] = false //when a unit leaves the map.
set nodecay[index] = false
set removing[index] = false
debug set altered[index] = false //In debug mode, this flag tracks wheter a unit's index was altered.
set idunit[index] = u //Attach the unit that is supposed to have this index to the index.
if duringinit then //If a unit enters the map during initialization...
call GroupAddUnit(preplaced, u) //Add the unit to the preplaced units group. This ensures that
endif //all units are noticed by OnUnitIndexed during initialization.
loop //Create AutoCreate structs for the entering unit.
exitwhen n > creators_n
call creators[n].evaluate(u)
set n = n + 1
endloop
set n = 0
loop //Run the OnUnitIndexed events.
exitwhen n > indexfuncs_n
call indexfuncs[n].evaluate(u)
set n = n + 1
endloop
endmethod
private static method onIssuedOrder takes nothing returns boolean
static if SafeMode then //If SafeMode is enabled, perform this extra check.
if getIndex(GetTriggerUnit()) == 0 then //If the unit doesn't already have
call unitEntersMap(GetTriggerUnit()) //an index, then assign it one.
endif
endif
return GetIssuedOrderId() == 852056 //If the order is Undefend, allow detectStatus to run.
endmethod
private static method initEnteringUnit takes nothing returns boolean
call unitEntersMap(GetFilterUnit())
return false
endmethod
//===========================================================================
private static method afterInit takes nothing returns nothing
set duringinit = false //Initialization is over; set a flag.
call DestroyTimer(GetExpiredTimer()) //Destroy the timer.
call GroupClear(preplaced) //The preplaced units group is
call DestroyGroup(preplaced) //no longer needed, so clean it.
set preplaced = null
endmethod
private static method onInit takes nothing returns nothing
local region maparea = CreateRegion()
local rect bounds = GetWorldBounds()
local group g = CreateGroup()
local integer i = 15
static if not UseUnitUserData then
set ht = InitHashtable() //Only create a hashtable if it will be used.
endif
loop
exitwhen i < 0
call SetPlayerAbilityAvailable(Player(i), LeaveDetectAbilityID, false)
//Make the LeaveDetect ability unavailable so that it doesn't show up on the command card of every unit.
call TriggerRegisterPlayerUnitEvent(order, Player(i), EVENT_PLAYER_UNIT_ISSUED_ORDER, null)
//Register the "EVENT_PLAYER_UNIT_ISSUED_ORDER" event for each player.
call GroupEnumUnitsOfPlayer(g, Player(i), function AutoIndex.initEnteringUnit)
//Enum every non-filtered unit on the map during initialization and assign it a unique
//index. By using GroupEnumUnitsOfPlayer, even units with Locust can be detected.
set i = i - 1
endloop
call TriggerAddCondition(order, And(function AutoIndex.onIssuedOrder, function AutoIndex.detectStatus))
//The detectStatus method will fire every time a non-filtered unit recieves an undefend order.
//And() is used here to avoid using a trigger action, which starts a new thread and is slower.
call TriggerRegisterPlayerUnitEvent(creepdeath, Player(12), EVENT_PLAYER_UNIT_DEATH, null)
call TriggerAddCondition(creepdeath, function AutoIndex.detectStatus)
//The detectStatus method must also fire when a neutral hostile creep dies, in case it was
//sleeping. Sleeping creeps don't fire undefend orders on non-damaging deaths.
call RegionAddRect(maparea, bounds) //GetWorldBounds() includes the shaded boundry areas.
call TriggerRegisterEnterRegion(enter, maparea, function AutoIndex.initEnteringUnit)
//The filter function of an EnterRegion trigger runs instantly when a unit is created.
call TimerStart(CreateTimer(), 0., false, function AutoIndex.afterInit)
//After any time elapses, perform after-initialization actions.
call GroupClear(g)
call DestroyGroup(g)
call RemoveRect(bounds)
set g = null
set bounds = null
endmethod
endstruct
//===========================================================================
// User functions:
//=================
function GetUnitId takes unit u returns integer
static if DEBUG_MODE then //If debug mode is enabled...
return AutoIndex.getIndexDebug(u) //call the debug version of GetUnitId.
else //If debug mode is disabled...
return AutoIndex.getIndex(u) //call the normal, inlinable version.
endif
endfunction
function IsUnitIndexed takes unit u returns boolean
return AutoIndex.isUnitIndexed(u)
endfunction
function OnUnitIndexed takes IndexFunc func returns nothing
call AutoIndex.onUnitIndexed(func)
endfunction
function OnUnitDeindexed takes IndexFunc func returns nothing
call AutoIndex.onUnitDeindexed(func)
endfunction
endlibrary
//TESH.scrollpos=-1
//TESH.alwaysfold=0
library DamageEvent initializer Init requires optional DamageModifiers, optional LightLeaklessDamageDetect, optional IntuitiveDamageSystem, optional xedamage
//*****************************************************************
//* DAMAGE EVENT LIBRARY
//*
//* written by: Anitarf
//* supports: -DamageModifiers
//*
//* This is a damage detection library designed to compensate for
//* the lack of a generic "unit takes damage" event in JASS.
//*
//* All functions following the Response function interface that
//* is defined at the end of the calibration section of this
//* library can be used to respond to damage events. Simply add
//* such functions to the system's call list with the
//* RegisterDamageResponse function.
//*
//* function RegisterDamageResponse takes Response r returns nothing
//*
//* DamageEvent fully supports the use of DamageModifiers. As
//* long as you have the DamageModifiers library in your map
//* DamageEvent will use it to modify the damage before calling
//* the response functions.
//*
//* If the map contains another damage detection library,
//* DamageEvent will interface with it instead of creating
//* it's own damage event triggers to improve performance.
//* Currently supported libraries are LightLeaklessDamageDetect
//* and IntuitiveDamageSystem.
//*
//* DamageEvent is also set up to automatically ignore dummy
//* damage events sometimes caused by xedamage when validating
//* targets (only works with xedamage 0.7 or higher).
//*****************************************************************
globals
// In wc3, damage events sometimes occur when no real damage is dealt,
// for example when some spells are cast that don't really deal damage,
// so this system will only consider damage events where damage is
// higher than this threshold value.
private constant real DAMAGE_THRESHOLD = 0.0
// The following calibration options are only used if the system uses
// it's own damage detection triggers instead of interfacing with other
// damage event engines:
// If this boolean is true, the damage detection trigger used by this
// system will be periodically destroyed and remade, thus getting rid
// of damage detection events for units that have decayed/been removed.
private constant boolean REFRESH_TRIGGER = true
// Each how many seconds should the trigger be refreshed?
private constant real TRIGGER_REFRESH_PERIOD = 300.0
endglobals
private function CanTakeDamage takes unit u returns boolean
// You can filter out which units need damage detection events with this function.
// For example, dummy casters will never take damage so the system doesn't need to register events for them,
// by filtering them out you are reducing the number of handles the game will create, thus increasing performance.
//return GetUnitTypeId(u)!='e000' // This is a sample return statement that lets you ignore a specific unit type.
return true
endfunction
// This function interface is included in the calibration section
// for user reference only and should not be changed in any way.
public function interface Response takes unit damagedUnit, unit damageSource, real damage returns nothing
// END OF CALIBRATION SECTION
// ================================================================
globals
private Response array responses
private integer responsesCount = 0
endglobals
function RegisterDamageResponse takes Response r returns nothing
set responses[responsesCount]=r
set responsesCount=responsesCount+1
endfunction
private function Damage takes nothing returns nothing
// Main damage event function.
local unit damaged=GetTriggerUnit()
local unit damager=GetEventDamageSource()
local real damage=GetEventDamage()
local integer i = 0
loop
exitwhen not (damage>DAMAGE_THRESHOLD)
static if LIBRARY_xedamage then
exitwhen xedamage.isDummyDamage
endif
static if LIBRARY_DamageModifiers then
set damage=RunDamageModifiers()
endif
loop
exitwhen i>=responsesCount
call responses[i].execute(damaged, damager, damage)
set i=i+1
endloop
exitwhen true
endloop
set damaged=null
set damager=null
endfunction
private function DamageC takes nothing returns boolean
call Damage() // Used to interface with LLDD.
return false
endfunction
// ================================================================
globals
private group g
private boolexpr b
private boolean clear
private trigger currentTrg
private triggeraction currentTrgA
private trigger oldTrg = null
private triggeraction oldTrgA = null
endglobals
private function TriggerRefreshEnum takes nothing returns nothing
// Code "borrowed" from Captain Griffen's GroupRefresh function.
// This clears the group of any "shadows" left by removed units.
if clear then
call GroupClear(g)
set clear = false
endif
call GroupAddUnit(g, GetEnumUnit())
// For units that are still in the game, add the event to the new trigger.
call TriggerRegisterUnitEvent( currentTrg, GetEnumUnit(), EVENT_UNIT_DAMAGED )
endfunction
private function TriggerRefresh takes nothing returns nothing
// The old trigger is destroyed with a delay for extra safety.
// If you get bugs despite this then turn off trigger refreshing.
if oldTrg!=null then
call TriggerRemoveAction(oldTrg, oldTrgA)
call DestroyTrigger(oldTrg)
endif
// The current trigger is prepared for delayed destruction.
call DisableTrigger(currentTrg)
set oldTrg=currentTrg
set oldTrgA=currentTrgA
// The current trigger is then replaced with a new trigger.
set currentTrg = CreateTrigger()
set currentTrgA = TriggerAddAction(currentTrg, function Damage)
set clear = true
call ForGroup(g, function TriggerRefreshEnum)
if clear then
call GroupClear(g)
endif
endfunction
// ================================================================
private function DamageRegister takes nothing returns boolean
local unit u = GetFilterUnit()
if CanTakeDamage(u) then
call TriggerRegisterUnitEvent( currentTrg, u, EVENT_UNIT_DAMAGED )
call GroupAddUnit(g, u)
endif
set u = null
return false
endfunction
private function Init takes nothing returns nothing
local rect rec
local region reg
local trigger t
static if LIBRARY_IntuitiveDamageSystem then
// IDDS initialization code
set t=CreateTrigger()
call TriggerAddAction(t, function Damage)
call TriggerRegisterDamageEvent(t, 0)
else
static if LIBRARY_LightLeaklessDamageDetect then
// LLDD initialization code
call AddOnDamageFunc(Condition(function DamageC))
else
// DamageEvent initialization code
set rec = GetWorldBounds()
set reg = CreateRegion()
set t = CreateTrigger()
call RegionAddRect(reg, rec)
call TriggerRegisterEnterRegion(t, reg, Condition(function DamageRegister))
set currentTrg = CreateTrigger()
set currentTrgA = TriggerAddAction(currentTrg, function Damage)
set g = CreateGroup()
call GroupEnumUnitsInRect(g, rec, Condition(function DamageRegister))
if REFRESH_TRIGGER then
call TimerStart(CreateTimer(), TRIGGER_REFRESH_PERIOD, true, function TriggerRefresh)
endif
call RemoveRect(rec)
set rec = null
set b = null
endif
endif
endfunction
endlibrary
//TESH.scrollpos=-1
//TESH.alwaysfold=0
library DamageModifiers initializer Init requires AutoIndex, optional AbilityPreload, optional xepreload
//*****************************************************************
//* DAMAGE MODIFIERS LIBRARY (AutoIndex version)
//*
//* written by: Anitarf
//* requires: -AutoIndex
//*
//* This is a library that allows you to modify the damage taken
//* by units. It is the most robust system of this kind available,
//* it can both prevent and increase damage, when increasing
//* damage it still awards experience correctly, it can tolerate
//* users dealing additional damage on the damage event as well as
//* allow them to get the correct life of the unit on this event
//* rather than a value inflated by damage prevention.
//*
//* It is important to note that different damage modifying
//* systems can not operate in the same map, so you can not use
//* this system if you are already using a different one.
//*
//* Damage modifiers are implemented as structs that extend the
//* system's DamageModifier struct. A damage modifier is created
//* for a specific unit and can modify both the damage that the
//* unit deals and receives (using the onDamageDealt and
//* onDamageTaken methods respectively, see the interface defined
//* at the end of the calibration section).
//*
//* Keep in mind that when a struct extends DamageModifier, the
//* allocate method of that struct must match the create method of
//* DamageModifier:
//*
//* static method create takes unit u, integer priority returns DamageModifier
//*
//* The unit argument determines on which unit will the modifier
//* be applied and the priority argument determines the order in
//* which the modifiers will be evaluated. The system always
//* evaluates the modifiers on the unit dealing the damage and on
//* the unit taking the damage simultaneously, starting with the
//* modifiers with the highest priority. If two modifiers share
//* the same priority, the one on the unit dealing the damage
//* is evaluated first, if the two modifiers are on the same unit
//* then the NEW_MODIFIER_ON_TOP calibration constant determines
//* which gets evaluated first.
//*
//* Here is an example of a modifier struct:
//*
//* struct armour extends DamageModifier
//* // All additional struct members are optional:
//* static integer PRIORITY=0 // Default priority
//* real power // This lets us give different units differently strong armour.
//*
//* // create method is optional, if you don't declare one then you must use
//* // the .allocate parameters (unit, integer) when creating a modifier of this kind.
//* static method create takes unit u, real power returns armour
//* // Note the parameters for .allocate, this is because this struct extends
//* // DamageModifier which asks for these parameters in its create method:
//* local armour this = armour.allocate(u, armour.PRIORITY)
//* set this.power = power
//* return this
//* endmethod
//*
//* // This is the method that runs when damage is dealt to the unit with the modifier.
//* // The damage parameter tells how much damage got to this modifier past any modifiers
//* // with a higher priority that the unit may have.
//* // The value that the method returns tells the system by how much to modify the damage,
//* // a positive return value increases damage while a negative value reduces it.
//* method onDamageTaken takes unit damageSource, real damage returns real
//* // This is a simple modifier that just gives a flat damage reduction.
//* if this.power>damage then
//* return -damage
//* endif
//* return -this.power
//* endmethod
//* // There is no onDamageDealt method so this modifier does not affect the damage that the unit deals.
//* // onDestroy method is optional, in this case we don't need it.
//* endstruct
//*
//* In order for the system to modify damage, you need to call the
//* RunDamageModifiers function whenever a damage event occurs.
//* Since there is no generic "unit takes damage" event in JASS,
//* you need to use a damage detection system such as DamageEvent,
//* which is already designed to support DamageModifiers.
//*
//* function RunDamageModifiers takes nothing returns real
//*
//* The real value that the RunDamageModifiers function returns
//* equals what the damage is after it has been affected by all
//* modifiers.
//*
//* To be able to modify damage in situations where it exceeds the
//* max hp of the damaged unit, this system must temporarily add a
//* bonus hp ability to the unit. An external Objectmerger call is
//* provided in the calibration section that generates this
//* ability in a map automatically when the map is saved. The
//* ability will also get automatically preloaded to prevent
//* first-use lag as long as either the xepreload library or the
//* AbilityPreload library is present in the map.
//*
//* The library also provides the functions GetUnitLife and
//* SetUnitLife. When used outside damage detection, these
//* functions work the same as the natives GetWidgetLife and
//* SetWidgetLife, however when called on a unit that has damage
//* stacked on it these functions will return/modify the life the
//* unit will have after the damage resolves, rather than its
//* current life which may or may not be inflated by damage
//* prevention. Again, the functions are:
//*
//* function GetUnitLife takes unit u returns real
//* function SetUnitLife takes unit u, real newLife returns nothing
//*****************************************************************
globals
// If two modifiers have the same priority, which one should
// modify the damage first? If true, the newer modifier will.
private constant boolean NEW_MODIFIER_ON_TOP = true
// If more damage is dealt to the unit than the unit's max hp, then this
// system must temporarily add a bonus hp ability to the unit in order to
// facilitate damage prevention.
// An objectmerger call is provided below that automatically generates
// the ability when you save the map in the NewGen world editor.
private constant integer SURVIVAL_ABILITY = 'DMsa'
endglobals
// This objectmerger call only needs to run once to generate the ability in a map,
// just save the map to run it, then close the map and re-open it and the ability
// should be there, after that you can disable the objectmerget call to speed up
// the map saving process in the future.
// (To disable the call, delete the "!" so it turns from a command into a comment.)
//! external ObjectMerger w3a AIl1 DMsa anam "LifeBonus" ansf "(DamageModifiers)" Ilif 1 100000 aite 0
// This interface is included in the calibration section
// for user reference only and should not be changed in any way.
// It is private because your shields shouldn't extend it directly,
// but should extend the DamageModifer struct instead.
private interface DamageModiferTemplate
// Returned real determines by how much the damage the unit deals should be modified.
method onDamageDealt takes unit damagedUnit, real damage returns real defaults 0.0
// Returned real determines by how much the damage the unit is dealt should be modified.
method onDamageTaken takes unit damageSource, real damage returns real defaults 0.0
endinterface
// END OF CALIBRATION SECTION
// ================================================================
private keyword Evaluate
struct DamageModifier extends DamageModiferTemplate
private unit target
private DamageModifier next=0
integer priority
private static DamageModifier array first
private method listInsert takes nothing returns nothing
local DamageModifier dm=DamageModifier.first[GetUnitId(.target)]
if dm==0 or dm.priority<.priority or (NEW_MODIFIER_ON_TOP and dm.priority==.priority) then
set .next=DamageModifier.first[GetUnitId(.target)]
set DamageModifier.first[GetUnitId(.target)]=this
else
loop
exitwhen dm.next == 0 or dm.next.priority<.priority or (NEW_MODIFIER_ON_TOP and dm.next.priority==.priority)
set dm=dm.next
endloop
set .next=dm.next
set dm.next=this
endif
endmethod
private method listRemove takes nothing returns nothing
local DamageModifier dm=DamageModifier.first[GetUnitId(.target)]
if dm==this then
set DamageModifier.first[GetUnitId(.target)]=.next
else
loop
exitwhen dm.next==this
set dm=dm.next
endloop
set dm.next=.next
endif
endmethod
static method create takes unit u, integer priority returns DamageModifier
local DamageModifier this
if u==null then
return 0
endif
set this=DamageModifier.allocate()
set .target=u
set .priority = priority
call .listInsert()
return this
endmethod
method onDestroy takes nothing returns nothing
call .listRemove()
endmethod
static method Evaluate takes unit damaged, unit damager, real damage returns real
// Loops through all the modifiers involved in a damage event and
// returns the total amount by which the damage must be modified.
// Positive values meaning a damage increase and negative a damage decrease.
local real modifier=0.0
local DamageModifier this=DamageModifier(DamageModifier.first[GetUnitId(damager)])
local DamageModifier that=DamageModifier(DamageModifier.first[GetUnitId(damaged)])
loop
exitwhen this==0 and that==0
if this!=0 and (that==0 or this.priority>=that.priority) then
set modifier=modifier+this.onDamageDealt(damaged, damage+modifier)
set this=this.next
elseif that !=0 then
set modifier=modifier+that.onDamageTaken(damager, damage+modifier)
set that=that.next
endif
endloop
return modifier
endmethod
endstruct
// ================================================================
private struct Damage
unit u
real hp=0.0
real dmg=0.0
real temp=0.0
private Damage next
private static Damage first=0
private static timer delay=CreateTimer()
private static Damage array unitlist
private static method get takes unit u returns Damage
return Damage.unitlist[GetUnitId(u)]
endmethod
private static method create takes unit u returns Damage
// The only time a Damage is created is when it doesn't yet exist for the unit.
local Damage this=Damage.allocate()
set .u = u
set .next = Damage.first
set Damage.first=this
set Damage.unitlist[GetUnitId(.u)]=this
return this
endmethod
method onDestroy takes nothing returns nothing
// The only time a Damage is destroyed is when it is first, so this works.
set Damage.first=.next
set Damage.unitlist[GetUnitId(.u)]=0
set .u = null
endmethod
private static method end takes nothing returns nothing
// This method is called with a very slight delay, which allows it to run
// after the life of the damaged unit has already been reduced by the damage.
loop
exitwhen Damage.first==0
call UnitRemoveAbility(Damage.first.u, SURVIVAL_ABILITY)
call SetWidgetLife(Damage.first.u, Damage.first.hp)
call Damage.first.destroy()
endloop
endmethod
static method RunModifiers takes nothing returns real
local unit damaged = GetTriggerUnit()
local unit damager = GetEventDamageSource()
local real damage = GetEventDamage()
local real modifier = 0.0
local real life = GetWidgetLife(damaged)
local real maxlife = GetUnitState(damaged,UNIT_STATE_MAX_LIFE)
local Damage d
if damaged==null then
// Method not called from a damage event, terminate.
set damager=null
set damaged=null
return 0.0
endif
// Calculate by how much to modify the damage.
set modifier=DamageModifier.Evaluate(damaged, damager, damage)
// Modify the damage.
set d=Damage.get(damaged)
if d==0 then
// First damage, create a damage struct.
set d=Damage.create(damaged)
set d.hp=life
call TimerStart(Damage.delay, 0.0, false, function Damage.end)
endif
// Update the damage struct.
set d.hp=d.hp-(damage+modifier)
set d.dmg=d.dmg+damage
// If the unit's life couldn't be reduced or increased as much as we wanted
// on a previous damage event, it's time we compensate for that now.
set modifier=modifier+d.temp
set d.temp=0.0
if maxlife-d.dmg<=0.405 then
// Enough damage stacked to potentially kill the unit, need a survival ability.
call UnitAddAbility(damaged, SURVIVAL_ABILITY)
set maxlife = GetUnitState(damaged,UNIT_STATE_MAX_LIFE)
endif
if life-modifier>0.405 then
// Set the unit's life so that after the damage is dealt, it will be correct.
call SetWidgetLife(damaged, life-modifier)
if life-modifier>maxlife then
// The unit's maxlife is preventing us from increasing life as needed,
// we need to remember that in the case the survival ability is ever added
// to the unit on another damage event before this damage resolves.
set d.temp=modifier-(life-maxlife)
endif
else
// If we have a damage modifier value that would kill the unit,
// we instead reduce the unit's hp so the actual damage can kill it,
// thus awarding proper bounty.
call SetWidgetLife(damaged, 0.406)
// We need to remember that we failed to reduce the life as needed in case
// damage is dealt in response to this damage and modified into the negative.
set d.temp=modifier-(life-0.406)
endif
set damaged=null
set damager=null
return damage+modifier
endmethod
static method getLife takes unit u returns real
local Damage d=Damage.get(u)
if d==0 then
return GetWidgetLife(u)
endif
return d.hp
endmethod
static method setLife takes unit u, real newLife returns nothing
local Damage d=Damage.get(u)
local real modifier
local real delta
if d==0 then
// Unit not taking damage, proceed with setting the unit's life.
call SetWidgetLife(u, newLife)
else
// Calculate by how much the unit's life will change after the damage.
set modifier=newLife-d.hp
if modifier<0 or d.hp>0 then
// Nothing to worry about, the rest of the code will handle everything.
set d.hp=newLife
set d.temp=d.temp-modifier
else
// Increasing life in a situation where the damage is about to kill the unit,
// so we must intervene and increase the unit's actual life as well.
set d.hp=newLife
set delta=GetUnitState(u,UNIT_STATE_MAX_LIFE)-GetWidgetLife(u)
if delta>modifier then
call SetWidgetLife(u, GetWidgetLife(u)+modifier)
else
call SetWidgetLife(u, GetWidgetLife(u)+delta)
set d.temp=d.temp-(modifier-delta)
endif
endif
endif
endmethod
endstruct
private function Init takes nothing returns nothing
static if LIBRARY_xepreload then
call XE_PreloadAbility(SURVIVAL_ABILITY)
else
static if LIBRARY_AbilityPreload then
call AbilityPreload(SURVIVAL_ABILITY)
endif
endif
endfunction
// ================================================================
// To make this system work, call this function once whenever a damage event occurs.
function RunDamageModifiers takes nothing returns real
return Damage.RunModifiers()
endfunction
// These functions allow you to get/set the proper life of a unit even when it's taking damage.
function GetUnitLife takes unit u returns real
return Damage.getLife(u)
endfunction
function SetUnitLife takes unit u, real newLife returns nothing
call Damage.setLife(u, newLife)
endfunction
endlibrary
//TESH.scrollpos=-1
//TESH.alwaysfold=0
library Table
//***************************************************************
//* Table object 3.0
//* ------------
//*
//* set t=Table.create() - instanceates a new table object
//* call t.destroy() - destroys it
//* t[1234567] - Get value for key 1234567
//* (zero if not assigned previously)
//* set t[12341]=32 - Assigning it.
//* call t.flush(12341) - Flushes the stored value, so it
//* doesn't use any more memory
//* t.exists(32) - Was key 32 assigned? Notice
//* that flush() unassigns values.
//* call t.reset() - Flushes the whole contents of the
//* Table.
//*
//* call t.destroy() - Does reset() and also recycles the id.
//*
//* If you use HandleTable instead of Table, it is the same
//* but it uses handles as keys, the same with StringTable.
//*
//* You can use Table on structs' onInit if the struct is
//* placed in a library that requires Table or outside a library.
//*
//* You can also do 2D array syntax if you want to touch
//* mission keys directly, however, since this is shared space
//* you may want to prefix your mission keys accordingly:
//*
//* set Table["thisstring"][ 7 ] = 2
//* set Table["thisstring"][ 5 ] = Table["thisstring"][7]
//*
//***************************************************************
//=============================================================
globals
private constant integer MAX_INSTANCES=8100 //400000
//Feel free to change max instances if necessary, it will only affect allocation
//speed which shouldn't matter that much.
//=========================================================
private hashtable ht
endglobals
private struct GTable[MAX_INSTANCES]
method reset takes nothing returns nothing
call FlushChildHashtable(ht, integer(this) )
endmethod
private method onDestroy takes nothing returns nothing
call this.reset()
endmethod
//=============================================================
// initialize it all.
//
private static method onInit takes nothing returns nothing
set ht = InitHashtable()
endmethod
endstruct
//Hey: Don't instanciate other people's textmacros that you are not supposed to, thanks.
//! textmacro Table__make takes name, type, key
struct $name$ extends GTable
method operator [] takes $type$ key returns integer
return LoadInteger(ht, integer(this), $key$)
endmethod
method operator []= takes $type$ key, integer value returns nothing
call SaveInteger(ht, integer(this) ,$key$, value)
endmethod
method flush takes $type$ key returns nothing
call RemoveSavedInteger(ht, integer(this), $key$)
endmethod
method exists takes $type$ key returns boolean
return HaveSavedInteger( ht, integer(this) ,$key$)
endmethod
static method flush2D takes string firstkey returns nothing
call $name$(- StringHash(firstkey)).reset()
endmethod
static method operator [] takes string firstkey returns $name$
return $name$(- StringHash(firstkey) )
endmethod
endstruct
//! endtextmacro
//! runtextmacro Table__make("Table","integer","key" )
//! runtextmacro Table__make("StringTable","string", "StringHash(key)" )
//! runtextmacro Table__make("HandleTable","handle","GetHandleId(key)" )
endlibrary
//TESH.scrollpos=-1
//TESH.alwaysfold=0
library Messages initializer Init
private function CreditsProxy takes nothing returns nothing
call ExecuteFunc(SCOPE_PRIVATE+"Credits")
endfunction
private function Commands takes nothing returns nothing
call ClearTextMessages()
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,30, "|cff00bfffCommands:")
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,30, " |cffffcc00-reset|r: Spawns some footmen around you.")
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,30, " |cffffcc00-level |cff0000e0<level>|r: Sets the level of your hero to the specified level.")
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,30, " Note that you cannot decrease your hero's level this way.")
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,30, " |cffffcc00-handleid|r: Creates a location, displays its index - 0x1000001 and destroys it.")
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,30, " |cffffcc00-commands|r: Shows this list.")
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,30, " |cffffcc00-credits|r: Shows the credits.")
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,30, " |cffffcc00-clear|r: Clears all text-messages.")
if GetExpiredTimer()!=null then
call TimerStart(GetExpiredTimer(), 60., false, function CreditsProxy)
endif
endfunction
private function Credits takes nothing returns nothing
call ClearTextMessages()
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,30, "|cff00bfffCredits:")
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,30, " - |cffffcc00Anitarf|r for his DamageEvent library")
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,30, " - |cffffcc00grim001|r for his AutoIndex library")
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,30, " - |cffffcc00Rising_Dusk|r for his GroupUtils library")
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,30, " - |cffffcc00Vexorian|r for JassHelper and Table")
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,30, " - |cffffcc00PipeDream|r for Grimoire")
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,30, " - |cffffcc00MindWorX|r and |cffffcc00PitzerMike|r for JassNewGenPack")
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,30, " - |cffffcc00SFilip|r for TESH")
if GetExpiredTimer()!=null then
call TimerStart(GetExpiredTimer(), 60., false, function Commands)
endif
endfunction
private function ClearActions takes nothing returns nothing
call ClearTextMessages()
endfunction
private function Init takes nothing returns nothing
local trigger t=CreateTrigger()
call TriggerRegisterPlayerChatEvent(t, Player(0), "-clear", true)
call TriggerAddAction(t, function ClearActions)
set t=CreateTrigger()
call TriggerRegisterPlayerChatEvent(t, Player(0), "-commands", true)
call TriggerAddAction(t, function Commands)
set t=CreateTrigger()
call TriggerRegisterPlayerChatEvent(t, Player(0), "-credits", true)
call TriggerAddAction(t, function Credits)
call TimerStart(CreateTimer(), 60., false, function Commands)
endfunction
endlibrary
//TESH.scrollpos=-1
//TESH.alwaysfold=0
library Basic initializer Init
globals
unit HERO
endglobals
function H2I takes handle h returns integer
return GetHandleId(h)
endfunction
private function Init takes nothing returns nothing
set HERO=CreateUnit(Player(0), 'HERO', 0,0,0)
call SetWidgetLife(HERO, GetWidgetLife(HERO)/2)
call PanCameraToTimed(0,0,0)
call FogEnable(false)
call FogMaskEnable(false)
endfunction
endlibrary
//TESH.scrollpos=-1
//TESH.alwaysfold=0
library Reset initializer Init
private function Actions takes nothing returns nothing
local real r
set r=0
loop
call CreateUnit(Player(12), 'hfoo', 0+(256*Cos(r)), 512+(256*Sin(r)), 0)
set r=r+(bj_PI/4)
exitwhen r>=(7*bj_PI)/4
endloop
set r=0
loop
call CreateUnit(Player(12), 'hfoo', 512+(256*Cos(r)), 0+(256*Sin(r)), 0)
set r=r+(bj_PI/4)
exitwhen r>=(7*bj_PI)/4
endloop
set r=0
loop
call CreateUnit(Player(12), 'hfoo', 0+(256*Cos(r)), -512+(256*Sin(r)), 0)
set r=r+(bj_PI/4)
exitwhen r>=(7*bj_PI)/4
endloop
set r=0
loop
call CreateUnit(Player(12), 'hfoo', -512+(256*Cos(r)), 0+(256*Sin(r)), 0)
set r=r+(bj_PI/4)
exitwhen r>=(7*bj_PI)/4
endloop
endfunction
private function Init takes nothing returns nothing
local trigger t=CreateTrigger()
call TriggerAddAction(t, function Actions)
call TriggerRegisterPlayerChatEvent(t, Player(0), "-reset", true)
endfunction
endlibrary
//TESH.scrollpos=-1
//TESH.alwaysfold=0
library LevelUp initializer Init uses Basic
private function LevelAction takes nothing returns nothing
local integer newlvl
local string s = GetEventPlayerChatString()
if SubString(s, 0, 7)=="-level " then
set newlvl=S2I(SubString(s, 7, StringLength(s)))
if newlvl>GetUnitLevel(HERO) and newlvl<=10 then
call SetHeroLevel(HERO, newlvl, true)
else
debug call BJDebugMsg("LevelUp - Wrong new level!")
endif
else
debug call BJDebugMsg("LevelUp - Don't try to trick my triggers!")
endif
endfunction
private function Init takes nothing returns nothing
local trigger t=CreateTrigger()
call TriggerRegisterPlayerChatEvent(t, Player(0), "-level ", false)
call TriggerAddAction(t, function LevelAction)
endfunction
endlibrary
//TESH.scrollpos=-1
//TESH.alwaysfold=0
function Trig_handleid_Actions takes nothing returns nothing
local location l=Location(0,0)
call BJDebugMsg("Max Handle ID: "+I2S(H2I(l)-0x100001))
call RemoveLocation(l)
set l=null
endfunction
//===========================================================================
function InitTrig_handleid takes nothing returns nothing
set gg_trg_handleid = CreateTrigger( )
call TriggerRegisterPlayerChatEvent(gg_trg_handleid, Player(0), "-handleid", true)
call TriggerAddAction( gg_trg_handleid, function Trig_handleid_Actions )
endfunction