Exemple #1
0
        public Missile(MissileInfo info, ProjectileArgs args)
        {
            this.info = info;
            this.args = args;

            pos = args.Source;
            facing = args.Facing;

            targetPosition = args.PassiveTarget;

            var world = args.SourceActor.World;

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

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

            if (info.Image != null)
            {
                anim = new Animation(world, info.Image, () => facing);
                anim.PlayRepeating("idle");
            }

            if (info.ContrailLength > 0)
            {
                var color = info.ContrailUsePlayerColor ? ContrailRenderable.ChooseColor(args.SourceActor) : info.ContrailColor;
                trail = new ContrailRenderable(world, color, info.ContrailLength, info.ContrailDelay, 0);
            }
        }
Exemple #2
0
        public void RenderAfterWorld(WorldRenderer wr, Actor self)
        {
            if (devMode == null || !devMode.ShowCombatGeometry)
                return;

            var wcr = Game.Renderer.WorldRgbaColorRenderer;
            var iz = 1 / wr.Viewport.Zoom;

            if (healthInfo != null)
                healthInfo.Shape.DrawCombatOverlay(wr, wcr, self);

            var blockers = allBlockers.Where(Exts.IsTraitEnabled).ToList();
            if (blockers.Count > 0)
            {
                var hc = Color.Orange;
                var height = new WVec(0, 0, blockers.Max(b => b.BlockingHeight.Length));
                var ha = wr.ScreenPosition(self.CenterPosition);
                var hb = wr.ScreenPosition(self.CenterPosition + height);
                wcr.DrawLine(ha, hb, iz, hc);
                TargetLineRenderable.DrawTargetMarker(wr, hc, ha);
                TargetLineRenderable.DrawTargetMarker(wr, hc, hb);
            }

            foreach (var attack in self.TraitsImplementing<AttackBase>().Where(x => !x.IsTraitDisabled))
                DrawArmaments(self, attack, wr, wcr, iz);
        }
        public void RenderAfterWorld(WorldRenderer wr, Actor self)
        {
            if (devMode == null || !devMode.ShowMuzzles)
                return;

            if (health.Value != null)
                wr.DrawRangeCircle(Color.Red, wr.ScreenPxPosition(self.CenterPosition), health.Value.Info.Radius / Game.CellSize);

            var wlr = Game.Renderer.WorldLineRenderer;
            var c = Color.White;

            foreach (var a in armaments.Value)
            {
                foreach (var b in a.Barrels)
                {
                    var muzzle = self.CenterPosition + a.MuzzleOffset(self, b);
                    var dirOffset = new WVec(0, -224, 0).Rotate(a.MuzzleOrientation(self, b));

                    var sm = wr.ScreenPosition(muzzle);
                    var sd = wr.ScreenPosition(muzzle + dirOffset);
                    wlr.DrawLine(sm, sd, c, c);
                    wr.DrawTargetMarker(c, sm);
                }
            }
        }
Exemple #4
0
        public Missile(MissileInfo info, ProjectileArgs args)
        {
            this.info = info;
            this.args = args;

            pos = args.Source;
            facing = args.Facing;

            targetPosition = args.PassiveTarget;

            // Convert ProjectileArg definitions to world coordinates
            // TODO: Change the yaml definitions so we don't need this
            var inaccuracy = (int)(info.Inaccuracy * 1024 / Game.CellSize);
            speed = info.Speed * 1024 / (5 * Game.CellSize);

            if (info.Inaccuracy > 0)
                offset = WVec.FromPDF(args.SourceActor.World.SharedRandom, 2) * inaccuracy / 1024;

            if (info.Image != null)
            {
                anim = new Animation(info.Image, () => facing);
                anim.PlayRepeating("idle");
            }

            if (info.ContrailLength > 0)
            {
                var color = info.ContrailUsePlayerColor ? ContrailRenderable.ChooseColor(args.SourceActor) : info.ContrailColor;
                trail = new ContrailRenderable(args.SourceActor.World, color, info.ContrailLength, info.ContrailDelay, 0);
            }
        }
Exemple #5
0
        public override Activity Tick(Actor self)
        {
            if (self.CenterPosition.Z <= 0)
            {
                if (info.Explosion != null)
                {
                    var weapon = self.World.Map.Rules.Weapons[info.Explosion.ToLowerInvariant()];

                    // Use .FromPos since this actor is killed. Cannot use Target.FromActor
                    weapon.Impact(Target.FromPos(self.CenterPosition), self, Enumerable.Empty<int>());
                }

                self.Destroy();
                return null;
            }

            if (info.Spins)
            {
                spin += acceleration;
                aircraft.Facing = (aircraft.Facing + spin) % 256;
            }

            var move = info.Moves ? aircraft.FlyStep(aircraft.Facing) : WVec.Zero;
            move -= new WVec(WRange.Zero, WRange.Zero, info.Velocity);
            aircraft.SetPosition(self, aircraft.CenterPosition + move);

            return this;
        }
Exemple #6
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;
		}
Exemple #7
0
        public EditorActorBrush(EditorViewportControllerWidget editorWidget, ActorInfo actor, PlayerReference owner, WorldRenderer wr)
        {
            this.editorWidget = editorWidget;
            worldRenderer = wr;
            world = wr.World;
            editorLayer = world.WorldActor.Trait<EditorActorLayer>();

            Actor = actor;
            this.owner = owner;

            preview = editorWidget.Get<ActorPreviewWidget>("DRAG_ACTOR_PREVIEW");
            preview.GetScale = () => worldRenderer.Viewport.Zoom;
            preview.IsVisible = () => editorWidget.CurrentBrush == this;

            var buildingInfo = actor.Traits.GetOrDefault<BuildingInfo>();
            if (buildingInfo != null)
            {
                locationOffset = -FootprintUtils.AdjustForBuildingSize(buildingInfo);
                previewOffset = FootprintUtils.CenterOffset(world, buildingInfo);
            }

            var td = new TypeDictionary();
            td.Add(new FacingInit(facing));
            td.Add(new TurretFacingInit(facing));
            td.Add(new OwnerInit(owner.Name));
            td.Add(new RaceInit(owner.Race));
            preview.SetPreview(actor, td);

            // The preview widget may be rendered by the higher-level code before it is ticked.
            // Force a manual tick to ensure the bounds are set correctly for this first draw.
            Tick();
        }
		public FallDown(Actor self, WPos dropPosition, int fallRate, Actor ignoreActor = null)
		{
			pos = self.TraitOrDefault<IPositionable>();
			IsInterruptible = false;
			fallVector = new WVec(0, 0, fallRate);
			this.dropPosition = dropPosition;
		}
Exemple #9
0
		public IRenderable OffsetBy(WVec vec)
		{
			return new VoxelRenderable(
				voxels, pos + vec, zOffset, camera, scale,
				lightSource, lightAmbientColor, lightDiffuseColor,
				palette, normalsPalette, shadowPalette);
		}
Exemple #10
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;
        }
Exemple #11
0
        public override Activity Tick(Actor self)
        {
            if (self.World.Map.DistanceAboveTerrain(self.CenterPosition).Length <= 0)
            {
                if (info.ExplosionWeapon != null)
                {
                    // Use .FromPos since this actor is killed. Cannot use Target.FromActor
                    info.ExplosionWeapon.Impact(Target.FromPos(self.CenterPosition), self, Enumerable.Empty<int>());
                }

                self.Dispose();
                return null;
            }

            if (info.Spins)
            {
                spin += acceleration;
                aircraft.Facing = (aircraft.Facing + spin) % 256;
            }

            var move = info.Moves ? aircraft.FlyStep(aircraft.Facing) : WVec.Zero;
            move -= new WVec(WDist.Zero, WDist.Zero, info.Velocity);
            aircraft.SetPosition(self, aircraft.CenterPosition + move);

            return this;
        }
        void CreateActors(string actorName, string deliveringActorName, out Actor cargo, out Actor carrier)
        {
            // Get a carryall spawn location
            var location = Info.SpawnLocation;
            if (location == CPos.Zero)
                location = self.World.Map.ChooseClosestEdgeCell(self.Location);

            var spawn = self.World.Map.CenterOfCell(location);

            var initialFacing = self.World.Map.FacingBetween(location, self.Location, 0);

            // If aircraft, spawn at cruise altitude
            var aircraftInfo = self.World.Map.Rules.Actors[deliveringActorName.ToLower()].TraitInfoOrDefault<AircraftInfo>();
            if (aircraftInfo != null)
                spawn += new WVec(0, 0, aircraftInfo.CruiseAltitude.Length);

            // Create delivery actor
            carrier = self.World.CreateActor(false, deliveringActorName, new TypeDictionary
            {
                new LocationInit(location),
                new CenterPositionInit(spawn),
                new OwnerInit(self.Owner),
                new FacingInit(initialFacing)
            });

            // Create delivered actor
            cargo = self.World.CreateActor(false, actorName, new TypeDictionary
            {
                new OwnerInit(self.Owner),
            });
        }
Exemple #13
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;
        }
Exemple #14
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);
        }
Exemple #15
0
        public Missile(MissileInfo info, ProjectileArgs args)
        {
            this.info = info;
            this.args = args;

            pos = args.Source;
            facing = args.Facing;

            targetPosition = args.PassiveTarget;

            if (info.Inaccuracy.Range > 0)
                offset = WVec.FromPDF(args.SourceActor.World.SharedRandom, 2) * info.Inaccuracy.Range / 1024;

            if (info.Image != null)
            {
                anim = new Animation(args.SourceActor.World, info.Image, () => facing);
                anim.PlayRepeating("idle");
            }

            if (info.ContrailLength > 0)
            {
                var color = info.ContrailUsePlayerColor ? ContrailRenderable.ChooseColor(args.SourceActor) : info.ContrailColor;
                trail = new ContrailRenderable(args.SourceActor.World, color, info.ContrailLength, info.ContrailDelay, 0);
            }
        }
Exemple #16
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;
        }
Exemple #17
0
		static void ConvertInt2ToWVec(ref string input)
		{
			var offset = FieldLoader.GetValue<int2>("(value)", input);
			var ts = Game.modData.Manifest.TileSize;
			var world = new WVec(offset.X * 1024 / ts.Width, offset.Y * 1024 / ts.Height, 0);
			input = world.ToString();
		}
Exemple #18
0
        public Parachute(Actor cargo, WPos dropPosition)
        {
            this.cargo = cargo;

            parachutableInfo = cargo.Info.Traits.GetOrDefault<ParachutableInfo>();

            if (parachutableInfo != null)
                fallVector = new WVec(0, 0, parachutableInfo.FallRate);

            var parachuteSprite = parachutableInfo != null ? parachutableInfo.ParachuteSequence : null;
            if (parachuteSprite != null)
            {
                parachute = new Animation(cargo.World, parachuteSprite);
                parachute.PlayThen("open", () => parachute.PlayRepeating("idle"));
            }

            var shadowSprite = parachutableInfo != null ? parachutableInfo.ShadowSequence : null;
            if (shadowSprite != null)
            {
                shadow = new Animation(cargo.World, shadowSprite);
                shadow.PlayRepeating("idle");
            }

            if (parachutableInfo != null)
                parachuteOffset = parachutableInfo.ParachuteOffset;

            // Adjust x,y to match the target subcell
            cargo.Trait<IPositionable>().SetPosition(cargo, cargo.World.Map.CellContaining(dropPosition));
            var cp = cargo.CenterPosition;
            pos = new WPos(cp.X, cp.Y, dropPosition.Z);
        }
Exemple #19
0
        public override void Activate(Actor self, Order order)
        {
            var info = Info as AirstrikePowerInfo;

            var attackFacing = Util.QuantizeFacing(self.World.SharedRandom.Next(256), info.QuantizedFacings) * (256 / info.QuantizedFacings);
            var attackRotation = WRot.FromFacing(attackFacing);
            var delta = new WVec(0, -1024, 0).Rotate(attackRotation);

            var altitude = Rules.Info[info.UnitType].Traits.Get<PlaneInfo>().CruiseAltitude * 1024 / Game.CellSize;
            var target = order.TargetLocation.CenterPosition + new WVec(0, 0, altitude);
            var startEdge = target - (self.World.DistanceToMapEdge(target, -delta) + info.Cordon).Range * delta / 1024;
            var finishEdge = target + (self.World.DistanceToMapEdge(target, delta) + info.Cordon).Range * delta / 1024;

            self.World.AddFrameEndTask(w =>
            {
                var notification = self.Owner.IsAlliedWith(self.World.RenderPlayer) ? Info.LaunchSound : Info.IncomingSound;
                Sound.Play(notification);

                Actor flare = null;
                if (info.FlareType != null)
                {
                    flare = w.CreateActor(info.FlareType, new TypeDictionary
                    {
                        new LocationInit(order.TargetLocation),
                        new OwnerInit(self.Owner),
                    });

                    flare.QueueActivity(new Wait(info.FlareTime));
                    flare.QueueActivity(new RemoveSelf());
                }

                for (var i = -info.SquadSize / 2; i <= info.SquadSize / 2; i++)
                {
                    // Even-sized squads skip the lead plane
                    if (i == 0 && (info.SquadSize & 1) == 0)
                        continue;

                    // Includes the 90 degree rotation between body and world coordinates
                    var so = info.SquadOffset;
                    var spawnOffset = new WVec(i*so.Y, -Math.Abs(i)*so.X, 0).Rotate(attackRotation);
                    var targetOffset = new WVec(i*so.Y, 0, 0).Rotate(attackRotation);

                    var a = w.CreateActor(info.UnitType, new TypeDictionary
                    {
                        new CenterPositionInit(startEdge + spawnOffset),
                        new OwnerInit(self.Owner),
                        new FacingInit(attackFacing),
                    });

                    a.Trait<AttackBomber>().SetTarget(target + targetOffset);

                    if (flare != null)
                        a.QueueActivity(new CallFunc(() => flare.Destroy()));

                    a.QueueActivity(Fly.ToPos(finishEdge + spawnOffset));
                    a.QueueActivity(new RemoveSelf());
                }
            });
        }
 public BeamRenderable(WPos pos, int zOffset, WVec length, float width, Color color)
 {
     this.pos = pos;
     this.zOffset = zOffset;
     this.length = length;
     this.color = color;
     this.width = width;
 }
		public SpriteActorPreview(Animation animation, WVec offset, int zOffset, PaletteReference pr, float scale)
		{
			this.animation = animation;
			this.offset = offset;
			this.zOffset = zOffset;
			this.pr = pr;
			this.scale = scale;
		}
Exemple #22
0
        public WDist DistanceFromEdge(WVec v)
        {
            var r = new int2(
                Math.Max(Math.Abs(v.X - center.X) - quadrantSize.X, 0),
                Math.Max(Math.Abs(v.Y - center.Y) - quadrantSize.Y, 0));

            return new WDist(r.Length);
        }
Exemple #23
0
		void Calculate(Actor self)
		{
			if (dest == null || Reservable.IsReserved(dest))
				dest = ChooseAirfield(self, true);

			if (dest == null)
				return;

			var plane = self.Trait<Plane>();
			var planeInfo = self.Info.Traits.Get<PlaneInfo>();
			var res = dest.TraitOrDefault<Reservable>();
			if (res != null)
			{
				plane.UnReserve();
				plane.Reservation = res.Reserve(dest, self, plane);
			}

			var landPos = dest.CenterPosition;
			var altitude = planeInfo.CruiseAltitude.Range;

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

			// Land towards the east
			var approachStart = landPos + new WVec(-landDistance, 0, altitude);

			// Add 10% to the turning radius to ensure we have enough room
			var speed = plane.MovementSpeed * 32 / 35;
			var turnRadius = (int)(141 * speed / planeInfo.ROT / (float)Math.PI);

			// Find the center of the turning circles for clockwise and counterclockwise turns
			var angle = WAngle.FromFacing(plane.Facing);
			var fwd = -new WVec(angle.Sin(), angle.Cos(), 0);

			// Work out whether we should turn clockwise or counter-clockwise for approach
			var side = new WVec(-fwd.Y, fwd.X, fwd.Z);
			var approachDelta = self.CenterPosition - approachStart;
			var sideTowardBase = new[] { side, -side }
				.MinBy(a => WVec.Dot(a, approachDelta));

			// Calculate the tangent line that joins the turning circles at the current and approach positions
			var cp = self.CenterPosition + turnRadius * sideTowardBase / 1024;
			var posCenter = new WPos(cp.X, cp.Y, altitude);
			var approachCenter = approachStart + new WVec(0, turnRadius * Math.Sign(self.CenterPosition.Y - approachStart.Y), 0);
			var tangentDirection = approachCenter - posCenter;
			var tangentOffset = new WVec(-tangentDirection.Y, tangentDirection.X, 0) * turnRadius / tangentDirection.Length;

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

			w1 = posCenter + tangentOffset;
			w2 = approachCenter + tangentOffset;
			w3 = approachStart;
			plane.RTBPathHash = w1 + (WVec)w2 + (WVec)w3;

			isCalculated = true;
		}
Exemple #24
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;
 }
Exemple #25
0
        public Parachute(Actor self, WPos dropPosition)
        {
            um = self.TraitOrDefault<UpgradeManager>();
            pos = self.TraitOrDefault<IPositionable>();

            // Parachutable trait is a prerequisite for running this activity
            para = self.Info.Traits.Get<ParachutableInfo>();
            fallVector = new WVec(0, 0, para.FallRate);
            this.dropPosition = dropPosition;
        }
Exemple #26
0
 public SpriteRenderable(Sprite sprite, WPos pos, WVec offset, int zOffset, PaletteReference palette, float scale, bool isDecoration)
 {
     this.sprite = sprite;
     this.pos = pos;
     this.offset = offset;
     this.zOffset = zOffset;
     this.palette = palette;
     this.scale = scale;
     this.isDecoration = isDecoration;
 }
Exemple #27
0
        public Parachute(Actor self, WPos dropPosition, Actor ignoreActor = null)
        {
            pos = self.TraitOrDefault<IPositionable>();
            ignore = ignoreActor;

            // Parachutable trait is a prerequisite for running this activity
            para = self.Info.TraitInfo<ParachutableInfo>();
            fallVector = new WVec(0, 0, para.FallRate);
            this.dropPosition = dropPosition;
            IsInterruptible = false;
        }
Exemple #28
0
        protected virtual bool IsVisibleInner(Actor self, Player byPlayer)
        {
            if (Info.Type == VisibilityType.Footprint)
                return byPlayer.Shroud.AnyExplored(self.OccupiesSpace.OccupiedCells());

            var pos = self.CenterPosition;
            if (Info.Type == VisibilityType.GroundPosition)
                pos -= new WVec(WDist.Zero, WDist.Zero, self.World.Map.DistanceAboveTerrain(pos));

            return byPlayer.Shroud.IsExplored(pos);
        }
		public SmokeParticle(WPos pos, WVec[] gravity, World world, string image, string sequence, string palette, bool visibleThroughFog = false, bool scaleSizeWithZoom = false)
		{
			this.world = world;
			this.pos = pos;
			this.gravity = gravity;
			this.palette = palette;
			this.scaleSizeWithZoom = scaleSizeWithZoom;
			this.visibleThroughFog = visibleThroughFog;
			anim = new Animation(world, image, () => 0);
			anim.PlayThen(sequence, () => world.AddFrameEndTask(w => w.Remove(this)));
		}
 public HarvesterDockSequence(Actor self, Actor refinery, int dockAngle, bool isDragRequired, WVec dragOffset, int dragLength)
 {
     dockingState = State.Turn;
     Refinery = refinery;
     DockAngle = dockAngle;
     IsDragRequired = isDragRequired;
     DragOffset = dragOffset;
     DragLength = dragLength;
     Harv = self.Trait<Harvester>();
     StartDrag = self.CenterPosition;
     EndDrag = refinery.CenterPosition + DragOffset;
 }
Exemple #31
0
 public IRenderable OffsetBy(WVec vec)
 {
     return(new CircleAnnotationRenderable(centerPosition + vec, radius, width, color, filled));
 }
Exemple #32
0
 public IRenderable OffsetBy(WVec vec)
 {
     return(new SpriteRenderable(sprite, pos + vec, offset, zOffset, palette, scale, isDecoration));
 }
 public IRenderable OffsetBy(WVec vec)
 {
     return(new LineAnnotationRenderable(start + vec, end + vec, width, startColor, endColor));
 }
Exemple #34
0
        WVec MoveStep(int speed, int facing)
        {
            var dir = new WVec(0, -1024, 0).Rotate(WRot.FromFacing(facing));

            return(speed * dir / 1024);
        }
Exemple #35
0
 public IRenderable OffsetBy(WVec vec)
 {
     return(new ContrailRenderable(world, trail.Select(pos => pos + vec).ToArray(), next, length, skip, color, zOffset));
 }
Exemple #36
0
        WVec HomingTick(World world, WVec tarDistVec, int relTarHorDist)
        {
            int predClfHgt  = 0;
            int predClfDist = 0;
            int lastHtChg   = 0;
            int lastHt      = 0;

            if (info.TerrainHeightAware)
            {
                InclineLookahead(world, relTarHorDist, out predClfHgt, out predClfDist, out lastHtChg, out lastHt);
            }

            // Height difference between the incline height and missile height
            var diffClfMslHgt = predClfHgt - pos.Z;

            // Get underestimate of distance from target in next tick
            var nxtRelTarHorDist = (relTarHorDist - speed - info.Acceleration.Length).Clamp(0, relTarHorDist);

            // Target height relative to the missile
            var relTarHgt = tarDistVec.Z;

            // Compute which direction the projectile should be facing
            var velVec         = tarDistVec + predVel;
            var desiredHFacing = velVec.HorizontalLengthSquared != 0 ? velVec.Yaw.Facing : hFacing;

            var delta = Common.Util.NormalizeFacing(hFacing - desiredHFacing);

            if (allowPassBy && delta > 64 && delta < 192)
            {
                desiredHFacing = (desiredHFacing + 128) & 0xFF;
                targetPassedBy = true;
            }
            else
            {
                targetPassedBy = false;
            }

            var desiredVFacing = HomingInnerTick(predClfDist, diffClfMslHgt, relTarHorDist, lastHtChg, lastHt,
                                                 nxtRelTarHorDist, relTarHgt, vFacing, targetPassedBy);

            // The target has been passed by
            if (tarDistVec.HorizontalLength < speed * WAngle.FromFacing(vFacing).Cos() / 1024)
            {
                targetPassedBy = true;
            }

            // Check whether the homing mechanism is jammed
            var jammed = info.Jammable && world.ActorsWithTrait <JamsMissiles>().Any(JammedBy);

            if (jammed)
            {
                desiredHFacing = hFacing + world.SharedRandom.Next(-info.JammedDiversionRange, info.JammedDiversionRange + 1);
                desiredVFacing = vFacing + world.SharedRandom.Next(-info.JammedDiversionRange, info.JammedDiversionRange + 1);
            }
            else if (!args.GuidedTarget.IsValidFor(args.SourceActor))
            {
                desiredHFacing = hFacing;
            }

            // Compute new direction the projectile will be facing
            hFacing = Common.Util.TickFacing(hFacing, desiredHFacing, info.HorizontalRateOfTurn);
            vFacing = Common.Util.TickFacing(vFacing, desiredVFacing, info.VerticalRateOfTurn);

            // Compute the projectile's guided displacement
            return(new WVec(0, -1024 * speed, 0)
                   .Rotate(new WRot(WAngle.FromFacing(vFacing), WAngle.Zero, WAngle.Zero))
                   .Rotate(new WRot(WAngle.Zero, WAngle.Zero, WAngle.FromFacing(hFacing)))
                   / 1024);
        }
Exemple #37
0
 public HarvesterDockSequence(Actor self, Actor refinery, int dockAngle, bool isDragRequired, WVec dragOffset, int dragLength)
 {
     dockingState   = DockingState.Turn;
     Refinery       = refinery;
     DockAngle      = dockAngle;
     IsDragRequired = isDragRequired;
     DragOffset     = dragOffset;
     DragLength     = dragLength;
     Harv           = self.Trait <Harvester>();
     StartDrag      = self.CenterPosition;
     EndDrag        = refinery.CenterPosition + DragOffset;
 }
Exemple #38
0
 public IRenderable OffsetBy(WVec vec)
 {
     return(new DetectionCircleAnnotationRenderable(centerPosition + vec, radius, zOffset,
                                                    trailCount, trailSeparation, trailAngle, color, width, borderColor, borderWidth));
 }
Exemple #39
0
        public void SendDropPods(Actor self, Order order, int podFacing)
        {
            var actorInfo        = self.World.Map.Rules.Actors[info.UnitTypes.First().ToLowerInvariant()];
            var aircraftInfo     = actorInfo.TraitInfo <AircraftInfo>();
            var altitude         = aircraftInfo.CruiseAltitude.Length;
            var approachRotation = WRot.FromFacing(podFacing);
            var fallsToEarthInfo = actorInfo.TraitInfo <FallsToEarthInfo>();
            var delta            = new WVec(0, -altitude * aircraftInfo.Speed / fallsToEarthInfo.Velocity.Length, 0).Rotate(approachRotation);

            self.World.AddFrameEndTask(w =>
            {
                var target       = order.Target.CenterPosition;
                var targetCell   = self.World.Map.CellContaining(target);
                var podLocations = self.World.Map.FindTilesInCircle(targetCell, info.PodScatter)
                                   .Where(c => aircraftInfo.LandableTerrainTypes.Contains(w.Map.GetTerrainInfo(c).Type) &&
                                          !self.World.ActorMap.GetActorsAt(c).Any());

                if (!podLocations.Any())
                {
                    return;
                }

                if (info.CameraActor != null)
                {
                    var camera = w.CreateActor(info.CameraActor, new TypeDictionary
                    {
                        new LocationInit(targetCell),
                        new OwnerInit(self.Owner),
                    });

                    camera.QueueActivity(new Wait(info.CameraRemoveDelay));
                    camera.QueueActivity(new RemoveSelf());
                }

                PlayLaunchSounds();

                var drops = self.World.SharedRandom.Next(info.Drops.X, info.Drops.Y);
                for (var i = 0; i < drops; i++)
                {
                    var unitType    = info.UnitTypes.Random(self.World.SharedRandom);
                    var podLocation = podLocations.Random(self.World.SharedRandom);
                    var podTarget   = Target.FromCell(w, podLocation);
                    var location    = self.World.Map.CenterOfCell(podLocation) - delta + new WVec(0, 0, altitude);

                    var pod = w.CreateActor(false, unitType, new TypeDictionary
                    {
                        new CenterPositionInit(location),
                        new OwnerInit(self.Owner),
                        new FacingInit(podFacing)
                    });

                    var aircraft = pod.Trait <Aircraft>();
                    if (!aircraft.CanLand(podLocation))
                    {
                        pod.Dispose();
                    }
                    else
                    {
                        w.Add(new DropPodImpact(self.Owner, info.WeaponInfo, w, location, podTarget, info.WeaponDelay,
                                                info.EntryEffect, info.EntryEffectSequence, info.EntryEffectPalette));
                        w.Add(pod);
                    }
                }
            });
        }
Exemple #40
0
 public IRenderable OffsetBy(WVec vec)
 {
     return(new BeamRenderable(pos + vec, zOffset, length, width, color));
 }
Exemple #41
0
        public Pair <Actor[], Actor[]> SendParatroopers(Actor self, WPos target, int facing = -1)
        {
            var aircraft = new List <Actor>();
            var units    = new List <Actor>();

            var info = Info as ParatroopersPowerInfo;

            if (facing < 0)
            {
                facing = 256 * self.World.SharedRandom.Next(info.QuantizedFacings) / info.QuantizedFacings;
            }

            var       utLower = info.UnitType.ToLowerInvariant();
            ActorInfo unitType;

            if (!self.World.Map.Rules.Actors.TryGetValue(utLower, out unitType))
            {
                throw new YamlException("Actors ruleset does not include the entry '{0}'".F(utLower));
            }

            var altitude     = unitType.TraitInfo <AircraftInfo>().CruiseAltitude.Length;
            var dropRotation = WRot.FromFacing(facing);
            var delta        = new WVec(0, -1024, 0).Rotate(dropRotation);

            target = target + new WVec(0, 0, altitude);
            var startEdge  = target - (self.World.Map.DistanceToEdge(target, -delta) + info.Cordon).Length * delta / 1024;
            var finishEdge = target + (self.World.Map.DistanceToEdge(target, delta) + info.Cordon).Length * delta / 1024;

            Actor  camera          = null;
            Beacon beacon          = null;
            var    aircraftInRange = new Dictionary <Actor, bool>();

            Action <Actor> onEnterRange = a =>
            {
                // Spawn a camera and remove the beacon when the first plane enters the target area
                if (info.CameraActor != null && camera == null && !aircraftInRange.Any(kv => kv.Value))
                {
                    self.World.AddFrameEndTask(w =>
                    {
                        camera = w.CreateActor(info.CameraActor, new TypeDictionary
                        {
                            new LocationInit(self.World.Map.CellContaining(target)),
                            new OwnerInit(self.Owner),
                        });
                    });
                }

                RemoveBeacon(beacon);

                if (!aircraftInRange.Any(kv => kv.Value))
                {
                    Game.Sound.PlayNotification(self.World.Map.Rules, self.Owner, "Speech",
                                                info.ReinforcementsArrivedSpeechNotification, self.Owner.Faction.InternalName);
                }

                aircraftInRange[a] = true;
            };

            Action <Actor> onExitRange = a =>
            {
                aircraftInRange[a] = false;

                // Remove the camera when the final plane leaves the target area
                if (!aircraftInRange.Any(kv => kv.Value))
                {
                    RemoveCamera(camera);
                }
            };

            Action <Actor> onRemovedFromWorld = a =>
            {
                aircraftInRange[a] = false;

                // Checking for attack range is not relevant here because
                // aircraft may be shot down before entering. Thus we remove
                // the camera and beacon only if the whole squad is dead.
                if (aircraftInRange.All(kv => kv.Key.IsDead))
                {
                    RemoveCamera(camera);
                    RemoveBeacon(beacon);
                }
            };

            // Create the actors immediately so they can be returned
            for (var i = -info.SquadSize / 2; i <= info.SquadSize / 2; i++)
            {
                // Even-sized squads skip the lead plane
                if (i == 0 && (info.SquadSize & 1) == 0)
                {
                    continue;
                }

                // Includes the 90 degree rotation between body and world coordinates
                var so          = info.SquadOffset;
                var spawnOffset = new WVec(i * so.Y, -Math.Abs(i) * so.X, 0).Rotate(dropRotation);

                aircraft.Add(self.World.CreateActor(false, info.UnitType, new TypeDictionary
                {
                    new CenterPositionInit(startEdge + spawnOffset),
                    new OwnerInit(self.Owner),
                    new FacingInit(facing),
                }));
            }

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

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

                Actor distanceTestActor = null;

                var passengersPerPlane = (info.DropItems.Length + info.SquadSize - 1) / info.SquadSize;
                var added = 0;
                var j     = 0;
                for (var i = -info.SquadSize / 2; i <= info.SquadSize / 2; i++)
                {
                    // Even-sized squads skip the lead plane
                    if (i == 0 && (info.SquadSize & 1) == 0)
                    {
                        continue;
                    }

                    // Includes the 90 degree rotation between body and world coordinates
                    var so           = info.SquadOffset;
                    var spawnOffset  = new WVec(i * so.Y, -Math.Abs(i) * so.X, 0).Rotate(dropRotation);
                    var targetOffset = new WVec(i * so.Y, 0, 0).Rotate(dropRotation);
                    var a            = aircraft[j++];
                    w.Add(a);

                    var drop = a.Trait <ParaDrop>();
                    drop.SetLZ(w.Map.CellContaining(target + targetOffset), !info.AllowImpassableCells);
                    drop.OnEnteredDropRange += onEnterRange;
                    drop.OnExitedDropRange  += onExitRange;
                    drop.OnRemovedFromWorld += onRemovedFromWorld;

                    var cargo = a.Trait <Cargo>();
                    foreach (var unit in units.Skip(added).Take(passengersPerPlane))
                    {
                        cargo.Load(a, unit);
                        added++;
                    }

                    a.QueueActivity(new Fly(a, Target.FromPos(target + spawnOffset)));
                    a.QueueActivity(new Fly(a, Target.FromPos(finishEdge + spawnOffset)));
                    a.QueueActivity(new RemoveSelf());
                    aircraftInRange.Add(a, false);
                    distanceTestActor = a;
                }

                // Dispose any unused units
                for (var i = added; i < units.Count; i++)
                {
                    units[i].Dispose();
                }

                if (Info.DisplayBeacon)
                {
                    var distance = (target - startEdge).HorizontalLength;

                    beacon = new Beacon(
                        self.Owner,
                        target - new WVec(0, 0, altitude),
                        Info.BeaconPaletteIsPlayerPalette,
                        Info.BeaconPalette,
                        Info.BeaconImage,
                        Info.BeaconPoster,
                        Info.BeaconPosterPalette,
                        Info.BeaconSequence,
                        Info.ArrowSequence,
                        Info.CircleSequence,
                        Info.ClockSequence,
                        () => 1 - ((distanceTestActor.CenterPosition - target).HorizontalLength - info.BeaconDistanceOffset.Length) * 1f / distance,
                        Info.BeaconDelay);

                    w.Add(beacon);
                }
            });

            return(Pair.New(aircraft.ToArray(), units.ToArray()));
        }
Exemple #42
0
        public void Tick(World world)
        {
            // Fade the trail out gradually
            if (exploded && info.ContrailLength > 0)
            {
                trail.Update(pos);
                return;
            }

            ticks++;
            anim.Tick();

            // Missile tracks target
            if (args.GuidedTarget.IsValidFor(args.SourceActor))
            {
                targetPosition = args.GuidedTarget.CenterPosition;
            }

            var dist            = targetPosition + offset - pos;
            var desiredFacing   = Traits.Util.GetFacing(dist, facing);
            var desiredAltitude = targetPosition.Z;
            var jammed          = info.Jammable && world.ActorsWithTrait <JamsMissiles>().Any(j => JammedBy(j));

            if (jammed)
            {
                desiredFacing   = facing + world.SharedRandom.Next(-20, 21);
                desiredAltitude = world.SharedRandom.Next(-43, 86);
            }
            else if (!args.GuidedTarget.IsValidFor(args.SourceActor))
            {
                desiredFacing = facing;
            }

            facing = Traits.Util.TickFacing(facing, desiredFacing, info.ROT);
            var move = new WVec(0, -1024, 0).Rotate(WRot.FromFacing(facing)) * speed / 1024;

            if (targetPosition.Z > 0 && info.TurboBoost)
            {
                move = (move * 3) / 2;
            }

            if (pos.Z != desiredAltitude)
            {
                var delta = move.HorizontalLength * info.MaximumPitch.Tan() / 1024;
                var dz    = (targetPosition.Z - pos.Z).Clamp(-delta, delta);
                move += new WVec(0, 0, dz);
            }

            pos += move;

            if (info.Trail != null && --ticksToNextSmoke < 0)
            {
                world.AddFrameEndTask(w => w.Add(new Smoke(w, pos - 3 * move / 2, info.Trail)));
                ticksToNextSmoke = info.TrailInterval;
            }

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

            var shouldExplode = (pos.Z < 0) ||          // Hit the ground
                                (dist.LengthSquared < MissileCloseEnough.Range * MissileCloseEnough.Range) || // Within range
                                (info.RangeLimit != 0 && ticks > info.RangeLimit) || // Ran out of fuel
                                (!info.High && world.ActorMap.GetUnitsAt(pos.ToCPos())
                                 .Any(a => a.HasTrait <IBlocksBullets>()));       // Hit a wall

            if (shouldExplode)
            {
                Explode(world);
            }
        }
		public Actor[] SendParatroopers(Actor self, WPos target, bool randomize = true, int dropFacing = 0)
		{
			var units = new List<Actor>();

			var info = Info as ParatroopersPowerInfo;

			if (randomize)
				dropFacing = 256 * self.World.SharedRandom.Next(info.QuantizedFacings) / info.QuantizedFacings;

			var utLower = info.UnitType.ToLowerInvariant();
			ActorInfo unitType;
			if (!self.World.Map.Rules.Actors.TryGetValue(utLower, out unitType))
				throw new YamlException("Actors ruleset does not include the entry '{0}'".F(utLower));

			var altitude = unitType.TraitInfo<AircraftInfo>().CruiseAltitude.Length;
			var dropRotation = WRot.FromFacing(dropFacing);
			var delta = new WVec(0, -1024, 0).Rotate(dropRotation);
			target = target + new WVec(0, 0, altitude);
			var startEdge = target - (self.World.Map.DistanceToEdge(target, -delta) + info.Cordon).Length * delta / 1024;
			var finishEdge = target + (self.World.Map.DistanceToEdge(target, delta) + info.Cordon).Length * delta / 1024;

			Actor camera = null;
			Beacon beacon = null;
			var aircraftInRange = new Dictionary<Actor, bool>();

			Action<Actor> onEnterRange = a =>
			{
				// Spawn a camera and remove the beacon when the first plane enters the target area
				if (info.CameraActor != null && !aircraftInRange.Any(kv => kv.Value))
				{
					self.World.AddFrameEndTask(w =>
					{
						camera = w.CreateActor(info.CameraActor, new TypeDictionary
						{
							new LocationInit(self.World.Map.CellContaining(target)),
							new OwnerInit(self.Owner),
						});
					});
				}

				if (beacon != null)
				{
					self.World.AddFrameEndTask(w =>
					{
						w.Remove(beacon);
						beacon = null;
					});
				}

				if (!aircraftInRange.Any(kv => kv.Value))
					Game.Sound.PlayNotification(self.World.Map.Rules, self.Owner, "Speech",
						info.ReinforcementsArrivedSpeechNotification, self.Owner.Faction.InternalName);

				aircraftInRange[a] = true;
			};

			Action<Actor> onExitRange = a =>
			{
				aircraftInRange[a] = false;

				// Remove the camera when the final plane leaves the target area
				if (!aircraftInRange.Any(kv => kv.Value))
				{
					if (camera != null)
					{
						camera.QueueActivity(new Wait(info.CameraRemoveDelay));
						camera.QueueActivity(new RemoveSelf());
					}

					camera = null;

					if (beacon != null)
					{
						self.World.AddFrameEndTask(w =>
						{
							w.Remove(beacon);
							beacon = null;
						});
					}
				}
			};

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

				units.Add(unit);
			}

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

				Actor distanceTestActor = null;

				var passengersPerPlane = (info.DropItems.Length + info.SquadSize - 1) / info.SquadSize;
				var added = 0;
				for (var i = -info.SquadSize / 2; i <= info.SquadSize / 2; i++)
				{
					// Even-sized squads skip the lead plane
					if (i == 0 && (info.SquadSize & 1) == 0)
						continue;

					// Includes the 90 degree rotation between body and world coordinates
					var so = info.SquadOffset;
					var spawnOffset = new WVec(i * so.Y, -Math.Abs(i) * so.X, 0).Rotate(dropRotation);
					var targetOffset = new WVec(i * so.Y, 0, 0).Rotate(dropRotation);

					var a = w.CreateActor(info.UnitType, new TypeDictionary
					{
						new CenterPositionInit(startEdge + spawnOffset),
						new OwnerInit(self.Owner),
						new FacingInit(dropFacing),
					});

					var drop = a.Trait<ParaDrop>();
					drop.SetLZ(w.Map.CellContaining(target + targetOffset), !info.AllowImpassableCells);
					drop.OnEnteredDropRange += onEnterRange;
					drop.OnExitedDropRange += onExitRange;
					drop.OnRemovedFromWorld += onExitRange;

					var cargo = a.Trait<Cargo>();
					var passengers = units.Skip(added).Take(passengersPerPlane);
					added += passengersPerPlane;

					foreach (var p in passengers)
						cargo.Load(a, p);

					a.QueueActivity(new Fly(a, Target.FromPos(target + spawnOffset)));
					a.QueueActivity(new Fly(a, Target.FromPos(finishEdge + spawnOffset)));
					a.QueueActivity(new RemoveSelf());
					aircraftInRange.Add(a, false);
					distanceTestActor = a;
				}

				if (Info.DisplayBeacon)
				{
					var distance = (target - startEdge).HorizontalLength;

					beacon = new Beacon(
						self.Owner,
						target - new WVec(0, 0, altitude),
						Info.BeaconPaletteIsPlayerPalette,
						Info.BeaconPalette,
						Info.BeaconImage,
						Info.BeaconPoster,
						Info.BeaconPosterPalette,
						Info.ArrowSequence,
						Info.CircleSequence,
						Info.ClockSequence,
						() => 1 - ((distanceTestActor.CenterPosition - target).HorizontalLength - info.BeaconDistanceOffset.Length) * 1f / distance,
						Info.BeaconDelay);

					w.Add(beacon);
				}
			});

			return units.ToArray();
		}
Exemple #44
0
 public IRenderable OffsetBy(WVec vec)
 {
     return(new TeslaZapRenderable(pos + vec, zOffset, length, image, brightSequence, brightZaps, dimSequence, dimZaps, palette));
 }
Exemple #45
0
        // For scaling vectors to pixel sizes in the voxel renderer
        public float[] ScreenVector(WVec vec)
        {
            var ts = Game.modData.Manifest.TileSize;

            return(new float[] { ts.Width *vec.X / 1024f, ts.Height *vec.Y / 1024f, ts.Height *vec.Z / 1024f, 1 });
        }
Exemple #46
0
 public WVec LocalToWorld(WVec vec)
 {
     // RA's 2d perspective doesn't correspond to an orthonormal 3D
     // coordinate system, so fudge the y axis to make things look good
     return(new WVec(vec.Y, -info.CameraPitch.Sin() * vec.X / 1024, vec.Z));
 }
Exemple #47
0
 public CarryallDeliverUnitTargeter(AircraftInfo aircraftInfo, CarryallInfo info, WVec carryableOffset)
     : base(aircraftInfo)
 {
     OrderID              = "DeliverUnit";
     OrderPriority        = 6;
     this.carryableOffset = carryableOffset;
     this.aircraftInfo    = aircraftInfo;
     this.info            = info;
 }
Exemple #48
0
 public IRenderable OffsetBy(WVec vec)
 {
     return(new VoxelRenderable(voxels, pos + vec, zOffset, camera, scale,
                                lightSource, lightAmbientColor, lightDiffuseColor,
                                palette, normalsPalette, shadowPalette));
 }
Exemple #49
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.Weapon.TargetActorCenter ? args.GuidedTarget.CenterPosition : args.GuidedTarget.Positions.PositionClosestTo(args.Source))
                            + new WVec(WDist.Zero, WDist.Zero, info.AirburstAltitude);
            }

            // Compute target's predicted velocity vector (assuming uniform circular motion)
            var yaw1 = tarVel.HorizontalLengthSquared != 0 ? tarVel.Yaw : WAngle.FromFacing(hFacing);

            tarVel = newTarPos - targetPosition;
            var yaw2 = tarVel.HorizontalLengthSquared != 0 ? tarVel.Yaw : WAngle.FromFacing(hFacing);

            predVel        = tarVel.Rotate(WRot.FromYaw(yaw2 - yaw1));
            targetPosition = newTarPos;

            // Compute current distance from target position
            var tarDistVec    = targetPosition + offset - pos;
            var relTarDist    = tarDistVec.Length;
            var relTarHorDist = tarDistVec.HorizontalLength;

            WVec move;

            if (state == States.Freefall)
            {
                move = FreefallTick();
            }
            else
            {
                move = HomingTick(world, tarDistVec, relTarHorDist);
            }

            renderFacing = new WVec(move.X, move.Y - move.Z, 0).Yaw.Facing;

            // Move the missile
            var lastPos = pos;

            if (info.AllowSnapping && state != States.Freefall && relTarDist < move.Length)
            {
                pos = targetPosition + offset;
            }
            else
            {
                pos += move;
            }

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

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

            // Create the sprite trail effect
            if (!string.IsNullOrEmpty(info.TrailImage) && --ticksToNextSmoke < 0 && (state != States.Freefall || info.TrailWhenDeactivated))
            {
                world.AddFrameEndTask(w => w.Add(new SpriteEffect(pos - 3 * move / 2, w, info.TrailImage, info.TrailSequences.Random(world.SharedRandom),
                                                                  trailPalette, facing: renderFacing)));

                ticksToNextSmoke = info.TrailInterval;
            }

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

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

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

            if (!string.IsNullOrEmpty(info.PointDefenseType))
            {
                shouldExplode |= world.ActorsWithTrait <IPointDefense>().Any(x => x.Trait.Destroy(pos, args.SourceActor.Owner, info.PointDefenseType));
            }

            if (shouldExplode)
            {
                Explode(world);
            }
        }
Exemple #50
0
 public IRenderable OffsetBy(WVec vec)
 {
     return(new RangeCircleRenderable(centerPosition + vec, radius, zOffset, color, contrastColor));
 }
Exemple #51
0
        int HomingInnerTick(int predClfDist, int diffClfMslHgt, int relTarHorDist, int lastHtChg, int lastHt,
                            int nxtRelTarHorDist, int relTarHgt, int vFacing, bool targetPassedBy)
        {
            int desiredVFacing = vFacing;

            // Incline coming up -> attempt to reach the incline so that after predClfDist
            // the height above the terrain is positive but as close to 0 as possible
            // Also, never change horizontal facing and never travel backwards
            // Possible techniques to avoid close cliffs are deceleration, turning
            // as sharply as possible to travel directly upwards and then returning
            // to zero vertical facing as low as possible while still not hitting the
            // high terrain. A last technique (and the preferred one, normally used when
            // the missile hasn't been fired near a cliff) is simply finding the smallest
            // vertical facing that allows for a smooth climb to the new terrain's height
            // and coming in at predClfDist at exactly zero vertical facing
            if (info.TerrainHeightAware && diffClfMslHgt >= 0 && !allowPassBy)
            {
                desiredVFacing = IncreaseAltitude(predClfDist, diffClfMslHgt, relTarHorDist, vFacing);
            }
            else if (relTarHorDist <= 3 * loopRadius || state == States.Hitting)
            {
                // No longer travel at cruise altitude
                state = States.Hitting;

                if (lastHt >= targetPosition.Z)
                {
                    allowPassBy = true;
                }

                if (!allowPassBy && (lastHt < targetPosition.Z || targetPassedBy))
                {
                    // Aim for the target
                    var vDist = new WVec(-relTarHgt, -relTarHorDist, 0);
                    desiredVFacing = (sbyte)vDist.HorizontalLengthSquared != 0 ? vDist.Yaw.Facing : vFacing;

                    // Do not accept -1  as valid vertical facing since it is usually a numerical error
                    // and will lead to premature descent and crashing into the ground
                    if (desiredVFacing == -1)
                    {
                        desiredVFacing = 0;
                    }

                    // If the target has been passed by, limit the absolute value of
                    // vertical facing by the maximum vertical rate of turn
                    // Do this because the missile will be looping horizontally
                    // and thus needs smaller vertical facings so as not
                    // to hit the ground prematurely
                    if (targetPassedBy)
                    {
                        desiredVFacing = desiredVFacing.Clamp(-info.VerticalRateOfTurn, info.VerticalRateOfTurn);
                    }
                    else if (lastHt == 0)
                    {                     // Before the target is passed by, missile speed should be changed
                                          // Target's height above loop's center
                        var tarHgt = (loopRadius * WAngle.FromFacing(vFacing).Cos() / 1024 - System.Math.Abs(relTarHgt)).Clamp(0, loopRadius);

                        // Target's horizontal distance from loop's center
                        var tarDist = Exts.ISqrt(loopRadius * loopRadius - tarHgt * tarHgt);

                        // Missile's horizontal distance from loop's center
                        var missDist = loopRadius * WAngle.FromFacing(vFacing).Sin() / 1024;

                        // If the current height does not permit the missile
                        // to hit the target before passing it by, lower speed
                        // Otherwise, increase speed
                        if (relTarHorDist <= tarDist - System.Math.Sign(relTarHgt) * missDist)
                        {
                            ChangeSpeed(-1);
                        }
                        else
                        {
                            ChangeSpeed();
                        }
                    }
                }
                else if (allowPassBy || (lastHt != 0 && relTarHorDist - lastHtChg < loopRadius))
                {
                    // Only activate this part if target too close to cliff
                    allowPassBy = true;

                    // Vector from missile's current position pointing to the loop's center
                    var radius = new WVec(loopRadius, 0, 0)
                                 .Rotate(new WRot(WAngle.Zero, WAngle.Zero, WAngle.FromFacing(64 - vFacing)));

                    // Vector from loop's center to incline top hardcoded in height buffer zone
                    var edgeVector = new WVec(lastHtChg, lastHt - pos.Z, 0) - radius;

                    if (!targetPassedBy)
                    {
                        // Climb to critical height
                        if (relTarHorDist > 2 * loopRadius)
                        {
                            // Target's distance from cliff
                            var d1 = relTarHorDist - lastHtChg;
                            if (d1 < 0)
                            {
                                d1 = 0;
                            }
                            if (d1 > 2 * loopRadius)
                            {
                                return(0);
                            }

                            // Find critical height at which the missile must be once it is at one loopRadius
                            // away from the target
                            var h1 = loopRadius - Exts.ISqrt(d1 * (2 * loopRadius - d1)) - (pos.Z - lastHt);

                            if (h1 > loopRadius * (1024 - WAngle.FromFacing(vFacing).Cos()) / 1024)
                            {
                                desiredVFacing = WAngle.ArcTan(Exts.ISqrt(h1 * (2 * loopRadius - h1)), loopRadius - h1).Angle >> 2;
                            }
                            else
                            {
                                desiredVFacing = 0;
                            }

                            // TODO: deceleration checks!!!
                        }
                        else
                        {
                            // Avoid the cliff edge
                            if (info.TerrainHeightAware && edgeVector.Length > loopRadius && lastHt > targetPosition.Z)
                            {
                                int vFac;
                                for (vFac = vFacing + 1; vFac <= vFacing + info.VerticalRateOfTurn - 1; vFac++)
                                {
                                    // Vector from missile's current position pointing to the loop's center
                                    radius = new WVec(loopRadius, 0, 0)
                                             .Rotate(new WRot(WAngle.Zero, WAngle.Zero, WAngle.FromFacing(64 - vFac)));

                                    // Vector from loop's center to incline top + 64 hardcoded in height buffer zone
                                    edgeVector = new WVec(lastHtChg, lastHt - pos.Z, 0) - radius;
                                    if (edgeVector.Length <= loopRadius)
                                    {
                                        break;
                                    }
                                }

                                desiredVFacing = vFac;
                            }
                            else
                            {
                                // Aim for the target
                                var vDist = new WVec(-relTarHgt, -relTarHorDist, 0);
                                desiredVFacing = (sbyte)vDist.HorizontalLengthSquared != 0 ? vDist.Yaw.Facing : vFacing;
                                if (desiredVFacing < 0 && info.VerticalRateOfTurn < (sbyte)vFacing)
                                {
                                    desiredVFacing = 0;
                                }
                            }
                        }
                    }
                    else
                    {
                        // Aim for the target
                        var vDist = new WVec(-relTarHgt, relTarHorDist, 0);
                        desiredVFacing = (sbyte)vDist.HorizontalLengthSquared != 0 ? vDist.Yaw.Facing : vFacing;
                        if (desiredVFacing < 0 && info.VerticalRateOfTurn < (sbyte)vFacing)
                        {
                            desiredVFacing = 0;
                        }
                    }
                }
                else
                {
                    // Aim to attain cruise altitude as soon as possible while having the absolute value
                    // of vertical facing bound by the maximum vertical rate of turn
                    var vDist = new WVec(-diffClfMslHgt - info.CruiseAltitude.Length, -speed, 0);
                    desiredVFacing = (sbyte)vDist.HorizontalLengthSquared != 0 ? vDist.Yaw.Facing : vFacing;

                    // If the missile is launched above CruiseAltitude, it has to descend instead of climbing
                    if (-diffClfMslHgt > info.CruiseAltitude.Length)
                    {
                        desiredVFacing = -desiredVFacing;
                    }

                    desiredVFacing = desiredVFacing.Clamp(-info.VerticalRateOfTurn, info.VerticalRateOfTurn);

                    ChangeSpeed();
                }
            }
            else
            {
                // Aim to attain cruise altitude as soon as possible while having the absolute value
                // of vertical facing bound by the maximum vertical rate of turn
                var vDist = new WVec(-diffClfMslHgt - info.CruiseAltitude.Length, -speed, 0);
                desiredVFacing = (sbyte)vDist.HorizontalLengthSquared != 0 ? vDist.Yaw.Facing : vFacing;

                // If the missile is launched above CruiseAltitude, it has to descend instead of climbing
                if (-diffClfMslHgt > info.CruiseAltitude.Length)
                {
                    desiredVFacing = -desiredVFacing;
                }

                desiredVFacing = desiredVFacing.Clamp(-info.VerticalRateOfTurn, info.VerticalRateOfTurn);

                ChangeSpeed();
            }

            return(desiredVFacing);
        }
 public IRenderable OffsetBy(WVec vec)
 {
     return(new TextRenderable(font, pos + vec, zOffset, color, text));
 }
Exemple #53
0
        protected virtual void FireBarrel(Actor self, IFacing facing, Target target, Barrel barrel)
        {
            Func <WPos> muzzlePosition = () => self.CenterPosition + MuzzleOffset(self, barrel);
            var         legacyFacing   = MuzzleOrientation(self, barrel).Yaw.Angle / 4;

            var passiveTarget = Weapon.TargetActorCenter ? target.CenterPosition : target.Positions.PositionClosestTo(muzzlePosition());
            var initialOffset = Weapon.FirstBurstTargetOffset;

            if (initialOffset != WVec.Zero)
            {
                // We want this to match Armament.LocalOffset, so we need to convert it to forward, right, up
                initialOffset  = new WVec(initialOffset.Y, -initialOffset.X, initialOffset.Z);
                passiveTarget += initialOffset.Rotate(WRot.FromFacing(legacyFacing));
            }

            var followingOffset = Weapon.FollowingBurstTargetOffset;

            if (followingOffset != WVec.Zero)
            {
                // We want this to match Armament.LocalOffset, so we need to convert it to forward, right, up
                followingOffset = new WVec(followingOffset.Y, -followingOffset.X, followingOffset.Z);
                passiveTarget  += ((Weapon.Burst - Burst) * followingOffset).Rotate(WRot.FromFacing(legacyFacing));
            }

            var args = new ProjectileArgs
            {
                Weapon = Weapon,
                Facing = legacyFacing,

                DamageModifiers = damageModifiers.ToArray(),

                InaccuracyModifiers = inaccuracyModifiers.ToArray(),

                RangeModifiers = rangeModifiers.ToArray(),

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

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

            ScheduleDelayedAction(Info.FireDelay, () =>
            {
                if (args.Weapon.Projectile != null)
                {
                    var projectile = args.Weapon.Projectile.Create(args);
                    if (projectile != null)
                    {
                        self.World.Add(projectile);
                    }

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

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

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

                    Recoil = Info.Recoil;
                }
            });
        }
 public VoxelHarvesterDockSequence(Actor self, Actor refinery, int dockAngle, bool isDragRequired, WVec dragOffset, int dragLength) :
     base(self, refinery, dockAngle, isDragRequired, dragOffset, dragLength)
 {
     body          = self.Trait <WithVoxelUnloadBody>();
     spriteOverlay = refinery.TraitOrDefault <WithDockingOverlay>();
 }
Exemple #55
0
 public WVec LocalToWorld(WVec vec)
 {
     return(info.LocalToWorld(vec));
 }
 public IRenderable OffsetBy(WVec vec)
 {
     return(new UITextRenderable(font, effectiveWorldPos + vec, screenPos, zOffset, color, text));
 }
        public override Activity Tick(Actor self)
        {
            // Refuse to take off if it would land immediately again.
            // Special case: Don't kill other deploy hotkey activities.
            if (aircraft.ForceLanding)
            {
                return(NextActivity);
            }

            if (IsCanceled)
            {
                return(NextActivity);
            }

            if (dest == null || dest.IsDead || Reservable.IsReserved(dest))
            {
                dest = ChooseResupplier(self, true);
            }

            var initialFacing = aircraft.Info.InitialFacing;

            if (dest == null || dest.IsDead)
            {
                var nearestResupplier = ChooseResupplier(self, false);

                // If a heli was told to return and there's no (available) RearmBuilding, going to the probable next queued activity (HeliAttack)
                // would be pointless (due to lack of ammo), and possibly even lead to an infinite loop due to HeliAttack.cs:L79.
                if (nearestResupplier == null && aircraft.Info.LandWhenIdle)
                {
                    if (aircraft.Info.TurnToLand)
                    {
                        return(ActivityUtils.SequenceActivities(new Turn(self, initialFacing), new HeliLand(self, true)));
                    }

                    return(new HeliLand(self, true));
                }
                else if (nearestResupplier == null && !aircraft.Info.LandWhenIdle)
                {
                    return(null);
                }
                else
                {
                    var distanceFromResupplier = (nearestResupplier.CenterPosition - self.CenterPosition).HorizontalLength;
                    var distanceLength         = aircraft.Info.WaitDistanceFromResupplyBase.Length;

                    // If no pad is available, move near one and wait
                    if (distanceFromResupplier > distanceLength)
                    {
                        var randomPosition = WVec.FromPDF(self.World.SharedRandom, 2) * distanceLength / 1024;

                        var target = Target.FromPos(nearestResupplier.CenterPosition + randomPosition);

                        return(ActivityUtils.SequenceActivities(new HeliFly(self, target, WDist.Zero, aircraft.Info.WaitDistanceFromResupplyBase), this));
                    }

                    return(this);
                }
            }

            var landingProcedures = new List <Activity>();
            var exit   = dest.FirstExitOrDefault(null);
            var offset = exit != null ? exit.Info.SpawnOffset : WVec.Zero;

            landingProcedures.Add(new HeliFly(self, Target.FromPos(dest.CenterPosition + offset)));

            if (ShouldLandAtBuilding(self, dest))
            {
                aircraft.MakeReservation(dest);

                if (aircraft.Info.TurnToDock)
                {
                    landingProcedures.Add(new Turn(self, initialFacing));
                }

                landingProcedures.Add(new HeliLand(self, false));
                landingProcedures.Add(new ResupplyAircraft(self));
                if (!abortOnResupply)
                {
                    landingProcedures.Add(NextActivity);
                }
            }
            else
            {
                landingProcedures.Add(NextActivity);
            }

            return(ActivityUtils.SequenceActivities(landingProcedures.ToArray()));
        }
 public SpriteHarvesterDockSequence(Actor self, Actor refinery, int dockAngle, bool isDragRequired, WVec dragOffset, int dragLength) : base(self, refinery, dockAngle, isDragRequired, dragOffset, dragLength)
 {
     wsb = self.Trait <WithSpriteBody>();
     wda = self.Info.TraitInfo <WithDockingAnimationInfo>();
 }
Exemple #59
0
        public override void Activate(Actor self, Order order, SupportPowerManager manager)
        {
            base.Activate(self, order, manager);

            var info           = Info as AirstrikePowerInfo;
            var attackFacing   = Util.QuantizeFacing(self.World.SharedRandom.Next(256), info.QuantizedFacings) * (256 / info.QuantizedFacings);
            var attackRotation = WRot.FromFacing(attackFacing);
            var delta          = new WVec(0, -1024, 0).Rotate(attackRotation);

            var altitude   = self.World.Map.Rules.Actors[info.UnitType].Traits.Get <PlaneInfo>().CruiseAltitude.Range;
            var target     = self.World.Map.CenterOfCell(order.TargetLocation) + new WVec(0, 0, altitude);
            var startEdge  = target - (self.World.Map.DistanceToEdge(target, -delta) + info.Cordon).Range * delta / 1024;
            var finishEdge = target + (self.World.Map.DistanceToEdge(target, delta) + info.Cordon).Range * delta / 1024;

            Actor  camera          = null;
            Beacon beacon          = null;
            var    aircraftInRange = new Dictionary <Actor, bool>();

            Action <Actor> onEnterRange = a =>
            {
                // Spawn a camera and remove the beacon when the first plane enters the target area
                if (info.CameraActor != null && !aircraftInRange.Any(kv => kv.Value))
                {
                    self.World.AddFrameEndTask(w =>
                    {
                        camera = w.CreateActor(info.CameraActor, new TypeDictionary
                        {
                            new LocationInit(order.TargetLocation),
                            new OwnerInit(self.Owner),
                        });
                    });
                }

                if (beacon != null)
                {
                    self.World.AddFrameEndTask(w =>
                    {
                        w.Remove(beacon);
                        beacon = null;
                    });
                }

                aircraftInRange[a] = true;
            };

            Action <Actor> onExitRange = a =>
            {
                aircraftInRange[a] = false;

                // Remove the camera when the final plane leaves the target area
                if (!aircraftInRange.Any(kv => kv.Value))
                {
                    if (camera != null)
                    {
                        camera.QueueActivity(new Wait(info.CameraRemoveDelay));
                        camera.QueueActivity(new RemoveSelf());
                    }

                    camera = null;
                }
            };

            self.World.AddFrameEndTask(w =>
            {
                var notification = self.Owner.IsAlliedWith(self.World.RenderPlayer) ? Info.LaunchSound : Info.IncomingSound;
                Sound.Play(notification);

                Actor distanceTestActor = null;
                for (var i = -info.SquadSize / 2; i <= info.SquadSize / 2; i++)
                {
                    // Even-sized squads skip the lead plane
                    if (i == 0 && (info.SquadSize & 1) == 0)
                    {
                        continue;
                    }

                    // Includes the 90 degree rotation between body and world coordinates
                    var so           = info.SquadOffset;
                    var spawnOffset  = new WVec(i * so.Y, -Math.Abs(i) * so.X, 0).Rotate(attackRotation);
                    var targetOffset = new WVec(i * so.Y, 0, 0).Rotate(attackRotation);

                    var a = w.CreateActor(info.UnitType, new TypeDictionary
                    {
                        new CenterPositionInit(startEdge + spawnOffset),
                        new OwnerInit(self.Owner),
                        new FacingInit(attackFacing),
                    });

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

                    a.QueueActivity(new Fly(a, Target.FromPos(finishEdge + spawnOffset)));
                    a.QueueActivity(new RemoveSelf());
                    aircraftInRange.Add(a, false);
                    distanceTestActor = a;
                }

                if (Info.DisplayBeacon)
                {
                    var distance = (target - startEdge).HorizontalLength;

                    beacon = new Beacon(
                        order.Player,
                        self.World.Map.CenterOfCell(order.TargetLocation),
                        Info.BeaconPalettePrefix,
                        Info.BeaconPoster,
                        Info.BeaconPosterPalette,
                        () => 1 - ((distanceTestActor.CenterPosition - target).HorizontalLength - info.BeaconDistanceOffset.Range) * 1f / distance
                        );

                    w.Add(beacon);
                }
            });
        }
 public SpriteHarvesterDockSequence(Actor self, Actor refinery, int dockAngle, bool isDragRequired, WVec dragOffset, int dragLength)
     : base(self, refinery, dockAngle, isDragRequired, dragOffset, dragLength)
 {
     ru = self.Trait <RenderUnit>();
 }