Name | Type | is_array | initial_value |
//TESH.scrollpos=34
//TESH.alwaysfold=0
library xebasic
//**************************************************************************
//
// xebasic 0.4
// =======
// XE_DUMMY_UNITID : Rawcode of the dummy unit in your map. It should
// use the dummy.mdx model, so remember to import it as
// well, just use copy&paste to copy the dummy from the
// xe map to yours, then change the rawcode.
//
// XE_HEIGHT_ENABLER: Medivh's raven form ability, you may need to change
// this rawcode to another spell that morphs into a flier
// in case you modified medivh's spell in your map.
//
// XE_TREE_RECOGNITION: The ancients' Eat tree ability, same as with medivh
// raven form, you might have to change it.
//
// XE_ANIMATION_PERIOD: The global period of animation used by whatever
// timer that depends on it, if you put a low value
// the movement will look good but it may hurt your
// performance, if instead you use a high value it
// will not lag but will be fast.
//
// XE_MAX_COLLISION_SIZE: The maximum unit collision size in your map, if
// you got a unit bigger than 197.0 it would be
// a good idea to update this constant, since some
// enums will not find it. Likewise, if none of
// your units can go bellow X and X is much smaller
// than 197.0, it would be a good idea to update
// as well, since it will improve the performance
// those enums.
//
// Notice you probably don't have to update this library, unless I specify
// there are new constants which would be unlikely.
//
//**************************************************************************
//===========================================================================
globals
constant integer XE_DUMMY_UNITID = 'xeca'
constant integer XE_DUMMY_90 = 'xe90'
constant integer XE_DUMMY_180 = 'x180'
constant integer XE_HEIGHT_ENABLER = 'Amrf'
constant integer XE_TREE_RECOGNITION = 'Aeat'
constant real XE_ANIMATION_PERIOD = 0.025
constant real XE_MAX_COLLISION_SIZE = 197.0
endglobals
endlibrary
//TESH.scrollpos=363
//TESH.alwaysfold=0
library xedamage initializer init requires xebasic
//************************************************************************
// xedamage 0.8
// --------
// For all your damage and targetting needs.
//
//************************************************************************
//===========================================================================================================
globals
private constant integer MAX_SUB_OPTIONS = 3
private constant real FACTOR_TEST_DAMAGE = 0.01
// a low damage to do on units to test their damage factors for specific
// attacktype/damagetype combos.
// You'll need something as high as 20.0 to make it work well with armor resistances.
// (Yes, you read it correctly, 20 ...
//
// If you use such a large value, there may be conflicts with some things relying on damage
// (ie they are not away of the isDummyDamage tag that xedamage posseses.) which may be quite bad if you think about it...
// then maybe it is better to change it to 0.01 ... then will work fine, just fine - but it will generally ignore armor -
// I am leaving it as 0.01 by default, because honestly, I'd rather make it ignore armor than have a lot of people sending me
// rants about how they detect 20.0 damage where none is visible...
private constant real MAX_DAMAGE_FACTOR = 3.00
// The maximum damage factor in the map. I think 3 is fine.
//=======================================================
private constant real EPSILON = 0.000000001
private unit dmger
private constant integer MAX_SPACE = 8190 // MAX_SPACE/MAX_SUB_OPTIONS is the instance limit for xedamage, usually big enough...
endglobals
private keyword structInit
struct xedamage[MAX_SPACE]
//----
// fields and methods for a xedamage object, they aid determining valid targets and special
// damage factor conditions.
//
// Notice the default values.
//
boolean damageSelf = false // the damage and factor methods usually have a source unit parameter
// xedamage would consider this unit as immune unless you set damageSelf to true
boolean damageAllies = false // Alliance dependent target options.
boolean damageEnemies = true // *
boolean damageNeutral = true // *
boolean ranged = true // Is the attack ranged? This has some effect on the AI of the affected units
// true by default, you may not really need to modify this.
boolean visibleOnly = false // Should only units that are visible for source unit's owner be affected?
boolean deadOnly = false // Should only corpses be affected by "the damage"? (useful when using xedamage as a target selector)
boolean alsoDead = false // Should even corpses and alive units be considered?
boolean damageTrees = false //Also damage destructables? Notice this is used only in certain methods.
//AOE for example targets a circle, so it can affect the destructables
//in that circle, a custom spell using xedamage for targetting configuration
//could also have an if-then-else implemented so it can verify if it is true
//then affect trees manually.
//
// Damage type stuff:
// .dtype : the "damagetype" , determines if the spell is physical, magical or ultimate.
// .atype : the "attacktype" , deals with armor.
// .wtype : the "weapontype" , determines the sound effect to be played when damage is done.
//
// Please use common.j/blizzard.j/ some guide to know what damage/attack/weapon types can be used
//
damagetype dtype = DAMAGE_TYPE_UNIVERSAL
attacktype atype = ATTACK_TYPE_NORMAL
weapontype wtype = WEAPON_TYPE_WHOKNOWS
//
// Damage type 'tag' people might use xedamage.isInUse() to detect xedamage usage, there are other static
// variables like xedamage.CurrentDamageType and xedamage.CurrentDamageTag. The tag allows you to specify
// a custom id for the damage type ** Notice the tag would aid you for some spell stuff, for example,
// you can use it in a way similar to Rising_Dusk's damage system.
//
integer tag = 0
//
// if true, forceDamage will make xedamage ignore dtype and atype and try as hard as possible to deal 100%
// damage.
boolean forceDamage = false
//
// Ally factor! Certain spells probably have double purposes and heal allies while harming enemies. This
// field allows you to do such thing.
//
real allyfactor = 1.0
//
// field: .exception = SOME_UNIT_TYPE
// This field adds an exception unittype (classification), if the unit belongs to this unittype it will
// be ignored.
//
method operator exception= takes unittype ut returns nothing
set this.use_ex=true
set this.ex_ut=ut
endmethod
//
// field: .required = SOME_UNIT_TYPE
// This field adds a required unittype (classification), if the unit does not belong to this unittype
// it will be ignored.
//
method operator required= takes unittype ut returns nothing
set this.use_req=true
set this.req_ut=ut
endmethod
private boolean use_ex = false
private unittype ex_ut = null
private boolean use_req = false
private unittype req_ut = null
private unittype array fct[MAX_SUB_OPTIONS]
private real array fc[MAX_SUB_OPTIONS]
private integer fcn=0
//
// method .factor(SOME_UNIT_TYPE, factor)
// You might call factor() if you wish to specify a special damage factor for a certain classification,
// for example call d.factor(UNIT_TYPE_STRUCTURE, 0.5) makes xedamage do half damage to structures.
//
method factor takes unittype ut, real fc returns nothing
if(this.fcn==MAX_SUB_OPTIONS) then
debug call BJDebugMsg("In one instance of xedamage, you are doing too much calls to factor(), please increase MAX_SUB_OPTIONS to allow more, or cut the number of factor() calls")
return
endif
set this.fct[this.fcn] = ut
set this.fc[this.fcn] = fc
set this.fcn = this.fcn+1
endmethod
private integer array abifct[MAX_SUB_OPTIONS]
private real array abifc[MAX_SUB_OPTIONS]
private integer abifcn=0
//
// method .abilityFactor('abil', factor)
// You might call abilityFactor() if you wish to specify a special damage factor for units that have a
// certain ability/buff.
// for example call d.abilityFactor('A000', 1.5 ) makes units that have the A000 ability take 50% more
// damage than usual.
//
method abilityFactor takes integer abilityId, real fc returns nothing
if(this.abifcn==MAX_SUB_OPTIONS) then
debug call BJDebugMsg("In one instance of xedamage, you are doing too much calls to abilityFactor(), please increase MAX_SUB_OPTIONS to allow more, or cut the number of abilityFactor() calls")
return
endif
set this.abifct[this.abifcn] = abilityId
set this.abifc[this.abifcn] = fc
set this.abifcn = this.abifcn+1
endmethod
private boolean usefx = false
private string fxpath
private string fxattach
//
// method .useSpecialEffect("effect\\path.mdl", "origin")
// Makes it add (and destroy) an effect when damage is performed.
//
method useSpecialEffect takes string path, string attach returns nothing
set this.usefx = true
set this.fxpath=path
set this.fxattach=attach
endmethod
//********************************************************************
//* Now, the usage stuff:
//*
//================================================================================
// static method xedamage.isInUse() will return true during a unit damaged
// event in case this damage was caused by xedamage, in this case, you can
// read variables like CurrentDamageType, CurrentAttackType and CurrentDamageTag
// to be able to recognize what sort of damage was done.
//
readonly static damagetype CurrentDamageType=null
readonly static attacktype CurrentAttackType=null
readonly static integer CurrentDamageTag =0
private static integer inUse = 0
static method isInUse takes nothing returns boolean
return (inUse>0) //inline friendly.
endmethod
readonly static boolean isDummyDamage = false
//========================================================================================================
// This function calculates the damage factor caused by a certain attack and damage
// type, it is static : xedamage.getDamageTypeFactor(someunit, ATTAcK_TYPE_NORMAL, DAMAGE_TYPE_FIRE, 100)
//
static method getDamageTypeFactor takes unit u, attacktype a, damagetype d returns real
local real hp=GetWidgetLife(u)
local real mana=GetUnitState(u,UNIT_STATE_MANA)
local real r
local real fc = FACTOR_TEST_DAMAGE
//Since a unit is in that point, we don't need checks.
call SetUnitX(dmger,GetUnitX(u))
call SetUnitY(dmger,GetUnitY(u))
call SetUnitOwner(dmger,GetOwningPlayer(u),false)
set r=hp
if (hp< FACTOR_TEST_DAMAGE*MAX_DAMAGE_FACTOR) then
call SetWidgetLife(u, hp + FACTOR_TEST_DAMAGE*MAX_DAMAGE_FACTOR )
set r = hp + FACTOR_TEST_DAMAGE*MAX_DAMAGE_FACTOR
set fc = GetWidgetLife(u)-hp + EPSILON
endif
set isDummyDamage = true
call UnitDamageTarget(dmger,u, fc ,false,false,a,d,null)
static if DEBUG_MODE then
if IsUnitType(u, UNIT_TYPE_DEAD) and (hp>0.405) then
call BJDebugMsg("xedamage: For some reason, the unit being tested by getDamageTypeFactor has died. Verify MAX_DAMAGE_FACTOR is set to a correct value. ")
endif
endif
set isDummyDamage = false
call SetUnitOwner(dmger,Player(15),false)
if (mana>GetUnitState(u,UNIT_STATE_MANA)) then
//Unit had mana shield, return 1 and restore mana too.
call SetUnitState(u,UNIT_STATE_MANA,mana)
set r=1
else
set r= (r-GetWidgetLife(u)) / fc
endif
call SetWidgetLife(u,hp)
return r
endmethod
private method getTargetFactorCore takes unit source, unit target, boolean usetypes returns real
local player p=GetOwningPlayer(source)
local boolean allied=IsUnitAlly(target,p)
local boolean enemy =IsUnitEnemy(target,p)
local boolean neutral=allied
local real f
local real negf=1.0
local integer i
if(this.damageAllies != this.damageNeutral) then
set neutral= allied and not (GetPlayerAlliance(GetOwningPlayer(target),p, ALLIANCE_HELP_REQUEST ))
//I thought accuracy was not as important as speed , I think that REQUEST is false is enough to consider
// it neutral.
//set neutral= allied and not (GetPlayerAlliance(GetOwningPlayer(target),p, ALLIANCE_HELP_RESPONSE ))
//set neutral= allied and not (GetPlayerAlliance(GetOwningPlayer(target),p, ALLIANCE_SHARED_XP ))
//set neutral= allied and not (GetPlayerAlliance(GetOwningPlayer(target),p, ALLIANCE_SHARED_SPELLS ))
set allied= allied and not(neutral)
endif
if (not this.damageAllies) and allied then
return 0.0
elseif (not this.damageEnemies) and enemy then
return 0.0
elseif( (not this.damageSelf) and (source==target) ) then
return 0.0
elseif (not this.damageNeutral) and neutral then
return 0.0
elseif( this.use_ex and IsUnitType(target, this.ex_ut) ) then
return 0.0
elseif( this.visibleOnly and not IsUnitVisible(target,p) ) then
return 0.0
elseif ( this.deadOnly and not IsUnitType(target,UNIT_TYPE_DEAD) ) then
return 0.0
elseif ( not(this.alsoDead) and IsUnitType(target,UNIT_TYPE_DEAD) ) then
return 0.0
endif
set f=1.0
if ( IsUnitAlly(target,p) ) then
set f=f*this.allyfactor
if(f<=-EPSILON) then
set f=-f
set negf=-1.0
endif
endif
if (this.use_req and not IsUnitType(target,this.req_ut)) then
return 0.0
endif
set i=.fcn-1
loop
exitwhen (i<0)
if( IsUnitType(target, this.fct[i] ) ) then
set f=f*this.fc[i]
if(f<=-EPSILON) then
set f=-f
set negf=-1.0
endif
endif
set i=i-1
endloop
set i=.abifcn-1
loop
exitwhen (i<0)
if( GetUnitAbilityLevel(target,this.abifct[i] )>0 ) then
set f=f*this.abifc[i]
if(f<=-EPSILON) then
set f=-f
set negf=-1.0
endif
endif
set i=i-1
endloop
set f=f*negf
if ( f<EPSILON) and (f>-EPSILON) then
return 0.0
endif
if( this.forceDamage or not usetypes ) then
return f
endif
set f=f*xedamage.getDamageTypeFactor(target,this.atype,this.dtype)
if ( f<EPSILON) and (f>-EPSILON) then
return 0.0
endif
return f
endmethod
//====================================================================
// With this you might decide if a unit is a valid target for a spell.
//
method getTargetFactor takes unit source, unit target returns real
return this.getTargetFactorCore(source,target,true)
endmethod
//======================================================================
// a little better, I guess
//
method allowedTarget takes unit source, unit target returns boolean
return (this.getTargetFactorCore(source,target,false)!=0.0)
endmethod
//=======================================================================
// performs damage to the target unit, for unit 'source'.
//
method damageTarget takes unit source, unit target, real damage returns boolean
local damagetype dt=.CurrentDamageType
local attacktype at=.CurrentAttackType
local integer tg=.CurrentDamageTag
local real f = this.getTargetFactorCore(source,target,false)
local real pl
if(f!=0.0) then
set .CurrentDamageType = .dtype
set .CurrentAttackType = .atype
set .CurrentDamageTag = .tag
set .inUse = .inUse +1
set pl=GetWidgetLife(target)
call UnitDamageTarget(source,target, f*damage, true, .ranged, .atype, .dtype, .wtype )
set .inUse = .inUse -1
set .CurrentDamageTag = tg
set .CurrentDamageType = dt
set .CurrentAttackType = at
if(pl != GetWidgetLife(target) ) then
if(usefx) then
call DestroyEffect( AddSpecialEffectTarget(this.fxpath, target, this.fxattach) )
endif
return true
endif
endif
return false
endmethod
//=======================================================================================
// The same as damageTarget, but it forces a specific damage value, good if you already
// know the target.
//
method damageTargetForceValue takes unit source, unit target, real damage returns nothing
local damagetype dt=.CurrentDamageType
local attacktype at=.CurrentAttackType
local integer tg=.CurrentDamageTag
set .CurrentDamageType = .dtype
set .CurrentAttackType = .atype
set .CurrentDamageTag = .tag
if( usefx) then
call DestroyEffect( AddSpecialEffectTarget(this.fxpath, target, this.fxattach) )
endif
set .inUse = .inUse +1
call UnitDamageTarget(source,target, damage, true, .ranged, null, null, .wtype )
set .inUse = .inUse -1
set .CurrentDamageTag = tg
set .CurrentDamageType = dt
set .CurrentAttackType = at
endmethod
//=====================================================================================
// Notice: this will not Destroy the group, but it will certainly empty the group.
//
method damageGroup takes unit source, group targetGroup, real damage returns integer
local damagetype dt=.CurrentDamageType
local attacktype at=.CurrentAttackType
local integer tg=.CurrentDamageTag
local unit target
local real f
local integer count=0
local real hp
set .CurrentDamageType = .dtype
set .CurrentAttackType = .atype
set .CurrentDamageTag = .tag
set .inUse = .inUse +1
loop
set target=FirstOfGroup(targetGroup)
exitwhen (target==null)
call GroupRemoveUnit(targetGroup,target)
set f= this.getTargetFactorCore(source,target,false)
if (f!=0.0) then
set count=count+1
if(usefx) then
set hp = GetWidgetLife(target)
endif
call UnitDamageTarget(source,target, f*damage, true, .ranged, .atype, .dtype, .wtype )
if(usefx and (hp > GetWidgetLife(target)) ) then
call DestroyEffect( AddSpecialEffectTarget(this.fxpath, target, this.fxattach) )
endif
endif
endloop
set .inUse = .inUse -1
set .CurrentDamageTag=tg
set .CurrentDamageType = dt
set .CurrentAttackType = at
return count
endmethod
private static xedamage instance
private integer countAOE
private unit sourceAOE
private real AOEx
private real AOEy
private real AOEradius
private real AOEdamage
private static boolexpr filterAOE
private static boolexpr filterDestAOE
private static group enumgroup
private static rect AOERect
private static method damageAOE_Enum takes nothing returns boolean
local unit target=GetFilterUnit()
local xedamage this=.instance //adopting a instance.
local real f
local real hp
if( not IsUnitInRangeXY(target,.AOEx, .AOEy, .AOEradius) ) then
set target=null
return false
endif
set f=.getTargetFactorCore(.sourceAOE, target, false)
if(f!=0.0) then
set .countAOE=.countAOE+1
if(this.usefx) then
set hp =GetWidgetLife(target)
endif
call UnitDamageTarget(.sourceAOE,target, f*this.AOEdamage, true, .ranged, .atype, .dtype, .wtype )
if(this.usefx and (hp > GetWidgetLife(target) ) ) then
call DestroyEffect( AddSpecialEffectTarget(this.fxpath, target, this.fxattach) )
endif
endif
set .instance= this //better restore, nesting IS possible!
set target=null
return false
endmethod
private static method damageAOE_DestructablesEnum takes nothing returns boolean
local destructable target=GetFilterDestructable()
local xedamage this=.instance //adopting a instance.
local real dx=.AOEx-GetDestructableX(target)
local real dy=.AOEy-GetDestructableY(target)
if( dx*dx + dy*dy >= .AOEradius+EPSILON ) then
set target=null
return false
endif
set .countAOE=.countAOE+1
if(.usefx) then
call DestroyEffect( AddSpecialEffectTarget(this.fxpath, target, this.fxattach) )
endif
call UnitDamageTarget(.sourceAOE,target, this.AOEdamage, true, .ranged, .atype, .dtype, .wtype )
set .instance= this //better restore, nesting IS possible!
set target=null
return false
endmethod
//==========================================================================================
// will affect trees if damageTrees is true!
//
method damageAOE takes unit source, real x, real y, real radius, real damage returns integer
local damagetype dt=.CurrentDamageType
local attacktype at=.CurrentAttackType
local integer tg=.CurrentDamageTag
set .CurrentDamageType = .dtype
set .CurrentAttackType = .atype
set .CurrentDamageTag = .tag
set .inUse = .inUse +1
set .instance=this
set .countAOE=0
set .sourceAOE=source
set .AOEx=x
set .AOEradius=radius
set .AOEy=y
set .AOEdamage=damage
call GroupEnumUnitsInRange(.enumgroup,x,y,radius+XE_MAX_COLLISION_SIZE, .filterAOE)
if(.damageTrees) then
call SetRect(.AOERect, x-radius, y-radius, x+radius, y+radius)
set .AOEradius=.AOEradius*.AOEradius
call EnumDestructablesInRect(.AOERect, .filterDestAOE, null)
endif
set .inUse = .inUse -1
set .CurrentDamageTag = tg
set .CurrentDamageType = dt
set .CurrentAttackType = at
return .countAOE
endmethod
method damageAOELoc takes unit source, location loc, real radius, real damage returns integer
return .damageAOE(source, GetLocationX(loc), GetLocationY(loc), radius, damage)
endmethod
//==========================================================================================
// only affects trees, ignores damageTrees
//
method damageDestructablesAOE takes unit source, real x, real y, real radius, real damage returns integer
set .instance=this
set .countAOE=0
set .sourceAOE=source
set .AOEx=x
set .AOEradius=radius*radius
set .AOEy=y
set .AOEdamage=damage
//if(.damageTrees) then
call SetRect(.AOERect, x-radius, y-radius, x+radius, y+radius)
call EnumDestructablesInRect(.AOERect, .filterDestAOE, null)
//endif
return .countAOE
endmethod
method damageDestructablesAOELoc takes unit source, location loc, real radius, real damage returns integer
return .damageDestructablesAOE(source,GetLocationX(loc), GetLocationY(loc), radius, damage)
endmethod
//'friend' with the library init
static method structInit takes nothing returns nothing
set .AOERect= Rect(0,0,0,0)
set .filterAOE= Condition(function xedamage.damageAOE_Enum)
set .filterDestAOE = Condition( function xedamage.damageAOE_DestructablesEnum)
set .enumgroup = CreateGroup()
endmethod
endstruct
private function init takes nothing returns nothing
set dmger=CreateUnit(Player(15), XE_DUMMY_UNITID , 0.,0.,0.)
call UnitAddAbility(dmger,'Aloc')
call xedamage.structInit()
endfunction
endlibrary
//TESH.scrollpos=-1
//TESH.alwaysfold=0
// API:
//
// >>CreateDelayedEffect(whichEffect, X, Y, Delay, Timeout)
// - whichEffect is of type string and contains the path of the
// effect-to-be-spawned
// - X and Y indicate where the effect should be spawned
// - Delay is of type real and indicates how long to wait before spawning the
// effect
// - Timeout is of type real and indicates how long to wait before destroying
// the effect after it has been created
//
// >>CreateDelayedEffectZ(whichEffect, X, Y, Z, Delay, Timeout)
// - whichEffect: see above
// - X, Y and Z indicate where to spawn the effect
// - Delay: see above
// - Timeout: see above
//
// >>CreateDelayedEffectTarget(whichEffect, Target, AttachmentPoint, Delay, Timeout)
// - whichEffect: see above
// - Target is of type widget and indicates on which widget the effect should
// be spawned
// - AttachmentPoint is of type string and holds the attachment point where
// the effect should be spawned on target widget
// - Delay: see above
// - Timeout: see above
//
// CREDITS:
// - Vexorian (JassHelper; TimerUtils)
// - Anitarf (Suggestions)
// - KaTTaNa (AddSpecialEffectZ function @ wc3jass.com)
// - PitzerMike (JassNewGenPack)
// - Pipedream (Grimoire)
// - SFilip (TESH)
library DelFX uses TimerUtils
private struct DELFX
private effect fx
private string path
private boolean target
private widget tar
private string attpt
private real x
private real y
private real z
private timer t
private real timeout
private method onDestroy takes nothing returns nothing
if .fx!=null then
call DestroyEffect(.fx)
set .fx=null
endif
set .tar=null
call ReleaseTimer(.t)
endmethod
private static method Release takes nothing returns nothing
call DELFX.destroy(GetTimerData(GetExpiredTimer()))
endmethod
private static method Callback takes nothing returns nothing
local DELFX s=GetTimerData(GetExpiredTimer())
local destructable d
debug if s.fx==null then
if s.target then
set s.fx=AddSpecialEffectTarget(s.path, s.tar, s.attpt)
elseif s.z==0 then
set s.fx=AddSpecialEffect(s.path, s.x, s.y)
else
set d=CreateDestructableZ('OTip', s.x, s.y, s.z, 0,1.,0)
set s.fx=AddSpecialEffect(s.path, s.x, s.y)
call RemoveDestructable(d)
set d=null
endif
call TimerStart(s.t, s.timeout, false, function DELFX.Release)
debug else
debug call BJDebugMsg("DELFX["+I2S(s)+"].Callback: Effect already spawned!")
debug endif
endmethod
static method Create takes string path, boolean target, widget tar, string attpt, real x, real y, real z, real delay, real timeout returns DELFX
local DELFX s=DELFX.allocate()
set s.t=NewTimer()
call SetTimerData(s.t, s)
set s.path=path
set s.target=target
set s.tar=tar
set s.attpt=attpt
set s.x=x
set s.y=y
set s.z=z
set s.timeout=timeout
call TimerStart(s.t, delay, false, function DELFX.Callback)
return s
endmethod
endstruct
// The functions below have been explained above.
function CreateDelayedEffect takes string path, real x, real y, real delay, real timeout returns nothing
call DELFX.Create(path, false, null, "", x, y, 0, delay, timeout)
endfunction
function CreateDelayedEffectZ takes string path, real x, real y, real z, real delay, real timeout returns nothing
call DELFX.Create(path, false, null, "", x, y, z, delay, timeout)
endfunction
function CreateDelayedEffectTarget takes string path, widget target, string attachmentpoint, real delay, real timeout returns nothing
call DELFX.Create(path, true, target, attachmentpoint, 0, 0, 0, delay, timeout)
endfunction
endlibrary
//TESH.scrollpos=9
//TESH.alwaysfold=0
library Table /* made by Bribe, special thanks to Vexorian & Nestharus, version 3.1.0.1
One map, one hashtable. Welcome to NewTable 3.1
This library was originally called NewTable so it didn't conflict with
the API of Table by Vexorian. However, the damage is done and it's too
late to change the library name now. To help with damage control, I
have provided an extension library called TableBC, which bridges all
the functionality of Vexorian's Table except for 2-D string arrays &
the ".flush(integer)" method. I use ".flush()" to flush a child hash-
table, because I wanted the API in NewTable to reflect the API of real
hashtables (I thought this would be more intuitive).
API
------------
struct Table
| static method create takes nothing returns Table
| create a new Table
|
| method destroy takes nothing returns nothing
| destroy it
|
| method flush takes nothing returns nothing
| flush all stored values inside of it
|
| method remove takes integer key returns nothing
| remove the value at index "key"
|
| method operator []= takes integer key, $TYPE$ value returns nothing
| assign "value" to index "key"
|
| method operator [] takes integer key returns $TYPE$
| load the value at index "key"
|
| method has takes integer key returns boolean
| whether or not the key was assigned
|
----------------
struct TableArray
| static method operator [] takes integer array_size returns TableArray
| create a new array of Tables of size "array_size"
|
| method destroy takes nothing returns nothing
| destroy it
|
| method flush takes nothing returns nothing
| flush and destroy it
|
| method operator size takes nothing returns integer
| returns the size of the TableArray
|
| method operator [] takes integer key returns Table
| returns a Table accessible exclusively to index "key"
*/
globals
private integer less = 0 //Index generation for TableArrays (below 0).
private integer more = 8190 //Index generation for Tables.
//Configure it if you use more than 8190 "key" variables in your map (this will never happen though).
private hashtable ht = InitHashtable()
private key sizeK
private key listK
endglobals
private struct dex extends array
static method operator size takes nothing returns Table
return sizeK
endmethod
static method operator list takes nothing returns Table
return listK
endmethod
endstruct
private struct handles extends array
method has takes integer key returns boolean
return HaveSavedHandle(ht, this, key)
endmethod
method remove takes integer key returns nothing
call RemoveSavedHandle(ht, this, key)
endmethod
endstruct
private struct agents extends array
method operator []= takes integer key, agent value returns nothing
call SaveAgentHandle(ht, this, key, value)
endmethod
endstruct
//! textmacro NEW_ARRAY_BASIC takes SUPER, FUNC, TYPE
private struct $TYPE$s extends array
method operator [] takes integer key returns $TYPE$
return Load$FUNC$(ht, this, key)
endmethod
method operator []= takes integer key, $TYPE$ value returns nothing
call Save$FUNC$(ht, this, key, value)
endmethod
method has takes integer key returns boolean
return HaveSaved$SUPER$(ht, this, key)
endmethod
method remove takes integer key returns nothing
call RemoveSaved$SUPER$(ht, this, key)
endmethod
endstruct
private module $TYPE$m
method operator $TYPE$ takes nothing returns $TYPE$s
return this
endmethod
endmodule
//! endtextmacro
//! textmacro NEW_ARRAY takes FUNC, TYPE
private struct $TYPE$s extends array
method operator [] takes integer key returns $TYPE$
return Load$FUNC$Handle(ht, this, key)
endmethod
method operator []= takes integer key, $TYPE$ value returns nothing
call Save$FUNC$Handle(ht, this, key, value)
endmethod
endstruct
private module $TYPE$m
method operator $TYPE$ takes nothing returns $TYPE$s
return this
endmethod
endmodule
//! endtextmacro
//Run these textmacros to include the entire hashtable API as wrappers.
//Don't be intimidated by the number of macros - Vexorian's map optimizer is
//supposed to kill functions which inline (all of these functions inline).
//! runtextmacro NEW_ARRAY_BASIC("Real", "Real", "real")
//! runtextmacro NEW_ARRAY_BASIC("Boolean", "Boolean", "boolean")
//! runtextmacro NEW_ARRAY_BASIC("String", "Str", "string")
//! runtextmacro NEW_ARRAY("Player", "player")
//! runtextmacro NEW_ARRAY("Widget", "widget")
//! runtextmacro NEW_ARRAY("Destructable", "destructable")
//! runtextmacro NEW_ARRAY("Item", "item")
//! runtextmacro NEW_ARRAY("Unit", "unit")
//! runtextmacro NEW_ARRAY("Ability", "ability")
//! runtextmacro NEW_ARRAY("Timer", "timer")
//! runtextmacro NEW_ARRAY("Trigger", "trigger")
//! runtextmacro NEW_ARRAY("TriggerCondition", "triggercondition")
//! runtextmacro NEW_ARRAY("TriggerAction", "triggeraction")
//! runtextmacro NEW_ARRAY("TriggerEvent", "event")
//! runtextmacro NEW_ARRAY("Force", "force")
//! runtextmacro NEW_ARRAY("Group", "group")
//! runtextmacro NEW_ARRAY("Location", "location")
//! runtextmacro NEW_ARRAY("Rect", "rect")
//! runtextmacro NEW_ARRAY("BooleanExpr", "boolexpr")
//! runtextmacro NEW_ARRAY("Sound", "sound")
//! runtextmacro NEW_ARRAY("Effect", "effect")
//! runtextmacro NEW_ARRAY("UnitPool", "unitpool")
//! runtextmacro NEW_ARRAY("ItemPool", "itempool")
//! runtextmacro NEW_ARRAY("Quest", "quest")
//! runtextmacro NEW_ARRAY("QuestItem", "questitem")
//! runtextmacro NEW_ARRAY("DefeatCondition", "defeatcondition")
//! runtextmacro NEW_ARRAY("TimerDialog", "timerdialog")
//! runtextmacro NEW_ARRAY("Leaderboard", "leaderboard")
//! runtextmacro NEW_ARRAY("Multiboard", "multiboard")
//! runtextmacro NEW_ARRAY("MultiboardItem", "multiboarditem")
//! runtextmacro NEW_ARRAY("Trackable", "trackable")
//! runtextmacro NEW_ARRAY("Dialog", "dialog")
//! runtextmacro NEW_ARRAY("Button", "button")
//! runtextmacro NEW_ARRAY("TextTag", "texttag")
//! runtextmacro NEW_ARRAY("Lightning", "lightning")
//! runtextmacro NEW_ARRAY("Image", "image")
//! runtextmacro NEW_ARRAY("Ubersplat", "ubersplat")
//! runtextmacro NEW_ARRAY("Region", "region")
//! runtextmacro NEW_ARRAY("FogState", "fogstate")
//! runtextmacro NEW_ARRAY("FogModifier", "fogmodifier")
//! runtextmacro NEW_ARRAY("Hashtable", "hashtable")
struct Table extends array
// Implement modules for intuitive syntax (tb.handle; tb.unit; etc.)
implement realm
implement booleanm
implement stringm
implement playerm
implement widgetm
implement destructablem
implement itemm
implement unitm
implement abilitym
implement timerm
implement triggerm
implement triggerconditionm
implement triggeractionm
implement eventm
implement forcem
implement groupm
implement locationm
implement rectm
implement boolexprm
implement soundm
implement effectm
implement unitpoolm
implement itempoolm
implement questm
implement questitemm
implement defeatconditionm
implement timerdialogm
implement leaderboardm
implement multiboardm
implement multiboarditemm
implement trackablem
implement dialogm
implement buttonm
implement texttagm
implement lightningm
implement imagem
implement ubersplatm
implement regionm
implement fogstatem
implement fogmodifierm
implement hashtablem
method operator handle takes nothing returns handles
return this
endmethod
method operator agent takes nothing returns agents
return this
endmethod
//set this = tb[GetSpellAbilityId()]
method operator [] takes integer key returns Table
return LoadInteger(ht, this, key)
endmethod
//set tb[389034] = 8192
method operator []= takes integer key, Table tb returns nothing
call SaveInteger(ht, this, key, tb)
endmethod
//set b = tb.has(2493223)
method has takes integer key returns boolean
return HaveSavedInteger(ht, this, key)
endmethod
//call tb.remove(294080)
method remove takes integer key returns nothing
call RemoveSavedInteger(ht, this, key)
endmethod
//Remove all data from a Table instance
method flush takes nothing returns nothing
call FlushChildHashtable(ht, this)
endmethod
//local Table tb = Table.create()
static method create takes nothing returns Table
local Table this = dex.list[0]
if this == 0 then
set this = more + 1
set more = this
else
set dex.list[0] = dex.list[this]
call dex.list.remove(this) //Clear hashed memory
endif
debug set dex.list[this] = -1
return this
endmethod
// Removes all data from a Table instance and recycles its index.
//
// call tb.destroy()
//
method destroy takes nothing returns nothing
debug if dex.list[this] != -1 then
debug call BJDebugMsg("Table Error: Tried to double-free instance: " + I2S(this))
debug return
debug endif
call this.flush()
set dex.list[this] = dex.list[0]
set dex.list[0] = this
endmethod
//! runtextmacro optional TABLE_BC_METHODS()
endstruct
//! runtextmacro optional TABLE_BC_STRUCTS()
struct TableArray extends array
//Returns a new TableArray to do your bidding. Simply use:
//
// local TableArray ta = TableArray[array_size]
//
static method operator [] takes integer array_size returns TableArray
local Table tb = dex.size[array_size] //Get the unique recycle list for this array size
local TableArray this = tb[0] //The last-destroyed TableArray that had this array size
debug if array_size <= 0 then
debug call BJDebugMsg("TypeError: Invalid specified TableArray size: " + I2S(array_size))
debug return 0
debug endif
if this == 0 then
set this = less - array_size
set less = this
else
set tb[0] = tb[this] //Set the last destroyed to the last-last destroyed
call tb.remove(this) //Clear hashed memory
endif
set dex.size[this] = array_size //This remembers the array size
return this
endmethod
//Returns the size of the TableArray
method operator size takes nothing returns integer
return dex.size[this]
endmethod
//This magic method enables two-dimensional[array][syntax] for Tables,
//similar to the two-dimensional utility provided by hashtables them-
//selves.
//
//ta[integer a].unit[integer b] = unit u
//ta[integer a][integer c] = integer d
//
//Inline-friendly when not running in debug mode
//
method operator [] takes integer key returns Table
static if DEBUG_MODE then
local integer i = this.size
if i == 0 then
call BJDebugMsg("IndexError: Tried to get key from invalid TableArray instance: " + I2S(this))
return 0
elseif key < 0 or key >= i then
call BJDebugMsg("IndexError: Tried to get key [" + I2S(key) + "] from outside TableArray bounds: " + I2S(i))
return 0
endif
endif
return this + key
endmethod
//Destroys a TableArray without flushing it; I assume you call .flush()
//if you want it flushed too. This is a public method so that you don't
//have to loop through all TableArray indices to flush them if you don't
//need to (ie. if you were flushing all child-keys as you used them).
//
method destroy takes nothing returns nothing
local Table tb = dex.size[this.size]
debug if this.size == 0 then
debug call BJDebugMsg("TypeError: Tried to destroy an invalid TableArray: " + I2S(this))
debug return
debug endif
if tb == 0 then
//Create a Table to index recycled instances with their array size
set tb = Table.create()
set dex.size[this.size] = tb
endif
call dex.size.remove(this) //Clear the array size from hash memory
set tb[this] = tb[0]
set tb[0] = this
endmethod
private static Table tempTable
private static integer tempEnd
//Avoids hitting the op limit
private static method clean takes nothing returns nothing
local Table tb = .tempTable
local integer end = tb + 0x1000
if end < .tempEnd then
set .tempTable = end
call ForForce(bj_FORCE_PLAYER[0], function thistype.clean)
else
set end = .tempEnd
endif
loop
call tb.flush()
set tb = tb + 1
exitwhen tb == end
endloop
endmethod
//Flushes the TableArray and also destroys it. Doesn't get any more
//similar to the FlushParentHashtable native than this.
//
method flush takes nothing returns nothing
debug if this.size == 0 then
debug call BJDebugMsg("TypeError: Tried to flush an invalid TableArray instance: " + I2S(this))
debug return
debug endif
set .tempTable = this
set .tempEnd = this + this.size
call ForForce(bj_FORCE_PLAYER[0], function thistype.clean)
call this.destroy()
endmethod
endstruct
endlibrary
//TESH.scrollpos=24
//TESH.alwaysfold=0
/**************************************************************
*
* RegisterPlayerUnitEvent
* v5.1.0.1
* By Magtheridon96
*
* I would like to give a special thanks to Bribe, azlier
* and BBQ for improving this library. For modularity, it only
* supports player unit events.
*
* Functions passed to RegisterPlayerUnitEvent must either
* return a boolean (false) or nothing. (Which is a Pro)
*
* Warning:
* --------
*
* - Don't use TriggerSleepAction inside registered code.
* - Don't destroy a trigger unless you really know what you're doing.
*
* API:
* ----
*
* - function RegisterPlayerUnitEvent takes playerunitevent whichEvent, code whichFunction returns nothing
* - Registers code that will execute when an event fires.
* - function RegisterPlayerUnitEventForPlayer takes playerunitevent whichEvent, code whichFunction, player whichPlayer returns nothing
* - Registers code that will execute when an event fires for a certain player.
* - function GetPlayerUnitEventTrigger takes playerunitevent whichEvent returns trigger
* - Returns the trigger corresponding to ALL functions of a playerunitevent.
*
**************************************************************/
library RegisterPlayerUnitEvent // Special Thanks to Bribe and azlier
globals
private trigger array t
endglobals
function RegisterPlayerUnitEvent takes playerunitevent p, code c returns nothing
local integer i = GetHandleId(p)
local integer k = 15
if t[i] == null then
set t[i] = CreateTrigger()
loop
call TriggerRegisterPlayerUnitEvent(t[i], Player(k), p, null)
exitwhen k == 0
set k = k - 1
endloop
endif
call TriggerAddCondition(t[i], Filter(c))
endfunction
function RegisterPlayerUnitEventForPlayer takes playerunitevent p, code c, player pl returns nothing
local integer i = 16 * GetHandleId(p) + GetPlayerId(pl)
if t[i] == null then
set t[i] = CreateTrigger()
call TriggerRegisterPlayerUnitEvent(t[i], pl, p, null)
endif
call TriggerAddCondition(t[i], Filter(c))
endfunction
function GetPlayerUnitEventTrigger takes playerunitevent p returns trigger
return t[GetHandleId(p)]
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
//============================================================================
// SpellEffectEvent
// - Version 1.1.0.0
//
// API
// ---
// RegisterSpellEffectEvent(integer abil, code onCast)
//
// Requires
// --------
// RegisterPlayerUnitEvent: hiveworkshop.com/forums/showthread.php?t=203338
//
// Optional
// --------
// Table: hiveworkshop.com/forums/showthread.php?t=188084
//
library SpellEffectEvent requires RegisterPlayerUnitEvent, optional Table
//============================================================================
private module M
static if LIBRARY_Table then
static Table tb
else
static hashtable ht = InitHashtable()
endif
static method onCast takes nothing returns nothing
static if LIBRARY_Table then
call TriggerEvaluate(.tb.trigger[GetSpellAbilityId()])
else
call TriggerEvaluate(LoadTriggerHandle(.ht, 0, GetSpellAbilityId()))
endif
endmethod
private static method onInit takes nothing returns nothing
static if LIBRARY_Table then
set .tb = Table.create()
endif
call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_SPELL_EFFECT, function thistype.onCast)
endmethod
endmodule
//============================================================================
private struct S extends array
implement M
endstruct
//============================================================================
function RegisterSpellEffectEvent takes integer abil, code onCast returns nothing
static if LIBRARY_Table then
if not S.tb.handle.has(abil) then
set S.tb.trigger[abil] = CreateTrigger()
endif
call TriggerAddCondition(S.tb.trigger[abil], Filter(onCast))
else
if not HaveSavedHandle(S.ht, 0, abil) then
call SaveTriggerHandle(S.ht, 0, abil, CreateTrigger())
endif
call TriggerAddCondition(LoadTriggerHandle(S.ht, 0, abil), Filter(onCast))
endif
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library TimerUtils initializer init
//*********************************************************************
//* TimerUtils (red+blue+orange flavors for 1.24b+) 2.0
//* ----------
//*
//* To implement it , create a custom text trigger called TimerUtils
//* and paste the contents of this script there.
//*
//* To copy from a map to another, copy the trigger holding this
//* library to your map.
//*
//* (requires vJass) More scripts: htt://www.wc3c.net
//*
//* For your timer needs:
//* * Attaching
//* * Recycling (with double-free protection)
//*
//* set t=NewTimer() : Get a timer (alternative to CreateTimer)
//* set t=NewTimerEx(x) : Get a timer (alternative to CreateTimer), call
//* Initialize timer data as x, instead of 0.
//*
//* ReleaseTimer(t) : Relese a timer (alt to DestroyTimer)
//* SetTimerData(t,2) : Attach value 2 to timer
//* GetTimerData(t) : Get the timer's value.
//* You can assume a timer's value is 0
//* after NewTimer.
//*
//* Multi-flavor:
//* Set USE_HASH_TABLE to true if you don't want to complicate your life.
//*
//* If you like speed and giberish try learning about the other flavors.
//*
//********************************************************************
//================================================================
globals
//How to tweak timer utils:
// USE_HASH_TABLE = true (new blue)
// * SAFEST
// * SLOWEST (though hash tables are kind of fast)
//
// USE_HASH_TABLE = false, USE_FLEXIBLE_OFFSET = true (orange)
// * kinda safe (except there is a limit in the number of timers)
// * ALMOST FAST
//
// USE_HASH_TABLE = false, USE_FLEXIBLE_OFFSET = false (red)
// * THE FASTEST (though is only faster than the previous method
// after using the optimizer on the map)
// * THE LEAST SAFE ( you may have to tweak OFSSET manually for it to
// work)
//
private constant boolean USE_HASH_TABLE = true
private constant boolean USE_FLEXIBLE_OFFSET = false
private constant integer OFFSET = 0x100000
private integer VOFFSET = OFFSET
//Timers to preload at map init:
private constant integer QUANTITY = 256
//Changing this to something big will allow you to keep recycling
// timers even when there are already AN INCREDIBLE AMOUNT of timers in
// the stack. But it will make things far slower so that's probably a bad idea...
private constant integer ARRAY_SIZE = 8190
endglobals
//==================================================================================================
globals
private integer array data[ARRAY_SIZE]
private hashtable ht
endglobals
//It is dependent on jasshelper's recent inlining optimization in order to perform correctly.
function SetTimerData takes timer t, integer value returns nothing
static if(USE_HASH_TABLE) then
// new blue
call SaveInteger(ht,0,GetHandleId(t), value)
elseif (USE_FLEXIBLE_OFFSET) then
// orange
static if (DEBUG_MODE) then
if(GetHandleId(t)-VOFFSET<0) then
call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
endif
endif
set data[GetHandleId(t)-VOFFSET]=value
else
// new red
static if (DEBUG_MODE) then
if(GetHandleId(t)-OFFSET<0) then
call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
endif
endif
set data[GetHandleId(t)-OFFSET]=value
endif
endfunction
function GetTimerData takes timer t returns integer
static if(USE_HASH_TABLE) then
// new blue
return LoadInteger(ht,0,GetHandleId(t) )
elseif (USE_FLEXIBLE_OFFSET) then
// orange
static if (DEBUG_MODE) then
if(GetHandleId(t)-VOFFSET<0) then
call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
endif
endif
return data[GetHandleId(t)-VOFFSET]
else
// new red
static if (DEBUG_MODE) then
if(GetHandleId(t)-OFFSET<0) then
call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
endif
endif
return data[GetHandleId(t)-OFFSET]
endif
endfunction
//==========================================================================================
globals
private timer array tT[ARRAY_SIZE]
private integer tN = 0
private constant integer HELD=0x28829022
//use a totally random number here, the more improbable someone uses it, the better.
private boolean didinit = false
endglobals
private keyword init
//==========================================================================================
// I needed to decide between duplicating code ignoring the "Once and only once" rule
// and using the ugly textmacros. I guess textmacros won.
//
//! textmacro TIMERUTIS_PRIVATE_NewTimerCommon takes VALUE
// On second thought, no.
//! endtextmacro
function NewTimerEx takes integer value returns timer
if (tN==0) then
if (not didinit) then
//This extra if shouldn't represent a major performance drawback
//because QUANTITY rule is not supposed to be broken every day.
call init.evaluate()
set tN = tN - 1
else
//If this happens then the QUANTITY rule has already been broken, try to fix the
// issue, else fail.
debug call BJDebugMsg("NewTimer: Warning, Exceeding TimerUtils_QUANTITY, make sure all timers are getting recycled correctly")
set tT[0]=CreateTimer()
static if( not USE_HASH_TABLE) then
debug call BJDebugMsg("In case of errors, please increase it accordingly, or set TimerUtils_USE_HASH_TABLE to true")
static if( USE_FLEXIBLE_OFFSET) then
if (GetHandleId(tT[0])-VOFFSET<0) or (GetHandleId(tT[0])-VOFFSET>=ARRAY_SIZE) then
//all right, couldn't fix it
call BJDebugMsg("NewTimer: Unable to allocate a timer, you should probably set TimerUtils_USE_HASH_TABLE to true or fix timer leaks.")
return null
endif
else
if (GetHandleId(tT[0])-OFFSET<0) or (GetHandleId(tT[0])-OFFSET>=ARRAY_SIZE) then
//all right, couldn't fix it
call BJDebugMsg("NewTimer: Unable to allocate a timer, you should probably set TimerUtils_USE_HASH_TABLE to true or fix timer leaks.")
return null
endif
endif
endif
endif
else
set tN=tN-1
endif
call SetTimerData(tT[tN],value)
return tT[tN]
endfunction
function NewTimer takes nothing returns timer
return NewTimerEx(0)
endfunction
//==========================================================================================
function ReleaseTimer takes timer t returns nothing
if(t==null) then
debug call BJDebugMsg("Warning: attempt to release a null timer")
return
endif
if (tN==ARRAY_SIZE) then
debug call BJDebugMsg("Warning: Timer stack is full, destroying timer!!")
//stack is full, the map already has much more troubles than the chance of bug
call DestroyTimer(t)
else
call PauseTimer(t)
if(GetTimerData(t)==HELD) then
debug call BJDebugMsg("Warning: ReleaseTimer: Double free!")
return
endif
call SetTimerData(t,HELD)
set tT[tN]=t
set tN=tN+1
endif
endfunction
private function init takes nothing returns nothing
local integer i=0
local integer o=-1
local boolean oops = false
if ( didinit ) then
return
else
set didinit = true
endif
static if( USE_HASH_TABLE ) then
set ht = InitHashtable()
loop
exitwhen(i==QUANTITY)
set tT[i]=CreateTimer()
call SetTimerData(tT[i], HELD)
set i=i+1
endloop
set tN = QUANTITY
else
loop
set i=0
loop
exitwhen (i==QUANTITY)
set tT[i] = CreateTimer()
if(i==0) then
set VOFFSET = GetHandleId(tT[i])
static if(USE_FLEXIBLE_OFFSET) then
set o=VOFFSET
else
set o=OFFSET
endif
endif
if (GetHandleId(tT[i])-o>=ARRAY_SIZE) then
exitwhen true
endif
if (GetHandleId(tT[i])-o>=0) then
set i=i+1
endif
endloop
set tN = i
exitwhen(tN == QUANTITY)
set oops = true
exitwhen not USE_FLEXIBLE_OFFSET
debug call BJDebugMsg("TimerUtils_init: Failed a initialization attempt, will try again")
endloop
if(oops) then
static if ( USE_FLEXIBLE_OFFSET) then
debug call BJDebugMsg("The problem has been fixed.")
//If this message doesn't appear then there is so much
//handle id fragmentation that it was impossible to preload
//so many timers and the thread crashed! Therefore this
//debug message is useful.
elseif(DEBUG_MODE) then
call BJDebugMsg("There were problems and the new timer limit is "+I2S(i))
call BJDebugMsg("This is a rare ocurrence, if the timer limit is too low:")
call BJDebugMsg("a) Change USE_FLEXIBLE_OFFSET to true (reduces performance a little)")
call BJDebugMsg("b) or try changing OFFSET to "+I2S(VOFFSET) )
endif
endif
endif
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library BoundSentinel initializer init
//*************************************************
//* BoundSentinel
//* -------------
//* Don't leave your units unsupervised, naughty
//* them may try to get out of the map bounds and
//* crash your game.
//*
//* To implement, just get a vJass compiler and
//* copy this library/trigger to your map.
//*
//*************************************************
//==================================================
globals
// High enough so the unit is no longer visible, low enough so the
// game doesn't crash...
//
// I think you need 0.0 or soemthing negative prior to patch 1.22
//
private constant real EXTRA = 500.0
endglobals
//=========================================================================================
globals
private real maxx
private real maxy
private real minx
private real miny
endglobals
//=======================================================================
private function dis takes nothing returns nothing
local unit u=GetTriggerUnit()
local real x=GetUnitX(u)
local real y=GetUnitY(u)
if(x>maxx) then
set x=maxx
elseif(x<minx) then
set x=minx
endif
if(y>maxy) then
set y=maxy
elseif(y<miny) then
set y=miny
endif
call SetUnitX(u,x)
call SetUnitY(u,y)
set u=null
endfunction
private function init takes nothing returns nothing
local trigger t=CreateTrigger()
local region r=CreateRegion()
local rect rc
set minx=GetCameraBoundMinX() - EXTRA
set miny=GetCameraBoundMinY() - EXTRA
set maxx=GetCameraBoundMaxX() + EXTRA
set maxy=GetCameraBoundMaxY() + EXTRA
set rc=Rect(minx,miny,maxx,maxy)
call RegionAddRect(r, rc)
call RemoveRect(rc)
call TriggerRegisterLeaveRegion(t,r, null)
call TriggerAddAction(t, function dis)
//this is not necessary but I'll do it anyway:
set t=null
set r=null
set rc=null
endfunction
endlibrary
//TESH.scrollpos=291
//TESH.alwaysfold=0
//*************************************************************************************
//*************************************************************************************
//*************************************************************************************
//*************************************************************************************
//*************************************************************************************
//*************************************************************************************
// L I G H T N I N G S T O R M
// - Spell Create By:
// + Elphis
//*************************************************************************************
//*************************************************************************************
//*************************************************************************************
library LightningStorm /*
*/ requires /*
// -*/ DelFX /* http://www.hiveworkshop.com/forums/spells-569/delfx-delayed-effects-1-4-2-a-116064/?prev=search%3DDel%2520Effect%26d%3Dlist%26r%3D20%26c%3D112
// -*/ TimerUtils /* http://www.wc3c.net/showthread.php?t=101322
// -*/ SpellEffectEvent /* http://www.hiveworkshop.com/forums/jass-resources-412/snippet-spelleffectevent-187193/
// -*/ xebasic /* http://www.wc3c.net/showthread.php?t=101150
// -*/ xedamage /* http://www.wc3c.net/showthread.php?t=101150
// -*/ Table /* http://www.wc3c.net/showthread.php?t=101246
// -*/ LightningStormPlugin /* Plugin */
// Optional:
// LightningAura.mdx - http://www.hiveworkshop.com/forums/models-530/lightning-aura-98346/?prev=search%3DLightning%2520Aura%26d%3Dlist%26r%3D20
// BoundSentinel - http://www.wc3c.net/showthread.php?t=102576 (You must adding it, this library help you prevent crash the game when lightning outside XY of map.
//*************************************************************************************
//*************************************************************************************
//*************************************************************************************
//*************************************************************************************
// - Spell Information:
// - Call a powerful beam of energy from the ground, accumulate and discharge power super strong
// when enemies within 150 range of this power, enemies will shock and lost 30/60/90/120 hit points
// after storm release 30/40/50/60 lightning, it will absorbed all previous energy
// and discharge 30/40/50/60 lightning again also deals 30 damage to enemies
// and then, it accumulates, and exploded, the explosion caused a power flow and deals 15/30/45/60 damage to enemies.
//*************************************************************************************
//*************************************************************************************
//*************************************************************************************
//*************************************************************************************
// - Installation:
// - Import/copy the required libraries and Lightning Storm code to your map
// - Import/copy the custom ability and unit to your map and change the SPELL_ID,DUMMY_LIGHTNING if needed
// - Export model LightningAura.mdx, dummy.mdx and import it into your map
// - You may view the raw ID of the objects by pressing CTRL+D in the object editor
// - You may play with the configurables below
// - When you copy dummy "xe unit (Caster System?)", you must change rawcode ID of this dummy = xeca
// or you must change rawcode in xebasic with name globals: XE_DUMMY_UNITID
//*************************************************************************************
//*************************************************************************************
//*************************************************************************************
//*************************************************************************************
//*************************************************************************************
globals
//============================================SPELL CONFIGURATION==========================================================
//*************************************************************************************************************************
//Spell rawcode, change if needed
private constant integer SPELL_ID = 'A000'
//
//Dummy lightning rawcode, change if needed
private constant integer DUMMY_LIGHTNING = 'e000'
//Ability allow unit can fly change if needed
private constant integer ABI_FLY = 'Amrf'
//Spell Periodic
private constant real PERIODIC = 0.031250000
//
//*************************************************************************************************************************
//*************************************************************************************************************************
//*************************************************************************************************************************
//Damage base when lightning hit enemies
private constant real DAMAGE_BASE = 30.
//Damag radius when lightning hit ground
private constant real DAMAGE_RADIUS = 150.
//Damage radius when powerful lightning causes an explotion, don't increase this value too much because it very powerful
private constant real DAMAGE_RADIUS_EXPLORE = 75.
//Area of effect of spell
//This value must be divisible by two
private constant real AREA_OF_EFFECT = 600.
//Powerful lightning height
//This value must be divisible by two
private constant real L_HEIGHT_POWER = 600.
//Lightning release intevar, decrease if you want to spell "SPAM" ^^ (just kidding), increase if spell very powerful :)
private constant real RELEASE_INTEVAR = 0.1
//Size of center lighning (also size of other lightning/2)
private constant real SIZE = 3.
//Min height of lightning
private constant real MIN_HEIGHT = 50.
//Distance increase of other lightning per periodic
private constant real DISTANCE_INC = 10.
//Min distance release lightning
private constant real MIN_REALSE_L = 200.
//Lightning release speed
private constant real LIGHTNING_R_S = 10.
//Height of center lightning when spell expire
private constant real HEIGHT_INC = 15.
//
//*************************************************************************************************************************
//*************************************************************************************************************************
//*************************************************************************************************************************
//Total lightning release of spell (Spell expire when release all lightning with value)
private constant integer RELEASE_COUNT = 30
//
//*************************************************************************************************************************
//*************************************************************************************************************************
//*************************************************************************************************************************
//Center Lightning Vortex (Like Eye Storm)
private constant string M_MODEL = "war3mapImported\\LightningAura.mdx"
//Lightning effect when all lightning causes an explotion
private constant string E_MODEL = "war3mapImported\\Great Lightning.mdx"
//Orb of lightning model
private constant string O_MODEL = "Abilities\\Weapons\\FarseerMissile\\FarseerMissile.mdl"
//Effect when lightning hit ground
private constant string RELEASE_L_EFFECT = "Abilities\\Weapons\\Bolt\\BoltImpact.mdl"
//Main lightning attach to orb
private constant string M_ALT = "DRAM"
//Other lightning attach to orb
private constant string O_ALT = "MFPB"
//Hmm, i don't know how to explain
private constant string E_ALT = "CLPB"
//Release lightning model
private constant string R_ALT = "AFOD"
//Attachment effect of dummy
private constant string D_ATTACHMENT = "origin"
//Attachment effect of center lightning
private constant string M_ATTACHMENT = "origin"
//
//*************************************************************************************************************************
//*************************************************************************************************************************
//*************************************************************************************************************************
// ======DAMAGE SETTINGS======
private constant attacktype ATTACKTYPE = ATTACK_TYPE_HERO
private constant damagetype DAMAGETYPE = DAMAGE_TYPE_COLD
private constant weapontype WEAPONTYPE = WEAPON_TYPE_METAL_HEAVY_BASH
//*************************************************************************************************************************
//*************************************************************************************************************************
//*************************************************************************************************************************
endglobals
struct LighningStorm
unit caster
unit dummy
//
real xx
real yy
real angle
real s
real reach = 0.
real intevar = 0.
real dam
//
integer count
integer k = 0
//
Table tab
xedamage xed
static method onCount takes unit u returns integer
return RELEASE_COUNT + GetUnitAbilityLevel(u,SPELL_ID)*10 //Count lightning release per periodic
endmethod
static method onDamage takes unit u returns real
return DAMAGE_BASE + GetUnitAbilityLevel(u,SPELL_ID) //Damage per lightning hit enemies unit
endmethod
static method onPeriodic takes nothing returns nothing
//
local timer t = GetExpiredTimer()
local thistype this = GetTimerData(t)
//
local integer i = 6
local real x
local real y
local real cos
local real sin
//
if k == 0 then
if s < SIZE then
set s = s + tab.real[this]
//
call SetUnitScale(dummy,s,0.,0.)
//
loop
exitwhen i == 0
call SetUnitScale(tab.unit[i],s/2,0.,0.)
call SetUnitScale(tab.unit[10+i],s/2,0.,0.)
set i = i - 1
endloop
//
set reach = reach + DISTANCE_INC
//
else
if intevar < RELEASE_INTEVAR then
set intevar = intevar + PERIODIC
else
set intevar = 0.
set count = count - 1
//This line release lightning
call RLPLLS.add(R_ALT,RELEASE_L_EFFECT,caster,tab.unit[50+GetRandomInt(1,6)],dam,GetRandomReal(MIN_REALSE_L,AREA_OF_EFFECT),DAMAGE_RADIUS,LIGHTNING_R_S,ATTACKTYPE,DAMAGETYPE,WEAPONTYPE)
//
if count == 0 then
set k = 1
set s = 0.
set intevar = 0.
endif
//
endif
endif
elseif k == 1 then
set reach = reach - DISTANCE_INC
endif
//
//
if k <= 1 then
//
set angle = angle + 5.
set i = 6
//
loop
exitwhen i == 0
//
if reach > 0. then
set cos = Cos((tab.real[20+i]+angle)*.0174533)
set sin = Sin((tab.real[20+i]+angle)*.0174533)
set x = xx + reach/2 * cos
set y = yy + reach/2 * sin
//
call SetUnitX(tab.unit[i],x)
call SetUnitY(tab.unit[i],y)
call MoveLightning(tab.lightning[30+i],true,x,y,xx,yy)
//
set x = xx + reach * cos
set y = yy + reach * sin
//
call SetUnitX(tab.unit[10+i],x)
call SetUnitY(tab.unit[10+i],y)
call MoveLightning(tab.lightning[40+i],true,x,y,GetWidgetX(tab.unit[i]),GetWidgetY(tab.unit[i]))
call MoveLightningEx(tab.lightning[60+i],true,x,y,MIN_HEIGHT,GetWidgetX(tab.unit[51]),GetWidgetY(tab.unit[51]),GetUnitFlyHeight(tab.unit[51]))
else
set k = 2
set s = 0.
call RemoveUnit(tab.unit[i])
call RemoveUnit(tab.unit[10+i])
call DestroyLightning(tab.lightning[30+i])
call DestroyLightning(tab.lightning[40+i])
call DestroyLightning(tab.lightning[60+i])
//
set tab.unit[i] = null
set tab.unit[10+i] = null
set tab.lightning[30+i] = null
set tab.lightning[40+i] = null
set tab.lightning[60+i] = null
//
endif
//
set i = i - 1
endloop
elseif s < L_HEIGHT_POWER and k == 2 then
//
set s = s + HEIGHT_INC
//
call SetUnitFlyHeight(dummy,s,0.)
//
set i = 6
//
loop
exitwhen i == 0
if s >= tab.real[70+i] and tab.unit[50+i] != null then
call RemoveUnit(tab.unit[50+i])
set tab.unit[50+i] = null
endif
set i = i - 1
endloop
//
if s >= L_HEIGHT_POWER then
//
set i = 6
set k = 3
//
loop
exitwhen i == 0
//
set x = xx + 100. * Cos(i*60*.0174533)
set y = yy + 100. * Sin(i*60*.0174533)
//
set tab.unit[i] = CreateUnit(Player(15),DUMMY_LIGHTNING,x,y,0.)
set tab.lightning[10+i] = AddLightningEx(M_ALT,true,x,y,s,xx,yy,s)
call SetUnitScale(tab.unit[i],SIZE/2,0.,0.)
call UnitAddAbility(tab.unit[i],ABI_FLY)
call UnitRemoveAbility(tab.unit[i],ABI_FLY)
call SetUnitFlyHeight(tab.unit[i],s,0.)
//
set i = i - 1
endloop
//
endif
//
elseif reach < AREA_OF_EFFECT and k == 3 then
//
set reach = reach + DISTANCE_INC
//
loop
exitwhen i == 0
//
set x = xx + reach * Cos((tab.real[20+i]+angle)*.0174533)
set y = yy + reach * Sin((tab.real[20+i]+angle)*.0174533)
//
call SetUnitX(tab.unit[i],x)
call SetUnitY(tab.unit[i],y)
call MoveLightningEx(tab.lightning[10+i],true,x,y,s,xx,yy,L_HEIGHT_POWER)
//
set i = i - 1
endloop
//
if reach >= AREA_OF_EFFECT then
set k = 4
endif
elseif s > MIN_HEIGHT and k == 4 then
//
set s = s - HEIGHT_INC*2
//
loop
exitwhen i == 0
//
set x = xx + reach * Cos((tab.real[20+i]+angle)*.0174533)
set y = yy + reach * Sin((tab.real[20+i]+angle)*.0174533)
//
call MoveLightningEx(tab.lightning[10+i],true,x,y,s,xx,yy,L_HEIGHT_POWER)
//
call SetUnitFlyHeight(tab.unit[i],s,0.)
//
set i = i - 1
endloop
//
if s <= MIN_HEIGHT then
set k = 5
set count = onCount(caster)
endif
//
elseif count > 0 and k == 5 then
set count = count - 1
//This line release lightning
call RLPLLS.add(R_ALT,RELEASE_L_EFFECT,caster,dummy,dam,GetRandomReal(MIN_REALSE_L,AREA_OF_EFFECT),DAMAGE_RADIUS,LIGHTNING_R_S*3,ATTACKTYPE,DAMAGETYPE,WEAPONTYPE)
if count == 0 then
set k = 6
endif
elseif s < L_HEIGHT_POWER and k == 6 then
set s = s + HEIGHT_INC
call SetUnitFlyHeight(dummy,GetUnitFlyHeight(dummy)-HEIGHT_INC,0.)
loop
exitwhen i == 0
//
call MoveLightningEx(tab.lightning[10+i],true,GetWidgetX(tab.unit[i]),GetWidgetY(tab.unit[i]),s,xx,yy,GetUnitFlyHeight(dummy))
//
call SetUnitFlyHeight(tab.unit[i],s,0.)
//
set i = i - 1
endloop
if s >= L_HEIGHT_POWER then
set k = 7
endif
elseif reach > 0. and k == 7 then
set reach = reach - DISTANCE_INC
loop
exitwhen i == 0
//
set x = xx + reach * Cos((tab.real[20+i]+angle)*.0174533)
set y = yy + reach * Sin((tab.real[20+i]+angle)*.0174533)
//
call MoveLightningEx(tab.lightning[10+i],true,x,y,s,xx,yy,0.)
//
call SetUnitX(tab.unit[i],x)
call SetUnitY(tab.unit[i],y)
//
set i = i - 1
endloop
if reach <= 0. then
set k = 8
endif
elseif s > MIN_HEIGHT and k == 8 then
set s = s - HEIGHT_INC*2
//
loop
exitwhen i == 0
//
call MoveLightningEx(tab.lightning[10+i],true,GetWidgetX(tab.unit[i]),GetWidgetY(tab.unit[i]),s,xx,yy,0.)
//
call SetUnitFlyHeight(tab.unit[i],s,0.)
//
set i = i - 1
endloop
//
set i = 6
//
if s <= MIN_HEIGHT then
set k = 9
//
loop
exitwhen i == 0
//
call RemoveUnit(tab.unit[i])
call DestroyLightning(tab.lightning[10+i])
//
set tab.unit[i] = null
set tab.lightning[10+i] = null
//
set i = i - 1
endloop
//
set s = SIZE
//
endif
elseif s > 0. and k == 9 then
set s = s - tab.real[this]*2
call SetUnitScale(dummy,s,0.,0.)
//
if s <= 0. then
set k = 10
call RemoveUnit(dummy)
set dummy = null
endif
elseif reach < AREA_OF_EFFECT and k == 10 then
set reach = reach + DISTANCE_INC*4
loop
exitwhen i == 0
//
set x = xx + reach * Cos(i*60*.0174533)
set y = yy + reach * Sin(i*60*.0174533)
//
call DestroyEffect(AddSpecialEffect(RELEASE_L_EFFECT,x,y))
//This line deals damage to enemies unit
call xed.damageAOE(caster,x,y,DAMAGE_RADIUS_EXPLORE,dam/2)
//
set i = i - 1
endloop
else
//Clean data
call ReleaseTimer(t)
set caster = null
call tab.flush()
call tab.destroy()
call xed.destroy()
call destroy()
endif
//
set t = null
//
endmethod
static method onCast takes nothing returns nothing
//
local thistype this = allocate()
//
local integer ii = 6
local real x
local real y
//Spell config
set caster = GetTriggerUnit()
//
set tab = Table.create()
//
set xx = GetSpellTargetX()
set yy = GetSpellTargetY()
//Xe damage install
set xed = xedamage.create()
set xed.dtype = DAMAGETYPE
set xed.atype = ATTACKTYPE
set xed.wtype = WEAPONTYPE
set xed.exception = UNIT_TYPE_STRUCTURE
set xed.damageEnemies = true
set xed.damageTrees = true
//
set dam = onDamage(caster)
set angle = L_HEIGHT_POWER/6
set count = onCount(caster)
set tab.real[this] = SIZE/AREA_OF_EFFECT*DISTANCE_INC
//
set dummy = CreateUnit(Player(15),XE_DUMMY_UNITID,xx,yy,0.)
call SetUnitScale(dummy,0.,0.,0.)
call UnitAddAbility(dummy,ABI_FLY)
call UnitRemoveAbility(dummy,ABI_FLY)
call CreateDelayedEffectTarget(M_MODEL,dummy,M_ATTACHMENT,0.,30.)
//
loop
exitwhen ii == 0
//
set x = xx + 100. * Cos(ii*60*.0174533)
set y = yy + 100. * Sin(ii*60*.0174533)
//
set tab.unit[ii] = CreateUnit(Player(15),DUMMY_LIGHTNING,x,y,0.)
set tab.real[20+ii] = 57.29582 * Atan2(y - yy, x - xx)
call SetUnitScale(tab.unit[ii],0.,0.,0.)
call UnitAddAbility(tab.unit[ii],ABI_FLY)
call UnitRemoveAbility(tab.unit[ii],ABI_FLY)
call SetUnitFlyHeight(tab.unit[ii],MIN_HEIGHT,MIN_HEIGHT)
//
set tab.unit[10+ii] = CreateUnit(Player(15),DUMMY_LIGHTNING,x,y,0.)
call SetUnitScale(tab.unit[10+ii],0.,0.,0.)
call UnitAddAbility(tab.unit[10+ii],ABI_FLY)
call UnitRemoveAbility(tab.unit[10+ii],ABI_FLY)
call SetUnitFlyHeight(tab.unit[10+ii],MIN_HEIGHT,MIN_HEIGHT)
//
set tab.unit[50+ii] = CreateUnit(Player(15),DUMMY_LIGHTNING,xx,yy,0.)
call SetUnitScale(tab.unit[50+ii],SIZE/2,0.,0.)
call UnitAddAbility(tab.unit[50+ii],ABI_FLY)
call UnitRemoveAbility(tab.unit[50+ii],ABI_FLY)
set s = s + angle
call SetUnitFlyHeight(tab.unit[50+ii],s,s)
set tab.real[70+ii] = s
//
set tab.lightning[30+ii] = AddLightning(M_ALT,true,x,y,xx,yy)
set tab.lightning[40+ii] = AddLightning(O_ALT,true,x,y,xx,yy)
set tab.lightning[60+ii] = AddLightning(E_ALT,true,x,y,xx,yy)
//
//
set ii = ii - 1
endloop
//
set angle = 0.
set s = 0.
//
call TimerStart(NewTimerEx(this),PERIODIC,true,function thistype.onPeriodic)
//
endmethod
static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(SPELL_ID,function thistype.onCast)
endmethod
endstruct
endlibrary
//TESH.scrollpos=88
//TESH.alwaysfold=0
library LightningStormPlugin requires TimerUtils,xedamage
//
globals
private constant real PERIODIC = 0.031250000
endglobals
//
struct RLPLLS
//
unit c
unit dummy
//
lightning l
//
real xx
real yy
real an
real s
real reach
real in
real dam
real cos
real sin
real sp
real dr
//
string ef
//
xedamage xed
//
//PLUGIN OF STORM LIGHTNING
static method periodicPlugin takes nothing returns nothing
//
local timer t = GetExpiredTimer()
local thistype this = GetTimerData(t)
//
local real x
local real y
//
if an > 0. then
//
set an = an - in
//
set reach = reach + sp
//
set x = xx + reach * cos
set y = yy + reach * sin
//
call MoveLightningEx(l,true,xx,yy,s,x,y,an)
//
else
//
call ReleaseTimer(t)
//
set x = xx + reach * cos
set y = yy + reach * sin
//
call xed.damageAOE(c,x,y,dr,dam)
call DestroyEffect(AddSpecialEffect(ef,x,y))
//
call DestroyLightning(l)
set l = null
set dummy = null
set c = null
call xed.destroy()
call destroy()
endif
//
set t = null
//
endmethod
static method add takes string lightningmodel,string effe,unit caster,unit attach,real damage,real dis,real damageradius,real speed,attacktype a,damagetype dd,weapontype w returns nothing
local thistype this = allocate()
//
set c = caster
set ef = effe
set dummy = attach
set dam = damage
set sp = speed
set reach = 0.
set an = GetRandomReal(0,360)
set s = GetUnitFlyHeight(dummy)
set in = s/dis*sp
set l = AddLightning(lightningmodel,true,GetUnitX(dummy),GetUnitY(dummy),GetUnitX(dummy),GetUnitY(dummy))
set xx = GetUnitX(dummy)
set yy = GetUnitY(dummy)
set cos = Cos(an*0.0174533)
set sin = Sin(an*0.0174533)
set dr = damageradius
set an = s
//
set xed = xedamage.create()
set xed.dtype = dd
set xed.atype = a
set xed.wtype = w
set xed.damageEnemies = true
set xed.damageTrees = true
//
call TimerStart(NewTimerEx(this),PERIODIC,true,function thistype.periodicPlugin)
//
endmethod
//
endstruct
//
endlibrary