Example #1
0
		public List<CPos> FindUnitPathToRange(CPos source, SubCell srcSub, WPos target, WDist range, Actor self)
		{
			var mi = self.Info.TraitInfo<MobileInfo>();
			var targetCell = world.Map.CellContaining(target);

			// Correct for SubCell offset
			target -= world.Map.OffsetOfSubCell(srcSub);

			// Select only the tiles that are within range from the requested SubCell
			// This assumes that the SubCell does not change during the path traversal
			var tilesInRange = world.Map.FindTilesInCircle(targetCell, range.Length / 1024 + 1)
				.Where(t => (world.Map.CenterOfCell(t) - target).LengthSquared <= range.LengthSquared
							&& mi.CanEnterCell(self.World, self, t));

			// See if there is any cell within range that does not involve a cross-domain request
			// Really, we only need to check the circle perimeter, but it's not clear that would be a performance win
			var domainIndex = world.WorldActor.TraitOrDefault<DomainIndex>();
			if (domainIndex != null)
			{
				var passable = mi.GetMovementClass(world.TileSet);
				tilesInRange = new List<CPos>(tilesInRange.Where(t => domainIndex.IsPassable(source, t, (uint)passable)));
				if (!tilesInRange.Any())
					return EmptyPath;
			}

			using (var fromSrc = PathSearch.FromPoints(world, mi, self, tilesInRange, source, true))
			using (var fromDest = PathSearch.FromPoint(world, mi, self, source, targetCell, true).Reverse())
				return FindBidiPath(fromSrc, fromDest);
		}
Example #2
0
 public UpgradeActorsNear(Actor self, UpgradeActorsNearInfo info)
 {
     this.info = info;
     this.self = self;
     cachedRange = info.Range;
     cachedVRange = info.MaximumVerticalOffset;
 }
Example #3
0
        public NukeLaunch(Player firedBy, string weapon, WPos launchPos, WPos targetPos, WDist velocity, int delay, bool skipAscent, string flashType)
        {
            this.firedBy = firedBy;
            this.weapon = weapon;
            this.delay = delay;
            this.turn = delay / 2;
            this.flashType = flashType;

            var offset = new WVec(WDist.Zero, WDist.Zero, velocity * turn);
            ascendSource = launchPos;
            ascendTarget = launchPos + offset;
            descendSource = targetPos + offset;
            descendTarget = targetPos;

            anim = new Animation(firedBy.World, weapon);
            anim.PlayRepeating("up");

            pos = launchPos;
            var weaponRules = firedBy.World.Map.Rules.Weapons[weapon.ToLowerInvariant()];
            if (weaponRules.Report != null && weaponRules.Report.Any())
                Sound.Play(weaponRules.Report.Random(firedBy.World.SharedRandom), pos);

            if (skipAscent)
                ticks = turn;
        }
Example #4
0
        /// <summary>
        /// Finds all the actors of which their health radius is intersected by a line (with a definable width) between two points.
        /// </summary>
        /// <param name="world">The engine world the line intersection is to be done in.</param>
        /// <param name="lineStart">The position the line should start at</param>
        /// <param name="lineEnd">The position the line should end at</param>
        /// <param name="lineWidth">How close an actor's health radius needs to be to the line to be considered 'intersected' by the line</param>
        /// <returns>A list of all the actors intersected by the line</returns>
        public static IEnumerable<Actor> FindActorsOnLine(this World world, WPos lineStart, WPos lineEnd, WDist lineWidth, WDist targetExtraSearchRadius)
        {
            // This line intersection check is done by first just finding all actors within a square that starts at the source, and ends at the target.
            // Then we iterate over this list, and find all actors for which their health radius is at least within lineWidth of the line.
            // For actors without a health radius, we simply check their center point.
            // The square in which we select all actors must be large enough to encompass the entire line's width.
            var xDir = Math.Sign(lineEnd.X - lineStart.X);
            var yDir = Math.Sign(lineEnd.Y - lineStart.Y);

            var dir = new WVec(xDir, yDir, 0);
            var overselect = dir * (1024 + lineWidth.Length + targetExtraSearchRadius.Length);
            var finalTarget = lineEnd + overselect;
            var finalSource = lineStart - overselect;

            var actorsInSquare = world.ActorMap.ActorsInBox(finalTarget, finalSource);
            var intersectedActors = new List<Actor>();

            foreach (var currActor in actorsInSquare)
            {
                var actorWidth = 0;
                var healthInfo = currActor.Info.TraitInfoOrDefault<HealthInfo>();
                if (healthInfo != null)
                    actorWidth = healthInfo.Shape.OuterRadius.Length;

                var projection = MinimumPointLineProjection(lineStart, lineEnd, currActor.CenterPosition);
                var distance = (currActor.CenterPosition - projection).HorizontalLength;
                var maxReach = actorWidth + lineWidth.Length;

                if (distance <= maxReach)
                    intersectedActors.Add(currActor);
            }

            return intersectedActors;
        }
Example #5
0
        public NukeLaunch(Player firedBy, string name, WeaponInfo weapon, string weaponPalette, string upSequence, string downSequence,
			WPos launchPos, WPos targetPos, WDist velocity, int delay, bool skipAscent, string flashType)
        {
            this.firedBy = firedBy;
            this.weapon = weapon;
            this.weaponPalette = weaponPalette;
            this.downSequence = downSequence;
            this.delay = delay;
            turn = delay / 2;
            this.flashType = flashType;

            var offset = new WVec(WDist.Zero, WDist.Zero, velocity * turn);
            ascendSource = launchPos;
            ascendTarget = launchPos + offset;
            descendSource = targetPos + offset;
            descendTarget = targetPos;

            anim = new Animation(firedBy.World, name);
            anim.PlayRepeating(upSequence);

            pos = launchPos;
            if (weapon.Report != null && weapon.Report.Any())
                Game.Sound.Play(weapon.Report.Random(firedBy.World.SharedRandom), pos);

            if (skipAscent)
                ticks = turn;
        }
Example #6
0
 public Repair(Actor self, Actor host, WDist closeEnough)
 {
     this.host = Target.FromActor(host);
     this.closeEnough = closeEnough;
     repairsUnits = host.Info.TraitInfo<RepairsUnitsInfo>();
     health = self.TraitOrDefault<Health>();
 }
Example #7
0
        public AreaBeam(AreaBeamInfo info, ProjectileArgs args, Color color)
        {
            this.info = info;
            this.args = args;
            this.color = color;
            actorAttackBase = args.SourceActor.Trait<AttackBase>();

            var world = args.SourceActor.World;
            if (info.Speed.Length > 1)
                speed = new WDist(world.SharedRandom.Next(info.Speed[0].Length, info.Speed[1].Length));
            else
                speed = info.Speed[0];

            // Both the head and tail start at the source actor, but initially only the head is travelling.
            headPos = args.Source;
            tailPos = headPos;

            target = args.PassiveTarget;
            if (info.Inaccuracy.Length > 0)
            {
                var inaccuracy = Util.ApplyPercentageModifiers(info.Inaccuracy.Length, args.InaccuracyModifiers);
                var maxOffset = inaccuracy * (target - headPos).Length / args.Weapon.Range.Length;
                target += WVec.FromPDF(world.SharedRandom, 2) * maxOffset / 1024;
            }

            towardsTargetFacing = (target - headPos).Yaw.Facing;

            // Update the target position with the range we shoot beyond the target by
            // I.e. we can deliberately overshoot, so aim for that position
            var dir = new WVec(0, -1024, 0).Rotate(WRot.FromFacing(towardsTargetFacing));
            target += dir * info.BeyondTargetRange.Length / 1024;

            length = Math.Max((target - headPos).Length / speed.Length, 1);
        }
 public ProximityExternalCondition(Actor self, ProximityExternalConditionInfo info)
 {
     this.info = info;
     this.self = self;
     cachedRange = info.Range;
     cachedVRange = info.MaximumVerticalOffset;
 }
Example #9
0
		public FlyFollow(Actor self, Target target, WDist minRange, WDist maxRange)
		{
			this.target = target;
			plane = self.Trait<Aircraft>();
			this.minRange = minRange;
			this.maxRange = maxRange;
		}
Example #10
0
		public static ImpactType GetImpactType(World world, CPos cell, WPos pos)
		{
			// Missiles need a margin because they sometimes explode a little above ground
			// due to their explosion check triggering slightly too early (because of CloseEnough).
			// TODO: Base ImpactType on target altitude instead of explosion altitude.
			var airMargin = new WDist(128);

			var dat = world.Map.DistanceAboveTerrain(pos);
			var isAir = dat.Length > airMargin.Length;
			var isWater = dat.Length <= 0 && world.Map.GetTerrainInfo(cell).IsWater;
			var isDirectHit = GetDirectHit(world, cell, pos);

			if (isAir && !isDirectHit)
				return ImpactType.Air;
			else if (isWater && !isDirectHit)
				return ImpactType.Water;
			else if (isAir && isDirectHit)
				return ImpactType.AirHit;
			else if (isWater && isDirectHit)
				return ImpactType.WaterHit;
			else if (isDirectHit)
				return ImpactType.GroundHit;

			return ImpactType.Ground;
		}
Example #11
0
		protected bool TryGetAlternateTargetInCircle(
			Actor self, WDist radius, Action<Target> update, Func<Actor, bool> primaryFilter, Func<Actor, bool>[] preferenceFilters = null)
		{
			var diff = new WVec(radius, radius, WDist.Zero);
			var candidates = self.World.ActorMap.ActorsInBox(self.CenterPosition - diff, self.CenterPosition + diff)
				.Where(primaryFilter).Select(a => new { Actor = a, Ls = (self.CenterPosition - a.CenterPosition).HorizontalLengthSquared })
				.Where(p => p.Ls <= radius.LengthSquared).OrderBy(p => p.Ls).Select(p => p.Actor);
			if (preferenceFilters != null)
				foreach (var filter in preferenceFilters)
				{
					var preferredCandidate = candidates.FirstOrDefault(filter);
					if (preferredCandidate == null)
						continue;
					target = Target.FromActor(preferredCandidate);
					update(target);
					return true;
				}

			var candidate = candidates.FirstOrDefault();
			if (candidate == null)
				return false;
			target = Target.FromActor(candidate);
			update(target);
			return true;
		}
Example #12
0
		public Follow(Actor self, Target target, WDist minRange, WDist maxRange)
		{
			this.target = target;
			this.minRange = minRange;
			this.maxRange = maxRange;

			move = self.Trait<IMove>();
		}
		public ProximityBounty(Actor self, ProximityBountyInfo info)
		{
			this.Info = info;
			this.self = self;
			cachedRange = info.Range;
			cachedVRange = info.MaximumVerticalOffset;
			ticks = Info.Delay;
		}
Example #14
0
		public RangeCircleRenderable(WPos centerPosition, WDist radius, int zOffset, Color color, Color contrastColor)
		{
			this.centerPosition = centerPosition;
			this.radius = radius;
			this.zOffset = zOffset;
			this.color = color;
			this.contrastColor = contrastColor;
		}
Example #15
0
 public BeamRenderable(WPos pos, int zOffset, WVec length, BeamRenderableShape shape, WDist width, Color color)
 {
     this.pos = pos;
     this.zOffset = zOffset;
     this.length = length;
     this.shape = shape;
     this.width = width;
     this.color = color;
 }
Example #16
0
 ContrailRenderable(World world, WPos[] trail, WDist width, int next, int length, int skip, Color color, int zOffset)
 {
     this.world = world;
     this.trail = trail;
     this.width = width;
     this.next = next;
     this.length = length;
     this.skip = skip;
     this.color = color;
     this.zOffset = zOffset;
 }
Example #17
0
        public static bool AdjustAltitude(Actor self, Helicopter helicopter, WDist targetAltitude)
        {
            var altitude = helicopter.CenterPosition.Z;
            if (altitude == targetAltitude.Length)
                return false;

            var delta = helicopter.Info.AltitudeVelocity.Length;
            var dz = (targetAltitude.Length - altitude).Clamp(-delta, delta);
            helicopter.SetPosition(self, helicopter.CenterPosition + new WVec(0, 0, dz));

            return true;
        }
        public DetectionCircleRenderable(WPos centerPosition, WDist radius, int zOffset,
			int lineTrails, WAngle trailSeparation, WAngle trailAngle, Color color, Color contrastColor)
        {
            this.centerPosition = centerPosition;
            this.radius = radius;
            this.zOffset = zOffset;
            trailCount = lineTrails;
            this.trailSeparation = trailSeparation;
            this.trailAngle = trailAngle;
            this.color = color;
            this.contrastColor = contrastColor;
        }
Example #19
0
        public Attack(Actor self, Target target, WDist minRange, WDist maxRange, bool allowMovement)
        {
            Target = target;
            this.minRange = minRange;
            this.maxRange = maxRange;

            attack = self.Trait<AttackBase>();
            facing = self.Trait<IFacing>();
            positionable = self.Trait<IPositionable>();

            move = allowMovement ? self.TraitOrDefault<IMove>() : null;
        }
Example #20
0
        public static bool AdjustAltitude(Actor self, Aircraft helicopter, WDist targetAltitude)
        {
            targetAltitude = new WDist(helicopter.CenterPosition.Z) + targetAltitude - self.World.Map.DistanceAboveTerrain(helicopter.CenterPosition);

            var altitude = helicopter.CenterPosition.Z;
            if (altitude == targetAltitude.Length)
                return false;

            var delta = helicopter.Info.AltitudeVelocity.Length;
            var dz = (targetAltitude.Length - altitude).Clamp(-delta, delta);
            helicopter.SetPosition(self, helicopter.CenterPosition + new WVec(0, 0, dz));

            return true;
        }
Example #21
0
		public static IEnumerable<PPos> ProjectedCellsInRange(Map map, WPos pos, WDist range)
		{
			// Account for potential extra half-cell from odd-height terrain
			var r = (range.Length + 1023 + 512) / 1024;
			var limit = range.LengthSquared;

			// Project actor position into the shroud plane
			var projectedPos = pos - new WVec(0, pos.Z, pos.Z);
			var projectedCell = map.CellContaining(projectedPos);

			foreach (var c in map.FindTilesInCircle(projectedCell, r, true))
				if ((map.CenterOfCell(c) - projectedPos).HorizontalLengthSquared <= limit)
					yield return (PPos)c.ToMPos(map);
		}
Example #22
0
        public Missile(MissileInfo info, ProjectileArgs args)
        {
            this.info = info;
            this.args = args;

            pos = args.Source;
            hFacing = args.Facing;
            gravity = new WVec(0, 0, -info.Gravity);
            targetPosition = args.PassiveTarget;
            rangeLimit = info.RangeLimit != WDist.Zero ? info.RangeLimit : args.Weapon.Range;
            minLaunchSpeed = info.MinimumLaunchSpeed.Length > -1 ? info.MinimumLaunchSpeed.Length : info.Speed.Length;
            maxLaunchSpeed = info.MaximumLaunchSpeed.Length > -1 ? info.MaximumLaunchSpeed.Length : info.Speed.Length;
            maxSpeed = info.Speed.Length;
            minLaunchAngle = info.MinimumLaunchAngle;
            maxLaunchAngle = info.MaximumLaunchAngle;

            var world = args.SourceActor.World;

            if (info.Inaccuracy.Length > 0)
            {
                var inaccuracy = Util.ApplyPercentageModifiers(info.Inaccuracy.Length, args.InaccuracyModifiers);
                offset = WVec.FromPDF(world.SharedRandom, 2) * inaccuracy / 1024;
            }

            DetermineLaunchSpeedAndAngle(world, out speed, out vFacing);

            velocity = new WVec(0, -speed, 0)
                .Rotate(new WRot(WAngle.FromFacing(vFacing), WAngle.Zero, WAngle.Zero))
                .Rotate(new WRot(WAngle.Zero, WAngle.Zero, WAngle.FromFacing(hFacing)));

            if (world.SharedRandom.Next(100) <= info.LockOnProbability)
                lockOn = true;

            if (!string.IsNullOrEmpty(info.Image))
            {
                anim = new Animation(world, info.Image, () => renderFacing);
                anim.PlayRepeating(info.Sequences.Random(world.SharedRandom));
            }

            if (info.ContrailLength > 0)
            {
                var color = info.ContrailUsePlayerColor ? ContrailRenderable.ChooseColor(args.SourceActor) : info.ContrailColor;
                contrail = new ContrailRenderable(world, color, info.ContrailWidth, info.ContrailLength, info.ContrailDelay, info.ContrailZOffset);
            }

            trailPalette = info.TrailPalette;
            if (info.TrailUsePlayerPalette)
                trailPalette += args.SourceActor.Owner.InternalName;
        }
Example #23
0
		public Actor[] ActorsInCircle(WPos location, WDist radius, LuaFunction filter = null)
		{
			var actors = Context.World.FindActorsInCircle(location, radius);

			if (filter != null)
			{
				actors = actors.Where(a =>
				{
					using (var f = filter.Call(a.ToLuaValue(Context)))
						return f.First().ToBoolean();
				});
			}

			return actors.ToArray();
		}
Example #24
0
        public Bullet(BulletInfo info, ProjectileArgs args)
        {
            this.info = info;
            this.args = args;
            pos = args.Source;

            var world = args.SourceActor.World;

            if (info.LaunchAngle.Length > 1)
                angle = new WAngle(world.SharedRandom.Next(info.LaunchAngle[0].Angle, info.LaunchAngle[1].Angle));
            else
                angle = info.LaunchAngle[0];

            if (info.Speed.Length > 1)
                speed = new WDist(world.SharedRandom.Next(info.Speed[0].Length, info.Speed[1].Length));
            else
                speed = info.Speed[0];

            target = args.PassiveTarget;
            if (info.Inaccuracy.Length > 0)
            {
                var inaccuracy = Util.ApplyPercentageModifiers(info.Inaccuracy.Length, args.InaccuracyModifiers);
                var range = Util.ApplyPercentageModifiers(args.Weapon.Range.Length, args.RangeModifiers);
                var maxOffset = inaccuracy * (target - pos).Length / range;
                target += WVec.FromPDF(world.SharedRandom, 2) * maxOffset / 1024;
            }

            facing = (target - pos).Yaw.Facing;
            length = Math.Max((target - pos).Length / speed.Length, 1);

            if (!string.IsNullOrEmpty(info.Image))
            {
                anim = new Animation(world, info.Image, new Func<int>(GetEffectiveFacing));
                anim.PlayRepeating(info.Sequences.Random(world.SharedRandom));
            }

            if (info.ContrailLength > 0)
            {
                var color = info.ContrailUsePlayerColor ? ContrailRenderable.ChooseColor(args.SourceActor) : info.ContrailColor;
                contrail = new ContrailRenderable(world, color, info.ContrailWidth, info.ContrailLength, info.ContrailDelay, info.ContrailZOffset);
            }

            trailPalette = info.TrailPalette;
            if (info.TrailUsePlayerPalette)
                trailPalette += args.SourceActor.Owner.InternalName;

            smokeTicks = info.TrailDelay;
        }
Example #25
0
        public static void FlyToward(Actor self, Plane plane, int desiredFacing, WDist desiredAltitude)
        {
            var move = plane.FlyStep(plane.Facing);
            var altitude = plane.CenterPosition.Z;

            plane.Facing = Util.TickFacing(plane.Facing, desiredFacing, plane.ROT);

            if (altitude != desiredAltitude.Length)
            {
                var delta = move.HorizontalLength * plane.Info.MaximumPitch.Tan() / 1024;
                var dz = (desiredAltitude.Length - altitude).Clamp(-delta, delta);
                move += new WVec(0, 0, dz);
            }

            plane.SetPosition(self, plane.CenterPosition + move);
        }
		public List<CPos> FindUnitPathToRange(CPos source, SubCell srcSub, WPos target, WDist range, Actor self)
		{
			using (new PerfSample("Pathfinder"))
			{
				var key = "FindUnitPathToRange" + self.ActorID + source.X + source.Y + target.X + target.Y;
				var cachedPath = cacheStorage.Retrieve(key);

				if (cachedPath != null)
					return cachedPath;

				var pb = pathFinder.FindUnitPathToRange(source, srcSub, target, range, self);

				cacheStorage.Store(key, pb);

				return pb;
			}
		}
Example #27
0
        public static void DrawRangeCircle(WorldRenderer wr, WPos centerPosition, WDist radius,
			float width, Color color, float contrastWidth, Color contrastColor)
        {
            var wcr = Game.Renderer.WorldRgbaColorRenderer;
            var offset = new WVec(radius.Length, 0, 0);
            for (var i = 0; i < RangeCircleSegments; i++)
            {
                var a = wr.ScreenPosition(centerPosition + offset.Rotate(RangeCircleStartRotations[i]));
                var b = wr.ScreenPosition(centerPosition + offset.Rotate(RangeCircleEndRotations[i]));

                if (contrastWidth > 0)
                    wcr.DrawLine(a, b, contrastWidth / wr.Viewport.Zoom, contrastColor);

                if (width > 0)
                    wcr.DrawLine(a, b, width / wr.Viewport.Zoom, color);
            }
        }
Example #28
0
File: Fly.cs Project: pchote/OpenRA
        public static void FlyToward(Actor self, Aircraft plane, int desiredFacing, WDist desiredAltitude)
        {
            desiredAltitude = new WDist(plane.CenterPosition.Z) + desiredAltitude - self.World.Map.DistanceAboveTerrain(plane.CenterPosition);

            var move = plane.FlyStep(plane.Facing);
            var altitude = plane.CenterPosition.Z;

            plane.Facing = Util.TickFacing(plane.Facing, desiredFacing, plane.TurnSpeed);

            if (altitude != desiredAltitude.Length)
            {
                var delta = move.HorizontalLength * plane.Info.MaximumPitch.Tan() / 1024;
                var dz = (desiredAltitude.Length - altitude).Clamp(-delta, delta);
                move += new WVec(0, 0, dz);
            }

            plane.SetPosition(self, plane.CenterPosition + move);
        }
Example #29
0
        public void Tick(Actor self)
        {
            var disabled = self.IsDisabled();

            if (cachedDisabled != disabled)
            {
                Sound.Play(disabled ? info.DisableSound : info.EnableSound, self.CenterPosition);
                desiredRange = disabled ? WDist.Zero : info.Range;
                cachedDisabled = disabled;
            }

            if (self.CenterPosition != cachedPosition || desiredRange != cachedRange)
            {
                cachedPosition = self.CenterPosition;
                cachedRange = desiredRange;
                self.World.ActorMap.UpdateProximityTrigger(proximityTrigger, cachedPosition, cachedRange);
            }
        }
Example #30
0
		public ImpactType GetImpactType(World world, CPos cell, WPos pos, Actor firedBy)
		{
			// Missiles need a margin because they sometimes explode a little above ground
			// due to their explosion check triggering slightly too early (because of CloseEnough).
			// TODO: Base ImpactType on target altitude instead of explosion altitude.
			var airMargin = new WDist(128);

			// Matching target actor
			if (GetDirectHit(world, cell, pos, firedBy, true))
				return ImpactType.TargetHit;

			var dat = world.Map.DistanceAboveTerrain(pos);

			if (dat.Length > airMargin.Length)
				return ImpactType.Air;

			return ImpactType.Ground;
		}
Example #31
0
 public Activity MoveWithinRange(Target target, WDist minRange, WDist maxRange)
 {
     return(new MoveWithinRange(self, target, minRange, maxRange));
 }
Example #32
0
 public void Move(CPos cell, int closeEnough = 0)
 {
     Self.QueueActivity(new Move(Self, cell, WDist.FromCells(closeEnough)));
 }
Example #33
0
 public AthenaProjectile(AthenaProjectileInfo info, ProjectileArgs args)
 {
     this.args = args;
     altitude  = info.Altitude;
     delay     = info.Delay;
 }
Example #34
0
        public Actor[] SendDropPods(Actor self, Order order, int[] podFacing, string[] spawnSounds)
        {
            var units = new List <Actor>();
            var info  = Info as DropPodsPowerInfo;

            var       utLower = info.DropPodType.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 pFacing          = podFacing.Random(self.World.SharedRandom);
            var approachRotation = WRot.FromFacing(pFacing);
            var delta            = new WVec(0, -altitude, 0).Rotate(approachRotation);

            foreach (var p in info.DropItems)
            {
                var unit = self.World.CreateActor(false, p.ToLowerInvariant(),
                                                  new TypeDictionary {
                    new OwnerInit(self.Owner)
                });

                units.Add(unit);
            }

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

                Game.Sound.PlayNotification(self.World.Map.Rules, self.Owner, "Speech",
                                            info.DropPodsAvailableNotification, self.Owner.Faction.InternalName);

                var target       = order.Target.CenterPosition;
                var posOffset    = new WVec(-altitude, -altitude, altitude);
                var targetCell   = self.World.Map.CellContaining(target);
                var podLocations = self.World.Map.FindTilesInCircle(targetCell, info.PodScatter).Shuffle(self.World.SharedRandom);
                string dropType  = null;

                if (pFacing >= 160)
                {
                    posOffset = new WVec(-altitude, -altitude, altitude);
                    dropType  = info.DropPodType;
                }
                else
                {
                    posOffset = new WVec(altitude, -altitude, altitude);
                    dropType  = info.DropPodType2;
                }

                using (var pe = podLocations.GetEnumerator())
                    foreach (var u in units)
                    {
                        CPos podDropCellPos = pe.Current;

                        var a = w.CreateActor(dropType, new TypeDictionary
                        {
                            new CenterPositionInit(self.World.Map.CenterOfCell(podDropCellPos) - delta + posOffset),
                            new OwnerInit(self.Owner),
                            new FacingInit(pFacing)
                        });

                        var sound = spawnSounds.RandomOrDefault(Game.CosmeticRandom);
                        if (sound != null)
                        {
                            Game.Sound.Play(SoundType.World, sound, target);
                        }

                        var unloadDist = new WDist(10);

                        a.QueueActivity(new Land(a, Target.FromCell(a.World, podDropCellPos)));
                        a.QueueActivity(new CallFunc(() => a.Kill(a)));
                        pe.MoveNext();
                    }
            });

            return(units.ToArray());
        }
Example #35
0
            public override Activity Tick(Actor self)
            {
                if (ChildActivity != null)
                {
                    ChildActivity = ActivityUtils.RunActivityTick(self, ChildActivity);
                    if (ChildActivity != null)
                    {
                        return(this);
                    }
                }

                if (IsCanceling)
                {
                    // Cancel the requested target, but keep firing on it while in range
                    if (attack.Info.PersistentTargeting)
                    {
                        attack.OpportunityTarget      = attack.RequestedTarget;
                        attack.OpportunityForceAttack = attack.RequestedForceAttack;
                        attack.OpportunityTargetIsPersistentTarget = true;
                    }

                    attack.RequestedTarget = Target.Invalid;
                    return(NextActivity);
                }

                // Check that AttackFollow hasn't cancelled the target by modifying attack.Target
                // Having both this and AttackFollow modify that field is a horrible hack.
                if (hasTicked && attack.RequestedTarget.Type == TargetType.Invalid)
                {
                    return(NextActivity);
                }

                if (attack.IsTraitPaused)
                {
                    return(this);
                }

                bool targetIsHiddenActor;

                attack.RequestedForceAttack    = forceAttack;
                attack.RequestedTarget         = target = target.Recalculate(self.Owner, out targetIsHiddenActor);
                attack.RequestedTargetLastTick = self.World.WorldTick;
                hasTicked = true;

                if (!targetIsHiddenActor && target.Type == TargetType.Actor)
                {
                    lastVisibleTarget       = Target.FromTargetPositions(target);
                    lastVisibleMaximumRange = attack.GetMaximumRangeVersusTarget(target);
                    lastVisibleMinimumRange = attack.GetMinimumRange();
                    lastVisibleOwner        = target.Actor.Owner;
                    lastVisibleTargetTypes  = target.Actor.GetEnabledTargetTypes();

                    // Try and sit at least one cell away from the min or max ranges to give some leeway if the target starts moving.
                    if (move != null && target.Actor.Info.HasTraitInfo <IMoveInfo>())
                    {
                        var preferMinRange = Math.Min(lastVisibleMinimumRange.Length + 1024, lastVisibleMaximumRange.Length);
                        var preferMaxRange = Math.Max(lastVisibleMaximumRange.Length - 1024, lastVisibleMinimumRange.Length);
                        lastVisibleMaximumRange = new WDist((lastVisibleMaximumRange.Length - 1024).Clamp(preferMinRange, preferMaxRange));
                    }
                }

                var oldUseLastVisibleTarget = useLastVisibleTarget;
                var maxRange = lastVisibleMaximumRange;
                var minRange = lastVisibleMinimumRange;

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

                // Most actors want to be able to see their target before shooting
                if (target.Type == TargetType.FrozenActor && !attack.Info.TargetFrozenActors && !forceAttack)
                {
                    var rs = revealsShroud
                             .Where(Exts.IsTraitEnabled)
                             .MaxByOrDefault(s => s.Range);

                    // Default to 2 cells if there are no active traits
                    var sightRange = rs != null ? rs.Range : WDist.FromCells(2);
                    if (sightRange < maxRange)
                    {
                        maxRange = sightRange;
                    }
                }

                // If we are ticking again after previously sequencing a MoveWithRange then that move must have completed
                // Either we are in range and can see the target, or we've lost track of it and should give up
                if (wasMovingWithinRange && targetIsHiddenActor)
                {
                    attack.RequestedTarget = Target.Invalid;
                    return(NextActivity);
                }

                // Update target lines if required
                if (useLastVisibleTarget != oldUseLastVisibleTarget)
                {
                    self.SetTargetLine(useLastVisibleTarget ? lastVisibleTarget : target, Color.Red, false);
                }

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

                var pos         = self.CenterPosition;
                var checkTarget = useLastVisibleTarget ? lastVisibleTarget : target;

                // We've reached the required range - if the target is visible and valid then we wait
                // otherwise if it is hidden or dead we give up
                if (checkTarget.IsInRange(pos, maxRange) && !checkTarget.IsInRange(pos, minRange))
                {
                    if (useLastVisibleTarget)
                    {
                        attack.RequestedTarget = Target.Invalid;
                        return(NextActivity);
                    }

                    return(this);
                }

                // We can't move into range, so give up
                if (move == null || maxRange == WDist.Zero || maxRange < minRange)
                {
                    attack.RequestedTarget = Target.Invalid;
                    return(NextActivity);
                }

                wasMovingWithinRange = true;
                QueueChild(self, move.MoveWithinRange(target, minRange, maxRange, checkTarget.CenterPosition, Color.Red), true);
                return(this);
            }
Example #36
0
        public void Tick(SquadCA owner)
        {
            // Basic check
            if (!owner.IsValid)
            {
                return;
            }

            if (!owner.IsTargetValid)
            {
                var randomSuitableEnemy = GetRandomValuableTarget(owner);
                if (randomSuitableEnemy != null)
                {
                    owner.TargetActor = randomSuitableEnemy;
                }
                else
                {
                    owner.FuzzyStateMachine.ChangeState(owner, new GroundUnitsFleeState(), true);
                    return;
                }
            }

            // Initialize PathGuider. Optimaze pathfinding by using PathGuider.
            PathGuider = owner.Units.FirstOrDefault();
            if (PathGuider == null)
            {
                return;
            }

            // 1. Threat scan surroundings
            var attackScanRadius = WDist.FromCells(owner.SquadManager.Info.AttackScanRadius);

            var targetActor = ThreatScan(owner, PathGuider, attackScanRadius);

            if (targetActor != null)
            {
                owner.TargetActor = targetActor;
                owner.FuzzyStateMachine.ChangeState(owner, new GroundUnitsAttackState(), true);
                return;
            }

            // 2. Force scattered for navigator if needed
            if (StuckInPath <= 0)
            {
                if (TryMakeWay > 0)
                {
                    owner.Bot.QueueOrder(new Order("AttackMove", PathGuider, Target.FromCell(owner.World, owner.TargetActor.Location), false));

                    foreach (var a in owner.Units)
                    {
                        if (a != PathGuider)
                        {
                            owner.Bot.QueueOrder(new Order("Scatter", a, false));
                        }
                    }

                    TryMakeWay--;
                }
                else
                {
                    // When going through is over, restore the check
                    StuckInPath = StuckInPathCheckTimes + MakeWayTick;
                    TryMakeWay  = MakeWayTick;
                }

                return;
            }

            // 3. Check if the squad is stucked due to the map has very twisted path
            // or currently bridge and tunnel from TS mod
            if (PathGuider.CenterPosition == LastPos)
            {
                StuckInPath--;
            }
            else
            {
                StuckInPath = StuckInPathCheckTimes;
            }

            LastPos = PathGuider.CenterPosition;

            // 4. Since units have different movement speeds, they get separated while approaching the target.

            /* Let them regroup into tighter formation towards "PathGuider".
             *
             * "unitsArea" means the space the squad units will occupy (if 1 per Cell).
             * PathGuider only stop when scope of "unitsAround" is not covered all units;
             * units in "unitsHurryUp"  will catch up,
             * which keep the team tight while not stucked.
             *
             * Imagining "unitsArea" takes up a a place shape like square, we need to draw a circle
             * to cover the the enitire circle.
             *
             * When look around, PathGuider find units around to decide if it need to continue.
             * and units that need hurry up will try catch up before guider waiting
             *
             * However in practice because of the poor PF, squad tend to PF to a eclipse.
             * "lookAround" now has the radius of two times of the circle mentioned before.
             */

            var groupArea = (long)WDist.FromCells(owner.Units.Count).Length * 1024;

            var unitsHurryUp = owner.Units.Where(a => (a.CenterPosition - PathGuider.CenterPosition).LengthSquared >= groupArea).ToArray();
            var lookAround   = owner.Units.Where(a => (a.CenterPosition - PathGuider.CenterPosition).LengthSquared <= groupArea * 4).ToArray();

            if (owner.Units.Count > lookAround.Length)
            {
                owner.Bot.QueueOrder(new Order("Stop", PathGuider, false));
            }
            else
            {
                owner.Bot.QueueOrder(new Order("AttackMove", PathGuider, Target.FromCell(owner.World, owner.TargetActor.Location), false));
            }

            foreach (var unit in unitsHurryUp)
            {
                owner.Bot.QueueOrder(new Order("AttackMove", unit, Target.FromCell(owner.World, PathGuider.Location), false));
            }
        }
 public RadBeamRenderable(WPos pos, int zOffset, WVec sourceToTarget, WDist width, Color color, WDist amplitude, WDist wavelength, int quantizationCount)
 {
     this.pos               = pos;
     this.zOffset           = zOffset;
     this.sourceToTarget    = sourceToTarget;
     this.width             = width;
     this.color             = color;
     this.amplitude         = amplitude;
     this.wavelength        = wavelength;
     this.quantizationCount = quantizationCount;
 }
        void ProtectOwn(IBot bot, Actor attacker)
        {
            var protectSq = GetSquadOfType(SquadTypeCA.Protection);

            if (protectSq == null)
            {
                protectSq = RegisterNewSquad(bot, SquadTypeCA.Protection, attacker);
            }

            if (!protectSq.IsTargetValid)
            {
                protectSq.TargetActor = attacker;
            }

            if (!protectSq.IsValid)
            {
                var ownUnits = World.FindActorsInCircle(World.Map.CenterOfCell(GetRandomBaseCenter()), WDist.FromCells(Info.ProtectUnitScanRadius))
                               .Where(unit => unit.Owner == Player && !unit.Info.HasTraitInfo <BuildingInfo>() && !unit.Info.HasTraitInfo <HarvesterInfo>() &&
                                      !unit.Info.HasTraitInfo <AircraftInfo>() && !Info.NavalUnitsTypes.Contains(unit.Info.Name) && unit.Info.HasTraitInfo <AttackBaseInfo>());

                foreach (var a in ownUnits)
                {
                    protectSq.Units.Add(a);
                }
            }
        }
Example #39
0
 public HeliLand(Actor self, bool requireSpace, WDist landAltitude)
 {
     this.requireSpace = requireSpace;
     this.landAltitude = landAltitude;
     aircraft          = self.Trait <Aircraft>();
 }
 internal Actor FindClosestEnemy(WPos pos, WDist radius)
 {
     return(World.FindActorsInCircle(pos, radius).Where(a => IsEnemyUnit(a) && IsNotHiddenUnit(a)).ClosestTo(pos));
 }
Example #41
0
 public UnloadCargo(Actor self, WDist unloadRange, bool unloadAll = true)
     : this(self, Target.Invalid, unloadRange, unloadAll)
 {
     assignTargetOnFirstRun = true;
 }
Example #42
0
 public UpgradeActorsNear(Actor self, UpgradeActorsNearInfo info)
 {
     this.info   = info;
     this.self   = self;
     cachedRange = info.Range;
 }
Example #43
0
 public Activity MoveFollow(Actor self, Target target, WDist minRange, WDist maxRange)
 {
     return(new Follow(self, target, minRange, maxRange));
 }
Example #44
0
        public void Tick(SquadCA owner)
        {
            if (!owner.IsValid)
            {
                return;
            }

            if (!owner.IsTargetValid)
            {
                var closestEnemy = FindClosestEnemy(owner);
                if (closestEnemy == null)
                {
                    return;
                }

                owner.TargetActor = closestEnemy;
            }

            var enemyUnits = owner.World.FindActorsInCircle(owner.TargetActor.CenterPosition, WDist.FromCells(owner.SquadManager.Info.IdleScanRadius))
                             .Where(owner.SquadManager.IsEnemyUnit).ToList();

            if (enemyUnits.Count == 0)
            {
                Retreat(owner, false, true, true);
                return;
            }

            if (AttackOrFleeFuzzyCA.Default.CanAttack(owner.Units, enemyUnits))
            {
                // We have gathered sufficient units. Attack the nearest enemy unit.
                // Inform human allies about AI's rush attack.
                owner.Bot.QueueOrder(new Order("PlaceBeacon", owner.SquadManager.Player.PlayerActor, Target.FromCell(owner.World, owner.TargetActor.Location), false)
                {
                    SuppressVisualFeedback = true
                });
                owner.FuzzyStateMachine.ChangeState(owner, new GroundUnitsAttackMoveState(), true);
            }
            else
            {
                owner.FuzzyStateMachine.ChangeState(owner, new GroundUnitsFleeState(), true);
            }
        }
Example #45
0
        public override bool Tick(Actor self)
        {
            if ((this.DockActor.IsDead || !this.DockActor.IsInWorld || this.Dock.IsTraitDisabled) && !this.IsCanceling)
            {
                this.Cancel(self, true);
            }

            switch (this.DockingState)
            {
            case DockingState.Approaching:
                if (this.State == ActivityState.Canceling)
                {
                    return(true);
                }

                if (this.ChildActivity != null)
                {
                    break;
                }

                var distance = WDist.FromCells(this.Dock.Info.QueueDistance);

                if ((this.dockableActor.CenterPosition - this.DockActor.CenterPosition).Length > distance.Length)
                {
                    this.QueueChild(new Move(this.dockableActor, Target.FromActor(this.DockActor), distance));
                }
                else
                {
                    this.DockingState = DockingState.Waiting;
                    this.Dock.Add(this.dockableActor);
                }

                break;

            case DockingState.Waiting:
                if (this.State == ActivityState.Canceling)
                {
                    this.Dock.Remove(this.dockableActor);

                    return(true);
                }

                break;

            case DockingState.PrepareDocking:
                if (this.State == ActivityState.Canceling)
                {
                    this.Dock.Remove(this.dockableActor);

                    return(true);
                }

                if (this.ChildActivity != null)
                {
                    break;
                }

                var target = this.DockActor.World.Map.CellContaining(this.DockActor.CenterPosition + this.Dock.Info.Position + this.Dock.Info.DragOffset);

                if (this.dockableActor.Location != target)
                {
                    this.QueueChild(new Move(this.dockableActor, target));
                }
                else
                {
                    this.DockingState = DockingState.Docking;

                    this.QueueChild(new Turn(this.dockableActor, this.Dock.Info.Angle));
                    this.initialPosition = this.dockableActor.CenterPosition;

                    this.QueueChild(
                        new Drag(
                            this.dockableActor,
                            this.dockableActor.CenterPosition,
                            this.DockActor.CenterPosition + this.Dock.Info.Position,
                            this.Dock.Info.DragLength
                            )
                        );
                }

                break;

            case DockingState.Docking:
                if (this.State == ActivityState.Canceling)
                {
                    this.StartUndocking();

                    return(false);
                }

                if (this.ChildActivity == null)
                {
                    this.DockingState = DockingState.Docked;
                    this.Dock.OnDock(this.DockActor);
                }

                break;

            case DockingState.Docked:
                if (this.State == ActivityState.Canceling)
                {
                    this.StartUndocking();

                    return(false);
                }

                break;

            case DockingState.Undocking:
                if (this.ChildActivity == null)
                {
                    this.DockingState = DockingState.None;
                    this.Dock.OnUndock();
                    this.Dock.Remove(this.dockableActor);

                    if (!this.DockActor.IsDead && this.DockActor.IsInWorld)
                    {
                        var rallyPoint = this.DockActor.TraitOrDefault <RallyPoint>();

                        if (rallyPoint != null && rallyPoint.Path.Any())
                        {
                            foreach (var cell in rallyPoint.Path)
                            {
                                this.dockableActor.QueueActivity(new Move(this.dockableActor, cell));
                            }
                        }
                    }
                }

                break;

            case DockingState.None:
                return(true);

            default:
                throw new ArgumentOutOfRangeException(Enum.GetName(this.DockingState));
            }

            return(false);
        }
Example #46
0
        public void Tick(Squad owner)
        {
            if (!owner.IsValid)
            {
                return;
            }

            if (!owner.IsTargetValid)
            {
                var closestEnemy = FindClosestEnemy(owner);
                if (closestEnemy != null)
                {
                    owner.TargetActor = closestEnemy;
                }
                else
                {
                    owner.FuzzyStateMachine.ChangeState(owner, new NavyUnitsFleeState(), true);
                    return;
                }
            }

            var leader = owner.Units.ClosestTo(owner.TargetActor.CenterPosition);

            if (leader == null)
            {
                return;
            }

            var ownUnits = owner.World.FindActorsInCircle(leader.CenterPosition, WDist.FromCells(owner.Units.Count) / 3)
                           .Where(a => a.Owner == owner.Units.First().Owner&& owner.Units.Contains(a)).ToHashSet();

            if (ownUnits.Count < owner.Units.Count)
            {
                // Since units have different movement speeds, they get separated while approaching the target.
                // Let them regroup into tighter formation.
                owner.Bot.QueueOrder(new Order("Stop", leader, false));
                foreach (var unit in owner.Units.Where(a => !ownUnits.Contains(a)))
                {
                    owner.Bot.QueueOrder(new Order("AttackMove", unit, Target.FromCell(owner.World, leader.Location), false));
                }
            }
            else
            {
                var enemies = owner.World.FindActorsInCircle(leader.CenterPosition, WDist.FromCells(owner.SquadManager.Info.AttackScanRadius))
                              .Where(owner.SquadManager.IsPreferredEnemyUnit);
                var target = enemies.ClosestTo(leader.CenterPosition);
                if (target != null)
                {
                    owner.TargetActor = target;
                    owner.FuzzyStateMachine.ChangeState(owner, new NavyUnitsAttackState(), true);
                }
                else
                {
                    foreach (var a in owner.Units)
                    {
                        owner.Bot.QueueOrder(new Order("AttackMove", a, Target.FromCell(owner.World, owner.TargetActor.Location), false));
                    }
                }
            }

            if (ShouldFlee(owner))
            {
                owner.FuzzyStateMachine.ChangeState(owner, new NavyUnitsFleeState(), true);
            }
        }
Example #47
0
 public MoveWithinRange(Actor self, Target target, WDist minRange, WDist maxRange)
     : base(self, target)
 {
     this.minRange = minRange;
     this.maxRange = maxRange;
 }
Example #48
0
        public void Tick(Squad owner)
        {
            if (!owner.IsValid)
            {
                return;
            }

            if (!owner.IsTargetValid)
            {
                var closestEnemy = FindClosestEnemy(owner);
                if (closestEnemy == null)
                {
                    return;
                }

                owner.TargetActor = closestEnemy;
            }

            var enemyUnits = owner.World.FindActorsInCircle(owner.TargetActor.CenterPosition, WDist.FromCells(owner.SquadManager.Info.IdleScanRadius))
                             .Where(owner.SquadManager.IsPreferredEnemyUnit).ToList();

            if (enemyUnits.Count == 0)
            {
                return;
            }

            if (AttackOrFleeFuzzy.Default.CanAttack(owner.Units, enemyUnits))
            {
                foreach (var u in owner.Units)
                {
                    owner.Bot.QueueOrder(new Order("AttackMove", u, Target.FromCell(owner.World, owner.TargetActor.Location), false));
                }

                // We have gathered sufficient units. Attack the nearest enemy unit.
                owner.FuzzyStateMachine.ChangeState(owner, new NavyUnitsAttackMoveState(), true);
            }
            else
            {
                owner.FuzzyStateMachine.ChangeState(owner, new NavyUnitsFleeState(), true);
            }
        }
Example #49
0
 public WDist FromCells(int numCells)
 {
     return(WDist.FromCells(numCells));
 }
Example #50
0
        public static bool AnyBlockingActorsBetween(World world, WPos start, WPos end, WDist width, WDist overscan, out WPos hit)
        {
            var actors = world.FindActorsOnLine(start, end, width, overscan);
            var length = (end - start).Length;

            foreach (var a in actors)
            {
                var blockers = a.TraitsImplementing <IBlocksProjectiles>()
                               .Where(Exts.IsTraitEnabled).ToList();

                if (!blockers.Any())
                {
                    continue;
                }

                var hitPos = WorldExtensions.MinimumPointLineProjection(start, end, a.CenterPosition);
                var dat    = world.Map.DistanceAboveTerrain(hitPos);
                if ((hitPos - start).Length < length && blockers.Any(t => t.BlockingHeight > dat))
                {
                    hit = hitPos;
                    return(true);
                }
            }

            hit = WPos.Zero;
            return(false);
        }
Example #51
0
        Actor ChooseTarget(Actor self, AttackBase ab, Stance attackStances, WDist range, bool allowMove)
        {
            var actorsByArmament = new Dictionary <Armament, List <Actor> >();
            var actorsInRange    = self.World.FindActorsInCircle(self.CenterPosition, range);

            foreach (var actor in actorsInRange)
            {
                // PERF: Most units can only attack enemy units. If this is the case but the target is not an enemy, we
                // can bail early and avoid the more expensive targeting checks and armament selection. For groups of
                // allied units, this helps significantly reduce the cost of auto target scans. This is important as
                // these groups will continuously rescan their allies until an enemy finally comes into range.
                if (attackStances == OpenRA.Traits.Stance.Enemy && !actor.AppearsHostileTo(self))
                {
                    continue;
                }

                if (PreventsAutoTarget(self, actor) || !self.Owner.CanTargetActor(actor))
                {
                    continue;
                }

                // Select only the first compatible armament for each actor: if this actor is selected
                // it will be thanks to the first armament anyways, since that is the first selection
                // criterion
                var target    = Target.FromActor(actor);
                var armaments = ab.ChooseArmamentsForTarget(target, false);
                if (!allowMove)
                {
                    armaments = armaments.Where(arm =>
                                                target.IsInRange(self.CenterPosition, arm.MaxRange()) &&
                                                !target.IsInRange(self.CenterPosition, arm.Weapon.MinRange));
                }

                var armament = armaments.FirstOrDefault();
                if (armament == null)
                {
                    continue;
                }

                List <Actor> actors;
                if (actorsByArmament.TryGetValue(armament, out actors))
                {
                    actors.Add(actor);
                }
                else
                {
                    actorsByArmament.Add(armament, new List <Actor> {
                        actor
                    });
                }
            }

            // Armaments are enumerated in attack.Armaments in construct order
            // When autotargeting, first choose targets according to the used armament construct order
            // And then according to distance from actor
            // This enables preferential treatment of certain armaments
            // (e.g. tesla trooper's tesla zap should have precedence over tesla charge)
            foreach (var arm in ab.Armaments)
            {
                List <Actor> actors;
                if (actorsByArmament.TryGetValue(arm, out actors))
                {
                    return(actors.ClosestTo(self));
                }
            }

            return(null);
        }
Example #52
0
 public Fly(Actor self, Target t, WDist minRange, WDist maxRange)
     : this(self, t)
 {
     this.maxRange = maxRange;
     this.minRange = minRange;
 }
Example #53
0
        public override bool Tick(Actor self)
        {
            returnToBase = false;

            // Refuse to take off if it would land immediately again.
            if (aircraft.ForceLanding)
            {
                Cancel(self);
            }

            if (IsCanceling)
            {
                return(true);
            }

            // Check that AttackFollow hasn't cancelled the target by modifying attack.Target
            // Having both this and AttackFollow modify that field is a horrible hack.
            if (hasTicked && attackAircraft.RequestedTarget.Type == TargetType.Invalid)
            {
                return(true);
            }

            if (attackAircraft.IsTraitPaused)
            {
                return(false);
            }

            bool targetIsHiddenActor;

            target = target.Recalculate(self.Owner, out targetIsHiddenActor);
            attackAircraft.SetRequestedTarget(self, target, forceAttack);
            hasTicked = true;

            if (!targetIsHiddenActor && target.Type == TargetType.Actor)
            {
                lastVisibleTarget       = Target.FromTargetPositions(target);
                lastVisibleMaximumRange = attackAircraft.GetMaximumRangeVersusTarget(target);
                lastVisibleOwner        = target.Actor.Owner;
                lastVisibleTargetTypes  = target.Actor.GetEnabledTargetTypes();
            }

            // The target may become hidden in the same tick the FlyAttack constructor is called,
            // causing lastVisible* to remain uninitialized.
            // Fix the fallback values based on the frozen actor properties
            else if (target.Type == TargetType.FrozenActor && !lastVisibleTarget.IsValidFor(self))
            {
                lastVisibleTarget       = Target.FromTargetPositions(target);
                lastVisibleMaximumRange = attackAircraft.GetMaximumRangeVersusTarget(target);
                lastVisibleOwner        = target.FrozenActor.Owner;
                lastVisibleTargetTypes  = target.FrozenActor.TargetTypes;
            }

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

            // If all valid weapons have depleted their ammo and Rearmable trait exists, return to RearmActor to reload
            // and resume the activity after reloading if AbortOnResupply is set to 'false'
            if (rearmable != null && !useLastVisibleTarget && attackAircraft.Armaments.All(x => x.IsTraitPaused || !x.Weapon.IsValidAgainst(target, self.World, self)))
            {
                // Attack moves never resupply
                if (source == AttackSource.AttackMove)
                {
                    return(true);
                }

                // AbortOnResupply cancels the current activity (after resupplying) plus any queued activities
                if (attackAircraft.Info.AbortOnResupply && NextActivity != null)
                {
                    NextActivity.Cancel(self);
                }

                QueueChild(new ReturnToBase(self));
                returnToBase = true;
                return(attackAircraft.Info.AbortOnResupply);
            }

            var pos         = self.CenterPosition;
            var checkTarget = useLastVisibleTarget ? lastVisibleTarget : target;

            // We don't know where the target actually is, so move to where we last saw it
            if (useLastVisibleTarget)
            {
                // We've reached the assumed position but it is not there - give up
                if (checkTarget.IsInRange(pos, lastVisibleMaximumRange))
                {
                    return(true);
                }

                // Fly towards the last known position
                QueueChild(new Fly(self, target, WDist.Zero, lastVisibleMaximumRange, checkTarget.CenterPosition, Color.Red));
                return(false);
            }

            var delta         = attackAircraft.GetTargetPosition(pos, target) - pos;
            var desiredFacing = delta.HorizontalLengthSquared != 0 ? delta.Yaw : aircraft.Facing;

            QueueChild(new TakeOff(self));

            var minimumRange = attackAircraft.Info.AttackType == AirAttackType.Strafe ? WDist.Zero : attackAircraft.GetMinimumRangeVersusTarget(target);

            // Move into range of the target.
            if (!target.IsInRange(pos, lastVisibleMaximumRange) || target.IsInRange(pos, minimumRange))
            {
                QueueChild(aircraft.MoveWithinRange(target, minimumRange, lastVisibleMaximumRange, target.CenterPosition, Color.Red));
            }

            // The aircraft must keep moving forward even if it is already in an ideal position.
            else if (attackAircraft.Info.AttackType == AirAttackType.Strafe)
            {
                QueueChild(new StrafeAttackRun(self, attackAircraft, target, strafeDistance != WDist.Zero ? strafeDistance : lastVisibleMaximumRange));
            }
            else if (attackAircraft.Info.AttackType == AirAttackType.Default && !aircraft.Info.CanHover)
            {
                QueueChild(new FlyAttackRun(self, target, lastVisibleMaximumRange));
            }

            // Turn to face the target if required.
            else if (!attackAircraft.TargetInFiringArc(self, target, 4 * attackAircraft.Info.FacingTolerance))
            {
                aircraft.Facing = Util.TickFacing(aircraft.Facing, desiredFacing, aircraft.TurnSpeed);
            }

            return(false);
        }
Example #54
0
 public Activity MoveWithinRange(Target target, WDist range)
 {
     return(new MoveWithinRange(self, target, WDist.Zero, range));
 }
Example #55
0
        public Actor[] ActorsInCircle(WPos location, WDist radius, LuaFunction filter = null)
        {
            var actors = Context.World.FindActorsInCircle(location, radius);

            return(FilteredObjects(actors, filter).ToArray());
        }
 public ContrailRenderable(World world, Color color, WDist width, int length, int skip, int zOffset)
     : this(world, new WPos[length], width, 0, 0, skip, color, zOffset)
 {
 }
Example #57
0
 public Activity MoveTo(CPos cell, int nearEnough)
 {
     return(new Move(self, cell, WDist.FromCells(nearEnough)));
 }
Example #58
0
        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.GuidedTarget.CenterPosition
                            + 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;

            pos += move;

            // Check for walls or other blocking obstacles
            var  shouldExplode = false;
            WPos blockedPos;

            if (info.Blockable && BlocksProjectiles.AnyBlockingActorsBetween(world, lastPos, pos, info.Width,
                                                                             info.TargetExtraSearchRadius, out blockedPos))
            {
                pos           = blockedPos;
                shouldExplode = true;
            }

            // Create the smoke 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.TrailSequence, trailPalette, false, false, renderFacing)));
                ticksToNextSmoke = info.TrailInterval;
            }

            if (info.ContrailLength > 0)
            {
                contrail.Update(pos);
            }

            distanceCovered += new WDist(speed);
            var cell   = world.Map.CellContaining(pos);
            var height = world.Map.DistanceAboveTerrain(pos);

            shouldExplode |= height.Length < 0 ||          // Hit the ground
                             relTarDist < info.CloseEnough.Length ||    // Within range
                             (info.ExplodeWhenEmpty && rangeLimit >= WDist.Zero && distanceCovered > rangeLimit) ||    // Ran out of fuel
                             !world.Map.Contains(cell) ||    // This also avoids an IndexOutOfRangeException in GetTerrainInfo below.
                             (!string.IsNullOrEmpty(info.BoundToTerrainType) && world.Map.GetTerrainInfo(cell).Type != info.BoundToTerrainType) ||    // Hit incompatible terrain
                             (height.Length < info.AirburstAltitude.Length && relTarHorDist < info.CloseEnough.Length);       // Airburst

            if (shouldExplode)
            {
                Explode(world);
            }
        }
Example #59
0
        public void Tick(Squad owner)
        {
            if (!owner.IsValid)
            {
                return;
            }

            if (!owner.IsTargetValid)
            {
                var closestEnemy = FindClosestEnemy(owner);
                if (closestEnemy != null)
                {
                    owner.TargetActor = closestEnemy;
                }
                else
                {
                    owner.FuzzyStateMachine.ChangeState(owner, new GroundUnitsFleeState(), true);
                    return;
                }
            }

            var leader = owner.Units.ClosestTo(owner.TargetActor.CenterPosition);

            if (leader == null)
            {
                return;
            }

            if (leader.Location != lastLeaderLocation)
            {
                lastLeaderLocation = leader.Location;
                lastUpdatedTick    = owner.World.WorldTick;
            }

            if (owner.TargetActor != lastTarget)
            {
                lastTarget      = owner.TargetActor;
                lastUpdatedTick = owner.World.WorldTick;
            }

            // HACK: Drop back to the idle state if we haven't moved in 2.5 seconds
            // This works around the squad being stuck trying to attack-move to a location
            // that they cannot path to, generating expensive pathfinding calls each tick.
            if (owner.World.WorldTick > lastUpdatedTick + 63)
            {
                owner.FuzzyStateMachine.ChangeState(owner, new GroundUnitsIdleState(), true);
                return;
            }

            var ownUnits = owner.World.FindActorsInCircle(leader.CenterPosition, WDist.FromCells(owner.Units.Count) / 3)
                           .Where(a => a.Owner == owner.Units.First().Owner&& owner.Units.Contains(a)).ToHashSet();

            if (ownUnits.Count < owner.Units.Count)
            {
                // Since units have different movement speeds, they get separated while approaching the target.
                // Let them regroup into tighter formation.
                owner.Bot.QueueOrder(new Order("Stop", leader, false));

                var units = owner.Units.Where(a => !ownUnits.Contains(a)).ToArray();
                owner.Bot.QueueOrder(new Order("AttackMove", null, Target.FromCell(owner.World, leader.Location), false, groupedActors: units));
            }
            else
            {
                var enemies = owner.World.FindActorsInCircle(leader.CenterPosition, WDist.FromCells(owner.SquadManager.Info.AttackScanRadius))
                              .Where(owner.SquadManager.IsPreferredEnemyUnit);
                var target = enemies.ClosestTo(leader.CenterPosition);
                if (target != null)
                {
                    owner.TargetActor = target;
                    owner.FuzzyStateMachine.ChangeState(owner, new GroundUnitsAttackState(), true);
                }
                else
                {
                    owner.Bot.QueueOrder(new Order("AttackMove", null, Target.FromCell(owner.World, owner.TargetActor.Location), false, groupedActors: owner.Units.ToArray()));
                }
            }

            if (ShouldFlee(owner))
            {
                owner.FuzzyStateMachine.ChangeState(owner, new GroundUnitsFleeState(), true);
            }
        }
Example #60
0
        public override Activity Tick(Actor self)
        {
            if (ChildActivity != null)
            {
                ChildActivity = ActivityUtils.RunActivity(self, ChildActivity);
                if (ChildActivity != null)
                {
                    return(this);
                }
            }

            if (cargo != carryall.Carryable)
            {
                return(NextActivity);
            }

            if (cargo.IsDead || IsCanceling || carryable.IsTraitDisabled || !cargo.AppearsFriendlyTo(self))
            {
                carryall.UnreserveCarryable(self);
                return(NextActivity);
            }

            if (carryall.State != Carryall.CarryallState.Reserved)
            {
                return(NextActivity);
            }

            switch (state)
            {
            case PickupState.Intercept:
                QueueChild(self, movement.MoveWithinRange(Target.FromActor(cargo), WDist.FromCells(4), targetLineColor: Color.Yellow), true);
                state = PickupState.LockCarryable;
                return(this);

            case PickupState.LockCarryable:
                if (!carryable.LockForPickup(cargo, self))
                {
                    Cancel(self);
                }

                state = PickupState.MoveToCarryable;
                return(this);

            case PickupState.MoveToCarryable:
            {
                // Line up with the attachment point
                var localOffset    = carryall.OffsetForCarryable(self, cargo).Rotate(carryableBody.QuantizeOrientation(self, cargo.Orientation));
                var targetPosition = cargo.CenterPosition - carryableBody.LocalToWorld(localOffset);
                if ((self.CenterPosition - targetPosition).HorizontalLengthSquared != 0)
                {
                    QueueChild(self, new Fly(self, Target.FromPos(targetPosition)), true);
                    return(this);
                }

                state = PickupState.Turn;
                return(this);
            }

            case PickupState.Turn:
                if (carryallFacing.Facing != carryableFacing.Facing)
                {
                    QueueChild(self, new Turn(self, carryableFacing.Facing), true);
                    return(this);
                }

                state = PickupState.Land;
                return(this);

            case PickupState.Land:
            {
                var localOffset    = carryall.OffsetForCarryable(self, cargo).Rotate(carryableBody.QuantizeOrientation(self, cargo.Orientation));
                var targetPosition = cargo.CenterPosition - carryableBody.LocalToWorld(localOffset);
                if ((self.CenterPosition - targetPosition).HorizontalLengthSquared != 0 || carryallFacing.Facing != carryableFacing.Facing)
                {
                    state = PickupState.MoveToCarryable;
                    return(this);
                }

                if (targetPosition.Z != self.CenterPosition.Z)
                {
                    QueueChild(self, new Land(self, Target.FromActor(cargo), -carryableBody.LocalToWorld(localOffset)));
                    return(this);
                }

                state = delay > 0 ? PickupState.Wait : PickupState.Pickup;
                return(this);
            }

            case PickupState.Wait:
                QueueChild(self, new Wait(delay, false), true);
                state = PickupState.Pickup;
                return(this);

            case PickupState.Pickup:
                // Remove our carryable from world
                Attach(self);
                return(this);
            }

            return(NextActivity);
        }