- Joined
- May 16, 2012
- Messages
- 644
Hello Hive!
This is a Missile system that I've put together based on Vexorian , BPower and Dirac Missiles System, so Full credits to them. Also thanks to AGD for the tips and ideas to solve some problems.
Why?
- Well, recently I've been coding quite a bit of resources and the stuff that i was creating usually required a missile system, but all those system usually have a little flaw that forced me to use something else in order to achieve what i wanted.
For example, let's say i wanted a missile that can arc and homing to a target, well there goes BPower's system. It can arc/curve but can't do homing with arc/curve, so the options left are Vexorian xemissile which can arc and do homing but cant curve and Dirac's Missile system. Then again more problems with those, because Vexorian system do not have a lot of the useful events like Dirac's and BPower's systems. Dirac's system can arc/curve and do homing but it's timer is never stopped and it do not have some useful events that the BPwer system has. So i decided to get all three system together and build my own system with the stuff i think is useful and with the syntax i think is the most friendly, because I'm not a genius of vJASS myself.
Since i based this system on their job, full credits to them.
There are 2 versions of this system. The first is for versions 1.30.x and lower. This version uses the dummy method. The second version is for patch 1.31+ and uses the new natives to manipulate effects, so no dummy, only effects.
Available Members:
Available Methods Calls:
Available Operators;
Available Events:
And a little video demonstrating the system
This is a Missile system that I've put together based on Vexorian , BPower and Dirac Missiles System, so Full credits to them. Also thanks to AGD for the tips and ideas to solve some problems.
Why?
- Well, recently I've been coding quite a bit of resources and the stuff that i was creating usually required a missile system, but all those system usually have a little flaw that forced me to use something else in order to achieve what i wanted.
For example, let's say i wanted a missile that can arc and homing to a target, well there goes BPower's system. It can arc/curve but can't do homing with arc/curve, so the options left are Vexorian xemissile which can arc and do homing but cant curve and Dirac's Missile system. Then again more problems with those, because Vexorian system do not have a lot of the useful events like Dirac's and BPower's systems. Dirac's system can arc/curve and do homing but it's timer is never stopped and it do not have some useful events that the BPwer system has. So i decided to get all three system together and build my own system with the stuff i think is useful and with the syntax i think is the most friendly, because I'm not a genius of vJASS myself.
Since i based this system on their job, full credits to them.
There are 2 versions of this system. The first is for versions 1.30.x and lower. This version uses the dummy method. The second version is for patch 1.31+ and uses the new natives to manipulate effects, so no dummy, only effects.
Available Members:
JASS:
Coordinates impact -> the impact coordiantes (x, y, z, anngle, slope, alpha, distance)
Coordinates origin -> same, but for origin
Effect effect -> The missile (only in version 1.31+)
//-------------------------------------------------------
readonly real x -> current x position of the missile
readonly real y -> current y position of the missile
readonly real z -> current z position of the missile
readonly real prevX -> last x position of the missile
readonly real prevY -> last y position of the missile
readonly real prevZ -> last z position of the missile
readonly real turn -> the turn rate of the missile
readonly real veloc -> the missile speed (change it using the .speed or .duration operator)
readonly real travel -> distance travelled
readonly unit dummy -> the dummy unit (only in version 1.30.x -)
readonly boolean launched -> true if the missile was already launched
readonly boolean allocated -> true if the missile can still perform its movement operations
//-------------------------------------------------------
unit source -> the source unit (optional)
unit target -> the target unit (optional and if set to a valid unit the missile will be homing)
player owner -> the owner of the missile (optional)
real collision -> the collision size of the missile (optional. The onHit and onDestructable events only works if this is greater than 0)
real damage -> stores the damage you want the missile to deal (optional)
real acceleration -> if different than 0 then the missile will change its speed with time (optional)
integer data -> just in case you want to pass some information to the missile to retrieve it later (optional)
Available Methods Calls:
JASS:
(call)
method deflect takes real x, real y returns nothing
- Deflects the missile from the target point, changing
- its course to the new x and y.
- If target is a valid unit, deflect will null the target
- and then deflects the missile
(call)
method deflectZ takes real x, real y, real z returns nothing
- Deflects the missile from the target point, changing
- its course to the new x, y and z.
- If target is a valid unit, deflect will null the target
- and then deflects the missile
(call)
method bounce takes nothing returns nothing
- Bounces the missile from its current Z position.
- Useful when assigning targets to missiles that were
- already active, it fixes the rough Z position change.
(call)
method flush takes widget w returns nothing
- call this method to allow the missile to be able to hit
- a unit or destructable again
(call)
method flushAll nothing returns nothing
- flushes the hit table for the missile
(call)
method hitted takes widget w returns boolean
- returns true if the missile has hitted the widget
Available Operators;
JASS:
/* -------------------------- Model of the missile -------------------------- */
method operator model= takes string path returns nothing
method operator model takes nothing returns string
/* ----------------------------- Curved movement ---------------------------- */
method operator curve= takes real value returns nothing
method operator curve takes nothing returns real
/* ----------------------------- Arced Movement ----------------------------- */
method operator arc= takes real value returns nothing
method operator arc takes nothing returns real
/* ------------------------------ Effect scale ------------------------------ */
method operator scale= takes real v returns nothing
method operator scale takes nothing returns real
/* ------------------------------ Missile Speed ----------------------------- */
method operator speed= takes real newspeed returns nothing
method operator speed takes nothing returns real
/* ------------------------------- Flight Time ------------------------------ */
method operator duration= takes real flightTime returns nothing
Available Events:
JASS:
(optional)
method onHit takes unit hit returns boolean
- Runs every time the missile collides with a unit.
- If returns true the missile is destroyed.
(optional)
method onDestructable takes destructable dest returns boolean
- Runs every time the missile collides with a destructable.
- If returns true the missile is destroyed.
(optional)
method onItem takes item i returns boolean
- Runs every time the missile collides with an item.
- If returns true the missile is destroyed.
(optional)
method onMissile takes Missiles missile returns boolean
- Runs every time the missile collides with another missile.
- By default, missiles collide only once
- If returns true the missile is destroyed.
- Please, be aware that this method can be very performance
- intensive, so careful!
(optional)
method onPeriod takes nothing returns boolean
- Runs every period.
- If returns true the missile is destroyed.
(optional)
method onFinish takes nothing returns boolean
- Runs whenever the missile finishes its course.
- If returns true the missile is destroyed.
(optional)
method onTerrain takes nothing returns boolean
- Runs whenever the missile collides with terrain height
- greater then the missile current z
- If returns true the missile is destroyed.
(optional)
method onRemove takes Missile this returns nothing
- Runs whenever the missile is deallocated.
JASS:
library Missiles requires optional WorldBounds
/* ----------------------- Missiles v1.0 by Chopinski ----------------------- */
// Thanks and Full Credits to BPower, Dirac and Vexorian for the Missile Library's at which i based
// this Missiles library. Credits to Vexorian for the dummy model.
// How to Import:
// 1 -First copy the Missile dummy unit into your map and then import the dummy.mdx
// model, setting the missile dummy model path to imported dummy.mdx model.
// Dummy model: https://www.hiveworkshop.com/threads/vexorians-dummy-model.149230/
// WorldBounds: https://raw.githubusercontent.com/nestharus/JASS/master/jass/Systems/WorldBounds/script.j
// 2 - Copy this library into your map and set the
// DUMMY_RAW_CODE to the raw code of the missile dummy (ctrl + d) and you
// are done
// How to Use:
// This system works almost identicaly to the Missile library by BPower but
// with a more user friendly interface in my opinion. Also this system
// allows you to create Arc/Curved Homing missiles like Dirac's system.
// Differently than both BPower and Dirac systems, you are not required
// to use the ImplementStruct at the end of your struct. To make your struct
// behave like a Missile simply make it extends Missiles and you are done.
// After that you will have access to the events in the MissileEvents Interface.
// Simply declare the event you want for your strcut to have and the system
// takes care of the rest. You can access the members and functions using
// the "this" or "." syntax which is a plus. to terminate a missile, simply
// return true or call terminate() within the method. Example:
// struct MySpell extends Missiles
// method onPeriod takes nothing returns boolean
// // will display the missile current position
// call ClearTextMessages()
// call BJDebugMsg(I2S(.x))
// call BJDebugMsg(I2S(.y))
// call BJDebugMsg(I2S(.z))
// return false
// endmethod
// endstruct
// function Spell takes nothing returns nothing
// // the create method takes the initial and the final coordinates
// // if the target member is set, the missile home
// local MySpell spell = MySpell.create(fromX, fromY, fromZ, toX, toY, toZ)
// set spell.source = GetTriggerUnit()
// set spell.target = GetSpellTargetUnit() -> when a target is specified the missile will be homing
// set spell.speed = 1000 -> warcraft 3 speed unit
// set spell.duration = 1.5 -> will set the speed to match the time passed
// set spell.model = "Some Model.dmx"
// set spell.scale = 1.3
// set spell.arc = 30 (degrees converted to radians internally)
// set spell.curve = GetRandomReal(-40, 40) (degrees converted to radians internally
// call spell.launch()
// endfunction
// Available members and methods:
// Coordinates impact -> the impact coordiantes (x, y, z, anngle, slope, alpha, distance)
// Coordinates origin -> same, but for origin
// //-------------------------------------------------------
// readonly real x -> current x position of the missile
// readonly real y -> current y position of the missile
// readonly real z -> current z position of the missile
// readonly real prevX -> last x position of the missile
// readonly real prevY -> last y position of the missile
// readonly real prevZ -> last z position of the missile
// readonly real height -> the arc of the missile (change it using the .arc operator)
// readonly real turn -> the turn rate of the missile
// readonly real open -> the curvature of the missile (change it using the .curve operator)
// readonly real veloc -> the missile speed (change it using the .speed or .duration operator)
// readonly real travel -> distance travelled
// readonly unit dummy -> the dummy unit
// readonly boolean launched -> true if the missile was already launched
// readonly boolean allocated -> true if the missile can still perform its movement operations
// //-------------------------------------------------------
// unit source -> the source unit (optional)
// unit target -> the target unit (optional and if set to a valid unit the missile will be homing)
// player owner -> the owner of the missile (optional)
// real collision -> the collision size of the missile (optional. The onHit and onDestructable events only works if this is greater than 0)
// real damage -> stores the damage you want the missile to deal (optional)
// real acceleration -> if different than 0 then the missile will change its speed with time (optional)
// integer data -> just in case you want to pass some information to the missile to retrieve it later (optional)
// (call)
// method deflect takes real x, real y returns nothing
// - Deflects the missile from the target point, changing
// - it's course to the new x and y.
// - If target is a valid unit, deflect will null the target
// - and then deflects the missile
// (call)
// method deflectZ takes real x, real y, real z returns nothing
// - Deflects the missile from the target point, changing
// - it's course to the new x, y and z.
// - If target is a valid unit, deflect will null the target
// - and then deflects the missile
// (call)
// method bounce takes nothing returns nothing
// - Bounces the missile from it's current Z position.
// - Useful when assigning targets to missiles that were
// - already active, it fixes the rough Z position change.
// (call)
// method flush takes widget w returns nothing
// - call this method to allow the missile to be able to hit
// - a unit or destructable again
// (call)
// method flushAll nothing returns nothing
// - flushes the hit table for the missile
// (call)
// method hitted takes widget w returns boolean
// - returns true if the missile has hitted the widget
// (optional)
// method onHit takes unit hit returns boolean
// - Runs every time the missile collides with a unit.
// - If returns true the missile is destroyed.
// (optional)
// method onDestructable takes destructable dest returns boolean
// - Runs every time the missile collides with a destructable.
// - If returns true the missile is destroyed.
// (optional)
// method onItem takes item i returns boolean
// - Runs every time the missile collides with an item.
// - If returns true the missile is destroyed.
// (optional)
// method onMissile takes Missiles missile returns boolean
// - Runs every time the missile collides with another missile.
// - By default, missiles collide only once
// - If returns true the missile is destroyed.
// - Please, be aware that this method can be very performance
// - intensive, so careful!
// (optional)
// method onPeriod takes nothing returns boolean
// - Runs every period.
// - If returns true the missile is destroyed.
// (optional)
// method onFinish takes nothing returns boolean
// - Runs whenever the missile finishes it's course.
// - If returns true the missile is destroyed.
// (optional)
// method onTerrain takes nothing returns boolean
// - Runs whenever the missile collides with terrain height
// - greater then the missile current z
// - If returns true the missile is destroyed.
// (optional)
// method onRemove takes Missile this returns nothing
// - Runs whenever the missile is deallocated.
// /* ----------------------------------- END ---------------------------------- */
/* --------------------------------- System --------------------------------- */
globals
// The raw code of the dummy unit. Match it with the
// Object Editor id
private constant integer DUMMY_RAW_CODE = 'dumi'
// The update period of the system
public constant real PERIOD = 1./32.
// the avarage collision size compensation when detecting
// collisions
private constant real COLLISION_SIZE = 128.
// How long takes for the missile to be removed.
// This is necessary so the death animation of the
// effect can play through
private constant real RECYCLE_TIME = 2.
// Needed, dont touch. Seriously, dont touch!
private location LOC = Location(0., 0.)
private rect RECT = Rect(0., 0., 0., 0.)
private hashtable table = InitHashtable()
endglobals
// The available methods the user can implement in their structs
private interface MissileEvents
method onHit takes unit hit returns boolean defaults false
method onItem takes item i returns boolean defaults false
method onMissile takes Missiles missile returns boolean defaults false
method onTerrain takes nothing returns boolean defaults false
method onPeriod takes nothing returns boolean defaults false
method onFinish takes nothing returns boolean defaults false
method onDestructable takes destructable dest returns boolean defaults false
method onRemove takes nothing returns nothing defaults nothing
endinterface
function GetLocZ takes real x, real y returns real
call MoveLocation(LOC, x, y)
return GetLocationZ(LOC)
endfunction
private function DistanceBetweenCoordinates takes real x1, real y1, real x2, real y2 returns real
local real dx = (x2 - x1)
local real dy = (y2 - y1)
return SquareRoot(dx*dx + dy*dy)
endfunction
//Little snippet to remove the dummy unit after a delay
//I'm not using MissileRecycler because of a leaking problem i discover
private struct Timed
timer t
unit u
static method onPeriod takes nothing returns nothing
local thistype this = LoadInteger(table, GetHandleId(GetExpiredTimer()), 0)
call FlushChildHashtable(table, GetHandleId(.t))
call PauseTimer(.t)
call DestroyTimer(.t)
call RemoveUnit(.u)
set .u = null
set .t = null
call .deallocate()
endmethod
static method Recycle takes unit u, real timeout returns nothing
local thistype this = thistype.allocate()
set .u = u
set .t = CreateTimer()
call SaveInteger(table, GetHandleId(.t), 0, this)
call TimerStart(.t, timeout, false, function thistype.onPeriod)
endmethod
endstruct
//Credits to Dirac for AdvLoc and BPower for fixing an error in it
private struct Coordinates
readonly real x
readonly real y
readonly real z
readonly real angle
readonly real distance
readonly real square
readonly real slope
readonly real alpha
// Creates an origin - impact link.
private thistype ref
private static method math takes thistype a, thistype b returns nothing
local real dx
local real dy
loop
set dx = b.x - a.x
set dy = b.y - a.y
set dx = dx*dx + dy*dy
set dy = SquareRoot(dx)
exitwhen dx != 0. and dy != 0.
set b.x = b.x + .01
set b.z = b.z - GetLocZ(b.x -.01, b.y) + GetLocZ(b.x, b.y)
endloop
set a.square = dx
set a.distance = dy
set a.angle = Atan2(b.y - a.y, b.x - a.x)
set a.slope = (b.z - a.z)/dy
set a.alpha = Atan(a.slope)*bj_RADTODEG
// Set b.
if b.ref == a then
set b.angle = a.angle + bj_PI
set b.distance = dy
set b.slope = -a.slope
set b.alpha = -a.alpha
set b.square = dx
endif
endmethod
static method link takes thistype a, thistype b returns nothing
set a.ref = b
set b.ref = a
call math(a, b)
endmethod
method move takes real toX, real toY, real toZ returns nothing
set x = toX
set y = toY
set z = toZ + GetLocZ(toX, toY)
if ref != this then
call math(this, ref)
endif
endmethod
method destroy takes nothing returns nothing
call .deallocate()
endmethod
static method create takes real x, real y, real z returns Coordinates
local thistype this = thistype.allocate()
set ref = this
call move(x, y, z)
return this
endmethod
endstruct
/* ------------------------------ Missile Sytem ----------------------------- */
struct Missiles extends MissileEvents
private static thistype array missiles
private static integer didx = -1
private static timer t = CreateTimer()
private static group hitGroup = CreateGroup()
private static thistype temp = 0
//-------------------------------------------------------
private real cA //current angle
private effect sfx //effect
private string fxpath //model
private real size //scale
private real height // Arcs
private real open // Curves
//-------------------------------------------------------
Coordinates impact
Coordinates origin
//-------------------------------------------------------
readonly real x
readonly real y
readonly real z
readonly real prevX
readonly real prevY
readonly real prevZ
readonly real turn
readonly real veloc
readonly real travel
readonly unit dummy
readonly boolean launched
readonly boolean allocated
//-------------------------------------------------------
unit source
unit target
player owner
real collision
real damage
real acceleration
integer data
//-------------------------------------------------------
/* -------------------------- Model of the missile -------------------------- */
method operator model= takes string path returns nothing
call DestroyEffect(sfx)
set fxpath = path
set sfx = AddSpecialEffectTarget(path, dummy, "origin")
endmethod
method operator model takes nothing returns string
return fxpath
endmethod
/* ----------------------------- Curved movement ---------------------------- */
method operator curve= takes real value returns nothing
set open = Tan(value*bj_DEGTORAD)*origin.distance
endmethod
method operator curve takes nothing returns real
return Atan(open/origin.distance)
endmethod
/* ----------------------------- Arced Movement ----------------------------- */
method operator arc= takes real value returns nothing
set height = Tan(value*bj_DEGTORAD)*origin.distance/4
endmethod
method operator arc takes nothing returns real
return Atan(4*height/origin.distance)
endmethod
/* ------------------------------ Effect scale ------------------------------ */
method operator scale= takes real v returns nothing
call SetUnitScale(dummy, v, v, v)
set size = v
endmethod
method operator scale takes nothing returns real
return size
endmethod
/* ------------------------------ Missile Speed ----------------------------- */
method operator speed= takes real newspeed returns nothing
set veloc = newspeed*PERIOD
endmethod
method operator speed takes nothing returns real
return veloc
endmethod
/* ------------------------------- Flight Time ------------------------------ */
method operator duration= takes real flightTime returns nothing
set veloc = RMaxBJ(0.00000001, (origin.distance - travel)*PERIOD/RMaxBJ(0.00000001, flightTime))
endmethod
/* ---------------------------- Bound and Deflect --------------------------- */
method bounce takes nothing returns nothing
local real locZ = GetLocZ(x, y)
// This is here just to avoid an infinite loop
// with a deflect being called within an onTerrain
// event
if z < locZ then
set z = locZ
call impact.move(impact.x, impact.y, origin.z - GetLocZ(impact.x, impact.y))
endif
call origin.move(x, y, origin.z - GetLocZ(origin.x, origin.y))
set travel = 0
endmethod
method deflect takes real tx, real ty returns nothing
if target != null then
set target = null
endif
call impact.move(tx, ty, impact.z - GetLocZ(impact.x, impact.y))
call bounce()
endmethod
method deflectZ takes real tx, real ty, real tz returns nothing
call impact.move(impact.x, impact.y, tz)
call deflect(tx, ty)
endmethod
/* ---------------------------- Flush hit targets --------------------------- */
method flushAll takes nothing returns nothing
call FlushChildHashtable(table, this)
endmethod
method flush takes widget w returns nothing
if w != null then
call RemoveSavedBoolean(table, this, GetHandleId(w))
endif
endmethod
method hitted takes widget w returns boolean
return HaveSavedBoolean(table, this, GetHandleId(w))
endmethod
/* ------------------------- Destructable hit method ------------------------ */
private static method onDest takes nothing returns nothing
local thistype this = temp
local destructable d = GetEnumDestructable()
if not HaveSavedBoolean(table, this, GetHandleId(d)) then
call SaveBoolean(table, this, GetHandleId(d), true)
if allocated and .onDestructable(d) then
set d = null
call terminate()
return
endif
endif
set d = null
endmethod
/* -------------------------- Item collision method ------------------------- */
private static method onItems takes nothing returns nothing
local thistype this = temp
local item i = GetEnumItem()
if not HaveSavedBoolean(table, this, GetHandleId(i)) then
call SaveBoolean(table, this, GetHandleId(i), true)
if allocated and .onItem(i) then
set i = null
call terminate()
return
endif
endif
set i = null
endmethod
/* ------------------------------ Reset members ----------------------------- */
private method reset takes nothing returns nothing
set launched = false
set source = null
set target = null
set owner = null
set sfx = null
set dummy = null
set fxpath = ""
set open = 0.
set height = 0.
set veloc = 0.
set acceleration = 0.
set collision = 0.
set damage = 0.
set travel = 0.
set turn = 0.
set size = 0.
set data = 0
endmethod
/* -------------------------------- Terminate ------------------------------- */
method terminate takes nothing returns nothing
if allocated then
set allocated = false
// onRemove event
if .onRemove.exists then
call .onRemove()
endif
call FlushChildHashtable(table, this)
endif
endmethod
/* -------------------------- Destroys the missile -------------------------- */
method remove takes integer i returns integer
call terminate()
call DestroyEffect(sfx)
call origin.destroy()
call impact.destroy()
call Timed.Recycle(dummy, RECYCLE_TIME)
call reset()
set missiles[i] = missiles[didx]
set didx = didx - 1
if didx == -1 then
call PauseTimer(t)
endif
call deallocate()
return i - 1
endmethod
/* --------------------------- Launch the Missile --------------------------- */
method launch takes nothing returns nothing
if not launched and allocated then
set launched = true
set didx = didx + 1
set missiles[didx] = this
if didx == 0 then
call TimerStart(t, PERIOD, true, function thistype.move)
endif
endif
endmethod
/* ---------------------------- Missiles movement --------------------------- */
static method move takes nothing returns nothing
local integer i = 0
local integer j
local unit u
local real a
local real d
local real s
local real h
local real c
local real dx
local real dy
local real yaw
local real pitch
local Missiles missile
local Coordinates o
local thistype this
loop
exitwhen i > didx
set this = missiles[i]
set temp = this
if allocated then
set o = origin
set h = height
set c = open
set d = o.distance
//onPeriod Event
if .onPeriod.exists then
if allocated and .onPeriod() then
call terminate()
endif
endif
// onHit Event
if .onHit.exists then
if allocated and collision > 0 then
call GroupEnumUnitsInRange(hitGroup, x, y, collision + COLLISION_SIZE, null)
loop
set u = FirstOfGroup(hitGroup)
exitwhen u == null
if not HaveSavedBoolean(table, this, GetHandleId(u)) then
if IsUnitInRangeXY(u, x, y, collision) then
call SaveBoolean(table, this, GetHandleId(u), true)
if allocated and .onHit(u) then
call terminate()
exitwhen true
endif
endif
endif
call GroupRemoveUnit(hitGroup, u)
endloop
endif
endif
// onMissile Event
if .onMissile.exists then
if allocated and collision > 0 then
set j = 0
loop
exitwhen j > didx
set missile = missiles[j]
if missile != this then
if not HaveSavedBoolean(table, this, missile) then
set dx = x
set dy = y
if DistanceBetweenCoordinates(dx, dy, missile.x, missile.y) <= collision then
call SaveBoolean(table, this, missile, true)
if allocated and .onMissile(missile) then
call terminate()
exitwhen true
endif
endif
endif
endif
set j = j + 1
endloop
endif
endif
// onDestructable Event
if .onDestructable.exists then
if allocated and collision > 0 then
set dx = collision
set RECT = Rect(x - dx, y - dx, x + dx, y + dx)
call EnumDestructablesInRect(RECT, null, function thistype.onDest)
endif
endif
// onItem Event
if .onItem.exists then
if allocated and collision > 0 then
set dx = collision
set RECT = Rect(x - dx, y - dx, x + dx, y + dx)
call EnumItemsInRect(RECT, null, function thistype.onItems)
endif
endif
// Homing or not
set u = target
if u != null and GetUnitTypeId(u) != 0 then
call impact.move(GetUnitX(u), GetUnitY(u), GetUnitFlyHeight(u) + impact.z)
set dx = impact.x - prevX
set dy = impact.y - prevY
set a = Atan2(dy, dx)
set travel = o.distance - SquareRoot(dx*dx + dy*dy)
else
set a = o.angle
set target = null
endif
// turn rate
if turn != 0 and not (Cos(cA-a) >= Cos(turn)) then
if Sin(a-cA) >= 0 then
set cA = cA + turn
else
set cA = cA - turn
endif
else
set cA = a
endif
set x = prevX + veloc*Cos(cA)
set y = prevY + veloc*Sin(cA)
set s = travel + veloc
set prevX = x
set prevY = y
set prevZ = z
set travel = s
set veloc = veloc + acceleration
set pitch = origin.alpha
set yaw = cA*bj_RADTODEG
// arc calculation
if h != 0 or o.slope != 0 then
set z = 4*h*s*(d-s)/(d*d) + o.slope*s + o.z
set pitch = pitch - Atan(((4*h)*(2*s - d))/(d*d))*bj_RADTODEG
call SetUnitFlyHeight(dummy, z - GetLocZ(x, y), 0)
endif
// curve calculation
if c != 0 then
set dx = 4*c*s*(d-s)/(d*d)
set a = cA + bj_PI/2
set x = x + dx*Cos(a)
set y = y + dx*Sin(a)
set yaw = (cA + Atan(-((4*c)*(2*s - d))/(d*d)))*bj_RADTODEG
endif
// onTerrain event
if .onTerrain.exists then
if GetLocZ(x, y) > z then
if allocated and .onTerrain() then
call terminate()
endif
endif
endif
if s >= d then
// onFinish event
if .onFinish.exists then
if allocated and .onFinish() then
call terminate()
else
// deflected onFinish
if travel > 0 then
call terminate()
endif
endif
else
call terminate()
endif
endif
// missile orientation and positioning
call SetUnitAnimationByIndex(dummy, R2I(pitch + 90.5))
call SetUnitFacing(dummy, yaw)
static if LIBRARY_WorldBounds then
if not (x > WorldBounds.maxX or x < WorldBounds.minX or y > WorldBounds.maxY or y < WorldBounds.minY) then
call SetUnitX(dummy, x)
call SetUnitY(dummy, y)
endif
else
if RectContainsCoords(bj_mapInitialPlayableArea, x, y) then
call SetUnitX(dummy, x)
call SetUnitY(dummy, y)
endif
endif
else
set i = remove(i)
endif
set i = i + 1
endloop
set u = null
endmethod
/* --------------------------- Main Creator method -------------------------- */
static method create takes real x, real y, real z, real toX, real toY, real toZ returns thistype
local thistype this = thistype.allocate()
local real angle = Atan2(toY - y, toX - x)*bj_RADTODEG
call .reset()
set .origin = Coordinates.create(x, y, z)
set .impact = Coordinates.create(toX, toY, toZ)
set .allocated = true
set .x = x
set .y = y
set .z = z
set .prevX = x
set .prevY = y
set .prevZ = z
set .dummy = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE), DUMMY_RAW_CODE, x, y, angle)
call SetUnitFlyHeight(.dummy, z - GetLocZ(x, y), 0)
call Coordinates.link(origin, impact)
set .cA = origin.angle
return this
endmethod
endstruct
endlibrary
JASS:
library Missiles requires optional WorldBounds
/* ----------------------- Missiles v1.0 by Chopinski ----------------------- */
// Thanks and Full Credits to BPower, Dirac and Vexorian for the Missile Library's at which i based
// this Missiles library. Credits and thanks to AGD and for the effect orientation ideas.
// This version of Missiles requires patch 1.31+
// How to Import:
// 1 - Copy this library into your map and optionally WorldBounds
// WorldBounds: https://raw.githubusercontent.com/nestharus/JASS/master/jass/Systems/WorldBounds/script.j
// How to Use:
// This system works almost identicaly to the Missile library by BPower but
// with a more user friendly interface in my opinion. Also this system
// allows you to create Arc/Curved Homing missiles like Dirac's system.
// Differently than both BPower and Dirac systems, you are not required
// to use the ImplementStruct at the end of your struct. To make your struct
// behave like a Missile simply make it extends Missiles and you are done.
// After that you will have access to the events in the MissileEvents Interface.
// Simply declare the event you want for your strcut to have and the system
// takes care of the rest. You can access the members and functions using
// the "this" or "." syntax which is a plus. to terminate a missile, simply
// return true or call terminate() within the method. Example:
// struct MySpell extends Missiles
// method onPeriod takes nothing returns boolean
// // will display the missile current position
// call ClearTextMessages()
// call BJDebugMsg(I2S(.x))
// call BJDebugMsg(I2S(.y))
// call BJDebugMsg(I2S(.z))
// return false
// endmethod
// endstruct
// function Spell takes nothing returns nothing
// // the create method takes the initial and the final coordinates
// // if the target member is set, the missile home
// local MySpell spell = MySpell.create(fromX, fromY, fromZ, toX, toY, toZ)
// set spell.source = GetTriggerUnit()
// set spell.target = GetSpellTargetUnit() -> when a target is specified the missile will be homing
// set spell.speed = 1000 -> warcraft 3 speed unit
// set spell.duration = 1.5 -> will set the speed to match the time passed
// set spell.model = "Some Model.dmx"
// set spell.scale = 1.3
// set spell.arc = 30 (degrees converted to radians internally)
// set spell.curve = GetRandomReal(-40, 40) (degrees converted to radians internally
// call spell.launch()
// endfunction
// Available members and methods:
// Coordinates impact -> the impact coordiantes (x, y, z, anngle, slope, alpha, distance)
// Coordinates origin -> same, but for origin
// //-------------------------------------------------------
// readonly real x -> current x position of the missile
// readonly real y -> current y position of the missile
// readonly real z -> current z position of the missile
// readonly real prevX -> last x position of the missile
// readonly real prevY -> last y position of the missile
// readonly real prevZ -> last z position of the missile
// readonly real height -> the arc of the missile (change it using the .arc operator)
// readonly real turn -> the turn rate of the missile
// readonly real open -> the curvature of the missile (change it using the .curve operator)
// readonly real veloc -> the missile speed (change it using the .speed or .duration operator)
// readonly real travel -> distance travelled
// readonly boolean launched -> true if the missile was already launched
// readonly boolean allocated -> true if the missile can still perform its movement operations
// //-------------------------------------------------------
// unit source -> the source unit (optional)
// unit target -> the target unit (optional and if set to a valid unit the missile will be homing)
// player owner -> the owner of the missile (optional)
// real collision -> the collision size of the missile (optional. The onHit and onDestructable events only works if this is greater than 0)
// real damage -> stores the damage you want the missile to deal (optional)
// real acceleration -> if different than 0 then the missile will change its speed with time (optional)
// integer data -> just in case you want to pass some information to the missile to retrieve it later (optional)
// (call)
// method deflect takes real x, real y returns nothing
// - Deflects the missile from the target point, changing
// - it's course to the new x and y.
// - If target is a valid unit, deflect will null the target
// - and then deflects the missile
// (call)
// method deflectZ takes real x, real y, real z returns nothing
// - Deflects the missile from the target point, changing
// - it's course to the new x, y and z.
// - If target is a valid unit, deflect will null the target
// - and then deflects the missile
// (call)
// method bounce takes nothing returns nothing
// - Bounces the missile from it's current Z position.
// - Useful when assigning targets to missiles that were
// - already active, it fixes the rough Z position change.
// (call)
// method flush takes widget w returns nothing
// - call this method to allow the missile to be able to hit
// - a unit or destructable again
// (call)
// method flushAll nothing returns nothing
// - flushes the hit table for the missile
// (call)
// method hitted takes widget w returns boolean
// - returns true if the missile has hitted the widget
// (optional)
// method onHit takes unit hit returns boolean
// - Runs every time the missile collides with a unit.
// - If returns true the missile is destroyed.
// (optional)
// method onDestructable takes destructable dest returns boolean
// - Runs every time the missile collides with a destructable.
// - If returns true the missile is destroyed.
// (optional)
// method onItem takes item i returns boolean
// - Runs every time the missile collides with an item.
// - If returns true the missile is destroyed.
// (optional)
// method onMissile takes Missiles missile returns boolean
// - Runs every time the missile collides with another missile.
// - By default, missiles collide only once
// - If returns true the missile is destroyed.
// - Please, be aware that this method can be very performance
// - intensive, so careful!
// (optional)
// method onPeriod takes nothing returns boolean
// - Runs every period.
// - If returns true the missile is destroyed.
// (optional)
// method onFinish takes nothing returns boolean
// - Runs whenever the missile finishes it's course.
// - If returns true the missile is destroyed.
// (optional)
// method onTerrain takes nothing returns boolean
// - Runs whenever the missile collides with terrain height
// - greater then the missile current z
// - If returns true the missile is destroyed.
// (optional)
// method onRemove takes Missile this returns nothing
// - Runs whenever the missile is deallocated.
// /* ----------------------------------- END ---------------------------------- */
/* --------------------------------- System --------------------------------- */
globals
// The update period of the system
public constant real PERIOD = 1./32.
// the avarage collision size compensation when detecting
// collisions
private constant real COLLISION_SIZE = 128.
// Needed, dont touch. Seriously, dont touch!
private location LOC = Location(0., 0.)
private rect RECT = Rect(0., 0., 0., 0.)
private hashtable table = InitHashtable()
endglobals
// The available Events the user can implement in their structs
private interface MissileEvents
method onHit takes unit hit returns boolean defaults false
method onItem takes item i returns boolean defaults false
method onMissile takes Missiles missile returns boolean defaults false
method onTerrain takes nothing returns boolean defaults false
method onPeriod takes nothing returns boolean defaults false
method onFinish takes nothing returns boolean defaults false
method onDestructable takes destructable dest returns boolean defaults false
method onRemove takes nothing returns nothing defaults nothing
endinterface
function GetLocZ takes real x, real y returns real
call MoveLocation(LOC, x, y)
return GetLocationZ(LOC)
endfunction
// To check missiles collisions
private function DistanceBetweenCoordinates takes real x1, real y1, real x2, real y2 returns real
local real dx = (x2 - x1)
local real dy = (y2 - y1)
return SquareRoot(dx*dx + dy*dy)
endfunction
// The Missile effect
private struct Effect
effect effect
string path
real size
method scale takes real scale returns nothing
call BlzSetSpecialEffectScale(effect, scale)
endmethod
// Must be set in this order
method orient takes real yaw, real pitch, real roll returns nothing
call BlzSetSpecialEffectYaw(effect, yaw)
call BlzSetSpecialEffectPitch(effect, pitch)
call BlzSetSpecialEffectRoll(effect, roll)
endmethod
method move takes real x, real y, real z returns nothing
static if LIBRARY_WorldBounds then
if not (x > WorldBounds.maxX or x < WorldBounds.minX or y > WorldBounds.maxY or y < WorldBounds.minY) then
call BlzSetSpecialEffectPosition(effect, x, y, z)
endif
else
if RectContainsCoords(bj_mapInitialPlayableArea, x, y) then
call BlzSetSpecialEffectPosition(effect, x, y, z)
endif
endif
endmethod
/* -------------------------- Contructor/Destructor ------------------------- */
method destroy takes nothing returns nothing
call DestroyEffect(effect)
set effect = null
set path = null
set size = 1.
call .deallocate()
endmethod
static method create takes real x, real y, real z returns thistype
local thistype this = thistype.allocate()
set effect = AddSpecialEffect("", x, y)
set path = ""
set size = 1
call BlzSetSpecialEffectZ(effect, z)
return this
endmethod
endstruct
//Credits to Dirac for AdvLoc and BPower for fixing an error in it
private struct Coordinates
readonly real x
readonly real y
readonly real z
readonly real angle
readonly real distance
readonly real square
readonly real slope
readonly real alpha
// Creates an origin - impact link.
private thistype ref
private static method math takes thistype a, thistype b returns nothing
local real dx
local real dy
loop
set dx = b.x - a.x
set dy = b.y - a.y
set dx = dx*dx + dy*dy
set dy = SquareRoot(dx)
exitwhen dx != 0. and dy != 0.
set b.x = b.x + .01
set b.z = b.z - GetLocZ(b.x -.01, b.y) + GetLocZ(b.x, b.y)
endloop
set a.square = dx
set a.distance = dy
set a.angle = Atan2(b.y - a.y, b.x - a.x)
set a.slope = (b.z - a.z)/dy
set a.alpha = Atan(a.slope)
// Set b.
if b.ref == a then
set b.angle = a.angle + bj_PI
set b.distance = dy
set b.slope = -a.slope
set b.alpha = -a.alpha
set b.square = dx
endif
endmethod
static method link takes thistype a, thistype b returns nothing
set a.ref = b
set b.ref = a
call math(a, b)
endmethod
method move takes real toX, real toY, real toZ returns nothing
set x = toX
set y = toY
set z = toZ + GetLocZ(toX, toY)
if ref != this then
call math(this, ref)
endif
endmethod
method destroy takes nothing returns nothing
call .deallocate()
endmethod
static method create takes real x, real y, real z returns Coordinates
local thistype this = thistype.allocate()
set ref = this
call move(x, y, z)
return this
endmethod
endstruct
/* ------------------------------ Missile Sytem ----------------------------- */
struct Missiles extends MissileEvents
private static timer t = CreateTimer()
private static group hitGroup = CreateGroup()
private static integer didx = -1
private static thistype temp = 0
private static thistype array missiles
//-------------------------------------------------------
private real cA // Current Angle
private real height // Arcs
private real open // Curves
//-------------------------------------------------------
Coordinates impact
Coordinates origin
Effect effect
//-------------------------------------------------------
readonly real x
readonly real y
readonly real z
readonly real prevX
readonly real prevY
readonly real prevZ
readonly real turn
readonly real veloc
readonly real travel
readonly boolean launched
readonly boolean allocated
//-------------------------------------------------------
unit source
unit target
player owner
real collision
real damage
real acceleration
integer data
//-------------------------------------------------------
/* -------------------------- Model of the missile -------------------------- */
method operator model= takes string fx returns nothing
call DestroyEffect(effect.effect)
set effect.path = fx
set effect.effect = AddSpecialEffect(fx, origin.x, origin.y)
call BlzSetSpecialEffectZ(effect.effect, origin.z)
endmethod
method operator model takes nothing returns string
return effect.path
endmethod
/* ----------------------------- Curved movement ---------------------------- */
method operator curve= takes real value returns nothing
set open = Tan(value*bj_DEGTORAD)*origin.distance
endmethod
method operator curve takes nothing returns real
return Atan(open/origin.distance)
endmethod
/* ----------------------------- Arced Movement ----------------------------- */
method operator arc= takes real value returns nothing
set height = Tan(value*bj_DEGTORAD)*origin.distance/4
endmethod
method operator arc takes nothing returns real
return Atan(4*height/origin.distance)
endmethod
/* ------------------------------ Effect scale ------------------------------ */
method operator scale= takes real value returns nothing
set effect.size = value
call effect.scale(value)
endmethod
method operator scale takes nothing returns real
return effect.size
endmethod
/* ------------------------------ Missile Speed ----------------------------- */
method operator speed= takes real newspeed returns nothing
set veloc = newspeed*PERIOD
endmethod
method operator speed takes nothing returns real
return veloc
endmethod
/* ------------------------------- Flight Time ------------------------------ */
method operator duration= takes real flightTime returns nothing
set veloc = RMaxBJ(0.00000001, (origin.distance - travel)*PERIOD/RMaxBJ(0.00000001, flightTime))
endmethod
/* ---------------------------- Bound and Deflect --------------------------- */
method bounce takes nothing returns nothing
local real locZ = GetLocZ(x, y)
// This is here just to avoid an infinite loop
// with a deflect being called within an onTerrain
// event
if z < locZ then
set z = locZ
call impact.move(impact.x, impact.y, origin.z - GetLocZ(impact.x, impact.y))
endif
call origin.move(x, y, origin.z - GetLocZ(origin.x, origin.y))
set travel = 0
endmethod
method deflect takes real tx, real ty returns nothing
if target != null then
set target = null
endif
call impact.move(tx, ty, impact.z - GetLocZ(impact.x, impact.y))
call bounce()
endmethod
method deflectZ takes real tx, real ty, real tz returns nothing
call impact.move(impact.x, impact.y, tz)
call deflect(tx, ty)
endmethod
/* ---------------------------- Flush hit targets --------------------------- */
method flushAll takes nothing returns nothing
call FlushChildHashtable(table, this)
endmethod
method flush takes widget w returns nothing
if w != null then
call RemoveSavedBoolean(table, this, GetHandleId(w))
endif
endmethod
method hitted takes widget w returns boolean
return HaveSavedBoolean(table, this, GetHandleId(w))
endmethod
/* ---------------------- Destructable collision method --------------------- */
private static method onDest takes nothing returns nothing
local thistype this = temp
local destructable d = GetEnumDestructable()
if not HaveSavedBoolean(table, this, GetHandleId(d)) then
call SaveBoolean(table, this, GetHandleId(d), true)
if allocated and .onDestructable(d) then
set d = null
call terminate()
return
endif
endif
set d = null
endmethod
/* -------------------------- Item collision method ------------------------- */
private static method onItems takes nothing returns nothing
local thistype this = temp
local item i = GetEnumItem()
if not HaveSavedBoolean(table, this, GetHandleId(i)) then
call SaveBoolean(table, this, GetHandleId(i), true)
if allocated and .onItem(i) then
set i = null
call terminate()
return
endif
endif
set i = null
endmethod
/* ------------------------------ Reset members ----------------------------- */
private method reset takes nothing returns nothing
set launched = false
set source = null
set target = null
set owner = null
set open = 0.
set height = 0.
set veloc = 0.
set acceleration = 0.
set collision = 0.
set damage = 0.
set travel = 0.
set turn = 0.
set data = 0
endmethod
/* -------------------------------- Terminate ------------------------------- */
method terminate takes nothing returns nothing
if allocated then
set allocated = false
// onRemove event
if .onRemove.exists then
call .onRemove()
endif
call FlushChildHashtable(table, this)
endif
endmethod
/* -------------------------- Destroys the missile -------------------------- */
method remove takes integer i returns integer
call terminate()
call origin.destroy()
call impact.destroy()
call effect.destroy()
call reset()
set missiles[i] = missiles[didx]
set didx = didx - 1
if didx == -1 then
call PauseTimer(t)
endif
call deallocate()
return i - 1
endmethod
/* --------------------------- Launch the Missile --------------------------- */
method launch takes nothing returns nothing
if not launched and allocated then
set launched = true
set didx = didx + 1
set missiles[didx] = this
if didx == 0 then
call TimerStart(t, PERIOD, true, function thistype.move)
endif
endif
endmethod
/* ---------------------------- Missiles movement --------------------------- */
static method move takes nothing returns nothing
local integer i = 0
local integer j
local unit u
local real a
local real d
local real s
local real h
local real c
local real dx
local real dy
local real yaw
local real pitch
local Missiles missile
local Coordinates o
local thistype this
loop
exitwhen i > didx
set this = missiles[i]
set temp = this
if allocated then
set o = origin
set h = height
set c = open
set d = o.distance
//onPeriod Event
if .onPeriod.exists then
if allocated and .onPeriod() then
call terminate()
endif
endif
// onHit Event
if .onHit.exists then
if allocated and collision > 0 then
call GroupEnumUnitsInRange(hitGroup, x, y, collision + COLLISION_SIZE, null)
loop
set u = FirstOfGroup(hitGroup)
exitwhen u == null
if not HaveSavedBoolean(table, this, GetHandleId(u)) then
if IsUnitInRangeXY(u, x, y, collision) then
call SaveBoolean(table, this, GetHandleId(u), true)
if allocated and .onHit(u) then
call terminate()
exitwhen true
endif
endif
endif
call GroupRemoveUnit(hitGroup, u)
endloop
endif
endif
// onMissile Event
if .onMissile.exists then
if allocated and collision > 0 then
set j = 0
loop
exitwhen j > didx
set missile = missiles[j]
if missile != this then
if not HaveSavedBoolean(table, this, missile) then
set dx = x
set dy = y
if DistanceBetweenCoordinates(dx, dy, missile.x, missile.y) <= collision then
call SaveBoolean(table, this, missile, true)
if allocated and .onMissile(missile) then
call terminate()
exitwhen true
endif
endif
endif
endif
set j = j + 1
endloop
endif
endif
// onDestructable Event
if .onDestructable.exists then
if allocated and collision > 0 then
set dx = collision
set RECT = Rect(x - dx, y - dx, x + dx, y + dx)
call EnumDestructablesInRect(RECT, null, function thistype.onDest)
endif
endif
// onItem Event
if .onItem.exists then
if allocated and collision > 0 then
set dx = collision
set RECT = Rect(x - dx, y - dx, x + dx, y + dx)
call EnumItemsInRect(RECT, null, function thistype.onItems)
endif
endif
// Homing or not
set u = target
if u != null and GetUnitTypeId(u) != 0 then
call impact.move(GetUnitX(u), GetUnitY(u), GetUnitFlyHeight(u) + impact.z)
set dx = impact.x - prevX
set dy = impact.y - prevY
set a = Atan2(dy, dx)
set travel = o.distance - SquareRoot(dx*dx + dy*dy)
else
set a = o.angle
set target = null
endif
// turn rate
if turn != 0 and not (Cos(cA-a) >= Cos(turn)) then
if Sin(a-cA) >= 0 then
set cA = cA + turn
else
set cA = cA - turn
endif
else
set cA = a
endif
set yaw = cA
set pitch = o.alpha
set x = prevX + veloc*Cos(yaw)
set y = prevY + veloc*Sin(yaw)
set s = travel + veloc
set prevX = x
set prevY = y
set prevZ = z
set veloc = veloc + acceleration
set travel = s
// arc calculation
if h != 0 or o.slope != 0 then
set z = 4*h*s*(d-s)/(d*d) + o.slope*s + o.z
set pitch = pitch - Atan(((4*h)*(2*s - d))/(d*d))
endif
// curve calculation
if c != 0 then
set dx = 4*c*s*(d-s)/(d*d)
set a = yaw + bj_PI/2
set x = x + dx*Cos(a)
set y = y + dx*Sin(a)
set yaw = yaw + Atan(-((4*c)*(2*s - d))/(d*d))
endif
// onTerrain event
if .onTerrain.exists then
if GetLocZ(x, y) > z then
if allocated and .onTerrain() then
call terminate()
endif
endif
endif
if s >= d - 0.0001 then
// onFinish event
if .onFinish.exists then
if allocated and .onFinish() then
call terminate()
else
// deflected onFinish
if travel > 0 then
call terminate()
endif
endif
else
call terminate()
endif
else
call effect.orient(yaw, -pitch, 0)
endif
call effect.move(x, y, z)
else
set i = remove(i)
endif
set i = i + 1
endloop
set u = null
endmethod
/* --------------------------- Main Creator method -------------------------- */
static method create takes real x, real y, real z, real toX, real toY, real toZ returns thistype
local thistype this = thistype.allocate()
call .reset()
set .origin = Coordinates.create(x, y, z)
set .impact = Coordinates.create(toX, toY, toZ)
set .effect = Effect.create(x, y, z)
set .allocated = true
set .x = x
set .y = y
set .z = z
set .prevX = x
set .prevY = y
set .prevZ = z
call Coordinates.link(origin, impact)
set .cA = origin.angle
return this
endmethod
endstruct
endlibrary
JASS:
scope MissilesQ
private struct Misisle extends Missiles
unit caster
// method onPeriod takes nothing returns boolean
// call ClearTextMessages()
// call DisplayTimedTextToPlayer(Player(0), 0, 0, 0.1, "onPeriod(" + (I2S(this)) + ") X: " + R2S(.x))
// call DisplayTimedTextToPlayer(Player(0), 0, 0, 0.1, "onPeriod(" + (I2S(this)) + ") Y: " + R2S(.y))
// call DisplayTimedTextToPlayer(Player(0), 0, 0, 0.1, "onPeriod(" + (I2S(this)) + ") Z: " + R2S(.z))
// return false
// endmethod
method onHit takes unit hit returns boolean
if hit != .source and UnitAlive(hit) then
call BJDebugMsg("onHit(" + (I2S(this)) + "): " + GetUnitName(hit))
call KillUnit(hit)
endif
return false
endmethod
method onMissile takes Missiles missile returns boolean
call BJDebugMsg("onMissile(" + (I2S(this)) + "): Collided with Missile(" + (I2S(missile)) + ")")
return false
endmethod
method onItem takes item i returns boolean
call BJDebugMsg("onItem(" + (I2S(this)) + "): Removing")
call RemoveItem(i)
return false
endmethod
method onDestructable takes destructable dest returns boolean
call BJDebugMsg("onDestructable(" + (I2S(this)) + "): Killing")
call KillDestructable(dest)
return false
endmethod
method onTerrain takes nothing returns boolean
call BJDebugMsg("onTerrain(" + (I2S(this)) + "): Deflecting")
call deflect(GetUnitX(.caster), GetUnitY(.caster))
return false
endmethod
method onFinish takes nothing returns boolean
call BJDebugMsg("onFinish(" + (I2S(this)) + "): Exploding")
call DestroyEffect(AddSpecialEffect("Abilities\\Weapons\\FireBallMissile\\FireBallMissile.mdl", .x, .y))
return false
endmethod
method onRemove takes nothing returns nothing
call BJDebugMsg("onRemove(" + (I2S(this)) + "): Cleaning")
set .caster = null
endmethod
endstruct
private struct Test
static method onCast takes nothing returns nothing
local unit c = GetTriggerUnit()
local unit t = GetSpellTargetUnit()
local real fromX = GetUnitX(c)
local real fromY = GetUnitY(c)
local real fromZ = GetUnitFlyHeight(c) + 50
local real toX = GetSpellTargetX()
local real toY = GetSpellTargetY()
local real toZ = fromZ
local Misisle m = Misisle.create(fromX, fromY, fromZ, toX, toY, 50)
set m.model = "Abilities\\Weapons\\FireBallMissile\\FireBallMissile.mdl"
set m.speed = 500
set m.target = t
set m.source = c
//set m.arc = GetRandomReal(10, 40)
//set m.curve = GetRandomReal(-25, 25)
set m.collision = 75
set m.caster = c
call m.launch()
endmethod
static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent('A000', function thistype.onCast)
call Cheat("iseedeadpeople")
endmethod
endstruct
endscope
JASS:
scope MissilesW
private struct Missile extends Missiles
method onHit takes unit hit returns boolean
if hit != .source and UnitAlive(hit) then
call KillUnit(hit)
endif
return false
endmethod
method onDestructable takes destructable dest returns boolean
call KillDestructable(dest)
return false
endmethod
endstruct
private struct Test
timer t
unit c
real fx
real fy
real fz
real tx
real ty
integer count
integer i = 1
private static method GetRandomRange takes real radius returns real
local real r = GetRandomReal(0, 1) + GetRandomReal(0, 1)
if r > 1 then
return (2 - r)*radius
endif
return r*radius
endmethod
static method onLoop takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
local real maxRange
local real theta
local real radius
local real toX
local real toY
local Missile missile
if count > 0 then
set i = -i
set count = count - 1
set theta = 2*bj_PI*GetRandomReal(0, 1)
set radius = GetRandomRange(350)
set toX = tx + radius*Cos(theta)
set toY = ty + radius*Sin(theta)
set missile = Missile.create(fx, fy, fz, toX, toY, 0)
set missile.source = c
set missile.model = "Abilities\\Weapons\\FireBallMissile\\FireBallMissile.mdl"
set missile.speed = 800
set missile.collision = 75
set missile.arc = GetRandomReal(10, 45)
set missile.curve = GetRandomReal(5, 20)*i
call missile.launch()
else
call ReleaseTimer(t)
set t = null
set c = null
call deallocate()
endif
endmethod
static method onCast takes nothing returns nothing
local thistype this = thistype.allocate()
set t = NewTimerEx(this)
set c = GetTriggerUnit()
set fx = GetUnitX(c)
set fy = GetUnitY(c)
set fz = GetUnitFlyHeight(c) + 50
set tx = GetSpellTargetX()
set ty = GetSpellTargetY()
set count = 50
call TimerStart(.t, 0.1, true, function thistype.onLoop)
endmethod
static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent('A001', function thistype.onCast)
endmethod
endstruct
endscope
JASS:
scope MissilesE
private struct Missile extends Missiles
method onHit takes unit hit returns boolean
local real dz = GetLocZ(GetUnitX(hit), GetUnitY(hit)) - GetLocZ(x, y)
local real uz = BlzGetUnitCollisionSize(hit)
if hit != .source and UnitAlive(hit) and (dz + uz >= z - collision and dz <= z + collision) then
call KillUnit(hit)
endif
return false
endmethod
method onDestructable takes destructable dest returns boolean
local real dz = GetLocZ(GetWidgetX(dest), GetWidgetY(dest)) - GetLocZ(x, y)
local real tz = GetDestructableOccluderHeight(dest)
if dz + tz >= z - collision and dz <= z + collision then
call KillDestructable(dest)
endif
return false
endmethod
method onRemove takes nothing returns nothing
call DestroyEffect(AddSpecialEffect("Abilities\\Spells\\Other\\Stampede\\StampedeMissileDeath.mdl", x, y))
endmethod
endstruct
private struct Test
timer t
unit c
real fx
real fy
real tx
real ty
integer count
private static method AngleBetweenCoordinates takes real x, real y, real x2, real y2 returns real
return Atan2(y2 - y, x2 - x)
endmethod
private static method GetRandomRange takes real radius returns real
local real r = GetRandomReal(0, 1) + GetRandomReal(0, 1)
if r > 1 then
return (2 - r)*radius
endif
return r*radius
endmethod
static method onLoop takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
local real maxRange
local real theta
local real radius
local real toX
local real toY
local real fromX
local real fromY
local Missile missile
if count > 0 then
set count = count - 1
set theta = 2*bj_PI*GetRandomReal(0, 1)
set radius = GetRandomRange(600)
set toX = tx + radius*Cos(theta)
set toY = ty + radius*Sin(theta)
set theta = AngleBetweenCoordinates(tx, ty, fx, fy)
set fromX = toX + 3000*Cos(theta)
set fromY = toY + 3000*Sin(theta)
set missile = Missile.create(fromX, fromY, 1500, toX, toY, 0)
set missile.source = c
set missile.model = "Abilities\\Weapons\\AncientProtectorMissile\\AncientProtectorMissile.mdl"
set missile.scale = 1.5
set missile.duration = 2.
set missile.collision = 100
call missile.launch()
else
call ReleaseTimer(t)
set t = null
set c = null
call deallocate()
endif
endmethod
static method onCast takes nothing returns nothing
local thistype this = thistype.allocate()
set t = NewTimerEx(this)
set c = GetTriggerUnit()
set fx = GetUnitX(c)
set fy = GetUnitY(c)
set tx = GetSpellTargetX()
set ty = GetSpellTargetY()
set count = 50
call TimerStart(.t, 0.2, true, function thistype.onLoop)
endmethod
static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent('A002', function thistype.onCast)
endmethod
endstruct
endscope
JASS:
scope MissilesR
private struct Missile extends Missiles
method onFinish takes nothing returns boolean
if UnitAlive(target) then
call KillUnit(target)
endif
return false
endmethod
endstruct
private struct Test
static method onCast takes nothing returns nothing
local unit c = GetTriggerUnit()
local real x = GetUnitX(c)
local real y = GetUnitY(c)
local real z = GetUnitFlyHeight(c) + 50
local group g = CreateGroup()
local unit u
local Missile hammer
call GroupEnumUnitsInRange(g, GetSpellTargetX(), GetSpellTargetY(), 500, null)
loop
set u = FirstOfGroup(g)
exitwhen u == null
if UnitAlive(u) and u != c then
set hammer = Missile.create(x, y, z, GetUnitX(u), GetUnitY(u), GetUnitFlyHeight(u) + 50)
set hammer.model = "Abilities\\Spells\\Human\\StormBolt\\StormBoltMissile.mdl"
set hammer.speed = 500
set hammer.arc = GetRandomReal(0, 35)
set hammer.curve = GetRandomReal(-25, 25)
set hammer.target = u
call hammer.launch()
endif
call GroupRemoveUnit(g, u)
endloop
call DestroyGroup(g)
set c = null
set g = null
endmethod
static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent('A003', function thistype.onCast)
endmethod
endstruct
endscope
JASS:
scope MissilesD
private struct Missile extends Missiles
boolean deflected = false
method onFinish takes nothing returns boolean
if not deflected then
set deflected = true
call deflect(GetUnitX(source), GetUnitY(source))
endif
return false
endmethod
static method onCast takes nothing returns nothing
local unit c = GetTriggerUnit()
local real x = GetUnitX(c)
local real y = GetUnitY(c)
local real z = GetUnitFlyHeight(c) + 50
local real tx = GetSpellTargetX()
local real ty = GetSpellTargetY()
local real tz = z
local thistype this = thistype.create(x, y, z, tx, ty, tz)
set source = c
set model = "units\\human\\phoenix\\phoenix"
set speed = 1000
set arc = GetRandomReal(0, 35)
set curve = GetRandomReal(15, 30)*GetRandomInt(-1, 1)
set target = GetSpellTargetUnit()
call BlzPlaySpecialEffect(effect.effect, ConvertAnimType(5))
call launch()
set c = null
endmethod
static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent('A004', function thistype.onCast)
endmethod
endstruct
endscope
JASS:
scope MissilesF
private struct Missile extends Missiles
method onHit takes unit hit returns boolean
if hit != source and UnitAlive(hit) then
call KillUnit(hit)
endif
return false
endmethod
method onDestructable takes destructable dest returns boolean
call KillDestructable(dest)
return false
endmethod
static method onCast takes nothing returns nothing
local unit c = GetTriggerUnit()
local real x = GetUnitX(c)
local real y = GetUnitY(c)
local real z = GetUnitFlyHeight(c)
local real a = 0
local real i = 0
local real tx
local real ty
local thistype this
loop
exitwhen i >= 10
set tx = x + 400*Cos(a)
set ty = y + 400*Sin(a)
set this = Missile.create(x, y, z, tx, ty, z)
set model = "Abilities\\Spells\\Other\\BreathOfFire\\BreathOfFireMissile.mdl"
set speed = 1050
set source = c
set collision = 125
set a = a + 36*bj_DEGTORAD
call launch()
set i = i + 1
endloop
set c = null
endmethod
static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent('A005', function thistype.onCast)
endmethod
endstruct
endscope
And a little video demonstrating the system
Attachments
Last edited: