A stealth-based 2D platformer where you don't have to kill anyone unless you want to. https://www.semicolin.games
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

194 lines
8.9 KiB

  1. using Microsoft.Xna.Framework;
  2. using Microsoft.Xna.Framework.Graphics;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. namespace SemiColinGames {
  6. enum Terrain {
  7. Empty,
  8. Grass,
  9. GrassL,
  10. GrassR,
  11. Rock,
  12. RockL,
  13. RockR,
  14. Water,
  15. Block
  16. }
  17. class Tile {
  18. readonly Texture2D texture;
  19. public Tile(Texture2D texture, Terrain terrain, Rectangle position) {
  20. this.texture = texture;
  21. Terrain = terrain;
  22. Position = position;
  23. }
  24. public Rectangle Position { get; private set; }
  25. public Terrain Terrain { get; private set; }
  26. public void Draw(SpriteBatch spriteBatch, Camera camera) {
  27. int size = World.TileSize;
  28. Vector2 drawPos = new Vector2(Position.Left - camera.Left, Position.Top);
  29. switch (Terrain) {
  30. case Terrain.Grass: {
  31. Rectangle source = new Rectangle(3 * size, 0 * size, size, size);
  32. spriteBatch.Draw(texture, drawPos, source, Color.White);
  33. break;
  34. }
  35. case Terrain.GrassL: {
  36. Rectangle source = new Rectangle(2 * size, 0 * size, size, size);
  37. spriteBatch.Draw(texture, drawPos, source, Color.White);
  38. break;
  39. }
  40. case Terrain.GrassR: {
  41. Rectangle source = new Rectangle(4 * size, 0 * size, size, size);
  42. spriteBatch.Draw(texture, drawPos, source, Color.White);
  43. break;
  44. }
  45. case Terrain.Rock: {
  46. Rectangle source = new Rectangle(3 * size, 1 * size, size, size);
  47. spriteBatch.Draw(texture, drawPos, source, Color.White);
  48. break;
  49. }
  50. case Terrain.RockL: {
  51. Rectangle source = new Rectangle(1 * size, 2 * size, size, size);
  52. spriteBatch.Draw(texture, drawPos, source, Color.White);
  53. break;
  54. }
  55. case Terrain.RockR: {
  56. Rectangle source = new Rectangle(5 * size, 2 * size, size, size);
  57. spriteBatch.Draw(texture, drawPos, source, Color.White);
  58. break;
  59. }
  60. case Terrain.Water: {
  61. Rectangle source = new Rectangle(9 * size, 2 * size, size, size);
  62. spriteBatch.Draw(texture, drawPos, source, Color.White);
  63. break;
  64. }
  65. case Terrain.Block: {
  66. Rectangle source = new Rectangle(6 * size, 3 * size, size, size);
  67. spriteBatch.Draw(texture, drawPos, source, Color.White);
  68. break;
  69. }
  70. case Terrain.Empty:
  71. default:
  72. break;
  73. }
  74. }
  75. }
  76. class World {
  77. public const int TileSize = 16;
  78. readonly Tile[] tiles;
  79. readonly Aabb[] collisionTargets;
  80. // Size of World in terms of tile grid.
  81. private readonly int tileWidth;
  82. private readonly int tileHeight;
  83. // Size of World in pixels.
  84. public int Width {
  85. get { return tileWidth * TileSize; }
  86. }
  87. readonly string worldString = @"
  88. X
  89. .
  90. X <======> <==X X <=> <XX> XX .
  91. XXX .
  92. XXXX .
  93. XXXXX .
  94. X <X=X> <> <> <X> = <> X X X X <> X X XX X <=X> XXXXXX .
  95. <> [] [] XX XX XXX XX XXXXXXX .
  96. <> [] [] [] XXX XXX XXXX XXX <> <> XXXXXXXX
  97. []12345678[]123456[]123456789[]1234567890 123456 123456 12345 1234 12345 1234 123XXXX XXXX1234XXXXX XXXX1234[]123 1234567[]XXXXXXXXX12345678
  98. ====================> <====..========..======..=========..=========> <=============> <==============================================================> <=======..==============..==============================
  99. ....................] [............................................] [.............] [..............................................................] [.......................................................";
  100. public World(Texture2D texture) {
  101. var tilesList = new List<Tile>();
  102. string[] worldDesc = worldString.Split('\n');
  103. tileWidth = worldDesc.AsQueryable().Max(a => a.Length);
  104. tileHeight = worldDesc.Length;
  105. Debug.WriteLine("world size: {0}x{1}", tileWidth, tileHeight);
  106. for (int i = 0; i < tileWidth; i++) {
  107. for (int j = 0; j < tileHeight; j++) {
  108. Terrain terrain = Terrain.Empty;
  109. if (i < worldDesc[j].Length) {
  110. switch (worldDesc[j][i]) {
  111. case '=':
  112. terrain = Terrain.Grass;
  113. break;
  114. case '<':
  115. terrain = Terrain.GrassL;
  116. break;
  117. case '>':
  118. terrain = Terrain.GrassR;
  119. break;
  120. case '.':
  121. terrain = Terrain.Rock;
  122. break;
  123. case '[':
  124. terrain = Terrain.RockL;
  125. break;
  126. case ']':
  127. terrain = Terrain.RockR;
  128. break;
  129. case '~':
  130. terrain = Terrain.Water;
  131. break;
  132. case 'X':
  133. terrain = Terrain.Block;
  134. break;
  135. default:
  136. terrain = Terrain.Empty;
  137. break;
  138. }
  139. }
  140. if (terrain != Terrain.Empty) {
  141. var position = new Rectangle(i * TileSize, j * TileSize, TileSize, TileSize);
  142. tilesList.Add(new Tile(texture, terrain, position));
  143. }
  144. }
  145. }
  146. tiles = tilesList.ToArray();
  147. // Because we added tiles from left to right, the collisionTargets are sorted by x-position.
  148. // We maintain this invariant so that it's possible to efficiently find collisionTargets that
  149. // are nearby a given x-position.
  150. collisionTargets = new Aabb[tiles.Length + 2];
  151. // Add a synthetic collisionTarget on the left side of the world.
  152. collisionTargets[0] = new Aabb(new Vector2(-1, 0), new Vector2(1, float.MaxValue));
  153. // Now add all the normal collisionTargets for every static terrain tile.
  154. Vector2 halfSize = new Vector2(TileSize / 2, TileSize / 2);
  155. for (int i = 0; i < tiles.Length; i++) {
  156. Vector2 center = new Vector2(
  157. tiles[i].Position.Left + halfSize.X, tiles[i].Position.Top + halfSize.Y);
  158. collisionTargets[i + 1] = new Aabb(center, halfSize);
  159. }
  160. // Add a final synthetic collisionTarget on the right side of the world.
  161. collisionTargets[tiles.Length + 1] = new Aabb(
  162. new Vector2(Width + 1, 0), new Vector2(1, float.MaxValue));
  163. }
  164. public void Draw(SpriteBatch spriteBatch, Camera camera) {
  165. foreach (Tile t in tiles) {
  166. t.Draw(spriteBatch, camera);
  167. }
  168. }
  169. public Aabb[] CollisionTargets {
  170. get { return collisionTargets; }
  171. }
  172. }
  173. }