//TESH.scrollpos=0
//TESH.alwaysfold=0
Name | Type | is_array | initial_value |
//TESH.scrollpos=51
//TESH.alwaysfold=0
scope TormentalWrath initializer Init // requires xedamage, xebasic, xefx, xecollider, BoundSentinel
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
//***********************************************************************************************************//
//@@//////////////////////////////////Tormental Wrath, Made by Komaqtion///////////////////////////////////@@//
//@@ Now using XE! @@//
//@@ @@//
//@@ @@//
//@@ How to import: @@//
//@@ @@//
//@@ ¤ First copy the ability "Tormental Wrath" and the unit "Dummy" and change the constant @@//
//@@ "SPELL_ID" to the rawcode of the newly copied spell, then go into the trigger called @@//
//@@ "xebasic", and change "XE_DUMMY_UNITID" to the Dummy's rawcode ! @@//
//@@ Then you need to import Vexorian's dummy.mdx file from this map, or another, and change @@//
//@@ the "Dummy" unit's model to it. Then, of course, you need to import the triggers, which @@//
//@@ are in the categories "The Spell" and "Required Systems". @@//
//@@ Then you're free to change any of the given global constants to suit your needs. @@//
//@@ @@//
//@@ @@//
//@@ Requirements: @@//
//@@ @@//
//@@ ¤ Patch 1.23b or newer. @@//
//@@ ¤ JASS Newgen Pack, version 5d or newer. @@//
//@@ ¤ This spell, of course ! @@//
//@@ ¤ The dummy.mdx, BoundSentinel and the XE libraries, all made by Vexorian ! @@//
//@@ @@//
//@@ @@//
//@@ Credits to: @@//
//@@ @@//
//@@ * Vexorian, for the dummy.mdx file, BoundSentinel and the XE libraries ! @@//
//@@ * Anitarf and Pyrogasm, for their help on the rewrite of it :D @@//
//@@ * Rising_Dusk, for helping me a bit on the forum ! @@//
//@@ @@//
//@@ And, of course, so many thanks to all of the many helpful people at WC3C.net @@//
//@@ who has helped get is spell working! @@//
//@@ @@//
//@@ @@//
//@@ Note: This is my very first fully working, and quite good, spell made completely in vJASS. @@//
//@@ @@//
//@@ So much feedback, and suggestions are very much welcome, and maybe some tips to make it @@//
//@@ even better would be nice :D @@//
//@@ @@//
//@@///////////////////////////////////////////////////////////////////////////////////////////////////////@@//
//***********************************************************************************************************//
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
globals
// Effects! Change as you'd like ;)\\
private constant string EFFECT_C = "Abilities\\Spells\\Human\\HolyBolt\\HolyBoltSpecialArt.mdl" // Effect on the caster!
private constant string EFFECT_1 = "Abilities\\Spells\\Undead\\AnimateDead\\AnimateDeadTarget.mdl" // Effect 200 range from the caster!
private constant string EFFECT_2 = "Abilities\\Spells\\Orc\\WarStomp\\WarStompCaster.mdl" // Effect at the end!
private constant string EFFECT_T = "Abilities\\Spells\\Items\\AIfb\\AIfbSpecialArt.mdl" // Effect on the damaged units!
private constant string ATTACH_POINT = "origin" // The attach point of EFFECT_T!
private constant string ODD_EFFECT = "Abilities\\Spells\\Demon\\DarkPortal\\DarkPortalTarget.mdl" // The effect which appears at each ODD_PERIOD!
private constant string DUMMY_EFFECT = "Abilities\\Weapons\\DemolisherFireMissile\\DemolisherFireMissile.mdl" // Effect on the moving dummy!
// Rawcodes! Change these rawcodes to the ones on your map :D (Note: To change the Dummy unit's ID, you'll need to go into the trigger called "xebasic", and change "XE_DUMMY_UNITID" !) \\
private constant integer SPELL_ID = '0000' // ID of the spell!
// Other constant integers! Change at will :P\\
private constant real DAMAGE_FACTOR = 1. // Just a simple real to make it easy to change the damage!
private constant real RANGE_FACTOR = 1. // Just a simple real to make it easy to change the range!
private constant integer ANGLES = 16 // The number of angles the effects will show at! (360/ANGLES)
private constant attacktype ATTACK_TYPE = ATTACK_TYPE_NORMAL // Explains itself i think XD
private constant damagetype DAMAGE_TYPE = DAMAGE_TYPE_UNIVERSAL // Same here :P
private constant weapontype WEAPON_TYPE = WEAPON_TYPE_WHOKNOWS // And same here :D
private constant real TIME = 2.5 // How long it takes for the spell to complete!
private constant real SLIDE_SPEED = TIME/XE_ANIMATION_PERIOD * TIME // How much the dummy effect moves each second! (It's 200. atm :D)
private constant real EXPLOSION_OFFSET = 200. // How much range the first explosions are offset the casters porition!
private constant boolean ODD_EFFECTS = false // Wether or not the ODD_EFFECT s will appear!
private constant real ODD_EFFECT_TIMEOUT = .75 // How frequently the ODD_EFFECT s appear!
private constant boolean DAMAGE_ALLIES = false // Wether the spell should damage allies, or not!
private constant boolean ATTACK = true // If the spell should count the damage dealt by it as an attack!
private constant boolean RANGED = true // If the spell should count the damage dealt by it as ranged!
// Here are some UNIT_TYPE's which you can add that won't be damages during this spell!\\
private constant unittype STRUCTURE = UNIT_TYPE_STRUCTURE
private constant unittype MAGIC = UNIT_TYPE_MAGIC_IMMUNE
// To add more "exeptions" to the spell, simply create another on of these constants, and also add another one of " set dmg.factor(input unittype constant here, .0) "
// to the function "Spell_Actions"... There are other ones of these there, so you should find it :D
endglobals
// Damage functions!\\
// V // This one is for the DPS on the projectiles! \\ V \\
private function Damage_Small takes integer lv returns real
return DAMAGE_FACTOR * lv
endfunction
// V // This one is for the explosions around the caster! \\ V \\
private function Damage_Medium takes integer lv returns real
return 50 + lv * DAMAGE_FACTOR * 25
endfunction
// V // This one is for the explosions at the very end! \\ V \\
private function Damage_High takes integer lv returns real
return 50 + lv * DAMAGE_FACTOR * 50
endfunction
// Range functions!\\
// V // This one is for the DPS on the projectiles! \\ V \\
private function Range_Small takes nothing returns real
return RANGE_FACTOR * 100
endfunction
// V // This one is for the explosions around the caster! \\ V \\
private function Range_Medium takes nothing returns real
return RANGE_FACTOR * 10
endfunction
// V // This one is for the explosions at the very end! \\ V \\
private function Range_High takes nothing returns real
return RANGE_FACTOR * RANGE_FACTOR
endfunction
// End of Configuration!\\
globals // Don't touch these globals ! \\
private integer tempData
private xedamage dmg
endglobals // Don't touch these globals ! \\
private struct Data extends xecollider
integer lvl
player play
real cx
real cy
unit cast
private real tick
private method onDestroy takes nothing returns nothing
call .flash(EFFECT_2)
call dmg.damageAOE(.cast, .x, .y, Range_High(), Damage_High(.lvl))
endmethod
private method loopControl takes nothing returns nothing
set tempData = this
set .tick = .tick + XE_ANIMATION_PERIOD
if .tick >= ODD_EFFECT_TIMEOUT and ODD_EFFECTS == true then
call DestroyEffect(AddSpecialEffect(ODD_EFFECT, .x, .y))
call dmg.damageAOE(.cast, .x, .y, Range_Medium(), Damage_Medium(.lvl))
set .tick = 0
else
call dmg.damageAOE(.cast, .x, .y, Range_Small(), Damage_Small(.lvl))
endif
endmethod
endstruct
private function Spell_Conditions takes nothing returns boolean
return GetSpellAbilityId() == SPELL_ID
endfunction
private function Spell_Actions takes nothing returns nothing
local Data d
local integer i = 0
local unit u = GetTriggerUnit()
local real x=GetUnitX(u)
local real y=GetUnitY(u)
local real x2
local real y2
local real a=GetUnitFacing(u)*bj_DEGTORAD
call DestroyEffect(AddSpecialEffect(EFFECT_C, x, y))
loop
exitwhen i >= ANGLES
set d = Data.create(x, y, a)
set d.fxpath=(DUMMY_EFFECT)
set d.collisionSize=(Range_Small())
set d.direction=a
set d.speed = SLIDE_SPEED
set d.expirationTime = TIME
set d.play = GetOwningPlayer(u)
set d.cast = u
set d.cx = x
set d.cy = y
set d.lvl = GetUnitAbilityLevel(u, SPELL_ID)
set x2 = x+EXPLOSION_OFFSET*Cos(a)
set y2 = y+EXPLOSION_OFFSET*Sin(a)
call DestroyEffect(AddSpecialEffect(EFFECT_1, x2, y2))
call dmg.damageAOE(d.cast, x2, y2, Range_Medium(), Damage_Medium(d.lvl))
set a = a + (2*bj_PI/ANGLES)
set i = i + 1
endloop
set u = null
endfunction
private function Init takes nothing returns nothing
local trigger t=CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(t,Condition(function Spell_Conditions))
call TriggerAddAction(t,function Spell_Actions)
set dmg = xedamage.create()
set dmg.dtype = DAMAGE_TYPE
set dmg.atype = ATTACK_TYPE
set dmg.wtype = WEAPON_TYPE
set dmg.damageAllies = DAMAGE_ALLIES
call dmg.factor(STRUCTURE, .0)
call dmg.factor(MAGIC, .0)
call dmg.useSpecialEffect(EFFECT_T, ATTACH_POINT)
endfunction
endscope
//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=19
//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.03125
constant real XE_MAX_COLLISION_SIZE = 200.
endglobals
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library xefx initializer init requires xebasic
//**************************************************
// xefx 0.6
// --------
// 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 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
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
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
endstruct
endlibrary
//TESH.scrollpos=39
//TESH.alwaysfold=0
library xecollider initializer init requires xefx, xebasic
//****************************************************************
//*
//* xecollider 0.5
//* --------------
//* 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
//##==========================================================================
// 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.homingMode=0
else
set this.homingMode= .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.homingMode= .HOMING_POINT
set this.homingTargetX=x
set this.homingTargetY=y
endmethod
public method setTargetPointLoc takes location loc returns nothing
set this.homingMode= .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.homingMode = 0
endmethod
method terminate takes nothing returns nothing
set this.dead=true
set this.fxpath=""
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 HOMING_UNIT =1
private static constant integer HOMING_POINT=2
private integer homingMode =0
private unit homingTargetUnit = null
private real homingTargetX
private real homingTargetY
private method onDestroy takes nothing returns nothing
call GroupClear(this.seen)
call this.fx.destroy()
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.homingMode== .HOMING_UNIT ) then
set u=this.homingTargetUnit
if(GetUnitTypeId(u)==0) then
set this.homingMode=.HOMING_POINT
else
set this.homingTargetX=GetUnitX(u)
set this.homingTargetY=GetUnitY(u)
endif
set u=null
endif
if (this.homingMode != 0) 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
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(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=477
//TESH.alwaysfold=0
library xedamage initializer init requires xebasic
//************************************************************************
// xedamage 0.6
// --------
// For all your damage and targetting needs.
//
//************************************************************************
//===========================================================================================================
globals
private constant integer MAX_SUB_OPTIONS = 3
//=======================================================
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 = true // Should only units that are visible for source unit's owner be affected?
boolean deadOnly = false // Should only corpses be affected by "the damage"? (useful when using xedamage as a target selector)
boolean alsoDead = false // Should even corpses and alive units be considered?
boolean damageTrees = false //Also damage destructables? Notice this is used only in certain methods.
//AOE for example targets a circle, so it can affect the destructables
//in that circle, a custom spell using xedamage for targetting configuration
//could also have an if-then-else implemented so it can verify if it is true
//then affect trees manually.
//
// Damage type stuff:
// .dtype : the "damagetype" , determines if the spell is physical, magical or ultimate.
// .atype : the "attacktype" , deals with armor.
// .wtype : the "weapontype" , determines the sound effect to be played when damage is done.
//
// Please use common.j/blizzard.j/ some guide to know what damage/attack/weapon types can be used
//
damagetype dtype = DAMAGE_TYPE_UNIVERSAL
attacktype atype = ATTACK_TYPE_NORMAL
weapontype wtype = WEAPON_TYPE_WHOKNOWS
//
// Damage type 'tag' people might use xedamage.isInUse() to detect xedamage usage, there are other static
// variables like xedamage.CurrentDamageType and xedamage.CurrentDamageTag. The tag allows you to specify
// a custom id for the damage type ** Notice the tag would aid you for some spell stuff, for example,
// you can use it in a way similar to Rising_Dusk's damage system.
//
integer tag = 0
//
// if true, forceDamage will make xedamage ignore dtype and atype and try as hard as possible to deal 100%
// damage.
boolean forceDamage = false
//
// Ally factor! Certain spells probably have double purposes and heal allies while harming enemies. This
// field allows you to do such thing.
//
real allyfactor = 1.0
//
// field: .exception = SOME_UNIT_TYPE
// This field adds an exception unittype (classification), if the unit belongs to this unittype it will
// be ignored.
//
method operator exception= takes unittype ut returns nothing
set this.use_ex=true
set this.ex_ut=ut
endmethod
//
// field: .required = SOME_UNIT_TYPE
// This field adds a required unittype (classification), if the unit does not belong to this unittype
// it will be ignored.
//
method operator required= takes unittype ut returns nothing
set this.use_req=true
set this.req_ut=ut
endmethod
private boolean use_ex = false
private unittype ex_ut = null
private boolean use_req = false
private unittype req_ut = null
private unittype array fct[MAX_SUB_OPTIONS]
private real array fc[MAX_SUB_OPTIONS]
private integer fcn=0
//
// method .factor(SOME_UNIT_TYPE, factor)
// You might call factor() if you wish to specify a special damage factor for a certain classification,
// for example call d.factor(UNIT_TYPE_STRUCTURE, 0.5) makes xedamage do half damage to structures.
//
method factor takes unittype ut, real fc returns nothing
if(this.fcn==MAX_SUB_OPTIONS) then
debug call BJDebugMsg("In one instance of xedamage, you are doing too much calls to factor(), please increase MAX_SUB_OPTIONS to allow more, or cut the number of factor() calls")
return
endif
set this.fct[this.fcn] = ut
set this.fc[this.fcn] = fc
set this.fcn = this.fcn+1
endmethod
private integer array abifct[MAX_SUB_OPTIONS]
private real array abifc[MAX_SUB_OPTIONS]
private integer abifcn=0
//
// method .abilityFactor('abil', factor)
// You might call abilityFactor() if you wish to specify a special damage factor for units that have a
// certain ability/buff.
// for example call d.abilityFactor('A000', 1.5 ) makes units that have the A000 ability take 50% more
// damage than usual.
//
method abilityFactor takes integer abilityId, real fc returns nothing
if(this.abifcn==MAX_SUB_OPTIONS) then
debug call BJDebugMsg("In one instance of xedamage, you are doing too much calls to abilityFactor(), please increase MAX_SUB_OPTIONS to allow more, or cut the number of abilityFactor() calls")
return
endif
set this.abifct[this.abifcn] = abilityId
set this.abifc[this.abifcn] = fc
set this.abifcn = this.abifcn+1
endmethod
private boolean usefx = false
private string fxpath
private string fxattach
//
// method .useSpecialEffect("effect\\path.mdl", "origin")
// Makes it add (and destroy) an effect when damage is performed.
//
method useSpecialEffect takes string path, string attach returns nothing
set this.usefx = true
set this.fxpath=path
set this.fxattach=attach
endmethod
//********************************************************************
//* Now, the usage stuff:
//*
//================================================================================
// static method xedamage.isInUse() will return true during a unit damaged
// event in case this damage was caused by xedamage, in this case, you can
// read variables like CurrentDamageType, CurrentAttackType and CurrentDamageTag
// to be able to recognize what sort of damage was done.
//
readonly static damagetype CurrentDamageType=null
readonly static attacktype CurrentAttackType=null
readonly static integer CurrentDamageTag =0
private static integer inUse = 0
static method isInUse takes nothing returns boolean
return (.inUse>0) //inline friendly.
endmethod
//========================================================================================================
// 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
//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<1) then
call SetWidgetLife(u,1)
set r=1
endif
call UnitDamageTarget(dmger,u,0.01,false,false,a,d,null)
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))*100
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
if(.usefx) then
call DestroyEffect( AddSpecialEffectTarget(this.fxpath, target, this.fxattach) )
endif
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
return (pl!=GetWidgetLife(target))
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
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
call DestroyEffect( AddSpecialEffectTarget(this.fxpath, target, this.fxattach) )
endif
call UnitDamageTarget(source,target, f*damage, true, .ranged, .atype, .dtype, .wtype )
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
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
call UnitDamageTarget(.sourceAOE,target, f*this.AOEdamage, true, .ranged, .atype, .dtype, .wtype )
if(.usefx) then
call DestroyEffect( AddSpecialEffectTarget(this.fxpath, target, this.fxattach) )
endif
endif
set .instance= this //better restore, nesting IS possible!
set target=null
return false
endmethod
private static method damageAOE_DestructablesEnum takes nothing returns boolean
local destructable target=GetFilterDestructable()
local xedamage this=.instance //adopting a instance.
local real dx=.AOEx-GetDestructableX(target)
local real dy=.AOEy-GetDestructableY(target)
if( dx*dx + dy*dy >= .AOEradius+EPSILON ) then
set target=null
return false
endif
set .countAOE=.countAOE+1
if(.usefx) then
call DestroyEffect( AddSpecialEffectTarget(this.fxpath, target, this.fxattach) )
endif
call UnitDamageTarget(.sourceAOE,target, this.AOEdamage, true, .ranged, .atype, .dtype, .wtype )
set .instance= this //better restore, nesting IS possible!
set target=null
return false
endmethod
//==========================================================================================
// will affect trees if damageTrees is true!
//
method damageAOE takes unit source, real x, real y, real radius, real damage returns integer
local damagetype dt=.CurrentDamageType
local attacktype at=.CurrentAttackType
local integer tg=.CurrentDamageTag
set .CurrentDamageType = .dtype
set .CurrentAttackType = .atype
set .CurrentDamageTag = .tag
set .inUse = .inUse +1
set .instance=this
set .countAOE=0
set .sourceAOE=source
set .AOEx=x
set .AOEradius=radius
set .AOEy=y
set .AOEdamage=damage
call GroupEnumUnitsInRange(.enumgroup,x,y,radius+XE_MAX_COLLISION_SIZE, .filterAOE)
if(.damageTrees) then
call SetRect(.AOERect, x-radius, y-radius, x+radius, y+radius)
set .AOEradius=.AOEradius*.AOEradius
call EnumDestructablesInRect(.AOERect, .filterDestAOE, null)
endif
set .inUse = .inUse -1
set .CurrentDamageTag = tg
set .CurrentDamageType = dt
set .CurrentAttackType = at
return .countAOE
endmethod
method damageAOELoc takes unit source, location loc, real radius, real damage returns integer
return .damageAOE(source, GetLocationX(loc), GetLocationY(loc), radius, damage)
endmethod
//==========================================================================================
// only affects trees, ignores damageTrees
//
method damageDestructablesAOE takes unit source, real x, real y, real radius, real damage returns integer
set .instance=this
set .countAOE=0
set .sourceAOE=source
set .AOEx=x
set .AOEradius=radius*radius
set .AOEy=y
set .AOEdamage=damage
//if(.damageTrees) then
call SetRect(.AOERect, x-radius, y-radius, x+radius, y+radius)
call EnumDestructablesInRect(.AOERect, .filterDestAOE, null)
//endif
return .countAOE
endmethod
method damageDestructablesAOELoc takes unit source, location loc, real radius, real damage returns integer
return .damageDestructablesAOE(source,GetLocationX(loc), GetLocationY(loc), radius, damage)
endmethod
//'friend' with the library init
static method structInit takes nothing returns nothing
set .AOERect= Rect(0,0,0,0)
set .filterAOE= Condition(function xedamage.damageAOE_Enum)
set .filterDestAOE = Condition( function xedamage.damageAOE_DestructablesEnum)
set .enumgroup = CreateGroup()
endmethod
endstruct
private function init takes nothing returns nothing
set dmger=CreateUnit(Player(15), XE_DUMMY_UNITID , 0.,0.,0.)
call UnitAddAbility(dmger,'Aloc')
call xedamage.structInit()
endfunction
endlibrary
//TESH.scrollpos=9
//TESH.alwaysfold=0
scope CreateHero initializer Create_Hero
globals
private constant string EFFECT = "Abilities\\Spells\\NightElf\\BattleRoar\\RoarCaster.mdl"
private constant string MSG = "Press the Esc key to revive eventually fallen units!
If there are no recently fallen units, a peasant will be created at the center of the map, instead."
private constant real X = 0.
private constant real Y = 0.
private constant integer ID = '0000'
private constant integer ID2 = 'N000'
private integer LOOP = 0
endglobals
private function Create_Hero takes nothing returns nothing
local unit u = CreateUnit(Player(0),ID2,X,Y,bj_UNIT_FACING)
call DestroyEffect(AddSpecialEffect(EFFECT,X,Y))
call SelectHeroSkill(u,ID)
call SetUnitAbilityLevel(u,ID,1)
call SelectUnit(u,true)
call FogEnable(false)
call FogMaskEnable(false)
call DisplayTimedTextToPlayer(Player(0),0,0, 999999.00, MSG)
loop
exitwhen LOOP >= 6
call CreateUnit(Player(PLAYER_NEUTRAL_AGGRESSIVE),ChooseRandomCreep(-1),GetRandomReal(GetRectMinX(gg_rct_NorthWest),GetRectMaxX(gg_rct_NorthWest)),GetRandomReal(GetRectMinY(gg_rct_NorthWest),GetRectMaxY(gg_rct_NorthWest)),bj_UNIT_FACING)
call CreateUnit(Player(PLAYER_NEUTRAL_AGGRESSIVE),ChooseRandomCreep(-1),GetRandomReal(GetRectMinX(gg_rct_NorthEast),GetRectMaxX(gg_rct_NorthEast)),GetRandomReal(GetRectMinY(gg_rct_NorthEast),GetRectMaxY(gg_rct_NorthEast)),bj_UNIT_FACING)
call CreateUnit(Player(PLAYER_NEUTRAL_AGGRESSIVE),ChooseRandomCreep(-1),GetRandomReal(GetRectMinX(gg_rct_SouthWest),GetRectMaxX(gg_rct_SouthWest)),GetRandomReal(GetRectMinY(gg_rct_SouthWest),GetRectMaxY(gg_rct_SouthWest)),bj_UNIT_FACING)
call CreateUnit(Player(PLAYER_NEUTRAL_AGGRESSIVE),ChooseRandomCreep(-1),GetRandomReal(GetRectMinX(gg_rct_SouthEast),GetRectMaxX(gg_rct_SouthEast)),GetRandomReal(GetRectMinY(gg_rct_SouthEast),GetRectMaxY(gg_rct_SouthEast)),bj_UNIT_FACING)
set LOOP = LOOP+1
endloop
loop
exitwhen LOOP <= 0
call CreateUnit(Player(PLAYER_NEUTRAL_AGGRESSIVE),ChooseRandomCreep(1),GetRandomReal(GetRectMinX(gg_rct_Center),GetRectMaxX(gg_rct_Center)),GetRandomReal(GetRectMinY(gg_rct_Center),GetRectMaxY(gg_rct_Center)),bj_UNIT_FACING)
set LOOP = LOOP-1
endloop
set u = null
endfunction
endscope
//TESH.scrollpos=0
//TESH.alwaysfold=0
scope Revive initializer Init
globals
private constant real DEAD = 0.405
private group G = CreateGroup()
private real X = 0.
private real Y = 0.
private boolean ALL_ALIVE = true
endglobals
private function ForRevive takes nothing returns nothing
local unit eu = GetEnumUnit()
set X = GetUnitX(eu)
set Y = GetUnitY(eu)
call CreateUnit(Player(PLAYER_NEUTRAL_AGGRESSIVE), GetUnitTypeId(eu), X, Y, bj_UNIT_FACING)
call GroupRemoveUnit(G, eu)
set eu = null
endfunction
private function Revive takes nothing returns nothing
if ALL_ALIVE == true then
call CreateUnit(Player(PLAYER_NEUTRAL_AGGRESSIVE), 'hpea', GetRectCenterX(bj_mapInitialPlayableArea), GetRectCenterY(bj_mapInitialPlayableArea), bj_UNIT_FACING)
else
call ForGroup(G, function ForRevive)
set ALL_ALIVE = true
endif
endfunction
private function Hero_Revive takes nothing returns nothing
call ReviveHero(GetTriggerUnit(),0,0,true)
call PanCameraTo(0,0)
call SelectUnit(GetTriggerUnit(),true)
endfunction
private function Add_When_Dead takes nothing returns nothing
call GroupAddUnit(G, GetTriggerUnit())
set ALL_ALIVE = false
endfunction
//===========================================================================
private function Init takes nothing returns nothing
local trigger t = CreateTrigger()
call TriggerRegisterPlayerEvent(t, Player(0), EVENT_PLAYER_END_CINEMATIC)
call TriggerAddAction(t, function Revive)
set t = CreateTrigger()
call TriggerRegisterPlayerUnitEvent(t, Player(PLAYER_NEUTRAL_AGGRESSIVE), EVENT_PLAYER_UNIT_DEATH, null)
call TriggerAddAction(t, function Add_When_Dead)
set t = CreateTrigger()
call TriggerRegisterPlayerUnitEvent(t, Player(0), EVENT_PLAYER_UNIT_DEATH, null)
call TriggerAddAction(t, function Hero_Revive)
endfunction
endscope
//TESH.scrollpos=10
//TESH.alwaysfold=0
library Preload initializer init
globals
private constant integer DUMMY_ID = 'h000'
private constant real X = -4096
private constant real Y = 4096
private constant player OWNER = Player(15)
private unit DUMMY = null
private integer array ID
private timer t = CreateTimer()
endglobals
public function Ability_Add takes integer abilid returns nothing
if GetUnitAbilityLevel(DUMMY, abilid) <= 0 and abilid != 0 then
call UnitAddAbility(DUMMY, abilid)
endif
endfunction
public function Effect_Add takes string path returns nothing
call DestroyEffect(AddSpecialEffect(path,X,Y))
endfunction
public function Unit_Add takes integer unitid returns nothing
call RemoveUnit(CreateUnit(OWNER, unitid, X, Y, 0.))
endfunction
private function Destroy takes nothing returns nothing
call RemoveUnit(DUMMY)
set DUMMY = null
call PauseTimer(t)
call DestroyTimer(t)
set t = null
endfunction
private function init takes nothing returns nothing
set DUMMY = CreateUnit(OWNER,DUMMY_ID, X,Y,0)
call TimerStart(t,0.0,false,function Destroy)
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
scope PreloadTrig initializer Preloader
private function Preloader takes nothing returns nothing
call Preload_Ability_Add ('0000')
call Preload_Ability_Add ('Aloc')
call Preload_Effect_Add ("Abilities\\Spells\\Demon\\DarkPortal\\DarkPortalTarget.mdl")
call Preload_Effect_Add ("Abilities\\Spells\\Human\\MarkOfChaos\\MarkOfChaosTarget.mdl")
call Preload_Effect_Add ("Abilities\\Spells\\Orc\\WarStomp\\WarStompCaster.mdl")
call Preload_Effect_Add ("Abilities\\Weapons\\DemolisherFireMissile\\DemolisherFireMissile.mdl")
call Preload_Effect_Add ("Abilities\\Spells\\NightElf\\BattleRoar\\RoarCaster.mdl")
call Preload_Effect_Add ("Abilities\\Spells\\Other\\BreathOfFire\\BreathOfFireDamage.mdl")
endfunction
endscope