public Missile(MissileInfo info, ProjectileArgs args) { this.info = info; this.args = args; pos = args.Source; facing = args.Facing; targetPosition = args.PassiveTarget; var world = args.SourceActor.World; if (world.SharedRandom.Next(100) <= info.LockOnProbability) lockOn = true; if (info.Inaccuracy.Range > 0) { var inaccuracy = OpenRA.Traits.Util.ApplyPercentageModifiers(info.Inaccuracy.Range, args.InaccuracyModifiers); offset = WVec.FromPDF(world.SharedRandom, 2) * inaccuracy / 1024; } if (info.Image != null) { anim = new Animation(world, info.Image, () => facing); 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); } }
public void RenderAfterWorld(WorldRenderer wr, Actor self) { if (devMode == null || !devMode.ShowCombatGeometry) return; var wcr = Game.Renderer.WorldRgbaColorRenderer; var iz = 1 / wr.Viewport.Zoom; if (healthInfo != null) healthInfo.Shape.DrawCombatOverlay(wr, wcr, self); var blockers = allBlockers.Where(Exts.IsTraitEnabled).ToList(); if (blockers.Count > 0) { var hc = Color.Orange; var height = new WVec(0, 0, blockers.Max(b => b.BlockingHeight.Length)); var ha = wr.ScreenPosition(self.CenterPosition); var hb = wr.ScreenPosition(self.CenterPosition + height); wcr.DrawLine(ha, hb, iz, hc); TargetLineRenderable.DrawTargetMarker(wr, hc, ha); TargetLineRenderable.DrawTargetMarker(wr, hc, hb); } foreach (var attack in self.TraitsImplementing<AttackBase>().Where(x => !x.IsTraitDisabled)) DrawArmaments(self, attack, wr, wcr, iz); }
public void RenderAfterWorld(WorldRenderer wr, Actor self) { if (devMode == null || !devMode.ShowMuzzles) return; if (health.Value != null) wr.DrawRangeCircle(Color.Red, wr.ScreenPxPosition(self.CenterPosition), health.Value.Info.Radius / Game.CellSize); var wlr = Game.Renderer.WorldLineRenderer; var c = Color.White; foreach (var a in armaments.Value) { foreach (var b in a.Barrels) { var muzzle = self.CenterPosition + a.MuzzleOffset(self, b); var dirOffset = new WVec(0, -224, 0).Rotate(a.MuzzleOrientation(self, b)); var sm = wr.ScreenPosition(muzzle); var sd = wr.ScreenPosition(muzzle + dirOffset); wlr.DrawLine(sm, sd, c, c); wr.DrawTargetMarker(c, sm); } } }
public Missile(MissileInfo info, ProjectileArgs args) { this.info = info; this.args = args; pos = args.Source; facing = args.Facing; targetPosition = args.PassiveTarget; // Convert ProjectileArg definitions to world coordinates // TODO: Change the yaml definitions so we don't need this var inaccuracy = (int)(info.Inaccuracy * 1024 / Game.CellSize); speed = info.Speed * 1024 / (5 * Game.CellSize); if (info.Inaccuracy > 0) offset = WVec.FromPDF(args.SourceActor.World.SharedRandom, 2) * inaccuracy / 1024; if (info.Image != null) { anim = new Animation(info.Image, () => facing); 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); } }
public override Activity Tick(Actor self) { if (self.CenterPosition.Z <= 0) { if (info.Explosion != null) { var weapon = self.World.Map.Rules.Weapons[info.Explosion.ToLowerInvariant()]; // Use .FromPos since this actor is killed. Cannot use Target.FromActor weapon.Impact(Target.FromPos(self.CenterPosition), self, Enumerable.Empty<int>()); } self.Destroy(); return null; } if (info.Spins) { spin += acceleration; aircraft.Facing = (aircraft.Facing + spin) % 256; } var move = info.Moves ? aircraft.FlyStep(aircraft.Facing) : WVec.Zero; move -= new WVec(WRange.Zero, WRange.Zero, info.Velocity); aircraft.SetPosition(self, aircraft.CenterPosition + move); return this; }
protected bool TryGetAlternateTargetInCircle( Actor self, WDist radius, Action<Target> update, Func<Actor, bool> primaryFilter, Func<Actor, bool>[] preferenceFilters = null) { var diff = new WVec(radius, radius, WDist.Zero); var candidates = self.World.ActorMap.ActorsInBox(self.CenterPosition - diff, self.CenterPosition + diff) .Where(primaryFilter).Select(a => new { Actor = a, Ls = (self.CenterPosition - a.CenterPosition).HorizontalLengthSquared }) .Where(p => p.Ls <= radius.LengthSquared).OrderBy(p => p.Ls).Select(p => p.Actor); if (preferenceFilters != null) foreach (var filter in preferenceFilters) { var preferredCandidate = candidates.FirstOrDefault(filter); if (preferredCandidate == null) continue; target = Target.FromActor(preferredCandidate); update(target); return true; } var candidate = candidates.FirstOrDefault(); if (candidate == null) return false; target = Target.FromActor(candidate); update(target); return true; }
public EditorActorBrush(EditorViewportControllerWidget editorWidget, ActorInfo actor, PlayerReference owner, WorldRenderer wr) { this.editorWidget = editorWidget; worldRenderer = wr; world = wr.World; editorLayer = world.WorldActor.Trait<EditorActorLayer>(); Actor = actor; this.owner = owner; preview = editorWidget.Get<ActorPreviewWidget>("DRAG_ACTOR_PREVIEW"); preview.GetScale = () => worldRenderer.Viewport.Zoom; preview.IsVisible = () => editorWidget.CurrentBrush == this; var buildingInfo = actor.Traits.GetOrDefault<BuildingInfo>(); if (buildingInfo != null) { locationOffset = -FootprintUtils.AdjustForBuildingSize(buildingInfo); previewOffset = FootprintUtils.CenterOffset(world, buildingInfo); } var td = new TypeDictionary(); td.Add(new FacingInit(facing)); td.Add(new TurretFacingInit(facing)); td.Add(new OwnerInit(owner.Name)); td.Add(new RaceInit(owner.Race)); preview.SetPreview(actor, td); // The preview widget may be rendered by the higher-level code before it is ticked. // Force a manual tick to ensure the bounds are set correctly for this first draw. Tick(); }
public FallDown(Actor self, WPos dropPosition, int fallRate, Actor ignoreActor = null) { pos = self.TraitOrDefault<IPositionable>(); IsInterruptible = false; fallVector = new WVec(0, 0, fallRate); this.dropPosition = dropPosition; }
public IRenderable OffsetBy(WVec vec) { return new VoxelRenderable( voxels, pos + vec, zOffset, camera, scale, lightSource, lightAmbientColor, lightDiffuseColor, palette, normalsPalette, shadowPalette); }
public NukeLaunch(Player firedBy, string weapon, WPos launchPos, WPos targetPos, WDist velocity, int delay, bool skipAscent, string flashType) { this.firedBy = firedBy; this.weapon = weapon; this.delay = delay; this.turn = delay / 2; this.flashType = flashType; var offset = new WVec(WDist.Zero, WDist.Zero, velocity * turn); ascendSource = launchPos; ascendTarget = launchPos + offset; descendSource = targetPos + offset; descendTarget = targetPos; anim = new Animation(firedBy.World, weapon); anim.PlayRepeating("up"); pos = launchPos; var weaponRules = firedBy.World.Map.Rules.Weapons[weapon.ToLowerInvariant()]; if (weaponRules.Report != null && weaponRules.Report.Any()) Sound.Play(weaponRules.Report.Random(firedBy.World.SharedRandom), pos); if (skipAscent) ticks = turn; }
public override Activity Tick(Actor self) { if (self.World.Map.DistanceAboveTerrain(self.CenterPosition).Length <= 0) { if (info.ExplosionWeapon != null) { // Use .FromPos since this actor is killed. Cannot use Target.FromActor info.ExplosionWeapon.Impact(Target.FromPos(self.CenterPosition), self, Enumerable.Empty<int>()); } self.Dispose(); return null; } if (info.Spins) { spin += acceleration; aircraft.Facing = (aircraft.Facing + spin) % 256; } var move = info.Moves ? aircraft.FlyStep(aircraft.Facing) : WVec.Zero; move -= new WVec(WDist.Zero, WDist.Zero, info.Velocity); aircraft.SetPosition(self, aircraft.CenterPosition + move); return this; }
void CreateActors(string actorName, string deliveringActorName, out Actor cargo, out Actor carrier) { // Get a carryall spawn location var location = Info.SpawnLocation; if (location == CPos.Zero) location = self.World.Map.ChooseClosestEdgeCell(self.Location); var spawn = self.World.Map.CenterOfCell(location); var initialFacing = self.World.Map.FacingBetween(location, self.Location, 0); // If aircraft, spawn at cruise altitude var aircraftInfo = self.World.Map.Rules.Actors[deliveringActorName.ToLower()].TraitInfoOrDefault<AircraftInfo>(); if (aircraftInfo != null) spawn += new WVec(0, 0, aircraftInfo.CruiseAltitude.Length); // Create delivery actor carrier = self.World.CreateActor(false, deliveringActorName, new TypeDictionary { new LocationInit(location), new CenterPositionInit(spawn), new OwnerInit(self.Owner), new FacingInit(initialFacing) }); // Create delivered actor cargo = self.World.CreateActor(false, actorName, new TypeDictionary { new OwnerInit(self.Owner), }); }
public NukeLaunch(Player firedBy, string name, WeaponInfo weapon, string weaponPalette, string upSequence, string downSequence, WPos launchPos, WPos targetPos, WDist velocity, int delay, bool skipAscent, string flashType) { this.firedBy = firedBy; this.weapon = weapon; this.weaponPalette = weaponPalette; this.downSequence = downSequence; this.delay = delay; turn = delay / 2; this.flashType = flashType; var offset = new WVec(WDist.Zero, WDist.Zero, velocity * turn); ascendSource = launchPos; ascendTarget = launchPos + offset; descendSource = targetPos + offset; descendTarget = targetPos; anim = new Animation(firedBy.World, name); anim.PlayRepeating(upSequence); pos = launchPos; if (weapon.Report != null && weapon.Report.Any()) Game.Sound.Play(weapon.Report.Random(firedBy.World.SharedRandom), pos); if (skipAscent) ticks = turn; }
public AreaBeam(AreaBeamInfo info, ProjectileArgs args, Color color) { this.info = info; this.args = args; this.color = color; actorAttackBase = args.SourceActor.Trait<AttackBase>(); var world = args.SourceActor.World; if (info.Speed.Length > 1) speed = new WDist(world.SharedRandom.Next(info.Speed[0].Length, info.Speed[1].Length)); else speed = info.Speed[0]; // Both the head and tail start at the source actor, but initially only the head is travelling. headPos = args.Source; tailPos = headPos; target = args.PassiveTarget; if (info.Inaccuracy.Length > 0) { var inaccuracy = Util.ApplyPercentageModifiers(info.Inaccuracy.Length, args.InaccuracyModifiers); var maxOffset = inaccuracy * (target - headPos).Length / args.Weapon.Range.Length; target += WVec.FromPDF(world.SharedRandom, 2) * maxOffset / 1024; } towardsTargetFacing = (target - headPos).Yaw.Facing; // Update the target position with the range we shoot beyond the target by // I.e. we can deliberately overshoot, so aim for that position var dir = new WVec(0, -1024, 0).Rotate(WRot.FromFacing(towardsTargetFacing)); target += dir * info.BeyondTargetRange.Length / 1024; length = Math.Max((target - headPos).Length / speed.Length, 1); }
public Missile(MissileInfo info, ProjectileArgs args) { this.info = info; this.args = args; pos = args.Source; facing = args.Facing; targetPosition = args.PassiveTarget; if (info.Inaccuracy.Range > 0) offset = WVec.FromPDF(args.SourceActor.World.SharedRandom, 2) * info.Inaccuracy.Range / 1024; if (info.Image != null) { anim = new Animation(args.SourceActor.World, info.Image, () => facing); 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); } }
/// <summary> /// Finds all the actors of which their health radius is intersected by a line (with a definable width) between two points. /// </summary> /// <param name="world">The engine world the line intersection is to be done in.</param> /// <param name="lineStart">The position the line should start at</param> /// <param name="lineEnd">The position the line should end at</param> /// <param name="lineWidth">How close an actor's health radius needs to be to the line to be considered 'intersected' by the line</param> /// <returns>A list of all the actors intersected by the line</returns> public static IEnumerable<Actor> FindActorsOnLine(this World world, WPos lineStart, WPos lineEnd, WDist lineWidth, WDist targetExtraSearchRadius) { // This line intersection check is done by first just finding all actors within a square that starts at the source, and ends at the target. // Then we iterate over this list, and find all actors for which their health radius is at least within lineWidth of the line. // For actors without a health radius, we simply check their center point. // The square in which we select all actors must be large enough to encompass the entire line's width. var xDir = Math.Sign(lineEnd.X - lineStart.X); var yDir = Math.Sign(lineEnd.Y - lineStart.Y); var dir = new WVec(xDir, yDir, 0); var overselect = dir * (1024 + lineWidth.Length + targetExtraSearchRadius.Length); var finalTarget = lineEnd + overselect; var finalSource = lineStart - overselect; var actorsInSquare = world.ActorMap.ActorsInBox(finalTarget, finalSource); var intersectedActors = new List<Actor>(); foreach (var currActor in actorsInSquare) { var actorWidth = 0; var healthInfo = currActor.Info.TraitInfoOrDefault<HealthInfo>(); if (healthInfo != null) actorWidth = healthInfo.Shape.OuterRadius.Length; var projection = MinimumPointLineProjection(lineStart, lineEnd, currActor.CenterPosition); var distance = (currActor.CenterPosition - projection).HorizontalLength; var maxReach = actorWidth + lineWidth.Length; if (distance <= maxReach) intersectedActors.Add(currActor); } return intersectedActors; }
static void ConvertInt2ToWVec(ref string input) { var offset = FieldLoader.GetValue<int2>("(value)", input); var ts = Game.modData.Manifest.TileSize; var world = new WVec(offset.X * 1024 / ts.Width, offset.Y * 1024 / ts.Height, 0); input = world.ToString(); }
public Parachute(Actor cargo, WPos dropPosition) { this.cargo = cargo; parachutableInfo = cargo.Info.Traits.GetOrDefault<ParachutableInfo>(); if (parachutableInfo != null) fallVector = new WVec(0, 0, parachutableInfo.FallRate); var parachuteSprite = parachutableInfo != null ? parachutableInfo.ParachuteSequence : null; if (parachuteSprite != null) { parachute = new Animation(cargo.World, parachuteSprite); parachute.PlayThen("open", () => parachute.PlayRepeating("idle")); } var shadowSprite = parachutableInfo != null ? parachutableInfo.ShadowSequence : null; if (shadowSprite != null) { shadow = new Animation(cargo.World, shadowSprite); shadow.PlayRepeating("idle"); } if (parachutableInfo != null) parachuteOffset = parachutableInfo.ParachuteOffset; // Adjust x,y to match the target subcell cargo.Trait<IPositionable>().SetPosition(cargo, cargo.World.Map.CellContaining(dropPosition)); var cp = cargo.CenterPosition; pos = new WPos(cp.X, cp.Y, dropPosition.Z); }
public override void Activate(Actor self, Order order) { var info = Info as AirstrikePowerInfo; var attackFacing = Util.QuantizeFacing(self.World.SharedRandom.Next(256), info.QuantizedFacings) * (256 / info.QuantizedFacings); var attackRotation = WRot.FromFacing(attackFacing); var delta = new WVec(0, -1024, 0).Rotate(attackRotation); var altitude = Rules.Info[info.UnitType].Traits.Get<PlaneInfo>().CruiseAltitude * 1024 / Game.CellSize; var target = order.TargetLocation.CenterPosition + new WVec(0, 0, altitude); var startEdge = target - (self.World.DistanceToMapEdge(target, -delta) + info.Cordon).Range * delta / 1024; var finishEdge = target + (self.World.DistanceToMapEdge(target, delta) + info.Cordon).Range * delta / 1024; self.World.AddFrameEndTask(w => { var notification = self.Owner.IsAlliedWith(self.World.RenderPlayer) ? Info.LaunchSound : Info.IncomingSound; Sound.Play(notification); Actor flare = null; if (info.FlareType != null) { flare = w.CreateActor(info.FlareType, new TypeDictionary { new LocationInit(order.TargetLocation), new OwnerInit(self.Owner), }); flare.QueueActivity(new Wait(info.FlareTime)); flare.QueueActivity(new RemoveSelf()); } 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 targetOffset = new WVec(i*so.Y, 0, 0).Rotate(attackRotation); var a = w.CreateActor(info.UnitType, new TypeDictionary { new CenterPositionInit(startEdge + spawnOffset), new OwnerInit(self.Owner), new FacingInit(attackFacing), }); a.Trait<AttackBomber>().SetTarget(target + targetOffset); if (flare != null) a.QueueActivity(new CallFunc(() => flare.Destroy())); a.QueueActivity(Fly.ToPos(finishEdge + spawnOffset)); a.QueueActivity(new RemoveSelf()); } }); }
public BeamRenderable(WPos pos, int zOffset, WVec length, float width, Color color) { this.pos = pos; this.zOffset = zOffset; this.length = length; this.color = color; this.width = width; }
public SpriteActorPreview(Animation animation, WVec offset, int zOffset, PaletteReference pr, float scale) { this.animation = animation; this.offset = offset; this.zOffset = zOffset; this.pr = pr; this.scale = scale; }
public WDist DistanceFromEdge(WVec v) { var r = new int2( Math.Max(Math.Abs(v.X - center.X) - quadrantSize.X, 0), Math.Max(Math.Abs(v.Y - center.Y) - quadrantSize.Y, 0)); return new WDist(r.Length); }
void Calculate(Actor self) { if (dest == null || Reservable.IsReserved(dest)) dest = ChooseAirfield(self, true); if (dest == null) return; var plane = self.Trait<Plane>(); var planeInfo = self.Info.Traits.Get<PlaneInfo>(); 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.Range; // Distance required for descent. var landDistance = altitude * 1024 / plane.Info.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; }
public BeamRenderable(WPos pos, int zOffset, WVec length, BeamRenderableShape shape, WDist width, Color color) { this.pos = pos; this.zOffset = zOffset; this.length = length; this.shape = shape; this.width = width; this.color = color; }
public Parachute(Actor self, WPos dropPosition) { um = self.TraitOrDefault<UpgradeManager>(); pos = self.TraitOrDefault<IPositionable>(); // Parachutable trait is a prerequisite for running this activity para = self.Info.Traits.Get<ParachutableInfo>(); fallVector = new WVec(0, 0, para.FallRate); this.dropPosition = dropPosition; }
public SpriteRenderable(Sprite sprite, WPos pos, WVec offset, int zOffset, PaletteReference palette, float scale, bool isDecoration) { this.sprite = sprite; this.pos = pos; this.offset = offset; this.zOffset = zOffset; this.palette = palette; this.scale = scale; this.isDecoration = isDecoration; }
public Parachute(Actor self, WPos dropPosition, Actor ignoreActor = null) { pos = self.TraitOrDefault<IPositionable>(); ignore = ignoreActor; // Parachutable trait is a prerequisite for running this activity para = self.Info.TraitInfo<ParachutableInfo>(); fallVector = new WVec(0, 0, para.FallRate); this.dropPosition = dropPosition; IsInterruptible = false; }
protected virtual bool IsVisibleInner(Actor self, Player byPlayer) { if (Info.Type == VisibilityType.Footprint) return byPlayer.Shroud.AnyExplored(self.OccupiesSpace.OccupiedCells()); var pos = self.CenterPosition; if (Info.Type == VisibilityType.GroundPosition) pos -= new WVec(WDist.Zero, WDist.Zero, self.World.Map.DistanceAboveTerrain(pos)); return byPlayer.Shroud.IsExplored(pos); }
public SmokeParticle(WPos pos, WVec[] gravity, World world, string image, string sequence, string palette, bool visibleThroughFog = false, bool scaleSizeWithZoom = false) { this.world = world; this.pos = pos; this.gravity = gravity; this.palette = palette; this.scaleSizeWithZoom = scaleSizeWithZoom; this.visibleThroughFog = visibleThroughFog; anim = new Animation(world, image, () => 0); anim.PlayThen(sequence, () => world.AddFrameEndTask(w => w.Remove(this))); }
public HarvesterDockSequence(Actor self, Actor refinery, int dockAngle, bool isDragRequired, WVec dragOffset, int dragLength) { dockingState = State.Turn; Refinery = refinery; DockAngle = dockAngle; IsDragRequired = isDragRequired; DragOffset = dragOffset; DragLength = dragLength; Harv = self.Trait<Harvester>(); StartDrag = self.CenterPosition; EndDrag = refinery.CenterPosition + DragOffset; }
public IRenderable OffsetBy(WVec vec) { return(new CircleAnnotationRenderable(centerPosition + vec, radius, width, color, filled)); }
public IRenderable OffsetBy(WVec vec) { return(new SpriteRenderable(sprite, pos + vec, offset, zOffset, palette, scale, isDecoration)); }
public IRenderable OffsetBy(WVec vec) { return(new LineAnnotationRenderable(start + vec, end + vec, width, startColor, endColor)); }
WVec MoveStep(int speed, int facing) { var dir = new WVec(0, -1024, 0).Rotate(WRot.FromFacing(facing)); return(speed * dir / 1024); }
public IRenderable OffsetBy(WVec vec) { return(new ContrailRenderable(world, trail.Select(pos => pos + vec).ToArray(), next, length, skip, color, zOffset)); }
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 = Common.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 = Common.Util.TickFacing(hFacing, desiredHFacing, info.HorizontalRateOfTurn); vFacing = Common.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); }
public HarvesterDockSequence(Actor self, Actor refinery, int dockAngle, bool isDragRequired, WVec dragOffset, int dragLength) { dockingState = DockingState.Turn; Refinery = refinery; DockAngle = dockAngle; IsDragRequired = isDragRequired; DragOffset = dragOffset; DragLength = dragLength; Harv = self.Trait <Harvester>(); StartDrag = self.CenterPosition; EndDrag = refinery.CenterPosition + DragOffset; }
public IRenderable OffsetBy(WVec vec) { return(new DetectionCircleAnnotationRenderable(centerPosition + vec, radius, zOffset, trailCount, trailSeparation, trailAngle, color, width, borderColor, borderWidth)); }
public void SendDropPods(Actor self, Order order, int podFacing) { var actorInfo = self.World.Map.Rules.Actors[info.UnitTypes.First().ToLowerInvariant()]; var aircraftInfo = actorInfo.TraitInfo <AircraftInfo>(); var altitude = aircraftInfo.CruiseAltitude.Length; var approachRotation = WRot.FromFacing(podFacing); var fallsToEarthInfo = actorInfo.TraitInfo <FallsToEarthInfo>(); var delta = new WVec(0, -altitude * aircraftInfo.Speed / fallsToEarthInfo.Velocity.Length, 0).Rotate(approachRotation); self.World.AddFrameEndTask(w => { var target = order.Target.CenterPosition; var targetCell = self.World.Map.CellContaining(target); var podLocations = self.World.Map.FindTilesInCircle(targetCell, info.PodScatter) .Where(c => aircraftInfo.LandableTerrainTypes.Contains(w.Map.GetTerrainInfo(c).Type) && !self.World.ActorMap.GetActorsAt(c).Any()); if (!podLocations.Any()) { return; } if (info.CameraActor != null) { var camera = w.CreateActor(info.CameraActor, new TypeDictionary { new LocationInit(targetCell), new OwnerInit(self.Owner), }); camera.QueueActivity(new Wait(info.CameraRemoveDelay)); camera.QueueActivity(new RemoveSelf()); } PlayLaunchSounds(); var drops = self.World.SharedRandom.Next(info.Drops.X, info.Drops.Y); for (var i = 0; i < drops; i++) { var unitType = info.UnitTypes.Random(self.World.SharedRandom); var podLocation = podLocations.Random(self.World.SharedRandom); var podTarget = Target.FromCell(w, podLocation); var location = self.World.Map.CenterOfCell(podLocation) - delta + new WVec(0, 0, altitude); var pod = w.CreateActor(false, unitType, new TypeDictionary { new CenterPositionInit(location), new OwnerInit(self.Owner), new FacingInit(podFacing) }); var aircraft = pod.Trait <Aircraft>(); if (!aircraft.CanLand(podLocation)) { pod.Dispose(); } else { w.Add(new DropPodImpact(self.Owner, info.WeaponInfo, w, location, podTarget, info.WeaponDelay, info.EntryEffect, info.EntryEffectSequence, info.EntryEffectPalette)); w.Add(pod); } } }); }
public IRenderable OffsetBy(WVec vec) { return(new BeamRenderable(pos + vec, zOffset, length, width, color)); }
public Pair <Actor[], Actor[]> SendParatroopers(Actor self, WPos target, int facing = -1) { var aircraft = new List <Actor>(); var units = new List <Actor>(); var info = Info as ParatroopersPowerInfo; if (facing < 0) { facing = 256 * self.World.SharedRandom.Next(info.QuantizedFacings) / info.QuantizedFacings; } var utLower = info.UnitType.ToLowerInvariant(); ActorInfo unitType; if (!self.World.Map.Rules.Actors.TryGetValue(utLower, out unitType)) { throw new YamlException("Actors ruleset does not include the entry '{0}'".F(utLower)); } var altitude = unitType.TraitInfo <AircraftInfo>().CruiseAltitude.Length; var dropRotation = WRot.FromFacing(facing); var delta = new WVec(0, -1024, 0).Rotate(dropRotation); target = target + new WVec(0, 0, altitude); var startEdge = target - (self.World.Map.DistanceToEdge(target, -delta) + info.Cordon).Length * delta / 1024; var finishEdge = target + (self.World.Map.DistanceToEdge(target, delta) + info.Cordon).Length * delta / 1024; Actor camera = null; Beacon beacon = null; var aircraftInRange = new Dictionary <Actor, bool>(); Action <Actor> onEnterRange = a => { // Spawn a camera and remove the beacon when the first plane enters the target area if (info.CameraActor != null && camera == null && !aircraftInRange.Any(kv => kv.Value)) { self.World.AddFrameEndTask(w => { camera = w.CreateActor(info.CameraActor, new TypeDictionary { new LocationInit(self.World.Map.CellContaining(target)), new OwnerInit(self.Owner), }); }); } RemoveBeacon(beacon); if (!aircraftInRange.Any(kv => kv.Value)) { Game.Sound.PlayNotification(self.World.Map.Rules, self.Owner, "Speech", info.ReinforcementsArrivedSpeechNotification, self.Owner.Faction.InternalName); } aircraftInRange[a] = true; }; Action <Actor> onExitRange = a => { aircraftInRange[a] = false; // Remove the camera when the final plane leaves the target area if (!aircraftInRange.Any(kv => kv.Value)) { RemoveCamera(camera); } }; Action <Actor> onRemovedFromWorld = a => { aircraftInRange[a] = false; // Checking for attack range is not relevant here because // aircraft may be shot down before entering. Thus we remove // the camera and beacon only if the whole squad is dead. if (aircraftInRange.All(kv => kv.Key.IsDead)) { RemoveCamera(camera); RemoveBeacon(beacon); } }; // Create the actors immediately so they can be returned 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(dropRotation); aircraft.Add(self.World.CreateActor(false, info.UnitType, new TypeDictionary { new CenterPositionInit(startEdge + spawnOffset), new OwnerInit(self.Owner), new FacingInit(facing), })); } foreach (var p in info.DropItems) { units.Add(self.World.CreateActor(false, p.ToLowerInvariant(), new TypeDictionary { new OwnerInit(self.Owner) })); } self.World.AddFrameEndTask(w => { PlayLaunchSounds(); Actor distanceTestActor = null; var passengersPerPlane = (info.DropItems.Length + info.SquadSize - 1) / info.SquadSize; var added = 0; var j = 0; 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(dropRotation); var targetOffset = new WVec(i * so.Y, 0, 0).Rotate(dropRotation); var a = aircraft[j++]; w.Add(a); var drop = a.Trait <ParaDrop>(); drop.SetLZ(w.Map.CellContaining(target + targetOffset), !info.AllowImpassableCells); drop.OnEnteredDropRange += onEnterRange; drop.OnExitedDropRange += onExitRange; drop.OnRemovedFromWorld += onRemovedFromWorld; var cargo = a.Trait <Cargo>(); foreach (var unit in units.Skip(added).Take(passengersPerPlane)) { cargo.Load(a, unit); added++; } a.QueueActivity(new Fly(a, Target.FromPos(target + spawnOffset))); a.QueueActivity(new Fly(a, Target.FromPos(finishEdge + spawnOffset))); a.QueueActivity(new RemoveSelf()); aircraftInRange.Add(a, false); distanceTestActor = a; } // Dispose any unused units for (var i = added; i < units.Count; i++) { units[i].Dispose(); } if (Info.DisplayBeacon) { var distance = (target - startEdge).HorizontalLength; beacon = new Beacon( self.Owner, target - new WVec(0, 0, altitude), Info.BeaconPaletteIsPlayerPalette, Info.BeaconPalette, Info.BeaconImage, Info.BeaconPoster, Info.BeaconPosterPalette, Info.BeaconSequence, Info.ArrowSequence, Info.CircleSequence, Info.ClockSequence, () => 1 - ((distanceTestActor.CenterPosition - target).HorizontalLength - info.BeaconDistanceOffset.Length) * 1f / distance, Info.BeaconDelay); w.Add(beacon); } }); return(Pair.New(aircraft.ToArray(), units.ToArray())); }
public void Tick(World world) { // Fade the trail out gradually if (exploded && info.ContrailLength > 0) { trail.Update(pos); return; } ticks++; anim.Tick(); // Missile tracks target if (args.GuidedTarget.IsValidFor(args.SourceActor)) { targetPosition = args.GuidedTarget.CenterPosition; } var dist = targetPosition + offset - pos; var desiredFacing = Traits.Util.GetFacing(dist, facing); var desiredAltitude = targetPosition.Z; var jammed = info.Jammable && world.ActorsWithTrait <JamsMissiles>().Any(j => JammedBy(j)); if (jammed) { desiredFacing = facing + world.SharedRandom.Next(-20, 21); desiredAltitude = world.SharedRandom.Next(-43, 86); } else if (!args.GuidedTarget.IsValidFor(args.SourceActor)) { desiredFacing = facing; } facing = Traits.Util.TickFacing(facing, desiredFacing, info.ROT); var move = new WVec(0, -1024, 0).Rotate(WRot.FromFacing(facing)) * speed / 1024; if (targetPosition.Z > 0 && info.TurboBoost) { move = (move * 3) / 2; } if (pos.Z != desiredAltitude) { var delta = move.HorizontalLength * info.MaximumPitch.Tan() / 1024; var dz = (targetPosition.Z - pos.Z).Clamp(-delta, delta); move += new WVec(0, 0, dz); } pos += move; if (info.Trail != null && --ticksToNextSmoke < 0) { world.AddFrameEndTask(w => w.Add(new Smoke(w, pos - 3 * move / 2, info.Trail))); ticksToNextSmoke = info.TrailInterval; } if (info.ContrailLength > 0) { trail.Update(pos); } var shouldExplode = (pos.Z < 0) || // Hit the ground (dist.LengthSquared < MissileCloseEnough.Range * MissileCloseEnough.Range) || // Within range (info.RangeLimit != 0 && ticks > info.RangeLimit) || // Ran out of fuel (!info.High && world.ActorMap.GetUnitsAt(pos.ToCPos()) .Any(a => a.HasTrait <IBlocksBullets>())); // Hit a wall if (shouldExplode) { Explode(world); } }
public Actor[] SendParatroopers(Actor self, WPos target, bool randomize = true, int dropFacing = 0) { var units = new List<Actor>(); var info = Info as ParatroopersPowerInfo; if (randomize) dropFacing = 256 * self.World.SharedRandom.Next(info.QuantizedFacings) / info.QuantizedFacings; var utLower = info.UnitType.ToLowerInvariant(); ActorInfo unitType; if (!self.World.Map.Rules.Actors.TryGetValue(utLower, out unitType)) throw new YamlException("Actors ruleset does not include the entry '{0}'".F(utLower)); var altitude = unitType.TraitInfo<AircraftInfo>().CruiseAltitude.Length; var dropRotation = WRot.FromFacing(dropFacing); var delta = new WVec(0, -1024, 0).Rotate(dropRotation); target = target + new WVec(0, 0, altitude); var startEdge = target - (self.World.Map.DistanceToEdge(target, -delta) + info.Cordon).Length * delta / 1024; var finishEdge = target + (self.World.Map.DistanceToEdge(target, delta) + info.Cordon).Length * delta / 1024; Actor camera = null; Beacon beacon = null; var aircraftInRange = new Dictionary<Actor, bool>(); Action<Actor> onEnterRange = a => { // Spawn a camera and remove the beacon when the first plane enters the target area if (info.CameraActor != null && !aircraftInRange.Any(kv => kv.Value)) { self.World.AddFrameEndTask(w => { camera = w.CreateActor(info.CameraActor, new TypeDictionary { new LocationInit(self.World.Map.CellContaining(target)), new OwnerInit(self.Owner), }); }); } if (beacon != null) { self.World.AddFrameEndTask(w => { w.Remove(beacon); beacon = null; }); } if (!aircraftInRange.Any(kv => kv.Value)) Game.Sound.PlayNotification(self.World.Map.Rules, self.Owner, "Speech", info.ReinforcementsArrivedSpeechNotification, self.Owner.Faction.InternalName); aircraftInRange[a] = true; }; Action<Actor> onExitRange = a => { aircraftInRange[a] = false; // Remove the camera when the final plane leaves the target area if (!aircraftInRange.Any(kv => kv.Value)) { if (camera != null) { camera.QueueActivity(new Wait(info.CameraRemoveDelay)); camera.QueueActivity(new RemoveSelf()); } camera = null; if (beacon != null) { self.World.AddFrameEndTask(w => { w.Remove(beacon); beacon = null; }); } } }; foreach (var p in info.DropItems) { var unit = self.World.CreateActor(false, p.ToLowerInvariant(), new TypeDictionary { new OwnerInit(self.Owner) }); units.Add(unit); } self.World.AddFrameEndTask(w => { PlayLaunchSounds(); Actor distanceTestActor = null; var passengersPerPlane = (info.DropItems.Length + info.SquadSize - 1) / info.SquadSize; var added = 0; 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(dropRotation); var targetOffset = new WVec(i * so.Y, 0, 0).Rotate(dropRotation); var a = w.CreateActor(info.UnitType, new TypeDictionary { new CenterPositionInit(startEdge + spawnOffset), new OwnerInit(self.Owner), new FacingInit(dropFacing), }); var drop = a.Trait<ParaDrop>(); drop.SetLZ(w.Map.CellContaining(target + targetOffset), !info.AllowImpassableCells); drop.OnEnteredDropRange += onEnterRange; drop.OnExitedDropRange += onExitRange; drop.OnRemovedFromWorld += onExitRange; var cargo = a.Trait<Cargo>(); var passengers = units.Skip(added).Take(passengersPerPlane); added += passengersPerPlane; foreach (var p in passengers) cargo.Load(a, p); a.QueueActivity(new Fly(a, Target.FromPos(target + spawnOffset))); a.QueueActivity(new Fly(a, Target.FromPos(finishEdge + spawnOffset))); a.QueueActivity(new RemoveSelf()); aircraftInRange.Add(a, false); distanceTestActor = a; } if (Info.DisplayBeacon) { var distance = (target - startEdge).HorizontalLength; beacon = new Beacon( self.Owner, target - new WVec(0, 0, altitude), Info.BeaconPaletteIsPlayerPalette, Info.BeaconPalette, Info.BeaconImage, Info.BeaconPoster, Info.BeaconPosterPalette, Info.ArrowSequence, Info.CircleSequence, Info.ClockSequence, () => 1 - ((distanceTestActor.CenterPosition - target).HorizontalLength - info.BeaconDistanceOffset.Length) * 1f / distance, Info.BeaconDelay); w.Add(beacon); } }); return units.ToArray(); }
public IRenderable OffsetBy(WVec vec) { return(new TeslaZapRenderable(pos + vec, zOffset, length, image, brightSequence, brightZaps, dimSequence, dimZaps, palette)); }
// For scaling vectors to pixel sizes in the voxel renderer public float[] ScreenVector(WVec vec) { var ts = Game.modData.Manifest.TileSize; return(new float[] { ts.Width *vec.X / 1024f, ts.Height *vec.Y / 1024f, ts.Height *vec.Z / 1024f, 1 }); }
public WVec LocalToWorld(WVec vec) { // RA's 2d perspective doesn't correspond to an orthonormal 3D // coordinate system, so fudge the y axis to make things look good return(new WVec(vec.Y, -info.CameraPitch.Sin() * vec.X / 1024, vec.Z)); }
public CarryallDeliverUnitTargeter(AircraftInfo aircraftInfo, CarryallInfo info, WVec carryableOffset) : base(aircraftInfo) { OrderID = "DeliverUnit"; OrderPriority = 6; this.carryableOffset = carryableOffset; this.aircraftInfo = aircraftInfo; this.info = info; }
public IRenderable OffsetBy(WVec vec) { return(new VoxelRenderable(voxels, pos + vec, zOffset, camera, scale, lightSource, lightAmbientColor, lightDiffuseColor, palette, normalsPalette, shadowPalette)); }
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, facing: 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 (!string.IsNullOrEmpty(info.PointDefenseType)) { shouldExplode |= world.ActorsWithTrait <IPointDefense>().Any(x => x.Trait.Destroy(pos, args.SourceActor.Owner, info.PointDefenseType)); } if (shouldExplode) { Explode(world); } }
public IRenderable OffsetBy(WVec vec) { return(new RangeCircleRenderable(centerPosition + vec, radius, zOffset, color, contrastColor)); }
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, info.VerticalRateOfTurn); } 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 - 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 < (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 < (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, info.VerticalRateOfTurn); 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, info.VerticalRateOfTurn); ChangeSpeed(); } return(desiredVFacing); }
public IRenderable OffsetBy(WVec vec) { return(new TextRenderable(font, pos + vec, zOffset, color, text)); }
protected virtual void FireBarrel(Actor self, IFacing facing, Target target, Barrel barrel) { Func <WPos> muzzlePosition = () => self.CenterPosition + MuzzleOffset(self, barrel); var legacyFacing = MuzzleOrientation(self, barrel).Yaw.Angle / 4; var passiveTarget = Weapon.TargetActorCenter ? target.CenterPosition : target.Positions.PositionClosestTo(muzzlePosition()); var initialOffset = Weapon.FirstBurstTargetOffset; if (initialOffset != WVec.Zero) { // We want this to match Armament.LocalOffset, so we need to convert it to forward, right, up initialOffset = new WVec(initialOffset.Y, -initialOffset.X, initialOffset.Z); passiveTarget += initialOffset.Rotate(WRot.FromFacing(legacyFacing)); } var followingOffset = Weapon.FollowingBurstTargetOffset; if (followingOffset != WVec.Zero) { // We want this to match Armament.LocalOffset, so we need to convert it to forward, right, up followingOffset = new WVec(followingOffset.Y, -followingOffset.X, followingOffset.Z); passiveTarget += ((Weapon.Burst - Burst) * followingOffset).Rotate(WRot.FromFacing(legacyFacing)); } var args = new ProjectileArgs { Weapon = Weapon, Facing = legacyFacing, DamageModifiers = damageModifiers.ToArray(), InaccuracyModifiers = inaccuracyModifiers.ToArray(), RangeModifiers = rangeModifiers.ToArray(), Source = muzzlePosition(), CurrentSource = muzzlePosition, SourceActor = self, PassiveTarget = passiveTarget, GuidedTarget = target }; foreach (var na in notifyAttacks) { na.PreparingAttack(self, target, this, barrel); } ScheduleDelayedAction(Info.FireDelay, () => { if (args.Weapon.Projectile != null) { var projectile = args.Weapon.Projectile.Create(args); if (projectile != null) { self.World.Add(projectile); } if (args.Weapon.Report != null && args.Weapon.Report.Any()) { Game.Sound.Play(SoundType.World, args.Weapon.Report.Random(self.World.SharedRandom), self.CenterPosition); } if (Burst == args.Weapon.Burst && args.Weapon.StartBurstReport != null && args.Weapon.StartBurstReport.Any()) { Game.Sound.Play(SoundType.World, args.Weapon.StartBurstReport.Random(self.World.SharedRandom), self.CenterPosition); } foreach (var na in notifyAttacks) { na.Attacking(self, target, this, barrel); } Recoil = Info.Recoil; } }); }
public VoxelHarvesterDockSequence(Actor self, Actor refinery, int dockAngle, bool isDragRequired, WVec dragOffset, int dragLength) : base(self, refinery, dockAngle, isDragRequired, dragOffset, dragLength) { body = self.Trait <WithVoxelUnloadBody>(); spriteOverlay = refinery.TraitOrDefault <WithDockingOverlay>(); }
public WVec LocalToWorld(WVec vec) { return(info.LocalToWorld(vec)); }
public IRenderable OffsetBy(WVec vec) { return(new UITextRenderable(font, effectiveWorldPos + vec, screenPos, zOffset, color, text)); }
public override Activity Tick(Actor self) { // Refuse to take off if it would land immediately again. // Special case: Don't kill other deploy hotkey activities. if (aircraft.ForceLanding) { return(NextActivity); } if (IsCanceled) { return(NextActivity); } if (dest == null || dest.IsDead || Reservable.IsReserved(dest)) { dest = ChooseResupplier(self, true); } var initialFacing = aircraft.Info.InitialFacing; if (dest == null || dest.IsDead) { var nearestResupplier = ChooseResupplier(self, false); // If a heli was told to return and there's no (available) RearmBuilding, going to the probable next queued activity (HeliAttack) // would be pointless (due to lack of ammo), and possibly even lead to an infinite loop due to HeliAttack.cs:L79. if (nearestResupplier == null && aircraft.Info.LandWhenIdle) { if (aircraft.Info.TurnToLand) { return(ActivityUtils.SequenceActivities(new Turn(self, initialFacing), new HeliLand(self, true))); } return(new HeliLand(self, true)); } else if (nearestResupplier == null && !aircraft.Info.LandWhenIdle) { return(null); } else { var distanceFromResupplier = (nearestResupplier.CenterPosition - self.CenterPosition).HorizontalLength; var distanceLength = aircraft.Info.WaitDistanceFromResupplyBase.Length; // If no pad is available, move near one and wait if (distanceFromResupplier > distanceLength) { var randomPosition = WVec.FromPDF(self.World.SharedRandom, 2) * distanceLength / 1024; var target = Target.FromPos(nearestResupplier.CenterPosition + randomPosition); return(ActivityUtils.SequenceActivities(new HeliFly(self, target, WDist.Zero, aircraft.Info.WaitDistanceFromResupplyBase), this)); } return(this); } } var landingProcedures = new List <Activity>(); var exit = dest.FirstExitOrDefault(null); var offset = exit != null ? exit.Info.SpawnOffset : WVec.Zero; landingProcedures.Add(new HeliFly(self, Target.FromPos(dest.CenterPosition + offset))); if (ShouldLandAtBuilding(self, dest)) { aircraft.MakeReservation(dest); if (aircraft.Info.TurnToDock) { landingProcedures.Add(new Turn(self, initialFacing)); } landingProcedures.Add(new HeliLand(self, false)); landingProcedures.Add(new ResupplyAircraft(self)); if (!abortOnResupply) { landingProcedures.Add(NextActivity); } } else { landingProcedures.Add(NextActivity); } return(ActivityUtils.SequenceActivities(landingProcedures.ToArray())); }
public SpriteHarvesterDockSequence(Actor self, Actor refinery, int dockAngle, bool isDragRequired, WVec dragOffset, int dragLength) : base(self, refinery, dockAngle, isDragRequired, dragOffset, dragLength) { wsb = self.Trait <WithSpriteBody>(); wda = self.Info.TraitInfo <WithDockingAnimationInfo>(); }
public override void Activate(Actor self, Order order, SupportPowerManager manager) { base.Activate(self, order, manager); var info = Info as AirstrikePowerInfo; var attackFacing = Util.QuantizeFacing(self.World.SharedRandom.Next(256), info.QuantizedFacings) * (256 / info.QuantizedFacings); var attackRotation = WRot.FromFacing(attackFacing); var delta = new WVec(0, -1024, 0).Rotate(attackRotation); var altitude = self.World.Map.Rules.Actors[info.UnitType].Traits.Get <PlaneInfo>().CruiseAltitude.Range; var target = self.World.Map.CenterOfCell(order.TargetLocation) + new WVec(0, 0, altitude); var startEdge = target - (self.World.Map.DistanceToEdge(target, -delta) + info.Cordon).Range * delta / 1024; var finishEdge = target + (self.World.Map.DistanceToEdge(target, delta) + info.Cordon).Range * delta / 1024; Actor camera = null; Beacon beacon = null; var aircraftInRange = new Dictionary <Actor, bool>(); Action <Actor> onEnterRange = a => { // Spawn a camera and remove the beacon when the first plane enters the target area if (info.CameraActor != null && !aircraftInRange.Any(kv => kv.Value)) { self.World.AddFrameEndTask(w => { camera = w.CreateActor(info.CameraActor, new TypeDictionary { new LocationInit(order.TargetLocation), new OwnerInit(self.Owner), }); }); } if (beacon != null) { self.World.AddFrameEndTask(w => { w.Remove(beacon); beacon = null; }); } aircraftInRange[a] = true; }; Action <Actor> onExitRange = a => { aircraftInRange[a] = false; // Remove the camera when the final plane leaves the target area if (!aircraftInRange.Any(kv => kv.Value)) { if (camera != null) { camera.QueueActivity(new Wait(info.CameraRemoveDelay)); camera.QueueActivity(new RemoveSelf()); } camera = null; } }; self.World.AddFrameEndTask(w => { var notification = self.Owner.IsAlliedWith(self.World.RenderPlayer) ? Info.LaunchSound : Info.IncomingSound; Sound.Play(notification); Actor distanceTestActor = null; 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 targetOffset = new WVec(i * so.Y, 0, 0).Rotate(attackRotation); var a = w.CreateActor(info.UnitType, new TypeDictionary { new CenterPositionInit(startEdge + spawnOffset), new OwnerInit(self.Owner), new FacingInit(attackFacing), }); var attack = a.Trait <AttackBomber>(); attack.SetTarget(w, target + targetOffset); attack.OnEnteredAttackRange += onEnterRange; attack.OnExitedAttackRange += onExitRange; attack.OnRemovedFromWorld += onExitRange; a.QueueActivity(new Fly(a, Target.FromPos(finishEdge + spawnOffset))); a.QueueActivity(new RemoveSelf()); aircraftInRange.Add(a, false); distanceTestActor = a; } if (Info.DisplayBeacon) { var distance = (target - startEdge).HorizontalLength; beacon = new Beacon( order.Player, self.World.Map.CenterOfCell(order.TargetLocation), Info.BeaconPalettePrefix, Info.BeaconPoster, Info.BeaconPosterPalette, () => 1 - ((distanceTestActor.CenterPosition - target).HorizontalLength - info.BeaconDistanceOffset.Range) * 1f / distance ); w.Add(beacon); } }); }
public SpriteHarvesterDockSequence(Actor self, Actor refinery, int dockAngle, bool isDragRequired, WVec dragOffset, int dragLength) : base(self, refinery, dockAngle, isDragRequired, dragOffset, dragLength) { ru = self.Trait <RenderUnit>(); }