public Level(Game game, SpriteBatch batch) { completeCode = CompleteCode.NotLoaded; this.game = game; player = new Player(this, game.joeTexture, game.tomatoTexture, 0, ScreenHeight - BlockSize - Player.Height); camera = new Camera(batch, this); }
public void LoadRandomLevel(int difficulty) { difficulty *= 5; difficulty += 0; // land constants const int maxLandHeight = 8; // the maximum height from the bottom of the screen that land can appear const int maxJumpHeight = 3; // the maximum vertical distance that Joe can jump upwards const float maxFlatJumpWidth = 6.6f; // the maximum horizontal jumping distance if both ledges are the same height const float jumpDifferential = 0.8f; // the amount the maximum horizontal distance changes per vertical square // item constants const int avgRegTomatoDist = 20; // the average number of squares between a regular tomato and the next tomato const int avgGoldTomatoDist = 60; // the average number of squares between a golden tomato and the next tomato const int varRegTomatoDist = 5; // the variance in number of squares between a regular tomato and the next tomato const int varGoldTomatoDist = 20; // the variance in number of squares between a golden tomato and the next tomato const float avgOddsOfGolden = 0.15f; // the average chance that a spawned tomato will be golden const float avgOddsofExLife = 0.002f; // the average chance each square that an extra life will be spawned const int minItemHeight = 3; // the minimum height above the ground that an item can be spawned const int maxItemHeight = 5; // the maximum height above the ground that an item can be spawned // calculate the length of the level and initialize int avgLength = 150 + difficulty; int levelLength = rand.Next(avgLength * 20 / 100, avgLength * 120 / 100); // length can vary 20% from the average land = new int[levelLength]; itemList = new List <Item>(); enemyList = new List <Enemy>(); List <Item.Temp> tempItemList = new List <Item.Temp>(); completeCode = CompleteCode.Incomplete; // generate random background color int red = 255 - difficulty - rand.Next(100 + difficulty); int green = 255 - difficulty - rand.Next(100 + difficulty); int blue = 255 - difficulty - rand.Next(100 + difficulty); if (red < 0) { red = rand.Next(100); } if (green < 0) { green = rand.Next(100); } if (blue < 0) { blue = rand.Next(100); } backgroundColor = new Color(red, green, blue); // begin generating land int curLandHeight = 4; for (int i = 0; i < 3; ++i) { curLandHeight += rand.Next(-1, 1); } int prevHeight = curLandHeight; int curLength = 0; // tell it to generate a fresh length bool isHole = true; // ...as if a hole just ended int curTomatoDist = curTomatoDist = avgRegTomatoDist * 2 + rand.Next(-varRegTomatoDist * 2, varRegTomatoDist * 2); for (int square = 0; square < levelLength;) { // is the last section finished? if (curLength <= 0) { curLength = 0; // generate a new length for the next section // did we just finish a hole? if (isHole) { // add a new section of land (land height is already specified, lets get the length) isHole = false; int odds = 200; curLength = 0; while (rand.Next(200) < odds) { odds -= 10 + rand.Next(difficulty); ++curLength; } } // did we just finish a section of land? else { // determine the height difference with the next section int heightDifference = 0; for (int i = 0; i < 8; ++i) { if (rand.Next(200) < 50 + difficulty) { heightDifference += rand.Next(-1, 1); } } // if land is already at max or min height, reverse the height change if necessary if (curLandHeight == maxLandHeight && heightDifference > 0 || curLandHeight == 1 && heightDifference < 0) { heightDifference *= -1; } // if the height change is higher than Joe can jump, reduce it to his maximum jump height if (heightDifference > maxJumpHeight) { heightDifference = maxJumpHeight; } // if the change will take the land higher than maximum, set it to the maximum difference if (curLandHeight + heightDifference > maxLandHeight) { heightDifference = maxLandHeight - curLandHeight; } // if the change will take the land lower than minimum, set it to the minimum difference if (curLandHeight + heightDifference < 1) { heightDifference = 1 - curLandHeight; } // make sure we haven't ended up with an invalid land height somehow! if (curLandHeight + heightDifference > maxLandHeight + heightDifference || curLandHeight < 1) { throw new Exception("Level Randomizer Error #1: INVALID Land Height!"); } // determine whether we should next add a hole, or just a conjoined section of land if (rand.Next(50) < 10 + rand.Next(difficulty)) { // calculate the maximum length of the hole int maxHoleLength = (int)(0.5f + maxFlatJumpWidth - jumpDifferential * heightDifference); // early in the level, shorten the max hole length if (square < 20) { maxHoleLength -= 2; } // determine the length that this hole should be, based on difficulty and randomness curLength = 0; for (int i = 0; i < maxHoleLength; ++i) { if (rand.Next(50) < 20 + rand.Next(difficulty)) { ++curLength; } } } // regardless of whether we're adding a new hole or a conjoined section, we need to set these isHole = true; prevHeight = curLandHeight; curLandHeight += heightDifference; // is the length still zero? if (curLength <= 0) { // lets go back and add on a new conjoined section of land continue; } } } // continue adding the current section of land if (isHole) { land[square] = 0; } else { land[square] = curLandHeight; } --curLength; // continue adding tomatoes where necessary if (curTomatoDist <= 0) { // add a new tomato Item.Temp tomato = new Item.Temp(square - 1, prevHeight + rand.Next(minItemHeight, maxItemHeight), Item.Type.Food); // should we add a golden tomato? if (rand.NextDouble() < avgOddsOfGolden) { // make it a golden tomato tomato.type = Item.Type.Gold; // determine the distance to the next tomato curTomatoDist = avgGoldTomatoDist + rand.Next(-varGoldTomatoDist, varGoldTomatoDist); // increase the distance haphazardly based on difficulty curTomatoDist += rand.Next(difficulty) * varGoldTomatoDist / 50; // make sure the distance to next tomato isn't too far if (curTomatoDist > avgGoldTomatoDist + varGoldTomatoDist * 4) { curTomatoDist = avgGoldTomatoDist; for (int i = 0; i < 4; ++i) { curTomatoDist += rand.Next(varGoldTomatoDist); } } } // should we add a regular tomato? else { // make it a regular tomato tomato.type = Item.Type.Food; // determine the distance to the next tomato curTomatoDist = avgRegTomatoDist + rand.Next(-varRegTomatoDist, varRegTomatoDist); // increase the distance haphazardly based on difficulty curTomatoDist += rand.Next(difficulty) * varRegTomatoDist / 50; // make sure the distance to next tomato isn't too far if (curTomatoDist > avgRegTomatoDist + varRegTomatoDist * 4) { curTomatoDist = avgRegTomatoDist; for (int i = 0; i < 4; ++i) { curTomatoDist += rand.Next(varRegTomatoDist); } } } // add the tomato item to the temp list tempItemList.Add(tomato); } --curTomatoDist; // should we add an extra life? if (rand.NextDouble() < avgOddsofExLife * (50 + difficulty) / 50) { Item.Temp life = new Item.Temp(square - 1, prevHeight + rand.Next(minItemHeight, maxItemHeight), Item.Type.Life); tempItemList.Add(life); } // only go to the next square if we've finished all the tasks for this square ++square; } // add the items to the main list foreach (Item.Temp temp in tempItemList) { itemList.Add(new Item(game.itemTexture, temp.column, temp.row, temp.type)); } }
public void Load(int levelNum) { /* TEMP * itemList = new List<Item>(); * completeCode = CompleteCode.Incomplete; * backgroundColor = Color.CornflowerBlue; * //this.levelNum = levelNum; TODO:CHECK? * * switch (levelNum) * { * case 1: // the first test level * backgroundColor = Color.LightSkyBlue; * land = new int[] { 2, 2, 2, 2, 2, 2, 2, 2, 4, 4, 4, 4, 0, 0, 3, 3, 3, 3, 0, 0, 2, 2, 2, 2, 2, 2, 5, 5, * 5, 5, 5, 6, 6, 6, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 4, 4, 4, 4, 4, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 2, * 2, 2, 2, 5, 5, 5, 3, 3, 3, 3, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, }; * itemList.Add(new Item(game.itemTexture, 23, 8, Item.Type.Food)); * itemList.Add(new Item(game.itemTexture, 61, 5, Item.Type.Food)); * itemList.Add(new Item(game.itemTexture, 84, 6, Item.Type.Food)); * break; * case 2: // the second test level * backgroundColor = Color.DarkKhaki; * land = new int[] { 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 2, 2, 2, 2, 2, 4, 4, 4, 4, 4, 0, 0, 0, * 0, 2, 2, 2, 2, 2, 0, 0, 4, 4, 4, 4, 4, 1, 1, 1, 1, 1, 0, 4, 4, 4, 4, 6, 6, 6, 6, 6, 0, 0, 0, 0, 0, * 2, 2, 2, 2, 5, 5, 5, 0, 0, 0, 6, 6, 0, 0, 6, 6, 6, 0, 0, 0, 5, 5, 5, 5, 5, 5, 0, 0, 0, 0, 3, 3, 3, * 3, 3, 3, }; * itemList.Add(new Item(game.itemTexture, 2, 6, Item.Type.Food)); * itemList.Add(new Item(game.itemTexture, 63, 8, Item.Type.Gold)); * break; * case 3: // the hard test level * backgroundColor = Color.Crimson; * land = new int[] { * // 0 1 2 3 4 5 6 7 8 9 * 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 10 * 0, 0, 0, 0, 2, 2, 0, 0, 0, 4, // 20 * 4, 4, 0, 0, 0, 0, 4, 0, 0, 0, // 30 * 0, 4, 0, 0, 0, 6, 6, 6, 0, 0, // 40 * 0, 0, 0, 2, 2, 2, 2, 2, 4, 4, // 50 * // 0 1 2 3 4 5 6 7 8 9 * 4, 0, 0, 0, 0, 0, 2, 0, 0, 0, // 60 * 0, 2, 2, 0, 0, 0, 0, 3, 3, 3, // 70 * 0, 0, 0, 0, 4, 4, 0, 0, 0, 0, // 80 * 0, 3, 3, 0, 0, 0, 0, 0, 0, 1, // 90 * 1, 1, 1, 0, 0, 0, 0, 3, 0, 0, // 100 * // 0 1 2 3 4 5 6 7 8 9 * 0, 0, 4, 0, 0, 0, 0, 5, 0, 0, // 110 * 0, 0, 0, 3, 0, 0, 0, 0, 0, 2, // 120 * 2, 2, 2, }; * itemList.Add(new Item(game.itemTexture, 46, 5, Item.Type.Food)); * itemList.Add(new Item(game.itemTexture, 58, 4, Item.Type.Food)); * break; * default: // an empty level * land = new int[] { 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, }; * break; * } * /*/// TEMP if (levelNum < 0 || levelNum > 99) { levelNum = 0; } game.LevelNum = levelNum; // load a random level? if (randomLevels) { LoadRandomLevel(levelNum); return; } // load the level file string filename = "Level"; if (levelNum < 10) { filename += '0'; } filename += levelNum.ToString() + ".txt"; string path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Levels/" + filename); string lineOfText; StreamReader sr; try { sr = new StreamReader(path); } catch (FileNotFoundException) { Load(levelNum + 1); return; } itemList = new List <Item>(); enemyList = new List <Enemy>(); completeCode = CompleteCode.Incomplete; backgroundColor = Color.CornflowerBlue; const int TabWidth = 8; const int MaxLevelLength = 1000; const int MaxVars = 10; int levelLength = 0; int levelHeight = 0; int[] tempLand = new int[MaxLevelLength]; List <Item.Temp> tempItemList = new List <Item.Temp>(); List <Enemy.Temp> tempEnemyList = new List <Enemy.Temp>(); string[] tempVarNames = new string[MaxVars]; string[] tempVarValues = new string[MaxVars]; for (int i = 0; i < MaxVars; ++i) { tempVarNames[i] = ""; tempVarValues[i] = ""; } //List<string> tempVarName = new List<string>(); //List<string> tempVarValue = new List<string>(); string tempVarData = ""; int curVars = 0; int varState = 0; for (int lineNum = 0; (lineOfText = sr.ReadLine()) != null; ++lineNum) { int length = lineOfText.Length; if (length > MaxLevelLength) { length = MaxLevelLength; } for (int cursor = 0, colNum = 0; cursor < length; ++cursor, ++colNum) { if (varState == 0) { bool modified = false; switch (lineOfText[cursor]) { case '#': modified = true; if (tempLand[colNum] == 0) { tempLand[colNum] = lineNum + 1; } break; case 'c': modified = true; tempItemList.Add(new Item.Temp(colNum - 1, lineNum, Item.Type.Food)); break; case 'g': modified = true; tempItemList.Add(new Item.Temp(colNum - 1, lineNum, Item.Type.Gold)); break; case 'e': modified = true; tempItemList.Add(new Item.Temp(colNum - 1, lineNum, Item.Type.Life)); break; case 'F': modified = true; tempEnemyList.Add(new Enemy.Temp(colNum - 1, lineNum, Enemy.Type.Gobbler, (int)Gobbler.SubType.Blue)); break; case 'G': modified = true; tempEnemyList.Add(new Enemy.Temp(colNum - 1, lineNum, Enemy.Type.Gobbler, (int)Gobbler.SubType.Green)); break; case 'H': modified = true; tempEnemyList.Add(new Enemy.Temp(colNum - 1, lineNum, Enemy.Type.Gobbler, (int)Gobbler.SubType.Yellow)); break; case 'I': modified = true; tempEnemyList.Add(new Enemy.Temp(colNum - 1, lineNum, Enemy.Type.Gobbler, (int)Gobbler.SubType.Purple)); break; case '\t': colNum += TabWidth - (colNum % TabWidth) - 1; break; case '!': varState = 1; break; } if (modified) { if (levelLength < colNum + 1) { levelLength = colNum + 1; } if (levelHeight < lineNum + 1) { levelHeight = lineNum + 1; } } } else { char c = lineOfText[cursor]; if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) { tempVarData += c; } else if (varState == 1 && c == '=') { tempVarNames[curVars] = tempVarData; tempVarData = ""; varState = 2; } else if (varState == 2 && c == '!') { tempVarValues[curVars] = tempVarData; tempVarData = ""; varState = 1; ++curVars; } } } } if (varState == 2) { tempVarValues[curVars] = tempVarData; } // assign and correct land heights land = new int[levelLength]; for (int i = 0; i < levelLength; ++i) { if (tempLand[i] == 0) { land[i] = 0; } else { land[i] = levelHeight + 1 - tempLand[i]; } } // add items and enemies and correct initial heights foreach (Item.Temp temp in tempItemList) { itemList.Add(new Item(game.itemTexture, temp.column, levelHeight - temp.row, temp.type)); } foreach (Enemy.Temp temp in tempEnemyList) { temp.row = levelHeight - temp.row; enemyList.Add(Enemy.Create(game, this, temp)); } // load options for (int i = 0; i < MaxVars; ++i) { string varName = tempVarNames[i].ToLower(); if (varName == "color") { PropertyInfo colorProperty = typeof(Color).GetProperty(tempVarValues[i]); try { backgroundColor = (Color)colorProperty.GetValue(null, null); } catch (Exception) { backgroundColor = Color.DarkGray; } continue; } if (varName == "author") { author = tempVarValues[i]; if (author == "") { author = "Tim"; } continue; } } // TEMP */ }