1 /// contains the tile map object class 2 module ysge.objects.tileMap; 3 4 import std.math; 5 import ysge.project; 6 7 struct Tile { 8 int id; 9 10 this(int pid) { 11 id = pid; 12 } 13 } 14 15 /// tile type definition 16 struct TileDef { 17 RenderInfo render; 18 bool hasCollision; /// whether it can collide with other boxes 19 } 20 21 /// tile map object with fast AABB collision 22 class TileMap : GameObject { 23 Tile*[][] tiles; 24 Vec2!int tileSize; 25 TileDef[int] tileDefs; 26 Vec2!int pos; 27 bool doCollision = true; /// whether collision should be checked at all 28 29 /// initialises the tile map 30 this(Vec2!ulong size) { 31 tiles = new Tile*[][](size.y, size.x); 32 33 foreach (ref line ; tiles) { 34 foreach (ref tile ; line) { 35 tile = new Tile(0); 36 } 37 } 38 } 39 40 /// loads tiles from a 2d array of tile ids 41 void FromIDs(int[][] ids) { 42 foreach (i, ref line ; ids) { 43 foreach (j, ref tile ; line) { 44 tiles[i][j] = new Tile(tile); 45 } 46 } 47 } 48 49 /// returns the map size 50 Vec2!ulong GetSize() { 51 if (tiles.length == 0) { 52 return Vec2!ulong(0, 0); 53 } 54 55 return Vec2!ulong( 56 tiles[0].length, 57 tiles.length 58 ); 59 } 60 61 /// sets the map size (resets all tiles) 62 void SetSize(Vec2!ulong size) { 63 tiles = new Tile*[][](size.y, size.x); 64 65 foreach (ref line ; tiles) { 66 foreach (ref tile ; line) { 67 tile = new Tile(0); 68 } 69 } 70 } 71 72 /** 73 * updates the tile map 74 * should not be called by user 75 */ 76 override void Update(Project parent) { 77 78 } 79 80 /** 81 * checks collision with fast AABB 82 * should not be called by user 83 */ 84 override bool CollidesWith(SimpleBox other) { 85 if (!doCollision) { 86 return false; 87 } 88 89 Vec2!int posOnMap = Vec2!int(other.box.x, other.box.y); 90 posOnMap.x -= pos.x; 91 posOnMap.y -= pos.y; 92 93 Vec2!int start = Vec2!int( 94 cast(int) floor(posOnMap.CastTo!float().x / tileSize.x), 95 cast(int) floor(posOnMap.CastTo!float().y / tileSize.y) 96 ); 97 Vec2!int end = Vec2!int( 98 cast(int) ceil(posOnMap.CastTo!float().x / tileSize.x), 99 cast(int) ceil(posOnMap.CastTo!float().y / tileSize.y) 100 ); 101 102 Vec2!int otherSize = Vec2!int(other.box.w, other.box.h); 103 104 end.x += cast(int) ceil(otherSize.CastTo!float().x / tileSize.x); 105 end.y += cast(int) ceil(otherSize.CastTo!float().y / tileSize.y); 106 107 for (int y = start.y; y <= end.y; ++ y) { 108 for (int x = start.x; x <= end.x; ++ x) { 109 if ( 110 (y < 0) || 111 (x < 0) || 112 (y >= tiles.length) || 113 (x >= tiles[0].length) 114 ) { 115 continue; 116 } 117 118 auto tile = tiles[y][x]; 119 auto def = tileDefs[tile.id]; 120 121 if (!def.hasCollision) { 122 continue; 123 } 124 125 SDL_Rect box; 126 box.x = x * tileSize.x; 127 box.y = y * tileSize.y; 128 box.w = tileSize.x; 129 box.h = tileSize.y; 130 131 if ( 132 (box.x < other.box.x + other.box.w) && 133 (box.x + box.w > other.box.x) && 134 (box.y < other.box.y + other.box.h) && 135 (box.y + box.h > other.box.y) 136 ) { 137 return true; 138 } 139 } 140 } 141 142 return false; 143 } 144 145 /** 146 * renders the object 147 * should not be called by user 148 */ 149 override void Render(Project parent) { 150 Vec2!int start = Vec2!int( 151 pos.x - parent.currentScene.camera.x, 152 pos.y - parent.currentScene.camera.y 153 ); 154 155 for (int y = 0; y < tiles.length; ++ y) { 156 for (int x = 0; x < tiles[0].length; ++ x) { 157 Vec2!int tilePos = Vec2!int( 158 start.x + (x * tileSize.x), 159 start.y + (y * tileSize.y) 160 ); 161 Vec2!int tileEnd = Vec2!int( 162 tilePos.x + tileSize.x, 163 tilePos.y + tileSize.y 164 ); 165 166 auto res = parent.GetResolution(); 167 168 if ( 169 (tileEnd.x < 0) || 170 (tileEnd.y < 0) 171 ) { 172 continue; 173 } 174 175 if ( 176 (tilePos.x > res.x) || 177 (tilePos.y > res.y) 178 ) { 179 break; 180 } 181 182 auto tile = tiles[y][x]; 183 auto def = tileDefs[tile.id]; 184 auto rect = SDL_Rect( 185 tilePos.x, tilePos.y, tileSize.x, tileSize.y 186 ); 187 188 switch (def.render.type) { 189 case RenderType.Colour: { 190 if (def.render.value.colour.a == 0) { 191 break; 192 } 193 194 SDL_SetRenderDrawColor( 195 parent.renderer, 196 def.render.value.colour.r, 197 def.render.value.colour.g, 198 def.render.value.colour.b, 199 def.render.value.colour.a 200 ); 201 SDL_RenderFillRect(parent.renderer, &rect); 202 break; 203 } 204 case RenderType.Texture: { 205 SDL_Rect* src; 206 207 if (def.render.props.doCrop) { 208 src = new SDL_Rect(); 209 *src = def.render.props.crop; 210 } 211 212 SDL_RenderCopy( 213 parent.renderer, def.render.value.texture.texture, 214 src, &rect 215 ); 216 break; 217 } 218 default: assert(0); 219 } 220 } 221 } 222 } 223 }