//TESH.scrollpos=0
//TESH.alwaysfold=0
Name | Type | is_array | initial_value |
//TESH.scrollpos=-1
//TESH.alwaysfold=0
void CustomGrantItemsToHero (unit whichUnit){
integer owner = GetPlayerId(GetOwningPlayer(whichUnit))
if (bj_meleeTwinkedHeroes[owner] < bj_MELEE_MAX_TWINKED_HEROES) then
if GetUnitRace(whichUnit)==RACE_HUMAN then
UnitAddItemById(whichUnit, 'mcri');UnitAddItemById(whichUnit, 'stwp')
elseif GetUnitRace(whichUnit)==RACE_NIGHTELF then
UnitAddItemById(whichUnit, 'moon');UnitAddItemById(whichUnit, 'stwp')
elseif GetUnitRace(whichUnit)==RACE_UNDEAD then
UnitAddItemById(whichUnit, 'skul');UnitAddItemById(whichUnit, 'stwp')
elseif GetUnitRace(whichUnit)==RACE_ORC then
UnitAddItemById(whichUnit, 'shas');UnitAddItemById(whichUnit, 'stwp')
else;UnitAddItemById(whichUnit, 'I001');UnitAddItemById(whichUnit, 'tret')
endif;set bj_meleeTwinkedHeroes[owner] = bj_meleeTwinkedHeroes[owner] + 1;endif}
void CustomGrantItemsToTrainedHero(){CustomGrantItemsToHero(GetTrainedUnit())}
void CustomGrantItemsToHiredHero(){CustomGrantItemsToHero(GetSoldUnit())}
void CustomGrantHeroItems(){integer index;trigger trig;index = 0;loop;bj_meleeTwinkedHeroes[index] = 0
index = index + 1;exitwhen index == bj_MAX_PLAYER_SLOTS;endloop;index = 0;loop
trig = CreateTrigger();TriggerRegisterPlayerUnitEvent(trig, Player(index), EVENT_PLAYER_UNIT_TRAIN_FINISH, filterMeleeTrainedUnitIsHeroBJ)
TriggerAddAction(trig, function CustomGrantItemsToTrainedHero);index = index + 1;exitwhen index == bj_MAX_PLAYERS
endloop;trig = CreateTrigger();TriggerRegisterPlayerUnitEvent(trig, Player(PLAYER_NEUTRAL_PASSIVE), EVENT_PLAYER_UNIT_SELL, filterMeleeTrainedUnitIsHeroBJ)
TriggerAddAction(trig, function CustomGrantItemsToHiredHero);bj_meleeGrantHeroItems = true}
unit CustomRandomHeroLoc(player p, integer id1, integer id2, integer id3, integer id4, integer id5, location loc){
unit hero = null;integer roll,pick;version v;v = VersionGet();if (v == VERSION_REIGN_OF_CHAOS) then
roll = GetRandomInt(1,3);else;roll = GetRandomInt(1,5);endif;if roll == 1 then;pick = id1
elseif roll == 2 then;pick = id2;elseif roll == 3 then;pick = id3;elseif roll == 4 then
pick = id4;elseif roll == 5 then;pick = id5;else;pick = id1;endif;hero = CreateUnitAtLoc(p, pick, loc, bj_UNIT_FACING)
if bj_meleeGrantHeroItems then;MeleeGrantItemsToHero(hero);endif;return hero}
void CustomStartingUnitsHuman(player whichPlayer, location startLoc, boolean doHeroes, boolean doCamera, boolean doPreload)
{boolean useRandomHero = IsMapFlagSet(MAP_RANDOM_HERO)
real unitSpacing = 64.00;unit nearestMine,townHall = null;location nearMineLoc,heroLoc
real peonX,peonY;if (doPreload) then;Preloader( "scripts\\HumanMelee.pld" );endif
nearestMine = MeleeFindNearestMine(startLoc, bj_MELEE_MINE_SEARCH_RADIUS)
if(nearestMine!=null)then;townHall=CreateUnitAtLoc(whichPlayer,'htow',startLoc,bj_UNIT_FACING)
nearMineLoc = MeleeGetProjectedLoc(GetUnitLoc(nearestMine), startLoc, 320, 0)
peonX = GetLocationX(nearMineLoc);peonY = GetLocationY(nearMineLoc)
CreateUnit(whichPlayer, 'hpea', peonX + 0.00 * unitSpacing, peonY + 1.00 * unitSpacing, bj_UNIT_FACING)
CreateUnit(whichPlayer, 'hpea', peonX + 1.00 * unitSpacing, peonY + 0.15 * unitSpacing, bj_UNIT_FACING)
CreateUnit(whichPlayer, 'hpea', peonX - 1.00 * unitSpacing, peonY + 0.15 * unitSpacing, bj_UNIT_FACING)
CreateUnit(whichPlayer, 'hpea', peonX + 0.60 * unitSpacing, peonY - 1.00 * unitSpacing, bj_UNIT_FACING)
CreateUnit(whichPlayer, 'hpea', peonX - 0.60 * unitSpacing, peonY - 1.00 * unitSpacing, bj_UNIT_FACING)
heroLoc = MeleeGetProjectedLoc(GetUnitLoc(nearestMine), startLoc, 384, 45);else;
townHall = CreateUnitAtLoc(whichPlayer, 'htow', startLoc, bj_UNIT_FACING)
peonX = GetLocationX(startLoc);peonY = GetLocationY(startLoc) - 224.00
CreateUnit(whichPlayer, 'hpea', peonX + 2.00 * unitSpacing, peonY + 0.00 * unitSpacing, bj_UNIT_FACING)
CreateUnit(whichPlayer, 'hpea', peonX + 1.00 * unitSpacing, peonY + 0.00 * unitSpacing, bj_UNIT_FACING)
CreateUnit(whichPlayer, 'hpea', peonX + 0.00 * unitSpacing, peonY + 0.00 * unitSpacing, bj_UNIT_FACING)
CreateUnit(whichPlayer, 'hpea', peonX - 1.00 * unitSpacing, peonY + 0.00 * unitSpacing, bj_UNIT_FACING)
CreateUnit(whichPlayer, 'hpea', peonX - 2.00 * unitSpacing, peonY + 0.00 * unitSpacing, bj_UNIT_FACING)
heroLoc = Location(peonX, peonY - 2.00 * unitSpacing);endif;if (townHall != null) then
UnitAddAbilityBJ('Amic', townHall);UnitMakeAbilityPermanentBJ(true, 'Amic', townHall);endif
if (doHeroes) then;if useRandomHero then
CustomRandomHeroLoc(whichPlayer, 'Hamg', 'Hmkg', 'Hpal', 'Hblm', 'E007', heroLoc);else
SetPlayerState(whichPlayer, PLAYER_STATE_RESOURCE_HERO_TOKENS, bj_MELEE_STARTING_HERO_TOKENS)
endif;endif;if (doCamera) then;SetCameraPositionForPlayer(whichPlayer, peonX, peonY)
SetCameraQuickPositionForPlayer(whichPlayer, peonX, peonY);endif}
void CustomStartingUnits(){integer index;player indexPlayer;location indexStartLoc
race indexRace;Preloader( "scripts\\SharedMelee.pld" );index = 0;loop;
indexPlayer = Player(index);if (GetPlayerSlotState(indexPlayer) == PLAYER_SLOT_STATE_PLAYING) then
indexStartLoc = GetStartLocationLoc(GetPlayerStartLocation(indexPlayer));indexRace = GetPlayerRace(indexPlayer)
if (indexRace == RACE_HUMAN) then;CustomStartingUnitsHuman(indexPlayer, indexStartLoc, true, true, true)
elseif (indexRace == RACE_ORC) then;MeleeStartingUnitsOrc(indexPlayer, indexStartLoc, true, true, true)
elseif (indexRace == RACE_UNDEAD) then;MeleeStartingUnitsUndead(indexPlayer, indexStartLoc, true, true, true)
elseif (indexRace == RACE_NIGHTELF) then;MeleeStartingUnitsNightElf(indexPlayer, indexStartLoc, true, true, true)
else;MeleeStartingUnitsUnknownRace(indexPlayer, indexStartLoc, true, true, true);endif;endif
set index = index + 1;exitwhen index == bj_MAX_PLAYERS;endloop}
void Trig_Custom_Initialization_Actions(){MeleeStartingVisibility();MeleeStartingHeroLimit()
CustomGrantHeroItems();MeleeStartingResources();MeleeClearExcessUnits();CustomStartingUnits()
MeleeStartingAI();MeleeInitVictoryDefeat()}
//==
void InitTrig_Custom_Initialization(){gg_trg_Custom_Initialization=CreateTrigger()
TriggerAddAction( gg_trg_Custom_Initialization, function Trig_Custom_Initialization_Actions )}
//TESH.scrollpos=-1
//TESH.alwaysfold=0
//'AIpr'
scope shieldpala initializer I
struct divineShield; unit x; integer y
private method onTimedLoop takes nothing returns boolean
y++;if y>=160 then//160 is 4 seconds
SetUnitInvulnerable(x,false);return false; endif;SetUnitInvulnerable(x,true); return true; endmethod
implement TimedLoop
static method create takes unit u returns divineShield
local divineShield m= divineShield.allocate()
set m.x = u;set m.y=0
call m.startTimedLoop()
return m
endmethod
endstruct
private function Actions takes nothing returns boolean;call divineShield.create(GetSpellTargetUnit());return false;endfunction
private void I (){effectstartGT(Actions, 'AIpr')};endscope
//TESH.scrollpos=-1
//TESH.alwaysfold=0
/*
so far we have the idea that slark will have a few different commands.
one command will alternate running speed. push it once he goes to 200 ms. push it again he goes back to 300 ms.
now if he gets a powerup that might make him go to 400 or 500 ms. then he can use the command to go back to 200? if he push the command again he'll go back to 300 instead of the upgraded speed.
might need to give debuff if we give buff in the first place.
what else would we do. it's like sonic the hedgehog. collect coins. maybe enable collect lumber...not sure. that could be like lives or continues.
will need a somewhat elaborate multiboard system. it will help keep the score available.
will have to subtract current hp from the maximum hp to determine how many times slark got attacked.
will have to check maximum hp to see how many coins slark got. and so on...
*/
//TESH.scrollpos=108
//TESH.alwaysfold=0
library TimedLoop
// *******************************************************
// TimedLoop
// ---------
//
// Requires jasshelper 0.9.G.1 or greater.
//
// A library + module that are meant to make those
// array + timer loops easy, yet still faster than
// other alternatives meant to be easy (In other words
// no TriggerEvaluate is involved).
//
// The OOPness is interesting.
//
// Before implementing TimedLoop
// your struct needs an onTimedLoop method that takes
// nothing and returns boolean, if the method
// returns false, the instance will get removed
// from the loop and destroyed, else it will continue,
// think of it as if the call asks the method a
// question: "Should I continue the loop afterwards?"
//
// Alternatively, if you are not convinced, you may
// use the TimedLoop_CONTINUE and TimedLoop_STOP
// constants in the method's returns.
//
// After implementing TimedLoop, you can call
// the startTimedLoop method to add the periodic event
// to that instance, only call it once per instance.
//
// I recommend to call implement just bellow the
// declaration of the onLoop method, else it will
// actually use TriggerEvaluate, which is lame. Remind
// me to implement a topsort in jasshelper.
//
// If you feel the need to destroy the struct outside
// the loop, well, you'll have to add a flag to it so
// you send a message to onLoop to make it return false.
// A more complicated module to allow that easily would
// come later.
//
// *******************************************************
//========================================================
// config:
globals
public constant real PERIOD = 0.025
// A lower value and everything using the module will
// look better, yet performance will drop.
endglobals
//========================================================
// implementation:
//
globals
public constant boolean STOP = false
public constant boolean CONTINUE = true
endglobals
//===========================
module TimedLoop
// god bless private module members.
//
private static thistype array V // The array
private static integer N = 0 // The count
private static timer T = null // the timer, one per
// struct that implements this
private static method onExpire takes nothing returns nothing
local integer n = 0
local thistype this // yay for odd-sounding syntax constructs
local integer i = 0
loop
exitwhen (i== thistype.N)
set this = .V[i]
if ( this.onTimedLoop() == CONTINUE ) then
set .V[n] = this
set n=n+1
else
call this.destroy()
endif
set i=i+1
endloop
set thistype.N = n
if (n== 0) then
call PauseTimer(.T)
endif
endmethod
public method startTimedLoop takes nothing returns nothing
set .V[.N] = this
set .N=.N + 1
if (.N == 1) then
if (.T == null) then
set .T = CreateTimer()
endif
call TimerStart(.T, PERIOD, true, function thistype.onExpire)
endif
endmethod
endmodule
endlibrary
//example
/*struct moveUnit
unit u
//=====================================================
// You need to code an onTimedLoop method before
// implementing the module.
//
private method onTimedLoop takes nothing returns boolean
//instance's timer expired:
// This will just move a unit's x coordinate with
// a speed of 100 until it reaches 5000.0
call SetUnitX(u, GetUnitX(u) + 100.0* TimedLoop_PERIOD )
// notice the use of the TimedLoop_PERIOD constant
// since it may be tweaked by the user...
if ( GetUnitX(u) >= 5000) then
return TimedLoop_STOP
endif
return TimedLoop_CONTINUE
//You are free to/should use false and true instead of the
//constants.
endmethod
implement TimedLoop //This does the module magic
static method create takes unit u returns moveUnit
local moveUnit m= moveUnit.allocate()
set m.u = u
call m.startTimedLoop() //The module works by
// creating a startTimedLoop method that will
// do all the dirty work and end up calling
// .onTimedLoop...
//
return m
endmethod
endstruct
//call moveUnit.create(GetTriggerUnit()) and see...
*/
//TESH.scrollpos=-1
//TESH.alwaysfold=0
// Ice Trap by BRUTAL.
// Requires GT, and KT2.
// Credits are welcome but not necessary.
// October 12th 2009.
//
// Modified to be Fire Barrier for Valkyrie Hero by SanKakU
// My memories are a little fuzzy of the Xena episodes but
// from what i remember, Odin's top blonde valkyrie cursed
// Gabrielle by putting her in a fire trap much like this one.
// In further research it can be found that in the tv show
// Bleach, the Captain Commander Yamamoto uses an ability
// like this.
//
// the spell now enables countering of escape spells.
scope FireBarrier initializer I
define{private ID='A00E';private IMMO_ID='A00F';private DUMMY_ID='h000';;private PATH_BLOCK='YTpb'
private PERIOD=.04;private SCALE=1. ;private ALPHA=75 ;private MAX_UNITS=36;private DISTANCE=350.}
private struct Data;unit caster, target;real x, y;real angle;integer pbInt=0, ticks, maxTicks
destructable array pathBlock[MAX_UNITS];unit array a[MAX_UNITS]
static method create takes unit u, unit z returns Data
local Data data=Data.allocate()
set data.caster=u
set data.target=z
set data.x=GetUnitX(data.target)
set data.y=GetUnitY(data.target)
set data.angle=GetUnitFacing(u)+140
set data.ticks=R2I(45*(GetUnitAbilityLevel(data.caster,ID))/PERIOD)+10
set data.maxTicks=data.ticks
return data
endmethod
method circle takes nothing returns nothing
integer level=GetUnitAbilityLevel(.caster,ID)
real dis=DISTANCE
//==AS AN ULTIMATE, THIS SPELL LASTS 45 SECONDS AT LEVEL 1, 90 SECONDS AT LEVEL 2, AND 135 SECONDS AT LEVEL 3
real x=.x+dis*Cos(.angle*bj_DEGTORAD)
real y=.y+dis*Sin(.angle*bj_DEGTORAD)
.a[.pbInt]=CreateUnit(GetOwningPlayer(.caster),DUMMY_ID,x,y,bj_RADTODEG*Atan2(y-.y,x-.x))
call SetUnitScale(.a[pbInt],SCALE,SCALE,1.75)
call SetUnitVertexColor(.a[pbInt],255,255,255,R2I(2.55*ALPHA))
call UnitAddAbility(.a[pbInt],IMMO_ID)
call SetUnitAbilityLevel(.a[pbInt],IMMO_ID,level)
call IssueImmediateOrder(.a[pbInt],"immolation")
set .angle=.angle+360./MAX_UNITS
set .pathBlock[.pbInt]=CreateDestructable(PATH_BLOCK,x,y,0,1,0)
set .pbInt=.pbInt+1
endmethod
method end takes nothing returns nothing
integer i=-1;whilenot(i++>MAX_UNITS){RemoveUnit(.a[i]);KillDestructable(.pathBlock[i])};endmethod
endstruct
private function PeriodicFunc takes nothing returns boolean
local Data data=KT_GetData()
if getdisr(data.x,data.y,data.target)>=DISTANCE then
call SetUnitX(data.target,data.x)
call SetUnitY(data.target,data.y)
endif
set data.ticks=data.ticks-1
if data.ticks<=0 then
call data.end()
return true
else
if data.ticks>=data.maxTicks-MAX_UNITS then
call data.circle()
endif
return false
endif
endfunction
private function actions takes nothing returns nothing
call KT_Add(function PeriodicFunc,Data.create(GetTriggerUnit(),GetSpellTargetUnit()),PERIOD)
endfunction
private function I takes nothing returns nothing
XE_PreloadAbility(IMMO_ID)
call TriggerAddAction(GT_RegisterStartsEffectEvent(CreateTrigger(),ID),function actions)
endfunction
endscope
scope Cloudarchmage initializer I
private function f takes nothing returns boolean
location tl = GetSpellTargetLoc();real tx = GetLocationX(tl),ty = GetLocationY(tl)
unit ut = GetTriggerUnit(),u = CreateUnit(GetOwningPlayer(ut), 'e005', tx, ty, 270.00)
integer lvl = GetUnitAbilityLevel(ut,'A008')
UnitAddAbility(u, 'Aclf')
SetUnitAbilityLevel(u, 'Aclf', lvl)
IssuePointOrder(u, "cloudoffog", tx,ty)
UnitApplyTimedLife(u, 'BTLF', 20. )
RemoveLocation(tl);tl = null;u = null;ut = null;return false;endfunction
public function I takes nothing returns nothing
effectstartGT(f,'A008')
XE_PreloadAbility('Aclf')
endfunction
endscope
scope banishher initializer I;globals;private boolean ya=false;private integer za=0;endglobals
//define private {HEROABI = 'AOsf';DUMMABI = 'AHbn';TEMPABI = 'AHpx';DUMMID='e000';PURGABI='A006'}
struct banaaa;unit x,u;integer y,z
private method onTimedLoop takes nothing returns boolean;SetUnitX(u,gux(x));SetUnitY(u,guy(x));if ya==false then;KillUnit(u);
return false;endif;y++;if y>=1280 then;KillUnit(u);return false;else;y++;if y>=1200 then;if z>0 then;return true;endif;z++;
if ya==true then;ya=false;UnitRemoveAbility(x,'A001');UnitAddAbility(x, 'AOsf');SetUnitAbilityLevel(x, 'AOsf', za);
UnitAddAbility(u, 'A006');IssueTargetOrder(u, "purge", x);endif;else;;endif;endif;
return true;endmethod;implement TimedLoop ;static method create takes unit x returns banaaa
local banaaa m= banaaa.allocate();set m.x = x;set m.y=0;set m.z=0
m.u = CreateUnit(GetOwningPlayer(m.x), 'e004', gux(m.x), guy(m.x), 270.00)
UnitRemoveAbility(m.x,'AOsf');UnitAddAbility(m.x, 'A001');UnitAddAbility(m.u, 'AHbn');SetUnitAbilityLevel(m.u, 'AHbn', za)
IssueTargetOrder(m.u, "banish", m.x);ya=true;call m.startTimedLoop() ; return m;endmethod;endstruct
private boolean e(){unit u=CreateUnit(GetOwningPlayer(gtu), 'e004', gux(gtu), guy(gtu), 270.00)
;ya=false;UnitRemoveAbility(gtu,'A001');UnitAddAbility(gtu, 'AOsf');SetUnitAbilityLevel(gtu, 'AOsf', za);
UnitAddAbility(u, 'A006');IssueTargetOrder(u, "purge", gtu);UnitApplyTimedLife(u,'BTLF',2.);return false}
private boolean g (){za++;return false};private boolean Actions (){call banaaa.create(gtu);return false}
private void I (){effectstartGT(Actions, 'AOsf');effectstartGT(e, 'A001');skillGT(g,'AOsf')
call XE_PreloadAbility('A006');call XE_PreloadAbility('AHbn');XE_PreloadAbility('AOsf');XE_PreloadAbility('A001')};endscope
//
scope projectora initializer I;struct propur;unit x,d,p,t;integer y;real a,b
private method onTimedLoop takes nothing returns boolean;b=getdis(t,p);a=b-10.0
SetDistanceToUnit(t,p,a);if b<129.0 then;KillUnit(p);KillUnit(d);
UnitDamageTarget(x,t,100.0,false,false,ATTACK_TYPE_HERO,DAMAGE_TYPE_UNIVERSAL,WEAPON_TYPE_WHOKNOWS);return false;
endif;y++;if y>=400 then;KillUnit(p);KillUnit(d);return false;endif;SetUnitX(d,gux(p));SetUnitY(d,guy(p));
UnitAddAbility(d, 'A006');IssueTargetOrder(d, "purge", p);return true
endmethod;implement TimedLoop ;static method create takes unit x, unit w returns propur
local propur m= propur.allocate();set m.t = w;m.x = x;set m.y=0;m.a=0.0;m.b=0.0
m.d = CreateUnit(GetOwningPlayer(m.x), 'e004', gux(m.x), guy(m.x), 270.00)
m.p = CreateUnit(GetOwningPlayer(m.x), 'e006', gux(m.x), guy(m.x), 270.00)
call m.startTimedLoop() ; return m;endmethod;endstruct
private boolean Actions (){call propur.create(gtu,GetSpellTargetUnit());return false}
private void I (){effectstartGT(Actions, 'A009')};endscope
//
//function SetDistanceToUnit takes unit y, unit x, real dist returns nothing
//extension to snippet.
//function getdis takes unit x, unit y returns real
//
scope SpinBash initializer Init
// Spin Bash by The_Witcher
// The Blademaster spins the target around,
// damaging all enemies in range.
// After some time he releases his target, throwing it away.
globals
// the rawcode of your ability(press CTRL + D in object editor)
private constant integer SpellID = 'A0C5'
// the interval of the timer (increase if laggy)
private constant real interval = 0.01
// the distance between the target and the caster when
// spinnig around
private constant real SpinDistance = 150
// the curve is a constant used by the parabola forumla
private constant real Curve = 1.5
// this is the range in where units have to be around the target
// to get damaged
private constant real DamageRange = 80
endglobals
private function GetRotateTime takes integer lvl returns real
local real lowest = IMaxBJ(4-lvl,0)
local real highest = lowest + 2
return GetRandomReal(lowest,highest) // on lvl 1 between 4 and 6 seconds
endfunction
private function GetThrowDistance takes integer lvl returns real
return 200.*lvl //100 on lvl 1, 200 on lvl 2,...
endfunction
private function GetRotateSpeed takes integer lvl returns real
return 4.*lvl// +4 degree every interval
endfunction
private function GetSpinDamage takes integer lvl returns real
return 30.*lvl // every unit hit by the spinning/thrown unit takes this damage (here 30/60/90)
endfunction
private function GetTargetDamage takes integer lvl returns real
return 40.*lvl // the damage the target takes (here 40/80/120)
endfunction
//-----------Don't modify anything below this line---------
private struct data
unit u
unit v
real a
real d
real s
real maxd
integer lvl
integer mode = 1
real dmg1
real dmg2
group DamageOnceGroup
endstruct
globals
private data array DATAS
private integer total = 0
private timer tim = CreateTimer()
private group g = CreateGroup()
private item ite
private player TempPlayer
private data TempDat
endglobals
private function IsCoordPathable takes real x, real y returns boolean
local real xx
local real yy
call SetItemVisible(ite,true)
call SetItemPosition(ite,x,y)
set xx = GetItemX( ite ) - x
set yy = GetItemY( ite ) - y
call SetItemVisible(ite,false)
if xx < 1 and xx > -1 and yy < 1 and yy > -1 then
return true
endif
return false
endfunction
private function JumpParabola takes real dist, real maxdist, real curve returns real
local real t = (dist * 2) / maxdist - 1
return (- t * t + 1) * (maxdist / curve)
endfunction
private function EnemiesOnly takes nothing returns boolean
return IsUnitEnemy(GetFilterUnit(),TempPlayer) and not (IsUnitType(GetFilterUnit(),UNIT_TYPE_DEAD) or GetUnitTypeId(GetFilterUnit()) == 0 )
endfunction
private function Damage takes nothing returns nothing
if not IsUnitInGroup(GetEnumUnit(),TempDat.DamageOnceGroup) then
call UnitDamageTarget(TempDat.u,GetEnumUnit(),TempDat.dmg1,true,false,ATTACK_TYPE_NORMAL, DAMAGE_TYPE_NORMAL, WEAPON_TYPE_WHOKNOWS)
call GroupAddUnit(TempDat.DamageOnceGroup,GetEnumUnit())
endif
endfunction
private function execute takes nothing returns nothing
local data dat
local integer i = 0
local real x
local real y
local unit u
loop
exitwhen i >= total
set dat = DATAS[i]
if dat.mode == 1 then
// Spinning around
set x = GetUnitX(dat.u) + SpinDistance * Cos(dat.a*bj_DEGTORAD)
set y = GetUnitY(dat.u) + SpinDistance * Sin(dat.a*bj_DEGTORAD)
call SetUnitX(dat.v,x)
call SetUnitY(dat.v,y)
call SetUnitFacing(dat.v,dat.a-180)
set dat.a = dat.a + dat.s
set dat.d = dat.d - interval
set TempPlayer = GetOwningPlayer(dat.u)
call GroupEnumUnitsInRange(g,GetUnitX(dat.v),GetUnitY(dat.v), DamageRange,Condition(function EnemiesOnly))
call GroupRemoveUnit(g,dat.v)
set TempDat = dat
call ForGroup(g, function Damage)
call GroupClear(g)
if dat.a > 360 then
set dat.a = dat.a - 360
call GroupClear(dat.DamageOnceGroup)
endif
if dat.d <= 0 then
set dat.d = 0
set dat.maxd = GetThrowDistance(dat.lvl)
set dat.mode = 2
call PauseUnit(dat.u,false)
call SetUnitAnimation(dat.u,"stand")
call GroupClear(dat.DamageOnceGroup)
endif
else
// throw
set x = GetUnitX(dat.v) + dat.s * Cos(dat.a*bj_DEGTORAD)
set y = GetUnitY(dat.v) + dat.s * Sin(dat.a*bj_DEGTORAD)
if IsCoordPathable(x,y) then
call SetUnitX(dat.v,x)
call SetUnitY(dat.v,y)
call GroupEnumUnitsInRange(g,GetUnitX(dat.v),GetUnitY(dat.v), DamageRange,Condition(function EnemiesOnly))
call GroupRemoveUnit(g,dat.v)
set TempDat = dat
call ForGroup(g, function Damage)
call GroupClear(g)
endif
set dat.d = dat.d + dat.s
if dat.mode == 3 then
call SetUnitFlyHeight(dat.v,JumpParabola(dat.d,dat.maxd,Curve),0)
else
call SetUnitFlyHeight(dat.v,JumpParabola(dat.d,dat.maxd,Curve*3),0)
endif
if dat.d >= dat.maxd and dat.mode == 2 then
set dat.d = 0
set dat.maxd = GetThrowDistance(dat.lvl) / 3
set dat.mode = 3
call GroupClear(dat.DamageOnceGroup)
call UnitDamageTarget(dat.u,dat.v,dat.dmg2,true,false,ATTACK_TYPE_NORMAL,DAMAGE_TYPE_NORMAL,WEAPON_TYPE_WHOKNOWS)
elseif dat.d >= dat.maxd and dat.mode == 3 then
call SetUnitFlyHeight(dat.v,0,0)
call PauseUnit(dat.v,false)
call SetUnitTimeScale(dat.v,1)
call DestroyGroup(dat.DamageOnceGroup)
set total = total - 1
set DATAS[i] = DATAS[total]
call dat.destroy()
set i = i - 1
endif
endif
set i = i + 1
endloop
if total == 0 then
call PauseTimer(tim)
endif
endfunction
private function start takes nothing returns boolean
local data dat
if GetSpellAbilityId() == SpellID then
set dat = data.create()
set dat.u = GetTriggerUnit()
set dat.v = GetSpellTargetUnit()
set dat.a = bj_RADTODEG * Atan2(GetUnitY(dat.v) - GetUnitY(dat.u), GetUnitX(dat.v) - GetUnitX(dat.u))
set dat.lvl = GetUnitAbilityLevel(dat.u,SpellID)
set dat.d = GetRotateTime(dat.lvl)
set dat.s = GetRotateSpeed(dat.lvl)
set dat.dmg1 = GetSpinDamage(dat.lvl)
set dat.dmg2 = GetTargetDamage(dat.lvl)
set dat.DamageOnceGroup = CreateGroup()
call IssueImmediateOrder(dat.u,"stop")
call PauseUnit(dat.u,true)
call PauseUnit(dat.v,true)
call SetUnitTimeScale(dat.v,0)
call SetUnitAnimation(dat.u,"spin")
call UnitAddAbility(dat.v,'Amrf')
call UnitRemoveAbility(dat.v,'Amrf')
set DATAS[total] = dat
set total = total + 1
if total == 1 then
call TimerStart(tim,interval,true, function execute)
endif
endif
return false
endfunction
private function Init takes nothing returns nothing
local trigger t = CreateTrigger()
call TriggerAddCondition(t,Condition(function start))
call TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_SPELL_EFFECT)
set ite = CreateItem('wolg',0,0)
call SetItemVisible(ite,false)
call SetItemInvulnerable( ite, true)
endfunction
endscope
//TESH.scrollpos=-1
//TESH.alwaysfold=0
scope killmonster initializer I
private boolean f(){BJDebugMsg("slay satyr");return false}
private void I(){GT_AddUnitDiesAction(function f, 'n000')}
endscope
//TESH.scrollpos=74
//TESH.alwaysfold=0
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//~~ GT ~~ GTrigger ~~ By Jesus4Lyf ~~ Version 1.05 ~~
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// What is GTrigger?
// - GTrigger is an event system that replaces the cumbersome WC3
// event system.
// - GTrigger only launches the necessary threads instead of x threads,
// where x is the number of times the event type occurs in the map.
//
// =Pros=
// - Instead of having 16 events (for "16" players) per use of an,
// event type, you have 0 per use and 16 total for that event type.
// - If you have 100 events of one type in your map, instead of firing
// 100 triggers each time any spell is cast, you fire only what's needed.
// - GTrigger is faster to code with, more efficient to execute, and just
// better programming practises and nicer code all round.
//
// =Cons=
// - If a trigger with a GTrigger event is destroyed, it must have its
// event unregistered first or it will leak an event (slows firing down).
// - Shouldn't use "wait" actions anywhere in the triggers.
//
// Functions:
// // General
// - GT_UnregisterTriggeringEvent()
//
// // Ability events
// - GT_RegisterStartsEffectEvent(trigger, abilityid) (returns the trigger passed in)
// - GT_RegisterBeginsChanellingEvent(trigger, abilityid) (returns the trigger passed in)
// - GT_RegisterBeginsCastingEvent(trigger, abilityid) (returns the trigger passed in)
// - GT_RegisterStopsCastingEvent(trigger, abilityid) (returns the trigger passed in)
// - GT_RegisterFinishesCastingEvent(trigger, abilityid) (returns the trigger passed in)
// - GT_RegisterLearnsAbilityEvent(trigger, abilityid) (returns the trigger passed in)
// // Order events // (can use String2OrderIdBJ("OrderString") for orderid
// - GT_RegisterTargetOrderEvent(trigger, orderid) (returns the trigger passed in)
// - GT_RegisterPointOrderEvent(trigger, orderid) (returns the trigger passed in)
// - GT_RegisterNoTargetOrderEvent(trigger, orderid) (returns the trigger passed in)
// // Item events
// - GT_RegisterItemUsedEvent(trigger, itemtypeid) (returns the trigger passed in)
// - GT_RegisterItemAcquiredEvent(trigger, itemtypeid) (returns the trigger passed in)
// - GT_RegisterItemDroppedEvent(trigger, itemtypeid) (returns the trigger passed in)
// // Unit events
// - GT_RegisterUnitDiesEvent(trigger, unittypeid) (returns the trigger passed in)
//
// // Ability Events
// - GT_UnregisterSpellEffectEvent(trigger, abilityid) (returns the trigger passed in)
// - GT_UnregisterBeginsChanellingEvent(trigger, abilityid) (returns the trigger passed in)
// - GT_UnregisterBeginsCastingEvent(trigger, abilityid) (returns the trigger passed in)
// - GT_UnregisterStopsCastingEvent(trigger, abilityid) (returns the trigger passed in)
// - GT_UnregisterFinishesCastingEvent(trigger, abilityid) (returns the trigger passed in)
// - GT_UnregisterLearnsAbilityEvent(trigger, abilityid) (returns the trigger passed in)
// // Order events // (can use String2OrderIdBJ("OrderString") for orderid
// - GT_UnregisterTargetOrderEvent(trigger, orderid) (returns the trigger passed in)
// - GT_UnregisterPointOrderEvent(trigger, orderid) (returns the trigger passed in)
// - GT_UnregisterNoTargetOrderEvent(trigger, orderid) (returns the trigger passed in)
// // Item events
// - GT_UnregisterItemUsedEvent(trigger, itemtypeid) (returns the trigger passed in)
// - GT_UnregisterItemAcquiredEvent(trigger, itemtypeid) (returns the trigger passed in)
// - GT_UnregisterItemDroppedEvent(trigger, itemtypeid) (returns the trigger passed in)
// // Unit events
// - GT_UnregisterUnitDiesEvent(trigger, unittypeid) (returns the trigger passed in)
//
// Alternative interface (not recommended):
// If you aren't familiar with how this works, you shouldn't use it.
// All funcs must return false. (That is the only reason it isn't recommended.)
// // General
// - GT_RemoveTriggeringAction() // Use this to remove actions.
// // Ability Events
// - GT_AddStartsEffectAction(func, abilityid)
// - GT_AddBeginsChanellingActon(func, abilityid)
// - GT_AddBeginsCastingAction(func, abilityid)
// - GT_AddStopsCastingAction(func, abilityid)
// - GT_AddFinishesCastingAction(func, abilityid)
// - GT_AddLearnsAbilityAction(func, abilityid)
// // Order events // (can use String2OrderIdBJ("OrderString") for orderid
// - GT_AddTargetOrderAction(func, orderid)
// - GT_AddPointOrderAction(func, orderid)
// - GT_AddNoTargetOrderAction(func, orderid)
// // Item events
// - GT_AddItemUsedAction(func, itemtypeid)
// - GT_AddItemAcquiredAction(func, itemtypeid)
// - GT_AddItemDroppedAction(func, itemtypeid)
// // Unit events
// - GT_AddUnitDiesAction(func, unittypeid)
//
// Details:
// - Due to the storage method, only 8191 GTrigger events are possible at any one time.
//
// Thanks:
// - Daxtreme: For voluntarily testing this system and the UnitDies event idea.
// - kenny!: For the Order and Learns Ability event ideas.
//
// How to import:
// - Create a trigger named GT.
// - Convert it to custom text and replace the whole trigger text with this.
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
library GT initializer Init
//////////////
// Pointers //
////////////////////////////////////////////////////////////////////////////
// Assigned to abilities, and point to trigger grouping linked lists.
//
// Use:
// GetPointer --> int (pointer)
// FreePointer(int (pointer))
// set PointerTarget[int (pointer)]=int (list link)
// PointerTarget[int (pointer)] --> int (list link)
globals
// Pointer
private integer array PointerTarget
private integer PointerMax=0
// Spare Pointer Stack
private integer array NextPointer
private integer NextPointerMaxPlusOne=1
endglobals
private function GetPointer takes nothing returns integer
if NextPointerMaxPlusOne==1 then
set PointerMax=PointerMax+1
return PointerMax
endif
set NextPointerMaxPlusOne=NextPointerMaxPlusOne-1
return NextPointer[NextPointerMaxPlusOne]
endfunction
private function FreePointer takes integer pointer returns nothing
set PointerTarget[pointer]=0
set NextPointer[NextPointerMaxPlusOne]=pointer
set NextPointerMaxPlusOne=NextPointerMaxPlusOne+1
endfunction
///////////////////////////////////
// Trigger Grouping Linked Lists //
////////////////////////////////////////////////////////////////////////////
// Contains a chain of triggers to be executed together.
//
// Use:
// GetMem() --> int (mem)
// FreeMem(int (mem))
// Link(int (pointer), int (mem))
// Unlink(int (pointer), int (mem))
globals
// Spare Link Stack
private integer array NextMem
private integer NextMemMaxPlusOne=1
// Linked list
private trigger array Trig
private integer array Next
private integer array Prev
private integer TrigMax=0
endglobals
private function GetMem takes nothing returns integer
if NextMemMaxPlusOne==1 then
set TrigMax=TrigMax+1
return TrigMax
endif
set NextMemMaxPlusOne=NextMemMaxPlusOne-1
return NextMem[NextMemMaxPlusOne]
endfunction
private function FreeMem takes integer i returns nothing
set Trig[i]=null
set NextMem[NextMemMaxPlusOne]=i
set NextMemMaxPlusOne=NextMemMaxPlusOne+1
endfunction
// Linked list functionality
// NOTE: This means "Next" must be loaded BEFORE executing the trigger, which could delete the current link.
private function Link takes integer pointer, integer new returns nothing
set Prev[new]=0
set Next[new]=PointerTarget[pointer]
set Prev[PointerTarget[pointer]]=new
set PointerTarget[pointer]=new
endfunction
private function Unlink takes integer pointer, integer rem returns nothing
if Prev[rem]==0 then
set PointerTarget[pointer]=Next[rem]
set Prev[Next[rem]]=0
endif
set Next[Prev[rem]]=Next[rem]
set Prev[Next[rem]]=Prev[rem]
endfunction
//////////////////////
// GTrigger General //
////////////////////////////////////////////////////////////////////////////
// Only contains the UnregisterTriggeringEvent action for public use.
globals
boolean UnregisterLastEvent=false
endglobals
public function UnregisterTriggeringEvent takes nothing returns nothing
set UnregisterLastEvent=true
endfunction
/////////////////////////////////////
// GTrigger Ability Implementation //
////////////////////////////////////////////////////////////////////////////
// The nasty textmacro implementation of special "All Players" events.
//! textmacro SetupSpecialAllPlayersEvent takes NAME, EVENT, GETSPECIAL
globals
private trigger $NAME$Trigger=CreateTrigger()
// Extendable arrays
private integer array $NAME$AbilityIdA
private integer array $NAME$ListPointerA
private integer array $NAME$AbilityIdB
private integer array $NAME$ListPointerB
private integer array $NAME$AbilityIdC
private integer array $NAME$ListPointerC
private integer array $NAME$AbilityIdD
private integer array $NAME$ListPointerD
private integer array $NAME$AbilityIdE
private integer array $NAME$ListPointerE
endglobals
globals//locals
private integer GetOrCreateListPointer$NAME$AbilHashed
endglobals
private function GetOrCreate$NAME$ListPointer takes integer abil returns integer
set GetOrCreateListPointer$NAME$AbilHashed=abil-(abil/8191)*8191
if $NAME$AbilityIdA[GetOrCreateListPointer$NAME$AbilHashed]==abil then // Correct
return $NAME$ListPointerA[GetOrCreateListPointer$NAME$AbilHashed]
elseif $NAME$AbilityIdA[GetOrCreateListPointer$NAME$AbilHashed]<1 then // Empty
set $NAME$AbilityIdA[GetOrCreateListPointer$NAME$AbilHashed]=abil
set $NAME$ListPointerA[GetOrCreateListPointer$NAME$AbilHashed]=GetPointer()
return $NAME$ListPointerA[GetOrCreateListPointer$NAME$AbilHashed]
endif
if $NAME$AbilityIdB[GetOrCreateListPointer$NAME$AbilHashed]==abil then // Correct
return $NAME$ListPointerB[GetOrCreateListPointer$NAME$AbilHashed]
elseif $NAME$AbilityIdB[GetOrCreateListPointer$NAME$AbilHashed]<1 then // Empty
set $NAME$AbilityIdB[GetOrCreateListPointer$NAME$AbilHashed]=abil
set $NAME$ListPointerB[GetOrCreateListPointer$NAME$AbilHashed]=GetPointer()
return $NAME$ListPointerB[GetOrCreateListPointer$NAME$AbilHashed]
endif
if $NAME$AbilityIdC[GetOrCreateListPointer$NAME$AbilHashed]==abil then // Correct
return $NAME$ListPointerC[GetOrCreateListPointer$NAME$AbilHashed]
elseif $NAME$AbilityIdC[GetOrCreateListPointer$NAME$AbilHashed]<1 then // Empty
set $NAME$AbilityIdC[GetOrCreateListPointer$NAME$AbilHashed]=abil
set $NAME$ListPointerC[GetOrCreateListPointer$NAME$AbilHashed]=GetPointer()
return $NAME$ListPointerC[GetOrCreateListPointer$NAME$AbilHashed]
endif
if $NAME$AbilityIdD[GetOrCreateListPointer$NAME$AbilHashed]==abil then // Correct
return $NAME$ListPointerD[GetOrCreateListPointer$NAME$AbilHashed]
elseif $NAME$AbilityIdD[GetOrCreateListPointer$NAME$AbilHashed]<1 then // Empty
set $NAME$AbilityIdD[GetOrCreateListPointer$NAME$AbilHashed]=abil
set $NAME$ListPointerD[GetOrCreateListPointer$NAME$AbilHashed]=GetPointer()
return $NAME$ListPointerD[GetOrCreateListPointer$NAME$AbilHashed]
endif
if $NAME$AbilityIdE[GetOrCreateListPointer$NAME$AbilHashed]==abil then // Correct
return $NAME$ListPointerE[GetOrCreateListPointer$NAME$AbilHashed]
elseif $NAME$AbilityIdE[GetOrCreateListPointer$NAME$AbilHashed]<1 then // Empty
set $NAME$AbilityIdE[GetOrCreateListPointer$NAME$AbilHashed]=abil
set $NAME$ListPointerE[GetOrCreateListPointer$NAME$AbilHashed]=GetPointer()
return $NAME$ListPointerE[GetOrCreateListPointer$NAME$AbilHashed]
endif
call BJDebugMsg("GTrigger Error: Ran out of storage locations for pointers on object "+GetObjectName(abil)+"!")
set PointerTarget[0]=0
return 0
endfunction
globals//locals
private integer GetListPointer$NAME$AbilHashed
endglobals
private function Get$NAME$ListPointer takes integer abil returns integer
set GetListPointer$NAME$AbilHashed=abil-(abil/8191)*8191
if $NAME$AbilityIdA[GetListPointer$NAME$AbilHashed]==abil then // Correct
return $NAME$ListPointerA[GetListPointer$NAME$AbilHashed]
elseif $NAME$AbilityIdA[GetListPointer$NAME$AbilHashed]<1 then // Empty
set PointerTarget[0]=0 // Make sure.
return 0
endif
if $NAME$AbilityIdB[GetListPointer$NAME$AbilHashed]==abil then // Correct
return $NAME$ListPointerB[GetListPointer$NAME$AbilHashed]
elseif $NAME$AbilityIdB[GetListPointer$NAME$AbilHashed]<1 then // Empty
set PointerTarget[0]=0 // Make sure.
return 0
endif
if $NAME$AbilityIdC[GetListPointer$NAME$AbilHashed]==abil then // Correct
return $NAME$ListPointerC[GetListPointer$NAME$AbilHashed]
elseif $NAME$AbilityIdC[GetListPointer$NAME$AbilHashed]<1 then // Empty
set PointerTarget[0]=0 // Make sure.
return 0
endif
if $NAME$AbilityIdD[GetListPointer$NAME$AbilHashed]==abil then // Correct
return $NAME$ListPointerD[GetListPointer$NAME$AbilHashed]
elseif $NAME$AbilityIdD[GetListPointer$NAME$AbilHashed]<1 then // Empty
set PointerTarget[0]=0 // Make sure.
return 0
endif
if $NAME$AbilityIdE[GetListPointer$NAME$AbilHashed]==abil then // Correct
return $NAME$ListPointerE[GetListPointer$NAME$AbilHashed]
elseif $NAME$AbilityIdE[GetListPointer$NAME$AbilHashed]<1 then // Empty
set PointerTarget[0]=0 // Make sure.
return 0
endif
call BJDebugMsg("GTrigger Error: Ran out of storage locations for pointers at ability "+GetObjectName(abil)+"!")
set PointerTarget[0]=0
return 0
endfunction
globals//locals
private integer Register$NAME$Mem
endglobals
public function Register$NAME$Event takes trigger t, integer abil returns trigger
set Register$NAME$Mem=GetMem()
set Trig[Register$NAME$Mem]=t
call Link(GetOrCreate$NAME$ListPointer(abil),Register$NAME$Mem)
return t
endfunction
globals//locals
private integer Unregister$NAME$Pointer
private integer Unregister$NAME$Mem
endglobals
public function Unregister$NAME$Event takes trigger t, integer abil returns trigger
set Unregister$NAME$Pointer=Get$NAME$ListPointer(abil)
set Unregister$NAME$Mem=PointerTarget[Unregister$NAME$Pointer]
loop
exitwhen Trig[Unregister$NAME$Mem]==t
if Unregister$NAME$Mem==0 then
return t // Not found.
endif
set Unregister$NAME$Mem=Next[Unregister$NAME$Mem]
endloop
call Unlink(Unregister$NAME$Pointer,Unregister$NAME$Mem)
call FreeMem(Unregister$NAME$Mem)
return t
endfunction
private function Trigger$NAME$Event takes nothing returns boolean
local integer Trigger$NAME$Pointer=Get$NAME$ListPointer($GETSPECIAL$)
local integer Trigger$NAME$Mem=PointerTarget[Trigger$NAME$Pointer]
local integer Trigger$NAME$NextMem
set UnregisterLastEvent=false
loop
exitwhen Trigger$NAME$Mem<1
set Trigger$NAME$NextMem=Next[Trigger$NAME$Mem]
if TriggerEvaluate(Trig[Trigger$NAME$Mem]) then
call TriggerExecute(Trig[Trigger$NAME$Mem])
endif
if UnregisterLastEvent then
set UnregisterLastEvent=false
call Unlink(Trigger$NAME$Pointer,Trigger$NAME$Mem)
call FreeMem(Trigger$NAME$Mem)
endif
set Trigger$NAME$Mem=Trigger$NAME$NextMem
endloop
return false
endfunction
private function Init$NAME$ takes nothing returns nothing
local integer i=bj_MAX_PLAYER_SLOTS
call TriggerAddCondition($NAME$Trigger,Condition(function Trigger$NAME$Event))
loop
set i=i-1
call TriggerRegisterPlayerUnitEvent($NAME$Trigger,Player(i),EVENT_PLAYER_$EVENT$,null)
exitwhen i==0
endloop
endfunction
//! endtextmacro
//! runtextmacro SetupSpecialAllPlayersEvent("StartsEffect", "UNIT_SPELL_EFFECT", "GetSpellAbilityId()")
//! runtextmacro SetupSpecialAllPlayersEvent("BeginsChanelling", "UNIT_SPELL_CHANNEL", "GetSpellAbilityId()")
//! runtextmacro SetupSpecialAllPlayersEvent("BeginsCasting", "UNIT_SPELL_CAST", "GetSpellAbilityId()")
//! runtextmacro SetupSpecialAllPlayersEvent("StopsCasting", "UNIT_SPELL_ENDCAST", "GetSpellAbilityId()")
//! runtextmacro SetupSpecialAllPlayersEvent("FinishesCasting", "UNIT_SPELL_FINISH", "GetSpellAbilityId()")
//! runtextmacro SetupSpecialAllPlayersEvent("TargetOrder", "UNIT_ISSUED_TARGET_ORDER", "GetIssuedOrderId()")
//! runtextmacro SetupSpecialAllPlayersEvent("PointOrder", "UNIT_ISSUED_POINT_ORDER", "GetIssuedOrderId()")
//! runtextmacro SetupSpecialAllPlayersEvent("NoTargetOrder", "UNIT_ISSUED_ORDER", "GetIssuedOrderId()")
//! runtextmacro SetupSpecialAllPlayersEvent("ItemUsed", "UNIT_USE_ITEM", "GetItemTypeId(GetManipulatedItem())")
//! runtextmacro SetupSpecialAllPlayersEvent("ItemAcquired", "UNIT_PICKUP_ITEM", "GetItemTypeId(GetManipulatedItem())")
//! runtextmacro SetupSpecialAllPlayersEvent("ItemDropped", "UNIT_DROP_ITEM", "GetItemTypeId(GetManipulatedItem())")
//! runtextmacro SetupSpecialAllPlayersEvent("UnitDies", "UNIT_DEATH", "GetUnitTypeId(GetTriggerUnit())")
//! runtextmacro SetupSpecialAllPlayersEvent("LearnsAbility", "HERO_SKILL", "GetLearnedSkill()")
// Note to self: Remember to update the Init function.
/////////////////////////////////////////
// GTrigger All Players Implementation //
////////////////////////////////////////////////////////////////////////////
// The textmacro implementation of other "All Players" events.
//! textmacro SetupAllPlayersEvent takes NAME, EVENT
globals
private trigger $NAME$Trigger=CreateTrigger()
private integer $NAME$ListPointer=0
endglobals
globals//locals
private integer Register$NAME$Mem
endglobals
public function Register$NAME$Event takes trigger t returns trigger
set Register$NAME$Mem=GetMem()
set Trig[Register$NAME$Mem]=t
call Link($NAME$ListPointer,Register$NAME$Mem)
return t
endfunction
globals//locals
private integer Unregister$NAME$Pointer
private integer Unregister$NAME$Mem
endglobals
public function Unregister$NAME$Event takes trigger t returns trigger
set Unregister$NAME$Mem=PointerTarget[$NAME$ListPointer]
loop
exitwhen Trig[Unregister$NAME$Mem]==t
if Unregister$NAME$Mem==0 then
return t // Not found.
endif
set Unregister$NAME$Mem=Next[Unregister$NAME$Mem]
endloop
call Unlink($NAME$ListPointer,Unregister$NAME$Mem)
call FreeMem(Unregister$NAME$Mem)
return t
endfunction
private function Trigger$NAME$Event takes nothing returns boolean
local integer Trigger$NAME$Mem=PointerTarget[$NAME$ListPointer]
local integer Trigger$NAME$NextMem
set UnregisterLastEvent=false
loop
exitwhen Trigger$NAME$Mem<1
set Trigger$NAME$NextMem=Next[Trigger$NAME$Mem]
if TriggerEvaluate(Trig[Trigger$NAME$Mem]) then
call TriggerExecute(Trig[Trigger$NAME$Mem])
endif
if UnregisterLastEvent then
set UnregisterLastEvent=false
call Unlink($NAME$ListPointer,Trigger$NAME$Mem)
call FreeMem(Trigger$NAME$Mem)
endif
set Trigger$NAME$Mem=Trigger$NAME$NextMem
endloop
return false
endfunction
private function Init$NAME$ takes nothing returns nothing
local integer i=bj_MAX_PLAYER_SLOTS
call TriggerAddCondition($NAME$Trigger,Condition(function Trigger$NAME$Event))
loop
set i=i-1
call TriggerRegisterPlayerUnitEvent($NAME$Trigger,Player(i),EVENT_PLAYER_UNIT_$EVENT$,null)
exitwhen i==0
endloop
// Initialise the pointer.
set $NAME$ListPointer=GetPointer()
endfunction
//! endtextmacro
// Old: //! runtextmacro SetupAllPlayersEvent("AnyUnitDies", "DEATH")
private function Init takes nothing returns nothing
// Ability events
call InitStartsEffect()
call InitBeginsChanelling()
call InitBeginsCasting()
call InitStopsCasting()
call InitFinishesCasting()
call InitLearnsAbility()
// Order events
call InitTargetOrder()
call InitPointOrder()
call InitNoTargetOrder()
// Item events
call InitItemUsed()
call InitItemAcquired()
call InitItemDropped()
// Unit events
call InitUnitDies()
endfunction
//////////////
// Wrappers //
////////////////////////////////////////////////////////////////////////////
// Wraps it up, for those who really want this interface.
// General
public function RemoveTriggeringAction takes nothing returns nothing
call UnregisterTriggeringEvent()
call DestroyTrigger(GetTriggeringTrigger())
endfunction
// Special All Player Events
//! textmacro AddSpecialAllPlayersWrapper takes EVENT
public function Add$EVENT$Action takes code func, integer special returns nothing
call TriggerAddCondition(Register$EVENT$Event(CreateTrigger(),special),Condition(func))
endfunction
//! endtextmacro
//! runtextmacro AddSpecialAllPlayersWrapper("StartsEffect")
//! runtextmacro AddSpecialAllPlayersWrapper("BeginsChanelling")
//! runtextmacro AddSpecialAllPlayersWrapper("BeginsCasting")
//! runtextmacro AddSpecialAllPlayersWrapper("StopsCasting")
//! runtextmacro AddSpecialAllPlayersWrapper("FinishesCasting")
//! runtextmacro AddSpecialAllPlayersWrapper("TargetOrder")
//! runtextmacro AddSpecialAllPlayersWrapper("PointOrder")
//! runtextmacro AddSpecialAllPlayersWrapper("NoTargetOrder")
//! runtextmacro AddSpecialAllPlayersWrapper("ItemUsed")
//! runtextmacro AddSpecialAllPlayersWrapper("ItemAcquired")
//! runtextmacro AddSpecialAllPlayersWrapper("ItemDropped")
//! runtextmacro AddSpecialAllPlayersWrapper("UnitDies")
//! runtextmacro AddSpecialAllPlayersWrapper("LearnsAbility")
// Note to self: Remember to update the Init function.
// All Player Events
//! textmacro AddAllPlayersWrapper takes EVENT
public function Add$EVENT$Action takes code func returns nothing
call TriggerAddCondition(Register$EVENT$Event(CreateTrigger()),Condition(func))
endfunction
//! endtextmacro
//// runtextmacro AddAllPlayersWrapper("UnitSell")
// Old: //! runtextmacro AddAllPlayersWrapper("AnyUnitDies")
endlibrary
//TESH.scrollpos=-1
//TESH.alwaysfold=0
library MSGS{define{c_goldbrown="daa520";c_gold="ffd700";c_palegreen="7aff7c";c_pink="db7093";c_pretty="ee82ee"
c_grassgreen="7fff00";c_brightgreen="00ff00";c_brightblue="0000ff";c_aqua="00ffff";c_white="f0ffff"
c_black="000000";c_seagreen="7fffd4";c_twilightblur="20b2aa";c_twilightgreen="009966"
c_coralgreen="00fa9a";c_coralpink="f08080";c_coralblue="33aaff";c_plum="ba55d3";c_indigo="4169e1"
c_flowerpurple="aaaaff";c_green="32cd32";c_blue="4169e1";c_yellowgreen="adff2f";c_brightpurple="ff00ff"
c_paledenim="c3dbff";c_orange="ffa500";c_purple="da70d6";c_royalpurple="aa00ff";c_flowerred="ee2222"
c_grey="c0c0c0";c_gray="c0c0c0";c_redorange="ff4500";c_red="ff0000";c_spanishgreen="9acd32"
c_darkspanishgreen="6b8e23";c_brown ="995500";c_yellow="ffff00";c_sand="ffffcc";private low=1;private high=5
}//if I2R(d.orb/2)==I2R(d.orb)/I2R(2) then
string gtc(string x, string y){return "|cff"+x+y+"|r"}//colors text
string msggetcolor(integer x, string y){if x==0 then;return gtc(c_red,y);elseif x==1 then
return gtc(c_brightblue,y);elseif x==2 then;return gtc(c_aqua,y);elseif x==3 then
return gtc(c_purple,y);elseif x==4 then;return gtc(c_yellow,y);elseif x==5 then
return gtc(c_orange,y);else;return gtc(c_white,y);endif;return y}//colors text based on player color
integer GetPlayerTeamMSGS(integer playerid){if playerid==0 then;return 1;elseif playerid==1 then
return 2;elseif playerid==3 then;return 1;elseif playerid>high then;return 3;endif;return 2}//player number for team number//customize as needed
nothing msgally(real x, player y, string z, boolean ally){integer a=GetPlayerId(y),b=GetPlayerTeamMSGS(a)
if ally then;a=low;whilenot(a++>high){if GetPlayerTeamMSGS(a)==b then;timedmsg(Player(a),0,0,x,z);endif}
else;a=low;whilenot(a++>high){if GetPlayerTeamMSGS(a)!=b then;timedmsg(Player(a),0,0,x,z);endif}endif}
nothing msgallyo(real x, player y, string z){integer a=GetPlayerId(y),b=GetPlayerTeamMSGS(a)
a=low;whilenot(a++>high){if GetPlayerTeamMSGS(a)==b then;timedmsg(Player(a),0,0,x,z);endif}}
nothing msgenemyno(real x, player y, string z){integer a=GetPlayerId(y),b=GetPlayerTeamMSGS(a)
a=low;whilenot(a++>high){if GetPlayerTeamMSGS(a)!=b then;timedmsg(Player(a),0,0,x,z);endif}}
nothing msgall(real dur, string s){integer i=low;whilenot (i++>high){timedmsg(Player(i),0,0,dur,s)}}//standard text
nothing msgone(real dur, player p, string s, boolean one){integer i=low,ip=GetPlayerId(p);if one then
timedmsg(p,0,0,dur,(s));else;whilenot(i++>high){if i!=ip then;timedmsg(Player(i),0,0,dur,s);endif}endif}}
//TESH.scrollpos=-1
//TESH.alwaysfold=0
library DebugS uses MSGS
define{gdx=GetDestructableX;gdy=GetDestructableY;gux=GetUnitX;guy=GetUnitY;gix=GetItemX;giy=GetItemY
gtu=GetTriggerUnit()}
void gos(string order, integer orderid){msgall(5,order);msgall(5,I2S(orderid))}
string sxyi(item x){return R2S(gix(x))+","+R2S(giy(x))};string sxyu(unit x){return R2S(gux(x))+","+R2S(guy(x))}
string sxyd(destructable x){return R2S(gdx(x))+","+R2S(gdy(x))}
boolean cxyi(item x, real x1, real y1, real x2, real y2)
{if gix(x) > x1 and gix(x) < x2 and giy(x) > y1 and giy(x) < y2 then;return true;endif;return false}
boolean cxyu(unit x, real x1, real y1, real x2, real y2)
{if gux(x) > x1 and gux(x) < x2 and guy(x) > y1 and guy(x) < y2 then;return true;endif;return false}
boolean cxyd(destructable x, real x1, real y1, real x2, real y2)
{if gdx(x) > x1 and gdx(x) < x2 and gdy(x) > y1 and gdy(x) < y2 then;return true;endif;return false};endlibrary
scope GetOrderStringForUnit initializer I
define {feedgos=OrderId2String(GetIssuedOrderId()),GetIssuedOrderId()
unitgos=GetUnitTypeId(GetOrderedUnit());private UNITID='U004'}
private boolean f(){if unitgos==UNITID then;msgall(5,"target order:");gos(feedgos);endif;return false}
private boolean g(){if unitgos==UNITID then;msgall(5,"point order:");gos(feedgos);msgall(5,R2S(GetOrderPointX())+\
R2S(GetOrderPointY()));endif;return false}
private boolean h(){if unitgos==UNITID then;msgall(5,"order:");gos(feedgos);endif;return false}
private void I(){trigger t=CreateTrigger();TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER);TriggerAddCondition( t,Condition( function f));set t = CreateTrigger( )
TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER );TriggerAddCondition( t,Condition( function g));set t = CreateTrigger( )
TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_ISSUED_ORDER );TriggerAddCondition( t,Condition( function h))}
endscope
//TESH.scrollpos=-1
//TESH.alwaysfold=0
library Sorter
//===========================================================================
// Information:
//==============
//
// SortUtils allows you sort arrays of reals, units and structs efficiently.
// Many sorting methods were benchmarked, and a QuickSort/InsertionSort hybrid
// was found to be the most efficient for general purposes. You can sort lists
// that are several hundred values long without any issues.
//
//===========================================================================
// How to use SortUtils dynamic arrays:
//======================================
//
// JASS does not natively support dynamic arrays or array pointers. vJASS
// supports them, but this library does not use those. It uses customized ones
// that behave a bit differently. How to use them is shown below:
//
// local RealArray ra = RealArray.create(size)
// local UnitArray ua = UnitArray.create(size)
// local StructArray sa = StructArray.create(size)
//
// When creating an array, you must specify the size you intend to utilize.
// For example, if ra[0...99] were utilized, the size should be set to 100. The
// size can go as high as MaxArraySize. When you sort an array, only the indexes
// within 0...size - 1 will be sorted, so make sure it is accurate. If you want
// to alter the size after creating an array, you can do so using the .size var-
// iable. For example: set ra.size = ra.size + 1
//
// You must use .destroy() on your arrays as well, or you will quickly run
// out of instances. You can have (8190 / MaxArraySize) simultaneous instances
// of each array type. When you use .destroy(), all the values up to .size in
// the array will be zeroed or nulled, so you don't need to worry about non-
// zero values in newly created arrays, or reference leaks with UnitArrays.
//
//===========================================================================
// How to use SortUtils:
//======================
//
// SortUtils includes the standard capability to sort a RealArray from least
// to greatest. (If you want greatest to least, just read the array backwards
// starting from index .size - 1). You can also sort a UnitArray or StructArray
// according to a parallel RealArray. This is easy to understand with an example:
//
// Before sorting:
// UnitArray: [Paladin][Archmage][Rifleman][Footman]
// RealArray: [ 500.0 ][ 1750.0 ][ 1250.0 ][ 250.0 ]
//
// After sorting:
// UnitArray: [Footman][Paladin][Rifleman][Archmage]
// RealArray: [ 250.0 ][ 500.0 ][ 1250.0 ][ 1750.0 ]
//
// As the values contained in the RealArray were sorted from least to greatest,
// the same swaps were made on the values in the UnitArray. The result is that the
// UnitArray is ordered as well. The values in the RealArray may represent anything,
// such as the distance to a specific point, the current life of a unit, "threat
// values" in an aggro system, etc.
//
// However, there is an easier way to sort UnitArrays/StructArrays than using
// a parallel RealArray, and that is with a SortFunc. A SortFunc takes a unit or
// struct and returns a real. You can use these functions to specify whatever
// criteria you wish to use to sort a UnitArray or StructArray by. Example:
//
// function UnitDistance takes unit u returns real
// return SquareRoot(Pow(GetUnitX(u) - PointX, 2.) + Pow(GetUnitY(u) - PointY, 2.))
// endfunction
//
// function Example takes nothing returns nothing
// local UnitArray ua = UnitArray.create(4)
// local RealArray ra
// local integer i
// set ua[0] = Paladin
// set ua[1] = Archmage
// set ua[2] = Rifleman
// set ua[3] = Footman
// set PointX = -423.0 //Transfering data with globals, just like a ForGroup.
// set PointY = 624.0
// call SortUnits(ua, UnitDistance) //The units are now sorted according to their
// //respective distances from PointX, PointY.
// set ra = GetSortedValues() //This will return a sorted RealArray containing the
// //distances calculated for each of the units.
// set i = ra.size - 1 //Reading through the lists backwards- farthest to nearest.
// loop
// exitwhen i < 0
// call BJDebugMsg(GetUnitName(ua[i])+" is "+R2S(ra[i])+" distance away.")
// set i = i - 1
// endloop
// endfunction
//
//===========================================================================
// SortUtils API:
//================
//
// RealArray.create(size) -> RealArray
// Create a dynamic real array.
//
// UnitArray.create(size) -> UnitArray
// Create a dynamic unit array.
//
// StructArray.create(size) -> StructArray
// Create a dynamic struct (integer) array. You should not mix different
// types of structs within the same StructArray.
//
// .size -> integer
// You can alter the size of a dynamic array after creating it. Only indexes
// up to .size - 1 will be sorted.
//
// .destroy()
// You must destroy dynamic arrays when you're done with them. Indexes up to
// .size - 1 will be automatically zeroed or nulled.
//
// SortValues(RealArray)
// Sort the values contained in a RealArray from least to greatest. Read it
// backwards starting from index .size - 1 if you want greatest to least.
//
// SortUnitsByValues(UnitArray, RealArray)
// Sorts the values contained in a UnitArray according to the values contained
// in a parallel RealArray. As the RealArray is sorted, the same swaps are
// performed on the UnitArray.
//
// SortStructsByValues(StructArray, RealArray)
// Same as above, but sorts a StructArray rather than a UnitArray.
//
// SortUnits(UnitArray, SortUnitFunc)
// Sorts a UnitArray by whatever criteria is specified in the SortUnitFunc.
// A SortUnitFunc must take a unit and return a real.
//
// SortStructs(StructArray, SortStructFunc)
// Sorts a StructArray by whatever criteria is specified in the SortStructFunc.
// A SortStructFunc must take the same kind of struct stored in the StructArray
// and return a real.
//
// GetSortedValues() -> RealArray
// After using SortUnits or SortStructs, this function will return a RealArray
// containing the ordered values that the units or structs were sorted by.
//
// Group2UnitArray(group) -> UnitArray
// This will convert a group into a UnitArray. The group remains unchanged. If
// you want to sort the contents of a unitgroup, use this to convert it to a
// UnitArray, which you can then use other SortUtils functions to sort.
//
///===========================================================================
// SortUtils textmacro:
//======================
//
// If you intend to use SortUtils with a library that makes heavy use of
// dynamic arrays and you are worried about instance limits, a special text-
// macro has been provided to solve that problem. If you use this textmacro
// within a scope or library, the RealArrays/UnitArrays/StructArrays used
// within that scope or library will magically have their own instance limit.
// However, arrays created within the scope or library cannot be used outside
// of it.
//
// The textmacro must be placed above any calls to the SortUtils API.
// Also, you will be required to declare a private constant integer named
// MaxArraySize within that scope or library, which will be used within it
// rather than the MaxArraySize defined within SortUtils.
//
// To use the textmacro, type: //! runtextmacro SortUtils("private")
//
///===========================================================================
// Configuration:
//================
globals
public constant integer InsertionSortThreshold = 16
//Sublists with size <= InsertionSortThreshold will be sorted with
//InsertionSort rather than QuickSort. Benchmarking showed that values
//between 10 and 20 produced the greatest speed-up.
private constant integer MaxArraySize = 200
//The higher this is set, the fewer instances will be available of each
//dynamic array type. Setting it higher than 500 is probably useless
//since sorting lists that large may hit the op-limit.
endglobals
//===========================================================================
function interface SortUnitFunc takes unit u returns real
function interface SortStructFunc takes integer i returns real
///! runtextmacro SortUtils("")
//! textmacro SortUtils takes SCOPE
$SCOPE$ struct RealArray
private real array values[MaxArraySize]
integer size = 0
static method create takes integer size returns RealArray
local RealArray this
if size > MaxArraySize then
call BJDebugMsg("SortUtils error: Attempted to create RealArray with size larger than "+I2S(MaxArraySize)+".")
return 0
endif
set this = .allocate()
set .size = size
return this
endmethod
method operator []= takes integer i, real value returns nothing
set .values[i] = value
endmethod
method operator [] takes integer i returns real
return .values[i]
endmethod
method onDestroy takes nothing returns nothing
local integer i = .size - 1
loop
exitwhen i < 0
set .values[i] = 0.
set i = i - 1
endloop
set .size = 0
endmethod
endstruct
$SCOPE$ struct UnitArray
private unit array values[MaxArraySize]
integer size = 0
static method create takes integer size returns UnitArray
local UnitArray this
if size > MaxArraySize then
call BJDebugMsg("SortUtils error: Attempted to create UnitArray with size larger than "+I2S(MaxArraySize)+".")
return 0
endif
set this = .allocate()
set .size = size
return this
endmethod
method operator []= takes integer i, unit value returns nothing
set .values[i] = value
endmethod
method operator [] takes integer i returns unit
return .values[i]
endmethod
method onDestroy takes nothing returns nothing
local integer i = .size - 1
loop
exitwhen i < 0
set .values[i] = null
set i = i - 1
endloop
set .size = 0
endmethod
endstruct
$SCOPE$ struct StructArray
private integer array values[MaxArraySize]
integer size = 0
static method create takes integer size returns StructArray
local StructArray this
if size > MaxArraySize then
call BJDebugMsg("SortUtils error: Attempted to create StructArray with size larger than "+I2S(MaxArraySize)+".")
return 0
endif
set this = .allocate()
set .size = size
return this
endmethod
method operator []= takes integer i, integer value returns nothing
set .values[i] = value
endmethod
method operator [] takes integer i returns integer
return .values[i]
endmethod
method onDestroy takes nothing returns nothing
local integer i = .size - 1
loop
exitwhen i < 0
set .values[i] = 0
set i = i - 1
endloop
set .size = 0
endmethod
endstruct
//===========================================================================
private function SortValues_Sub takes RealArray values, integer left, integer right returns nothing
local integer pivot = GetRandomInt(left, right)
local real value = values[pivot]
local integer i = left
local integer j = left
local real swap
set values[pivot] = values[right]
set values[right] = value
loop
exitwhen j >= right
if values[j] < value then
set swap = values[j]
set values[j] = values[i]
set values[i] = swap
set i = i + 1
endif
set j = j + 1
endloop
set values[right] = values[i]
set values[i] = value
if right - (i + 1) > SortUtils_InsertionSortThreshold then
call SortValues_Sub(values, i + 1, right)
endif
if (i - 1) - left > SortUtils_InsertionSortThreshold then
call SortValues_Sub(values, left, i - 1)
endif
endfunction
$SCOPE$ function SortValues takes RealArray values returns nothing
local integer left = 1
local integer right = values.size - 1
local real value
local integer j
call SortValues_Sub(values, 0, right)
loop
exitwhen left > right
set value = values[left]
set j = left - 1
loop
exitwhen j < 0 or values[j] < value
set values[j + 1] = values[j]
set j = j - 1
endloop
set values[j + 1] = value
set left = left + 1
endloop
endfunction
//===========================================================================
private function SortUnitsByValues_Sub takes UnitArray units, RealArray values, integer left, integer right returns nothing
local integer pivot = GetRandomInt(left, right)
local real value = values[pivot]
local unit u = units[pivot]
local integer i = left
local integer j = left
local real swap
local unit swapu
set values[pivot] = values[right]
set units[pivot] = units[right]
set values[right] = value
set units[right] = u
loop
exitwhen j >= right
if values[j] < value then
set swap = values[j]
set swapu = units[j]
set values[j] = values[i]
set units[j] = units[i]
set values[i] = swap
set units[i] = swapu
set i = i + 1
endif
set j = j + 1
endloop
set values[right] = values[i]
set units[right] = units[i]
set values[i] = value
set units[i] = u
if right - (i + 1) > SortUtils_InsertionSortThreshold then
call SortUnitsByValues_Sub(units, values, i + 1, right)
endif
if (i - 1) - left > SortUtils_InsertionSortThreshold then
call SortUnitsByValues_Sub(units, values, left, i - 1)
endif
set u = null
set swapu = null
endfunction
$SCOPE$ function SortUnitsByValues takes UnitArray units, RealArray values returns nothing
local integer left = 1
local integer right = values.size - 1
local real value
local unit u
local integer j
call SortUnitsByValues_Sub(units, values, 0, right)
loop
exitwhen left > right
set value = values[left]
set u = units[left]
set j = left - 1
loop
exitwhen j < 0 or values[j] < value
set values[j + 1] = values[j]
set units[j + 1] = units[j]
set j = j - 1
endloop
set values[j + 1] = value
set units[j + 1] = u
set left = left + 1
endloop
endfunction
//===========================================================================
private function SortStructsByValues_Sub takes StructArray structs, RealArray values, integer left, integer right returns nothing
local integer pivot = GetRandomInt(left, right)
local real value = values[pivot]
local integer s = structs[pivot]
local integer i = left
local integer j = left
local real swap
local integer swaps
set values[pivot] = values[right]
set structs[pivot] = structs[right]
set values[right] = value
set structs[right] = s
loop
exitwhen j >= right
if values[j] < value then
set swap = values[j]
set swaps = structs[j]
set values[j] = values[i]
set structs[j] = structs[i]
set values[i] = swap
set structs[i] = swaps
set i = i + 1
endif
set j = j + 1
endloop
set values[right] = values[i]
set structs[right] = structs[i]
set values[i] = value
set structs[i] = s
if right - (i + 1) > SortUtils_InsertionSortThreshold then
call SortStructsByValues_Sub(structs, values, i + 1, right)
endif
if (i - 1) - left > SortUtils_InsertionSortThreshold then
call SortStructsByValues_Sub(structs, values, left, i - 1)
endif
endfunction
$SCOPE$ function SortStructsByValues takes StructArray structs, RealArray values returns nothing
local integer left = 1
local integer right = values.size - 1
local real value
local integer s
local integer j
call SortStructsByValues_Sub(structs, values, 0, right)
loop
exitwhen left > right
set value = values[left]
set s = structs[left]
set j = left - 1
loop
exitwhen j < 0 or values[j] < value
set values[j + 1] = values[j]
set structs[j + 1] = structs[j]
set j = j - 1
endloop
set values[j + 1] = value
set structs[j + 1] = s
set left = left + 1
endloop
endfunction
//===========================================================================
globals
private UnitArray UnitArrayUnits
endglobals
private function PopulateUnitArray takes nothing returns nothing
set UnitArrayUnits[bj_groupCountUnits] = GetEnumUnit()
set bj_groupCountUnits = bj_groupCountUnits + 1
endfunction
$SCOPE$ function Group2UnitArray takes group g returns UnitArray
set UnitArrayUnits = UnitArray.create(0)
set bj_groupCountUnits = 0
call ForGroup(g, function PopulateUnitArray)
set UnitArrayUnits.size = bj_groupCountUnits
return UnitArrayUnits
endfunction
//===========================================================================
$SCOPE$ function SortUnits takes UnitArray objects, SortUnitFunc sort returns nothing
local integer i = objects.size - 1
if SortUtils_Values != 0 then
call RealArray(SortUtils_Values).destroy()
endif
set SortUtils_Values = RealArray.create(i + 1)
loop
exitwhen i < 0
set RealArray(SortUtils_Values)[i] = sort.evaluate(objects[i])
set i = i - 1
endloop
call SortUnitsByValues(objects, SortUtils_Values)
endfunction
$SCOPE$ function SortStructs takes StructArray objects, SortStructFunc sort returns nothing
local integer i = objects.size - 1
if SortUtils_Values != 0 then
call RealArray(SortUtils_Values).destroy()
endif
set SortUtils_Values = RealArray.create(i + 1)
loop
exitwhen i < 0
set RealArray(SortUtils_Values)[i] = sort.evaluate(objects[i])
set i = i - 1
endloop
call SortStructsByValues(objects, SortUtils_Values)
endfunction
//! endtextmacro
//===========================================================================
globals
public integer Values = 0
endglobals
function GetSortedValues takes nothing returns integer
local integer values = Values
set Values = 0
return values
endfunction
endlibrary
//TESH.scrollpos=36
//TESH.alwaysfold=0
//==============================================================================
// jBoard -- ADVANCED MULTIBOARD SYSTEM BY MAGENTIX -- v1.2
//==============================================================================
//==============================================================================
// Quick manual:
//==============================================================================
//
// Implement by adding "requires jBoardLib" behind your library/scope names
//
// ----------------------------------------------------------------------
// What is jBoard?
// ----------------------------------------------------------------------
//
// jBoard is an enhanced multiboard management system, inspired by the
// jQuery library that is available to webdesigners. That javascript library
// allows for a cool functionality called "chaining".
//
// Because WC3 multiboards often require a huge amount of function calls,
// I came up with jBoard to allow you to make your multiboard code shorter,
// smoother and overall more easy to use.
//
//
// ----------------------------------------------------------------------
// What is chaining and how to use it?
// ----------------------------------------------------------------------
//
// An example says a thousand words, assume this is your 10x5 multiboard setup
// and you want to set all fields to "0" and the first field to "1":
//
// - local multiboard MB = CreateMultiboard()
// local multiboarditem MBI
// call MultiboardSetTitleText(MB,"My Multiboard")
// call MultiboardDisplay(MB,true)
// call MultiboardSetRowCount(MB,10)
// call MultiboardSetColumnCount(MB,5)
// call MultiboardSetItemsValue(MB,"0")
// call MultiboardSetItemsStyle(MB,true,false)
// set MBI = MultiboardGetItem(MB,0,0)
// call MultiboardSetItemValue(MBI,"1")
//
// Now take a look at jBoard:
// - //! runtextmacro jBoardCreate("MyBoard","10","5")
// local MyBoard MB = MyBoard.create("My Multiboard").field(0,0).setValue("1")
// That's all you need to type!
//
// Remember: the textmacro also only has to be called ONCE to create a
// multiboard with the wanted dimensions. So actually that line wouldn't be
// there, but at the top of your script.
//
// Did you also notice how every method call returned an object that you
// could then call another method on? That is the beauty of chaining.
//
// !! EVERY FUNCTION CALL RETURNS A jBoard OBJECT UNLESS STATED OTHERWISE !!
//
//
// ----------------------------------------------------------------------
// I noticed a textmacro there, what gives?
// ----------------------------------------------------------------------
//
// Since you need to create structs that represent your multiboard, jBoard
// needs to know the dimensions of your multiboard to ensure the structs
// don't take up more space than they need to.
//
// For every multiboard variation you plan to use, simply call this once at
// the top of your script:
// //! runtextmacro jBoardCreate("StructName","RowAmount","ColAmount")
//
// Example for an 8 row, 4 col board:
// //! runtextmacro jBoardCreate("My8x4Board","8","4")
// Which you can then instanciate in your script as
// My8x4Board.create("BoardTitle")
//
//
// ----------------------------------------------------------------------
// Defaults
// ----------------------------------------------------------------------
//
// Somtimes we don't want our board cells to be empty by default, but show
// 0 for example. This is easy to achieve by setting a default. Take a look
// at the jBoard struct further down this info block and edit the defaults
// you like to see changed. Every jBoard that gets created after a change
// will take those defaults into account.
//
// Note that you can hardcode defaults below and still change them on the fly!
// set jBoard.defaultItemValue = "1" -- Valid syntax!
//
// Edit your defaults here:
//ReplaceableTextures\CommandButtons\BTNUndeadShrine.blp
//ReplaceableTextures\CommandButtons\BTNReplay-Speedup.blp
//ReplaceableTextures\CommandButtons\BTNReplay-Speeddown.blp
//ReplaceableTextures\CommandButtons\BTNReplay-Loop.blp
//ReplaceableTextures\CommandButtons\BTNReplay-Pause.blp
//ReplaceableTextures\CommandButtons\BTNReplay-Play.blp
//! textmacro jBoardDefaults
static string defaultItemValue = "/"
static string defaultIconPath = "war3mapImported\\sankaku.blp"
static boolean defaultShowValue = true
static boolean defaultShowIcon = true
static boolean defaultShowBoard = true
static real defaultItemWidth = 0.03
//! endtextmacro
//
//==============================================================================
//==============================================================================
// Tip: Commenting out newlines
//==============================================================================
//
// Sometimes when chaining methods, a command can get quite lengthy. jQuery
// doesn't suffer from this visually, because javascript allows people to write
// code over multiple lines until they end it with a semicolon (;)
//
// JASS, however, doesn't allow that directly. Fortunately you can call jBoard
// commands just as easily on multiple lines. Remember: Until you call .reset(),
// your selection will be saved to the next line!
//
// Example:
// local MyBoard MB = MyBoard.create("funkyboard").setplayerrow(0,2).setcolour("ff0000").reset().setplayerrow(1,3).setcolour("0000ff").reset().etc...
//
// Could become:
// local MyBoard MB = MyBoard.create("funkyboard")
// call MB.setplayerrow(0,2).setcolour("ff0000").reset()
// call MB.setplayerrow(1,3)
// call MB.setcolour("0000ff").reset() <-- Previous row selection was saved to this line
//
// In the end it all boils down to which type of coding you prefer, of course.
//
//==============================================================================
//==============================================================================
// Tip: -R Functions
//==============================================================================
//
// When chaining several commands onto a set of fields, one could easily say:
// "There, now let me reset my selection and continue chaining methods"
//
// However, when chaining that way, a string of jBoard commands could easily
// contain the call .reset() a dozen times.
//
// To save coding space and safeguard readability, I introduced the -R functions:
// Basically, any non-selector function that returns a jBoard object can be called
// with a capital "R" behind the name and the jBoard system will automatically
// call a .reset() for you.
//
// The above example of commenting out newlines would then become:
// local MyBoard MB = MyBoard.create("funkyboard").setPlayerRow(0,2).setColourR("ff0000").setPlayerRow(1,3).setColourR("0000ff").etc...
//
// As you can see, this makes the chaining process yet again a little smoother
// Once again: this functionality does not work on selectors/"getters"!
//
//==============================================================================
//==============================================================================
// Function index:
//==============================================================================
//
// ----------------------------------------------------------------------
// Standard Functions - Creating, showing, etc...
// ----------------------------------------------------------------------
//
// Main functionality you'd expect from any multiboard. May be used within
// a local player block. Can be used on any jBoard object (boad, row, col,
// field(s)) and returns the selection you called the function on.
//
// .create("MultiboardName")
// .show()
// .hide()
// .clear() -- SHOULD NEVER BE CALLED, just here for completion
// .fold()
// .unfold()
// .setName()
// .getName() -- Returns the multiboard name as a string, not a jBoard object
// .setNameColour("rgb value")
// Colour syntax: rrggbb (in hexadecimal)
// .getNameColour()
// .destroy()
//
//
// ----------------------------------------------------------------------
// Selection Functions - Adds rows, cols or fields to the selection
// ----------------------------------------------------------------------
//
// Selectors can be used on any jBoard object (boad, row, col, field(s))
// They always return the collection of all the objects you selected from
// the last .reset() up to that point.
//
// VERY IMPORTANT!
// .reset()
// Resets the current selection to the board
//
// MAIN SELECTORS
// .row(integer start, integer end)
// .col(integer start, integer end)
// Selects a row, a columns or multiple of any
// - .row(0,0) returns the row object of row 0
// - .row(0,1) returns the combined fields of row 0 and row 1
// - ONLY WHEN HAVING -ONE- ROW/COL SELECTED will you be able
// to use row/col only functionality
// (it won't give errors or bugs if you try, though)
//
// SINGLE SELECTOR
// .field(integer row, integer col)
// Adds one field to the selection or (if selection is empty)
// returns one field to use field-level manipulations on
// .getPlayerRowField(integer player, integer column)
// .getPlayerColField(integer player, integer row)
// The above two return just ONE field of a player's row.
// Since the player selectors return an entire row and a deep
// selector inside that row would clear the selection first,
// it would otherwise be impossible to select multiple fields
// from player rows or columns...
// You should use this over .getPlayerRow(0).getField(3) unless
// you want only that field of course.
//
// DEEP SELECTOR
// .getField(integer which)
// Returns just ONE field, selected from your previous selection
//
// Quick list of what it does when used on a:
// - Board: Returns the field in position X of the board,
// reading from top left to bottom right
// - Row: Returns the field in column X of that row
// - Column: Returns the field in row X of that column
// - Fields: Returns the field in position X of the selection,
// reading from top left to bottom right
// - Field: Does nothing (returns same field)
//
//
// ----------------------------------------------------------------------
// Player Selectors
// ----------------------------------------------------------------------
//
// Simple functions that allow you to save a row/col to a player.
//
// Both the setters and the getters add the row/col you just set
// to the current selection. Hence, this is both valid syntax:
// - MB.setPlayerRow(0,1).setcolour("ff0000")
// - MB.setPlayerRow(0,1)
// MB.getPlayerRow(0,1).setcolour("ff0000")
//
// The player "setters" have -R functionality, but the "getters" don't!
//
// .setPlayerRow(integer player, integer row)
// .setPlayerCol(integer player, integer col)
// .getPlayerRow(integer player)
// .getPlayerCol(integer player)
//
//
// ----------------------------------------------------------------------
// Manipulation Functions - Also referred to as "setters"
// ----------------------------------------------------------------------
//
// Setters can be used on any jBoard object (boad, row, col, field(s))
// They always return the object that was just manipulated
//
// ABOUT PREFIXES AND SUFFIXES:
// Imagine you want a row with user experience values, by setting a suffix
// "XP", you could still use .add(value) to a field, because the content
// can still be handled by S2I(). Example:
// - .field(1,2) has content "2" and suffix " xp", the field shows "2 xp"
// A user calls .field(1,2).add(4), the field now show "6 xp", without
// having to worry about the " xp" bit!
//
// VALUE SETTERS
// .setValue("value")
// .setColour("rgb value")
// Colour syntax: rrggbb (in hexadecimal)
// .setPrefix("value")
// .setSuffix("value")
// .setIconPath("path")
// .setWidth(real width)
//
// VALUE MANIPULATORS
// .add(integer value)
// Only works if field contents are numerical strings, value can be negative
//
//
// ----------------------------------------------------------------------
// Retrieval Functions - Also referred to as "getters"
// ----------------------------------------------------------------------
//
// Getters can be used on any jBoard object (boad, row, col, field(s))
// !! EVERY GETTER RETURNS A STRING !!
//
// HERE'S WHY:
// When using a getter on anything but a single field, a string is returned
// with all values in it, separated by a pipe (|).
// Future versions may support smart retrieval of a row's/col's properties
// should they all have the same colour for example...
//
// Naturally, the fact that these methods return a string instead of a
// jBoard object means that calling any of these methods is in fact
// "The end of the line" for that chaining process...
//
// .getValue()
// .getColour()
// .getPrefix()
// .getSuffix()
// .getIconPath() -- CAREFUL USE on many fields! (might blow up in your face)
// .getWidth()
//
//
// ----------------------------------------------------------------------
// Extra Functions - Various
// ----------------------------------------------------------------------
//
// .showIcon()
// .hideIcon()
// .showValue()
// .hideValue()
//
//==============================================================================
//==============================================================================
// Credits:
//==============================================================================
//
// John Resig for coming up with jQuery and its chaining
// Vexorian for vJass and JassHelper, making jBoard possible
//
//==============================================================================
//==============================================================================
// Open letter to Vexorian:
//==============================================================================
//
// Take a look at the setter/getter methods and the -R functions. If that isn't
// a reason why textmacroes should be allowed to be nested, I don't know what
// is :x (They're all the same bar the function name...)
//
// Protip: Yes, this is a request to you to get nested textmacroes working :)
//
//==============================================================================
//==============================================================================
// Macro function core -- No touchy from here on out!
//==============================================================================
//! textmacro jBoardCreate takes NAME, ROWS, COLS
globals
private constant integer $NAME$SIZE = $ROWS$*$COLS$
private constant integer $NAME$ROWS = $ROWS$
private constant integer $NAME$COLS = $COLS$
endglobals
private struct $NAME$FIELD
$NAME$ parent
integer realRow
integer realCol
multiboarditem mbi
real width= jBoard.defaultItemWidth
string iconpath = jBoard.defaultIconPath
string colour = "ffffff"
string prefix = ""
string content = jBoard.defaultItemValue
string suffix = ""
boolean showV = jBoard.defaultShowValue
boolean showI = jBoard.defaultShowIcon
static method create takes $NAME$ parent, integer row, integer col returns thistype
local thistype f = thistype.allocate()
set f.parent = parent
set f.realRow = row
set f.realCol = col
set f.mbi = MultiboardGetItem(f.parent.MB,f.realRow,f.realCol)
return f
endmethod
method onDestroy takes nothing returns nothing
call MultiboardReleaseItem(.mbi)
endmethod
private method updateField takes nothing returns nothing
call MultiboardSetItemValue(.mbi,"|cff"+.colour+.prefix+.content+.suffix+"|r")
endmethod
method add takes integer i returns nothing
set .content = I2S(S2I(.content) + i)
call .updateField()
endmethod
method setValue takes string s returns nothing
set .content = s
call .updateField()
endmethod
method setPrefix takes string s returns nothing
set .prefix = s
call .updateField()
endmethod
method setSuffix takes string s returns nothing
set .suffix = s
call .updateField()
endmethod
method setColour takes string rgb returns nothing
set .colour = rgb
call .updateField()
endmethod
method setWidth takes real w returns nothing
set .width = w
call MultiboardSetItemWidth(.mbi,w)
endmethod
method showIcon takes nothing returns nothing
set .showI = true
call MultiboardSetItemStyle(.mbi,.showV,.showI)
endmethod
method hideIcon takes nothing returns nothing
set .showI = false
call MultiboardSetItemStyle(.mbi,.showV,.showI)
endmethod
method showValue takes nothing returns nothing
set .showV = true
call MultiboardSetItemStyle(.mbi,.showV,.showI)
endmethod
method hideValue takes nothing returns nothing
set .showV = false
call MultiboardSetItemStyle(.mbi,.showV,.showI)
endmethod
method setIconPath takes string s returns nothing
set .iconpath = s
call MultiboardSetItemIcon(.mbi,.iconpath)
endmethod
method getValue takes nothing returns string
return .content
endmethod
method getPrefix takes nothing returns string
return .prefix
endmethod
method getSuffix takes nothing returns string
return .suffix
endmethod
method getColour takes nothing returns string
return .colour
endmethod
method getWidth takes nothing returns string
return R2S(.width)
endmethod
method getIconPath takes nothing returns string
return .iconpath
endmethod
endstruct
private interface $NAME$FIELDCOLLECTION
$NAME$ parent
method getField takes integer i returns $NAME$FIELD
endinterface
private struct $NAME$UTILITY extends $NAME$FIELDCOLLECTION
stub method getField takes integer i returns $NAME$FIELD
return 0
endmethod
endstruct
private struct $NAME$ROW extends $NAME$UTILITY
$NAME$FIELD array fields[$COLS$]
integer realPos
integer fromPlayer = -1
static method create takes $NAME$ parent, integer pos returns thistype
local thistype row = thistype.allocate()
local integer i = 0
set row.parent = parent
set row.realPos = pos
loop
exitwhen i == $NAME$COLS
set row.fields[i] = $NAME$FIELD.create(parent,pos,i)
set i = i + 1
endloop
return row
endmethod
method getField takes integer i returns $NAME$FIELD
return .fields[i]
endmethod
endstruct
private struct $NAME$COL extends $NAME$UTILITY
$NAME$FIELD array fields[$ROWS$]
integer realPos
integer fromPlayer = -1
static method create takes $NAME$ parent, integer pos returns thistype
local thistype col = thistype.allocate()
local integer i = 0
set col.parent = parent
set col.realPos = pos
loop
exitwhen i == $NAME$ROWS
set col.fields[i] = col.parent.rows[i].fields[pos]
set i = i + 1
endloop
return col
endmethod
method getField takes integer i returns $NAME$FIELD
return .fields[i]
endmethod
endstruct
private struct $NAME$FLD extends $NAME$UTILITY
$NAME$FIELD array fields[$NAME$SIZE]
static method create takes $NAME$ parent returns thistype
local thistype fld = thistype.allocate()
set fld.parent = parent
return fld
endmethod
private method hasField takes $NAME$FIELD f returns boolean
local integer i = 0
loop
exitwhen i == .parent.selectionAmount
if (.fields[i] == f) then
return true
endif
set i = i + 1
endloop
return false
endmethod
method addFields takes $NAME$FIELDCOLLECTION f, integer amount returns nothing
local integer i = 0
loop
exitwhen i == amount
if not(.hasField(f.getField(i))) then
set .fields[.parent.selectionAmount] = f.getField(i)
set .parent.selectionAmount = .parent.selectionAmount + 1
endif
set i = i + 1
endloop
endmethod
method getField takes integer i returns $NAME$FIELD
return .fields[i]
endmethod
endstruct
struct $NAME$ extends jBoard
$NAME$ROW array rows[$ROWS$]
$NAME$COL array cols[$COLS$]
$NAME$FLD selection
static method create takes string boardName returns thistype
local thistype board = thistype.allocate(boardName)
local integer i = 0
call MultiboardSetRowCount(board.MB,$NAME$ROWS)
call MultiboardSetColumnCount(board.MB,$NAME$COLS)
set board.selection = $NAME$FLD.create(board)
loop
exitwhen i == $NAME$ROWS
set board.rows[i] = $NAME$ROW.create(board,i)
set i = i + 1
endloop
set i = 0
loop
exitwhen i == $NAME$COLS
set board.cols[i] = $NAME$COL.create(board,i)
set i = i + 1
endloop
call board.initBoard()
return board
endmethod
private method transfer takes $NAME$FIELDCOLLECTION f, integer ftype, integer amount returns nothing
set .selectionLevel = IntegerTertiaryOp(.selectionLevel == jBoard_SELECTION_LVL_BOARD,ftype,jBoard_SELECTION_LVL_FIELDS)
call .selection.addFields(f,amount)
endmethod
method reset takes nothing returns thistype
set .selectionLevel = jBoard_SELECTION_LVL_BOARD
set .selectionAmount = 0
return this
endmethod
method field takes integer row, integer col returns thistype
set .selection.fields[0] = .rows[row].fields[col]
set .selectionLevel = jBoard_SELECTION_LVL_FLD
return this
endmethod
method getField takes integer which returns thistype
if .selectionLevel == jBoard_SELECTION_LVL_ROW then
set .selection.fields[0] = .rows[.selectionSpecial].fields[which]
elseif .selectionLevel == jBoard_SELECTION_LVL_COL then
set .selection.fields[0] = .cols[.selectionSpecial].fields[which]
elseif .selectionLevel == jBoard_SELECTION_LVL_FIELDS then
set .selection.fields[0] = .selection.fields[which]
elseif .selectionLevel == jBoard_SELECTION_LVL_BOARD then
set .selection.fields[0] = .rows[which/$NAME$COLS].fields[ModuloInteger(which,$NAME$COLS)]
endif
set .selectionLevel = jBoard_SELECTION_LVL_FLD
return this
endmethod
method row takes integer start, integer end returns thistype
local integer i = IntegerTertiaryOp(start<end,start,end)
local integer j = IntegerTertiaryOp(start<end,end,start)
if start == end then
set .selectionSpecial = start
call .transfer(.rows[start],jBoard_SELECTION_LVL_ROW,$NAME$COLS)
else
loop
exitwhen i > j
call .transfer(.rows[i],jBoard_SELECTION_LVL_ROW,$NAME$COLS)
set i = i + 1
endloop
endif
return this
endmethod
method col takes integer start, integer end returns thistype
local integer i = IntegerTertiaryOp(start<end,start,end)
local integer j = IntegerTertiaryOp(start<end,end,start)
if start == end then
set .selectionSpecial = start
call .transfer(.cols[start],jBoard_SELECTION_LVL_COL,$NAME$ROWS)
else
loop
exitwhen i > j
call .transfer(.cols[i],jBoard_SELECTION_LVL_COL,$NAME$ROWS)
set i = i + 1
endloop
endif
return this
endmethod
method setPlayerRow takes integer p, integer row returns thistype
set .playerRow[p] = row
set .rows[row].fromPlayer = p
return .row(row,row)
endmethod
method getPlayerRow takes integer p returns thistype
return .row(.playerRow[p],.playerRow[p])
endmethod
method getPlayerRowField takes integer p, integer i returns thistype
return .field(.playerRow[p],i)
endmethod
method setPlayerCol takes integer p, integer col returns thistype
set .playerCol[p] = col
set .cols[col].fromPlayer = p
return .col(col,col)
endmethod
method getPlayerCol takes integer p returns thistype
return .col(.playerCol[p],.playerCol[p])
endmethod
method getPlayerColField takes integer p, integer i returns thistype
return .field(i,.playerCol[p])
endmethod
method setColour takes string rgb returns thistype
local integer i = 0
if (.selectionLevel == jBoard_SELECTION_LVL_BOARD) then
loop
exitwhen i == $NAME$ROWS
call .reset().row(i,i).setColour(rgb)
set i = i + 1
endloop
set .selectionLevel = jBoard_SELECTION_LVL_BOARD
elseif (.selectionLevel == jBoard_SELECTION_LVL_FIELDS) then
loop
exitwhen i == .selectionAmount
call .selection.fields[i].setColour(rgb)
set i = i + 1
endloop
elseif (.selectionLevel == jBoard_SELECTION_LVL_ROW) then
loop
exitwhen i == $NAME$COLS
call .rows[.selectionSpecial].fields[i].setColour(rgb)
set i = i + 1
endloop
elseif (.selectionLevel == jBoard_SELECTION_LVL_COL) then
loop
exitwhen i == $NAME$ROWS
call .cols[.selectionSpecial].fields[i].setColour(rgb)
set i = i + 1
endloop
elseif (.selectionLevel == jBoard_SELECTION_LVL_FLD) then
call .selection.fields[0].setColour(rgb)
endif
return this
endmethod
method setWidth takes real w returns thistype
local integer i = 0
if (.selectionLevel == jBoard_SELECTION_LVL_BOARD) then
loop
exitwhen i == $NAME$ROWS
call .reset().row(i,i).setWidth(w)
set i = i + 1
endloop
set .selectionLevel = jBoard_SELECTION_LVL_BOARD
elseif (.selectionLevel == jBoard_SELECTION_LVL_FIELDS) then
loop
exitwhen i == .selectionAmount
call .selection.fields[i].setWidth(w)
set i = i + 1
endloop
elseif (.selectionLevel == jBoard_SELECTION_LVL_ROW) then
loop
exitwhen i == $NAME$COLS
call .rows[.selectionSpecial].fields[i].setWidth(w)
set i = i + 1
endloop
elseif (.selectionLevel == jBoard_SELECTION_LVL_COL) then
loop
exitwhen i == $NAME$ROWS
call .cols[.selectionSpecial].fields[i].setWidth(w)
set i = i + 1
endloop
elseif (.selectionLevel == jBoard_SELECTION_LVL_FLD) then
call .selection.fields[0].setWidth(w)
endif
return this
endmethod
method setPrefix takes string val returns thistype
local integer i = 0
if (.selectionLevel == jBoard_SELECTION_LVL_BOARD) then
loop
exitwhen i == $NAME$ROWS
call .reset().row(i,i).setPrefix(val)
set i = i + 1
endloop
set .selectionLevel = jBoard_SELECTION_LVL_BOARD
elseif (.selectionLevel == jBoard_SELECTION_LVL_FIELDS) then
loop
exitwhen i == .selectionAmount
call .selection.fields[i].setPrefix(val)
set i = i + 1
endloop
elseif (.selectionLevel == jBoard_SELECTION_LVL_ROW) then
loop
exitwhen i == $NAME$COLS
call .rows[.selectionSpecial].fields[i].setPrefix(val)
set i = i + 1
endloop
elseif (.selectionLevel == jBoard_SELECTION_LVL_COL) then
loop
exitwhen i == $NAME$ROWS
call .cols[.selectionSpecial].fields[i].setPrefix(val)
set i = i + 1
endloop
elseif (.selectionLevel == jBoard_SELECTION_LVL_FLD) then
call .selection.fields[0].setPrefix(val)
endif
return this
endmethod
method setSuffix takes string val returns thistype
local integer i = 0
if (.selectionLevel == jBoard_SELECTION_LVL_BOARD) then
loop
exitwhen i == $NAME$ROWS
call .reset().row(i,i).setSuffix(val)
set i = i + 1
endloop
set .selectionLevel = jBoard_SELECTION_LVL_BOARD
elseif (.selectionLevel == jBoard_SELECTION_LVL_FIELDS) then
loop
exitwhen i == .selectionAmount
call .selection.fields[i].setSuffix(val)
set i = i + 1
endloop
elseif (.selectionLevel == jBoard_SELECTION_LVL_ROW) then
loop
exitwhen i == $NAME$COLS
call .rows[.selectionSpecial].fields[i].setSuffix(val)
set i = i + 1
endloop
elseif (.selectionLevel == jBoard_SELECTION_LVL_COL) then
loop
exitwhen i == $NAME$ROWS
call .cols[.selectionSpecial].fields[i].setSuffix(val)
set i = i + 1
endloop
elseif (.selectionLevel == jBoard_SELECTION_LVL_FLD) then
call .selection.fields[0].setSuffix(val)
endif
return this
endmethod
method setIconPath takes string val returns thistype
local integer i = 0
if (.selectionLevel == jBoard_SELECTION_LVL_BOARD) then
loop
exitwhen i == $NAME$ROWS
call .reset().row(i,i).setIconPath(val)
set i = i + 1
endloop
set .selectionLevel = jBoard_SELECTION_LVL_BOARD
elseif (.selectionLevel == jBoard_SELECTION_LVL_FIELDS) then
loop
exitwhen i == .selectionAmount
call .selection.fields[i].setIconPath(val)
set i = i + 1
endloop
elseif (.selectionLevel == jBoard_SELECTION_LVL_ROW) then
loop
exitwhen i == $NAME$COLS
call .rows[.selectionSpecial].fields[i].setIconPath(val)
set i = i + 1
endloop
elseif (.selectionLevel == jBoard_SELECTION_LVL_COL) then
loop
exitwhen i == $NAME$ROWS
call .cols[.selectionSpecial].fields[i].setIconPath(val)
set i = i + 1
endloop
elseif (.selectionLevel == jBoard_SELECTION_LVL_FLD) then
call .selection.fields[0].setIconPath(val)
endif
return this
endmethod
method showIcon takes nothing returns thistype
local integer i = 0
if (.selectionLevel == jBoard_SELECTION_LVL_BOARD) then
loop
exitwhen i == $NAME$ROWS
call .reset().row(i,i).showIcon()
set i = i + 1
endloop
set .selectionLevel = jBoard_SELECTION_LVL_BOARD
elseif (.selectionLevel == jBoard_SELECTION_LVL_FIELDS) then
loop
exitwhen i == .selectionAmount
call .selection.fields[i].showIcon()
set i = i + 1
endloop
elseif (.selectionLevel == jBoard_SELECTION_LVL_ROW) then
loop
exitwhen i == $NAME$COLS
call .rows[.selectionSpecial].fields[i].showIcon()
set i = i + 1
endloop
elseif (.selectionLevel == jBoard_SELECTION_LVL_COL) then
loop
exitwhen i == $NAME$ROWS
call .cols[.selectionSpecial].fields[i].showIcon()
set i = i + 1
endloop
elseif (.selectionLevel == jBoard_SELECTION_LVL_FLD) then
call .selection.fields[0].showIcon()
endif
return this
endmethod
method hideIcon takes nothing returns thistype
local integer i = 0
if (.selectionLevel == jBoard_SELECTION_LVL_BOARD) then
loop
exitwhen i == $NAME$ROWS
call .reset().row(i,i).hideIcon()
set i = i + 1
endloop
set .selectionLevel = jBoard_SELECTION_LVL_BOARD
elseif (.selectionLevel == jBoard_SELECTION_LVL_FIELDS) then
loop
exitwhen i == .selectionAmount
call .selection.fields[i].hideIcon()
set i = i + 1
endloop
elseif (.selectionLevel == jBoard_SELECTION_LVL_ROW) then
loop
exitwhen i == $NAME$COLS
call .rows[.selectionSpecial].fields[i].hideIcon()
set i = i + 1
endloop
elseif (.selectionLevel == jBoard_SELECTION_LVL_COL) then
loop
exitwhen i == $NAME$ROWS
call .cols[.selectionSpecial].fields[i].hideIcon()
set i = i + 1
endloop
elseif (.selectionLevel == jBoard_SELECTION_LVL_FLD) then
call .selection.fields[0].hideIcon()
endif
return this
endmethod
method showValue takes nothing returns thistype
local integer i = 0
if (.selectionLevel == jBoard_SELECTION_LVL_BOARD) then
loop
exitwhen i == $NAME$ROWS
call .reset().row(i,i).showValue()
set i = i + 1
endloop
set .selectionLevel = jBoard_SELECTION_LVL_BOARD
elseif (.selectionLevel == jBoard_SELECTION_LVL_FIELDS) then
loop
exitwhen i == .selectionAmount
call .selection.fields[i].showValue()
set i = i + 1
endloop
elseif (.selectionLevel == jBoard_SELECTION_LVL_ROW) then
loop
exitwhen i == $NAME$COLS
call .rows[.selectionSpecial].fields[i].showValue()
set i = i + 1
endloop
elseif (.selectionLevel == jBoard_SELECTION_LVL_COL) then
loop
exitwhen i == $NAME$ROWS
call .cols[.selectionSpecial].fields[i].showValue()
set i = i + 1
endloop
elseif (.selectionLevel == jBoard_SELECTION_LVL_FLD) then
call .selection.fields[0].showValue()
endif
return this
endmethod
method hideValue takes nothing returns thistype
local integer i = 0
if (.selectionLevel == jBoard_SELECTION_LVL_BOARD) then
loop
exitwhen i == $NAME$ROWS
call .reset().row(i,i).hideValue()
set i = i + 1
endloop
set .selectionLevel = jBoard_SELECTION_LVL_BOARD
elseif (.selectionLevel == jBoard_SELECTION_LVL_FIELDS) then
loop
exitwhen i == .selectionAmount
call .selection.fields[i].hideValue()
set i = i + 1
endloop
elseif (.selectionLevel == jBoard_SELECTION_LVL_ROW) then
loop
exitwhen i == $NAME$COLS
call .rows[.selectionSpecial].fields[i].hideValue()
set i = i + 1
endloop
elseif (.selectionLevel == jBoard_SELECTION_LVL_COL) then
loop
exitwhen i == $NAME$ROWS
call .cols[.selectionSpecial].fields[i].hideValue()
set i = i + 1
endloop
elseif (.selectionLevel == jBoard_SELECTION_LVL_FLD) then
call .selection.fields[0].hideValue()
endif
return this
endmethod
method setValue takes string val returns thistype
local integer i = 0
if (.selectionLevel == jBoard_SELECTION_LVL_BOARD) then
loop
exitwhen i == $NAME$ROWS
call .reset().row(i,i).setValue(val)
set i = i + 1
endloop
set .selectionLevel = jBoard_SELECTION_LVL_BOARD
elseif (.selectionLevel == jBoard_SELECTION_LVL_FIELDS) then
loop
exitwhen i == .selectionAmount
call .selection.fields[i].setValue(val)
set i = i + 1
endloop
elseif (.selectionLevel == jBoard_SELECTION_LVL_ROW) then
loop
exitwhen i == $NAME$COLS
call .rows[.selectionSpecial].fields[i].setValue(val)
set i = i + 1
endloop
elseif (.selectionLevel == jBoard_SELECTION_LVL_COL) then
loop
exitwhen i == $NAME$ROWS
call .cols[.selectionSpecial].fields[i].setValue(val)
set i = i + 1
endloop
elseif (.selectionLevel == jBoard_SELECTION_LVL_FLD) then
call .selection.fields[0].setValue(val)
endif
return this
endmethod
method add takes integer val returns thistype
local integer i = 0
if (.selectionLevel == jBoard_SELECTION_LVL_BOARD) then
loop
exitwhen i == $NAME$ROWS
call .reset().row(i,i).add(val)
set i = i + 1
endloop
set .selectionLevel = jBoard_SELECTION_LVL_BOARD
elseif (.selectionLevel == jBoard_SELECTION_LVL_FIELDS) then
loop
exitwhen i == .selectionAmount
call .selection.fields[i].add(val)
set i = i + 1
endloop
elseif (.selectionLevel == jBoard_SELECTION_LVL_ROW) then
loop
exitwhen i == $NAME$COLS
call .rows[.selectionSpecial].fields[i].add(val)
set i = i + 1
endloop
elseif (.selectionLevel == jBoard_SELECTION_LVL_COL) then
loop
exitwhen i == $NAME$ROWS
call .cols[.selectionSpecial].fields[i].add(val)
set i = i + 1
endloop
elseif (.selectionLevel == jBoard_SELECTION_LVL_FLD) then
call .selection.fields[0].add(val)
endif
return this
endmethod
method getValue takes nothing returns string
local integer i = 0
local string s = ""
if (.selectionLevel == jBoard_SELECTION_LVL_FLD) then
set s = .selection.fields[0].getValue()
else
loop
exitwhen i == .selectionAmount
set s = s + .selection.fields[i].getValue()
set i = i + 1
exitwhen i == .selectionAmount
set s = s + "|"
endloop
endif
call .reset()
return s
endmethod
method getPrefix takes nothing returns string
local integer i = 0
local string s = ""
if (.selectionLevel == jBoard_SELECTION_LVL_FLD) then
set s = .selection.fields[0].getPrefix()
else
loop
exitwhen i == .selectionAmount
set s = s + .selection.fields[i].getPrefix()
set i = i + 1
exitwhen i == .selectionAmount
set s = s + "|"
endloop
endif
call .reset()
return s
endmethod
method getSuffix takes nothing returns string
local integer i = 0
local string s = ""
if (.selectionLevel == jBoard_SELECTION_LVL_FLD) then
set s = .selection.fields[0].getSuffix()
else
loop
exitwhen i == .selectionAmount
set s = s + .selection.fields[i].getSuffix()
set i = i + 1
exitwhen i == .selectionAmount
set s = s + "|"
endloop
endif
call .reset()
return s
endmethod
method getWidth takes nothing returns string
local integer i = 0
local string s = ""
if (.selectionLevel == jBoard_SELECTION_LVL_FLD) then
set s = .selection.fields[0].getWidth()
else
loop
exitwhen i == .selectionAmount
set s = s + .selection.fields[i].getWidth()
set i = i + 1
exitwhen i == .selectionAmount
set s = s + "|"
endloop
endif
call .reset()
return s
endmethod
method getColour takes nothing returns string
local integer i = 0
local string s = ""
if (.selectionLevel == jBoard_SELECTION_LVL_FLD) then
set s = .selection.fields[0].getColour()
else
loop
exitwhen i == .selectionAmount
set s = s + .selection.fields[i].getColour()
set i = i + 1
exitwhen i == .selectionAmount
set s = s + "|"
endloop
endif
call .reset()
return s
endmethod
method getIconPath takes nothing returns string
local integer i = 0
local string s = ""
if (.selectionLevel == jBoard_SELECTION_LVL_FLD) then
set s = .selection.fields[0].getIconPath()
else
loop
exitwhen i == .selectionAmount
set s = s + .selection.fields[i].getIconPath()
set i = i + 1
exitwhen i == .selectionAmount
set s = s + "|"
endloop
endif
call .reset()
return s
endmethod
method setName takes string s returns thistype
set .boardName = s
call MultiboardSetTitleText(.MB,"|cff"+.boardNameColour+.boardName+"|r")
return this
endmethod
method getName takes nothing returns string
call .reset()
return .boardName
endmethod
method setNameColour takes string s returns thistype
set .boardNameColour = s
call MultiboardSetTitleText(.MB,"|cff"+.boardNameColour+.boardName+"|r")
return this
endmethod
method getNameColour takes nothing returns string
call .reset()
return .boardNameColour
endmethod
method show takes nothing returns thistype
call MultiboardDisplay(.MB,true)
return this
endmethod
method hide takes nothing returns thistype
call MultiboardDisplay(.MB,false)
return this
endmethod
method clear takes nothing returns thistype
call MultiboardClear(.MB)
return this
endmethod
method fold takes nothing returns thistype
call MultiboardMinimize(.MB,true)
return this
endmethod
method unfold takes nothing returns thistype
call MultiboardMinimize(.MB,false)
return this
endmethod
method setValueR takes string val returns thistype
call .setValue(val)
call .reset()
return this
endmethod
method setColourR takes string rgb returns thistype
call .setColour(rgb)
call .reset()
return this
endmethod
method setPrefixR takes string val returns thistype
call .setPrefix(val)
call .reset()
return this
endmethod
method setSuffixR takes string val returns thistype
call .setSuffix(val)
call .reset()
return this
endmethod
method setIconPathR takes string val returns thistype
call .setIconPath(val)
call .reset()
return this
endmethod
method setWidthR takes real w returns thistype
call .setWidth(w)
call .reset()
return this
endmethod
method addR takes integer val returns thistype
call .add(val)
call .reset()
return this
endmethod
method setPlayerRowR takes integer p, integer col returns thistype
call .setPlayerRow(p,row)
call .reset()
return this
endmethod
method setPlayerColR takes integer p, integer col returns thistype
call .setPlayerCol(p,col)
call .reset()
return this
endmethod
method setNameR takes string s returns thistype
call .setName(s)
call .reset()
return this
endmethod
method setNameColourR takes string s returns thistype
call .setNameColour(s)
call .reset()
return this
endmethod
method showR takes nothing returns thistype
call .show()
call .reset()
return this
endmethod
method showIconR takes nothing returns thistype
call .showIcon()
call .reset()
return this
endmethod
method hideIconR takes nothing returns thistype
call .hideIcon()
call .reset()
return this
endmethod
method showValueR takes nothing returns thistype
call .showValue()
call .reset()
return this
endmethod
method hideValueR takes nothing returns thistype
call .hideValue()
call .reset()
return this
endmethod
method hideR takes nothing returns thistype
call .hide()
call .reset()
return this
endmethod
method clearR takes nothing returns thistype
call .clear()
call .reset()
return this
endmethod
method foldR takes nothing returns thistype
call .fold()
call .reset()
return this
endmethod
method unfoldR takes nothing returns thistype
call .unfold()
call .reset()
return this
endmethod
method destroy takes nothing returns nothing
local integer i = 0
local integer j = 0
loop
exitwhen i == $NAME$ROWS
loop
exitwhen j == $NAME$COLS
call rows[i].fields[j].destroy()
set j = j + 1
endloop
set i = i + 1
endloop
call DestroyMultiboard(.MB)
call .deallocate()
endmethod
endstruct
//! endtextmacro
/*method setItemIcon takes integer column, integer row, string iconFileName returns nothing
//Works similar to the MultiboardSetItem* functions.
//However, a value of 0 refers to the first column/row, -1 refers to all columns/rows
//Using a Column or Row higher than the current max will result in the function not doing anything.
local integer curCol = 0
local integer curRow = 0
if column > .columnCount or row > .rowCount then
return
endif
if column == -1 or row == -1 then
if column == -1 and row == -1 then
call MultiboardSetItemsIcon(.which,iconFileName)
return
elseif column == -1 then
loop
exitwhen curCol > .columnCount
call MultiboardSetItemIcon(.MBIZ[curCol * MAX_ROW_COUNT + row],iconFileName)
set curCol = curCol + 1
endloop
return
else
loop
exitwhen curRow > .rowCount
call MultiboardSetItemIcon(.MBIZ[column * MAX_ROW_COUNT + curRow],iconFileName)
set curRow = curRow + 1
endloop
return
endif
endif
call MultiboardSetItemIcon(.MBIZ[column * MAX_ROW_COUNT + row],iconFileName)
endmethod*/
//==============================================================================
// ACTUAL LIBRARY
//==============================================================================
library jBoardLib
globals
constant integer jBoard_SELECTION_LVL_BOARD = 0
constant integer jBoard_SELECTION_LVL_ROW = 1
constant integer jBoard_SELECTION_LVL_COL = 2
constant integer jBoard_SELECTION_LVL_FLD = 3
constant integer jBoard_SELECTION_LVL_FIELDS = 4
endglobals
struct jBoard
//! runtextmacro jBoardDefaults()
multiboard MB
string boardName
string boardNameColour = "ffffcc"
integer selectionLevel = 0
integer selectionSpecial = 0
integer selectionAmount = 0
integer array playerRow[12]
integer array playerCol[12]
static method create takes string boardName returns jBoard
local integer i = 0
set jB = thistype.allocate()
set jB.MB = CreateMultiboard()
set jB.boardName = boardName
call MultiboardSetTitleText(jB.MB,boardName)
call MultiboardDisplay(jB.MB,false)
return jB
endmethod
method initBoard takes nothing returns nothing
call MultiboardSetItemsValue(.MB,.defaultItemValue)
call MultiboardSetItemsIcon(.MB,defaultIconPath)
call MultiboardSetItemsStyle(.MB,.defaultShowValue,.defaultShowIcon)
call MultiboardSetItemsWidth(.MB,.defaultItemWidth)
call MultiboardDisplay(.MB,.defaultShowBoard)
endmethod
endstruct
endlibrary
//==============================================================================
// END OF jBoard ADVANCED MULTIBOARD SYSTEM
//==============================================================================
//TESH.scrollpos=-1
//TESH.alwaysfold=0
/*function Trig_HitUnitTest_Conditions takes nothing returns boolean
return ( GetUnitTypeId(GetAttacker()) == 'hrif' )
endfunction
function Test takes nothing returns nothing
call BJDebugMsg("Damage: "+R2S(GetEventDamage()))
endfunction
function Trig_HitUnitTest_Actions takes nothing returns nothing
call ApplyHitAction( GetAttacker(), GetTriggerUnit(), function Test, 0.17, 0.0 )
endfunction
//===========================================================================
function InitTrig_HitUnitTest takes nothing returns nothing
set gg_trg_HitUnitTest = CreateTrigger( )
call TriggerRegisterAnyUnitEventBJ( gg_trg_HitUnitTest, EVENT_PLAYER_UNIT_ATTACKED )
call TriggerAddCondition( gg_trg_HitUnitTest, Condition( function Trig_HitUnitTest_Conditions ) )
call TriggerAddAction( gg_trg_HitUnitTest, function Trig_HitUnitTest_Actions )
endfunction*/
function GetPeriod takes nothing returns real
return 0.0001
endfunction
function GetMissilePeriod takes nothing returns real
return 0.02
endfunction
function RegisterHitAction_Done takes nothing returns boolean
if GetEventDamageSource() == GetHandleUnit( GetTriggeringTrigger(), "attacker" ) then
call DisableTrigger(GetTriggeringTrigger())
return true
endif
return false
endfunction
function CreateHitPeriod_Start takes nothing returns nothing
local timer T1 = GetExpiredTimer()
local trigger hitdetector = GetHandleTrigger( T1, "hitdetector" )
call TriggerRegisterUnitEvent( hitdetector, GetHandleUnit(hitdetector, "target"), EVENT_UNIT_DAMAGED )
call FlushHandleLocals(T1)
call DestroyTimer(T1)
set T1 = null
set hitdetector = null
endfunction
function CreateHitPeriod_End takes nothing returns nothing
local timer T2 = GetExpiredTimer()
local trigger hitdetector = GetHandleTrigger( T2, "hitdetector" )
call TriggerRemoveCondition( hitdetector, GetHandleCondition(hitdetector, "condition") )
call TriggerRemoveAction( hitdetector, GetHandleAction(hitdetector, "action") )
call FlushHandleLocals(hitdetector)
call FlushHandleLocals(T2)
call DestroyTrigger(hitdetector)
call DestroyTimer(T2)
set T2 = null
set hitdetector = null
endfunction
function CreateHitPeriod takes trigger hitdetector, real time, boolean missile returns nothing
local timer T1 = CreateTimer()
local timer T2 = CreateTimer()
if missile then
call TimerStart( T1, time-GetMissilePeriod(), false, function CreateHitPeriod_Start )
call TimerStart( T2, time+GetMissilePeriod(), false, function CreateHitPeriod_End )
else
call TimerStart( T1, time-GetPeriod(), false, function CreateHitPeriod_Start )
call TimerStart( T2, time+GetPeriod(), false, function CreateHitPeriod_End )
endif
call SetHandleHandle( T1, "hitdetector", hitdetector )
call SetHandleHandle( T2, "hitdetector", hitdetector )
set T1 = null
set T2 = null
endfunction
function ApplyHitAction takes unit attacker, unit target, code callback, real damagepoint, real missilespeed returns nothing
local trigger T = CreateTrigger()
local trigger hitdetector = CreateTrigger()
local boolexpr b = Condition(function RegisterHitAction_Done)
local triggercondition C = TriggerAddCondition( hitdetector, b )
local triggeraction A = TriggerAddAction( hitdetector, callback )
call SetHandleHandle( hitdetector, "attacker", attacker)
call SetHandleHandle( hitdetector, "target", target)
call SetHandleHandle( hitdetector, "condition", C)
call SetHandleHandle( hitdetector, "action", A)
if missilespeed <= 0.0 then //unreasonable missile speed means instant/normal weapon type
call CreateHitPeriod( hitdetector, damagepoint, false)
else
call CreateHitPeriod( hitdetector, damagepoint+DistanceBetweenPointsXY(GetUnitX(attacker),GetUnitY(attacker),GetUnitX(target),GetUnitY(target)), true)
endif
call DestroyBoolExpr(b)
set T = null
set hitdetector = null
set b = null
set C = null
set A = null
endfunction
//6 dice?
function GetDiceRolls takes integer d4NR, integer d6NR, integer d8NR, integer d10NR, integer d12NR, integer d20NR, integer bonusmod returns integer
local integer ia = 1
local integer ib = 1
local integer RT = 0
local integer array NS
local integer array NR
local integer array DR
set NS[1] = 4
set NS[2] = 6
set NS[3] = 8
set NS[4] = 10
set NS[5] = 12
set NS[6] = 20
set NR[1] = d4NR
set NR[2] = d6NR
set NR[3] = d8NR
set NR[4] = d10NR
set NR[5] = d12NR
set NR[6] = d20NR
loop
exitwhen ia > 6
set ib = 1
loop
exitwhen ib > NR[ia]
set DR[ia] = GetRandomInt(1, NS[ia])
set RT = ( RT + DR[ia] )
set ib = ib + 1
endloop
set ia = ia + 1
endloop
set RT = ( RT + bonusmod )
return RT
endfunction
//TESH.scrollpos=97
//TESH.alwaysfold=0
scope mschangeskel initializer I;globals;integer mschange=0;endglobals;private function f takes nothing returns boolean;if mschange==0 then;mschange=1;call SetUnitMoveSpeed( gtu, 200.00 );SetUnitAbilityLevel(gtu,'ADbl',1);
return false;else;mschange=0;SetUnitMoveSpeed( gtu, 300.00 );SetUnitAbilityLevel(gtu,'ADbl',2);return false;endif;return false;endfunction;public function I takes nothing returns nothing;effectstartGT(f,'Acms');endfunction;endscope
/*scope jumpphnx initializer I;private function f takes nothing returns boolean;local unit ut = GetTriggerUnit();local real uf=Deg2Rad(GetUnitFacing(ut));if mschange==0 then;call UnitJump(ut,800.00,600.00,uf,200.0);set ut = null;return false
elseif mschange==1 then;call UnitJump(ut,800.00,450.00,uf,200.0);set ut = null;return false;elseif mschange==2 then;call UnitJump(ut,800.00,750.00,uf,200.0);set ut = null;return false;elseif mschange==3 then
call UnitJump(ut,800.00,900.00,uf,200.0);set ut = null;return false;endif;set ut = null;return false;endfunction;public function I takes nothing returns nothing;call GT_AddStartsEffectAction(function f,'A001');endfunction;endscope
*/
//Skeleton Lord model by levigeorge1617
library DeathNinja
define{
DeathNinja_DeathOrb = 'e00P'//special dummy caster00p
DeathNinja_DeathWalk = 'A05J'//triggered spell05j
//DeathNinja_DeathSlow = 'ADim'//a damage aura for the dummy caster
DeathNinja_DeathCloud = 'ADcl'//triggered spell
//= 'ADbl'//triggered spell
DeathNinja_DeathCloudBlinkCast = 'ADsi'//cloud ability for dummy caster
DeathNinja_DeathCloudCloudCast = 'ADs2'//cloud ability for dummy caster
//DeathNinja_AnimatedDeath = 'A05N'//this ability is not triggered
//DeathNinja_DeathWalkSlowBuff = 'B000'//the death walk slowing buff011
//DeathNinja_DeathCloudBuff = 'B012'//the death cloud buff
//DeathNinja_AnimatedDeathBuff = 'B014'//the animated death buff
//DeathNinja_DeathWalkBuff = 'B016'//the death walker buff
DeathNinja_Zael = 'USkL'//the death ninja
}
endlibrary
scope DeathWalk initializer I
globals
private constant real PERIOD = 0.34
endglobals
private struct Data
unit cast
unit dum
integer walk
integer orb
timer tim
static method create takes unit cast returns Data
local Data d = Data.allocate()
set d.cast = cast
set d.walk = GetUnitAbilityLevel(d.cast, DeathNinja_DeathWalk)
set d.tim = NewTimer()
call SetTimerData(d.tim, d)
return d
endmethod
method onDestroy takes nothing returns nothing
call ReleaseTimer(.tim)
endmethod
endstruct
private function Callback takes nothing returns boolean
local timer tim = GetExpiredTimer()
local Data d = GetTimerData(tim)
set d.dum = CreateUnit(GetOwningPlayer(d.cast), DeathNinja_DeathOrb, GetUnitX(d.cast), GetUnitY(d.cast), 270.)
call UnitApplyTimedLife(d.dum, 'BTLF', 2.0 + (1.0 * I2R(d.walk)))
call d.destroy()
set tim = null
return false
endfunction
private function Actions takes nothing returns boolean
local Data d = Data.create(GetTriggerUnit())
call TimerStart(d.tim, PERIOD, true, function Callback)
return false
endfunction
public function I takes nothing returns nothing
effectstartGT(Actions, DeathNinja_DeathWalk)
endfunction
endscope
scope DeathBlink initializer I
globals
private constant real PERIOD = 0.34
endglobals
private struct Data
unit cast
unit dum
timer tim
static method create takes unit cast returns Data
local Data d = Data.allocate()
set d.cast = cast
set d.tim = NewTimer()
call SetTimerData(d.tim, d)
return d
endmethod
method onDestroy takes nothing returns nothing
call ReleaseTimer(.tim)
endmethod
endstruct
private function Callback takes nothing returns boolean
local timer tim = GetExpiredTimer()
local Data d = GetTimerData(tim)
set d.dum = CreateUnit(GetOwningPlayer(d.cast), DeathNinja_DeathOrb, GetUnitX(d.cast), GetUnitY(d.cast), 270.)
//if d.cloud > 0 then
call UnitAddAbility(d.dum, DeathNinja_DeathCloudBlinkCast)
call IssuePointOrder(d.dum, "cloudoffog", GetUnitX(d.cast), GetUnitY(d.cast))
//endif
call UnitApplyTimedLife(d.dum, 'BTLF', 2.0)
call d.destroy()
set tim = null
return false
endfunction
private function Actions takes nothing returns boolean
local Data d = Data.create(GetTriggerUnit())
call TimerStart(d.tim, PERIOD, true, function Callback)
return false
endfunction
public function I takes nothing returns nothing
effectstartGT(Actions, 'ADbl')
endfunction
endscope
scope DeathCloud initializer I
private function f takes nothing returns boolean
location tl = GetSpellTargetLoc();real tx = GetLocationX(tl),ty = GetLocationY(tl)
unit ut = GetTriggerUnit(),u = CreateUnit(GetOwningPlayer(ut), DeathNinja_DeathOrb, tx, ty, 270.00)
integer lvl = GetUnitAbilityLevel(ut,DeathNinja_DeathCloud)
UnitAddAbility(u, DeathNinja_DeathCloudCloudCast)
SetUnitAbilityLevel(u, DeathNinja_DeathCloudCloudCast, lvl)
IssuePointOrder(u, "cloudoffog", tx,ty)
UnitApplyTimedLife(u, 'BTLF', 2.0 )
RemoveLocation(tl);tl = null;u = null;ut = null;return false;endfunction
public function I takes nothing returns nothing
effectstartGT(f,DeathNinja_DeathCloud)
XE_PreloadAbility(DeathNinja_DeathCloudCloudCast)
XE_PreloadAbility(DeathNinja_DeathCloudBlinkCast)
endfunction
endscope
/*
struct moveUnit
unit u
private method onTimedLoop takes nothing returns boolean
call SetUnitX(u, GetUnitX(u) + 100.0* TimedLoop_PERIOD )
if ( GetUnitX(u) >= 5000) then
return false
endif
return true
endmethod
implement TimedLoop
static method create takes unit u returns moveUnit
local moveUnit m= moveUnit.allocate()
set m.u = u
call m.startTimedLoop()
return m
endmethod
endstruct
call moveUnit.create(GetTriggerUnit()) //and see...
*/
//animated death
//==This spell is just a self only roar spell that heals, it isn't triggered.
//==it has an animate dead spell effect to make it look cool.
//==Because the Death Ninja takes away his own life power to cast spells...
//==they of course will cost little or no mana and his ultimate will regenerate him
//==and his ultimate will cost mana.
//==A05N
//==B014
//TESH.scrollpos=-1
//TESH.alwaysfold=0
native Acos takes real x returns real
Script code
native Asin takes real y returns real
native Cos takes real radians returns real
native CreateCorpse takes player whichPlayer, integer unitid, real x, real y, real face returns unit
Script info
native CreateFogModifierRect takes player forWhichPlayer, fogstate whichState, rect where, boolean useSharedVision, boolean afterUnits returns fogmodifier
ative CreateMIDISound takes string soundLabel, integer fadeInRate, integer fadeOutRate returns sound
Name: Acos
Author: Blizzard
Submitted: Sun Nov 28, 2004 8:30 pm
Lines: 3
function RegionAddCircle takes region whichRegion, real x, real y, real radius returns nothing
local real r = 16.00
local real w = 0
local rect t = Rect(0, 0, 0, 0)
loop
exitwhen r > radius
set w = Cos(Asin(r / radius)) * radius
call SetRect(t, x - w, y + r - 16.00, x + w, y + r + 16.00)
call RegionAddRect(whichRegion, t)
call SetRect(t, x - w, y - r - 16.00, x + w, y - r + 16.00)
call RegionAddRect(whichRegion, t)
set r = r + 32.00
endloop
call RemoveRect(t)
set t = null
endfunction
Functions:
native DisplayTimedTextFromPlayer takes player toPlayer, real x, real y, real duration, string message returns nothing
native DisplayTimedTextToPlayer takes player toPlayer, real x, real y, real duration, string message returns nothing
native DisplayTextToPlayer takes player toPlayer, real x, real y, string message returns nothing
native CripplePlayer takes player whichPlayer, force toWhichPlayers, boolean flag returns nothing
Description
function GetArcLength takes real radius, real centAngle returns real
return ((2*bj_PI*radius)*(centAngle/360))
endfunction
function GetArcLength takes real radius, real centAngle returns real
return radius*centAngle*bj_DEGTORAD
endfunction
function JumpParabola takes real dist, real maxdist,real curve returns real
local real t = (dist*2)/maxdist-1
return (-t*t+1)*(maxdist/curve)
endfunction
A generic function for calculating the height in a jump skill..
u use a timer to move the unit, from position 1 to position 2, the distance between position 1 and 2 is a constant and is used for maxdist for the function
the current distance from position 1 (or 2, w/e u pick result is the same anyway) to the unit is dist, so u need to calculate it everytime the timer is ran.
and curve.. hard to explain, so here's a pic
Returns the Arc Cosine of x. Do not use values outside less than -1 or greater than 1.
Study a math book/site if you don't know the trigonometrical functions.
native AddItemToStock takes unit whichUnit, integer itemId, integer currentStock, integer stockMax returns nothing
native CommandAI takes player num, integer command, integer data returns nothing
function AngleBetweenPointsXY takes real x1, real y1, real x2, real y2 returns real
return Atan2BJ((y2-y1),(x2-x1))
endfunction
Script info
Name: AddItemToStock
Author: Blizzard
Submitted: Sun Nov 28, 2004 8:30 pm
Lines: 3
Functions:
Description
Sets the current availability of an item in the specified shop. The stock is set to currentStock and will grow until reaching stockMax.
//TESH.scrollpos=84
//TESH.alwaysfold=0
scope RazorGalefudo initializer Init
define private DISABLE_SPELL_SOUND = true
private real GlideSpeed(integer level){return 750.0}
private real GlideMaxDistance(integer level){return 500.0}
private real GlideMinDistance(integer level){return 200.0}
private real Damage(integer level){return 120.0+(level*60.0)}
private real DamageRadius(integer level){return 160.0}
private void DamageOptions(xedamage spellDamage){set spellDamage.dtype=DAMAGE_TYPE_UNIVERSAL
set spellDamage.atype=PureWarriorAttack;set spellDamage.tag=0;set spellDamage.exception=UNIT_TYPE_STRUCTURE}
private keyword instance;globals;private xedamage xed;private timer slide;private instance array instances
private integer instanceCount = 0;private group tempg = CreateGroup();private boolexpr tempbx
private real tempx;private real tempy;private instance tempsi;endglobals
private boolean Targets(){unit u=GetFilterUnit()
if not(IsUnitInGroup(u, tempsi.affected)) and IsUnitInRangeXY(u, tempx, tempy, DamageRadius(tempsi.level)) then
GroupAddUnit(tempsi.affected, u);u=null;return true;endif;u=null;return false}
private void Periodic(){integer i=0;loop;exitwhen i>=instanceCount;tempsi = instances[i]
tempx = GetUnitX(tempsi.caster)+tempsi.dx;tempy = GetUnitY(tempsi.caster)+tempsi.dy
tempsi.time=tempsi.time-0.025;if tempsi.time>0.0 and IsTerrainWalkable(tempx,tempy) then
SetUnitX(tempsi.caster, tempx);SetUnitY(tempsi.caster, tempy);if tempsi.whenToDamage <= 0 then
tempsi.whenToDamage=3
GroupEnumUnitsInRange(tempg,tempx,tempy,DamageRadius(tempsi.level) + XE_MAX_COLLISION_SIZE , tempbx)
xed.damageGroup(tempsi.caster, tempg, Damage(tempsi.level));endif;tempsi.whenToDamage=tempsi.whenToDamage-1
i=i+1;else;tempsi.finish();endif;endloop}
private void Animation_Finish(){instance si=instance(GetTimerData(GetExpiredTimer()))
SetUnitTimeScale(si.caster, 1.0);si.destroy()}
private void Animation_Child(){instance si=instance(GetTimerData(GetExpiredTimer()))
SetUnitFlyHeight(si.caster, 0.0, 48.0/0.125*si.timescale);ReleaseTimer(GetExpiredTimer())}
private void Animation(){instance si=instance(GetTimerData(GetExpiredTimer()));timer t;if si.active then
t = NewTimer();SetTimerData(t, integer(si));SetUnitTimeScale(si.caster, si.timescale)
SetUnitAnimationByIndex(si.caster, 6);SetUnitFlyHeight(si.caster, 48.0, 48.0/0.125*si.timescale)
TimerStart(t, 0.125/si.timescale, false, function Animation_Child)
TimerStart(GetExpiredTimer(), 0.35/si.timescale, false, function Animation)
si.timescale=si.timescale+si.dtimescale*0.35/si.timescale;else;SetUnitTimeScale(si.caster, 1.5)
TimerStart(GetExpiredTimer(), 0.45, false, function Animation_Finish);endif}
private struct instance;private integer index;private timer t;real timescale;real dtimescale;boolean active = true
boolean interrupted = false;integer whenToDamage = 3;unit caster;integer level;real dx;real dy
group affected;real time
static method create takes unit caster, integer level, real targetx, real targety returns instance
instance si=instance.allocate();real distance;real factor=1.0;UnitAddAbility(caster, XE_HEIGHT_ENABLER)
UnitRemoveAbility(caster, XE_HEIGHT_ENABLER);si.caster=caster;si.level=level;si.dx=targetx-GetUnitX(caster)
si.dy=targety-GetUnitY(caster);distance = SquareRoot(si.dx*si.dx+si.dy*si.dy)+1.0
if distance>GlideMaxDistance(level) then;factor = GlideMaxDistance(level)/distance
elseif distance<GlideMinDistance(level) then;factor = GlideMinDistance(level)/distance;endif
si.time = factor*distance/GlideSpeed(level);factor = factor/si.time*0.025;si.dx=si.dx*factor
si.dy=si.dy*factor;si.timescale=1.2
si.dtimescale=(0.9-1.2)/(GlideMaxDistance(level)/GlideSpeed(level))
if si.affected == null then;si.affected=CreateGroup();endif;si.t=NewTimer();SetTimerData(si.t, integer(si))
TimerStart(si.t, 0.0, false, function Animation);if DISABLE_SPELL_SOUND then
VolumeGroupSetVolume(SOUND_VOLUMEGROUP_SPELLS, 0.0);endif;if instanceCount==0 then;slide=NewTimer()
TimerStart(slide, 0.025, true, function Periodic);endif;instances[instanceCount]=si
si.index=instanceCount;instanceCount=instanceCount+1;return si;endmethod
static method get takes unit u returns instance;integer i=0;loop;exitwhen i==instanceCount
if instances[i].caster==u then;return instances[i];endif;i++;endloop;return 0;endmethod
method finish takes nothing returns nothing;this.active=false;instanceCount=instanceCount-1
instances[this.index]=instances[instanceCount];instances[instanceCount].index=this.index
if instanceCount==0 then;ReleaseTimer(slide);if DISABLE_SPELL_SOUND then;VolumeGroupReset()
endif;endif;endmethod
method onDestroy takes nothing returns nothing;if not(this.interrupted) then
IssueImmediateOrder(this.caster, "stop");endif;ReleaseTimer(this.t);GroupClear(this.affected)
endmethod;endstruct
private void SpellEffect(){integer lvl;unit u;location l;if GetSpellAbilityId() == 'A0CO' then
lvl =GetUnitAbilityLevel(GetTriggerUnit(), 'A0CO');u = GetSpellTargetUnit();if u == null then
l = GetSpellTargetLoc();instance.create(GetTriggerUnit(), lvl, GetLocationX(l), GetLocationY(l))
RemoveLocation(l);l = null;else
instance.create(GetTriggerUnit(), lvl, GetUnitX(u), GetUnitY(u));u = null;endif;endif}
private void SpellStop(){instance si;if GetSpellAbilityId() == 'A0CO' then
si = instance.get(GetTriggerUnit());if si != 0 then;si.finish();si.interrupted=true;endif;endif}
private void Init(){trigger tr = CreateTrigger()
TriggerRegisterAnyUnitEventBJ( tr, EVENT_PLAYER_UNIT_SPELL_EFFECT );TriggerAddAction( tr, function SpellEffect )
tr = CreateTrigger(); TriggerRegisterAnyUnitEventBJ( tr, EVENT_PLAYER_UNIT_SPELL_ENDCAST )
TriggerAddAction( tr, function SpellStop );tempbx=Condition(function Targets);xed=xedamage.create()
DamageOptions(xed)};endscope
scope ShadowServant initializer I
globals;private real PERIOD = 0.34;private unit array z[3];private integer d=0;endglobals
define {}
private struct Data;real x,y;string s;widget w;timer tim;integer m;unit r
static method create takes string s, real x, real y, widget w, integer q, unit r returns Data
Data da = Data.allocate()
if q==1 then
da.s=s;da.w=w;da.m=0;da.tim = NewTimer();SetTimerData(da.tim, da);return da
elseif q==2 then
da.s=s;da.x=x;da.y=y;da.m=0;da.tim = NewTimer();SetTimerData(da.tim, da);return da
elseif q==3 then
da.s=s;da.m=0;da.tim = NewTimer();SetTimerData(da.tim, da);return da
else
da.m=0;da.r=r;whilenot(da.m++>3){
SetUnitX(z[da.m],gux(da.r));SetUnitY(z[da.m],guy(da.r));SetUnitFacing(z[da.m],GetUnitFacing(da.r));ShowUnit(z[da.m],true)
UnitAddAbility(z[da.m],'Aloc');PauseUnit(z[da.m],false);SuspendHeroXP(z[da.m],false);if GetHeroLevel(da.r)!=GetHeroLevel(z[da.m]) then
SetHeroLevel(z[da.m],GetHeroLevel(da.r),false);SuspendHeroXP(z[da.m],true);endif;}
da.tim = NewTimer();SetTimerData(da.tim, da);return da;endif;endmethod
method onDestroy takes nothing returns nothing;ReleaseTimer(.tim);endmethod;endstruct
private boolean CallbackF(){timer tim = GetExpiredTimer();Data da = GetTimerData(tim)
da.m++;if da.m >75 then ;da.m=0;whilenot(da.m++>3){SetUnitX(z[da.m],-3000.0);SetUnitY(z[da.m],3000.0)
ShowUnit(z[da.m],false);UnitRemoveAbility(z[da.m],'Aloc');PauseUnit(z[da.m],true);};da.destroy();endif;set tim = null;return false}
private boolean f(){Data da = Data.create("q",0.0,0.0,null,4,GetTriggerUnit())
TimerStart(da.tim, PERIOD, true, function CallbackF);return false}
private boolean g(){d++;z[d]=CD;UnitRemoveType(z[d],UNIT_TYPE_HERO);ShowUnit(z[d],false);SetUnitVertexColor(z[d],255,255,255,135)
if GetUnitAbilityLevel(gtu,'A0FY')>0 then;UnitAddAbility(z[d],'A0FY');SetUnitAbilityLevel(z[d],'A0FY',GetUnitAbilityLevel(gtu,'A0FY'));endif
if GetUnitAbilityLevel(gtu,'A0G3')>0 then;UnitAddAbility(z[d],'A0G3');SetUnitAbilityLevel(z[d],'A0G3',GetUnitAbilityLevel(gtu,'A0G3'));endif
if GetUnitAbilityLevel(gtu,'A0CO')>0 then;UnitAddAbility(z[d],'A0CO');SetUnitAbilityLevel(z[d],'A0CO',GetUnitAbilityLevel(gtu,'A0CO'));endif
if GetUnitAbilityLevel(gtu,'A0G6')>0 then;UnitAddAbility(z[d],'A0G6');SetUnitAbilityLevel(z[d],'A0G6',GetUnitAbilityLevel(gtu,'A0G6'));endif
if GetHeroLevel(gtu)!=GetHeroLevel(z[d]) then;SetHeroLevel(z[d],GetHeroLevel(gtu),false);endif;SuspendHeroXP(z[d],true)
PauseUnit(z[d],true);return false}
private boolean h(){if d>0 then;if GetUnitAbilityLevel(gtu,'A0G3')==1 then;UnitAddAbility(z[1],'A0G3');endif
SetUnitAbilityLevel(z[1],'A0G3',GetUnitAbilityLevel(gtu,'A0G3'));endif;if d>1 then;SetUnitAbilityLevel(z[2],'A0G3',GetUnitAbilityLevel(gtu,'A0G3'))
endif;if d>2 then;SetUnitAbilityLevel(z[3],'A0G3',GetUnitAbilityLevel(gtu,'A0G3'));endif;return false}
private boolean i(){if d>0 then;if GetUnitAbilityLevel(gtu,'A0FY')==1 then;UnitAddAbility(z[1],'A0FY');endif
SetUnitAbilityLevel(z[1],'A0FY',GetUnitAbilityLevel(gtu,'A0FY'));endif;if d>1 then;SetUnitAbilityLevel(z[2],'A0FY',GetUnitAbilityLevel(gtu,'A0FY'))
endif;if d>2 then;SetUnitAbilityLevel(z[3],'A0FY',GetUnitAbilityLevel(gtu,'A0FY'));endif;return false}
private boolean j(){if GetUnitTypeId(gtu)==Z then;if d>0 then;if GetUnitAbilityLevel(gtu,'A0G6')==1 then;UnitAddAbility(z[1],'A0G6');endif
SetUnitAbilityLevel(z[1],'A0G6',GetUnitAbilityLevel(gtu,'A0G6'));endif;if d>1 then;SetUnitAbilityLevel(z[2],'A0G6',GetUnitAbilityLevel(gtu,'A0G6'))
endif;if d>2 then;SetUnitAbilityLevel(z[3],'A0G6',GetUnitAbilityLevel(gtu,'A0G6'));endif;endif;return false}
private boolean k(){if d>0 then;if GetUnitAbilityLevel(gtu,'A0CO')==1 then;UnitAddAbility(z[1],'A0CO');endif
SetUnitAbilityLevel(z[1],'A0CO',GetUnitAbilityLevel(gtu,'A0CO'));endif;if d>1 then;SetUnitAbilityLevel(z[2],'A0CO',GetUnitAbilityLevel(gtu,'A0CO'))
endif;if d>2 then;SetUnitAbilityLevel(z[3],'A0CO',GetUnitAbilityLevel(gtu,'A0CO'));endif;return false}
private void I(){trigger t=CreateTrigger();TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER)
TriggerAddCondition( t,Condition( function toC));TriggerAddAction( t, function to );set t = CreateTrigger( );TriggerRegisterAnyUnitEventBJ( t, \
EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER );TriggerAddCondition( t,Condition( function poC));TriggerAddAction( t, function po );set t = CreateTrigger( )
TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_ISSUED_ORDER );TriggerAddCondition( t,Condition( function ioC));TriggerAddAction( t, function io )
effectstartGT(f,'A0FS');skillGT(g,'A0FS');skillGT(h,'A0G3');skillGT(i,'A0FY');skillGT(j,'A0G6');skillGT(k,'A0CO');};endscope
//TESH.scrollpos=31
//TESH.alwaysfold=0
//SLARK, THE MURLOC
//==By SanKakU
//==
//==Don't bother looking in the quests section for credits because you won't see them there.
//==Just find what you want here in the Trigger editor. It's all here.
library STM uses MSGS
define CREDITSFORSTM={
ICONS
kola
MODELS
republicola
SKINS
chr2
sankaku
}
define {timedmsg=DisplayTimedTextToPlayer;void=nothing
getitemGT(x, y) = {GT_AddItemAcquiredAction(function x, y)}
targetorderGT(x,y) = {GT_AddTargetOrderAction(function x, y)}
pointorderGT(x,y) = {GT_AddPointOrderAction(function x, y)}
iorderGT(x,y) = {GT_AddNoTargetOrderAction(function x, y)}
unitdiesGT(x, y) = {GT_AddUnitDiesAction(function x, y)}
effectstartGT(x, y) = {GT_AddStartsEffectAction(function x, y)}
skillGT(x, y) = {GT_AddLearnsAbilityAction(function x, y)}
STM_PUE(x,y)={TriggerRegisterPlayerUnitEvent(x,Player(6),y,null)}
}
define iconTIME="UI\\Widgets\\BattleNet\\bnet-tournament-clock.blp"
globals
endglobals
//== snippet by Jesus4Lyf
function SetDistanceToUnit takes unit y, unit x, real dist returns nothing
local real rx=GetUnitX(x)-GetUnitX(y)
local real ry=GetUnitY(x)-GetUnitY(y)
local real factor=dist/SquareRoot(rx*rx+ry*ry)
call SetUnitX(x,GetUnitX(y)+rx*factor)
call SetUnitY(x,GetUnitY(y)+ry*factor)
endfunction
function SetDistanceToXY takes real rx, real ry, unit x, real dist returns nothing
local real tx=GetUnitX(x)-rx
local real ty=GetUnitY(x)-ry
local real factor=dist/SquareRoot(tx*tx+ty*ty)
call SetUnitX(x,rx+tx*factor)
call SetUnitY(x,ry+ty*factor)
endfunction
//extension to snippet.
function getdis takes unit x, unit y returns real
local real dx = GetUnitX(x) - GetUnitX(y)
local real dy = GetUnitY(x) - GetUnitY(y)
return SquareRoot(dx * dx + dy * dy)
endfunction
function getdisr takes real x, real y, unit z returns real
local real dx = x - GetUnitX(z)
local real dy = y - GetUnitY(z)
return SquareRoot(dx * dx + dy * dy)
endfunction
//==end of extension to snippet
function GetHeroSlotMultiboard takes integer heroid, integer playerid returns integer
return heroid+((playerid - 2)*3)
endfunction
//
public function MID takes integer a, integer b, integer c, real x, real y, itempool p returns nothing
call ItemPoolAddItemType(p,a,6)//monster common itemdrop
call ItemPoolAddItemType(p,b,3)//monster uncommon itemdrop
call ItemPoolAddItemType(p,c,1)//monster rare itemdrop
call PlaceRandomItem(p,x,y)
endfunction
//not sure if i'm going to end up using this function, but i used to use it so...here it is
public function IUTNI takes unit u, unittype ut returns boolean
return IsUnitType(u, ut) and IsUnitIllusion(u)==false
endfunction
//this is an interesting function, i need to write more like this
public function IUIDNI takes unit u, integer uid returns boolean
return GetUnitTypeId(u)==uid and IsUnitIllusion(u)==false
endfunction
endlibrary
//TESH.scrollpos=-1
//TESH.alwaysfold=0
library texttagsgalore
globals
private constant integer MEAN_CHAR_WIDTH = 8
private constant integer MAX_TEXT_SHIFT = 32
private constant real FONT_SIZE = 0.03
private constant real VELOCITY_X = 0
private constant real VELOCITY_Y = 0.01
private constant real LIFESPAN = 4
private constant real HEIGHT = 8
private constant real FADE_POINT = 2.5
endglobals
function TextTagOnUnit takes unit whichUnit, string text returns nothing
local texttag t = CreateTextTag()
local integer shift = IMinBJ(StringLength(text), MAX_TEXT_SHIFT) * MEAN_CHAR_WIDTH
call SetTextTagText(t, text, FONT_SIZE)
call SetTextTagPos(t, GetUnitX(whichUnit)-shift, GetUnitY(whichUnit), HEIGHT)
call SetTextTagVelocity(t, VELOCITY_X, VELOCITY_Y)
call SetTextTagVisibility(t, true)
call SetTextTagFadepoint(t, FADE_POINT)
call SetTextTagLifespan(t, LIFESPAN)
call SetTextTagPermanent(t, false)
set t = null
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
scope TimerEvents initializer I
globals
private boolean debugmode=true
endglobals
private function mbtime takes nothing returns boolean
call ConditionalTriggerExecute( gg_trg_mbSTM )
call SetPlayerState(Player(6), PLAYER_STATE_RESOURCE_GOLD, 1)
return false
endfunction
private function mbtimeaddsec takes nothing returns boolean
local integer x=S2I(jBoard_STM.field(0,1).getSuffix())+1
if x==60 then
call jBoard_STM.field(0,1).setSuffix("0")
set x=S2I(jBoard_STM.field(0,1).getPrefix())+1
if x==60 then
call jBoard_STM.field(0,1).setPrefix("0")
return false
endif
call jBoard_STM.field(0,1).setPrefix(I2S(x))
return false
endif
call jBoard_STM.field(0,1).setSuffix(I2S(x))
return false
endfunction
globals
group TIMERGROUP=CreateGroup()
endglobals
private function ru0 takes nothing returns boolean
if GetUnitAbilityLevel(GetFilterUnit(), 'A0C3')>1 then
call IssuePointOrder(GetFilterUnit(), "attack", 3000.0,3000.0 )
endif
return false
endfunction
private function ru1 takes nothing returns boolean
if GetUnitAbilityLevel(GetFilterUnit(), 'A0C3')>1 then
call IssuePointOrder(GetFilterUnit(), "attack", -3000.0,-3000.0 )
endif
return false
endfunction
private function removeall takes nothing returns nothing
call GroupEnumUnitsOfPlayer(TIMERGROUP, Player(0), Filter(function ru0))
call GroupEnumUnitsOfPlayer(TIMERGROUP, Player(1), Filter(function ru1))
endfunction
private function fbf takes nothing returns boolean
call removeall()
return false
endfunction//
public function I takes nothing returns nothing
local trigger t = CreateTrigger( )
call TriggerRegisterTimerEvent( t, 1.0 ,false)
call TriggerAddCondition( t,Condition( function mbtime ))
set t= CreateTrigger()
call TriggerRegisterTimerEvent( t, 1.0 ,true)
call TriggerAddCondition( t,Condition( function mbtimeaddsec ))
endfunction
endscope
//TESH.scrollpos=7
//TESH.alwaysfold=0
library Pool
//******************************************************************************
//* BY: Rising_Dusk
//*
//* This script gives the user access to general integer pools. A pool is a data
//* structure that allows you to give entries to it a weight. This weight
//* essentially scales how likely certain random accesses to the pool will be
//* returned by the internal .getRandomInt method. Pools can be useful in any
//* number of ways, such as item drops, randomizing enemy encounters, randomly
//* selecting rects weighted by area, and so forth.
//*
//******************************************************************************
//*
//* Example usage:
//* local intpool ip = intpool.create()
//* call ip.addInt(1, 1.0)
//* call ip.addInt(2, 0.5)
//* call ip.getRInt()
//* call ip.getChance(2)
//* call ip.getWeight(2)
//* call ip.removeInt(1)
//* //or
//8 globals
/// intpool firstip
/// intpool secondip
/// endglobals
// //later on:
/// set firstip=intpool.create()
//* call firstip.addInt(1, 1.0)
//* call firstip.addInt(2, 0.5)
//* call firstip.getRInt()
//* call firstip.getChance(2)
//* call firstip.getWeight(2)
//* call firstip.removeInt(1)
//* //etc...
/// //global intpool are usually easier to use because you don't have to keep
/// //creating them over and over again and unless they need to change with
/// //events or time or whatever you only need 1 line to use them.
/// //that is, call nameofintpool.getRInt()
///
//* You will first need to create an intpool as shown above. Once you've done
//* that, you may use the .addInt method to add an entry to the pool with a
//* specific weight. The example above adds 2 integers, one twice as likely to
//* be randomly selected as the other. That means for the above example, the
//* .getRInt method will 66% of the time return 1 and 33% of the time
//* return 2. If you want to remove an entry from an intpool, the .removeInt
//* method is what you will want to use. If you would like to update an entry's
//* weight after already adding it, simply use .addInt again with the new
//* weight.
//*
//* The .getChance and .getWeight methods are there for convenience. If you are
//* interested in the exact chance of the intpool returning a specific entry,
//* then you should use .getChance to obtain the decimal chance out of 1. If you
//* want to know the weight input for a specific entry, .getWeight will return
//* that for you.
//*
//* When adding an entry to the intpool with .addInt, the actual magnitude of
//* the weight doesn't matter. What matters is its magnitude relative to the
//* magnitudes of all other entries in the intpool. This means that it is ok to
//* use very large or very small weights and is done at the user's discretion.
//*
//* It is worth noting that if you use .getRInt on an intpool with no
//* entries, the function will return INTPOOL_NO_ENTRIES, which is about as
//* random an integer as possible so as to avoid people accidentally using it.
//*
globals
//These constants can be changed//i think he meant to say can change the value
private constant integer MAX_INSTANCES = 2500//8191//current is about 2?
//i don't understand how the numbers work...
private constant integer MAX_ENTRIES = 150//256//current is around 43?
constant integer INTPOOL_NO_ENTRIES = 0x672819
//what is the point? why not return 0? what is that integer!?
//Don't change the following global declaration//i changed it lol(the name)
private hashtable poolht = InitHashtable()
endglobals
struct intpool[MAX_INSTANCES]
private integer Cnt = 0
private real WeightTotal = 0.
private integer array Entries[MAX_ENTRIES]
private real array Weights[MAX_ENTRIES]
method getWeight takes integer entry returns real
return Weights[LoadInteger(poolht, integer(this), entry)]
endmethod
method getChance takes integer entry returns real
if WeightTotal > 0. then
return Weights[LoadInteger(poolht, integer(this), entry)]/WeightTotal
endif
return 0.
endmethod
method addInt takes integer entry, real weight returns nothing
local integer in = 0
if .Cnt == MAX_ENTRIES then
//Can't hold any more entries
debug call BJDebugMsg(SCOPE_PREFIX+"Error: .addEntry has reached MAX_ENTRIES")
return
elseif weight <= 0. then
//Zero or negative weights make no sense
debug call BJDebugMsg(SCOPE_PREFIX+"Error: .addEntry can't take zero or negative weights")
return
endif
set in = LoadInteger(poolht, integer(this), entry)
if in > 0 then
//Update old entry
set .WeightTotal = .WeightTotal - .Weights[in] + weight
set .Weights[in] = weight
else
//Make a new entry
set .Cnt = .Cnt + 1
call SaveInteger(poolht, integer(this), entry, .Cnt)
set .Entries[.Cnt] = entry
set .Weights[.Cnt] = weight
set .WeightTotal = .WeightTotal + weight
endif
endmethod
method removeInt takes integer entry returns nothing
local integer in = LoadInteger(poolht, integer(this), entry)
if in > 0 then
call RemoveSavedInteger(poolht, integer(this), entry)
//Remove its entry in the arrays
set .WeightTotal = .WeightTotal - .Weights[in]
set .Entries[in] = .Entries[.Cnt]
set .Weights[in] = .Weights[.Cnt]
set .Cnt = .Cnt - 1
debug else
debug call BJDebugMsg(SCOPE_PREFIX+"Error: .removeEntry entry doesn't exist")
endif
endmethod
method getRInt takes nothing returns integer
local real r = GetRandomReal(0, .WeightTotal)
local integer c = 0
if .WeightTotal <= 0. then
debug call BJDebugMsg(SCOPE_PREFIX+"Error: intpool has no entries")
return INTPOOL_NO_ENTRIES
endif
loop
set r = r - .Weights[c]
exitwhen r <= 0
set c = c + 1
endloop
return .Entries[c]
endmethod
endstruct
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=0
//TESH.alwaysfold=0
library ParabolicMovement2
function ParabolaZ2 takes real y0, real y1, real h, real d, real x returns real
local real A = (2*(y0+y1)-4*h)/(d*d)
local real B = (y1-y0-A*d*d)/d
return A*x*x + B*x + y0
endfunction
endlibrary
//Requires:
//TimedLoop
//xebasic
//ParabolicMovement2.
//It is also recommended to use BoundSentinel in most cases, but this is not neccesary.
//function UnitJump takes unit whichunit, real speed, real distance, real direction, real height returns nothing
//whichunit is the unit that you want to move.
//speed is how fast the unit is moving.
//distance is how far the unit will be moved.
//height is the maximum height of the parabola.
library JumpSystem requires TimedLoop, xebasic, ParabolicMovement2
private struct Jump
static location Loc = Location(0, 0)
implement TimedLoop
unit target
real distance = 0
real increment
real max
real p
real x = 0
real y = 0
real cos
real sin
real height
real y0
real y1
method onTimedLoop takes nothing returns boolean
set this.x = this.x + this.cos
set this.y = this.y + this.sin
set this.distance = this.distance + this.increment
call MoveLocation(Loc, this.x, this.y)
call SetUnitFlyHeight(this.target, ParabolaZ2(this.y0, this.y1, this.height,this.max,this.distance) - GetLocationZ(Loc), 0)
call SetUnitX(this.target, this.x)
call SetUnitY(this.target, this.y)
if this.distance >= this.max then
return TimedLoop_STOP
endif
return TimedLoop_CONTINUE
endmethod
static method create takes unit whichunit, real speed, real distance, real direction, real height returns thistype
local thistype d = thistype.allocate()
local real r
set d.target = whichunit
set d.max = distance
set d.increment = speed * TimedLoop_PERIOD
set d.cos = d.increment * Cos(direction)
set d.sin = d.increment * Sin(direction)
set d.height = height
set d.x = GetUnitX(whichunit)
set d.y = GetUnitY(whichunit)
call MoveLocation(Loc, d.x, d.y)
set d.y0 = GetLocationZ(Loc)
call MoveLocation(Loc, d.x + distance * Cos(direction), d.y + distance * Sin(direction))
set d.y1 = GetLocationZ(Loc)
call UnitAddAbility(d.target, XE_HEIGHT_ENABLER)
call UnitRemoveAbility(d.target, XE_HEIGHT_ENABLER)
call d.startTimedLoop()
return d
endmethod
endstruct
function UnitJump takes unit whichunit, real speed, real distance, real direction, real height returns nothing
call Jump.create(whichunit, speed, distance, direction, height)
endfunction
endlibrary
//TESH.scrollpos=-1
//TESH.alwaysfold=0
function Trig_Untitled_Trigger_007_Actions takes nothing returns nothing
local quest q = CreateQuest()
call QuestSetTitle(q, "Damage %")
call QuestSetDescription(q, "Primary Attack and Defense Types:
M=Mage
W=Warrior
N=Normal
F=Fortified
H=Heavy
P=Piercing
N Deals 50% Damage to M,W, 25% to F, H.
W Deals 150% Damage to M, 50% to F, 25% to H.
M Deals 150% Damage to W, 50% to F, 25% to H.
P Deals 75% Damage to F,M,W, 25% to H.
Spells deal 50% Damage to F, 300% to H.
Damage is 100% for everything else.")
call QuestSetIconPath(q, "ReplaceableTextures\\CommandButtons\\BTNAmbush.blp")
call QuestSetRequired(q, true)
call QuestSetDiscovered(q, true)
call QuestSetCompleted(q, false)
set q = CreateQuest()
call QuestSetTitle(q, "Just a filler")
call QuestSetDescription(q, "this is just a filler description")
call QuestSetIconPath(q, "ReplaceableTextures\\CommandButtons\\BTNAmbush.blp")
call QuestSetRequired(q, true)
call QuestSetDiscovered(q, true)
call QuestSetCompleted(q, false)
set q = CreateQuest()
call QuestSetTitle(q, "Just a filler")
call QuestSetDescription(q, "this is just a filler description")
call QuestSetIconPath(q, "ReplaceableTextures\\CommandButtons\\BTNAmbush.blp")
call QuestSetRequired(q, true)
call QuestSetDiscovered(q, true)
call QuestSetCompleted(q, false)
set q = CreateQuest()
call QuestSetTitle(q, "Just a filler")
call QuestSetDescription(q, "this is just a filler description")
call QuestSetIconPath(q, "ReplaceableTextures\\CommandButtons\\BTNAmbush.blp")
call QuestSetRequired(q, false)
call QuestSetDiscovered(q, true)
call QuestSetCompleted(q, false)
set q = CreateQuest()
call QuestSetTitle(q, "Just a filler")
call QuestSetDescription(q, "this is just a filler description")
call QuestSetIconPath(q, "ReplaceableTextures\\CommandButtons\\BTNAmbush.blp")
call QuestSetRequired(q, false)
call QuestSetDiscovered(q, true)
call QuestSetCompleted(q, false)
set q = CreateQuest()
call QuestSetTitle(q, "Just a filler")
call QuestSetDescription(q, "this is just a filler description")
call QuestSetIconPath(q, "ReplaceableTextures\\CommandButtons\\BTNAmbush.blp")
call QuestSetRequired(q, false)
call QuestSetDiscovered(q, true)
call QuestSetCompleted(q, false)
set q = null
endfunction
//===========================================================================
function InitTrig_quests takes nothing returns nothing
set gg_trg_quests = CreateTrigger( )
call TriggerAddAction( gg_trg_quests, function Trig_Untitled_Trigger_007_Actions )
endfunction
//TESH.scrollpos=84
//TESH.alwaysfold=0
scope DeathWalkHOLD initializer I
globals
private constant real DISTANCE = 200.00
private constant real PERIOD = .05
private constant integer ORBCOUNT = 300//0.05*300=15
private constant string LINK="DRAB"
endglobals
private struct Data
unit cast
real rx
real ry
real ra
real rb
integer orb
timer tim
lightning link
static method create takes unit cast returns Data
local Data d = Data.allocate()
set d.cast = cast
set d.rx=GetUnitX(d.cast)
set d.ry=GetUnitY(d.cast)
set d.link=AddLightning(GetAbilityEffectById('A0D4',EFFECT_TYPE_LIGHTNING,0),true,d.rx,d.ry,d.rx,d.ry)
set d.orb = 0
set d.tim = NewTimer()
call SetTimerData(d.tim, d)
return d
endmethod
method onDestroy takes nothing returns nothing
call ReleaseTimer(.tim)
endmethod
endstruct
private function Callback takes nothing returns boolean
local timer tim = GetExpiredTimer()
local Data d = GetTimerData(tim)
if getdisr(d.rx,d.ry,d.cast)>=DISTANCE then
call SetDistanceToXY(d.rx,d.ry,d.cast,DISTANCE)
endif
call MoveLightning(d.link,true,d.rx,d.ry,GetUnitX(d.cast),GetUnitY(d.cast))
set d.orb = d.orb + 1
if d.orb >= ORBCOUNT then
call DestroyLightning(d.link)
call d.destroy()
endif
set tim = null
return false
endfunction
private function Actions takes nothing returns boolean
local Data d = Data.create(GetTriggerUnit())
call TimerStart(d.tim, PERIOD, true, function Callback)
return false
endfunction
public function I takes nothing returns nothing
call GT_AddStartsEffectAction(function Actions, 'A0D4')
//call XE_PreloadAbility('A0D4')
endfunction
endscope
scope DeathWalkPull initializer I
globals
private constant real DISTANCE = 200.00
private constant real PERIOD = .05
private constant string LINK="DRAB"
endglobals
private struct Data
unit cast
unit targ
integer walk
real dist
real rx
real ry
timer tim
//lightning link
static method create takes unit cast, unit targ returns Data
local Data d = Data.allocate()
set d.cast = cast
set d.targ = targ
set d.walk = GetUnitAbilityLevel(d.cast,'A0D7')
set d.rx=GetUnitX(d.cast)
set d.ry=GetUnitY(d.cast)
set d.dist=getdis(d.cast,d.targ)
//set d.link=AddLightning(LINK,true,GetUnitX(d.cast),GetUnitY(d.cast),GetUnitX(d.targ),GetUnitY(d.targ))
set d.tim = NewTimer()
call SetTimerData(d.tim, d)
return d
endmethod
method onDestroy takes nothing returns nothing
call ReleaseTimer(.tim)
endmethod
endstruct
private function Callback takes nothing returns boolean
local timer tim = GetExpiredTimer()
local Data d = GetTimerData(tim)
call SetUnitX(d.cast,d.rx)
call SetUnitY(d.cast,d.ry)
set d.dist = d.dist -(d.walk * 5.00)
call SetDistanceToUnit(d.cast,d.targ,d.dist)
//call MoveLightning(d.link,true,GetUnitX(d.cast),GetUnitY(d.cast),GetUnitX(d.targ),GetUnitY(d.targ))
if d.dist < 100.00 then
//call DestroyLightning(d.link)
call d.destroy()
endif
set tim = null
return false
endfunction
private function Actions takes nothing returns boolean
local Data d = Data.create(GetTriggerUnit(),GetSpellTargetUnit())
call TimerStart(d.tim, PERIOD, true, function Callback)
return false
endfunction
public function I takes nothing returns nothing
call GT_AddStartsEffectAction(function Actions, 'A0D7')
//call XE_PreloadAbility('A0D7')
endfunction
endscope
library xecollider initializer init requires xefx, xebasic
//****************************************************************
//*
//* xecollider 0.8
//* --------------
//* A xecollider object is a special effect that has a collision
//* size that can trigger a hit event and also many options to
//* configure its automatic movement.
//*
//* Please use .terminate() instead of .destroy() this ensures
//* that it will be safe to destroy it (else you would have to
//* worry about destroying it during the animation loop/etc.)
//*
//* To use this struct is a little different than the other
//* current parts of xe. Instead of just creating the xecollider
//* (which works, but it would only be a xefx that can have speed)
//* you probably need to make it do something special on the
//* unit hit event... For this reason, you need to make a new
//* struct extending xecollider that declares an onUnitHit method
//* you may also declare a loopControl method, very useful, can
//* help you reduce 'attaching'.
//*
//****************************************************************
//================================================================
globals
private constant real DEFAULT_COLLISION_SIZE = 50.0 // These are defaults, on one hand you can change them
private constant real DEFAULT_MAX_SPEED = 1500.0 // on the other hand, if a spell relies on the defaults
private constant real DEFAULT_EXPIRATION_TIME = 5.0 // changing them would make the behavior vary...
private constant real PI2 = 6.28318 //It might not be wise to change this
endglobals
//===========================================================================
// So, this exists merely so you can declare your own event handler methods
// if interfaces make your brain blow out, please skip the next four lines.
//
private interface eventHandler
method onUnitHit takes unit hitTarget returns nothing defaults nothing
method loopControl takes nothing returns nothing defaults nothing
endinterface
//===========================================================================
struct xecollider extends eventHandler
// use terminate() instead of .destroy() to "kill" the collider.
// don't worry, terminate will call destroy automatically.
//============================================================================
// delegates:
// We are delegating a xefx object so that people call all the xefx methods
// and member from a xecollider object. This means that from a user
// perspective a xecollider is also an instance of xefx.
//
// Notable ones are: .x , .y , .fxpath and .z ,
// check out xefx's documentation for more info.
//
private delegate xefx fx
//##==========================================================================
// public variables:
//
public real expirationTime = DEFAULT_EXPIRATION_TIME
// Movement speed for the missile.
public real speed = 0.0
// Speed added per second (notice you can use a negative value here)
public real acceleration = 0.0
// If there is acceleration, it is wise to have a cap...
public real maxSpeed = DEFAULT_MAX_SPEED
public real minSpeed = 0.0
public real angleSpeed = 0.0 //The increment in radians per second to the
// direction angle, allows curved movement.
private static integer lastSeen = 0
private group seen
private boolean silent = false
//##==========================================================================
// public methods:
//
//----
// Well, it is a good idea to actually create the missiles.
// notice that if your custom missile struct needs to declare its own create
// method, you can call this as allocate(x,y,dir).
//
// Sorry, no Loc version.
//
public static method create takes real x, real y, real dir returns xecollider
local xecollider xc= xecollider.allocate()
set xc.fx = xefx.create(x,y,dir)
set xc.dir=dir
set xecollider.V[xecollider.N]=xc
set xecollider.N=xecollider.N+1
if(xecollider.N==1) then
call TimerStart(xecollider.T, XE_ANIMATION_PERIOD, true, xecollider.timerLoopFunction )
endif
if(.lastSeen < integer(xc)) then //with this I do group recycling
set .lastSeen = integer(xc)
set xc.seen = CreateGroup()
endif
return xc
endmethod
//----
// The direction is just the angle in radians to which the missile's model faces
// and the automatic movement uses.
//
method operator direction takes nothing returns real
return this.dir
endmethod
method operator direction= takes real v returns nothing
set this.dir=v
set this.fx.xyangle=v
endmethod
//----
// The collisionSize
//
method operator collisionSize takes nothing returns real
return this.csize
endmethod
method operator collisionSize= takes real value returns nothing
set this.csize = value
//good long attribute name, but we use csize in the loop
//don't worry this gets inlined, it would also be helpful if
//I ever decide to add a control for assignment.
endmethod
//---
// targetUnit is a unit to follow (or try to follow), notice that homing
// options require an angleSpeed different to 0.0
//
public method operator targetUnit takes nothing returns unit
return this.homingTargetUnit
endmethod
public method operator targetUnit= takes unit u returns nothing
if(u==null) then
set this.angleMode= ANGLE_NO_MOVEMENT
else
set this.angleMode= ANGLE_HOMING_UNIT
endif
set this.homingTargetUnit=u
endmethod
//----
// targetPoint is a point to reach (or try to reach), notice that homing
// options require an angleSpeed different to 0.0
//
public method setTargetPoint takes real x, real y returns nothing
set this.angleMode= ANGLE_HOMING_POINT
set this.homingTargetX=x
set this.homingTargetY=y
endmethod
public method setTargetPointLoc takes location loc returns nothing
set this.angleMode= ANGLE_HOMING_POINT
set this.homingTargetX=GetLocationX(loc)
set this.homingTargetY=GetLocationY(loc)
endmethod
//----
// Call this in case you used targetUnit or TargetPoint so the missile
// forgets the order to home that target.
//
public method forgetTarget takes nothing returns nothing
set this.angleMode = ANGLE_NO_MOVEMENT
endmethod
public method operator rotating takes nothing returns boolean
return (angleMode ==ANGLE_ROTATING)
endmethod
public method operator rotating= takes boolean val returns nothing
if(val) then
set angleMode = ANGLE_ROTATING
elseif (angleMode == ANGLE_ROTATING) then
set angleMode = ANGLE_NO_MOVEMENT
endif
endmethod
method terminate takes nothing returns nothing
set this.dead=true
set this.fxpath=""
endmethod
/* declare hiddenDestroy so people don't call directly on the delegate xefx */
method hiddenDestroy takes nothing returns nothing
set silent = true
call terminate()
endmethod
//--------
private static timer T
private static integer N=0
private static xecollider array V
private static code timerLoopFunction //I use a code var so create can be above the timerloop function, more readable
private boolean dead=false
private real csize = DEFAULT_COLLISION_SIZE
private real dir
private static constant integer ANGLE_HOMING_UNIT =1
private static constant integer ANGLE_HOMING_POINT=2
private static constant integer ANGLE_NO_MOVEMENT=0
private static constant integer ANGLE_ROTATING=3
private integer angleMode =0
private unit homingTargetUnit = null
private real homingTargetX
private real homingTargetY
private method onDestroy takes nothing returns nothing
call GroupClear(this.seen)
if(this.silent) then
call this.fx.hiddenDestroy()
else
call this.fx.destroy()
endif
endmethod
private static xecollider cinstance
private static real newx
private static real newy
private static group enumGroup
private static boolexpr enumBoolexpr
private static unit array picked
private static integer pickedN
static method timerLoop takes nothing returns nothing
local integer i=0
local integer j=0
local integer c=0
local xecollider this
local real d
local real ns
local real wa
local real df1
local real df2
local unit u
loop
exitwhen (i== xecollider.N )
set this=.V[i] //adopt-a-instance
set this.expirationTime = this.expirationTime - XE_ANIMATION_PERIOD
if(.dead or (this.expirationTime <=0.0) ) then
call this.destroy()
else
set ns=this.angleSpeed*XE_ANIMATION_PERIOD
if (ns!=0.0) then
if(this.angleMode== ANGLE_HOMING_UNIT ) then
set u=this.homingTargetUnit
if ( (GetUnitTypeId(u)==0) or IsUnitType(u, UNIT_TYPE_DEAD) ) then
set this.angleMode= ANGLE_NO_MOVEMENT
set this.homingTargetUnit = null
else
set this.homingTargetX=GetUnitX(u)
set this.homingTargetY=GetUnitY(u)
endif
set u=null
endif
if (this.angleMode == ANGLE_ROTATING) then
//nothing (ns is already ns)
elseif( this.angleMode != ANGLE_NO_MOVEMENT) then
if(ns<=0) then
set ns=-ns
endif
set wa=Atan2(this.homingTargetY - this.y , this.homingTargetX-this.x)
//if(wa<0.0) then
// set wa=wa+PI2
//endif
set df1=wa-this.dir
set df2=(PI2+wa)-this.dir
if (df1<=0) then
if(df2<=0) then
if(df2>=df1) then
set df1=df2
endif
else
if(-df1>=df2) then
set df1=df2
endif
endif
else
if(df2<=0) then
if(-df2<=df1) then
set df1=df2
endif
else
if(df2<=df1) then
set df1=df2
endif
endif
endif
if(df1<=0) then
if(-df1>=ns) then
set ns=-ns
else
set ns=df1
endif
else
if(df1<=ns) then
set ns=df1
endif
endif
else
set ns = 0
endif
set d=this.dir
set d = d + ns
if(d>=PI2) then
set d=d - PI2
elseif(d<0) then
set d=d + PI2
endif
set this.dir = d
set this.xyangle = d
endif
// function calls are expensive, damned we are, long code inside of loop
// correct software dev. tells us this should go to another function,
// but this is Jass, not real life.
set .cinstance = this
set ns = this.speed + this.acceleration*XE_ANIMATION_PERIOD
if ( ns<this.minSpeed) then
set ns=this.minSpeed
elseif (ns>this.maxSpeed) then
set ns=this.maxSpeed
endif
set d=((this.speed+ns)/2) * XE_ANIMATION_PERIOD
set this.speed=ns
set .newx= .x+d*Cos(this.dir)
set .newy= .y+d*Sin(this.dir)
set .x=.newx
set .y=.newy
set xecollider.pickedN = 0
call GroupEnumUnitsInRange( .enumGroup, .newx, .newy, .csize + XE_MAX_COLLISION_SIZE, .enumBoolexpr)
call GroupClear(this.seen)
set j=0
loop
exitwhen (j==xecollider.pickedN)
call GroupAddUnit( this.seen, xecollider.picked[j])
set j=j+1
endloop
set .V[c]=this
set c=c+1
if( this.loopControl.exists and not this.dead ) then
call this.loopControl()
endif
endif
set i=i+1
endloop
//call BJDebugMsg("}")
set xecollider.N=c
if(c==0) then
call PauseTimer(xecollider.T)
endif
endmethod
private static method inRangeEnum takes nothing returns boolean
local xecollider this= .cinstance //adopt-a-instance
local unit u=GetFilterUnit()
if not IsUnitType(u, UNIT_TYPE_DEAD) and not(this.dead) and (GetUnitTypeId(u)!=XE_DUMMY_UNITID) and IsUnitInRangeXY(u, .newx, .newy, .csize) then
// ah, the advantages of a standardized unit id...
set xecollider.picked[xecollider.pickedN] = u
set xecollider.pickedN=xecollider.pickedN + 1
if not IsUnitInGroup (u, this.seen ) then
call this.onUnitHit(u)
endif
endif
set u=null
return false
endmethod
//============================================================================
// you aren't supposed to call doInit yourself, try not to do it.
//
static method doInit takes nothing returns nothing
set xecollider.enumGroup = CreateGroup()
set xecollider.enumBoolexpr = Condition( function xecollider.inRangeEnum)
set xecollider.timerLoopFunction = (function xecollider.timerLoop)
set xecollider.T=CreateTimer()
endmethod
endstruct
private function init takes nothing returns nothing
call xecollider.doInit()
endfunction
endlibrary
//TESH.scrollpos=132
//TESH.alwaysfold=0
library xefx initializer init requires xebasic
//**************************************************
// xefx 0.7
// --------
// Recommended: ARGB (adds ARGBrecolor method)
// For your movable fx needs
//
//**************************************************
//==================================================
globals
private constant integer MAX_INSTANCES = 8190 //change accordingly.
private constant real RECYCLE_DELAY = 4.0
//recycling, in order to show the effect correctly, must wait some time before
//removing the unit.
private timer recycler
private timer NOW
endglobals
private struct recyclebin extends array
unit u
real schedule
static recyclebin end=0
static recyclebin begin=0
static method Recycle takes nothing returns nothing
call RemoveUnit(.begin.u) //this unit is private, systems shouldn't mess with it.
set .begin.u=null
set .begin=recyclebin(integer(.begin)+1)
if(.begin==.end) then
set .begin=0
set .end=0
else
call TimerStart(recycler, .begin.schedule-TimerGetElapsed(NOW), false, function recyclebin.Recycle)
endif
endmethod
endstruct
private function init takes nothing returns nothing
set recycler=CreateTimer()
set NOW=CreateTimer()
call TimerStart(NOW,43200,true,null)
endfunction
struct xefx[MAX_INSTANCES]
public integer tag=0
private unit dummy
private effect fx=null
private real zang=0.0
private integer r=255
private integer g=255
private integer b=255
private integer a=255
private integer abil=0
static method create takes real x, real y, real facing returns xefx
local xefx this=xefx.allocate()
set this.dummy= CreateUnit(Player(15), XE_DUMMY_UNITID, x,y, facing*bj_RADTODEG)
call UnitAddAbility(this.dummy,XE_HEIGHT_ENABLER)
call UnitAddAbility(this.dummy,'Aloc')
call UnitRemoveAbility(this.dummy,XE_HEIGHT_ENABLER)
call SetUnitX(this.dummy,x)
call SetUnitY(this.dummy,y)
return this
endmethod
method operator owner takes nothing returns player
return GetOwningPlayer(this.dummy)
endmethod
method operator owner= takes player p returns nothing
call SetUnitOwner(this.dummy,p,false)
endmethod
method operator teamcolor= takes playercolor c returns nothing
call SetUnitColor(this.dummy,c)
endmethod
method operator scale= takes real value returns nothing
call SetUnitScale(this.dummy,value,value,value)
endmethod
//! textmacro XEFX_colorstuff takes colorname, colorvar
method operator $colorname$ takes nothing returns integer
return this.$colorvar$
endmethod
method operator $colorname$= takes integer value returns nothing
set this.$colorvar$=value
call SetUnitVertexColor(this.dummy,this.r,this.g,this.b,this.a)
endmethod
//! endtextmacro
//! runtextmacro XEFX_colorstuff("red","r")
//! runtextmacro XEFX_colorstuff("green","g")
//! runtextmacro XEFX_colorstuff("blue","b")
//! runtextmacro XEFX_colorstuff("alpha","a")
method recolor takes integer r, integer g , integer b, integer a returns nothing
set this.r=r
set this.g=g
set this.b=b
set this.a=a
call SetUnitVertexColor(this.dummy,this.r,this.g,this.b,this.a)
endmethod
implement optional ARGBrecolor
method operator abilityid takes nothing returns integer
return this.abil
endmethod
method operator abilityid= takes integer a returns nothing
if(this.abil!=0) then
call UnitRemoveAbility(this.dummy,this.abil)
endif
if(a!=0) then
call UnitAddAbility(this.dummy,a)
endif
set this.abil=a
endmethod
method operator abilityLevel takes nothing returns integer
return GetUnitAbilityLevel( this.dummy, this.abil)
endmethod
method operator abilityLevel= takes integer newLevel returns nothing
call SetUnitAbilityLevel(this.dummy, this.abil, newLevel)
endmethod
method flash takes string fx returns nothing
call DestroyEffect(AddSpecialEffectTarget(fx,this.dummy,"origin"))
endmethod
method operator xyangle takes nothing returns real
return GetUnitFacing(this.dummy)*bj_DEGTORAD
endmethod
method operator xyangle= takes real value returns nothing
call SetUnitFacing(this.dummy,value*bj_RADTODEG)
endmethod
method operator zangle takes nothing returns real
return this.zang
endmethod
method operator zangle= takes real value returns nothing
local integer i=R2I(value*bj_RADTODEG+90.5)
set this.zang=value
if(i>=180) then
set i=179
elseif(i<0) then
set i=0
endif
call SetUnitAnimationByIndex(this.dummy, i )
endmethod
method operator x takes nothing returns real
return GetUnitX(this.dummy)
endmethod
method operator y takes nothing returns real
return GetUnitY(this.dummy)
endmethod
method operator z takes nothing returns real
return GetUnitFlyHeight(this.dummy)
endmethod
method operator z= takes real value returns nothing
call SetUnitFlyHeight(this.dummy,value,0)
endmethod
method operator x= takes real value returns nothing
call SetUnitX(this.dummy,value)
endmethod
method operator y= takes real value returns nothing
call SetUnitY(this.dummy,value)
endmethod
method operator fxpath= takes string newpath returns nothing
if (this.fx!=null) then
call DestroyEffect(this.fx)
endif
if (newpath=="") then
set this.fx=null
else
set this.fx=AddSpecialEffectTarget(newpath,this.dummy,"origin")
endif
endmethod
method hiddenReset takes string newfxpath, real newfacing returns nothing
local real x = GetUnitX(this.dummy)
local real y = GetUnitY(this.dummy)
local real z = this.z
local real za = this.zangle
local integer level = this.abilityLevel
set fxpath=null
call RemoveUnit(this.dummy)
set this.dummy= CreateUnit(Player(15), XE_DUMMY_UNITID, x,y, newfacing*bj_RADTODEG)
if(level != 0) then
call UnitAddAbility(this.dummy, abilityid)
endif
call UnitAddAbility(this.dummy,XE_HEIGHT_ENABLER)
call UnitAddAbility(this.dummy,'Aloc')
call UnitRemoveAbility(this.dummy,XE_HEIGHT_ENABLER)
call SetUnitX(this.dummy,x)
call SetUnitY(this.dummy,y)
set this.z = z
set zangle = za
endmethod
private method onDestroy takes nothing returns nothing
if(this.abil!=0) then
call UnitRemoveAbility(this.dummy,this.abil)
endif
if(this.fx!=null) then
call DestroyEffect(this.fx)
set this.fx=null
endif
if (recyclebin.end==MAX_INSTANCES) then
//I'd like to see this happen...
call TimerStart(recycler,0,false,function recyclebin.Recycle)
call ExplodeUnitBJ(this.dummy)
else
set recyclebin.end.u=this.dummy
set recyclebin.end.schedule=TimerGetElapsed(NOW)+RECYCLE_DELAY
set recyclebin.end= recyclebin( integer(recyclebin.end)+1)
if( recyclebin.end==1) then
call TimerStart(recycler, RECYCLE_DELAY, false, function recyclebin.Recycle)
endif
call SetUnitOwner(this.dummy,Player(15),false)
endif
set this.dummy=null
endmethod
method hiddenDestroy takes nothing returns nothing
call ShowUnit(dummy,false)
call destroy()
endmethod
endstruct
endlibrary
//TESH.scrollpos=50
//TESH.alwaysfold=0
library SomeNeatFunctions initializer Init
globals
private real minX
private real maxX
private real maxY
private real minY
private item Item
endglobals
//Pathability Check by Vexorian
private function CheckPathabilityTrickGet takes nothing returns nothing
set bj_rescueChangeColorUnit = bj_rescueChangeColorUnit or GetEnumItem() != Item
endfunction
private function CheckPathabilityTrick takes real x, real y returns boolean
local integer i = 30
local real X
local real Y
local rect r
call SetItemPosition(Item, x, y)
set X = GetItemX(Item) - x
set Y = GetItemY(Item) - y
if X * X + Y * Y <= 100 then
return true
endif
set r = Rect(x - i, y - i, x + i, y + i)
set bj_rescueChangeColorUnit = false
call EnumItemsInRect(r, null, function CheckPathabilityTrickGet)
call RemoveRect(r)
set r = null
return bj_rescueChangeColorUnit
endfunction
function CheckPathability takes real x, real y returns boolean
local boolean b = CheckPathabilityTrick(x, y)
call SetItemVisible(Item, false)
return b
endfunction
// SafeX and SafeY by me ( Viikuna )
function SafeX takes real x returns real
if x > maxX then
return maxX
endif
if x < minX then
return minX
endif
return x
endfunction
function SafeY takes real y returns real
if y > maxY then
return maxY
endif
if y < minY then
return minY
endif
return y
endfunction
//Init
private function Init takes nothing returns nothing
set minX = GetRectMinX(bj_mapInitialPlayableArea)
set minY = GetRectMinY(bj_mapInitialPlayableArea)
set maxX = GetRectMaxX(bj_mapInitialPlayableArea)
set maxY = GetRectMaxY(bj_mapInitialPlayableArea)
set Item = CreateItem('ciri', 0, 0)
call SetItemVisible(Item, false)
endfunction
endlibrary
//TESH.scrollpos=132
//TESH.alwaysfold=0
scope BlackHole
//********************************************************************************
//*
//* B L A C K H O L E
//* v1.00
//* Requested by: Darkwulfv Written by: Malf
//*
//* Requirements:
//*
//* - Geometry Funcs that I use, obviously for geometry(doh)
//* - A dummy caster, you can just copy+paste the one here in this demo map
//* - A triggerer ability
//*
//*
//********************************************************************************
//********************************************************************************
//*
//*
//* C O N F I G U R A T I O N A R E A
//*
//*
//********************************************************************************
globals
private constant string BlackHole_FX = "Effects\\BlackHole.mdx" //Path of the model, used for preloading
private constant string Explode_FX = "Effects\\DarkNova.mdx" //Same as above
private constant string Dust = "Objects\\Spawnmodels\\Undead\\ImpaleTargetDust\\ImpaleTargetDust.mdl"
private constant integer BlackHole_ID = 'A003' //Rawcode of the Black Hole dummy spell
private constant integer Dummy_ID = 'e001' //Rawcode of the Black Hole dummy unit
private constant integer Explode_ID = 'e002' //Rawcode of the Explosion dummy unit
private constant real SUCK_INTERVAL = 0.035
private constant attacktype ATTACK_TYPE = ATTACK_TYPE_CHAOS
private constant damagetype DAMAGE_TYPE = DAMAGE_TYPE_UNIVERSAL
endglobals
private constant function SuckRadius takes integer lvl returns real
return 600. //Units within this distance will get moved closer to the center
endfunction
private constant function SuckDistance takes integer lvl returns real
return 100.//Distance the unit must be within for it to be sucked inside
endfunction
private constant function SuckSpeed takes integer lvl returns real
return 5.5 //Distance travelled every interval when units are sucked
endfunction
private constant function Expiration takes integer lvl returns real
return 8. //The black hole explodes upon expiration!
endfunction
private constant function ExplosionDamage takes integer lvl returns real
return 350. //Damage upon explosion
endfunction
private constant function ExplosionRadius takes integer lvl returns real
return 900. //Explosion AOE radius
endfunction
private constant function KnockbackSpeed takes integer lvl returns real
return 35. //Same as SUCK_SPEED but applies to knockback
endfunction
private constant function KnockbackBreak takes integer lvl returns real
return 0.45 //The higher the value, the greater the friction.
endfunction
private function Filter takes nothing returns boolean
return not IsUnitType(GetFilterUnit(),UNIT_TYPE_MECHANICAL) and GetWidgetLife(GetFilterUnit()) > .405
//Determines which units are sucked in.
endfunction
private function ShouldUnitBeKnockedBack takes unit u, player p returns boolean
return (not IsUnitType(GetFilterUnit(),UNIT_TYPE_MECHANICAL) and GetWidgetLife(GetFilterUnit()) > .405 and IsUnitEnemy(u,p))
//Determines if the unit 'u' should be knocked back.
//unit 'u' acts like GetFilterUnit() and player 'p' is only used for purposes like trying to affect only enemies
//In that case, use IsUnitEnemy(u,p)
endfunction
//***********************************************
//*
//* P R I V A T E C O D E
//* Feel free to modify at your own risk
//*
//***********************************************
private struct data
unit caster
unit dummy //This is private and just used for the black hole model
real expiration
real x
real y
group sucked
integer lvl
method onDestroy takes nothing returns nothing
call DestroyGroup(.sucked)
endmethod
endstruct
private function ExplosionMove takes nothing returns nothing
local timer t = GetExpiredTimer()
local data d = GetTimerData( t )
local real x
local real y
local real a
local unit temp
local group g
if d.expiration <= 0 then //Remember that expiration is used as KNOCKBACK_SPEED? =þ
call PauseTimer(t)
call ReleaseTimer( t )
call data.destroy(d)
else
set g = CreateGroup()
call GroupAddGroup(d.sucked,g)
loop
set temp = FirstOfGroup(g)
exitwhen temp == null
set x = GetUnitX(temp)
set y = GetUnitY(temp)
set a = AngleBetweenPointsXY(d.x,d.y,x,y)
set x = x + d.expiration * Cos(a*0.01745)
set y = y + d.expiration * Sin(a*0.01745)
if (x < GetRectMinX(bj_mapInitialPlayableArea) or x > GetRectMaxX(bj_mapInitialPlayableArea)) or (y < GetRectMinY(bj_mapInitialPlayableArea) or y > GetRectMaxY(bj_mapInitialPlayableArea)) then
call GroupRemoveUnit(d.sucked,temp)
else
call DestroyEffect(AddSpecialEffect(Dust,x,y))
call SetUnitX(temp,x)
call SetUnitY(temp,y) //I just realized SetUnitPosition is laggy. SetUnitX/y is much better :)
//call SetUnitPosition(temp,x,y) //SetUnitPosition so that it checks for pathing =þ
endif
call GroupRemoveUnit(g,temp)
endloop
call DestroyGroup(g)
set d.expiration = d.expiration - KnockbackBreak(d.lvl)
endif
endfunction
private function Unhide takes nothing returns nothing
call ShowUnit(GetEnumUnit(),true)
call SetUnitInvulnerable(GetEnumUnit(),false)
call PauseUnit(GetEnumUnit(),false)
endfunction
private function Callback takes nothing returns nothing
local timer t = GetExpiredTimer()
local data d = GetTimerData( t )
local group g = CreateGroup()
local unit temp
local real x
local real y
local real a
if d.expiration <= 0 then
//Begin explosion
call KillUnit(d.dummy)
call DestroyEffect(AddSpecialEffect(Explode_FX,d.x,d.y))
set d.dummy = CreateUnit(Player(13),Explode_ID,d.x,d.y,270)
call SetUnitTimeScale(d.dummy,.5)
call UnitApplyTimedLife(d.dummy,'BTLF',5.)
call SetUnitAnimation(d.dummy,"Birth") //Just to be sure =þ
call ForGroup(d.sucked, function Unhide)
call GroupEnumUnitsInRange(g,d.x,d.y,ExplosionRadius(d.lvl),Condition(function Filter))
loop
set temp = FirstOfGroup(g)
exitwhen temp == null
if IsUnitEnemy(temp,GetOwningPlayer(d.caster)) then
call UnitDamageTarget(d.caster,temp,ExplosionDamage(d.lvl),true,false,ATTACK_TYPE,DAMAGE_TYPE,null)
endif
call GroupRemoveUnit(g,temp)
if ShouldUnitBeKnockedBack(temp,GetOwningPlayer(d.caster)) then
call GroupAddUnit(d.sucked,temp) //Add it so that it will get knocked back later
endif
endloop
//**** Start the knockback now! ****
//**** Well not really =þ ****
set d.expiration = KnockbackSpeed(d.lvl) //I do not want to declare another member for the struct so I use expiration
call PauseTimer(t)
call TimerStart(t,SUCK_INTERVAL,true,function ExplosionMove)
else
call GroupEnumUnitsInRange(g,d.x,d.y,SuckRadius(d.lvl),Condition(function Filter))
loop
set temp = FirstOfGroup(g)
exitwhen temp == null
if IsUnitEnemy(temp,GetOwningPlayer(d.caster)) and not IsUnitInGroup(temp,d.sucked) then
set x = GetUnitX(temp)
set y = GetUnitY(temp)
set a = AngleBetweenPointsXY(x,y,d.x,d.y)
if DistanceBetweenPointsXY(d.x,d.y,x,y) <= SuckDistance(d.lvl) then
//Unit is within sucking distance, kick that unit into the hole, THIS IS SPARTAAA!!!
call GroupAddUnit(d.sucked,temp)
call ShowUnit(temp,false)
call SetUnitInvulnerable(temp,true)
call PauseUnit(temp,true)
call SetUnitX(temp,d.x)
call SetUnitY(temp,d.y)
else
set x = x + SuckSpeed(d.lvl) * Cos(a*0.01745)
set y = y + SuckSpeed(d.lvl) * Sin(a*0.01745)
call SetUnitX(temp,x)
call SetUnitY(temp,y)
endif
endif
call GroupRemoveUnit(g,temp)
endloop
set d.expiration = d.expiration - SUCK_INTERVAL
endif
call DestroyGroup(g)
set g = null
endfunction
private function Conditions takes nothing returns boolean
return GetSpellAbilityId() == BlackHole_ID
endfunction
private function Actions takes nothing returns nothing
local location l = GetSpellTargetLoc()
local timer t = NewTimer()
local data d = data.create()
set d.caster = GetTriggerUnit()
set d.x = GetLocationX(l)
set d.y = GetLocationY(l)
set d.dummy = CreateUnit(GetOwningPlayer(d.caster),Dummy_ID,d.x,d.y,270)
set d.sucked = CreateGroup()
set d.lvl = GetUnitAbilityLevel(d.caster,BlackHole_ID)
set d.expiration = Expiration(d.lvl)
call SetTimerData(t,d)
call TimerStart(t,SUCK_INTERVAL,true,function Callback)
call RemoveLocation(l)
set l = null
endfunction
function InitTrig_Black_Hole takes nothing returns nothing
local trigger t = CreateTrigger( )
call Preload(BlackHole_FX)
call Preload(Explode_FX)
call Preload(Dust)
call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_SPELL_EFFECT )
call TriggerAddCondition( t, Condition( function Conditions ) )
call TriggerAddAction( t, function Actions )
set t = null
endfunction
endscope
//TESH.scrollpos=-1
//TESH.alwaysfold=0
scope SithDebilitate initializer I
globals
private constant real PERIOD = 5.50
private constant integer ORBCOUNT = 3
endglobals
private struct Data
unit cast
unit targ
unit dum
integer lvl
integer orb
integer str
integer int
real sum
timer tim
boolean boo
static method create takes unit cast, unit targ returns Data
local Data d = Data.allocate()
set d.cast = cast
set d.targ = targ
set d.lvl = GetUnitAbilityLevel(d.cast, SithApprentice_SithDebilitate)
set d.str = GetHeroStr(d.targ,false)
set d.int = GetHeroInt(d.cast,false)
set d.sum = (d.str * (0.0 + (0.25*d.lvl)))
if d.str < d.int then
set d.boo = true
call SetHeroStr(d.targ, GetHeroStr(d.targ,false) - R2I(d.sum), true)
else
set d.boo = false
endif
set d.orb = 0
set d.tim = NewTimer()
call SetTimerData(d.tim, d)
return d
endmethod
method onDestroy takes nothing returns nothing
call ReleaseTimer(.tim)
endmethod
endstruct
private function Callback takes nothing returns boolean
local timer tim = GetExpiredTimer()
local Data d = GetTimerData(tim)
call SetUnitMoveSpeed( d.targ, ( GetUnitMoveSpeed(d.targ) - 45.00 ) )
set d.orb = d.orb + 1
if d.orb >= ORBCOUNT then
if d.boo==true then
call SetHeroStr(d.targ, GetHeroStr(d.targ,false) + R2I(d.sum), true)
endif
call SetUnitMoveSpeed( d.targ, ( GetUnitMoveSpeed(d.targ) + 135.00 ) )
call d.destroy()
endif
set tim = null
return false
endfunction
private function f takes nothing returns boolean
local Data d = Data.create(GetTriggerUnit(),GetSpellTargetUnit())
call TimerStart(d.tim, PERIOD, true, function Callback)
return false
endfunction
private function I takes nothing returns nothing
call GT_AddStartsEffectAction(function f, SithApprentice_SithDebilitate)
//call XE_PreloadAbility(SithApprentice_SithDebilitate)
endfunction
endscope
//TESH.scrollpos=-1
//TESH.alwaysfold=0
scope Maelstrom initializer I
globals
private constant real PERIOD = 0.02
endglobals
private struct Data
unit cast
unit dum
unit targ
integer level
integer orb
integer orbcount
integer orbend
timer tim
real ox
real oy
real uf
boolean damaged
static method create takes unit cast, unit targ returns Data
local Data d = Data.allocate()
set d.cast = cast
set d.targ = targ
set d.ox = GetUnitX(d.targ)
set d.oy = GetUnitY(d.targ)
set d.level = GetUnitAbilityLevel(d.cast, SithApprentice_SithWhirlWind)
set d.orb = 0
set d.orbcount = 150
set d.orbend = 250
set d.damaged = false
set d.uf = GetUnitFacing(d.targ)
set d.tim = NewTimer()
set d.dum = CreateUnit(GetOwningPlayer(d.cast), SithApprentice_MorgShellcream, GetUnitX(d.targ), GetUnitY(d.targ), GetUnitFacing(d.cast)+180.00)
call UnitApplyTimedLife(d.dum, 'BTLF', 5.0)
call SetTimerData(d.tim, d)
return d
endmethod
method onDestroy takes nothing returns nothing
call ReleaseTimer(.tim)
endmethod
endstruct
private function Callback takes nothing returns boolean
local timer tim = GetExpiredTimer()
local Data d = GetTimerData(tim)
set d.orb = d.orb + 1
if d.orb >= d.orbend then
call d.destroy()
set tim = null
return false
elseif d.orb < d.orbcount then
if IsTerrainPathable(GetUnitX(d.dum),GetUnitY(d.dum),PATHING_TYPE_WALKABILITY) then
call SetDistanceToUnit(d.cast,d.dum,getdis(d.cast,d.dum)+5.50+1.75*d.level)
call SetUnitFacing(d.dum,GetUnitFacing(d.cast)+180.00)
call SetUnitX(d.targ,GetUnitX(d.dum))
call SetUnitY(d.targ,GetUnitY(d.dum))
call SetUnitFacing(d.targ,d.uf+5.00)
set tim = null
return false
else
call TAF_MDT(d.cast,d.targ,getdisr(d.ox,d.oy,d.targ) / 5.00,0.00)
call msgall(10,R2S(getdisr(d.ox,d.oy,d.targ)))
call d.destroy()
set tim = null
return false
endif
elseif d.orb == d.orbcount then
/*if IsTerrainPathable(GetUnitX(d.dum),GetUnitY(d.dum),PATHING_TYPE_BLIGHTPATHING) != true then
call TAF_MDT(d.cast,d.targ,getdisr(d.ox,d.oy,d.targ) / 5.00,0.00)
call msgall(10,R2S(getdisr(d.ox,d.oy,d.targ)))
call d.destroy()
set tim = null
return false
endif*/
call SetDistanceToUnit(d.cast,d.dum,getdis(d.cast,d.dum)+5.50+1.75*d.level)
call SetUnitFacing(d.dum,GetUnitFacing(d.cast)+180.00)
call SetUnitX(d.targ,GetUnitX(d.dum))
call SetUnitY(d.targ,GetUnitY(d.dum))
call SetUnitFacing(d.targ,d.uf+5.00)
call TAF_MDT(d.cast,d.targ,getdisr(d.ox,d.oy,d.targ) / 5.00,0.00)
call msgall(10,R2S(getdisr(d.ox,d.oy,d.targ)))
set tim = null
return false
else
call SetDistanceToUnit(d.cast,d.dum,getdis(d.cast,d.dum)+5.50+1.75*d.level)
call SetUnitFacing(d.dum,GetUnitFacing(d.cast)+180.00)
set tim = null
return false
endif
endfunction
private function f takes nothing returns boolean
local Data d = Data.create(GetTriggerUnit(), GetSpellTargetUnit())
call TimerStart(d.tim, PERIOD, true, function Callback)
return false
endfunction
private function I takes nothing returns nothing
call GT_AddStartsEffectAction(function f, SithApprentice_SithWhirlWind)
//call XE_PreloadAbility(SithApprentice_SithWhirlWind)
endfunction
endscope
//TESH.scrollpos=24
//TESH.alwaysfold=0
//=============================================================================
// Passive Polymorph
// By: scorpion182
//
// Gives a percent chance to turns a target enemy unit into a sheep.
//
//
//
// Spell Requires: Passive Polymorph ability
// Polymorph Dummy ability
// Dummy unit
//
// Implementation:
// Simply copy the forementioned requirements to your map, go through the
// options below and enjoy!
//=============================================================================
//Configuration Options:
//=============================================================================
constant function Dummy_Caster takes nothing returns integer
return 'e000' // rawcode of Dummy Caster unit
endfunction
constant function PP_AbilId takes nothing returns integer
return 'A060' // ability rawcode of Passive Polymorph ability
endfunction
constant function PP_Dummy_AbilId takes nothing returns integer
return 'A061' // ability rawcode of Polymorph Dummy ability
endfunction
constant function PP_Chance takes integer lvl returns integer
return 10+5*lvl // % chance
endfunction
//Configuration end
//=============================================================================
function PP_Conditions takes nothing returns boolean
if GetUnitAbilityLevel(GetAttacker(),PP_AbilId())==0 then
return false
endif
if IsUnitIllusion(GetAttacker())==true then
return false
endif
if IsPlayerEnemy(GetOwningPlayer(GetAttacker()),GetOwningPlayer(GetTriggerUnit()))==false then
return false
endif
if GetUnitAbilityLevel(GetTriggerUnit(),'Bply')>0 then
return false
endif
return true
endfunction
function PP_Main takes nothing returns nothing
local unit target=GetTriggerUnit()
local unit caster=GetAttacker()
local unit dummy
if(GetRandomInt(1,100)<=PP_Chance(GetUnitAbilityLevel(caster,PP_AbilId()))) then
set dummy=CreateUnit(GetOwningPlayer(caster),Dummy_Caster(),GetUnitX(target),GetUnitY(target),0)
call UnitApplyTimedLife(dummy,'BTLF',1.0)
call UnitAddAbility(dummy,PP_Dummy_AbilId())
call SetUnitAbilityLevel(dummy,PP_Dummy_AbilId(),GetUnitAbilityLevel(caster,PP_AbilId()))
call IssueTargetOrder(dummy,"polymorph",target)
endif
set caster=null
set dummy=null
set target=null
endfunction
//===========================================================================
function InitTrig_Passive_Polymorph takes nothing returns nothing
set gg_trg_Passive_Polymorph = CreateTrigger( )
call TriggerRegisterAnyUnitEventBJ(gg_trg_Passive_Polymorph,EVENT_PLAYER_UNIT_ATTACKED)
call TriggerAddCondition(gg_trg_Passive_Polymorph,Condition(function PP_Conditions))
call TriggerAddAction( gg_trg_Passive_Polymorph, function PP_Main )
endfunction
//TESH.scrollpos=-1
//TESH.alwaysfold=0
scope forceshield initializer I
private function f takes nothing returns boolean
local unit u = null
set u = CreateUnit(GetOwningPlayer(GetTriggerUnit()), 'e00S', GetUnitX(GetTriggerUnit()), GetUnitY(GetTriggerUnit()), 270.00)
call UnitAddAbility(u,'A05E')
call SetUnitAbilityLevel(u, 'A05E', GetUnitAbilityLevel(GetTriggerUnit(),'A099'))
call IssueTargetOrder(u, "antimagicshell",GetTriggerUnit())
call UnitApplyTimedLife(u, 'BTLF', 5.0)
set u = null
return false
endfunction
public function I takes nothing returns nothing
call GT_AddStartsEffectAction(function f, 'A099')
//call XE_PreloadAbility('A099')
call XE_PreloadAbility('A05E')
endfunction
endscope
//TESH.scrollpos=93
//TESH.alwaysfold=0
scope DemonSlash initializer Init
// Demon Slash by The_Witcher
// The hero slashes through numerous enemys dealing aoe damage.
//
// well not much to say look through/edit the setup part for your wishes
//
// The SETUP part
globals
// the rawcode of the ghost/demon appearing behind the caster when channeling
private constant integer GhostDummy = 'h000'
// a animationtag added to the ghost/demon (manipulating animations)
private constant string Animation = "alternate"
// the rawcode of the Oni Giri ability
private constant integer AbilityID = 'A0C6'
// the size the dummy will be when fully grown (2 = 200%)
private constant real DummySize = 2
//the effect created when a unit is hit
private constant string BloodEffect = "Objects\\Spawnmodels\\Human\\HumanBlood\\BloodElfSpellThiefBlood.mdl"
//the effect created on the weapon of the caster
private constant string WeaponEffect = "Abilities\\Weapons\\ZigguratMissile\\ZigguratMissile.mdl"
//the effect created on the body of the caster
private constant string BodyEffect = "Abilities\\Spells\\Undead\\DeathCoil\\DeathCoilMissile.mdl"
//the effect created when the dummy disappears cause of abort
private constant string AbortEffect = "Abilities\\Spells\\Undead\\CarrionSwarm\\CarrionSwarmDamage.mdl"
// If this boolean is
// false = ordering the caster while channeling aborts the spell
// BUT the caster must have [Can Flee = False] in the object editor
// true = ordering the caster while channeling has no effect
private constant boolean pause = false
// the timer interval
private real interval = 0.01
endglobals
private function GetDamage takes integer level returns real
return 70.00 + (50 * I2R(level) ) //120 on level 1, 170 level 2, 220 level 3,...
endfunction
private function GetChannelTime takes integer level returns real
return 2.00 - (I2R(level)/4) //3,5 on level 1, 3 level 2, 2,5 level 3,...
endfunction
private function GetDamageRange takes integer level returns real
return 160.00 +( 0 * I2R(level)) //120 on level 1, 140 level 2, 160 level 3,...
endfunction
private function GetSlashTime takes integer level returns real
return 0.5 - (0.00*level) //0.55 on level 1, 0.5 level 2, 0.45 level 3,...
endfunction
// END OF SETUP PART
private struct data
unit u
unit b
real xx
real yy
real phase = 0
real temp = 0
real t = 0
integer level
effect array sfx[3]
group dg = CreateGroup()
integer index
endstruct
globals
private timer tim = CreateTimer()
private data array DATAS
private integer total = 0
private unit Temp
private boolexpr BoolExpr
private hashtable h = InitHashtable()
private group gg = CreateGroup()
private data var
private item ite
private trigger trg = CreateTrigger()
endglobals
private function IsCoordPathable takes real x, real y returns boolean
local real xx
local real yy
call SetItemVisible(ite, true)
call SetItemPosition(ite,x,y)
set xx = GetItemX( ite ) - x
set yy = GetItemY( ite ) - y
call SetItemVisible(ite, false)
if xx < 1 and xx > -1 and yy < 1 and yy > -1 then
return true
endif
return false
endfunction
private function AngleBetweenCoords takes real x, real y, real xx, real yy returns real
return bj_RADTODEG * Atan2(yy - y, xx - x)
endfunction
private function DamageGroup takes nothing returns nothing
if not IsUnitInGroup(GetEnumUnit(),var.dg) then
call GroupAddUnit(var.dg,GetEnumUnit())
call UnitDamageTarget( var.u, GetEnumUnit(), GetDamage(var.level), true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_NORMAL, WEAPON_TYPE_WHOKNOWS )
call DestroyEffect(AddSpecialEffectTarget(BloodEffect,GetEnumUnit(),"chest"))
endif
endfunction
private function execute takes nothing returns nothing
local integer i = 0
local data dat
local unit b
loop
exitwhen i >= total
set dat = DATAS[i]
if dat.phase == 0 then // phase 1
call SetUnitVertexColor(dat.b,255,255,255,R2I(dat.temp/100*255))
call SetUnitScalePercent(dat.b, dat.temp*DummySize, dat.temp*DummySize, dat.temp*DummySize)
set dat.temp = dat.temp + 100/GetChannelTime(dat.level)*interval
if not pause then
call DisableTrigger(trg)
call SetUnitPosition(dat.u,GetUnitX(dat.u),GetUnitY(dat.u))
call EnableTrigger(trg)
endif
if dat.temp >= 100 then
set dat.phase = 1
set dat.temp = 0
set dat.sfx[1] = AddSpecialEffectTarget(WeaponEffect,dat.u,"weapon")
set dat.sfx[2] = AddSpecialEffectTarget(BodyEffect,dat.u,"chest")
call PauseUnit(dat.u,true)
call SetUnitAnimation(dat.u,"attack")
endif
if (IsUnitType(dat.u,UNIT_TYPE_DEAD) or GetUnitTypeId(dat.u) == 0 ) then
call DestroyEffect(dat.sfx[1])
call DestroyEffect(dat.sfx[2])
call UnitApplyTimedLife(dat.b,'BTLF',1)
call SetUnitScale(dat.b,2,2,2)
call SetUnitVertexColor(dat.b,255,255,255,0)
call DestroyGroup(dat.dg)
call FlushChildHashtable(h,GetHandleId(dat.u))
call DestroyEffect(AddSpecialEffectTarget(AbortEffect, dat.b, "origin"))
set DATAS[i] = DATAS[total-1]
set DATAS[i].index = i
set total = total - 1
call dat.destroy()
endif
else // phase 2
call SetUnitVertexColor(dat.b,255,255,255, R2I((100 - dat.temp)/100*255))
set dat.temp = dat.temp + 100/(GetChannelTime(dat.level)/2)*interval
if (dat.temp >= 100 and dat.t >= GetSlashTime(dat.level)) or not IsCoordPathable(GetUnitX(dat.u)+ dat.xx,GetUnitY(dat.u)+ dat.yy ) then
call PauseUnit(dat.u,false)
call DestroyEffect(dat.sfx[1])
call DestroyEffect(dat.sfx[2])
call RemoveUnit(dat.b)
call DestroyGroup(dat.dg)
call FlushChildHashtable(h,GetHandleId(dat.u))
set DATAS[i] = DATAS[total-1]
set DATAS[i].index = i
set total = total - 1
call dat.destroy()
elseif dat.t < GetSlashTime(dat.level) then
call SetUnitX(dat.u, GetUnitX(dat.u)+ dat.xx )
call SetUnitY(dat.u, GetUnitY(dat.u)+ dat.yy )
set dat.t = dat.t + interval
//damage part
set Temp = dat.u
call GroupEnumUnitsInRange(gg,GetUnitX(dat.u),GetUnitY(dat.u), GetDamageRange(dat.level), BoolExpr)
set var = dat
call ForGroup(gg,function DamageGroup)
call GroupClear(gg)
else
if dat.sfx[1] != null then
call PauseUnit(dat.u,false)
call SetUnitAnimation(dat.u, "stand")
call DestroyEffect(dat.sfx[1])
call DestroyEffect(dat.sfx[2])
set dat.sfx[1] = null
endif
endif
if (IsUnitType(dat.u,UNIT_TYPE_DEAD) or GetUnitTypeId(dat.u) == 0 ) then
call UnitApplyTimedLife(dat.b,'BTLF',1)
call DestroyGroup(dat.dg)
call SetUnitScale(dat.b,2,2,2)
call SetUnitVertexColor(dat.b,255,255,255,0)
call DestroyEffect(AddSpecialEffectTarget(AbortEffect, dat.b, "origin"))
call FlushChildHashtable(h,GetHandleId(dat.u))
set DATAS[i] = DATAS[total-1]
set DATAS[i].index = i
set total = total - 1
call dat.destroy()
endif
endif
set i = i + 1
endloop
if total == 0 then
call PauseTimer(tim)
endif
set b = null
endfunction
private function cast takes nothing returns boolean
local data dat = data.create()
local unit u = GetTriggerUnit()
local real xx = GetSpellTargetX()
local real yy = GetSpellTargetY()
local real x = GetUnitX(u)
local real y = GetUnitY(u)
local real r = AngleBetweenCoords(x,y,xx,yy)
if GetSpellAbilityId() == AbilityID then
call SetUnitPosition(u,x,y)
if pause then
call PauseUnit(u,true)
endif
call SetUnitFacing(u,r)
set x = GetUnitX(u) + 200 * Cos((r-180) * bj_DEGTORAD)
set y = GetUnitY(u) + 200 * Sin((r-180) * bj_DEGTORAD)
set dat.b = CreateUnit(GetOwningPlayer(u),GhostDummy,x,y,r)
call UnitAddAbility(dat.b, 'Aloc')
call PauseUnit(dat.b,true)
call SetUnitVertexColor(dat.b,255,255,255,0)
call AddUnitAnimationProperties(dat.b, Animation, true)
call SetUnitAnimation(u, "Channel")
set dat.u = u
set dat.level = GetUnitAbilityLevel(u,AbilityID)
set dat.xx = (xx - GetUnitX(u)) / GetSlashTime(dat.level) * interval
set dat.yy = (yy - GetUnitY(u)) / GetSlashTime(dat.level) * interval
call SaveInteger(h,GetHandleId(u),0,dat)
set DATAS[total] = dat
set dat.index = total
set total = total + 1
if total == 1 then
call TimerStart(tim,interval,true,function execute)
endif
endif
set u = null
return false
endfunction
private function abort takes nothing returns boolean
local data dat = LoadInteger(h,GetHandleId(GetTriggerUnit()),0)
local integer i = dat.index
if dat != 0 and dat.phase == 0 then
call UnitApplyTimedLife(dat.b,'BTLF',1)
call SetUnitScale(dat.b,2,2,2)
call SetUnitVertexColor(dat.b,255,255,255,0)
call DestroyGroup(dat.dg)
call DestroyEffect(AddSpecialEffectTarget(AbortEffect, dat.b, "origin"))
call FlushChildHashtable(h,GetHandleId(dat.u))
set DATAS[i] = DATAS[total-1]
set DATAS[i].index = i
set total = total - 1
call dat.destroy()
endif
return false
endfunction
private function AllyFilter takes nothing returns boolean
return ( IsUnitAlly(GetFilterUnit(), GetOwningPlayer(Temp)) == false ) and not (IsUnitType(GetFilterUnit(),UNIT_TYPE_DEAD) or GetUnitTypeId(GetFilterUnit()) == 0 )
endfunction
private function Init takes nothing returns nothing
local trigger t = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_SPELL_CAST )
call TriggerAddCondition(t,Condition(function cast))
call TriggerRegisterAnyUnitEventBJ( trg, EVENT_PLAYER_UNIT_ISSUED_ORDER )
call TriggerRegisterAnyUnitEventBJ( trg, EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER )
call TriggerRegisterAnyUnitEventBJ( trg, EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER )
call TriggerRegisterAnyUnitEventBJ( trg, EVENT_PLAYER_UNIT_ISSUED_UNIT_ORDER )
call TriggerAddCondition(trg,Condition(function abort))
set BoolExpr = Condition(function AllyFilter)
set ite = CreateItem( 'wolg', 0,0 )
call SetItemVisible(ite, false)
endfunction
endscope
//TESH.scrollpos=-1
//TESH.alwaysfold=0
//
//
//
//manaburn
//evasion
//rejuvenation
//library_once Boomerang uses GroupUtils, xefx, DestructableLib, IsTerrainWalkable
scope abbafgbdfaggaggfdf
private keyword Data // DO NOT TOUCH; configuration is below
// Credits:
// - Ciebron for the inspiration
// - -JonNny for reporting some bugs
// - Rising_Dusk for his GroupUtils library
// - Anitarf for his IsTerrainWalkable library
// - Vexorian for JassHelper and xe
// - PipeDream for Grimoire
// - PitzerMike for JassNewGenPack and DestructableLib
// - MindWorX for JassNewGenPack
// - SFilip for TESH
globals
private constant real TICK = 1./32 // granulation of movement
private constant integer AID = 'A0G8' // the ability triggering this spell
private constant string BOOMERANG_MODEL = "war3mapImported\\s_Greenmagic Projectile.mdl" // this is the model representing the boomerang
private real array DAMAGE // damage dealt by the boomerang to units it hits
private real array DAMAGE_ABSORPTION // the damage dealt is reduced by this much everytime the boomerang hits a unit or a tree
private constant boolean DAMAGE_ABSORPTION_RELATIVE = true // is DAMAGE_ABSORPTION to be treated as an absolute value or a value relative to the current damage
private constant real DAMAGE_BOUNDARY = 10. // once the damage is lower or equal to this, the boomerang stops flying
private constant real BOOMERANG_COLLSIZE = 96. // the AoE in which units are damaged by the boomerang
private constant real BOOMERANG_SIZE = 1.25 // the scaling of the boomerang
private constant real BOOMERANG_HEIGHT = 64. // the Z height of the boomerang
private constant real array BOOMERANG_SPEED // the speed at which the boomerang moves // due to limitations this is only an approximation
private constant real BOOMERANG_FOCUS = 6. // the higher this number the more focused is the path of the boomerang. Any value greater than 0 should work. Values below 2 might not look so good.
private constant boolean ALLOW_MULTIPLE_HITS = true // if this is true, the boomerang can hit units more than once on his path
private constant boolean USE_RIGHT_BOOMERANG = true // just avoid setting both to false, okay?
private constant boolean USE_LEFT_BOOMERANG = false
private constant boolean COLLIDE_WITH_GROUND = false // do boomerangs collide with unwalkable terrain?
private real array MIN_RANGE // minimum throwing distance for the boomerang
private constant boolean IGNORE_TREES = true // if true, the boomerang will just fly through the trees without doing anything
private constant boolean KILL_TREES = false // if true, trees are killed once the boomerang hits one, if this is false, the boomerang stops flying
private constant string HIT_FX = "Objects\\Spawnmodels\\Critters\\Albatross\\CritterBloodAlbatross.mdl" // when the boomerang hits a unit, this effect is spawned on the unit hit
private constant string HIT_FX_ATTPT = "chest" // the beforementioned effect will be attached to this point
private constant attacktype ATTACK_TYPE = ATTACK_TYPE_MAGIC // the attack type of the damage the boomerang deals
private constant damagetype DAMAGE_TYPE = DAMAGE_TYPE_MAGIC // the damage type of the damage the boomerang deals
private constant weapontype WEAPON_TYPE = WEAPON_TYPE_METAL_MEDIUM_SLICE // sound when boomerang hits a unit
endglobals
private function Damage takes integer level returns real // PROXY
return DAMAGE[level]
endfunction
private function Damage_Absorption takes integer level returns real // PROXY
return DAMAGE_ABSORPTION[level]
endfunction
private function Boomerang_Speed takes integer level returns real // PROXY
return BOOMERANG_SPEED[level]
endfunction
private function Min_Range takes integer level returns real // PROXY
return MIN_RANGE[level]
endfunction
private function SetUpSpellData takes nothing returns nothing
set DAMAGE[1]=200. // initially deals 200 damage
set DAMAGE[2]=275.
set DAMAGE[3]=350.
set DAMAGE_ABSORPTION[1]=0.16 // lowers damage dealt by 16% of current damage.
set DAMAGE_ABSORPTION[2]=0.12
set DAMAGE_ABSORPTION[3]=0.08
set BOOMERANG_SPEED[1]=600.
set BOOMERANG_SPEED[2]=600.
set BOOMERANG_SPEED[3]=600.
set MIN_RANGE[1]=800.
set MIN_RANGE[2]=800.
set MIN_RANGE[3]=800.
endfunction
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 (not IsUnitInGroup(u, s.g)) \
and IsUnitEnemy(u, GetOwningPlayer(s.c))
endfunction
// This is shit. Don't touch shit.
globals
private rect R=Rect(0,0,1,1)
private Data tmps
private integer tmpd
private boolean OverTree=false
endglobals
private struct Data
unit c // caster
xefx dum1 // boomerang dummy 1
xefx dum2 // boomerang dummy 2
boolean d1a // is dummy 1 active
boolean d2a // is dummy 2 active
integer level
real d // planned distance
real a // current angle
real x // x-coord of launch point
real y // y-coord
real tx // targeted x-coord
real ty // targeted y-coord
real f // base-angle
real dam1 // damage dealt to the next unit the boomerang hits
real dam2 // damage dealt to the next unit the boomerang hits
group g // group holding already damaged enemies
boolean destroyed=false
static boolexpr DamageFilter
static boolexpr TreeFilter
private integer i
private static thistype array Structs
private static timer T=CreateTimer()
private static integer Count=0
method onDestroy takes nothing returns nothing
set .c=null
if .d1a then
call .dum1.destroy()
endif
if .d2a then
call .dum2.destroy()
endif
call ReleaseGroup(.g)
set .destroyed=true
// clean your struct here
set thistype.Count=thistype.Count-1
set thistype.Structs[.i]=thistype.Structs[thistype.Count]
set thistype.Structs[.i].i=.i
if thistype.Count==0 then
call PauseTimer(thistype.T)
endif
endmethod
private static method UnitDistCheck takes nothing returns nothing
local unit u=GetEnumUnit()
local real x=GetUnitX(u)
local real y=GetUnitY(u)
local real d1x=tmps.dum1.x
local real d1y=tmps.dum1.y
local real d2x=tmps.dum2.x
local real d2y=tmps.dum2.y
if ((not tmps.d1a) or (x-d1x)*(x-d1x)+(y-d1y)*(y-d1y)>BOOMERANG_COLLSIZE*BOOMERANG_COLLSIZE) and ((not tmps.d2a) or (x-d2x)*(x-d2x)+(y-d2y)*(y-d2y)>BOOMERANG_COLLSIZE*BOOMERANG_COLLSIZE) then // if the unit is not inside any boomerang anymore
// unit is not near any of the active boomerangs
call GroupRemoveUnit(tmps.g, u)
endif
set u=null
endmethod
private static method DamageFilterFunc takes nothing returns boolean
local unit u=GetFilterUnit()
// check if unit is a valid target for damage
if ValidTarget(u, tmps) then
if tmpd==1 then
/*addedarmorreduction*/ TAF_USA('A01A',tmps.level,"blackarrow",tmps.c,u)
// tmpd hold the current boomerang; 1 for left-wing, 2 for right-wing
// damage the unit; if the unit for some reason cant be damaged, dont continue
if UnitDamageTarget(tmps.c, u, tmps.dam1, false, false, ATTACK_TYPE, DAMAGE_TYPE, WEAPON_TYPE) then
call DestroyEffect(AddSpecialEffectTarget(HIT_FX, u, HIT_FX_ATTPT))
call GroupAddUnit(tmps.g, u)
static if DAMAGE_ABSORPTION_RELATIVE then
set tmps.dam1=tmps.dam1*(1-Damage_Absorption(tmps.level))
else
set tmps.dam1=tmps.dam1-Damage_Absorption(tmps.level)
endif
if tmps.dam1<=DAMAGE_BOUNDARY then // damage has become too low
call tmps.dum1.destroy() // destroy the boomerang dummy
set tmps.d1a=false // mark that boomerang as destroyed
if not tmps.d2a then // if the other boomerang is dead as well, destroy the spells instance
call tmps.destroy()
endif
endif
endif
elseif tmpd==2 then // pretty much the same here
/*addedarmorreduction*/ TAF_USA('A01A',tmps.level,"blackarrow",tmps.c,u)
if UnitDamageTarget(tmps.c, u, tmps.dam2, false, false, ATTACK_TYPE, DAMAGE_TYPE, WEAPON_TYPE) then
call DestroyEffect(AddSpecialEffectTarget(HIT_FX, u, HIT_FX_ATTPT))
call GroupAddUnit(tmps.g, u)
static if DAMAGE_ABSORPTION_RELATIVE then
set tmps.dam2=tmps.dam2*(1-Damage_Absorption(tmps.level))
else
set tmps.dam2=tmps.dam2-Damage_Absorption(tmps.level)
endif
if tmps.dam2<=DAMAGE_BOUNDARY then
call tmps.dum2.destroy()
set tmps.d2a=false
if not tmps.d1a then
call tmps.destroy()
endif
endif
endif
endif
endif
set u=null
return false
endmethod
private static method TreeFilterFunc takes nothing returns boolean
local destructable d=GetFilterDestructable()
local real x
local real y
local real bx
local real by
if (not IsDestructableDead(d)) and IsDestructableTree(d) then // filter out dead and non-tree destructables
set OverTree=true
static if not IGNORE_TREES then
set x=GetWidgetX(d)
set y=GetWidgetY(d)
if tmpd==1 and tmps.d1a then // same as above
set bx=tmps.dum1.x
set by=tmps.dum1.y
if (x-bx)*(x-bx)+(y-by)*(y-by)<=BOOMERANG_COLLSIZE*BOOMERANG_COLLSIZE then // tree must be inside the collision radius
static if KILL_TREES then // if the boomerang is allowed to kill trees, do so
call KillDestructable(d)
// but adjust the damage as if a unit had been hit
static if DAMAGE_ABSORPTION_RELATIVE then
set tmps.dam1=tmps.dam1*(1-Damage_Absorption(tmps.level))
else
set tmps.dam1=tmps.dam1-Damage_Absorption(tmps.level)
endif
if tmps.dam1<=DAMAGE_BOUNDARY then // same as above
call tmps.dum1.destroy()
set tmps.d1a=false
if not tmps.d2a then
call tmps.destroy()
endif
endif
else // if Trees may not be destroyed, destroy the boomerang instantly
call tmps.dum1.destroy()
set tmps.d1a=false
if not tmps.d2a then // and end the spell instance if appropriate
call tmps.destroy()
endif
endif
endif
elseif tmpd==2 and tmps.d2a then // next section is the same as above
set bx=tmps.dum2.x
set by=tmps.dum2.y
if (x-bx)*(x-bx)+(y-by)*(y-by)<=BOOMERANG_COLLSIZE*BOOMERANG_COLLSIZE then
static if KILL_TREES then
call KillDestructable(d)
static if DAMAGE_ABSORPTION_RELATIVE then
set tmps.dam2=tmps.dam2*(1-Damage_Absorption(tmps.level))
else
set tmps.dam2=tmps.dam2-Damage_Absorption(tmps.level)
endif
if tmps.dam2<=DAMAGE_BOUNDARY then
call tmps.dum2.destroy()
set tmps.d2a=false
if not tmps.d1a then
call tmps.destroy()
endif
endif
else
call tmps.dum2.destroy()
set tmps.d2a=false
if not tmps.d1a then
call tmps.destroy()
endif
endif
endif
endif
endif
endif
set d=null
return false
endmethod
private static method Callback takes nothing returns nothing
local integer i=thistype.Count-1
local thistype s
local real r
local real x
local real y
loop
exitwhen i<0
set s=thistype.Structs[i]
//
// make the boomerang home, even if the caster moves
set s.x=GetUnitX(s.c)
set s.y=GetUnitY(s.c)
set s.d=SquareRoot( (s.tx-s.x)*(s.tx-s.x)+(s.ty-s.y)*(s.ty-s.y) )
set s.f=Atan2(s.ty-s.y, s.tx-s.x)-bj_PI/(BOOMERANG_FOCUS*2)
// functions for moving the boomerang:
// r(a)=distance*Sin(2*a) // a is the angle and goes from 90 to 0 // distance from center point
// x(a)=Cos(a)*r(a) // x and y coordinates in relation to the location it was cast.
// y(a)=Sin(a)*r(a) // note that i inlined some things to allow casting the boomerang in all directions from any point on the map
set r=(s.d*Sin(BOOMERANG_FOCUS*s.a))
set tmps=s
static if ALLOW_MULTIPLE_HITS then
call ForGroup(s.g, function Data.UnitDistCheck)
endif
if s.d1a then // is dummy 1 active
set tmpd=1 // indicate were working with dummy 1
set x=s.x+(Cos(s.a+s.f)*r)
set y=s.y+(Sin(s.a+s.f)*r)
set s.dum1.x=x
set s.dum1.y=y
call GroupEnumUnitsInRange(ENUM_GROUP, x, y, BOOMERANG_COLLSIZE, Data.DamageFilter)
static if not COLLIDE_WITH_GROUND and IGNORE_TREES then
else
set OverTree=false
call SetRect(R, x-BOOMERANG_COLLSIZE, y-BOOMERANG_COLLSIZE, x+BOOMERANG_COLLSIZE, y+BOOMERANG_COLLSIZE)
call EnumDestructablesInRect(R, Data.TreeFilter, null)
static if COLLIDE_WITH_GROUND then
if not OverTree and not IsTerrainWalkable(x,y) and not s.destroyed then // if boomerangs collide with unwalkable terrain and the terrain is unwalkable
call s.dum1.destroy() // destroy the dummy
set s.d1a=false
if not s.d2a then // if the other dummy is inactive
call s.destroy() // destroy this spell instance
endif
endif
endif
endif
endif
if s.d2a then
set tmpd=2
set x=s.x+(Cos(bj_PI/BOOMERANG_FOCUS-s.a+s.f)*r)
set y=s.y+(Sin(bj_PI/BOOMERANG_FOCUS-s.a+s.f)*r)
set s.dum2.x=x
set s.dum2.y=y
call GroupEnumUnitsInRange(ENUM_GROUP, x, y, BOOMERANG_COLLSIZE, Data.DamageFilter)
static if not COLLIDE_WITH_GROUND and IGNORE_TREES then
else
set OverTree=false
call SetRect(R, x-BOOMERANG_COLLSIZE, y-BOOMERANG_COLLSIZE, x+BOOMERANG_COLLSIZE, y+BOOMERANG_COLLSIZE)
call EnumDestructablesInRect(R, Data.TreeFilter, null)
static if COLLIDE_WITH_GROUND then
if not OverTree and not IsTerrainWalkable(x,y) and not s.destroyed then
call s.dum2.destroy()
set s.d2a=false
if not s.d1a then
call s.destroy()
endif
endif
endif
endif
endif
set s.a=s.a-(TICK*((bj_PI*s.d)/(BOOMERANG_FOCUS*2*Boomerang_Speed(s.level))))
if s.a<=0 or IsUnitType(s.c, UNIT_TYPE_DEAD)==true then // stop this spell when caster is dead or the boomerang is at the casters position again.
call s.destroy()
endif
// do your things here, dont forget to call s.destroy() somewhen
//
set i=i-1
endloop
endmethod
static method SpellCond takes nothing returns boolean
return GetSpellAbilityId()==AID
endmethod
static method create takes nothing returns thistype
local thistype s=thistype.allocate()
set s.tx=GetSpellTargetX()
set s.ty=GetSpellTargetY()
set s.c=GetTriggerUnit()
set s.x=GetUnitX(s.c)
set s.y=GetUnitY(s.c)
set s.level=GetUnitAbilityLevel(s.c, AID)
set s.d=SquareRoot( ((s.tx-s.x)*(s.tx-s.x))+((s.ty-s.y)*(s.ty-s.y)) )
if s.d==0 then
set s.f=(GetUnitFacing(s.c)*bj_DEGTORAD)
else
set s.f=Atan2(s.ty-s.y, s.tx-s.x)
endif
if s.d<Min_Range(s.level) then // enforce the minimum distance
set s.d=Min_Range(s.level)
set s.tx=s.x+s.d*Cos(s.f)
set s.ty=s.y+s.d*Sin(s.f)
endif
set s.f=s.f-(bj_PI/(BOOMERANG_FOCUS*2))
set s.a=(bj_PI/BOOMERANG_FOCUS)
set s.g=NewGroup()
set s.d1a=false
set s.d2a=false
static if USE_RIGHT_BOOMERANG then
set s.d1a=true
set s.dam1=Damage(s.level)
set s.dum1=xefx.create(s.x, s.y, 0)
set s.dum1.fxpath=BOOMERANG_MODEL
set s.dum1.scale=BOOMERANG_SIZE
set s.dum1.z=BOOMERANG_HEIGHT
endif
static if USE_LEFT_BOOMERANG then
set s.d2a=true
set s.dam2=Damage(s.level)
set s.dum2=xefx.create(s.x, s.y, 0)
set s.dum2.fxpath=BOOMERANG_MODEL
set s.dum2.scale=BOOMERANG_SIZE
set s.dum2.z=BOOMERANG_HEIGHT
endif
// initialize the struct here
set thistype.Structs[thistype.Count]=s
set s.i=thistype.Count
if thistype.Count==0 then
call TimerStart(thistype.T, TICK, true, function thistype.Callback)
endif
set thistype.Count=thistype.Count+1
if (not s.d1a) and (not s.d2a) then
call s.destroy()
return 0
endif
return s
endmethod
private static method onInit takes nothing returns nothing
local trigger t=CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(t, Condition(function Data.SpellCond))
call TriggerAddAction(t, function Data.create)
set Data.DamageFilter=Condition(function Data.DamageFilterFunc)
set Data.TreeFilter=Condition(function Data.TreeFilterFunc)
call SetUpSpellData()
endmethod
endstruct
/*
private keyword Data // DO NOT TOUCH; configuration is below
// Credits:
// - Ciebron for the inspiration
// - -JonNny for reporting some bugs
// - Rising_Dusk for his GroupUtils library
// - Anitarf for his IsTerrainWalkable library
// - Vexorian for JassHelper and xe
// - PipeDream for Grimoire
// - PitzerMike for JassNewGenPack and DestructableLib
// - MindWorX for JassNewGenPack
// - SFilip for TESH
globals
private constant real TICK = 1./32 // granulation of movement
private constant integer AID = 'A0G8' // the ability triggering this spell
private real array DAMAGE // damage dealt by the boomerang to units it hits
private real array DAMAGE_ABSORPTION // the damage dealt is reduced by this much everytime the boomerang hits a unit or a tree
private constant boolean DAMAGE_ABSORPTION_RELATIVE = true // is DAMAGE_ABSORPTION to be treated as an absolute value or a value relative to the current damage
private constant real DAMAGE_BOUNDARY = 10. // once the damage is lower or equal to this, the boomerang stops flying
private constant string BOOMERANG_MODEL = "war3mapImported\\s_Greenmagic Projectile.mdl" // this is the model representing the boomerang
private constant real BOOMERANG_COLLSIZE = 96. // the AoE in which units are damaged by the boomerang
private constant real BOOMERANG_SIZE = 1.25 // the scaling of the boomerang
private constant real BOOMERANG_HEIGHT = 64. // the Z height of the boomerang
private constant real BOOMERANG_SPEED = 600. // the speed at which the boomerang moves // due to limitations this is only an approximation
private constant boolean ALLOW_MULTIPLE_HITS = true // if this is true, the boomerang can hit units more than once on his path
private constant boolean USE_RIGHT_BOOMERANG = true // just avoid setting both to false, okay?
private constant boolean USE_LEFT_BOOMERANG = false
private constant boolean COLLIDE_WITH_GROUND = false // do boomerangs collide with unwalkable terrain?
private real array MIN_RANGE // minimum throwing distance for the boomerang
private constant boolean KILL_TREES = false // if true, trees are killed once the boomerang hits one, if this is false, the boomerang stops flying
private constant string HIT_FX = "Objects\\Spawnmodels\\Critters\\Albatross\\CritterBloodAlbatross.mdl" // when the boomerang hits a unit, this effect is spawned on the unit hit
private constant string HIT_FX_ATTPT = "chest" // the beforementioned effect will be attached to this point
private constant attacktype ATTACK_TYPE = ATTACK_TYPE_MAGIC // the attack type of the damage the boomerang deals
private constant damagetype DAMAGE_TYPE = DAMAGE_TYPE_MAGIC // the damage type of the damage the boomerang deals
private constant weapontype WEAPON_TYPE = WEAPON_TYPE_METAL_MEDIUM_SLICE // sound when boomerang hits a unit
endglobals
private function SetUpDAMAGE takes nothing returns nothing
set DAMAGE[1]=200. // initially deals 200 damage
set DAMAGE[2]=275.
set DAMAGE[3]=350.
endfunction
private function Damage takes integer level returns real // PROXY
return DAMAGE[level]
endfunction
private function SetUpDAMAGE_ABSORPTION takes nothing returns nothing
set DAMAGE_ABSORPTION[1]=0.16 // lowers damage dealt by 16% of current damage.
set DAMAGE_ABSORPTION[2]=0.12
set DAMAGE_ABSORPTION[3]=0.08
endfunction
private function Damage_Absorption takes integer level returns real // PROXY
return DAMAGE_ABSORPTION[level]
endfunction
private function SetUpMIN_RANGE takes nothing returns nothing
set MIN_RANGE[1]=800.
set MIN_RANGE[2]=800.
set MIN_RANGE[3]=800.
endfunction
private function Min_Range takes integer level returns real // PROXY
return MIN_RANGE[level]
endfunction
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 (not IsUnitInGroup(u, s.g)) \
and IsUnitEnemy(u, GetOwningPlayer(s.c))
endfunction
// This is shit. Don't touch shit.
globals
private location l
private rect R=Rect(0,0,1,1)
private Data tmps
private integer tmpd
endglobals
private struct Data
unit c // caster
xefx dum1 // boomerang dummy 1
xefx dum2 // boomerang dummy 2
boolean d1a // is dummy 1 active
boolean d2a // is dummy 2 active
integer level
real d // planned distance
real a // current angle
real x // x-coord of launch point
real y // y-coord
real tx // targeted x-coord
real ty // targeted y-coord
real f // base-angle
real dam1 // damage dealt to the next unit the boomerang hits
real dam2 // damage dealt to the next unit the boomerang hits
group g // group holding already damaged enemies
boolean destroyed=false
static boolexpr DamageFilter
static boolexpr TreeFilter
private integer i
private static thistype array Structs
private static timer T=CreateTimer()
private static integer Count=0
method onDestroy takes nothing returns nothing
set .c=null
if .d1a then
call .dum1.destroy()
endif
if .d2a then
call .dum2.destroy()
endif
call ReleaseGroup(.g)
set .destroyed=true
// clean your struct here
set thistype.Count=thistype.Count-1
set thistype.Structs[.i]=thistype.Structs[thistype.Count]
set thistype.Structs[.i].i=.i
if thistype.Count==0 then
call PauseTimer(thistype.T)
endif
endmethod
private static method UnitDistCheck takes nothing returns nothing
local unit u=GetEnumUnit()
local real x=GetUnitX(u)
local real y=GetUnitY(u)
local real d1x=tmps.dum1.x
local real d1y=tmps.dum1.y
local real d2x=tmps.dum2.x
local real d2y=tmps.dum2.y
if ((not tmps.d1a) or (((x-d1x)*(x-d1x))+((y-d1y)*(y-d1y)))>BOOMERANG_COLLSIZE*BOOMERANG_COLLSIZE) and ((not tmps.d2a) or (((x-d2x)*(x-d2x))+((y-d2y)*(y-d2y)))>BOOMERANG_COLLSIZE*BOOMERANG_COLLSIZE) then // if the unit is not inside any boomerang anymore
// unit is not near any of the active boomerangs
call GroupRemoveUnit(tmps.g, u)
endif
set u=null
endmethod
private static method DamageFilterFunc takes nothing returns boolean
local unit u=GetFilterUnit()
// check if unit is a valid target for damage
if ValidTarget(u, tmps) then
if tmpd==1 then // tmpd hold the current boomerang; 1 for left-wing, 2 for right-wing
// damage the unit; if the unit for some reason cant be damaged, dont continue
if UnitDamageTarget(tmps.c, u, tmps.dam1, false, false, ATTACK_TYPE, DAMAGE_TYPE, WEAPON_TYPE) then
call DestroyEffect(AddSpecialEffectTarget(HIT_FX, u, HIT_FX_ATTPT))
call GroupAddUnit(tmps.g, u)
if DAMAGE_ABSORPTION_RELATIVE then
set tmps.dam1=tmps.dam1*(1-Damage_Absorption(tmps.level))
else
set tmps.dam1=tmps.dam1-Damage_Absorption(tmps.level)
endif
if tmps.dam1<=DAMAGE_BOUNDARY then // damage has become too low
call tmps.dum1.destroy() // destroy the boomerang dummy
set tmps.d1a=false // mark that boomerang as destroyed
if not tmps.d2a then // if the other boomerang is dead as well, destroy the spells instance
call tmps.destroy()
endif
endif
endif
elseif tmpd==2 then // pretty much the same here
if UnitDamageTarget(tmps.c, u, tmps.dam2, false, false, ATTACK_TYPE, DAMAGE_TYPE, WEAPON_TYPE) then
call DestroyEffect(AddSpecialEffectTarget(HIT_FX, u, HIT_FX_ATTPT))
call GroupAddUnit(tmps.g, u)
if DAMAGE_ABSORPTION_RELATIVE then
set tmps.dam2=tmps.dam2*(1-Damage_Absorption(tmps.level))
else
set tmps.dam2=tmps.dam2-Damage_Absorption(tmps.level)
endif
if tmps.dam2<=DAMAGE_BOUNDARY then
call tmps.dum2.destroy()
set tmps.d2a=false
if not tmps.d1a then
call tmps.destroy()
endif
endif
endif
endif
endif
set u=null
return false
endmethod
private static method TreeFilterFunc takes nothing returns boolean
local destructable d=GetFilterDestructable()
local real x
local real y
local real bx
local real by
if (not IsDestructableDead(d)) and IsDestructableTree(d) then // filter out dead and non-tree destructables
set x=GetWidgetX(d)
set y=GetWidgetY(d)
if tmpd==1 and tmps.d1a then // same as above
set bx=tmps.dum1.x
set by=tmps.dum1.y
if (((x-bx)*(x-bx))+((y-by)*(y-by)))<=BOOMERANG_COLLSIZE*BOOMERANG_COLLSIZE then // tree must be inside the collision radius
if KILL_TREES then // if the boomerang is allowed to kill trees, do so
call KillDestructable(d)
// but adjust the damage as if a unit had been hit
if DAMAGE_ABSORPTION_RELATIVE then
set tmps.dam1=tmps.dam1*(1-Damage_Absorption(tmps.level))
else
set tmps.dam1=tmps.dam1-Damage_Absorption(tmps.level)
endif
if tmps.dam1<=DAMAGE_BOUNDARY then // same as above
call tmps.dum1.destroy()
set tmps.d1a=false
if not tmps.d2a then
call tmps.destroy()
endif
endif
else // if Trees may not be destroyed, destroy the boomerang instantly
call tmps.dum1.destroy()
set tmps.d1a=false
if not tmps.d2a then // and end the spell instance if appropriate
call tmps.destroy()
endif
endif
endif
elseif tmpd==2 and tmps.d2a then // next section is the same as above
set bx=tmps.dum2.x
set by=tmps.dum2.y
if (((x-bx)*(x-bx))+((y-by)*(y-by)))<=BOOMERANG_COLLSIZE*BOOMERANG_COLLSIZE then
if KILL_TREES then
call KillDestructable(d)
if DAMAGE_ABSORPTION_RELATIVE then
set tmps.dam2=tmps.dam2*(1-Damage_Absorption(tmps.level))
else
set tmps.dam2=tmps.dam2-Damage_Absorption(tmps.level)
endif
if tmps.dam2<=DAMAGE_BOUNDARY then
call tmps.dum2.destroy()
set tmps.d2a=false
if not tmps.d1a then
call tmps.destroy()
endif
endif
else
call tmps.dum2.destroy()
set tmps.d2a=false
if not tmps.d1a then
call tmps.destroy()
endif
endif
endif
endif
endif
set d=null
return false
endmethod
private static method Callback takes nothing returns nothing
local integer i=thistype.Count-1
local thistype s
local real r
local real x
local real y
loop
exitwhen i<0
set s=thistype.Structs[i]
//
// make the boomerang home, even if the caster moves
set s.x=GetUnitX(s.c)
set s.y=GetUnitY(s.c)
set s.d=SquareRoot( ((s.tx-s.x)*(s.tx-s.x))+((s.ty-s.y)*(s.ty-s.y)) )
set s.f=Atan2(s.ty-s.y, s.tx-s.x)-(bj_PI/4)
// functions for moving the boomerang:
// r(a)=distance*Sin(2*a) // a is the angle and goes from 90 to 0 // distance from center point
// x(a)=Cos(a)*r(a) // x and y coordinates in relation to the location it was cast.
// y(a)=Sin(a)*r(a) // note that i inlined some things to allow casting the boomerang in all directions from any point on the map
set r=(s.d*Sin(2*s.a))
set tmps=s
if ALLOW_MULTIPLE_HITS then
call ForGroup(s.g, function Data.UnitDistCheck)
endif
if s.d1a then // is dummy 1 active
set tmpd=1 // indicate were working with dummy 1
set x=s.x+(Cos(s.a+s.f)*r)
set y=s.y+(Sin(s.a+s.f)*r)
set s.dum1.x=x
set s.dum1.y=y
call GroupEnumUnitsInRange(ENUM_GROUP, x, y, BOOMERANG_COLLSIZE, Data.DamageFilter)
call SetRect(R, x-BOOMERANG_COLLSIZE, y-BOOMERANG_COLLSIZE, x+BOOMERANG_COLLSIZE, y+BOOMERANG_COLLSIZE)
call EnumDestructablesInRect(R, Data.TreeFilter, null)
if COLLIDE_WITH_GROUND and not IsTerrainWalkable(x,y) and not s.destroyed then // if boomerangs collide with unwalkable terrain and the terrain is unwalkable
call s.dum1.destroy() // destroy the dummy
set s.d1a=false
if not s.d2a then // if the other dummy is inactive
call s.destroy() // destroy this spell instance
endif
endif
endif
if s.d2a then
set tmpd=2
set x=s.x+(Cos((bj_PI/2-s.a)+s.f)*r)
set y=s.y+(Sin((bj_PI/2-s.a)+s.f)*r)
set s.dum2.x=x
set s.dum2.y=y
call GroupEnumUnitsInRange(ENUM_GROUP, x, y, BOOMERANG_COLLSIZE, Data.DamageFilter)
call SetRect(R, x-BOOMERANG_COLLSIZE, y-BOOMERANG_COLLSIZE, x+BOOMERANG_COLLSIZE, y+BOOMERANG_COLLSIZE)
call EnumDestructablesInRect(R, Data.TreeFilter, null)
if COLLIDE_WITH_GROUND and not IsTerrainWalkable(x,y) and not s.destroyed then
call s.dum2.destroy()
set s.d2a=false
if not s.d1a then
call s.destroy()
endif
endif
endif
set s.a=s.a-(TICK*((bj_PI*BOOMERANG_SPEED)/(4*s.d)))
if s.a<=0 or IsUnitType(s.c, UNIT_TYPE_DEAD)==true then // stop this spell when caster is dead or the boomerang is at the casters position again.
call s.destroy()
endif
// do your things here, dont forget to call s.destroy() somewhen
//
set i=i-1
endloop
endmethod
static method SpellCond takes nothing returns boolean
return GetSpellAbilityId()==AID
endmethod
static method create takes nothing returns thistype
local thistype s=thistype.allocate()
set l=GetSpellTargetLoc()
set s.tx=GetLocationX(l)
set s.ty=GetLocationY(l)
call RemoveLocation(l)
set l=null
set s.c=GetTriggerUnit()
set s.x=GetUnitX(s.c)
set s.y=GetUnitY(s.c)
set s.level=GetUnitAbilityLevel(s.c, AID)
set s.d=SquareRoot( ((s.tx-s.x)*(s.tx-s.x))+((s.ty-s.y)*(s.ty-s.y)) )
if s.d==0 then
set s.f=(GetUnitFacing(s.c)*bj_DEGTORAD)
else
set s.f=Atan2(s.ty-s.y, s.tx-s.x)
endif
if s.d<Min_Range(s.level) then // enforce the minimum distance
set s.d=Min_Range(s.level)
set s.tx=s.x+s.d*Cos(s.f)
set s.ty=s.y+s.d*Sin(s.f)
endif
set s.f=s.f-(bj_PI/4)
set s.a=(bj_PI/2)
set s.g=NewGroup()
set s.d1a=false
set s.d2a=false
if USE_RIGHT_BOOMERANG then
set s.d1a=true
set s.dam1=Damage(s.level)
set s.dum1=xefx.create(s.x, s.y, 0)
set s.dum1.fxpath=BOOMERANG_MODEL
set s.dum1.scale=BOOMERANG_SIZE
set s.dum1.z=BOOMERANG_HEIGHT
endif
if USE_LEFT_BOOMERANG then
set s.d2a=true
set s.dam2=Damage(s.level)
set s.dum2=xefx.create(s.x, s.y, 0)
set s.dum2.fxpath=BOOMERANG_MODEL
set s.dum2.scale=BOOMERANG_SIZE
set s.dum2.z=BOOMERANG_HEIGHT
endif
// initialize the struct here
set thistype.Structs[thistype.Count]=s
set s.i=thistype.Count
if thistype.Count==0 then
call TimerStart(thistype.T, TICK, true, function thistype.Callback)
endif
set thistype.Count=thistype.Count+1
if (not s.d1a) and (not s.d2a) then
call s.destroy()
return 0
endif
return s
endmethod
private static method onInit takes nothing returns nothing
local trigger t=CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(t, Condition(function Data.SpellCond))
call TriggerAddAction(t, function Data.create)
set Data.DamageFilter=Condition(function Data.DamageFilterFunc)
set Data.TreeFilter=Condition(function Data.TreeFilterFunc)
call SetUpDAMAGE()
call SetUpDAMAGE_ABSORPTION()
call SetUpMIN_RANGE()
endmethod
endstruct
*/
endscope
//TESH.scrollpos=-1
//TESH.alwaysfold=0
//Spirit Walker Hero
//made by Tavaunum (hiveworkshop.com) and edited by SanKakU
//needs gt, xepreload, and i think i need to edit it so that it uses timerutils as well...
library SpiritWalker
globals
public constant integer FrozenDivinity = 'A03N'//hero spell frozen divinity
public constant integer AvatarOfLife = 'A03M'//dummyspell frozen divinity
public constant integer AvatarOfIce = 'o006'//iceavatardummy
public constant integer AvatarOfLight = 'o007'//lightavatardummy
public constant integer FrozenDivinityEffect = 'o008'//the frozendivinitydummyeffectunit
public constant integer IceShard = 'o00A'//frozen divinity obstacles summoned
public constant integer SearingLightBeam = 'o00B'//the searinglight beam/dummy
public constant integer SearingLightBeamLarge = 'o00C'//the searinglight beam/dummy
public constant integer SearingLightStatus = 'A0BX'//searing fire, the status effect for searing light
public constant string SearingLightStatusOrder = "faeriefire"//
//public constant integer Enchantment = 'A03Q' //untriggered spell
public constant integer SearingLight = 'A03P' //triggered spell
public constant integer ICENOVA = 'A03O' //triggered spell
public constant integer ICENOVASLOWER = 'o009'//the slowing dummy
public constant integer ICENOVASLOW = 'A03L'//slowing dummy spell
public constant string DIVINITYSTART_EFFECT = "Abilities\\Spells\\Orc\\Reincarnation\\ReincarnationTarget.mdl"//effectrequired for frozen divinity
public constant string DIVINITYREAPPEAR_EFFECT = "Abilities\\Spells\\Other\\Monsoon\\MonsoonBoltTarget.mdl"//effectrequired for frozen divinity
public constant integer Tavaunum = 'ETHP'//the spiritwalker hero
endglobals
endlibrary
//NEEDS MORE WORK...RECODE IT ALL?
//==the spell is
//==
//==originally done by Tavaunum, redesigned by SanKakU
//==instead of simply doing damage, it also gives armor
//==reduction AND sight of the units for 2.5/5 seconds
//=='A0BX'
scope SearingLight initializer Init
globals
private group grp=CreateGroup()
private boolexpr be = null
private boolean spareally=true
private constant attacktype at=PureMageAttack
private constant damagetype dt=MagicalDamage
private constant weapontype wt=GenericWeapon
private constant integer iac=SpiritWalker_SearingLight
private constant integer status=SpiritWalker_SearingLightStatus
private constant string order=SpiritWalker_SearingLightStatusOrder
endglobals
private function damage takes integer lvl returns real
return 50.00* 1.00+(lvl *50.00)
endfunction
private function range takes integer lvl returns real
return 400.00* 1.00+(lvl *0.00)
endfunction
private function SL_Filter takes nothing returns boolean
return IsUnitEnemy(GetFilterUnit(),GetOwningPlayer(SpellEvent.CastingUnit))
endfunction
private function OnEffect takes nothing returns nothing
local integer lvl=GetUnitAbilityLevel(SpellEvent.CastingUnit,iac)
local unit x = null
local player y = GetOwningPlayer(SpellEvent.CastingUnit)
local real ux = GetUnitX(SpellEvent.CastingUnit)
local real uy = GetUnitY(SpellEvent.CastingUnit)
//! textmacro searinglight takes reala, realb
set x = CreateUnit(y, SpiritWalker_SearingLightBeam, ux + range(lvl) * Cos($reala$ * bj_DEGTORAD), uy + range(lvl) * Sin($reala$ * bj_DEGTORAD), $realb$)
call UnitApplyTimedLife(x, 'BTLF', 3.0)
//! endtextmacro
//! runtextmacro searinglight("45.00","270.00")
//! runtextmacro searinglight("45.00","180.00")
//! runtextmacro searinglight("45.00","225.00")
//! runtextmacro searinglight("135.00","270.00")
//! runtextmacro searinglight("135.00","0.00")
//! runtextmacro searinglight("135.00","315.00")
//! runtextmacro searinglight("225.00","90.00")
//! runtextmacro searinglight("225.00","0.00")
//! runtextmacro searinglight("225.00","45.00")
//! runtextmacro searinglight("315.00","90.00")
//! runtextmacro searinglight("315.00","180.00")
//! runtextmacro searinglight("315.00","135.00")
set x = null
set be=Filter(function SL_Filter)
call TAF_USAP(status,lvl,order,SpellEvent.CastingUnit,grp,ux,uy,range(lvl),be)
call TAF_UDP(SpellEvent.CastingUnit,grp,ux,uy,range(lvl),be,at,dt,wt,damage(lvl))
endfunction
private function Init takes nothing returns nothing
call RegisterSpellEffectResponse(iac, OnEffect)
//call XE_PreloadAbility(iac)
call XE_PreloadAbility(status)
endfunction
endscope
//
/*
//flame storm speedup effect
//made by SanKakU so please give credits if you use this spell in your map
else
if i == AvengingFlames then
if GetFloatGameState(GAME_STATE_TIME_OF_DAY) < 18.00 then
set u = CreateUnit(GetOwningPlayer(GetSpellAbilityUnit()), 'e000', ux, uy, 270.00)
call UnitAddAbility(u, 'A00Z')
call SetUnitAbilityLevel(u, 'A00Z', GetUnitAbilityLevel(GetSpellAbilityUnit(),AvengingFlames))
call UnitApplyTimedLife(u, 'BTLF', 5.0)
endif
else
//coldstorm slow effect
//made by SanKakU so please give credits if you use this spell in your map
if i == ColdStorm then
if GetFloatGameState(GAME_STATE_TIME_OF_DAY) >= 18.00 then
set u = CreateUnit(GetOwningPlayer(GetSpellAbilityUnit()), 'e000', ux, uy, 270.00)
call UnitAddAbility(u, 'A00Y')
call SetUnitAbilityLevel(u, 'A00Y', GetUnitAbilityLevel(GetSpellAbilityUnit(),ColdStorm))
call UnitApplyTimedLife(u, 'BTLF', 5.0)
endif
else
*/
//ENCHANTMENT
//==the spell isn't triggered.
//==
//==originally done by Tavaunum, redesigned by SanKakU
//=='A03Q'
//SEE IF YOU CAN IMPROVE THE SPELL SOMEHOW...THINK HARD
scope ICENOVA initializer I
private function f takes nothing returns boolean
local unit ut = GetTriggerUnit()
local player p = GetOwningPlayer(ut)
local integer lvl = GetUnitAbilityLevel(ut,SpiritWalker_ICENOVA)
local real ux = GetUnitX(ut)
local real uy = GetUnitY(ut)
set ut = CreateUnit(p, SpiritWalker_ICENOVASLOWER, ux, uy, 270.00)
call SetUnitAbilityLevel(ut, SpiritWalker_ICENOVASLOW, lvl)
call IssueImmediateOrder( ut, "thunderclap" )
call UnitApplyTimedLife(ut, 'BTLF', 3.0)
set ut = null
return false
endfunction
public function I takes nothing returns nothing
call GT_AddStartsEffectAction(function f,SpiritWalker_ICENOVA)
call XE_PreloadAbility(SpiritWalker_ICENOVASLOW)
endfunction
endscope
//plan to recode the spell again one day,just minor changes,maybe...
scope FrozenDivinity initializer I
//! textmacro MakeCubes
set d.dum = CreateUnit(d.play, SpiritWalker_IceShard, d.castx + GetRandomReal(150.00, 800.00) * Cos(GetRandomReal(0, 359.99) * bj_DEGTORAD), d.casty + GetRandomReal(150.00, 800.00) * Sin(GetRandomReal(0, 359.99) * bj_DEGTORAD), GetRandomReal(0, 359.99))
call UnitApplyTimedLife(d.dum, 'BTLF', 30.00)
//! endtextmacro
globals
private constant real PERIOD = 0.10
private constant integer ORBCOUNTSTART = 18
private integer ORBCOUNT = 0
endglobals
private struct Data
unit cast
unit dum
player play
integer walk
integer orb
real castx
real casty
effect eff
timer tim
static method create takes unit cast returns Data
local Data d = Data.allocate()
set d.cast = cast
set d.castx = GetUnitX(d.cast)
set d.casty = GetUnitY(d.cast)
set d.walk = GetUnitAbilityLevel(d.cast,SpiritWalker_FrozenDivinity)
set ORBCOUNT = ORBCOUNTSTART + 10*d.walk
set d.orb = 0
set d.play = GetOwningPlayer(d.cast)
call ShowUnit (d.cast, false)
//call TriggerSleepAction( 1.267 )//the animation duration for spellcasting
//i think by setting it to trigger when it finishes casting the above commented call isn't needed
set d.eff = AddSpecialEffect(SpiritWalker_DIVINITYSTART_EFFECT, d.castx, d.casty)
set d.dum = CreateUnit(d.play, SpiritWalker_AvatarOfIce, d.castx, d.casty, GetRandomReal(0, 359.99))
call UnitApplyTimedLife(d.dum, 'BTLF', 1.80 + (1.00 * d.walk))
call SetUnitAnimation( d.dum, "Birth" )
set d.tim = NewTimer()
call SetTimerData(d.tim, d)
return d
endmethod
method onDestroy takes nothing returns nothing
call ReleaseTimer(.tim)
endmethod
endstruct
private function Callback takes nothing returns boolean
local timer tim = GetExpiredTimer()
local Data d = GetTimerData(tim)
set d.orb = d.orb + 1
if d.orb == 13 then
call SetUnitAnimationWithRarity( d.dum, "Stand", RARITY_FREQUENT )
set d.dum = CreateUnit(d.play, SpiritWalker_FrozenDivinityEffect, d.castx, d.casty, GetRandomReal(0, 359.99))
call UnitApplyTimedLife(d.dum, 'BTLF', 0.50 + (1.00 * d.walk))
call SetUnitAnimation( d.dum, "birth" )
elseif d.orb ==18 then
call SetUnitAnimationWithRarity( d.dum, "stand", RARITY_FREQUENT )
elseif d.orb >= ORBCOUNT then
call DestroyEffect(AddSpecialEffect(SpiritWalker_DIVINITYREAPPEAR_EFFECT, d.castx, d.casty))
call DestroyEffect(d.eff)
call CinematicFadeBJ( bj_CINEFADETYPE_FADEIN, 1.00, "ReplaceableTextures\\CameraMasks\\White_mask.blp", 100.00, 100.00, 100.00, 0 )
call ShowUnit(d.cast, true )
call SelectUnitForPlayerSingle(d.cast, d.play)
//! runtextmacro MakeCubes()
//! runtextmacro MakeCubes()
//! runtextmacro MakeCubes()
//! runtextmacro MakeCubes()
//! runtextmacro MakeCubes()
//! runtextmacro MakeCubes()
//! runtextmacro MakeCubes()
//! runtextmacro MakeCubes()
//! runtextmacro MakeCubes()
//! runtextmacro MakeCubes()
//! runtextmacro MakeCubes()
//! runtextmacro MakeCubes()
//! runtextmacro MakeCubes()
//! runtextmacro MakeCubes()
//! runtextmacro MakeCubes()
set d.dum = CreateUnit(d.play, SpiritWalker_AvatarOfLight, d.castx + 200 * Cos(270 * bj_DEGTORAD), d.casty + 200 * Sin(270 * bj_DEGTORAD), GetRandomReal(0, 359.99))
call SetUnitAbilityLevel(d.dum, SpiritWalker_AvatarOfLight, d.walk)
call SetUnitAnimationWithRarity( d.dum, "Stand Upgrade First", RARITY_FREQUENT )
call UnitApplyTimedLife(d.dum, 'BTLF', 30.00)
call d.destroy()
endif
set tim = null
return false
endfunction
private function Actions takes nothing returns boolean
local Data d = Data.create(GetTriggerUnit())
call TimerStart(d.tim, PERIOD, true, function Callback)
return false
endfunction
public function I takes nothing returns nothing
call GT_AddFinishesCastingAction(function Actions, SpiritWalker_FrozenDivinity)
call Preload(SpiritWalker_DIVINITYSTART_EFFECT)
call Preload(SpiritWalker_DIVINITYREAPPEAR_EFFECT)
//call XE_PreloadAbility(SpiritWalker_FrozenDivinity)
call XE_PreloadAbility(SpiritWalker_AvatarOfLife)
endfunction
endscope
//TESH.scrollpos=229
//TESH.alwaysfold=0
// Bird Strike by Viikuna
// This spell is made for wc3Creations Spell Contest
// Theme: Missile, Light. ( and Birds )
scope Birds initializer init
// this uses SomeFunctions, TT ( Credits go to Cohadar )
globals
//==================================================================================
//==================================================================================
// Ability is based on Channel
private constant integer ABILITY_ID = 'A089'
// AoE for Knockback
private constant real AOE = 300.0
// AoE fot missile collinsion
private constant real MISSILE_AOE = 50.0
// Damage is calculated like this: DAMAGE_LEVEL*AbilityLevel+DAMAGE_BASE
private constant real DAMAGE_BASE = 0.0
private constant real DAMAGE_LEVEL = 50.0
// All the Birds
private constant integer MISSILE_ID0 = 'h016'
private constant integer MISSILE_ID1 = 'h017'
private constant integer MISSILE_ID2 = 'h018'
private constant integer MISSILE_ID3 = 'h019'
private constant integer MISSILE_ID4 = 'h01A'
// Damage types
private constant damagetype DAMAGE_TYPE = DAMAGE_TYPE_MAGIC
private constant attacktype ATTACK_TYPE = ATTACK_TYPE_MAGIC
private constant weapontype WEAPON_TYPE = WEAPON_TYPE_WHOKNOWS
// When unit collides with some object, the damage is calculated like this:
// Units Sliding Velocity * DAMAGE_FACTOR
private constant real DAMAGE_FACTOR = 2.5
// The bigger the number, the bigger the knockback
private constant real KNOCKBACK_FACTOR = 0.01
// missiles Maximum travelling range
private constant real MAX_RANGE = 1500.0
// Missiles move speed
private constant real STARTING_VELOCITY = 10.0
private constant real ACCELERATION = 0.5 / 2
// How fast sliding units stop
private constant real FRICTION = 2.5 / 2
// Maximum targets knocked per Missile
private constant integer MAX_TARGETS = 10
//==================================================================================
//==================================================================================
private integer array MISSILE_ID
private filterfunc F
private filterfunc True
private group TempG = CreateGroup()
private unit TempU
endglobals
private struct Data2
unit u
unit knocker
real sin
real cos
real x
effect e
real y
real velocity
boolean impact = false
method move takes nothing returns nothing
set this.velocity = this.velocity - FRICTION
set this.x = SafeX(this.x + this.velocity * this.cos)
set this.y = SafeY(this.y + this.velocity * this.sin)
set this.velocity = this.velocity - FRICTION
if CheckPathability(this.x,this.y) == true then
call SetUnitX(this.u,this.x)
call SetUnitY(this.u,this.y)
else
set this.impact = true
endif
endmethod
method onDestroy takes nothing returns nothing
call PauseUnit(this.u,false)
call DestroyEffect(this.e)
if this.impact == true then
call UnitDamageTarget(this.knocker,this.u,this.velocity*DAMAGE_FACTOR,false,false,ATTACK_TYPE,DAMAGE_TYPE,WEAPON_TYPE)
call DestroyEffect(AddSpecialEffect("Abilities\\Weapons\\AncientProtectorMissile\\AncientProtectorMissile.mdl",this.x,this.y))
endif
endmethod
endstruct
private function Loop2 takes nothing returns boolean
local Data2 D = TT_GetData()
if D.impact == true or D.velocity <= 0 then
call D.destroy()
return true
endif
call D.move()
return false
endfunction
private struct Data
unit array bird[10]
unit shooter
real velocity
real range = 0.0
real x
real y
real array birdX[10]
real array birdY[10]
real sin
real cos
real array birdCos[10]
real array birdSin[10]
boolean spreaded = false
boolean end = false
method KnockThem takes nothing returns nothing
local unit u
local real x
local real y
local real dx
local real dy
local real a
local Data2 D
local integer i = 0
set TempU = this.shooter
call GroupEnumUnitsInRange(TempG,this.x,this.y,AOE,F)
loop
set u = FirstOfGroup(TempG)
set i = i + 1
exitwhen u == null or i == MAX_TARGETS
set x = GetUnitX(u)
set y = GetUnitY(u)
set dx = x - this.x
set dy = y - this.y
set D = Data2.create()
set D.velocity = (AOE - (SquareRoot(dx * dx + dy * dy)/2) ) * ( this.velocity / 2 ) * KNOCKBACK_FACTOR
set a = Atan2(dy,dx)
set D.sin = Sin(a)
set D.cos = Cos(a)
set D.x = x
set D.y = y
set D.u = u
set D.e = AddSpecialEffectTarget("Abilities\\Spells\\NightElf\\FaerieDragonInvis\\FaerieDragon_Invis.mdl",D.u,"chest")
set D.knocker = this.shooter
call PauseUnit(D.u,true)
call D.move()
call TT_Start(function Loop2,D)
call UnitDamageTarget(this.shooter,u,DAMAGE_LEVEL*GetUnitAbilityLevel(this.shooter,ABILITY_ID)+DAMAGE_BASE,false,false,ATTACK_TYPE,DAMAGE_TYPE,WEAPON_TYPE)
call DestroyEffect(AddSpecialEffect("Abilities\\Weapons\\ProcMissile\\ProcMissile.mdl",x,y))
call GroupRemoveUnit(TempG,u)
endloop
call GroupClear(TempG)
endmethod
method move takes nothing returns nothing
local unit u
local real a
local real dx
local real dy
local real d
local real r
local integer i = 0
local real cos = this.velocity * this.cos
local real sin = this.velocity * this.sin
set this.x = SafeX( this.x + cos)
set this.y = SafeY( this.y + sin)
set TempU = this.shooter
call GroupEnumUnitsInRange(TempG,this.x,this.y,MISSILE_AOE,F)
set u = FirstOfGroup(TempG)
if this.range >= MAX_RANGE or u != null or CheckPathability(this.x,this.y) == false then
call GroupClear(TempG)
set this.spreaded = true
set r = bj_PI / 5
set a = 0
set this.range = 0.0
call this.KnockThem()
loop
exitwhen i > 9
set a = a + r
call SetUnitFacing(this.bird[i],a*bj_RADTODEG)
set this.birdCos[i] = Cos(a)
set this.birdSin[i] = Sin(a)
set i = i + 1
endloop
return
endif
loop
exitwhen i > 9
set dx = this.x - this.birdX[i]
set dy = this.y - this.birdY[i]
set d = SquareRoot(dx * dx + dy * dy)
if d > 150.0 then
set a = Atan2(dy,dx)
set this.birdSin[i] = Sin(a)
set this.birdCos[i] = Cos(a)
else
if d < 10 then
set this.birdSin[i] = GetRandomReal(0.0,1.0)
set this.birdCos[i] = GetRandomReal(0.0,1.0)
endif
endif
set this.birdX[i] = SafeX(this.birdX[i] + cos)
set this.birdY[i] = SafeY(this.birdY[i] + sin)
set this.birdX[i] = SafeX(this.birdX[i] + 5.0 * this.birdCos[i])
set this.birdY[i] = SafeY(this.birdY[i] + 5.0 * this.birdSin[i])
call SetUnitX(this.bird[i],this.birdX[i])
call SetUnitY(this.bird[i],this.birdY[i])
set i = i + 1
endloop
set this.velocity = this.velocity + ACCELERATION
set this.range = this.range + this.velocity
set this.velocity = this.velocity + ACCELERATION
set u = null
endmethod
method spread takes nothing returns nothing
local integer i = 0
if this.range > AOE then
set this.end = true
return
endif
loop
exitwhen i > 9
set this.birdX[i] = SafeX(this.birdX[i] + this.velocity * this.birdCos[i])
set this.birdY[i] = SafeY(this.birdY[i] + this.velocity * this.birdSin[i])
call SetUnitX(this.bird[i],this.birdX[i])
call SetUnitY(this.bird[i],this.birdY[i])
set i = i + 1
endloop
set this.velocity = this.velocity + ACCELERATION
set this.range = this.range + this.velocity
set this.velocity = this.velocity + ACCELERATION
endmethod
method onDestroy takes nothing returns nothing
local integer i = 0
loop
exitwhen i > 9
call KillUnit(this.bird[i])
set i = i + 1
endloop
endmethod
endstruct
private function ItIsTrue takes nothing returns boolean
return true
endfunction
private function filter takes nothing returns boolean
return IsUnitEnemy(GetFilterUnit(),GetOwningPlayer(TempU)) == true and GetWidgetLife(GetFilterUnit()) > 0.0451 and GetUnitFlyHeight(GetFilterUnit()) == 0 and IsUnitType(GetFilterUnit(), UNIT_TYPE_STRUCTURE) == false
endfunction
private function Loop takes nothing returns boolean
local Data D = TT_GetData()
if D.end == true then
call D.destroy()
return true
endif
if D.spreaded == true then
call D.spread()
else
call D.move()
endif
return false
endfunction
private function B takes nothing returns nothing
local Data D = Data.create()
local integer i = 0
local unit u = GetTriggerUnit()
local location loc = GetSpellTargetLoc()
local real x = GetUnitX(u)
local real y = GetUnitY(u)
local real x2 = GetLocationX(loc)
local real y2 = GetLocationY(loc)
local real dx = x2 - x
local real dy = y2 - y
local real angle = Atan2(dy, dx)
set D.sin = Sin(angle)
set D.cos = Cos(angle)
set D.x = x
set D.y = y
set D.shooter = u
set D.velocity = STARTING_VELOCITY
loop
exitwhen i > 9
set D.bird[i] = CreateUnit(Player(13),MISSILE_ID[GetRandomInt(0,4)],D.x,D.y,angle*bj_RADTODEG)
call PauseUnit(D.bird[i],true)
set D.birdX[i] = x
set D.birdY[i] = y
set D.birdCos[i] = GetRandomReal(0.0,1.0)
set D.birdSin[i] = GetRandomReal(0.0,1.0)
set i = i + 1
endloop
call TT_Start(function Loop, D)
call RemoveLocation(loc)
set loc = null
set u = null
endfunction
private function A takes nothing returns boolean
return GetSpellAbilityId() == ABILITY_ID
endfunction
private function init takes nothing returns nothing
local trigger t = CreateTrigger()
local integer i = 0
set MISSILE_ID[0] = MISSILE_ID0
set MISSILE_ID[1] = MISSILE_ID1
set MISSILE_ID[2] = MISSILE_ID2
set MISSILE_ID[3] = MISSILE_ID3
set MISSILE_ID[4] = MISSILE_ID4
set True = Filter(function ItIsTrue)
loop
call TriggerRegisterPlayerUnitEvent(t,Player(i),EVENT_PLAYER_UNIT_SPELL_EFFECT,True)
set i = i + 1
exitwhen i == bj_MAX_PLAYER_SLOTS
endloop
call TriggerAddCondition(t,Condition(function A))
call TriggerAddAction(t,function B)
set F = Filter(function filter)
endfunction
endscope
//TESH.scrollpos=0
//TESH.alwaysfold=0
scope AquaticBlastNoLightning initializer Init
// Aquatic Blast by The_Witcher
//
// well not much to say look through/edit the setup part so they fit your wishes
//
// This NoLightning version looks not as good as the lightning but will run faster/ won't cause lagg
// Because I think there will never be that many instances that it laggs i suggest
// useing the Lightning version, but who knows^^
//
// The SETUP part
globals
// The rawcode of your ability
private constant integer AbiID = 'A000'
// The effect created at the target loc
private constant string GroundSFX = "Abilities\\Spells\\Human\\Brilliance\\Brilliance.mdl"
// The seconde effect created at the target loc
private constant string MainSFX = "Abilities\\Spells\\Other\\Drain\\ManaDrainTarget.mdl"
// The effect created when the whole thing explodes
private constant string ExplodeSFX = "Objects\\Spawnmodels\\Naga\\NagaDeath\\NagaDeath.mdl"
// The effect shown on damaged units
private constant string DamageSFX = "Abilities\\Weapons\\FrostWyrmMissile\\FrostWyrmMissile.mdl"
// The attachment point where the DamageSFX appears
private constant string DamagePoint = "chest"
// The interval (0.01 is smoothest but can lagg in huge maps)
private constant real interval = 0.01
// The boolean whether the spell affects only enemies or all units
private constant boolean EnemyOnly = true
endglobals
private function GetDamage takes integer level returns real
return 120.00 + 50 * (I2R(level) - 1) //120 on level 1, 170 on level 2, 220 on level 3,...
endfunction
private function GetMaxTime takes integer level returns real
return 3 + 2 * I2R(level) //5 on level 1, 7 on level 2, 9 on level 3,...
endfunction
private function GetRange takes integer level returns real
return 200 + 100 * I2R(level) //300 on level 1, 400 level 2, 500 level 3,...
endfunction
private function GetPullSpeed takes integer level returns real
return 2 + 0.5 * I2R(level) //2 on level 1, 2.5 level 2, 3 level 3,...
endfunction
// END OF SETUP PART
private struct data
real x
real y
real t = 0
real mt
real dmg
real r
real d
player pl
integer level
effect sfx1
effect sfx2
integer temp = 0
endstruct
globals
private timer tim = CreateTimer()
private data array DATAS
private integer total = 0
private boolexpr BoolExpr
private group g = CreateGroup()
private player Temp
endglobals
private function EnemyFilter takes nothing returns boolean
return IsUnitEnemy(GetFilterUnit(), Temp)
endfunction
private function True takes nothing returns boolean
return true
endfunction
private function AngleBetweenCoords takes real x, real y, real xx, real yy returns real
return bj_RADTODEG * Atan2(yy - y, xx - x)
endfunction
private function DistanceBetweenCoords takes real x , real y , real xx , real yy returns real
return SquareRoot((x-xx)*(x-xx)+(y-yy)*(y-yy))+0.5
endfunction
private function execute takes nothing returns nothing
local integer i = 0
local data dat
local unit b
local real x
local real y
local real r
loop
exitwhen i >= total
set dat = DATAS[i]
set Temp = dat.pl
set dat.t = dat.t + interval
call GroupEnumUnitsInRange(g, dat.x, dat.y, dat.r, BoolExpr)
loop
set b = FirstOfGroup(g)
exitwhen b == null
if not(IsUnitType(b, UNIT_TYPE_DEAD) or GetUnitTypeId(b) == 0) then
set r = AngleBetweenCoords(GetUnitX(b), GetUnitY(b), dat.x, dat.y) * bj_DEGTORAD
set x = GetUnitX(b) + dat.d * Cos(r)
set y = GetUnitY(b) + dat.d * Sin(r)
call SetUnitX(b,x)
call SetUnitY(b,y)
if DistanceBetweenCoords(x,y,dat.x,dat.y) < dat.d+1 then
call GroupClear(g)
call GroupEnumUnitsInRange(g, dat.x, dat.y, dat.r, BoolExpr)
loop
set b = FirstOfGroup(g)
exitwhen b == null
call SetWidgetLife(b, GetWidgetLife(b) - dat.dmg)
call DestroyEffect(AddSpecialEffectTarget(DamageSFX,b,DamagePoint))
call GroupRemoveUnit(g,b)
endloop
set dat.t = dat.mt+1
endif
endif
call GroupRemoveUnit(g,b)
endloop
if dat.t > dat.mt then
call DestroyEffect(AddSpecialEffect(ExplodeSFX,dat.x,dat.y))
call DestroyEffect(dat.sfx1)
call DestroyEffect(dat.sfx2)
set DATAS[i] = DATAS[total-1]
set total = total - 1
call dat.destroy()
endif
set i = i + 1
endloop
if total == 0 then
call PauseTimer(tim)
endif
set b = null
endfunction
private function cast takes nothing returns nothing
local data dat = data.create()
local unit u = GetTriggerUnit()
local unit b
set dat.x = GetSpellTargetX()
set dat.y = GetSpellTargetY()
set dat.sfx1 = AddSpecialEffect(MainSFX,dat.x,dat.y)
set dat.sfx2 = AddSpecialEffect(GroundSFX,dat.x,dat.y)
set dat.level = GetUnitAbilityLevel(u,AbiID)
set dat.mt = GetMaxTime(dat.level)
set dat.r = GetRange(dat.level)
set dat.dmg = GetDamage(dat.level)
set dat.d = GetPullSpeed(dat.level)
set dat.pl = GetOwningPlayer(u)
set DATAS[total] = dat
set total = total + 1
if total == 1 then
call TimerStart(tim, interval, true, function execute)
endif
call GroupEnumUnitsInRange(g, dat.x, dat.y, dat.r, BoolExpr)
loop
set b = FirstOfGroup(g)
exitwhen b == null
call IssueTargetOrder(b,"attack",u)
call GroupRemoveUnit(g,b)
endloop
set u = null
set b = null
endfunction
private function check takes nothing returns boolean
return GetSpellAbilityId() == AbiID
endfunction
private function Init takes nothing returns nothing
local trigger t = CreateTrigger()
call TriggerAddAction(t, function cast)
call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_SPELL_CAST )
call TriggerAddCondition(t,Condition(function check))
if EnemyOnly then
set BoolExpr = Condition(function EnemyFilter)
else
set BoolExpr = Filter(function True)
endif
endfunction
endscope
//TESH.scrollpos=0
//TESH.alwaysfold=0
/*use this water spirits for creep spell probably...
*/
library WaterSpirits requires TimerUtils, GetNearest, SimError, xefx, xedamage optional BoundSentinel
//-----------------------------------------------------------------------------------------------
// Water Spirits v1.01 by scorpion182
// requires:
// - TimerUtils, SimError, xe by Vexorian
// - GroupUtils (required by GetNearest) by Rising_Dusk
// - GetNearest by grim001
//
//
//
//
//-----------------------------------------------------------------------------------------------
//----------------CALIBRATION SECTION------------------------------------------------------------
globals
private keyword data //don't touch this
private constant integer SPELL_ID='A08Z' //ability rawcode
private constant integer MAX_MISSILE=50 //maximum missiles
private constant string ORB_PATH="Abilities\\Weapons\\SpiritOfVengeanceMissile\\SpiritOfVengeanceMissile.mdl" //orb fx path
private constant string DAMAGE_FX="" //on-damage effect
private constant string DAMAGE_ATTCH="origin" //on-death effect attachment point
private constant string HEAL_FX="" //0n-heal effect
private constant string HEAL_ATTCH="origin" //on-heal effect attachment point
private constant real SCALE=1.2 //orb scale
private constant real ANGLE_SPEED=.15 //orb angle speed
private constant real HEIGHT=100. //orb height
private constant real MIN_DIST=80. //minimum distance to absorb, make sure the value higher than 80, if not it won't work correctly
private constant real MAX_DIST=1000. //max distance to attack the target
private constant string ERROR_MSG="Another instance is running for this unit." //error message
endglobals
private constant function GetDamage takes integer lvl returns real
return 15.+lvl*0 //do damage amount of hp each orb
endfunction
private constant function GetHealFactor takes integer lvl returns real
return 0.50+lvl*0 //heals 50% of the damage dealt by it
endfunction
private constant function GetMissileCount takes integer lvl returns integer
return 3+lvl*0 //orb count
endfunction
private constant function GetDuration takes integer lvl returns real
return 60.+lvl*0 //spell duration
endfunction
private constant function GetDistance takes integer lvl returns real
return 150.+lvl*0 //orb's rotation distance
endfunction
private constant function GetAoE takes integer lvl returns real
return 500.+lvl*0 //aoe each orb to find a target
endfunction
private constant function GetSpeed takes integer lvl returns real
return 800.*XE_ANIMATION_PERIOD+lvl*0. //orb missile speed
endfunction
private function DamageOptions takes xedamage spellDamage returns nothing
set spellDamage.dtype=DAMAGE_TYPE_UNIVERSAL
set spellDamage.atype=ATTACK_TYPE_NORMAL
set spellDamage.exception=UNIT_TYPE_STRUCTURE
set spellDamage.visibleOnly=true
set spellDamage.damageSelf=true
set spellDamage.damageAllies=true //heals for amount of the damage dealt by orb
set spellDamage.allyfactor=-1.0 //to the target ally.
endfunction
//filter the targets, should match the value from DamageOptions
private function IsValidTarget takes unit u, data s returns boolean
return not IsUnitType(u, UNIT_TYPE_DEAD) and GetUnitTypeId(u) != 0 and IsUnitType(u,UNIT_TYPE_STRUCTURE)==false and IsUnitEnemy(u,GetOwningPlayer(s.caster))==true and IsUnitVisible(u,GetOwningPlayer(s.caster))==true
endfunction
//------------------END OF CALIBRATION-----------------------------------------------------------
globals
private constant real A=2*bj_PI
private xedamage xed
private group casters=CreateGroup()
endglobals
private struct data
unit caster //target ally
unit owner //caster
unit array target[MAX_MISSILE] //target enemies
xefx array fx[MAX_MISSILE] //effect
boolean array IsAttack[MAX_MISSILE] //attacking status
boolean array IsHome[MAX_MISSILE] //homing status
boolean array IsFinish[MAX_MISSILE] //finish status
real array angle[MAX_MISSILE] //current angle
real array tx[MAX_MISSILE] //current x track
real array ty[MAX_MISSILE] //current y track
real duration //spell duration
boolean array heal[MAX_MISSILE] //attacking succeed status
timer t
integer total //orbs total
integer lvl //ability level
private static thistype temp
static method create takes unit c, unit o returns data
local thistype this=data.allocate()
local integer i=0
local real x=GetUnitX(c)
local real y=GetUnitY(c)
local real a
set .lvl=GetUnitAbilityLevel(c,SPELL_ID)
set .total=GetMissileCount(.lvl)
set .duration=GetDuration(.lvl)
set .caster=c
set .owner=o
set .t=NewTimer()
loop
exitwhen i==.total
set a=i*A/.total
set .fx[i]=xefx.create(x,y,a)
set .fx[i].fxpath=ORB_PATH
set .fx[i].scale=SCALE
set .fx[i].z=HEIGHT
set .angle[i]=a
set i=i+1
endloop
call GroupAddUnit(casters,c)
return this
endmethod
static method VictimFilter takes nothing returns boolean
return IsValidTarget(GetFilterUnit(),temp)
endmethod
static method onLoop takes nothing returns nothing
local thistype this=data(GetTimerData(GetExpiredTimer()))
local real x=GetUnitX(.caster)
local real y=GetUnitY(.caster)
local integer i=0
local real x1
local real y1
local real x2
local real y2
local real ang
local real newx
local real newy
local real dx
local real dy
local real dist
if .duration>0. and not IsUnitType(.caster, UNIT_TYPE_DEAD) then
set .duration=.duration-XE_ANIMATION_PERIOD
loop
exitwhen i==.total
set .angle[i]=.angle[i]+ANGLE_SPEED
set tx[i]=x+GetDistance(.lvl)*Cos(.angle[i])
set ty[i]=y+GetDistance(.lvl)*Sin(.angle[i])
//if not attacking, order to rotate
if not .IsAttack[i] then
set .fx[i].x=tx[i]
set .fx[i].y=ty[i]
set temp=this
set .target[i]=GetNearestUnit(.fx[i].x,fx[i].y,GetAoE(.lvl),Condition(function data.VictimFilter))
if .target[i]!=null then
set .IsAttack[i]=true
endif
else
//if find a target, order to attack
if not .IsHome[i] then
set x1=.fx[i].x
set y1=.fx[i].y
set x2=GetUnitX(.target[i])
set y2=GetUnitY(.target[i])
set dx=x2-x1
set dy=y2-y1
set ang=Atan2(dy,dx)
set newx=x1+GetSpeed(.lvl)*Cos(ang)
set newy=y1+GetSpeed(.lvl)*Sin(ang)
set dist=SquareRoot(dx*dx+dy*dy)
if (dist>MIN_DIST) then
set .fx[i].x=newx
set .fx[i].y=newy
set .fx[i].xyangle=ang
//if the target is too far away from the caster or dead, order it to come back
if dist>MAX_DIST or IsUnitType(.target[i], UNIT_TYPE_DEAD) then
set .IsHome[i]=true
set .target[i]=null
endif
else
//damage the target
if not IsUnitType(.target[i], UNIT_TYPE_DEAD) then
call xed.useSpecialEffect(DAMAGE_FX,DAMAGE_ATTCH)
call xed.damageTarget(.owner,.target[i],GetDamage(.lvl))
set .heal[i]=true
endif
set .IsHome[i]=true
set .target[i]=null
endif
else
//back to caster
if not .IsFinish[i] then
set x1=.fx[i].x
set y1=.fx[i].y
set x2=GetUnitX(.caster)
set y2=GetUnitY(.caster)
set dx=x2-x1
set dy=y2-y1
set ang=Atan2(dy,dx)
set newx=x1+GetSpeed(.lvl)*Cos(ang)
set newy=y1+GetSpeed(.lvl)*Sin(ang)
set dist=SquareRoot(dx*dx+dy*dy)
if (dist>MIN_DIST) then
set .fx[i].x=newx
set .fx[i].y=newy
set .fx[i].xyangle=ang
else
//heal the caster
if not IsUnitType(.caster, UNIT_TYPE_DEAD) and .heal[i] then
call xed.useSpecialEffect(HEAL_FX,HEAL_ATTCH)
call xed.damageTarget(.owner,.caster,GetDamage(.lvl)*GetHealFactor(.lvl))
endif
set .IsFinish[i]=true
endif
else
//back to its rotation point
set x1=.fx[i].x
set y1=.fx[i].y
set x2=tx[i]
set y2=ty[i]
set dx=x2-x1
set dy=y2-y1
set ang=Atan2(dy,dx)
set newx=x1+GetSpeed(.lvl)*Cos(ang)
set newy=y1+GetSpeed(.lvl)*Sin(ang)
set dist=SquareRoot(dx*dx+dy*dy)
if (dist>MIN_DIST) then
set .fx[i].x=newx
set .fx[i].y=newy
set .fx[i].xyangle=ang
else
set .IsFinish[i]=false
set .IsHome[i]=false
set .IsAttack[i]=false
set .heal[i]=false
set .fx[i].x=tx[i]
set .fx[i].y=ty[i]
endif
endif
endif
endif
set i=i+1
endloop
else
call .destroy()
endif
endmethod
private method onDestroy takes nothing returns nothing
local integer i=0
call ReleaseTimer(.t)
call GroupRemoveUnit(casters,.caster)
loop
exitwhen i==.total
call .fx[i].destroy()
set i=i+1
endloop
endmethod
static method SpellEffect takes nothing returns boolean
local thistype this
if (GetSpellAbilityId()==SPELL_ID) then
set this=data.create(GetSpellTargetUnit(),GetTriggerUnit())
call SetTimerData(.t,this)
call TimerStart(.t,XE_ANIMATION_PERIOD,true,function data.onLoop)
endif
return false
endmethod
static method Stop takes nothing returns boolean
local unit c=GetSpellTargetUnit()
local unit u=GetTriggerUnit()
if GetSpellAbilityId()==SPELL_ID and IsUnitInGroup(c,casters) then
call SimError(GetOwningPlayer(u),ERROR_MSG)
call PauseUnit(u,true)
call IssueImmediateOrder(u,"stop")
call PauseUnit(u,false)
endif
set c=null
set u=null
return false
endmethod
static method onInit takes nothing returns nothing
local trigger t=CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_SPELL_CAST)
call TriggerAddCondition(t,Condition(function data.SpellEffect))
set t=CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_SPELL_CHANNEL)
call TriggerAddCondition(t,Condition(function data.Stop))
//init xedamage
set xed=xedamage.create()
call DamageOptions(xed)
endmethod
endstruct
endlibrary
//TESH.scrollpos=-1
//TESH.alwaysfold=0
constant function WS_Interval takes nothing returns real
return 3.0 //The delay between casting the spell, and blinking to a target
endfunction
constant function WS_Damage_Base takes nothing returns real
return 60.0 //The base damage for the spell: lvl 1 = 60 dmg, lvl 2 = 120 dmg...
endfunction
constant function WS_Detection_AoE takes nothing returns real
return 625.0 //The detection AoE that is used to find an enemy unit
endfunction
constant function WS_AoE takes nothing returns real
return 300.0 //The AoE of the spell that is used when the caster blinks to dmg targets
endfunction
constant function WS_SFX_1 takes nothing returns string
return "Abilities\\Spells\\Other\\Charm\\CharmTarget.mdl"
endfunction
constant function WS_SFX_2 takes nothing returns string
return "Objects\\Spawnmodels\\Naga\\NagaDeath\\NagaDeath.mdl" //Created on every enemy unit's "origin" in the AoE
endfunction
constant function WS_Ability_ID takes nothing returns integer
return 'A08G' //The raw code of the ability "Water Strike"
endfunction
constant function WS_Dummy_Caster_ID takes nothing returns integer
return 'h01C' //The raw code of the unit "Water Strike Dummy"
endfunction
constant function WS_Dummy_Ability_ID takes nothing returns integer
return 'A08F' //The raw code of the ability "Water Strike Dummy Ability"
endfunction
function WS_Filter takes nothing returns boolean
return IsUnitEnemy(GetFilterUnit(), GetOwningPlayer(GetTriggerUnit())) and GetWidgetLife(GetFilterUnit()) > 0.405
endfunction
function WS_Actions takes nothing returns nothing
local unit u = GetTriggerUnit()
local group g = CreateGroup()
local boolexpr b = Condition(function WS_Filter)
local real dmg = GetUnitAbilityLevel(u, WS_Ability_ID()) * WS_Damage_Base()
local real X = GetUnitX(u)
local real Y = GetUnitY(u)
local real ang
local unit p
local unit d
call DestroyEffect(AddSpecialEffect(WS_SFX_1(), X, Y))
call ShowUnit(u, false)
call PauseUnit(u, true)
call SetUnitInvulnerable(u, true)
call PolledWait(WS_Interval())
call GroupEnumUnitsInRange(g, X, Y, WS_Detection_AoE(), b)
if CountUnitsInGroup(g) > 0 then
set p = GroupPickRandomUnit(g)
call SetUnitPosition(u, GetUnitX(p), GetUnitY(p))
call SetUnitFacing(u, GetUnitFacing(p) + 180.0)
endif
call ShowUnit(u, true)
call PauseUnit(u, false)
call SetUnitInvulnerable(u, false)
call SetUnitAnimation(u, "attack")
call SelectUnit(u, true)
set ang = Atan2(GetUnitY(p)-GetUnitY(u), GetUnitX(p)-GetUnitX(u))
call GroupEnumUnitsInRange(g, GetUnitX(u) + 150.0 * Cos(ang), GetUnitY(u) + 150.0 * Sin(ang), WS_AoE(), b)
loop
set p = FirstOfGroup(g)
exitwhen p == null
call GroupRemoveUnit(g, p)
call DestroyEffect(AddSpecialEffectTarget(WS_SFX_2(), p, "origin"))
call UnitDamageTarget(u, p, dmg, false, false, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_NORMAL, null)
set d = CreateUnit(GetOwningPlayer(u), WS_Dummy_Caster_ID(), GetUnitX(u), GetUnitY(u), 0)
call SetUnitAbilityLevel(d, WS_Dummy_Ability_ID(), GetUnitAbilityLevel(u, WS_Ability_ID()))
call IssueTargetOrder(d, "thunderbolt", p)
endloop
call DestroyGroup(g)
call DestroyBoolExpr(b)
set u = null
set g = null
set b = null
set d = null
endfunction
function WS_Cond takes nothing returns boolean
return GetSpellAbilityId() == WS_Ability_ID()
endfunction
function InitTrig_Water_Strike takes nothing returns nothing
set gg_trg_Water_Strike = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(gg_trg_Water_Strike,EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(gg_trg_Water_Strike,Condition(function WS_Cond))
call TriggerAddAction(gg_trg_Water_Strike,function WS_Actions)
call Preload(WS_SFX_1())
call Preload(WS_SFX_2())
endfunction
//TESH.scrollpos=-1
//TESH.alwaysfold=0
constant function HR_Ring_Interval takes nothing returns real
return 0.35 //The raw code of the ability "Hydro Rings"
endfunction
constant function HR_Damage_Base takes nothing returns real
return 30.0 //The base damage for the spell
endfunction
constant function HR_AoE takes nothing returns real
return 200.0 //The AoE of each explosion
endfunction
constant function HR_Ring1_Explosion_Offset takes nothing returns real
return 350.0 //The explosion offset in the 1st ring
endfunction
constant function HR_Ring2_Explosion_Offset takes nothing returns real
return 250.0 //The explosion offset in the 2nd ring
endfunction
constant function HR_Ring3_Explosion_Offset takes nothing returns real
return 150.0 //The explosion offset in the 3rd ring
endfunction
constant function HR_Ring1_Explosion_Count takes nothing returns integer
return 10 //The amount of water explosions in the 1st ring
endfunction
constant function HR_Ring2_Explosion_Count takes nothing returns integer
return 8 //The amount of water explosions in the 2nd ring
endfunction
constant function HR_Ring3_Explosion_Count takes nothing returns integer
return 6 //The amount of water explosions in the 3rd ring
endfunction
constant function HR_Ability_ID takes nothing returns integer
return 'A08D' //The raw code of the ability "Hydro Rings"
endfunction
constant function HR_Dummy_Unit_ID takes nothing returns integer
return 'h01D' //The raw code of the unit "Hydro Rings Dummy"
endfunction
function HR_Filter takes nothing returns boolean
return IsUnitEnemy(GetFilterUnit(), GetOwningPlayer(GetTriggerUnit())) and GetWidgetLife(GetFilterUnit()) > 0.405
endfunction
function HR_Actions takes nothing returns nothing
local unit u = GetTriggerUnit()
local location l = GetSpellTargetLoc()
local real dmg = GetUnitAbilityLevel(u, HR_Ability_ID()) * HR_Damage_Base()
local integer i = 0
local group g = CreateGroup()
local boolexpr b = Condition(function HR_Filter)
local unit p
local location ll
loop
exitwhen i > HR_Ring1_Explosion_Count()
set ll = PolarProjectionBJ(l, HR_Ring1_Explosion_Offset(), 360.00/HR_Ring1_Explosion_Count() * i)
call CreateUnitAtLoc(GetOwningPlayer(u), HR_Dummy_Unit_ID(), ll, 0)
call GroupEnumUnitsInRangeOfLoc(g, ll, HR_AoE(), b)
loop
set p = FirstOfGroup(g)
exitwhen p == null
call GroupRemoveUnit(g, p)
call UnitDamageTarget(u, p, dmg, false, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_NORMAL, null)
endloop
call RemoveLocation(ll)
set i = i + 1
endloop
call PolledWait(HR_Ring_Interval())
set i = 0
loop
exitwhen i > HR_Ring2_Explosion_Count()
set ll = PolarProjectionBJ(l, HR_Ring2_Explosion_Offset(), 360.00/HR_Ring2_Explosion_Count() * i)
call CreateUnitAtLoc(GetOwningPlayer(u), HR_Dummy_Unit_ID(), ll, 0)
call GroupEnumUnitsInRangeOfLoc(g, ll, HR_AoE(), b)
loop
set p = FirstOfGroup(g)
exitwhen p == null
call GroupRemoveUnit(g, p)
call UnitDamageTarget(u, p, dmg, false, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_NORMAL, null)
endloop
call RemoveLocation(ll)
set i = i + 1
endloop
call PolledWait(HR_Ring_Interval())
set i = 0
loop
exitwhen i > HR_Ring3_Explosion_Count()
set ll = PolarProjectionBJ(l, HR_Ring3_Explosion_Offset(), 360.00/HR_Ring3_Explosion_Count() * i)
call CreateUnitAtLoc(GetOwningPlayer(u), HR_Dummy_Unit_ID(), ll, 0)
call GroupEnumUnitsInRangeOfLoc(g, ll, HR_AoE(), b)
loop
set p = FirstOfGroup(g)
exitwhen p == null
call GroupRemoveUnit(g, p)
call UnitDamageTarget(u, p, dmg, false, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_NORMAL, null)
endloop
call RemoveLocation(ll)
set i = i + 1
endloop
call RemoveLocation(l)
call DestroyGroup(g)
call DestroyBoolExpr(b)
set u = null
set l = null
set ll = null
set g = null
set b = null
endfunction
function HR_Cond takes nothing returns boolean
return GetSpellAbilityId() == HR_Ability_ID()
endfunction
function InitTrig_Hydro_Rings takes nothing returns nothing
set gg_trg_Hydro_Rings = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(gg_trg_Hydro_Rings, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(gg_trg_Hydro_Rings, Condition(function HR_Cond))
call TriggerAddAction(gg_trg_Hydro_Rings, function HR_Actions)
endfunction
//TESH.scrollpos=9
//TESH.alwaysfold=0
//******************************************************************************
//* *
//* Arachnian Queen Spellpack *
//* v1.01b *
//* *
//* By: scorpion182 aka ranzi aka ada_aja *
//* http://www.jade-wars.com *
//* *
//******************************************************************************
library Web initializer init requires xecollider, xecast, TimerUtils
//*************************************************************
//* Configuration Constants
globals
private constant integer SPELL_ID='A043' //web ability rawcode
private constant integer BUFF_ID='B01E' //web buff rawcode
private constant integer SLOW_BUFF='Bslo' //slow buff rawcode
private constant integer DUMMY_SPELL_ID='A051' //dummy slow rawcode
private constant string DUMMY_ORDER="slow" //dummy slow order string
private constant real RADIUS=150. //web collosion size radius
private constant real HEIGHT=100. //web height
private constant string FX="Abilities\\Weapons\\ChimaeraAcidMissile\\ChimaeraAcidMissile.mdl" //caster fx
private constant string WEB_FX="Abilities\\Spells\\Undead\\Web\\WebTarget.mdl" //web fx
private constant real SCALE=1.0 //web scale
private constant string ATT_POINT="origin" //caster fx attachment point
private constant real WEB_DURATION=10. //web duration
private constant real MIN_DIST=100. //minimum distance between webs
private constant real WEB_INTERVAL=0.35 //web create interval
endglobals
//* Configuration End
//*****************************************************************
globals
private xecast cast
endglobals
private struct data
unit caster
timer t
real x1=0
real x2=0
real y1=0
real y2=0
static method create takes unit u, timer t returns data
local data d=data.allocate()
set d.caster=u
set d.t=t
return d
endmethod
private method onDestroy takes nothing returns nothing
call ReleaseTimer(.t)
endmethod
endstruct
private function SetDummy takes xecast DummySpell returns nothing
set DummySpell.abilityid=DUMMY_SPELL_ID
set DummySpell.orderstring=DUMMY_ORDER
endfunction
private function IsSlowed takes unit u returns boolean
return GetUnitAbilityLevel(u,SLOW_BUFF)>0
endfunction
private struct web extends xecollider
unit caster
method onUnitHit takes unit target returns nothing
local xecast xc
if (this.caster != target and IsSlowed(target)==false) then
set xc=xecast.createA()
set cast.owningplayer = GetOwningPlayer(this.caster)
call cast.castOnTarget(target)
endif
endmethod
endstruct
private function Check takes nothing returns nothing
local timer t=GetExpiredTimer()
local data d=data(GetTimerData(t))
local real dx = d.x2 - d.x1
local real dy = d.y2 - d.y1
local real dis= SquareRoot(dx * dx + dy * dy)
local web xc
if (GetUnitAbilityLevel(d.caster,BUFF_ID)>0) then
set d.x1=d.x2
set d.y1=d.y2
if (dis>MIN_DIST) then
set xc=web.create(GetUnitX(d.caster),GetUnitY(d.caster),GetUnitFacing(d.caster))
set xc.scale=SCALE
set xc.collisionSize=RADIUS
set xc.expirationTime=WEB_DURATION
set xc.fxpath=WEB_FX
set xc.z=HEIGHT
set xc.caster=d.caster
endif
set d.x2=GetUnitX(d.caster)
set d.y2=GetUnitY(d.caster)
call DestroyEffect(AddSpecialEffectTarget(FX,d.caster,ATT_POINT))
else
call d.destroy()
endif
endfunction
private function Actions takes nothing returns nothing
local unit u
local timer t
local data d
if GetSpellAbilityId() == SPELL_ID then
set u=GetSpellAbilityUnit()
set t=NewTimer()
set d=data.create(u,t)
call SetTimerData(t,integer(d))
call TimerStart(t,WEB_INTERVAL,true,function Check)
endif
set u=null
endfunction
private function init takes nothing returns nothing
//init spellcast trigger
local trigger t = CreateTrigger( )
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT )
call TriggerAddAction(t, function Actions )
//init xecast
set cast = xecast.create()
call SetDummy(cast)
//preload dummy ability
call XE_PreloadAbility(DUMMY_SPELL_ID)
endfunction
endlibrary
//TESH.scrollpos=224
//TESH.alwaysfold=0
library DestructableDestroy initializer DestInit
//Since several spells use this function I placed it into a library
//Make sure you set destructables that you cannot afford to get destroyed to be invulnerable using the function DestInvu
function Dest takes nothing returns nothing
local destructable d = GetEnumDestructable()
if not IsDestructableInvulnerable(d) then
call KillDestructable(d)
endif
set d = null
endfunction
//This function makes the default pathing blockers invulnerable
//If you want a destructable that is used more than once in your map to be invulnerable
//just make another if-then statement and change the rawcode into the rawcode of that destructable
function DestInvu takes nothing returns nothing
local destructable d = GetEnumDestructable()
local integer ID = GetDestructableTypeId(d)
if ID == 'YTab' then
call SetDestructableInvulnerable(d, true)
endif
if ID == 'YTac' then
call SetDestructableInvulnerable(d, true)
endif
if ID == 'YTfb' then
call SetDestructableInvulnerable(d, true)
endif
if ID == 'YTfc' then
call SetDestructableInvulnerable(d, true)
endif
if ID == 'YTpb' then
call SetDestructableInvulnerable(d, true)
endif
if ID == 'YTpc' then
call SetDestructableInvulnerable(d, true)
endif
set d = null
endfunction
function DestInit takes nothing returns nothing
call EnumDestructablesInRect(GetPlayableMapRect(), null, function DestInvu)
endfunction
endlibrary
///////////////////////////////////////////////////
// Aqua Crush
// by: Adiktuz
///////////////////////////////////////////////////
scope AquaCrush initializer Aqua_Init
//configurables
globals
private constant integer FLY = 'Amrf' //Rawcode of Medivh's raven form
private constant integer DUMMY = 'h01N' //Rawcode of the dummy unit(horizontal)
private constant integer SPELL = 'A0F7' //Rawcode of the spell
private constant real DAMAGE = .833 //Explosion damage per level (multiply this value by 12)
private constant real DAMAGER = 4.17 //Impact damage per level (multiply this by 12)
private constant real HEIGHT = 600 //The maximum height that units will be thrown up to
private constant string WAVE = "Abilities\\Spells\\Other\\CrushingWave\\CrushingWaveMissile.mdl" //The path to the wave effect
//Note: Using WATER and SPLASH can cause a little lag on some PCs
private boolean SE = false //If true, there will be an explosion effect
private boolean SPE = false //If true, there will be a splashdown effect
private constant string WATER = "Objects\\Spawnmodels\\Naga\\NagaDeath\\NagaDeath.mdl" //The path to the explosion effect
private constant string SPLASH = "Objects\\Spawnmodels\\Naga\\NagaDeath\\NagaDeath.mdl" //The path to the splashdown effect
private constant real AOE = 100 //The base radius of the spell
private constant real AOE_B = 50 //The bonus radius of the spell per level
private constant real RANGE = 500 //The distance of the waves from the target point
private constant real SPEED = 20 //The rate at which the waves move
private attacktype ATYPE = ATTACK_TYPE_MAGIC //The attack type of the spell
private damagetype DTYPE = DAMAGE_TYPE_COLD //The damage type of the spell
private constant boolean TREE = true //If set to true, the explosion will also destroy destructibles: if you use the normal bridges make them invulnerable or the spell will also destroy them.
private constant boolean FLYING = false //If set to true, flying units will also get lifted
//End of configurables
private integer array Data
private integer array Datax
private integer Total = 0
private integer Totalx = 0
private timer t = CreateTimer()
private timer tx = CreateTimer()
private group Temp = CreateGroup()
private constant real ACCELERATION = 3 //The increase in rate at which units get thrown up/down
endglobals
private struct AC
real x
real y
real distance
real facing
unit u
real height
effect wave
player owner
unit v
real time
integer level
boolean direction = false
static method create takes real x, real y, player p, real facing, integer level, unit caster returns AC
local AC dat = AC.allocate()
set Data[Total] = dat
set dat.x = x - RANGE*Cos(facing)
set dat.y = y - RANGE*Sin(facing)
set dat.u = CreateUnit(p, DUMMY, dat.x, dat.y, facing*bj_RADTODEG)
set dat.distance = RANGE
set dat.owner = p
set dat.level = level
set dat.facing = facing
set dat.v = caster
set dat.wave = AddSpecialEffectTarget(WAVE, dat.u, "chest")
if Total == 0 then
call TimerStart(t, .03, true, function AC.move)
endif
set Total = Total + 1
set p = null
return dat
endmethod
method onDestroy takes nothing returns nothing
local rect r = Rect(this.x - AOE - AOE_B*this.level, this.y - AOE - AOE_B*this.level, this.x + AOE + AOE_B*this.level, this.y + AOE + AOE_B*this.level)
if TREE then
call EnumDestructablesInRect(r, null, function Dest)
endif
call DestroyEffect(this.wave)
set this.u = null
set this.wave = null
set this.owner = null
set this.v = null
call RemoveRect(r)
set r = null
endmethod
static method move takes nothing returns nothing
local AC dat
local integer i = 0
local unit u
local unit x
loop
exitwhen i == Total
set dat = Data[i]
set dat.x = dat.x + SPEED*Cos(dat.facing)
set dat.y = dat.y + SPEED*Sin(dat.facing)
call SetUnitPosition(dat.u, dat.x, dat.y)
set dat.distance = dat.distance - SPEED
if dat.distance == 0 then
call GroupEnumUnitsInRange(Temp, dat.x, dat.y, AOE + AOE_B*dat.level, null)
loop
set u = FirstOfGroup(Temp)
exitwhen u == null
if (IsPlayerEnemy(GetOwningPlayer(u), dat.owner) and GetWidgetLife(u) > .405) then
call AC.LiftStart(u,dat.v, dat.level)
call UnitDamageTarget(dat.u,u, dat.level*DAMAGE, false, false, ATYPE, DTYPE, WEAPON_TYPE_WHOKNOWS)
endif
call GroupRemoveUnit(Temp, u)
endloop
if SE then
call DestroyEffect(AddSpecialEffectTarget(WATER, dat.u, "origin"))
endif
call UnitApplyTimedLife(dat.u, 'BTLF', 1.00)
call dat.destroy()
set Total = Total - 1
set Data[i] = Data[Total]
set i = i - 1
endif
set i = i + 1
endloop
if Total == 0 then
call PauseTimer(t)
endif
set x = null
set u = null
endmethod
static method LiftStart takes unit u,unit x, integer level returns nothing
local AC dat = AC.allocate()
if FLYING then
set dat.x = GetUnitX(u)
set dat.y = GetUnitY(u)
call UnitAddAbility(u, FLY)
call UnitRemoveAbility(u, FLY)
call SetUnitFlyHeight(u, 10.00, 0)
set dat.v = u
set dat.u = x
set dat.level = level
set dat.time = 0
else
if IsUnitType(u, UNIT_TYPE_FLYING) == false then
set dat.x = GetUnitX(u)
set dat.y = GetUnitY(u)
call UnitAddAbility(u, FLY)
call UnitRemoveAbility(u, FLY)
call SetUnitFlyHeight(u, 10.00, 0)
set dat.v = u
set dat.u = x
set dat.level = level
set dat.time = 0
else
set dat.v = null
set dat.u = null
set dat.direction = true
endif
endif
set Datax[Totalx] = dat
if Totalx == 0 then
call TimerStart(tx, .03, true, function AC.Lift)
endif
set Totalx = Totalx + 1
set u = null
set x = null
endmethod
static method Lift takes nothing returns nothing
local AC dat
local integer i = 0
loop
exitwhen i == Totalx
set dat = Datax[i]
set dat.time = dat.time + 1
if dat.direction then
set dat.height = dat.height - ACCELERATION*dat.time
call SetUnitPosition(dat.v,dat.x,dat.y)
call SetUnitFlyHeight(dat.v,dat.height,0)
else
set dat.height = dat.height + ACCELERATION*(20.00 - dat.time)
call SetUnitPosition(dat.v,dat.x,dat.y)
call SetUnitFlyHeight(dat.v,dat.height,0)
if dat.height >= 600 then
set dat.direction = true
set dat.time = 0
endif
endif
if dat.height <= 0 then
if SPE then
call DestroyEffect(AddSpecialEffect(SPLASH, GetUnitX(dat.v), GetUnitY(dat.v)))
endif
call UnitDamageTarget(dat.u,dat.v, dat.level*DAMAGER, false, false, ATYPE, DTYPE, WEAPON_TYPE_WHOKNOWS)
set dat.u = null
set dat.v = null
call dat.destroy()
set Totalx = Totalx - 1
set Datax[i] = Datax[Totalx]
set i = i - 1
endif
set i = i + 1
endloop
if Totalx == 0 then
call PauseTimer(tx)
endif
endmethod
endstruct
private function Aqua_Check takes nothing returns boolean
return GetSpellAbilityId() == SPELL
endfunction
private function Aqua_Start takes nothing returns nothing
local integer i = 0
local unit a = GetTriggerUnit()
local real x = GetSpellTargetX()
local real y = GetSpellTargetY()
loop
exitwhen i == 12
call AC.create(x,y,GetOwningPlayer(a), i*30*.017453292, GetUnitAbilityLevel(a, SPELL), a)
set i = i + 1
endloop
set a = null
endfunction
private function Aqua_Init takes nothing returns nothing
local trigger Aqua = CreateTrigger( )
call TriggerAddAction( Aqua, function Aqua_Start )
call TriggerAddCondition(Aqua, function Aqua_Check)
call TriggerRegisterAnyUnitEventBJ(Aqua, EVENT_PLAYER_UNIT_SPELL_EFFECT)
endfunction
endscope
//TESH.scrollpos=-1
//TESH.alwaysfold=0
constant function UL_Wave_Count takes nothing returns integer
return 18 //The number of shockwaves
endfunction
constant function UL_Wave_Offset takes nothing returns real
return 35.0 //The offset of each shockwave from the caster
endfunction
constant function UL_SFX takes nothing returns string
return "Objects\\Spawnmodels\\Naga\\NagaDeath\\NagaDeath.mdl" //Created on u's "origin"
endfunction
constant function UL_Ability_ID takes nothing returns integer
return 'A08H' //The raw code of the ability "Unleash Liquid"
endfunction
constant function UL_Dummy_Ability_ID takes nothing returns integer
return 'A08E' //The raw code of the ability "Unleash Liquid Dummy Ability"
endfunction
constant function UL_Dummy_Caster_ID takes nothing returns integer
return 'h01E' //The raw code of the unit "Unleash Liquid Dummy"
endfunction
function UL_Actions takes nothing returns nothing
local unit u = GetTriggerUnit()
local location l = GetSpellTargetLoc()
local real X = GetLocationX(l)
local real Y = GetLocationY(l)
local integer lvl = GetUnitAbilityLevel(u, UL_Ability_ID())
local integer i = 0
local unit d
local location ll
call SetUnitPosition(u, X, Y)
call DestroyEffect(AddSpecialEffectTarget(UL_SFX(), u, "origin"))
loop
exitwhen i > UL_Wave_Count()
set ll = PolarProjectionBJ(l, UL_Wave_Offset(), 360.00/UL_Wave_Count() * i)
set d = CreateUnit(GetOwningPlayer(u), UL_Dummy_Caster_ID(), X, Y, 360.00/UL_Wave_Count() * i)
call SetUnitAbilityLevel(d, UL_Dummy_Ability_ID(), lvl)
call IssuePointOrderLoc(d, "carrionswarm", ll)
call RemoveLocation(ll)
set i = i + 1
endloop
call RemoveLocation(l)
set u = null
set l = null
set d = null
set ll = null
endfunction
function UL_Conditions takes nothing returns boolean
return GetSpellAbilityId() == UL_Ability_ID()
endfunction
function InitTrig_Unleash_Liquid takes nothing returns nothing
set gg_trg_Unleash_Liquid = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(gg_trg_Unleash_Liquid, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(gg_trg_Unleash_Liquid, Condition(function UL_Conditions))
call TriggerAddAction(gg_trg_Unleash_Liquid, function UL_Actions)
call Preload(UL_SFX())
endfunction
//TESH.scrollpos=0
//TESH.alwaysfold=0
//******************************************************************************
//* *
//* Arachnian Queen Spellpack *
//* v1.01b *
//* *
//* By: scorpion182 aka ranzi aka ada_aja *
//* http://www.jade-wars.com *
//* *
//******************************************************************************
library Eggshack initializer init requires xefx, TimerUtils
//*************************************************************
//* Configuration Constants
globals
private constant integer SPELL_ID='A050' //eggshack ability rawcode
private constant integer BUFF_ID='B021' //eggshack buff
private constant integer BEETLE='u00Z' //beetle unit rawcode
private constant integer DUMMY_ID='e00S' //dummy unit rawcode
private constant string FX="Doodads\\Dungeon\\Terrain\\EggSack\\EggSack1.mdl" //eggshack fx
private constant integer NUM=3 //how many beetle spawns
private constant real EGG_DISTANCE=-35.0 //egg distance from caster
private constant real EGG_HEIGHT=100. //egg height
private constant real PATROL_RADIUS=500. //beetle spawns patrol radius
private integer array B_ABILITY[3]
endglobals
//beetle spawn abilities
private function InitBSpells takes nothing returns nothing
//set B_ABILITY[0]='ACct'
set B_ABILITY[1]='ACct' //critical strike
set B_ABILITY[2]='ACbh' //bash
//...and so on
endfunction
private constant function Duration takes integer lvl returns real
return 30.+0.*lvl //beetle spawns duration
endfunction
private constant function LifeCost takes integer lvl returns real
return 5.*lvl //life cost per second
endfunction
//* Configuration End
//*****************************************************************
private struct data
unit caster
timer t
unit Egg
unit array Beetle[NUM]
boolean patrol=false
real counter=0.
real hpcost=0.
integer lvl
xefx fx
static method create takes unit c, timer t returns data
local data d=data.allocate()
local integer lvl=GetUnitAbilityLevel(c,SPELL_ID)
set d.caster=c
set d.t=t
set d.counter=Duration(lvl)
set d.hpcost=LifeCost(lvl)
set d.lvl=lvl
set d.fx=xefx.create(GetUnitX(c),GetUnitY(c),GetUnitFacing(c))
set d.fx.z=EGG_HEIGHT
set d.fx.fxpath=FX
return d
endmethod
private method onDestroy takes nothing returns nothing
call ReleaseTimer(.t)
endmethod
endstruct
//distance between X cord
private function PolarProjectionX takes real x, real distance, real angle returns real
return x+distance*Cos(angle * bj_DEGTORAD)
endfunction
//distance between Y cord
private function PolarProjectionY takes real y, real distance, real angle returns real
return y+distance*Sin(angle * bj_DEGTORAD)
endfunction
private function Check takes nothing returns nothing
local timer t=GetExpiredTimer()
local data d=data(GetTimerData(t))
local real x=PolarProjectionX(GetUnitX(d.caster),EGG_DISTANCE,GetUnitFacing(d.caster))
local real y=PolarProjectionY(GetUnitY(d.caster),EGG_DISTANCE,GetUnitFacing(d.caster))
local rect r
local integer i=0
if (GetUnitAbilityLevel(d.caster,BUFF_ID)>0) then
set d.fx.x=x
set d.fx.y=y
set d.fx.z=EGG_HEIGHT+GetUnitFlyHeight(d.caster)
call SetUnitState(d.caster,UNIT_STATE_LIFE,GetUnitState(d.caster,UNIT_STATE_LIFE)-d.hpcost*.035)
call TimerStart(t,XE_ANIMATION_PERIOD,false,function Check)
elseif (d.patrol==false) then
call d.fx.destroy()
set d.patrol=true
loop
exitwhen i>NUM-1
set d.Beetle[i]=CreateUnit(GetOwningPlayer(d.caster),BEETLE,GetUnitX(d.caster),GetUnitY(d.caster),0)
call UnitApplyTimedLife(d.Beetle[i],'BTLF',d.counter)
//--add ability--
if (d.lvl==2) then
call UnitAddAbility(d.Beetle[i],B_ABILITY[1])
elseif (d.lvl==3) then
call UnitAddAbility(d.Beetle[i],B_ABILITY[1])
call UnitAddAbility(d.Beetle[i],B_ABILITY[2])
endif
//--------------
set i=i+1
endloop
call TimerStart(t,XE_ANIMATION_PERIOD,false,function Check)
elseif (d.patrol==true and GetWidgetLife(d.caster)>0.405) then
set r = Rect(GetUnitX(d.caster)-PATROL_RADIUS, GetUnitY(d.caster)-PATROL_RADIUS, GetUnitX(d.caster)+PATROL_RADIUS, GetUnitY(d.caster)+PATROL_RADIUS)
set i=0
loop
exitwhen i>NUM-1
set x=GetRandomReal(GetRectMinX(r), GetRectMaxX(r))
set y=GetRandomReal(GetRectMinY(r), GetRectMaxY(r))
call IssuePointOrder(d.Beetle[i],"patrol",x,y)
set i=i+1
endloop
if (d.counter>0.) then
call TimerStart(t,1.0,false,function Check)
set d.counter=d.counter-1.0
else
call d.destroy()
endif
call RemoveRect(r)
elseif (GetWidgetLife(d.caster)<=0.405) then
call d.destroy()
endif
set r=null
endfunction
private function Actions takes nothing returns nothing
local timer t
local unit u
local data d
if GetSpellAbilityId() == SPELL_ID then
set t=NewTimer()
set u=GetSpellAbilityUnit()
set d=data.create(u,t)
call SetTimerData(t,integer(d))
call TimerStart(t,XE_ANIMATION_PERIOD,false,function Check)
endif
set u=null
endfunction
private function init takes nothing returns nothing
local trigger t = CreateTrigger( )
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT )
call TriggerAddAction(t, function Actions )
call InitBSpells()
endfunction
endlibrary
//TESH.scrollpos=-1
//TESH.alwaysfold=0
constant function D_Movespeed_Decrease_Base takes nothing returns real
return 4.0 //The base amount that 't's MS is decreased/interval: lvl 1 = 5MS DCRSE, lvl 2 = 6MS DCRSE...
endfunction
constant function D_Damage_Multiplier takes nothing returns real
return 5.0 //The base multiplier for the dmg of the spell: lvl 1 = 10dmg/loop, lvl 2 = 15dmg/loop...
endfunction
constant function D_Interval takes nothing returns real
return 0.5 //The delay between each damaging and MS decreasing 'session'
endfunction
constant function D_Loop_Count_Base takes nothing returns integer
return 9 //The base amount of times to damage and decrease the MS of 't': lvl 1 = 10 loops, lvl 2 = 11 loops...
endfunction
constant function D_SFX takes nothing returns string
return "Objects\\Spawnmodels\\Naga\\NagaDeath\\NagaDeath.mdl"
endfunction
constant function D_Ability_ID takes nothing returns integer
return 'A08C' //The raw code of the ability "Drown"
endfunction
function D_Actions takes nothing returns nothing
local unit u = GetTriggerUnit()
local unit t = GetSpellTargetUnit()
local real MS = GetUnitMoveSpeed(t)
local real MS_DCRSE = GetUnitAbilityLevel(u, D_Ability_ID()) + D_Movespeed_Decrease_Base()
local real dmg = GetUnitAbilityLevel(u, D_Ability_ID()) * D_Damage_Multiplier() + D_Damage_Multiplier()
local integer loops = GetUnitAbilityLevel(u, D_Ability_ID()) + D_Loop_Count_Base()
local integer i = 0
loop
exitwhen (i == loops)
if GetWidgetLife(t) > 0.405 then
call DestroyEffect(AddSpecialEffectTarget(D_SFX(), t, "chest"))
call SetUnitMoveSpeed(t, GetUnitMoveSpeed(t) - MS_DCRSE)
call UnitDamageTarget(u, t, dmg, false, false, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_NORMAL, null)
set i = i + 1
call PolledWait(D_Interval())
else
set i = loops
endif
endloop
call SetUnitMoveSpeed(t, MS)
set u = null
set t = null
endfunction
function D_Cond takes nothing returns boolean
return GetSpellAbilityId() == D_Ability_ID()
endfunction
function InitTrig_Drown takes nothing returns nothing
set gg_trg_Drown = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(gg_trg_Drown, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(gg_trg_Drown, Condition(function D_Cond))
call TriggerAddAction(gg_trg_Drown, function D_Actions)
call Preload(D_SFX())
endfunction
//TESH.scrollpos=165
//TESH.alwaysfold=0
//===========================================================================
//A spell that makes meteors fall randomly in the map, damaging enemy units
//and maybe summoning powerfull infernals
//
//Requires:
// - TimerUtils
// - Table
//
//@author:
// - Flame_Phoenix
//
//@credits:
// - Cohadar, Vexorian, Anitarf, Moyack, Themerion, Captain Griffen, tamisrah,
//Pyrogasm, chobibo, spiwn, for their help in teh code.
// - Daelin for the inspiration and the idea
// - And last, but not least, Blue_Jeans, my first teacher of vJASS
//
//
//@version 2.7.1
//===========================================================================
scope Apocalypse initializer Init
//===========================================================================
//=============================SETUP START===================================
//===========================================================================
globals
private constant integer AID = 'A0AV' //The rawcode of the Apocalypse ability
private constant integer INFERNAL = 'ANin' //The rawcode of the Infernal ability
private constant string INFERNALORDER = "dreadlordinferno" //the string order for Infernal
private constant integer SPAWN = 'ACfs' //The rawcode for FlameStrike when we want to spawn an Infernal
private constant string SPAWNORDER = "flamestrike" //the order for the FlameStrike ability when we spawn an Infernal
private constant integer NO_SPAWN = 'ANfs' //The rawcode of the FlameStrike ability when we don't want to spanw an Infernal
private constant string NO_SPAWNORDER = "flamestrike" //the order for the FlameStrike ability when we don't spawn an Infernal
private constant integer DUMMY_RAW = 'e000' //rawcode of the dummy caster
private constant real DUMMY_LIFE = 3.0 //The timed life of the dummy unit caster
endglobals
private constant function meteorCicle takes integer level returns real
return 2.0 / level //the time interval that separates each serie (a cicle)
endfunction
private constant function infernalChance takes integer level returns integer
//the chances you have to spawn an infernal each time a meteor falls
// from 0 to 100
return 30 * level + 1
endfunction
private constant function meteorSeries takes integer level returns integer
//the number of series. Each serie is separated by a cicle and each
//serie makes meteorsPerSerie meteors fall
return 25 + (5 * level)
endfunction
private constant function meteorsPerSerie takes integer level returns integer
//the number of meteors that fall per serie
local integer value = 0
if (level < 3) then
set value = 3
else
set value = 6
endif
//In this case, in levels 1 and 2, it will make fall 1 meteor per serie
//in level 3 makes 2 meteors per serie fall
return value
endfunction
private constant function minimumRange takes integer level returns real
//Minimal range. Meteors won't fall before this number.
return (level * 0) + 300.
endfunction
private constant function maximumRange takes integer level returns real
//Maximum range of spell. Meteors won't fall after this number
return (level * 0) + 900.
endfunction
//===========================================================================
//=============================SETUP END=====================================
//===========================================================================
globals
private group ApocalypseCasters = CreateGroup()
private HandleTable activeTable //your private Table's global variable
endglobals
private struct MyStruct
unit caster
integer level
timer repeator
integer serieCounter
integer serieLimit
static method create takes unit caster returns MyStruct
local MyStruct data = MyStruct.allocate()
//set variables
set data.caster = caster
set data.level = GetUnitAbilityLevel(data.caster, AID)
set data.repeator = NewTimer()
set data.serieCounter = 0
set data.serieLimit = meteorSeries(data.level)
return data
endmethod
method onDestroy takes nothing returns nothing
//this will clean all mess
//since the spell is not active anymore, we clean the Table
call activeTable.flush(.caster)
//the unit is not anymore in the active units group.
call GroupRemoveUnit(ApocalypseCasters, .caster)
call ReleaseTimer(.repeator)
endmethod
endstruct
//==========================================================================
private function onDeath takes nothing returns boolean
local MyStruct data
local unit u = GetTriggerUnit()
//this will save you unnecessary gamecache calls during units' deaths.
//It also prevents conflicts with units getting the same handle id as a ghost,
//however, since you flush when the spell ends that's most likely not an issue.
if(IsUnitInGroup(u, ApocalypseCasters)) then
set data = activeTable[u] //recover that data (the struct) from the caster
call data.destroy()
endif
set u = null
return false
endfunction
//==========================================================================
//Function made by Vexorian, it selects a random region in a disk.
//All regions have the same chance of beeing choosen
private function GetRandomPointInDisk takes real centerx, real centery, real minradius, real maxradius returns location
local real d = SquareRoot(GetRandomReal(minradius * minradius, maxradius * maxradius))
local real a = GetRandomReal(0, 2 * bj_PI)
return Location(centerx + d * Cos(a), centery + d * Sin(a))
endfunction
//==========================================================================
private function Effect takes nothing returns nothing
local MyStruct data = MyStruct(GetTimerData(GetExpiredTimer()))
local real casterX
local real casterY
local integer infernal
local real randomX
local real randomY
local unit dummy1
local unit dummy2
local location point
local integer loopCounter
local real dummyFacing = 0
if (data.serieCounter >= data.serieLimit) then
call data.destroy()
else
//Here we make meteors fall andall that stuff
set loopCounter = 0
loop
exitwhen(loopCounter >= meteorsPerSerie(data.level))
set casterX = GetUnitX(data.caster)
set casterY = GetUnitY(data.caster)
set point = GetRandomPointInDisk(casterX, casterY, minimumRange(data.level), maximumRange(data.level))
set randomX = GetLocationX(point)
set randomY = GetLocationY(point)
set infernal = GetRandomInt(0, 100)
if (infernal <= infernalChance(data.level)) then
set dummy1 = CreateUnit(GetOwningPlayer(data.caster), DUMMY_RAW, casterX, casterY, dummyFacing)
call UnitAddAbility(dummy1, INFERNAL)
call SetUnitAbilityLevel(dummy1, INFERNAL, data.level)
call IssuePointOrder(dummy1, INFERNALORDER, randomX, randomY)
call UnitApplyTimedLife(dummy1, 'BTLF', DUMMY_LIFE)
set dummy2 = CreateUnit(GetOwningPlayer(data.caster), DUMMY_RAW, casterX, casterY, dummyFacing)
call UnitAddAbility(dummy2, SPAWN)
call SetUnitAbilityLevel(dummy2, SPAWN, data.level)
call IssuePointOrder(dummy2, SPAWNORDER, randomX, randomY)
call UnitApplyTimedLife(dummy2, 'BTLF', DUMMY_LIFE)
else
set dummy1 = CreateUnit(GetOwningPlayer(data.caster), DUMMY_RAW, casterX, casterY, dummyFacing)
call UnitAddAbility(dummy1, NO_SPAWN)
call SetUnitAbilityLevel(dummy1, NO_SPAWN, data.level)
call IssuePointOrder(dummy1, NO_SPAWNORDER, randomX, randomY)
call UnitApplyTimedLife(dummy1, 'BTLF', DUMMY_LIFE)
endif
call RemoveLocation(point)
set point = null
set dummy1 = null
set dummy2 = null
set loopCounter = loopCounter + 1
endloop
set data.serieCounter = data.serieCounter + 1
endif
endfunction
//==========================================================================
private function Conditions takes nothing returns boolean
local MyStruct data
if (GetSpellAbilityId() == AID) then
set data = MyStruct.create(GetTriggerUnit())
//put the struct in the Table, we just use the caster's handle adress as
//the key which tells us where in the Table the struct is stored
set activeTable[data.caster] = data
//we attach the struct to the timer
call SetTimerData(data.repeator, integer(data))
call TimerStart(data.repeator, meteorCicle(data.level), true, function Effect)
// we add the casting unit to some sort of "pool"
call GroupAddUnit(ApocalypseCasters, data.caster)
endif
return false
endfunction
//===========================================================================
private function Init takes nothing returns nothing
local trigger ApocalypseTrigger =CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(ApocalypseTrigger, EVENT_PLAYER_UNIT_SPELL_EFFECT )
call TriggerAddCondition(ApocalypseTrigger, Condition( function Conditions ) )
set ApocalypseTrigger = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(ApocalypseTrigger, EVENT_PLAYER_UNIT_DEATH)
call TriggerAddCondition(ApocalypseTrigger, Condition(function onDeath))
//Create your spell's private table.
set activeTable = HandleTable.create()
set ApocalypseTrigger = null
endfunction
endscope
//TESH.scrollpos=-1
//TESH.alwaysfold=0
scope angelskiss initializer I
private function f takes nothing returns nothing/*boolean*/
local unit u = null
set u = CreateUnit(GetOwningPlayer(SpellEvent.CastingUnit), TAF_Archer, GetUnitX(SpellEvent.TargetUnit), GetUnitY(SpellEvent.TargetUnit), 270.00)
call UnitAddAbility(u, DARKLORDOFTHESITH_Trance)
call IssueTargetOrder(u, "sleep", SpellEvent.TargetUnit )
call UnitApplyTimedLife(u, 'BTLF', 2.0)
set u = null
//return false
endfunction
public function I takes nothing returns nothing
call RegisterSpellEffectResponse(DARKLORDOFTHESITH_JediHealingTrance, f)
//call GT_AddStartsEffectAction(function f, DARKLORDOFTHESITH_JediHealingTrance)
//call XE_PreloadAbility(DARKLORDOFTHESITH_JediHealingTrance)
call XE_PreloadAbility(DARKLORDOFTHESITH_Trance)
endfunction
endscope
//REMEMBER TO TRIGGER THE HEAL SO YOU CAN USE COUNTER ON IT FULLY
// 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
//TESH.scrollpos=-1
//TESH.alwaysfold=0
scope coloritems initializer I
define{greenitem='I005';blackitem='I009';reditem='I00B';blueitem='I00C'
pinkitem='I00E';greyitem='I00F';lightblueitem='I00G';darkgreenitem='I00H';brownitem='I00I'
tealitem='I00D';purpleitem='I00J';yellowitem='I00L';orangeitem='I00M'
private suc(x)=SetUnitColor(GetManipulatingUnit(),ConvertPlayerColor(x))}
private boolean pink(){suc(7);return false};private boolean grey(){suc(8);return false}
private boolean lightblue(){suc(9);return false};private boolean darkgreen(){suc(10);return false}
private boolean brown(){suc(11);return false};private boolean black(){suc(12);return false}
private boolean green(){suc(6);return false};private boolean orange(){suc(5);return false}
private boolean yellow(){suc(4);return false};private boolean purple(){suc(3);return false}
private boolean teal(){suc(2);return false};private boolean blue(){suc(1);return false}
private boolean red(){suc(0);return false};private nothing I(){
getitemGT(brown, brownitem);getitemGT(green, greenitem);getitemGT(pink, pinkitem)
getitemGT(red, reditem);getitemGT(lightblue, lightblueitem);getitemGT(black, blackitem)
getitemGT(grey, greyitem);getitemGT(blue, blueitem);getitemGT(purple, purpleitem)
getitemGT(darkgreen, darkgreenitem);getitemGT(orange, orangeitem);getitemGT(yellow, yellowitem)
getitemGT(teal, tealitem)};endscope
/*
// - GT_AddItemUsedAction(func, itemtypeid)
// - GT_AddItemAcquiredAction(func, itemtypeid)
// - GT_AddItemDroppedAction(func, itemtypeid)
*/
//TESH.scrollpos=-1
//TESH.alwaysfold=0
scope mercitems initializer I
define{ratitem='I00Q';albatrossitem='I00P';private cu(x)=CreateUnit(GetOwningPlayer(GetManipulatingUnit()),x,\
GetUnitX(GetManipulatingUnit()),GetUnitY(GetManipulatingUnit()),GetUnitFacing(GetManipulatingUnit()))}
private boolean rat(){cu('nrat');return false};private boolean albatross(){cu('nalb');return false}
private nothing I(){
getitemGT(rat, ratitem);getitemGT(albatross, albatrossitem)}
endscope
//TESH.scrollpos=12
//TESH.alwaysfold=0
//
// How to use the RecipeSYS
// ¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨
// ---- Adding recipes
// Adding recipes is rather easy. You just call a function
// and give it some arguments. The function you call is
// recipe.create, and is called like this:
//
// call recipe.create(i1, i2, i3, i4, i5, i6, result, sfx, attPoint, charges, chargeType)
//
// i1 - i6 are the raw codes* of the items required for the
// recipe to combine. result is the raw code of the item they
// will combine to. sfx is the path of the special effect you
// want to use (give "" for default), and attPoint the attachment
// point of it ("" for default). The boolean charges is whether
// the result should have some other amount of charges than the
// default one. If it is true, chargeType determines what amount
// of charges the result will get when combined (-1 => amount of
// charges left in the ingredient with most charges, 0 => sum of
// charges left in the ingredients, anything else => the number
// given as the argument).
//
// * You can find out the rawcodes by going to the object
// editor and pressing ctrl + D. The first 4 characters
// are the rawcode of the item. When calling the function,
// remember to put the rawcode between ' and ' in case
// you want it to work.
//
// If you want the recipe to require less than 6 items, just give
// zeros as ingredient arguments.
//
// --- AddRecipe Wrappers
// You can also add recipes using some of the wrapper
// functions: AddRecipe, AddRecipeEx, AddRecipeWithCharges,
// and AddRecipeWithChargesEx.
//
// AddRecipe takes i1, i2, i3, i4, i5, i6, result
// AddRecipeEx takes i1, i2, i3, i4, i5, i6, result, sfx, attPoint
// AddRecipeWithCharges takes i1, i2, i3, i4, i5, i6, result, chargeType
// AddRecipeWithChargesEx takes i1, i2, i3, i4, i5, i6, result, chargeType, sfx, attPoint
//
// ---- Removing Recipes
// There are two ways of removing recipes: destroyin a specific
// recipe and removing recipes by the result (removes all recipes
// with that item as result).
// Removing a specific recipe is done by calling the .destroy method
// for that recipe, and removing by result is done by using the
// RemoveRecipeByResult function, eg. like this:
//
// call RemoveRecipeByResult('belv')
//
// ---- Disassembling Items
// Disassembling an item that is a result of some recipe would cause
// the item to be replaced with the ingredients of the recipe.
// Example of usage:
//
// call DisassembleItem(GetManipulatedItem(), GetTriggerUnit())
//
// The first argument is the item to be disassembled and the second
// one is the unit to give the ingredients to. In case you don't want
// to give the items to an unit, give null as the unit argument.
// You also have the DisassembleItemByRecipe function, which takes
// one argument more: the recipe whose ingredients you want the item
// to be disassembled to.
//
// There are a few restrictions with the disassembling:
// - If the item being disassemled isn't created by the system,
// and used on an item that is the result of several recipes
// the system will just give the items that are required for one
// of the recipes.
// - When used on an recipe added via the charged recipe thingy
// the ingredient items won't recieve the amount of items they
// had when the result was made, but the default amount of charges.
//
// Note: If you are using the function with the 'A unit Loses an item'
// event you'll need to add a little wait before the function
// call in case you want the items to be created at the position
// where the item was dropped. And the wait is also needed when
// the item being disassembled is the result of a recipe that has
// only the same type of items as ingredients (or at least I think so).
//
// ---- Disabling and Re-Enabling Recipes
// Instead of removing a recipe to disable it and then adding the
// recipe again you can also disable and enable the recipe.
// This is done by setting the enabled member of the recipe to false
// (or to true if enabling). You can also use the EnableRecipeByResult
// function:
//
// call EnableRecipeByResult('fgdg', false)
//
// That would disable the recipes that have the item 'fgdg' as result.
// The enabling of the recipe would happen with the same function, but
// with true as the boolean argument.
//
// ---- Disabling/Re-enabling the System
// If you need to disable/enable the system for some time, you can use these lines:
//
// call DisableTrigger(RecipeSYS_TRIGGER)
// call EnableTrigger(RecipeSYS_TRIGGER)
//
// Note, that this only disables the combining of items by the system itself,
// if you're using a combining trigger of your own, you'll need to disable that
// trigger.
//
// ---- Manual Checking For Recipes
// In case you'd want to use some other event than the 'A unit Acquires an
// item', you can set AUTO_COMBINE to false, and create a trigger that has
// the event you want (eg. 'A unit Starts the effect of an ability'), and
// then call the CheckForRecipes function from there. The arguments it takes
// are the item that should be checked if it belongs to a recipe and the unit
// the result should be created to (if any is created).
// The function returns a boolean depending on whether it combined the item
// to something or not. And if it combined something, you can refer to the
// created item with bj_lastCreatedItem (Last created item in GUI).
//
// Strengths of RecipeSYS
// ¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨
// - Supports having several items of the same type as
// ingredients in the same recipe.
// - Supports having the same item type as an ingredient in
// several recipes.
// - Allows to specify the special effect and it's attachment
// point (each recipe can have its own ones, if you wish so).
// - All you need to do is add the recipes.
// - You can remove recipes.
// - You can add recipes with results that have charges.
// - The amount of charges can be predefined, the sum of charges
// left in the ingredients, or the amount of charges in the
// ingredient with most charges.
// - You can disable and enable single recipes or the whole sytem.
// - You can disassemble items.
// - You can disable the automatic combining of items, and create
// a trigger that combines items manually (so you can have any
// event you want).
//
// Weaknesses of RecipeSYS
// ¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨
// - Don't know if it's optimized as much as it could nor if it's
// the best recipe system out there. ^_^
// - The item disassembling has some restrictions, and doesn't work
// flawlessy with the 'A unit Loses an item' event (see the Test2
// trigger in the demo map or the instructions of the function for
// additional information).
// - The max amount of item types you can use in recipes as
// ingredients is 8190 / AMOUNT_ING and as results 8190 / AMOUNT_RES.
//
// How to import RecipeSYS
// ¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨
// Two easy steps:
// - Copy this trigger to your map.
// - Copy the trigger 'Table' to your map.
// Alternatively:
// - Create a trigger named 'RecipeSYS' in your map.
// - Convert it to Jass (Custom Script) and replace all
// of the code in there with the code in this trigger.
// - Create a trigger named 'Table' in your map.
// - Convert it to Jass (Custom Script) and replace all
// of the code in there with the code in the trigger 'Table'.
//TESH.scrollpos=0
//TESH.alwaysfold=0
// ¤ v0.6d ¤
// ( *'*-._.-*'*-._.-*'*-._.-*'* )
// ) RecipeSYS (
// ( Created by Artificial )
// )*'*-._.-*'*-._.-*'*-._.-*'*(
// ¤ ¤
scope recipesTAF initializer I
globals
integer Ankh='rlif'
integer Wand='rde1'
integer StarWand='rde2'
integer HammerStr='ratc'
integer BladeAgi='rat9'
integer StaffInt='rat6'
integer PowerPendant='mcou'
integer DivineGem='I004'
integer DivineHammer='I002'
integer DoomStone='I00T'
integer DoomStickRecipe='I00S'
integer DoomStick='I003'
integer SpectreShield='I00R'
integer BeastHeart='I00U'
integer GoldItem='I00V'
endglobals
//===========================================================================
private function I takes nothing returns nothing
call AddRecipe(Ankh,Wand,0,0,0,0,StarWand)
//the good idea? make monsters drop items not sold for muchg old, but worth good money
//because save you money on recipes since you went and got the item yourself
call AddRecipe(StaffInt,BladeAgi,HammerStr,DoomStone,0,0,DoomStick)
call AddRecipe(StaffInt,BladeAgi,HammerStr,DoomStickRecipe,0,0,DoomStick)
call AddRecipe(HammerStr,PowerPendant,DivineGem,0,0,0,DivineHammer)
endfunction
endscope
//! runtextmacro DisplayToRecipe("'I004'","'I003'","divinehammer")
//TESH.scrollpos=122
//TESH.alwaysfold=0
//compilation of itemtriggers composed by sankaku
//dubbed item management
library WorldBounds
//struct WorldBounds extends array
//static readonly rect world
// same as GetWorldBounds()
//static readonly region worldRegion
// contains world for triggers
//static readonly real maxX
//static readonly real maxY
//static readonly real minX
//static readonly real minY
//static readonly real centerX
//static readonly real centerY
private module WorldBoundInit
private static method onInit takes nothing returns nothing
set world = GetWorldBounds()
set maxX = GetRectMaxX(world)
set maxY = GetRectMaxY(world)
set minX = GetRectMinX(world)
set minY = GetRectMinY(world)
set centerX = (maxX+minX)/2
set centerY = (minY+maxY)/2
set worldRegion = CreateRegion()
call RegionAddRect(worldRegion, world)
endmethod
endmodule
struct WorldBounds extends array
readonly static real maxX
readonly static real maxY
readonly static real minX
readonly static real minY
readonly static real centerX
readonly static real centerY
readonly static rect world
readonly static region worldRegion
implement WorldBoundInit
endstruct
endlibrary
library ClearItems requires optional WorldBounds
/*
This library fixes the leak and the lag caused by unremoved items,
including powerups and manually-destroyed items.
The dead items are periodically removed. You can adjust the period by changing
the constant CLEANING_PERIOD. Note that items' death animations need some time
to play so adjust the DEATH_TIME delay accordingly.
If you don't know exactly what you are doing, you shouldn't change the life
of a dead item; the items are no longer usable after their death but
you can still change their life. If you set their life to more than 0.405,
they won't be properly cleaned. You should also remove items manually
if you kill them when they are carried by a unit.
*/
globals
// Interval between item-cleanups.
private constant real CLEANING_PERIOD = 95
// Time for the item's death animation, optimized for tomes and runes.
private constant real DEATH_TIME = 1.5
endglobals
globals
private keyword S
private integer N = 0
private code s_code
private boolexpr s_bool
private timer s_timer = CreateTimer()
private item array I
endglobals
private function DeleteItems takes nothing returns nothing
loop
set N = N - 1
call SetWidgetLife(I[N], 1)
call RemoveItem(I[N])
set I[N] = null
exitwhen (N == 0)
endloop
call TimerStart(s_timer, CLEANING_PERIOD - DEATH_TIME, true, s_code)
endfunction
private function CleanItems takes nothing returns boolean
if (GetWidgetLife(GetFilterItem()) < 0.405) then
set I[N] = GetFilterItem()
set N = N + 1
endif
return false
endfunction
private function SweepItems takes nothing returns nothing
//static if (LIBRARY_WorldBounds) then
call EnumItemsInRect(WorldBounds.world, s_bool, null)
//else
// call EnumItemsInRect(S.world, s_bool, null)
//endif
if (N > 0) then
call TimerStart(s_timer, DEATH_TIME, false, function DeleteItems)
endif
endfunction
private struct S extends array
// static if (not LIBRARY_WorldBounds) then
// static rect world
//endif
static method onInit takes nothing returns nothing
//static if (not LIBRARY_WorldBounds) then
// set world = GetWorldBounds()
//endif
set s_code = function SweepItems
set s_bool = Filter(function CleanItems)
call TimerStart(s_timer, CLEANING_PERIOD, true, s_code)
endmethod
endstruct
endlibrary
/*scope powerupSENTINEL initializer I
private function F takes nothing returns nothing
if GetWidgetLife(GetManipulatedItem()) == 0 then
call RemoveItem(GetManipulatedItem())
endif
endfunction
private function I takes nothing returns nothing
local trigger T = CreateTrigger( )
call TriggerRegisterAnyUnitEventBJ( T, EVENT_PLAYER_UNIT_PICKUP_ITEM )
call TriggerAddAction( T, function F)
endfunction
endscope*/
scope mergeitems initializer I
private function f takes nothing returns boolean
call AddItemCharges(GetManipulatingUnit(),GetManipulatedItem())
return false
endfunction
private function I takes nothing returns nothing
local trigger t = CreateTrigger( )
STM_PUE(t,EVENT_PLAYER_UNIT_PICKUP_ITEM)
call TriggerAddCondition( t, Condition(function f) )
endfunction
endscope
//=====================================================================================
//HOW TO USE:
//=====================================================================================
//
//COPY ALL THE TEXT FROM THIS TRIGGER AND REPLACE THE TEXT FOR A TRIGGER NAMED
//StackItems WITH IT
//
//DO THE SAME FOR THE TRIGGER CALLED GT, YOU CAN GET IT AT thehelper.net
//
//=====================================================================================
//MAKE YOUR OWN TRIGGER, CALL IT WHATEVER YOU LIKE...AND MAKE YOUR OWN TEXTMACRO SCOPES
//=====================================================================================
//
//FOLLOW THE INSTRUCTIONS THROUGHOUT THE CODE AND READ THE EXAMPLE TEXTMACRO SCOPES
//IT'S REALLY JUST A SUPER FAST WAY TO CODE LOTS OF ITEMS FOR STACKING...
//
//=====================================================================================
//CREDITS
//=====================================================================================
//RDZ's Incredible Item Stacking System of Doom - by Rao Dao Zao
//=====================================================================================
//GTrigger Event System - by Jesus4Lyf
//=====================================================================================
//Itemdex - Author Unknown
//=====================================================================================
library StackItems uses GT//and Itemdex
//no need to edit the first two functions unless your map has special item needs
function GetIndexItemCopy takes unit u, integer i, integer in returns integer
local integer index
local item indexItem
set index = 0
loop
set indexItem = UnitItemInSlot(u, index)
if (indexItem != null) and (GetItemTypeId(indexItem) == i) and (GetItemId(indexItem)!=in) then
return index
else
set index=index+1
endif
exitwhen index>5
endloop
return 6
endfunction
function GetInventoryHero takes unit u, integer i returns integer
local integer index
local item indexItem
set index = 0
loop
set indexItem = UnitItemInSlot(u, index)
if (indexItem != null) and (GetItemTypeId(indexItem) == i) then
return index
else
set index=index+1
endif
exitwhen index>5
endloop
return 6
endfunction
function GetItemHero takes unit whichUnit, integer itemId returns item
local integer index = GetInventoryHero(whichUnit, itemId)
if (index == 6) then
return null
else
return UnitItemInSlot(whichUnit, index)
endif
endfunction
function AddItemCharges takes unit x, item y returns boolean
local integer w = GetIndexItemCopy(x, GetItemTypeId(y),GetItemId(y))
local item z = null
if GetItemType(y) !=ConvertItemType(1) then
return false
endif
if w !=6 then
set z=UnitItemInSlot(x,w)
call SetItemCharges(y,GetItemCharges(z)+GetItemCharges(y))
call RemoveItem(z)
set z=null
return false
else
return false
endif
endfunction
//the following is for if you want to add the AUTOMATIC stacking.
//this is useful for if you have units that expect to have full inventories already.
//! textmacro ammostacking takes itemA, itemB, count, type
scope ammo$type$ initializer I
private function f takes nothing returns boolean
local unit mu = GetManipulatingUnit()
local integer invi = GetInventoryHero(mu, $itemB$)
local item itm = null
if invi !=6 then
set itm=GetItemHero(mu,$itemB$)
call SetItemCharges(itm,GetItemCharges(itm)+$count$)
set mu=null
return false
else
call UnitAddItemById(mu,$itemB$)
set mu=null
return false
endif
set mu=null
return false
endfunction
private function I takes nothing returns nothing
call GT_AddItemAcquiredAction(function f, $itemA$)
endfunction
endscope
//! endtextmacro
//this textmacro uses GTrigger by Jesus4Lyf, download it at thehelper.net
//! textmacro itemstacking takes itemA, itemB, type
scope stack$type$ initializer I
private function f takes nothing returns boolean
local unit mu = GetManipulatingUnit()
local integer invi = GetInventoryHero(mu, $itemB$)
local item itm = null
if invi !=6 then
set itm=GetItemHero(mu,$itemB$)
call SetItemCharges(itm,GetItemCharges(itm)+1)
set mu=null
return false
else
call UnitAddItemById(mu,$itemB$)
set mu=null
return false
endif
endfunction
private function I takes nothing returns nothing
call GT_AddItemAcquiredAction(function f, $itemA$)
endfunction
endscope
//! endtextmacro
//remember to make your own textmacro scopes in another trigger, or if you prefer, at the
//bottom of this trigger
//for example:
//! textmacro DisplayToRecipe takes itemA, itemB, type
scope display$type$ initializer I
private function f takes nothing returns boolean
call UnitAddItemById(GetManipulatingUnit(),$itemB$)
return false
endfunction
private function I takes nothing returns nothing
call GT_AddItemAcquiredAction(function f, $itemA$)
endfunction
endscope
//! endtextmacro
/*the following is a textmacro that exchanges one item for another, think of it like
in dota when a hero is ranged or melee, or you have a bear or syllabear item versions...
well it is kinda like that. you can make additional textmacro scopes for different unit
types easily by replacing the UNIT_TYPE_HERO with something else, like in the dota
example of melee or ranged... UNIT_TYPE_MELEE_ATTACKER, in this case, we are checking
to make sure that the heroes get the itemA, but if a different unit picks up the itemA,
that unit gets itemB instead. you can of course name your textmacro whatever you want,
we can of course, make the next one be called itemswitchhero and make a second one
called itemswitchmelee*/
//! textmacro itemswitching takes itemA, itemB, type
scope switch$type$ initializer I
private function f takes nothing returns boolean
local unit mu = GetManipulatingUnit()
if IsUnitType(mu, UNIT_TYPE_HERO) then
set mu=null
return false
else
call UnitRemoveItem(mu,GetManipulatedItem())
call UnitAddItemById(mu,$itemB$)
set mu=null
return false
endif
endfunction
private function I takes nothing returns nothing
call GT_AddItemAcquiredAction(function f, $itemA$)
endfunction
endscope
//! endtextmacro
endlibrary
library Itemdex initializer init
//**************************************************************
//* Itemdex (Multi-version)
//* -------
//* I need this for many things, something that indexes items
//* Problems of item indexing:
//* a) Only way to detect item death is with a dynamic trigger
//* b) No known way to detect an item removal event. Besides
//* expecting the user to tell you.
//* c) Runes leave some kind of ghost item with 0.0 life that
//* still got all the data of the item , including its type.
//*
//* ...So, I use a loop to do index garbage collection,
//* see you in hell. Luckily it is even rarer to think of 8190
//* items than to think of 8190 units so it should work ok...
//*
//* ItemUserData Version//comments added for table version creation
//*
//***************************************************************
globals
private constant real RECYCLE_INTERVAL = 45.0//2.5
private constant integer RECYCLE_COUNT= 72//10
// Smaller RECYCLE_INTERVAL / Bigger RECYCLE_COUNT equal faster
// detection of removed items and worse performance.
//
// "Every RECYCLE_INTERVAL seconds RECYCLE_COUNT indexes are
// reviewed looking for removed/dead ones to recycle"
endglobals
private struct index
item attachedto
endstruct
globals
private index array activeIndexes
private integer indexN = 0
private integer DataInteger
endglobals
function GetItemId takes item it returns integer
local index d
//sankaku adds the if then to avoid indexing blank inventory slots...
if it==null then
return 0
endif
//iftablethenset d = index( HandleTable(DataInteger)[it] )
set d = index( GetItemUserData(it) )
if(d==0) then
set d = index.create()
set d.attachedto = it
//iftablethenset HandleTable(DataInteger)[it] = integer(d)
call SetItemUserData(it, integer(d) )
set activeIndexes[indexN] = d
set indexN = indexN + 1
endif
return integer(d)
endfunction
// Requirements to use ItemdexModule:
// : create takes no arguments
// : no custom .destroy method
//
module Itemdex
private static thistype array V
private item attachedto = null
method operator thatItem takes nothing returns item
return this.attachedto
endmethod
private static method get takes nothing returns nothing
//remind me to add .name to structs
endmethod
static method operator [] takes item it returns thistype
local integer d = GetItemId(it)
if( (.V[d]!=0) and (.V[d].attachedto != it) ) then
call .V[d].destroy()
set .V[d] = 0
endif
if ( .V[d] == 0) then
set .V[d] = thistype.create()
set .V[d].attachedto = it
endif
return .V[d]
endmethod
method destroy takes nothing returns nothing
local integer d
if ( this.attachedto != null) then
set d = GetItemId( this.attachedto)
set .V[d] = 0
call deallocate()
set this.attachedto = null
else
call deallocate()
endif
endmethod
method release takes nothing returns nothing
call destroy()
endmethod
endmodule
globals
private integer lastindex = 0
endglobals
private function recycleLoop takes nothing returns nothing
local integer start
local integer i
local integer j
if(indexN>0) then
//set start = lastindex % indexN
set start = lastindex - indexN * (lastindex/indexN)
set i=start
set j=0
loop
exitwhen (j>=RECYCLE_COUNT)
if (GetItemTypeId(activeIndexes[i].attachedto) == 0) or (GetWidgetLife(activeIndexes[i].attachedto) == 0.0) then
set activeIndexes[i].attachedto = null
call activeIndexes[i].destroy()
set indexN = indexN-1
set activeIndexes[i] = activeIndexes[ indexN ]
endif
set i = i +1
exitwhen (indexN==0)
if(i>=indexN) then
set i=0
endif
if(start>=indexN) then
set start = 0
endif
exitwhen(i==start)
set j=j+1
endloop
set lastindex = i
endif
endfunction
private function init takes nothing returns nothing
local timer T= CreateTimer()
//iftablethenset DataInteger = integer(HandleTable.create())//noexclusion
call TimerStart(T, RECYCLE_INTERVAL, true, function recycleLoop)
set T=null
endfunction
endlibrary
scope itemdrops initializer I
globals
private constant integer CrystalGolem='n059'
private itempool sorceresspool
endglobals
private function golem takes nothing returns boolean
call CreateItem('ankh',GetUnitX(GetTriggerUnit()),GetUnitY(GetTriggerUnit()))
return false
endfunction
private function I takes nothing returns nothing
call GT_AddUnitDiesAction(function golem, CrystalGolem)
endfunction
endscope
//TESH.scrollpos=0
//TESH.alwaysfold=0
function GetIssuedOrderItem takes nothing returns item
if not (GetIssuedOrderId() >= 852008) and (GetIssuedOrderId() <= 852013) then
return null
endif
return(UnitItemInSlot(GetTriggerUnit(), (GetIssuedOrderId()-852008)))
endfunction
/*
852002-852007 are for dragging and dropping items in your inventory (moveslot)
, 852008-852013 are orders that force units to use items in specified slots.
Read more here. This function is for GetIssuedOrderItem(), so it will run from ..08-..13 for its orders.
It would also be cute if you had a GetIssuedDragItem() too.
*/
function I2Roman takes integer n returns string
local string r=""
if n>3999 or n < 1 then
return I2S(n)
endif
loop
exitwhen n<1000
set r=r+"M"
set n=n-1000
endloop
loop
exitwhen n < 900
set r=r+"CM"
set n=n-900
endloop
loop
exitwhen n<500
set r=r+"D"
set n=n-500
endloop
loop
exitwhen n < 400
set r=r+"CD"
set n=n-400
endloop
loop
exitwhen n<100
set r=r+"C"
set n=n-100
endloop
loop
exitwhen n < 90
set r=r+"XC"
set n=n-90
endloop
loop
exitwhen n<50
set r=r+"L"
set n=n-50
endloop
loop
exitwhen n < 40
set r=r+"XL"
set n=n-40
endloop
loop
exitwhen n<10
set r=r+"X"
set n=n-10
endloop
loop
exitwhen n < 9
set r=r+"IX"
set n=n-9
endloop
loop
exitwhen n<5
set r=r+"V"
set n=n-5
endloop
loop
exitwhen n < 4
set r=r+"IV"
set n=n-4
endloop
loop
exitwhen n<1
set r=r+"I"
set n=n-1
endloop
return r
endfunction
library GroupAddUnitsInTriangle initializer init
//**************************************************************************************************
//*
//* GrupAddUnitsInTriange
//*
//* 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 rect limit
endglobals
private function makeLimit takes real x1, real y1, real x2, real y2, real x3, real y3 returns nothing
local real maxx
local real minx
if (x1>x2) then
if (x2>x3) then //x1>x2>x3
set maxx=x1
set minx=x3
elseif (x3>x1) then //x3>x1>x2
set maxx=x3
set minx=x2
else //x3>x1>x2
set maxx=x1
set minx=x2
endif
elseif (x1>x3) then //x2>x1>x3
set maxx=x2
set minx=x3
elseif (x3>x2) then //x3>x2>x1
set maxx=x3
set minx=x1
else //x2>x3>x1
set maxx=x2
set minx=x1
endif
if (y1>y2) then
if (y2>y3) then //y1>y2>y3
call SetRect(limit,minx,y3,maxx,y1)
elseif (y3>y1) then //y3>y1>y2
call SetRect(limit,minx,y2,maxx,y3)
else //y3>y1>y2
call SetRect(limit,minx,y2,maxx,y1)
endif
elseif (y1>y3) then //y2>y1>y3
call SetRect(limit,minx,y3,maxx,y2)
elseif (y3>y2) then //y3>y2>y1
call SetRect(limit,minx,y1,maxx,y3)
else
//y2>y3>y1
call SetRect(limit,minx,y1,maxx,y2)
endif
endfunction
globals
private group enumgroup
private boolexpr checkFunc
private group output
private constant real PI2=6.28318
private real X1
private real X2
private real X3
private real Y1
private real Y2
private real Y3
private real EPS=0.000000001
endglobals
private function enum takes nothing returns boolean
local unit u=GetFilterUnit()
local real x=GetUnitX(u)
local real y=GetUnitY(u)
local real r1=x*Y2-X2*y + X2*Y3-X3*Y2 + X3*y-x*Y3
local real r2=X1*y-x*Y1 + x*Y3-X3*y + X3*Y1-X1*Y3
local real r3=X1*Y2-X2*Y1 + X2*y-x*Y2 + x*Y1-X1*y
if((r1<=-EPS) and (r2<=-EPS) and (r3<=-EPS)) or ((r1>=EPS) and (r2>=EPS) and (r3>=EPS)) then
call GroupAddUnit( output, u)
endif
set u=null
return false
endfunction
function GroupAddUnitsInTriangle takes group whichGroup, real x1, real y1, real x2, real y2, real x3, real y3 returns nothing
set output=whichGroup
set X3=x3
set Y3=y3
set X1=x1
set Y1=y1
set X2=x2
set Y2=y2
call makeLimit(x1,y1,x2,y2,x3,y3)
call GroupEnumUnitsInRect(enumgroup, limit, checkFunc)
endfunction
// NOT inline friendly, (not yet), I guess that's what you get for using locations...
function GroupAddUnitsInTriangleLoc takes group whichGroup, location loc1, location loc2, location loc3 returns nothing
call GroupAddUnitsInTriangle(whichGroup, GetLocationX(loc1),GetLocationY(loc1), GetLocationX(loc2),GetLocationY(loc2), GetLocationX(loc3),GetLocationY(loc3) )
endfunction
private function init takes nothing returns nothing
set limit=Rect(0,0,0,0)
set checkFunc=Condition(function enum)
set enumgroup=CreateGroup()
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library mbSTM initializer Init requires jBoardLib
globals
MyBoard jBoard_STM
jBoard jB
endglobals
//! runtextmacro jBoardCreate("MyBoard","1","2")
private function Actions takes nothing returns nothing
set jBoard_STM = MyBoard.create(".")
call jBoard_STM.col(0,0).hideIconR().field(0,0).hideValue().showIcon().setIconPathR(iconTIME)
call jBoard_STM.field(0,1).hideIcon().setPrefix("0").setSuffix("0").setValueR(":")
endfunction
public function Init takes nothing returns nothing
set gg_trg_mbSTM =CreateTrigger()
call TriggerAddAction(gg_trg_mbSTM,function Actions)
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
//TESH.scrollpos=0
//TESH.alwaysfold=0
//==============================================================================
// TT -- TIMER TICKER SYSTEM BY COHADAR -- v3.4
//==============================================================================
//
// PURPOUSE OF TT:
// * Passing data to timers
// * using only one timer for all actions
//
// PROS:
// * It is ridiculously easy to use.
// * It is faster than ABC (and any other attaching system)
//
// CONS:
// * It can be used only for high-frequency timers
// This method fails if PERIOD > 0.1 second
//
// FUNCTIONS:
// * TT_Start(userFunc, struct)
// TT_GetData() -> struct
//
// * userFunc is a user function that takes nothing and return boolean
// it will be periodically called by the system until it returns true.
//
// * TT_GetData() is a function that can be used inside userFunc
// TT_GetData() will return struct passed to Start function
//
// DETAILS:
// * All user functions are stored in an array.
// Timer will call all those functions each period.
//
// * While user function returns false timer will continue to call it each period
// Once user function returns true it will be removed from system
//
// REQUIREMENTS:
// * NewGen v4c and above (there might be some problems with older NewGen's)
//
// THANKS TO:
// * Vexorian - he was nagging me so much about how attaching to timers is bad
// that I finally had to do something about it.
//
// HOW TO IMPORT:
// * Just create a trigger named TT
// * convert it to text and replace the whole trigger text with this one
//
//==============================================================================
library TT initializer Init
globals
// List of recommended periods:
// 0.04 = 25 calls per second
// 0.03125 = 32 calls per second
// 0.025 = 40 calls per second
// 0.02 = 50 calls per second
public constant real PERIOD = 0.03125
// One Timer to rule them all, One Timer to find them,
// One Timer to call them all and in the jass bind them
// In the land of warcraft where the desyncs lie.
private timer Timer = CreateTimer()
private integer Data
private integer Counter = 0
private trigger array Triggz
private integer array Dataz
endglobals
//==============================================================================
private function Handler takes nothing returns nothing
local trigger swap
local integer i = Counter
loop
exitwhen i<=0
set Data = Dataz[i]
if TriggerEvaluate(Triggz[i]) then
set swap = Triggz[i]
call TriggerClearConditions(swap)
set Triggz[i] = Triggz[Counter]
set Triggz[Counter] = swap
set Dataz[i] = Dataz[Counter]
set Counter = Counter - 1
endif
set i = i - 1
endloop
// who can guess why am I not nulling swap here?
endfunction
//==============================================================================
public function Start takes code userFunc, integer data returns nothing
debug if userFunc == null then
debug call BJDebugMsg("ERROR: TT_Start - null userFunc")
debug return
debug endif
set Counter = Counter + 1
if Triggz[Counter] == null then
set Triggz[Counter] = CreateTrigger()
endif
call TriggerAddCondition(Triggz[Counter], Condition(userFunc))
set Dataz[Counter] = data
endfunction
//==============================================================================
// Call this function only inside the userFunc you passed to Start
//==============================================================================
public function GetData takes nothing returns integer
return Data
endfunction
//==============================================================================
private function Init takes nothing returns nothing
call TimerStart(Timer, PERIOD, true, function Handler)
endfunction
endlibrary
//==============================================================================
// END OF TIMER TICKER SYSTEM
//==============================================================================
library GeometryFuncs
function DistanceBetweenPointsXY takes real x1, real y1, real x2, real y2 returns real
return SquareRoot((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2))
endfunction
function AngleBetweenPointsXY takes real x1, real y1, real x2, real y2 returns real
return 57.29582 * Atan2(y2 - y1, x2 - x1)
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library RecipeSYS initializer Init uses Table
// ¤ v0.6d ¤
// ( *'*-._.-*'*-._.-*'*-._.-*'* )
// ) RecipeSYS (
// ( Created by Artificial )
// )*'*-._.-*'*-._.-*'*-._.-*'*(
// ¤ ¤
//
// How to use the RecipeSYS
// ¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨
// ---- Adding recipes
// Adding recipes is rather easy. You just call a function
// and give it some arguments. The function you call is
// recipe.create, and is called like this:
//
// call recipe.create(i1, i2, i3, i4, i5, i6, result, sfx, attPoint, charges, chargeType)
//
// i1 - i6 are the raw codes* of the items required for the
// recipe to combine. result is the raw code of the item they
// will combine to. sfx is the path of the special effect you
// want to use (give "" for default), and attPoint the attachment
// point of it ("" for default). The boolean charges is whether
// the result should have some other amount of charges than the
// default one. If it is true, chargeType determines what amount
// of charges the result will get when combined (-1 => amount of
// charges left in the ingredient with most charges, 0 => sum of
// charges left in the ingredients, anything else => the number
// given as the argument).
//
// * You can find out the rawcodes by going to the object
// editor and pressing ctrl + D. The first 4 characters
// are the rawcode of the item. When calling the function,
// remember to put the rawcode between ' and ' in case
// you want it to work.
//
// If you want the recipe to require less than 6 items, just give
// zeros as ingredient arguments.
//
// --- AddRecipe Wrappers
// You can also add recipes using some of the wrapper
// functions: AddRecipe, AddRecipeEx, AddRecipeWithCharges,
// and AddRecipeWithChargesEx.
//
// AddRecipe takes i1, i2, i3, i4, i5, i6, result
// AddRecipeEx takes i1, i2, i3, i4, i5, i6, result, sfx, attPoint
// AddRecipeWithCharges takes i1, i2, i3, i4, i5, i6, result, chargeType
// AddRecipeWithChargesEx takes i1, i2, i3, i4, i5, i6, result, chargeType, sfx, attPoint
//
// ---- Removing Recipes
// There are two ways of removing recipes: destroyin a specific
// recipe and removing recipes by the result (removes all recipes
// with that item as result).
// Removing a specific recipe is done by calling the .destroy method
// for that recipe, and removing by result is done by using the
// RemoveRecipeByResult function, eg. like this:
//
// call RemoveRecipeByResult('belv')
//
// ---- Disassembling Items
// Disassembling an item that is a result of some recipe would cause
// the item to be replaced with the ingredients of the recipe.
// Example of usage:
//
// call DisassembleItem(GetManipulatedItem(), GetTriggerUnit())
//
// The first argument is the item to be disassembled and the second
// one is the unit to give the ingredients to. In case you don't want
// to give the items to an unit, give null as the unit argument.
// You also have the DisassembleItemByRecipe function, which takes
// one argument more: the recipe whose ingredients you want the item
// to be disassembled to.
//
// There are a few restrictions with the disassembling:
// - If the item being disassemled isn't created by the system,
// and used on an item that is the result of several recipes
// the system will just give the items that are required for one
// of the recipes.
// - When used on an recipe added via the charged recipe thingy
// the ingredient items won't recieve the amount of items they
// had when the result was made, but the default amount of charges.
//
// Note: If you are using the function with the 'A unit Loses an item'
// event you'll need to add a little wait before the function
// call in case you want the items to be created at the position
// where the item was dropped. And the wait is also needed when
// the item being disassembled is the result of a recipe that has
// only the same type of items as ingredients (or at least I think so).
//
// ---- Disabling and Re-Enabling Recipes
// Instead of removing a recipe to disable it and then adding the
// recipe again you can also disable and enable the recipe.
// This is done by setting the enabled member of the recipe to false
// (or to true if enabling). You can also use the EnableRecipeByResult
// function:
//
// call EnableRecipeByResult('fgdg', false)
//
// That would disable the recipes that have the item 'fgdg' as result.
// The enabling of the recipe would happen with the same function, but
// with true as the boolean argument.
//
// ---- Disabling/Re-enabling the System
// If you need to disable/enable the system for some time, you can use these lines:
//
// call DisableTrigger(RecipeSYS_TRIGGER)
// call EnableTrigger(RecipeSYS_TRIGGER)
//
// Note, that this only disables the combining of items by the system itself,
// if you're using a combining trigger of your own, you'll need to disable that
// trigger.
//
// ---- Manual Checking For Recipes
// In case you'd want to use some other event than the 'A unit Acquires an
// item', you can set AUTO_COMBINE to false, and create a trigger that has
// the event you want (eg. 'A unit Starts the effect of an ability'), and
// then call the CheckForRecipes function from there. The arguments it takes
// are the item that should be checked if it belongs to a recipe and the unit
// the result should be created to (if any is created).
// The function returns a boolean depending on whether it combined the item
// to something or not. And if it combined something, you can refer to the
// created item with bj_lastCreatedItem (Last created item in GUI).
//
// Strengths of RecipeSYS
// ¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨
// - Supports having several items of the same type as
// ingredients in the same recipe.
// - Supports having the same item type as an ingredient in
// several recipes.
// - Allows to specify the special effect and it's attachment
// point (each recipe can have its own ones, if you wish so).
// - All you need to do is add the recipes.
// - You can remove recipes.
// - You can add recipes with results that have charges.
// - The amount of charges can be predefined, the sum of charges
// left in the ingredients, or the amount of charges in the
// ingredient with most charges.
// - You can disable and enable single recipes or the whole sytem.
// - You can disassemble items.
// - You can disable the automatic combining of items, and create
// a trigger that combines items manually (so you can have any
// event you want).
//
// Weaknesses of RecipeSYS
// ¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨
// - Don't know if it's optimized as much as it could nor if it's
// the best recipe system out there. ^_^
// - The item disassembling has some restrictions, and doesn't work
// flawlessy with the 'A unit Loses an item' event (see the Test2
// trigger in the demo map or the instructions of the function for
// additional information).
// - The max amount of item types you can use in recipes as
// ingredients is 8190 / AMOUNT_ING and as results 8190 / AMOUNT_RES.
//
// How to import RecipeSYS
// ¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨
// Two easy steps:
// - Copy this trigger to your map.
// - Copy the trigger 'Table' to your map.
// Alternatively:
// - Create a trigger named 'RecipeSYS' in your map.
// - Convert it to Jass (Custom Script) and replace all
// of the code in there with the code in this trigger.
// - Create a trigger named 'Table' in your map.
// - Convert it to Jass (Custom Script) and replace all
// of the code in there with the code in the trigger 'Table'.
globals
// ¤ ¤
// ( *'*-._.-*'*-._.-*'*-._.-*'* )
// ) RecipeSYS configuration (
// ( *'*-._.-*'*-._.-*'*-._.-*'* )
// ¤ ¤
private constant boolean AUTO_COMBINE = true
//Whether you want the system to automatically combine
//the ingredients of a recipe to the result when a unit
//has all of them.
private constant string SFX_PATH = "Abilities\\Spells\\Items\\TomeOfRetraining\\TomeOfRetrainingCaster.mdl"
//The path of the default effect.
private constant string SFX_POINT = "origin"
//The default attachment point.
private constant integer AMOUNT_ING = 10
//The amount of recipes an item can be an ingredient in.
private constant integer AMOUNT_RES = 10
//The amount of recipes an item can be a result in.
// ¤ ¤
// ( *'*-._.-*'*-._.-*'*-._.-*'* )
// )DO NOT EDIT PAST THIS POINT(
// ( *'*-._.-*'*-._.-*'*-._.-*'* )
// )Well, you can if you really(
// ( want to, but only if you )
// )know what you're doing. And(
// ( it's not needed, anyways. )
// )*'*-._.-*'*-._.-*'*-._.-*'*(
// ¤ ¤
public constant trigger TRIGGER = CreateTrigger()
private Table ING_TABLE
private Table RES_TABLE
private HandleTable COMBINE_TABLE
endglobals
private type ingIn extends recipe array [AMOUNT_ING]
private type resIn extends recipe array [AMOUNT_RES]
struct recipe
integer array items [6]
integer result
integer chargeType
string sfx
string point
boolean resultHasCharges
boolean enabled
static method create takes integer i1, integer i2, integer i3, integer i4, integer i5, integer i6, integer result, string sfx, string attPoint, boolean charges, integer chargetype returns recipe
local recipe i = recipe.allocate()
local ingIn ing
local resIn res
local boolean array b
local integer j = 0
local integer k
local integer l = 0
set i.items[0] = i1
set i.items[1] = i2
set i.items[2] = i3
set i.items[3] = i4
set i.items[4] = i5
set i.items[5] = i6
set i.result = result
set i.enabled = true
set i.resultHasCharges = charges
set i.chargeType = chargetype
//To prevent linking the recipe to the item type twice.
set b[0] = true
set b[1] = i2 != i1
set b[2] = i3 != i2 and i3 != i1
set b[3] = i4 != i3 and i4 != i2 and i4 != i1
set b[4] = i5 != i4 and i5 != i3 and i5 != i2 and i5 != i1
set b[5] = i6 != i5 and i6 != i4 and i6 != i3 and i6 != i2 and i6 != i1
//Set the SFX things (default or specified).
if sfx == "" then
set i.sfx = SFX_PATH
else
set i.sfx = sfx
endif
if attPoint == "" then
set i.point = SFX_POINT
else
set i.point = attPoint
endif
//Linking the recipe to the item types.
loop
if i.items[j] != 0 and b[j] then
set k = ING_TABLE[i.items[j]]
if k != 0 then
//Item type is in another recipe aswell.
set ing = ingIn(k)
set k = 0
//Seek for an open slot.
loop
exitwhen ing[k] == 0
set k = k + 1
if k == ingIn.size then
debug call BJDebugMsg("|cffff0202RecipeSYS Error:|r AMOUNT_ING is too small!")
return 0
endif
endloop
else
//First appearance for the item type.
set ing = ingIn.create()
set k = 0
endif
set ing[k] = i
set ING_TABLE[i.items[j]] = integer(ing)
endif
set j = j + 1
exitwhen j == 6
endloop
set k = RES_TABLE[i.result]
if k != 0 then
//Item type is in some other recipe, too.
set res = resIn(k)
set k = 0
loop
exitwhen res[k] == 0
set k = k + 1
if k == resIn.size then
debug call BJDebugMsg("|cffff0202RecipeSYS Error:|r AMOUNT_RES too small!")
return 0
endif
endloop
else
//First time being in a recipe for the item type.
set res = resIn.create()
endif
set res[k] = i
set RES_TABLE[i.result] = integer(res)
return i
endmethod
private method ClearRecipeFromIngredient takes integer it returns nothing
local integer i = 0
local integer j
local ingIn ing = ingIn(ING_TABLE[it])
if it == 0 or integer(ing[0]) == 0 then
return
endif
//Finding the right array slot and setting it's value to 0.
loop
exitwhen i == ingIn.size
if ing[i] == this then
set j = i + 1
//Finding the last used array slot.
loop
exitwhen j == ingIn.size or ing[j] == 0
set j = j + 1
endloop
set j = j - 1
set ing[i] = ing[j]
set ing[j] = 0
exitwhen true
endif
set i = i + 1
endloop
//Free the array and flush if the slots are empty.
if integer(ing[0]) == 0 then
call ING_TABLE.flush(it)
call ing.destroy()
endif
endmethod
private method ClearRecipeFromResult takes integer it returns nothing
local integer i = 0
local integer j
local resIn res = resIn(RES_TABLE[it])
if it == 0 then
return
endif
//Finding the right array slot and setting it's value to 0.
loop
exitwhen i == resIn.size
if res[i] == this then
set j = i + 1
//Finding the last used array slot.
loop
exitwhen j == resIn.size or res[j] == 0
set j = j + 1
endloop
set j = j - 1
set res[i] = res[j]
set res[j] = 0
exitwhen true
endif
set i = i + 1
endloop
//Free the array slots and flush if it's empty.
if res[0] == 0 then
call RES_TABLE.flush(it)
call res.destroy()
endif
endmethod
method onDestroy takes nothing returns nothing
local integer i = 0
loop
call .ClearRecipeFromIngredient(.items[i])
set i = i + 1
exitwhen i == 6
endloop
call .ClearRecipeFromResult(.result)
set .result = 0
endmethod
endstruct
//****************************************************
//** Recipes Without Charges **
//****************************************************
function AddRecipe takes integer item1, integer item2, integer item3, integer item4, integer item5, integer item6, integer result returns recipe
return recipe.create(item1, item2, item3, item4, item5, item6, result, "", "", false, 0)
endfunction
function AddRecipeEx takes integer item1, integer item2, integer item3, integer item4, integer item5, integer item6, integer result, string sfx, string attachmentPoint returns recipe
return recipe.create(item1, item2, item3, item4, item5, item6, result, sfx, attachmentPoint, false, 0)
endfunction
//****************************************************
//** Recipes With Charges **
//****************************************************
function AddRecipeWithCharges takes integer item1, integer item2, integer item3, integer item4, integer item5, integer item6, integer result, integer chargeType returns recipe
return recipe.create(item1, item2, item3, item4, item5, item6, result, "", "", true, chargeType)
endfunction
function AddRecipeWithChargesEx takes integer item1, integer item2, integer item3, integer item4, integer item5, integer item6, integer result, integer chargeType, string sfx, string attachmentPoint returns recipe
return recipe.create(item1, item2, item3, item4, item5, item6, result, sfx, attachmentPoint, true, chargeType)
endfunction
//****************************************************
//** Removing Recipes **
//****************************************************
function RemoveRecipeByResult takes integer result returns nothing
local resIn res = resIn(RES_TABLE[result])
loop
exitwhen integer(res[0]) == 0
call res[0].destroy()
endloop
endfunction
//****************************************************
//** Disabling and enabling recipes **
//****************************************************
function EnableRecipeByResult takes integer result, boolean enable returns nothing
local resIn res = resIn(RES_TABLE[result])
local integer i = 0
loop
exitwhen integer(res[i]) == 0
set res[i].enabled = enable
set i = i + 1
endloop
endfunction
//****************************************************
//** Disassembling Combined Items **
//****************************************************
function DisassembleItemByRecipe takes item whichItem, unit whichUnit, recipe re returns nothing
local integer i = 0
local real x = GetItemX(whichItem)
local real y = GetItemY(whichItem)
local boolean b = IsTriggerEnabled(GetTriggeringTrigger())
local boolean c = IsTriggerEnabled(TRIGGER)
if re.result == GetItemTypeId(whichItem) then
if b then
//Avoid clashing with some events.
call DisableTrigger(GetTriggeringTrigger())
endif
call COMBINE_TABLE.flush(whichItem)
call RemoveItem(whichItem)
//Should the items be created for a unit or at ground?
if whichUnit == null then
loop
call CreateItem(re.items[i], x, y)
set i = i + 1
exitwhen i == 6
endloop
else
if c then
//Preventing infinite loop.
call DisableTrigger(TRIGGER)
endif
loop
call UnitAddItemById(whichUnit, re.items[i])
set i = i + 1
exitwhen i == 6
endloop
if c then
call EnableTrigger(TRIGGER)
endif
endif
if b then
call EnableTrigger(GetTriggeringTrigger())
endif
endif
endfunction
function DisassembleItem takes item whichItem, unit whichUnit returns nothing
local integer id = GetItemTypeId(whichItem)
local resIn r = resIn(RES_TABLE[id])
local recipe re = recipe(COMBINE_TABLE[whichItem])
if integer(re) == 0 or re.result != id then //The last one just in case the recipe has been destroyed.
if integer(r[0]) == 0 then
return
endif
set re = r[0]
endif
call DisassembleItemByRecipe(whichItem, whichUnit, re)
endfunction
//****************************************************
//** Combining **
//****************************************************
private function Combine takes recipe rec, unit u returns boolean
local integer array itemsReq
local integer itemInSlot
local integer topCharges = 0
local integer sumCharges = 0
local integer i = 0
local integer j = 0
local item resu
local item array itemsToCombine
//Setting the items.
loop
set itemsReq[i] = rec.items[i]
set i = i + 1
exitwhen i == 6
endloop
set i = 0
//Finding the items.
loop
set itemInSlot = GetItemTypeId(UnitItemInSlot(u, i))
loop
if itemsReq[j] == itemInSlot and itemInSlot != 0 then
set itemsToCombine[j] = UnitItemInSlot(u, i)
set itemsReq[j] = 0
exitwhen true
endif
set j = j + 1
exitwhen j == 6
endloop
set j = 0
set i = i + 1
exitwhen i == 6
endloop
set i = 0
set j = 0
//Checking if all items were found.
loop
if itemsReq[i] != 0 then
loop
set itemsToCombine[j] = null
set j = j + 1
exitwhen j == 6
endloop
return false
endif
set i = i + 1
exitwhen i == 6
endloop
set i = 0
//Remove the old items.
loop
//Set the different charge amounts in case the result has charges.
set j = GetItemCharges(itemsToCombine[i])
set sumCharges = sumCharges + j
if j > topCharges then
set topCharges = j
endif
call COMBINE_TABLE.flush(itemsToCombine[i])
call RemoveItem(itemsToCombine[i])
set itemsToCombine[i] = null
set i = i + 1
exitwhen i == 6
endloop
//Create the result.
set resu = UnitAddItemById(u, rec.result)
call DestroyEffect(AddSpecialEffectTarget(rec.sfx, u, rec.point))
//Set charges if it should have any.
if rec.resultHasCharges then
if rec.chargeType == 0 then
call SetItemCharges(resu, sumCharges)
elseif rec.chargeType == -1 then
call SetItemCharges(resu, topCharges)
else
call SetItemCharges(resu, rec.chargeType)
endif
endif
//Set the data if wanted.
set COMBINE_TABLE[resu] = integer(rec)
set bj_lastCreatedItem = resu
set resu = null
return true
endfunction
//****************************************************
//** Recipe Finding **
//****************************************************
function CheckForRecipes takes item check, unit u returns boolean
local ingIn r = ingIn(ING_TABLE[GetItemTypeId(check)])
local integer i = 0
loop
exitwhen integer(r[i]) == 0
if r[i].enabled then
if Combine(r[i], u) then
return true
endif
endif
set i = i + 1
endloop
return false
endfunction
private function Check takes nothing returns boolean
return CheckForRecipes(GetManipulatedItem(), GetTriggerUnit())
endfunction
//=====================================================================
private function SafeFilt takes nothing returns boolean
return true
endfunction
//=====================================================================
private function Init takes nothing returns nothing
local integer index = 0
if AUTO_COMBINE then
loop
call TriggerRegisterPlayerUnitEvent(TRIGGER, Player(index), EVENT_PLAYER_UNIT_PICKUP_ITEM, Filter(function SafeFilt))
set index = index + 1
exitwhen index == bj_MAX_PLAYER_SLOTS
endloop
call TriggerAddCondition(TRIGGER, Condition(function Check))
endif
set ING_TABLE = Table.create()
set RES_TABLE = Table.create()
set COMBINE_TABLE = HandleTable.create()
endfunction
endlibrary
//TESH.scrollpos=0
//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=231
//TESH.alwaysfold=0
library SpellEvent initializer Init requires Table
//*****************************************************************
//* SPELL EVENT LIBRARY 1.1
//*
//* 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.
//*
//* Note that a few spells such as Berserk and Wind Walk behave
//* somewhat differently from regular spells: they are cast
//* instantly without regard for cast animation times, they do not
//* interrupt the unit's current order, as well as any spell it
//* may be casting. SpellEvent can now handle such spells without
//* errors provided they are truly instant (without casting time).
//*
//*****************************************************************
// 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
private spellEvent interrupt
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 s.interrupt=spellEvent.casterTable[s.CastingUnit]
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
if .interrupt==0 then
call spellEvent.casterTable.flush(.CastingUnit)
else
set spellEvent.casterTable[.CastingUnit]=.interrupt
endif
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=0
//TESH.alwaysfold=0
//***********************
//* Timed Effects *
//* by moyack. 2009 *
//***********************
//*
//* Requires Jass NewGen Pack and TimerUtils
//*
//* Introduction
//* ============
//*
//* So... have you been needing a simple way (or function) to create an effect temporally
//* and don't worry about destroying it? or you're needing that an effect with different
//* animations (birth, stand, death) will show all of them?? if your answer is yes to any
//* of those questions, then you're in the right place.
//*
//* This script is used in my project Power of Corruption ([url]www.pocr.tk)[/url], as you'll see,
//* it's simple as hell.
//*
//* Just to point out: this library is very useful with effects that have different
//* animations, so using this library with single animation effect will do the same as
//* call DestroyEffect(AddSpecialEffect(....))
//*
//* How to use it?
//* ==============
//* - Create a trigger with a convenient name (like Timed Effects :P)
//* - Convert the trigger to custom text
//* - Copy and paste this code and save your map.
//*
//* With all this done, you just have to call the function StartTimedEffect(), and as parameters
//* an effect and a real value which will be the effect duration.
//*
//* Example: call StartTimedEffect(AddSpecialEffect(fx, 0., 0.), 2.)
//*
//* This function will return an integer which will represent the timed effect index, this value
//* can be used when, for example, you need to end the effect before the duration time.
//*
//* To stop it, you just call the function StopTimedEffect():
//*
//* Example: call StopTimedEffect(1) // will stop the timed effect with that index.
//*
//* You won't need anything else, the system will care of cleaning the effect properly
//* and recycle the values for you.
library_once TimedEffects requires TimerUtils
private struct data
effect f = null
timer t
static method create takes effect f returns data
local data dt = data.allocate()
set dt.t = NewTimer()
set dt.f = f
call SetTimerData(dt.t, integer(dt))
return dt
endmethod
endstruct
function StopTimedEffect takes integer index returns nothing
call DestroyEffect(data(index).f)
call ReleaseTimer(data(index).t)
call data(index).destroy()
endfunction
private function DestroyTimedEffect takes nothing returns nothing
call StopTimedEffect(GetTimerData(GetExpiredTimer()))
endfunction
function StartTimedEffect takes effect f, real dur returns integer
local data d = data.create(f)
call TimerStart(d.t, dur, false, function DestroyTimedEffect)
return integer(d)
endfunction
endlibrary
//TESH.scrollpos=36
//TESH.alwaysfold=0
library GroupUtils initializer Init requires optional xebasic
//******************************************************************************
//* BY: Rising_Dusk
//*
//* This library is a combination of several features relevant to groups. First
//* and foremost, it contains a group stack that you can access dynamic groups
//* from. It also provides means to refresh groups and clear any shadow
//* references within them. The included boolexprs are there for backwards
//* compatibility with maps that happen to use them. Since the 1.24c patch,
//* null boolexprs used in GroupEnumUnits* calls no longer leak, so there is no
//* performance gain to using the BOOLEXPR_TRUE constant.
//*
//* Instead of creating/destroying groups, we have moved on to recycling them.
//* NewGroup pulls a group from the stack and ReleaseGroup adds it back. Always
//* remember to call ReleaseGroup on a group when you are done using it. If you
//* fail to do so enough times, the stack will overflow and no longer work.
//*
//* GroupRefresh cleans a group of any shadow references which may be clogging
//* its hashtable. 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. It is only worth doing this on
//* groups that you plan to have around for awhile.
//*
//* Constants that can be used from the library:
//* [group] ENUM_GROUP As you might expect, this group is good for
//* when you need a group just for enumeration.
//* [boolexpr] BOOLEXPR_TRUE This is a true boolexpr, which is important
//* because a 'null' boolexpr in enumeration
//* calls results in a leak. Use this instead.
//* [boolexpr] BOOLEXPR_FALSE This exists mostly for completeness.
//*
//* This library also includes a simple implementation of a group enumeration
//* call that factors collision of units in a given area of effect. This is
//* particularly useful because GroupEnumUnitsInRange doesn't factor collision.
//*
//* In your map, you can just replace all instances of GroupEnumUnitsInRange
//* with GroupEnumUnitsInArea with identical arguments and your spells will
//* consider all units colliding with the area of effect. After calling this
//* function as you would normally call GroupEnumUnitsInRange, you are free to
//* do anything with the group that you would normally do.
//*
//* If you don't use xebasic in your map, you may edit the MAX_COLLISION_SIZE
//* variable below and the library will use that as the added radius to check.
//* If you use xebasic, however, the script will automatically use xe's
//* collision size variable.
//*
//* You are also able to use GroupUnitsInArea. This function returns all units
//* within the area, no matter what they are, which can be convenient for those
//* instances where you actually want that.
//*
//* Example usage:
//* local group MyGroup = NewGroup()
//* call GroupRefresh(MyGroup)
//* call ReleaseGroup(MyGroup)
//* call GroupEnumUnitsInArea(ENUM_GROUP, x, y, 350., BOOLEXPR_TRUE)
//* call GroupUnitsInArea(ENUM_GROUP, x, y, 350.)
//*
globals
//If you don't have xebasic in your map, this value will be used instead.
//This value corresponds to the max collision size of a unit in your map.
private constant real MAX_COLLISION_SIZE = 197.
//If you are insane and don't care about any of the protection involved in
//this library, but want this script to be really fast, set this to true.
private constant boolean LESS_SAFETY = false
endglobals
globals
//* Constants that are available to the user
group ENUM_GROUP = CreateGroup()
boolexpr BOOLEXPR_TRUE = null
boolexpr BOOLEXPR_FALSE = null
endglobals
globals
//* Hashtable for debug purposes
private hashtable ht = InitHashtable()
//* Temporary references for GroupRefresh
private boolean Flag = false
private group Refr = null
//* Arrays and counter for the group stack
private group array Groups
private integer Count = 0
//* Variables for use with the GroupUnitsInArea function
private real X = 0.
private real Y = 0.
private real R = 0.
private hashtable H = InitHashtable()
endglobals
private function HookDestroyGroup takes group g returns nothing
if g == ENUM_GROUP then
call BJDebugMsg(SCOPE_PREFIX+"Warning: ENUM_GROUP destroyed")
endif
endfunction
debug hook DestroyGroup HookDestroyGroup
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
static if not LESS_SAFETY then
call SaveInteger(ht, 0, GetHandleId(Groups[Count]), 1)
endif
return Groups[Count]
endfunction
function ReleaseGroup takes group g returns boolean
local integer id = GetHandleId(g)
static if LESS_SAFETY then
if g == null then
debug call BJDebugMsg(SCOPE_PREFIX+"Error: Null groups cannot be released")
return false
elseif Count == 8191 then
debug call BJDebugMsg(SCOPE_PREFIX+"Error: Max groups achieved, destroying group")
call DestroyGroup(g)
return false
endif
else
if g == null then
debug call BJDebugMsg(SCOPE_PREFIX+"Error: Null groups cannot be released")
return false
elseif not HaveSavedInteger(ht, 0, id) then
debug call BJDebugMsg(SCOPE_PREFIX+"Error: Group not part of stack")
return false
elseif LoadInteger(ht, 0, id) == 2 then
debug call BJDebugMsg(SCOPE_PREFIX+"Error: Groups cannot be multiply released")
return false
elseif Count == 8191 then
debug call BJDebugMsg(SCOPE_PREFIX+"Error: Max groups achieved, destroying group")
call DestroyGroup(g)
return false
endif
call SaveInteger(ht, 0, id, 2)
endif
call GroupClear(g)
set Groups[Count] = g
set Count = Count + 1
return true
endfunction
private function Filter takes nothing returns boolean
return IsUnitInRangeXY(GetFilterUnit(), X, Y, R)
endfunction
private function HookDestroyBoolExpr takes boolexpr b returns nothing
local integer bid = GetHandleId(b)
if HaveSavedHandle(H, 0, bid) then
//Clear the saved boolexpr
call DestroyBoolExpr(LoadBooleanExprHandle(H, 0, bid))
call RemoveSavedHandle(H, 0, bid)
endif
endfunction
hook DestroyBoolExpr HookDestroyBoolExpr
private constant function GetRadius takes real radius returns real
static if LIBRARY_xebasic then
return radius+XE_MAX_COLLISION_SIZE
else
return radius+MAX_COLLISION_SIZE
endif
endfunction
function GroupEnumUnitsInArea takes group whichGroup, real x, real y, real radius, boolexpr filter returns nothing
local real prevX = X
local real prevY = Y
local real prevR = R
local integer bid = 0
//Set variables to new values
set X = x
set Y = y
set R = radius
if filter == null then
//Adjusts for null boolexprs passed to the function
set filter = Condition(function Filter)
else
//Check for a saved boolexpr
set bid = GetHandleId(filter)
if HaveSavedHandle(H, 0, bid) then
//Set the filter to use to the saved one
set filter = LoadBooleanExprHandle(H, 0, bid)
else
//Create a new And() boolexpr for this filter
set filter = And(Condition(function Filter), filter)
call SaveBooleanExprHandle(H, 0, bid, filter)
endif
endif
//Enumerate, if they want to use the boolexpr, this lets them
call GroupEnumUnitsInRange(whichGroup, x, y, GetRadius(radius), filter)
//Give back original settings so nested enumerations work
set X = prevX
set Y = prevY
set R = prevR
endfunction
function GroupUnitsInArea takes group whichGroup, real x, real y, real radius returns nothing
local real prevX = X
local real prevY = Y
local real prevR = R
//Set variables to new values
set X = x
set Y = y
set R = radius
//Enumerate
call GroupEnumUnitsInRange(whichGroup, x, y, GetRadius(radius), Condition(function Filter))
//Give back original settings so nested enumerations work
set X = prevX
set Y = prevY
set R = prevR
endfunction
private function True takes nothing returns boolean
return true
endfunction
private function False takes nothing returns boolean
return false
endfunction
private function Init takes nothing returns nothing
set BOOLEXPR_TRUE = Condition(function True)
set BOOLEXPR_FALSE = Condition(function False)
endfunction
endlibrary
//TESH.scrollpos=56
//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
library SoundUtils requires Stack, TimerUtils
//******************************************************************************
//* BY: Rising_Dusk
//*
//* Sounds are a very picky datatype in WC3. They have many quirks that one must
//* account for in order to use them, and simply using the internal WE Sound
//* Editor isn't enough because the sounds it makes can't be played multiple
//* times at once. 3-D sounds are also very tricky because there are different
//* WC3 sound options that a user can have activated where certain sounds will
//* or will not work. This library attempts to streamline the handling of sounds
//* so that it is less likely to confuse you or cause problems.
//*
//* The .mp3 format can be used for 3-D sounds, but there is one problem that
//* must be noted. If your computer supports the "Dolby Surround" sound option
//* in WC3 and you have it selected, then .mp3 files will work for 3-D sounds.
//* If you don't, however, they may not work depending on what you do have
//* selected and what is available for your computer. The .wav format works on
//* all possible settings, making them excellent for general use. This library
//* can interface with sounds of either type.
//*
//* Known issues with sounds that this library resolves:
//* - A given sound variable can only be played once at a time. In order to
//* play a sound type multiple times at once, you need multiple variables.
//* - A sound cannot be played at the same instant that it is created.
//*
//* The DefineSound function defines a sound type based on some basic parameters
//* the user provides. DefineSoundEx is available if the user wants control over
//* all possible parameters, though they won't have an impact most of the time.
//* The duration parameter for DefineSound and DefineSoundEx is in milliseconds,
//* which is consistent with Blizzard's natives. To get the duration of a given
//* sound, open up the WE's Sound Editor, navigate to your sound, and select
//* "Add as Sound." In doing so, it will show its duration in seconds. Multiply
//* that number by 1000 and use it as the duration argument.
//*
//* This library returns a sound variable with RunSound that you can change the
//* settings of using the standard JASS sound API. The library assigns default
//* values to the parameters for 2-D and 3-D sounds, that way they will run
//* without any further help.
//*
//* The library automatically allocates, runs, and recycles a sound when you
//* call RunSound. This library will not automatically recycle looping sounds,
//* so you will need to call ReleaseSound on the looping sound when you want it
//* to end.
//*
//******************************************************************************
//*
//* > function DefineSound takes string fileName, integer duration, ...
//* boolean looping, boolean is3D returns integer
//*
//* This function defines a sound type with a short list of parameters. The
//* returned integer serves as a SOUND_TYPE for running this type of sound at
//* any other point in a map.
//*
//* > function DefineSoundEx takes string fileName, integer duration, ...
//* boolean looping, boolean is3D, boolean stopwhenoutofrange, ...
//* integer fadeInRate, integer fadeOutRate, string eaxSetting ...
//* returns integer
//*
//* This function serves an identical purpose to DefineSound, but gives the user
//* full control over the entire list of parameters. Similar to DefineSound, the
//* returned integer serves as a SOUND_TYPE for running this type of sound.
//*
//* > function RunSound takes integer soundRef returns sound
//*
//* This function runs a sound with the parameters held within the soundRef
//* integer argument. The soundRef argument is the returned value of DefineSound
//* or DefineSoundEx.
//*
//* > function RunSoundOnUnit takes integer soundRef, unit whichUnit returns sound
//*
//* The same as RunSound, just this function runs a sound of a given type on a
//* specified unit.
//*
//* > function RunSoundAtPoint takes integer soundRef, real x, real y, real z returns sound
//*
//* The same as RunSound, just this function runs a sound of a given type at a
//* specified point in 3D space.
//*
//* > function RunSoundForPlayer takes integer soundRef, player p returns sound
//*
//* The same as RunSound, just this function runs a sound of a given type only
//* for the specified player.
//*
//* > function ReleaseSound takes sound s returns boolean
//*
//* This function need only be called on looping sounds. If a sound is not
//* looping, it will be released and recycled on its own. This function should
//* be used on looping sounds when you want them to end.
//*
//* Example usage:
//* set SOUND_TYPE = DefineSound("Sound\\Path.wav", 300, false, true)
//* call RunSound(SOUND_TYPE)
//* call RunSoundOnUnit(SOUND_TYPE, SomeUnit)
//* call RunSoundAtPoint(SOUND_TYPE, x, y, z)
//* call RunSoundForPlayer(SOUND_TYPE, Player(5))
//* call ReleaseSound(SomeLoopingSound)
//*
globals
private hashtable ht = InitHashtable() //Attach sound types to sounds
private hashtable st = InitHashtable() //Sound hashtable
private hashtable rt = InitHashtable() //Attach soundrecyclers to sounds
private hashtable kt = InitHashtable() //Attach StopSound data
endglobals
//Struct for each sound type
private struct soundhelper
//Stack associated to each struct
Stack sta
//Sound Settings for this sound type
string fileName = ""
integer duration = 0
boolean looping = false
boolean is3D = false
boolean stopwhenoutofrange = false
integer fadeInRate = 0
integer fadeOutRate = 0
string eaxSetting = ""
static method create takes string fileName, integer duration, boolean looping, boolean is3D, boolean stopwhenoutofrange, integer fadeInRate, integer fadeOutRate, string eaxSetting returns soundhelper
local soundhelper sh = soundhelper.allocate()
//Load the parameters so the sound can be created later as necessary
set sh.fileName = fileName
set sh.duration = duration
set sh.looping = looping
set sh.is3D = is3D
set sh.stopwhenoutofrange = stopwhenoutofrange
set sh.fadeInRate = fadeInRate
set sh.fadeOutRate = fadeOutRate
set sh.eaxSetting = eaxSetting
//Create the stack for the struct
set sh.sta = Stack.create()
return sh
endmethod
endstruct
//Struct for holding data for the sound recycling
private struct soundrecycler
timer t = null
sound s = null
integer sh = 0
boolean stopped = false //Only gets used if StopSound is called on a new sound
static method create takes sound whichSound, integer soundRef returns soundrecycler
local soundrecycler sr = soundrecycler.allocate()
set sr.t = NewTimer()
set sr.s = whichSound
set sr.sh = soundRef
call SetTimerData(sr.t, integer(sr))
//Hook the value to the soundRef and whichSound
call SaveInteger(rt, soundRef, GetHandleId(whichSound), integer(sr))
return sr
endmethod
private method onDestroy takes nothing returns nothing
call RemoveSavedInteger(rt, .sh, GetHandleId(.s))
call ReleaseTimer(.t)
endmethod
endstruct
//******************************************************************************
private function HookStopSound takes sound soundHandle, boolean killWhenDone, boolean fadeOut returns nothing
local integer id = GetHandleId(soundHandle)
local integer soundRef = 0
local soundrecycler sr = 0
if HaveSavedInteger(ht, 0, id) then //Sound is from stacks
set soundRef = LoadInteger(ht, 0, id)
if HaveSavedInteger(rt, soundRef, id) then //Sound has a recycler
set sr = soundrecycler(LoadInteger(rt, soundRef, id))
set sr.stopped = true
endif
if killWhenDone then
debug call BJDebugMsg(SCOPE_PREFIX+"Warning: (StopSound) Destroying a sound in the stack")
endif
endif
endfunction
hook StopSound HookStopSound
private function HookKillSoundWhenDone takes sound soundHandle returns nothing
if HaveSavedInteger(ht, 0, GetHandleId(soundHandle)) then
call BJDebugMsg(SCOPE_PREFIX+"Warning: (KillSoundWhenDone) Destroying a sound in the stack")
endif
endfunction
debug hook KillSoundWhenDone HookKillSoundWhenDone
//******************************************************************************
function DefineSoundEx takes string fileName, integer duration, boolean looping, boolean is3D, boolean stopwhenoutofrange, integer fadeInRate, integer fadeOutRate, string eaxSetting returns integer
return integer(soundhelper.create(fileName, duration, looping, is3D, stopwhenoutofrange, fadeInRate, fadeOutRate, eaxSetting))
endfunction
function DefineSound takes string fileName, integer duration, boolean looping, boolean is3D returns integer
return DefineSoundEx(fileName, duration, looping, is3D, true, 10, 10, "CombatSoundsEAX")
endfunction
function ReleaseSound takes sound s returns boolean
local integer id = GetHandleId(s)
local integer soundRef = 0
local soundhelper sh = 0
local soundrecycler sr = 0
if s == null then
debug call BJDebugMsg(SCOPE_PREFIX+"Error: Cannot recycle a null sound")
return false
elseif not HaveSavedInteger(ht, 0, id) then
debug call BJDebugMsg(SCOPE_PREFIX+"Error: Cannot recycle a sound not allocated by RunSound")
return false
endif
set soundRef = LoadInteger(ht, 0, id)
set sh = soundhelper(soundRef)
call StopSound(s, false, true) //Stop the sound
call sh.sta.push(id) //Return it to the stack
call SaveSoundHandle(st, soundRef, id, s) //Save it to hashtable
if not sh.looping then
//soundrecycler only exists for non-looping sounds
set sr = soundrecycler(LoadInteger(rt, soundRef, id))
call sr.destroy() //Destroy recycler helper
endif
return true
endfunction
private function Recycle takes nothing returns nothing
local soundrecycler sr = soundrecycler(GetTimerData(GetExpiredTimer()))
local soundhelper sh = soundhelper(sr.sh)
local integer id = GetHandleId(sr.s)
call StopSound(sr.s, false, true) //Stop the sound
call sh.sta.push(id) //Return it to the stack
call SaveSoundHandle(st, integer(sh), id, sr.s) //Save it to hashtable
call sr.destroy() //Destroy recycler helper
endfunction
private function Run takes nothing returns nothing
local soundrecycler sr = soundrecycler(GetTimerData(GetExpiredTimer()))
local soundhelper sh = soundhelper(sr.sh)
if not sr.stopped then
call StartSound(sr.s) //Play sound here
endif
if not sh.looping and not sr.stopped then
call TimerStart(sr.t, sh.duration*0.001, false, function Recycle)
else
call sr.destroy()
endif
endfunction
function RunSound takes integer soundRef returns sound
local sound s = null
local integer i = 0
local soundhelper sh = soundhelper(soundRef)
local soundrecycler sr = 0
if soundRef <= 0 then
debug call BJDebugMsg(SCOPE_PREFIX+"Error: Cannot run sound of undefined type")
return null
endif
//Check if the stack is empty
if sh.sta.peek() == Stack.EMPTY then
//Create a new sound for the stack
set s = CreateSound(sh.fileName, sh.looping, sh.is3D, sh.stopwhenoutofrange, sh.fadeInRate, sh.fadeOutRate, sh.eaxSetting)
//Attach the type to the sound for future reference
call SaveInteger(ht, 0, GetHandleId(s), integer(sh))
call SetSoundDuration(s, sh.duration)
//Stuff that must be performed immediately upon creation of sounds
call SetSoundChannel(s, 5)
call SetSoundVolume(s, 127)
call SetSoundPitch(s, 1.)
if sh.is3D then
//These are settings necessary for 3-D sounds to function properly
//You can change them at will outside of this function
call SetSoundDistances(s, 600., 10000.)
call SetSoundDistanceCutoff(s, 3000.)
call SetSoundConeAngles(s, 0., 0., 127)
call SetSoundConeOrientation(s, 0., 0., 0.)
endif
//Start sound after a delay because it was created here
set sr = soundrecycler.create(s, soundRef)
call TimerStart(sr.t, 0.001, false, function Run)
else
//Allocate a sound from the stack
set i = sh.sta.pop()
if not HaveSavedHandle(st, soundRef, i) then
debug call BJDebugMsg(SCOPE_PREFIX+"Error: No sound in given stack member")
return null
endif
set s = LoadSoundHandle(st, soundRef, i)
call RemoveSavedInteger(st, soundRef, i)
call SetSoundVolume(s, 127) //Start volume at max
//Start it here since it wasn't created here
call StartSound(s)
//Recycle the sound in a timer callback after it's finished if nonlooping
if not sh.looping then
set sr = soundrecycler.create(s, soundRef)
call TimerStart(sr.t, sh.duration*0.001, false, function Recycle)
endif
endif
return s
endfunction
function RunSoundOnUnit takes integer soundRef, unit whichUnit returns sound
local sound s = RunSound(soundRef)
call AttachSoundToUnit(s, whichUnit)
return s
endfunction
function RunSoundAtPoint takes integer soundRef, real x, real y, real z returns sound
local sound s = RunSound(soundRef)
call SetSoundPosition(s, x, y, z)
return s
endfunction
function RunSoundForPlayer takes integer soundRef, player p returns sound
local sound s = RunSound(soundRef)
if GetLocalPlayer() != p then
call SetSoundVolume(s, 0)
else
call SetSoundVolume(s, 127)
endif
return s
endfunction
endlibrary
library Stack
//*****************************************************************
//* STACK
//*
//* written by: Anitarf
//*
//* This is an efficient implementation of a stack in vJass. Since
//* it is based on a linked list, I decided to add common list
//* methods to the data structure so it can function both as
//* a stack and a simple linked list.
//*
//* As a linked list, it has less functionality than Ammorth's
//* LinkedList, but is considerably faster. Note only that most of
//* the list methods have O(n) time complexity so they may not be
//* suitable for operations on very large lists, however due to
//* their simplicity the list would need to be really large for
//* this to become a problem.
//*
//* All stack methods are of course O(1) and as fast as possible.
//* If you just need a stack, this is definitely the best choice.
//*
//* set s=Stack.create() - Instanceates a new Stack object.
//* call s.destroy() - Destroys the Stack.
//*
//* Stack syntax:
//* call s.push(123) - Pushes the value 123 on the stack.
//* A stack may contain multiple
//* instances of the same value.
//* set i=s.peek() - Reads the top value of the stack
//* and stores it to the variable i.
//* set i=s.pop() - Removes the top value from the stack
//* and stores it to the variable i.
//* s.peek()==Stack.EMPTY - Checks if the stack is empty.
//*
//* List syntax:
//* call s.add(123) - Adds the value 123 to the list.
//* A list may contain multiple
//* instances of the same value.
//* s.size - The total number of values on the list.
//* s.contains(123) - Checks if the value 123 is on the list.
//* set n=s.count(123) - Stores the number of times the value
//* 123 is on the list to the variable n.
//* call s.remove(123) - Removes one instance of the value 123
//* from the list. Returns false if
//* the value was not found on the list.
//* call s.purge(123) - Removes all instances of the value 123
//* from the list. Returns the number of
//* values that were removed.
//* set i=s.first - Reads the first value from the list
//* and stores it to the variable i.
//* set i=s.random - Reads a random value from the list
//* and stores it to the variable i.
//* set s2=s.copy() - Makes a copy of the list and stores
//* it to the variable s2.
//* call s.enum(Func,b) - Calls function Func for all values
//* on the list. The function must follow
//* the Enum function interface.
//* b is a boolean value, if it is true
//* then the values will be enumerated
//* top to bottom, if false then bottom
//* to top.
//*****************************************************************
public function interface Enum takes integer value returns nothing
struct Stack
// Use a totally random number here, the more improbable someone uses it, the better.
// This is the value that is returned by .pop and .peek methods and .first and .random operators when called on an empty stack.
public static constant integer EMPTY=0x28829022
// End of calibration.
readonly integer size = 0
private integer top = 0
private static integer free = 1
private static integer array next
private static integer array value
method push takes integer i returns nothing
// Get an index from the list of free indexes.
local integer n=Stack.free
set Stack.free=Stack.next[n]
// Extend the list of free indexes if needed.
if Stack.free==0 then
set Stack.free=n+1
endif
// Store the value to the index.
set Stack.value[n]=i
// Add index to the top of the stack.
set Stack.next[n]=.top
set .top=n
set .size=.size+1
endmethod
method pop takes nothing returns integer
// Get the top index of stack.
local integer n=.top
// Safety check in case a user pops an empty stack.
if n==0 then
debug call BJDebugMsg("stack warning: .pop called on an empty stack!")
return Stack.EMPTY
endif
// Remove the top index from stack.
set .top=Stack.next[n]
set .size=.size-1
// Add the index to the list of free indexes.
set Stack.next[n]=Stack.free
set Stack.free=n
// Return the value.
return Stack.value[n]
endmethod
method peek takes nothing returns integer
// Read the value of the top index.
return Stack.value[.top]
endmethod
method add takes integer value returns nothing
call .push(value)
endmethod
method contains takes integer value returns boolean
// Get the first index of the list.
local integer i=.top
// Search through the list.
loop
// Stop the search when the end of the list is reached.
exitwhen i==0
// Stop the search if the value is found.
if Stack.value[i]==value then
return true
endif
// Get the next index of the list.
set i=Stack.next[i]
endloop
return false
endmethod
method count takes integer value returns integer
local integer count=0
// Get the first index of the list.
local integer i=.top
// Search through the list.
loop
// Stop the search when the end of the list is reached.
exitwhen i==0
// Increase the count if the value is found.
if Stack.value[i]==value then
set count=count+1
endif
// Get the next index of the list.
set i=Stack.next[i]
endloop
return count
endmethod
method operator first takes nothing returns integer
return .peek()
endmethod
method operator random takes nothing returns integer
local integer r=GetRandomInt(1,.size)
// Get the first index of the list.
local integer i=.top
// Loop through the list.
loop
// Stop the loop after a random amount of repeats.
set r=r-1
exitwhen r==0 or i==0
// Get the next index of the list.
set i=Stack.next[i]
endloop
return Stack.value[i]
endmethod
method remove takes integer value returns boolean
// Get the first index of the list.
local integer i1=.top
local integer i2
// Check if the first index holds the value.
if Stack.value[i1]==value then
call .pop()
return true
endif
// Search through the rest of the list.
loop
set i2=Stack.next[i1]
// Stop the search when the end of the list is reached.
exitwhen i2==0
// Check if the next index holds the value.
if Stack.value[i2]==value then
// Remove the index from the stack.
set Stack.next[i1]=Stack.next[i2]
// Add the removed index to the list of free indexes.
set Stack.next[i2]=Stack.free
set Stack.free=i2
set .size=.size-1
return true
endif
set i1=i2
endloop
return false
endmethod
method purge takes integer value returns integer
local integer count=0
local integer i1
local integer i2
// If the first index holds the value, pop it.
loop
// If the list is empty, return.
if .top==0 then
return count
endif
// Repeat until the first index doesn't hold the value.
exitwhen Stack.value[.top]!=value
call .pop()
set count=count+1
endloop
// Get the first index of the list.
set i1=.top
// Search through the rest of the list.
loop
set i2=Stack.next[i1]
// Stop the search when the end of the list is reached.
exitwhen i2==0
// Check if the next index holds the value.
if Stack.value[i2]==value then
// Remove the index from the stack.
set Stack.next[i1]=Stack.next[i2]
// Add the removed index to the list of free indexes.
set Stack.next[i2]=Stack.free
set Stack.free=i2
set .size=.size-1
set count=count+1
else
set i1=i2
endif
endloop
return count
endmethod
method enum takes Enum f, boolean top2bottom returns nothing
local integer array value
// Get the first index of the list.
local integer i1=.top
local integer i2=0
// Populate the array.
loop
exitwhen i1==0
set value[i2]=Stack.value[i1]
set i2=i2+1
set i1=Stack.next[i1]
endloop
// Call the Enum function for each value in the array.
set i1=i2-1
loop
exitwhen i2==0
set i2=i2-1
// Enumerate in which direction?
if top2bottom then
call f.evaluate(value[i1-i2])
else
call f.evaluate(value[i2])
endif
endloop
endmethod
method copy takes nothing returns Stack
local Stack that=Stack.create()
// Get the first index of the list.
local integer i1=.top
local integer i2
// Add a dummy index to the top of the new list.
call that.push(0)
set i2=that.top
loop
exitwhen i1==0
// Get an index from the list of free indexes and add it at the end of the list.
set Stack.next[i2]=Stack.free
set i2=Stack.next[i2]
set Stack.free=Stack.next[i2]
// Extend the list of free indexes if needed.
if Stack.free==0 then
set Stack.free=i2+1
endif
// Copy the value to the new index.
set Stack.value[i2]=Stack.value[i1]
set i1=Stack.next[i1]
endloop
// End the new list correctly.
set Stack.next[i2]=0
// Remove the dummy index.
call that.pop()
// Copy the size value to the new list.
set that.size=this.size
return that
endmethod
method onDestroy takes nothing returns nothing
local integer n
// Remove all remaining indexes from the stack.
loop
// Get the top index.
set n=.top
exitwhen n==0
// Remove it from the stack.
set .top=Stack.next[n]
// Add it to the list of free indexes.
set Stack.next[n]=Stack.free
set Stack.free=n
endloop
endmethod
static method onInit takes nothing returns nothing
// Store the EMPTY value to index 0 to make .peek inline friendly.
set Stack.value[0]=Stack.EMPTY
endmethod
endstruct
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library PolarProjection
//distance between X cord
function PolarProjectionX takes real x, real distance, real angle returns real
return x+distance*Cos(angle * bj_DEGTORAD)
endfunction
//distance between Y cord
function PolarProjectionY takes real y, real distance, real angle returns real
return y+distance*Sin(angle * bj_DEGTORAD)
endfunction
//distance between unit
function Unit_Distance takes unit A, unit B returns real
local real dx = GetUnitX(B) - GetUnitX(A)
local real dy = GetUnitY(B) - GetUnitY(A)
return SquareRoot(dx * dx + dy * dy)
endfunction
endlibrary
//TESH.scrollpos=84
//TESH.alwaysfold=0
/*
This is a compilation of libraries intended to replace the damage detection
and prevention engine [thread=102079]ADamage[/thread]. Originally, this was
intended to be merely the next version of ADamage, but some conceptual changes
such as moving from damage prevention to more general damage modification made
it difficult to maintain backwards compatibility, so I decided to make it a new
resource instead which could provide a more general, robust and modular damage
detection and modification framework than ADamage. Also, I know some people
will like that fact that I gave it a new name.
The resource is split into two libraries:
[list][*][b]DamageEvent[/b], a damage detection engine.
If you are already using another damage detection system in your map,
DamageEvent will interface with it instead of creating its own damage
detection triggers to avoid wasting performance. Currently supported damage
detection systems are [thread=100618]IDDS[/thread] and [thread=101865]LLDD[/thread].
*/
library DamageEvent initializer Init requires optional DamageModifiers, optional LightLeaklessDamageDetect, optional IntuitiveDamageSystem, optional xedamage
//=================================================================
//= DAMAGE EVENT LIBRARY
//=
//= written by: Anitarf
//= supports: -DamageModifiers
//=
//= This is a damage detection library designed to compensate for
//= the lack of a generic "unit takes damage" event in JASS.
//=
//= All functions following the Response function interface that
//= is defined at the end of the calibration section of this
//= library can be used to respond to damage events. Simply add
//= such functions to the system's call list with the
//= RegisterDamageResponse function.
//=
//= function RegisterDamageResponse takes Response r returns nothing
//=
//= DamageEvent fully supports the use of DamageModifiers. As
//= long as you have the DamageModifiers library in your map
//= DamageEvent will use it to modify the damage before calling
//= the response functions.
//=
//= If the map contains another damage detection library,
//= DamageEvent will interface with it instead of creating
//= it's own damage event triggers to improve performance.
//= Currently supported libraries are LightLeaklessDamageDetect
//= and IntuitiveDamageSystem.
//=
//= DamageEvent is also set up to automatically ignore dummy
//= damage events sometimes caused by xedamage when validating
//= targets (only works with xedamage 0.7 or higher).
//=================================================================
globals
// In wc3, damage events sometimes occur when no real damage is dealt,
// for example when some spells are cast that don't really deal damage,
// so this system will only consider damage events where damage is
// higher than this threshold value.
private constant real DAMAGE_THRESHOLD = 0.0
// The following calibration options are only used if the system uses
// it's own damage detection triggers instead of interfacing with other
// damage event engines:
// If this boolean is true, the damage detection trigger used by this
// system will be periodically destroyed and remade, thus getting rid
// of damage detection events for units that have decayed/been removed.
private constant boolean REFRESH_TRIGGER = true
// Each how many seconds should the trigger be refreshed?
private constant real TRIGGER_REFRESH_PERIOD = 300.0
endglobals
private function CanTakeDamage takes unit u returns boolean
// You can filter out which units need damage detection events with this function.
// For example, dummy casters will never take damage so the system doesn't need to register events for them,
// by filtering them out you are reducing the number of handles the game will create, thus increasing performance.
//return GetUnitTypeId(u)!='e000' // This is a sample return statement that lets you ignore a specific unit type.
return true
endfunction
// This function interface is included in the calibration section
// for user reference only and should not be changed in any way.
public function interface Response takes unit damagedUnit, unit damageSource, real damage returns nothing
// END OF CALIBRATION SECTION
// ================================================================
globals
private Response array responses
private integer responsesCount = 0
endglobals
function RegisterDamageResponse takes Response r returns nothing
set responses[responsesCount]=r
set responsesCount=responsesCount+1
endfunction
private function Damage takes nothing returns nothing
// Main damage event function.
local unit damaged=GetTriggerUnit()
local unit damager=GetEventDamageSource()
local real damage=GetEventDamage()
local integer i = 0
loop
exitwhen not (damage>DAMAGE_THRESHOLD)
static if LIBRARY_xedamage then
exitwhen xedamage.isDummyDamage
endif
static if LIBRARY_DamageModifiers then
set damage=RunDamageModifiers()
endif
loop
exitwhen i>=responsesCount
call responses[i].execute(damaged, damager, damage)
set i=i+1
endloop
exitwhen true
endloop
set damaged=null
set damager=null
endfunction
private function DamageC takes nothing returns boolean
call Damage() // Used to interface with LLDD.
return false
endfunction
// ================================================================
globals
private group g
private boolexpr b
private boolean clear
private trigger currentTrg
private triggeraction currentTrgA
private trigger oldTrg = null
private triggeraction oldTrgA = null
endglobals
private function TriggerRefreshEnum takes nothing returns nothing
// Code "borrowed" from Captain Griffen's GroupRefresh function.
// This clears the group of any "shadows" left by removed units.
if clear then
call GroupClear(g)
set clear = false
endif
call GroupAddUnit(g, GetEnumUnit())
// For units that are still in the game, add the event to the new trigger.
call TriggerRegisterUnitEvent( currentTrg, GetEnumUnit(), EVENT_UNIT_DAMAGED )
endfunction
private function TriggerRefresh takes nothing returns nothing
// The old trigger is destroyed with a delay for extra safety.
// If you get bugs despite this then turn off trigger refreshing.
if oldTrg!=null then
call TriggerRemoveAction(oldTrg, oldTrgA)
call DestroyTrigger(oldTrg)
endif
// The current trigger is prepared for delayed destruction.
call DisableTrigger(currentTrg)
set oldTrg=currentTrg
set oldTrgA=currentTrgA
// The current trigger is then replaced with a new trigger.
set currentTrg = CreateTrigger()
set currentTrgA = TriggerAddAction(currentTrg, function Damage)
set clear = true
call ForGroup(g, function TriggerRefreshEnum)
if clear then
call GroupClear(g)
endif
endfunction
// ================================================================
private function DamageRegister takes nothing returns boolean
local unit u = GetFilterUnit()
if CanTakeDamage(u) then
call TriggerRegisterUnitEvent( currentTrg, u, EVENT_UNIT_DAMAGED )
call GroupAddUnit(g, u)
endif
set u = null
return false
endfunction
private function Init takes nothing returns nothing
local rect rec
local region reg
local trigger t
static if LIBRARY_IntuitiveDamageSystem then
// IDDS initialization code
set t=CreateTrigger()
call TriggerAddAction(t, function Damage)
call TriggerRegisterDamageEvent(t, 0)
else
static if LIBRARY_LightLeaklessDamageDetect then
// LLDD initialization code
call AddOnDamageFunc(Condition(function DamageC))
else
// DamageEvent initialization code
set rec = GetWorldBounds()
set reg = CreateRegion()
set t = CreateTrigger()
call RegionAddRect(reg, rec)
call TriggerRegisterEnterRegion(t, reg, Condition(function DamageRegister))
set currentTrg = CreateTrigger()
set currentTrgA = TriggerAddAction(currentTrg, function Damage)
set g = CreateGroup()
call GroupEnumUnitsInRect(g, rec, Condition(function DamageRegister))
if REFRESH_TRIGGER then
call TimerStart(CreateTimer(), TRIGGER_REFRESH_PERIOD, true, function TriggerRefresh)
endif
call RemoveRect(rec)
set rec = null
set b = null
endif
endif
endfunction
endlibrary
/*
[*][b]DamageModifiers[/b], an optional addon that allows you to modify damage.
Two versions of DamageModifiers are provided, one that uses [thread=101246]Table[/thread]
and one that uses [thread=105643]AutoIndex[/thread].
DamageModifiers can use either [thread=101150]xepreload[/thread] or
[thread=105279]AbilityPreload[/thread] to preload the ability it uses.
*/
library DamageModifiers initializer Init requires Table, optional AbilityPreload, optional xepreload
//=================================================================
//= DAMAGE MODIFIERS LIBRARY (Table version)
//=
//= written by: Anitarf
//= requires: -Table
//=
//= This is a library that allows you to modify the damage taken
//= by units. It is the most robust system of this kind available,
//= it can both prevent and increase damage, when increasing
//= damage it still awards experience correctly, it can tolerate
//= users dealing additional damage on the damage event as well as
//= allow them to get the correct life of the unit on this event
//= rather than a value inflated by damage prevention.
//=
//= It is important to note that different damage modifying
//= systems can not operate in the same map, so you can not use
//= this system if you are already using a different one.
//=
//= Damage modifiers are implemented as structs that extend the
//= system's DamageModifier struct. A damage modifier is created
//= for a specific unit and can modify both the damage that the
//= unit deals and receives (using the onDamageDealt and
//= onDamageTaken methods respectively, see the interface defined
//= at the end of the calibration section).
//=
//= Keep in mind that when a struct extends DamageModifier, the
//= allocate method of that struct must match the create method of
//= DamageModifier:
//=
//= static method create takes unit u, integer priority returns DamageModifier
//=
//= The unit argument determines on which unit will the modifier
//= be applied and the priority argument determines the order in
//= which the modifiers will be evaluated. The system always
//= evaluates the modifiers on the unit dealing the damage and on
//= the unit taking the damage simultaneously, starting with the
//= modifiers with the highest priority. If two modifiers share
//= the same priority, the one on the unit dealing the damage
//= is evaluated first, if the two modifiers are on the same unit
//= then the NEW_MODIFIER_ON_TOP calibration constant determines
//= which gets evaluated first.
//=
//= Here is an example of a modifier struct:
//=
//= struct armour extends DamageModifier
//= // All additional struct members are optional:
//= static integer PRIORITY=0 // Default priority
//= real power // This lets us give different units differently strong armour.
//=
//= // create method is optional, if you don't declare one then you must use
//= // the .allocate parameters (unit, integer) when creating a modifier of this kind.
//= static method create takes unit u, real power returns armour
//= // Note the parameters for .allocate, this is because this struct extends
//= // DamageModifier which asks for these parameters in its create method:
//= local armour this = armour.allocate(u, armour.PRIORITY)
//= set this.power = power
//= return this
//= endmethod
//=
//= // This is the method that runs when damage is dealt to the unit with the modifier.
//= // The damage parameter tells how much damage got to this modifier past any modifiers
//= // with a higher priority that the unit may have.
//= // The value that the method returns tells the system by how much to modify the damage,
//= // a positive return value increases damage while a negative value reduces it.
//= method onDamageTaken takes unit damageSource, real damage returns real
//= // This is a simple modifier that just gives a flat damage reduction.
//= if this.power>damage then
//= return -damage
//= endif
//= return -this.power
//= endmethod
//= // There is no onDamageDealt method so this modifier does not affect the damage that the unit deals.
//= // onDestroy method is optional, in this case we don't need it.
//= endstruct
//=
//= In order for the system to modify damage, you need to call the
//= RunDamageModifiers function whenever a damage event occurs.
//= Since there is no generic "unit takes damage" event in JASS,
//= you need to use a damage detection system such as DamageEvent,
//= which is already designed to support DamageModifiers.
//=
//= function RunDamageModifiers takes nothing returns real
//=
//= The real value that the RunDamageModifiers function returns
//= equals what the damage is after it has been affected by all
//= modifiers.
//=
//= To be able to modify damage in situations where it exceeds the
//= max hp of the damaged unit, this system must temporarily add a
//= bonus hp ability to the unit. An external Objectmerger call is
//= provided in the calibration section that generates this
//= ability in a map automatically when the map is saved. The
//= ability will also get automatically preloaded to prevent
//= first-use lag as long as either the xepreload library or the
//= AbilityPreload library is present in the map.
//=
//= The library also provides the functions GetUnitLife and
//= SetUnitLife. When used outside damage detection, these
//= functions work the same as the natives GetWidgetLife and
//= SetWidgetLife, however when called on a unit that has damage
//= stacked on it these functions will return/modify the life the
//= unit will have after the damage resolves, rather than its
//= current life which may or may not be inflated by damage
//= prevention. Again, the functions are:
//=
//= function GetUnitLife takes unit u returns real
//= function SetUnitLife takes unit u, real newLife returns nothing
//=================================================================
globals
// If two modifiers have the same priority, which one should
// modify the damage first? If true, the newer modifier will.
private constant boolean NEW_MODIFIER_ON_TOP = true
// If more damage is dealt to the unit than the unit's max hp, then this
// system must temporarily add a bonus hp ability to the unit in order to
// facilitate damage prevention.
// An objectmerger call is provided below that automatically generates
// the ability when you save the map in the NewGen world editor.
private constant integer SURVIVAL_ABILITY = 'DMsa'
endglobals
// This objectmerger call only needs to run once to generate the ability in a map,
// just save the map to run it, then close the map and re-open it and the ability
// should be there, after that you can disable the objectmerget call to speed up
// the map saving process in the future.
// (To disable the call, delete the "!" so it turns from a command into a comment.)
// externalbjectMerger w3a AIl1 DMsa anam "LifeBonus" ansf "(DamageModifiers)" Ilif 1 100000 aite 0
// This interface is included in the calibration section
// for user reference only and should not be changed in any way.
// It is private because your shields shouldn't extend it directly,
// but should extend the DamageModifer struct instead.
private interface DamageModiferTemplate
// Returned real determines by how much the damage the unit deals should be modified.
method onDamageDealt takes unit damagedUnit, real damage returns real defaults 0.0
// Returned real determines by how much the damage the unit is dealt should be modified.
method onDamageTaken takes unit damageSource, real damage returns real defaults 0.0
endinterface
// END OF CALIBRATION SECTION
// ================================================================
private keyword Evaluate
struct DamageModifier extends DamageModiferTemplate
private unit target
private DamageModifier next=0
integer priority
private static HandleTable first
private method listInsert takes nothing returns nothing
local DamageModifier dm=DamageModifier(DamageModifier.first[.target])
if dm==0 or dm.priority<.priority or (NEW_MODIFIER_ON_TOP and dm.priority==.priority) then
set .next=DamageModifier(DamageModifier.first[.target])
set DamageModifier.first[.target]=integer(this)
else
loop
exitwhen dm.next == 0 or dm.next.priority<.priority or (NEW_MODIFIER_ON_TOP and dm.next.priority==.priority)
set dm=dm.next
endloop
set .next=dm.next
set dm.next=this
endif
endmethod
private method listRemove takes nothing returns nothing
local DamageModifier dm=DamageModifier(DamageModifier.first[.target])
if dm==this then
set DamageModifier.first[.target]=integer(.next)
else
loop
exitwhen dm.next==this
set dm=dm.next
endloop
set dm.next=.next
endif
endmethod
static method create takes unit u, integer priority returns DamageModifier
local DamageModifier this
if u==null then
return 0
endif
set this=DamageModifier.allocate()
set .target=u
set .priority = priority
call .listInsert()
return this
endmethod
method onDestroy takes nothing returns nothing
call .listRemove()
if DamageModifier.first[.target]==0 then
call DamageModifier.first.flush(.target)
endif
endmethod
static method onInit takes nothing returns nothing
set DamageModifier.first=HandleTable.create()
endmethod
static method Evaluate takes unit damaged, unit damager, real damage returns real
// Loops through all the modifiers involved in a damage event and
// returns the total amount by which the damage must be modified.
// Positive values meaning a damage increase and negative a damage decrease.
local real modifier=0.0
local DamageModifier this=DamageModifier(DamageModifier.first[damager])
local DamageModifier that=DamageModifier(DamageModifier.first[damaged])
loop
exitwhen this==0 and that==0
if this!=0 and (that==0 or this.priority>=that.priority) then
set modifier=modifier+this.onDamageDealt(damaged, damage+modifier)
set this=this.next
elseif that !=0 then
set modifier=modifier+that.onDamageTaken(damager, damage+modifier)
set that=that.next
endif
endloop
return modifier
endmethod
endstruct
// ================================================================
private struct Damage
unit u
real hp=0.0
real dmg=0.0
real temp=0.0
private Damage next
private static Damage first=0
private static timer delay=CreateTimer()
private static method get takes unit u returns Damage
// In most cases, only one or very few units will be damaged
// at the same time, so a linear O(n) search is acceptable.
local Damage this=Damage.first
loop
exitwhen this==0 or .u==u
set this=.next
endloop
return this
endmethod
private static method create takes unit u returns Damage
// The only time a Damage is created is when it doesn't yet exist for the unit.
local Damage this=Damage.allocate()
set .u = u
set .next = Damage.first
set Damage.first=this
return this
endmethod
method onDestroy takes nothing returns nothing
// The only time a Damage is destroyed is when it is first, so this works.
set Damage.first=.next
set .u = null
endmethod
private static method end takes nothing returns nothing
// This method is called with a very slight delay, which allows it to run
// after the life of the damaged unit has already been reduced by the damage.
loop
exitwhen Damage.first==0
call UnitRemoveAbility(Damage.first.u, SURVIVAL_ABILITY)
call SetWidgetLife(Damage.first.u, Damage.first.hp)
call Damage.first.destroy()
endloop
endmethod
static method RunModifiers takes nothing returns real
local unit damaged = GetTriggerUnit()
local unit damager = GetEventDamageSource()
local real damage = GetEventDamage()
local real modifier = 0.0
local real life = GetWidgetLife(damaged)
local real maxlife = GetUnitState(damaged,UNIT_STATE_MAX_LIFE)
local Damage d
if damaged==null then
// Method not called from a damage event, terminate.
set damager=null
set damaged=null
return 0.0
endif
// Calculate by how much to modify the damage.
set modifier=DamageModifier.Evaluate(damaged, damager, damage)
// Modify the damage.
set d=Damage.get(damaged)
if d==0 then
// First damage, create a damage struct.
set d=Damage.create(damaged)
set d.hp=life
call TimerStart(Damage.delay, 0.0, false, function Damage.end)
endif
// Update the damage struct.
set d.hp=d.hp-(damage+modifier)
set d.dmg=d.dmg+damage
// If the unit's life couldn't be reduced or increased as much as we wanted
// on a previous damage event, it's time we compensate for that now.
set modifier=modifier+d.temp
set d.temp=0.0
if maxlife-d.dmg<=0.405 then
// Enough damage stacked to potentially kill the unit, need a survival ability.
call UnitAddAbility(damaged, SURVIVAL_ABILITY)
set maxlife = GetUnitState(damaged,UNIT_STATE_MAX_LIFE)
endif
if life-modifier>0.405 then
// Set the unit's life so that after the damage is dealt, it will be correct.
call SetWidgetLife(damaged, life-modifier)
if life-modifier>maxlife then
// The unit's maxlife is preventing us from increasing life as needed,
// we need to remember that in the case the survival ability is ever added
// to the unit on another damage event before this damage resolves.
set d.temp=modifier-(life-maxlife)
endif
else
// If we have a damage modifier value that would kill the unit,
// we instead reduce the unit's hp so the actual damage can kill it,
// thus awarding proper bounty.
call SetWidgetLife(damaged, 0.406)
// We need to remember that we failed to reduce the life as needed in case
// damage is dealt in response to this damage and modified into the negative.
set d.temp=modifier-(life-0.406)
endif
set damaged=null
set damager=null
return damage+modifier
endmethod
static method getLife takes unit u returns real
local Damage d=Damage.get(u)
if d==0 then
return GetWidgetLife(u)
endif
return d.hp
endmethod
static method setLife takes unit u, real newLife returns nothing
local Damage d=Damage.get(u)
local real modifier
local real delta
if d==0 then
// Unit not taking damage, proceed with setting the unit's life.
call SetWidgetLife(u, newLife)
else
// Calculate by how much the unit's life will change after the damage.
set modifier=newLife-d.hp
if modifier<0 or d.hp>0 then
// Nothing to worry about, the rest of the code will handle everything.
set d.hp=newLife
set d.temp=d.temp-modifier
else
// Increasing life in a situation where the damage is about to kill the unit,
// so we must intervene and increase the unit's actual life as well.
set d.hp=newLife
set delta=GetUnitState(u,UNIT_STATE_MAX_LIFE)-GetWidgetLife(u)
if delta>modifier then
call SetWidgetLife(u, GetWidgetLife(u)+modifier)
else
call SetWidgetLife(u, GetWidgetLife(u)+delta)
set d.temp=d.temp-(modifier-delta)
endif
endif
endif
endmethod
endstruct
private function Init takes nothing returns nothing
static if LIBRARY_xepreload then
call XE_PreloadAbility(SURVIVAL_ABILITY)
else
static if LIBRARY_AbilityPreload then
call AbilityPreload(SURVIVAL_ABILITY)
endif
endif
endfunction
// ================================================================
// To make this system work, call this function once whenever a damage event occurs.
function RunDamageModifiers takes nothing returns real
return Damage.RunModifiers()
endfunction
// These functions allow you to get/set the proper life of a unit even when it's taking damage.
function GetUnitLife takes unit u returns real
return Damage.getLife(u)
endfunction
function SetUnitLife takes unit u, real newLife returns nothing
call Damage.setLife(u, newLife)
endfunction
endlibrary
/*
library DamageModifiers initializer Init requires AutoIndex, optional AbilityPreload, optional xepreload
//=================================================================
//= DAMAGE MODIFIERS LIBRARY (AutoIndex version)
//=
//= written by: Anitarf
//= requires: -AutoIndex
//=
//= This is a library that allows you to modify the damage taken
//= by units. It is the most robust system of this kind available,
//= it can both prevent and increase damage, when increasing
//= damage it still awards experience correctly, it can tolerate
//= users dealing additional damage on the damage event as well as
//= allow them to get the correct life of the unit on this event
//= rather than a value inflated by damage prevention.
//=
//= It is important to note that different damage modifying
//= systems can not operate in the same map, so you can not use
//= this system if you are already using a different one.
//=
//= Damage modifiers are implemented as structs that extend the
//= system's DamageModifier struct. A damage modifier is created
//= for a specific unit and can modify both the damage that the
//= unit deals and receives (using the onDamageDealt and
//= onDamageTaken methods respectively, see the interface defined
//= at the end of the calibration section).
//=
//= Keep in mind that when a struct extends DamageModifier, the
//= allocate method of that struct must match the create method of
//= DamageModifier:
//=
//= static method create takes unit u, integer priority returns DamageModifier
//=
//= The unit argument determines on which unit will the modifier
//= be applied and the priority argument determines the order in
//= which the modifiers will be evaluated. The system always
//= evaluates the modifiers on the unit dealing the damage and on
//= the unit taking the damage simultaneously, starting with the
//= modifiers with the highest priority. If two modifiers share
//= the same priority, the one on the unit dealing the damage
//= is evaluated first, if the two modifiers are on the same unit
//= then the NEW_MODIFIER_ON_TOP calibration constant determines
//= which gets evaluated first.
//=
//= Here is an example of a modifier struct:
//=
//= struct armour extends DamageModifier
//= // All additional struct members are optional:
//= static integer PRIORITY=0 // Default priority
//= real power // This lets us give different units differently strong armour.
//=
//= // create method is optional, if you don't declare one then you must use
//= // the .allocate parameters (unit, integer) when creating a modifier of this kind.
//= static method create takes unit u, real power returns armour
//= // Note the parameters for .allocate, this is because this struct extends
//= // DamageModifier which asks for these parameters in it's create method:
//= local armour this = armour.allocate(u, armour.PRIORITY)
//= set this.power = power
//= return this
//= endmethod
//=
//= // This is the method that runs when damage is dealt to the unit with the modifier.
//= // The damage parameter tells how much damage got to this modifier past any modifiers
//= // with a higher priority that the unit may have.
//= // The value that the method returns tells the system by how much to modify the damage,
//= // a positive return value increases damage while a negative value reduces it.
//= method onDamageTaken takes unit damageSource, real damage returns real
//= // This is a simple modifier that just gives a flat damage reduction.
//= if this.power>damage then
//= return -damage
//= endif
//= return -this.power
//= endmethod
//= // There is no onDamageDealt method so this modifier does not affect the damage that the unit deals.
//= // onDestroy method is optional, in this case we don't need it.
//= endstruct
//=
//= In order for the system to modify damage, you need to call the
//= RunDamageModifiers function whenever a damage event occurs.
//= Since there is no generic "unit takes damage" event in JASS,
//= you need to use a damage detection system such as DamageEvent,
//= which is already designed to support DamageModifiers.
//=
//= function RunDamageModifiers takes nothing returns real
//=
//= The real value that the RunDamageModifiers function returns
//= equals what the damage is after it has been affected by all
//= modifiers.
//=
//= To be able to modify damage in situations where it exceeds the
//= max hp of the damaged unit, this system must temporarily add a
//= bonus hp ability to the unit. An external Objectmerger call is
//= provided in the calibration section that generates this
//= ability in a map automatically when the map is saved. The
//= ability will also get automatically preloaded to prevent
//= first-use lag as long as either the xepreload library or the
//= AbilityPreload library is present in the map.
//=
//= The library also provides the functions GetUnitLife and
//= SetUnitLife. When used outside damage detection, these
//= functions work the same as the natives GetWidgetLife and
//= SetWidgetLife, however when called on a unit that has damage
//= stacked on it these functions will return/modify the life the
//= unit will have after the damage resolves, rather than its
//= current life which may or may not be inflated by damage
//= prevention. Again, the functions are:
//=
//= function GetUnitLife takes unit u returns real
//= function SetUnitLife takes unit u, real newLife returns nothing
//=
//= In this AutoIndex version of this library, damage modifiers
//= can not be added to units that are not indexed, also, if such
//= units take damage, damage modifiers will not be evaluated.
//=================================================================
globals
// If two modifiers have the same priority, which one should
// modify the damage first? If true, the newer modifier will.
private constant boolean NEW_MODIFIER_ON_TOP = true
// If more damage is dealt to the unit than the unit's max hp, then this
// system must temporarily add a bonus hp ability to the unit in order to
// facilitate damage prevention.
// An objectmerger call is provided below that automatically generates
// the ability when you save the map in the NewGen world editor.
private constant integer SURVIVAL_ABILITY = 'DMsa'
endglobals
// This objectmerger call only needs to run once to generate the ability in a map,
// just save the map to run it, then close the map and re-open it and the ability
// should be there, after that you can disable the objectmerget call to speed up
// the map saving process in the future.
// (To disable the call, delete the "!" so it turns from a command into a comment.)
//// external ObjectMerger w3a AIl1 DMsa anam "LifeBonus" ansf "(DamageModifiers)" Ilif 1 100000 aite 0
// This interface is included in the calibration section
// for user reference only and should not be changed in any way.
// It is private because your shields shouldn't extend it directly,
// but should extend the DamageModifer struct instead.
private interface DamageModiferTemplate
// Returned real determines by how much the damage the unit deals should be modified.
method onDamageDealt takes unit damagedUnit, real damage returns real defaults 0.0
// Returned real determines by how much the damage the unit is dealt should be modified.
method onDamageTaken takes unit damageSource, real damage returns real defaults 0.0
endinterface
// END OF CALIBRATION SECTION
// ================================================================
private keyword Evaluate
struct DamageModifier extends DamageModiferTemplate
private unit target
private DamageModifier next=0
integer priority
private static DamageModifier array first
private method listInsert takes nothing returns nothing
local DamageModifier dm=DamageModifier.first[GetUnitId(.target)]
if dm==0 or dm.priority<.priority or (NEW_MODIFIER_ON_TOP and dm.priority==.priority) then
set .next=DamageModifier.first[GetUnitId(.target)]
set DamageModifier.first[GetUnitId(.target)]=this
else
loop
exitwhen dm.next == 0 or dm.next.priority<.priority or (NEW_MODIFIER_ON_TOP and dm.next.priority==.priority)
set dm=dm.next
endloop
set .next=dm.next
set dm.next=this
endif
endmethod
private method listRemove takes nothing returns nothing
local DamageModifier dm=DamageModifier.first[GetUnitId(.target)]
if dm==this then
set DamageModifier.first[GetUnitId(.target)]=.next
else
loop
exitwhen dm.next==this
set dm=dm.next
endloop
set dm.next=.next
endif
endmethod
static method create takes unit u, integer priority returns DamageModifier
local DamageModifier this
if u==null or not(IsUnitIndexed(u)) then
return 0
endif
set this=DamageModifier.allocate()
set .target=u
set .priority = priority
call .listInsert()
return this
endmethod
method onDestroy takes nothing returns nothing
call .listRemove()
endmethod
static method Evaluate takes unit damaged, unit damager, real damage returns real
// Loops through all the modifiers involved in a damage event and
// returns the total amount by which the damage must be modified.
// Positive values meaning a damage increase and negative a damage decrease.
local real modifier=0.0
local DamageModifier this=0
local DamageModifier that=DamageModifier(DamageModifier.first[GetUnitId(damaged)])
if IsUnitIndexed(damager) then
set this=DamageModifier(DamageModifier.first[GetUnitId(damager)])
endif
loop
exitwhen this==0 and that==0
if this!=0 and (that==0 or this.priority>=that.priority) then
set modifier=modifier+this.onDamageDealt(damaged, damage+modifier)
set this=this.next
elseif that !=0 then
set modifier=modifier+that.onDamageTaken(damager, damage+modifier)
set that=that.next
endif
endloop
return modifier
endmethod
endstruct
// ================================================================
private struct Damage
unit u
real hp=0.0
real dmg=0.0
real temp=0.0
private Damage next
private static Damage first=0
private static timer delay=CreateTimer()
private static Damage array unitlist
private static method get takes unit u returns Damage
return Damage.unitlist[GetUnitId(u)]
endmethod
private static method create takes unit u returns Damage
// The only time a Damage is created is when it doesn't yet exist for the unit.
local Damage this=Damage.allocate()
set .u = u
set .next = Damage.first
set Damage.first=this
set Damage.unitlist[GetUnitId(.u)]=this
return this
endmethod
method onDestroy takes nothing returns nothing
// The only time a Damage is destroyed is when it is first, so this works.
set Damage.first=.next
set Damage.unitlist[GetUnitId(.u)]=0
set .u = null
endmethod
private static method end takes nothing returns nothing
// This method is called with a very slight delay, which allows it to run
// after the life of the damaged unit has already been reduced by the damage.
loop
exitwhen Damage.first==0
call UnitRemoveAbility(Damage.first.u, SURVIVAL_ABILITY)
call SetWidgetLife(Damage.first.u, Damage.first.hp)
call Damage.first.destroy()
endloop
endmethod
static method RunModifiers takes nothing returns real
local unit damaged = GetTriggerUnit()
local unit damager = GetEventDamageSource()
local real damage = GetEventDamage()
local real modifier = 0.0
local real life = GetWidgetLife(damaged)
local real maxlife = GetUnitState(damaged,UNIT_STATE_MAX_LIFE)
local Damage d
if damaged==null or not(IsUnitIndexed(damaged)) then
// Method not called from a damage event or damage event on an unindexed unit, terminate.
set damager=null
set damaged=null
return 0.0
endif
// Calculate by how much to modify the damage.
set modifier=DamageModifier.Evaluate(damaged, damager, damage)
// Modify the damage.
set d=Damage.get(damaged)
if d==0 then
// First damage, create a damage struct.
set d=Damage.create(damaged)
set d.hp=life
call TimerStart(Damage.delay, 0.0, false, function Damage.end)
endif
// Update the damage struct.
set d.hp=d.hp-(damage+modifier)
set d.dmg=d.dmg+damage
// If the unit's life couldn't be reduced or increased as much as we wanted
// on a previous damage event, it's time we compensate for that now.
set modifier=modifier+d.temp
set d.temp=0.0
if maxlife-d.dmg<=0.405 then
// Enough damage stacked to potentially kill the unit, need a survival ability.
call UnitAddAbility(damaged, SURVIVAL_ABILITY)
set maxlife = GetUnitState(damaged,UNIT_STATE_MAX_LIFE)
endif
if life-modifier>0.405 then
// Set the unit's life so that after the damage is dealt, it will be correct.
call SetWidgetLife(damaged, life-modifier)
if life-modifier>maxlife then
// The unit's maxlife is preventing us from increasing life as needed,
// we need to remember that in the case the survival ability is ever added
// to the unit on another damage event before this damage resolves.
set d.temp=modifier-(life-maxlife)
endif
else
// If we have a damage modifier value that would kill the unit,
// we instead reduce the unit's hp so the actual damage can kill it,
// thus awarding proper bounty.
call SetWidgetLife(damaged, 0.406)
// We need to remember that we failed to reduce the life as needed in case
// damage is dealt in response to this damage and modified into the negative.
set d.temp=modifier-(life-0.406)
endif
set damaged=null
set damager=null
return damage+modifier
endmethod
static method getLife takes unit u returns real
local Damage d=0
if IsUnitIndexed(u) then
set d=Damage.get(u)
endif
if d==0 then
return GetWidgetLife(u)
endif
return d.hp
endmethod
static method setLife takes unit u, real newLife returns nothing
local Damage d=0
local real modifier
local real delta
if IsUnitIndexed(u) then
set d=Damage.get(u)
endif
if d==0 then
// Unit not taking damage, proceed with setting the unit's life.
call SetWidgetLife(u, newLife)
else
// Calculate by how much the unit's life will change after the damage.
set modifier=newLife-d.hp
if modifier<0 or d.hp>0 then
// Nothing to worry about, the rest of the code will handle everything.
set d.hp=newLife
set d.temp=d.temp-modifier
else
// Increasing life in a situation where the damage is about to kill the unit,
// so we must intervene and increase the unit's actual life as well.
set d.hp=newLife
set delta=GetUnitState(u,UNIT_STATE_MAX_LIFE)-GetWidgetLife(u)
if delta>modifier then
call SetWidgetLife(u, GetWidgetLife(u)+modifier)
else
call SetWidgetLife(u, GetWidgetLife(u)+delta)
set d.temp=d.temp-(modifier-delta)
endif
endif
endif
endmethod
endstruct
private function Init takes nothing returns nothing
static if LIBRARY_xepreload then
call XE_PreloadAbility(SURVIVAL_ABILITY)
else
static if LIBRARY_AbilityPreload then
call AbilityPreload(SURVIVAL_ABILITY)
endif
endif
endfunction
// ================================================================
// To make this system work, call this function once whenever a damage event occurs.
function RunDamageModifiers takes nothing returns real
return Damage.RunModifiers()
endfunction
// These functions allow you to get/set the proper life of a unit even when it's taking damage.
function GetUnitLife takes unit u returns real
return Damage.getLife(u)
endfunction
function SetUnitLife takes unit u, real newLife returns nothing
call Damage.setLife(u, newLife)
endfunction
endlibrary
*/
//TESH.scrollpos=30
//TESH.alwaysfold=0
library GetNearest
//===========================================================================
// Information:
//==============
//
// GetNearest is a very simple library that allows you to find the nearest
// unit, destructable, or item to a specified point within a specified radius.
// You can use a boolexpr filter as well, just like native Enum functions.
//
//===========================================================================
// GetNearest API:
//=================
//
// GetNearestUnit(x, y, radius, boolexpr) -> unit
// This function returns the closest unit to the specified x/y point within
// the specified radius matching the specified boolexpr filter. This function
// will consider any unit whose collision radius overlaps the specified radius.
//
// GetNearestDestructable(x, y, radius, boolexpr) -> destructable
// This function returns the closest destructable to the specified x/y point
// within the specified radius matching the specified boolexpr filter.
//
// GetNearestItem(x, y, radius, boolexpr) -> item
// This function returns the closest itemto the specified x/y point within
// the specified radius matching the specified boolexpr filter.
//
//===========================================================================
globals
private real MAX_VALUE = 10000000000. //Larger than any squared distance value.
endglobals
//===========================================================================
private function GetNearestUnit_Sub takes unit nearest, real x, real y, real radius, boolexpr b returns unit
local group g = CreateGroup()
local real nearestdist = MAX_VALUE
local real dist
local unit u
call GroupEnumUnitsInRange(g, x, y, radius, b)
// call GroupEnumUnitsInArea(g, x, y, radius, b) //This function takes collision radius into account.
loop //Iterate through the units in the group and find the closest to the specified point.
set u = FirstOfGroup(g)
exitwhen u == null
set dist = Pow(x - GetUnitX(u), 2.) + Pow(y - GetUnitY(u), 2.)
if dist < nearestdist then
set nearestdist = dist
set nearest = u
endif
call GroupRemoveUnit(g, u)
endloop
call DestroyGroup(g)
return nearest //Local handle parameters are automatically nulled.
endfunction
function GetNearestUnit takes real x, real y, real radius, boolexpr b returns unit
return GetNearestUnit_Sub(null, x, y, radius, b) //Avoids leaking a reference to the returned unit.
endfunction
//===========================================================================
globals
private rect enumrect = Rect(0., 0., 0., 0.)
private real nearestdist = MAX_VALUE
private real enumradius = 0.
private real enumx = 0.
private real enumy = 0.
endglobals
//! textmacro GetNearestFunc takes NAME, TYPE
globals
private $TYPE$ nearest$TYPE$ = null
endglobals
private function GetNearest$NAME$_Enum takes nothing returns nothing
local real dist = Pow(enumx - Get$NAME$X(GetEnum$NAME$()), 2.) + Pow(enumy - Get$NAME$Y(GetEnum$NAME$()), 2.)
if dist <= enumradius and dist < nearestdist then
set nearest$TYPE$ = GetEnum$NAME$()
set nearestdist = dist
endif
endfunction //Enumerate through the dests/items and find the closest one to the point.
private function GetNearest$NAME$_Sub takes $TYPE$ nearest, real x, real y, real radius, boolexpr b returns $TYPE$
local $TYPE$ old_nearest$TYPE$ = nearest$TYPE$ //Cache the previous values of the globals.
local real old_nearestdist = nearestdist
local real old_enumradius = enumradius
local real old_enumx = enumx
local real old_enumy = enumy
set nearest$TYPE$ = null //Assign new values to the globals for this call.
set nearestdist = MAX_VALUE
set enumradius = radius*radius
set enumx = x
set enumy = y
call SetRect(enumrect, x - radius, y - radius, x + radius, y + radius) //Adjust the rect to cover the radius.
call Enum$NAME$sInRect(enumrect, b, function GetNearest$NAME$_Enum) //Find the closest dest/item to the point.
set nearest = nearest$TYPE$ //Assign the closest dest/item to a local variable.
set nearest$TYPE$ = old_nearest$TYPE$ //Restore the previous values for the globals for nested enumerations.
set nearestdist = old_nearestdist
set enumradius = old_enumradius
set enumx = old_enumx
set enumy = old_enumy
return nearest //Local handle parameters are automatically nulled.
endfunction
function GetNearest$NAME$ takes real x, real y, real radius, boolexpr b returns $TYPE$
return GetNearest$NAME$_Sub(null, x, y, radius, b) //Avoids leaking a reference to the returned unit.
endfunction
//! endtextmacro
//! runtextmacro GetNearestFunc("Destructable", "destructable")
//! runtextmacro GetNearestFunc("Item", "item")
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 = 'e000' // 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
/*
struct chasernotplaying extends array
private trigger t
private integer utype
private static method Callback takes nothing returns boolean
local thistype this=KT_GetData()
call this.AIDS_removeLock()
return true
endmethod
private static method OnDeath takes nothing returns boolean
local thistype this=thistype[GetTriggerUnit()]
call this.AIDS_addLock() // So the struct can't disappear before the timer ticks.
call KT_Add(function thistype.Callback,this,5.0)
call DestroyTrigger(this.t)
set this.t=null
return false
endmethod
private static method AIDS_filter takes unit u returns boolean
return GetUnitTypeId(u)=='nfnp'
endmethod
private method AIDS_onCreate takes nothing returns nothing
set this.utype=GetUnitTypeId(this.unit)
set this.t=CreateTrigger()
call TriggerAddCondition(this.t,Condition(function thistype.OnDeath))
SetDestructableLife takes destructable d, real life returns nothing
constant widgetevent EVENT_WIDGET_DEATH=ConvertWidgetEvent(89)
call TriggerRegisterUnitEvent(this.t,this.unit, EVENT_UNIT_DEATH )
endmethod
endstruct
scope revivetrees*/
//TESH.scrollpos=44
//TESH.alwaysfold=0
library TerrainPathability initializer Init
//******************************************************************************
//* BY: Rising_Dusk
//*
//* This script can be used to detect the type of pathing at a specific point.
//* It is valuable to do it this way because the IsTerrainPathable is very
//* counterintuitive and returns in odd ways and aren't always as you would
//* expect. This library, however, facilitates detecting those things reliably
//* and easily.
//*
//******************************************************************************
//*
//* > function IsTerrainDeepWater takes real x, real y returns boolean
//* > function IsTerrainShallowWater takes real x, real y returns boolean
//* > function IsTerrainLand takes real x, real y returns boolean
//* > function IsTerrainPlatform takes real x, real y returns boolean
//* > function IsTerrainWalkable takes real x, real y returns boolean
//*
//* These functions return true if the given point is of the type specified
//* in the function's name and false if it is not. For the IsTerrainWalkable
//* function, the MAX_RANGE constant below is the maximum deviation range from
//* the supplied coordinates that will still return true.
//*
//* The IsTerrainPlatform works for any preplaced walkable destructable. It will
//* return true over bridges, destructable ramps, elevators, and invisible
//* platforms. Walkable destructables created at runtime do not create the same
//* pathing hole as preplaced ones do, so this will return false for them. All
//* other functions except IsTerrainWalkable return false for platforms, because
//* the platform itself erases their pathing when the map is saved.
//*
//* After calling IsTerrainWalkable(x, y), the following two global variables
//* gain meaning. They return the X and Y coordinates of the nearest walkable
//* point to the specified coordinates. These will only deviate from the
//* IsTerrainWalkable function arguments if the function returned false.
//*
//* Variables that can be used from the library:
//* [real] TerrainPathability_X
//* [real] TerrainPathability_Y
//*
globals
private constant real MAX_RANGE = 10.
private constant integer DUMMY_ITEM_ID = 'wolg'
endglobals
globals
private item Item = null
private rect Find = null
private item array Hid
private integer HidMax = 0
public real X = 0.
public real Y = 0.
endglobals
function IsTerrainDeepWater takes real x, real y returns boolean
return not IsTerrainPathable(x, y, PATHING_TYPE_FLOATABILITY) and IsTerrainPathable(x, y, PATHING_TYPE_WALKABILITY)
endfunction
function IsTerrainShallowWater takes real x, real y returns boolean
return not IsTerrainPathable(x, y, PATHING_TYPE_FLOATABILITY) and not IsTerrainPathable(x, y, PATHING_TYPE_WALKABILITY) and IsTerrainPathable(x, y, PATHING_TYPE_BUILDABILITY)
endfunction
function IsTerrainLand takes real x, real y returns boolean
return IsTerrainPathable(x, y, PATHING_TYPE_FLOATABILITY)
endfunction
function IsTerrainPlatform takes real x, real y returns boolean
return not IsTerrainPathable(x, y, PATHING_TYPE_FLOATABILITY) and not IsTerrainPathable(x, y, PATHING_TYPE_WALKABILITY) and not IsTerrainPathable(x, y, PATHING_TYPE_BUILDABILITY)
endfunction
private function HideItem takes nothing returns nothing
if IsItemVisible(GetEnumItem()) then
set Hid[HidMax] = GetEnumItem()
call SetItemVisible(Hid[HidMax], false)
set HidMax = HidMax + 1
endif
endfunction
function IsTerrainWalkable takes real x, real y returns boolean
//Hide any items in the area to avoid conflicts with our item
call MoveRectTo(Find, x, y)
call EnumItemsInRect(Find ,null, function HideItem)
//Try to move the test item and get its coords
call SetItemPosition(Item, x, y) //Unhides the item
set X = GetItemX(Item)
set Y = GetItemY(Item)
static if LIBRARY_IsTerrainWalkable then
//This is for compatibility with the IsTerrainWalkable library
set IsTerrainWalkable_X = X
set IsTerrainWalkable_Y = Y
endif
call SetItemVisible(Item, false)//Hide it again
//Unhide any items hidden at the start
loop
exitwhen HidMax <= 0
set HidMax = HidMax - 1
call SetItemVisible(Hid[HidMax], true)
set Hid[HidMax] = null
endloop
//Return walkability
return (X-x)*(X-x)+(Y-y)*(Y-y) <= MAX_RANGE*MAX_RANGE and not IsTerrainPathable(x, y, PATHING_TYPE_WALKABILITY)
endfunction
private function Init takes nothing returns nothing
set Find = Rect(0., 0., 128., 128.)
set Item = CreateItem(DUMMY_ITEM_ID, 0, 0)
call SetItemVisible(Item, false)
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
// ==================================================================================
// LineSegment - by Ammorth, April 13, 2009 - rev4
// Please give credit when using this in your map
// This, and myself, can be found at wc3c.net
//
// Used to determine certain things about segments, involving units and geometry.
//
// Requirements:
// - vJass
// - xebasic - by Vexorian ( [url]http://www.wc3c.net/showthread.php?t=101150[/url] )
//
// Installation:
// - Create a new trigger called LineSegments and convert it to custom text
// - Copy the code to the new trigger, replacing everything
//
// How To Use:
// - call GetNearestPointOnSegment(Ax, Ay, Bx, By, Cx, Cy) to get the nearest point on
// the line segment AB to point C (returns a location)
// - call GetDistanceFromSegment(Ax, Ay, Bx, By, Cx, Cy) to get the distance from the
// line segment AB to a given point C
// - call GroupEnumUnitsInRangeOfSegment(whichgroup, Ax, Ay, Bx, By, distance, filter) to
// add all of the units within distance of segment AB to whichgroup, according to the filter.
// - call IsUnitInRangeOfSegment(unit, Ax, Ay, Bx, By, distance) to see if the unit givin is
// within distance of segment AB
//
// Notes:
// - All functions have a location version wrappers incase you would rather pass locations
// They are named as follows:
// > GetNearestPointOnSegmentLoc()
// > GetDistanceFromSegmentLoc()
// > GroupEnumUnitsInRangeOfSegmentLoc()
// > IsUnitInRangeOfSegmentLoc()
//
// ==================================================================================
library LineSegment requires xebasic
globals
private group udg_LineTempGroup = CreateGroup()
endglobals
// Script By Ammorth, requires xebasic by Vexorian
// A and B are endpoints of line segments, C is point to test.
// Aka: Function returns closest point to C on line segment AB.
// This is true for all functions in this library.
// Ax is the x cord of A and Ay is the y cord of A; this is the same for all other points.
//
// GetNearestPointOnSeg
//
function GetNearestPointOnSeg takes real Ax, real Ay, real Bx, real By, real Cx, real Cy returns location
local real r
local real L
// could use a point struct here if you really wanted, instead of a location.
local real dx = Bx-Ax
local real dy = By-Ay
set L = ((dx)*(dx) + (dy)*(dy)) // Get quasi length
if L == 0 then // seg is actually a point so lets return the point
return Location(Ax, Ay)
endif
set r = ((Cx-Ax)*(dx) + (Cy-Ay)*(dy))/(L) // get the ratio
if r > 1 then // closests point is past seg, so return end point B
return Location(Bx, By)
elseif r < 0 then // same as B, but at A instead
return Location(Ax, Ay)
endif // In the middle of A and B so use the ratio to find the point
return Location(Ax+r*(dx), Ay+r*(dy))
endfunction
function GetNearestPointOnSegLoc takes location A, location B, location C returns location
return GetNearestPointOnSeg(GetLocationX(A), GetLocationY(A), GetLocationX(B), GetLocationY(B), GetLocationX(C), GetLocationY(C))
endfunction
//
// GetDistanceFromSeg
//
function GetDistanceFromSeg takes real Ax, real Ay, real Bx, real By, real Cx, real Cy returns real
local real r
local real L
local real dx = Bx-Ax
local real dy = By-Ay
set L = ((dx)*(dx) + (dy)*(dy)) // Get quasi length
if L == 0 then // seg is actually a point so lets return the distance to the point
return SquareRoot((Cx-Ax)*(Cx-Ax)+(Cy-Ay)*(Cy-Ay))
endif
set r = ((Cx-Ax)*(dx) + (Cy-Ay)*(dy))/(L) // get the ratio
if r > 1 then // closests point is past seg, so return distance to point B
return SquareRoot((Cx-Bx)*(Cx-Bx)+(Cy-By)*(Cy-By))
elseif r < 0 then // same as B, but at A instead
return SquareRoot((Cx-Ax)*(Cx-Ax)+(Cy-Ay)*(Cy-Ay))
endif // In the middle of A and B so use the ratio to find the point
set Ax = Ax+r*(dx)
set Ay = Ay+r*(dy)
return SquareRoot((Cx-Ax)*(Cx-Ax)+(Cy-Ay)*(Cy-Ay))
endfunction
function GetDistanceFromSegLoc takes location A, location B, location C returns real
return GetDistanceFromSeg(GetLocationX(A), GetLocationY(A), GetLocationX(B), GetLocationY(B), GetLocationX(C), GetLocationY(C))
endfunction
//
// GetEnumUnitsInRangeOfSeg
//
function GroupEnumUnitsInRangeOfSeg takes group whichgroup, real Ax, real Ay, real Bx, real By, real distance, boolexpr filter returns nothing
local real dx = Bx-Ax
local real dy = By-Ay
local real L
local real r = SquareRoot(dx*dx+dy*dy)/2+distance + XE_MAX_COLLISION_SIZE // double-purpose for r
local unit u
call GroupClear(udg_LineTempGroup)
call GroupEnumUnitsInRange(udg_LineTempGroup, Ax+(dx/2), Ay+(dy/2), r, filter)
loop
set u = FirstOfGroup(udg_LineTempGroup)
exitwhen u == null
set L = ((dx)*(dx) + (dy)*(dy)) // Get quasi length
if L == 0 and IsUnitInRangeXY(u, Ax, Ay, distance) then // seg is actually a point so lets return the point
call GroupAddUnit(whichgroup, u)
else
set r = ((GetUnitX(u)-Ax)*(dx) + (GetUnitY(u)-Ay)*(dy))/(L) // get the ratio
if r > 1 then // split if/thens so that it exists properly
if IsUnitInRangeXY(u, Bx, By, distance) then // closests point is past seg, so return end point B
call GroupAddUnit(whichgroup, u)
endif
elseif r < 0 then
if IsUnitInRangeXY(u, Ax, Ay, distance) then // same as B, but at A instead
call GroupAddUnit(whichgroup, u)
endif
elseif IsUnitInRangeXY(u, Ax+r*(dx), Ay+r*(dy), distance) then // In the middle of A and B so use the ratio to find the point
call GroupAddUnit(whichgroup, u)
endif
endif
call GroupRemoveUnit(udg_LineTempGroup, u)
endloop
set u = null
endfunction
function GetNearestPointOnSegment takes real Ax, real Ay, real Bx, real By, real Cx, real Cy returns location
local real r
// could use a point struct here if you really wanted, instead of a location.
local real dx = Bx-Ax
local real dy = By-Ay
local real L = ((dx)*(dx) + (dy)*(dy)) // Get quasi length
if L == 0 then // seg is actually a point so lets return the point
return Location(Ax, Ay)
endif
set r = ((Cx-Ax)*(dx) + (Cy-Ay)*(dy))/(L) // get the ratio
if r > 1 then // closests point is past seg, so return end point B
return Location(Bx, By)
elseif r < 0 then // same as B, but at A instead
return Location(Ax, Ay)
endif // In the middle of A and B so use the ratio to find the point
return Location(Ax+r*(dx), Ay+r*(dy))
endfunction
function GetNearestPointOnSegmentLoc takes location A, location B, location C returns location
return GetNearestPointOnSegment(GetLocationX(A), GetLocationY(A), GetLocationX(B), GetLocationY(B), GetLocationX(C), GetLocationY(C))
endfunction
function GetDistanceFromSegment takes real Ax, real Ay, real Bx, real By, real Cx, real Cy returns real
local real r
local real dx = Bx-Ax
local real dy = By-Ay
local real L = ((dx)*(dx) + (dy)*(dy)) // Get quasi length
if L == 0 then // seg is actually a point so lets return the distance to the point
return SquareRoot((Cx-Ax)*(Cx-Ax)+(Cy-Ay)*(Cy-Ay))
endif
set r = ((Cx-Ax)*(dx) + (Cy-Ay)*(dy))/(L) // get the ratio
if r > 1 then // closests point is past seg, so return distance to point B
return SquareRoot((Cx-Bx)*(Cx-Bx)+(Cy-By)*(Cy-By))
elseif r < 0 then // same as B, but at A instead
return SquareRoot((Cx-Ax)*(Cx-Ax)+(Cy-Ay)*(Cy-Ay))
endif // In the middle of A and B so use the ratio to find the point
set Ax = Ax+r*(dx)
set Ay = Ay+r*(dy)
return SquareRoot((Cx-Ax)*(Cx-Ax)+(Cy-Ay)*(Cy-Ay))
endfunction
function GetDistanceFromSegmentLoc takes location A, location B, location C returns real
return GetDistanceFromSegment(GetLocationX(A), GetLocationY(A), GetLocationX(B), GetLocationY(B), GetLocationX(C), GetLocationY(C))
endfunction
function GroupEnumUnitsInRangeOfSegment takes group whichgroup, real Ax, real Ay, real Bx, real By, real distance, boolexpr filter returns nothing
local real dx = Bx-Ax
local real dy = By-Ay
local real L = ((dx)*(dx) + (dy)*(dy)) // Get quasi length
local real r = SquareRoot(dx*dx+dy*dy)/2+distance + XE_MAX_COLLISION_SIZE // double-purpose for r
local unit u
call GroupClear(udg_LineTempGroup)
call GroupEnumUnitsInRange(udg_LineTempGroup, Ax+(dx/2), Ay+(dy/2), r, filter)
loop
set u = FirstOfGroup(udg_LineTempGroup)
exitwhen u == null
if L == 0 and IsUnitInRangeXY(u, Ax, Ay, distance) then // seg is actually a point so lets return the point
call GroupAddUnit(whichgroup, u)
else
set r = ((GetUnitX(u)-Ax)*(dx) + (GetUnitY(u)-Ay)*(dy))/(L) // get the ratio
if r > 1 then // split if/thens so that it exists properly
if IsUnitInRangeXY(u, Bx, By, distance) then // closests point is past seg, so return end point B
call GroupAddUnit(whichgroup, u)
endif
elseif r < 0 then
if IsUnitInRangeXY(u, Ax, Ay, distance) then // same as B, but at A instead
call GroupAddUnit(whichgroup, u)
endif
elseif IsUnitInRangeXY(u, Ax+r*(dx), Ay+r*(dy), distance) then // In the middle of A and B so use the ratio to find the point
call GroupAddUnit(whichgroup, u)
endif
endif
call GroupRemoveUnit(udg_LineTempGroup, u)
endloop
set u = null
endfunction
function GroupEnumUnitsInRangeOfSegmentLoc takes group whichgroup, location A, location B, real distance, boolexpr filter returns nothing
call GroupEnumUnitsInRangeOfSegment(whichgroup, GetLocationX(A), GetLocationY(A), GetLocationX(B), GetLocationY(B), distance, filter)
endfunction
function IsUnitInRangeOfSegment takes unit u, real Ax, real Ay, real Bx, real By, real distance returns boolean
local real r
local real dx = Bx-Ax
local real dy = By-Ay
local real L = ((dx)*(dx) + (dy)*(dy)) // Get quasi length
if L == 0 then // seg is actually a point so lets return the point
return IsUnitInRangeXY(u, Ax, Ay, distance)
endif
set r = ((GetUnitX(u)-Ax)*(dx) + (GetUnitY(u)-Ay)*(dy))/(L) // get the ratio
if r > 1 then // closests point is past seg, so return end point B
return IsUnitInRangeXY(u, Bx, By, distance)
elseif r < 0 then // same as B, but at A instead
return IsUnitInRangeXY(u, Ax, Ay, distance)
endif // In the middle of A and B so use the ratio to find the point
return IsUnitInRangeXY(u, Ax+r*(dx), Ay+r*(dy), distance)
endfunction
function IsUnitInRangeOfSegmentLoc takes unit u, location A, location B, real distance returns boolean
return IsUnitInRangeOfSegment(u, GetLocationX(A), GetLocationY(A), GetLocationX(B), GetLocationY(B), distance)
endfunction
endlibrary
function IsUnitInRangeOfSegXY takes unit u, real Ax, real Ay, real Bx, real By, real distance returns boolean
local real r
local real L
local real dx = Bx-Ax
local real dy = By-Ay
set L = ((dx)*(dx) + (dy)*(dy)) // Get quasi length
if L == 0 then // seg is actually a point so lets return the point
return IsUnitInRangeXY(u, Ax, Ay, distance)
endif
set r = ((GetUnitX(u)-Ax)*(dx) + (GetUnitY(u)-Ay)*(dy))/(L) // get the ratio
if r > 1 then // closests point is past seg, so return end point B
return IsUnitInRangeXY(u, Bx, By, distance)
elseif r < 0 then // same as B, but at A instead
return IsUnitInRangeXY(u, Ax, Ay, distance)
endif // In the middle of A and B so use the ratio to find the point
return IsUnitInRangeXY(u, Ax+r*(dx), Ay+r*(dy), distance)
endfunction
function GroupEnumUnitsInRangeOfSegLoc takes group whichgroup, location A, location B, real distance, boolexpr filter returns nothing
call GroupEnumUnitsInRangeOfSeg(whichgroup, GetLocationX(A), GetLocationY(A), GetLocationX(B), GetLocationY(B), distance, filter)
endfunction
function IsUnitInRangeOfSegLoc takes unit u, location A, location B, real distance returns boolean
return IsUnitInRangeOfSegXY(u, GetLocationX(A), GetLocationY(A), GetLocationX(B), GetLocationY(B), distance)
endfunction
//TESH.scrollpos=0
//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 = 'e000'
constant integer XE_HEIGHT_ENABLER = 'Amrf'
constant integer XE_TREE_RECOGNITION = 'Aeat'
constant real XE_ANIMATION_PERIOD = 0.025
constant real XE_MAX_COLLISION_SIZE = 197.0
endglobals
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library xedamage initializer init requires xebasic
//************************************************************************
// xedamage 0.8
// --------
// For all your damage and targetting needs.
//
//************************************************************************
//===========================================================================================================
globals
private constant integer MAX_SUB_OPTIONS = 3
private constant real FACTOR_TEST_DAMAGE = 0.01
// a low damage to do on units to test their damage factors for specific
// attacktype/damagetype combos.
// You'll need something as high as 20.0 to make it work well with armor resistances.
// (Yes, you read it correctly, 20 ...
//
// If you use such a large value, there may be conflicts with some things relying on damage
// (ie they are not away of the isDummyDamage tag that xedamage posseses.) which may be quite bad if you think about it...
// then maybe it is better to change it to 0.01 ... then will work fine, just fine - but it will generally ignore armor -
// I am leaving it as 0.01 by default, because honestly, I'd rather make it ignore armor than have a lot of people sending me
// rants about how they detect 20.0 damage where none is visible...
private constant real MAX_DAMAGE_FACTOR = 3.00
// The maximum damage factor in the map. I think 3 is fine.
//=======================================================
private constant real EPSILON = 0.000000001
private unit dmger
private constant integer MAX_SPACE = 8190 // MAX_SPACE/MAX_SUB_OPTIONS is the instance limit for xedamage, usually big enough...
endglobals
private keyword structInit
struct xedamage[MAX_SPACE]
//----
// fields and methods for a xedamage object, they aid determining valid targets and special
// damage factor conditions.
//
// Notice the default values.
//
boolean damageSelf = false // the damage and factor methods usually have a source unit parameter
// xedamage would consider this unit as immune unless you set damageSelf to true
boolean damageAllies = false // Alliance dependent target options.
boolean damageEnemies = true // *
boolean damageNeutral = true // *
boolean ranged = true // Is the attack ranged? This has some effect on the AI of the affected units
// true by default, you may not really need to modify this.
boolean visibleOnly = false // Should only units that are visible for source unit's owner be affected?
boolean deadOnly = false // Should only corpses be affected by "the damage"? (useful when using xedamage as a target selector)
boolean alsoDead = false // Should even corpses and alive units be considered?
boolean damageTrees = true //Also damage destructables? Notice this is used only in certain methods.
//AOE for example targets a circle, so it can affect the destructables
//in that circle, a custom spell using xedamage for targetting configuration
//could also have an if-then-else implemented so it can verify if it is true
//then affect trees manually.
//
// Damage type stuff:
// .dtype : the "damagetype" , determines if the spell is physical, magical or ultimate.
// .atype : the "attacktype" , deals with armor.
// .wtype : the "weapontype" , determines the sound effect to be played when damage is done.
//
// Please use common.j/blizzard.j/ some guide to know what damage/attack/weapon types can be used
//
damagetype dtype = DAMAGE_TYPE_UNIVERSAL
attacktype atype = ATTACK_TYPE_MAGIC
weapontype wtype = WEAPON_TYPE_WHOKNOWS
//
// Damage type 'tag' people might use xedamage.isInUse() to detect xedamage usage, there are other static
// variables like xedamage.CurrentDamageType and xedamage.CurrentDamageTag. The tag allows you to specify
// a custom id for the damage type ** Notice the tag would aid you for some spell stuff, for example,
// you can use it in a way similar to Rising_Dusk's damage system.
//
integer tag = 0
//
// if true, forceDamage will make xedamage ignore dtype and atype and try as hard as possible to deal 100%
// damage.
boolean forceDamage = false
//
// Ally factor! Certain spells probably have double purposes and heal allies while harming enemies. This
// field allows you to do such thing.
//
real allyfactor = 1.0
//
// field: .exception = SOME_UNIT_TYPE
// This field adds an exception unittype (classification), if the unit belongs to this unittype it will
// be ignored.
//
method operator exception= takes unittype ut returns nothing
set this.use_ex=true
set this.ex_ut=ut
endmethod
//
// field: .required = SOME_UNIT_TYPE
// This field adds a required unittype (classification), if the unit does not belong to this unittype
// it will be ignored.
//
method operator required= takes unittype ut returns nothing
set this.use_req=true
set this.req_ut=ut
endmethod
private boolean use_ex = false
private unittype ex_ut = null
private boolean use_req = false
private unittype req_ut = null
private unittype array fct[MAX_SUB_OPTIONS]
private real array fc[MAX_SUB_OPTIONS]
private integer fcn=0
//
// method .factor(SOME_UNIT_TYPE, factor)
// You might call factor() if you wish to specify a special damage factor for a certain classification,
// for example call d.factor(UNIT_TYPE_STRUCTURE, 0.5) makes xedamage do half damage to structures.
//
method factor takes unittype ut, real fc returns nothing
if(this.fcn==MAX_SUB_OPTIONS) then
debug call BJDebugMsg("In one instance of xedamage, you are doing too much calls to factor(), please increase MAX_SUB_OPTIONS to allow more, or cut the number of factor() calls")
return
endif
set this.fct[this.fcn] = ut
set this.fc[this.fcn] = fc
set this.fcn = this.fcn+1
endmethod
private integer array abifct[MAX_SUB_OPTIONS]
private real array abifc[MAX_SUB_OPTIONS]
private integer abifcn=0
//
// method .abilityFactor('abil', factor)
// You might call abilityFactor() if you wish to specify a special damage factor for units that have a
// certain ability/buff.
// for example call d.abilityFactor('A000', 1.5 ) makes units that have the A000 ability take 50% more
// damage than usual.
//
method abilityFactor takes integer abilityId, real fc returns nothing
if(this.abifcn==MAX_SUB_OPTIONS) then
debug call BJDebugMsg("In one instance of xedamage, you are doing too much calls to abilityFactor(), please increase MAX_SUB_OPTIONS to allow more, or cut the number of abilityFactor() calls")
return
endif
set this.abifct[this.abifcn] = abilityId
set this.abifc[this.abifcn] = fc
set this.abifcn = this.abifcn+1
endmethod
private boolean usefx = false
private string fxpath
private string fxattach
//
// method .useSpecialEffect("effect\\path.mdl", "origin")
// Makes it add (and destroy) an effect when damage is performed.
//
method useSpecialEffect takes string path, string attach returns nothing
set this.usefx = true
set this.fxpath=path
set this.fxattach=attach
endmethod
//********************************************************************
//* Now, the usage stuff:
//*
//================================================================================
// static method xedamage.isInUse() will return true during a unit damaged
// event in case this damage was caused by xedamage, in this case, you can
// read variables like CurrentDamageType, CurrentAttackType and CurrentDamageTag
// to be able to recognize what sort of damage was done.
//
readonly static damagetype CurrentDamageType=null
readonly static attacktype CurrentAttackType=null
readonly static integer CurrentDamageTag =0
private static integer inUse = 0
static method isInUse takes nothing returns boolean
return (inUse>0) //inline friendly.
endmethod
readonly static boolean isDummyDamage = false
//========================================================================================================
// This function calculates the damage factor caused by a certain attack and damage
// type, it is static : xedamage.getDamageTypeFactor(someunit, ATTAcK_TYPE_NORMAL, DAMAGE_TYPE_FIRE, 100)
//
static method getDamageTypeFactor takes unit u, attacktype a, damagetype d returns real
local real hp=GetWidgetLife(u)
local real mana=GetUnitState(u,UNIT_STATE_MANA)
local real r
local real fc = FACTOR_TEST_DAMAGE
//Since a unit is in that point, we don't need checks.
call SetUnitX(dmger,GetUnitX(u))
call SetUnitY(dmger,GetUnitY(u))
call SetUnitOwner(dmger,GetOwningPlayer(u),false)
set r=hp
if (hp< FACTOR_TEST_DAMAGE*MAX_DAMAGE_FACTOR) then
call SetWidgetLife(u, hp + FACTOR_TEST_DAMAGE*MAX_DAMAGE_FACTOR )
set r = hp + FACTOR_TEST_DAMAGE*MAX_DAMAGE_FACTOR
set fc = GetWidgetLife(u)-hp + EPSILON
endif
set isDummyDamage = true
call UnitDamageTarget(dmger,u, fc ,false,false,a,d,null)
static if DEBUG_MODE then
if IsUnitType(u, UNIT_TYPE_DEAD) and (hp>0.405) then
call BJDebugMsg("xedamage: For some reason, the unit being tested by getDamageTypeFactor has died. Verify MAX_DAMAGE_FACTOR is set to a correct value. ")
endif
endif
set isDummyDamage = false
call SetUnitOwner(dmger,Player(15),false)
if (mana>GetUnitState(u,UNIT_STATE_MANA)) then
//Unit had mana shield, return 1 and restore mana too.
call SetUnitState(u,UNIT_STATE_MANA,mana)
set r=1
else
set r= (r-GetWidgetLife(u)) / fc
endif
call SetWidgetLife(u,hp)
return r
endmethod
private method getTargetFactorCore takes unit source, unit target, boolean usetypes returns real
local player p=GetOwningPlayer(source)
local boolean allied=IsUnitAlly(target,p)
local boolean enemy =IsUnitEnemy(target,p)
local boolean neutral=allied
local real f
local real negf=1.0
local integer i
if(this.damageAllies != this.damageNeutral) then
set neutral= allied and not (GetPlayerAlliance(GetOwningPlayer(target),p, ALLIANCE_HELP_REQUEST ))
//I thought accuracy was not as important as speed , I think that REQUEST is false is enough to consider
// it neutral.
//set neutral= allied and not (GetPlayerAlliance(GetOwningPlayer(target),p, ALLIANCE_HELP_RESPONSE ))
//set neutral= allied and not (GetPlayerAlliance(GetOwningPlayer(target),p, ALLIANCE_SHARED_XP ))
//set neutral= allied and not (GetPlayerAlliance(GetOwningPlayer(target),p, ALLIANCE_SHARED_SPELLS ))
set allied= allied and not(neutral)
endif
if (not this.damageAllies) and allied then
return 0.0
elseif (not this.damageEnemies) and enemy then
return 0.0
elseif( (not this.damageSelf) and (source==target) ) then
return 0.0
elseif (not this.damageNeutral) and neutral then
return 0.0
elseif( this.use_ex and IsUnitType(target, this.ex_ut) ) then
return 0.0
elseif( this.visibleOnly and not IsUnitVisible(target,p) ) then
return 0.0
elseif ( this.deadOnly and not IsUnitType(target,UNIT_TYPE_DEAD) ) then
return 0.0
elseif ( not(this.alsoDead) and IsUnitType(target,UNIT_TYPE_DEAD) ) then
return 0.0
endif
set f=1.0
if ( IsUnitAlly(target,p) ) then
set f=f*this.allyfactor
if(f<=-EPSILON) then
set f=-f
set negf=-1.0
endif
endif
if (this.use_req and not IsUnitType(target,this.req_ut)) then
return 0.0
endif
set i=.fcn-1
loop
exitwhen (i<0)
if( IsUnitType(target, this.fct[i] ) ) then
set f=f*this.fc[i]
if(f<=-EPSILON) then
set f=-f
set negf=-1.0
endif
endif
set i=i-1
endloop
set i=.abifcn-1
loop
exitwhen (i<0)
if( GetUnitAbilityLevel(target,this.abifct[i] )>0 ) then
set f=f*this.abifc[i]
if(f<=-EPSILON) then
set f=-f
set negf=-1.0
endif
endif
set i=i-1
endloop
set f=f*negf
if ( f<EPSILON) and (f>-EPSILON) then
return 0.0
endif
if( this.forceDamage or not usetypes ) then
return f
endif
set f=f*xedamage.getDamageTypeFactor(target,this.atype,this.dtype)
if ( f<EPSILON) and (f>-EPSILON) then
return 0.0
endif
return f
endmethod
//====================================================================
// With this you might decide if a unit is a valid target for a spell.
//
method getTargetFactor takes unit source, unit target returns real
return this.getTargetFactorCore(source,target,true)
endmethod
//======================================================================
// a little better, I guess
//
method allowedTarget takes unit source, unit target returns boolean
return (this.getTargetFactorCore(source,target,false)!=0.0)
endmethod
//=======================================================================
// performs damage to the target unit, for unit 'source'.
//
method damageTarget takes unit source, unit target, real damage returns boolean
local damagetype dt=.CurrentDamageType
local attacktype at=.CurrentAttackType
local integer tg=.CurrentDamageTag
local real f = this.getTargetFactorCore(source,target,false)
local real pl
if(f!=0.0) then
set .CurrentDamageType = .dtype
set .CurrentAttackType = .atype
set .CurrentDamageTag = .tag
set .inUse = .inUse +1
set pl=GetWidgetLife(target)
call UnitDamageTarget(source,target, f*damage, true, .ranged, .atype, .dtype, .wtype )
set .inUse = .inUse -1
set .CurrentDamageTag = tg
set .CurrentDamageType = dt
set .CurrentAttackType = at
if(pl != GetWidgetLife(target) ) then
if(usefx) then
call DestroyEffect( AddSpecialEffectTarget(this.fxpath, target, this.fxattach) )
endif
return true
endif
endif
return false
endmethod
//=======================================================================================
// The same as damageTarget, but it forces a specific damage value, good if you already
// know the target.
//
method damageTargetForceValue takes unit source, unit target, real damage returns nothing
local damagetype dt=.CurrentDamageType
local attacktype at=.CurrentAttackType
local integer tg=.CurrentDamageTag
set .CurrentDamageType = .dtype
set .CurrentAttackType = .atype
set .CurrentDamageTag = .tag
if( usefx) then
call DestroyEffect( AddSpecialEffectTarget(this.fxpath, target, this.fxattach) )
endif
set .inUse = .inUse +1
call UnitDamageTarget(source,target, damage, true, .ranged, null, null, .wtype )
set .inUse = .inUse -1
set .CurrentDamageTag = tg
set .CurrentDamageType = dt
set .CurrentAttackType = at
endmethod
//=====================================================================================
// Notice: this will not Destroy the group, but it will certainly empty the group.
//
method damageGroup takes unit source, group targetGroup, real damage returns integer
local damagetype dt=.CurrentDamageType
local attacktype at=.CurrentAttackType
local integer tg=.CurrentDamageTag
local unit target
local real f
local integer count=0
local real hp
set .CurrentDamageType = .dtype
set .CurrentAttackType = .atype
set .CurrentDamageTag = .tag
set .inUse = .inUse +1
loop
set target=FirstOfGroup(targetGroup)
exitwhen (target==null)
call GroupRemoveUnit(targetGroup,target)
set f= this.getTargetFactorCore(source,target,false)
if (f!=0.0) then
set count=count+1
if(usefx) then
set hp = GetWidgetLife(target)
endif
call UnitDamageTarget(source,target, f*damage, true, .ranged, .atype, .dtype, .wtype )
if(usefx and (hp > GetWidgetLife(target)) ) then
call DestroyEffect( AddSpecialEffectTarget(this.fxpath, target, this.fxattach) )
endif
endif
endloop
set .inUse = .inUse -1
set .CurrentDamageTag=tg
set .CurrentDamageType = dt
set .CurrentAttackType = at
return count
endmethod
private static xedamage instance
private integer countAOE
private unit sourceAOE
private real AOEx
private real AOEy
private real AOEradius
private real AOEdamage
private static boolexpr filterAOE
private static boolexpr filterDestAOE
private static group enumgroup
private static rect AOERect
private static method damageAOE_Enum takes nothing returns boolean
local unit target=GetFilterUnit()
local xedamage this=.instance //adopting a instance.
local real f
local real hp
if( not IsUnitInRangeXY(target,.AOEx, .AOEy, .AOEradius) ) then
set target=null
return false
endif
set f=.getTargetFactorCore(.sourceAOE, target, false)
if(f!=0.0) then
set .countAOE=.countAOE+1
if(this.usefx) then
set hp =GetWidgetLife(target)
endif
call UnitDamageTarget(.sourceAOE,target, f*this.AOEdamage, true, .ranged, .atype, .dtype, .wtype )
if(this.usefx and (hp > GetWidgetLife(target) ) ) then
call DestroyEffect( AddSpecialEffectTarget(this.fxpath, target, this.fxattach) )
endif
endif
set .instance= this //better restore, nesting IS possible!
set target=null
return false
endmethod
private static method damageAOE_DestructablesEnum takes nothing returns boolean
local destructable target=GetFilterDestructable()
local xedamage this=.instance //adopting a instance.
local real dx=.AOEx-GetDestructableX(target)
local real dy=.AOEy-GetDestructableY(target)
if( dx*dx + dy*dy >= .AOEradius+EPSILON ) then
set target=null
return false
endif
set .countAOE=.countAOE+1
if(.usefx) then
call DestroyEffect( AddSpecialEffectTarget(this.fxpath, target, this.fxattach) )
endif
call UnitDamageTarget(.sourceAOE,target, this.AOEdamage, true, .ranged, .atype, .dtype, .wtype )
set .instance= this //better restore, nesting IS possible!
set target=null
return false
endmethod
//==========================================================================================
// will affect trees if damageTrees is true!
//
method damageAOE takes unit source, real x, real y, real radius, real damage returns integer
local damagetype dt=.CurrentDamageType
local attacktype at=.CurrentAttackType
local integer tg=.CurrentDamageTag
set .CurrentDamageType = .dtype
set .CurrentAttackType = .atype
set .CurrentDamageTag = .tag
set .inUse = .inUse +1
set .instance=this
set .countAOE=0
set .sourceAOE=source
set .AOEx=x
set .AOEradius=radius
set .AOEy=y
set .AOEdamage=damage
call GroupEnumUnitsInRange(.enumgroup,x,y,radius+XE_MAX_COLLISION_SIZE, .filterAOE)
if(.damageTrees) then
call SetRect(.AOERect, x-radius, y-radius, x+radius, y+radius)
set .AOEradius=.AOEradius*.AOEradius
call EnumDestructablesInRect(.AOERect, .filterDestAOE, null)
endif
set .inUse = .inUse -1
set .CurrentDamageTag = tg
set .CurrentDamageType = dt
set .CurrentAttackType = at
return .countAOE
endmethod
method damageAOELoc takes unit source, location loc, real radius, real damage returns integer
return .damageAOE(source, GetLocationX(loc), GetLocationY(loc), radius, damage)
endmethod
//==========================================================================================
// only affects trees, ignores damageTrees
//
method damageDestructablesAOE takes unit source, real x, real y, real radius, real damage returns integer
set .instance=this
set .countAOE=0
set .sourceAOE=source
set .AOEx=x
set .AOEradius=radius*radius
set .AOEy=y
set .AOEdamage=damage
//if(.damageTrees) then
call SetRect(.AOERect, x-radius, y-radius, x+radius, y+radius)
call EnumDestructablesInRect(.AOERect, .filterDestAOE, null)
//endif
return .countAOE
endmethod
method damageDestructablesAOELoc takes unit source, location loc, real radius, real damage returns integer
return .damageDestructablesAOE(source,GetLocationX(loc), GetLocationY(loc), radius, damage)
endmethod
//'friend' with the library init
static method structInit takes nothing returns nothing
set .AOERect= Rect(0,0,0,0)
set .filterAOE= Condition(function xedamage.damageAOE_Enum)
set .filterDestAOE = Condition( function xedamage.damageAOE_DestructablesEnum)
set .enumgroup = CreateGroup()
endmethod
endstruct
private function init takes nothing returns nothing
set dmger=CreateUnit(Player(15), XE_DUMMY_UNITID , 400,-400,0.)
call UnitAddAbility(dmger,'Aloc')
call xedamage.structInit()
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library BoundSentinel initializer init
//*************************************************
//* BoundSentinel
//* -------------
//* Don't leave your units unsupervised, naughty
//* them may try to get out of the map bounds and
//* crash your game.
//*
//* To implement, just get a vJass compiler and
//* copy this library/trigger to your map.
//*
//*************************************************
//==================================================
//=========================================================================================
globals
private constant boolean ALLOW_OUTSIDE_PLAYABLE_MAP_AREA = false
private real maxx
private real maxy
private real minx
private real miny
endglobals
//=======================================================================
private function dis takes nothing returns boolean
local unit u=GetTriggerUnit()
local real x=GetUnitX(u)
local real y=GetUnitY(u)
if(x>maxx) then
set x=maxx
elseif(x<minx) then
set x=minx
endif
if(y>maxy) then
set y=maxy
elseif(y<miny) then
set y=miny
endif
call SetUnitX(u,x)
call SetUnitY(u,y)
set u=null
return false
endfunction
private function init takes nothing returns nothing
local trigger t = CreateTrigger()
local region r = CreateRegion()
local rect map
local rect rc
if ALLOW_OUTSIDE_PLAYABLE_MAP_AREA then
set map = GetWorldBounds()
else
set map = bj_mapInitialPlayableArea
endif
set minx = GetRectMinX(map)
set miny = GetRectMinY(map)
set maxx = GetRectMaxX(map)
set maxy = GetRectMaxY(map)
set rc=Rect(minx,miny,maxx,maxy)
call RegionAddRect(r, rc)
call RemoveRect(rc)
if ALLOW_OUTSIDE_PLAYABLE_MAP_AREA then
call RemoveRect(map)
endif
call TriggerRegisterLeaveRegion(t,r, null)
call TriggerAddCondition(t, Condition(function dis))
set rc=null
set map = null
endfunction
endlibrary
//TESH.scrollpos=-1
//TESH.alwaysfold=0
library xepreload initializer init requires xebasic, optional TimerUtils
//******************************************************************************
// xepreload 0.8
// ---------
// 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
private keyword DebugIdInteger2IdString
//inline friendly (when debug mode is off..)
function XE_PreloadAbility takes integer abilid returns nothing
call UnitAddAbility(dum, abilid)
/*static if DEBUG_MODE then
if(dum==null) then
call BJDebugMsg("XE_PreloadAbility: do not load abilities after map init or during structs' onInit")
elseif GetUnitAbilityLevel(dum, abilid) == 0 then
call BJDebugMsg("XE_PreloadAbility: Ability "+DebugIdInteger2IdString.evaluate(abilid)+" does not exist.")
endif
endif*/
endfunction
// ................................................................................
//================================================================================
// Convert a integer id value into a 4-letter id code.
// * Taken from cheats.j so I don't have to code it again.
// * Used only on debug so making a whole library for it seemed silly
// * Private so people don't begin using xepreload just to call this function....
// * It will not work correctly if you paste this code in the custom script section
// due to the infamous % bug. Then again, if you do that then you probably
// deserve it....
//
private function DebugIdInteger2IdString takes integer value returns string
local string charMap = ".................................!.#$%&'()*+,-./0123456789:;<=>.@ABCDEFGHIJKLMNOPQRSTUVWXYZ[.]^_`abcdefghijklmnopqrstuvwxyz{|}~................................................................................................................................."
local string result = ""
local integer remainingValue = value
local integer charValue
local integer byteno
set byteno = 0
loop
set charValue = ModuloInteger(remainingValue, 256)
set remainingValue = remainingValue / 256
set result = SubString(charMap, charValue, charValue + 1) + result
set byteno = byteno + 1
exitwhen byteno == 4
endloop
return result
endfunction
//--------------------------------
private function kill takes nothing returns nothing
call RemoveUnit(dum)
set dum=null
// static if (LIBRARY_TimerUtils ) then
call ReleaseTimer( GetExpiredTimer() )
// else
// call DestroyTimer(GetExpiredTimer())
//endif
endfunction
private function init takes nothing returns nothing
local timer t
set dum = CreateUnit( Player(15), XE_DUMMY_UNITID, 400,-400,0)
if( dum == null) then
debug call BJDebugMsg("xePreload : XE_DUMMY_UNITID ("+DebugIdInteger2IdString.evaluate(XE_DUMMY_UNITID)+") not added correctly to the map.")
endif
//static if (LIBRARY_TimerUtils) then
set t=NewTimer()
// else
set t=CreateTimer()
// endif
call TimerStart(t,0.0,false,function kill)
set t=null
endfunction
endlibrary
//TESH.scrollpos=-1
//TESH.alwaysfold=0
library xecast initializer init requires xebasic
//******************************************************************************
// xecast 0.7
// ------
// Because dummy casters REALLY ARE this complicated!
//
//******************************************************************************
//==============================================================================
globals
private constant integer MAXINSTANCES = 8190
//this is a lot, unless you leak xecast objects (which is a bad thing)
private constant integer INITIAL_DUMMY_COUNT = 12
//don't allow to keep more than DUMMY_STACK_LIMIT innactive dummy units :
private constant integer DUMMY_STACK_LIMIT = 50
// 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.
private constant boolean FORCE_INVISIBLE_CAST = false
//When AUTO_RESET_MANA_COOLDOWN is set to true, xecast will reset
// the dummy unit's cooldown and mana before casting every spell.
// it is a performance penalty, so if you are sure that all dummy spells
// in your map got 0 mana and cooldown cost, you may set it to false.
private constant boolean AUTO_RESET_MANA_COOLDOWN = true
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,400,-400,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)
static if AUTO_RESET_MANA_COOLDOWN then
call UnitResetCooldown(dummy)
call SetUnitState(dummy, UNIT_STATE_MANA, 10000.0)
endif
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,400,-400,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
//TESH.scrollpos=66
//TESH.alwaysfold=0
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//~~ KT ~~ Key Timers 2 ~~ By Jesus4Lyf ~~ Version 1.6.1 ~~
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// What is Key Timers?
// - Key Timers attaches structs to timers, or more to the point timed
// effects.
// - You can specify different periods.
// - Key Timers only uses one timer with one trigger per low period
// to keep things efficient, especially within the looping.
// - Key Timers alternatively uses one single timer for all higher periods
// to allow accurate expirations in a stable and reasonably efficient fashion.
//
// =Pros=
// - Easy to use.
// - Fastest attachment loading system (storing in parallel arrays).
// - Fastest execution system for low periods (attaching all functions to one trigger).
// - Allows multiple periods to be used.
//
// =Cons=
// - The code passed into KT2 must call KT_GetData exactly once.
// - Periods must be a multiple of 0.00125 seconds. Not 0.007, for example.
//
// Functions:
// - KT_Add(userFunc, struct, period)
// - KT_GetData returns the struct
//
// - userFunc is to be a user function that takes nothing and returns boolean.
// It will be executed by the system every period until it returns true.
//
// - KT_GetData is to be used inside func, it will return the struct passed to
// to the Add function. It must be called exactly once within the func.
//
// Details:
// - KT2 treats low periods and high periods differently, optimizing each
// with appropriate speed and accuracy, although this effect is invisible
// to you, the mapper.
//
// - While func returns false the timer will continue to call it each period.
// Once func returns true the instance will be detached from system.
//
// Thanks:
// - Daxtreme: For encouraging me to return to Key Timers 2, rather than
// leave it to rot. His interest in the system restored it, and helped
// it to become what it is now. :)
//
// - Captain Griffen: For his work on Rapid Timers, demonstrating that it
// is possible to attach all functions to one trigger, and that it is
// indeed faster.
//
// - Cohadar: Told me to make Key Timers without a textmacro.
// Thanks to him for helping me with the original Key Timers system too.
// Also, I'd like to thank him for his work on Timer Ticker (TT) which
// demonstrated how to use triggers/conditions in this sort of system,
// which has been used in Key Timers 2.
//
// How to import:
// - Create a trigger named KT.
// - Convert it to custom text and replace the whole trigger text with this.
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
library KT initializer Init
///////////////
// Constants //
////////////////////////////////////////////////////////////////////////////
// That bit that users may play with if they know what they're doing.
// Not touching these at all is recommended.
globals
private constant real PERIODTHRESHOLD=0.3 // MUST be below 10.24 seconds.
private constant real HT_MARGINOFERROR=0.01
endglobals
//////////////////////////
// Previous KT2 Globals //
////////////////////////////////////////////////////////////////////////////
// These needed to be moved here for HT to hook GetData.
globals
private timer array KeyTimer
private trigger array TimerTrigger
private integer array KeyTimerListPointer
private integer array KeyTimerListEndPointer
private triggercondition array TriggerCond
private boolexpr array Boolexpr
private integer array Data
private integer array Next
private integer array Prev
private integer TrigMax=0
private integer array NextMem
private integer NextMemMaxPlusOne=1
private integer array ToAddMem
private triggercondition array ToRemove
private boolexpr array ToDestroy
private boolean array IsAdd
private integer AddRemoveMax=0
// Locals
private integer t_id=-1
private integer t_mem
private integer t_lastmem
private integer a_id
private integer a_mem
// Code Chunks
private conditionfunc RemoveInstanceCond
endglobals
/////////////////////
// Hibernate Timer //
////////////////////////////////////////////////////////////////////////////
// KT2 implementation for higher periods (low frequency).
private keyword HTnode
globals
private timer HT_Moderator=CreateTimer()
private real HT_Time=0.0
private HTnode HT_Top
private constant integer HT_DATAMEM=8190 // Added for KT2 hook. Don't change.
endglobals
globals//locals
private HTnode newHTnode
endglobals
private struct HTnode
HTnode prev // Make sure to link this before use
HTnode next // Make sure to link this before use
real period
real firewhen // = HT_Time at creation
trigger trig
integer data
static method new takes code func, integer data, real period returns HTnode
set newHTnode=HTnode.create()
if newHTnode.trig==null then
set newHTnode.trig=CreateTrigger()
endif
call TriggerAddCondition(newHTnode.trig,Condition(func))
set newHTnode.data=data
set newHTnode.period=period
set newHTnode.firewhen=HT_Time
return newHTnode
endmethod
method link takes HTnode prev, HTnode next returns nothing
set prev.next=this
set next.prev=this
set this.next=next
set this.prev=prev
endmethod
method unlink takes nothing returns nothing
set this.prev.next=this.next
set this.next.prev=this.prev
endmethod
method autolink takes HTnode start returns nothing
loop
if start.next==0 then
call this.link(start,0)
return
endif
if start.next.firewhen > this.firewhen then
call this.link(start,start.next)
return
endif
set start=start.next
endloop
endmethod
endstruct
globals//locals
private HTnode HT_handlenode
private HTnode HT_handlenext
endglobals
private function HT_Handler takes nothing returns nothing
// KT2 Data Hook
set t_mem=HT_DATAMEM
// End KT2 Data Hook
set HT_handlenode=HT_Top
set HT_handlenext=HT_handlenode.next
loop
set HT_handlenode=HT_handlenext
exitwhen HT_handlenode==0 // Would pause timer here under old method.
set HT_handlenext=HT_handlenode.next
if HT_handlenode.firewhen<HT_Time+HT_MARGINOFERROR then
// Load data
set Data[HT_DATAMEM]=HT_handlenode.data
// End load data
if TriggerEvaluate(HT_handlenode.trig) then
call HT_handlenode.unlink()
call TriggerClearConditions(HT_handlenode.trig)
call HT_handlenode.destroy()
else
set HT_handlenode.firewhen=HT_handlenode.firewhen+HT_handlenode.period
call HT_handlenode.unlink()
call HT_handlenode.autolink(HT_handlenode.prev)
if HT_handlenext==0 then
set HT_handlenext=HT_handlenode
endif
endif
else
// More to fire, but not yet.
return
endif
endloop
endfunction
globals//locals
private HTnode HT_addnode
endglobals
private function HTadd takes code func, integer data, real period returns nothing
set HT_addnode=HTnode.new(func, data, period)
set HT_addnode.firewhen=HT_Time+period
call HT_addnode.autolink(HT_Top)
//if HT_Top.next==HT_addnode then
// //Old start timer stuff
//endif
endfunction
private function HT_ModeratorLoop takes nothing returns nothing
set HT_Time=HT_Time+HT_MARGINOFERROR
if HT_Top.next.firewhen<HT_Time then
call HT_Handler()
endif
endfunction
private function HTinit takes nothing returns nothing
call TimerStart(HT_Moderator,HT_MARGINOFERROR,true,function HT_ModeratorLoop)
set HT_Top=HTnode.create()
set HT_Top.prev=0
set HT_Top.next=0
set HT_Top.firewhen=0.0
// Allow for GetData
set Next[HT_DATAMEM]=HT_DATAMEM
set Prev[HT_DATAMEM]=HT_DATAMEM
// End allow for GetData
endfunction
//////////////////
// Previous KT2 //
////////////////////////////////////////////////////////////////////////////
// The rest of the KT2 implementation
// Globals have been moved to top.
private function KeyTimerLoop takes nothing returns nothing
set t_id=R2I(TimerGetTimeout(GetExpiredTimer())*800)
set t_mem=KeyTimerListEndPointer[t_id]
call TriggerEvaluate(TimerTrigger[t_id])
set t_mem=0
loop
exitwhen t_mem==AddRemoveMax
set t_mem=t_mem+1
if IsAdd[t_mem] then
set TriggerCond[ToAddMem[t_mem]]=TriggerAddCondition(TimerTrigger[t_id],Boolexpr[ToAddMem[t_mem]])
else
call TriggerRemoveCondition(TimerTrigger[t_id],ToRemove[t_mem])
call DestroyBoolExpr(ToDestroy[t_mem])
endif
endloop
set AddRemoveMax=0
set t_id=-1
endfunction
private function RemoveInstance takes nothing returns boolean
// Will only fire if code returns true.
set AddRemoveMax=AddRemoveMax+1
set IsAdd[AddRemoveMax]=false
set ToRemove[AddRemoveMax]=TriggerCond[t_lastmem]
set ToDestroy[AddRemoveMax]=Boolexpr[t_lastmem]
if Next[t_lastmem]==0 then
set KeyTimerListEndPointer[t_id]=Prev[t_lastmem]
endif
set Prev[Next[t_lastmem]]=Prev[t_lastmem]
if Prev[t_lastmem]==0 then
set KeyTimerListPointer[t_id]=Next[t_lastmem]
if KeyTimerListPointer[t_id]<1 then
call PauseTimer(KeyTimer[t_id])
endif
else
set Next[Prev[t_lastmem]]=Next[t_lastmem]
endif
set NextMem[NextMemMaxPlusOne]=t_lastmem
set NextMemMaxPlusOne=NextMemMaxPlusOne+1
return false
endfunction
private function KTadd takes code func, integer data, real period returns nothing
set a_id=R2I(period*800)
if KeyTimer[a_id]==null then
set KeyTimer[a_id]=CreateTimer()
set TimerTrigger[a_id]=CreateTrigger()
endif
if NextMemMaxPlusOne==1 then
set TrigMax=TrigMax+1
set a_mem=TrigMax
else
set NextMemMaxPlusOne=NextMemMaxPlusOne-1
set a_mem=NextMem[NextMemMaxPlusOne]
endif
set Boolexpr[a_mem]=And(Condition(func),RemoveInstanceCond)
if t_id==a_id then
set AddRemoveMax=AddRemoveMax+1
set IsAdd[AddRemoveMax]=true
set ToAddMem[AddRemoveMax]=a_mem
else
if KeyTimerListPointer[a_id]<1 then
call TimerStart(KeyTimer[a_id],a_id/800.0,true,function KeyTimerLoop)
set KeyTimerListEndPointer[a_id]=a_mem
endif
set TriggerCond[a_mem]=TriggerAddCondition(TimerTrigger[a_id],Boolexpr[a_mem])
endif
set Data[a_mem]=data
set Prev[a_mem]=0
set Next[a_mem]=KeyTimerListPointer[a_id]
set Prev[KeyTimerListPointer[a_id]]=a_mem
set KeyTimerListPointer[a_id]=a_mem
endfunction
public function GetData takes nothing returns integer // Gets hooked by HT.
set t_lastmem=t_mem
set t_mem=Prev[t_mem]
return Data[t_lastmem]
endfunction
private function KTinit takes nothing returns nothing
set RemoveInstanceCond=Condition(function RemoveInstance)
endfunction
///////////////
// Interface //
////////////////////////////////////////////////////////////////////////////
// Stitches it all together neatly.
public function Add takes code func, integer data, real period returns nothing
if period<PERIODTHRESHOLD then
call KTadd(func,data,period)
else
call HTadd(func,data,period)
endif
endfunction
private function Init takes nothing returns nothing
call KTinit()
call HTinit()
endfunction
endlibrary
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// End of Key Timers 2
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//TESH.scrollpos=0
//TESH.alwaysfold=0
library ABuff initializer Init requires Table, TimerUtils, optional SpellEvent, optional DamageEvent, optional ABuffDisplay
//*****************************************************************
//* ABUFF SYSTEM 1.5
//*
//* written by: Anitarf
//* requires: -Table
//* -TimerUtils
//* optional: -SpellEvent
//* -DamageEvent
//*
//* A system for creating, managing and manipulating triggered
//* buffs and their effects. More detailed documentation can be
//* found in separate "triggers". If the map doesn't include them
//* then you can find them in the official release map here:
//* http://www.wc3campaigns.net/showthread.php?t=95521
//*****************************************************************
// ***************************
// ** CALIBRATION SECTION **
// ***************************
globals
private constant boolean USE_BUFF_EVENTS = true
private constant boolean USE_SPELL_EVENTS = true // Requires SpellEvent.
private constant boolean USE_DAMAGE_EVENTS = true // Requires DamageEvent.
private constant boolean USE_ATTACK_EVENTS = true
private constant boolean USE_PERIODIC_EVENTS = true
private constant integer MAX_CUSTOM_EVENTS = 1
public constant real PERIODIC_EVENT_PERIOD = 0.1
private constant real REFRESH_DURATION_FACTOR = 0.0
private constant boolean REFRESH_NEVER_REDUCE_DURATION = true
// ABuffType categories:
public constant key STANDARD
public constant key STACKING
public constant key REFCOUNT
endglobals
private constant function UnitRunsEvents takes unit u returns boolean
return true
endfunction
// ================================================================
// These function interfaces are included in the calibration section
// for user reference only and should not be changed in any way:
function interface ABuffEvent_Create takes aBuff eventBuff returns nothing
function interface ABuffEvent_Refresh takes aBuff eventBuff returns nothing
function interface ABuffEvent_Cleanup takes aBuff eventBuff returns nothing
function interface ABuffEvent_Destroy takes aBuff eventBuff returns nothing
function interface ABuffEvent_Expire takes aBuff eventBuff returns nothing
function interface ABuffEvent_BUnitDeath takes aBuff eventBuff, unit killer returns nothing
function interface ABuffEvent_BUnitKill takes aBuff eventBuff, unit killed returns nothing
function interface ABuffEvent_Periodic takes aBuff eventBuff returns nothing
function interface ABuffEvent_OtherCreate takes aBuff eventBuff, aBuff otherBuff returns nothing
function interface ABuffEvent_OtherRefresh takes aBuff eventBuff, aBuff otherBuff returns nothing
function interface ABuffEvent_OtherDestroy takes aBuff eventBuff, aBuff otherBuff returns nothing
function interface ABuffEvent_OtherExpire takes aBuff eventBuff, aBuff otherBuff returns nothing
function interface ABuffEvent_BUnitAttack takes aBuff eventBuff, unit attacked returns nothing
function interface ABuffEvent_BUnitAttacked takes aBuff eventBuff, unit attacker returns nothing
function interface ABuffEvent_BUnitDamage takes aBuff eventBuff, real damage, unit damagedUnit returns nothing
function interface ABuffEvent_BUnitDamaged takes aBuff eventBuff, real damage, unit damageSource returns nothing
function interface ABuffEvent_BUnitSpellCast takes aBuff eventBuff, integer spellId, unit target returns nothing
function interface ABuffEvent_BUnitSpellTargeted takes aBuff eventBuff, integer spellId, unit caster returns nothing
function interface ABuffEvent_Custom takes aBuff eventBuff, integer data returns nothing
// END OF CALIBRATION SECTION
// ================================================================
// *************************
// ** BUFF DATA STRUCTS **
// *************************
constant function PeriodicEventPeriod takes nothing returns real
// Function maintained for backwards compatibility, new code should use the constant directly
return PERIODIC_EVENT_PERIOD
endfunction
globals
private HandleTable cache //initialized in Init
private aBuff array aBuffList
private integer aBuffListMax = 0
endglobals
// ================================================================
// UNIT DATA
struct aBuffUnit
unit u
aBuff firstBuff = 0
integer numberOfBuffs = 0
boolean isDying = false
static method check takes unit u returns boolean
return cache.exists(u)
endmethod
static method get takes unit u returns aBuffUnit
local aBuffUnit abu = aBuffUnit(cache[u])
if abu==0 and u!=null then
set abu = aBuffUnit.create()
set cache[u]=integer(abu)
set abu.u = u
endif
return abu
endmethod
method onDestroy takes nothing returns nothing
//only gets destroyed when no buffs are attached
call cache.flush(.u)
set this.u = null
endmethod
endstruct
// ================================================================
// BUFFTYPE DATA
struct aBuffType
ABuffEvent_Create eventCreate = 0
ABuffEvent_Refresh eventRefresh = 0
ABuffEvent_Cleanup eventCleanup = 0
ABuffEvent_Destroy eventDestroy = 0
ABuffEvent_Expire eventExpire = 0
ABuffEvent_BUnitDeath eventDeath = 0
ABuffEvent_BUnitKill eventKill = 0
ABuffEvent_Periodic eventPeriodic = 0
ABuffEvent_OtherCreate eventOtherCreate = 0
ABuffEvent_OtherRefresh eventOtherRefresh = 0
ABuffEvent_OtherDestroy eventOtherDestroy = 0
ABuffEvent_OtherExpire eventOtherExpire = 0
ABuffEvent_BUnitAttack eventAttack = 0
ABuffEvent_BUnitAttacked eventAttacked = 0
ABuffEvent_BUnitDamage eventDamage = 0
ABuffEvent_BUnitDamaged eventDamaged = 0
ABuffEvent_BUnitSpellCast eventSpellCast = 0
ABuffEvent_BUnitSpellTargeted eventSpellTargeted = 0
ABuffEvent_Custom array eventCustom[MAX_CUSTOM_EVENTS]
boolean countsAsBuff = true
boolean ignoreAsBuff = false
integer data = 0
integer category = STANDARD
implement optional ABuffDisplay
endstruct
// ================================================================
// BUFF DATA
private keyword next
private keyword prev
private keyword beingDestroyed
private keyword referenceCount
private function ABuffExpire takes nothing returns nothing
local aBuff a = aBuff(GetTimerData(GetExpiredTimer()))
local aBuff b
set a.beingDestroyed=true
call a.id.eventExpire.execute(a)
if USE_BUFF_EVENTS and not(a.id.ignoreAsBuff) then
set b = a.target.firstBuff
loop
exitwhen b == 0
if a!=b then
call b.id.eventOtherExpire.execute(b,a)
endif
set b = b.next
endloop
endif
if a.beingDestroyed then
static if LIBRARY_ABuffDisplay then
if a.id.display!=0 then
call a.id.display.remove(a.target.u)
endif
endif
call a.id.eventCleanup.execute(a)
call a.destroy()
endif
endfunction
struct aBuff
private timer expiration = null
private integer aBuffListPlace = 0
aBuff next = 0
aBuff prev = 0
readonly aBuffType id
readonly integer level = 1
integer data = 0
integer olddata = 0
integer data1 = 0
integer data2 = 0
integer data3 = 0
readonly aBuffUnit target
readonly unit caster = null
readonly boolean friendly = true
boolean beingDestroyed = false
integer referenceCount = 0
method operator refcount takes nothing returns integer
return this.referenceCount
endmethod
static method create takes aBuffType id, unit u, unit caster, real duration, integer level, integer data returns aBuff
local aBuff a = aBuff.allocate()
local aBuffUnit abu= aBuffUnit.get(u)
set aBuffList[aBuffListMax]=integer(a)
set a.aBuffListPlace = aBuffListMax
set aBuffListMax=aBuffListMax+1
set a.id = id
set a.target = abu
set a.next = abu.firstBuff
if abu.firstBuff != 0 then
set abu.firstBuff.prev = a
endif
set abu.firstBuff = a
set a.data = data
set a.level = level
if caster !=null then
set a.caster = caster
if not(IsPlayerAlly(GetOwningPlayer(a.target.u),GetOwningPlayer(a.caster))) then
set a.friendly=false
endif
endif
if duration > 0 and a.id.category!=REFCOUNT then
set a.expiration = NewTimer()
call SetTimerData(a.expiration, integer(a))
call TimerStart(a.expiration, duration, false, function ABuffExpire)
endif
if id.countsAsBuff and not(id.ignoreAsBuff) then
set a.target.numberOfBuffs=a.target.numberOfBuffs+1
endif
return a
endmethod
method refresh takes unit caster, real newDuration, integer level, integer data returns nothing
local real prevDur = 0.0
if .expiration != null then
set prevDur = TimerGetRemaining(.expiration)
call ReleaseTimer(.expiration)
set .expiration = null
endif
if newDuration > 0 and .id.category!=REFCOUNT then
set newDuration = prevDur * REFRESH_DURATION_FACTOR + newDuration
if REFRESH_NEVER_REDUCE_DURATION and prevDur > newDuration then
set newDuration=prevDur
endif
set .expiration = NewTimer()
call SetTimerData(.expiration, integer(this))
call TimerStart(.expiration, newDuration, false, function ABuffExpire)
endif
if caster !=null then
set .caster = caster
if not(IsPlayerAlly(GetOwningPlayer(.target.u),GetOwningPlayer(.caster))) then
set .friendly=false
endif
endif
set .level=level
set .olddata=.data
set .data=data
set this.beingDestroyed=false
endmethod
method onDestroy takes nothing returns nothing
if .id.countsAsBuff and not(.id.ignoreAsBuff)then
set .target.numberOfBuffs=.target.numberOfBuffs-1
endif
if this.prev == 0 and this.next == 0 then
call .target.destroy()
else
if this.next != 0 then
set this.next.prev = this.prev
endif
if this.prev != 0 then
set this.prev.next = this.next
else
set this.target.firstBuff = this.next
endif
endif
set .caster = null
set aBuffListMax=aBuffListMax-1
set aBuffList[.aBuffListPlace]=aBuffList[aBuffListMax]
set aBuffList[.aBuffListPlace].aBuffListPlace=.aBuffListPlace
if .expiration != null then
call ReleaseTimer(.expiration)
endif
endmethod
method setTimeRemaining takes real time returns nothing
if .expiration != null then
call ReleaseTimer(.expiration)
set .expiration = null
endif
if time > 0 and .id.category!=REFCOUNT then
set .expiration = NewTimer()
call SetTimerData(.expiration, integer(this))
call TimerStart(.expiration, time, false, function ABuffExpire)
endif
endmethod
method getTimeRemaining takes nothing returns real
if .expiration != null then
return TimerGetRemaining(.expiration)
else
return 0.0
endif
endmethod
endstruct
// ================================================================
// *****************************
// ** ENUMERATION INTERFACE **
// *****************************
function interface ABuffEnum takes aBuff enumBuff, integer data returns nothing
function ABuffEnumerateAll takes ABuffEnum f, integer data returns nothing
local integer i=0
loop
exitwhen i >= aBuffListMax
if not(aBuffList[i].id.ignoreAsBuff) then
call f.evaluate(aBuffList[i], data)
endif
set i=i+1
endloop
endfunction
function ABuffEnumerateByType takes ABuffEnum f, aBuffType id, integer data returns nothing
local integer i=0
loop
exitwhen i >= aBuffListMax
if aBuffList[i].id==id then
call f.evaluate(aBuffList[i], data)
endif
set i=i+1
endloop
endfunction
function ABuffEnumerateUnit takes ABuffEnum f, unit u, integer data returns nothing
local aBuffUnit abu
local aBuff a
if aBuffUnit.check(u) then
set abu = aBuffUnit.get(u)
set a = abu.firstBuff
loop
exitwhen (a == 0)
if not(a.id.ignoreAsBuff) then
call f.evaluate(a, data)
endif
set a = a.next
endloop
endif
endfunction
// ================================================================
// ********************
// ** EVENT ENGINE **
// ********************
private function ABuffDelayedCleanup takes nothing returns nothing
local aBuff a = aBuff(GetTimerData(GetExpiredTimer()))
if a.beingDestroyed then
static if LIBRARY_ABuffDisplay then
if a.id.display!=0 then
call a.id.display.remove(a.target.u)
endif
endif
call a.id.eventCleanup.execute(a)
call a.destroy()
endif
call ReleaseTimer(GetExpiredTimer())
endfunction
private function DestroyEvent takes aBuff a returns nothing
local aBuff b
call a.id.eventDestroy.execute(a)
if USE_BUFF_EVENTS and not(a.id.ignoreAsBuff) then
set b = a.target.firstBuff
loop
exitwhen b == 0
if a!=b then
call b.id.eventOtherDestroy.execute(b,a)
endif
set b = b.next
endloop
endif
endfunction
private function CreateEvent takes aBuff a returns nothing
local aBuff b
static if LIBRARY_ABuffDisplay then
if a.id.display!=0 then
call a.id.display.apply(a.target.u, a.level)
endif
endif
call a.id.eventCreate.execute(a)
if USE_BUFF_EVENTS and not(a.id.ignoreAsBuff) then
set b = a.target.firstBuff
loop
exitwhen b == 0
if a!=b then
call b.id.eventOtherCreate.execute(b,a)
endif
set b = b.next
endloop
endif
endfunction
private function RefreshEvent takes aBuff a returns nothing
local aBuff b
static if LIBRARY_ABuffDisplay then
if a.id.display!=0 then
call a.id.display.apply(a.target.u, a.level)
endif
endif
call a.id.eventRefresh.execute(a)
if USE_BUFF_EVENTS and not(a.id.ignoreAsBuff) then
set b = a.target.firstBuff
loop
exitwhen b == 0
if a!=b then
call b.id.eventOtherRefresh.execute(b,a)
endif
set b = b.next
endloop
endif
endfunction
private function AttackEventCondition takes nothing returns boolean
return UnitRunsEvents(GetAttacker()) and UnitRunsEvents(GetTriggerUnit())
endfunction
private function DamageEventCondition takes unit damaged, unit damager returns boolean
return UnitRunsEvents(damaged) and UnitRunsEvents(damager)
endfunction
private function SpellCastEvent takes nothing returns nothing
local integer id
local unit caster
local unit target
local aBuffUnit abu
local aBuff a
static if LIBRARY_SpellEvent then
set id=SpellEvent.AbilityId
set caster=SpellEvent.CastingUnit
set target=SpellEvent.TargetUnit
endif
if UnitRunsEvents(caster) and (target==null or UnitRunsEvents(target)) then
if aBuffUnit.check(caster) then
set abu = aBuffUnit.get(caster)
set a = abu.firstBuff
loop
exitwhen (a == 0)
if a.id.eventSpellCast!=0 then
call a.id.eventSpellCast.execute(a, id, target)
endif
set a = a.next
endloop
endif
if target != null and aBuffUnit.check(target) then
set abu = aBuffUnit.get(caster)
set a = abu.firstBuff
loop
exitwhen (a == 0)
if a.id.eventSpellTargeted!=0 then
call a.id.eventSpellTargeted.execute(a, id, caster)
endif
set a = a.next
endloop
endif
endif
set caster = null
set target = null
endfunction
private function AttackEvent takes nothing returns nothing
local unit attacker=GetAttacker()
local unit attacked=GetTriggerUnit()
local aBuffUnit abu
local aBuff a
if aBuffUnit.check(attacker) then
set abu = aBuffUnit.get(attacker)
set a = abu.firstBuff
loop
exitwhen (a == 0)
if a.id.eventAttack!=0 then
call a.id.eventAttack.execute(a, attacked)
endif
set a = a.next
endloop
endif
if aBuffUnit.check(attacked) then
set abu = aBuffUnit.get(attacked)
set a = abu.firstBuff
loop
exitwhen (a == 0)
if a.id.eventAttacked!=0 then
call a.id.eventAttacked.execute(a, attacker)
endif
set a = a.next
endloop
endif
set attacker = null
set attacked = null
endfunction
private function DeathEvent takes nothing returns nothing
local unit killed=GetTriggerUnit()
local unit killer=GetKillingUnit()
local timer t
local aBuffUnit abu
local aBuff a
if killer!=null and aBuffUnit.check(killer) then
set abu = aBuffUnit.get(killer)
set a = abu.firstBuff
loop
exitwhen (a == 0)
if a.id.eventKill!=0 then
call a.id.eventKill.execute(a, killed)
endif
set a = a.next
endloop
endif
if aBuffUnit.check(killed) then
set abu = aBuffUnit.get(killed)
set a = abu.firstBuff
set abu.isDying = true
loop
exitwhen (a == 0)
set a.beingDestroyed=true
if a.id.eventDeath!=0 then
call a.id.eventDeath.execute(a, killer)
endif
set t = NewTimer()
call SetTimerData(t, integer(a))
call TimerStart(t, 0.0, false, function ABuffDelayedCleanup)
set a = a.next
endloop
endif
set killed = null
set killer = null
endfunction
private function DamageEvent takes unit damagedUnit, unit damageSource, real damage returns nothing
local aBuffUnit abu
local aBuff a
if not(DamageEventCondition(damagedUnit, damageSource)) then
return
endif
if damageSource!=null and aBuffUnit.check(damageSource) then
set abu = aBuffUnit.get(damageSource)
set a = abu.firstBuff
loop
exitwhen (a == 0)
if a.id.eventDamage!=0 then
call a.id.eventDamage.execute(a, damage, damagedUnit)
endif
set a = a.next
endloop
endif
if aBuffUnit.check(damagedUnit) then
set abu = aBuffUnit.get(damagedUnit)
set a = abu.firstBuff
loop
exitwhen (a == 0)
if a.id.eventDamaged!=0 then
call a.id.eventDamaged.execute(a, damage, damageSource)
endif
set a = a.next
endloop
endif
endfunction
private function PeriodicEvent takes nothing returns nothing
local integer i=0
loop
exitwhen i >= aBuffListMax
if aBuffList[i].id.eventPeriodic!=0 then
call aBuffList[i].id.eventPeriodic.execute(aBuffList[i])
endif
set i=i+1
endloop
endfunction
public function CustomEvent takes unit u, integer eventId, integer data returns nothing
local aBuffUnit abu
local aBuff a
if u!=null and aBuffUnit.check(u) then
set abu = aBuffUnit.get(u)
set a = abu.firstBuff
loop
exitwhen (a == 0)
if a.id.eventCustom[eventId]!=0 then
call a.id.eventCustom[eventId].execute(a, data)
endif
set a = a.next
endloop
endif
endfunction
// ================================================================
// EVENT INITIALIZER
private function Init takes nothing returns nothing
local trigger t
// TABLE
set cache=HandleTable.create()
// ATTACK EVENT
if USE_ATTACK_EVENTS then
set t = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_ATTACKED )
call TriggerAddCondition( t, Condition( function AttackEventCondition ) )
call TriggerAddAction( t, function AttackEvent )
endif
// DAMAGE EVENT
static if LIBRARY_DamageEvent then
if USE_DAMAGE_EVENTS then
call RegisterDamageResponse( DamageEvent )
endif
endif
// SPELLCAST EVENT
static if LIBRARY_SpellEvent then
if USE_SPELL_EVENTS then
call RegisterSpellEffectResponse( 0, SpellCastEvent )
endif
endif
// DEATH EVENT
set t = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_DEATH )
call TriggerAddAction( t, function DeathEvent )
// PERIODIC EVENT
if USE_PERIODIC_EVENTS then
set t = CreateTrigger()
call TriggerRegisterTimerEventPeriodic( t, PERIODIC_EVENT_PERIOD )
call TriggerAddAction( t, function PeriodicEvent )
endif
endfunction
// ================================================================
// **********************
// ** USER FUNCTIONS **
// **********************
function GetABuffFromUnitByType takes unit u, aBuffType id returns aBuff
local aBuffUnit abu
local aBuff a = 0
if u!=null and aBuffUnit.check(u) then
set abu = aBuffUnit.get(u)
set a = abu.firstBuff
loop
exitwhen (a == 0 or a.id == id)
set a = a.next
endloop
endif
return a
endfunction
function GetABuffFromBuffedUnitByType takes aBuff ab, aBuffType id returns aBuff
local aBuff a = ab.target.firstBuff
loop
exitwhen (a == 0 or a.id == id)
set a = a.next
endloop
return a
endfunction
function UnitHasABuff takes unit u, aBuffType id returns boolean
return GetABuffFromUnitByType(u, id) != 0
endfunction
function BuffedUnitHasABuff takes aBuff ab, aBuffType id returns boolean
return GetABuffFromBuffedUnitByType(ab, id) != 0
endfunction
// ================================================================
function ABuffApply takes aBuffType id, unit u, unit caster, real duration, integer level, integer data returns boolean
local aBuff a = GetABuffFromUnitByType(u, id)
if u==null or IsUnitType(u, UNIT_TYPE_DEAD) or GetUnitTypeId(u)==0 then
return false
elseif ((id.category==STANDARD or id.category==REFCOUNT) and a==0) or id.category==STACKING then
set a = aBuff.create(id, u, caster, duration, level, data)
if id.category==REFCOUNT then
set a.referenceCount=a.referenceCount+1
endif
call CreateEvent(a)
return true
elseif id.category==STANDARD or id.category==REFCOUNT then
call a.refresh(caster, duration, level, data)
if id.category==REFCOUNT then
set a.referenceCount=a.referenceCount+1
endif
call RefreshEvent(a)
return true
endif
return false
endfunction
function ABuffRemove takes aBuff a returns boolean
local timer t
if a.beingDestroyed then
return false
elseif a.id.category==STANDARD or a.id.category==STACKING then
set a.beingDestroyed=true
call DestroyEvent(a)
set t = NewTimer()
call SetTimerData(t, integer(a))
call TimerStart(t, 0.0, false, function ABuffDelayedCleanup)
return true
elseif a.id.category==REFCOUNT then
set a.referenceCount=a.referenceCount-1
if a.referenceCount==0 then
set a.beingDestroyed=true
call DestroyEvent(a)
set t = NewTimer()
call SetTimerData(t, integer(a))
call TimerStart(t, 0.0, false, function ABuffDelayedCleanup)
return true
else
call a.refresh(a.caster, 0.0, a.level, a.data)
call RefreshEvent(a)
return true
endif
else
return false
endif
endfunction
// Maintained for backwards compatibility:
function ABuffDestroy takes aBuff a returns boolean
return ABuffRemove(a)
endfunction
// ================================================================
private function EnumerateRemove takes aBuff enumBuff, integer data returns nothing
if enumBuff.id.countsAsBuff and enumBuff.id.category!=REFCOUNT then
call ABuffRemove(enumBuff)
endif
endfunction
function RemoveAllBuffsOnUnit takes unit u returns nothing
call ABuffEnumerateUnit( ABuffEnum.EnumerateRemove, u, 0)
endfunction
// Maintained for backwards compatibility:
function DestroyAllBuffsOnUnit takes unit u returns nothing
call RemoveAllBuffsOnUnit( u )
endfunction
// ================================================================
function UnitGetNumberOfBuffs takes unit u returns integer
if u!=null and aBuffUnit.check(u) then
return aBuffUnit.get(u).numberOfBuffs
endif
return 0
endfunction
// ================================================================
function GetABuffTimeRemaining takes aBuff a returns real
return a.getTimeRemaining()
endfunction
function SetABuffTimeRemaining takes aBuff a, real newDuration returns nothing
call a.setTimeRemaining(newDuration)
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library LightLeaklessDamageDetect initializer Init
// Creating threads off of this that last longer than the timeout below will likely cause issues, like everything blowing up (handle stack corruption)
// It seems that threads created by timers, rather than executefunc / .evaluate / .execute are not affected. Any threads created from the timer thread are fine.
// This being safe with even the usage laid out above isn't guarenteed. Use at own risk.
// If you start getting random bugs, see if commenting out the timer line below (see comments) helps
// If it does, report it in the thread for this script at [url]www.wc3campaigns.net[/url]
globals
private constant real SWAP_TIMEOUT = 600. // keep high; 600 should be about the right balance.
endglobals
globals
private conditionfunc array func
private integer funcNext = 0
private trigger current = null
private trigger toDestroy = null
private group swapGroup
private rect mapRect
endglobals
// One of the only accessible functions. Use it to add a condition. Must return boolean type, and then have return false at the end.
// Note that it's technically a condition, so if you put a wait in there, it'll die. But waits are lame anyway.
function AddOnDamageFunc takes conditionfunc cf returns nothing
call TriggerAddCondition(current, cf)
set func[funcNext] = cf
set funcNext = funcNext + 1
endfunction
// These inline. For avoiding feedback loops. Feel free to make your own wrapper function for damage functions using this.
function DisableDamageDetect takes nothing returns nothing
call DisableTrigger(current)
endfunction
function EnableDamageDetect takes nothing returns nothing
call EnableTrigger(current)
endfunction
// no more accessible functions, folks.
//! textmacro CGLeaklessDamageDetectAddFilter takes UNIT
// add here any conditions to add the unit to the trigger, example below, commented out:
// if GetUnitTypeId($UNIT$) != 'h000' then // where 'h000' is a dummy unit
call TriggerRegisterUnitEvent(current, $UNIT$, EVENT_UNIT_DAMAGED)
// endif
//! endtextmacro
private function AddEx takes nothing returns boolean
//! runtextmacro CGLeaklessDamageDetectAddFilter("GetFilterUnit()")
return false
endfunction
private function Enters takes nothing returns boolean
//! runtextmacro CGLeaklessDamageDetectAddFilter("GetTriggerUnit()")
return false
endfunction
private function Swap takes nothing returns nothing
local integer i = 0
local boolean b = IsTriggerEnabled(current)
call DisableTrigger(current)
if toDestroy != null then
call DestroyTrigger(toDestroy)
endif
set toDestroy = current
set current = CreateTrigger()
if not(b) then
call DisableTrigger(current)
endif
call GroupEnumUnitsInRect(swapGroup, mapRect, Filter(function AddEx))
loop
exitwhen i >= funcNext
call TriggerAddCondition(current, func[i])
set i = i + 1
endloop
endfunction
private function Init takes nothing returns nothing
local trigger t = CreateTrigger()
local region r = CreateRegion()
local integer i = 0
set mapRect = GetWorldBounds()
call RegionAddRect(r, mapRect)
call TriggerRegisterEnterRegion(t, r, null)
call TriggerAddCondition(t, Condition(function Enters))
set swapGroup = CreateGroup()
set current = CreateTrigger()
loop
exitwhen i >= funcNext
call TriggerAddCondition(current, func[i])
set i = i + 1
endloop
call GroupEnumUnitsInRect(swapGroup, GetWorldBounds(), Filter(function AddEx))
// Commenting out the next line will make the system leak indexes and events, but should make it safer.
call TimerStart(CreateTimer(), SWAP_TIMEOUT, true, function Swap)
endfunction
endlibrary