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 struct TileDef {
16 	RenderType  renderType;
17 	RenderValue render;
18 	bool        hasCollision;
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 
28 	/// initialises the tile map
29 	this(Vec2!ulong size) {
30 		tiles = new Tile*[][](size.y, size.x);
31 
32 		foreach (ref line ; tiles) {
33 			foreach (ref tile ; line) {
34 				tile = new Tile(0);
35 			}
36 		}
37 	}
38 
39 	/// loads tiles from a 2d array of tile ids
40 	void FromIDs(int[][] ids) {
41 		foreach (i, ref line ; ids) {
42 			foreach (j, ref tile ; line) {
43 				tiles[i][j] = new Tile(tile);
44 			}
45 		}
46 	}
47 
48 	/**
49 	* updates the tile map
50 	* should not be called by user
51 	*/
52 	override void Update(Project parent) {
53 		
54 	}
55 
56 	/**
57 	* checks collision with fast AABB
58 	* should not be called by user
59 	*/
60 	override bool CollidesWith(SimpleBox other) {
61 		Vec2!int posOnMap  = Vec2!int(other.box.x, other.box.y);
62 		posOnMap.x        -= pos.x;
63 		posOnMap.y        -= pos.y;
64 
65 		Vec2!int start = Vec2!int(
66 			cast(int) floor(posOnMap.CastTo!float().x / tileSize.x),
67 			cast(int) floor(posOnMap.CastTo!float().y / tileSize.y)
68 		);
69 		Vec2!int end = Vec2!int(
70 			cast(int) ceil(posOnMap.CastTo!float().x / tileSize.x),
71 			cast(int) ceil(posOnMap.CastTo!float().y / tileSize.y)
72 		);
73 
74 		Vec2!int otherSize = Vec2!int(other.box.w, other.box.h);
75 
76 		end.x += cast(int) ceil(otherSize.CastTo!float().x / tileSize.x);
77 		end.y += cast(int) ceil(otherSize.CastTo!float().y / tileSize.y);
78 
79 		for (int y = start.y; y <= end.y; ++ y) {
80 			for (int x = start.x; x <= end.x; ++ x) {
81 				if (
82 					(y < 0) ||
83 					(x < 0) ||
84 					(y >= tiles.length) ||
85 					(x >= tiles[0].length)
86 				) {
87 					continue;
88 				}
89 
90 				auto tile = tiles[y][x];
91 				auto def  = tileDefs[tile.id];
92 
93 				if (!def.hasCollision) {
94 					continue;
95 				}
96 
97 				SDL_Rect box;
98 				box.x = x * tileSize.x;
99 				box.y = y * tileSize.y;
100 				box.w = tileSize.x;
101 				box.h = tileSize.y;
102  
103 				if (
104 					(box.x < other.box.x + other.box.w) &&
105 					(box.x + box.w > other.box.x) &&
106 					(box.y < other.box.y + other.box.h) &&
107 					(box.y + box.h > other.box.y)
108 				) {
109 					return true;
110 				}
111 			}
112 		}
113 
114 		return false;
115 	}
116 
117 	/**
118 	* renders the object
119 	* should not be called by user
120 	*/
121 	override void Render(Project parent) {
122 		Vec2!int start = Vec2!int(
123 			pos.x - parent.currentScene.camera.x,
124 			pos.y - parent.currentScene.camera.y
125 		);
126 
127 		for (int y = 0; y < tiles.length; ++ y) {
128 			for (int x = 0; x < tiles[0].length; ++ x) {
129 				Vec2!int tilePos = Vec2!int(
130 					start.x + (x * tileSize.x),
131 					start.y + (y * tileSize.y)
132 				);
133 				Vec2!int tileEnd = Vec2!int(
134 					tilePos.x + tileSize.x,
135 					tilePos.y + tileSize.y
136 				);
137 
138 				auto res = parent.GetResolution();
139 
140 				if (
141 					(tileEnd.x < 0) ||
142 					(tileEnd.y < 0)
143 				) {
144 					continue;
145 				}
146 
147 				if (
148 					(tilePos.x > res.x) ||
149 					(tilePos.y > res.y)
150 				) {
151 					break;
152 				}
153 
154 				auto tile = tiles[y][x];
155 				auto def  = tileDefs[tile.id];
156 				auto rect = SDL_Rect(
157 					tilePos.x, tilePos.y, tileSize.x, tileSize.y
158 				);
159 
160 				switch (def.renderType) {
161 					case RenderType.Colour: {
162 						if (def.render.colour.a == 0) {
163 							break;
164 						}
165 						
166 						SDL_SetRenderDrawColor(
167 							parent.renderer,
168 							def.render.colour.r,
169 							def.render.colour.g,
170 							def.render.colour.b,
171 							def.render.colour.a
172 						);
173 						SDL_RenderFillRect(parent.renderer, &rect);
174 						break;
175 					}
176 					case RenderType.Texture: {
177 						SDL_RenderCopy(
178 							parent.renderer, def.render.texture, null, &rect
179 						);
180 						break;
181 					}
182 					default: assert(0);
183 				}
184 			}
185 		}
186 	}
187 }