예제 #1
0
        IEnumerable <IRenderable> RenderArmaments(Actor self, AttackBase attack)
        {
            // Fire ports on garrisonable structures
            if (attack is AttackGarrisoned garrison)
            {
                var bodyOrientation = coords.Value.QuantizeOrientation(self, self.Orientation);
                foreach (var p in garrison.Info.Ports)
                {
                    var pos = self.CenterPosition + coords.Value.LocalToWorld(p.Offset.Rotate(bodyOrientation));
                    var da  = coords.Value.LocalToWorld(new WVec(224, 0, 0).Rotate(WRot.FromYaw(p.Yaw + p.Cone)).Rotate(bodyOrientation));
                    var db  = coords.Value.LocalToWorld(new WVec(224, 0, 0).Rotate(WRot.FromYaw(p.Yaw - p.Cone)).Rotate(bodyOrientation));

                    yield return(new LineAnnotationRenderable(pos, pos + da * 224 / da.Length, 1, Color.White));

                    yield return(new LineAnnotationRenderable(pos, pos + db * 224 / da.Length, 1, Color.White));
                }

                yield break;
            }

            foreach (var a in attack.Armaments)
            {
                if (a.IsTraitDisabled)
                {
                    continue;
                }

                foreach (var b in a.Barrels)
                {
                    var barrelEnd = new Barrel
                    {
                        Offset = b.Offset + new WVec(224, 0, 0),
                        Yaw    = b.Yaw
                    };

                    var muzzle    = self.CenterPosition + a.MuzzleOffset(self, b);
                    var endMuzzle = self.CenterPosition + a.MuzzleOffset(self, barrelEnd);
                    yield return(new LineAnnotationRenderable(muzzle, endMuzzle, 1, Color.White));
                }
            }
        }
예제 #2
0
        public IEnumerable <IActorPreview> RenderPreviewSprites(ActorPreviewInitializer init, RenderSpritesInfo rs, string image, int facings, PaletteReference p)
        {
            if (!EnabledByDefault)
            {
                yield break;
            }

            if (Palette != null)
            {
                p = init.WorldRenderer.Palette(Palette);
            }

            Func <WAngle> facing;
            var           dynamicfacingInit = init.GetOrDefault <DynamicFacingInit>();

            if (dynamicfacingInit != null)
            {
                facing = dynamicfacingInit.Value;
            }
            else
            {
                var f = init.GetValue <FacingInit, WAngle>(WAngle.Zero);
                facing = () => f;
            }

            var anim = new Animation(init.World, image, facing);

            anim.IsDecoration = IsDecoration;
            anim.PlayRepeating(RenderSprites.NormalizeSequence(anim, init.GetDamageState(), Sequence));

            var         body        = init.Actor.TraitInfo <BodyOrientationInfo>();
            Func <WRot> orientation = () => body.QuantizeOrientation(WRot.FromYaw(facing()), facings);
            Func <WVec> offset      = () => body.LocalToWorld(Offset.Rotate(orientation()));
            Func <int>  zOffset     = () =>
            {
                var tmpOffset = offset();
                return(tmpOffset.Y + tmpOffset.Z + 1);
            };

            yield return(new SpriteActorPreview(anim, offset, zOffset, p, rs.Scale));
        }
예제 #3
0
        public IEnumerable <ModelAnimation> 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 t    = init.Actor.TraitInfos <TurretedInfo>()
                       .First(tt => tt.Turret == Turret);

            var         model        = init.World.ModelCache.GetModelSequence(image, Sequence);
            Func <WVec> turretOffset = () => body.LocalToWorld(t.Offset.Rotate(orientation()));

            var         turretFacing          = Turreted.TurretFacingFromInit(init, t.InitialFacing, Turret);
            Func <WRot> turretBodyOrientation = () => WRot.FromYaw(WAngle.FromFacing(turretFacing()) - orientation().Yaw);

            yield return(new ModelAnimation(model, turretOffset,
                                            () => new[] { turretBodyOrientation(), body.QuantizeOrientation(orientation(), facings) }, () => false, () => 0, ShowShadow));
        }
예제 #4
0
        public IEnumerable <VoxelAnimation> RenderPreviewVoxels(
            ActorPreviewInitializer init, RenderVoxelsInfo rv, string image, Func <WRot> orientation, int facings, PaletteReference p)
        {
            if (UpgradeMinEnabledLevel > 0)
            {
                yield break;
            }

            var body = init.Actor.TraitInfo <BodyOrientationInfo>();
            var t    = init.Actor.TraitInfos <TurretedInfo>()
                       .First(tt => tt.Turret == Turret);

            var         voxel        = VoxelProvider.GetVoxel(image, Sequence);
            Func <WVec> turretOffset = () => body.LocalToWorld(t.Offset.Rotate(orientation()));

            var         turretFacing          = Turreted.TurretFacingFromInit(init, t.InitialFacing, Turret);
            Func <WRot> turretBodyOrientation = () => WRot.FromYaw(WAngle.FromFacing(turretFacing()) - orientation().Yaw);

            yield return(new VoxelAnimation(voxel, turretOffset,
                                            () => new[] { turretBodyOrientation(), body.QuantizeOrientation(orientation(), facings) }, () => false, () => 0));
        }
예제 #5
0
파일: AreaBeam.cs 프로젝트: szlatkow/OpenRA
        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;

            // 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.FromYaw(towardsTargetFacing));

            target += dir * info.BeyondTargetRange.Length / 1024;

            length = Math.Max((target - headPos).Length / speed.Length, 1);
        }
예제 #6
0
        void DrawArmaments(Actor self, AttackBase attack, WorldRenderer wr, RgbaColorRenderer wcr, float iz)
        {
            var c = Color.White;

            // Fire ports on garrisonable structures
            var garrison = attack as AttackGarrisoned;

            if (garrison != null)
            {
                var bodyOrientation = coords.Value.QuantizeOrientation(self, self.Orientation);
                foreach (var p in garrison.Info.Ports)
                {
                    var pos = self.CenterPosition + coords.Value.LocalToWorld(p.Offset.Rotate(bodyOrientation));
                    var da  = coords.Value.LocalToWorld(new WVec(224, 0, 0).Rotate(WRot.FromYaw(p.Yaw + p.Cone)).Rotate(bodyOrientation));
                    var db  = coords.Value.LocalToWorld(new WVec(224, 0, 0).Rotate(WRot.FromYaw(p.Yaw - p.Cone)).Rotate(bodyOrientation));

                    var o = wr.ScreenPosition(pos);
                    var a = wr.ScreenPosition(pos + da * 224 / da.Length);
                    var b = wr.ScreenPosition(pos + db * 224 / db.Length);
                    wcr.DrawLine(o, a, iz, c);
                    wcr.DrawLine(o, b, iz, c);
                }

                return;
            }

            foreach (var a in attack.Armaments)
            {
                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);
                    wcr.DrawLine(sm, sd, iz, c);
                    TargetLineRenderable.DrawTargetMarker(wr, c, sm);
                }
            }
        }
예제 #7
0
            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);
                    }

                    if (self.Location.Layer == 0)
                    {
                        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);
                }
            }
예제 #8
0
        public IEnumerable <IActorPreview> RenderPreviewSprites(ActorPreviewInitializer init, string image, int facings, PaletteReference p)
        {
            if (!EnabledByDefault)
            {
                yield break;
            }

            var body = init.Actor.TraitInfo <BodyOrientationInfo>();
            var t    = init.Actor.TraitInfos <TurretedInfo>()
                       .First(tt => tt.Turret == Turret);

            var turretFacing = t.WorldFacingFromInit(init);
            var anim         = new Animation(init.World, image, turretFacing);

            anim.Play(RenderSprites.NormalizeSequence(anim, init.GetDamageState(), Sequence));

            var         facing      = init.GetFacing();
            Func <WRot> orientation = () => body.QuantizeOrientation(WRot.FromYaw(facing()), facings);
            Func <WVec> offset      = () => body.LocalToWorld(t.Offset.Rotate(orientation()));
            Func <int>  zOffset     = () =>
            {
                var tmpOffset = offset();
                return(-(tmpOffset.Y + tmpOffset.Z) + 1);
            };

            if (IsPlayerPalette)
            {
                p = init.WorldRenderer.Palette(Palette + init.Get <OwnerInit>().InternalName);
            }
            else if (Palette != null)
            {
                p = init.WorldRenderer.Palette(Palette);
            }

            yield return(new SpriteActorPreview(anim, offset, zOffset, p));
        }
예제 #9
0
        public GravityBomb(GravityBombInfo info, ProjectileArgs args)
        {
            this.info = info;
            this.args = args;
            pos       = args.Source;
            var convertedVelocity = new WVec(info.Velocity.Y, -info.Velocity.X, info.Velocity.Z);

            velocity     = convertedVelocity.Rotate(WRot.FromYaw(args.Facing));
            acceleration = new WVec(info.Acceleration.Y, -info.Acceleration.X, info.Acceleration.Z);

            if (!string.IsNullOrEmpty(info.Image))
            {
                anim = new Animation(args.SourceActor.World, info.Image, () => args.Facing);

                if (!string.IsNullOrEmpty(info.OpenSequence))
                {
                    anim.PlayThen(info.OpenSequence, () => anim.PlayRepeating(info.Sequences.Random(args.SourceActor.World.SharedRandom)));
                }
                else
                {
                    anim.PlayRepeating(info.Sequences.Random(args.SourceActor.World.SharedRandom));
                }
            }
        }
예제 #10
0
        WVec BarrelOffset()
        {
            var b                 = self.Orientation;
            var qb                = body.QuantizeOrientation(self, b);
            var localOffset       = Info.LocalOffset + new WVec(-armament.Recoil, WDist.Zero, WDist.Zero);
            var turretLocalOffset = turreted != null ? turreted.Offset : WVec.Zero;
            var turretOrientation = turreted != null?turreted.WorldOrientation(self) - b + WRot.FromYaw(b.Yaw - qb.Yaw) : WRot.None;

            return(body.LocalToWorld((turretLocalOffset + localOffset.Rotate(turretOrientation)).Rotate(qb)));
        }
예제 #11
0
        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);
        }
예제 #12
0
 public Turreted(ActorInitializer init, TurretedInfo info)
     : base(info)
 {
     LocalOrientation = WRot.FromYaw(info.LocalFacingFromInit(init)());
 }
예제 #13
0
        public Actor[] SendAirstrike(Actor self, WPos target, WAngle?facing = null)
        {
            var aircraft = new List <Actor>();

            if (!facing.HasValue)
            {
                facing = new WAngle(1024 * self.World.SharedRandom.Next(info.QuantizedFacings) / info.QuantizedFacings);
            }

            var altitude       = self.World.Map.Rules.Actors[info.UnitType].TraitInfo <AircraftInfo>().CruiseAltitude.Length;
            var attackRotation = WRot.FromYaw(facing.Value);
            var delta          = new WVec(0, -1024, 0).Rotate(attackRotation);

            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);

                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 the range.
                // If at the map's edge, they may be removed from world before leaving.
                if (aircraftInRange.All(kv => !kv.Key.IsInWorld))
                {
                    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(attackRotation);
                var targetOffset = new WVec(i * so.Y, 0, 0).Rotate(attackRotation);
                var a            = self.World.CreateActor(false, info.UnitType, new TypeDictionary
                {
                    new CenterPositionInit(startEdge + spawnOffset),
                    new OwnerInit(self.Owner),
                    new FacingInit(facing.Value),
                });

                aircraft.Add(a);
                aircraftInRange.Add(a, false);

                var attack = a.Trait <AttackBomber>();
                attack.SetTarget(target + targetOffset);
                attack.OnEnteredAttackRange += onEnterRange;
                attack.OnExitedAttackRange  += onExitRange;
                attack.OnRemovedFromWorld   += onRemovedFromWorld;
            }

            self.World.AddFrameEndTask(w =>
            {
                PlayLaunchSounds();

                var j = 0;
                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 a = aircraft[j++];
                    w.Add(a);

                    a.QueueActivity(new Fly(a, Target.FromPos(target + spawnOffset)));
                    a.QueueActivity(new Fly(a, Target.FromPos(finishEdge + spawnOffset)));
                    a.QueueActivity(new RemoveSelf());
                    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.BeaconSequence,
                        Info.ArrowSequence,
                        Info.CircleSequence,
                        Info.ClockSequence,
                        () => 1 - ((distanceTestActor.CenterPosition - target).HorizontalLength - info.BeaconDistanceOffset.Length) * 1f / distance,
                        Info.BeaconDelay);

                    w.Add(beacon);
                }
            });

            return(aircraft.ToArray());
        }
예제 #14
0
파일: Missile.cs 프로젝트: reaperrr/OpenRA
        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 (shouldExplode)
            {
                Explode(world);
            }
        }
예제 #15
0
        void ITick.Tick(Actor self)
        {
            if (IsTraitDisabled || IsTraitPaused || !self.IsInWorld || --ticks > 0)
            {
                return;
            }

            ticks = Util.RandomDelay(self.World, Info.Delay);

            var localoffset = body != null
                                        ? body.LocalToWorld(Info.LocalOffset.Rotate(body.QuantizeOrientation(self, self.Orientation)))
                                        : Info.LocalOffset;

            var position = self.CenterPosition + localoffset;

            var availableTargetActors = world.FindActorsOnCircle(position, Info.WeaponInfo.Range)
                                        .Where(x => (Info.AllowSelfHit || x != self) &&
                                               Info.WeaponInfo.IsValidAgainst(Target.FromActor(x), world, self) &&
                                               Info.AimTargetStances.HasRelationship(self.Owner.RelationshipWith(x.Owner)))
                                        .Where(x =>
            {
                var activeShapes = x.TraitsImplementing <HitShape>().Where(Exts.IsTraitEnabled);
                if (!activeShapes.Any())
                {
                    return(false);
                }

                var distance = activeShapes.Min(t => t.DistanceFromEdge(x, position));

                if (distance < Info.WeaponInfo.Range)
                {
                    return(true);
                }

                return(false);
            })
                                        .Shuffle(world.SharedRandom);

            var targetActor = availableTargetActors.GetEnumerator();

            var amount = Util.RandomDelay(self.World, Info.Amount);

            for (var i = 0; i < amount; i++)
            {
                var shrapnelTarget = Target.Invalid;

                if (world.SharedRandom.Next(100) < Info.AimChance && targetActor.MoveNext())
                {
                    shrapnelTarget = Target.FromActor(targetActor.Current);
                }

                if (Info.ThrowWithoutTarget && shrapnelTarget.Type == TargetType.Invalid)
                {
                    var rotation = self.Orientation;
                    if (Info.ThrowAngle == WAngle.Zero)
                    {
                        rotation = WRot.FromFacing(world.SharedRandom.Next(1024));
                    }
                    else
                    {
                        rotation.Rotate(WRot.FromYaw(Info.ThrowAngle));
                    }

                    var range     = world.SharedRandom.Next(Info.WeaponInfo.MinRange.Length, Info.WeaponInfo.Range.Length);
                    var targetpos = position + new WVec(range, 0, 0).Rotate(rotation);
                    var tpos      = Target.FromPos(new WPos(targetpos.X, targetpos.Y, world.Map.CenterOfCell(world.Map.CellContaining(targetpos)).Z));
                    if (Info.WeaponInfo.IsValidAgainst(tpos, world, self))
                    {
                        shrapnelTarget = tpos;
                    }
                }

                if (shrapnelTarget.Type == TargetType.Invalid)
                {
                    continue;
                }

                var args = new ProjectileArgs
                {
                    Weapon = Info.WeaponInfo,
                    Facing = (shrapnelTarget.CenterPosition - position).Yaw,

                    DamageModifiers = !self.IsDead ? self.TraitsImplementing <IFirepowerModifier>()
                                      .Select(a => a.GetFirepowerModifier()).ToArray() : new int[0],

                    InaccuracyModifiers = !self.IsDead ? self.TraitsImplementing <IInaccuracyModifier>()
                                          .Select(a => a.GetInaccuracyModifier()).ToArray() : new int[0],

                    RangeModifiers = !self.IsDead ? self.TraitsImplementing <IRangeModifier>()
                                     .Select(a => a.GetRangeModifier()).ToArray() : new int[0],

                    Source        = position,
                    CurrentSource = () => position,
                    SourceActor   = self,
                    GuidedTarget  = shrapnelTarget,
                    PassiveTarget = shrapnelTarget.CenterPosition
                };

                if (args.Weapon.Projectile != null)
                {
                    var projectile = args.Weapon.Projectile.Create(args);
                    if (projectile != null)
                    {
                        world.AddFrameEndTask(w => w.Add(projectile));
                    }

                    if (args.Weapon.Report != null && args.Weapon.Report.Any())
                    {
                        Game.Sound.Play(SoundType.World, args.Weapon.Report.Random(world.SharedRandom), position);
                    }
                }

                foreach (var animation in animations)
                {
                    animation.Trigger(self);
                }
            }
        }
예제 #16
0
        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);
        }
예제 #17
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);
            }

            if (blockInfo != null)
            {
                var hc     = Color.Orange;
                var height = new WVec(0, 0, blockInfo.Height.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);
            }

            // No armaments to draw
            if (attack.Value == null)
            {
                return;
            }

            var c = Color.White;

            // Fire ports on garrisonable structures
            var garrison = attack.Value as AttackGarrisoned;

            if (garrison != null)
            {
                var bodyOrientation = coords.Value.QuantizeOrientation(self, self.Orientation);
                foreach (var p in garrison.Info.Ports)
                {
                    var pos = self.CenterPosition + coords.Value.LocalToWorld(p.Offset.Rotate(bodyOrientation));
                    var da  = coords.Value.LocalToWorld(new WVec(224, 0, 0).Rotate(WRot.FromYaw(p.Yaw + p.Cone)).Rotate(bodyOrientation));
                    var db  = coords.Value.LocalToWorld(new WVec(224, 0, 0).Rotate(WRot.FromYaw(p.Yaw - p.Cone)).Rotate(bodyOrientation));

                    var o = wr.ScreenPosition(pos);
                    var a = wr.ScreenPosition(pos + da * 224 / da.Length);
                    var b = wr.ScreenPosition(pos + db * 224 / db.Length);
                    wcr.DrawLine(o, a, iz, c);
                    wcr.DrawLine(o, b, iz, c);
                }

                return;
            }

            foreach (var a in attack.Value.Armaments)
            {
                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);
                    wcr.DrawLine(sm, sd, iz, c);
                    TargetLineRenderable.DrawTargetMarker(wr, c, sm);
                }
            }
        }
예제 #18
0
 protected virtual WRot CalculateMuzzleOrientation(Actor self, Barrel b)
 {
     return(WRot.FromYaw(b.Yaw).Rotate(turret != null ? turret.WorldOrientation : self.Orientation));
 }
예제 #19
0
        public Actor[] SendAirstrike(Actor self, WPos target, WAngle?facing = null)
        {
            var aircraft = new List <Actor>();

            if (!facing.HasValue)
            {
                facing = new WAngle(1024 * self.World.SharedRandom.Next(info.QuantizedFacings) / info.QuantizedFacings);
            }

            var altitude       = self.World.Map.Rules.Actors[info.UnitTypes.First(ut => ut.Key == GetLevel()).Value].TraitInfo <AircraftInfo>().CruiseAltitude.Length;
            var attackRotation = WRot.FromYaw(facing.Value);
            var delta          = new WVec(0, -1024, 0).Rotate(attackRotation);

            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;
            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),
                        });
                    });
                }

                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 the range.
                // If at the map's edge, they may be removed from world before leaving.
                if (aircraftInRange.All(kv => !kv.Key.IsInWorld))
                {
                    RemoveCamera(camera);
                }
            };

            // Create the actors immediately so they can be returned
            var squadSize = info.SquadSizes.First(ss => ss.Key == GetLevel()).Value;

            for (var i = -squadSize / 2; i <= squadSize / 2; i++)
            {
                // Even-sized squads skip the lead plane
                if (i == 0 && (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            = self.World.CreateActor(false, info.UnitTypes.First(ut => ut.Key == GetLevel()).Value, new TypeDictionary
                {
                    new CenterPositionInit(startEdge + spawnOffset),
                    new OwnerInit(self.Owner),
                    new FacingInit(facing.Value),
                });

                aircraft.Add(a);
                aircraftInRange.Add(a, false);

                var attack = a.Trait <AttackBomber>();
                attack.SetTarget(self.World, target + targetOffset);
                attack.OnEnteredAttackRange += onEnterRange;
                attack.OnExitedAttackRange  += onExitRange;
                attack.OnRemovedFromWorld   += onRemovedFromWorld;
            }

            self.World.AddFrameEndTask(w =>
            {
                PlayLaunchSounds();

                var effect = new AirstrikePowerRVEffect(self.World, self.Owner, target, startEdge, finishEdge, attackRotation, altitude, GetLevel(), aircraft.ToArray(), this, info);
                self.World.Add(effect);
            });

            return(aircraft.ToArray());
        }
예제 #20
0
        public Pair <Actor[], Actor[]> SendParatroopers(Actor self, WPos target, WAngle?facing = null)
        {
            var aircraft = new List <Actor>();
            var units    = new List <Actor>();

            var info = Info as ParatroopersPowerInfo;

            if (!facing.HasValue)
            {
                facing = new WAngle(1024 * 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.FromYaw(facing.Value);
            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 the range.
                // If at the map's edge, they may be removed from world before leaving.
                if (aircraftInRange.All(kv => !kv.Key.IsInWorld))
                {
                    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.Value),
                }));
            }

            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()));
        }
예제 #21
0
파일: Armament.cs 프로젝트: szlatkow/OpenRA
        protected virtual void FireBarrel(Actor self, IFacing facing, Target target, Barrel barrel)
        {
            foreach (var na in notifyAttacks)
            {
                na.PreparingAttack(self, target, this, barrel);
            }

            Func <WPos>   muzzlePosition    = () => self.CenterPosition + MuzzleOffset(self, barrel);
            Func <WAngle> muzzleFacing      = () => MuzzleOrientation(self, barrel).Yaw;
            var           muzzleOrientation = WRot.FromYaw(muzzleFacing());

            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(muzzleOrientation);
            }

            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(muzzleOrientation);
            }

            var args = new ProjectileArgs
            {
                Weapon = Weapon,
                Facing = muzzleFacing(),
                CurrentMuzzleFacing = muzzleFacing,

                DamageModifiers = damageModifiers.ToArray(),

                InaccuracyModifiers = inaccuracyModifiers.ToArray(),

                RangeModifiers = rangeModifiers.ToArray(),

                Source        = muzzlePosition(),
                CurrentSource = muzzlePosition,
                SourceActor   = self,
                PassiveTarget = passiveTarget,
                GuidedTarget  = target
            };

            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, self.World, self.CenterPosition);
                    }

                    if (Burst == args.Weapon.Burst && args.Weapon.StartBurstReport != null && args.Weapon.StartBurstReport.Any())
                    {
                        Game.Sound.Play(SoundType.World, args.Weapon.StartBurstReport, self.World, self.CenterPosition);
                    }

                    foreach (var na in notifyAttacks)
                    {
                        na.Attacking(self, target, this, barrel);
                    }

                    Recoil = Info.Recoil;
                }
            });
        }
예제 #22
0
        public override bool Tick(Actor self)
        {
            // Refuse to take off if it would land immediately again.
            if (aircraft.ForceLanding)
            {
                Cancel(self);
            }

            var dat      = self.World.Map.DistanceAboveTerrain(aircraft.CenterPosition);
            var isLanded = dat <= aircraft.LandAltitude;

            // HACK: Prevent paused (for example, EMP'd) aircraft from taking off.
            // This is necessary until the TODOs in the IsCanceling block below are adressed.
            if (isLanded && aircraft.IsTraitPaused)
            {
                return(false);
            }

            if (IsCanceling)
            {
                // We must return the actor to a sensible height before continuing.
                // If the aircraft is on the ground we queue TakeOff to manage the influence reservation and takeoff sounds etc.
                // TODO: It would be better to not take off at all, but we lack the plumbing to detect current airborne/landed state.
                // If the aircraft lands when idle and is idle, we let the default idle handler manage this.
                // TODO: Remove this after fixing all activities to work properly with arbitrary starting altitudes.
                var landWhenIdle         = aircraft.Info.IdleBehavior == IdleBehaviorType.Land;
                var skipHeightAdjustment = landWhenIdle && self.CurrentActivity.IsCanceling && self.CurrentActivity.NextActivity == null;
                if (aircraft.Info.CanHover && !skipHeightAdjustment && dat != aircraft.Info.CruiseAltitude)
                {
                    if (isLanded)
                    {
                        QueueChild(new TakeOff(self));
                    }
                    else
                    {
                        VerticalTakeOffOrLandTick(self, aircraft, aircraft.Facing, aircraft.Info.CruiseAltitude);
                    }

                    return(false);
                }

                return(true);
            }
            else if (isLanded)
            {
                QueueChild(new TakeOff(self));
                return(false);
            }

            target = target.Recalculate(self.Owner, out var targetIsHiddenActor);
            if (!targetIsHiddenActor && target.Type == TargetType.Actor)
            {
                lastVisibleTarget = Target.FromTargetPositions(target);
            }

            useLastVisibleTarget = targetIsHiddenActor || !target.IsValidFor(self);

            // Target is hidden or dead, and we don't have a fallback position to move towards
            if (useLastVisibleTarget && !lastVisibleTarget.IsValidFor(self))
            {
                return(true);
            }

            var checkTarget = useLastVisibleTarget ? lastVisibleTarget : target;
            var pos         = aircraft.GetPosition();
            var delta       = checkTarget.CenterPosition - pos;

            // Inside the target annulus, so we're done
            var insideMaxRange = maxRange.Length > 0 && checkTarget.IsInRange(pos, maxRange);
            var insideMinRange = minRange.Length > 0 && checkTarget.IsInRange(pos, minRange);

            if (insideMaxRange && !insideMinRange)
            {
                return(true);
            }

            var isSlider      = aircraft.Info.CanSlide;
            var desiredFacing = delta.HorizontalLengthSquared != 0 ? delta.Yaw : aircraft.Facing;
            var move          = isSlider ? aircraft.FlyStep(desiredFacing) : aircraft.FlyStep(aircraft.Facing);

            // Inside the minimum range, so reverse if we CanSlide, otherwise face away from the target.
            if (insideMinRange)
            {
                if (isSlider)
                {
                    FlyTick(self, aircraft, desiredFacing, aircraft.Info.CruiseAltitude, -move);
                }
                else
                {
                    FlyTick(self, aircraft, desiredFacing + new WAngle(512), aircraft.Info.CruiseAltitude, move);
                }

                return(false);
            }

            // HACK: Consider ourselves blocked if we have moved by less than 64 WDist in the last five ticks
            // Stop if we are blocked and close enough
            if (positionBuffer.Count >= 5 && (positionBuffer.Last() - positionBuffer[0]).LengthSquared < 4096 &&
                delta.HorizontalLengthSquared <= nearEnough.LengthSquared)
            {
                return(true);
            }

            // The next move would overshoot, so consider it close enough or set final position if we CanSlide
            if (delta.HorizontalLengthSquared < move.HorizontalLengthSquared)
            {
                // For VTOL landing to succeed, it must reach the exact target position,
                // so for the final move it needs to behave as if it had CanSlide.
                if (isSlider || aircraft.Info.VTOL)
                {
                    // Set final (horizontal) position
                    if (delta.HorizontalLengthSquared != 0)
                    {
                        // Ensure we don't include a non-zero vertical component here that would move us away from CruiseAltitude
                        var deltaMove = new WVec(delta.X, delta.Y, 0);
                        FlyTick(self, aircraft, desiredFacing, dat, deltaMove);
                    }

                    // Move to CruiseAltitude, if not already there
                    if (dat != aircraft.Info.CruiseAltitude)
                    {
                        Fly.VerticalTakeOffOrLandTick(self, aircraft, aircraft.Facing, aircraft.Info.CruiseAltitude);
                        return(false);
                    }
                }

                return(true);
            }

            if (!isSlider)
            {
                // Using the turn rate, compute a hypothetical circle traced by a continuous turn.
                // If it contains the destination point, it's unreachable without more complex manuvering.
                var turnRadius = CalculateTurnRadius(aircraft.MovementSpeed, aircraft.TurnSpeed);

                // The current facing is a tangent of the minimal turn circle.
                // Make a perpendicular vector, and use it to locate the turn's center.
                var turnCenterFacing = aircraft.Facing + new WAngle(Util.GetTurnDirection(aircraft.Facing, desiredFacing) * 256);

                var turnCenterDir = new WVec(0, -1024, 0).Rotate(WRot.FromYaw(turnCenterFacing));
                turnCenterDir *= turnRadius;
                turnCenterDir /= 1024;

                // Compare with the target point, and keep flying away if it's inside the circle.
                var turnCenter = aircraft.CenterPosition + turnCenterDir;
                if ((checkTarget.CenterPosition - turnCenter).HorizontalLengthSquared < turnRadius * turnRadius)
                {
                    desiredFacing = aircraft.Facing;
                }
            }

            positionBuffer.Add(self.CenterPosition);
            if (positionBuffer.Count > 5)
            {
                positionBuffer.RemoveAt(0);
            }

            FlyTick(self, aircraft, desiredFacing, aircraft.Info.CruiseAltitude);

            return(false);
        }
예제 #23
0
        public override bool Tick(Actor self)
        {
            if (IsCanceling || target.Type == TargetType.Invalid)
            {
                if (landingInitiated)
                {
                    // We must return the actor to a sensible height before continuing.
                    // If the aircraft lands when idle and is idle, continue landing,
                    // otherwise climb back to CruiseAltitude.
                    // TODO: Remove this after fixing all activities to work properly with arbitrary starting altitudes.
                    var shouldLand      = aircraft.Info.IdleBehavior == IdleBehaviorType.Land;
                    var continueLanding = shouldLand && self.CurrentActivity.IsCanceling && self.CurrentActivity.NextActivity == null;
                    if (!continueLanding)
                    {
                        var dat = self.World.Map.DistanceAboveTerrain(aircraft.CenterPosition);
                        if (dat > aircraft.LandAltitude && dat < aircraft.Info.CruiseAltitude)
                        {
                            QueueChild(new TakeOff(self));
                            return(false);
                        }

                        aircraft.RemoveInfluence();
                        return(true);
                    }
                }
                else
                {
                    return(true);
                }
            }

            var pos = aircraft.GetPosition();

            // Reevaluate target position in case the target has moved.
            targetPosition = target.CenterPosition + offset;
            landingCell    = self.World.Map.CellContaining(targetPosition);

            // We are already at the landing location.
            if ((targetPosition - pos).LengthSquared == 0)
            {
                return(true);
            }

            // Look for free landing cell
            if (target.Type == TargetType.Terrain && !landingInitiated)
            {
                var newLocation = aircraft.FindLandingLocation(landingCell, landRange);

                // Cannot land so fly towards the last target location instead.
                if (!newLocation.HasValue)
                {
                    QueueChild(aircraft.MoveTo(landingCell, 0));
                    return(true);
                }

                if (newLocation.Value != landingCell)
                {
                    target         = Target.FromCell(self.World, newLocation.Value);
                    targetPosition = target.CenterPosition + offset;
                    landingCell    = self.World.Map.CellContaining(targetPosition);
                }
            }

            // Move towards landing location/facing
            if (aircraft.Info.VTOL)
            {
                if ((pos - targetPosition).HorizontalLengthSquared != 0)
                {
                    QueueChild(new Fly(self, Target.FromPos(targetPosition)));
                    return(false);
                }

                if (desiredFacing.HasValue && desiredFacing.Value != aircraft.Facing)
                {
                    QueueChild(new Turn(self, desiredFacing.Value));
                    return(false);
                }
            }

            if (!aircraft.Info.VTOL && !finishedApproach)
            {
                // Calculate approach trajectory
                var altitude = aircraft.Info.CruiseAltitude.Length;

                // Distance required for descent.
                var landDistance = altitude * 1024 / aircraft.Info.MaximumPitch.Tan();

                // Approach landing from the opposite direction of the desired facing
                // TODO: Calculate sensible trajectory without preferred facing.
                var rotation = WRot.Zero;
                if (desiredFacing.HasValue)
                {
                    rotation = WRot.FromYaw(desiredFacing.Value);
                }

                var approachStart = targetPosition + new WVec(0, landDistance, altitude).Rotate(rotation);

                // Add 10% to the turning radius to ensure we have enough room
                var speed      = aircraft.MovementSpeed * 32 / 35;
                var turnRadius = Fly.CalculateTurnRadius(speed, aircraft.TurnSpeed);

                // Find the center of the turning circles for clockwise and counterclockwise turns
                var angle = aircraft.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 tangentLength    = tangentDirection.Length;
                var tangentOffset    = WVec.Zero;
                if (tangentLength != 0)
                {
                    tangentOffset = new WVec(-tangentDirection.Y, tangentDirection.X, 0) * turnRadius / tangentLength;
                }

                // TODO: correctly handle CCW <-> CW turns
                if (tangentOffset.X > 0)
                {
                    tangentOffset = -tangentOffset;
                }

                var w1 = posCenter + tangentOffset;
                var w2 = approachCenter + tangentOffset;
                var w3 = approachStart;

                turnRadius = Fly.CalculateTurnRadius(aircraft.Info.Speed, aircraft.TurnSpeed);

                // Move along approach trajectory.
                QueueChild(new Fly(self, Target.FromPos(w1), WDist.Zero, new WDist(turnRadius * 3)));
                QueueChild(new Fly(self, Target.FromPos(w2)));

                // Fix a problem when the airplane is sent to land near the landing cell
                QueueChild(new Fly(self, Target.FromPos(w3), WDist.Zero, new WDist(turnRadius / 2)));
                finishedApproach = true;
                return(false);
            }

            if (!landingInitiated)
            {
                var blockingCells = clearCells.Append(landingCell);

                if (!aircraft.CanLand(blockingCells, target.Actor))
                {
                    // Maintain holding pattern.
                    QueueChild(new FlyIdle(self, 25));

                    self.NotifyBlocker(blockingCells);
                    finishedApproach = false;
                    return(false);
                }

                if (aircraft.Info.LandingSounds.Length > 0)
                {
                    Game.Sound.Play(SoundType.World, aircraft.Info.LandingSounds, self.World, aircraft.CenterPosition);
                }

                aircraft.AddInfluence(landingCell);
                aircraft.EnteringCell(self);
                landingInitiated = true;
            }

            // Final descent.
            if (aircraft.Info.VTOL)
            {
                var landAltitude = self.World.Map.DistanceAboveTerrain(targetPosition) + aircraft.LandAltitude;
                if (Fly.VerticalTakeOffOrLandTick(self, aircraft, aircraft.Facing, landAltitude))
                {
                    return(false);
                }

                return(true);
            }

            var d = targetPosition - pos;

            // The next move would overshoot, so just set the final position
            var move = aircraft.FlyStep(aircraft.Facing);

            if (d.HorizontalLengthSquared < move.HorizontalLengthSquared)
            {
                var landingAltVec = new WVec(WDist.Zero, WDist.Zero, aircraft.LandAltitude);
                aircraft.SetPosition(self, targetPosition + landingAltVec);
                return(true);
            }

            var landingAlt = self.World.Map.DistanceAboveTerrain(targetPosition) + aircraft.LandAltitude;

            Fly.FlyTick(self, aircraft, d.Yaw, landingAlt);

            return(false);
        }
예제 #24
0
        public void SendDropPods(Actor self, Order order, WAngle facing)
        {
            var actorInfo        = self.World.Map.Rules.Actors[info.UnitTypes.First().ToLowerInvariant()];
            var aircraftInfo     = actorInfo.TraitInfo <AircraftInfo>();
            var altitude         = aircraftInfo.CruiseAltitude.Length;
            var approachRotation = WRot.FromYaw(facing);
            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(facing)
                    });

                    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);
                    }
                }
            });
        }
예제 #25
0
 // 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));
 }
예제 #26
0
        public override bool Produce(Actor self, ActorInfo producee, string productionType, TypeDictionary inits, int refundableValue)
        {
            if (IsTraitDisabled || IsTraitPaused)
            {
                return(false);
            }

            var info         = (ProductionAirdropASInfo)Info;
            var owner        = self.Owner;
            var map          = owner.World.Map;
            var aircraftInfo = self.World.Map.Rules.Actors[info.ActorType].TraitInfo <AircraftInfo>();

            CPos   startPos;
            CPos   endPos;
            WAngle spawnFacing;

            if (info.BaselineSpawn)
            {
                var bounds   = map.Bounds;
                var center   = new MPos(bounds.Left + bounds.Width / 2, bounds.Top + bounds.Height / 2).ToCPos(map);
                var spawnVec = owner.HomeLocation - center;
                startPos = owner.HomeLocation + spawnVec * (Exts.ISqrt((bounds.Height * bounds.Height + bounds.Width * bounds.Width) / (4 * spawnVec.LengthSquared)));
                endPos   = startPos;
                var spawnDirection = new WVec((self.Location - startPos).X, (self.Location - startPos).Y, 0);
                spawnFacing = spawnDirection.Yaw;
            }
            else
            {
                // Start a fixed distance away: the width of the map.
                // This makes the production timing independent of spawnpoint
                var rotation = WRot.FromYaw(info.Facing);
                var distance = new WVec(0, map.Bounds.Width * -1024, 0).Rotate(rotation);
                startPos    = self.World.Map.CellContaining(self.CenterPosition - distance);
                endPos      = self.World.Map.CellContaining(self.CenterPosition + distance);
                spawnFacing = info.Facing;
            }

            // Assume a single exit point for simplicity
            var exit = self.Info.TraitInfos <ExitInfo>().First();

            foreach (var tower in self.TraitsImplementing <INotifyDelivery>())
            {
                tower.IncomingDelivery(self);
            }

            owner.World.AddFrameEndTask(w =>
            {
                if (!self.IsInWorld || self.IsDead)
                {
                    return;
                }

                var actor = w.CreateActor(info.ActorType, new TypeDictionary
                {
                    new CenterPositionInit(w.Map.CenterOfCell(startPos) + new WVec(WDist.Zero, WDist.Zero, aircraftInfo.CruiseAltitude)),
                    new OwnerInit(owner),
                    new FacingInit(spawnFacing)
                });

                var exitCell = self.Location + exit.ExitCell;
                actor.QueueActivity(new Land(actor, Target.FromActor(self), WDist.Zero, WVec.Zero, clearCells: new CPos[1] {
                    exitCell
                }));
                actor.QueueActivity(new CallFunc(() =>
                {
                    if (!self.IsInWorld || self.IsDead)
                    {
                        owner.PlayerActor.Trait <PlayerResources>().GiveCash(refundableValue);
                        return;
                    }

                    foreach (var cargo in self.TraitsImplementing <INotifyDelivery>())
                    {
                        cargo.Delivered(self);
                    }

                    self.World.AddFrameEndTask(ww => DoProduction(self, producee, exit, productionType, inits));
                    Game.Sound.PlayNotification(self.World.Map.Rules, self.Owner, "Speech", info.ReadyAudio, self.Owner.Faction.InternalName);
                }));

                actor.QueueActivity(new FlyOffMap(actor, Target.FromCell(w, endPos)));
                actor.QueueActivity(new RemoveSelf());
            });

            return(true);
        }
예제 #27
0
        WVec MoveStep(int speed, WAngle facing)
        {
            var dir = new WVec(0, -1024, 0).Rotate(WRot.FromYaw(facing));

            return(speed * dir / 1024);
        }
예제 #28
0
        public new IEnumerable <IActorPreview> RenderPreviewSprites(ActorPreviewInitializer init, string image, int facings, PaletteReference p)
        {
            if (!this.EnabledByDefault)
            {
                yield break;
            }

            var body         = init.Actor.TraitInfoOrDefault <BodyOrientationInfo>();
            var turretedInfo = init.Actor.TraitInfos <TurretedInfo>().FirstOrDefault(tt => tt.Turret == this.Turret);

            if (turretedInfo == null)
            {
                yield break;
            }

            var facing = init.GetFacing();
            var offset = new Func <WVec>(() => body.LocalToWorld(turretedInfo.Offset.Rotate(body.QuantizeOrientation(WRot.FromYaw(facing()), facings))));

            var bodyAnim = new Animation(init.World, image, init.GetFacing());

            bodyAnim.PlayRepeating(RenderSprites.NormalizeSequence(bodyAnim, init.GetDamageState(), "idle"));

            if (bodyAnim.CurrentSequence is OffsetsSpriteSequence bodySequence && bodySequence.EmbeddedOffsets.TryGetValue(bodyAnim.Image, out var imageOffset))
            {
                var point = imageOffset.FirstOrDefault(p1 => p1.Id == 0);

                if (point != null)
                {
                    offset = () => new(point.X * 32, point.Y * 32, 0);
                }
            }

            if (this.IsPlayerPalette)
            {
                p = init.WorldRenderer.Palette(this.Palette + init.Get <OwnerInit>().InternalName);
            }
            else if (this.Palette != null)
            {
                p = init.WorldRenderer.Palette(this.Palette);
            }

            var turretFacing = turretedInfo.WorldFacingFromInit(init);
            var anim         = new Animation(init.World, image, turretFacing);

            anim.Play(RenderSprites.NormalizeSequence(anim, init.GetDamageState(), this.Sequence));

            yield return(new SpriteActorPreview(
                             anim,
                             offset,
                             () =>
            {
                var tmpOffset = offset();

                return -(tmpOffset.Y + tmpOffset.Z) + 1;
            },
                             p
                             ));
        }