| 12-22-2011, 08:11 AM | #1 |
Missile System from Element of Water, i edit this 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 Custom Missile, this is the primary one that use extremely smooth parabola the code: JASS:library CustMissile initializer Init 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 //FILTER LIBRARY private function Act1 takes nothing returns boolean call UnitDamageTarget(GetEventMissile.Caster, GetFilterUnit(), Damage, false, true, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_MAGIC, null) return false endfunction //END FILTER LIBRARY private function GetAction takes integer acode returns nothing set act=actlib[acode] endfunction 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, integer acode, boolean boold returns nothing local ParabolaData d0 local DamageData d1 local MissileActions d local Missile m set destroy=boold if acode !=0 then call GetAction(acode) endif 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, integer acode, boolean boold returns nothing local ParabolaData d0 local DamageData d1 local MissileActions d local Missile m set destroy=boold if acode!=0 then call GetAction(acode) endif 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 private function Init takes nothing returns nothing local integer i=1 set actlib[i]=Filter(function Act1) endfunction endlibrary Flaws: 1. I don't know how to make a function as the Filter, so i use a filter library instead 2. You can notice when you make the missile, on the creation the missile has 0 Z-facing and it should be customized. The problem is, everytime i tried to modify the missile after the creation (no waits) i always got a bug. No missile come out, or just a destroyed missile and it screws up my other systems that used with it. Can anyone fix this ? ![]() |
