public override Activity Tick(Actor self) { var positionable = self.Trait <IPositionable>(); var mobile = positionable as Mobile; var pos = length > 1 ? WPos.Lerp(start, end, ticks, length - 1) : end; positionable.SetVisualPosition(self, pos); if (++ticks >= length) { if (mobile != null) { mobile.IsMoving = false; } return(NextActivity); } if (mobile != null) { mobile.IsMoving = true; } return(this); }
/// <summary> /// Find the point (D) on a line (A-B) that is closest to the target point (C). /// </summary> /// <param name="lineStart">The source point (tail) of the line</param> /// <param name="lineEnd">The target point (head) of the line</param> /// <param name="point">The target point that the minimum distance should be found to</param> /// <returns>The WPos that is the point on the line that is closest to the target point</returns> public static WPos MinimumPointLineProjection(WPos lineStart, WPos lineEnd, WPos point) { var squaredLength = (lineEnd - lineStart).HorizontalLengthSquared; // Line has zero length, so just use the lineEnd position as the closest position. if (squaredLength == 0) { return(lineEnd); } // Consider the line extending the segment, parameterized as target + t (source - target). // We find projection of point onto the line. // It falls where t = [(point - target) . (source - target)] / |source - target|^2 // The normal DotProduct math would be (xDiff + yDiff) / dist, where dist = (target - source).LengthSquared; // But in order to avoid floating points, we do not divide here, but rather work with the large numbers as far as possible. // We then later divide by dist, only AFTER we have multiplied by the dotproduct. var xDiff = ((long)point.X - lineEnd.X) * (lineStart.X - lineEnd.X); var yDiff = ((long)point.Y - lineEnd.Y) * (lineStart.Y - lineEnd.Y); var t = xDiff + yDiff; // Beyond the 'target' end of the segment if (t < 0) { return(lineEnd); } // Beyond the 'source' end of the segment if (t > squaredLength) { return(lineStart); } // Projection falls on the segment return(WPos.Lerp(lineEnd, lineStart, t, squaredLength)); }
// gets where main projectile should fly to WPos GetTargetPos() { var targetpos = args.PassiveTarget; var actorpos = args.SourceActor.CenterPosition; return(WPos.Lerp(actorpos, targetpos, args.Weapon.Range.Length, (targetpos - actorpos).Length)); }
void UpdateCenterLocation(Actor self, Mobile mobile) { // Avoid division through zero if (MoveFractionTotal != 0) { WPos pos; if (EnableArc) { var angle = WAngle.Lerp(ArcFromAngle, ArcToAngle, moveFraction, MoveFractionTotal); var length = int2.Lerp(ArcFromLength, ArcToLength, moveFraction, MoveFractionTotal); var height = int2.Lerp(From.Z, To.Z, moveFraction, MoveFractionTotal); pos = ArcCenter + new WVec(0, length, height).Rotate(WRot.FromYaw(angle)); } else { pos = WPos.Lerp(From, To, moveFraction, MoveFractionTotal); } pos -= new WVec(WDist.Zero, WDist.Zero, self.World.Map.DistanceAboveTerrain(pos)); mobile.SetVisualPosition(self, pos); } else { mobile.SetVisualPosition(self, To); } if (moveFraction >= MoveFractionTotal) { mobile.Facing = ToFacing; } else { mobile.Facing = WAngle.Lerp(FromFacing, ToFacing, moveFraction, MoveFractionTotal); } }
public static WPos MinimumPointLineProjection(WPos lineStart, WPos lineEnd, WPos point) { var squaredLength = (lineEnd - lineStart).HorizontalLengthSquared; if (squaredLength == 0) { return(lineEnd); } var xDiff = ((long)point.X - lineEnd.X) * (lineStart.X - lineEnd.X); var yDiff = ((long)point.Y - lineEnd.Y) * (lineStart.Y - lineEnd.Y); var t = xDiff + yDiff; if (t < 0) { return(lineEnd); } if (t > squaredLength) { return(lineStart); } return(WPos.Lerp(lineEnd, lineStart, t, squaredLength)); }
public override Activity Tick(Actor self) { if (disableable != null && disableable.IsTraitDisabled) { return(this); } var pos = length > 1 ? WPos.Lerp(start, end, ticks, length - 1) : end; positionable.SetVisualPosition(self, pos); if (++ticks >= length) { if (movement != null) { movement.IsMoving = false; } return(NextActivity); } if (movement != null) { movement.IsMoving = true; } return(this); }
void UpdateCenterLocation(Actor self, Mobile mobile) { //Avoid division through zero if (MoveFractionTotal != 0) { WPos pos = WPos.Zero; if (EnableArc) { var angle = WAngle.Lerp(ArcFromAngle, ArcToAngle, moveFraction, MoveFractionTotal); var length = Int2.Lerp(ArcFromLength, ArcToLength, moveFraction, MoveFractionTotal); var height = Int2.Lerp(From.Z, To.Z, moveFraction, MoveFractionTotal); pos = ArcCenter + new WVec(0, length, length).Rotate(WRot.FromYaw(angle)); } else { pos = WPos.Lerp(From, To, moveFraction, MoveFractionTotal); } mobile.SetVisualPosition(self, pos); } else { mobile.SetVisualPosition(self, To); } if (moveFraction >= MoveFractionTotal) { mobile.Facing = ToFacing & 0xFF; } else { mobile.Facing = Int2.Lerp(FromFacing, ToFacing, moveFraction, MoveFractionTotal) & 0xFF; } }
public static WPos BetweenCells(World w, CPos from, CPos to) { var fromPos = from.Layer == 0 ? w.Map.CenterOfCell(from) : w.GetCustomMovementLayers()[from.Layer].CenterOfCell(from); var toPos = to.Layer == 0 ? w.Map.CenterOfCell(to) : w.GetCustomMovementLayers()[to.Layer].CenterOfCell(to); return(WPos.Lerp(fromPos, toPos, 1, 2)); }
public void Tick(World world) { ticks++; if (anim != null) { anim.Tick(); } var lastPos = projectilepos; projectilepos = WPos.Lerp(source, targetpos, ticks, estimatedlifespan); // Check for walls or other blocking obstacles. WPos blockedPos; if (info.Blockable && BlocksProjectiles.AnyBlockingActorsBetween(world, lastPos, projectilepos, info.Width, out blockedPos)) { projectilepos = blockedPos; DetonateSelf = true; } if (!string.IsNullOrEmpty(info.TrailImage) && --smokeTicks < 0) { var delayedPos = WPos.Lerp(source, targetpos, ticks - info.TrailDelay, estimatedlifespan); world.AddFrameEndTask(w => w.Add(new SpriteEffect(delayedPos, w, info.TrailImage, info.TrailSequences.Random(world.SharedRandom), trailPalette, false, false, GetEffectiveFacing()))); smokeTicks = info.TrailInterval; } if (info.ContrailLength > 0) { contrail.Update(projectilepos); } var flightLengthReached = ticks >= lifespan; if (flightLengthReached) { DetonateSelf = true; } // Driving into cell with higher height level DetonateSelf |= world.Map.DistanceAboveTerrain(projectilepos) < info.ExplodeUnderThisAltitude; if (DetonateSelf) { Explode(world); } }
public override Activity Tick(Actor self) { if (canceled) { return(NextActivity); } // Correct the visual position after we jumped if (jumpComplete) { if (ChildActivity == null) { return(NextActivity); } ChildActivity = ActivityUtils.RunActivity(self, ChildActivity); return(this); } if (target.Type != TargetType.Invalid) { targetPosition = target.CenterPosition; } var position = length > 1 ? WPos.Lerp(origin, targetPosition, ticks, length - 1) : targetPosition; mobile.SetVisualPosition(self, position); // We are at the destination if (++ticks >= length) { // Revoke the run condition attack.IsAiming = false; // Move to the correct subcells, if our target actor uses subcells // (This does not update the visual position!) mobile.SetLocation(destinationCell, destinationSubCell, destinationCell, destinationSubCell); // Revoke the condition before attacking, as it is usually used to pause the attack trait attack.RevokeLeapCondition(self); attack.DoAttack(self, target); jumpComplete = true; QueueChild(self, mobile.VisualMove(self, position, self.World.Map.CenterOfSubCell(destinationCell, destinationSubCell)), true); return(this); } return(this); }
public override bool Tick(Actor self) { if (disableable != null && disableable.IsTraitDisabled) { return(false); } var pos = length > 1 ? WPos.Lerp(start, end, ticks, length - 1) : end; positionable.SetVisualPosition(self, pos); if (++ticks >= length) { return(true); } return(false); }
public override bool Tick(Actor self) { // Correct the visual position after we jumped if (canceled || jumpComplete) { return(true); } if (target.Type != TargetType.Invalid) { targetPosition = target.CenterPosition; } var position = length > 1 ? WPos.Lerp(origin, targetPosition, ticks, length - 1) : targetPosition; mobile.SetVisualPosition(self, position); // We are at the destination if (++ticks >= length) { // Revoke the run condition attack.IsAiming = false; // Move to the correct subcells, if our target actor uses subcells // (This does not update the visual position!) mobile.SetLocation(destinationCell, destinationSubCell, destinationCell, destinationSubCell); // Update movement which results in movementType set to MovementType.None. // This is needed to prevent the move animation from playing. mobile.UpdateMovement(self); // Revoke the condition before attacking, as it is usually used to pause the attack trait attack.RevokeLeapCondition(self); attack.DoAttack(self, target); jumpComplete = true; QueueChild(mobile.VisualMove(self, position, self.World.Map.CenterOfSubCell(destinationCell, destinationSubCell))); } return(false); }
void UpdateCenterLocation(Actor self, Mobile mobile) { // avoid division through zero if (MoveFractionTotal != 0) { mobile.SetVisualPosition(self, WPos.Lerp(From, To, moveFraction, MoveFractionTotal)); } else { mobile.SetVisualPosition(self, To); } if (moveFraction >= MoveFractionTotal) { mobile.Facing = ToFacing & 0xFF; } else { mobile.Facing = int2.Lerp(FromFacing, ToFacing, moveFraction, MoveFractionTotal) & 0xFF; } }
public virtual WVec GetRepulsionForce() { if (!Info.Repulsable) { return(WVec.Zero); } if (reservation != null) { var distanceFromReservationActor = (ReservedActor.CenterPosition - self.CenterPosition).HorizontalLength; if (distanceFromReservationActor < Info.WaitDistanceFromResupplyBase.Length) { return(WVec.Zero); } } // Repulsion only applies when we're flying at CruiseAltitude! if (!cruising) { return(WVec.Zero); } // PERF: Avoid LINQ. var repulsionForce = WVec.Zero; foreach (var actor in self.World.FindActorsInCircle(self.CenterPosition, Info.IdealSeparation)) { if (actor.IsDead) { continue; } var ai = actor.Info.TraitInfoOrDefault <AircraftInfo>(); if (ai == null || !ai.Repulsable || ai.CruiseAltitude != Info.CruiseAltitude) { continue; } repulsionForce += GetRepulsionForce(actor); } // Actors outside the map bounds receive an extra nudge towards the center of the map if (!self.World.Map.Contains(self.Location)) { // The map bounds are in projected coordinates, which is technically wrong for this, // but we avoid the issues in practice by guessing the middle of the map instead of the edge var center = WPos.Lerp(self.World.Map.ProjectedTopLeft, self.World.Map.ProjectedBottomRight, 1, 2); repulsionForce += new WVec(1024, 0, 0).Rotate(WRot.FromYaw((self.CenterPosition - center).Yaw)); } if (Info.CanHover) { return(repulsionForce); } // Non-hovering actors mush always keep moving forward, so they need extra calculations. var currentDir = FlyStep(Facing); var length = currentDir.HorizontalLength * repulsionForce.HorizontalLength; if (length == 0) { return(WVec.Zero); } var dot = WVec.Dot(currentDir, repulsionForce) / length; // avoid stalling the plane return(dot >= 0 ? repulsionForce : WVec.Zero); }
public void Tick(World world) { ticks++; if (anim != null) { anim.Tick(); } var lastPos = projectilepos; var dx = projectilepos.X - source.X; var dy = projectilepos.Y - source.Y; var normal = new WVec(dy, -dx, 0); targetpos = projectilepos; var originalVec = projectilepos - source; if (dx != 0 || dy != 0) { int circSpeed = 1 + (ticks * 3); int maxSpeed = 200; if (circSpeed > maxSpeed) { circSpeed = maxSpeed; } normal = WVec.Lerp(WVec.Zero, normal, circSpeed, normal.Length); targetpos -= normal; targetpos += WVec.Lerp(WVec.Zero, originalVec, 30, originalVec.Length); } else { targetpos += args.VecNormalized; } projectilepos = targetpos; if (ticks > 90) { DetonateSelf = true; } // Check for walls or other blocking obstacles. WPos blockedPos; if (info.Blockable && BlocksProjectiles.AnyBlockingActorsBetween(world, lastPos, projectilepos, info.Width, out blockedPos)) { projectilepos = blockedPos; DetonateSelf = true; } if (!string.IsNullOrEmpty(info.TrailImage) && --smokeTicks < 0) { var delayedPos = WPos.Lerp(lastPos, targetpos, ticks - info.TrailDelay, estimatedlifespan); world.AddFrameEndTask(w => w.Add(new SpriteEffect(delayedPos, w, info.TrailImage, info.TrailSequences.Random(world.SharedRandom), trailPalette, false, GetEffectiveFacing().Angle))); smokeTicks = info.TrailInterval; } if (info.ContrailLength > 0) { contrail.Update(projectilepos); } var flightLengthReached = ticks >= lifespan; if (flightLengthReached) { DetonateSelf = true; } // Driving into cell with higher height level DetonateSelf |= world.Map.DistanceAboveTerrain(projectilepos) < info.ExplodeUnderThisAltitude; if (DetonateSelf) { Explode(world); } }
// gets where main projectile should fly to WPos GetTargetPos() { var targetpos = args.PassiveTarget; return(WPos.Lerp(sourcepos, targetpos, args.Weapon.Range.Length, (targetpos - sourcepos).Length)); }
public static WPos BetweenCells(World w, CPos from, CPos to) { return(WPos.Lerp(w.Map.CenterOfCell(from), w.Map.CenterOfCell(to), 1, 2)); }
public virtual WVec GetRepulsionForce() { if (!Info.Repulsable) { return(WVec.Zero); } if (reservation != null) { var distanceFromReservationActor = (ReservedActor.CenterPosition - self.CenterPosition).HorizontalLength; if (distanceFromReservationActor < Info.WaitDistanceFromResupplyBase.Length) { return(WVec.Zero); } } var altitude = self.World.Map.DistanceAboveTerrain(CenterPosition).Length; if (altitude != Info.CruiseAltitude.Length) { return(WVec.Zero); } var repulsionForce = WVec.Zero; foreach (var actor in self.World.FindActorsInCircle(self.CenterPosition, Info.IdealSeparation)) { if (actor.IsDead) { continue; } var ai = actor.Info.TraitInfoOrDefault <AircraftInfo>(); if (ai == null || !ai.Repulsable || ai.CruiseAltitude != Info.CruiseAltitude) { continue; } repulsionForce += GetRepulsionForce(actor); } if (!self.World.Map.Contains(self.Location)) { var center = WPos.Lerp(self.World.Map.ProjectedTopLeft, self.World.Map.ProjectedBottomRight, 1, 2); repulsionForce += new WVec(1024, 0, 0).Rotate(WRot.FromYaw((self.CenterPosition - center).Yaw)); } if (Info.CanHover) { return(repulsionForce); } var currentDir = FlyStep(Facing); var length = currentDir.HorizontalLength * repulsionForce.HorizontalLength; if (length == 0) { return(WVec.Zero); } var dot = WVec.Dot(currentDir, repulsionForce) / length; //avoid stalling the plane return(dot >= 0 ? repulsionForce : WVec.Zero); }
public static WPos BetweenCells(CPos from, CPos to) { return(WPos.Lerp(from.CenterPosition, to.CenterPosition, 1, 2)); }
public ShockWaveProjectile(ShockWaveProjectileInfo info, ProjectileArgs args) { this.info = info; this.args = args; sourcepos = args.Source; var firedBy = args.SourceActor; 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]; } targetpos = GetTargetPos(); mindelay = args.Weapon.MinRange.Length / speed.Length; projectiles = new ShockWaveProjectileEffect[info.NumProjectiles]; var mainFacing = (targetpos - sourcepos).Yaw.Facing; // used for lerping projectiles at the same pace var estimatedLifespan = Math.Max(args.Weapon.Range.Length / speed.Length, 1); // target that will be assigned Target target; // subprojectiles facing WAngle facing; var convergePoint = WPos.Lerp(sourcepos, targetpos, info.SpreadUntilDistance.Length, (targetpos - sourcepos).Length); var dx = targetpos.X - sourcepos.X; var dy = targetpos.Y - sourcepos.Y; var normal = new WVec(-dy, dx, 0); for (int i = 0; i < info.NumProjectiles; i++) { target = Target.FromPos(targetpos); // If it's true then lifespan is counted from source position to target instead of max range. lifespan = info.KillProjectilesWhenReachedTargetLocation ? Math.Max((args.PassiveTarget - args.Source).Length / speed.Length, 1) : estimatedLifespan; facing = (targetpos - sourcepos).Yaw; int shiftIndex = (i - (info.NumProjectiles / 2)) * info.Splay; var newRotation = WRot.FromFacing(shiftIndex); var rotatedTarget = (targetpos - sourcepos).Rotate(newRotation); target = Target.FromPos(sourcepos + rotatedTarget); var projectileArgs = new ShockwaveProjectileArgs { Weapon = args.Weapon, DamageModifiers = args.DamageModifiers, Facing = facing, Source = sourcepos, CurrentSource = () => sourcepos, SourceActor = firedBy, GuidedTarget = target, PassiveTarget = sourcepos + rotatedTarget, ConvergePoint = convergePoint, OriginalTargetVec = targetpos - sourcepos, Normal = normal }; projectiles[i] = new ShockWaveProjectileEffect(info, projectileArgs, lifespan, estimatedLifespan); } foreach (var p in projectiles) { world.AddFrameEndTask(w => w.Add(p)); } }
public void Tick(Actor self) { if (world.FrameNumber % 100 == 0) { var actor = OffmapAttackers.Random(world.SharedRandom); var spawn = offmapAttackerSpawns.Random(world.SharedRandom); var u = world.CreateActor(actor, soviets, spawn.Location, Traits.Util.GetFacing(attackLocation.Location - spawn.Location, 0)); var cargo = u.TraitOrDefault <Cargo>(); if (cargo != null) { while (cargo.HasSpace(1)) { cargo.Load(u, world.CreateActor(false, AttackerCargo.Random(world.SharedRandom), soviets, null, null)); } } u.QueueActivity(new AttackMove.AttackMoveActivity(u, new Move.Move(attackLocation.Location, 0))); } if (world.FrameNumber % 25 == 0) { foreach (var actor in world.Actors.Where(a => a.IsInWorld && a.IsIdle && !a.IsDead() && a.HasTrait <AttackBase>() && a.HasTrait <Mobile>()).Except(actors.Values)) { MissionUtils.AttackNearestLandActor(true, actor, actor.Owner == soviets ? allies : soviets); } MissionUtils.StartProduction(world, allies, "Infantry", InfantryProductionUnits.Random(world.SharedRandom)); MissionUtils.StartProduction(world, allies, "Vehicle", VehicleProductionUnits.Random(world.SharedRandom)); } if (world.FrameNumber % 20 == 0 && coastUnitsLeft-- > 0) { var u = world.CreateActor(CoastUnits.Random(world.SharedRandom), soviets, coastWP1.Location, null); u.QueueActivity(new Move.Move(coastWP2.Location, 0)); u.QueueActivity(new AttackMove.AttackMoveActivity(u, new Move.Move(attackLocation.Location, 0))); } if (world.FrameNumber == nextCivilianMove) { var civilians = world.Actors.Where(a => !a.IsDead() && a.IsInWorld && a.Owner == neutral && a.HasTrait <Mobile>()); if (civilians.Any()) { var civilian = civilians.Random(world.SharedRandom); civilian.Trait <Mobile>().Nudge(civilian, civilian, true); nextCivilianMove += world.SharedRandom.Next(1, 75); } } if (world.FrameNumber == 1) { MissionUtils.Paradrop(world, soviets, ParadropUnits, paradrop1Entry.Location, paradrop1LZ.Location); MissionUtils.Paradrop(world, soviets, ParadropUnits, paradrop2Entry.Location, paradrop2LZ.Location); } if (--waitTicks <= 0) { if (++mul <= div) { worldRenderer.Viewport.Center(WPos.Lerp(viewportOrigin, viewportTarget, mul, div)); } else { mul = 0; viewportOrigin = viewportTarget; viewportTarget = viewportTargets[(viewportTargetNumber = (viewportTargetNumber + 1) % viewportTargets.Length)]; waitTicks = 100; if (viewportTargetNumber == 0) { coastUnitsLeft = 15; SendChinookReinforcements(chinook1Entry.Location, chinook1LZ); SendChinookReinforcements(chinook2Entry.Location, chinook2LZ); } if (viewportTargetNumber == 1) { MissionUtils.Paradrop(world, soviets, ParadropUnits, paradrop1Entry.Location, paradrop1LZ.Location); MissionUtils.Paradrop(world, soviets, ParadropUnits, paradrop2Entry.Location, paradrop2LZ.Location); } if (viewportTargetNumber == 2) { AttackWithHeavyTanks(); ChronoSpawnMediumTanks(); } if (viewportTargetNumber == 4) { FlyMigs(mig1Waypoints); FlyMigs(mig2Waypoints); } } } MissionUtils.CapOre(soviets); }