summaryrefslogtreecommitdiff
path: root/src/gfx2d.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/gfx2d.c')
-rw-r--r--src/gfx2d.c573
1 files changed, 364 insertions, 209 deletions
diff --git a/src/gfx2d.c b/src/gfx2d.c
index da265b0..8d78c2b 100644
--- a/src/gfx2d.c
+++ b/src/gfx2d.c
@@ -20,6 +20,12 @@
20/// Time between animation updates. 20/// Time between animation updates.
21#define ANIMATION_UPDATE_DELTA (1.0 / ANIMATION_FPS) 21#define ANIMATION_UPDATE_DELTA (1.0 / ANIMATION_FPS)
22 22
23/// Take the maximum of two values.
24#define max(a, b) ((a) > (b) ? (a) : (b))
25
26/// Take the minimum of two values.
27#define min(a, b) ((a) < (b) ? (a) : (b))
28
23typedef struct ivec2 { 29typedef struct ivec2 {
24 int x, y; 30 int x, y;
25} ivec2; 31} ivec2;
@@ -32,11 +38,11 @@ typedef struct vec2 {
32// Renderer state. 38// Renderer state.
33// ----------------------------------------------------------------------------- 39// -----------------------------------------------------------------------------
34 40
35typedef struct CoordSystem { 41typedef struct IsoCoordSystem {
36 ivec2 o; // Origin. 42 ivec2 o; // Origin.
37 ivec2 x; 43 ivec2 x;
38 ivec2 y; 44 ivec2 y;
39} CoordSystem; 45} IsoCoordSystem;
40 46
41typedef struct Screen { 47typedef struct Screen {
42 int width; 48 int width;
@@ -52,9 +58,9 @@ typedef struct SpriteInstance {
52 int frame; // Current frame of animation. 58 int frame; // Current frame of animation.
53} SpriteInstance; 59} SpriteInstance;
54 60
55typedef struct IsoGfx { 61typedef struct Gfx2d {
56 Screen screen; 62 Screen screen;
57 CoordSystem iso_space; 63 IsoCoordSystem iso_space;
58 ivec2 camera; 64 ivec2 camera;
59 double last_animation_time; 65 double last_animation_time;
60 Tile next_tile; // For procedurally-generated tiles. 66 Tile next_tile; // For procedurally-generated tiles.
@@ -63,7 +69,7 @@ typedef struct IsoGfx {
63 SpriteInstance* head_sprite; // Head of sprites list. 69 SpriteInstance* head_sprite; // Head of sprites list.
64 memstack stack; 70 memstack stack;
65 size_t watermark; 71 size_t watermark;
66} IsoGfx; 72} Gfx2d;
67 73
68// ----------------------------------------------------------------------------- 74// -----------------------------------------------------------------------------
69// Math and world / tile / screen access. 75// Math and world / tile / screen access.
@@ -79,17 +85,58 @@ static inline ivec2 ivec2_scale(ivec2 a, int s) {
79 85
80static inline ivec2 ivec2_neg(ivec2 a) { return (ivec2){.x = -a.x, .y = -a.y}; } 86static inline ivec2 ivec2_neg(ivec2 a) { return (ivec2){.x = -a.x, .y = -a.y}; }
81 87
82static inline ivec2 iso2cart(ivec2 iso, int s, int t, int w) {
83 return (ivec2){.x = (iso.x - iso.y) * (s / 2) + (w / 2),
84 .y = (iso.x + iso.y) * (t / 2)};
85}
86
87static inline vec2 vec2_add(vec2 a, vec2 b) { 88static inline vec2 vec2_add(vec2 a, vec2 b) {
88 return (vec2){.x = a.x + b.x, .y = a.y + b.y}; 89 return (vec2){.x = a.x + b.x, .y = a.y + b.y};
89} 90}
90 91
91static inline vec2 ivec2_to_vec2(ivec2 a) { return (vec2){a.x, a.y}; } 92static inline vec2 ivec2_to_vec2(ivec2 a) { return (vec2){a.x, a.y}; }
92 93
94/// Map map coordinates to screen coordinates, both Cartesian.
95static ivec2 map2screen(
96 ivec2 camera, int tile_width, int tile_height, int map_x, int map_y) {
97 return ivec2_add(
98 ivec2_neg(camera),
99 (ivec2){.x = map_x * tile_width, .y = map_y * tile_height});
100}
101
102// Not actually used because we pre-compute the two axis vectors instead.
103// See make_iso_coord_system() and the other definition of iso2cart() below.
104// static inline ivec2 iso2cart(ivec2 iso, int s, int t, int w) {
105// return (ivec2){.x = (iso.x - iso.y) * (s / 2) + (w / 2),
106// .y = (iso.x + iso.y) * (t / 2)};
107// }
108
109/// Create the basis for the isometric coordinate system with origin and vectors
110/// expressed in the Cartesian system.
111static IsoCoordSystem make_iso_coord_system(
112 const Tm_Map* const map, const Screen* const screen) {
113 assert(map);
114 assert(screen);
115 const ivec2 o = {screen->width / 2, 0};
116 const ivec2 x = {
117 .x = map->base_tile_width / 2, .y = map->base_tile_height / 2};
118 const ivec2 y = {
119 .x = -map->base_tile_width / 2, .y = map->base_tile_height / 2};
120 return (IsoCoordSystem){o, x, y};
121}
122
123/// Map isometric coordinates to Cartesian coordinates.
124///
125/// For a tile, this gets the screen position of the top diamond-corner of the
126/// tile.
127///
128/// Takes the camera displacement into account.
129static ivec2 iso2cart(
130 const IsoCoordSystem iso_space, ivec2 camera, int iso_x, int iso_y) {
131 const ivec2 vx_offset = ivec2_scale(iso_space.x, iso_x);
132 const ivec2 vy_offset = ivec2_scale(iso_space.y, iso_y);
133 const ivec2 origin_world_space =
134 ivec2_add(iso_space.o, ivec2_add(vx_offset, vy_offset));
135 const ivec2 origin_view_space =
136 ivec2_add(origin_world_space, ivec2_neg(camera));
137 return origin_view_space;
138}
139
93// Method 1. 140// Method 1.
94// static inline vec2 cart2iso(vec2 cart, int s, int t, int w) { 141// static inline vec2 cart2iso(vec2 cart, int s, int t, int w) {
95// const double x = cart.x - (double)(w / 2); 142// const double x = cart.x - (double)(w / 2);
@@ -103,8 +150,8 @@ static inline vec2 cart2iso(vec2 cart, int s, int t, int w) {
103 const double one_over_s = 1. / (double)s; 150 const double one_over_s = 1. / (double)s;
104 const double one_over_t = 1. / (double)t; 151 const double one_over_t = 1. / (double)t;
105 const double x = cart.x - (double)(w / 2); 152 const double x = cart.x - (double)(w / 2);
106 return (vec2){.x = (one_over_s * x + one_over_t * cart.y), 153 return (vec2){.x = ((one_over_s * x) + (one_over_t * cart.y)),
107 .y = (-one_over_s * x + one_over_t * cart.y)}; 154 .y = ((-one_over_s * x) + (one_over_t * cart.y))};
108} 155}
109 156
110static inline const Pixel* screen_xy_const_ref( 157static inline const Pixel* screen_xy_const_ref(
@@ -114,10 +161,10 @@ static inline const Pixel* screen_xy_const_ref(
114 assert(y >= 0); 161 assert(y >= 0);
115 assert(x < screen->width); 162 assert(x < screen->width);
116 assert(y < screen->height); 163 assert(y < screen->height);
117 return &screen->pixels[y * screen->width + x]; 164 return &screen->pixels[(y * screen->width) + x];
118} 165}
119 166
120static inline Pixel screen_xy(Screen* screen, int x, int y) { 167static inline Pixel screen_xy(const Screen* screen, int x, int y) {
121 return *screen_xy_const_ref(screen, x, y); 168 return *screen_xy_const_ref(screen, x, y);
122} 169}
123 170
@@ -125,81 +172,67 @@ static inline Pixel* screen_xy_mut(Screen* screen, int x, int y) {
125 return (Pixel*)screen_xy_const_ref(screen, x, y); 172 return (Pixel*)screen_xy_const_ref(screen, x, y);
126} 173}
127 174
128/// Create the basis for the isometric coordinate system with origin and vectors
129/// expressed in the Cartesian system.
130static CoordSystem make_iso_coord_system(
131 const Tm_Map* const map, const Screen* const screen) {
132 assert(map);
133 assert(screen);
134 const ivec2 o = {screen->width / 2, 0};
135 const ivec2 x = {
136 .x = map->base_tile_width / 2, .y = map->base_tile_height / 2};
137 const ivec2 y = {
138 .x = -map->base_tile_width / 2, .y = map->base_tile_height / 2};
139 return (CoordSystem){o, x, y};
140}
141
142// ----------------------------------------------------------------------------- 175// -----------------------------------------------------------------------------
143// Renderer, world and tile management. 176// Renderer, world and tile management.
144// ----------------------------------------------------------------------------- 177// -----------------------------------------------------------------------------
145 178
146IsoGfx* isogfx_new(const IsoGfxDesc* desc) { 179Gfx2d* gfx2d_new(const Gfx2dDesc* desc) {
147 assert(desc->screen_width > 0); 180 assert(desc->screen_width > 0);
148 assert(desc->screen_height > 0); 181 assert(desc->screen_height > 0);
149 // Part of our implementation assumes even widths and heights for precision. 182 // Part of our implementation assumes even widths and heights for precision.
150 assert((desc->screen_width & 1) == 0); 183 assert((desc->screen_width & 1) == 0);
151 assert((desc->screen_height & 1) == 0); 184 assert((desc->screen_height & 1) == 0);
152 185
153 IsoGfx tmp = {0}; 186 Gfx2d tmp = {0};
154 if (!memstack_make(&tmp.stack, desc->memory_size, desc->memory)) { 187 if (!memstack_make(&tmp.stack, desc->memory_size, desc->memory)) {
155 goto cleanup; 188 goto cleanup;
156 } 189 }
157 IsoGfx* iso = 190 Gfx2d* gfx =
158 memstack_alloc_aligned(&tmp.stack, sizeof(IsoGfx), alignof(IsoGfx)); 191 memstack_alloc_aligned(&tmp.stack, sizeof(Gfx2d), alignof(Gfx2d));
159 *iso = tmp; 192 *gfx = tmp;
160 193
161 const size_t screen_size_bytes = 194 const size_t screen_size_bytes =
162 desc->screen_width * desc->screen_height * sizeof(Pixel); 195 desc->screen_width * desc->screen_height * sizeof(Pixel);
163 Pixel* screen = 196 Pixel* screen =
164 memstack_alloc_aligned(&iso->stack, screen_size_bytes, alignof(Pixel)); 197 memstack_alloc_aligned(&gfx->stack, screen_size_bytes, alignof(Pixel));
165 198
166 iso->screen = (Screen){.width = desc->screen_width, 199 gfx->screen = (Screen){.width = desc->screen_width,
167 .height = desc->screen_height, 200 .height = desc->screen_height,
168 .pixels = screen}; 201 .pixels = screen};
169 202
170 iso->last_animation_time = 0.0; 203 gfx->last_animation_time = 0.0;
171 iso->watermark = memstack_watermark(&iso->stack); 204 gfx->watermark = memstack_watermark(&gfx->stack);
172 205
173 return iso; 206 return gfx;
174 207
175cleanup: 208cleanup:
176 isogfx_del(&iso); 209 gfx2d_del(&gfx);
177 return nullptr; 210 return nullptr;
178} 211}
179 212
180void isogfx_clear(IsoGfx* iso) { 213void gfx2d_clear(Gfx2d* gfx) {
181 assert(iso); 214 assert(gfx);
182 iso->last_animation_time = 0.0; 215 gfx->last_animation_time = 0.0;
183 iso->next_tile = 0; 216 gfx->next_tile = 0;
184 iso->map = nullptr; 217 gfx->map = nullptr;
185 iso->tileset = nullptr; 218 gfx->tileset = nullptr;
186 iso->head_sprite = nullptr; 219 gfx->head_sprite = nullptr;
187 // The base of the stack contains the IsoGfx and the screen buffer. Make sure 220 // The base of the stack contains the Gfx2d and the screen buffer. Make sure
188 // we don't clear them. 221 // we don't clear them.
189 memstack_set_watermark(&iso->stack, iso->watermark); 222 memstack_set_watermark(&gfx->stack, gfx->watermark);
190} 223}
191 224
192void isogfx_del(IsoGfx** ppIso) { 225void gfx2d_del(Gfx2d** ppGfx) {
193 assert(ppIso); 226 assert(ppGfx);
194 IsoGfx* iso = *ppIso; 227 Gfx2d* gfx = *ppGfx;
195 if (iso) { 228 if (gfx) {
196 memstack_del(&iso->stack); 229 memstack_del(&gfx->stack);
197 *ppIso = nullptr; 230 *ppGfx = nullptr;
198 } 231 }
199} 232}
200 233
201void isogfx_make_map(IsoGfx* iso, const MapDesc* desc) { 234void gfx2d_make_map(Gfx2d* gfx, const MapDesc* desc) {
202 assert(iso); 235 assert(gfx);
203 assert(desc); 236 assert(desc);
204 assert(desc->tile_width > 0); 237 assert(desc->tile_width > 0);
205 assert(desc->tile_height > 0); 238 assert(desc->tile_height > 0);
@@ -214,7 +247,7 @@ void isogfx_make_map(IsoGfx* iso, const MapDesc* desc) {
214 assert(desc->num_tiles > 0); 247 assert(desc->num_tiles > 0);
215 248
216 // Handle recreation by destroying the previous world and sprites. 249 // Handle recreation by destroying the previous world and sprites.
217 isogfx_clear(iso); 250 gfx2d_clear(gfx);
218 251
219 const int world_size = desc->world_width * desc->world_height; 252 const int world_size = desc->world_width * desc->world_height;
220 const size_t map_size_bytes = sizeof(Tm_Map) + (world_size * sizeof(Tile)); 253 const size_t map_size_bytes = sizeof(Tm_Map) + (world_size * sizeof(Tile));
@@ -229,43 +262,45 @@ void isogfx_make_map(IsoGfx* iso, const MapDesc* desc) {
229 (desc->num_tiles * sizeof(Ts_Tile)) + 262 (desc->num_tiles * sizeof(Ts_Tile)) +
230 tile_data_size_bytes; 263 tile_data_size_bytes;
231 264
232 iso->map = memstack_alloc_aligned(&iso->stack, map_size_bytes, 4); 265 gfx->map = memstack_alloc_aligned(&gfx->stack, map_size_bytes, 4);
233 *iso->map = (Tm_Map){ 266 *gfx->map = (Tm_Map){
234 .world_width = desc->world_width, 267 .world_width = desc->world_width,
235 .world_height = desc->world_height, 268 .world_height = desc->world_height,
236 .base_tile_width = desc->tile_width, 269 .base_tile_width = desc->tile_width,
237 .base_tile_height = desc->tile_height, 270 .base_tile_height = desc->tile_height,
238 .num_layers = 1, 271 .num_layers = 1,
272 .flags =
273 (desc->orientation == MapOrthogonal) ? Tm_Orthogonal : Tm_Isometric,
239 }; 274 };
240 275
241 iso->tileset = memstack_alloc_aligned(&iso->stack, tileset_size_bytes, 4); 276 gfx->tileset = memstack_alloc_aligned(&gfx->stack, tileset_size_bytes, 4);
242 *iso->tileset = (Ts_TileSet){ 277 *gfx->tileset = (Ts_TileSet){
243 .num_tiles = desc->num_tiles, 278 .num_tiles = desc->num_tiles,
244 }; 279 };
245 280
246 iso->iso_space = make_iso_coord_system(iso->map, &iso->screen); 281 gfx->iso_space = make_iso_coord_system(gfx->map, &gfx->screen);
247} 282}
248 283
249bool isogfx_load_map(IsoGfx* iso, const char* filepath) { 284bool gfx2d_load_map(Gfx2d* gfx, const char* filepath) {
250 assert(iso); 285 assert(gfx);
251 assert(filepath); 286 assert(filepath);
252 287
253 bool success = false; 288 bool success = false;
254 289
255 // Handle recreation by destroying the previous world and sprites. 290 // Handle recreation by destroying the previous world and sprites.
256 isogfx_clear(iso); 291 gfx2d_clear(gfx);
257 292
258 // Load the map. 293 // Load the map.
259 printf("Load tile map: %s\n", filepath); 294 printf("Load tile map: %s\n", filepath);
260 WITH_FILE(filepath, { 295 WITH_FILE(filepath, {
261 const size_t map_size = get_file_size_f(file); 296 const size_t map_size = get_file_size_f(file);
262 iso->map = memstack_alloc_aligned(&iso->stack, map_size, 4); 297 gfx->map = memstack_alloc_aligned(&gfx->stack, map_size, 4);
263 success = read_file_f(file, iso->map); 298 success = read_file_f(file, gfx->map);
264 }); 299 });
265 if (!success) { 300 if (!success) {
266 goto cleanup; 301 goto cleanup;
267 } 302 }
268 Tm_Map* const map = iso->map; 303 Tm_Map* const map = gfx->map;
269 304
270 printf("Map orientation: %d\n", ((Tm_Flags*)&map->flags)->orientation); 305 printf("Map orientation: %d\n", ((Tm_Flags*)&map->flags)->orientation);
271 306
@@ -281,39 +316,39 @@ bool isogfx_load_map(IsoGfx* iso, const char* filepath) {
281 printf("Load tile set: %s\n", ts_path_cwd); 316 printf("Load tile set: %s\n", ts_path_cwd);
282 WITH_FILE(ts_path_cwd, { 317 WITH_FILE(ts_path_cwd, {
283 const size_t file_size = get_file_size_f(file); 318 const size_t file_size = get_file_size_f(file);
284 iso->tileset = memstack_alloc_aligned(&iso->stack, file_size, 4); 319 gfx->tileset = memstack_alloc_aligned(&gfx->stack, file_size, 4);
285 success = read_file_f(file, iso->tileset); 320 success = read_file_f(file, gfx->tileset);
286 }); 321 });
287 if (!success) { 322 if (!success) {
288 // TODO: Log errors using the log library. 323 // TODO: Log errors using the log library.
289 goto cleanup; 324 goto cleanup;
290 } 325 }
291 const Ts_TileSet* const tileset = iso->tileset; 326 const Ts_TileSet* const tileset = gfx->tileset;
292 printf("Loaded tile set (%u tiles): %s\n", tileset->num_tiles, ts_path_cwd); 327 printf("Loaded tile set (%u tiles): %s\n", tileset->num_tiles, ts_path_cwd);
293 328
294 // TODO: These assertions on input data should be library runtime errors. 329 // TODO: These assertions on input data should be library runtime errors.
295 assert(ts_validate_tileset(tileset)); 330 assert(ts_validate_tileset(tileset));
296 assert(tm_validate_map(map, tileset)); 331 assert(tm_validate_map(map, tileset));
297 332
298 iso->iso_space = make_iso_coord_system(iso->map, &iso->screen); 333 gfx->iso_space = make_iso_coord_system(gfx->map, &gfx->screen);
299 334
300 success = true; 335 success = true;
301 336
302cleanup: 337cleanup:
303 if (!success) { 338 if (!success) {
304 isogfx_clear(iso); 339 gfx2d_clear(gfx);
305 } 340 }
306 return success; 341 return success;
307} 342}
308 343
309int isogfx_world_width(const IsoGfx* iso) { 344int gfx2d_world_width(const Gfx2d* gfx) {
310 assert(iso); 345 assert(gfx);
311 return iso->map->world_width; 346 return gfx->map->world_width;
312} 347}
313 348
314int isogfx_world_height(const IsoGfx* iso) { 349int gfx2d_world_height(const Gfx2d* gfx) {
315 assert(iso); 350 assert(gfx);
316 return iso->map->world_height; 351 return gfx->map->world_height;
317} 352}
318 353
319static void make_tile_from_colour( 354static void make_tile_from_colour(
@@ -325,10 +360,10 @@ static void make_tile_from_colour(
325 const int height = tile->height; 360 const int height = tile->height;
326 const int r = width / height; 361 const int r = width / height;
327 for (int y = 0; y < height / 2; ++y) { 362 for (int y = 0; y < height / 2; ++y) {
328 const int mask_start = width / 2 - r * y - 1; 363 const int mask_start = (width / 2) - (r * y) - 1;
329 const int mask_end = width / 2 + r * y + 1; 364 const int mask_end = (width / 2) + (r * y) + 1;
330 for (int x = 0; x < width; ++x) { 365 for (int x = 0; x < width; ++x) {
331 const bool mask = (mask_start <= x) && (x <= mask_end); 366 const bool mask = ((mask_start <= x) && (x <= mask_end)) != 0;
332 const Pixel val = mask ? colour : (Pixel){.r = 0, .g = 0, .b = 0, .a = 0}; 367 const Pixel val = mask ? colour : (Pixel){.r = 0, .g = 0, .b = 0, .a = 0};
333 368
334 // Top half. 369 // Top half.
@@ -341,19 +376,19 @@ static void make_tile_from_colour(
341 } 376 }
342} 377}
343 378
344Tile isogfx_make_tile(IsoGfx* iso, const TileDesc* desc) { 379Tile gfx2d_make_tile(Gfx2d* gfx, const TileDesc* desc) {
345 assert(iso); 380 assert(gfx);
346 assert(desc); 381 assert(desc);
347 // Client must create a world first. 382 // Client must create a world first.
348 assert(iso->map); 383 assert(gfx->map);
349 assert(iso->tileset); 384 assert(gfx->tileset);
350 // Currently, procedural tiles must match the base tile size. 385 // Currently, procedural tiles must match the base tile size.
351 assert(desc->width == iso->map->base_tile_width); 386 assert(desc->width == gfx->map->base_tile_width);
352 assert(desc->height == iso->map->base_tile_height); 387 assert(desc->height == gfx->map->base_tile_height);
353 // Cannot exceed max tiles. 388 // Cannot exceed max tiles.
354 assert(iso->next_tile < iso->tileset->num_tiles); 389 assert(gfx->next_tile < gfx->tileset->num_tiles);
355 390
356 const Tile tile = iso->next_tile++; 391 const Tile tile = gfx->next_tile++;
357 392
358 const size_t tile_size_bytes = desc->width * desc->height * sizeof(Pixel); 393 const size_t tile_size_bytes = desc->width * desc->height * sizeof(Pixel);
359 394
@@ -362,16 +397,16 @@ Tile isogfx_make_tile(IsoGfx* iso, const TileDesc* desc) {
362 assert(desc->width > 0); 397 assert(desc->width > 0);
363 assert(desc->height > 0); 398 assert(desc->height > 0);
364 399
365 Ts_Tile* const ts_tile = ts_tileset_get_tile_mut(iso->tileset, tile); 400 Ts_Tile* const ts_tile = ts_tileset_get_tile_mut(gfx->tileset, tile);
366 401
367 *ts_tile = (Ts_Tile){ 402 *ts_tile = (Ts_Tile){
368 .width = iso->map->base_tile_width, 403 .width = gfx->map->base_tile_width,
369 .height = iso->map->base_tile_height, 404 .height = gfx->map->base_tile_height,
370 .pixels = tile * tile_size_bytes, 405 .pixels = tile * tile_size_bytes,
371 }; 406 };
372 407
373 Pixel* const tile_pixels = 408 Pixel* const tile_pixels =
374 ts_tileset_get_tile_pixels_mut(iso->tileset, tile); 409 ts_tileset_get_tile_pixels_mut(gfx->tileset, tile);
375 make_tile_from_colour(desc->colour, ts_tile, tile_pixels); 410 make_tile_from_colour(desc->colour, ts_tile, tile_pixels);
376 break; 411 break;
377 } 412 }
@@ -389,31 +424,31 @@ Tile isogfx_make_tile(IsoGfx* iso, const TileDesc* desc) {
389 return tile; 424 return tile;
390} 425}
391 426
392void isogfx_set_tile(IsoGfx* iso, int x, int y, Tile tile) { 427void gfx2d_set_tile(Gfx2d* gfx, int x, int y, Tile tile) {
393 assert(iso); 428 assert(gfx);
394 429
395 Tm_Layer* const layer = tm_map_get_layer_mut(iso->map, 0); 430 Tm_Layer* const layer = tm_map_get_layer_mut(gfx->map, 0);
396 Tile* map_tile = tm_layer_get_tile_mut(iso->map, layer, x, y); 431 Tile* map_tile = tm_layer_get_tile_mut(gfx->map, layer, x, y);
397 432
398 *map_tile = tile; 433 *map_tile = tile;
399} 434}
400 435
401void isogfx_set_tiles(IsoGfx* iso, int x0, int y0, int x1, int y1, Tile tile) { 436void gfx2d_set_tiles(Gfx2d* gfx, int x0, int y0, int x1, int y1, Tile tile) {
402 assert(iso); 437 assert(gfx);
403 for (int y = y0; y < y1; ++y) { 438 for (int y = y0; y < y1; ++y) {
404 for (int x = x0; x < x1; ++x) { 439 for (int x = x0; x < x1; ++x) {
405 isogfx_set_tile(iso, x, y, tile); 440 gfx2d_set_tile(gfx, x, y, tile);
406 } 441 }
407 } 442 }
408} 443}
409 444
410SpriteSheet isogfx_load_sprite_sheet(IsoGfx* iso, const char* filepath) { 445SpriteSheet gfx2d_load_sprite_sheet(Gfx2d* gfx, const char* filepath) {
411 assert(iso); 446 assert(gfx);
412 assert(filepath); 447 assert(filepath);
413 448
414 bool success = false; 449 bool success = false;
415 SpriteSheet spriteSheet = 0; 450 SpriteSheet spriteSheet = 0;
416 const size_t watermark = memstack_watermark(&iso->stack); 451 const size_t watermark = memstack_watermark(&gfx->stack);
417 452
418 // Load sprite sheet file. 453 // Load sprite sheet file.
419 printf("Load sprite sheet: %s\n", filepath); 454 printf("Load sprite sheet: %s\n", filepath);
@@ -421,7 +456,7 @@ SpriteSheet isogfx_load_sprite_sheet(IsoGfx* iso, const char* filepath) {
421 WITH_FILE(filepath, { 456 WITH_FILE(filepath, {
422 const size_t file_size = get_file_size_f(file); 457 const size_t file_size = get_file_size_f(file);
423 ss_sheet = 458 ss_sheet =
424 memstack_alloc_aligned(&iso->stack, file_size, alignof(Ss_SpriteSheet)); 459 memstack_alloc_aligned(&gfx->stack, file_size, alignof(Ss_SpriteSheet));
425 success = read_file_f(file, ss_sheet); 460 success = read_file_f(file, ss_sheet);
426 }); 461 });
427 if (!success) { 462 if (!success) {
@@ -434,63 +469,63 @@ SpriteSheet isogfx_load_sprite_sheet(IsoGfx* iso, const char* filepath) {
434cleanup: 469cleanup:
435 if (!success) { 470 if (!success) {
436 if (ss_sheet) { 471 if (ss_sheet) {
437 memstack_set_watermark(&iso->stack, watermark); 472 memstack_set_watermark(&gfx->stack, watermark);
438 } 473 }
439 } 474 }
440 return spriteSheet; 475 return spriteSheet;
441} 476}
442 477
443Sprite isogfx_make_sprite(IsoGfx* iso, SpriteSheet sheet) { 478Sprite gfx2d_make_sprite(Gfx2d* gfx, SpriteSheet sheet) {
444 assert(iso); 479 assert(gfx);
445 assert(sheet); 480 assert(sheet);
446 481
447 // TODO: Remove memstack_alloc() and replace it with a same-name macro that 482 // TODO: Remove memstack_alloc() and replace it with a same-name macro that
448 // calls memstack_alloc_aligned() with sizeof/alignof. No real point in 483 // calls memstack_alloc_aligned() with sizeof/alignof. No real point in
449 // having unaligned allocations. 484 // having unaligned allocations.
450 SpriteInstance* sprite = memstack_alloc_aligned( 485 SpriteInstance* sprite = memstack_alloc_aligned(
451 &iso->stack, sizeof(SpriteInstance), alignof(SpriteInstance)); 486 &gfx->stack, sizeof(SpriteInstance), alignof(SpriteInstance));
452 487
453 sprite->sheet = (const Ss_SpriteSheet*)sheet; 488 sprite->sheet = (const Ss_SpriteSheet*)sheet;
454 sprite->next = iso->head_sprite; 489 sprite->next = gfx->head_sprite;
455 iso->head_sprite = sprite; 490 gfx->head_sprite = sprite;
456 491
457 return (Sprite)sprite; 492 return (Sprite)sprite;
458} 493}
459 494
460void isogfx_set_sprite_position(IsoGfx* iso, Sprite hSprite, int x, int y) { 495void gfx2d_set_sprite_position(Gfx2d* gfx, Sprite hSprite, int x, int y) {
461 assert(iso); 496 assert(gfx);
462 SpriteInstance* sprite = (SpriteInstance*)hSprite; 497 SpriteInstance* sprite = (SpriteInstance*)hSprite;
463 sprite->position.x = x; 498 sprite->position.x = x;
464 sprite->position.y = y; 499 sprite->position.y = y;
465} 500}
466 501
467void isogfx_set_sprite_animation(IsoGfx* iso, Sprite hSprite, int animation) { 502void gfx2d_set_sprite_animation(Gfx2d* gfx, Sprite hSprite, int animation) {
468 assert(iso); 503 assert(gfx);
469 SpriteInstance* sprite = (SpriteInstance*)hSprite; 504 SpriteInstance* sprite = (SpriteInstance*)hSprite;
470 sprite->animation = animation; 505 sprite->animation = animation;
471} 506}
472 507
473void isogfx_update(IsoGfx* iso, double t) { 508void gfx2d_update(Gfx2d* gfx, double t) {
474 assert(iso); 509 assert(gfx);
475 510
476 // If this is the first time update() is called after initialization, just 511 // If this is the first time update() is called after initialization, just
477 // record the starting animation time. 512 // record the starting animation time.
478 if (iso->last_animation_time == 0.0) { 513 if (gfx->last_animation_time == 0.0) {
479 iso->last_animation_time = t; 514 gfx->last_animation_time = t;
480 return; 515 return;
481 } 516 }
482 517
483 if ((t - iso->last_animation_time) >= ANIMATION_UPDATE_DELTA) { 518 if ((t - gfx->last_animation_time) >= ANIMATION_UPDATE_DELTA) {
484 // TODO: Consider linking animated sprites in a separate list so that we 519 // TODO: Consider linking animated sprites in a separate list so that we
485 // only walk over those here and not also the static sprites. 520 // only walk over those here and not also the static sprites.
486 for (SpriteInstance* sprite = iso->head_sprite; sprite; 521 for (SpriteInstance* sprite = gfx->head_sprite; sprite;
487 sprite = sprite->next) { 522 sprite = sprite->next) {
488 const Ss_SpriteSheet* sheet = sprite->sheet; 523 const Ss_SpriteSheet* sheet = sprite->sheet;
489 const Ss_Row* row = ss_get_sprite_sheet_row(sheet, sprite->animation); 524 const Ss_Row* row = ss_get_sprite_sheet_row(sheet, sprite->animation);
490 sprite->frame = (sprite->frame + 1) % row->num_cols; 525 sprite->frame = (sprite->frame + 1) % row->num_cols;
491 } 526 }
492 527
493 iso->last_animation_time = t; 528 gfx->last_animation_time = t;
494 } 529 }
495} 530}
496 531
@@ -498,18 +533,6 @@ void isogfx_update(IsoGfx* iso, double t) {
498// Rendering and picking. 533// Rendering and picking.
499// ----------------------------------------------------------------------------- 534// -----------------------------------------------------------------------------
500 535
501/// Get the screen position of the top diamond-corner of the tile at world
502/// (x,y).
503static ivec2 GetTileScreenOrigin(
504 const CoordSystem iso_space, ivec2 camera, int world_x, int world_y) {
505 const ivec2 vx_offset = ivec2_scale(iso_space.x, world_x);
506 const ivec2 vy_offset = ivec2_scale(iso_space.y, world_y);
507 const ivec2 screen_origin =
508 ivec2_add(iso_space.o, ivec2_add(vx_offset, vy_offset));
509 const ivec2 origin_view_space = ivec2_add(screen_origin, ivec2_neg(camera));
510 return origin_view_space;
511}
512
513static Pixel alpha_blend(Pixel src, Pixel dst) { 536static Pixel alpha_blend(Pixel src, Pixel dst) {
514 if ((src.a == 255) || (dst.a == 0)) { 537 if ((src.a == 255) || (dst.a == 0)) {
515 return src; 538 return src;
@@ -541,13 +564,13 @@ static void draw_rect(
541 Screen* screen, ivec2 top_left, int rect_width, int rect_height, 564 Screen* screen, ivec2 top_left, int rect_width, int rect_height,
542 const Pixel* pixels, const uint8_t* indices) { 565 const Pixel* pixels, const uint8_t* indices) {
543 assert(screen); 566 assert(screen);
567 assert(pixels);
544 568
545#define rect_pixel(X, Y) \ 569#define rect_pixel(X, Y) \
546 (indices ? pixels[indices[Y * rect_width + X]] : pixels[Y * rect_width + X]) 570 (indices ? pixels[indices[Y * rect_width + X]] : pixels[Y * rect_width + X])
547 571
548 // Rect origin can be outside screen bounds, so we must offset accordingly to 572 // Rect origin can be outside screen bounds, so we must offset accordingly to
549 // draw only the visible portion. 573 // draw only the visible portion.
550#define max(a, b) (a > b ? a : b)
551 const int px_offset = max(0, -top_left.x); 574 const int px_offset = max(0, -top_left.x);
552 const int py_offset = max(0, -top_left.y); 575 const int py_offset = max(0, -top_left.y);
553 576
@@ -568,137 +591,269 @@ static void draw_rect(
568 } 591 }
569} 592}
570 593
571/// Draw a tile. 594/// Draw a tile in an orthogonal map.
572/// 595static void draw_tile_ortho(Gfx2d* gfx, Tile tile, int x, int y) {
573/// 'screen_origin' is the screen coordinates of the top diamond-corner of the 596 assert(gfx);
574/// tile (the base tile for super tiles). 597 assert(gfx->tileset);
575/// World (0, 0) -> (screen_width / 2, 0). 598 assert(x >= 0);
576static void draw_tile(IsoGfx* iso, ivec2 screen_origin, Tile tile) { 599 assert(y >= 0);
577 assert(iso); 600 assert(x < gfx->map->world_width);
578 assert(iso->tileset); 601 assert(y < gfx->map->world_height);
602
603 const Ts_Tile* pTile = ts_tileset_get_tile(gfx->tileset, tile);
604 const Pixel* pixels = ts_tileset_get_tile_pixels(gfx->tileset, tile);
605
606 const ivec2 screen_origin = map2screen(
607 gfx->camera, gfx->map->base_tile_width, gfx->map->base_tile_height, x, y);
608
609 draw_rect(
610 &gfx->screen, screen_origin, pTile->width, pTile->height, pixels,
611 nullptr);
612}
579 613
580 const Ts_Tile* pTile = ts_tileset_get_tile(iso->tileset, tile); 614/// Draw a tile in an isometric map.
581 const Pixel* pixels = ts_tileset_get_tile_pixels(iso->tileset, tile); 615static void draw_tile_iso(Gfx2d* gfx, Tile tile, int iso_x, int iso_y) {
616 assert(gfx);
617 assert(gfx->tileset);
618 assert(iso_x >= 0);
619 assert(iso_y >= 0);
620 assert(iso_x < gfx->map->world_width);
621 assert(iso_y < gfx->map->world_height);
622
623 const Ts_Tile* pTile = ts_tileset_get_tile(gfx->tileset, tile);
624 const Pixel* pixels = ts_tileset_get_tile_pixels(gfx->tileset, tile);
625
626 // Compute the screen coordinates of the top diamond-corner of the tile (the
627 // base tile for super tiles).
628 // World (0, 0) -> (screen_width / 2, 0).
629 const ivec2 screen_origin =
630 iso2cart(gfx->iso_space, gfx->camera, iso_x, iso_y);
582 631
583 // Move from the top diamond-corner to the top-left corner of the tile image. 632 // Move from the top diamond-corner to the top-left corner of the tile image.
584 // For regular tiles, tile height == base tile height, so the y offset is 0. 633 // For regular tiles, tile height == base tile height, so the y offset is 0.
585 // For super tiles, move as high up as the height of the tile. 634 // For super tiles, move as high up as the height of the tile.
586 const ivec2 offset = { 635 const ivec2 offset = {
587 -(iso->map->base_tile_width / 2), 636 -(gfx->map->base_tile_width / 2),
588 pTile->height - iso->map->base_tile_height}; 637 pTile->height - gfx->map->base_tile_height};
589 const ivec2 top_left = ivec2_add(screen_origin, offset); 638 const ivec2 top_left = ivec2_add(screen_origin, offset);
590 639
591 draw_rect( 640 draw_rect(
592 &iso->screen, top_left, pTile->width, pTile->height, pixels, nullptr); 641 &gfx->screen, top_left, pTile->width, pTile->height, pixels, nullptr);
593} 642}
594 643
595static void draw_map(IsoGfx* iso) { 644static void draw_map_ortho(Gfx2d* gfx) {
596 assert(iso); 645 assert(gfx);
646 assert(gfx->map);
647
648 // TODO: Same TODOs as in draw_map_iso().
597 649
598 const int W = iso->screen.width; 650 const Tm_Layer* layer = tm_map_get_layer(gfx->map, 0);
599 const int H = iso->screen.height;
600 651
601 memset(iso->screen.pixels, 0, W * H * sizeof(Pixel)); 652 for (int wy = 0; wy < gfx->map->world_height; ++wy) {
653 for (int wx = 0; wx < gfx->map->world_width; ++wx) {
654 const Tile tile = tm_layer_get_tile(gfx->map, layer, wx, wy);
655 draw_tile_ortho(gfx, tile, wx, wy);
656 }
657 }
658}
602 659
603 const Tm_Layer* layer = tm_map_get_layer(iso->map, 0); 660static void draw_map_iso(Gfx2d* gfx) {
661 assert(gfx);
662 assert(gfx->map);
663
664 // TODO: Support for multiple layers.
665 const Tm_Layer* layer = tm_map_get_layer(gfx->map, 0);
604 666
605 // TODO: Culling. 667 // TODO: Culling.
606 // Ex: map the screen corners to tile space to cull. 668 // Ex: map the screen corners to tile space to cull.
607 // Ex: walk in screen space and fetch the tile. 669 // Ex: walk in screen space and fetch the tile.
608 // The tile-centric approach might be more cache-friendly since the 670 // The tile-centric approach might be more cache-friendly since the
609 // screen-centric approach would juggle multiple tiles throughout the scan. 671 // screen-centric approach would juggle multiple tiles throughout the scan.
610 for (int wy = 0; wy < iso->map->world_height; ++wy) { 672 for (int wy = 0; wy < gfx->map->world_height; ++wy) {
611 for (int wx = 0; wx < iso->map->world_width; ++wx) { 673 for (int wx = 0; wx < gfx->map->world_width; ++wx) {
612 const Tile tile = tm_layer_get_tile(iso->map, layer, wx, wy); 674 const Tile tile = tm_layer_get_tile(gfx->map, layer, wx, wy);
613 const ivec2 screen_origin = 675 draw_tile_iso(gfx, tile, wx, wy);
614 GetTileScreenOrigin(iso->iso_space, iso->camera, wx, wy);
615 draw_tile(iso, screen_origin, tile);
616 } 676 }
617 } 677 }
618} 678}
619 679
620static void draw_sprite( 680static void draw_map(Gfx2d* gfx) {
621 IsoGfx* iso, ivec2 origin, const SpriteInstance* sprite, 681 assert(gfx);
622 const Ss_SpriteSheet* sheet) { 682 assert(gfx->map);
623 assert(iso); 683 assert(gfx->screen.pixels);
684
685 const int W = gfx->screen.width;
686 const int H = gfx->screen.height;
687
688 memset(gfx->screen.pixels, 0, W * H * sizeof(Pixel));
689
690 const Tm_Flags* flags = (const Tm_Flags*)&gfx->map->flags;
691 switch (flags->orientation) {
692 case Tm_Orthogonal:
693 draw_map_ortho(gfx);
694 break;
695 case Tm_Isometric:
696 draw_map_iso(gfx);
697 break;
698 }
699}
700
701/// Draw a sprite in an orthogonal/Cartesian coordinate system.
702static void draw_sprite_ortho(
703 Gfx2d* gfx, const SpriteInstance* sprite, const Ss_SpriteSheet* sheet) {
704 assert(gfx);
624 assert(sprite); 705 assert(sprite);
625 assert(sheet); 706 assert(sheet);
626 assert(sprite->animation >= 0); 707 assert(sprite->animation >= 0);
627 assert(sprite->animation < sheet->num_rows); 708 assert(sprite->animation < sheet->num_rows);
628 assert(sprite->frame >= 0); 709 assert(sprite->frame >= 0);
629 710
711 // Apply an offset similarly to how we offset tiles. The sprite is offset by
712 // -base_tile_width/2 along the x-axis to align the sprite with the leftmost
713 // edge of the tile it is on.
714 const ivec2 screen_origin = map2screen(
715 gfx->camera, gfx->map->base_tile_width, gfx->map->base_tile_height,
716 sprite->position.x, sprite->position.y);
717
630 const Ss_Row* row = ss_get_sprite_sheet_row(sheet, sprite->animation); 718 const Ss_Row* row = ss_get_sprite_sheet_row(sheet, sprite->animation);
631 const uint8_t* frame = ss_get_sprite_sheet_sprite(sheet, row, sprite->frame); 719 const uint8_t* frame = ss_get_sprite_sheet_sprite(sheet, row, sprite->frame);
632 draw_rect( 720 draw_rect(
633 &iso->screen, origin, sheet->sprite_width, sheet->sprite_height, 721 &gfx->screen, screen_origin, sheet->sprite_width, sheet->sprite_height,
634 sheet->palette.colours, frame); 722 sheet->palette.colours, frame);
635} 723}
636 724
637static void draw_sprites(IsoGfx* iso) { 725/// Draw a sprite in an isometric coordinate system.
638 assert(iso); 726static void draw_sprite_iso(
727 Gfx2d* gfx, const SpriteInstance* sprite, const Ss_SpriteSheet* sheet) {
728 assert(gfx);
729 assert(sprite);
730 assert(sheet);
731 assert(sprite->animation >= 0);
732 assert(sprite->animation < sheet->num_rows);
733 assert(sprite->frame >= 0);
639 734
640 for (const SpriteInstance* sprite = iso->head_sprite; sprite; 735 // Apply an offset similarly to how we offset tiles. The sprite is offset by
641 sprite = sprite->next) { 736 // -base_tile_width/2 along the x-axis to align the sprite with the leftmost
642 const Ss_SpriteSheet* sheet = sprite->sheet; 737 // edge of the tile it is on.
643 assert(sheet); 738 const ivec2 screen_origin = iso2cart(
739 gfx->iso_space, gfx->camera, sprite->position.x, sprite->position.y);
740 const ivec2 offset = {-(gfx->map->base_tile_width / 2), 0};
741 const ivec2 top_left = ivec2_add(screen_origin, offset);
644 742
645 const ivec2 screen_origin = GetTileScreenOrigin( 743 const Ss_Row* row = ss_get_sprite_sheet_row(sheet, sprite->animation);
646 iso->iso_space, iso->camera, sprite->position.x, sprite->position.y); 744 const uint8_t* frame = ss_get_sprite_sheet_sprite(sheet, row, sprite->frame);
647 draw_sprite(iso, screen_origin, sprite, sheet); 745 draw_rect(
746 &gfx->screen, top_left, sheet->sprite_width, sheet->sprite_height,
747 sheet->palette.colours, frame);
748}
749
750static void draw_sprites(Gfx2d* gfx) {
751 assert(gfx);
752 assert(gfx->map);
753
754 const Tm_Flags* flags = (const Tm_Flags*)&gfx->map->flags;
755 switch (flags->orientation) {
756 case Tm_Orthogonal:
757 for (const SpriteInstance* sprite = gfx->head_sprite; sprite;
758 sprite = sprite->next) {
759 draw_sprite_ortho(gfx, sprite, sprite->sheet);
760 }
761 break;
762 case Tm_Isometric:
763 for (const SpriteInstance* sprite = gfx->head_sprite; sprite;
764 sprite = sprite->next) {
765 draw_sprite_iso(gfx, sprite, sprite->sheet);
766 }
767 break;
648 } 768 }
649} 769}
650 770
651void isogfx_set_camera(IsoGfx* iso, int x, int y) { 771void gfx2d_render(Gfx2d* gfx, int camera_x, int camera_y) {
652 assert(iso); 772 assert(gfx);
653 iso->camera = (ivec2){x, y}; 773 // Storing the camera mostly for debugging convenience. It could otherwise be
774 // passed around.
775 gfx->camera = (ivec2){camera_x, camera_y};
776 draw_map(gfx);
777 draw_sprites(gfx);
778}
779
780void gfx2d_draw_tile(Gfx2d* gfx, int x, int y, Tile tile) {
781 assert(gfx);
782 assert(gfx->map);
783
784 const Tm_Flags* flags = (const Tm_Flags*)&gfx->map->flags;
785 switch (flags->orientation) {
786 case Tm_Orthogonal:
787 draw_tile_ortho(gfx, tile, x, y);
788 break;
789 case Tm_Isometric:
790 draw_tile_iso(gfx, tile, x, y);
791 break;
792 }
654} 793}
655 794
656void isogfx_render(IsoGfx* iso) { 795static void clip_camera_ortho(const Gfx2d* gfx, float* x, float* y) {
657 assert(iso); 796 assert(gfx);
658 draw_map(iso); 797 assert(gfx->map);
659 draw_sprites(iso); 798
799 if (x != nullptr) {
800 const int width_pixels = gfx->map->world_width * gfx->map->base_tile_width;
801 const int x_max = width_pixels - gfx->screen.width;
802 *x = min((float)x_max, max(0.F, *x));
803 }
804 if (y != nullptr) {
805 const int height_pixels =
806 gfx->map->world_height * gfx->map->base_tile_height;
807 const int y_max = height_pixels - gfx->screen.height;
808 *y = min((float)y_max, max(0.F, *y));
809 }
660} 810}
661 811
662void isogfx_draw_tile(IsoGfx* iso, int x, int y, Tile tile) { 812void gfx2d_clip_camera(const Gfx2d* gfx, float* x, float* y) {
663 assert(iso); 813 assert(gfx);
664 assert(x >= 0); 814 assert(gfx->map);
665 assert(y >= 0); 815 assert(x);
666 assert(x < iso->map->world_width); 816 assert(y);
667 assert(y < iso->map->world_height);
668 817
669 const ivec2 screen_origin = 818 const Tm_Flags* flags = (const Tm_Flags*)&gfx->map->flags;
670 GetTileScreenOrigin(iso->iso_space, iso->camera, x, y); 819 switch (flags->orientation) {
671 draw_tile(iso, screen_origin, tile); 820 case Tm_Orthogonal:
821 clip_camera_ortho(gfx, x, y);
822 break;
823 case Tm_Isometric:
824 // TODO: Clip camera in isometric maps.
825 break;
826 }
672} 827}
673 828
674void isogfx_get_screen_size(const IsoGfx* iso, int* width, int* height) { 829void gfx2d_get_screen_size(const Gfx2d* gfx, int* width, int* height) {
675 assert(iso); 830 assert(gfx);
676 assert(width); 831 assert(width);
677 assert(height); 832 assert(height);
678 *width = iso->screen.width; 833 *width = gfx->screen.width;
679 *height = iso->screen.height; 834 *height = gfx->screen.height;
680} 835}
681 836
682const Pixel* isogfx_get_screen_buffer(const IsoGfx* iso) { 837const Pixel* gfx2d_get_screen_buffer(const Gfx2d* gfx) {
683 assert(iso); 838 assert(gfx);
684 return iso->screen.pixels; 839 return gfx->screen.pixels;
685} 840}
686 841
687void isogfx_pick_tile( 842void gfx2d_pick_tile(
688 const IsoGfx* iso, double xcart, double ycart, int* xiso, int* yiso) { 843 const Gfx2d* gfx, double xcart, double ycart, int* xiso, int* yiso) {
689 assert(iso); 844 assert(gfx);
690 assert(xiso); 845 assert(xiso);
691 assert(yiso); 846 assert(yiso);
692 847
693 const vec2 camera = ivec2_to_vec2(iso->camera); 848 const vec2 camera = ivec2_to_vec2(gfx->camera);
694 const vec2 xy_cart = vec2_add(camera, (vec2){xcart, ycart}); 849 const vec2 xy_cart = vec2_add(camera, (vec2){xcart, ycart});
695 850
696 const vec2 xy_iso = cart2iso( 851 const vec2 xy_iso = cart2iso(
697 xy_cart, iso->map->base_tile_width, iso->map->base_tile_height, 852 xy_cart, gfx->map->base_tile_width, gfx->map->base_tile_height,
698 iso->screen.width); 853 gfx->screen.width);
699 854
700 if ((0 <= xy_iso.x) && (xy_iso.x < iso->map->world_width) && 855 if ((0 <= xy_iso.x) && (xy_iso.x < gfx->map->world_width) &&
701 (0 <= xy_iso.y) && (xy_iso.y < iso->map->world_height)) { 856 (0 <= xy_iso.y) && (xy_iso.y < gfx->map->world_height)) {
702 *xiso = (int)xy_iso.x; 857 *xiso = (int)xy_iso.x;
703 *yiso = (int)xy_iso.y; 858 *yiso = (int)xy_iso.y;
704 } else { 859 } else {