public DetectionCircleRenderable(WPos centerPosition, WDist radius, int zOffset, int lineTrails, WAngle trailSeparation, WAngle trailAngle, Color color, Color contrastColor) { this.centerPosition = centerPosition; this.radius = radius; this.zOffset = zOffset; trailCount = lineTrails; this.trailSeparation = trailSeparation; this.trailAngle = trailAngle; this.color = color; this.contrastColor = contrastColor; }
public Missile(MissileInfo info, ProjectileArgs args) { this.info = info; this.args = args; pos = args.Source; hFacing = args.Facing; gravity = new WVec(0, 0, -info.Gravity); targetPosition = args.PassiveTarget; rangeLimit = info.RangeLimit != WDist.Zero ? info.RangeLimit : args.Weapon.Range; minLaunchSpeed = info.MinimumLaunchSpeed.Length > -1 ? info.MinimumLaunchSpeed.Length : info.Speed.Length; maxLaunchSpeed = info.MaximumLaunchSpeed.Length > -1 ? info.MaximumLaunchSpeed.Length : info.Speed.Length; maxSpeed = info.Speed.Length; minLaunchAngle = info.MinimumLaunchAngle; maxLaunchAngle = info.MaximumLaunchAngle; var world = args.SourceActor.World; if (info.Inaccuracy.Length > 0) { var inaccuracy = Util.ApplyPercentageModifiers(info.Inaccuracy.Length, args.InaccuracyModifiers); offset = WVec.FromPDF(world.SharedRandom, 2) * inaccuracy / 1024; } DetermineLaunchSpeedAndAngle(world, out speed, out vFacing); velocity = new WVec(0, -speed, 0) .Rotate(new WRot(WAngle.FromFacing(vFacing), WAngle.Zero, WAngle.Zero)) .Rotate(new WRot(WAngle.Zero, WAngle.Zero, WAngle.FromFacing(hFacing))); if (world.SharedRandom.Next(100) <= info.LockOnProbability) lockOn = true; if (!string.IsNullOrEmpty(info.Image)) { anim = new Animation(world, info.Image, () => renderFacing); anim.PlayRepeating(info.Sequences.Random(world.SharedRandom)); } if (info.ContrailLength > 0) { var color = info.ContrailUsePlayerColor ? ContrailRenderable.ChooseColor(args.SourceActor) : info.ContrailColor; contrail = new ContrailRenderable(world, color, info.ContrailWidth, info.ContrailLength, info.ContrailDelay, info.ContrailZOffset); } trailPalette = info.TrailPalette; if (info.TrailUsePlayerPalette) trailPalette += args.SourceActor.Owner.InternalName; }
public Bullet(BulletInfo info, ProjectileArgs args) { this.info = info; this.args = args; pos = args.Source; var world = args.SourceActor.World; if (info.LaunchAngle.Length > 1) angle = new WAngle(world.SharedRandom.Next(info.LaunchAngle[0].Angle, info.LaunchAngle[1].Angle)); else angle = info.LaunchAngle[0]; if (info.Speed.Length > 1) speed = new WDist(world.SharedRandom.Next(info.Speed[0].Length, info.Speed[1].Length)); else speed = info.Speed[0]; target = args.PassiveTarget; if (info.Inaccuracy.Length > 0) { var inaccuracy = Util.ApplyPercentageModifiers(info.Inaccuracy.Length, args.InaccuracyModifiers); var range = Util.ApplyPercentageModifiers(args.Weapon.Range.Length, args.RangeModifiers); var maxOffset = inaccuracy * (target - pos).Length / range; target += WVec.FromPDF(world.SharedRandom, 2) * maxOffset / 1024; } facing = (target - pos).Yaw.Facing; length = Math.Max((target - pos).Length / speed.Length, 1); if (!string.IsNullOrEmpty(info.Image)) { anim = new Animation(world, info.Image, new Func<int>(GetEffectiveFacing)); anim.PlayRepeating(info.Sequences.Random(world.SharedRandom)); } if (info.ContrailLength > 0) { var color = info.ContrailUsePlayerColor ? ContrailRenderable.ChooseColor(args.SourceActor) : info.ContrailColor; contrail = new ContrailRenderable(world, color, info.ContrailWidth, info.ContrailLength, info.ContrailDelay, info.ContrailZOffset); } trailPalette = info.TrailPalette; if (info.TrailUsePlayerPalette) trailPalette += args.SourceActor.Owner.InternalName; smokeTicks = info.TrailDelay; }
public VoxelPreview(VoxelAnimation[] components, WVec offset, int zOffset, float scale, WAngle lightPitch, WAngle lightYaw, float[] lightAmbientColor, float[] lightDiffuseColor, WAngle cameraPitch, PaletteReference colorPalette, PaletteReference normalsPalette, PaletteReference shadowPalette) { this.components = components; this.scale = scale; this.lightAmbientColor = lightAmbientColor; this.lightDiffuseColor = lightDiffuseColor; lightSource = new WRot(WAngle.Zero, new WAngle(256) - lightPitch, lightYaw); camera = new WRot(WAngle.Zero, cameraPitch - new WAngle(256), new WAngle(256)); this.colorPalette = colorPalette; this.normalsPalette = normalsPalette; this.shadowPalette = shadowPalette; this.offset = offset; this.zOffset = zOffset; }
public Bullet(BulletInfo info, ProjectileArgs args) { this.info = info; this.args = args; this.pos = args.Source; var world = args.SourceActor.World; if (info.Angle.Length > 1 && info.Speed.Length > 1) { angle = new WAngle(world.SharedRandom.Next(info.Angle[0].Angle, info.Angle[1].Angle)); speed = new WRange(world.SharedRandom.Next(info.Speed[0].Range, info.Speed[1].Range)); } else { angle = info.Angle[0]; speed = info.Speed[0]; } target = args.PassiveTarget; if (info.Inaccuracy.Range > 0) { var inaccuracy = OpenRA.Traits.Util.ApplyPercentageModifiers(info.Inaccuracy.Range, args.InaccuracyModifiers); var maxOffset = inaccuracy * (target - pos).Length / args.Weapon.Range.Range; target += WVec.FromPDF(world.SharedRandom, 2) * maxOffset / 1024; } facing = OpenRA.Traits.Util.GetFacing(target - pos, 0); length = Math.Max((target - pos).Length / speed.Range, 1); if (info.Image != null) { anim = new Animation(world, info.Image, GetEffectiveFacing); anim.PlayRepeating("idle"); } if (info.ContrailLength > 0) { var color = info.ContrailUsePlayerColor ? ContrailRenderable.ChooseColor(args.SourceActor) : info.ContrailColor; trail = new ContrailRenderable(world, color, info.ContrailLength, info.ContrailDelay, 0); } smokeTicks = info.TrailDelay; }
public Leap(Actor self, Actor target, WeaponInfo weapon, WRange speed, WAngle angle) { var targetMobile = target.TraitOrDefault<Mobile>(); if (targetMobile == null) throw new InvalidOperationException("Leap requires a target actor with the Mobile trait"); this.weapon = weapon; this.angle = angle; mobile = self.Trait<Mobile>(); mobile.SetLocation(mobile.fromCell, mobile.fromSubCell, targetMobile.fromCell, targetMobile.fromSubCell); mobile.IsMoving = true; from = self.CenterPosition; to = targetMobile.fromCell.CenterPosition + MobileInfo.SubCellOffsets[targetMobile.fromSubCell]; length = Math.Max((to - from).Length / speed.Range, 1); self.Trait<RenderInfantry>().Attacking(self, Target.FromActor(target)); if (weapon.Report != null && weapon.Report.Any()) Sound.Play(weapon.Report.Random(self.World.SharedRandom), self.CenterPosition); }
public Leap(Actor self, Actor target, WeaponInfo weapon, WDist speed, WAngle angle) { var targetMobile = target.TraitOrDefault<Mobile>(); if (targetMobile == null) throw new InvalidOperationException("Leap requires a target actor with the Mobile trait"); this.weapon = weapon; this.angle = angle; mobile = self.Trait<Mobile>(); mobile.SetLocation(mobile.FromCell, mobile.FromSubCell, targetMobile.FromCell, targetMobile.FromSubCell); mobile.IsMoving = true; from = self.CenterPosition; to = self.World.Map.CenterOfSubCell(targetMobile.FromCell, targetMobile.FromSubCell); length = Math.Max((to - from).Length / speed.Length, 1); // HACK: why isn't this using the interface? self.Trait<WithInfantryBody>().Attacking(self, Target.FromActor(target)); if (weapon.Report != null && weapon.Report.Any()) Sound.Play(weapon.Report.Random(self.World.SharedRandom), self.CenterPosition); }
public Bullet(BulletInfo info, ProjectileArgs args) { this.info = info; this.args = args; this.pos = args.Source; // Convert ProjectileArg definitions to world coordinates // TODO: Change the yaml definitions so we don't need this var range = new WRange((int)(1024 * args.Weapon.Range)); // Range in world units var inaccuracy = new WRange((int)(info.Inaccuracy * 1024 / Game.CellSize)); // Offset in world units at max range var speed = (int)(info.Speed * 4 * 1024 / (10 * Game.CellSize)); // Speed in world units per tick angle = WAngle.ArcTan((int)(info.Angle * 4 * 1024), 1024); // Angle in world angle target = args.PassiveTarget; if (info.Inaccuracy > 0) { var maxOffset = inaccuracy.Range * (target - pos).Length / range.Range; target += WVec.FromPDF(args.SourceActor.World.SharedRandom, 2) * maxOffset / 1024; } facing = Traits.Util.GetFacing(target - pos, 0); length = Math.Max((target - pos).Length / speed, 1); if (info.Image != null) { anim = new Animation(info.Image, GetEffectiveFacing); anim.PlayRepeating("idle"); } if (info.ContrailLength > 0) { var color = info.ContrailUsePlayerColor ? ContrailRenderable.ChooseColor(args.SourceActor) : info.ContrailColor; trail = new ContrailRenderable(args.SourceActor.World, color, info.ContrailLength, info.ContrailDelay, 0); } smokeTicks = info.TrailDelay; }
public void Tick(World world) { ticks++; if (anim != null) { anim.Tick(); } // Switch from freefall mode to homing mode if (ticks == info.HomingActivationDelay + 1) { state = States.Homing; speed = velocity.Length; // Compute the vertical loop radius loopRadius = LoopRadius(speed, info.VerticalRateOfTurn); } // Switch from homing mode to freefall mode if (rangeLimit >= WDist.Zero && distanceCovered > rangeLimit) { state = States.Freefall; velocity = new WVec(0, -speed, 0) .Rotate(new WRot(WAngle.FromFacing(vFacing), WAngle.Zero, WAngle.Zero)) .Rotate(new WRot(WAngle.Zero, WAngle.Zero, WAngle.FromFacing(hFacing))); } // Check if target position should be updated (actor visible & locked on) var newTarPos = targetPosition; if (args.GuidedTarget.IsValidFor(args.SourceActor) && lockOn) { newTarPos = (args.Weapon.TargetActorCenter ? args.GuidedTarget.CenterPosition : args.GuidedTarget.Positions.PositionClosestTo(args.Source)) + new WVec(WDist.Zero, WDist.Zero, info.AirburstAltitude); } // Compute target's predicted velocity vector (assuming uniform circular motion) var yaw1 = tarVel.HorizontalLengthSquared != 0 ? tarVel.Yaw : WAngle.FromFacing(hFacing); tarVel = newTarPos - targetPosition; var yaw2 = tarVel.HorizontalLengthSquared != 0 ? tarVel.Yaw : WAngle.FromFacing(hFacing); predVel = tarVel.Rotate(WRot.FromYaw(yaw2 - yaw1)); targetPosition = newTarPos; // Compute current distance from target position var tarDistVec = targetPosition + offset - pos; var relTarDist = tarDistVec.Length; var relTarHorDist = tarDistVec.HorizontalLength; WVec move; if (state == States.Freefall) { move = FreefallTick(); } else { move = HomingTick(world, tarDistVec, relTarHorDist); } renderFacing = new WVec(move.X, move.Y - move.Z, 0).Yaw.Facing; // Move the missile var lastPos = pos; if (info.AllowSnapping && state != States.Freefall && relTarDist < move.Length) { pos = targetPosition + offset; } else { pos += move; } // Check for walls or other blocking obstacles var shouldExplode = false; WPos blockedPos; if (info.Blockable && BlocksProjectiles.AnyBlockingActorsBetween(world, lastPos, pos, info.Width, out blockedPos)) { pos = blockedPos; shouldExplode = true; } // Create the sprite trail effect if (!string.IsNullOrEmpty(info.TrailImage) && --ticksToNextSmoke < 0 && (state != States.Freefall || info.TrailWhenDeactivated)) { world.AddFrameEndTask(w => w.Add(new SpriteEffect(pos - 3 * move / 2, w, info.TrailImage, info.TrailSequences.Random(world.SharedRandom), trailPalette, false, false, renderFacing))); ticksToNextSmoke = info.TrailInterval; } if (info.ContrailLength > 0) { contrail.Update(pos); } distanceCovered += new WDist(speed); var cell = world.Map.CellContaining(pos); var height = world.Map.DistanceAboveTerrain(pos); shouldExplode |= height.Length < 0 || // Hit the ground relTarDist < info.CloseEnough.Length || // Within range (info.ExplodeWhenEmpty && rangeLimit >= WDist.Zero && distanceCovered > rangeLimit) || // Ran out of fuel !world.Map.Contains(cell) || // This also avoids an IndexOutOfRangeException in GetTerrainInfo below. (!string.IsNullOrEmpty(info.BoundToTerrainType) && world.Map.GetTerrainInfo(cell).Type != info.BoundToTerrainType) || // Hit incompatible terrain (height.Length < info.AirburstAltitude.Length && relTarHorDist < info.CloseEnough.Length); // Airburst if (shouldExplode) { Explode(world); } }
void ITick.Tick(Actor self) { lineAngle += info.UpdateLineTick; }
// Orientation in unit-space public WRot LocalOrientation(Actor self) { // Hack: turretFacing is relative to the world, so subtract the body yaw return(WRot.FromYaw(WAngle.FromFacing(turretFacing) - self.Orientation.Yaw)); }
Sprite ISpriteSequence.GetSprite(int frame, WAngle facing) { throw exception; }
public IEnumerable <IActorPreview> RenderPreviewSprites(ActorPreviewInitializer init, RenderSpritesInfo rs, string image, int facings, PaletteReference p) { if (UpgradeMinEnabledLevel > 0) { yield break; } var body = init.Actor.TraitInfo <BodyOrientationInfo>(); var armament = init.Actor.TraitInfos <ArmamentInfo>() .First(a => a.Name == Armament); var t = init.Actor.TraitInfos <TurretedInfo>() .First(tt => tt.Turret == armament.Turret); var anim = new Animation(init.World, image, () => t.InitialFacing); anim.Play(RenderSprites.NormalizeSequence(anim, init.GetDamageState(), Sequence)); var turretOrientation = body.QuantizeOrientation(new WRot(WAngle.Zero, WAngle.Zero, WAngle.FromFacing(t.InitialFacing)), facings); var turretOffset = body.LocalToWorld(t.Offset.Rotate(turretOrientation)); yield return(new SpriteActorPreview(anim, turretOffset, turretOffset.Y + turretOffset.Z, p, rs.Scale)); }
public Sprite GetSprite(int frame, WAngle facing) { return(GetSprite(Start, frame, facing)); }
protected virtual int GetFacingFrameOffset(WAngle facing) { return(Util.IndexFacing(facing, Facings)); }
int HomingInnerTick(int predClfDist, int diffClfMslHgt, int relTarHorDist, int lastHtChg, int lastHt, int nxtRelTarHorDist, int relTarHgt, int vFacing, bool targetPassedBy) { int desiredVFacing = vFacing; // Incline coming up -> attempt to reach the incline so that after predClfDist // the height above the terrain is positive but as close to 0 as possible // Also, never change horizontal facing and never travel backwards // Possible techniques to avoid close cliffs are deceleration, turning // as sharply as possible to travel directly upwards and then returning // to zero vertical facing as low as possible while still not hitting the // high terrain. A last technique (and the preferred one, normally used when // the missile hasn't been fired near a cliff) is simply finding the smallest // vertical facing that allows for a smooth climb to the new terrain's height // and coming in at predClfDist at exactly zero vertical facing if (info.TerrainHeightAware && diffClfMslHgt >= 0 && !allowPassBy) { desiredVFacing = IncreaseAltitude(predClfDist, diffClfMslHgt, relTarHorDist, vFacing); } else if (relTarHorDist <= 3 * loopRadius || state == States.Hitting) { // No longer travel at cruise altitude state = States.Hitting; if (lastHt >= targetPosition.Z) { allowPassBy = true; } if (!allowPassBy && (lastHt < targetPosition.Z || targetPassedBy)) { // Aim for the target var vDist = new WVec(-relTarHgt, -relTarHorDist, 0); desiredVFacing = (sbyte)vDist.HorizontalLengthSquared != 0 ? vDist.Yaw.Facing : vFacing; // Do not accept -1 as valid vertical facing since it is usually a numerical error // and will lead to premature descent and crashing into the ground if (desiredVFacing == -1) { desiredVFacing = 0; } // If the target has been passed by, limit the absolute value of // vertical facing by the maximum vertical rate of turn // Do this because the missile will be looping horizontally // and thus needs smaller vertical facings so as not // to hit the ground prematurely if (targetPassedBy) { desiredVFacing = desiredVFacing.Clamp(-info.VerticalRateOfTurn.Facing, info.VerticalRateOfTurn.Facing); } else if (lastHt == 0) { // Before the target is passed by, missile speed should be changed // Target's height above loop's center var tarHgt = (loopRadius * WAngle.FromFacing(vFacing).Cos() / 1024 - System.Math.Abs(relTarHgt)).Clamp(0, loopRadius); // Target's horizontal distance from loop's center var tarDist = Exts.ISqrt(loopRadius * loopRadius - tarHgt * tarHgt); // Missile's horizontal distance from loop's center var missDist = loopRadius * WAngle.FromFacing(vFacing).Sin() / 1024; // If the current height does not permit the missile // to hit the target before passing it by, lower speed // Otherwise, increase speed if (relTarHorDist <= tarDist - System.Math.Sign(relTarHgt) * missDist) { ChangeSpeed(-1); } else { ChangeSpeed(); } } } else if (allowPassBy || (lastHt != 0 && relTarHorDist - lastHtChg < loopRadius)) { // Only activate this part if target too close to cliff allowPassBy = true; // Vector from missile's current position pointing to the loop's center var radius = new WVec(loopRadius, 0, 0) .Rotate(new WRot(WAngle.Zero, WAngle.Zero, WAngle.FromFacing(64 - vFacing))); // Vector from loop's center to incline top hardcoded in height buffer zone var edgeVector = new WVec(lastHtChg, lastHt - pos.Z, 0) - radius; if (!targetPassedBy) { // Climb to critical height if (relTarHorDist > 2 * loopRadius) { // Target's distance from cliff var d1 = relTarHorDist - lastHtChg; if (d1 < 0) { d1 = 0; } if (d1 > 2 * loopRadius) { return(0); } // Find critical height at which the missile must be once it is at one loopRadius // away from the target var h1 = loopRadius - Exts.ISqrt(d1 * (2 * loopRadius - d1)) - (pos.Z - lastHt); if (h1 > loopRadius * (1024 - WAngle.FromFacing(vFacing).Cos()) / 1024) { desiredVFacing = WAngle.ArcTan(Exts.ISqrt(h1 * (2 * loopRadius - h1)), loopRadius - h1).Angle >> 2; } else { desiredVFacing = 0; } // TODO: deceleration checks!!! } else { // Avoid the cliff edge if (info.TerrainHeightAware && edgeVector.Length > loopRadius && lastHt > targetPosition.Z) { int vFac; for (vFac = vFacing + 1; vFac <= vFacing + info.VerticalRateOfTurn.Facing - 1; vFac++) { // Vector from missile's current position pointing to the loop's center radius = new WVec(loopRadius, 0, 0) .Rotate(new WRot(WAngle.Zero, WAngle.Zero, WAngle.FromFacing(64 - vFac))); // Vector from loop's center to incline top + 64 hardcoded in height buffer zone edgeVector = new WVec(lastHtChg, lastHt - pos.Z, 0) - radius; if (edgeVector.Length <= loopRadius) { break; } } desiredVFacing = vFac; } else { // Aim for the target var vDist = new WVec(-relTarHgt, -relTarHorDist, 0); desiredVFacing = (sbyte)vDist.HorizontalLengthSquared != 0 ? vDist.Yaw.Facing : vFacing; if (desiredVFacing < 0 && info.VerticalRateOfTurn.Facing < (sbyte)vFacing) { desiredVFacing = 0; } } } } else { // Aim for the target var vDist = new WVec(-relTarHgt, relTarHorDist, 0); desiredVFacing = (sbyte)vDist.HorizontalLengthSquared != 0 ? vDist.Yaw.Facing : vFacing; if (desiredVFacing < 0 && info.VerticalRateOfTurn.Facing < (sbyte)vFacing) { desiredVFacing = 0; } } } else { // Aim to attain cruise altitude as soon as possible while having the absolute value // of vertical facing bound by the maximum vertical rate of turn var vDist = new WVec(-diffClfMslHgt - info.CruiseAltitude.Length, -speed, 0); desiredVFacing = (sbyte)vDist.HorizontalLengthSquared != 0 ? vDist.Yaw.Facing : vFacing; // If the missile is launched above CruiseAltitude, it has to descend instead of climbing if (-diffClfMslHgt > info.CruiseAltitude.Length) { desiredVFacing = -desiredVFacing; } desiredVFacing = desiredVFacing.Clamp(-info.VerticalRateOfTurn.Facing, info.VerticalRateOfTurn.Facing); ChangeSpeed(); } } else { // Aim to attain cruise altitude as soon as possible while having the absolute value // of vertical facing bound by the maximum vertical rate of turn var vDist = new WVec(-diffClfMslHgt - info.CruiseAltitude.Length, -speed, 0); desiredVFacing = (sbyte)vDist.HorizontalLengthSquared != 0 ? vDist.Yaw.Facing : vFacing; // If the missile is launched above CruiseAltitude, it has to descend instead of climbing if (-diffClfMslHgt > info.CruiseAltitude.Length) { desiredVFacing = -desiredVFacing; } desiredVFacing = desiredVFacing.Clamp(-info.VerticalRateOfTurn.Facing, info.VerticalRateOfTurn.Facing); ChangeSpeed(); } return(desiredVFacing); }
public Bullet(BulletInfo info, ProjectileArgs args) { this.info = info; this.args = args; pos = args.Source; source = args.Source; var world = args.SourceActor.World; if (info.LaunchAngle.Length > 1) { angle = new WAngle(world.SharedRandom.Next(info.LaunchAngle[0].Angle, info.LaunchAngle[1].Angle)); } else { angle = info.LaunchAngle[0]; } if (info.Speed.Length > 1) { speed = new WDist(world.SharedRandom.Next(info.Speed[0].Length, info.Speed[1].Length)); } else { speed = info.Speed[0]; } target = args.PassiveTarget; if (info.Inaccuracy.Length > 0) { var maxInaccuracyOffset = Util.GetProjectileInaccuracy(info.Inaccuracy.Length, info.InaccuracyType, args); target += WVec.FromPDF(world.SharedRandom, 2) * maxInaccuracyOffset / 1024; } if (info.AirburstAltitude > WDist.Zero) { target += new WVec(WDist.Zero, WDist.Zero, info.AirburstAltitude); } facing = (target - pos).Yaw; length = Math.Max((target - pos).Length / speed.Length, 1); if (!string.IsNullOrEmpty(info.Image)) { anim = new Animation(world, info.Image, new Func <WAngle>(GetEffectiveFacing)); anim.PlayRepeating(info.Sequences.Random(world.SharedRandom)); } if (info.ContrailLength > 0) { var color = info.ContrailUsePlayerColor ? ContrailRenderable.ChooseColor(args.SourceActor) : info.ContrailColor; contrail = new ContrailRenderable(world, color, info.ContrailWidth, info.ContrailLength, info.ContrailDelay, info.ContrailZOffset); } trailPalette = info.TrailPalette; if (info.TrailUsePlayerPalette) { trailPalette += args.SourceActor.Owner.InternalName; } smokeTicks = info.TrailDelay; remainingBounces = info.BounceCount; }
// This function checks if the missile's vertical facing is // nonnegative, and the incline top's horizontal distance from the missile is // less than loopRadius * (1024 - WAngle.FromFacing(vFacing).Sin()) / 1024 static bool IsNearInclineTop(int vFacing, int loopRadius, int predClfDist) { return(vFacing >= 0 && predClfDist <= loopRadius * (1024 - WAngle.FromFacing(vFacing).Sin()) / 1024); }
int IncreaseAltitude(int predClfDist, int diffClfMslHgt, int relTarHorDist, int vFacing) { var desiredVFacing = vFacing; // If missile is below incline top height and facing downwards, bring back // its vertical facing above zero as soon as possible if ((sbyte)vFacing < 0) { desiredVFacing = info.VerticalRateOfTurn.Facing; } // Missile will climb around incline top if bringing vertical facing // down to zero on an arc of radius loopRadius else if (IsNearInclineTop(vFacing, loopRadius, predClfDist) && WillClimbAroundInclineTop(vFacing, loopRadius, predClfDist, diffClfMslHgt, speed)) { desiredVFacing = 0; } // Missile will not climb terrAltDiff w-units within hHeightChange w-units // all the while ending the ascent with vertical facing 0 else if (!WillClimbWithinDistance(vFacing, loopRadius, predClfDist, diffClfMslHgt)) { // Find smallest vertical facing, attainable in the next tick, // for which the missile will be able to climb terrAltDiff w-units // within hHeightChange w-units all the while ending the ascent // with vertical facing 0 for (var vFac = System.Math.Min(vFacing + info.VerticalRateOfTurn.Facing - 1, 63); vFac >= vFacing; vFac--) { if (!WillClimbWithinDistance(vFac, loopRadius, predClfDist, diffClfMslHgt) && !(predClfDist <= loopRadius * (1024 - WAngle.FromFacing(vFac).Sin()) / 1024 && WillClimbAroundInclineTop(vFac, loopRadius, predClfDist, diffClfMslHgt, speed))) { desiredVFacing = vFac + 1; break; } } } // Attained height after ascent as predicted from upper part of incline surmounting manoeuvre var predAttHght = loopRadius * (1024 - WAngle.FromFacing(vFacing).Cos()) / 1024 - diffClfMslHgt; // Should the missile be slowed down in order to make it more maneuverable var slowDown = info.Acceleration.Length != 0 && // Possible to decelerate ((desiredVFacing != 0 // Lower part of incline surmounting manoeuvre // Incline will be hit before vertical facing attains 64 && (predClfDist <= loopRadius * (1024 - WAngle.FromFacing(vFacing).Sin()) / 1024 // When evaluating this the incline will be *not* be hit before vertical facing attains 64 // At current speed target too close to hit without passing it by || relTarHorDist <= 2 * loopRadius * (2048 - WAngle.FromFacing(vFacing).Sin()) / 1024 - predClfDist)) || (desiredVFacing == 0 && // Upper part of incline surmounting manoeuvre relTarHorDist <= loopRadius * WAngle.FromFacing(vFacing).Sin() / 1024 + Exts.ISqrt(predAttHght * (2 * loopRadius - predAttHght)))); // Target too close to hit at current speed if (slowDown) { ChangeSpeed(-1); } return(desiredVFacing); }
void DetermineLaunchSpeedAndAngleForIncline(int predClfDist, int diffClfMslHgt, int relTarHorDist, out int speed, out int vFacing) { speed = maxLaunchSpeed; // Find smallest vertical facing, for which the missile will be able to climb terrAltDiff w-units // within hHeightChange w-units all the while ending the ascent with vertical facing 0 vFacing = maxLaunchAngle.Angle >> 2; // Compute minimum speed necessary to both be able to face directly upwards and have enough space // to hit the target without passing it by (and thus having to do horizontal loops) var minSpeed = ((System.Math.Min(predClfDist * 1024 / (1024 - WAngle.FromFacing(vFacing).Sin()), (relTarHorDist + predClfDist) * 1024 / (2 * (2048 - WAngle.FromFacing(vFacing).Sin()))) * info.VerticalRateOfTurn.Facing * 157) / 6400).Clamp(minLaunchSpeed, maxLaunchSpeed); if ((sbyte)vFacing < 0) { speed = minSpeed; } else if (!WillClimbWithinDistance(vFacing, loopRadius, predClfDist, diffClfMslHgt) && !WillClimbAroundInclineTop(vFacing, loopRadius, predClfDist, diffClfMslHgt, speed)) { // Find highest speed greater than the above minimum that allows the missile // to surmount the incline var vFac = vFacing; speed = BisectionSearch(minSpeed, maxLaunchSpeed, spd => { var lpRds = LoopRadius(spd, info.VerticalRateOfTurn.Facing); return(WillClimbWithinDistance(vFac, lpRds, predClfDist, diffClfMslHgt) || WillClimbAroundInclineTop(vFac, lpRds, predClfDist, diffClfMslHgt, spd)); }); } else { // Find least vertical facing that will allow the missile to climb // terrAltDiff w-units within hHeightChange w-units // all the while ending the ascent with vertical facing 0 vFacing = BisectionSearch(System.Math.Max((sbyte)(minLaunchAngle.Angle >> 2), (sbyte)0), (sbyte)(maxLaunchAngle.Angle >> 2), vFac => !WillClimbWithinDistance(vFac, loopRadius, predClfDist, diffClfMslHgt)) + 1; } }
public IEnumerable <VoxelAnimation> RenderPreviewVoxels(ActorPreviewInitializer init, RenderVoxelsInfo rv, string image, WRot orientation, int facings, PaletteReference p) { var body = init.Actor.Traits.Get <BodyOrientationInfo>(); var armament = init.Actor.Traits.WithInterface <ArmamentInfo>() .First(a => a.Name == Armament); var t = init.Actor.Traits.WithInterface <TurretedInfo>() .First(tt => tt.Turret == armament.Turret); var voxel = VoxelProvider.GetVoxel(image, Sequence); var turretOrientation = body.QuantizeOrientation(new WRot(WAngle.Zero, WAngle.Zero, WAngle.FromFacing(t.InitialFacing) - orientation.Yaw), facings); var turretOffset = body.LocalToWorld(t.Offset.Rotate(orientation)); yield return(new VoxelAnimation(voxel, () => turretOffset, () => new [] { turretOrientation, orientation }, () => false, () => 0)); }
public void SendAirstrike(Actor self, WPos target, bool randomize = true, int attackFacing = 0) { var info = Info as AirstrikePowerASInfo; if (randomize) { attackFacing = 256 * self.World.SharedRandom.Next(info.QuantizedFacings) / info.QuantizedFacings; } var altitude = self.World.Map.Rules.Actors[info.UnitType].TraitInfo <AircraftInfo>().CruiseAltitude.Length; var attackRotation = WRot.FromFacing(attackFacing); var delta = new WVec(0, -1024, 0).Rotate(attackRotation); target = target + new WVec(0, 0, altitude); var startPos = target - (self.World.Map.DistanceToEdge(target, -delta) + info.Cordon).Length * delta / 1024; self.World.AddFrameEndTask(w => { PlayLaunchSounds(); var aircrafts = new HashSet <Actor>(); for (var i = -info.SquadSize / 2; i <= info.SquadSize / 2; i++) { // Even-sized squads skip the lead plane if (i == 0 && (info.SquadSize & 1) == 0) { continue; } // Includes the 90 degree rotation between body and world coordinates var so = info.SquadOffset; var spawnOffset = new WVec(i * so.Y, -Math.Abs(i) * so.X, 0).Rotate(attackRotation); var a = w.CreateActor(info.UnitType, new TypeDictionary { new CenterPositionInit(startPos + spawnOffset), new OwnerInit(self.Owner), new FacingInit(WAngle.FromFacing(attackFacing)), }); delta = new WVec(WDist.Zero, info.BeaconDistanceOffset, WDist.Zero).Rotate(attackRotation); if (info.Mission == AirstrikeMission.Attack) { var height = self.World.Map.DistanceAboveTerrain(target + spawnOffset); a.QueueActivity(new FlyAttack(a, AttackSource.Default, Target.FromPos(target + spawnOffset - new WVec(WDist.Zero, WDist.Zero, height)), true, Color.OrangeRed)); } else { a.QueueActivity(new Fly(a, Target.FromPos(target + spawnOffset))); a.QueueActivity(new AttackMoveActivity(a, () => new FlyIdle(a, info.GuardDuration, false))); } a.QueueActivity(new FlyOffMap(a)); a.QueueActivity(new RemoveSelf()); aircrafts.Add(a); } ; var effect = new AirstrikePowerASEffect(self.World, self.Owner, target, aircrafts, this, info); self.World.Add(effect); }); }
public IEnumerable <VoxelAnimation> RenderPreviewVoxels( ActorPreviewInitializer init, RenderVoxelsInfo rv, string image, Func <WRot> orientation, int facings, PaletteReference p) { if (!EnabledByDefault) { yield break; } var body = init.Actor.TraitInfo <BodyOrientationInfo>(); var armament = init.Actor.TraitInfos <ArmamentInfo>() .First(a => a.Name == Armament); var t = init.Actor.TraitInfos <TurretedInfo>() .First(tt => tt.Turret == armament.Turret); var voxel = VoxelProvider.GetVoxel(image, Sequence); var turretFacing = Turreted.TurretFacingFromInit(init, t.InitialFacing, t.Turret); Func <WRot> turretOrientation = () => body.QuantizeOrientation(WRot.FromYaw(WAngle.FromFacing(turretFacing()) - orientation().Yaw), facings); Func <WRot> quantizedTurret = () => body.QuantizeOrientation(turretOrientation(), facings); Func <WRot> quantizedBody = () => body.QuantizeOrientation(orientation(), facings); Func <WVec> barrelOffset = () => body.LocalToWorld((t.Offset + LocalOffset.Rotate(quantizedTurret())).Rotate(quantizedBody())); yield return(new VoxelAnimation(voxel, barrelOffset, () => new[] { turretOrientation(), orientation() }, () => false, () => 0, ShowShadow)); }
public LuaTable ReinforceWithTransport(Player owner, string actorType, string[] cargoTypes, CPos[] entryPath, CPos[] exitPath = null, LuaFunction actionFunc = null, LuaFunction exitFunc = null, int dropRange = 3) { var transport = CreateActor(owner, actorType, true, entryPath[0], entryPath.Length > 1 ? entryPath[1] : (CPos?)null); var cargo = transport.TraitOrDefault <Cargo>(); var passengers = new List <Actor>(); if (cargo != null && cargoTypes != null) { foreach (var cargoType in cargoTypes) { var passenger = CreateActor(owner, cargoType, false, entryPath[0]); passengers.Add(passenger); cargo.Load(transport, passenger); } } for (var i = 1; i < entryPath.Length; i++) { Move(transport, entryPath[i]); } if (actionFunc != null) { var af = (LuaFunction)actionFunc.CopyReference(); transport.QueueActivity(new CallFunc(() => { using (af) using (LuaValue t = transport.ToLuaValue(Context), p = passengers.ToArray().ToLuaValue(Context)) af.Call(t, p); })); } else { var aircraft = transport.TraitOrDefault <Aircraft>(); // Scripted cargo aircraft must turn to default position before unloading. // TODO: pass facing through UnloadCargo instead. if (aircraft != null) { transport.QueueActivity(new Land(transport, Target.FromCell(transport.World, entryPath.Last()), WDist.FromCells(dropRange), WAngle.FromFacing(aircraft.Info.InitialFacing))); } if (cargo != null) { transport.QueueActivity(new UnloadCargo(transport, WDist.FromCells(dropRange))); } } if (exitFunc != null) { var ef = (LuaFunction)exitFunc.CopyReference(); transport.QueueActivity(new CallFunc(() => { using (ef) using (var t = transport.ToLuaValue(Context)) ef.Call(t); })); } else if (exitPath != null) { foreach (var wpt in exitPath) { Move(transport, wpt); } transport.QueueActivity(new RemoveSelf()); } var ret = Context.CreateTable(); using (LuaValue tKey = 1, tValue = transport.ToLuaValue(Context), pKey = 2, pValue = passengers.ToArray().ToLuaValue(Context)) { ret.Add(tKey, tValue); ret.Add(pKey, pValue); } return(ret); }
public static void FlyTick(Actor self, Aircraft aircraft, WAngle desiredFacing, WDist desiredAltitude, bool idleTurn = false) { FlyTick(self, aircraft, desiredFacing, desiredAltitude, WVec.Zero, idleTurn); }
public Sprite GetShadow(int frame, WAngle facing) { return(ShadowStart >= 0 ? GetSprite(ShadowStart, frame, facing) : null); }
public int[] AsQuarternion() { // Angles increase clockwise var r = new WAngle(-Roll.Angle / 2); var p = new WAngle(-Pitch.Angle / 2); var y = new WAngle(-Yaw.Angle / 2); var cr = (long)r.Cos(); var sr = (long)r.Sin(); var cp = (long)p.Cos(); var sp = (long)p.Sin(); var cy = (long)y.Cos(); var sy = (long)y.Sin(); // Normalized to 1024 == 1.0 return new int[4] { (int)((sr*cp*cy - cr*sp*sy) / 1048576), // x (int)((cr*sp*cy + sr*cp*sy) / 1048576), // y (int)((cr*cp*sy - sr*sp*cy) / 1048576), // z (int)((cr*cp*cy + sr*sp*sy) / 1048576) // w }; }
public IEnumerable <IActorPreview> RenderPreviewSprites(ActorPreviewInitializer init, RenderSpritesInfo rs, string image, int facings, PaletteReference p) { var body = init.Actor.TraitInfo <BodyOrientationInfo>(); var facing = init.Contains <FacingInit>() ? init.Get <FacingInit, int>() : 0; var anim = new Animation(init.World, image, () => facing); anim.PlayRepeating(RenderSprites.NormalizeSequence(anim, init.GetDamageState(), Sequence)); var orientation = body.QuantizeOrientation(new WRot(WAngle.Zero, WAngle.Zero, WAngle.FromFacing(facing)), facings); var offset = body.LocalToWorld(Offset.Rotate(orientation)); yield return(new SpriteActorPreview(anim, offset, offset.Y + offset.Z + 1, p, rs.Scale)); }
public WRot(WAngle roll, WAngle pitch, WAngle yaw) { Roll = roll; Pitch = pitch; Yaw = yaw; }
void Explode(World world) { if (info.ContrailLength > 0) { world.AddFrameEndTask(w => w.Add(new ContrailFader(pos, contrail))); } world.AddFrameEndTask(w => w.Remove(this)); // Don't blow up in our launcher's face! if (ticks <= info.Arm) { return; } var warheadArgs = new WarheadArgs(args) { ImpactOrientation = new WRot(WAngle.Zero, WAngle.FromFacing(vFacing), WAngle.FromFacing(hFacing)), ImpactPosition = pos, }; args.Weapon.Impact(Target.FromPos(pos), warheadArgs); }
public Laser(ProjectileArgs args, LaserInfo info) { this.info = info; colors = new Color[info.Radius]; for (var i = 0; i < info.Radius; i++) { var color = info.Color == Color.Transparent ? args.SourceActor.Owner.Color.RGB : info.Color; var bw = (float)((info.InnerLightness - info.OuterLightness) * i / (info.Radius - 1) + info.OuterLightness) / 0xff; var dstR = bw > .5 ? 1 - (1 - 2 * (bw - .5)) * (1 - (float)color.R / 0xff) : 2 * bw * ((float)color.R / 0xff); var dstG = bw > .5 ? 1 - (1 - 2 * (bw - .5)) * (1 - (float)color.G / 0xff) : 2 * bw * ((float)color.G / 0xff); var dstB = bw > .5 ? 1 - (1 - 2 * (bw - .5)) * (1 - (float)color.B / 0xff) : 2 * bw * ((float)color.B / 0xff); colors[i] = Color.FromArgb((int)(dstR * 0xff), (int)(dstG * 0xff), (int)(dstB * 0xff)); } var direction = args.PassiveTarget - args.Source; if (info.Distortion != 0 || info.DistortionAnimation != 0) { leftVector = new WVec(direction.Y, -direction.X, 0); if (leftVector.Length != 0) { leftVector = 1024 * leftVector / leftVector.Length; } upVector = new WVec( -direction.X * direction.Z, -direction.Z * direction.Y, direction.X * direction.X + direction.Y * direction.Y); if (upVector.Length != 0) { upVector = 1024 * upVector / upVector.Length; } random = args.SourceActor.World.SharedRandom; } if (this.info.SegmentLength == WDist.Zero) { offsets = new[] { args.Source, args.PassiveTarget }; } else { var numSegments = (direction.Length - 1) / info.SegmentLength.Length + 1; offsets = new WPos[numSegments + 1]; offsets[0] = args.Source; offsets[offsets.Length - 1] = args.PassiveTarget; for (var i = 1; i < numSegments; i++) { var segmentStart = direction / numSegments * i; offsets[i] = args.Source + segmentStart; if (info.Distortion != 0) { var angle = WAngle.FromDegrees(random.Next(360)); var distortion = random.Next(info.Distortion); var offset = distortion * angle.Cos() * leftVector / (1024 * 1024) + distortion * angle.Sin() * upVector / (1024 * 1024); offsets[i] += offset; } } } args.Weapon.Impact(Target.FromPos(args.PassiveTarget), args.SourceActor, args.DamageModifiers); }
public IEnumerable <IActorPreview> RenderPreviewSprites(ActorPreviewInitializer init, RenderSpritesInfo rs, string image, int facings, PaletteReference p) { if (UpgradeMinEnabledLevel > 0) { yield break; } if (image == null) { yield break; } // For this, image must not be null if (Palette != null) { p = init.WorldRenderer.Palette(Palette); } var anim = new Animation(init.World, image); anim.PlayThen(OpeningSequence, () => anim.PlayRepeating(Sequence)); var body = init.Actor.TraitInfo <BodyOrientationInfo>(); var facing = init.Contains <FacingInit>() ? init.Get <FacingInit, int>() : 0; var orientation = body.QuantizeOrientation(new WRot(WAngle.Zero, WAngle.Zero, WAngle.FromFacing(facing)), facings); var offset = body.LocalToWorld(Offset.Rotate(orientation)); yield return(new SpriteActorPreview(anim, offset, offset.Y + offset.Z + 1, p, rs.Scale)); }
void ITick.Tick(Actor self) { if (IsTraitDisabled) { return; } wasStationary = !isMoving; isMoving = self.CenterPosition != cachedPosition; if ((isMoving && !Info.TrailWhileMoving) || (!isMoving && !Info.TrailWhileStationary)) { return; } if (isMoving == wasStationary && (Info.StartDelay > -1)) { cachedInterval = Info.StartDelay; ticks = 0; } if (++ticks >= cachedInterval) { var spawnCell = Info.SpawnAtLastPosition ? self.World.Map.CellContaining(cachedPosition) : self.World.Map.CellContaining(self.CenterPosition); if (!self.World.Map.Contains(spawnCell)) { return; } var type = self.World.Map.GetTerrainInfo(spawnCell).Type; if (++offset >= Info.Offsets.Length) { offset = 0; } if (!Info.TerrainTypes.Any() || Info.TerrainTypes.Contains(type)) { var spawnFacing = Info.SpawnAtLastPosition ? cachedFacing : facing?.Facing ?? WAngle.Zero; if (previouslySpawned && previousSpawnCell == spawnCell) { spawnFacing = previousSpawnFacing; } var offsetRotation = Info.Offsets[offset].Rotate(body.QuantizeOrientation(self, self.Orientation)); var spawnPosition = Info.SpawnAtLastPosition ? cachedPosition : self.CenterPosition; var pos = Info.Type == TrailType.CenterPosition ? spawnPosition + body.LocalToWorld(offsetRotation) : self.World.Map.CenterOfCell(spawnCell); self.World.AddFrameEndTask(w => w.Add(new SpriteEffect(pos, spawnFacing, self.World, Info.Image, Info.Sequences.Random(Game.CosmeticRandom), Info.Palette, Info.VisibleThroughFog))); previouslySpawned = true; previousSpawnCell = spawnCell; previousSpawnFacing = spawnFacing; } cachedPosition = self.CenterPosition; cachedFacing = facing?.Facing ?? WAngle.Zero; ticks = 0; cachedInterval = isMoving ? Info.MovingInterval : Info.StationaryInterval; } }
public WRot WithYaw(WAngle yaw) { return new WRot(Roll, Pitch, yaw); }
public WAngle GetInitialFacing() { return(WAngle.FromFacing(InitialFacing)); }
public static WRot FromYaw(WAngle yaw) { return new WRot(WAngle.Zero, WAngle.Zero, yaw); }
void Calculate(Actor self) { if (dest == null || Reservable.IsReserved(dest)) { dest = ChooseAirfield(self, true); } if (dest == null) { return; } var res = dest.TraitOrDefault <Reservable>(); if (res != null) { plane.UnReserve(); plane.Reservation = res.Reserve(dest, self, plane); } var landPos = dest.CenterPosition; var altitude = planeInfo.CruiseAltitude.Length; // Distance required for descent. var landDistance = altitude * 1024 / planeInfo.MaximumPitch.Tan(); // Land towards the east var approachStart = landPos + new WVec(-landDistance, 0, altitude); // Add 10% to the turning radius to ensure we have enough room var speed = plane.MovementSpeed * 32 / 35; var turnRadius = (int)(141 * speed / planeInfo.ROT / (float)Math.PI); // Find the center of the turning circles for clockwise and counterclockwise turns var angle = WAngle.FromFacing(plane.Facing); var fwd = -new WVec(angle.Sin(), angle.Cos(), 0); // Work out whether we should turn clockwise or counter-clockwise for approach var side = new WVec(-fwd.Y, fwd.X, fwd.Z); var approachDelta = self.CenterPosition - approachStart; var sideTowardBase = new[] { side, -side } .MinBy(a => WVec.Dot(a, approachDelta)); // Calculate the tangent line that joins the turning circles at the current and approach positions var cp = self.CenterPosition + turnRadius * sideTowardBase / 1024; var posCenter = new WPos(cp.X, cp.Y, altitude); var approachCenter = approachStart + new WVec(0, turnRadius * Math.Sign(self.CenterPosition.Y - approachStart.Y), 0); var tangentDirection = approachCenter - posCenter; var tangentOffset = new WVec(-tangentDirection.Y, tangentDirection.X, 0) * turnRadius / tangentDirection.Length; // TODO: correctly handle CCW <-> CW turns if (tangentOffset.X > 0) { tangentOffset = -tangentOffset; } w1 = posCenter + tangentOffset; w2 = approachCenter + tangentOffset; w3 = approachStart; plane.RTBPathHash = w1 + (WVec)w2 + (WVec)w3; isCalculated = true; }
protected override int GetFacingFrameOffset(WAngle facing) { return(useClassicFacings ? Util.ClassicIndexFacing(facing, Facings) : Common.Util.IndexFacing(facing, Facings)); }
public IEnumerable <VoxelAnimation> RenderPreviewVoxels(ActorPreviewInitializer init, RenderVoxelsInfo rv, string image, WRot orientation, int facings, PaletteReference p) { if (UpgradeMinEnabledLevel > 0) { yield break; } var body = init.Actor.TraitInfo <BodyOrientationInfo>(); var armament = init.Actor.TraitInfos <ArmamentInfo>() .First(a => a.Name == Armament); var t = init.Actor.TraitInfos <TurretedInfo>() .First(tt => tt.Turret == armament.Turret); var voxel = VoxelProvider.GetVoxel(image, Sequence); var turretFacing = Turreted.GetInitialTurretFacing(init, t.InitialFacing, t.Turret); var turretOrientation = body.QuantizeOrientation(new WRot(WAngle.Zero, WAngle.Zero, WAngle.FromFacing(turretFacing) - orientation.Yaw), facings); var quantizedTurret = body.QuantizeOrientation(turretOrientation, facings); var quantizedBody = body.QuantizeOrientation(orientation, facings); var barrelOffset = body.LocalToWorld((t.Offset + LocalOffset.Rotate(quantizedTurret)).Rotate(quantizedBody)); yield return(new VoxelAnimation(voxel, () => barrelOffset, () => new[] { turretOrientation, orientation }, () => false, () => 0)); }
IEnumerable <ActorInit> IActorPreviewInitInfo.ActorPreviewInits(ActorInfo ai, ActorPreviewType type) { yield return(new FacingInit(WAngle.FromFacing(PreviewFacing))); }
WVec HomingTick(World world, WVec tarDistVec, int relTarHorDist) { int predClfHgt = 0; int predClfDist = 0; int lastHtChg = 0; int lastHt = 0; if (info.TerrainHeightAware) { InclineLookahead(world, relTarHorDist, out predClfHgt, out predClfDist, out lastHtChg, out lastHt); } // Height difference between the incline height and missile height var diffClfMslHgt = predClfHgt - pos.Z; // Get underestimate of distance from target in next tick var nxtRelTarHorDist = (relTarHorDist - speed - info.Acceleration.Length).Clamp(0, relTarHorDist); // Target height relative to the missile var relTarHgt = tarDistVec.Z; // Compute which direction the projectile should be facing var velVec = tarDistVec + predVel; var desiredHFacing = velVec.HorizontalLengthSquared != 0 ? velVec.Yaw.Facing : hFacing; var delta = Util.NormalizeFacing(hFacing - desiredHFacing); if (allowPassBy && delta > 64 && delta < 192) { desiredHFacing = (desiredHFacing + 128) & 0xFF; targetPassedBy = true; } else { targetPassedBy = false; } var desiredVFacing = HomingInnerTick(predClfDist, diffClfMslHgt, relTarHorDist, lastHtChg, lastHt, nxtRelTarHorDist, relTarHgt, vFacing, targetPassedBy); // The target has been passed by if (tarDistVec.HorizontalLength < speed * WAngle.FromFacing(vFacing).Cos() / 1024) { targetPassedBy = true; } // Check whether the homing mechanism is jammed var jammed = info.Jammable && world.ActorsWithTrait <JamsMissiles>().Any(JammedBy); if (jammed) { desiredHFacing = hFacing + world.SharedRandom.Next(-info.JammedDiversionRange, info.JammedDiversionRange + 1); desiredVFacing = vFacing + world.SharedRandom.Next(-info.JammedDiversionRange, info.JammedDiversionRange + 1); } else if (!args.GuidedTarget.IsValidFor(args.SourceActor)) { desiredHFacing = hFacing; } // Compute new direction the projectile will be facing hFacing = Util.TickFacing(hFacing, desiredHFacing, info.HorizontalRateOfTurn); vFacing = Util.TickFacing(vFacing, desiredVFacing, info.VerticalRateOfTurn); // Compute the projectile's guided displacement return(new WVec(0, -1024 * speed, 0) .Rotate(new WRot(WAngle.FromFacing(vFacing), WAngle.Zero, WAngle.Zero)) .Rotate(new WRot(WAngle.Zero, WAngle.Zero, WAngle.FromFacing(hFacing))) / 1024); }