Name | Type | is_array | initial_value |
Hero | unit | No | |
Missile | unit | No |
//TESH.scrollpos=343
//TESH.alwaysfold=0
[tabs]
[tab=Foreword]
An extremely simple missile projectile system that collides and damages on enemy units
or blows on impact when desired distance is reached.
The missile is recycle able so that it will not create different dummies everytime.
[/tab]
[tab=Code]
[jass]
library MissileCollision /* v1.1
*************************************************************************************
*
* by mckill2009
*
*************************************************************************************
*
* An extremely simple missile projectile system that collides and damages on enemy units
* or blows on impact when desired distance is reached.
* The missile is recycle able so that it will not create different dummies everytime.
*
*************************************************************************************
*
* */ uses /*
*
* */ T32 /* http://www.thehelper.net/forums/showthread.php/132538-Timer32
* */ Dummy /* http://www.hiveworkshop.com/forums/jass-resources-412/system-dummy-213908/
*
************************************************************************************
*
* Credits
*
* Vexorian
* -----------------------
* For his DUMMY.mdx model
*
* Jesus4Lyf
* -----------------------
* For his Timer32 library
*
* Nesthaurus
* -----------------------
* For his Dummy library
*
************************************************************************************
*
* Installation:
* - Import the DUMMY.mdx model to your map
* - Copy the DummySfx unit from the object editor to your map and change the model to DUMMY.mdx
* - Copy ALL the Required Libraries and MissileCollicion folder to your map (except the DEMO)
* - Change the DUMMY_ID of the Dummy library to your imported DummySfx
*
************************************************************************************
*
* API:
* struct MC
* static method create takes player owningPlayer, real xWhere, real yWhere, string missileModel returns thistype
* - owningPlayer is the owner of the missile
* - xWhere and yWhere is the coordinate where the missile's origin or launched
*
* method setupCollision takes real aoe, real collision, string collisionEffect returns nothing
* - aoe is the area of effect damage when missile explodes
* - collision, missile will explode and deals damage if it collides with the first enemy unit in range
* - collisionEffect, the effect or eye candy of the explosion
*
* method setupDamage takes real damage, attacktype attackType, damagetype damageType returns nothing
* - damage, the damage when missile explodes
* - attackType and damageType is self explanatory
*
* method launch takes real facingInRadiants, real distance, real height, real speed returns nothing
* - facingInRadiants is the angle where the missile goes
* - distance is how far the missile traver
* - height and speed is the height of the missile and speed travel of the missile respectively
*
*
************************************************************************************
*/
private struct Delay
unit missile
real del
Dummy remove
endstruct
struct MC
private unit missile
private real aoeDamage
private real collision
private real distance
private real damage
private real height
private real speed
private real cos
private real sin
private string collisionEffect
private effect missileModel
private player pl
private attacktype atk
private damagetype dmg
private Dummy remove
private static group g = CreateGroup()
/*
*
* This block is used to delay the dummy to be recycled so that the effects will be
* visible from their origin attachment
*
*/
private static timer t = CreateTimer()
private static integer index = 0
private static integer array indexAR
private static method looper takes nothing returns nothing
local Delay this
local integer i = 0
loop
set i = i+1
set this = indexAR[i]
set this.del = this.del - 0.03125
if 0 > this.del then
call this.remove.destroy()
set this.missile = null
call this.destroy()
set indexAR[i] = indexAR[index]
set i = i - 1
set index = index - 1
if index==0 then
call PauseTimer(t)
endif
endif
exitwhen i==index
endloop
endmethod
private method delay takes nothing returns nothing returns nothing
local Delay m = Delay.create()
set m.missile = .missile
set m.del = 2.0
set m.remove = .remove
if index==0 then
call TimerStart(t, 0.03125, true, function thistype.looper)
endif
set index = index + 1
set indexAR[index] = m
endmethod
/*
*
* End of Delay block
*
*/
private method stop takes nothing returns nothing
call .delay()
call DestroyEffect(.missileModel)
set .missile = null
set .missileModel = null
call .stopPeriodic()
call .deallocate()
endmethod
private method damageThem takes real x, real y returns nothing
local unit first
if .collisionEffect!="" then
call DestroyEffect(AddSpecialEffectTarget(.collisionEffect, .missile, "origin"))
endif
call GroupEnumUnitsInRange(g, x, y, .aoeDamage, null)
loop
set first = FirstOfGroup(g)
exitwhen first==null
if not IsUnitType(first, UNIT_TYPE_DEAD) and IsUnitEnemy(first, .pl) then
if .atk==null or .dmg==null then
call UnitDamageTarget(.missile, first, .damage, false, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_NORMAL, null)
else
call UnitDamageTarget(.missile, first, .damage, false, false, .atk, .dmg, null)
endif
endif
call GroupRemoveUnit(g, first)
endloop
call .stop()
endmethod
private method periodic takes nothing returns nothing
local unit first
local real xMissile = GetUnitX(.missile)
local real yMissile = GetUnitY(.missile)
if .distance > 0 then
set .distance = .distance - .speed
call SetUnitX(.missile, xMissile + .cos)
call SetUnitY(.missile, yMissile + .sin)
call GroupEnumUnitsInRange(g, xMissile, yMissile, .collision, null)
loop
set first = FirstOfGroup(g)
exitwhen first==null
if not IsUnitType(first, UNIT_TYPE_DEAD) and IsUnitEnemy(first, .pl) then
call .damageThem(xMissile, yMissile)
exitwhen true
endif
call GroupRemoveUnit(g, first)
endloop
else
call .damageThem(xMissile, yMissile)
endif
endmethod
implement T32x
/*
*
* API:
*
*/
static method create takes player owningPlayer, real xWhere, real yWhere, string missileModel returns thistype
local thistype this = allocate()
local Dummy d = Dummy.create(xWhere, yWhere, 0)
set .missile = d.unit
set .missileModel = AddSpecialEffectTarget(missileModel, .missile, "origin")
set .pl = owningPlayer
set .collisionEffect = ""
set .remove = d
if UnitAddAbility(.missile, 'Aloc') then
endif
return this
endmethod
method setupCollision takes real aoe, real collision, string collisionEffect returns nothing
set .aoeDamage = aoe
set .collision = collision
set .collisionEffect = collisionEffect
endmethod
method setupDamage takes real damage, attacktype attackType, damagetype damageType returns nothing
set .damage = damage
set .atk = attackType
set .dmg = damageType
endmethod
method launch takes real facingInRadiants, real distance, real height, real speed returns nothing
set .distance = distance
set .height = height
set .speed = speed
set .cos = speed * Cos(facingInRadiants)
set .sin = speed * Sin(facingInRadiants)
call SetUnitFlyHeight(.missile, height, 0)
call SetUnitFacing(.missile, facingInRadiants*bj_RADTODEG)
call .startPeriodic()
endmethod
endstruct
endlibrary
[/jass]
[/tab]
[tab=Instructions]
- Import the DUMMY.mdx model to your map
- Copy the DummySfx unit from the object editor to your map and change the model to DUMMY.mdx
- Copy ALL the Required Libraries and MissileCollicion folder to your map (except the DEMO)
- Change the DUMMY_ID of the Dummy library to your imported DummySfx
[/tab]
[tab=Demo]
[trigger]
MC Demo
Events
Player - Player 1 (Red) skips a cinematic sequence
Conditions
Actions
Custom script: local MC m
Custom script: set m = m.create(Player(0), GetUnitX(udg_Hero), GetUnitY(udg_Hero), "Abilities\\Weapons\\PoisonArrow\\PoisonArrowMissile.mdl")
Custom script: call m.setupCollision(300, 80, "Objects\\Spawnmodels\\Human\\FragmentationShards\\FragBoomSpawn.mdl")
Custom script: call m.setupDamage(100, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_NORMAL)
Custom script: call m.launch(GetUnitFacing(udg_Hero)*bj_DEGTORAD, 800, 75, 5)
[/trigger]
[jass]
scope MCInsaneTest
globals
private string array missileSfx
private string array sfxExplode
endglobals
struct MCD
static method insaneTest takes nothing returns nothing
local MC m = MC.create(Player(0), 0, 0, missileSfx[GetRandomInt(1,4)])
/*
* The locals is really not needed, I only put it here to see what are those numbers for
*/
local real angle = GetRandomReal(1, 6.2)
local real aoe = GetRandomReal(150, 200)
local real collision = GetRandomReal(80, 100)
local real damage = GetRandomReal(50, 150)
local real distance = GetRandomReal(500, 800)
local real height = GetRandomReal(75, 150)
local real speed = GetRandomReal(2, 10)
local integer randomSfx = GetRandomInt(1, 5)
call m.setupCollision(aoe, collision, sfxExplode[GetRandomInt(1,5)])
if GetRandomInt(1, 2)==1 then
call m.setupDamage(damage, null, null)
else
call m.setupDamage(damage, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_COLD)
endif
call m.launch(angle, distance, height, speed)
endmethod
static method onInit takes nothing returns nothing
set missileSfx[1] = "Abilities\\Weapons\\PoisonArrow\\PoisonArrowMissile.mdl"
set missileSfx[2] = "Abilities\\Weapons\\CannonTowerMissile\\CannonTowerMissile.mdl"
set missileSfx[3] = "Abilities\\Weapons\\WingedSerpentMissile\\WingedSerpentMissile.mdl"
set missileSfx[4] = "Abilities\\Weapons\\DemonHunterMissile\\DemonHunterMissile.mdl"
set sfxExplode[1] = "Objects\\Spawnmodels\\Human\\FragmentationShards\\FragBoomSpawn.mdl"
set sfxExplode[2] = "Objects\\Spawnmodels\\NightElf\\EntBirthTarget\\EntBirthTarget.mdl"
set sfxExplode[3] = "UI\\Feedback\\GoldCredit\\GoldCredit.mdl"
set sfxExplode[4] = "Objects\\Spawnmodels\\NightElf\\NEDeathMedium\\NEDeath.mdl"
set sfxExplode[5] = "Objects\\Spawnmodels\\Undead\\UDeathSmall\\UDeathSmall.mdl"
call TimerStart(CreateTimer(), 0.1, true, function MCD.insaneTest)
endmethod
endstruct
endscope
[/jass]
[jass]
scope MCExecutionMode
globals
private real array y
endglobals
struct MCDE
static method insaneTest takes nothing returns nothing
local MC m = MC.create(Player(0), -550, y[GetRandomInt(0,5)], "Abilities\\Weapons\\CannonTowerMissile\\CannonTowerMissile.mdl")
/*
* The locals is really not needed, I only put it here to see what are those numbers for
*/
local real angle = 180*bj_DEGTORAD
local real aoe = 150
local real collision = 80
local real damage = 75
local real distance = 1200
local real height = 30
local real speed = GetRandomReal(2, 10)
call m.setupCollision(aoe, collision, "Objects\\Spawnmodels\\Undead\\UDeathSmall\\UDeathSmall.mdl")
call m.setupDamage(damage, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_COLD)
call m.launch(angle, distance, height, speed)
endmethod
static method onInit takes nothing returns nothing
set y[0] = -790
set y[1] = -520
set y[2] = -260
set y[3] = -8
set y[4] = 260
set y[5] = 520
call TimerStart(CreateTimer(), 0.1, true, function MCDE.insaneTest)
endmethod
endstruct
endscope
[/jass]
[/tab]
[tab=Credits]
Vexorian for DUMMY.mdx model
Jesus4Lyf for Timer32
Nesthaurus for Dummy
Adiktuz tips in thsi thread
[/tab]
[tab=Changelogs]
v1.1
- Effects can now be seen at location Z
- API changed
[/tab]
[tab=Bugs]
-
[/tab]
[/tabs]
[youtube=]//www.youtube.com/watch?v=C5b03HFT988&feature=youtu.be&noredirect=1[/youtube]
//TESH.scrollpos=220
//TESH.alwaysfold=0
library UnitIndexer /* v4.0.2.7
*************************************************************************************
*
* Assigns unique indexes to units via unit user data.
*
*************************************************************************************
*
* */uses/*
*
* */ WorldBounds /* hiveworkshop.com/forums/jass-functions-413/snippet-worldbounds-180494/
* */ Event /* hiveworkshop.com/forums/submissions-414/snippet-event-186555/
*
************************************************************************************
*
* SETTINGS
*/
globals
constant integer ABILITIES_UNIT_INDEXER = 'A000'
endglobals
/*
************************************************************************************
*
* Functions
*
* function RegisterUnitIndexEvent takes boolexpr codeToRegister, Event unitIndexEvent returns nothing
* function TriggerRegisterUnitIndexEvent takes trigger triggerToRegister, Event unitIndexEvent returns nothing
*
* function GetUnitById takes integer index returns unit
* - Returns unit given a unit index
* function GetUnitId takes unit u returns integer
* - Returns unit index given a unit
*
* function IsUnitIndexed takes unit u returns boolean
* function IsUnitDeindexing takes unit u returns boolean
*
* function GetIndexedUnitId takes nothing returns integer
* function GetIndexedUnit takes nothing returns unit
*
************************************************************************************
*
* module UnitIndexStructMethods
* static method operator [] takes unit u returns thistype
* - Return GetUnitUserData(u)
*
* readonly unit unit
* - The indexed unit of the struct
*
************************************************************************************
*
* module UnitIndexStruct extends UnitIndexStructMethods
*
* - A pseudo module interface that runs a set of methods if they exist and provides
* - a few fields and operators. Runs on static ifs to minimize code.
*
* readonly boolean allocated
* - Is unit allocated for the struct
*
* Interface:
*
* - These methods don't have to exist. If they don't exist, the code
* - that calls them won't even be in the module.
*
* private method index takes nothing returns nothing
* - called when a unit is indexed and passes the filter.
* -
* - thistype this: Unit's index
* private method deindex takes nothing returns nothing
* - called when a unit is deindexed and is allocated for struct
* -
* - thistype this: Unit's index
* private static method filter takes unit unitToIndex returns boolean
* - Determines whether or not to allocate struct for unit
* -
* - unit unitToIndex: Unit being filtered
*
************************************************************************************
*
* struct UnitIndexer extends array
*
* - Controls the unit indexer system.
*
* static constant Event UnitIndexer.INDEX
* static constant Event UnitIndexer.DEINDEX
* - Don't register functions and triggers directly to the events. Register them via
* - RegisterUnitIndexEvent and TriggerRegisterUnitIndexEvent.
*
* static boolean enabled
* - Enables and disables unit indexing. Useful for filtering out dummy units.
*
************************************************************************************
*
* struct UnitIndex extends UnitIndexStructMethods
*
* - Constrols specific unit indexes.
*
* method lock takes nothing returns nothing
* - Locks an index. When an index is locked, it will not be recycled
* - when the unit is deindexed until all locks are removed. Deindex
* - events still fire at the appropriate times, the index just doesn't
* - get thrown into the recycler.
* method unlock takes nothing returns nothing
* - Unlocks an index.
*
************************************************************************************/
globals
private trigger q=CreateTrigger()
private trigger l=CreateTrigger()
private unit array e
private integer r=0
private integer y=0
private integer o=0
private boolean a=false
private integer array n
private integer array p
private integer array lc
endglobals
function GetIndexedUnitId takes nothing returns integer
return o
endfunction
function GetIndexedUnit takes nothing returns unit
return e[o]
endfunction
//! runtextmacro optional UNIT_LIST_LIB()
private struct PreLoader extends array
public static method run takes nothing returns nothing
call DestroyTimer(GetExpiredTimer())
set a=true
endmethod
public static method eval takes trigger t returns nothing
local integer f=n[0]
local integer d=o
loop
exitwhen 0==f
if (IsTriggerEnabled(t)) then
set o=f
if (TriggerEvaluate(t)) then
call TriggerExecute(t)
endif
else
exitwhen true
endif
set f=n[f]
endloop
set o=d
endmethod
public static method evalb takes boolexpr c returns nothing
local trigger t=CreateTrigger()
local thistype f=n[0]
local integer d=o
call TriggerAddCondition(t,c)
loop
exitwhen 0==f
set o=f
call TriggerEvaluate(t)
set f=n[f]
endloop
call DestroyTrigger(t)
set t=null
set o=d
endmethod
endstruct
//! runtextmacro optional UNIT_EVENT_MACRO()
private module UnitIndexerInit
private static method onInit takes nothing returns nothing
local integer i=15
local boolexpr bc=Condition(function thistype.onLeave)
local boolexpr bc2=Condition(function thistype.onEnter)
local group g=CreateGroup()
local player p
set INDEX=CreateEvent()
set DEINDEX=CreateEvent()
call TriggerRegisterEnterRegion(q,WorldBounds.worldRegion,bc2)
loop
set p=Player(i)
call TriggerRegisterPlayerUnitEvent(l,p,EVENT_PLAYER_UNIT_ISSUED_ORDER,bc)
call SetPlayerAbilityAvailable(p,ABILITIES_UNIT_INDEXER,false)
call GroupEnumUnitsOfPlayer(g,p,bc2)
exitwhen 0==i
set i=i-1
endloop
call DestroyGroup(g)
set bc=null
set g=null
set bc2=null
set p=null
call TimerStart(CreateTimer(),0,false,function PreLoader.run)
endmethod
endmodule
struct UnitIndex extends array
method lock takes nothing returns nothing
debug if (null!=e[this]) then
set lc[this]=lc[this]+1
debug else
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"UNIT INDEXER ERROR: ATTEMPT TO LOCK NULL INDEX")
debug endif
endmethod
method unlock takes nothing returns nothing
debug if (0<lc[this]) then
set lc[this]=lc[this]-1
if (0==lc[this] and null==e[this]) then
set n[this]=y
set y=this
endif
debug else
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"UNIT INDEXER ERROR: ATTEMPT TO UNLOCK UNLOCKED INDEX")
debug endif
endmethod
method operator unit takes nothing returns unit
return e[this]
endmethod
static method operator [] takes unit whichUnit returns thistype
return GetUnitUserData(whichUnit)
endmethod
endstruct
struct UnitIndexer extends array
readonly static Event INDEX
readonly static Event DEINDEX
static boolean enabled=true
private static method onEnter takes nothing returns boolean
local unit Q=GetFilterUnit()
local integer i
local integer d=o
if (enabled and Q!=e[GetUnitUserData(Q)] and 0==GetUnitUserData(Q)) then
if (0==y) then
set r=r+1
set i=r
else
set i=y
set y=n[y]
endif
call UnitAddAbility(Q,ABILITIES_UNIT_INDEXER)
call UnitMakeAbilityPermanent(Q,true,ABILITIES_UNIT_INDEXER)
call SetUnitUserData(Q,i)
set e[i]=Q
static if not LIBRARY_UnitList then
if (not a)then
set p[i]=p[0]
set n[p[0]]=i
set n[i]=0
set p[0]=i
endif
else
set p[i]=p[0]
set n[p[0]]=i
set n[i]=0
set p[0]=i
call GroupAddUnit(g,e[i])
endif
set o=i
call FireEvent(INDEX)
set o=d
endif
set Q=null
return false
endmethod
private static method onLeave takes nothing returns boolean
static if LIBRARY_UnitEvent then
implement optional UnitEventModule
else
local unit u=GetFilterUnit()
local integer i=GetUnitUserData(u)
local integer d=o
if (0==GetUnitAbilityLevel(u,ABILITIES_UNIT_INDEXER) and u==e[i]) then
static if not LIBRARY_UnitList then
if (not a)then
set n[p[i]]=n[i]
set p[n[i]]=p[i]
endif
else
set n[p[i]]=n[i]
set p[n[i]]=p[i]
call GroupRemoveUnit(g,e[i])
endif
set o=i
call FireEvent(DEINDEX)
set o=d
if (0==lc[i]) then
set n[i]=y
set y=i
endif
set e[i]=null
endif
set u=null
endif
return false
endmethod
implement UnitIndexerInit
endstruct
//! runtextmacro optional UNIT_EVENT_MACRO_2()
function RegisterUnitIndexEvent takes boolexpr c,integer ev returns nothing
call RegisterEvent(c, ev)
if (not a and ev==UnitIndexer.INDEX and 0!=n[0]) then
call PreLoader.evalb(c)
endif
endfunction
function TriggerRegisterUnitIndexEvent takes trigger t,integer ev returns nothing
call TriggerRegisterEvent(t,ev)
if (not a and ev == UnitIndexer.INDEX and 0!=n[0]) then
call PreLoader.eval(t)
endif
endfunction
function GetUnitById takes integer W returns unit
return e[W]
endfunction
function GetUnitId takes unit u returns integer
return GetUnitUserData(u)
endfunction
function IsUnitIndexed takes unit u returns boolean
return u==e[GetUnitUserData(u)]
endfunction
function IsUnitDeindexing takes unit u returns boolean
return IsUnitIndexed(u) and 0==GetUnitAbilityLevel(u,ABILITIES_UNIT_INDEXER)
endfunction
module UnitIndexStructMethods
static method operator [] takes unit u returns thistype
return GetUnitUserData(u)
endmethod
method operator unit takes nothing returns unit
return e[this]
endmethod
endmodule
module UnitIndexStruct
implement UnitIndexStructMethods
static if thistype.filter.exists then
static if thistype.index.exists then
static if thistype.deindex.exists then
readonly boolean allocated
else
method operator allocated takes nothing returns boolean
return filter(e[this])
endmethod
endif
else
method operator allocated takes nothing returns boolean
return filter(e[this])
endmethod
endif
elseif (thistype.index.exists) then
static if thistype.deindex.exists then
readonly boolean allocated
else
method operator allocated takes nothing returns boolean
return this==GetUnitUserData(e[this])
endmethod
endif
else
method operator allocated takes nothing returns boolean
return this==GetUnitUserData(e[this])
endmethod
endif
static if thistype.index.exists then
private static method onIndexEvent takes nothing returns boolean
static if thistype.filter.exists then
if (filter(e[o])) then
static if thistype.deindex.exists then
set thistype(o).allocated=true
endif
call thistype(o).index()
endif
else
static if thistype.deindex.exists then
set thistype(o).allocated=true
endif
call thistype(o).index()
endif
return false
endmethod
endif
static if thistype.deindex.exists then
private static method onDeindexEvent takes nothing returns boolean
static if thistype.filter.exists then
static if thistype.index.exists then
if (thistype(o).allocated) then
set thistype(o).allocated=false
call thistype(o).deindex()
endif
else
if (filter(e[o])) then
call thistype(o).deindex()
endif
endif
else
static if thistype.index.exists then
set thistype(o).allocated=false
endif
call thistype(o).deindex()
endif
return false
endmethod
endif
static if thistype.index.exists then
static if thistype.deindex.exists then
private static method onInit takes nothing returns nothing
call RegisterUnitIndexEvent(Condition(function thistype.onIndexEvent),UnitIndexer.INDEX)
call RegisterUnitIndexEvent(Condition(function thistype.onDeindexEvent),UnitIndexer.DEINDEX)
endmethod
else
private static method onInit takes nothing returns nothing
call RegisterUnitIndexEvent(Condition(function thistype.onIndexEvent),UnitIndexer.INDEX)
endmethod
endif
elseif thistype.deindex.exists then
private static method onInit takes nothing returns nothing
call RegisterUnitIndexEvent(Condition(function thistype.onDeindexEvent),UnitIndexer.DEINDEX)
endmethod
endif
endmodule
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library Event
//2.0.0.1
/////////////////////////////////////////////////////////////////////////
//function CreateEvent takes nothing returns integer
//function TriggerRegisterEvent takes trigger t, integer ev returns nothing
//function RegisterEvent takes boolexpr c, integer ev returns nothing
//function FireEvent takes integer ev returns nothing
//struct Event extends array
//static method create takes nothing returns thistype
//method registerTrigger takes trigger t returns nothing
//method register takes boolexpr c returns nothing
//method fire takes nothing returns nothing
/////////////////////////////////////////////////////////////////////////
globals
private real q=0
endglobals
struct Event extends array
private static integer w=0
private static trigger array e
static method create takes nothing returns thistype
set w=w+1
set e[w]=CreateTrigger()
return w
endmethod
method registerTrigger takes trigger t returns nothing
call TriggerRegisterVariableEvent(t,SCOPE_PRIVATE+"q",EQUAL,this)
endmethod
method register takes boolexpr c returns nothing
call TriggerAddCondition(e[this],c)
endmethod
method fire takes nothing returns nothing
set q=0
set q=this
call TriggerEvaluate(e[this])
endmethod
endstruct
function CreateEvent takes nothing returns Event
return Event.create()
endfunction
function TriggerRegisterEvent takes trigger t,Event ev returns nothing
call ev.registerTrigger(t)
endfunction
function RegisterEvent takes boolexpr c,Event ev returns nothing
call ev.register(c)
endfunction
function FireEvent takes Event ev returns nothing
call ev.fire()
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
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
//TESH.scrollpos=0
//TESH.alwaysfold=0
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//~~ Timer32 ~~ By Jesus4Lyf ~~ Version 1.06 ~~
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// What is Timer32?
// - Timer32 implements a fully optimised timer loop for a struct.
// - Instances can be added to the loop, which will call .periodic every
// PERIOD until .stopPeriodic() is called.
//
// =Pros=
// - Efficient.
// - Simple.
//
// =Cons=
// - Only allows one period.
// - The called method must be named ".periodic".
//
// Methods:
// - struct.startPeriodic()
// - struct.stopPeriodic()
//
// - private method periodic takes nothing returns nothing
//
// This must be defined in structs that implement Periodic Module.
// It will be executed by the module every PERIOD until .stopPeriodic() is called.
// Put "implement T32x" BELOW this method.
//
// Modules:
// - T32x
// Has no safety on .stopPeriodic or .startPeriodic (except debug messages
// to warn).
//
// - T32xs
// Has safety on .stopPeriodic and .startPeriodic so if they are called
// multiple times, or while otherwise are already stopped/started respectively,
// no error will occur, the call will be ignored.
//
// - T32
// The original, old version of the T32 module. This remains for backwards
// compatability, and is deprecated. The periodic method must return a boolean,
// false to continue running or true to stop.
//
// Details:
// - Uses one timer.
//
// - Do not, within a .periodic method, follow a .stopPeriodic call with a
// .startPeriodic call.
//
// How to import:
// - Create a trigger named T32.
// - Convert it to custom text and replace the whole trigger text with this.
//
// Thanks:
// - Infinitegde for finding a bug in the debug message that actually altered
// system operation (when in debug mode).
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
library T32 initializer OnInit
globals
public constant real PERIOD=0.03125
public constant integer FPS=R2I(1/PERIOD)
public integer Tick=0 // very useful.
//==============================================================================
private trigger Trig=CreateTrigger()
endglobals
//==============================================================================
// The standard T32 module, T32x.
//
module T32x
private thistype next
private thistype prev
private static method PeriodicLoop takes nothing returns boolean
local thistype this=thistype(0).next
loop
exitwhen this==0
call this.periodic()
set this=this.next
endloop
return false
endmethod
method startPeriodic takes nothing returns nothing
debug if this.prev!=0 or thistype(0).next==this then
debug call BJDebugMsg("T32 ERROR: Struct #"+I2S(this)+" had startPeriodic called while already running!")
debug endif
set thistype(0).next.prev=this
set this.next=thistype(0).next
set thistype(0).next=this
set this.prev=thistype(0)
endmethod
method stopPeriodic takes nothing returns nothing
debug if this.prev==0 and thistype(0).next!=this then
debug call BJDebugMsg("T32 ERROR: Struct #"+I2S(this)+" had stopPeriodic called while not running!")
debug endif
// This is some real magic.
set this.prev.next=this.next
set this.next.prev=this.prev
// This will even work for the starting element.
debug set this.prev=0
endmethod
private static method onInit takes nothing returns nothing
call TriggerAddCondition(Trig,Condition(function thistype.PeriodicLoop))
endmethod
endmodule
//==============================================================================
// The standard T32 module with added safety checks on .startPeriodic() and
// .stopPeriodic(), T32xs.
//
module T32xs
private thistype next
private thistype prev
private boolean runningPeriodic
private static method PeriodicLoop takes nothing returns boolean
local thistype this=thistype(0).next
loop
exitwhen this==0
call this.periodic()
set this=this.next
endloop
return false
endmethod
method startPeriodic takes nothing returns nothing
if not this.runningPeriodic then
set thistype(0).next.prev=this
set this.next=thistype(0).next
set thistype(0).next=this
set this.prev=thistype(0)
set this.runningPeriodic=true
endif
endmethod
method stopPeriodic takes nothing returns nothing
if this.runningPeriodic then
// This is some real magic.
set this.prev.next=this.next
set this.next.prev=this.prev
// This will even work for the starting element.
set this.runningPeriodic=false
endif
endmethod
private static method onInit takes nothing returns nothing
call TriggerAddCondition(Trig,Condition(function thistype.PeriodicLoop))
endmethod
endmodule
//==============================================================================
// The original T32 module, for backwards compatability only.
//
module T32 // deprecated.
private thistype next
private thistype prev
private static method PeriodicLoop takes nothing returns boolean
local thistype this=thistype(0).next
loop
exitwhen this==0
if this.periodic() then
// This is some real magic.
set this.prev.next=this.next
set this.next.prev=this.prev
// This will even work for the starting element.
debug set this.prev=0
endif
set this=this.next
endloop
return false
endmethod
method startPeriodic takes nothing returns nothing
debug if this.prev!=0 or thistype(0).next==this then
debug call BJDebugMsg("T32 ERROR: Struct #"+I2S(this)+" had startPeriodic called while already running!")
debug endif
set thistype(0).next.prev=this
set this.next=thistype(0).next
set thistype(0).next=this
set this.prev=thistype(0)
endmethod
private static method onInit takes nothing returns nothing
call TriggerAddCondition(Trig,Condition(function thistype.PeriodicLoop))
endmethod
endmodule
//==============================================================================
// System Core.
//
private function OnExpire takes nothing returns nothing
set Tick=Tick+1
call TriggerEvaluate(Trig)
endfunction
private function OnInit takes nothing returns nothing
call TimerStart(CreateTimer(),PERIOD,true,function OnExpire)
endfunction
endlibrary
//TESH.scrollpos=34
//TESH.alwaysfold=0
library Dummy /* v1.0.0.5
*************************************************************************************
*
* Allows one to create dummy units that are either at or are close
* to the angle specified.
*
* Dummy recycling minimizes the number of dummy units on the map while supporting near
* instant SetUnitFacing.
*
* Assigned dummy indexes are not unit indexes.
*
* Errors
* ----------------------------
*
* Any error will result in the system disabling itself and an error message
*
* -> May not kill dummies
* -> May not remove dummies
* -> May not attempt to recycle non dummies
*
*************************************************************************************
*
* Credits
*
* Vexorian for dummy.mdx
*
* Bribe
*
* Delayed recycling implemetation
* ----------------------------
*
* Bribe's delayed recycling implementation uses timestamps rather than timers, which
* helps improve performance.
*
* Stamps for queue node movement
* ----------------------------
*
* Convinced me that this was worth it
*
* Time it takes to rotate 180 degrees
* ----------------------------
*
* Supplied me with the number .73
*
*************************************************************************************
*
* */ uses /*
*
* /* Any Unit Indexer */
* */ UnitIndexer /* can be any, but one must be chosen
*
************************************************************************************
*
* SETTINGS
*
*/
globals
/*
* The unit id of dummy.mdx
*/
private constant integer DUMMY_ID = 'h000'
/*
* The space between angles for the recycler
*
* Angles used are angles from 0 to 359 in intervals of ANGLE_SPACING
*
* Higher spacing means less units but lower accuracy when creating the facing
*
*/
private constant integer ANGLE_SPACING = 15
/*
* How many projectiles to preload per angle
*
* Preloaded projectile count is 360/ANGLE_SPACING*MAX_PROJECTILES
*
*/
private constant integer PRELOAD_PROJECTILES_PER_ANGLE = 1//50
/*
* How much to delay before recycling dummy
*/
private constant real RECYCLE_DELAY = 2
endglobals
/*
************************************************************************************
*
* library MissileRecycler uses Dummy
* ----------------------------
*
* For compatibility with Bribe's resource
*
* function GetRecycledMissile takes real x, real y, real z, real facing returns unit
* function RecycleMissile takes unit whichUnit returns nothing
*
************************************************************************************
*
* Functions
* ----------------------------
*
* function IsUnitDummy takes unit whichUnit returns boolean
*
************************************************************************************
*
*
* struct Dummy extends array
*
* Creators/Destructors
* ----------------------------
*
* static method create takes real x, real y, real facing returns Dummy
* - For those of you who really want this to return a unit, getting
* - the unit from this is very easy, so don't whine
*
* - Dummy.create().unit -> unit
*
* method destroy takes nothing returns nothing
* - For those of you who really want this to take a unit, getting
* - the dummy index is very easy.
*
* - Dummy[whichUnit].destroy()
*
* Fields
* ----------------------------
*
* readonly unit unit
*
* Operators
* ----------------------------
*
* static method operator [] takes unit dummyUnit returns Dummy
*
************************************************************************************/
private keyword Queue
globals
/*
* Used for dummy instancing
* Dummy indexes are never destroyed, so there is no need for a recycler
*/
private Queue dummyCount = 0
/*
* Used to retrieve unit handle via dummy index
*/
private unit array dummies
private integer array indexPointer
private integer array dummyPointer
/*
* The owner of all dummy units. This shouldn't be changed.
*/
private constant player DUMMY_OWNER = Player(15)
/*
* Used to apply time stamps to dummies for recycling
* purposes. A dummy is only considered recycled if its
* stamp is less than the elapsed time of stamp timer.
*/
private timer stampTimer
endglobals
function IsUnitDummy takes unit whichUnit returns boolean
return dummies[GetUnitUserData(whichUnit)] == whichUnit
endfunction
/*
* min == max - 1
* max == min + 1
*
* variance of counts must be 1
*/
private struct ArrayStack extends array
/*
* The minimum and maximum counts
*/
static thistype max = 0
static thistype min = 0
/*
* list[count].first
*/
thistype first
/*
* queue.size
*/
thistype count_p
/*
* list[count].next
*/
thistype next
/*
* list[count].prev
*/
thistype prev
/*
* list[count].first -> queue of dummies
*/
static method operator [] takes thistype index returns thistype
return index.first
endmethod
/*
* list[count].add(queue of dummies)
*/
private method add takes thistype node returns nothing
/*
* Update min/max
*/
if (integer(this) > integer(max)) then
set max = this
elseif (integer(this) < integer(min)) then
set min = this
endif
/*
* Push on to front of list like a stack
*/
set node.next = first
set node.next.prev = node
set node.prev = 0
set first = node
set node.count_p = this
endmethod
/*
* list[count].remove(list of dummies)
*/
private method remove takes thistype node returns nothing
/*
* If node is the first, update the first
*/
if (node == first) then
set first = node.next
/*
* If list is empty, update min/max
*/
if (0 == first) then
if (this == min) then
set min = max
else
set max = min
endif
endif
else
/*
* Simple removal
*/
set node.prev.next = node.next
set node.next.prev = node.prev
endif
endmethod
method operator count takes nothing returns integer
return count_p
endmethod
method operator count= takes thistype value returns nothing
/*
* Remove from list node was on
*/
call count_p.remove(this)
/*
* Add to new list
*/
call value.add(this)
endmethod
endstruct
/*
* queue = angle + 1
*/
private struct Queue extends array
private real stamp
thistype next
thistype last
/*
* Update dummy count for queue
*/
private method operator count takes nothing returns integer
return ArrayStack(this).count
endmethod
private method operator count= takes integer value returns nothing
set ArrayStack(this).count = value
endmethod
/*
* Queue with smallest number of dummies
*/
private static method operator min takes nothing returns thistype
return ArrayStack.min.first
endmethod
/*
* Queue with largest number of dummies
*/
private static method operator max takes nothing returns thistype
return ArrayStack.max.first
endmethod
static method add takes thistype dummy returns nothing
/*
* Always add to the queue with the least amount of dummies
*/
local thistype this = min
/*
* Add to end of queue
*/
set last.next = dummy
set last = dummy
set dummy.next = 0
/*
* Update queue count
*/
set count = count + 1
/*
* Match unit angle with queue
*/
call SetUnitFacing(dummies[indexPointer[dummy]], this - 1)
/*
* Apply stamp so that dummy isn't used until the stamp is expired
*/
set dummy.stamp = TimerGetElapsed(stampTimer) + RECYCLE_DELAY - .01
endmethod
static method pop takes thistype this, real x, real y, real facing returns integer
/*
* Retrieve queue and first dummy on queue given angle
*/
local unit dummyUnit //dummy unit
local thistype dummyIndex = next //dummy index
local integer unitIndex //unit idex
local thistype this2
local thistype node
local real stamp
/*
* If the queue is empty, return new dummy
*/
if (0 == dummyIndex or dummyIndex.stamp > TimerGetElapsed(stampTimer)) then
/*
* Allocate new dummy
*/
debug if (dummyCount == 8191) then
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,"DUMMY RECYCLER FATAL ERROR: DUMMY OVERLOAD")
debug set Dummy.enabled = false
debug set this = 1/0
debug endif
set dummyIndex = dummyCount + 1
set dummyCount = dummyIndex
/*
* Create and initialize new unit handle
*/
set dummyUnit = CreateUnit(DUMMY_OWNER, DUMMY_ID, x, y, facing)
set unitIndex = GetUnitUserData(dummyUnit)
set indexPointer[dummyIndex] = unitIndex
set dummyPointer[unitIndex] = dummyIndex
set dummies[unitIndex] = dummyUnit
call UnitAddAbility(dummyUnit, 'Amrf')
call UnitRemoveAbility(dummyUnit, 'Amrf')
call PauseUnit(dummyUnit, true)
return dummyIndex
endif
/*
* Remove the dummy from the queue
*/
set next = dummyIndex.next
if (0 == next) then
set last = this
endif
/*
* Only remove from the count if the queue has most dummies in it
*
* If queue doesn't have most dummies in it, take a dummy from the queue
* with most dummies in it and keep count the same
*/
if (count == ArrayStack.max) then
set count = count - 1
else
/*
* Retrieve the queue with most dummies in it as well as the
* first dummy in that queue
*/
set this2 = max
set node = this2.next
/*
* Remove first dummy from largest queue
*/
if (0 == node.next) then
set this2.last = this2
else
set this2.next = node.next
endif
set this2.count = this2.count - 1
/*
* Add first dummy to current queue
*/
set last.next = node
set last = node
set node.next = 0
/*
* Match unit angle with queue
*/
call SetUnitFacing(dummies[indexPointer[node]], this - 1)
/*
* .73 seconds is how long it takes for a dummy to rotate 180 degrees
*
* Credits to Bribe for these 4 lines of code and the .73 value
*/
set stamp = TimerGetElapsed(stampTimer) + .73
if (stamp > node.stamp) then
set node.stamp = stamp
endif
endif
/*
* Move dummy to target position
*/
set dummyUnit = dummies[indexPointer[dummyIndex]]
call SetUnitX(dummyUnit, x)
call SetUnitY(dummyUnit, y)
call SetUnitFacing(dummyUnit, facing)
set dummyUnit = null
/*
* Return first dummy from current queue
*/
return dummyIndex
endmethod
endstruct
struct Dummy extends array
debug static boolean enabled = false
debug private boolean allocated
/*
* Retrieve index given unit handle
*/
static method operator [] takes unit dummyUnit returns thistype
debug if (not enabled) then
debug return 1/0
debug endif
return GetUnitUserData(dummyUnit)
endmethod
/*
* Retrieve unit handle given index
*/
method operator unit takes nothing returns unit
debug if (not enabled) then
debug set this = 1/0
debug endif
return dummies[this]
endmethod
/*
* Slightly faster than ModuloInteger due to less args + constants
*/
private static method getClosestAngle takes integer angle returns integer
set angle = angle - angle/360*360
if (0 > angle) then
set angle = angle + 360
endif
return angle/ANGLE_SPACING*ANGLE_SPACING
endmethod
/*
* Returns either a new or a recycled dummy index
*/
static method create takes real x, real y, real facing returns Dummy
static if DEBUG_MODE then
local thistype this
if (not enabled) then
set x = 1/0
endif
set this = indexPointer[Queue.pop(getClosestAngle(R2I(facing)) + 1, x, y, facing)]
debug set allocated = true
return this
else
return indexPointer[Queue.pop(getClosestAngle(R2I(facing)) + 1, x, y, facing)]
endif
endmethod
/*
* Recycles dummy index
*/
method destroy takes nothing returns nothing
debug if (not enabled) then
debug set this = 1/0
debug endif
/*
* If the recycled dummy was invalid, issue critical error
*/
debug if (0 == GetUnitTypeId(unit) or 0 == GetWidgetLife(unit) or not allocated) then
debug if (not allocated) then
debug call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 10, "DUMMY RECYCLER FATAL ERROR: DOUBLE FREE")
debug elseif (null == unit) then
debug call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 10, "DUMMY RECYCLER FATAL ERROR: REMOVED A DUMMY")
debug elseif (0 == GetWidgetLife(unit)) then
debug call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 10, "DUMMY RECYCLER FATAL ERROR: KILLED A DUMMY")
debug else
debug call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 10, "DUMMY RECYCLER FATAL ERROR: ATTEMPTED TO RECYCLE NON DUMMY UNIT")
debug endif
debug set enabled = false
debug set this = 1/0
debug endif
debug if (indexPointer[dummyPointer[this]] != this) then
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"ERROR")
debug endif
debug set allocated = false
call SetUnitPosition(dummies[this], 2147483647, 2147483647)
call Queue.add(dummyPointer[this])
endmethod
endstruct
/*
* Initialization
*/
private function Initialize takes nothing returns nothing
local unit dummy
local integer last
local integer angle
local ArrayStack queue
local integer count
/*
* This timer
*/
set stampTimer = CreateTimer()
call TimerStart(stampTimer, 604800, false, null)
/*
* The highest possible angle
*/
set last = 360/ANGLE_SPACING*ANGLE_SPACING
if (360 == last) then
set last = last - ANGLE_SPACING
if (last < ANGLE_SPACING) then
set last = 0
endif
endif
/*
* The lowest possible angle
*/
set angle = 0
/*
* Start dummy count at the last possible angle so that
* angles don't overlap with dummy indexes. This is done
* to simplify queue algorithm and improve overall performance.
* At most 360 possible dummy unit indexes will be lost due to this.
*/
set dummyCount = last + 1
/*
* Initialize ArrayStack
*/
set ArrayStack.min = PRELOAD_PROJECTILES_PER_ANGLE
set ArrayStack.max = PRELOAD_PROJECTILES_PER_ANGLE
set ArrayStack(PRELOAD_PROJECTILES_PER_ANGLE).first = 1
loop
/*
* queue pointer is angle + 1
*/
set queue = angle + 1
/*
* Only add projectiles to queue if MAX_PROJECTILES < 0
*/
if (0 < PRELOAD_PROJECTILES_PER_ANGLE) then
set count = PRELOAD_PROJECTILES_PER_ANGLE
set queue.count_p = PRELOAD_PROJECTILES_PER_ANGLE
set dummyCount = dummyCount + 1
set Queue(queue).next = dummyCount
/*
* Create and add all dummies to queue
*/
loop
/*
* Create and initialize unit handle
*/
set dummy = CreateUnit(DUMMY_OWNER, DUMMY_ID, 0, 0, angle)
set indexPointer[dummyCount] = GetUnitUserData(dummy)
set dummyPointer[GetUnitUserData(dummy)] = dummyCount
set dummies[indexPointer[dummyCount]] = dummy
call UnitAddAbility(dummy, 'Amrf')
call UnitRemoveAbility(dummy, 'Amrf')
call PauseUnit(dummy, true)
set count = count - 1
exitwhen 0 == count
/*
* Point to next
*/
set dummyCount.next = dummyCount + 1
set dummyCount = dummyCount + 1
endloop
set Queue(queue).last = dummyCount
else
set Queue(queue).last = queue
endif
exitwhen last == angle
/*
* Go to next angle
*/
set angle = angle + ANGLE_SPACING
/*
* Link queues together
*/
set queue.next = angle + 1
set ArrayStack(angle + 1).prev = queue
/*
* Go to next queue
*/
set queue = angle + 1
endloop
set dummy = null
debug set Dummy.enabled = true
endfunction
private module Init
private static method onInit takes nothing returns nothing
static if DEBUG_MODE then
call ExecuteFunc(SCOPE_PRIVATE + "Initialize")
if (not Dummy.enabled) then
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,"DUMMY RECYCLER FATAL ERROR: INITIALIZATION CRASHED, LOWER PRELOAD DUMMY COUNT")
endif
else
call Initialize()
endif
endmethod
endmodule
private struct Inits extends array
implement Init
endstruct
endlibrary
library MissileRecycler uses Dummy
function GetRecycledMissile takes real x, real y, real z, real facing returns unit
local Dummy dummy = Dummy.create(x, y, facing)
call SetUnitFlyHeight(dummy.unit, z, 0)
return dummy.unit
endfunction
function RecycleMissile takes unit u returns nothing
call Dummy[u].destroy()
endfunction
endlibrary
//TESH.scrollpos=35
//TESH.alwaysfold=0
library MissileCollision /* v1.1
*************************************************************************************
*
* by mckill2009
*
*************************************************************************************
*
* An extremely simple missile projectile system that collides and damages on enemy units
* or blows on impact when desired distance is reached.
* The missile is recycle able so that it will not create different dummies everytime.
*
*************************************************************************************
*
* */ uses /*
*
* */ T32 /* http://www.thehelper.net/forums/showthread.php/132538-Timer32
* */ Dummy /* http://www.hiveworkshop.com/forums/jass-resources-412/system-dummy-213908/
*
************************************************************************************
*
* Credits
*
* Vexorian
* -----------------------
* For his DUMMY.mdx model
*
* Jesus4Lyf
* -----------------------
* For his Timer32 library
*
* Nesthaurus
* -----------------------
* For his Dummy library
*
************************************************************************************
*
* Installation:
* - Import the DUMMY.mdx model to your map
* - Copy the DummySfx unit from the object editor to your map and change the model to DUMMY.mdx
* - Copy ALL the Required Libraries and MissileCollicion folder to your map (except the DEMO)
* - Change the DUMMY_ID of the Dummy library to your imported DummySfx
*
************************************************************************************
*
* API:
* struct MC
* static method create takes player owningPlayer, real xWhere, real yWhere, string missileModel returns thistype
* - owningPlayer is the owner of the missile
* - xWhere and yWhere is the coordinate where the missile's origin or launched
*
* method setupCollision takes real aoe, real collision, string collisionEffect returns nothing
* - aoe is the area of effect damage when missile explodes
* - collision, missile will explode and deals damage if it collides with the first enemy unit in range
* - collisionEffect, the effect or eye candy of the explosion
*
* method setupDamage takes real damage, attacktype attackType, damagetype damageType returns nothing
* - damage, the damage when missile explodes
* - attackType and damageType is self explanatory
*
* method launch takes real facingInRadiants, real distance, real height, real speed returns nothing
* - facingInRadiants is the angle where the missile goes
* - distance is how far the missile traver
* - height and speed is the height of the missile and speed travel of the missile respectively
*
*
************************************************************************************
*/
private struct Delay
unit missile
real del
Dummy remove
endstruct
struct MC
private unit missile
private real aoeDamage
private real collision
private real distance
private real damage
private real height
private real speed
private real cos
private real sin
private string collisionEffect
private effect missileModel
private player pl
private attacktype atk
private damagetype dmg
private Dummy remove
private static group g = CreateGroup()
/*
*
* This block is used to delay the dummy to be recycled so that the effects will be
* visible from their origin attachment
*
*/
private static timer t = CreateTimer()
private static integer index = 0
private static integer array indexAR
private static method looper takes nothing returns nothing
local Delay this
local integer i = 0
loop
set i = i+1
set this = indexAR[i]
set this.del = this.del - 0.03125
if 0 > this.del then
call this.remove.destroy()
set this.missile = null
call this.destroy()
set indexAR[i] = indexAR[index]
set i = i - 1
set index = index - 1
if index==0 then
call PauseTimer(t)
endif
endif
exitwhen i==index
endloop
endmethod
private method delay takes nothing returns nothing returns nothing
local Delay m = Delay.create()
set m.missile = .missile
set m.del = 2.0
set m.remove = .remove
if index==0 then
call TimerStart(t, 0.03125, true, function thistype.looper)
endif
set index = index + 1
set indexAR[index] = m
endmethod
/*
*
* End of Delay block
*
*/
private method stop takes nothing returns nothing
call .delay()
call DestroyEffect(.missileModel)
set .missile = null
set .missileModel = null
call .stopPeriodic()
call .deallocate()
endmethod
private method damageThem takes real x, real y returns nothing
local unit first
if .collisionEffect!="" then
call DestroyEffect(AddSpecialEffectTarget(.collisionEffect, .missile, "origin"))
endif
call GroupEnumUnitsInRange(g, x, y, .aoeDamage, null)
loop
set first = FirstOfGroup(g)
exitwhen first==null
if not IsUnitType(first, UNIT_TYPE_DEAD) and IsUnitEnemy(first, .pl) then
if .atk==null or .dmg==null then
call UnitDamageTarget(.missile, first, .damage, false, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_NORMAL, null)
else
call UnitDamageTarget(.missile, first, .damage, false, false, .atk, .dmg, null)
endif
endif
call GroupRemoveUnit(g, first)
endloop
call .stop()
endmethod
private method periodic takes nothing returns nothing
local unit first
local real xMissile = GetUnitX(.missile)
local real yMissile = GetUnitY(.missile)
if .distance > 0 then
set .distance = .distance - .speed
call SetUnitX(.missile, xMissile + .cos)
call SetUnitY(.missile, yMissile + .sin)
call GroupEnumUnitsInRange(g, xMissile, yMissile, .collision, null)
loop
set first = FirstOfGroup(g)
exitwhen first==null
if not IsUnitType(first, UNIT_TYPE_DEAD) and IsUnitEnemy(first, .pl) then
call .damageThem(xMissile, yMissile)
exitwhen true
endif
call GroupRemoveUnit(g, first)
endloop
else
call .damageThem(xMissile, yMissile)
endif
endmethod
implement T32x
/*
*
* API:
*
*/
static method create takes player owningPlayer, real xWhere, real yWhere, string missileModel returns thistype
local thistype this = allocate()
local Dummy d = Dummy.create(xWhere, yWhere, 0)
set .missile = d.unit
set .missileModel = AddSpecialEffectTarget(missileModel, .missile, "origin")
set .pl = owningPlayer
set .collisionEffect = ""
set .remove = d
if UnitAddAbility(.missile, 'Aloc') then
endif
return this
endmethod
method setupCollision takes real aoe, real collision, string collisionEffect returns nothing
set .aoeDamage = aoe
set .collision = collision
set .collisionEffect = collisionEffect
endmethod
method setupDamage takes real damage, attacktype attackType, damagetype damageType returns nothing
set .damage = damage
set .atk = attackType
set .dmg = damageType
endmethod
method launch takes real facingInRadiants, real distance, real height, real speed returns nothing
set .distance = distance
set .height = height
set .speed = speed
set .cos = speed * Cos(facingInRadiants)
set .sin = speed * Sin(facingInRadiants)
call SetUnitFlyHeight(.missile, height, 0)
call SetUnitFacing(.missile, facingInRadiants*bj_RADTODEG)
call .startPeriodic()
endmethod
endstruct
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
scope MCInsaneTest
globals
private string array missileSfx
private string array sfxExplode
endglobals
struct MCD
static method insaneTest takes nothing returns nothing
local MC m = MC.create(Player(0), 0, 0, missileSfx[GetRandomInt(1,4)])
/*
* The locals is really not needed, I only put it here to see what are those numbers for
*/
local real angle = GetRandomReal(1, 6.2)
local real aoe = GetRandomReal(150, 200)
local real collision = GetRandomReal(80, 100)
local real damage = GetRandomReal(50, 150)
local real distance = GetRandomReal(500, 800)
local real height = GetRandomReal(75, 150)
local real speed = GetRandomReal(2, 10)
local integer randomSfx = GetRandomInt(1, 5)
call m.setupCollision(aoe, collision, sfxExplode[GetRandomInt(1,5)])
if GetRandomInt(1, 2)==1 then
call m.setupDamage(damage, null, null)
else
call m.setupDamage(damage, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_COLD)
endif
call m.launch(angle, distance, height, speed)
endmethod
static method onInit takes nothing returns nothing
set missileSfx[1] = "Abilities\\Weapons\\PoisonArrow\\PoisonArrowMissile.mdl"
set missileSfx[2] = "Abilities\\Weapons\\CannonTowerMissile\\CannonTowerMissile.mdl"
set missileSfx[3] = "Abilities\\Weapons\\WingedSerpentMissile\\WingedSerpentMissile.mdl"
set missileSfx[4] = "Abilities\\Weapons\\DemonHunterMissile\\DemonHunterMissile.mdl"
set sfxExplode[1] = "Objects\\Spawnmodels\\Human\\FragmentationShards\\FragBoomSpawn.mdl"
set sfxExplode[2] = "Objects\\Spawnmodels\\NightElf\\EntBirthTarget\\EntBirthTarget.mdl"
set sfxExplode[3] = "UI\\Feedback\\GoldCredit\\GoldCredit.mdl"
set sfxExplode[4] = "Objects\\Spawnmodels\\NightElf\\NEDeathMedium\\NEDeath.mdl"
set sfxExplode[5] = "Objects\\Spawnmodels\\Undead\\UDeathSmall\\UDeathSmall.mdl"
call TimerStart(CreateTimer(), 0.1, true, function MCD.insaneTest)
endmethod
endstruct
endscope
//TESH.scrollpos=0
//TESH.alwaysfold=0
scope MCExecutionMode
globals
private real array y
endglobals
struct MCDE
static method insaneTest takes nothing returns nothing
local MC m = MC.create(Player(0), -550, y[GetRandomInt(0,5)], "Abilities\\Weapons\\CannonTowerMissile\\CannonTowerMissile.mdl")
/*
* The locals is really not needed, I only put it here to see what are those numbers for
*/
local real angle = 180*bj_DEGTORAD
local real aoe = 150
local real collision = 80
local real damage = 75
local real distance = 1200
local real height = 30
local real speed = GetRandomReal(2, 10)
call m.setupCollision(aoe, collision, "Objects\\Spawnmodels\\Undead\\UDeathSmall\\UDeathSmall.mdl")
call m.setupDamage(damage, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_COLD)
call m.launch(angle, distance, height, speed)
endmethod
static method onInit takes nothing returns nothing
set y[0] = -790
set y[1] = -520
set y[2] = -260
set y[3] = -8
set y[4] = 260
set y[5] = 520
call TimerStart(CreateTimer(), 0.1, true, function MCDE.insaneTest)
endmethod
endstruct
endscope