Name | Type | is_array | initial_value |
t | timer | No |
//TESH.scrollpos=0
//TESH.alwaysfold=0
function Trig_Melee_Initialization_Actions takes nothing returns nothing
call FogEnable(false)
call FogMaskEnable(false)
endfunction
//===========================================================================
function InitTrig_Melee_Initialization takes nothing returns nothing
set gg_trg_Melee_Initialization = CreateTrigger( )
call TriggerAddAction( gg_trg_Melee_Initialization, function Trig_Melee_Initialization_Actions )
endfunction
//TESH.scrollpos=293
//TESH.alwaysfold=0
scope fireWall initializer Init
// Fire Wall spell v1.01
// made by MaskedPoptart
//
// inspired by the spell of the same name in diablo 2
//================== START SETUP SECTION =======================
//If you understand what something does, go ahead and change it as you desire.
globals
private constant integer SPELL_ID = 'A000' //CHANGE THIS. rawcode of your FireWall ability in object editor.
private constant string FLAME_EFFECT = "Environment\\LargeBuildingFire\\LargeBuildingFire2.mdl"
private constant string FLAME_EFFECT2 = "Abilities\\Spells\\Other\\ImmolationRed\\ImmolationREDTarget.mdl"
private constant real FLAME_SPACING = 40.0
private constant real FLAME_SPAWN_INTERVAL = 0.0029*FLAME_SPACING
private constant real DAMAGE_INTERVAL = 0.2 //how often units are damaged by the fire wall, in seconds.
//increase the interval slightly if it causes the game to slow down.
private constant real DAMAGE_RADIUS = 100.0 //the maximum distance a unit can be from the wall and still be
//damaged. if this number is less than 0.5*FLAME_SPACING, units will be able to walk through the wall undamaged.
private constant string FIRE_DAMAGE_UNIT_EFFECT = "Abilities\\Spells\\Other\\ImmolationRed\\ImmolationRedDamage.mdl"
private constant string FIRE_DAMAGE_UNIT_ATTACH = "chest"
private constant real FIRE_DAMAGE_UNIT_EFFECT_LENGTH = 1.2
private constant string FIRE_DAMAGE_DEST_EFFECT = "Environment\\LargeBuildingFire\\LargeBuildingFire2.mdl"
private constant string FIRE_DAMAGE_DEST_ATTACH = "Stump0"
private constant real FIRE_DAMAGE_DEST_EFFECT_LENGTH = 0.967
private constant boolean DAMAGE_DESTRUCTABLES = true
endglobals
//returns the length of time that the wall will burn
private function GetDuration takes integer level returns real
return 10.0+5.0*level
endfunction
//returns how long the wall is
private function GetWallLength takes integer level returns real
return 64.0+192.0*level
endfunction
//returns the damage per second that the wall causes
private function GetDPS takes integer level returns real
return 50.0*level
endfunction
//used by the damage filters
private function IsValidUnitTarget takes unit u, player ownerOfCaster returns boolean
return not IsUnitType(u, UNIT_TYPE_DEAD) and/*
*/ not IsUnitType(u, UNIT_TYPE_MAGIC_IMMUNE) and/*
*/ IsUnitType(u, UNIT_TYPE_GROUND) and/*
*/ (IsUnitEnemy(u, ownerOfCaster) or/*
*/ GetOwningPlayer(u) == Player(PLAYER_NEUTRAL_PASSIVE))
endfunction
//used by the damage filters
private function IsValidDestTarget takes destructable d returns boolean
return GetDestructableLife(d) > 0
endfunction
//=================== END SETUP SECTION =======================
// DON'T TOUCH ANY OF THE CRAP AFTER THIS LINE
//returns the number of flames that make up the wall
private function GetNumFlames takes integer level returns integer
return R2I(GetWallLength(level)/FLAME_SPACING)
endfunction
private function Conditions takes nothing returns boolean
return GetSpellAbilityId() == SPELL_ID
endfunction
//each flame of the wall has its own struct instance
struct Flame
//each flame has two effects for optimal eye candy
private effect flameEffect
private effect flameEffect2
//each flame has a position, of course...
readonly real flameX
readonly real flameY
//each flame is spawned at a different time, so it needs a timer to tell it when to burn out.
private timer flameTimer
//returns true if flame is currently burning
readonly boolean isBurning
static method Destroy takes nothing returns nothing
local thistype f = GetTimerData(GetExpiredTimer())
call f.destroy()
endmethod
static method create takes real x, real y, integer level returns thistype
local thistype f = thistype.allocate()
set f.isBurning = true
set f.flameX = x
set f.flameY = y
set f.flameEffect = AddSpecialEffect(FLAME_EFFECT, x, y)
set f.flameEffect2 = AddSpecialEffect(FLAME_EFFECT2, x, y)
set f.flameTimer = NewTimer()
call SetTimerData(f.flameTimer, f)
//destroy f after GetDuration seconds
call TimerStart(f.flameTimer, GetDuration(level), false, function thistype.Destroy)
return f
endmethod
private method onDestroy takes nothing returns nothing
//put out the flame, clean up stuff
set this.isBurning = false
call DestroyEffect(this.flameEffect)
call DestroyEffect(this.flameEffect2)
call ReleaseTimer(this.flameTimer)
set this.flameTimer = null
endmethod
endstruct
private keyword FireWall
globals
//for use in the damage filters (below)
private rect destructableDamageRect
private group tempGroup = CreateGroup()
private FireWall tempFireWall
endglobals
//each "FireWall" instance contains a bunch of Flame instances as well as other properties
struct FireWall
private unit caster //caster of the spell
private player owner //owner of caster
private IntegerList flames //flames is a List of Flame struct objects.
//It's better to use a List than an array in this situation because arrays
//make terrible struct members, etc. See List documentation if you care.
private integer level //level of the spell
private real centerX //target x of casted spell
private real centerY //target y of casted spell
private real normalX //x component of perpendicular normalized vector (????)
private real normalY //y component of perpendicular normalized vector (don't worry about it)
private timer flameSpawnTimer //this is a repeating timer that will gradually form the wall by
//spawning pairs of flames.
private integer totalWidth //how many flames to spawn on each side of the wall
private integer currentWidth//how many flames are currently on each side of the wall (while spawning)
private timer damageTimer //this timer damages units near each flame, every DAMAGE_INTERVAL seconds
private static hashtable damagedTargets //this keeps track of units and/or destructables that were
//already damaged by this wall, during this DAMAGE_INTERVAL
private static boolexpr DamageUnitExpr //stores Filter(function thistype.DamageUnitFilter)
private static boolexpr DamageDestExpr //stores Filter(function thistype.DamageDestFilter)
//this method is used to periodically add one flame to both sides of the wall,
//if the total number of flames in the wall is supposed to be odd.
method addFlamesOdd takes nothing returns nothing
local integer i = this.currentWidth
local real x
local real y
set x = this.centerX + i*FLAME_SPACING*this.normalX //see how we can use vectors to efficiently
set y = this.centerY + i*FLAME_SPACING*this.normalY //form a perpendicular wall of flame objects?
call this.flames.addLast(Flame.create(x, y, this.level)) //create a flame and add it to the
//end of the list.
set x = this.centerX - i*FLAME_SPACING*this.normalX
set y = this.centerY - i*FLAME_SPACING*this.normalY
call this.flames.addFirst(Flame.create(x, y, this.level))//create a flame and add it to the
//start of the list.
//notice that we are able to keep the list sorted according to the order of the flames
// in the wall.
set this.currentWidth = i + 1
if(this.currentWidth>this.totalWidth)then //if we've spawned all the flames,
call ReleaseTimer(this.flameSpawnTimer) //stop the spawn timer
endif
endmethod
//Timers can't execute instance methods, so it executes a static method that redirects
//to an instance method, using the stored TimerData.
static method AddFlamesOdd takes nothing returns nothing
local thistype fw = GetTimerData(GetExpiredTimer())
call fw.addFlamesOdd()
endmethod
//this method is used to periodically add one flame to both sides of the wall,
//if the total number of flames in the wall is supposed to be even.
method addFlamesEven takes nothing returns nothing
local integer i = this.currentWidth
local real x
local real y
set x = this.centerX + (i+.5)*FLAME_SPACING*this.normalX
set y = this.centerY + (i+.5)*FLAME_SPACING*this.normalY
call this.flames.addLast(Flame.create(x, y, this.level))//same deal as addFlamesOdd
set x = this.centerX - (i+.5)*FLAME_SPACING*this.normalX
set y = this.centerY - (i+.5)*FLAME_SPACING*this.normalY
call this.flames.addFirst(Flame.create(x, y, this.level))
set this.currentWidth = i + 1
if(this.currentWidth>this.totalWidth)then
call ReleaseTimer(this.flameSpawnTimer)
endif
endmethod
static method AddFlamesEven takes nothing returns nothing
local thistype fw = GetTimerData(GetExpiredTimer())
call fw.addFlamesEven()
endmethod
//I don't like looping through groups twice, so I filter out units AND damage them
//in the filter.
static method DamageUnitFilter takes nothing returns boolean
local unit u = GetFilterUnit()
local real d = GetDPS(tempFireWall.level)*DAMAGE_INTERVAL
if(IsValidUnitTarget(u, tempFireWall.owner) /*
*/and LoadInteger(.damagedTargets, tempFireWall, GetHandleId(u)) == 0)then
//Usually one unit will be in range of more than one flame of the same wall. To keep
// the unit from being damaged multiple times by the same wall, we attach an integer
// to it in the static hashtable damagedTargets.
// 0 == not damaged yet this interval.
// 1 == already damaged this interval.
call UnitDamageTarget(tempFireWall.caster, u, d, false, true, ATTACK_TYPE_MAGIC,/*
*/ DAMAGE_TYPE_FIRE, WEAPON_TYPE_WHOKNOWS)
//here we use TimedBuffEffects to make the fire damage effect appear on the unit
// without stacking on previous fire damage effects
call AddBuffEffectTarget(FIRE_DAMAGE_UNIT_EFFECT, u,/*
*/ FIRE_DAMAGE_UNIT_ATTACH, FIRE_DAMAGE_UNIT_EFFECT_LENGTH)
//unit has now been damaged, so save 1 in the hashtable
call SaveInteger(.damagedTargets, tempFireWall, GetHandleId(u), 1)
endif
return false
endmethod
//same as above, but for destructables
static method DamageDestFilter takes nothing returns boolean
local destructable dest = GetFilterDestructable()
local real dmg = GetDPS(tempFireWall.level)*DAMAGE_INTERVAL
if(IsValidDestTarget(dest) /*
*/and LoadInteger(.damagedTargets, tempFireWall, GetHandleId(dest)) == 0)then
call UnitDamageTarget(tempFireWall.caster, dest, dmg, false, true, ATTACK_TYPE_MAGIC,/*
*/DAMAGE_TYPE_FIRE, WEAPON_TYPE_WHOKNOWS)
call SaveInteger(.damagedTargets, tempFireWall, GetHandleId(dest), 1)
endif
return false
endmethod
static method onInit takes nothing returns nothing
//this isn't completely necessary, but it keeps us from having to do an extra function call every loop.
set .DamageDestExpr = Filter(function thistype.DamageDestFilter)
set .DamageUnitExpr = Filter(function thistype.DamageUnitFilter)
endmethod
//This method loops through all the flames in the list.
//Each flame finds units in range of it, and damages them if they pass the conditions.
//keep in mind the filter checks the conditions and does the damaging.
method damageAllFlames takes nothing returns nothing
local Flame iFlame //current Flame object
local integer i //current index
if(this.flames.size>0)then
call FlushChildHashtable(.damagedTargets, this) //reset hashtable at start of each interval
set i = 0 //start at first index of list
loop
exitwhen i>=this.flames.size //stop after last index of list
set iFlame = Flame(this.flames[i]) //set iFlame to object stored at index i in the flames IntegerList
if(iFlame.isBurning)then //if flame is burning
set tempFireWall = this //store this FireWall in a temp global, damage units near iFlame's position.
call GroupEnumUnitsInRange(tempGroup, iFlame.flameX, iFlame.flameY, DAMAGE_RADIUS, .DamageUnitExpr)
if(DAMAGE_DESTRUCTABLES)then
call SetRect(destructableDamageRect, iFlame.flameX-DAMAGE_RADIUS, /*
*/iFlame.flameY-DAMAGE_RADIUS, iFlame.flameX+DAMAGE_RADIUS, iFlame.flameY+DAMAGE_RADIUS)
call EnumDestructablesInRect(destructableDamageRect, .DamageDestExpr, null)
endif
else
call this.flames.remove(i) //remove flame from list
endif
set i = i + 1
endloop
else
//individual flames burn out automatically because of their timers.
//after they burn out, we remove them from the list (see above).
//if there are no flames left (flames.size == 0), destroy this FireWall.
call this.destroy()
endif
endmethod
static method DamageAllFlames takes nothing returns nothing
local thistype fw = GetTimerData(GetExpiredTimer())
call fw.damageAllFlames()
endmethod
static method create takes unit caster, real targetX, real targetY, integer level returns thistype
local thistype fw = thistype.allocate()
local integer numFlames = GetNumFlames(level)
local real Dx = targetX - GetUnitX(caster) //Dx = x distance from caster to target
local real Dy = targetY - GetUnitY(caster) //Dy = y distance from caster to target
local real D = SquareRoot(Dx*Dx + Dy*Dy) //D = combined distance
local integer i
set fw.caster = caster
set fw.owner = GetOwningPlayer(caster)
set fw.centerX = targetX
set fw.centerY = targetY
set fw.normalX = -Dy/D //remember how to get a perpendicular line you take the negative reciprocal?
set fw.normalY = Dx/D //and to get a vector of length 1, you divide each component by the current length
set fw.level = level
set fw.flames = IntegerList.create() //initialize the IntegerList so it can store stuff.
set fw.totalWidth = numFlames/2 //width is from the center to the edge, remember?
set fw.flameSpawnTimer = NewTimer()
call SetTimerData(fw.flameSpawnTimer, fw)
if(numFlames/2*2 != numFlames)then //if numFlames is 3, for example, 3/2 =1.5 but 1.5 is converted to an
//integer by cutting off the stuff after the decimal. So, 3/2 = 1. 1*2 != 3, so 3 must be odd.
//Basically this checks if numFlames is odd.
call fw.flames.addLast(Flame.create(targetX, targetY, level))//create one flame at the center
set fw.currentWidth = 1
call TimerStart(fw.flameSpawnTimer, FLAME_SPAWN_INTERVAL, true, function thistype.AddFlamesOdd)
else
set fw.currentWidth = 0
call fw.addFlamesEven()//create two flames equally spaced from the center
call TimerStart(fw.flameSpawnTimer, FLAME_SPAWN_INTERVAL, true, function thistype.AddFlamesEven)
endif
set fw.damageTimer = NewTimer()
call SetTimerData(fw.damageTimer, fw)
call TimerStart(fw.damageTimer, DAMAGE_INTERVAL, true, function thistype.DamageAllFlames)
set fw.damagedTargets = InitHashtable()
return fw
endmethod
private method onDestroy takes nothing returns nothing
//clean up crap
call ReleaseTimer(this.damageTimer)
set this.damageTimer = null
call this.flames.destroy()
set this.flames = 0
set this.caster = null
call FlushChildHashtable(this.damagedTargets, this)
set this.damagedTargets = null
endmethod
endstruct
private function Actions takes nothing returns nothing
call FireWall.create(GetTriggerUnit(), GetSpellTargetX(), GetSpellTargetY(), GetUnitAbilityLevel(GetTriggerUnit(), SPELL_ID))
endfunction
private function Init takes nothing returns nothing
local trigger trig = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(trig, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(trig, Condition(function Conditions))
call TriggerAddAction(trig, function Actions)
set destructableDamageRect = Rect(0.0, 0.0, 1.0, 1.0)
endfunction
endscope
//TESH.scrollpos=0
//TESH.alwaysfold=0
library List
//==================================================================================
// List version 1.1
//----------------------------
// Use this version in your map.
//==================================================================================
// - made by MaskedPoptart
// - imitates the functionality of ArrayLists (from Java)
// using a hashtable.
//==================================================================================
// Basic Usage:
// set arr = IntegerList.create() - instantiate a List of integers
// arr.size - get the current number of elements
// call arr.addLast(5732) - add 5732 to the end of the list,
// increase .size by 1
// arr.removeLast() - remove the last element in the list,
// decrease .size by 1, return removed element
// arr.remove(6) - remove the 6th element, decrease .size by 1,
// return removed element, shift elements down
// arr[3] - get the element at index 3
// set arr[23] = 815 - replace the value at index 23 with 815
// call arr.clear() - remove all objects from the list
// call arr.destroy() - clear list and recycle the struct index
//
// WARNING: Most attempts to work with indexes <0 or >=.size will fail and
// generate an error message in debug mode. Returns default to 0 for integers/
// reals, null for handles, false for booleans, etc.
//
// LEAK WARNING: List does no automatic garbage collection. If a List
// is the only place you store a variable, make sure the variable will not leak when
// you remove it or replace it.
//===================================================================================
// Credits:
// - Vexorian for JassHelper
// - Vexorian for Table 3.0 (which I used for reference)
// - All the other people who worked on JassNewGenPack
//===================================================================================
globals
private constant integer MAX_INSTANCES = 8190
endglobals
//! textmacro List takes type, listType, typeName
struct $listType$List [MAX_INSTANCES]
private integer min = -1
private integer max = 0
private static hashtable contents = InitHashtable()
private static $type$ tempObject
private static $type$ DEFAULT_VALUE
private static method onInit takes nothing returns nothing
set thistype.DEFAULT_VALUE = Load$typeName$(thistype.contents,0,0)
endmethod
private method getActualIndex takes integer publicIndex returns integer
return publicIndex + this.min + 1
endmethod
private method getPublicIndex takes integer actualIndex returns integer
return actualIndex - this.min - 1
endmethod
private method isValidIndex takes integer actualIndex returns boolean
return actualIndex > this.min and actualIndex < this.max
endmethod
//---------------------USER METHODS------------------------
method operator size takes nothing returns integer
return this.max-this.min-1
endmethod
method addFirst takes $type$ object returns nothing
call Save$typeName$(this.contents, integer(this), this.min, object)
set this.min = this.min - 1
endmethod
method addLast takes $type$ object returns nothing
call Save$typeName$(this.contents, integer(this), this.max, object)
set this.max = this.max + 1
endmethod
method removeFirst takes nothing returns $type$
if(this.size>0)then
set this.min = this.min + 1
return Load$typeName$(this.contents, integer(this), this.min)
endif
debug call BJDebugMsg("ERROR: $listType$List \"removeFirst\" method. Cannot remove from an empty List.")
return this.DEFAULT_VALUE
endmethod
method removeLast takes nothing returns $type$
if(this.size>0)then
set this.max = this.max - 1
return Load$typeName$(this.contents, integer(this), this.max)
endif
debug call BJDebugMsg("ERROR: $listType$List \"removeLast\" method. Cannot remove from an empty List.")
return this.DEFAULT_VALUE
endmethod
method operator [] takes integer index returns $type$
local integer actualIndex = this.getActualIndex(index)
if(this.isValidIndex(actualIndex))then
return Load$typeName$(this.contents, integer(this), actualIndex)
endif
debug call BJDebugMsg("ERROR: $listType$List \"[]\" method. Index Out of Bounds ("+I2S(index)+").")
return this.DEFAULT_VALUE
endmethod
method operator []= takes integer index, $type$ object returns nothing
local integer actualIndex = this.getActualIndex(index)
if(this.isValidIndex(actualIndex))then
call Save$typeName$(this.contents, integer(this), actualIndex, object)
debug else
debug call BJDebugMsg("ERROR: $listType$List \"[]=\" method. Index Out of Bounds ("+I2S(index)+").")
endif
endmethod
method add takes integer index, $type$ object returns nothing
local integer i
local integer actualIndex = this.getActualIndex(index)
if(actualIndex > this.min and actualIndex <= this.max)then
if(actualIndex <= 0.5*(this.min+this.max))then
set actualIndex = actualIndex - 1
set i = this.min
loop
exitwhen i >= actualIndex
call Save$typeName$(this.contents, integer(this), i, Load$typeName$(this.contents, integer(this), i+1))
set i = i + 1
endloop
set this.min = this.min - 1
else
set i = this.max
loop
exitwhen i <= actualIndex
call Save$typeName$(this.contents, integer(this), i, Load$typeName$(this.contents, integer(this), i-1))
set i = i - 1
endloop
set this.max = this.max + 1
endif
call Save$typeName$(this.contents, integer(this), actualIndex, object)
debug else
debug call BJDebugMsg("ERROR: $listType$List \"add\" method. Index Out of Bounds ("+I2S(index)+").")
endif
endmethod
method remove takes integer index returns $type$
local integer i
local integer actualIndex = this.getActualIndex(index)
if(this.isValidIndex(actualIndex))then
set this.tempObject = Load$typeName$(this.contents, integer(this), actualIndex)
set i = actualIndex
if(actualIndex <= 0.5*(this.min+this.max))then
set this.min = this.min + 1
loop
exitwhen i <= this.min
call Save$typeName$(this.contents, integer(this), i, Load$typeName$(this.contents, integer(this), i-1))
set i = i - 1
endloop
else
set this.max = this.max - 1
loop
exitwhen i >= this.max
call Save$typeName$(this.contents, integer(this), i, Load$typeName$(this.contents, integer(this), i+1))
set i = i + 1
endloop
endif
return this.tempObject
endif
debug call BJDebugMsg("ERROR: $listType$List \"remove\" method. Index Out of Bounds ("+I2S(index)+").")
return this.DEFAULT_VALUE
endmethod
method clear takes nothing returns nothing
call FlushChildHashtable(this.contents, integer(this))
set this.min = -1
set this.max = 0
endmethod
method removeRange takes integer fromIndex, integer toIndex returns nothing
local integer i
local integer actualFromIndex = this.getActualIndex(fromIndex)
local integer actualToIndex = this.getActualIndex(toIndex-1)
if(this.isValidIndex(actualFromIndex))then
if(this.isValidIndex(actualToIndex) and toIndex>fromIndex)then
if(actualFromIndex-this.min < this.max-actualToIndex)then
set i = actualFromIndex
loop
set i = i - 1
exitwhen i<=this.min
call Save$typeName$(this.contents, integer(this), i-fromIndex+toIndex, Load$typeName$(this.contents, integer(this), i))
endloop
set this.min = this.min-fromIndex+toIndex
else
set i = actualToIndex
loop
set i = i + 1
exitwhen i>=this.max
call Save$typeName$(this.contents, integer(this), i+fromIndex-toIndex, Load$typeName$(this.contents, integer(this), i))
endloop
set this.max = this.max+fromIndex-toIndex
endif
debug else
debug call BJDebugMsg("ERROR: $listType$List \"removeRange\" method. Index Out of Bounds ("+I2S(toIndex)+").")
endif
debug else
debug call BJDebugMsg("ERROR: $listType$List \"removeRange\" method. Index Out of Bounds ("+I2S(fromIndex)+").")
endif
endmethod
method indexOf takes $type$ object returns integer
local integer i = this.min
loop
set i = i + 1
exitwhen i>=this.max
if(Load$typeName$(this.contents, integer(this), i) == object)then
return this.getPublicIndex(i)
endif
endloop
return -1
endmethod
method lastIndexOf takes $type$ object returns integer
local integer i = this.max
loop
set i = i - 1
exitwhen i<=this.min
if(Load$typeName$(this.contents, integer(this), i) == object)then
return this.getPublicIndex(i)
endif
endloop
return -1
endmethod
method swap takes integer index1, integer index2 returns nothing
local integer actualIndex1 = this.getActualIndex(index1)
local integer actualIndex2 = this.getActualIndex(index2)
if(this.isValidIndex(actualIndex1))then
if(this.isValidIndex(actualIndex2))then
set this.tempObject = Load$typeName$(this.contents, integer(this), actualIndex1)
call Save$typeName$(this.contents, integer(this), actualIndex1, Load$typeName$(this.contents, integer(this), actualIndex2))
call Save$typeName$(this.contents, integer(this), actualIndex2, this.tempObject)
debug else
debug call BJDebugMsg("ERROR: $listType$List \"swap\" method. Index Out of Bounds ("+I2S(index2)+").")
endif
debug else
debug call BJDebugMsg("ERROR: $listType$List \"swap\" method. Index Out of Bounds ("+I2S(index1)+").")
endif
endmethod
method exists takes integer index returns boolean
return index >= 0 and index < this.size
endmethod
method isEmpty takes nothing returns boolean
return this.max == 0
endmethod
//------------------END USER METHODS----------------------
private method onDestroy takes nothing returns nothing
call this.clear()
endmethod
endstruct
//! endtextmacro
//runtextmacro List( "type", "listType", "typeName" )
//type = type of object to store. must be exact.
//listType = prefix to the List struct name. can be whatever you want.
//ex: Timer --> TimerList
//typeName = suffix in Save, Load hashtable functions. must be exact.
//ex: Str --> LoadStr
//! runtextmacro List( "integer", "Integer", "Integer" )
// runtextmacro List( "string", "String", "Str" )
// runtextmacro List( "real", "Real", "Real" )
// runtextmacro List( "boolean", "Boolean", "Boolean" )
// runtextmacro List( "player", "Player", "PlayerHandle" )
// runtextmacro List( "unit", "Unit", "UnitHandle" )
// runtextmacro List( "item", "Item", "ItemHandle" )
// runtextmacro List( "widget", "Widget", "WidgetHandle" )
// runtextmacro List( "timer", "Timer", "TimerHandle" )
// runtextmacro List( "effect", "Effect", "EffectHandle" )
// runtextmacro List( "rect", "Rect", "RectHandle" )
// runtextmacro List( "group", "Group", "GroupHandle" )
// runtextmacro List( "force", "Force", "ForceHandle" )
// Add as many handle list types as you want, and comment out the ones you do not use
// so they do not take up unnecessary space in your map script.
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library_once TimerUtils initializer init
//*********************************************************************
//* TimerUtils (Blue flavor for 1.23b or later)
//* ----------
//*
//* To implement it , create a custom text trigger called TimerUtils
//* and paste the contents of this script there.
//*
//* To copy from a map to another, copy the trigger holding this
//* library to your map.
//*
//* (requires vJass) More scripts: htt://www.wc3campaigns.net
//*
//* For your timer needs:
//* * Attaching
//* * Recycling (with double-free protection)
//*
//* set t=NewTimer() : Get a timer (alternative to CreateTimer)
//* ReleaseTimer(t) : Relese a timer (alt to DestroyTimer)
//* SetTimerData(t,2) : Attach value 2 to timer
//* GetTimerData(t) : Get the timer's value.
//* You can assume a timer's value is 0
//* after NewTimer.
//*
//* Blue Flavor: Slower than the red flavor, it got a 408000 handle id
//* limit, which means that if more than 408000 handle ids
//* are used in your map, TimerUtils might fail, this
//* value is quite big and it is much bigger than the
//* timer limit in Red flavor.
//*
//********************************************************************
//==================================================================================================
globals
private hashtable hasht //I <3 blizz
endglobals
//It is dependent on jasshelper's recent inlining optimization in order to perform correctly.
function SetTimerData takes timer t, integer value returns nothing
call SaveInteger(hasht,0, GetHandleId(t), value)
endfunction
function GetTimerData takes timer t returns integer
return LoadInteger(hasht, 0, GetHandleId(t))
endfunction
//==========================================================================================
globals
private timer array tT
private integer tN = 0
private constant integer HELD=0x28829022
//use a totally random number here, the more improbable someone uses it, the better.
endglobals
//==========================================================================================
function NewTimer takes nothing returns timer
if (tN==0) then
set tT[0]=CreateTimer()
else
set tN=tN-1
endif
call SetTimerData(tT[tN],0)
return tT[tN]
endfunction
//==========================================================================================
function ReleaseTimer takes timer t returns nothing
if(t==null) then
debug call BJDebugMsg("Warning: attempt to release a null timer")
return
endif
if (tN==8191) then
debug call BJDebugMsg("Warning: Timer stack is full, destroying timer!!")
//stack is full, the map already has much more troubles than the chance of bug
call DestroyTimer(t)
else
call PauseTimer(t)
if(GetTimerData(t)==HELD) then
debug call BJDebugMsg("Warning: ReleaseTimer: Double free!")
return
endif
call SetTimerData(t,HELD)
set tT[tN]=t
set tN=tN+1
endif
endfunction
private function init takes nothing returns nothing
set hasht = InitHashtable()
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
/*TimedBuffEffects by MaskedPoptart
based off TimedHandles by TriggerHappy187
This is a library I threw together for the Firewall spell (and for fun).
Its sole purpose is to put special effects on units to imitate the effects created by buffs.
It has one public function:
function AddBuffEffectTarget takes string modelName, widget target, string attach, real duration returns nothing
It puts a special effect on a unit, and destroys the effect after the specified
amount of time. If the duration is 0, the effect will not expire until the unit dies.
If that unit already has an effect of the same model, on the same body part,
it will not place a new special effect, but will update the timer. If that unit dies, all the
special effects it has will be destroyed.
Feel free to use it for whatever you want. Edit it only if you need to and give credits to MaskedPoptart
for the original. I do not plan on releasing this as an individual system.
*/
library TimedEffects initializer Init requires TimerUtils, List
private struct EffectData
private effect effectVar
private widget target
private string modelName
private string attach
private timer tim
IntegerList list
static hashtable duplicateEffects = InitHashtable()
//this hashtable is used to prevent duplicates of the same effects in the same places on the same unit
private static method remove takes nothing returns nothing
local thistype d = GetTimerData(GetExpiredTimer())
call d.destroy()
endmethod
method onDestroy takes nothing returns nothing
call ReleaseTimer(this.tim)
call DestroyEffect(this.effectVar)
call SaveInteger(.duplicateEffects, (GetHandleId(this.target)-0x100000)/*
*/ * StringHash(this.modelName), StringHash(this.attach), 0)
set this.effectVar = null
set this.target = null
set this.tim = null
call this.list.remove(this.list.indexOf(this))
endmethod
static method create takes string modelName, widget target, string attach, real duration,/*
*/ IntegerList list returns thistype
local integer ID_target = GetHandleId(target)-0x100000
local integer SH_modelName = StringHash(modelName)
local integer SH_attach = StringHash(attach)
local thistype d = LoadInteger(.duplicateEffects, ID_target*SH_modelName, SH_attach)
if(d == 0)then
set d = thistype.allocate()
set d.effectVar = AddSpecialEffectTarget(modelName, target, attach)
set d.target = target
set d.modelName = modelName
set d.attach = attach
set d.tim = NewTimer()
set d.list = list
call list.addLast(d)
call SaveInteger(.duplicateEffects, ID_target*SH_modelName, SH_attach, d)
call SetTimerData(d.tim, d)
else
call PauseTimer(d.tim)
endif
if(duration != 0)then
call TimerStart(d.tim, duration, false, function thistype.remove)
endif
return d
endmethod
endstruct
private struct EffectTarget
widget target
IntegerList effectDataList //a list of all the effectData instances on this unit
static hashtable attachedData = InitHashtable()
//this hashtable is used to attach a struct of this type to the target
static method create takes string modelName, widget target, string attach, real duration returns thistype
local integer HI_target = GetHandleId(target)
local thistype d = LoadInteger(.attachedData, 0, HI_target)
local EffectData newEffect
if(d ==0)then
set d = thistype.allocate()
set d.effectDataList = IntegerList.create()
set d.target = target
call SaveInteger(.attachedData, 0, HI_target, d)
endif
set newEffect = EffectData.create(modelName, target, attach, duration, d.effectDataList)
return d
endmethod
method onDestroy takes nothing returns nothing
local EffectData currentEffectData
loop
exitwhen this.effectDataList.size == 0
set currentEffectData = EffectData(this.effectDataList[0])
call currentEffectData.destroy()
endloop
call this.effectDataList.destroy()
call SaveInteger(.attachedData, 0, GetHandleId(this.target), 0)
set this.target = null
endmethod
endstruct
function AddBuffEffectTarget takes string modelName, widget target, string attach, real duration returns nothing
call EffectTarget.create(modelName, target, attach, duration)
endfunction
private function DestroyUnitEffects takes nothing returns nothing
local unit killedUnit = GetTriggerUnit()
local EffectTarget et = LoadInteger(EffectTarget.attachedData, 0, GetHandleId(killedUnit))
if(et != 0)then
call et.destroy()
endif
endfunction
private function Init takes nothing returns nothing
local trigger trig = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(trig, EVENT_PLAYER_UNIT_DEATH)
call TriggerAddAction(trig, function DestroyUnitEffects)
endfunction
endlibrary