Exemple #1
0
        // This is actually much more like unit broken but oh well!
        public static List <EncounterAction> ActionsForUnitRetreat(EncounterState state, Entity parent, Unit unit)
        {
            var parentPos = parent.GetComponent <PositionComponent>().EncounterPosition;

            EncounterPosition targetEndPos = parentPos;
            var backwardsPositions         = new List <EncounterPosition>()
            {
                AIUtils.RotateAndProject(parentPos, 0, 1, unit.UnitFacing),
                AIUtils.RotateAndProject(parentPos, 1, 1, unit.UnitFacing),
                AIUtils.RotateAndProject(parentPos, -1, 1, unit.UnitFacing),
            };

            GameUtils.Shuffle(state.EncounterRand, backwardsPositions);
            foreach (var position in backwardsPositions)
            {
                if (state.EntitiesAtPosition(position.X, position.Y).Count == 0)
                {
                    targetEndPos = position;
                    break;
                }
            }
            if (targetEndPos == parentPos)
            {
                targetEndPos = backwardsPositions[0];
            }
            return(new List <EncounterAction>()
            {
                new MoveAction(parent.EntityId, targetEndPos)
            });
        }
Exemple #2
0
 private static bool PlayerSeesEnemies(EncounterState state)
 {
     return(state.FoVCache.VisibleCells
            .Select(cell => state.EntitiesAtPosition(cell.X, cell.Y))
            .Any(entitiesAtPosition => entitiesAtPosition.Any(e => e.GetComponent <AIComponent>() != null &&
                                                              !(e.GetComponent <PathAIComponent>() is PathAIComponent))));
 }
Exemple #3
0
        public static List <Entity> HostilesInPosition(EncounterState state, FactionName parentFaction, int x, int y)
        {
            var hostiles = new List <Entity>();

            foreach (Entity e in state.EntitiesAtPosition(x, y))
            {
                var factionComponent = e.GetComponent <FactionComponent>();
                if (factionComponent != null && factionComponent.Faction != parentFaction)
                {
                    hostiles.Add(e);
                }
            }
            return(hostiles);
        }
Exemple #4
0
        public static List <Entity> FriendliesInPosition(EncounterState state, Entity entity, FactionName parentFaction, int x, int y)
        {
            var friendlies = new List <Entity>();

            foreach (Entity e in state.EntitiesAtPosition(x, y))
            {
                if (e != entity)
                {
                    var factionComponent = e.GetComponent <FactionComponent>();
                    if (factionComponent != null && factionComponent.Faction == parentFaction)
                    {
                        friendlies.Add(e);
                    }
                }
            }
            return(friendlies);
        }
Exemple #5
0
        private static bool ResolveUseStairs(UseStairsAction action, EncounterState state)
        {
            var actorPosition = state.GetEntityById(action.ActorId).GetComponent <PositionComponent>().EncounterPosition;
            var stairs        = state.EntitiesAtPosition(actorPosition.X, actorPosition.Y)
                                .FirstOrDefault(e => e.GetComponent <StairsComponent>() != null);

            if (stairs != null)
            {
                state.ResetStateForNewLevel(state.Player, state.DungeonLevel + 1);
                state.WriteToFile();
                return(true);
            }
            else
            {
                state.LogMessage("No jump point found!", failed: true);
                return(false);
            }
        }
Exemple #6
0
        private bool positionNotTooFarAheadAndMovable(EncounterState state, EncounterPosition parentPos, Unit unit, EncounterPosition possible)
        {
            var entitiesAtPos = state.EntitiesAtPosition(possible.X, possible.Y);

            if (!AIUtils.IsNextRowTooFarAhead(parentPos, unit) && entitiesAtPos.Count == 0)
            {
                return(true);
            }

            var hostilesAtPos = AIUtils.HostilesInPosition(state, FactionName.PLAYER, possible.X, possible.Y);

            if (hostilesAtPos.Count > 0)
            {
                return(true);
            }

            return(false);
        }
Exemple #7
0
        private static bool ResolveGetItem(GetItemAction action, EncounterState state)
        {
            var actor = state.GetEntityById(action.ActorId);
            var inventoryComponent = actor.GetComponent <InventoryComponent>();
            var actorPosition      = actor.GetComponent <PositionComponent>().EncounterPosition;
            var item = state.EntitiesAtPosition(actorPosition.X, actorPosition.Y)
                       .FirstOrDefault(e => e.GetComponent <StorableComponent>() != null);

            if (item == null)
            {
                state.LogMessage("No item found!", failed: true);
                return(false);
            }
            else if (item.GetComponent <UsableComponent>() != null && item.GetComponent <UsableComponent>().UseOnGet)
            {
                // The responsibility for removing/not removing the usable from the EncounterState is in the usage code.
                bool successfulUsage = ResolveUse(new UseAction(actor.EntityId, item.EntityId, false), state);
                if (!successfulUsage)
                {
                    GD.PrintErr(string.Format("Item {0} was not successfully used after being picked up!", item.EntityName));
                }
                return(true);
            }
            else if (!inventoryComponent.CanFit(item))
            {
                state.LogMessage(string.Format("[b]{0}[/b] can't fit the [b]{1}[/b] in its inventory!",
                                               actor.EntityName, item.EntityName), failed: true);
                return(false);
            }
            else
            {
                state.RemoveEntity(item);
                actor.GetComponent <InventoryComponent>().AddEntity(item);

                var logMessage = string.Format("[b]{0}[/b] has taken the [b]{1}[/b]", actor.EntityName, item.EntityName);
                state.LogMessage(logMessage);
                return(true);
            }
        }
Exemple #8
0
        private static List <EncounterAction> ActionsUnitAdvanceFight(EncounterState state, Entity parent, Unit unit)
        {
            var actions = new List <EncounterAction>();

            var unitComponent = parent.GetComponent <UnitComponent>();
            var parentPos     = parent.GetComponent <PositionComponent>().EncounterPosition;
            var parentFaction = parent.GetComponent <FactionComponent>().Faction;

            var  targetEndPos = parentPos;
            bool tryAdvance   = true;

            if (tryAdvance)
            {
                if (IsNextRowTooFarAhead(parentPos, unit))
                {
                    tryAdvance = false;
                }
            }

            // Override for if you're too far back
            if (IsPositionTooFarBehind(AIUtils.RotateAndProject(parentPos, 0, -1, unit.UnitFacing), unit))
            {
                var aheadPos = AIUtils.RotateAndProject(parentPos, 0, -1, unit.UnitFacing);
                actions.Add(new MoveAction(parent.EntityId, aheadPos));
                TryAddAttackAdjacent(state, parent, actions, parentFaction, aheadPos);
                return(actions);
            }

            // Morale check for advancing directly into an enemy
            if (tryAdvance)
            {
                var twoStepsAhead = AIUtils.RotateAndProject(parentPos, 0, -2, unit.UnitFacing);
                if (AIUtils.HostilesInPosition(state, parentFaction, twoStepsAhead.X, twoStepsAhead.Y).Count > 0)
                {
                    var moraleState = parent.GetComponent <AIMoraleComponent>().CurrentMoraleState;
                    if (moraleState == MoraleState.WAVERING)
                    {
                        if (state.EncounterRand.Next(2) == 0)
                        {
                            tryAdvance = false;
                        }
                    }
                    else if (moraleState < MoraleState.WAVERING)
                    {
                        tryAdvance = false;
                    }
                }
            }

            if (tryAdvance)
            {
                // We're gonna have some serious Phalanx Drift goin' on I guess?
                var forwardPositions = new List <EncounterPosition>()
                {
                    AIUtils.RotateAndProject(parentPos, 0, -1, unit.UnitFacing)
                };
                if (!(unit.RightFlank && AIUtils.IsOnFlank(parentPos.X, parentPos.Y, unit, Flank.RIGHT)))
                {
                    forwardPositions.Add(AIUtils.RotateAndProject(parentPos, 1, -1, unit.UnitFacing));
                }
                if (!(unit.LeftFlank && AIUtils.IsOnFlank(parentPos.X, parentPos.Y, unit, Flank.LEFT)))
                {
                    forwardPositions.Add(AIUtils.RotateAndProject(parentPos, -1, -1, unit.UnitFacing));
                }

                foreach (var forwardPos in forwardPositions)
                {
                    if (state.EntitiesAtPosition(forwardPos.X, forwardPos.Y).Count == 0)
                    {
                        // Never go into a square unless it's flanked by an existing friendly
                        bool supported = false;
                        for (int y = -1; y < 2; y++)
                        {
                            var leftPos = AIUtils.RotateAndProject(forwardPos, -1, y, unit.UnitFacing);
                            if (AIUtils.FriendliesInPosition(state, parent, parentFaction, leftPos.X, leftPos.Y).Count > 0)
                            {
                                supported = true;
                                break;
                            }
                            var rightPos = AIUtils.RotateAndProject(forwardPos, 1, y, unit.UnitFacing);
                            if (AIUtils.FriendliesInPosition(state, parent, parentFaction, rightPos.X, rightPos.Y).Count > 0)
                            {
                                supported = true;
                                break;
                            }
                        }
                        if (supported)
                        {
                            targetEndPos = forwardPos;
                            break;
                        }
                    }
                }
                if (targetEndPos != parentPos)
                {
                    actions.Add(new MoveAction(parent.EntityId, targetEndPos));
                }
            }
            TryAddAttackAdjacent(state, parent, actions, parentFaction, targetEndPos);
            if (actions.Count == 0)
            {
                actions.Add(new WaitAction(parent.EntityId));
            }

            return(actions);
        }
Exemple #9
0
        private static void ResolveUseEffects(Entity user, Entity usable, EncounterState state)
        {
            // We keep this logic here instead of in the component itself because the component should have only state data. That said
            // we shouldn't keep it, like, *here* here, 'least not indefinitely.
            var useEffectHeal = usable.GetComponent <UseEffectHealComponent>();

            if (useEffectHeal != null)
            {
                var restored = user.GetComponent <DefenderComponent>().RestoreHP(useEffectHeal.Healpower);
                state.LogMessage(string.Format("{0} restored {1} HP to {2}!", usable.EntityName, restored, user.EntityName));
            }

            var useEffectAddIntel = usable.GetComponent <UseEffectAddIntelComponent>();

            if (useEffectAddIntel != null)
            {
                // Kinda funny because player's the only one for which this effect is meaningful so we just grab player it's fiiiiiine
                state.Player.GetComponent <PlayerComponent>().RegisterIntel(useEffectAddIntel.TargetDungeonLevel);
                state.LogMessage(string.Format("Discovered intel for [b]sector {0}[/b]!", useEffectAddIntel.TargetDungeonLevel));
            }

            var useEffectBoostPower = usable.GetComponent <UseEffectBoostPowerComponent>();

            if (useEffectBoostPower != null)
            {
                state.LogMessage(String.Format("Attack power boosted by {0} for duration {1}!",
                                               useEffectBoostPower.BoostPower, useEffectBoostPower.Duration));
                var statusEffectTracker = user.GetComponent <StatusEffectTrackerComponent>();
                statusEffectTracker.AddEffect(new StatusEffectTimedPowerBoost(
                                                  boostPower: useEffectBoostPower.BoostPower,
                                                  startTick: state.CurrentTick,
                                                  endTick: state.CurrentTick + useEffectBoostPower.Duration
                                                  ));
            }

            var useEffectBoostSpeed = usable.GetComponent <UseEffectBoostSpeedComponent>();

            if (useEffectBoostSpeed != null)
            {
                state.LogMessage(String.Format("Speed boosted by {0} for duration {1}!",
                                               useEffectBoostSpeed.BoostPower, useEffectBoostSpeed.Duration));
                var statusEffectTracker = user.GetComponent <StatusEffectTrackerComponent>();
                statusEffectTracker.AddEffect(new StatusEffectTimedSpeedBoost(
                                                  boostPower: useEffectBoostSpeed.BoostPower,
                                                  startTick: state.CurrentTick,
                                                  endTick: state.CurrentTick + useEffectBoostSpeed.Duration
                                                  ));
            }

            var useEffectEMP = usable.GetComponent <UseEffectEMPComponent>();

            if (useEffectEMP != null)
            {
                state.LogMessage(String.Format("EMP detonated in radius {0} - disables {1} turns!",
                                               useEffectEMP.Radius, useEffectEMP.DisableTurns));
                var userPosition = user.GetComponent <PositionComponent>().EncounterPosition;
                for (int x = userPosition.X - useEffectEMP.Radius; x <= userPosition.X + useEffectEMP.Radius; x++)
                {
                    for (int y = userPosition.Y - useEffectEMP.Radius; y <= userPosition.Y + useEffectEMP.Radius; y++)
                    {
                        var distance = userPosition.DistanceTo(x, y);
                        if (distance <= useEffectEMP.Radius && state.IsInBounds(x, y))
                        {
                            var entitiesAtPosition = state.EntitiesAtPosition(x, y);
                            foreach (var entity in entitiesAtPosition)
                            {
                                var speedComponent = entity.GetComponent <SpeedComponent>();
                                var statusTracker  = entity.GetComponent <StatusEffectTrackerComponent>();
                                if (entity != user && speedComponent != null && statusTracker != null)
                                {
                                    var disableTicks = speedComponent.Speed * useEffectEMP.DisableTurns;
                                    statusTracker.AddEffect(new StatusEffectTimedDisable(state.CurrentTick, state.CurrentTick + disableTicks));
                                    state.LogMessage(String.Format("{0} was disabled for {1} ticks!", entity.EntityName, disableTicks));
                                }
                            }
                        }
                    }
                }
            }
        }
Exemple #10
0
        /**
         * A zone is considered "explored" when:
         * 1: All storables have been found
         *   1a: If your inventory is not full, all storable items have been picked up
         * 2: All non-storable features have been seen
         *
         * Actual % of FoW revealed is not important. This is kinda cheaty, because it implies the autopilot knows intel for the
         * area, but also, who cares? It's fine. Likewise it doesn't factor into account having destroyed the enemies because if
         * there is an encounter, you'll definitely run into it!
         */
        private static bool ResolveAutopilotContinueExplore(EncounterState state)
        {
            var player          = state.Player;
            var playerComponent = player.GetComponent <PlayerComponent>();
            var playerPos       = player.GetComponent <PositionComponent>().EncounterPosition;
            var path            = playerComponent.AutopilotPath;

            var zone = state.GetZoneById(playerComponent.AutopilotZoneId);
            var nextUngottenStorable =
                zone.ReadoutItems.Concat(zone.ReadoutFeatures)
                .FirstOrDefault(i => state.GetEntityById(i.EntityId) != null &&
                                state.GetEntityById(i.EntityId).GetComponent <StorableComponent>() != null);
            // We rely on the fact that there's no ungettable features other than stairs, and that there's only one stair, to make this
            // work. If there were multiple ungettable features you'd want to put them into a list and autopilot between them so you
            // could cycle the 'x' button to find the one you want.
            var stairs = zone.ReadoutFeatures.FirstOrDefault(r =>
                                                             state.GetEntityById(r.EntityId).GetComponent <StairsComponent>() != null &&
                                                             state.GetEntityById(r.EntityId).GetComponent <PositionComponent>().EncounterPosition != playerPos
                                                             );

            if (PlayerSeesEnemies(state))
            {
                ResolveAction(new AutopilotEndAction(player.EntityId, AutopilotEndReason.ENEMY_DETECTED), state);
                return(false);
            } // If you are already on a path, progress on the path
            else if (path != null)
            {
                if (path.AtEnd)
                {
                    playerComponent.ClearAutopilotPath();
                    return(false);
                }
                else
                {
                    Rulebook.ResolveAction(new MoveAction(player.EntityId, path.Step()), state);
                    return(true);
                }
            } // If you're on top of a storable item, get it
            else if (state.EntitiesAtPosition(playerPos.X, playerPos.Y).Any(e => e.GetComponent <StorableComponent>() != null))
            {
                var nextStorable = state.EntitiesAtPosition(playerPos.X, playerPos.Y).First(e => e.GetComponent <StorableComponent>() != null);
                if (ResolveAction(new GetItemAction(player.EntityId), state))
                {
                    return(true);
                }
                else
                {
                    ResolveAction(new AutopilotEndAction(player.EntityId, AutopilotEndReason.INVENTORY_FULL), state);
                    return(false);
                }
            } // If there are any storable items, move towards them
            else if (nextUngottenStorable != null)
            {
                var nextPos   = state.GetEntityById(nextUngottenStorable.EntityId).GetComponent <PositionComponent>().EncounterPosition;
                var foundPath = Pathfinder.AStarWithNewGrid(playerPos, nextPos, state, 900);
                if (foundPath == null)
                {
                    ResolveAction(new AutopilotEndAction(player.EntityId, AutopilotEndReason.NO_PATH), state);
                    return(false);
                }
                else
                {
                    foundPath.Add(nextPos);
                    playerComponent.LayInAutopilotPathForExploration(new EncounterPath(foundPath));
                    return(true);
                }
            } // If this is the stairs zone, go to the stairs
            else if (stairs != null)
            {
                var nextPos   = state.GetEntityById(stairs.EntityId).GetComponent <PositionComponent>().EncounterPosition;
                var foundPath = Pathfinder.AStarWithNewGrid(playerPos, nextPos, state, 900);
                if (foundPath == null)
                {
                    ResolveAction(new AutopilotEndAction(player.EntityId, AutopilotEndReason.NO_PATH), state);
                    return(false);
                }
                else
                {
                    foundPath.Add(nextPos);
                    playerComponent.LayInAutopilotPathForExploration(new EncounterPath(foundPath));
                    return(true);
                }
            } // Otherwise you're done!
            else
            {
                ResolveAction(new AutopilotEndAction(player.EntityId, AutopilotEndReason.TASK_COMPLETED), state);
                return(false);
            }
        }