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.

195 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. // Size of World in terms of tile grid.
  80. private readonly int tileWidth;
  81. private readonly int tileHeight;
  82. // Size of World in pixels.
  83. public int Width {
  84. get { return tileWidth * TileSize; }
  85. }
  86. public int Height {
  87. get { return tileHeight * TileSize; }
  88. }
  89. readonly string worldString = @"
  90. X
  91. .
  92. X <======> <==X X <=> <XX> XX .
  93. XXX .
  94. XXXX .
  95. XXXXX .
  96. X <X=X> <> <> <X> = <> X X X X <> X X XX X <=X> XXXXXX .
  97. <> [] [] XX XX XXX XX XXXXXXX .
  98. <> [] [] [] XXX XXX XXXX XXX <> <> XXXXXXXX
  99. []12345678[]123456[]123456789[]1234567890 123456 123456 12345 1234 12345 1234 123XXXX XXXX1234XXXXX XXXX1234[]123 1234567[]XXXXXXXXX12345678
  100. ====================> <====..========..======..=========..=========> <=============> <==============================================================> <=======..==============..==============================
  101. ....................] [............................................] [.............] [..............................................................] [.......................................................";
  102. public World(Texture2D texture) {
  103. var tilesList = new List<Tile>();
  104. string[] worldDesc = worldString.Split('\n');
  105. tileWidth = worldDesc.AsQueryable().Max(a => a.Length);
  106. tileHeight = worldDesc.Length;
  107. Debug.WriteLine("world size: {0}x{1}", tileWidth, tileHeight);
  108. for (int i = 0; i < tileWidth; i++) {
  109. for (int j = 0; j < tileHeight; j++) {
  110. Terrain terrain = Terrain.Empty;
  111. if (i < worldDesc[j].Length) {
  112. switch (worldDesc[j][i]) {
  113. case '=':
  114. terrain = Terrain.Grass;
  115. break;
  116. case '<':
  117. terrain = Terrain.GrassL;
  118. break;
  119. case '>':
  120. terrain = Terrain.GrassR;
  121. break;
  122. case '.':
  123. terrain = Terrain.Rock;
  124. break;
  125. case '[':
  126. terrain = Terrain.RockL;
  127. break;
  128. case ']':
  129. terrain = Terrain.RockR;
  130. break;
  131. case '~':
  132. terrain = Terrain.Water;
  133. break;
  134. case 'X':
  135. terrain = Terrain.Block;
  136. break;
  137. default:
  138. terrain = Terrain.Empty;
  139. break;
  140. }
  141. }
  142. if (terrain != Terrain.Empty) {
  143. var position = new Rectangle(i * TileSize, j * TileSize, TileSize, TileSize);
  144. tilesList.Add(new Tile(texture, terrain, position));
  145. }
  146. }
  147. }
  148. tiles = tilesList.ToArray();
  149. // Because we added tiles from left to right, the CollisionTargets are sorted by x-position.
  150. // We maintain this invariant so that it's possible to efficiently find CollisionTargets that
  151. // are nearby a given x-position.
  152. CollisionTargets = new AABB[tiles.Length + 2];
  153. // Add a synthetic collisionTarget on the left side of the world.
  154. CollisionTargets[0] = new AABB(new Vector2(-1, 0), new Vector2(1, float.MaxValue));
  155. // Now add all the normal collisionTargets for every static terrain tile.
  156. Vector2 halfSize = new Vector2(TileSize / 2, TileSize / 2);
  157. for (int i = 0; i < tiles.Length; i++) {
  158. Vector2 center = new Vector2(
  159. tiles[i].Position.Left + halfSize.X, tiles[i].Position.Top + halfSize.Y);
  160. CollisionTargets[i + 1] = new AABB(center, halfSize);
  161. }
  162. // Add a final synthetic collisionTarget on the right side of the world.
  163. CollisionTargets[tiles.Length + 1] = new AABB(
  164. new Vector2(Width + 1, 0), new Vector2(1, float.MaxValue));
  165. }
  166. public void Draw(SpriteBatch spriteBatch, Camera camera) {
  167. foreach (Tile t in tiles) {
  168. t.Draw(spriteBatch, camera);
  169. }
  170. }
  171. public AABB[] CollisionTargets { get; }
  172. }
  173. }