Esempio n. 1
0
        CPos?FindDropLocation(CPos targetCell, WDist maxSearchDistance)
        {
            // The easy case
            if (positionable.CanEnterCell(targetCell))
            {
                return(targetCell);
            }

            var cellRange      = (maxSearchDistance.Length + 1023) / 1024;
            var centerPosition = self.World.Map.CenterOfCell(targetCell);

            foreach (var c in self.World.Map.FindTilesInCircle(targetCell, cellRange))
            {
                if (!positionable.CanEnterCell(c))
                {
                    continue;
                }

                var delta = self.World.Map.CenterOfCell(c) - centerPosition;
                if (delta.LengthSquared < maxSearchDistance.LengthSquared)
                {
                    return(c);
                }
            }

            return(null);
        }
Esempio n. 2
0
        // Find a suitable location to drop our carryable
        CPos GetLocationToDrop(CPos requestedPosition)
        {
            if (positionable.CanEnterCell(requestedPosition))
            {
                return(requestedPosition);
            }

            var candidateCells = Util.AdjacentCells(self.World, Target.FromCell(self.World, requestedPosition));

            // TODO: This will behave badly if there is no suitable drop point nearby
            do
            {
                foreach (var c in candidateCells)
                {
                    if (positionable.CanEnterCell(c))
                    {
                        return(c);
                    }
                }

                // Expanding dropable cells search area
                // TODO: This also includes all of the cells we have just checked
                candidateCells = Util.ExpandFootprint(candidateCells, true);
            } while (true);
        }
Esempio n. 3
0
        void INotifyParachuteLanded.OnLanded(Actor ignore)
        {
            if (!info.KilledOnImpassableTerrain)
            {
                return;
            }

            if (positionable.CanEnterCell(self.Location, self))
            {
                return;
            }

            if (ignore != null && self.World.ActorMap.GetActorsAt(self.Location).Any(a => a != ignore))
            {
                return;
            }

            var terrain = self.World.Map.GetTerrainInfo(self.Location);

            var sound = terrain.IsWater ? info.WaterImpactSound : info.GroundImpactSound;

            Game.Sound.Play(sound, self.CenterPosition);

            var sequence = terrain.IsWater ? info.WaterCorpseSequence : info.GroundCorpseSequence;
            var palette  = terrain.IsWater ? info.WaterCorpsePalette : info.GroundCorpsePalette;

            if (sequence != null && palette != null)
            {
                self.World.AddFrameEndTask(w => w.Add(new SpriteEffect(self.OccupiesSpace.CenterPosition, w, info.Image, sequence, palette)));
            }

            self.Kill(self);
        }
Esempio n. 4
0
        public void OnLanded()
        {
            if (!info.KilledOnImpassableTerrain)
            {
                return;
            }

            if (positionable.CanEnterCell(self.Location))
            {
                return;
            }

            var terrain = self.World.Map.GetTerrainInfo(self.Location);

            var sound = terrain.IsWater ? info.WaterImpactSound : info.GroundImpactSound;

            Sound.Play(sound, self.CenterPosition);

            var sequence = terrain.IsWater ? info.WaterCorpseSequence : info.GroundCorpseSequence;
            var palette  = terrain.IsWater ? info.WaterCorpsePalette : info.GroundCorpsePalette;

            self.World.AddFrameEndTask(w => w.Add(new Explosion(w, self.OccupiesSpace.CenterPosition, sequence, palette)));

            self.Kill(self);
        }
Esempio n. 5
0
        protected virtual Activity InnerTick(Actor self, AttackBase attack)
        {
            if (IsCanceled)
            {
                return(NextActivity);
            }

            var type = Target.Type;

            if (!Target.IsValidFor(self) || type == TargetType.FrozenActor)
            {
                return(NextActivity);
            }

            if (attack.Info.AttackRequiresEnteringCell && !positionable.CanEnterCell(Target.Actor.Location, null, false))
            {
                return(NextActivity);
            }

            // Drop the target if it moves under the shroud / fog.
            // HACK: This would otherwise break targeting frozen actors
            // The problem is that Shroud.IsTargetable returns false (as it should) for
            // frozen actors, but we do want to explicitly target the underlying actor here.
            if (!attack.Info.IgnoresVisibility && type == TargetType.Actor && !Target.Actor.Info.HasTraitInfo <FrozenUnderFogInfo>() && !self.Owner.CanTargetActor(Target.Actor))
            {
                return(NextActivity);
            }

            // Drop the target once none of the weapons are effective against it
            var armaments = attack.ChooseArmamentsForTarget(Target, forceAttack);

            if (!armaments.Any())
            {
                return(NextActivity);
            }

            // Update ranges
            minRange = armaments.Max(a => a.Weapon.MinRange);
            maxRange = armaments.Min(a => a.MaxRange());

            // Try to move within range
            if (move != null && (!Target.IsInRange(self.CenterPosition, maxRange) || Target.IsInRange(self.CenterPosition, minRange)))
            {
                return(Util.SequenceActivities(move.MoveWithinRange(Target, minRange, maxRange), this));
            }

            var desiredFacing = Util.GetFacing(Target.CenterPosition - self.CenterPosition, 0);

            if (facing.Facing != desiredFacing)
            {
                return(Util.SequenceActivities(new Turn(self, desiredFacing), this));
            }

            attack.DoAttack(self, Target, armaments);

            return(this);
        }
Esempio n. 6
0
        public override Activity Tick(Actor self)
        {
            if (countdown > 0)
            {
                countdown--;
                return(this);
            }

            // Wait for the worm to get back underground
            if (stance == AttackState.ReturningUnderground)
            {
                sandworm.IsAttacking = false;

                // There is a chance that the worm would just go away after attacking
                if (self.World.SharedRandom.Next() % 100 <= sandworm.Info.ChanceToDisappear)
                {
                    self.CancelActivity();
                    self.World.AddFrameEndTask(w => self.Kill(self));
                }
                else
                {
                    renderUnit.DefaultAnimation.ReplaceAnim("idle");
                }

                return(NextActivity);
            }

            // Wait for the worm to get in position
            if (stance == AttackState.Burrowed)
            {
                // This is so that the worm cancels an attack against a target that has reached solid rock
                if (!positionable.CanEnterCell(target.Actor.Location, null, false))
                {
                    return(NextActivity);
                }

                if (!WormAttack(self))
                {
                    renderUnit.DefaultAnimation.ReplaceAnim("idle");
                    return(NextActivity);
                }

                countdown = swallow.Info.ReturnTime;
                stance    = AttackState.ReturningUnderground;
            }

            return(this);
        }
Esempio n. 7
0
        void INotifyParachute.OnLanded(Actor self)
        {
            IsInAir = false;

            if (parachutingToken != Actor.InvalidConditionToken)
            {
                parachutingToken = self.RevokeCondition(parachutingToken);
            }

            if (!info.KilledOnImpassableTerrain)
            {
                return;
            }

            var cell = self.Location;

            if (positionable.CanEnterCell(cell, self))
            {
                return;
            }

            if (IgnoreActor != null && !self.World.ActorMap.GetActorsAt(cell)
                .Any(a => a != IgnoreActor && a != self && self.World.Map.DistanceAboveTerrain(a.CenterPosition) == WDist.Zero))
            {
                return;
            }

            var onWater = info.WaterTerrainTypes.Contains(self.World.Map.GetTerrainInfo(cell).Type);
            var sound   = onWater ? info.WaterImpactSound : info.GroundImpactSound;

            Game.Sound.Play(SoundType.World, sound, self.CenterPosition);

            var sequence = onWater ? info.WaterCorpseSequence : info.GroundCorpseSequence;
            var palette  = onWater ? info.WaterCorpsePalette : info.GroundCorpsePalette;

            if (!string.IsNullOrEmpty(info.Image) && !string.IsNullOrEmpty(sequence) && palette != null)
            {
                self.World.AddFrameEndTask(w => w.Add(new SpriteEffect(self.OccupiesSpace.CenterPosition, w, info.Image, sequence, palette)));
            }

            self.Kill(self, info.DamageTypes);
        }
Esempio n. 8
0
        void INotifyParachute.OnLanded(Actor self, Actor ignore)
        {
            IsInAir = false;

            if (parachutingToken != ConditionManager.InvalidConditionToken)
            {
                parachutingToken = conditionManager.RevokeCondition(self, parachutingToken);
            }

            if (!info.KilledOnImpassableTerrain)
            {
                return;
            }

            var cell = self.Location;

            if (positionable.CanEnterCell(cell, self))
            {
                return;
            }

            if (ignore != null && self.World.ActorMap.GetActorsAt(cell).Any(a => a != ignore))
            {
                return;
            }

            var onWater = info.WaterTerrainTypes.Contains(self.World.Map.GetTerrainInfo(cell).Type);

            var sound = onWater ? info.WaterImpactSound : info.GroundImpactSound;

            Game.Sound.Play(SoundType.World, sound, self.CenterPosition);

            var sequence = onWater ? info.WaterCorpseSequence : info.GroundCorpseSequence;
            var palette  = onWater ? info.WaterCorpsePalette : info.GroundCorpsePalette;

            if (sequence != null && palette != null)
            {
                self.World.AddFrameEndTask(w => w.Add(new SpriteEffect(self.OccupiesSpace.CenterPosition, w, info.Image, sequence, palette)));
            }

            self.Kill(self);
        }
Esempio n. 9
0
 // Can't be used in synced code, except with ignoreVis.
 public virtual bool CanChronoshiftTo(Actor self, CPos targetLocation)
 {
     // TODO: Allow enemy units to be chronoshifted into bad terrain to kill them
     return(!IsTraitDisabled && iPositionable != null && iPositionable.CanEnterCell(targetLocation));
 }
Esempio n. 10
0
        public override bool Tick(Actor self)
        {
            switch (stance)
            {
            case AttackState.Uninitialized:
                stance         = AttackState.Burrowed;
                countdown      = swallow.Info.AttackDelay;
                burrowLocation = self.Location;
                if (conditionManager != null && attackingToken == ConditionManager.InvalidConditionToken &&
                    !string.IsNullOrEmpty(swallow.Info.AttackingCondition))
                {
                    attackingToken = conditionManager.GrantCondition(self, swallow.Info.AttackingCondition);
                }
                break;

            case AttackState.Burrowed:
                if (--countdown > 0)
                {
                    return(false);
                }

                var targetLocation = target.Actor.Location;

                // The target has moved too far away
                if ((burrowLocation - targetLocation).Length > NearEnough)
                {
                    RevokeCondition(self);
                    return(true);
                }

                // The target reached solid ground
                if (!positionable.CanEnterCell(targetLocation, null, false))
                {
                    RevokeCondition(self);
                    return(true);
                }

                var targets = self.World.ActorMap.GetActorsAt(targetLocation)
                              .Where(t => !t.Equals(self) && weapon.IsValidAgainst(t, self));

                if (!targets.Any())
                {
                    RevokeCondition(self);
                    return(true);
                }

                stance               = AttackState.Attacking;
                countdown            = swallow.Info.ReturnDelay;
                sandworm.IsAttacking = true;
                AttackTargets(self, targets);

                break;

            case AttackState.Attacking:
                if (--countdown > 0)
                {
                    return(false);
                }

                sandworm.IsAttacking = false;

                // There is a chance that the worm would just go away after attacking
                if (self.World.SharedRandom.Next(100) <= sandworm.WormInfo.ChanceToDisappear)
                {
                    self.CancelActivity();
                    self.World.AddFrameEndTask(w => self.Dispose());
                }

                RevokeCondition(self);
                return(true);
            }

            return(false);
        }
Esempio n. 11
0
        protected virtual AttackStatus TickAttack(Actor self, AttackBase attack)
        {
            if (IsCanceled)
            {
                return(AttackStatus.UnableToAttack);
            }

            var type = Target.Type;

            if (!Target.IsValidFor(self) || type == TargetT.FrozenActor)
            {
                return(AttackStatus.UnableToAttack);
            }

            if (attack.Info.AttackRequiresEnteringCell && !positionable.CanEnterCell(Target.Actor.Location, null, false))
            {
                return(AttackStatus.UnableToAttack);
            }

            //Drop the target if it moves under the shroud / fog.
            if (!attack.Info.IgnoresVisibility && type == TargetT.Actor &&
                !Target.Actor.Info.HasTraitInfo <FrozenUnderFogInfo>() &&
                !Target.Actor.CanBeViewedByPlayer(self.Owner))
            {
                return(AttackStatus.UnableToAttack);
            }

            //Drop the target once none of the weapons are effective against it

            var armaments = attack.ChooseArmamentsForTarget(Target, forceAttack).ToList();

            if (armaments.Count == 0)
            {
                return(AttackStatus.UnableToAttack);
            }

            //Update ranges
            minRange = armaments.Max(a => a.Weapon.MinRange);
            maxRange = armaments.Min(a => a.MaxRange());

            var pos    = self.CenterPosition;
            var mobile = move as Mobile;

            if (!Target.IsInRange(pos, maxRange) ||
                (minRange.Length != 0 && Target.IsInRange(pos, minRange)) ||
                (mobile != null && !mobile.CanInteractWithGroundLayer(self)))
            {
                //Try to move within range,drop the target otherwise.
                if (move == null)
                {
                    return(AttackStatus.UnableToAttack);
                }

                attackStatus |= AttackStatus.NeedsToMove;
                moveActivity  = ActivityUtils.SequenceActivities(move.MoveWithinRange(Target, minRange, maxRange), this);
                return(AttackStatus.NeedsToMove);
            }

            var targetedPosition = attack.GetTargetPosition(pos, Target);
            var desiredFacing    = (targetedPosition - pos).Yaw.Facing;

            if (!Util.FacingWithinTolerance(facing.Facing, desiredFacing, facingTolerance))
            {
                attackStatus |= AttackStatus.NeedsToTurn;
                turnActivity  = ActivityUtils.SequenceActivities(new Turn(self, desiredFacing), this);
                return(AttackStatus.NeedsToTurn);
            }

            attackStatus |= AttackStatus.Attacking;
            attack.DoAttack(self, Target, armaments);

            return(AttackStatus.Attacking);
        }
Esempio n. 12
0
        protected virtual AttackStatus TickAttack(Actor self, AttackFrontal attack)
        {
            if (!target.IsValidFor(self))
            {
                return(AttackStatus.UnableToAttack);
            }

            if (attack.Info.AttackRequiresEnteringCell && !positionable.CanEnterCell(target.Actor.Location, null, false))
            {
                return(AttackStatus.UnableToAttack);
            }

            if (!attack.Info.TargetFrozenActors && !forceAttack && target.Type == TargetType.FrozenActor)
            {
                // Try to move within range, drop the target otherwise
                if (move == null)
                {
                    return(AttackStatus.UnableToAttack);
                }

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

                attackStatus |= AttackStatus.NeedsToMove;
                QueueChild(self, move.MoveWithinRange(target, sightRange, target.CenterPosition, Color.Red), true);
                return(AttackStatus.NeedsToMove);
            }

            // Drop the target once none of the weapons are effective against it
            var armaments = attack.ChooseArmamentsForTarget(target, forceAttack).ToList();

            if (armaments.Count == 0)
            {
                return(AttackStatus.UnableToAttack);
            }

            // Update ranges
            minRange = armaments.Max(a => a.Weapon.MinRange);
            maxRange = armaments.Min(a => a.MaxRange());

            var pos    = self.CenterPosition;
            var mobile = move as Mobile;

            if (!target.IsInRange(pos, maxRange) ||
                (minRange.Length != 0 && target.IsInRange(pos, minRange)) ||
                (mobile != null && !mobile.CanInteractWithGroundLayer(self)))
            {
                // Try to move within range, drop the target otherwise
                if (move == null)
                {
                    return(AttackStatus.UnableToAttack);
                }

                attackStatus |= AttackStatus.NeedsToMove;
                var checkTarget = useLastVisibleTarget ? lastVisibleTarget : target;
                QueueChild(self, move.MoveWithinRange(target, minRange, maxRange, checkTarget.CenterPosition, Color.Red), true);
                return(AttackStatus.NeedsToMove);
            }

            if (!attack.TargetInFiringArc(self, target, attack.Info.FacingTolerance))
            {
                var desiredFacing = (attack.GetTargetPosition(pos, target) - pos).Yaw.Facing;
                attackStatus |= AttackStatus.NeedsToTurn;
                QueueChild(self, new Turn(self, desiredFacing), true);
                return(AttackStatus.NeedsToTurn);
            }

            attackStatus |= AttackStatus.Attacking;
            DoAttack(self, attack, armaments);

            return(AttackStatus.Attacking);
        }
Esempio n. 13
0
        protected virtual Activity InnerTick(Actor self, AttackBase attack)
        {
            if (IsCanceled)
            {
                return(NextActivity);
            }

            var type = Target.Type;

            if (!Target.IsValidFor(self) || type == TargetType.FrozenActor)
            {
                return(NextActivity);
            }

            if (attack.Info.AttackRequiresEnteringCell && !positionable.CanEnterCell(Target.Actor.Location, null, false))
            {
                return(NextActivity);
            }

            // Drop the target if it moves under the shroud / fog.
            // HACK: This would otherwise break targeting frozen actors
            // The problem is that Shroud.IsTargetable returns false (as it should) for
            // frozen actors, but we do want to explicitly target the underlying actor here.
            if (!attack.Info.IgnoresVisibility && type == TargetType.Actor && !Target.Actor.Info.HasTraitInfo <FrozenUnderFogInfo>() && !self.Owner.CanTargetActor(Target.Actor))
            {
                return(NextActivity);
            }

            // Drop the target once none of the weapons are effective against it
            var armaments = attack.ChooseArmamentsForTarget(Target, forceAttack).ToList();

            if (armaments.Count == 0)
            {
                return(NextActivity);
            }

            // Update ranges
            minRange = armaments.Max(a => a.Weapon.MinRange);
            maxRange = armaments.Min(a => a.MaxRange());

            var pos    = self.CenterPosition;
            var mobile = move as Mobile;

            if (!Target.IsInRange(pos, maxRange) ||
                (minRange.Length != 0 && Target.IsInRange(pos, minRange)) ||
                (mobile != null && !mobile.CanInteractWithGroundLayer(self)))
            {
                // Try to move within range, drop the target otherwise
                if (move == null)
                {
                    return(NextActivity);
                }

                attackStatus |= AttackStatus.NeedsToMove;
                moveActivity  = ActivityUtils.SequenceActivities(move.MoveWithinRange(Target, minRange, maxRange), this);
                return(NextActivity);
            }

            var targetedPosition = attack.GetTargetPosition(pos, Target);
            var desiredFacing    = (targetedPosition - pos).Yaw.Facing;

            if (facing.Facing != desiredFacing)
            {
                attackStatus |= AttackStatus.NeedsToTurn;
                turnActivity  = ActivityUtils.SequenceActivities(new Turn(self, desiredFacing), this);
                return(NextActivity);
            }

            attackStatus |= AttackStatus.Attacking;
            attack.DoAttack(self, Target, armaments);

            return(this);
        }