Name | Type | is_array | initial_value |
TempGroup | group | No | |
TempInt | integer | No | |
TempLoc | location | No | |
Y_Map_Point | location | Yes | |
Y_Map_Region | rect | Yes |
//TESH.scrollpos=21
//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 = 'h006'
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=151
//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=0
//TESH.alwaysfold=0
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//~~ Timer32 ~~ By Jesus4Lyf ~~ Version 1.06 ~~
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// What is Timer32?
// - Timer32 implements a fully optimised timer loop for a struct.
// - Instances can be added to the loop, which will call .periodic every
// PERIOD until .stopPeriodic() is called.
//
// =Pros=
// - Efficient.
// - Simple.
//
// =Cons=
// - Only allows one period.
// - The called method must be named ".periodic".
//
// Methods:
// - struct.startPeriodic()
// - struct.stopPeriodic()
//
// - private method periodic takes nothing returns nothing
//
// This must be defined in structs that implement Periodic Module.
// It will be executed by the module every PERIOD until .stopPeriodic() is called.
// Put "implement T32x" BELOW this method.
//
// Modules:
// - T32x
// Has no safety on .stopPeriodic or .startPeriodic (except debug messages
// to warn).
//
// - T32xs
// Has safety on .stopPeriodic and .startPeriodic so if they are called
// multiple times, or while otherwise are already stopped/started respectively,
// no error will occur, the call will be ignored.
//
// - T32
// The original, old version of the T32 module. This remains for backwards
// compatability, and is deprecated. The periodic method must return a boolean,
// false to continue running or true to stop.
//
// Details:
// - Uses one timer.
//
// - Do not, within a .periodic method, follow a .stopPeriodic call with a
// .startPeriodic call.
//
// How to import:
// - Create a trigger named T32.
// - Convert it to custom text and replace the whole trigger text with this.
//
// Thanks:
// - Infinitegde for finding a bug in the debug message that actually altered
// system operation (when in debug mode).
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
library T32 initializer OnInit
globals
public constant real PERIOD=0.03125
public constant integer FPS=R2I(1/PERIOD)
public integer Tick=0 // very useful.
//==============================================================================
private trigger Trig=CreateTrigger()
endglobals
//==============================================================================
// The standard T32 module, T32x.
//
module T32x
private thistype next
private thistype prev
private static method PeriodicLoop takes nothing returns boolean
local thistype this=thistype(0).next
loop
exitwhen this==0
call this.periodic()
set this=this.next
endloop
return false
endmethod
method startPeriodic takes nothing returns nothing
debug if this.prev!=0 or thistype(0).next==this then
debug call BJDebugMsg("T32 ERROR: Struct #"+I2S(this)+" had startPeriodic called while already running!")
debug endif
set thistype(0).next.prev=this
set this.next=thistype(0).next
set thistype(0).next=this
set this.prev=thistype(0)
endmethod
method stopPeriodic takes nothing returns nothing
debug if this.prev==0 and thistype(0).next!=this then
debug call BJDebugMsg("T32 ERROR: Struct #"+I2S(this)+" had stopPeriodic called while not running!")
debug endif
// This is some real magic.
set this.prev.next=this.next
set this.next.prev=this.prev
// This will even work for the starting element.
debug set this.prev=0
endmethod
private static method onInit takes nothing returns nothing
call TriggerAddCondition(Trig,Condition(function thistype.PeriodicLoop))
endmethod
endmodule
//==============================================================================
// The standard T32 module with added safety checks on .startPeriodic() and
// .stopPeriodic(), T32xs.
//
module T32xs
private thistype next
private thistype prev
private boolean runningPeriodic
private static method PeriodicLoop takes nothing returns boolean
local thistype this=thistype(0).next
loop
exitwhen this==0
call this.periodic()
set this=this.next
endloop
return false
endmethod
method startPeriodic takes nothing returns nothing
if not this.runningPeriodic then
set thistype(0).next.prev=this
set this.next=thistype(0).next
set thistype(0).next=this
set this.prev=thistype(0)
set this.runningPeriodic=true
endif
endmethod
method stopPeriodic takes nothing returns nothing
if this.runningPeriodic then
// This is some real magic.
set this.prev.next=this.next
set this.next.prev=this.prev
// This will even work for the starting element.
set this.runningPeriodic=false
endif
endmethod
private static method onInit takes nothing returns nothing
call TriggerAddCondition(Trig,Condition(function thistype.PeriodicLoop))
endmethod
endmodule
//==============================================================================
// The original T32 module, for backwards compatability only.
//
module T32 // deprecated.
private thistype next
private thistype prev
private static method PeriodicLoop takes nothing returns boolean
local thistype this=thistype(0).next
loop
exitwhen this==0
if this.periodic() then
// This is some real magic.
set this.prev.next=this.next
set this.next.prev=this.prev
// This will even work for the starting element.
debug set this.prev=0
endif
set this=this.next
endloop
return false
endmethod
method startPeriodic takes nothing returns nothing
debug if this.prev!=0 or thistype(0).next==this then
debug call BJDebugMsg("T32 ERROR: Struct #"+I2S(this)+" had startPeriodic called while already running!")
debug endif
set thistype(0).next.prev=this
set this.next=thistype(0).next
set thistype(0).next=this
set this.prev=thistype(0)
endmethod
private static method onInit takes nothing returns nothing
call TriggerAddCondition(Trig,Condition(function thistype.PeriodicLoop))
endmethod
endmodule
//==============================================================================
// System Core.
//
private function OnExpire takes nothing returns nothing
set Tick=Tick+1
call TriggerEvaluate(Trig)
endfunction
private function OnInit takes nothing returns nothing
call TimerStart(CreateTimer(),PERIOD,true,function OnExpire)
endfunction
endlibrary
//TESH.scrollpos=7
//TESH.alwaysfold=0
library SimError initializer init
//**************************************************************************************************
//*
//* SimError
//*
//* Mimic an interface error message
//* call SimError(ForPlayer, msg)
//* ForPlayer : The player to show the error
//* msg : The error
//*
//* To implement this function, copy this trigger and paste it in your map.
//* Unless of course you are actually reading the library from wc3c's scripts section, then just
//* paste the contents into some custom text trigger in your map.
//*
//**************************************************************************************************
//==================================================================================================
globals
private sound error
endglobals
//====================================================================================================
function SimError takes player ForPlayer, string msg returns nothing
set msg="\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n|cffffcc00"+msg+"|r"
if (GetLocalPlayer() == ForPlayer) then
call ClearTextMessages()
call DisplayTimedTextToPlayer( ForPlayer, 0.52, 0.96, 2.00, msg )
call StartSound( error )
endif
endfunction
private function init takes nothing returns nothing
set error=CreateSoundFromLabel("InterfaceError",false,false,false,10,10)
//call StartSound( error ) //apparently the bug in which you play a sound for the first time
//and it doesn't work is not there anymore in patch 1.22
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
scope Rupture // requires xebasic, xedamage, T32 optional SimError
//
//
// *Rupture*
// by baassee
//
// *Requirements*
// *xebasic by Vexorian
// *xedamage by Vexorian
// *T32 by Jesus4Lyf
// *JASSHelper version 0.A.2.B or later by Vexorian
// *Optional*
// if the NOSTACK constant is set to true, this will be needed, else not.
// *SimError by Vexorian
//
// http://www.wc3c.net/showthread.php?t=101150 - xe 0.8
// http://www.thehelper.net/forums/showthread.php/132538-Timer32 - T32
// http://www.wc3c.net/showthread.php?t=88142 - JASSHelper
// http://www.wc3c.net/showthread.php?t=101260 - SimError
//
// *WARNING AND NOTICE!*
// This map uses a renamed dummy model by Vexorian
// please download the proper one or just export this
// one, if you already have xe implented then you
// wont have to worry about this.
//
// Also the spell uses the dummy id of the xedummy so
// remember to correct it!
//
// *Changelog*
// v1.0 - Released
// v1.1 - -Fixed spelling errors Rupture instead of Rapture.
// -Fixed preloading.
// -Added a struct member lvl to the struct.
// -Fixed bug with adding unit to group when nostack == false.
// -Changed ApplyLife with RemoveUnit
// -.count is initially 0.
// -Removed creating effect and added it on damage with the xe.
// v1.2 - -Removed the showunit command
// -Now the trigger doesn't initialize 2 triggers but creates one more with NOSTACK
// -Added another check to check if the unit is dead according to TH
// -Added locals to the conditions according to TH
// v1.2b -Fixed nulling -.-
// -Removed a action.
// v1.2c -Fixed an unnecessary function call
// -Fixed Error message.
// -Now when error message shows up the caster can choose a new target.
// v1.2d -Removed a static if.
// v1.2e -Fixed another static if with the globals. nostack is now set at the init.
// v1.2f -Made the spell run with T32. Fixed a few lines.
//
//
// *Standard Information*
// This spell was totally inspired by the spell Rupture in DOTA.
// It probably dont function the same way as in DOTA but this is
// my own version of it. I made it as the tooltip says at
// http://www.playdota.com/heroes/bloodseeker/
//
// Please don't beg me to do other DOTA spells and don't complain
// about its not-originality. I didn't find any Rapture at THW when
// searching so I thought it might have been useful to someone.
//
// Implent it as the how to import tutorial says and enjoy the spell!
// Please give proper credit to Vexorian for the libraries and me for
// this spell.
//
// Yes I use GetWidgetLife() instead of the JASSHelper native UnitAlive
// because UnitAlive bugs the optimizer by the same author so to prevent
// that bug I use GetWidgetLife(), also it's faster.
//
//
// *How to import*
//
// 1. Copy the dummy unit or just copy the one from the xe with the model included!
//
// 2. Copy the abilities, Rupture and Rupture Buff.
//
// 3. Copy the buff Rupture. Remember to set the buff of the spell Rupture Buff to
// this one.
//
// 4. Make sure you have xebasic and damage in your map with proper dummy
// and dummy model. Also import T32 or just copy the T32 Trigger.
//
// 5. Copy over this trigger called Rupture.
//
// 6. Follow the setup part which will explain all values and correct the ability
// ids and the buff id.
//
// 7. Add the spell to a unit, credit proper authors and enjoy!
//
//
// *SETUP PART*
//
//
globals
//
// The ability ID of the main spell. Select it in the object editor, press
// ctrl + d and you can see the ID.
//
private constant integer ABILID = 'A000'
//
// The ability id of the buff adding spell. How to get the id, check above.
//
private constant integer DUMMYID = 'A001'
//
// The buff id. Do as with the main ABILID but in the buff section.
//
private constant integer BUFFID = 'B000'
//
// The blood effect when a unit takes damage.
//
private constant string BLOODFX = "Objects\\Spawnmodels\\Human\\HumanBlood\\HumanBloodPeasant.mdl"
//
// The attachment point where the blood fx will be attached too.
// I would recommend chest, hands or head. Leave it as it is, no
// need to change.
//
private constant string ATTACH = "chest"
//
// If you want to preload the blood fx and the ability to remove
// first cast lagg set this to true. You might suffer a longer
// loading time if you do but it will prevent first cast lagg as
// said. I would recommend it set to true.
//
private constant boolean PRELOAD = true
//
// The error message that shows up with NOSTACK (below) is true
//
private constant string MESSAGE = "Target Unit has already Rupture"
//
// The hotkey of the spell
//
private constant string HOTKEY = "R"
//
// If the damage should be 100% pure then set this to true
// else if you want to make your own damage stuff, set it to false.
//
private constant boolean PURE = true
//
// Do you want the ability to stack at the same unit?
// This can bug the spell if set to false. Use at your own risk
// The bug it can make is that the buff wont show but it will damage
// proper.
//
private constant boolean NOSTACK = true
endglobals
//
// *FUNCTIONS* (Part of the setup part)
//
//
// The duration function, takes the level of the spell.
// The buff will last as long as the duration. Also
// the movement damage.
//
private function dur takes integer lvl returns real
return 3. + 2. * lvl
endfunction
//
// The initial hit damage. Set as you want.
//
private function fdmg takes integer lvl returns real
return 50. + 100. * lvl
endfunction
//
// The movement damage in percent. As it is now it will
// damage 20% * the level of the ability * the movement
// during the period in damage.
//
private function mdmg takes integer lvl returns real
return 0.2 * lvl
endfunction
//
// The max damage function. Functions with the interval
// function below the max damage function.
//
private function maxdmg takes integer lvl returns real
return 200. + 0 * lvl
endfunction
//
// The interval for the maximum damage. It will only
// be possible to make above damage during the interval.
// If the unit moves so much that it will take more damage,
// this will prevent it. Refreshes every set amount.
//
private function maxdmginterval takes integer lvl returns real
return 0.25 + 0 * lvl
endfunction
//
// The damage options, they're explained inside the function.
// This function is useless if you have set the PURE constant
// to true. Use the function below this one for the PURE constant
// true stuff.
//
private function xeoptions takes xedamage d returns nothing
// the damage type of the spell
// this wont be affected if forcedamage
// is set to true.
set d.dtype = DAMAGE_TYPE_UNIVERSAL
// the attack type of the spell
// this wont be affected if forcedamage
// is set to true.
set d.atype = ATTACK_TYPE_CHAOS
//
// the weapon type the spell will use, affilcts the
// sound when damaged, in this case, no sound
// when damaged but I made it configurable anyway
set d.wtype = WEAPON_TYPE_WHOKNOWS
// if the damage should be ranged,
// works different when AI's hit,
// usally makes the unit move as
// it got attacked. Perfect for
// Rapture.
set d.ranged = true
// if the spell should damage enemies
// quite obvious
set d.damageEnemies = true
// if the spell should damage neutrals if
// they are targeted.
set d.damageNeutral = true
// this one below is quite special
// I use the feature of xedamage to
// try to make the pure damage as close
// as possible to the amount.
// if set to true, it will be pure
// damage, if false then the damage
// type and attack type will matter
// if this is set to false and
// damage type to universal and
// attack type to chaos, it will act
// as pure damage, if you havent mixed
// with the gameplay constants.
set d.forceDamage = true
call d.useSpecialEffect(BLOODFX, ATTACH)
endfunction
//
// The second xedamage options, just basic the same as above so
// wont explain them further but these are used when the PURE
// constant is set to true.
//
private function xepureoptions takes xedamage d returns nothing
set d.wtype = WEAPON_TYPE_WHOKNOWS
set d.ranged = true
set d.damageEnemies = true
set d.damageNeutral = true
call d.useSpecialEffect(BLOODFX, ATTACH)
endfunction
//
// *END OF SETUP*
//
globals
private xedamage xed // the xedamage instance
private group nostack //nostack group, only declared
endglobals
private struct Rupture // the struct
unit c //casting unit
unit u //target unit
integer lvl
real dmg//the movement percent
real maxdmg//the max damage per interval
real check//the interval check
real count = 0.//counter to count the interval
real X//current x of the target
real Y//current y of the target
real dur//duration of the spell
//
// creation method takes unit caster, unit target, real x of target, real y of target
//
static method create takes unit c, unit u, real x, real y returns Rupture
local Rupture this = Rupture.allocate()
set .c = c
set .u = u
set .lvl = GetUnitAbilityLevel(c, ABILID)
set .dmg = mdmg(.lvl)
set .maxdmg = maxdmg(.lvl)
set .check = maxdmginterval(.lvl)
set .X = x
set .Y = y
set .dur = dur(.lvl)
static if PURE then
call xed.damageTargetForceValue(c, u, fdmg(.lvl))
else
call xed.damageTarget(c, u, fdmg(.lvl))
endif
call UnitAddAbility(u, DUMMYID)//adds the buff spell
call UnitMakeAbilityPermanent(u, true, DUMMYID)
static if NOSTACK then
call GroupAddUnit(nostack, u)
endif
call .startPeriodic()
return this
endmethod
//
// the method loop
//
private method periodic takes nothing returns nothing
local real dx = GetUnitX(.u) - .X //these are distance stuff
local real dy = GetUnitY(.u) - .Y //to see if the unit has moved
local real sq = SquareRoot(dx * dx + dy * dy) //distance function
if .dur > 0. and GetWidgetLife(.u) > 0.405 and not IsUnitType(.u, UNIT_TYPE_DEAD)then //check if the spell has ended
//or the target is already dead
set .dur = .dur - T32_PERIOD //reduce the duration
if sq > 0. then //check if the unit has moved any distance
//next if is to check if the interval has been reached and
//the max damage hasn't been reached and also that the
//damage that is about to happen isn't greater than the maxdmg
if .count < .check and .maxdmg > 0. and (sq * .dmg) <= .maxdmg then
// we do the damage
static if PURE then
call xed.damageTargetForceValue(.c, .u, (sq * .dmg))
else
call xed.damageTarget(.c, .u, (sq * .dmg))
endif
set .maxdmg = .maxdmg - (sq * .dmg)
set .count = .count + T32_PERIOD
set .X = GetUnitX(.u)//set the new coordinates of the unit
set .Y = GetUnitY(.u)//same as above
if .count >= .check then
set .count = 0.
set .maxdmg = maxdmg(.lvl)
endif
endif
endif
else
//instance have ended then we clear leaks and removes the ability and also the buff
call .stopPeriodic()
call UnitMakeAbilityPermanent(.u, false, DUMMYID)
call UnitRemoveAbility(.u, DUMMYID)
call UnitRemoveAbility(.u, BUFFID)
static if NOSTACK then
call GroupRemoveUnit(nostack, .u)
endif
call .destroy()
endif
endmethod
static method Conditions takes nothing returns boolean
local real x
local real y
local unit c
local unit u
if GetSpellAbilityId() == ABILID then
set c = GetTriggerUnit()
set u = GetSpellTargetUnit()
set x = GetUnitX(u)
set y = GetUnitY(u)
call Rupture.create(c, u, x, y)
endif
set c = null
set u = null
return false
endmethod
static method Conditions2 takes nothing returns boolean
local unit c
local unit u
if GetSpellAbilityId() == ABILID then
set c = GetTriggerUnit()
set u = GetSpellTargetUnit()
if IsUnitInGroup(u, nostack) == true then
call IssueImmediateOrder(c, "stop")
call SimError(GetTriggerPlayer(), MESSAGE)
if GetLocalPlayer() == GetTriggerPlayer() then
call ForceUIKey(HOTKEY)
endif
endif
endif
set c = null
set u = null
return false
endmethod
static method onInit takes nothing returns nothing
//start of the trigger
local trigger t = CreateTrigger()
local trigger trg
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
// to prevent mana and cooldown loss if the constant NOSTACK is on
static if NOSTACK then
set trg = CreateTrigger()
set nostack = CreateGroup()
call TriggerRegisterAnyUnitEventBJ(trg, EVENT_PLAYER_UNIT_SPELL_CAST)
call TriggerAddCondition(trg, Condition(function Rupture.Conditions2))
endif
//conditions are faster than actions? so I've heard
call TriggerAddCondition(t, Condition(function Rupture.Conditions))
//xe damage stuff
set xed = xedamage.create()
static if PURE then //checks the pure constant
call xepureoptions(xed)
else
call xeoptions(xed)
endif
//preload
static if PRELOAD then
call Preload(BLOODFX)
set bj_lastCreatedUnit = CreateUnit(Player(15), XE_DUMMY_UNITID, 0., 0., 0.)
call UnitAddAbility(bj_lastCreatedUnit, DUMMYID)
call RemoveUnit(bj_lastCreatedUnit)
endif
endmethod
implement T32x
endstruct
endscope