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.

137 lines
4.3 KiB

  1. using Microsoft.Xna.Framework;
  2. using Microsoft.Xna.Framework.Content;
  3. using Microsoft.Xna.Framework.Graphics;
  4. using System;
  5. using System.Collections.Generic;
  6. using System.Linq;
  7. namespace SemiColinGames {
  8. enum Terrain {
  9. Grass,
  10. GrassL,
  11. GrassR,
  12. Rock,
  13. RockL,
  14. RockR,
  15. Water,
  16. Block
  17. }
  18. class Tile {
  19. readonly Texture2D texture;
  20. readonly Rectangle textureSource;
  21. static readonly Dictionary<Terrain, Point> terrainToTilePosition =
  22. new Dictionary<Terrain, Point>() {
  23. { Terrain.Grass, new Point(3, 0) },
  24. { Terrain.GrassL, new Point(2, 0) },
  25. { Terrain.GrassR, new Point(4, 0) },
  26. { Terrain.Rock, new Point(3, 1) },
  27. { Terrain.RockL, new Point(1, 2) },
  28. { Terrain.RockR, new Point(5, 2) },
  29. { Terrain.Water, new Point(9, 2) },
  30. { Terrain.Block, new Point(6, 3) },
  31. };
  32. public Tile(Texture2D texture, Terrain terrain, Rectangle position) {
  33. this.texture = texture;
  34. Terrain = terrain;
  35. Position = position;
  36. this.textureSource = TextureSource();
  37. }
  38. public Rectangle Position { get; private set; }
  39. public Terrain Terrain { get; private set; }
  40. public void Draw(SpriteBatch spriteBatch) {
  41. spriteBatch.Draw(texture, Position.Location.ToVector2(), textureSource, Color.White);
  42. }
  43. private Rectangle TextureSource() {
  44. int size = World.TileSize;
  45. Point pos = terrainToTilePosition[Terrain];
  46. return new Rectangle(pos.X * size, pos.Y * size, size, size);
  47. }
  48. }
  49. class World {
  50. public const int TileSize = 16;
  51. readonly Tile[] tiles;
  52. // Size of World in terms of tile grid.
  53. private readonly int tileWidth;
  54. private readonly int tileHeight;
  55. // Size of World in pixels.
  56. public int Width {
  57. get { return tileWidth * TileSize; }
  58. }
  59. public int Height {
  60. get { return tileHeight * TileSize; }
  61. }
  62. private static readonly Dictionary<char, Terrain> charToTerrain =
  63. new Dictionary<char, Terrain>() {
  64. { '=', Terrain.Grass },
  65. { '<', Terrain.GrassL },
  66. { '>', Terrain.GrassR },
  67. { '.', Terrain.Rock },
  68. { '[', Terrain.RockL },
  69. { ']', Terrain.RockR },
  70. { '~', Terrain.Water },
  71. { 'X', Terrain.Block }
  72. };
  73. public World(ContentManager content, string levelSpecification) {
  74. Texture2D texture = content.Load<Texture2D>("tiles/anokolisa/grassland");
  75. var tilesList = new List<Tile>();
  76. string[] worldDesc = levelSpecification.Split('\n');
  77. tileWidth = worldDesc.AsQueryable().Max(a => a.Length);
  78. tileHeight = worldDesc.Length;
  79. Debug.WriteLine("world size: {0}x{1}", tileWidth, tileHeight);
  80. for (int i = 0; i < tileWidth; i++) {
  81. for (int j = 0; j < tileHeight; j++) {
  82. if (i < worldDesc[j].Length) {
  83. char key = worldDesc[j][i];
  84. if (charToTerrain.ContainsKey(key)) {
  85. Terrain terrain = charToTerrain[key];
  86. var position = new Rectangle(i * TileSize, j * TileSize, TileSize, TileSize);
  87. tilesList.Add(new Tile(texture, terrain, position));
  88. }
  89. }
  90. }
  91. }
  92. tiles = tilesList.ToArray();
  93. // Because we added tiles from left to right, the CollisionTargets are sorted by x-position.
  94. // We maintain this invariant so that it's possible to efficiently find CollisionTargets that
  95. // are nearby a given x-position.
  96. CollisionTargets = new AABB[tiles.Length + 2];
  97. // Add a synthetic collisionTarget on the left side of the world.
  98. CollisionTargets[0] = new AABB(new Vector2(-1, 0), new Vector2(1, float.MaxValue));
  99. // Now add all the normal collisionTargets for every static terrain tile.
  100. Vector2 halfSize = new Vector2(TileSize / 2, TileSize / 2);
  101. for (int i = 0; i < tiles.Length; i++) {
  102. Vector2 center = new Vector2(
  103. tiles[i].Position.Left + halfSize.X, tiles[i].Position.Top + halfSize.Y);
  104. CollisionTargets[i + 1] = new AABB(center, halfSize);
  105. }
  106. // Add a final synthetic collisionTarget on the right side of the world.
  107. CollisionTargets[tiles.Length + 1] = new AABB(
  108. new Vector2(Width + 1, 0), new Vector2(1, float.MaxValue));
  109. }
  110. public void Draw(SpriteBatch spriteBatch) {
  111. foreach (Tile t in tiles) {
  112. t.Draw(spriteBatch);
  113. }
  114. }
  115. public AABB[] CollisionTargets { get; }
  116. }
  117. }