- Joined
- Apr 30, 2011
- Messages
- 359
Perfect Missile System
I editted this system from Element of Water's Missile System
I have added more functions, and as well as the length of the calls
The problem of this system is, when you modify anything of the missile during its creation (after firing the missile, but before its onLoop actions), the missile bugged and it screws all of my other systems used along with it.
Please anyone help me, and if anyone can rewrite the system, it's gladly welcomed with +reps from me
The Code:
Now, it's its add-on . . .
The add-on (Custom Missile) is editted from Element of Water's add-on
I add the same things as above
The most brilliant thing here is the missile-parabola, it's not only change the height of the missile, it also change its Z-facing (an object facing to a Z coordinate), making the missile extremely smooth
But there's still some flaws,
1. I don't know how to make a filter/function as an argument [highlight](EDIT: SOLVED)[/code]
2. If you took a closer look, the missile use 0 degree Z-facing during its creation (made to parabola Z-facing in onLoop functions); and we already knew the System's prob . . . Now how can we fix this?
The Code
Fixes and Rewrites are gladly welcome exchanged with reps
Thanks for your helps ^_^
I editted this system from Element of Water's Missile System
I have added more functions, and as well as the length of the calls
The problem of this system is, when you modify anything of the missile during its creation (after firing the missile, but before its onLoop actions), the missile bugged and it screws all of my other systems used along with it.
Please anyone help me, and if anyone can rewrite the system, it's gladly welcomed with +reps from me
The Code:
JASS:
library Missile initializer Init
//CONSTANTS - change these suitably.
globals
public constant real FPS = 100.00
private constant integer DUMMY_ID = 'e001'
private constant player OWNER = Player(15)
endglobals
//END CONSTANTS
//MAIN SYSTEM CODE - do NOT alter unless you know what you're doing.
globals
private Missile array Dat
private integer Index = 0
private timer Tim = CreateTimer()
private boolexpr Conds
private group Missiles = CreateGroup()
Missile GetEventMissile
endglobals
struct MissileActions
stub method onHit takes nothing returns boolean
return false
endmethod
stub method onLoop takes nothing returns boolean
return true
endmethod
endstruct
//kinda transfers all the units in a group into a different group.
private function AddGroup takes group a, group b returns nothing
local unit u
loop
set u = FirstOfGroup(b)
exitwhen u == null
call GroupAddUnit(a, u)
call GroupRemoveUnit(b, u)
endloop
endfunction
private function CondsFunc takes nothing returns boolean
return (not IsUnitInGroup(GetFilterUnit(), GetEventMissile.hg)) and GetEventMissile.Caster != GetFilterUnit() and GetUnitTypeId(GetFilterUnit()) != DUMMY_ID and GetWidgetLife(GetFilterUnit()) > 0.405
endfunction
private function Execute takes nothing returns nothing
local integer i = 0
local location zl
local real angle
local real dx
local real dy
local real dist
loop
exitwhen i >= Index
set GetEventMissile = Dat[i]
//calculate how far the missile the missile has travelled
set GetEventMissile.CurrentDist = GetEventMissile.CurrentDist + GetEventMissile.sp
//If the missile is homing onto a target, recalculate a few values
if GetEventMissile.Homing then
//get the new target x/y
set GetEventMissile.TargetX = GetUnitX(GetEventMissile.Target)
set GetEventMissile.TargetY = GetUnitY(GetEventMissile.Target)
//get the angle between the missile and the target
set angle = Atan2(GetEventMissile.TargetY - GetEventMissile.Y, GetEventMissile.TargetX - GetEventMissile.X)
//calculate the sin/cos of the angle
set GetEventMissile.sn = Sin(angle)
set GetEventMissile.cs = Cos(angle)
//make the missile face towards the target
call SetUnitFacing(GetEventMissile.Dummy, angle * bj_RADTODEG)
//calcuclate the distance between the missile and its target
set dx = GetEventMissile.TargetX - GetEventMissile.X
set dy = GetEventMissile.TargetY - GetEventMissile.Y
set dist = SquareRoot(dx * dx + dy * dy)
else
set dist = GetEventMissile.StartDist - GetEventMissile.CurrentDist
endif
//calculate the new x/y/z coordinates
set GetEventMissile.X = GetEventMissile.X + GetEventMissile.sp * GetEventMissile.cs // set x = x + speed * cos
set GetEventMissile.Y = GetEventMissile.Y + GetEventMissile.sp * GetEventMissile.sn // set y = y + speed * sin
set zl = Location(GetEventMissile.X,GetEventMissile.Y)
set GetEventMissile.Z = GetLocationZ(zl)+GetEventMissile.StartZ
call RemoveLocation(zl)
//calculate the absolute value of the distance remaining
if dist < 0 then
set dist = -dist
endif
//if the distance remaining is less than the speed of the missile then...
if dist < GetEventMissile.sp then
//tell the user the missile has reached its target and...
set GetEventMissile.TargetReached = true
set GetEventMissile.OnTarget = true
//calculate the new x/y/z coordinates
set GetEventMissile.X = GetEventMissile.TargetX
set GetEventMissile.Y = GetEventMissile.TargetY
set zl = Location(GetEventMissile.X,GetEventMissile.Y)
set GetEventMissile.Z = GetLocationZ(zl)+GetEventMissile.StartZ
call RemoveLocation(zl)
//run the onHit actions, destroying the missile if they return false
if not GetEventMissile.ma.onHit() and GetEventMissile.ma != 0 then
call GetEventMissile.destroy()
endif
set GetEventMissile.OnTarget = false
endif
//if the missile is collideable, check for collision
if GetEventMissile.Radius > 0.00 and ( (GetEventMissile.TargetReached == false and GetEventMissile.Homing == true) or (GetEventMissile.TargetReached == true and GetEventMissile.Homing == false) ) then
//enumerate the units within range of the missile, which aren't missiles
call GroupEnumUnitsInRange(GetEventMissile.hu, GetEventMissile.X, GetEventMissile.Y, GetEventMissile.Radius, Conds)
//if the enumeration picked up any units, then...
if FirstOfGroup(GetEventMissile.hu) != null then
//run the onHit actions, and if they return false then...
if not GetEventMissile.ma.onHit() and GetEventMissile.ma != 0 then
//destroy the missile
call GetEventMissile.destroy()
endif
//add the hit units to the hit units group so the system doesn't falsely
//think they're hit more than once
call AddGroup(GetEventMissile.hg, GetEventMissile.hu)
endif
endif
//move the missile.
call SetUnitX(GetEventMissile.Dummy, GetEventMissile.X)
call SetUnitY(GetEventMissile.Dummy, GetEventMissile.Y)
call SetUnitFlyHeight(GetEventMissile.Dummy, GetEventMissile.Z,0)
//run the onLoop actions, destroying the missile if they return false
if GetEventMissile.ma != 0 then
if not GetEventMissile.ma.onLoop() then
call GetEventMissile.destroy()
endif
endif
set i = i + 1
endloop
endfunction
struct Missile
unit Dummy = null //the actual missile unit
string fs = "" //the model path of the missile
effect SFX = null //the actual model
unit Caster = null //missile caster or jumper
unit Target = null //homing target
boolean dm = false //dummy, or preplaced unit?
boolean Homing = false //homing?
real Radius = 0. //radius for collision
real StartX = 0. //start x
real StartY = 0. //start y
real StartZ = 0. //start z
real X = 0. //current x
real Y = 0. //current y
real Z = 0. //current z
real TargetX = 0. //target x
real TargetY = 0. //target y
real sn = 0. //sin
real cs = 0. //cos
real StartDist = 0 //starting distance from the target
real CurrentDist = 0 //distance from target
real sp = 0. //speed
group hu = null //the units hit this loop
group hg = null //units which have already been hit
boolean TargetReached = false //target reached?
boolean OnTarget = false //on target now?
MissileActions ma = 0 //the onHit and onLoop actions
integer data = 0 //attached data
integer id = 0 //the array index of the missile
//internal create method
private static method coreCreate takes real radius, MissileActions ma returns Missile
local Missile d = Missile.allocate()
set d.hg = CreateGroup()
set d.hu = CreateGroup()
set d.Radius = radius
set d.ma = ma
return d
endmethod
//creates a new missile with the given model
static method create takes string sfx, real x, real y, real z, real radius, MissileActions actions returns Missile
local Missile d = Missile.coreCreate(radius, actions)
set d.fs = sfx
set d.StartX = x
set d.StartY = y
set d.StartZ = z
set d.X = x
set d.Y = y
set d.Z = z
set d.dm = true
return d
endmethod
//creates a missile from an existing unit
static method createFromUnit takes unit u, real radius, MissileActions actions returns Missile
local Missile d = Missile.coreCreate(radius, actions)
set d.Dummy = u
set d.StartX = GetUnitX(u)
set d.StartY = GetUnitY(u)
set d.StartZ = GetUnitFlyHeight(u)
set d.X = d.StartX
set d.Y = d.StartY
set d.Z = d.StartZ
return d
endmethod
method operator HitUnits takes nothing returns group
local group g = CreateGroup()
set bj_groupAddGroupDest = g
call ForGroup(.hu, function GroupAddGroupEnum)
return g
endmethod
method operator Speed takes nothing returns real
return .sp * FPS
endmethod
method operator Speed= takes real speed returns nothing
set .sp = speed / FPS
endmethod
//Internal function to fire the missile
private method fire takes unit caster, real speed returns nothing
local real angle = Atan2(.TargetY - .Y, .TargetX - .X)
local real dx = .TargetX - .X
local real dy = .TargetY - .Y
set .sn = Sin(angle)
set .cs = Cos(angle)
set .StartDist = SquareRoot(dx * dx + dy * dy)
if .Dummy == null then
set .Dummy = CreateUnit(OWNER, DUMMY_ID, .X, .Y, angle * bj_RADTODEG)
set .SFX = AddSpecialEffectTarget(.fs, .Dummy, "origin")
endif
call SetUnitFlyHeight(.Dummy,.Z,0)
set .Caster = caster
set .sp = speed / FPS
call GroupAddUnit(Missiles, .Dummy)
set Dat[Index] = this
set .id = Index
set Index = Index + 1
if Index == 1 then
call TimerStart(Tim, 1./FPS, true, function Execute)
endif
endmethod
//fires the missile at a point!
method fireFixed takes unit caster, real targetX, real targetY, real speed returns nothing
set .Homing = false
set .TargetX = targetX
set .TargetY = targetY
call .fire(caster, speed)
endmethod
//fires the missile at a unit!
method fireTarget takes unit caster, unit target, real speed returns nothing
set .Homing = true
set .Target = target
set .TargetX = GetUnitX(.Target)
set .TargetY = GetUnitY(.Target)
call .fire(caster, speed)
endmethod
method onDestroy takes nothing returns nothing
set Index = Index - 1
set Dat[.id] = Dat[Index]
set Dat[.id].id = .id
call .ma.destroy()
call GroupClear(.hg)
call DestroyGroup(.hg)
call DestroyGroup(.hu)
if .SFX != null then
call DestroyEffect(.SFX)
endif
call GroupRemoveUnit(Missiles, .Dummy)
if .dm then
call KillUnit(.Dummy)
endif
endmethod
endstruct
private function Init takes nothing returns nothing
set Conds = Filter(function CondsFunc)
endfunction
endlibrary
Now, it's its add-on . . .
The add-on (Custom Missile) is editted from Element of Water's add-on
I add the same things as above
The most brilliant thing here is the missile-parabola, it's not only change the height of the missile, it also change its Z-facing (an object facing to a Z coordinate), making the missile extremely smooth
But there's still some flaws,
2. If you took a closer look, the missile use 0 degree Z-facing during its creation (made to parabola Z-facing in onLoop functions); and we already knew the System's prob . . . Now how can we fix this?
The Code
JASS:
library CustMissile needs Missile
globals
private boolexpr array actlib[8190]
private boolexpr act
private group ENUM_GROUP = CreateGroup()
private real Damage = 0.00
private boolean destroy
endglobals
private struct DamageData extends MissileActions
real damage
real aoe
real z
method getz takes nothing returns real
local location l=GetUnitLoc(GetEventMissile.Dummy)
local real z= GetLocationZ(l)
call RemoveLocation(l)
return z
endmethod
method onHit takes nothing returns boolean
local unit target = null
if .aoe <= 0 then
if GetEventMissile.OnTarget then
set target = GetEventMissile.Target
set destroy=false
else
set target = FirstOfGroup(GetEventMissile.HitUnits)
endif
if target != null then
call UnitDamageTarget(GetEventMissile.Caster, target, .damage, false, true, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_MAGIC, null)
endif
set target = null
else
if GetEventMissile.OnTarget then
set destroy=false
endif
set Damage = .damage
call GroupEnumUnitsInRange(ENUM_GROUP, GetEventMissile.X, GetEventMissile.Y, .aoe, act)
endif
return destroy
endmethod
method onLoop takes nothing returns boolean
call SetUnitFlyHeight(GetEventMissile.Dummy, .getz()+.z, 0.00)
return true
endmethod
endstruct
private struct ParabolaData extends MissileActions
real arc
unit target = null
real damage
real aoe
real z
real x1
real x2
real y1
real y2
method parabola takes nothing returns real
local location l1=GetUnitLoc(GetEventMissile.Target)
local location l2=GetUnitLoc(GetEventMissile.Dummy)
local real z0= GetLocationZ(l1)
local real z1= GetLocationZ(l2)
local real dx = GetEventMissile.TargetX - GetEventMissile.StartX
local real dy = GetEventMissile.TargetY - GetEventMissile.StartY
local real h= .arc*SquareRoot(dx * dx + dy * dy)
local real d= SquareRoot(dx * dx + dy * dy)
local real x= GetEventMissile.CurrentDist
call RemoveLocation(l1)
call RemoveLocation(l2)
return 4 * h * x * (d -x) / (d * d) + x * (z1-z0) / d + z0
endmethod
method parabolafacing takes nothing returns nothing
local integer zangle
set .x1 = .x2
set .x2 = GetUnitX(GetEventMissile.Dummy)
set .y1 = .y2
set .y2 = .parabola()+.arc
set zangle = R2I(Atan2(.y2 - .y1, .x2 - .x1) + 90.5)
if zangle >= 180 then
set zangle = 179
elseif zangle < 0 then
set zangle = 0
endif
call SetUnitAnimationByIndex(GetEventMissile.Dummy, zangle)
endmethod
method onHit takes nothing returns boolean
if .aoe <= 0 and .target != null then
call UnitDamageTarget(GetEventMissile.Caster, .target, .damage, false, true, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_MAGIC, null)
else
set Damage = .damage
call GroupEnumUnitsInRange(ENUM_GROUP, GetEventMissile.X, GetEventMissile.Y, .aoe, act)
endif
return false
endmethod
method onLoop takes nothing returns boolean
call SetUnitFlyHeight(GetEventMissile.Dummy, .parabola()+.z, 0.00)
call .parabolafacing()
return true
endmethod
endstruct
function CreateTargetMissile takes unit caster, unit target, string sfx, real x, real y, real z, real speed, real damage, real radius, real aoe, real arc, code acode, boolean boold returns nothing
local ParabolaData d0
local DamageData d1
local MissileActions d
local Missile m
set destroy=boold
set act=Filter(acode)
if arc <= 0. then
set d1 = DamageData.create()
set d1.z = z
set d1.damage = damage
set d1.aoe = aoe
set d = d1
else
set radius = 0.
set d0 = ParabolaData.create()
set d0.arc = arc
set d0.x1 = x
set d0.x2 = x
set d0.y1 = arc
set d0.y2 = arc
set d0.z = z
set d0.target = target
set d0.damage = damage
set d0.aoe = aoe
set d = d0
endif
set m = Missile.create(sfx, x, y, z, radius, d)
call m.fireTarget(caster, target, speed)
endfunction
function CreatePointMissile takes unit caster, real targetX, real targetY, string sfx, real x, real y, real z, real speed, real damage, real radius, real aoe, real arc, code acode, boolean boold returns nothing
local ParabolaData d0
local DamageData d1
local MissileActions d
local Missile m
set destroy=boold
set act=Filter(acode)
if arc <= 0. then
set d1 = DamageData.create()
set d1.z = z
set d1.damage = damage
set d1.aoe = aoe
set d = d1
else
set radius = 0.
set d0 = ParabolaData.create()
set d0.arc = arc
set d0.x1 = x
set d0.x2 = x
set d0.y1 = arc
set d0.y2 = arc
set d0.z = z
set d0.damage = damage
set d0.aoe = aoe
set d = d0
endif
set m = Missile.create(sfx, x, y, z, radius, d)
call m.fireFixed(caster, targetX, targetY, speed)
endfunction
endlibrary
Fixes and Rewrites are gladly welcome exchanged with reps
Thanks for your helps ^_^
Last edited: