Name | Type | is_array | initial_value |
//TESH.scrollpos=-1
//TESH.alwaysfold=0
library MeatPuppet initializer Init uses LastOrder, GroupUtils, DestructableLib, SpellEvent, Table,/*
*/UnitIndexingUtils, AbortSpell, Knockback, TimerUtils
private keyword Data // DO NOT CHANGE!
globals
private constant integer LEARN_AID = 'A006'
private constant integer CAST_AID = 'A000'
private constant integer BID = 'B000' // the buff placed by CAST_AID on the target unit.
private constant real TICK = 1./32
private constant integer RELEASE_AID = 'A005' // ability is based off of Berserk
private constant integer RELEASE_BID = 'B002' // placed on caster by RELEASE_AID
private constant boolean RELEASE_ALLOW = true // if true, RELEASE_AID gets added after casting the spell
private constant boolean RELEASE_REPLACE_CAST_AID = true // if true and MAX_INSTANCES_PER_CASTER equals 1, CAST_AID gets removed before placing RELEASE_AID
private constant string LIGHTNING_ID = "INIT" // Ordinary Lightning
private constant real LIGHTNING_RED = 1.0
private constant real LIGHTNING_GREEN = 0.2
private constant real LIGHTNING_BLUE = 0.7
private constant real LIGHTNING_ALPHA = 0.8 // put "0." in here if you dont want the lightning
private constant string COLLISION_FX = "" // empty, but someone might want to use it
private constant real COLLISION_AOE = 95.
private real array COLLISION_DAMAGE // When the puppet hits an enemy unit, how much damage does the unit hit take
private real array COLLISION_PUPPET_DAMAGE // When the puppet hits another unit, how much damage does the puppet take
private constant boolean COLLISION_DAMAGE_FRIENDLY = false // should a friendly puppet take damage from colliding?
private constant attacktype COLLISION_ATTACK_TYPE = ATTACK_TYPE_NORMAL
private constant damagetype COLLISION_DAMAGE_TYPE = DAMAGE_TYPE_NORMAL
private constant weapontype COLLISION_SOUND = WEAPON_TYPE_METAL_MEDIUM_BASH
private constant weapontype COLLISION_TREE_SOUND = WEAPON_TYPE_WOOD_MEDIUM_BASH
private real array COLLISION_KB_DISTANCE // how far is the unit hit pushed back
private real array COLLISION_KB_DURATION // for how long is the unit hit pushed back // values less than or equal to 0 disable knocking back
private constant boolean KB_UNITS_KILL_TREES = true
private constant boolean KB_UNITS_KNOCK_OTHERS = false
private constant boolean KB_UNITS_CHAIN = false // should units knocked back by units knocked back by this spell be able to knock other units back // refer to the manual of Knockback for information
private constant real MIN_RANGE = 200.
private constant real MIN_RANGE_TIME = 0.25 // 0. or less is instant
private constant real ANGLE_CHANGE_SPEED = 0.2 // from 0.0 to 1.0, please // closer to 0: longer time to sync with casters facing, closer to 1: less time to sync with casters facing (1 being instant syncing)
private constant real DISTANCE_CHANGE_SPEED = 0.1 // from 0.0 to 1.0, please
private constant integer MAX_INSTANCES_PER_CASTER = 1
private constant string ERROR_TARGET_ALREADY_IN_INSTANCE = "Target already targeted by another instance of this spell!"
private constant string ERROR_TOO_MANY_INSTANCES = "Too many instances of this spell for the caster!"
private constant string ABILITY_HOTKEY = "T" // hotkey triggering AID
endglobals
// Damage
private function SetUpCOLLISION_DAMAGE takes nothing returns nothing
set COLLISION_DAMAGE[1]=125.
set COLLISION_DAMAGE[2]=200.
set COLLISION_DAMAGE[3]=300.
endfunction
private function Collision_Damage takes integer level returns real
return COLLISION_DAMAGE[level]
endfunction
// Puppet Damage
private function SetUpCOLLISION_PUPPET_DAMAGE takes nothing returns nothing
set COLLISION_PUPPET_DAMAGE[1]=0.
set COLLISION_PUPPET_DAMAGE[2]=0.
set COLLISION_PUPPET_DAMAGE[3]=0.
endfunction
private function Collision_Puppet_Damage takes integer level returns real
return COLLISION_PUPPET_DAMAGE[level]
endfunction
// Distance
private function SetUpCOLLISION_KB_DISTANCE takes nothing returns nothing
set COLLISION_KB_DISTANCE[1]=125.
set COLLISION_KB_DISTANCE[2]=175.
set COLLISION_KB_DISTANCE[3]=225.
endfunction
private function Collision_Kb_Distance takes integer level returns real
return COLLISION_KB_DISTANCE[level]
endfunction
// Duration
private function SetUpCOLLISION_KB_DURATION takes nothing returns nothing
set COLLISION_KB_DURATION[1]=1.0
set COLLISION_KB_DURATION[2]=1.2
set COLLISION_KB_DURATION[3]=1.4
endfunction
private function Collision_Kb_Duration takes integer level returns real
return COLLISION_KB_DURATION[level]
endfunction
// Validate Target
private function ValidTarget takes unit u, Data s returns boolean
return IsUnitType(u, UNIT_TYPE_DEAD)==false/*
*/ and IsUnitType(u, UNIT_TYPE_STRUCTURE)==false/*
*/ and IsUnitType(u, UNIT_TYPE_FLYING)==IsUnitType(s.t, UNIT_TYPE_FLYING)/*
*/ and IsUnitEnemy(u, GetOwningPlayer(s.caster))/*
*/ and (not IsKnockedBack(u))/*
*/ and u!=s.t/*
*/ and GetUnitMoveSpeed(u)>0/*
*/
endfunction
//
private struct Data
unit caster
unit t // Target
lightning l // Link
integer level
integer instanceofcaster
real a // angleOffset
real d // distanceOffset
real dd // deltadistance // MIN_RANGE
real tf // targets facing, relative
real c // counter
static Table InstanceOfCaster
static integer array Instances
static thistype array InstanceOfUnit // Linking Back, so instances dont conflict with each other
static boolexpr Collision
static boolexpr TreeFilter
static thistype tmps
static rect R
static location LocZ=Location(0,0)
private integer i
private static thistype array Structs
private static timer T=CreateTimer()
private static integer Count=0
static method GetLocZ takes real x, real y returns real
call MoveLocation(.LocZ, x,y)
return GetLocationZ(.LocZ)
endmethod
method onDestroy takes nothing returns nothing
local integer id=GetUnitId(.caster)
// Recycle instance of this caster properly
set .Instances[id]=.Instances[id]-1
set .InstanceOfCaster[id*MAX_INSTANCES_PER_CASTER+.instanceofcaster]=.InstanceOfCaster[id*MAX_INSTANCES_PER_CASTER+.Instances[id]]
set thistype(.InstanceOfCaster[id*MAX_INSTANCES_PER_CASTER+.instanceofcaster]).instanceofcaster=.instanceofcaster
call .InstanceOfCaster.flush(id*MAX_INSTANCES_PER_CASTER+.Instances[id])
if RELEASE_ALLOW and .Instances[id]==0 then
call UnitRemoveAbility(.caster, RELEASE_AID)
if MAX_INSTANCES_PER_CASTER==1 and RELEASE_REPLACE_CAST_AID and GetUnitAbilityLevel(.caster, LEARN_AID)>0 then
call UnitAddAbility(.caster, CAST_AID)
call SetUnitAbilityLevel(.caster, CAST_AID, GetUnitAbilityLevel(.caster, LEARN_AID))
call UnitMakeAbilityPermanent(.caster, true, CAST_AID)
endif
endif
// Normal cleanup
set .InstanceOfUnit[GetUnitId(.t)]=0
call SetUnitPosition(.t, GetUnitX(.t), GetUnitY(.t)) // avoid getting stuck
call IssueLastOrder(.t) // avoid not carrying out the last order
set .t=null
set .caster=null
call DestroyLightning(.l)
set .l=null
// clean your struct here
set .Count=.Count-1
set .Structs[.i]=.Structs[.Count]
set .Structs[.i].i=.i
if .Count==0 then
call PauseTimer(.T)
endif
endmethod
private static method CollisionFunc takes nothing returns boolean
local unit u=GetFilterUnit()
local real a
local real v
if ValidTarget(u, .tmps) then
if UnitDamageTarget(.tmps.caster, u, Collision_Damage(.tmps.level), true, false, COLLISION_ATTACK_TYPE, COLLISION_DAMAGE_TYPE, COLLISION_SOUND) then // should the unit for some reason be immune to damage, dont continue
if COLLISION_DAMAGE_FRIENDLY or IsUnitEnemy(.tmps.t, GetOwningPlayer(.tmps.caster)) then
call UnitDamageTarget(.tmps.caster, .tmps.t, Collision_Puppet_Damage(.tmps.level), true, false, COLLISION_ATTACK_TYPE, COLLISION_DAMAGE_TYPE, null) // sound has already been played
endif
call DestroyEffect(AddSpecialEffect(COLLISION_FX, GetUnitX(.tmps.t), GetUnitY(.tmps.t)))
if Collision_Kb_Duration(.tmps.level)>0 then // if the user doesnt want the units hit to be knocked back, dont do it
set a=(2*Collision_Kb_Distance(.tmps.level))/(Collision_Kb_Duration(.tmps.level)*Collision_Kb_Duration(.tmps.level)) // s=a/2*t^2 --> a=2*s/(t^2)
set v=a*Collision_Kb_Duration(.tmps.level) // v=a*t
call KnockbackTarget(.tmps.caster, u, Atan2(GetUnitY(u)-GetUnitY(.tmps.t), GetUnitX(u)-GetUnitX(.tmps.t))*bj_RADTODEG, v, a, KB_UNITS_KILL_TREES, KB_UNITS_KNOCK_OTHERS, KB_UNITS_CHAIN)
endif
endif
endif
set u=null
return false
endmethod
private static method TreeFilterFunc takes nothing returns boolean
local destructable d=GetFilterDestructable()
local real dx=GetWidgetX(d)-GetUnitX(.tmps.t)
local real dy=GetWidgetY(d)-GetUnitY(.tmps.t)
if (not IsDestructableDead(d)) and IsDestructableTree(d) and (dx*dx+dy*dy<=COLLISION_AOE*COLLISION_AOE) then
if COLLISION_DAMAGE_FRIENDLY or IsUnitEnemy(.tmps.t, GetOwningPlayer(.tmps.caster)) then
call UnitDamageTarget(.tmps.caster, .tmps.t, Collision_Puppet_Damage(.tmps.level), true, false, COLLISION_ATTACK_TYPE, COLLISION_DAMAGE_TYPE, COLLISION_TREE_SOUND)
endif
call KillDestructable(d)
endif
set d=null
return false
endmethod
private static method Callback takes nothing returns nothing
local integer i=.Count-1
local thistype s
local real a
local real x
local real y
local real dx
local real dy
local real r
loop
exitwhen i<0
set s=.Structs[i]
if GetUnitAbilityLevel(s.t, BID)==0 or IsUnitType(s.caster, UNIT_TYPE_DEAD)==true then
call s.destroy()
endif
if s.d<MIN_RANGE then // move the unit to MIN_RANGE
set s.d=s.d+s.dd/MIN_RANGE_TIME*TICK
endif
set x=GetUnitX(s.caster)
set y=GetUnitY(s.caster)
set dx=GetUnitX(s.t)-x
set dy=GetUnitY(s.t)-y
set r=Atan2(dy, dx) // current angle from caster to target
if r<0 then // map the angle from -Pi..Pi to 0..2*Pi
set r=2*bj_PI+r
endif
set a=ModuloReal(((GetUnitFacing(s.caster)*bj_DEGTORAD+s.a)-r), 2*bj_PI) // calculate the difference between current angle between caster and target and the current facing of the caster
if a>bj_PI then // map the delta angle from 0..2*Pi to -Pi..Pi
set a=-2*bj_PI+a
endif
set a=r+a*ANGLE_CHANGE_SPEED // calculate the new angle in which the target if offset from the caster
set r=SquareRoot(dx*dx+dy*dy) // calculate the distance between caster and target
set r=r+(s.d-r)*DISTANCE_CHANGE_SPEED // calculate the new distance the target is offset from the caster
set x=Cos(a)*r+x
set y=Sin(a)*r+y
call SetUnitX(s.t, x)
call SetUnitY(s.t, y)
call SetUnitFacing(s.t, GetUnitFacing(s.caster)+s.tf)
call MoveLightningEx(s.l, true, GetUnitX(s.caster), GetUnitY(s.caster), .GetLocZ(GetUnitX(s.caster), GetUnitY(s.caster))+GetUnitFlyHeight(s.caster), x, y, .GetLocZ(x, y)+GetUnitFlyHeight(s.t))
set .tmps=s
call GroupEnumUnitsInRange(ENUM_GROUP, x,y, COLLISION_AOE, .Collision)
call MoveRectTo(.R, x,y)
call EnumDestructablesInRect(.R, .TreeFilter, null)
// do your things here, dont forget to call s.destroy() somewhen
//
set i=i-1
endloop
endmethod
private static method PlaceRelease takes nothing returns nothing
local thistype s=thistype(GetTimerData(GetExpiredTimer()))
if MAX_INSTANCES_PER_CASTER==1 and RELEASE_REPLACE_CAST_AID then
call UnitRemoveAbility(s.caster, CAST_AID)
endif
call UnitAddAbility(s.caster, RELEASE_AID)
call UnitMakeAbilityPermanent(s.caster, true, RELEASE_AID)
call ReleaseTimer(GetExpiredTimer())
endmethod
static method create takes nothing returns thistype
local thistype s=.allocate()
local real dx
local real dy
local real r
local integer id
local timer t
// Populate the struct
set s.caster=SpellEvent.CastingUnit
set s.t=SpellEvent.TargetUnit
set .InstanceOfUnit[GetUnitId(s.t)]=s
set s.level=GetUnitAbilityLevel(s.caster, CAST_AID)
// SetUp the relative position
set dx=GetUnitX(s.t)-GetUnitX(s.caster)
set dy=GetUnitY(s.t)-GetUnitY(s.caster)
set r=Atan2(dy, dx)
if r<0 then
set r=2*bj_PI+r
endif
set s.a=r-GetUnitFacing(s.caster)*bj_DEGTORAD
set s.d=SquareRoot(dx*dx+dy*dy)
if s.d<MIN_RANGE then
set s.dd=MIN_RANGE-s.d
if MIN_RANGE_TIME<=0 then
set s.d=MIN_RANGE
endif
endif
set s.tf=GetUnitFacing(s.t)-GetUnitFacing(s.caster)
// SetUp the lightning
set s.l=AddLightningEx(LIGHTNING_ID, true, GetUnitX(s.caster), GetUnitY(s.caster), .GetLocZ(GetUnitX(s.caster), GetUnitY(s.caster))+GetUnitFlyHeight(s.caster), GetUnitX(s.t), GetUnitY(s.t), .GetLocZ(GetUnitX(s.t), GetUnitY(s.t))+GetUnitFlyHeight(s.t))
call SetLightningColor(s.l, LIGHTNING_RED, LIGHTNING_GREEN, LIGHTNING_BLUE, LIGHTNING_ALPHA)
set id=GetUnitId(s.caster)
set .InstanceOfCaster[id*MAX_INSTANCES_PER_CASTER+.Instances[id]]=s
set s.instanceofcaster=.Instances[id]
if RELEASE_ALLOW and .Instances[id]==0 then
set t=NewTimer()
call SetTimerData(t, s)
call TimerStart(t, 0, false, function thistype.PlaceRelease)
endif
set .Instances[id]=.Instances[id]+1
// initialize the struct here
set .Structs[.Count]=s
set s.i=.Count
if .Count==0 then
call TimerStart(.T, TICK, true, function thistype.Callback)
endif
set .Count=.Count+1
return s
endmethod
private static method onInit takes nothing returns nothing
set .InstanceOfCaster=Table.create()
set .Collision=Condition(function thistype.CollisionFunc)
set .TreeFilter=Condition(function thistype.TreeFilterFunc)
set .R=Rect(-COLLISION_AOE, -COLLISION_AOE, COLLISION_AOE, COLLISION_AOE)
if MAX_INSTANCES_PER_CASTER<=0 then
call BJDebugMsg(SCOPE_PREFIX+": MAX_INSTANCES_PER_CASTER is too low (<=0)!")
endif
if ANGLE_CHANGE_SPEED<=0 then
call BJDebugMsg(SCOPE_PREFIX+": ANGLE_CHANGE_SPEED is too low (<=0)!")
elseif ANGLE_CHANGE_SPEED>1 then
call BJDebugMsg(SCOPE_PREFIX+": ANGLE_CHANGE_SPEED is too high (>1)!")
endif
if DISTANCE_CHANGE_SPEED<=0 then
call BJDebugMsg(SCOPE_PREFIX+": DISTANCE_CHANGE_SPEED is too low (<=0)!")
elseif DISTANCE_CHANGE_SPEED>1 then
call BJDebugMsg(SCOPE_PREFIX+": DISTANCE_CHANGE_SPEED is too high (>1)!")
endif
//
call SetUpCOLLISION_DAMAGE()
call SetUpCOLLISION_PUPPET_DAMAGE()
call SetUpCOLLISION_KB_DISTANCE()
call SetUpCOLLISION_KB_DURATION()
endmethod
endstruct
private function CreateProxy takes nothing returns nothing
call Data.create()
endfunction
private function CheckValidTarget takes nothing returns nothing
if Data.InstanceOfUnit[GetUnitId(SpellEvent.TargetUnit)]!=0 then
call AbortSpell(SpellEvent.CastingUnit, "\n"+ERROR_TARGET_ALREADY_IN_INSTANCE, ABILITY_HOTKEY)
endif
if Data.Instances[GetUnitId(SpellEvent.CastingUnit)]>=MAX_INSTANCES_PER_CASTER then
call AbortSpell(SpellEvent.CastingUnit, "\n"+ERROR_TOO_MANY_INSTANCES, ABILITY_HOTKEY)
endif
endfunction
private function Release takes nothing returns nothing
local integer id=GetUnitId(SpellEvent.CastingUnit)
call UnitRemoveAbility(SpellEvent.CastingUnit, RELEASE_BID)
call UnitRemoveAbility(Data(Data.InstanceOfCaster[id*MAX_INSTANCES_PER_CASTER+Data.Instances[id]-1]).t, BID)
endfunction
globals
private integer array LearnedAbilityLevel
endglobals
private function IsLearnAbility takes nothing returns boolean
return GetLearnedSkill()==LEARN_AID
endfunction
private function Learning takes nothing returns nothing
local unit u=GetLearningUnit()
if GetLearnedSkillLevel()==1 then
call UnitAddAbility(u, CAST_AID)
call UnitMakeAbilityPermanent(u, true, CAST_AID)
endif
if not(RELEASE_REPLACE_CAST_AID and MAX_INSTANCES_PER_CASTER==1 and Data.Instances[GetUnitId(u)]==1) then
call SetUnitAbilityLevel(u, CAST_AID, GetLearnedSkillLevel())
endif
set LearnedAbilityLevel[GetUnitId(u)]=GetLearnedSkillLevel()
set u=null
endfunction
private function UnlearnedAbility takes nothing returns boolean
return GetUnitAbilityLevel(GetTriggerUnit(), LEARN_AID)!=LearnedAbilityLevel[GetUnitId(GetTriggerUnit())]
endfunction
private function ResetAbility takes nothing returns nothing
call UnitRemoveAbility(GetTriggerUnit(), CAST_AID)
endfunction
private function Init takes nothing returns nothing
local trigger t=CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_HERO_SKILL)
call TriggerAddCondition(t, Condition(function IsLearnAbility))
call TriggerAddAction(t, function Learning)
set t=CreateTrigger() // just in case someone uses that damn tome of relearning // untested code
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_PICKUP_ITEM)
call TriggerAddCondition(t, Condition(function UnlearnedAbility))
call TriggerAddAction(t, function ResetAbility)
call RegisterSpellEffectResponse(CAST_AID, CreateProxy)
call RegisterSpellCastResponse(CAST_AID, CheckValidTarget)
call RegisterSpellEffectResponse(RELEASE_AID, Release)
endfunction
endlibrary
//TESH.scrollpos=-1
//TESH.alwaysfold=0
library NullBlast initializer Init uses LastOrder, SpellEvent, UnitIndexingUtils, UnitMaxState,/*
*/ TimerUtils, xecast, xepreload, TextTag
// This spell ignores the time the hero takes to reach the cast point.
// Achieving this without potentially breaking, requires some more sophisticated work,
// since players could potentially abort the casting of the ability after SPELL_CAST has been reached.
private keyword Data // DO NOT CHANGE!
globals
private constant real TICK = 1./32 // interval to check for the buff BLINDNESS_DUMMY_BID
private constant integer LEARN_AID = 'A001' // Dummy Ability for learning interface
private constant integer CAST_AID = 'A002' // Dummy Ability for casting only
private constant integer DUMMY_AID = 'A003' // the mana cost of CAST_AID and DUMMY_AID must match // dummy ability for mana cost and cooldown
private constant string DUMMY_ORDER = "berserk"
private constant integer DUMMY_BID = 'B002' // placed by DUMMY_AID on the caster
private constant integer BLINDNESS_DUMMY_AID = 'A004' // changes duration of spell and % to miss
private constant string BLINDNESS_DUMMY_ORDER = "curse"
private constant integer BLINDNESS_DUMMY_BID = 'B001' // placed by BLINDNESS_DUMMY_AID on the target unit
private constant string FX = "war3mapImported\\CurseBolt.mdx" // blast effect
private constant string FX_ATTPT = "head"
private real array COOLDOWN // must match the cooldown time given in DUMMY_AID
private real array DAMAGE // damage dealt by the primary blast
private real array SECONDARY_DAMAGE // damage dealt by the secondary blast
private constant real SECONDARY_DAMAGE_AOE = 192. // units in this area around the target get damaged when the spell ends
private constant boolean DAMAGE_FRIENDLY_UNITS = false
private constant boolean DEBUFF_FRIENDLY_UNITS = true
private constant attacktype ATTACK_TYPE = ATTACK_TYPE_MAGIC
private constant damagetype DAMAGE_TYPE = DAMAGE_TYPE_MAGIC
private constant weapontype WEAPON_TYPE = WEAPON_TYPE_WHOKNOWS // sound effect when damaging
private constant weapontype SECONDARY_WEAPON_TYPE = WEAPON_TYPE_WHOKNOWS // sound effect when damaging through second blast
private real array STAT_TAKEN // max hp or max mana taken away for a period of time by this ability // values less than 1 turn this into a relative loss instead of an absolute
private constant unitstate STAT_MODIFIED = UNIT_STATE_MAX_LIFE // either UNIT_STATE_MAX_LIFE or UNIT_STATE_MAX_MANA
private constant boolean SHOW_TT = true // should a texttag be shown
private constant string TT_PATTERN = "&d HP!" // place "&d" where the amount taken away should be (case insensitive)
private constant string TT_NEGATIVE_HP_BONUS_STRING = "-"
private constant string TT_POSITIVE_HP_BONUS_STRING = "+"
private constant string TT_COLOR = "|cffA00030" // color of the text in the texttag; standard WC3 formatting (|cAARRGGBB)
endglobals
// Cooldown
private function SetUpCOOLDOWN takes nothing returns nothing
set COOLDOWN[1]=10.
set COOLDOWN[2]=10.
set COOLDOWN[3]=10.
endfunction
private function Cooldown takes integer level returns real
return COOLDOWN[level]
endfunction
// Damage
private function SetUpDAMAGE takes nothing returns nothing
set DAMAGE[1]=150.
set DAMAGE[2]=200.
set DAMAGE[3]=250.
endfunction
private function Damage takes integer level returns real
return DAMAGE[level]
endfunction
// Secondary Damage
private function SetUpSECONDARY_DAMAGE takes nothing returns nothing
set SECONDARY_DAMAGE[1]=100.
set SECONDARY_DAMAGE[2]=125.
set SECONDARY_DAMAGE[3]=150.
endfunction
private function Secondary_Damage takes integer level returns real
return SECONDARY_DAMAGE[level]
endfunction
// HP Taken
private function SetUpSTAT_TAKEN takes nothing returns nothing
set STAT_TAKEN[1]=150.
set STAT_TAKEN[2]=200.
set STAT_TAKEN[3]=250.
endfunction
private function Stat_Taken takes integer level returns real
return STAT_TAKEN[level]
endfunction
// Validate Target
private function ValidTarget takes unit u, Data s returns boolean
return IsUnitType(u, UNIT_TYPE_DEAD)==false/*
*/ and IsUnitType(u, UNIT_TYPE_STRUCTURE)==false/*
*/ and IsUnitType(u, UNIT_TYPE_MAGIC_IMMUNE)==false/*
*/ and (DAMAGE_FRIENDLY_UNITS or IsUnitEnemy(u, GetOwningPlayer(s.caster)))/*
*/ and s.target!=u/*
*/
endfunction
//
globals
private integer PatternLength=StringLength(TT_PATTERN)
endglobals
private struct Data
unit caster
unit target
timer t
integer level
real hptaken
boolean cooldown=true
boolean inloop=true
static boolean array CooldownActive
static xecast BlindnessDummy
static boolexpr DamageFilter
static thistype tmps
private integer i
private static thistype array Structs
private static timer T=CreateTimer()
private static integer Count=0
private method onDestroy takes nothing returns nothing
set .target=null
set .caster=null
set .t=null
endmethod
private method RemoveFromLoop takes nothing returns nothing
if not .cooldown then
call .destroy()
endif
// clean your struct here
set .Count=.Count-1
set .Structs[.i]=.Structs[.Count]
set .Structs[.i].i=.i
if .Count==0 then
call PauseTimer(.T)
endif
set .inloop=false
endmethod
private static method EndCooldown takes nothing returns nothing
local thistype s=thistype(GetTimerData(GetExpiredTimer()))
// switch from dummy ability to casting ability
if GetUnitAbilityLevel(s.caster, LEARN_AID)>0 then
call UnitRemoveAbility(s.caster, DUMMY_AID)
call UnitAddAbility(s.caster, CAST_AID)
call UnitMakeAbilityPermanent(s.caster, true, CAST_AID) // to avoid removal through morphing abilities
call SetUnitAbilityLevel(s.caster, CAST_AID, GetUnitAbilityLevel(s.caster, LEARN_AID))
endif
call ReleaseTimer(s.t) // clean up
set .CooldownActive[GetUnitId(s.caster)]=false // mark the caster as "not in cooldown"
set s.cooldown=false
if GetUnitAbilityLevel(s.target, BLINDNESS_DUMMY_BID)==0 and (not s.inloop) then
call s.destroy() // if the cooldown is longer than the buff lasts, destroy this instance
endif
endmethod
private static method DamageFilterFunc takes nothing returns boolean
local unit u=GetFilterUnit()
if ValidTarget(u, .tmps) then
call UnitDamageTarget(.tmps.caster, u, Secondary_Damage(.tmps.level), true, false, ATTACK_TYPE, DAMAGE_TYPE, SECONDARY_WEAPON_TYPE)
endif
set u=null
return false
endmethod
private static method Callback takes nothing returns nothing
local integer i=.Count-1
local thistype s
loop
exitwhen i<0
set s=.Structs[i]
if GetUnitAbilityLevel(s.target, BLINDNESS_DUMMY_BID)==0 then
//buff got removed/expired
set .tmps=s
call GroupEnumUnitsInRange(ENUM_GROUP, GetUnitX(s.target), GetUnitY(s.target), SECONDARY_DAMAGE_AOE, .DamageFilter)
call AddUnitMaxState(s.target, STAT_MODIFIED, s.hptaken)
call s.RemoveFromLoop() // if the buff lasts longer than the cooldown, destroy the instance
endif
// do your things here, dont forget to call s.destroy() somewhen
//
set i=i-1
endloop
endmethod
static method create takes nothing returns thistype
local thistype s=.allocate()
local integer i=0
local string str=""
set s.t=NewTimer()
set s.caster=SpellEvent.CastingUnit
set s.target=SpellEvent.TargetUnit
set s.level=GetUnitAbilityLevel(s.caster, LEARN_AID)
call AbortOrder(s.caster) // cancel the spell before it goes into cooldown and costs mana
// apply the blindness
set .BlindnessDummy.level=s.level
//set .BlindnessDummy.owningplayer=GetOwningPlayer(s.caster) // comment out this line to allow targeting friendly units
call .BlindnessDummy.castOnTarget(s.target)
// some nice FX
call DestroyEffect(AddSpecialEffectTarget(FX, s.target, FX_ATTPT))
if DAMAGE_FRIENDLY_UNITS or IsUnitEnemy(s.target, GetOwningPlayer(s.caster)) then
call UnitDamageTarget(s.caster, s.target, Damage(s.level), true, false, ATTACK_TYPE, DAMAGE_TYPE, WEAPON_TYPE) // damage the target
endif
// switch from casting ability to dummy ability
call UnitRemoveAbility(s.caster, CAST_AID)
call UnitAddAbility(s.caster, DUMMY_AID)
call UnitMakeAbilityPermanent(s.caster, true, DUMMY_AID)
call SetUnitAbilityLevel(s.caster, DUMMY_AID, s.level)
call IssueImmediateOrder(s.caster, DUMMY_ORDER) // apply mana cost and cooldown
call UnitRemoveAbility(s.caster, DUMMY_BID) // remove the buff placed by DUMMY_AID
set .CooldownActive[GetUnitId(s.caster)]=true
call SetTimerData(s.t, s)
call TimerStart(s.t, Cooldown(s.level), false, function thistype.EndCooldown)
if DEBUFF_FRIENDLY_UNITS or IsUnitEnemy(s.target, GetOwningPlayer(s.caster)) then
if Stat_Taken(s.level)>=1. or Stat_Taken(s.level)<=-1. then // absolute max hp loss
if GetUnitState(s.target, STAT_MODIFIED)<=Stat_Taken(s.level) then
// if the current max hp of the target are smaller than the amount taken away
// Reduce the amount taken away so that it leaves the unit with exactly 1 max hp.
set s.hptaken=GetUnitState(s.target, STAT_MODIFIED)-1 // store the amount taken away
call SetUnitMaxState(s.target, STAT_MODIFIED, 1)
else
set s.hptaken=Stat_Taken(s.level) // store the amount taken away
call AddUnitMaxState(s.target, STAT_MODIFIED, -Stat_Taken(s.level))
endif
else // relative max hp loss
if GetUnitState(s.target, STAT_MODIFIED)*(1.-Stat_Taken(s.level))<1. then
set s.hptaken=GetUnitState(s.target, STAT_MODIFIED)-1 // store the amount taken away
call SetUnitMaxState(s.target, STAT_MODIFIED, 1)
else
set s.hptaken=Stat_Taken(s.level)*GetUnitState(s.target, STAT_MODIFIED)
call AddUnitMaxState(s.target, STAT_MODIFIED, -s.hptaken)
endif
endif
endif
if SHOW_TT and s.hptaken!=0. then
loop
exitwhen i>=PatternLength
if SubString(TT_PATTERN, i, i+1)=="&" and i+1<PatternLength then
if SubString(TT_PATTERN, i+1, i+2)=="d" or SubString(TT_PATTERN, i+1, i+2)=="D" then
if s.hptaken<0. then
set str=str+TT_POSITIVE_HP_BONUS_STRING
else
set str=str+TT_NEGATIVE_HP_BONUS_STRING
endif
set str=str+I2S(R2I(RAbsBJ(s.hptaken)))
set i=i+1
else
set str=str+"&"
endif
else
set str=str+SubString(TT_PATTERN, i, i+1)
endif
set i=i+1
endloop
call TextTag_Unit(s.target, str, TT_COLOR)
endif
// initialize the struct here
set .Structs[.Count]=s
set s.i=.Count
if .Count==0 then
call TimerStart(.T, TICK, true, function thistype.Callback)
endif
set .Count=.Count+1
return s
endmethod
private static method onInit takes nothing returns nothing
set .BlindnessDummy=xecast.create()
set .BlindnessDummy.abilityid=BLINDNESS_DUMMY_AID
set .BlindnessDummy.orderstring=BLINDNESS_DUMMY_ORDER
set .BlindnessDummy.recycledelay=1.0
set .DamageFilter=Condition(function thistype.DamageFilterFunc)
call Preload(FX)
call PreloadStart()
call XE_PreloadAbility(CAST_AID)
call XE_PreloadAbility(DUMMY_AID)
call XE_PreloadAbility(BLINDNESS_DUMMY_AID)
call SetUpCOOLDOWN()
call SetUpDAMAGE()
call SetUpSECONDARY_DAMAGE()
call SetUpSTAT_TAKEN()
endmethod
endstruct
private function Cast takes nothing returns nothing
call Data.create()
endfunction
globals
private integer array LearnedAbilityLevel
endglobals
private function IsLearnAbility takes nothing returns boolean
return GetLearnedSkill()==LEARN_AID
endfunction
private function Learning takes nothing returns nothing
local unit u=GetLearningUnit()
if GetLearnedSkillLevel()==1 then
call UnitAddAbility(u, CAST_AID)
call UnitMakeAbilityPermanent(u, true, CAST_AID)
endif
if Data.CooldownActive[GetUnitId(u)] then
call SetUnitAbilityLevel(u, DUMMY_AID, GetLearnedSkillLevel())
else
call SetUnitAbilityLevel(u, CAST_AID, GetLearnedSkillLevel())
endif
set LearnedAbilityLevel[GetUnitId(u)]=GetLearnedSkillLevel()
set u=null
endfunction
private function UnlearnedAbility takes nothing returns boolean
return GetUnitAbilityLevel(GetTriggerUnit(), LEARN_AID)!=LearnedAbilityLevel[GetUnitId(GetTriggerUnit())]
endfunction
private function ResetAbility takes nothing returns nothing
call UnitRemoveAbility(GetTriggerUnit(), CAST_AID)
call UnitRemoveAbility(GetTriggerUnit(), DUMMY_AID)
endfunction
private function Init takes nothing returns nothing
local trigger t=CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_HERO_SKILL)
call TriggerAddCondition(t, Condition(function IsLearnAbility))
call TriggerAddAction(t, function Learning)
set t=CreateTrigger() // just in case someone uses that damn tome of relearning // untested code
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_PICKUP_ITEM)
call TriggerAddCondition(t, Condition(function UnlearnedAbility))
call TriggerAddAction(t, function ResetAbility)
call RegisterSpellCastResponse(CAST_AID, Cast) // why trigger on cast? --
// well, when using SPELL_EFFECT the hero can be interrupted before actually
// reaching the Cast Point (where SPELL_EFFECT is triggered).
// This spell ignores the time the hero takes to reach the cast point.
endfunction
endlibrary
//TESH.scrollpos=-1
//TESH.alwaysfold=0
library UnitMaxState initializer Initialize
////////////////////////////////////////////////////////////////////////////////
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//@ UnitMaxState Readme
//@=============================================================================
//@ Credits:
//@-----------------------------------------------------------------------------
//@ Written By:
//@ Earth-Fury
//@
//@ Original System By:
//@ Blade.dk
//@
//@ Intermittent Version By:
//@ Deaod
//@
//@ With Thanks To:
//@ - weaaddar for BonusMod and thus inspiration
//@ - PitzerMike for the ObjectMerger
//@ - Vexorian for vJass and JASSHelper
//@ - PipeDream for Grimoire
//@ - SFilip for TESH
//@ - MindWorX for maintaining NewGen
//@-----------------------------------------------------------------------------
//@ If you use this system in your map, please at least give credit to Blade.dk.
//@ Without him, this library would not exist.
//@=============================================================================
//@ Requirements:
//@-----------------------------------------------------------------------------
//@ This library is written in vJass and thus requires JASSHelper in order to
//@ function correctly. The included ObjectMerger macros require both JASSHelper
//@ and PitzerMike's ObjectMerger. All of these things are included in the
//@ NewGen world editor.
//@=============================================================================
//@ Adding UnitMaxState to your map:
//@-----------------------------------------------------------------------------
//@ Copy this library in to a custom text trigger to add it to your map.
//@
//@ You must also create a large number of abilities in order for this library
//@ to work. The simplest way to do this is to use the ObjectMerger macros which
//@ are distributed alongside this system. See their documentation for
//@ instructions on how to use them to generate the needed abilities.
//@
//@ If your map contains abilities with rawcodes that begin with 'Zx', you have
//@ to change the rawcode prefixes of the abilities. This needs to be done both
//@ in the configuration section of this library, and in the included
//@ ObjectMerger macros. See below the Configuration section for changing the
//@ values in the library, and see the ObjectMerger macros' readme for changing
//@ the values for the abilities generated by the macros.
//@
//@ See the configuration section below for configuration options.
//@=============================================================================
//@ Using UnitMaxState:
//@-----------------------------------------------------------------------------
//@ nothing SetUnitMaxState(unit <target>, unitstate <state>, real <value>)
//@
//@ This function changes <target>'s unitstate <state> to be eqal to <value>.
//@-----------------------------------------------------------------------------
//@ nothing AddUnitMaxState(unit <target>, unitstate <state>, real <value>)
//@
//@ This function adds <value> to <target>'s unitstate <state>. Note that
//@ <value> can be less than 0.
//@-----------------------------------------------------------------------------
//@ Note that these functions accept unitstates other than UNIT_STATE_MAX_LIFE,
//@ and UNIT_STATE_MAX_MANA. There is, however, a minor performance penalty
//@ for unit states other than the named two, compared to SetUnitState().
//@
//@ Also note that attempting to set a unit's max life below 1, or a unit's max
//@ mana below 0 will do nothing.
//@=============================================================================
//@ Changing the precision and number of abilities of the library
//@-----------------------------------------------------------------------------
//@ You never _have_ to change anything for this library to function correctly.
//@ If you _want_ to change the number of abilities this library uses, or if
//@ you want to learn more about how this system works, read on.
//@
//@ Note that changing the number of abilities this library uses only has two
//@ possible uses: To speed it up for adding insanely high bonuses, or to
//@ slow it down for higher bonuses, while reducing the number of abilities
//@ that it uses.
//@
//@ To add to or remove from the number of abilities, you must create or remove
//@ the properly conforming abilities, and modify the constants below. You can
//@ use the ObjectMerger to generate the abilities for you by adding or removing
//@ lines from the ObjectMerger macros which came with this library. See the
//@ macros' documentation for more help with this.
//@-----------------------------------------------------------------------------
//@ Description of conforming abilities:
//@-----------------------------------------------------------------------------
//@ This library abuses a bug in Warcraft 3. That bug causes certain abilities
//@ to only ever add the bonus life/mana for the first level of the ability.
//@ However, when the ability is removed, the amount of life/mana that should
//@ have been added is taken away, no matter the level.
//@
//@ This library uses abilities of that vein which have 4 levels, with level 1
//@ always containing a bonus of 0. The reason this library does not simply use
//@ two abilities with more than 4 levels is that the Widgitizer tool can not
//@ convert abilities which have more than 4 levels.
//@
//@ The way a bonus is added or removed is via powers of 2. You can create any
//@ number, by simply adding together powers of 2. Eg:
//@ 25 = 2^4 + 2^3 + 2^0
//@ 25 = 16 + 8 + 1
//@
//@ The abilities this system uses are ordered. The lowest-ordered ability
//@ contains the lowest bonuses. The lowest level of each ability contains the
//@ lowest bonus for that ability, with the highest level containing the highest
//@ bonus. The value of the bonus each ability grants at each level are
//@ sequential powers of 2.
//@
//@ The rawcodes of the abilities this system uses matter. The first ability of
//@ a type (Life/Mana Addition/Subtraction) has a defined rawcode. The abilities
//@ that follow it have incrementing rawcodes starting from the first. Eg:
//@ Zxl0 has the bonuses: 0, 1, 2, 4
//@ Zxl1 has the bonuses: 0, 8, 16, 32
//@ Zxl2 has the bonuses: 0, 64, 128, 256
//@
//@ Note that abilities which add a bonus must have a negitive bonus declared
//@ in the ability data, while the inverse is true for abilities which subtract
//@ a bonus. This is due to the nature of the bug that is being abused.
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
////////////////////////////////////////////////////////////////////////////////
globals
//==============================================================================
// Configuration:
//==============================================================================
//------------------------------------------------------------------------------
// The following constants declare the rawcode prefix of each of the four
// needed ability types. If you change these constants, you must also change
// the rawcodes of the abilities this system uses. This can be done by
// changing the included ObjectMerger macros. See their documentation for more
// help with that.
//
// Note that the ObjectMerger lines take a 3-letter prefix. These constants
// must be set to '<prefix>0'.
//------------------------------------------------------------------------------
private constant integer RAWCODE_LIFE_ADD_START = 'ZxL0'
private constant integer RAWCODE_LIFE_SUB_START = 'Zxl0'
private constant integer RAWCODE_MANA_ADD_START = 'ZxM0'
private constant integer RAWCODE_MANA_SUB_START = 'Zxm0'
//------------------------------------------------------------------------------
// The following constant declares the number of abilities there are in each
// group of abilities. You should never have to modify this.
//
// See the "Changing the precision and number of abilities of the library"
// section of the readme for more information.
//------------------------------------------------------------------------------
public constant integer ABILITY_COUNT = 6
//------------------------------------------------------------------------------
// If set to false, the system's abilities will not be preloaded.
// Not preloading abilities reduces loading time, but causes lag during the game
// as abilities are loaded on the fly.
//------------------------------------------------------------------------------
private constant boolean PRELOAD_ABILITIES = true
//------------------------------------------------------------------------------
// The rawcode of the unit to use for preloading abilities.
// This can be any unit, as it should be removed before the loading screen is
// gone.
//------------------------------------------------------------------------------
private constant integer PRELOAD_UNIT_ID = 'hfoo'
//------------------------------------------------------------------------------
// The player that will own the unit used for preloading abilities.
//------------------------------------------------------------------------------
private constant integer PRELOAD_PLAYER_ID = 15
//------------------------------------------------------------------------------
// The location to place the unit used for preloading abilities. This can be
// any pathable location on the map.
//------------------------------------------------------------------------------
private constant real PRELOAD_UNIT_X = 0.0
private constant real PRELOAD_UNIT_Y = 0.0
//------------------------------------------------------------------------------
// End of configuration
//------------------------------------------------------------------------------
endglobals
globals
public integer array ABILITIES
private integer array POWERS_OF_2
endglobals
private function ErrorMsg takes string s returns nothing
debug call BJDebugMsg("SetUnitMaxState: " + s)
endfunction
function SetUnitMaxState takes unit target, unitstate state, real targetValue returns nothing
local integer difference
local integer offset
local integer abilityId
local integer abilityLevel
local integer currentAbility
if state == UNIT_STATE_MAX_LIFE then
set offset = 0
if targetValue < 1 then
call ErrorMsg("You can not set a unit's max life to below 1")
return
endif
elseif state == UNIT_STATE_MAX_MANA then
set offset = ABILITY_COUNT * 2
if targetValue < 0 then
call ErrorMsg("You can not set a unit's max mana to below 0")
return
endif
else
call SetUnitState(target, state, targetValue)
return
endif
set difference = R2I(targetValue) - R2I(GetUnitState(target, state))
if difference < 0 then
set offset = offset + ABILITY_COUNT
set difference = -difference
endif
set abilityId = ABILITY_COUNT - 1
set abilityLevel = 4
set currentAbility = ABILITIES[offset + abilityId]
loop
exitwhen difference == 0
if difference >= POWERS_OF_2[abilityId * 3 + (abilityLevel - 2)] then
call UnitAddAbility(target, currentAbility)
call SetUnitAbilityLevel(target, currentAbility, abilityLevel)
call UnitRemoveAbility(target, currentAbility)
set difference = difference - POWERS_OF_2[abilityId * 3 + (abilityLevel - 2)]
else
set abilityLevel = abilityLevel - 1
if abilityLevel <= 1 then
set abilityId = abilityId - 1
set abilityLevel = 4
set currentAbility = ABILITIES[offset + abilityId]
endif
endif
endloop
endfunction
function AddUnitMaxState takes unit target, unitstate state, real additionalValue returns nothing
call SetUnitMaxState(target, state, GetUnitState(target, state) + additionalValue)
endfunction
private function Initialize takes nothing returns nothing
local integer i
local integer k
local integer array prefixes
local unit preloadUnit
set i = 1
set POWERS_OF_2[0] = 1
loop
exitwhen i == ABILITY_COUNT * 2 * 2 * 3 + 1
set POWERS_OF_2[i] = POWERS_OF_2[i - 1] * 2
set i = i + 1
endloop
set prefixes[0] = RAWCODE_LIFE_ADD_START
set prefixes[1] = RAWCODE_LIFE_SUB_START
set prefixes[2] = RAWCODE_MANA_ADD_START
set prefixes[3] = RAWCODE_MANA_SUB_START
set i = 0
loop
exitwhen i == 4
set k = 0
if PRELOAD_ABILITIES then
set preloadUnit = CreateUnit(Player(PRELOAD_PLAYER_ID), PRELOAD_UNIT_ID, PRELOAD_UNIT_X, PRELOAD_UNIT_Y, 0)
if preloadUnit == null then
call ErrorMsg("Failed to create preload unit! PRELOAD_UNIT_ID is likely invalid!")
endif
loop
exitwhen k == ABILITY_COUNT
set ABILITIES[(ABILITY_COUNT * i) + k] = prefixes[i] + k
call UnitAddAbility(preloadUnit, prefixes[i] + k)
debug if GetUnitAbilityLevel(preloadUnit, prefixes[i] + k) == 0 then
debug call ErrorMsg("Failed to add ability " + I2S(prefixes[i] + k) + " to the preload unit!")
debug endif
set k = k + 1
endloop
call RemoveUnit(preloadUnit)
else
loop
exitwhen k == ABILITY_COUNT
set ABILITIES[(ABILITY_COUNT * i) + k] = prefixes[i] + k
set k = k + 1
endloop
endif
set i = i + 1
endloop
endfunction
endlibrary
//TESH.scrollpos=-1
//TESH.alwaysfold=0
////////////////////////////////////////////////////////////////////////////////
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//@ UnitMaxState - Objer Merger Macros
//@=============================================================================
//@ See the UnitMaxState library for credit list and further documentation
//@=============================================================================
//@ Using the ObjectMerger macros
//@-----------------------------------------------------------------------------
//@ This "library" is used to generate the abilities which are required by the
//@ UnitMaxState library.
//@
//@ To copy all the needed abilities in to your map:
//@ Copy this "library" in to a custom-text trigger in your map while using the
//@ NewGen world editor, save the map, close the map or the editor, and reopen
//@ the map. You should then disable the trigger this library is in, as the
//@ ObjectMerger macros only need to be run once.
//@
//@ Note that saving a map with this "library" in an enabled trigger will take
//@ some time. It is normal for the save process to take up to a few minutes.
//@
//@=============================================================================
//@ Changing the ObjectMerger macros
//@-----------------------------------------------------------------------------
//@ Most users will not need to change anything. It is recomended that you do
//@ not attempt changing anything below unless you understand the way the
//@ UnitMaxState library changes maximum life/mana.
//@
//@ The UnitMaxState_DefineAbility text macro is used to ease the process of
//@ adding or removing precision from the library.
//@
//@ To remove precision, and thus slow the addition/subtraction of large values,
//@ remove one of the //! external ObjectMerger lines from the textmacro.
//@
//@ To add precision, and thus speed up the addition/subtraction of large
//@ values, add an additional Object Merger line. Read the ObjectMerger's
//@ documentation for information on the syntax.
//@
//@ Note that any additional abilities must have 4 levels, with a bonus of 0 at
//@ level 1, and a bonus of 2^(n + level - 2), where n is the number of the
//@ ability, and level is the current level of the ability being created.
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
////////////////////////////////////////////////////////////////////////////////
//! textmacro UnitMaxState_DefineAbility takes RAWCODE_PREFIX, ABILITY_ID, VALUE_FIELD, NEG, NAME, ICON
//! external ObjectMerger w3a $ABILITY_ID$ $RAWCODE_PREFIX$0 alev 4 aite 0 $VALUE_FIELD$ 1 0 $VALUE_FIELD$ 2 $NEG$1 $VALUE_FIELD$ 3 $NEG$2 $VALUE_FIELD$ 4 $NEG$4 anam "UnitMaxState - $NAME$ (1)" ansf "" aart $ICON$
//! external ObjectMerger w3a $ABILITY_ID$ $RAWCODE_PREFIX$1 alev 4 aite 0 $VALUE_FIELD$ 1 0 $VALUE_FIELD$ 2 $NEG$8 $VALUE_FIELD$ 3 $NEG$16 $VALUE_FIELD$ 4 $NEG$32 anam "UnitMaxState - $NAME$ (2)" ansf "" aart $ICON$
//! external ObjectMerger w3a $ABILITY_ID$ $RAWCODE_PREFIX$2 alev 4 aite 0 $VALUE_FIELD$ 1 0 $VALUE_FIELD$ 2 $NEG$64 $VALUE_FIELD$ 3 $NEG$128 $VALUE_FIELD$ 4 $NEG$256 anam "UnitMaxState - $NAME$ (3)" ansf "" aart $ICON$
//! external ObjectMerger w3a $ABILITY_ID$ $RAWCODE_PREFIX$3 alev 4 aite 0 $VALUE_FIELD$ 1 0 $VALUE_FIELD$ 2 $NEG$512 $VALUE_FIELD$ 3 $NEG$1024 $VALUE_FIELD$ 4 $NEG$2048 anam "UnitMaxState - $NAME$ (4)" ansf "" aart $ICON$
//! external ObjectMerger w3a $ABILITY_ID$ $RAWCODE_PREFIX$4 alev 4 aite 0 $VALUE_FIELD$ 1 0 $VALUE_FIELD$ 2 $NEG$4096 $VALUE_FIELD$ 3 $NEG$8192 $VALUE_FIELD$ 4 $NEG$16384 anam "UnitMaxState - $NAME$ (5)" ansf "" aart $ICON$
//! external ObjectMerger w3a $ABILITY_ID$ $RAWCODE_PREFIX$5 alev 4 aite 0 $VALUE_FIELD$ 1 0 $VALUE_FIELD$ 2 $NEG$32768 $VALUE_FIELD$ 3 $NEG$65536 $VALUE_FIELD$ 4 $NEG$131072 anam "UnitMaxState - $NAME$ (6)" ansf "" aart $ICON$
//! endtextmacro
// Life abilities:
//! runtextmacro UnitMaxState_DefineAbility("ZxL", "AIlf", "Ilif", "-", "Life+", "ReplaceableTextures\CommandButtons\BTNHealthStone.blp")
//! runtextmacro UnitMaxState_DefineAbility("Zxl", "AIlf", "Ilif", "", "Life-", "ReplaceableTextures\CommandButtons\BTNHealthStone.blp")
// Mana abilities:
//! runtextmacro UnitMaxState_DefineAbility("ZxM", "AImz", "Iman", "-", "Mana+", "ReplaceableTextures\CommandButtons\BTNManaStone.blp")
//! runtextmacro UnitMaxState_DefineAbility("Zxm", "AImz", "Iman", "", "Mana-", "ReplaceableTextures\CommandButtons\BTNManaStone.blp")
// =============================================================================
// =!= DO NOT LET A //! external BE THE LAST LINE IN A TRIGGER =!=
// =============================================================================
//TESH.scrollpos=-1
//TESH.alwaysfold=0
//******************************************************************************
//* *
//* K N O C K B A C K *
//* Actual Code *
//* v1.07 *
//* *
//* By: Rising_Dusk *
//* *
//******************************************************************************
library Knockback initializer Init needs TerrainPathability, GroupUtils, UnitIndexingUtils, LastOrder
globals
//*********************************************************
//* These are the configuration constants for the system
//*
//* EFFECT_ATTACH_POINT: Where on the unit the effect attaches
//* EFFECT_PATH_WATER: What special effect to attach over water
//* EFFECT_PATH_GROUND: What special effect to attach over ground
//* DEST_RADIUS: Radius around which destructs die
//* DEST_RADIUS_SQUARED: Radius squared around which destructs die
//* ADJACENT_RADIUS: Radius for knocking back adjacent units
//* ADJACENT_FACTOR: Factor for collision speed transfers
//* TIMER_INTERVAL: The interval for the timer that gets run
//* ISSUE_LAST_ORDER: A boolean to issue last orders or not
//*
private constant string EFFECT_ATTACH_POINT = "origin"
private constant string EFFECT_PATH_WATER = "MDX\\KnockbackWater.mdx"
private constant string EFFECT_PATH_GROUND = "MDX\\KnockbackDust.mdx"
private constant real DEST_RADIUS = 180.
private constant real DEST_RADIUS_SQUARED = DEST_RADIUS*DEST_RADIUS
private constant real ADJACENT_RADIUS = 180.
private constant real ADJACENT_FACTOR = 0.75
private constant real TIMER_INTERVAL = 0.05
private constant boolean ISSUE_LAST_ORDER = true
//*********************************************************
//* These are static constants used by the system and shouldn't be changed
//*
//* Timer: The timer that runs all of the effects for the spell
//* Counter: The counter for how many KB instances exist
//* HitIndex: Indexes for a given unit's knockback
//* Knockers: The array of all struct instances that exist
//* Entries: Counters for specific unit instances in system
//* ToClear: How many instances to remove on next run
//* DesBoolexpr: The check used for finding destructables
//* AdjBoolexpr: The check for picking adjacent units to knockback
//* DestRect: The rect used to check for destructables
//*
private timer Timer = CreateTimer()
private integer Counter = 0
private integer array HitIndex
private integer array Knockers
private integer array Entries
private integer array ToClear
private boolexpr DesBoolexpr = null
private boolexpr AdjBoolexpr = null
private rect DestRect = Rect(0,0,1,1)
//* Temporary variables used by the system
private real TempX = 0.
private real TempY = 0.
private unit TempUnit1 = null
private unit TempUnit2 = null
endglobals
//* Boolean for whether or not to display effects on a unit
private function ShowEffects takes unit u returns boolean
return not IsUnitType(u, UNIT_TYPE_FLYING)
endfunction
//* Functions for the destructable destruction
private function KillDests_Check takes nothing returns boolean
local real x = GetDestructableX(GetFilterDestructable())
local real y = GetDestructableY(GetFilterDestructable())
return (TempX-x)*(TempX-x) + (TempY-y)*(TempY-y) <= DEST_RADIUS_SQUARED
endfunction
private function KillDests takes nothing returns nothing
call KillDestructable(GetEnumDestructable())
endfunction
//* Functions for knocking back adjacent units
private function KnockAdj_Check takes nothing returns boolean
return TempUnit2 != GetFilterUnit() and IsUnitEnemy(GetFilterUnit(), GetOwningPlayer(TempUnit1)) and IsUnitType(GetFilterUnit(), UNIT_TYPE_GROUND) and not IsUnitType(GetFilterUnit(), UNIT_TYPE_MECHANICAL) and GetWidgetLife(GetFilterUnit()) > 0.405 and GetUnitAbilityLevel(GetFilterUnit(), 'Avul') <= 0
endfunction
//******************************************************************************
//* Some additional functions that can be used
function KnockbackStop takes unit targ returns boolean
local integer id = GetUnitId(targ)
set ToClear[id] = Entries[id]
return ToClear[id] > 0
endfunction
function IsKnockedBack takes unit targ returns boolean
return Entries[GetUnitId(targ)] > 0
endfunction
//* Struct for the system, I recommend leaving it alone
private struct knocker
unit Source = null
unit Target = null
group HitGroup = null
effect KBEffect = null
integer FXMode = 0
boolean KillDest = false
boolean KnockAdj = false
boolean ChainAdj = false
boolean ShowEff = false
real Decrement = 0.
real Displace = 0.
real CosA = 0.
real SinA = 0.
public method checkterrain takes knocker n returns integer
local integer i = 0
local real x = GetUnitX(n.Target)
local real y = GetUnitY(n.Target)
if IsTerrainPathingType(x, y, TERRAIN_PATHING_LAND) then
set i = 1
elseif IsTerrainPathingType(x, y, TERRAIN_PATHING_SHALLOW) then
set i = 2
endif
return i
endmethod
static method create takes unit source, unit targ, real angle, real disp, real dec, boolean killDestructables, boolean knockAdjacent, boolean chainAdjacent returns knocker
local knocker n = knocker.allocate()
set n.Target = targ
set n.Source = source
set n.FXMode = n.checkterrain(n)
set n.HitGroup = NewGroup()
set n.KillDest = killDestructables
set n.KnockAdj = knockAdjacent
set n.ChainAdj = chainAdjacent
set n.ShowEff = ShowEffects(targ)
set n.Decrement = dec
set n.Displace = disp
set n.CosA = Cos(angle)
set n.SinA = Sin(angle)
if n.ShowEff then
if n.FXMode == 1 then
set n.KBEffect = AddSpecialEffectTarget(EFFECT_PATH_GROUND, n.Target, EFFECT_ATTACH_POINT)
elseif n.FXMode == 2 then
set n.KBEffect = AddSpecialEffectTarget(EFFECT_PATH_WATER, n.Target, EFFECT_ATTACH_POINT)
debug else
debug call BJDebugMsg(SCOPE_PREFIX+" Error (On Create): Unknown Terrain Type")
endif
endif
return n
endmethod
private method onDestroy takes nothing returns nothing
local integer id = GetUnitId(this.Target)
set Entries[id] = Entries[id] - 1
if GetWidgetLife(this.Target) > 0.405 and Entries[id] <= 0 and ISSUE_LAST_ORDER then
//* Issue last order if activated
call IssueLastOrder(this.Target)
endif
if this.ShowEff then
//* Destroy effect if it exists
call DestroyEffect(this.KBEffect)
endif
call ReleaseGroup(this.HitGroup)
endmethod
endstruct
private function Update takes nothing returns nothing
local unit u = null
local unit s = null
local rect r = null
local knocker n = 0
local knocker m = 0
local integer i = Counter - 1
local integer j = 0
local integer mode = 0
local integer id = 0
local real xi = 0.
local real yi = 0.
local real xf = 0.
local real yf = 0.
loop
exitwhen i < 0
set n = Knockers[i]
set u = n.Target
set mode = n.FXMode
set id = GetUnitId(u)
set xi = GetUnitX(u)
set yi = GetUnitY(u)
if n.Displace <= 0 or ToClear[id] > 0 then
//* Clean up the knockback when it is over
if ToClear[id] > 0 then
set ToClear[id] = ToClear[id] - 1
endif
call n.destroy()
set Counter = Counter - 1
if Counter < 0 then
call PauseTimer(Timer)
set Counter = 0
else
set Knockers[i] = Knockers[Counter]
endif
else
//* Propagate the knockback in space and time
set xf = xi + n.Displace*n.CosA
set yf = yi + n.Displace*n.SinA
call SetUnitPosition(u, xf, yf)
set n.FXMode = n.checkterrain(n)
//* Modify the special effect if necessary
if n.ShowEff then
if n.FXMode == 1 and mode == 2 then
call DestroyEffect(n.KBEffect)
set n.KBEffect = AddSpecialEffectTarget(EFFECT_PATH_GROUND, n.Target, EFFECT_ATTACH_POINT)
elseif n.FXMode == 2 and mode == 1 then
call DestroyEffect(n.KBEffect)
set n.KBEffect = AddSpecialEffectTarget(EFFECT_PATH_WATER, n.Target, EFFECT_ATTACH_POINT)
debug elseif n.FXMode == 0 then
debug call BJDebugMsg(SCOPE_PREFIX+" Error (In Update): Unknown Terrain Type")
endif
endif
//* Decrement displacement left to go
set n.Displace = n.Displace - n.Decrement
//* Destroy destructables if desired
if n.KillDest then
set TempX = GetUnitX(u)
set TempY = GetUnitY(u)
call MoveRectTo(DestRect, TempX, TempY)
call EnumDestructablesInRect(DestRect, DesBoolexpr, function KillDests)
endif
//* Knockback nearby units if desired
if n.KnockAdj then
set xi = GetUnitX(u)
set yi = GetUnitY(u)
set TempUnit1 = n.Source
set TempUnit2 = u
call GroupEnumUnitsInRange(ENUM_GROUP, xi, yi, ADJACENT_RADIUS, AdjBoolexpr)
loop
set s = FirstOfGroup(ENUM_GROUP)
exitwhen s == null
if not IsUnitInGroup(s, n.HitGroup) then
set xf = GetUnitX(s)
set yf = GetUnitY(s)
call GroupAddUnit(n.HitGroup, s)
set m = knocker.create(n.Source, s, Atan2(yf-yi, xf-xi), n.Displace*ADJACENT_FACTOR, n.Decrement, n.KillDest, n.ChainAdj, n.ChainAdj)
call GroupAddUnit(m.HitGroup, u)
set Knockers[Counter] = m
set Counter = Counter + 1
endif
call GroupRemoveUnit(ENUM_GROUP, s)
endloop
endif
endif
set i = i - 1
endloop
set u = null
set s = null
endfunction
//******************************************************************************
//* How to knockback a unit
function KnockbackTarget takes unit source, unit targ, real angle, real startspeed, real decrement, boolean killDestructables, boolean knockAdjacent, boolean chainAdjacent returns boolean
local knocker n = 0
local integer id = GetUnitId(targ)
local boolean b = true
//* Protect users from themselves
if decrement <= 0. or startspeed <= 0. or targ == null then
debug call BJDebugMsg(SCOPE_PREFIX+" Error (On Call): Invalid Starting Conditions")
set b = false
else
//* Can't chain if you don't knockback adjacent units
if not knockAdjacent and chainAdjacent then
set chainAdjacent = false
endif
set n = knocker.create(source, targ, angle*bj_DEGTORAD, startspeed*TIMER_INTERVAL, decrement*TIMER_INTERVAL*TIMER_INTERVAL, killDestructables, knockAdjacent, chainAdjacent)
if Counter == 0 then
call TimerStart(Timer, TIMER_INTERVAL, true, function Update)
endif
set Entries[id] = Entries[id] + 1
set HitIndex[id] = Counter + 1
set Knockers[Counter] = n
set Counter = Counter + 1
endif
return b
endfunction
private function Init takes nothing returns nothing
call SetRect(DestRect, -DEST_RADIUS, -DEST_RADIUS, DEST_RADIUS, DEST_RADIUS)
set DesBoolexpr = Condition(function KillDests_Check)
set AdjBoolexpr = Condition(function KnockAdj_Check)
endfunction
endlibrary
//TESH.scrollpos=-1
//TESH.alwaysfold=0
library UnitIndexingUtils initializer Init
//******************************************************************************
//* BY: Rising_Dusk
//*
//* -: RED FLAVOR :-
//*
//* This can be used to index units with a unique integer for use with arrays
//* and things like that. This has a limit of 8191 indexes allocated at once in
//* terms of actually being usable in arrays. It won't give you an error if you
//* exceed 8191, but that is an unrealistic limit anyways.
//*
//* The red flavor uses a periodic timer to automatically recycle a unit's index
//* when UnitUserData goes to 0 for that unit (when it is removed from the game).
//* This can be slower than the blue flavor if you have many units on the map at
//* a time because it uses an O(n) search, but it automatically recycles indexes
//* for units that get removed from the game by decaying or RemoveUnit. It will
//* run the timer for COUNT_PER_ITERATION units before ending. The timer will
//* pick up where it left off on the next run-through.
//*
//* To use, call GetUnitId on a unit to retrieve its unique integer id. This
//* library allocates a unique index to a unit the instant it is created, which
//* means you can call GetUnitId immediately after creating the unit with no
//* worry.
//*
//* Function Listing --
//* function GetUnitId takes unit u returns integer
//*
globals
private constant real TIMER_PERIODICITY = 5.
private constant integer COUNT_PER_ITERATION = 64
private integer POSITION = 0
private integer array STACK
private unit array UNIT_STACK
private integer STACK_SIZE = 0
private integer ASSIGNED = 1
private integer MAX_INDEX = 0
endglobals
//Function to get the unit's unique integer id, inlines to getting its userdata
function GetUnitId takes unit u returns integer
return GetUnitUserData(u)
endfunction
//Filter for units to index
private function UnitFilter takes nothing returns boolean
return true
endfunction
private function Clear takes nothing returns nothing
local integer i = POSITION
loop
exitwhen (POSITION > MAX_INDEX or POSITION > i+COUNT_PER_ITERATION)
if UNIT_STACK[POSITION] != null and GetUnitUserData(UNIT_STACK[POSITION]) == 0 then
set STACK[STACK_SIZE] = POSITION
set STACK_SIZE = STACK_SIZE + 1
set UNIT_STACK[POSITION] = null
endif
set POSITION = POSITION + 1
endloop
if POSITION > MAX_INDEX then
set POSITION = 0
endif
endfunction
private function Add takes nothing returns boolean
local integer id = 0
if STACK_SIZE > 0 then
set STACK_SIZE = STACK_SIZE - 1
set id = STACK[STACK_SIZE]
else
set id = ASSIGNED
set ASSIGNED = ASSIGNED + 1
if ASSIGNED > MAX_INDEX then
set MAX_INDEX = ASSIGNED
endif
endif
call SetUnitUserData(GetFilterUnit(), id)
set UNIT_STACK[id] = GetFilterUnit()
return true
endfunction
private function GroupAdd takes nothing returns nothing
local integer id = 0
if STACK_SIZE > 0 then
set STACK_SIZE = STACK_SIZE - 1
set id = STACK[STACK_SIZE]
else
set id = ASSIGNED
set ASSIGNED = ASSIGNED + 1
if ASSIGNED > MAX_INDEX then
set MAX_INDEX = ASSIGNED
endif
endif
call SetUnitUserData(GetEnumUnit(), id)
set UNIT_STACK[id] = GetEnumUnit()
endfunction
private function Init takes nothing returns nothing
local trigger t = CreateTrigger()
local region r = CreateRegion()
local group g = CreateGroup()
local integer i = 0
//Use a filterfunc so units are indexed immediately
call RegionAddRect(r, bj_mapInitialPlayableArea)
call TriggerRegisterEnterRegion(t, r, And(Condition(function UnitFilter), Condition(function Add)))
//Start the timer to recycle indexes
call TimerStart(CreateTimer(), TIMER_PERIODICITY, true, function Clear)
//Loop and group per player to grab all units, including those with locust
loop
exitwhen i > 15
call GroupEnumUnitsOfPlayer(g, Player(i), Condition(function UnitFilter))
call ForGroup(g, function GroupAdd)
set i = i + 1
endloop
call DestroyGroup(g)
set r = null
set t = null
set g = null
endfunction
endlibrary
//TESH.scrollpos=-1
//TESH.alwaysfold=0
library LastOrder initializer Init needs UnitIndexingUtils
//******************************************************************************
//* BY: Rising_Dusk
//*
//* This library has a lot of usefulness for when you want to interface with the
//* last order a unit was given. This can be useful for simulating spell errors
//* and where you'd want to give them back the order they had prior to the spell
//* cast (whereas without this library, they'd just forget their orders).
//*
//* There are some handy interfacing options for your use here --
//* function GetLastOrderId takes unit u returns integer
//* function GetLastOrderString takes unit u returns string
//* function GetLastOrderType takes unit u returns integer
//* function GetLastOrderX takes unit u returns real
//* function GetLastOrderY takes unit u returns real
//* function GetLastOrderTarget takes unit u returns widget
//* function AbortOrder takes unit u returns boolean
//*
//* There are also some order commands that can be useful --
//* function IssueLastOrder takes unit u returns boolean
//* function IssueSecondLastOrder takes unit u returns boolean
//* function IsLastOrderFinished takes unit u returns boolean
//*
//* You can access any information you'd like about the orders for your own
//* order handling needs.
//*
globals
//* Storage for last order
private integer array Order
private integer array Type
private widget array Targ
private boolean array Flag
private real array X
private real array Y
//* Storage for second last order
private integer array P_Order
private integer array P_Type
private widget array P_Targ
private boolean array P_Flag
private real array P_X
private real array P_Y
//* Order type variables
constant integer ORDER_TYPE_TARGET = 1
constant integer ORDER_TYPE_POINT = 2
constant integer ORDER_TYPE_IMMEDIATE = 3
//* Trigger for the order catching
private trigger OrderTrg = CreateTrigger()
endglobals
//**********************************************************
function GetLastOrderId takes unit u returns integer
return Order[GetUnitId(u)]
endfunction
function GetLastOrderString takes unit u returns string
return OrderId2String(Order[GetUnitId(u)])
endfunction
function GetLastOrderType takes unit u returns integer
return Type[GetUnitId(u)]
endfunction
function GetLastOrderX takes unit u returns real
return X[GetUnitId(u)]
endfunction
function GetLastOrderY takes unit u returns real
return Y[GetUnitId(u)]
endfunction
function GetLastOrderTarget takes unit u returns widget
return Targ[GetUnitId(u)]
endfunction
//**********************************************************
private function OrderExclusions takes unit u, integer id returns boolean
//* Excludes specific orders or unit types from registering with the system
//*
//* 851972: stop
//* Stop is excluded from the system, but you can change it by
//* adding a check for it below. id == 851972
//*
//* 851971: smart
//* 851986: move
//* 851983: attack
//* 851984: attackground
//* 851990: patrol
//* 851993: holdposition
//* These are the UI orders that are passed to the system.
//*
//* >= 852055, <= 852762
//* These are all spell IDs from defend to incineratearrowoff with
//* a bit of leeway at the ends for orders with no strings.
//*
return id == 851971 or id == 851986 or id == 851983 or id == 851984 or id == 851990 or id == 851993 or (id >= 852055 and id <= 852762)
endfunction
private function LastOrderFilter takes unit u returns boolean
//* Some criteria for whether or not a unit's last order should be given
//*
//* INSTANT type orders are excluded because generally, reissuing an instant
//* order doesn't make sense. You can remove that check below if you'd like,
//* though.
//*
//* The Type check is really just to ensure that no spell recursion can
//* occur with IssueLastOrder. The problem with intercepting the spell cast
//* event is that it happens after the order is 'caught' and registered to
//* this system. Therefore, to just IssueLastOrder tells it to recast the
//* spell! That's a problem, so we need a method to eliminate it.
//*
local integer id = GetUnitId(u)
return u != null and GetWidgetLife(u) > 0.405 and Type[id] != ORDER_TYPE_IMMEDIATE
endfunction
private function SecondLastOrderFilter takes unit u returns boolean
//* Same as above but with regard to the second last order issued
local integer id = GetUnitId(u)
return u != null and GetWidgetLife(u) > 0.405 and P_Type[id] != ORDER_TYPE_IMMEDIATE and P_Order[id] != Order[id]
endfunction
//**********************************************************
function IsLastOrderFinished takes unit u returns boolean
return (GetUnitCurrentOrder(u) == 0 and Order[GetUnitId(u)] != 851972) or Flag[GetUnitId(u)]
endfunction
function IssueLastOrder takes unit u returns boolean
local integer id = GetUnitId(u)
local boolean b = false
if LastOrderFilter(u) and Order[id] != 0 and not Flag[id] then
if Type[id] == ORDER_TYPE_TARGET then
set b = IssueTargetOrderById(u, Order[id], Targ[id])
elseif Type[id] == ORDER_TYPE_POINT then
set b = IssuePointOrderById(u, Order[id], X[id], Y[id])
elseif Type[id] == ORDER_TYPE_IMMEDIATE then
set b = IssueImmediateOrderById(u, Order[id])
endif
endif
return b
endfunction
function IssueSecondLastOrder takes unit u returns boolean
//* This function has to exist because of spell recursion
local integer id = GetUnitId(u)
local boolean b = false
if SecondLastOrderFilter(u) and P_Order[id] != 0 and not P_Flag[id] then
if P_Type[id] == ORDER_TYPE_TARGET then
set b = IssueTargetOrderById(u, P_Order[id], P_Targ[id])
elseif P_Type[id] == ORDER_TYPE_POINT then
set b = IssuePointOrderById(u, P_Order[id], P_X[id], P_Y[id])
elseif P_Type[id] == ORDER_TYPE_IMMEDIATE then
set b = IssueImmediateOrderById(u, P_Order[id])
endif
endif
return b
endfunction
function AbortOrder takes unit u returns boolean
local boolean b = true
if IsUnitPaused(u) then
set b = false
else
call PauseUnit(u, true)
call IssueImmediateOrder(u, "stop")
call PauseUnit(u, false)
endif
return b
endfunction
//**********************************************************
private function Conditions takes nothing returns boolean
return OrderExclusions(GetTriggerUnit(), GetIssuedOrderId())
endfunction
private function Actions takes nothing returns nothing
local unit u = GetTriggerUnit()
local integer id = GetUnitId(u)
//* Store second to last order to eliminate spell recursion
set P_Order[id] = Order[id]
set P_Targ[id] = Targ[id]
set P_Type[id] = Type[id]
set P_Flag[id] = Flag[id]
set P_X[id] = X[id]
set P_Y[id] = Y[id]
set Flag[id] = false
set Order[id] = GetIssuedOrderId()
if GetTriggerEventId() == EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER then
set Targ[id] = GetOrderTarget()
set Type[id] = ORDER_TYPE_TARGET
set X[id] = GetWidgetX(GetOrderTarget())
set Y[id] = GetWidgetY(GetOrderTarget())
elseif GetTriggerEventId() == EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER then
set Targ[id] = null
set Type[id] = ORDER_TYPE_POINT
set X[id] = GetOrderPointX()
set Y[id] = GetOrderPointY()
elseif GetTriggerEventId() == EVENT_PLAYER_UNIT_ISSUED_ORDER then
set Targ[id] = null
set Type[id] = ORDER_TYPE_IMMEDIATE
set X[id] = GetUnitX(u)
set Y[id] = GetUnitY(u)
debug else
debug call BJDebugMsg(SCOPE_PREFIX+" Error: Order Doesn't Exist")
endif
set u = null
endfunction
//**********************************************************
private function SpellActions takes nothing returns nothing
set Flag[GetUnitId(GetTriggerUnit())] = true
endfunction
//**********************************************************
private function Init takes nothing returns nothing
local trigger trg = CreateTrigger()
call TriggerAddAction(OrderTrg, function Actions)
call TriggerAddCondition(OrderTrg, Condition(function Conditions))
call TriggerRegisterAnyUnitEventBJ(OrderTrg, EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER)
call TriggerRegisterAnyUnitEventBJ(OrderTrg, EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER)
call TriggerRegisterAnyUnitEventBJ(OrderTrg, EVENT_PLAYER_UNIT_ISSUED_ORDER)
call TriggerAddAction(trg, function SpellActions)
call TriggerRegisterAnyUnitEventBJ(trg, EVENT_PLAYER_UNIT_SPELL_EFFECT)
set trg = null
endfunction
endlibrary
//TESH.scrollpos=-1
//TESH.alwaysfold=0
library AbortSpell needs LastOrder, SimError
//******************************************************************************
//* BY: Rising_Dusk
//*
//* The AbortSpell function in this library works just like the normal
//* SimError, except that it gives options for reissuing the unit's last order
//* and forcing a UI key for its owner to fully simulate a WC3 error message as
//* close as humanly possible.
//*
//* AbortSpell is valuable when used in the ISSUED_ORDER trigger event
//* callbacks. It is specifically designed to be used as a means for preventing
//* a unit from casting a spell and still continuing with whatever their last
//* order was. It also works on the SPELL_CAST event callback, but using it on
//* the order callbacks prevents the caster from turning/walking towards the
//* target point/unit/etc.
//*
//* Sample Function Usage:
//* call AbortSpell(MyUnit, "Error Message", "G")
//*
function AbortSpell takes unit u, string msg, string key returns boolean
if AbortOrder(u) then
call SimError(GetOwningPlayer(u), msg)
if GetLocalPlayer() == GetOwningPlayer(u) and StringLength(key) == 1 and key != " " then
call ForceUIKey(key)
endif
return IssueSecondLastOrder(u)
endif
return false
endfunction
endlibrary
//TESH.scrollpos=-1
//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|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=-1
//TESH.alwaysfold=0
library TerrainPathability initializer Initialization
//******************************************************************************
//* BY: Rising_Dusk
//*
//* This can be used to detect the type of pathing a specific point of terrain
//* is, whether land, shallow water, or deep water. This type of detection
//* should have been easy to do using natives, but the IsTerrainPathable(...)
//* native is very counterintuitive and does not permit easy detection in one
//* call. For that reason, this library was developed.
//*
//* The system requires a dummy unit of some sort. There are no real
//* requirements upon the dummy unit, but it needs a non-zero movement speed.
//* More importantly than the dummy unit, though, it needs a custom windwalk
//* based unit ability with a 0.00 duration and no fade time. Both of those
//* raw id's (for the unit and windwalk dummy) need to be configured below.
//*
//* There is an objectmerger call available for those of you too lazy to build
//* your own windwalk based ability. Simply uncomment it below and save once,
//* then close and reopen your map and recomment the line.
//*
globals
constant integer TERRAIN_PATHING_DEEP = 1
constant integer TERRAIN_PATHING_SHALLOW = 2
constant integer TERRAIN_PATHING_LAND = 3
constant integer TERRAIN_PATHING_WALKABLE = 4
private unit Dummy = null
private constant integer DUMMY_UNIT_ID = 'hfoo'
private constant integer DUMMY_WINDWALK_ID = 'win&'
private constant player OWNING_PLAYER = Player(15)
//* These variables shouldn't be adjusted
private real WorldMinX = 0.
private real WorldMinY = 0.
endglobals
// external ObjectMerger w3a ANwk win& anam "Collision Ability" ansf "" Owk3 1 0.0 Owk4 1 0 Owk2 1 0.0 Owk1 1 0.0 acdn 1 0.0 ahdu 1 0.0 adur 1 0. aher 0 amcs 1 0
function IsTerrainPathingType takes real x, real y, integer terrainPathingType returns boolean
local boolean b = false
if terrainPathingType == TERRAIN_PATHING_DEEP then
set b = not IsTerrainPathable(x, y, PATHING_TYPE_FLOATABILITY) and IsTerrainPathable(x, y, PATHING_TYPE_WALKABILITY)
elseif terrainPathingType == TERRAIN_PATHING_SHALLOW then
set b = not IsTerrainPathable(x, y, PATHING_TYPE_FLOATABILITY) and not IsTerrainPathable(x, y, PATHING_TYPE_WALKABILITY)
elseif terrainPathingType == TERRAIN_PATHING_LAND then
set b = IsTerrainPathable(x, y, PATHING_TYPE_FLOATABILITY)
elseif terrainPathingType == TERRAIN_PATHING_WALKABLE then
call SetUnitPosition(Dummy, x, y)
set b = GetUnitX(Dummy) == x and GetUnitY(Dummy) == y and not (not IsTerrainPathable(x, y, PATHING_TYPE_FLOATABILITY) and IsTerrainPathable(x, y, PATHING_TYPE_WALKABILITY))
call SetUnitX(Dummy, WorldMinX)
call SetUnitY(Dummy, WorldMinY)
endif
return b
endfunction
private function Initialization takes nothing returns nothing
set WorldMinX = GetRectMinX(bj_mapInitialPlayableArea)
set WorldMinY = GetRectMinY(bj_mapInitialPlayableArea)
set Dummy = CreateUnit(OWNING_PLAYER, DUMMY_UNIT_ID, WorldMinX, WorldMinY, 0.)
call UnitAddAbility(Dummy, DUMMY_WINDWALK_ID)
call UnitAddAbility(Dummy, 'Avul')
call IssueImmediateOrderById(Dummy, 852129)
call SetUnitX(Dummy, WorldMinX)
call SetUnitY(Dummy, WorldMinY)
endfunction
endlibrary
//TESH.scrollpos=-1
//TESH.alwaysfold=0
library DestructableLib initializer Initialization
//* ============================================================================ *
//* Made by PitzerMike *
//* *
//* I made this to detect if a destructable is a tree or not. It works not only *
//* for the standard trees but also for custom destructables created with the *
//* object editor. It uses a footie as a dummy with the goul's harvest ability. *
//* The dummy ids can be changed though. I also added the IsDestructableDead *
//* function for completeness. *
//* ============================================================================ *
globals
private constant integer DUMMY_UNIT_ID = 'hfoo' // footman
private constant integer HARVEST_ID = 'Ahrl' // ghouls harvest
private constant player OWNING_PLAYER = Player(15)
private unit dummy = null
endglobals
function IsDestructableDead takes destructable dest returns boolean
return GetWidgetLife(dest) <= 0.405
endfunction
function IsDestructableTree takes destructable dest returns boolean
local boolean result = false
if (dest != null) then
call PauseUnit(dummy, false)
set result = IssueTargetOrder(dummy, "harvest", dest)
call PauseUnit(dummy, true) // stops order
endif
return result
endfunction
private function Initialization takes nothing returns nothing
set dummy = CreateUnit(OWNING_PLAYER, DUMMY_UNIT_ID, 0.0, 0.0, 0.0)
call ShowUnit(dummy, false) // cannot enumerate
call UnitAddAbility(dummy, HARVEST_ID)
call UnitAddAbility(dummy, 'Aloc') // unselectable, invulnerable
call PauseUnit(dummy, true)
endfunction
endlibrary
//TESH.scrollpos=-1
//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 = 'h000'
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
library xecast initializer init requires xebasic
//************************************************************************
// xecast 0.5
// ------
// Because dummy casters REALLY ARE this complicated!
//
//************************************************************************
//===========================================================================================================
globals
private constant integer MAXINSTANCES = 8190 //this is a lot, unless you leak xecast objects
private constant integer INITIAL_DUMMY_COUNT = 12
private constant integer DUMMY_STACK_LIMIT = 50 //don't allow to keep more than DUMMY_STACK_LIMIT innactive dummy units
private constant boolean FORCE_INVISIBLE_CAST = false // If your map does not give visibility to all players, or
// for other reasons, you might want xecast to work on
// units that are not visible to the player, in that case
// change this to true, else it is just a performance loss.
endglobals
//=========================================================================
// Please notice all textmacros in this library are considered private.
// in other words: DON'T RUN THOSE TEXTMACROS!
//
private keyword structinit
globals
private real EPSILON=0.001 //noticed in war3 this is the sort of precision we want...
endglobals
struct xecast[MAXINSTANCES]
public integer abilityid = 0 //ID (rawcode) of the ability to cast
public integer level = 1 //Level of the ability to cast
public real recycledelay = 0.0 //Please notice, some spells need a recycle delay
// This is, a time period before they get recycle.
// For example, some spells are not instant, there is
// also the problem with damaging spells, this recycle
// delay must be large enough to contain all the time
// in which the spell can do damage.
public player owningplayer=Player(15) //which player to credit for the ability cast?
//notice this can also affect what units are targeteable
//==================================================================================================
// You need an order id for the ability so the dummy unit is able to cast it, two ways to assign it
// set instance.orderid = 288883 //would assign an integer orderid
// set instance.orderstring = "chainlightning" //would assign an orderstring
// (as those in the object editor)
//
method operator orderid= takes integer v returns nothing
set .oid=v
endmethod
method operator orderstring= takes string s returns nothing
set .oid=OrderId(s)
endmethod
//=================================================================================================
// Finally, you can determine from which point to cast the ability: z is the height coordinate.
//
public boolean customsource=false //Use a custom casting source?
public real sourcex // Determine the casting source for the dummy spell, require customsource =true
public real sourcey // You might prefer to use the setSourcePoint method
public real sourcez=0.0 //
method setSourcePoint takes real x, real y, real z returns nothing
set .sourcex=x
set .sourcey=y
set .sourcez=z
set .customsource=true
endmethod
method setSourceLoc takes location loc, real z returns nothing
set .sourcex=GetLocationX(loc)
set .sourcey=GetLocationY(loc)
set .sourcez=z
set .customsource=true
endmethod
private boolean autodestroy = false
//========================================================================================================
// you are always allowed to use .create() but you can also use createBasic which sets some things that
// are usually necessary up.
//
public static method createBasic takes integer abilityID, integer orderid, player owner returns xecast
local xecast r=xecast.allocate()
if(r==0) then
debug call BJDebugMsg("Warning: unbelievable but you actually used all xecast instances in your map! Please make sure you are not forgetting to destroy those what you create intensively, if that's not the case, then you'll have to increase xecast MAXINSTANCES")
endif
set r.oid=orderid
set r.abilityid=abilityID
set r.owningplayer=owner
return r
endmethod
//========================================================================================================
// Just like the above one, but the instance will self destruct after a call to any cast method
// (recommended)
//
public static method createBasicA takes integer abilityID, integer orderid, player owner returns xecast
local xecast r=xecast.allocate()
if(r==0) then
debug call BJDebugMsg("Warning: unbelievable but you actually used all xecast instances in your map! Please make sure you are not forgetting to destroy those what you create intensively, if that's not the case, then you'll have to increase xecast MAXINSTANCES")
endif
set r.oid=orderid
set r.abilityid=abilityID
set r.owningplayer=owner
set r.autodestroy=true
return r
endmethod
//==========================================================================================================
// Just like create, but the struct instance self destructs after a call to any cast method
// (Recommended)
//
public static method createA takes nothing returns xecast
local xecast r=xecast.allocate()
set r.autodestroy=true
return r
endmethod
//==========================================================================================================
// So, create the dummy, assign options and cast the skill!
// .castOnTarget(w) : If you want to hit a widget w with the ability
// .castOnPoint(x,y) : If you want to hit a point (x,y) with the ability
// .castInPoint(x,y) : For spells like warstomp which do not have a target.
// .castOnAOE(x,y,radius) : Classic area of effect cast. Considers collision size
// .castOnGroup(g) : Cast unit the unit group g, notice it will empty the group yet not destroy it.
//
//**********************************************************************************************************
// The implementation of such methods follows:
private static unit array dummystack
private static integer top=0
private static unit instantdummy
private integer oid=0
private static timer gametime
private static timer T
private static unit array recycle
private static real array expiretime
private static integer rn=0
//==========================================================================================================
// private dorecycle method, sorry but I need this up here.
//
private static method dorecycle takes nothing returns nothing
local unit u =.recycle[0]
local integer l
local integer r
local integer p
local real lt
call UnitRemoveAbility(u,GetUnitUserData(u))
call SetUnitUserData(u,0)
call SetUnitFlyHeight(u,0,0)
call PauseUnit(u,false)
if(.top==DUMMY_STACK_LIMIT) then
call RemoveUnit(u)
else
set .dummystack[.top]=u
set .top=.top+1
endif
set .rn=.rn-1
if(.rn==0) then
return
endif
set p=0
set lt=.expiretime[.rn]
loop
set l=p*2+1
exitwhen l>=.rn
set r=p*2+2
if(r>=.rn)then
if(.expiretime[l]<lt) then
set .expiretime[p]=.expiretime[l]
set .recycle[p]=.recycle[l]
set p=l
else
exitwhen true
endif
elseif (lt<=.expiretime[l]) and (lt<=.expiretime[r]) then
exitwhen true
elseif (.expiretime[l]<.expiretime[r]) then
set .expiretime[p]=.expiretime[l]
set .recycle[p]=.recycle[l]
set p=l
else
set .expiretime[p]=.expiretime[r]
set .recycle[p]=.recycle[r]
set p=r
endif
endloop
set .recycle[p]=.recycle[.rn]
set .expiretime[p]=lt
call TimerStart(.T, .expiretime[0]-TimerGetElapsed(.gametime), false, function xecast.dorecycle)
endmethod
private static trigger abilityRemove
// Repetitive process and no inline implemented for large functions, so for now it is a textmacro:
//! textmacro xecast_allocdummy
if(.recycledelay<EPSILON) then
set dummy=.instantdummy
call SetUnitOwner(dummy,.owningplayer,false)
elseif (.top>0) then
set .top=.top-1
set dummy=.dummystack[.top]
call SetUnitOwner(dummy,.owningplayer,false)
else
set dummy=CreateUnit(.owningplayer,XE_DUMMY_UNITID,0,0,0)
call TriggerRegisterUnitEvent(.abilityRemove,dummy,EVENT_UNIT_SPELL_ENDCAST)
call UnitAddAbility(dummy,'Aloc')
call UnitAddAbility(dummy,XE_HEIGHT_ENABLER)
call UnitRemoveAbility(dummy,XE_HEIGHT_ENABLER)
endif
call UnitAddAbility(dummy,.abilityid)
if(.level>1) then
call SetUnitAbilityLevel(dummy,.abilityid,.level)
endif
//! endtextmacro
private static integer cparent
private static integer current
private static real cexpire
//! textmacro xecast_deallocdummy
if(.recycledelay>=EPSILON) then
set .cexpire=TimerGetElapsed(.gametime)+.recycledelay
set .current=.rn
set .rn=.rn+1
loop
exitwhen (.current==0)
set .cparent=(.current-1)/2
exitwhen (.expiretime[.cparent]<=.cexpire)
set .recycle[.current]=.recycle[.cparent]
set .expiretime[.current]=.expiretime[.cparent]
set .current=.cparent
endloop
set .expiretime[.current]=.cexpire
set .recycle[.current]=dummy
call SetUnitUserData(dummy,.abilityid)
call TimerStart(.T, .expiretime[0]-TimerGetElapsed(.gametime), false, function xecast.dorecycle)
else
call SetUnitUserData(dummy,0)
call SetUnitFlyHeight(dummy,0,0)
call UnitRemoveAbility(dummy,.abilityid)
endif
//! endtextmacro
method castOnTarget takes unit target returns nothing
local unit dummy
local unit tar
//! runtextmacro xecast_allocdummy()
if (.customsource) then
call SetUnitX(dummy,.sourcex)
call SetUnitY(dummy,.sourcey)
call SetUnitFlyHeight(dummy,.sourcez,0.0)
else
call SetUnitX(dummy,GetWidgetX(target))
call SetUnitY(dummy,GetWidgetY(target))
endif
if (FORCE_INVISIBLE_CAST) then
call UnitShareVision(target, .owningplayer, true)
call IssueTargetOrderById(dummy,this.oid,target)
call UnitShareVision(target, .owningplayer, false)
else
call IssueTargetOrderById(dummy,this.oid,target)
endif
//! runtextmacro xecast_deallocdummy()
if(.autodestroy ) then
call this.destroy()
endif
endmethod
//accepts units, items and destructables, if you know it is
// a unit it is better to use castOnTarget since that would
// be able to use FORCE_INVISIBLE_CAST if necessary.
//
method castOnWidgetTarget takes widget target returns nothing
local unit dummy
local unit tar
//! runtextmacro xecast_allocdummy()
if (.customsource) then
call SetUnitX(dummy,.sourcex)
call SetUnitY(dummy,.sourcey)
call SetUnitFlyHeight(dummy,.sourcez,0.0)
else
call SetUnitX(dummy,GetWidgetX(target))
call SetUnitY(dummy,GetWidgetY(target))
endif
call IssueTargetOrderById(dummy,this.oid,target)
//! runtextmacro xecast_deallocdummy()
if(.autodestroy ) then
call this.destroy()
endif
endmethod
method castOnPoint takes real x, real y returns nothing
local unit dummy
//! runtextmacro xecast_allocdummy()
if (.customsource) then
call SetUnitX(dummy,.sourcex)
call SetUnitY(dummy,.sourcey)
call SetUnitFlyHeight(dummy,.sourcez,0.0)
else
call SetUnitX(dummy,x)
call SetUnitY(dummy,y)
endif
call IssuePointOrderById(dummy,this.oid,x,y)
//! runtextmacro xecast_deallocdummy()
if(.autodestroy ) then
call this.destroy()
endif
endmethod
method castOnLoc takes location loc returns nothing
//debug call BJDebugMsg("Warning: Locations are in use")
//nah but I should
call .castOnPoint(GetLocationX(loc),GetLocationY(loc))
endmethod
//ignores custom source x and y (for obvious reasons)
method castInPoint takes real x, real y returns nothing
local unit dummy
//! runtextmacro xecast_allocdummy()
if (.customsource) then
call SetUnitFlyHeight(dummy,.sourcez,0.0)
endif
call SetUnitX(dummy, x)
call SetUnitY(dummy, y)
call IssueImmediateOrderById(dummy,this.oid)
//! runtextmacro xecast_deallocdummy()
if(.autodestroy ) then
call this.destroy()
endif
endmethod
method castInLoc takes location loc returns nothing
//debug call BJDebugMsg("Warning: Locations are in use")
//nah but I should
call .castInPoint(GetLocationX(loc),GetLocationY(loc))
endmethod
//===================================================================================================
// For method castOnAOE:
//
private static group enumgroup
private static real aoex
private static real aoey
private static real aoeradius
private static xecast cinstance
private static boolexpr aoefunc
// Might look wrong, but this is the way to make it consider collision size, a spell that
// got a target circle and uses this method will let the user know which units it will
// hit with the mass cast.
static method filterAOE takes nothing returns boolean
local unit u=GetFilterUnit()
if IsUnitInRangeXY(u, .aoex, .aoey, .aoeradius) then
call .cinstance.castOnTarget(u)
endif
set u=null
return false
endmethod
//
method castOnAOE takes real x, real y, real radius returns nothing
local boolean ad=this.autodestroy
if(ad) then
set this.autodestroy=false
endif
set .aoex=x
set .aoey=y
set .aoeradius=radius
set .cinstance=this
call GroupEnumUnitsInRange(.enumgroup,x,y,radius + XE_MAX_COLLISION_SIZE , .aoefunc)
if(ad) then
call this.destroy()
endif
endmethod
method castOnAOELoc takes location loc,real radius returns nothing
call .castOnAOE(GetLocationX(loc),GetLocationY(loc),radius)
endmethod
//==================================================================================================
// A quick and dirt castOnGroup method, perhaps it'll later have castOntarget inlined, but not now
//
method castOnGroup takes group g returns nothing
local boolean ad=this.autodestroy
local unit t
if(ad) then
set this.autodestroy=false
endif
loop
set t=FirstOfGroup(g)
exitwhen(t==null)
call GroupRemoveUnit(g,t)
call .castOnTarget(t)
endloop
if(ad) then
call this.destroy()
endif
endmethod
private static method removeAbility takes nothing returns boolean
local unit u=GetTriggerUnit()
if(GetUnitUserData(u)!=0) then
call PauseUnit(u,true)
endif
//This is necessary, picture a value for recycle delay that's higher than the casting time,
//for example if the spell does dps, if you leave the dummy caster with the ability and it
//is owned by an AI player it will start casting the ability on player units, so it is
// a good idea to pause it...
set u=null
return true
endmethod
//===================================================================================================
// structinit is a scope private keyword.
//
static method structinit takes nothing returns nothing
local integer i=INITIAL_DUMMY_COUNT+1
local unit u
set .aoefunc=Condition(function xecast.filterAOE)
set .enumgroup=CreateGroup()
set .abilityRemove = CreateTrigger()
loop
exitwhen (i==0)
set u=CreateUnit(Player(15),XE_DUMMY_UNITID,0,0,0)
call TriggerRegisterUnitEvent(.abilityRemove,u,EVENT_UNIT_SPELL_ENDCAST)
call UnitAddAbility(u,'Aloc')
call UnitAddAbility(u,XE_HEIGHT_ENABLER)
call UnitRemoveAbility(u,XE_HEIGHT_ENABLER)
set .dummystack[.top]=u
set .top=.top+1
set i=i-1
endloop
call TriggerAddCondition(.abilityRemove, Condition(function xecast.removeAbility ) )
set .top=.top-1
set .instantdummy=.dummystack[.top]
set .T=CreateTimer()
set .gametime=CreateTimer()
call TimerStart(.gametime,12*60*60,false,null)
endmethod
endstruct
private function init takes nothing returns nothing
call xecast.structinit()
endfunction
endlibrary
library xepreload initializer init requires xebasic
//************************************************************************
// xepreload 0.4
// ---------
// Ah, the joy of preloading abilities, it is such a necessary evil...
// Notice you are not supposed to use this system in places outside map init
//
// This one does the preloading and tries to minimize the hit on loading time
// for example, it only needs one single native call per ability preloaded.
//
//************************************************************************
//===========================================================================================================
globals
private unit dum=null
endglobals
//inline friendly
function XE_PreloadAbility takes integer abilid returns nothing
call UnitAddAbility(dum, abilid)
endfunction
private function kill takes nothing returns nothing
call RemoveUnit(dum)
set dum=null
call DestroyTimer(GetExpiredTimer()) //I do hope this doesn't trigger the apocallypse. I didn't think
// it was a great idea to make this require a whole timer recycling
// system, so, just destroy. Anyone got a better idea on how to make
// this execute after all the other calls at init?
endfunction
private function init takes nothing returns nothing
set dum=CreateUnit(Player(15),XE_DUMMY_UNITID, 0,0,0)
call TimerStart(CreateTimer(),0.0,false,function kill)
endfunction
endlibrary
//TESH.scrollpos=-1
//TESH.alwaysfold=0
library GroupUtils
//******************************************************************************
//* BY: Rising_Dusk
//*
//* This library is a simple implementation of a stack for groups that need to
//* be in the user's control for greater than an instant of time. Additionally,
//* this library provides a single, global group variable for use with user-end
//* enumerations. It is important to note that users should not be calling
//* DestroyGroup() on the global group, since then it may not exist for when it
//* it is next needed.
//*
//* The group stack removes the need for destroying groups and replaces it with
//* a recycling method.
//* function NewGroup takes nothing returns group
//* function ReleaseGroup takes group g returns boolean
//* function GroupRefresh takes group g returns nothing
//*
//* NewGroup grabs a currently unused group from the stack or creates one if the
//* stack is empty. You can use this group however you'd like, but always
//* remember to call ReleaseGroup on it when you are done with it. If you don't
//* release it, it will 'leak' and your stack may eventually overflow if you
//* keep doing that.
//*
//* GroupRefresh cleans a group of any shadow references which may be clogging
//* its hash table. If you remove a unit from the game who is a member of a unit
//* group, it will 'effectively' remove the unit from the group, but leave a
//* shadow in its place. Calling GroupRefresh on a group will clean up any
//* shadow references that may exist within it.
//*
globals
//* Group for use with all instant enumerations
group ENUM_GROUP = CreateGroup()
//* Temporary references for GroupRefresh
private boolean Flag = false
private group Refr = null
//* Assorted constants
private constant integer MAX_HANDLE_COUNT = 408000
private constant integer MIN_HANDLE_ID = 0x100000
//* Arrays and counter for the group stack
private group array Groups
private integer array Status[MAX_HANDLE_COUNT]
private integer Count = 0
endglobals
private function AddEx takes nothing returns nothing
if Flag then
call GroupClear(Refr)
set Flag = false
endif
call GroupAddUnit(Refr, GetEnumUnit())
endfunction
function GroupRefresh takes group g returns nothing
set Flag = true
set Refr = g
call ForGroup(Refr, function AddEx)
if Flag then
call GroupClear(g)
endif
endfunction
function NewGroup takes nothing returns group
if Count == 0 then
set Groups[0] = CreateGroup()
else
set Count = Count - 1
endif
set Status[GetHandleId(Groups[Count])-MIN_HANDLE_ID] = 1
return Groups[Count]
endfunction
function ReleaseGroup takes group g returns boolean
local integer stat = Status[GetHandleId(g)-MIN_HANDLE_ID]
local boolean b = true
if g == null then
debug call BJDebugMsg(SCOPE_PREFIX+" Error: Null groups cannot be released")
set b = false
elseif stat == 0 then
debug call BJDebugMsg(SCOPE_PREFIX+" Error: Group not part of stack")
set b = false
elseif stat == 2 then
debug call BJDebugMsg(SCOPE_PREFIX+" Error: Groups cannot be multiply released")
set b = false
elseif Count == 8191 then
debug call BJDebugMsg(SCOPE_PREFIX+" Error: Max groups achieved, destroying group")
call DestroyGroup(g)
set b = false
else
call GroupClear(g)
set Groups[Count] = g
set Count = Count + 1
set Status[GetHandleId(g)-MIN_HANDLE_ID] = 2
endif
return b
endfunction
endlibrary
//TESH.scrollpos=-1
//TESH.alwaysfold=0
library SpellEvent initializer Init requires Table
//*****************************************************************
//* SPELL EVENT LIBRARY
//*
//* written by: Anitarf
//* requires: -Table
//*
//* Maps with many triggered spells require many triggers that run
//* on spell events and whenever a spell is cast, all those
//* triggers need to be evaluated by the game even though only one
//* actually needs to run. This library has been written to reduce
//* the number of triggers in such maps; instead of having a
//* trigger per spell, you can use this library's single trigger
//* to only run the code associated with the spell that's actually
//* being cast.
//*
//* Perhaps more significant than the marginal speed gain is the
//* feature that allows you to access all the spell event
//* responses from all spell events, something that the native
//* functions senselessly do not support. With this system you can
//* for example easily get the target unit of the spell on the
//* casting finish event.
//*
//* All functions following the Response function interface that
//* is defined at the start of this library can be used to respond
//* to damage events. Simply put them on the call list for any of
//* the spell events using the appropriate function:
//*
//* function RegisterSpellChannelResponse takes integer spellId, Response r returns nothing
//* function RegisterSpellCastResponse takes integer spellId, Response r returns nothing
//* function RegisterSpellEffectResponse takes integer spellId, Response r returns nothing
//* function RegisterSpellFinishResponse takes integer spellId, Response r returns nothing
//* function RegisterSpellEndCastResponse takes integer spellId, Response r returns nothing
//*
//* The first event occurs at the very start of the spell, when
//* the spell's casting time begins; most spells have 0 casting
//* time, so in most cases this first event occurs at the same
//* time as the second one, which runs when the unit actually
//* begins casting a spell by starting it's spell animation. The
//* third event occurs when the spell effect actually takes place,
//* which happens sometime into the unit's spell animation
//* depending on the unit's Animation - Cast Point property.
//* The fourth event runs if the unit finishes casting the spell
//* uninterrupted, which might be important for channeling spells.
//* The last event runs when the unit stops casting the spell,
//* regardless of whether it finished casting or was interrupted.
//*
//* If you specify a spell id when registering a function then
//* that function will only run when that ability is cast; only
//* one function per ability per event is supported, if you
//* register more functions then only the last one registered will
//* be called. If, however, you pass 0 as the ability id parameter
//* then the registered function will run for all spells. Up to
//* 8190 functions can be registered this way for each event.
//* These functions will be called before the ability's specific
//* function in the order they were registered.
//*
//* This library provides it's own event responses that work
//* better than the Blizzard's bugged native cast event responses.
//* They still aren't guaranteed to work after a wait, but aside
//* from that they will work in response functions no matter what
//* event they are registered to.
//*
//* Here are usage examples for all event responses:
//*
//* local integer a = SpellEvent.AbilityId
//* local unit u = SpellEvent.CastingUnit
//* local unit t = SpellEvent.TargetUnit
//* local item i = SpellEvent.TargetItem
//* local destructable d = SpellEvent.TargetDestructable
//* local location l = SpellEvent.TargetLoc
//* local real x = SpellEvent.TargetX
//* local real y = SpellEvent.TargetY
//* local boolean b = SpellEvent.CastFinished
//*
//* SpellEvent.TargetLoc is provided for odd people who insist on
//* using locations, note that if you use it you have to cleanup
//* the returned location yourself.
//*
//* SpellEvent.CastFinished boolean is intended only for the
//* EndCast event as it tells you whether the spell finished or
//* was interrupted.
//*
//*****************************************************************
// use the RegisterSpell*Response functions to add spell event responses to the library
public function interface Response takes nothing returns nothing
// ================================================================
private keyword casterTable
private keyword effectDone
private keyword init
private keyword get
private struct spellEvent
static HandleTable casterTable
boolean effectDone=false
integer AbilityId
unit CastingUnit
unit TargetUnit
item TargetItem=null
destructable TargetDestructable=null
real TargetX=0.0
real TargetY=0.0
boolean CastFinished=false
method operator TargetLoc takes nothing returns location
return Location(.TargetX, .TargetY)
endmethod
private static method create takes nothing returns spellEvent
return spellEvent.allocate()
endmethod
static method init takes nothing returns spellEvent
local spellEvent s=spellEvent.allocate()
set s.AbilityId = GetSpellAbilityId()
set s.CastingUnit = GetTriggerUnit()
set s.TargetUnit = GetSpellTargetUnit()
if s.TargetUnit != null then
set s.TargetX = GetUnitX(s.TargetUnit)
set s.TargetY = GetUnitY(s.TargetUnit)
else
set s.TargetDestructable = GetSpellTargetDestructable()
if s.TargetDestructable != null then
set s.TargetX = GetDestructableX(s.TargetDestructable)
set s.TargetY = GetDestructableY(s.TargetDestructable)
else
set s.TargetItem = GetSpellTargetItem()
if s.TargetItem != null then
set s.TargetX = GetItemX(s.TargetItem)
set s.TargetY = GetItemY(s.TargetItem)
else
set s.TargetX = GetSpellTargetX()
set s.TargetY = GetSpellTargetY()
endif
endif
endif
set spellEvent.casterTable[s.CastingUnit]=integer(s)
return s
endmethod
static method get takes unit caster returns spellEvent
return spellEvent(spellEvent.casterTable[caster])
endmethod
method onDestroy takes nothing returns nothing
call spellEvent.casterTable.flush(.CastingUnit)
set .CastingUnit=null
endmethod
endstruct
globals
spellEvent SpellEvent=0
endglobals
// ================================================================
//! textmacro spellEvent_make takes name
globals
private Response array $name$CallList
private integer $name$CallCount=0
private Table $name$Table
endglobals
private function $name$Calls takes nothing returns nothing
local integer i=0
local integer id=GetSpellAbilityId()
local spellEvent previous=SpellEvent
set SpellEvent=spellEvent.get(GetTriggerUnit())
loop
exitwhen i>=$name$CallCount
call $name$CallList[i].evaluate()
set i=i+1
endloop
if $name$Table.exists(id) then
call Response($name$Table[id]).evaluate()
endif
set SpellEvent=previous
endfunction
function RegisterSpell$name$Response takes integer spellId, Response r returns nothing
if spellId==0 then
set $name$CallList[$name$CallCount]=r
set $name$CallCount=$name$CallCount+1
else
set $name$Table[spellId]=integer(r)
endif
endfunction
//! endtextmacro
//! runtextmacro spellEvent_make("Channel")
//! runtextmacro spellEvent_make("Cast")
//! runtextmacro spellEvent_make("Effect")
//! runtextmacro spellEvent_make("Finish")
//! runtextmacro spellEvent_make("EndCast")
private function Channel takes nothing returns nothing
call spellEvent.init()
call ChannelCalls()
endfunction
private function Cast takes nothing returns nothing
call CastCalls()
endfunction
private function Effect takes nothing returns nothing
local spellEvent s=spellEvent.get(GetTriggerUnit())
if s!=0 and not s.effectDone then
set s.effectDone=true
call EffectCalls()
endif
endfunction
private function Finish takes nothing returns nothing
set spellEvent.get(GetTriggerUnit()).CastFinished=true
call FinishCalls()
endfunction
private function EndCast takes nothing returns nothing
call EndCastCalls()
call spellEvent.get(GetTriggerUnit()).destroy()
endfunction
// ================================================================
private function InitTrigger takes playerunitevent e, code c returns nothing
local trigger t=CreateTrigger()
call TriggerRegisterAnyUnitEventBJ( t, e )
call TriggerAddAction(t, c)
set t=null
endfunction
private function Init takes nothing returns nothing
set spellEvent.casterTable=HandleTable.create()
set ChannelTable=Table.create()
set CastTable=Table.create()
set EffectTable=Table.create()
set FinishTable=Table.create()
set EndCastTable=Table.create()
call InitTrigger(EVENT_PLAYER_UNIT_SPELL_CHANNEL, function Channel)
call InitTrigger(EVENT_PLAYER_UNIT_SPELL_CAST, function Cast)
call InitTrigger(EVENT_PLAYER_UNIT_SPELL_EFFECT, function Effect)
call InitTrigger(EVENT_PLAYER_UNIT_SPELL_FINISH, function Finish)
call InitTrigger(EVENT_PLAYER_UNIT_SPELL_ENDCAST, function EndCast)
endfunction
endlibrary
//TESH.scrollpos=-1
//TESH.alwaysfold=0
library Table
//***************************************************************
//* Table object 3.0
//* ------------
//*
//* set t=Table.create() - instanceates a new table object
//* call t.destroy() - destroys it
//* t[1234567] - Get value for key 1234567
//* (zero if not assigned previously)
//* set t[12341]=32 - Assigning it.
//* call t.flush(12341) - Flushes the stored value, so it
//* doesn't use any more memory
//* t.exists(32) - Was key 32 assigned? Notice
//* that flush() unassigns values.
//* call t.reset() - Flushes the whole contents of the
//* Table.
//*
//* call t.destroy() - Does reset() and also recycles the id.
//*
//* If you use HandleTable instead of Table, it is the same
//* but it uses handles as keys, the same with StringTable.
//*
//* You can use Table on structs' onInit if the struct is
//* placed in a library that requires Table or outside a library.
//*
//* You can also do 2D array syntax if you want to touch
//* mission keys directly, however, since this is shared space
//* you may want to prefix your mission keys accordingly:
//*
//* set Table["thisstring"][ 7 ] = 2
//* set Table["thisstring"][ 5 ] = Table["thisstring"][7]
//*
//***************************************************************
//=============================================================
globals
private constant integer MAX_INSTANCES=8100 //400000
//Feel free to change max instances if necessary, it will only affect allocation
//speed which shouldn't matter that much.
//=========================================================
private hashtable ht
endglobals
private struct GTable[MAX_INSTANCES]
method reset takes nothing returns nothing
call FlushChildHashtable(ht, integer(this) )
endmethod
private method onDestroy takes nothing returns nothing
call this.reset()
endmethod
//=============================================================
// initialize it all.
//
private static method onInit takes nothing returns nothing
set ht = InitHashtable()
endmethod
endstruct
//Hey: Don't instanciate other people's textmacros that you are not supposed to, thanks.
//! textmacro Table__make takes name, type, key
struct $name$ extends GTable
method operator [] takes $type$ key returns integer
return LoadInteger(ht, integer(this), $key$)
endmethod
method operator []= takes $type$ key, integer value returns nothing
call SaveInteger(ht, integer(this) ,$key$, value)
endmethod
method flush takes $type$ key returns nothing
call RemoveSavedInteger(ht, integer(this), $key$)
endmethod
method exists takes $type$ key returns boolean
return HaveSavedInteger( ht, integer(this) ,$key$)
endmethod
static method flush2D takes string firstkey returns nothing
call $name$(- StringHash(firstkey)).reset()
endmethod
static method operator [] takes string firstkey returns $name$
return $name$(- StringHash(firstkey) )
endmethod
endstruct
//! endtextmacro
//! runtextmacro Table__make("Table","integer","key" )
//! runtextmacro Table__make("StringTable","string", "StringHash(key)" )
//! runtextmacro Table__make("HandleTable","handle","GetHandleId(key)" )
endlibrary
//TESH.scrollpos=-1
//TESH.alwaysfold=0
library_once TimerUtils initializer init
//*********************************************************************
//* TimerUtils (Blue flavor for 1.23b or later)
//* ----------
//*
//* To implement it , create a custom text trigger called TimerUtils
//* and paste the contents of this script there.
//*
//* To copy from a map to another, copy the trigger holding this
//* library to your map.
//*
//* (requires vJass) More scripts: htt://www.wc3campaigns.net
//*
//* For your timer needs:
//* * Attaching
//* * Recycling (with double-free protection)
//*
//* set t=NewTimer() : Get a timer (alternative to CreateTimer)
//* ReleaseTimer(t) : Relese a timer (alt to DestroyTimer)
//* SetTimerData(t,2) : Attach value 2 to timer
//* GetTimerData(t) : Get the timer's value.
//* You can assume a timer's value is 0
//* after NewTimer.
//*
//* Blue Flavor: Slower than the red flavor, it got a 408000 handle id
//* limit, which means that if more than 408000 handle ids
//* are used in your map, TimerUtils might fail, this
//* value is quite big and it is much bigger than the
//* timer limit in Red flavor.
//*
//********************************************************************
//==================================================================================================
globals
private hashtable hasht //I <3 blizz
endglobals
//It is dependent on jasshelper's recent inlining optimization in order to perform correctly.
function SetTimerData takes timer t, integer value returns nothing
call SaveInteger(hasht,0, GetHandleId(t), value)
endfunction
function GetTimerData takes timer t returns integer
return LoadInteger(hasht, 0, GetHandleId(t))
endfunction
//==========================================================================================
globals
private timer array tT
private integer tN = 0
private constant integer HELD=0x28829022
//use a totally random number here, the more improbable someone uses it, the better.
endglobals
//==========================================================================================
function NewTimer takes nothing returns timer
if (tN==0) then
set tT[0]=CreateTimer()
else
set tN=tN-1
endif
call SetTimerData(tT[tN],0)
return tT[tN]
endfunction
//==========================================================================================
function ReleaseTimer takes timer t returns nothing
if(t==null) then
debug call BJDebugMsg("Warning: attempt to release a null timer")
return
endif
if (tN==8191) then
debug call BJDebugMsg("Warning: Timer stack is full, destroying timer!!")
//stack is full, the map already has much more troubles than the chance of bug
call DestroyTimer(t)
else
call PauseTimer(t)
if(GetTimerData(t)==HELD) then
debug call BJDebugMsg("Warning: ReleaseTimer: Double free!")
return
endif
call SetTimerData(t,HELD)
set tT[tN]=t
set tN=tN+1
endif
endfunction
private function init takes nothing returns nothing
set hasht = InitHashtable()
endfunction
endlibrary
//TESH.scrollpos=-1
//TESH.alwaysfold=0
//==============================================================================
// TEXT TAG - Floating text system by Cohadar - v5.0
//==============================================================================
//
// PURPOUSE:
// * Displaying floating text - the easy way
// * Has a set of useful and commonly needed texttag functions
//
// CREDITS:
// * DioD - for extracting proper color, fadepoint and lifespan parameters
// for default warcraft texttags (from miscdata.txt)
//
// HOW TO IMPORT:
// * Just create a trigger named TextTag
// convert it to text and replace the whole trigger text with this one
//==============================================================================
library TextTag
globals
// for custom centered texttags
private constant real MEAN_CHAR_WIDTH = 5.5
private constant real MAX_TEXT_SHIFT = 200.0
private constant real DEFAULT_HEIGHT = 16.0
// for default texttags
private constant real SIGN_SHIFT = 16.0
private constant real FONT_SIZE = 0.024
private constant string MISS = "miss"
endglobals
//===========================================================================
// Custom centered texttag on (x,y) position
// color is in default wc3 format, for example "|cFFFFCC00"
//===========================================================================
public function XY takes real x, real y, string text, string color returns nothing
local texttag tt = CreateTextTag()
local real shift = RMinBJ(StringLength(text)*MEAN_CHAR_WIDTH, MAX_TEXT_SHIFT)
call SetTextTagText(tt, color+text, FONT_SIZE)
call SetTextTagPos(tt, x-shift, y, DEFAULT_HEIGHT)
call SetTextTagVelocity(tt, 0.0, 0.04)
call SetTextTagVisibility(tt, true)
call SetTextTagFadepoint(tt, 2.5)
call SetTextTagLifespan(tt, 4.0)
call SetTextTagPermanent(tt, false)
set tt = null
endfunction
//===========================================================================
// Custom centered texttag above unit
//===========================================================================
public function Unit takes unit whichUnit, string text, string color returns nothing
local texttag tt = CreateTextTag()
local real shift = RMinBJ(StringLength(text)*MEAN_CHAR_WIDTH, MAX_TEXT_SHIFT)
call SetTextTagText(tt, color+text, FONT_SIZE)
call SetTextTagPos(tt, GetUnitX(whichUnit)-shift, GetUnitY(whichUnit), DEFAULT_HEIGHT)
call SetTextTagVelocity(tt, 0.0, 0.04)
call SetTextTagVisibility(tt, true)
call SetTextTagFadepoint(tt, 2.5)
call SetTextTagLifespan(tt, 4.0)
call SetTextTagPermanent(tt, false)
set tt = null
endfunction
//===========================================================================
// Standard wc3 gold bounty texttag, displayed only to killing player
//===========================================================================
public function GoldBounty takes unit whichUnit, integer bounty, player killer returns nothing
local texttag tt = CreateTextTag()
local string text = "+" + I2S(bounty)
call SetTextTagText(tt, text, FONT_SIZE)
call SetTextTagPos(tt, GetUnitX(whichUnit)-SIGN_SHIFT, GetUnitY(whichUnit), 0.0)
call SetTextTagColor(tt, 255, 220, 0, 255)
call SetTextTagVelocity(tt, 0.0, 0.03)
call SetTextTagVisibility(tt, GetLocalPlayer()==killer)
call SetTextTagFadepoint(tt, 2.0)
call SetTextTagLifespan(tt, 3.0)
call SetTextTagPermanent(tt, false)
set text = null
set tt = null
endfunction
//==============================================================================
public function LumberBounty takes unit whichUnit, integer bounty, player killer returns nothing
local texttag tt = CreateTextTag()
local string text = "+" + I2S(bounty)
call SetTextTagText(tt, text, FONT_SIZE)
call SetTextTagPos(tt, GetUnitX(whichUnit)-SIGN_SHIFT, GetUnitY(whichUnit), 0.0)
call SetTextTagColor(tt, 0, 200, 80, 255)
call SetTextTagVelocity(tt, 0.0, 0.03)
call SetTextTagVisibility(tt, GetLocalPlayer()==killer)
call SetTextTagFadepoint(tt, 2.0)
call SetTextTagLifespan(tt, 3.0)
call SetTextTagPermanent(tt, false)
set text = null
set tt = null
endfunction
//===========================================================================
public function ManaBurn takes unit whichUnit, integer dmg returns nothing
local texttag tt = CreateTextTag()
local string text = "-" + I2S(dmg)
call SetTextTagText(tt, text, FONT_SIZE)
call SetTextTagPos(tt, GetUnitX(whichUnit)-SIGN_SHIFT, GetUnitY(whichUnit), 0.0)
call SetTextTagColor(tt, 82, 82 ,255 ,255)
call SetTextTagVelocity(tt, 0.0, 0.04)
call SetTextTagVisibility(tt, true)
call SetTextTagFadepoint(tt, 2.0)
call SetTextTagLifespan(tt, 5.0)
call SetTextTagPermanent(tt, false)
set text = null
set tt = null
endfunction
//===========================================================================
public function Miss takes unit whichUnit returns nothing
local texttag tt = CreateTextTag()
call SetTextTagText(tt, MISS, FONT_SIZE)
call SetTextTagPos(tt, GetUnitX(whichUnit), GetUnitY(whichUnit), 0.0)
call SetTextTagColor(tt, 255, 0, 0, 255)
call SetTextTagVelocity(tt, 0.0, 0.03)
call SetTextTagVisibility(tt, true)
call SetTextTagFadepoint(tt, 1.0)
call SetTextTagLifespan(tt, 3.0)
call SetTextTagPermanent(tt, false)
set tt = null
endfunction
//===========================================================================
public function CriticalStrike takes unit whichUnit, integer dmg returns nothing
local texttag tt = CreateTextTag()
local string text = I2S(dmg) + "!"
call SetTextTagText(tt, text, FONT_SIZE)
call SetTextTagPos(tt, GetUnitX(whichUnit), GetUnitY(whichUnit), 0.0)
call SetTextTagColor(tt, 255, 0, 0, 255)
call SetTextTagVelocity(tt, 0.0, 0.04)
call SetTextTagVisibility(tt, true)
call SetTextTagFadepoint(tt, 2.0)
call SetTextTagLifespan(tt, 5.0)
call SetTextTagPermanent(tt, false)
set text = null
set tt = null
endfunction
//===========================================================================
public function ShadowStrike takes unit whichUnit, integer dmg, boolean initialDamage returns nothing
local texttag tt = CreateTextTag()
local string text = I2S(dmg)
if initialDamage then
set text = text + "!"
endif
call SetTextTagText(tt, text, FONT_SIZE)
call SetTextTagPos(tt, GetUnitX(whichUnit), GetUnitY(whichUnit), 0.0)
call SetTextTagColor(tt, 160, 255, 0, 255)
call SetTextTagVelocity(tt, 0.0, 0.04)
call SetTextTagVisibility(tt, true)
call SetTextTagFadepoint(tt, 2.0)
call SetTextTagLifespan(tt, 5.0)
call SetTextTagPermanent(tt, false)
set text = null
set tt = null
endfunction
endlibrary
//TESH.scrollpos=-1
//TESH.alwaysfold=0
library Messages initializer Init
private function CreditsProxy takes nothing returns nothing
call ExecuteFunc(SCOPE_PRIVATE+"Credits")
endfunction
private function Commands takes nothing returns nothing
call ClearTextMessages()
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,30, "|cff00bfffCommands:")
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,30, " |cffffcc00-reset|r: Spawns some footmen around you.")
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,30, " |cffffcc00-level |cff0000e0<level>|r: Sets the level of your hero to the specified level.")
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,30, " Note that you cannot decrease your hero's level this way.")
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,30, " |cffffcc00-handleid|r: Creates a location, displays its index - 0x1000001 and destroys it.")
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,30, " |cffffcc00-commands|r: Shows this list.")
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,30, " |cffffcc00-credits|r: Shows the credits.")
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,30, " |cffffcc00-clear|r: Clears all text-messages.")
if GetExpiredTimer()!=null then
call TimerStart(GetExpiredTimer(), 60., false, function CreditsProxy)
endif
endfunction
private function Credits takes nothing returns nothing
call ClearTextMessages()
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,30, "|cff00bfffCredits:")
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,30, " - |cffffcc00Archmage Owenalacaster|r for all his valuable help and feedback")
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,30, " - |cffffcc00WILL THE ALLMIGHTY|r for his CurseBolt model")
//call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,30, " - |cffffcc00Big Dub|r for his OrbPain icon")
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,30, " - |cffffcc00Earth-Fury|r for his UnitMaxState library")
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,30, " - |cffffcc00cohadar|r for his TextTag library")
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,30, " - |cffffcc00Rising_Dusk|r for his libraries: GroupUtils, UnitIndexingUtils, LastOrder, AbortSpell, Knockback and TerrainPathability")
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,30, " - |cffffcc00Anitarf|r for his SpellEvent library")
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,30, " - |cffffcc00Vexorian|r for JassHelper, TimerUtils, Table, SimError, xe and the dummy model")
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,30, " - |cffffcc00PipeDream|r for Grimoire")
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,30, " - |cffffcc00PitzerMike|r for JassNewGenPack and DestructableLib")
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,30, " - |cffffcc00MindWorX|r for JassNewGenPack")
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,30, " - |cffffcc00SFilip|r for TESH")
if GetExpiredTimer()!=null then
call TimerStart(GetExpiredTimer(), 60., false, function Commands)
endif
endfunction
private function ClearActions takes nothing returns nothing
call ClearTextMessages()
endfunction
private function Init takes nothing returns nothing
local trigger t=CreateTrigger()
call TriggerRegisterPlayerChatEvent(t, Player(0), "-clear", true)
call TriggerAddAction(t, function ClearActions)
set t=CreateTrigger()
call TriggerRegisterPlayerChatEvent(t, Player(0), "-commands", true)
call TriggerAddAction(t, function Commands)
set t=CreateTrigger()
call TriggerRegisterPlayerChatEvent(t, Player(0), "-credits", true)
call TriggerAddAction(t, function Credits)
call TimerStart(CreateTimer(), 0., false, function Commands)
endfunction
endlibrary
//TESH.scrollpos=-1
//TESH.alwaysfold=0
library Basic initializer Init
globals
unit HERO
endglobals
function H2I takes handle h returns integer
return GetHandleId(h)
endfunction
private function DeathActions takes nothing returns nothing
local unit u=GetTriggerUnit()
if IsUnitType(u, UNIT_TYPE_HERO) then
call PolledWait(5)
call ReviveHero(u, 0,0, true)
endif
set u=null
endfunction
private function Init takes nothing returns nothing
local trigger t=CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_DEATH)
call TriggerAddAction(t, function DeathActions)
set HERO=CreateUnit(Player(0), 'HERO', 0,0,0)
//call SetWidgetLife(HERO, GetWidgetLife(HERO)/2)
call PanCameraToTimed(0,0,0)
call FogEnable(false)
call FogMaskEnable(false)
endfunction
endlibrary
//TESH.scrollpos=-1
//TESH.alwaysfold=0
library Reset initializer Init
private function Actions takes nothing returns nothing
local real r
set r=0
loop
call CreateUnit(Player(12), 'hfoo', 0+(256*Cos(r)), 512+(256*Sin(r)), 0)
set r=r+(bj_PI/4)
exitwhen r>(7*bj_PI)/4
endloop
set r=0
loop
call CreateUnit(Player(12), 'hfoo', 512+(256*Cos(r)), 0+(256*Sin(r)), 0)
set r=r+(bj_PI/4)
exitwhen r>(7*bj_PI)/4
endloop
set r=0
loop
call CreateUnit(Player(12), 'hfoo', 0+(256*Cos(r)), -512+(256*Sin(r)), 0)
set r=r+(bj_PI/4)
exitwhen r>(7*bj_PI)/4
endloop
set r=0
loop
call CreateUnit(Player(12), 'hfoo', -512+(256*Cos(r)), 0+(256*Sin(r)), 0)
set r=r+(bj_PI/4)
exitwhen r>(7*bj_PI)/4
endloop
endfunction
private function Init takes nothing returns nothing
local trigger t=CreateTrigger()
call TriggerAddAction(t, function Actions)
call TriggerRegisterPlayerChatEvent(t, Player(0), "-reset", true)
endfunction
endlibrary
//TESH.scrollpos=-1
//TESH.alwaysfold=0
library LevelUp initializer Init uses Basic
private function LevelAction takes nothing returns nothing
local integer newlvl
local string s = GetEventPlayerChatString()
if SubString(s, 0, 7)=="-level " then
set newlvl=S2I(SubString(s, 7, StringLength(s)))
if newlvl>GetUnitLevel(HERO) and newlvl<=10 then
call SetHeroLevel(HERO, newlvl, true)
else
debug call BJDebugMsg("LevelUp - Wrong new level!")
endif
else
debug call BJDebugMsg("LevelUp - Don't try to trick my triggers!")
endif
endfunction
private function Init takes nothing returns nothing
local trigger t=CreateTrigger()
call TriggerRegisterPlayerChatEvent(t, Player(0), "-level ", false)
call TriggerAddAction(t, function LevelAction)
endfunction
endlibrary
//TESH.scrollpos=-1
//TESH.alwaysfold=0
function Trig_handleid_Actions takes nothing returns nothing
local location l=Location(0,0)
call BJDebugMsg("Max Handle ID: "+I2S(H2I(l)-0x100001))
call RemoveLocation(l)
set l=null
endfunction
//===========================================================================
function InitTrig_handleid takes nothing returns nothing
set gg_trg_handleid = CreateTrigger( )
call TriggerRegisterPlayerChatEvent(gg_trg_handleid, Player(0), "-handleid", true)
call TriggerAddAction( gg_trg_handleid, function Trig_handleid_Actions )
endfunction