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