public void Use(World world) { world.AddObject(new Drone(), world.Player.Position); }
//Place a level block in a level. The preferred walls can be specified. These can only be guaranteed // if the level has the wall type (for example, preferLeftWall can be ignored if HasLeftWall is false). //If a wall isn't preferred at a position, an exit is used instead if Has*Exit is true. public void Place(World world, Dictionary<char, ISpecialTileDefinition> specialTiles, int x, int y, bool preferLeftWall = true, bool preferRightWall = true, bool preferTopWall = true, bool preferBottomWall = true, bool isBottomOfRoom = false, int numberOfEnemies = 0, List<Type> possibleEnemyTypes = null) { possibleEnemyTypes = possibleEnemyTypes ?? new List<Type>(); int BlockID = random.Next(int.MaxValue); bool[,] isActuallyEmpty = new bool[Width, Height]; bool[,] isActuallyWall = new bool[Width, Height]; FairRandomCollection<Vector2> possibleEnemyPositions = new FairRandomCollection<Vector2>(); FairRandomCollection<Vector2> possibleTurretPositions = new FairRandomCollection<Vector2>(); //Place the tiles in the level for (int i = 0; i < Width; i++) { for (int j = 0; j < Height; j++) { char data = Data[i, j]; //Check special tiles until we find a normal tile. int loopCount = 0; //Some tiles can be turned into walls if they end up on the sides of the level. bool specialTileTurnsIntoWallOnSides = false; while (specialTiles.ContainsKey(data)) { specialTileTurnsIntoWallOnSides = specialTileTurnsIntoWallOnSides || specialTiles[data].CanBeWall; List<string> specialKeywords = new List<string>(); //Contains the special keywords that are true at this moment. if (preferLeftWall) specialKeywords.Add("LEFTWALL"); if (preferRightWall) specialKeywords.Add("RIGHTWALL"); if (preferTopWall) specialKeywords.Add("TOPWALL"); if (preferBottomWall) specialKeywords.Add("BOTTOMWALL"); data = specialTiles[data].GetTile(BlockID, specialKeywords); if (loopCount > 1000) throw new Exception("Possible infinite loop detected! Please change the level definition file."); } //If the special tile can be turned into a wall, check if this should happen. if (specialTileTurnsIntoWallOnSides) { if (i == 0) //Left wall { if (preferLeftWall) data = '1'; } if (i == Width - 1) //Right wall { if (preferRightWall) data = '1'; } if (j == 0) //Top wall { if (preferTopWall) data = '1'; } if (j == Height - 1) //Bottom wall { if (preferBottomWall) data = '1'; } } int baseX = x + World.TileWidth * i, baseY = y + World.TileHeight * j; float centerX = baseX + World.TileWidth / 2, centerY = baseY + World.TileHeight / 2; Rectangle stdCollisionRect = new Rectangle(baseX, baseY, World.TileWidth, World.TileHeight); //If it's nothing or something the player can move through, and the bottom of the room, then add a jumpthrough. if (data == '.' | data == '/' | data == '\\' && j == Height - 1 && isBottomOfRoom) world.AddObject(new JumpThrough(new Rectangle(baseX, baseY, World.TileWidth, 1))); if (data == '1') //A wall { world.AddObject(new Wall(stdCollisionRect)); isActuallyWall[i, j] = true; if (j != 0 && isActuallyEmpty[i, j - 1]) { //We can place an enemy here! possibleEnemyPositions.Add(new Vector2(centerX, centerY - World.TileHeight + 10)); } } else if (data == 'T') //A tile world.AddObject(new WindowTile(stdCollisionRect)); else if (data == 'D') //A gun door world.AddObject(new GunDoor(), baseX + World.TileWidth / 2, baseY); else if (data == 'M') //A melee door world.AddObject(new MeleeDoor(), baseX + World.TileWidth / 2, baseY); else if (data == 'C') //A rocket door world.AddObject(new RocketDoor(), baseX + World.TileWidth / 2, baseY); else if (data == 'B') //A boss door world.AddObject(new BossDoor(), baseX + World.TileWidth / 2, baseY); else if (data == 'O') world.AddObject(new Portal(), baseX + 6, baseY); else if (data == 'S') //Spikes world.AddObject(new Spikes(), baseX, baseY + (int)(World.TileHeight * (100f / 128f))); else if (data == 'G') //A gun pickup block world.AddObject(new GunPickup(), centerX, centerY); else if (data == 'U') //A gun upgrade pickup block world.AddObject(new GunUpgrade(), centerX, centerY); else if (data == 'W') //A wrench pickup block world.AddObject(new WrenchPickup(), centerX, centerY); else if (data == 'R') //A rocket pickup block world.AddObject(new RocketPickup(), centerX, centerY); else if (data == 'H') //A health pickup block world.AddObject(new HealthDrop(10), centerX, centerY); else if (data == 'A') //First health upgrade world.AddObject(new MaxHPUpgrade(), centerX, centerY); else if (data == 'E') //Huge health upgrade world.AddObject(new HugeMaxHPUpgrade(), centerX, centerY); else if (data == 'F') //Drone upgrade world.AddObject(new DroneRangeUpgrade(), centerX, centerY); else if (data == 'P') //The player { world.Player = new Player(); world.AddObject(world.Player, centerX, centerY); } else if (data == 'J') //A jumpthrough block world.AddObject(new JumpThrough(new Rectangle(baseX, baseY, World.TileWidth, 1))); else if (data == '\\') //A slope world.AddObject(new SlopeLeft(stdCollisionRect)); else if (data == '/') //A slope world.AddObject(new SlopeRight(stdCollisionRect)); else if (data == '.') { //Nothing isActuallyEmpty[i, j] = true; if (j != 0 && isActuallyWall[i, j - 1]) { //We can place a turret here! possibleTurretPositions.Add(new Vector2(centerX, baseY + 15)); } } } } //Place some enemies. for (int i = 0; i < numberOfEnemies; i++) { if (possibleEnemyTypes.Contains(typeof(Turret)) && World.Random.Next(3) == 0) { if (possibleTurretPositions.Count != 0) world.AddObject(new Turret(), possibleTurretPositions.Get()); } else if (possibleEnemyPositions.Count != 0) { world.AddObject(Activator.CreateInstance(possibleEnemyTypes.Except(new List<Type>() { typeof(Turret) }).ToList().GetRandomItem()) as GameObject, possibleEnemyPositions.Get()); } } }
public void Generate(World world) { string[] areaBorderNameRight = { "SecondRightBorder", "ThirdRightBorder" }; //The names of area borders. string[] areaBorderNameLeft = { "SecondLeftBorder", "ThirdLeftBorder" }; //Enemy types for each area List<List<Type>> enemyTypes = new List<List<Type>>(); enemyTypes.Add(new List<Type>() { typeof(SlimeMonster), typeof(MeleeMonster) }); enemyTypes.Add(new List<Type>() { typeof(SlimeMonster), typeof(ShootingMonster), typeof(MeleeMonster), typeof(Turret) }); enemyTypes.Add(new List<Type>() { typeof(ShootingMonster), typeof(MeleeMonster), typeof(Turret) }); //The first part of the second area has no turrets yet. List<Type> enemyTypesStartSecondArea = new List<Type>() { typeof(SlimeMonster), typeof(ShootingMonster), typeof(MeleeMonster) }; //Define and initialize variables bool[,] isRoom = new bool[WorldWidth, WorldHeight]; //Whether this is a room. int[,] area = new int[WorldWidth, WorldHeight]; //The area (for example, 0 for the starting area) string[,] theme = new string[WorldWidth, WorldHeight]; //The general appearance. bool[,] CanHaveRightExit = new bool[WorldWidth, WorldHeight]; //Whether this room can potentially have a right exit. bool[,] CanHaveBottomExit = new bool[WorldWidth, WorldHeight]; //Whether this room can potentially have a right exit. List<RoomExit>[,] roomExits = new List<RoomExit>[WorldWidth, WorldHeight]; List<string>[,] guaranteedSpecialBlocks = new List<string>[WorldWidth, WorldHeight]; //Guaranteed blocks. int[,] enemies = new int[WorldWidth, WorldHeight]; for (int i = 0; i < WorldWidth; i++) for (int j = 0; j < WorldHeight; j++) { roomExits[i, j] = new List<RoomExit>(); guaranteedSpecialBlocks[i, j] = new List<string>(); isRoom[i, j] = false; area[i, j] = 0; theme[i, j] = ""; CanHaveRightExit[i, j] = true; CanHaveBottomExit[i, j] = true; enemies[i, j] = 0; } //Your own position and the position of the boss. int startingY = WorldHeight / 2 + World.Random.Next(-1, 1); int bossY = WorldHeight / 2 + World.Random.Next(-1, 1); isRoom[0, startingY] = true; //Starting room guaranteedSpecialBlocks[0, startingY].Add("PlayerStart"); isRoom[1, startingY] = true; //Room right of starting room. guaranteedSpecialBlocks[1, startingY].Add("GunPickup"); guaranteedSpecialBlocks[1, startingY].Add(areaBorderNameRight[0]); //Other rooms (main areas). int areaTwoBorderStart = 3, areaThreeBorderStart = 5; for (int i = 2; i < WorldWidth; i++) for (int j = 0; j < WorldHeight; j++) { bool isTopOrBottomArea = j == 0 || j == WorldHeight - 1; isRoom[i, j] = true; //Set the area if (i >= areaThreeBorderStart + World.Random.Next(2) - (isTopOrBottomArea ? 1 : 0)) area[i, j] = 2; else if (i >= areaTwoBorderStart + World.Random.Next(2) - (isTopOrBottomArea ? 1 : 0)) area[i, j] = 1; else area[i, j] = 0; //Set the theme if (World.Random.Next(100) < (isTopOrBottomArea ? 60 : 20)) //Generate cramped rooms. There are more of 'em at the top and bottom. theme[i, j] = "Cramped"; else if (World.Random.Next(100) < 20) //Generate an open room theme[i, j] = "Open"; else if (area[i, j] == 2 && World.Random.Next(100) < 50) //Generate a room with lots of spikes theme[i, j] = "Spiky"; else if (World.Random.Next(100) < 10) //Create a room with windows theme[i, j] = "Windowed"; //These areas have a normal amount of enemies, with more enemies in later areas. if (area[i, j] == 0) enemies[i, j] = World.Random.Next(2, 3); else enemies[i, j] = World.Random.Next(3, 6) + area[i, j]; } //There should only be one enemy in the third room. enemies[2, startingY] = 1; //Add a wrench pickup and a rocket pickup. The rocket pickup should have some distance from the starting area. int wrenchPos, rocketPos; do { wrenchPos = World.Random.Next(1, WorldHeight - 1); } while (Math.Abs(wrenchPos - startingY) < 1); if (World.Random.Next(2) == 0) rocketPos = 0; else rocketPos = WorldHeight - 1; guaranteedSpecialBlocks[areaTwoBorderStart + 1, wrenchPos].Add("WrenchPickup"); guaranteedSpecialBlocks[areaThreeBorderStart + 1, rocketPos].Add("RocketPickup"); //Other rooms (secondary areas) for (int i = 0; i < 2; i++) for (int j = 0; j < WorldHeight; j++) { if (j != startingY) { isRoom[i, j] = true; area[i, j] = 2; CanHaveBottomExit[i, j] = false; //Set if we need a right exit. if ((j == startingY - 1 || j == startingY + 1) && i == 0) CanHaveRightExit[i, j] = true; else if ((j == 0 || j == WorldHeight - 1) && i == 1) CanHaveRightExit[i, j] = true; else CanHaveRightExit[i, j] = false; //And do the same for the bottom exit. if (j != startingY - 1) CanHaveBottomExit[i, j] = true; else CanHaveBottomExit[i, j] = false; //These areas often have lots of enemies enemies[i, j] = World.Random.Next(6, 10); } } //Add three max health upgrades somewhere guaranteedSpecialBlocks[World.Random.Next(3, WorldWidth - 1), World.Random.Next(0, WorldHeight - 1)].Add("MaxHPDropPickup"); guaranteedSpecialBlocks[WorldWidth - 1, World.Random.Next(bossY)].Add("MaxHPDropPickup"); guaranteedSpecialBlocks[WorldWidth - 1, World.Random.Next(bossY + 1, WorldHeight)].Add("MaxHPDropPickup"); //Add the special bonus gun upgrade guaranteedSpecialBlocks[0, 0].Add("GunUpgradePickup"); //Add the huge health upgrade guaranteedSpecialBlocks[0, WorldHeight - 1].Add("HugeMaxHPDropPickup"); //And the drone upgrade guaranteedSpecialBlocks[1, WorldHeight - 1].Add("DroneUpgradePickup"); //Add the boss room. CanHaveBottomExit[WorldWidth - 1, bossY - 1] = false; //The room above can't have a bottom exit. CanHaveBottomExit[WorldWidth - 1, bossY] = false; //The boss room can't have a bottom exit. enemies[WorldWidth - 1, bossY] = 0; //The boss room has no normal enemy spawns theme[WorldWidth - 1, bossY] = "Boss"; //The theme is "boss" //Add a right exit to it. roomExits[WorldWidth - 1, bossY].Add(new RoomExit(new Point(LevelWidth / LevelGenerator.BlockWidth - 1, LevelHeight / LevelGenerator.BlockHeight - 1), Direction.Right)); guaranteedSpecialBlocks[WorldWidth - 1, bossY].Add("LeftBossDoorBorder"); guaranteedSpecialBlocks[WorldWidth - 1, bossY].Add("RightBossDoorBorder"); guaranteedSpecialBlocks[WorldWidth - 1, bossY].Add("BossPortal"); //Add nine "normal tiles" for (int i = 0; i < 9; i++) guaranteedSpecialBlocks[WorldWidth - 1, bossY].Add("NormalBossRoomTile"); guaranteedSpecialBlocks[WorldWidth - 2, bossY].Add("RightRocketBorder"); //Add a border so players must have the rocket launcher. //Place the exits for (int i = 0; i < WorldWidth; i++) for (int j = 0; j < WorldHeight; j++) { //Place exits. if (i < WorldWidth - 1 && isRoom[i, j] && isRoom[i + 1, j] && CanHaveRightExit[i, j]) { int nextY; if (j == bossY && i == WorldWidth - 2) nextY = LevelHeight / LevelGenerator.BlockHeight - 1; else nextY = World.Random.Next(LevelHeight / LevelGenerator.BlockHeight); roomExits[i, j].Add(new RoomExit(new Point(LevelWidth / LevelGenerator.BlockWidth - 1, nextY), Direction.Right)); roomExits[i + 1, j].Add(new RoomExit(new Point(0, nextY), Direction.Left)); if (area[i, j] < area[i + 1, j]) guaranteedSpecialBlocks[i, j].Add(areaBorderNameRight[area[i + 1, j] - 1]); else if (area[i, j] > area[i + 1, j]) guaranteedSpecialBlocks[i + 1, j].Add(areaBorderNameLeft[area[i, j] - 1]); } if (j < WorldHeight - 1 && isRoom[i, j] && isRoom[i, j + 1] && CanHaveBottomExit[i, j] && (area[i, j] == area[i, j + 1])) { int nextX = World.Random.Next(LevelWidth / LevelGenerator.BlockWidth); roomExits[i, j].Add(new RoomExit(new Point(nextX, LevelHeight / LevelGenerator.BlockHeight - 1), Direction.Down)); roomExits[i, j + 1].Add(new RoomExit(new Point(nextX, 0), Direction.Up)); } } //And generate the levels. for (int i = 0; i < WorldWidth; i++) for (int j = 0; j < WorldHeight; j++) { List<Type> levelEnemyTypes = enemyTypes[area[i, j]]; if (i == 3 && area[i, j] == 1) levelEnemyTypes = enemyTypesStartSecondArea; if (isRoom[i, j]) levelGenerator.Generate(world, new Vector2(LevelWidth * World.TileWidth * i, LevelHeight * World.TileHeight * j), roomExits[i, j], guaranteedSpecialBlocks[i, j], theme[i, j], enemies[i, j], levelEnemyTypes); } //Add the "hack this game" object world.AddObject(new HackThisGame()); //Add the map object world.AddObject(new Map()); //Add the GUI object world.AddObject(new GUI()); //Autotile the world. Autotile(world); }