maug
Quick and dirty C mini-augmentation library.
Loading...
Searching...
No Matches
retrotil.h
1
2#ifndef RETROTIL_H
3#define RETROTIL_H
4
5#include <mjson.h>
6#include <mfile.h>
7
8/* TODO: Use a vector for tile_defs. */
9
17#ifndef RETROTILE_NAME_SZ_MAX
19# define RETROTILE_NAME_SZ_MAX 10
20#endif /* !RETROTILE_NAME_SZ_MAX */
21
22#ifndef RETROTILE_TILESET_IMAGE_STR_SZ_MAX
24# define RETROTILE_TILESET_IMAGE_STR_SZ_MAX 48
25#endif /* !RETROTILE_TILESET_IMAGE_STR_SZ_MAX */
26
27#ifndef RETROTILE_TILE_SCALE_DEFAULT
29# define RETROTILE_TILE_SCALE_DEFAULT 1.0f
30#endif /* !RETROTILE_TILE_SCALE_DEFAULT */
31
32#ifndef RETROTILE_TRACE_LVL
34# define RETROTILE_TRACE_LVL 0
35#endif /* !RETROTILE_TRACE_LVL */
36
37#ifndef RETROTILE_VORONOI_DEFAULT_SPB
38# define RETROTILE_VORONOI_DEFAULT_SPB 8
39#endif /* !RETROTILE_VORONOI_DEFAULT_SPB */
40
41#ifndef RETROTILE_VORONOI_DEFAULT_DRIFT
42# define RETROTILE_VORONOI_DEFAULT_DRIFT 4
43#endif /* !RETROTILE_VORONOI_DEFAULT_DRIFT */
44
45#ifdef MPARSER_TRACE_NAMES
46# define retrotile_mstate_name( state ) gc_retrotile_mstate_names[state]
47#else
48# define retrotile_mstate_name( state ) state
49#endif /* MPARSER_TRACE_NAMES */
50
61#define RETROTILE_PARSER_MODE_MAP 0
62#define RETROTILE_PARSER_MODE_DEFS 1
63
64#define RETROTILE_TILE_FLAG_BLOCK 0x01
65#define RETROTILE_TILE_FLAG_ROT_X 0x02
66#define RETROTILE_TILE_FLAG_ROT_Z 0x04
67#define RETROTILE_TILE_FLAG_PRTCL_FIRE 0x08
68
69 /* retrotile_defs_types */
70
81#define RETROTILE_DS_FLAG_INIT_DATA 0x02
82
83#define RETROTILE_IDX_FMT "%u"
84
86 uint8_t flags;
88};
89
90 /* retrotile_defs */
91
93 size_t sz;
94};
95
96struct RETROTILE {
97 uint32_t sz;
98 uint32_t layers_count;
99 uint32_t layers_offset;
100 char name[RETROTILE_NAME_SZ_MAX];
101 size_t tileset_fgid;
102 size_t tiles_h;
103 size_t tiles_w;
104 float tile_scale;
105};
106
113 int16_t sect_x;
115 int16_t sect_y;
117 int16_t sect_w;
119 int16_t sect_h;
121 int16_t sect_w_half;
123 int16_t sect_h_half;
124 retroflat_tile_t highest_generated;
125 retroflat_tile_t lowest_generated;
126};
127
129 int16_t tiles_changed;
130 retroflat_tile_t center;
131 retroflat_tile_t outside;
133 retroflat_tile_t mod_to[8];
134};
135
136#define retrotile_get_tile( tilemap, layer, x, y ) \
137 (retrotile_get_tiles_p( layer )[((y) * (tilemap)->tiles_w) + (x)])
138
139#define retrotile_set_tile( tilemap, layer, x, y, new_val ) \
140 (retrotile_get_tiles_p( layer )[((y) * (tilemap)->tiles_w) + (x)])
141
142#define retrotile_get_tiles_p( layer ) \
143 ((retroflat_tile_t*)(((uint8_t*)(layer)) + \
144 sizeof( struct RETROTILE_LAYER )))
145
146#define retrotile_clear_tiles( t, layer ) \
147 memset( retrotile_get_tiles_p( layer ), -1, \
148 t->tiles_w * t->tiles_h * sizeof( retroflat_tile_t ) )
149
155typedef MERROR_RETVAL (*retrotile_tj_parse_cb)(
156 const char* filename, MAUG_MHANDLE* p_tm_h,
157 MAUG_MHANDLE* p_td_h, size_t* p_td_c,
158 mparser_wait_cb_t wait_cb, void* wait_data );
159
161 uint8_t mstate;
162 /* TODO: Use flags and combine these. */
163 uint8_t pass;
164 uint8_t mode;
165 mparser_wait_cb_t wait_cb;
166 void* wait_data;
167 retroflat_ms_t wait_last;
168 size_t layer_tile_iter;
169 size_t pass_layer_iter;
175 size_t tiles_w;
176 size_t tiles_h;
177 struct RETROTILE* t;
178 retrotile_tj_parse_cb tj_parse_cb;
179 struct MJSON_PARSER jparser;
180 MAUG_MHANDLE* p_tile_defs_h;
181 size_t* p_tile_defs_count;
182};
183
184#define RETROTILE_PARSER_MSTATE_TABLE( f ) \
185 f( MTILESTATE_NONE, 0, "", 0, 0 ) \
186 f( MTILESTATE_HEIGHT, 1, "height", 0 , 0 ) \
187 f( MTILESTATE_WIDTH, 2, "width", 0 , 0 ) \
188 f( MTILESTATE_LAYERS, 3, "layers", 0 , 0 ) \
189 f( MTILESTATE_LAYER_DATA, 4, "data", 15 /* LAYER */, 0 ) \
190 f( MTILESTATE_LAYER_NAME, 5, "name", 15 /* LAYER */, 0 ) \
191 f( MTILESTATE_TILES, 6, "tiles", 0 , 1 ) \
192 f( MTILESTATE_TILES_ID, 7, "id", 6 /* TILES */ , 1 ) \
193 f( MTILESTATE_TILES_IMAGE, 8, "image", 6 /* TILES */ , 1 ) \
194 f( MTILESTATE_TILESETS, 9, "tilesets", 0 , 0 ) \
195 f( MTILESTATE_TILESETS_SRC, 10, "source", 9 /* TILESETS */, 0 ) \
196 f( MTILESTATE_TILESETS_FGID, 11, "firstgid",9 /* TILESETS */, 0 ) \
197 f( MTILESTATE_TILESETS_PROP, 12, "firstgid",9 /* TILESETS */, 0 ) \
198 f( MTILESTATE_GRID, 13, "grid", 0 , 1 ) \
199 f( MTILESTATE_TILES_PROP, 14, "properties",6 /* TILES */ , 1 ) \
200 f( MTILESTATE_LAYER, 15, "layers", /* [sic] */ 3 , 0 ) \
201 f( MTILESTATE_TILES_PROP_NAME, 16, "name", 14 /* TILES_PROP */ , 1 ) \
202 f( MTILESTATE_TILES_PROP_VAL, 17, "value", 14 /* TILES_PROP */ , 1 )
203
205retrotile_parse_json_c( struct RETROTILE_PARSER* parser, char c );
206
207MERROR_RETVAL retrotile_parse_json_file(
208 const char* filename, MAUG_MHANDLE* p_tilemap_h,
209 MAUG_MHANDLE* p_tile_defs_h, size_t* p_tile_defs_count,
210 mparser_wait_cb_t wait_cb, void* wait_data );
211
212 /* retrotile_parser */
213
220typedef MERROR_RETVAL (*retrotile_ani_cb)(
221 void* animation_cb_data, int16_t iter );
222
223typedef MERROR_RETVAL (*retrotile_gen_cb)(
224 struct RETROTILE* t, retroflat_tile_t min_z, retroflat_tile_t max_z,
225 uint32_t tuning, size_t layer_idx, uint8_t flags, void* data,
226 retrotile_ani_cb animation_cb, void* animation_cb_data );
227
238 struct RETROTILE* t, retroflat_tile_t min_z, retroflat_tile_t max_z,
239 uint32_t tuning, size_t layer_idx, uint8_t flags, void* data,
240 retrotile_ani_cb animation_cb, void* animation_cb_data );
241
253 struct RETROTILE* t, retroflat_tile_t min_z, retroflat_tile_t max_z,
254 uint32_t tuning, size_t layer_idx, uint8_t flags, void* data,
255 retrotile_ani_cb animation_cb, void* animation_cb_data );
256
265 struct RETROTILE* t, retroflat_tile_t min_z, retroflat_tile_t max_z,
266 uint32_t tuning, size_t layer_idx, uint8_t flags, void* data,
267 retrotile_ani_cb animation_cb, void* animation_cb_data );
268
276 struct RETROTILE* t, retroflat_tile_t min_z, retroflat_tile_t max_z,
277 uint32_t tuning, size_t layer_idx, uint8_t flags, void* data,
278 retrotile_ani_cb animation_cb, void* animation_cb_data );
279
280struct RETROTILE_LAYER* retrotile_get_layer_p(
281 struct RETROTILE* tilemap, uint32_t layer_idx );
282
283MERROR_RETVAL retrotile_alloc_tile_defs(
284 MAUG_MHANDLE* p_tile_defs_h, size_t* p_tile_defs_count, size_t ndefs );
285
286MERROR_RETVAL retrotile_alloc(
287 MAUG_MHANDLE* p_tilemap_h, size_t w, size_t h, size_t layers_count );
288
289 /* retrotile_gen */
290
291#ifdef RETROTIL_C
292
293# include <mparser.h>
294
295/* TODO: Function names should be verb_noun! */
296
297#define retrotile_parser_mstate( parser, new_mstate ) \
298 parser->mstate = new_mstate;
299
300#if 0
301 debug_printf( \
302 RETROTILE_TRACE_LVL, "parser mstate: %s", \
303 retrotile_mstate_name( parser->mstate ) );
304#endif
305
306# define RETROTILE_PARSER_MSTATE_TABLE_CONST( name, idx, tokn, parent, m ) \
307 static MAUG_CONST uint8_t SEG_MCONST name = idx;
308
309RETROTILE_PARSER_MSTATE_TABLE( RETROTILE_PARSER_MSTATE_TABLE_CONST )
310
311#ifdef MPARSER_TRACE_NAMES
312# define RETROTILE_PARSER_MSTATE_TABLE_NAME( name, idx, tokn, parent, m ) \
313 #name,
314
315static MAUG_CONST char* SEG_MCONST gc_retrotile_mstate_names[] = {
316 RETROTILE_PARSER_MSTATE_TABLE( RETROTILE_PARSER_MSTATE_TABLE_NAME )
317 ""
318};
319#endif /* MPARSER_TRACE_NAMES */
320
321# define RETROTILE_PARSER_MSTATE_TABLE_TOKEN( name, idx, tokn, parent, m ) \
322 tokn,
323
324static MAUG_CONST char* SEG_MCONST gc_retrotile_mstate_tokens[] = {
325 RETROTILE_PARSER_MSTATE_TABLE( RETROTILE_PARSER_MSTATE_TABLE_TOKEN )
326 ""
327};
328
329# define RETROTILE_PARSER_MSTATE_TABLE_PARNT( name, idx, tokn, parent, m ) \
330 parent,
331
332static MAUG_CONST uint8_t SEG_MCONST gc_retrotile_mstate_parents[] = {
333 RETROTILE_PARSER_MSTATE_TABLE( RETROTILE_PARSER_MSTATE_TABLE_PARNT )
334 0
335};
336
337# define RETROTILE_PARSER_MSTATE_TABLE_MODE( name, idx, tokn, parent, m ) \
338 m,
339
340static MAUG_CONST uint8_t SEG_MCONST gc_retrotile_mstate_modes[] = {
341 RETROTILE_PARSER_MSTATE_TABLE( RETROTILE_PARSER_MSTATE_TABLE_MODE )
342 0
343};
344
345/* === */
346
347static void retrotile_parser_match_token(
348 const char* token, size_t token_sz, struct RETROTILE_PARSER* parser
349) {
350 size_t j = 1;
351
352 /* Figure out what the key is for. */
353 while( '\0' != gc_retrotile_mstate_tokens[j][0] ) {
354 if(
355 /* Make sure tokens match. */
356 maug_strlen( gc_retrotile_mstate_tokens[j] ) != token_sz ||
357 0 != strncmp(
358 token, gc_retrotile_mstate_tokens[j], token_sz
359 )
360 ) {
361 j++;
362 continue;
363
364 } else if(
365 /* This state can only be
366 * reached THROUGH that parent state. This allows us to have
367 * keys with the same name but different parents!
368 */
369 parser->mstate != gc_retrotile_mstate_parents[j]
370 ) {
371 debug_printf(
373 "found token \"%s\" "
374#ifdef MPARSER_TRACE_NAMES
375 "but incorrect parent %s (%d) (needs %s (%d))!",
376#else
377 "but incorrect parent %d (needs %d)!",
378#endif /* MPARSER_TRACE_NAMES */
379 token,
380#ifdef MPARSER_TRACE_NAMES
381 retrotile_mstate_name( parser->mstate ),
382 parser->mstate,
383 retrotile_mstate_name( gc_retrotile_mstate_parents[j] ),
384 gc_retrotile_mstate_parents[j]
385#else
386 parser->mstate,
387 gc_retrotile_mstate_parents[j]
388#endif /* MPARSER_TRACE_NAMES */
389 );
390 j++;
391 continue;
392
393 } else if(
394 /* None works in all modes! */
395 /* MTILESTATE_NONE != parser->mstate && */
396 parser->mode != gc_retrotile_mstate_modes[j]
397 ) {
398 debug_printf(
399 RETROTILE_TRACE_LVL, "found token %s but incorrect mode %u!",
400 token,
401 gc_retrotile_mstate_modes[j] );
402 j++;
403 continue;
404
405 } else {
406 /* Found it! */
407 retrotile_parser_mstate( parser, j );
408 return;
409 }
410 }
411}
412
413/* === */
414
415MERROR_RETVAL retrotile_parser_parse_tiledef_token(
416 const char* token, size_t token_sz, void* parser_arg
417) {
418 MERROR_RETVAL retval = MERROR_OK;
419 struct RETROTILE_TILE_DEF* tile_defs = NULL;
420 struct RETROTILE_PARSER* parser = (struct RETROTILE_PARSER*)parser_arg;
421 size_t tileset_id_parsed = 0;
422
423 if( 0 < *(parser->p_tile_defs_count) ) {
424 maug_mlock( *(parser->p_tile_defs_h), tile_defs );
425 maug_cleanup_if_null_alloc( struct RETROTILE_TILE_DEF*, tile_defs );
426 }
427
428 if(
429 MJSON_PSTATE_OBJECT_VAL ==
430 mjson_parser_pstate( &(parser->jparser) )
431 ) {
432 if( MTILESTATE_TILES_ID == parser->mstate ) {
433 retrotile_parser_mstate( parser, MTILESTATE_TILES );
434 if( 0 == parser->pass ) {
435 /* Parse tile ID. */
436 tileset_id_parsed = atoi( token );
437 if( tileset_id_parsed > parser->tileset_id_cur ) {
438 parser->tileset_id_cur = tileset_id_parsed;
439 debug_printf(
441 "new highest tile ID: " SIZE_T_FMT,
442 parser->tileset_id_cur );
443 }
444 } else {
445 /* TODO: atoi or atoul? */
446 parser->tileset_id_cur = atoi( token );
447 debug_printf(
449 "next tile ID: " SIZE_T_FMT,
450 parser->tileset_id_cur );
451 }
452
453 } else if( MTILESTATE_TILES_IMAGE == parser->mstate ) {
454 if( 1 == parser->pass ) {
455 debug_printf(
456 RETROTILE_TRACE_LVL, "setting tile ID " SIZE_T_FMT "...",
457 parser->tileset_id_cur );
458
459 /* Parse tile image. */
460 maug_strncpy(
461 tile_defs[parser->tileset_id_cur].image_path,
462 token,
464
465 debug_printf(
466 RETROTILE_TRACE_LVL, "set tile ID " SIZE_T_FMT " to: %s",
467 parser->tileset_id_cur,
468 tile_defs[parser->tileset_id_cur].image_path );
469 }
470 retrotile_parser_mstate( parser, MTILESTATE_TILES );
471
472 } else if( MTILESTATE_TILES_PROP_NAME == parser->mstate ) {
473
474 if( 1 == parser->pass ) {
475 if( 0 == strncmp(
476 parser->jparser.base.token,
477 "rotate_x",
478 parser->jparser.base.token_sz
479 ) ) {
480 /* Found flag: rotate X! */
481 /* TODO: Read boolean value. */
482 if( parser->tileset_id_cur >= *(parser->p_tile_defs_count) ) {
483 error_printf(
484 "tileset ID " SIZE_T_FMT
485 " outside of tile defs count " SIZE_T_FMT "!",
486 parser->tileset_id_cur, *(parser->p_tile_defs_count) );
487 retval = MERROR_OVERFLOW;
488 goto cleanup;
489 }
490 tile_defs[parser->tileset_id_cur].flags |=
491 RETROTILE_TILE_FLAG_ROT_X;
492 }
493
494 /* TODO: Read boolean Z and fire particles prop/flag. */
495 }
496
497 retrotile_parser_mstate( parser, MTILESTATE_TILES_PROP );
498
499 } else if( MTILESTATE_TILES_PROP_VAL == parser->mstate ) {
500
501 retrotile_parser_mstate( parser, MTILESTATE_TILES_PROP );
502 }
503 goto cleanup;
504 }
505
506 retrotile_parser_match_token( token, token_sz, parser );
507
508cleanup:
509
510 if( NULL != tile_defs ) {
511 maug_munlock( *(parser->p_tile_defs_h), tile_defs );
512 }
513
514 return retval;
515}
516
517MERROR_RETVAL retrotile_parser_parse_token(
518 const char* token, size_t token_sz, void* parser_arg
519) {
520 MERROR_RETVAL retval = MERROR_OK;
521 struct RETROTILE_TILE_DEF* tile_defs = NULL;
522 struct RETROTILE_LAYER* tiles_layer = NULL;
523 struct RETROTILE_PARSER* parser = (struct RETROTILE_PARSER*)parser_arg;
524 retroflat_tile_t* tiles = NULL;
525
526 if( 0 < *(parser->p_tile_defs_count) ) {
527 maug_mlock( *(parser->p_tile_defs_h), tile_defs );
528 maug_cleanup_if_null_alloc( struct RETROTILE_TILE_DEF*, tile_defs );
529 }
530
531 if( MJSON_PSTATE_LIST == mjson_parser_pstate( &(parser->jparser) ) ) {
532 if(
533 1 == parser->pass &&
534 MTILESTATE_LAYER_DATA == parser->mstate
535 ) {
536 /*
537 assert( NULL != *(parser->p_tilemap_h) );
538 maug_mlock( *(parser->p_tilemap_h), tilemap );
539 maug_cleanup_if_null_alloc( struct RETROTILE*, tilemap );
540 */
541
542 debug_printf( RETROTILE_TRACE_LVL,
543 "selecting layer " SIZE_T_FMT "...",
544 parser->pass_layer_iter );
545 assert( NULL != parser->t );
546 tiles_layer = retrotile_get_layer_p(
547 parser->t, parser->pass_layer_iter );
548 assert( NULL != tiles_layer );
549
550 tiles = retrotile_get_tiles_p( tiles_layer );
551
552 /* Parse tilemap tile. */
553 debug_printf( RETROTILE_TRACE_LVL,
554 "layer " SIZE_T_FMT " tile: " SIZE_T_FMT " (tiles: %p)",
555 parser->pass_layer_iter, parser->layer_tile_iter,
556 tiles );
557 assert( NULL != token );
558 assert( NULL != parser );
559 assert( NULL != tiles );
560
561 if(
562 parser->layer_tile_iter >=
563 parser->t->tiles_w * parser->t->tiles_h
564 ) {
565 error_printf(
566 "tile " SIZE_T_FMT " outside of layer tile buffer size "
567 SIZE_T_FMT "!",
568 parser->layer_tile_iter,
569 parser->t->tiles_w * parser->t->tiles_h );
570 retval = MERROR_OVERFLOW;
571 goto cleanup;
572 }
573
574 assert( 0 == tiles[parser->layer_tile_iter] );
575
576 tiles[parser->layer_tile_iter] = atoi( token );
577 }
578 parser->layer_tile_iter++;
579 goto cleanup;
580
581 } else if(
582 MJSON_PSTATE_OBJECT_VAL ==
583 mjson_parser_pstate( &(parser->jparser) )
584 ) {
585 if( MTILESTATE_TILESETS_FGID == parser->mstate ) {
586 if( 1 == parser->pass ) {
587 parser->t->tileset_fgid = atoi( token );
588 debug_printf(
589 RETROTILE_TRACE_LVL, "tileset FGID set to: " SIZE_T_FMT,
590 parser->t->tileset_fgid );
591 }
592 retrotile_parser_mstate( parser, MTILESTATE_TILESETS );
593
594 } else if( MTILESTATE_TILESETS_SRC == parser->mstate ) {
595 if( 1 == parser->pass ) {
596 debug_printf( RETROTILE_TRACE_LVL, "parsing %s...", token );
597 parser->tj_parse_cb(
598 token, NULL, parser->p_tile_defs_h,
599 parser->p_tile_defs_count,
600 parser->wait_cb, parser->wait_data );
601 }
602 retrotile_parser_mstate( parser, MTILESTATE_TILESETS );
603
604 } else if( MTILESTATE_HEIGHT == parser->mstate ) {
605 if( 0 == parser->pass ) {
606 parser->tiles_h = atoi( token );
607 debug_printf(
608 RETROTILE_TRACE_LVL, "tilemap height: " SIZE_T_FMT,
609 parser->tiles_h );
610 }
611 retrotile_parser_mstate( parser, MTILESTATE_NONE );
612
613 } else if( MTILESTATE_WIDTH == parser->mstate ) {
614 if( 0 == parser->pass ) {
615 parser->tiles_w = atoi( token );
616 debug_printf(
617 RETROTILE_TRACE_LVL, "tilemap width: " SIZE_T_FMT,
618 parser->tiles_w );
619 }
620 retrotile_parser_mstate( parser, MTILESTATE_NONE );
621
622 } else if( MTILESTATE_LAYER_NAME == parser->mstate ) {
623 /* TODO: Store */
624 retrotile_parser_mstate( parser, MTILESTATE_LAYER );
625
626 }
627 goto cleanup;
628 }
629
630 retrotile_parser_match_token( token, token_sz, parser );
631
632cleanup:
633
634 if( NULL != tile_defs ) {
635 maug_munlock( *(parser->p_tile_defs_h), tile_defs );
636 }
637
638 return retval;
639}
640
641/* === */
642
643MERROR_RETVAL retrotile_json_close_list( void* parg ) {
644 struct RETROTILE_PARSER* parser = (struct RETROTILE_PARSER*)parg;
645
646 if( MTILESTATE_LAYER_DATA == parser->mstate ) {
647 assert( RETROTILE_PARSER_MODE_MAP == parser->mode );
648 assert( parser->layer_tile_iter == 1600 );
649 retrotile_parser_mstate( parser, MTILESTATE_LAYER );
650
651 } else if( MTILESTATE_LAYERS == parser->mstate ) {
652 assert( RETROTILE_PARSER_MODE_MAP == parser->mode );
653 retrotile_parser_mstate( parser, MTILESTATE_NONE );
654
655 } else if( MTILESTATE_TILESETS == parser->mstate ) {
656 assert( RETROTILE_PARSER_MODE_MAP == parser->mode );
657 retrotile_parser_mstate( parser, MTILESTATE_NONE );
658
659 } else if( MTILESTATE_TILES_PROP == parser->mstate ) {
660 assert( RETROTILE_PARSER_MODE_DEFS == parser->mode );
661 retrotile_parser_mstate( parser, MTILESTATE_TILES );
662 }
663
664 return MERROR_OK;
665}
666
667/* === */
668
669MERROR_RETVAL retrotile_json_open_obj( void* parg ) {
670 struct RETROTILE_PARSER* parser = (struct RETROTILE_PARSER*)parg;
671
672 if( MTILESTATE_LAYERS == parser->mstate ) {
673 assert( RETROTILE_PARSER_MODE_MAP == parser->mode );
674 /* Reset on open so count is retained for allocating after first
675 * pass. */
676 parser->layer_tile_iter = 0;
677 retrotile_parser_mstate( parser, MTILESTATE_LAYER );
678 }
679
680 return MERROR_OK;
681}
682
683/* === */
684
685MERROR_RETVAL retrotile_json_close_obj( void* parg ) {
686 struct RETROTILE_PARSER* parser = (struct RETROTILE_PARSER*)parg;
687
688 if( MTILESTATE_LAYER == parser->mstate ) {
689 assert( RETROTILE_PARSER_MODE_MAP == parser->mode );
690 assert( parser->layer_tile_iter == 1600 );
691 debug_printf( RETROTILE_TRACE_LVL,
692 "incrementing pass layer to " SIZE_T_FMT " after " SIZE_T_FMT
693 " tiles...",
694 parser->pass_layer_iter + 1, parser->layer_tile_iter );
695 parser->pass_layer_iter++;
696 retrotile_parser_mstate( parser, MTILESTATE_LAYERS );
697
698 } else if( MTILESTATE_GRID == parser->mstate ) {
699 retrotile_parser_mstate( parser, MTILESTATE_NONE );
700 }
701
702 return MERROR_OK;
703}
704
705/* === */
706
707MERROR_RETVAL retrotile_parse_json_file(
708 const char* filename, MAUG_MHANDLE* p_tilemap_h,
709 MAUG_MHANDLE* p_tile_defs_h, size_t* p_tile_defs_count,
710 mparser_wait_cb_t wait_cb, void* wait_data
711) {
712 MERROR_RETVAL retval = MERROR_OK;
713 MAUG_MHANDLE parser_h = (MAUG_MHANDLE)NULL;
714 struct RETROTILE_PARSER* parser = NULL;
715 char filename_path[RETROFLAT_PATH_MAX];
716 mfile_t buffer;
717 char c;
718 char* filename_ext = NULL;
719
720 /* Initialize parser. */
721 parser_h = maug_malloc( 1, sizeof( struct RETROTILE_PARSER ) );
722 maug_cleanup_if_null_alloc( MAUG_MHANDLE, parser_h );
723
724 maug_mlock( parser_h, parser );
725 maug_cleanup_if_null_alloc( struct RETROTILE_PARSER*, parser );
726 maug_mzero( parser, sizeof( struct RETROTILE_PARSER ) );
727
728 parser->tj_parse_cb = retrotile_parse_json_file;
729
730 /* Setup filename path. */
731 memset( filename_path, '\0', RETROFLAT_PATH_MAX );
732 /* TODO: Configurable path. */
733 maug_snprintf(
734 filename_path, RETROFLAT_PATH_MAX, "mapsrc/%s", filename );
735
736 debug_printf( RETROTILE_TRACE_LVL, "opening %s...", filename_path );
737
738 retval = mfile_open_read( filename_path, &buffer );
739 maug_cleanup_if_not_ok();
740
741 assert( NULL != p_tile_defs_count );
742
743 /* Parse JSON and react to state. */
744 for( parser->pass = 0 ; 2 > parser->pass ; parser->pass++ ) {
745 debug_printf( RETROTILE_TRACE_LVL, "beginning pass #%u...",
746 parser->pass );
747
748 /* Reset tilemap parser. */
749 parser->mstate = 0;
750
751 /* Reset JSON parser. */
752 maug_mzero( &(parser->jparser.base), sizeof( struct MJSON_PARSER ) );
753
754 parser->wait_cb = wait_cb;
755 parser->wait_data = wait_data;
756 parser->jparser.base.wait_cb = wait_cb;
757 parser->jparser.base.wait_data = wait_data;
758
759 /* Figure out if we're parsing a .tmj or .tsj. */
760 filename_ext = maug_strrchr( filename, '.' );
761 if( NULL == filename_ext ) {
762 error_printf( "could not parse filename extension!" );
763 retval = MERROR_FILE;
764 goto cleanup;
765 }
766 if( 's' == filename_ext[2] ) {
767 debug_printf( RETROTILE_TRACE_LVL, "(tile_defs mode)" );
768 parser->mode = RETROTILE_PARSER_MODE_DEFS;
769 parser->jparser.token_parser = retrotile_parser_parse_tiledef_token;
770 parser->jparser.token_parser_arg = parser;
771 parser->jparser.close_list = retrotile_json_close_list;
772 parser->jparser.close_list_arg = parser;
773 parser->jparser.close_obj = retrotile_json_close_obj;
774 parser->jparser.close_obj_arg = parser;
775 /*
776 parser->jparser.base.close_val = retrotile_json_close_val;
777 parser->jparser.base.close_val_arg = parser;
778 */
779 parser->p_tile_defs_h = p_tile_defs_h;
780 parser->p_tile_defs_count = p_tile_defs_count;
781
782 assert( NULL != p_tile_defs_h );
783 if( 1 == parser->pass ) {
784 /* Allocate tile defs based on highest tile ID found on
785 * first pass.
786 */
787 retval = retrotile_alloc_tile_defs(
788 p_tile_defs_h, p_tile_defs_count,
789 parser->tileset_id_cur + 1 );
790 maug_cleanup_if_not_ok();
791 }
792 } else {
793 debug_printf( RETROTILE_TRACE_LVL, "(tilemap mode)" );
794 parser->mode = RETROTILE_PARSER_MODE_MAP;
795
796 parser->jparser.close_list = retrotile_json_close_list;
797 parser->jparser.close_list_arg = parser;
798 parser->jparser.open_obj = retrotile_json_open_obj;
799 parser->jparser.open_obj_arg = parser;
800 parser->jparser.close_obj = retrotile_json_close_obj;
801 parser->jparser.close_obj_arg = parser;
802 parser->jparser.token_parser = retrotile_parser_parse_token;
803 parser->jparser.token_parser_arg = parser;
804 parser->p_tile_defs_h = p_tile_defs_h;
805 parser->p_tile_defs_count = p_tile_defs_count;
806
807 assert( NULL != p_tilemap_h );
808 if( 1 == parser->pass ) {
809 /* Allocate tiles for the new layers. */
810 retval = retrotile_alloc(
811 p_tilemap_h, parser->tiles_w, parser->tiles_h,
812 parser->pass_layer_iter );
813 maug_cleanup_if_not_ok();
814
815 maug_mlock( *p_tilemap_h, parser->t );
816 }
817 parser->pass_layer_iter = 0;
818 }
819
820 while( mfile_has_bytes( &buffer ) ) {
821 buffer.read_int( &buffer, (uint8_t*)&c, 1, 0 );
822 retval = mjson_parse_c( &(parser->jparser), c );
823 if( MERROR_OK != retval ) {
824 error_printf( "error parsing JSON!" );
825 goto cleanup;
826 }
827 }
828
829 buffer.seek( &buffer, 0 );
830
831 filename_ext = maug_strrchr( filename, '.' );
832 if( NULL == filename_ext ) {
833 error_printf( "could not parse filename extension!" );
834 retval = MERROR_FILE;
835 goto cleanup;
836 }
837 if( 's' != filename_ext[2] ) {
838 debug_printf( RETROTILE_TRACE_LVL,
839 "pass %u found " SIZE_T_FMT " layers",
840 parser->pass, parser->pass_layer_iter );
841 }
842 }
843
844 debug_printf(
845 RETROTILE_TRACE_LVL, "finished parsing %s...", filename_path );
846
847cleanup:
848
849 if( NULL != parser ) {
850 if( NULL != parser->t ) {
851 maug_munlock( *p_tilemap_h, parser->t );
852 }
853 maug_munlock( parser_h, parser );
854 }
855
856 if( NULL != parser_h ) {
857 maug_mfree( parser_h );
858 }
859
860 return retval;
861}
862
863/* === */
864
865static retroflat_tile_t retrotile_gen_diamond_square_rand(
866 retroflat_tile_t min_z, retroflat_tile_t max_z, uint32_t tuning,
867 retroflat_tile_t top_left_z
868) {
869 retroflat_tile_t avg = top_left_z;
870
871 if( 8 > rand() % 10 ) {
872 /* avg = min_z + (rand() % (max_z - min_z)); */
873 avg -= (min_z / tuning) + (rand() % (max_z / tuning));
874 /* } else {
875 avg += (min_z / 10) + (rand() % (max_z / 10)); */
876 }
877
878 /* Clamp the result. */
879
880 if( min_z > avg ) {
881 avg = min_z;
882 }
883
884 if( max_z < avg ) {
885 avg = max_z;
886 }
887
888 return avg;
889}
890
891/* === */
892
893static void retrotile_gen_diamond_square_corners(
894 int16_t corners_x[2][2], int16_t corners_y[2][2],
895 retroflat_tile_t min_z, retroflat_tile_t max_z,
896 uint32_t tuning, struct RETROTILE_DATA_DS* data_ds,
897 struct RETROTILE_LAYER* layer, struct RETROTILE* t
898) {
899 int16_t iter_x = 0,
900 iter_y = 0;
901 retroflat_tile_t* tile_iter = NULL;
902 retroflat_tile_t top_left_z = 0;
903
904 /* Generate missing corner data. Loop through X/Y coords stored in
905 * corners_x/corners_y convenience arrays.
906 */
907 for( iter_y = 0 ; iter_y < 2 ; iter_y++ ) {
908 for( iter_x = 0 ; iter_x < 2 ; iter_x++ ) {
909
910 /* Make sure corner X is in bounds. */
911 corners_x[iter_x][iter_y] =
912 (data_ds->sect_x - 1) + (iter_x * data_ds->sect_w);
913 if( 0 > corners_x[iter_x][iter_y] ) {
914 corners_x[iter_x][iter_y] += 1;
915 }
916
917 /* Make sure corner Y is in bounds. */
918 corners_y[iter_x][iter_y] =
919 (data_ds->sect_y - 1) + (iter_y * data_ds->sect_h);
920 if( 0 > corners_y[iter_x][iter_y] ) {
921 corners_y[iter_x][iter_y] += 1;
922 }
923 }
924 }
925
926 /* Should be handled by the check above. */
927 assert( 0 <= corners_x[0][0] );
928 assert( 0 <= corners_y[0][0] );
929 assert( t->tiles_w > corners_x[0][0] );
930 assert( t->tiles_h > corners_y[0][0] );
931
932 /* Grab the top-left Z-value to anchor generated corners to. */
933 top_left_z = retrotile_get_tile(
934 t, layer,
935 corners_x[0][0],
936 corners_y[0][0] );
937
938 if( 0 > top_left_z ) {
939 retrotile_get_tile(
940 t, layer,
941 corners_x[0][0] >= 0 ? corners_x[0][0] : 0,
942 corners_y[0][0] >= 0 ? corners_y[0][0] : 0 ) = max_z;
943 top_left_z = max_z;
944 }
945
946 /* Fill in missing corners. */
947 for( iter_y = 0 ; iter_y < 2 ; iter_y++ ) {
948 for( iter_x = 0 ; iter_x < 2 ; iter_x++ ) {
949 /* Grab a pointer to the corner so we can modify it easily. */
950 tile_iter = &(retrotile_get_tile(
951 t, layer,
952 corners_x[iter_x][iter_y],
953 corners_y[iter_x][iter_y] ));
954
955 /* Check if corner is already filled in. */
956 if( -1 != *tile_iter ) {
957 debug_printf(
958 RETROTILE_TRACE_LVL, "corner coord %d x %d present: %d",
959 corners_x[iter_x][iter_y], corners_y[iter_x][iter_y],
960 retrotile_get_tile(
961 t, layer,
962 corners_x[iter_x][iter_y],
963 corners_y[iter_x][iter_y] ) );
964 continue;
965 }
966
967 /* Generate a new value for this corner. */
968 *tile_iter = retrotile_gen_diamond_square_rand(
969 min_z, max_z, tuning, top_left_z );
970
971 debug_printf( RETROTILE_TRACE_LVL,
972 "missing corner coord %d x %d: %d",
973 corners_x[iter_x][iter_y], corners_y[iter_x][iter_y],
974 *tile_iter );
975 }
976 }
977}
978
979/* === */
980
981static retroflat_tile_t retrotile_gen_diamond_square_avg(
982 int16_t corners_x[2][2], int16_t corners_y[2][2],
983 struct RETROTILE* t, struct RETROTILE_LAYER* layer
984) {
985 retroflat_tile_t* tile_iter = NULL;
986 int16_t iter_x = 0,
987 iter_y = 0;
988 retroflat_tile_t avg = 0;
989
990 /* Average corner data. */
991 for( iter_y = 0 ; 2 > iter_y ; iter_y++ ) {
992 for( iter_x = 0 ; 2 > iter_x ; iter_x++ ) {
993 tile_iter = &(retrotile_get_tile(
994 t, layer,
995 corners_x[iter_x][iter_y],
996 corners_y[iter_x][iter_y] ));
997 assert( -1 != *tile_iter );
998 /*
999 debug_printf(
1000 RETROTILE_TRACE_LVL, "%d: adding from coords %d x %d: %d",
1001 iter_depth,
1002 corners_x[iter_x][iter_y], corners_y[iter_x][iter_y],
1003 *tile_iter ); */
1004 avg += *tile_iter;
1005 }
1006 }
1007
1008 /* TODO: Use right shift? */
1009 avg /= 4;
1010
1011 return avg;
1012}
1013
1014/* === */
1015
1017 struct RETROTILE* t, retroflat_tile_t min_z, retroflat_tile_t max_z,
1018 uint32_t tuning, size_t layer_idx, uint8_t flags, void* data,
1019 retrotile_ani_cb animation_cb, void* animation_cb_data
1020) {
1021 int16_t iter_x = 0,
1022 iter_y = 0,
1023 iter_depth = 0;
1024 int16_t corners_x[2][2];
1025 int16_t corners_y[2][2];
1026 int32_t avg = 0;
1027 /* size_t tile_idx = 0; */
1028 struct RETROTILE_DATA_DS data_ds_sub;
1029 MAUG_MHANDLE data_ds_h = NULL;
1030 struct RETROTILE_DATA_DS* data_ds = NULL;
1031 /* retroflat_tile_t* tiles = NULL; */
1032 MERROR_RETVAL retval = MERROR_OK;
1033 struct RETROTILE_LAYER* layer = NULL;
1034 retroflat_tile_t* tile_iter = NULL;
1035 uint8_t free_ds_data = 0;
1036
1037 /*
1038 maug_mlock( t->tiles, tiles );
1039 maug_cleanup_if_null_alloc( struct GRIDCITY_TILE*, tiles );
1040 */
1041
1042 #define _retrotile_ds_update_statistics( data, tile ) \
1043 /* Gather statistics. */ \
1044 if( (data)->highest_generated < (tile) && 32767 > (tile) ) { \
1045 (data)->highest_generated = (tile); \
1046 } \
1047 if( (data)->lowest_generated > (tile) && 0 < (tile) ) { \
1048 (data)->lowest_generated = (tile); \
1049 }
1050
1051 layer = retrotile_get_layer_p( t, layer_idx );
1052
1053 if(
1054 NULL == data ||
1056 ) {
1057 /* This must be the first call, so initialize or allocate a new struct.
1058 */
1059 if( NULL == data ) {
1060 /* An internal struct needs to be allocated before initialization. */
1061 data_ds_h = maug_malloc( 1, sizeof( struct RETROTILE_DATA_DS ) );
1062 maug_cleanup_if_null_alloc( MAUG_MHANDLE, data_ds_h );
1063 free_ds_data = 1;
1064 maug_mlock( data_ds_h, data_ds );
1065 maug_cleanup_if_null_alloc( struct RETROTILE_DATA_DS*, data_ds );
1066 } else {
1067 data_ds = (struct RETROTILE_DATA_DS*)data;
1068 }
1069
1070 /* Initialize passed tilemap while we're handling first call stuff. */
1071 memset( retrotile_get_tiles_p( layer ), -1,
1072 t->tiles_w * t->tiles_h * sizeof( retroflat_tile_t ) );
1073
1074 /* Initialize DS struct from tilemap properties. */
1075 maug_mzero( data_ds, sizeof( struct RETROTILE_DATA_DS ) );
1076 data_ds->sect_w = t->tiles_w;
1077 data_ds->sect_h = t->tiles_h;
1078 data_ds->sect_w_half = data_ds->sect_w >> 1;
1079 data_ds->sect_h_half = data_ds->sect_h >> 1;
1080 data_ds->lowest_generated = 32767;
1081
1082 /* Disable this flag for subsequent calls. */
1083 flags &= ~RETROTILE_DS_FLAG_INIT_DATA;
1084 } else {
1085 data_ds = (struct RETROTILE_DATA_DS*)data;
1086 }
1087 assert( NULL != data_ds );
1088
1089 /* Trivial case; end recursion. */
1090 if( 0 == data_ds->sect_w ) {
1091 debug_printf(
1092 RETROTILE_TRACE_LVL, "%d return: null sector", iter_depth );
1093 goto cleanup;
1094 }
1095
1096 if(
1097 data_ds->sect_x + data_ds->sect_w > t->tiles_w ||
1098 data_ds->sect_y + data_ds->sect_h > t->tiles_h
1099 ) {
1100 debug_printf(
1101 RETROTILE_TRACE_LVL, "%d return: overflow sector", iter_depth );
1102 goto cleanup;
1103 }
1104
1105 iter_depth = t->tiles_w / data_ds->sect_w;
1106
1107 /* Generate/grab corners before averaging them! */
1108 retrotile_gen_diamond_square_corners(
1109 corners_x, corners_y, min_z, max_z, tuning, data_ds, layer, t );
1110
1111 if( 2 == data_ds->sect_w || 2 == data_ds->sect_h ) {
1112 /* Nothing to average, this sector is just corners! */
1113 debug_printf(
1115 "%d return: reached innermost point", iter_depth );
1116 goto cleanup; /* Skip further descent regardless. */
1117 }
1118
1119 avg =
1120 retrotile_gen_diamond_square_avg( corners_x, corners_y, t, layer );
1121
1122 debug_printf( 1, "avg :%d", avg );
1123
1124 tile_iter = &(retrotile_get_tile(
1125 t, layer,
1126 data_ds->sect_x + data_ds->sect_w_half,
1127 data_ds->sect_y + data_ds->sect_h_half ));
1128 if( -1 != *tile_iter ) {
1129 debug_printf( RETROTILE_TRACE_LVL, "avg already present at %d x %d!",
1130 data_ds->sect_x + data_ds->sect_w_half,
1131 data_ds->sect_y + data_ds->sect_h_half );
1132 goto cleanup;
1133 }
1134 *tile_iter = avg;
1135 _retrotile_ds_update_statistics( data_ds, avg );
1136
1137 /* assert( 0 <= tiles[tile_idx].terrain );
1138
1139 maug_munlock( city->tiles, tiles );
1140 tiles = NULL; */
1141
1142 /* Recurse into subsectors. */
1143 for(
1144 iter_y = data_ds->sect_y ;
1145 iter_y < (data_ds->sect_y + data_ds->sect_h) ;
1146 iter_y++
1147 ) {
1148 for(
1149 iter_x = data_ds->sect_x ;
1150 iter_x < (data_ds->sect_x + data_ds->sect_w) ;
1151 iter_x++
1152 ) {
1153 data_ds_sub.sect_x = data_ds->sect_x + iter_x;
1154
1155 data_ds_sub.sect_y = data_ds->sect_y + iter_y;
1156
1157 data_ds_sub.sect_w = data_ds->sect_w_half;
1158 data_ds_sub.sect_h = data_ds->sect_h_half;
1159 data_ds_sub.sect_w_half = data_ds_sub.sect_w >> 1;
1160 data_ds_sub.sect_h_half = data_ds_sub.sect_h >> 1;
1161 data_ds_sub.lowest_generated = 32767;
1162 data_ds_sub.highest_generated = 0;
1163
1164 debug_printf(
1165 RETROTILE_TRACE_LVL, "%d: child sector at %d x %d, %d wide",
1166 iter_depth,
1167 data_ds_sub.sect_x, data_ds_sub.sect_y, data_ds_sub.sect_w );
1168
1170 t, min_z, max_z, tuning, layer_idx, flags, &data_ds_sub,
1171 animation_cb, animation_cb_data );
1172 maug_cleanup_if_not_ok();
1173
1174 _retrotile_ds_update_statistics(
1175 data_ds, data_ds_sub.highest_generated );
1176 _retrotile_ds_update_statistics(
1177 data_ds, data_ds_sub.lowest_generated );
1178 }
1179 }
1180
1181 if(
1182 data_ds->sect_w == t->tiles_w >> 1 &&
1183 NULL != animation_cb
1184 ) {
1185 retval = animation_cb( animation_cb_data, iter_y );
1186 maug_cleanup_if_not_ok();
1187 }
1188
1189 debug_printf(
1190 RETROTILE_TRACE_LVL, "%d return: all sectors complete", iter_depth );
1191
1192cleanup:
1193
1194 if( free_ds_data && NULL != data_ds ) {
1195 maug_munlock( data_ds_h, data_ds );
1196 }
1197
1198 if( free_ds_data && NULL != data_ds_h ) {
1199 maug_mfree( data_ds_h );
1200 }
1201
1202 return retval;
1203}
1204
1205/* === */
1206
1208 struct RETROTILE* t, retroflat_tile_t min_z, retroflat_tile_t max_z,
1209 uint32_t tuning, size_t layer_idx, uint8_t flags, void* data,
1210 retrotile_ani_cb animation_cb, void* animation_cb_data
1211) {
1212 size_t x = 0,
1213 y = 0;
1214 int16_t offset_x = 0,
1215 offset_y = 0,
1216 finished = 0;
1217 MERROR_RETVAL retval = MERROR_OK;
1218 struct RETROTILE_LAYER* layer = NULL;
1219 int16_t spb = RETROTILE_VORONOI_DEFAULT_SPB;
1220 int16_t drift = RETROTILE_VORONOI_DEFAULT_DRIFT;
1221 MAUG_MHANDLE temp_grid_h = (MAUG_MHANDLE)NULL;
1222 retroflat_tile_t* temp_grid = NULL;
1223 retroflat_tile_t* tiles = NULL;
1224 /* Only use 4 cardinal directions. */
1225 int8_t side_iter = 0;
1226
1227 layer = retrotile_get_layer_p( t, 0 );
1228
1229 tiles = retrotile_get_tiles_p( layer );
1230
1231 /* Initialize grid to empty. */
1232 memset( tiles, -1,
1233 t->tiles_w * t->tiles_h * sizeof( retroflat_tile_t ) );
1234
1235 /* Generate the initial sector starting points. */
1236 for( y = 0 ; t->tiles_w > y ; y += spb ) {
1237 for( x = 0 ; t->tiles_w > x ; x += spb ) {
1238 offset_x = x + ((drift * -1) + (rand() % drift));
1239 offset_y = y + ((drift * -1) + (rand() % drift));
1240
1241 /* Clamp sector offsets onto map borders. */
1242 if( 0 > offset_x ) {
1243 offset_x = 0;
1244 }
1245 if( offset_x >= t->tiles_w ) {
1246 offset_x = t->tiles_w - 1;
1247 }
1248 if( 0 > offset_y ) {
1249 offset_y = 0;
1250 }
1251 if( offset_y >= t->tiles_h ) {
1252 offset_y = t->tiles_h - 1;
1253 }
1254
1255 retrotile_get_tile( t, layer, offset_x, offset_y ) =
1256 min_z + (rand() % max_z);
1257 }
1258 }
1259
1260 temp_grid_h = maug_malloc(
1261 sizeof( retroflat_tile_t ), t->tiles_w * t->tiles_h );
1262 maug_cleanup_if_null_alloc( MAUG_MHANDLE, temp_grid_h );
1263
1264 maug_mlock( temp_grid_h, temp_grid );
1265 maug_cleanup_if_null_alloc( retroflat_tile_t*, temp_grid );
1266
1267 /* Grow the sector starting points. */
1268 while( !finished ) {
1269 if( NULL != animation_cb ) {
1270 retval = animation_cb( animation_cb_data, -1 );
1271 maug_cleanup_if_not_ok();
1272 }
1273
1274 /* Prepare sampling grid so we're working from unexpanded sections
1275 * below.
1276 */
1277 memcpy(
1278 temp_grid, tiles,
1279 sizeof( retroflat_tile_t ) * t->tiles_w * t->tiles_h );
1280
1281 /* Starting another pass, assume finished until proven otherwise. */
1282 finished = 1;
1283 for( y = 0 ; t->tiles_h > y ; y++ ) {
1284 for( x = 0 ; t->tiles_w > x ; x++ ) {
1285 if( -1 == retrotile_get_tile( t, layer, x, y ) ) {
1286 /* If there are still unfilled tiles, we're not finished
1287 * yet!
1288 */
1289 finished = 0;
1290
1291 /* Skip filled tile. */
1292 continue;
1293 }
1294
1295
1296 for( side_iter = 0 ; 4 > side_iter ; side_iter++ ) {
1297 debug_printf( RETROTILE_TRACE_LVL,
1298 SIZE_T_FMT " (%d), " SIZE_T_FMT " (%d) ("
1299 SIZE_T_FMT ", " SIZE_T_FMT ")",
1300 x,
1301 gc_retroflat_offsets4_x[side_iter],
1302 y,
1303 gc_retroflat_offsets4_y[side_iter],
1304 t->tiles_w, t->tiles_h );
1305
1306 /* Iterate through directions to expand. */
1307 /* TODO: Add tuning to select directional probability. */
1308 if(
1309 t->tiles_w > x + gc_retroflat_offsets4_x[side_iter] &&
1310 t->tiles_h > y + gc_retroflat_offsets4_y[side_iter] &&
1311 -1 == temp_grid[
1312 ((y + gc_retroflat_offsets4_y[side_iter]) *
1313 t->tiles_w) +
1314 (x + gc_retroflat_offsets4_x[side_iter])]
1315 ) {
1316 /* Copy center tile to this direction. */
1317 retrotile_get_tile( t, layer,
1318 x + gc_retroflat_offsets4_x[side_iter],
1319 y + gc_retroflat_offsets4_y[side_iter] ) =
1320 retrotile_get_tile( t, layer, x, y );
1321 break;
1322 }
1323 }
1324 }
1325 }
1326 }
1327
1328cleanup:
1329
1330 if( NULL != temp_grid ) {
1331 maug_munlock( temp_grid_h, temp_grid );
1332 }
1333
1334 if( NULL != temp_grid_h ) {
1335 maug_mfree( temp_grid_h );
1336 }
1337
1338 return retval;
1339}
1340
1341/* === */
1342
1344 struct RETROTILE* t, retroflat_tile_t min_z, retroflat_tile_t max_z,
1345 uint32_t tuning, size_t layer_idx, uint8_t flags, void* data,
1346 retrotile_ani_cb animation_cb, void* animation_cb_data
1347) {
1348 MERROR_RETVAL retval = MERROR_OK;
1349 size_t x = 0,
1350 y = 0;
1351 int16_t side_iter = 0,
1352 sides_avail = 0,
1353 sides_sum = 0;
1354 /* Sides start from 12 on the clock (up). */
1355 struct RETROTILE_LAYER* layer = NULL;
1356
1357 assert( NULL != t );
1358 layer = retrotile_get_layer_p( t, layer_idx );
1359 assert( NULL != layer );
1360
1361 for( y = 0 ; t->tiles_h > y ; y++ ) {
1362 if( NULL != animation_cb ) {
1363 retval = animation_cb( animation_cb_data, y );
1364 maug_cleanup_if_not_ok();
1365 }
1366 for( x = 0 ; t->tiles_w > x ; x++ ) {
1367 /* Reset average. */
1368 sides_avail = 0;
1369 sides_sum = 0;
1370
1371 /* Grab values for available sides. */
1372 for( side_iter = 0 ; 8 > side_iter ; side_iter++ ) {
1373 if(
1374 t->tiles_w <= x + gc_retroflat_offsets8_x[side_iter] ||
1375 t->tiles_h <= y + gc_retroflat_offsets8_y[side_iter]
1376 ) {
1377 continue;
1378 }
1379
1380 sides_avail++;
1381 debug_printf(
1383 "si %d: x, y: " SIZE_T_FMT " (+%d), " SIZE_T_FMT
1384 " (+%d) idx: " SIZE_T_FMT,
1385 side_iter,
1386 x + gc_retroflat_offsets8_x[side_iter],
1387 gc_retroflat_offsets8_x[side_iter],
1388 y + gc_retroflat_offsets8_y[side_iter],
1389 gc_retroflat_offsets8_y[side_iter],
1390 ((y + gc_retroflat_offsets8_y[side_iter]) * t->tiles_w) +
1391 x + gc_retroflat_offsets8_x[side_iter] );
1392 sides_sum += retrotile_get_tile(
1393 t, layer,
1394 x + gc_retroflat_offsets8_x[side_iter],
1395 y + gc_retroflat_offsets8_y[side_iter] );
1396 }
1397
1398 retrotile_get_tile( t, layer, x, y ) = sides_sum / sides_avail;
1399 }
1400 }
1401
1402cleanup:
1403
1404 return retval;
1405}
1406
1407/* === */
1408
1410 struct RETROTILE* t, retroflat_tile_t min_z, retroflat_tile_t max_z,
1411 uint32_t tuning, size_t layer_idx, uint8_t flags, void* data,
1412 retrotile_ani_cb animation_cb, void* animation_cb_data
1413) {
1414 MERROR_RETVAL retval = MERROR_OK;
1415 struct RETROTILE_DATA_BORDER* borders =
1416 (struct RETROTILE_DATA_BORDER*)data;
1417 size_t i = 0,
1418 x = 0,
1419 y = 0,
1420 x_plus_1 = 0,
1421 y_plus_1 = 0,
1422 side = 0;
1423 int16_t ctr_iter = 0,
1424 outside_iter = 0;
1425 struct RETROTILE_LAYER* layer = NULL;
1426
1427 assert( NULL != t );
1428 layer = retrotile_get_layer_p( t, layer_idx );
1429 assert( NULL != layer );
1430
1431 /* Reset tile counter for all defined borders. */
1432 for( i = 0 ; 0 <= borders[i].center ; i++ ) {
1433 borders[i].tiles_changed = 0;
1434 }
1435
1436 debug_printf( 1, "adding borders..." );
1437
1438 for( y = 0 ; t->tiles_h > y ; y++ ) {
1439 for( x = 0 ; t->tiles_w > x ; x++ ) {
1440 i = 0;
1441 while( 0 <= borders[i].center ) {
1442 /* Compare/grab current center tile. */
1443 ctr_iter = retrotile_get_tile( t, layer, x, y );
1444 debug_printf( RETROTILE_TRACE_LVL,
1445 "x: " SIZE_T_FMT ", y: " SIZE_T_FMT ", 0x%04x vs 0x%04x",
1446 x, y, ctr_iter, borders[i].center );
1447 if( ctr_iter != borders[i].center ) {
1448 i++;
1449 continue;
1450 }
1451
1452 debug_printf( RETROTILE_TRACE_LVL, "comparing sides..." );
1453
1454 /* Zeroth pass: look for stick-outs. */
1455 for( side = 0 ; 8 > side ; side += 2 ) {
1456 if(
1457 x + gc_retroflat_offsets8_x[side] > t->tiles_w ||
1458 y + gc_retroflat_offsets8_y[side] > t->tiles_h
1459 ) {
1460 /* Skip out-of-bounds. */
1461 continue;
1462 }
1463 /* Get the outside tile on this side. */
1464 outside_iter = retrotile_get_tile( t, layer,
1465 x + gc_retroflat_offsets8_x[side],
1466 y + gc_retroflat_offsets8_y[side] );
1467
1468 /* Get the outside tile next two clock-steps from this one.
1469 */
1470 if( side + 4 < 8 ) {
1471 x_plus_1 = x + gc_retroflat_offsets8_x[side + 4];
1472 y_plus_1 = y + gc_retroflat_offsets8_y[side + 4];
1473 } else {
1474 x_plus_1 = x + gc_retroflat_offsets8_x[side - 4];
1475 y_plus_1 = y + gc_retroflat_offsets8_y[side - 4];
1476 }
1477
1478 if(
1479 x_plus_1 < t->tiles_w && y_plus_1 < t->tiles_h &&
1480 outside_iter == borders[i].outside &&
1481 outside_iter == retrotile_get_tile( t, layer,
1482 x_plus_1, y_plus_1 )
1483 ) {
1484 /* This has the outside on two opposing sides, so just
1485 * erase it and use the outside. */
1486 retrotile_get_tile( t, layer, x, y ) =
1487 borders[i].outside;
1488 borders[i].tiles_changed++;
1489 goto tile_done;
1490 }
1491 }
1492
1493
1494 /* First pass: look for corners. */
1495 for( side = 0 ; 8 > side ; side += 2 ) {
1496 if(
1497 x + gc_retroflat_offsets8_x[side] > t->tiles_w ||
1498 y + gc_retroflat_offsets8_y[side] > t->tiles_h
1499 ) {
1500 /* Skip out-of-bounds. */
1501 continue;
1502 }
1503 /* Get the outside tile on this side. */
1504 outside_iter = retrotile_get_tile( t, layer,
1505 x + gc_retroflat_offsets8_x[side],
1506 y + gc_retroflat_offsets8_y[side] );
1507
1508 /* Get the outside tile next two clock-steps from this one.
1509 */
1510 if( side + 2 < 8 ) {
1511 x_plus_1 = x + gc_retroflat_offsets8_x[side + 2];
1512 y_plus_1 = y + gc_retroflat_offsets8_y[side + 2];
1513 } else {
1514 x_plus_1 = x + gc_retroflat_offsets8_x[0];
1515 y_plus_1 = y + gc_retroflat_offsets8_y[0];
1516 }
1517
1518 if(
1519 x_plus_1 < t->tiles_w && y_plus_1 < t->tiles_h &&
1520 outside_iter == borders[i].outside &&
1521 outside_iter == retrotile_get_tile( t, layer,
1522 x_plus_1, y_plus_1 )
1523 ) {
1524 /* This has the outside on two sides, so use a corner. */
1525 retrotile_get_tile( t, layer, x, y ) =
1526 borders[i].mod_to[side + 1 < 8 ? side + 1 : 0];
1527 borders[i].tiles_changed++;
1528 goto tile_done;
1529 }
1530 }
1531
1532 /* Second pass (if first pass fails): look for edges. */
1533 for( side = 0 ; 8 > side ; side += 2 ) {
1534 if(
1535 x + gc_retroflat_offsets8_x[side] > t->tiles_w ||
1536 y + gc_retroflat_offsets8_y[side] > t->tiles_h
1537 ) {
1538 /* Skip out-of-bounds. */
1539 continue;
1540 }
1541 /* Get the outside tile on this side. */
1542 outside_iter = retrotile_get_tile( t, layer,
1543 x + gc_retroflat_offsets8_x[side],
1544 y + gc_retroflat_offsets8_y[side] );
1545
1546 if( outside_iter == borders[i].outside ) {
1547 /* It only matches on this side. */
1548 debug_printf( RETROTILE_TRACE_LVL, "replacing..." );
1549 retrotile_get_tile( t, layer, x, y ) =
1550 borders[i].mod_to[side];
1551 borders[i].tiles_changed++;
1552 goto tile_done;
1553 }
1554 }
1555
1556tile_done:
1557 /* Tile replaced or not replaceable. */
1558 break;
1559 }
1560 }
1561 }
1562
1563 return retval;
1564}
1565
1566/* === */
1567
1568struct RETROTILE_LAYER* retrotile_get_layer_p(
1569 struct RETROTILE* tilemap, uint32_t layer_idx
1570) {
1571 struct RETROTILE_LAYER* layer_iter = NULL;
1572 uint8_t* tilemap_buf = (uint8_t*)tilemap;
1573
1574 if( 0 == tilemap->layers_count || layer_idx >= tilemap->layers_count ) {
1575 error_printf( "invalid layer " UPRINTF_U32_FMT
1576 " requested (of " UPRINTF_U32_FMT ")!",
1577 layer_idx, tilemap->layers_count );
1578 return NULL;
1579 }
1580
1581 /* Advance to first grid. */
1582 tilemap_buf += sizeof( struct RETROTILE );
1583 layer_iter = (struct RETROTILE_LAYER*)tilemap_buf;
1584 while( layer_idx > 0 ) {
1585 tilemap_buf += layer_iter->sz;
1586 layer_iter = (struct RETROTILE_LAYER*)tilemap_buf;
1587 layer_idx--;
1588 }
1589
1590 return layer_iter;
1591}
1592
1593
1594/* === */
1595
1596MERROR_RETVAL retrotile_alloc_tile_defs(
1597 MAUG_MHANDLE* p_tile_defs_h, size_t* p_tile_defs_count, size_t ndefs
1598) {
1599 MERROR_RETVAL retval = MERROR_OK;
1600 struct RETROTILE_TILE_DEF* tile_defs = NULL;
1601
1602 assert( 0 == *p_tile_defs_count );
1603 assert( NULL == *p_tile_defs_h );
1604 debug_printf( 1, "allocating " SIZE_T_FMT " tile definitions ("
1605 SIZE_T_FMT " bytes)...",
1606 ndefs, ndefs * sizeof( struct RETROTILE_TILE_DEF ) );
1607 *p_tile_defs_h =
1608 maug_malloc( ndefs, sizeof( struct RETROTILE_TILE_DEF ) );
1609 maug_cleanup_if_null_alloc( MAUG_MHANDLE, *p_tile_defs_h );
1610 *p_tile_defs_count = ndefs;
1611
1612 /* Zero new allocs. */
1613 maug_mlock( *p_tile_defs_h, tile_defs );
1614 maug_cleanup_if_null_alloc( struct RETROTILE_TILE_DEF*, tile_defs );
1615 maug_mzero( tile_defs, ndefs * sizeof( struct RETROTILE_TILE_DEF ) );
1616
1617cleanup:
1618
1619 if( NULL != tile_defs ) {
1620 maug_munlock( *p_tile_defs_h, tile_defs );
1621 }
1622
1623 return retval;
1624}
1625
1626/* === */
1627
1628MERROR_RETVAL retrotile_alloc(
1629 MAUG_MHANDLE* p_tilemap_h, size_t w, size_t h, size_t layers_count
1630) {
1631 struct RETROTILE_LAYER* layer_iter = NULL;
1632 MERROR_RETVAL retval = MERROR_OK;
1633 size_t tilemap_sz = 0;
1634 struct RETROTILE* tilemap = NULL;
1635 size_t i = 0;
1636
1637 tilemap_sz = sizeof( struct RETROTILE ) +
1638 (layers_count * sizeof( struct RETROTILE_LAYER )) +
1639 (layers_count * (w * h * sizeof( retroflat_tile_t ) ));
1640
1641 debug_printf( 1, "allocating new tilemap " SIZE_T_FMT "x" SIZE_T_FMT
1642 " tiles, " SIZE_T_FMT " layers (" SIZE_T_FMT " bytes)...",
1643 w, h, layers_count, tilemap_sz );
1644
1645 *p_tilemap_h = maug_malloc( 1, tilemap_sz );
1646 maug_cleanup_if_null_alloc( MAUG_MHANDLE, *p_tilemap_h );
1647
1648 maug_mlock( *p_tilemap_h, tilemap );
1649 maug_cleanup_if_null_alloc( struct RETROTILE*, tilemap );
1650
1651 maug_mzero( tilemap, tilemap_sz );
1652 tilemap->sz = tilemap_sz;
1653 tilemap->layers_count = layers_count;
1654 tilemap->tiles_w = w;
1655 tilemap->tiles_h = h;
1656 tilemap->tile_scale = RETROTILE_TILE_SCALE_DEFAULT;
1657
1658 for( i = 0 ; layers_count > i ; i++ ) {
1659 layer_iter = retrotile_get_layer_p( tilemap, i );
1660 assert( NULL != layer_iter );
1661 layer_iter->sz = sizeof( struct RETROTILE_LAYER ) +
1662 (w * h * sizeof( retroflat_tile_t ));
1663 maug_cleanup_if_not_ok();
1664 }
1665
1666cleanup:
1667
1668 if( NULL != tilemap ) {
1669 maug_munlock( *p_tilemap_h, tilemap );
1670 }
1671
1672 return retval;
1673}
1674
1675#endif /* RETROTIL_C */
1676
1677 /* retrotile */
1678
1679#endif /* !RETROTIL_H */
1680
int MERROR_RETVAL
Return type indicating function returns a value from this list.
Definition merror.h:19
#define maug_mzero(ptr, sz)
Zero the block of memory pointed to by ptr.
Definition mmem.h:62
MERROR_RETVAL mfile_open_read(const char *filename, mfile_t *p_file)
Open a file and read it into memory or memory-map it.
#define RETROTILE_DS_FLAG_INIT_DATA
Flag for retrotile_gen_diamond_square_iter() indicating that passed RETROTILE_DATA_DS object should b...
Definition retrotil.h:81
MERROR_RETVAL retrotile_gen_smooth_iter(struct RETROTILE *t, retroflat_tile_t min_z, retroflat_tile_t max_z, uint32_t tuning, size_t layer_idx, uint8_t flags, void *data, retrotile_ani_cb animation_cb, void *animation_cb_data)
Average the values in adjacent tiles over an already-generated tilemap.
MERROR_RETVAL retrotile_gen_borders_iter(struct RETROTILE *t, retroflat_tile_t min_z, retroflat_tile_t max_z, uint32_t tuning, size_t layer_idx, uint8_t flags, void *data, retrotile_ani_cb animation_cb, void *animation_cb_data)
Given a list of RETROTILE_DATA_BORDER structs, this will search for occurrences of RETROTILE_DATA_BOR...
MERROR_RETVAL retrotile_gen_diamond_square_iter(struct RETROTILE *t, retroflat_tile_t min_z, retroflat_tile_t max_z, uint32_t tuning, size_t layer_idx, uint8_t flags, void *data, retrotile_ani_cb animation_cb, void *animation_cb_data)
Generate tilemap terrain using diamond square algorithm.
MERROR_RETVAL retrotile_gen_voronoi_iter(struct RETROTILE *t, retroflat_tile_t min_z, retroflat_tile_t max_z, uint32_t tuning, size_t layer_idx, uint8_t flags, void *data, retrotile_ani_cb animation_cb, void *animation_cb_data)
Generate tilemap terrain using voronoi graph.
#define RETROTILE_TILESET_IMAGE_STR_SZ_MAX
Maximum number of chars in a RETROTILE_TILE_DEF::image_path.
Definition retrotil.h:24
#define RETROTILE_TILE_SCALE_DEFAULT
Default value for RETROTILE::tile_scale.
Definition retrotil.h:29
#define RETROTILE_TRACE_LVL
If defined, bring debug printf statements up to this level.
Definition retrotil.h:34
#define RETROTILE_NAME_SZ_MAX
Maximum number of chars in a RETROTILE::name.
Definition retrotil.h:19
Definition mfile.h:74
Definition retrotil.h:128
retroflat_tile_t mod_to[8]
If the center and outside match, use this mod-to.
Definition retrotil.h:133
Internal data structure used by retrotile_gen_diamond_square_iter().
Definition retrotil.h:111
int16_t sect_y
Starting Y of subsector in a given iteration.
Definition retrotil.h:115
int16_t sect_w
Width of subsector in a given iteration.
Definition retrotil.h:117
int16_t sect_w_half
Half of the width of subsector in a given iteration.
Definition retrotil.h:121
int16_t sect_x
Starting X of subsector in a given iteration.
Definition retrotil.h:113
int16_t sect_h
Height of subsector in a given iteration.
Definition retrotil.h:119
int16_t sect_h_half
Half of the height of subsector in a given iteration.
Definition retrotil.h:123
Definition retrotil.h:92
Definition retrotil.h:160
size_t tileset_id_cur
Highest tileset ID on first pass and next ID to be assigned on second.
Definition retrotil.h:174
Definition retrotil.h:85
Definition retrotil.h:96