Exemplo n.º 1
0
        private static void InitializeMapAndAddBorderWalls(EncounterState state, int width, int height)
        {
            // Initialize the map with empty tiles
            state.MapWidth        = width;
            state.MapHeight       = height;
            state._encounterTiles = new EncounterTile[width, height];
            for (int x = 0; x < width; x++)
            {
                for (int y = 0; y < height; y++)
                {
                    state._encounterTiles[x, y] = new EncounterTile();
                }
            }

            // Create border walls to prevent objects running off the map
            for (int x = 0; x < width; x++)
            {
                for (int y = 0; y < height; y++)
                {
                    if (x == 0 || x == width - 1 || y == 0 || y == height - 1)
                    {
                        state.PlaceEntity(EntityBuilder.CreateEdgeBlockerEntity(), new EncounterPosition(x, y));
                    }
                }
            }
        }
Exemplo n.º 2
0
        private bool ShouldHighlightEntity(EncounterState state, Entity entity, EncounterPosition entityPos, int nextPlayerTick)
        {
            var aiComponent = entity.GetComponent <AIComponent>();

            return(aiComponent != null &&
                   !(aiComponent is PathAIComponent) &&
                   state.FoVCache.IsVisible(entityPos) &&
                   entity.GetComponent <ActionTimeComponent>().NextTurnAtTick < nextPlayerTick);
        }
Exemplo n.º 3
0
        /**
         * Displays the danger map on the "DangerMap" TileMap. Sums the probable (get to that in a moment) damage the player would
         * take from being on the given square on their next turn.
         *
         * "Danger" is kind of tricky, because the possible danger can be influenced by enemy movement. For example, a railgun shot
         * can be intercepted by a scout, rendering all tiles in its path 'safe', but we have no idea if the scout will or won't move
         * onto that square. Our current rule is "You can never get hit on a safe square, but can sometimes avoid getting hit on a
         * dangerous square." So, we stop a projectile path if it would hit a satellite, but don't stop if it would hit a cruiser,
         * even if the cruiser wouldn't have time to move, because it might be destroyed by another projectile and then let the
         * projectile pass.
         *
         * While you could theoretically simulate to the maximum possible fidelity, such that you don't paint it if the cruiser has
         * HP than it could possibly take in a round and do if it doesn't, that doesn't seem worth it.
         */
        public void UpdateAllTiles(EncounterState state)
        {
            var pathEntities               = GetTree().GetNodesInGroup(PathAIComponent.ENTITY_GROUP);
            var timeToNextPlayerMove       = state.Player.GetComponent <SpeedComponent>().Speed;
            var positionsToPotentialDamage = new Dictionary <EncounterPosition, int>();

            // Fill the TileMap as appropriate & tally positionsToPotentialDamage
            this.Clear();
            // TODO: We don't actually need to update every entity, every time, since we only need to set the cell when the projectile itself moves
            foreach (Entity pathEntity in pathEntities)
            {
                int attackerPower   = GetAttackerPower(pathEntity);
                var dangerPositions = CalcDangerPositions(pathEntity, timeToNextPlayerMove);

                RotateSpriteTowardsDestination(pathEntity, dangerPositions);
                foreach (EncounterPosition dangerPosition in dangerPositions.Where(p => state.FoVCache.IsVisible(p)))
                {
                    // If we have a fully immobile, invincible entity at the position we stop the path - otherwise we still draw it.
                    var blockingEntity = state.BlockingEntityAtPosition(dangerPosition.X, dangerPosition.Y);
                    if (blockingEntity != null &&
                        blockingEntity.GetComponent <ActionTimeComponent>() == null &&
                        blockingEntity.GetComponent <DefenderComponent>() != null &&
                        blockingEntity.GetComponent <DefenderComponent>().IsInvincible)
                    {
                        break;
                    }

                    if (positionsToPotentialDamage.ContainsKey(dangerPosition))
                    {
                        positionsToPotentialDamage[dangerPosition] += attackerPower;
                    }
                    else
                    {
                        positionsToPotentialDamage[dangerPosition] = attackerPower;
                    }
                    this.SetCell(dangerPosition.X, dangerPosition.Y, 0);
                }
            }

            // Draw the damage numbers
            // If all this creation/deletion is a significant source of slowdown you can make an object pool, max size FoW area tiles
            foreach (var damageLabel in this._damageLabels)
            {
                this.RemoveChild(damageLabel);
                damageLabel.QueueFree();
            }
            _damageLabels.Clear();
            foreach (var pair in positionsToPotentialDamage)
            {
                var label = CreateLabel(pair.Value, pair.Key);
                this._damageLabels.Add(label);
                this.AddChild(label);
            }

            this.HighlightEnemies(state, timeToNextPlayerMove);
        }
Exemplo n.º 4
0
        public static FoVCache ComputeFoV(EncounterState state, EncounterPosition center, int radius)
        {
            int _x = center.X - radius;
            int _y = center.Y - radius;

            bool[,] visibleCells = new bool[radius * 2 + 1, radius * 2 + 1];

            var visible = rpasCalc.CalcVisibleCellsFrom(center.X, center.Y, radius, state.IsPositionVisible);

            foreach ((int, int)pos in visible)
            {
                if (state.IsInBounds(pos.Item1, pos.Item2))
                {
                    visibleCells[pos.Item1 - _x, pos.Item2 - _y] = true;
                }
            }

            return(new FoVCache(_x, _y, visibleCells));
        }
Exemplo n.º 5
0
        private void HighlightEnemies(EncounterState state, int timeToNextPlayerMove)
        {
            var nextPlayerTick = state.Player.GetComponent <ActionTimeComponent>().NextTurnAtTick + timeToNextPlayerMove;
            var actionEntities = state.ActionEntities();

            foreach (Entity actionEntity in actionEntities)
            {
                var actionEntityPos = actionEntity.GetComponent <PositionComponent>().EncounterPosition;

                if (ShouldHighlightEntity(state, actionEntity, actionEntityPos, nextPlayerTick))
                {
                    if (this.GetCell(actionEntityPos.X, actionEntityPos.Y) == 0)
                    {
                        this.SetCell(actionEntityPos.X, actionEntityPos.Y, 2);
                    }
                    else
                    {
                        this.SetCell(actionEntityPos.X, actionEntityPos.Y, 1);
                    }
                }
            }
        }
Exemplo n.º 6
0
        public static void PopulateStateForLevel(Entity player, int dungeonLevel, EncounterState state, Random seededRand,
                                                 int width = 300, int height = 300, int maxZoneGenAttempts = 100)
        {
            InitializeMapAndAddBorderWalls(state, width, height);

            // Place each empty zone onto the map
            int zoneGenAttemps         = 0;
            List <EncounterZone> zones = new List <EncounterZone>();

            while (zoneGenAttemps < maxZoneGenAttempts && zones.Count < LevelData.GetNumberOfZones(dungeonLevel))
            {
                int zoneWidth  = seededRand.Next(ZONE_MIN_SIZE, ZONE_MAX_SIZE + 1);
                int zoneHeight = seededRand.Next(ZONE_MIN_SIZE, ZONE_MAX_SIZE + 1);
                int zoneX      = seededRand.Next(1, state.MapWidth - zoneWidth);
                int zoneY      = seededRand.Next(1, state.MapHeight - zoneHeight);

                var newZone = new EncounterZone(Guid.NewGuid().ToString(), new EncounterPosition(zoneX, zoneY), zoneWidth, zoneHeight,
                                                "Zone " + zones.Count.ToString());

                bool overlaps = zones.Any(existing => existing.Intersects(newZone));
                if (!overlaps)
                {
                    zones.Add(newZone);
                }
            }
            state._zones = zones;

            // Add the player to the map
            var playerZoneIdx = seededRand.Next(0, zones.Count);

            state.PlaceEntity(player, zones[playerZoneIdx].Center);

            /*
             * var nextToPlayer = new EncounterPosition(zones[playerZoneIdx].Center.X + 2, zones[playerZoneIdx].Center.Y + 1);
             * state.PlaceEntity(EntityBuilder.CreateItemByEntityDefId(EntityDefId.ITEM_RED_PAINT), nextToPlayer);
             * nextToPlayer = new EncounterPosition(zones[playerZoneIdx].Center.X + 1, zones[playerZoneIdx].Center.Y + 1);
             * state.PlaceEntity(EntityBuilder.CreateItemByEntityDefId(EntityDefId.ITEM_EMP), nextToPlayer);
             * for (int i = 0; i < 26; i++) {
             * nextToPlayer = new EncounterPosition(zones[playerZoneIdx].Center.X + i, zones[playerZoneIdx].Center.Y + 3);
             * state.PlaceEntity(EntityBuilder.CreateItemByEntityDefId(EntityDefId.ITEM_EXTRA_BATTERY), nextToPlayer);
             * }
             */

            var nextToPlayer = new EncounterPosition(zones[playerZoneIdx].Center.X + 2, zones[playerZoneIdx].Center.Y + 2);

            //string[] defs = new string[] { EntityDefId.SCOUT, EntityDefId.FIGHTER, EntityDefId.GUNSHIP, EntityDefId.FRIGATE, EntityDefId.DESTROYER, EntityDefId.CRUISER, EntityDefId.CARRIER, EntityDefId.DIPLOMAT };
            string[] defs = new string[] { EntityDefId.ITEM_DUCT_TAPE, EntityDefId.ITEM_EMP, EntityDefId.ITEM_EXTRA_BATTERY, EntityDefId.ITEM_RED_PAINT };
            for (int x = 0; x < defs.Length; x++)
            {
                for (int y = 0; y < 3; y++)
                {
                    var pos = new EncounterPosition(nextToPlayer.X + x, nextToPlayer.Y + y);
                    //state.PlaceEntity(EntityBuilder.CreateEnemyByEntityDefId(defs[x], "12345", 0), pos, ignoreCollision: true);
                    state.PlaceEntity(EntityBuilder.CreateItemByEntityDefId(defs[x]), pos, ignoreCollision: true);
                }
            }

            // Add all the various zone features to the map
            // TODO: Draw this from LevelData instead of literally special-casing level 10 here
            if (dungeonLevel != 10)
            {
                // Generate the stairs (maybe we should refer interally as something more themetically appropriate?)
                // You can get stairs in your starting zone, but you probably shouldn't take them...
                // var stairsZone = zones[playerZoneIdx]; // For testing
                var stairsZone     = zones[seededRand.Next(0, zones.Count)];
                var stairsPosition = stairsZone.RandomEmptyPosition(seededRand, state);
                var stairs         = EntityBuilder.CreateStairsEntity();
                state.PlaceEntity(stairs, stairsPosition);
                stairsZone.AddFeatureToReadout(stairs);

                // Generate intel
                // var intelZone = zones[playerZoneIdx]; // For testing
                var intelZone     = zones[seededRand.Next(0, zones.Count)];
                var intelPosition = intelZone.RandomEmptyPosition(seededRand, state);
                var intel         = EntityBuilder.CreateIntelEntity(dungeonLevel + 1);
                state.PlaceEntity(intel, intelPosition);
                intelZone.AddFeatureToReadout(intel);
            }

            // Populate each zone with an encounter
            foreach (EncounterZone zone in zones)
            {
                if (zone == zones[playerZoneIdx])
                {
                    PopulateZone(zone, dungeonLevel, seededRand, state, safe: true);
                }
                else
                {
                    PopulateZone(zone, dungeonLevel, seededRand, state);
                }
            }
        }
Exemplo n.º 7
0
        private static void PopulateZone(EncounterZone zone, int dungeonLevel, Random seededRand, EncounterState state, bool safe = false)
        {
            // Add satellites
            int numSatellites = LevelData.GetNumberOfSatellites(dungeonLevel);

            for (int i = 0; i < numSatellites; i++)
            {
                var unblockedPosition = zone.RandomEmptyPosition(seededRand, state);
                var satellite         = EntityBuilder.CreateSatelliteEntity();
                state.PlaceEntity(satellite, unblockedPosition);
            }

            EncounterDef encounterDef;

            if (safe)
            {
                encounterDef = LevelData.GetEncounterDefById(EncounterDefId.EMPTY_ENCOUNTER);
            }
            else
            {
                encounterDef = LevelData.ChooseEncounter(dungeonLevel, seededRand);
            }
            zone.ReadoutEncounterName = encounterDef.Name;

            if (encounterDef.EntityDefIds.Count > 0)
            {
                string activationGroupId = Guid.NewGuid().ToString();
                foreach (string entityDefId in encounterDef.EntityDefIds)
                {
                    var unblockedPosition = zone.RandomEmptyPosition(seededRand, state);
                    var newEntity         = EntityBuilder.CreateEnemyByEntityDefId(entityDefId, activationGroupId, state.CurrentTick);
                    state.PlaceEntity(newEntity, unblockedPosition);
                }
            }

            var chosenItemDefs = LevelData.ChooseItemDefs(dungeonLevel, seededRand);

            foreach (string chosenItemDefId in chosenItemDefs)
            {
                var unblockedPosition = zone.RandomEmptyPosition(seededRand, state);
                var newEntity         = EntityBuilder.CreateItemByEntityDefId(chosenItemDefId);
                state.PlaceEntity(newEntity, unblockedPosition);
                zone.AddItemToReadout(newEntity);
            }
        }