maug
Quick and dirty C mini-augmentation library.
Loading...
Searching...
No Matches
retrotil.h
Go to the documentation of this file.
1
2#ifndef RETROTIL_H
3#define RETROTIL_H
4
5#include <mjson.h>
6#include <mfile.h>
7
14
15#define RETROTILE_PRESENT
16
17typedef int16_t retrotile_coord_t;
18
19#ifndef RETROTILE_TRACE_CHARS
20# define RETROTILE_TRACE_CHARS 0
21#endif /* !RETROTILE_TRACE_TOKENS */
22
23#ifndef RETROTILE_NAME_SZ_MAX
25# define RETROTILE_NAME_SZ_MAX 10
26#endif /* !RETROTILE_NAME_SZ_MAX */
27
28#ifndef RETROTILE_PROP_NAME_SZ_MAX
30# define RETROTILE_PROP_NAME_SZ_MAX 10
31#endif /* !RETROTILE_PROP_NAME_SZ_MAX */
32
33#ifndef RETROTILE_TILE_SCALE_DEFAULT
35# define RETROTILE_TILE_SCALE_DEFAULT 1.0f
36#endif /* !RETROTILE_TILE_SCALE_DEFAULT */
37
38#ifndef RETROTILE_TRACE_LVL
40# define RETROTILE_TRACE_LVL 0
41#endif /* !RETROTILE_TRACE_LVL */
42
43#ifndef RETROTILE_VORONOI_DEFAULT_SPB
44# define RETROTILE_VORONOI_DEFAULT_SPB 8
45#endif /* !RETROTILE_VORONOI_DEFAULT_SPB */
46
47#ifndef RETROTILE_VORONOI_DEFAULT_DRIFT
48# define RETROTILE_VORONOI_DEFAULT_DRIFT 4
49#endif /* !RETROTILE_VORONOI_DEFAULT_DRIFT */
50
51#ifdef MPARSER_TRACE_NAMES
52# define retrotile_mstate_name( state ) gc_retrotile_mstate_names[state]
53#else
54# define retrotile_mstate_name( state ) state
55#endif /* MPARSER_TRACE_NAMES */
56
57#ifndef RETROTILE_PARSER_FLAG_LITERAL_PATHS
63# define RETROTILE_PARSER_FLAG_LITERAL_PATHS 0x02
64#endif /* !RETROTILE_PARSER_FLAG_LITERAL_PATHS */
65
71#define RETROTILE_PARSER_MODE_MAP 0
72
78#define RETROTILE_PARSER_MODE_DEFS 1
79
80#define RETROTILE_CLASS_TABLE( f ) \
81 f( TILE, tile, 0 ) \
82 f( MOBILE, mobile, 1 ) \
83 f( WARP, warp, 2 ) \
84 f( ITEM, item, 3 ) \
85 f( CROP, crop, 4 )
86
91
96
101#define RETROTILE_TILE_FLAG_BLOCK 0x01
102
107#define RETROTILE_TILE_FLAG_RESERVED1 0x02
108
113#define RETROTILE_TILE_FLAG_RESERVED2 0x04
114
119#define RETROTILE_TILE_FLAG_RESERVED3 0x08
120 /* retrotile_defs_types */
122
127#define RETROTILE_PROP_TYPE_OTHER 0
128
133#define RETROTILE_PROP_TYPE_STRING 1
134
139#define RETROTILE_PROP_TYPE_FILE 2
140
145#define RETROTILE_PROP_TYPE_INT 3
146
157#define RETROTILE_DS_FLAG_INIT_DATA 0x02
158
162#define RETROTILE_IDX_FMT "%u"
163
165 uint8_t flags;
166 retroflat_asset_path image_path;
167 size_t x;
168 size_t y;
169 uint16_t tile_class;
170 char warp_dest[RETROTILE_NAME_SZ_MAX + 1];
177 retrotile_coord_t warp_x;
178 retrotile_coord_t warp_y;
179#ifdef RETROFLAT_3D
180 /* TODO: Work this into retrogxc? */
181 struct RETRO3DP_MODEL model;
182#endif /* RETROFLAT_3D */
183#ifdef RETROGXC_PRESENT
184 ssize_t image_cache_id;
185#else
186 struct RETROFLAT_BITMAP image;
187#endif /* RETROGXC_PRESENT */
188};
189 /* retrotile_defs */
191
193 size_t sz;
194 uint16_t layer_class;
195};
196
202 /* \brief X position in tilemap tiles. */
203 retrotile_coord_t x;
204 /* \brief Y position in tilemap tiles. */
205 retrotile_coord_t y;
206};
207
213struct RETROTILE {
214 char name[RETROTILE_NAME_SZ_MAX + 1];
215 char tileset[RETROTILE_NAME_SZ_MAX + 1];
217 uint32_t sz;
219 uint32_t layers_count;
223 size_t tiles_h;
225 size_t tiles_w;
231};
232
239 int16_t sect_x;
241 int16_t sect_y;
243 int16_t sect_w;
245 int16_t sect_h;
247 int16_t sect_w_half;
249 int16_t sect_h_half;
250 retroflat_tile_t highest_generated;
251 retroflat_tile_t lowest_generated;
252};
253
255 int16_t tiles_changed;
256 retroflat_tile_t center;
257 retroflat_tile_t outside;
260};
261
262#define retrotile_get_tile( tilemap, layer, x, y ) \
263 (retrotile_get_tiles_p( layer )[((y) * (tilemap)->tiles_w) + (x)])
264
265#define retrotile_set_tile( tilemap, layer, x, y, new_val ) \
266 (retrotile_get_tiles_p( layer )[((y) * (tilemap)->tiles_w) + (x)])
267
268#define retrotile_get_tiles_p( layer ) \
269 ((retroflat_tile_t*)(((uint8_t*)(layer)) + \
270 sizeof( struct RETROTILE_LAYER )))
271
272#define retrotile_clear_tiles( t, layer ) \
273 memset( retrotile_get_tiles_p( layer ), -1, \
274 t->tiles_w * t->tiles_h * sizeof( retroflat_tile_t ) )
275
280
281typedef MERROR_RETVAL (*retrotile_tj_parse_cb)(
282 const char* dirname, const char* filename, MAUG_MHANDLE* p_tm_h,
283 struct MDATA_VECTOR* p_td, mparser_wait_cb_t wait_cb, void* wait_data,
284 mparser_parse_token_cb token_cb, void* token_cb_data, uint8_t passes,
285 uint8_t flags );
286
288 uint8_t mstate;
289 uint16_t flags;
290 /* TODO: Use flags and combine these. */
291 uint8_t pass;
292 uint8_t passes_max;
294 uint8_t mode;
295 mparser_wait_cb_t wait_cb;
296 void* wait_data;
297 retroflat_ms_t wait_last;
298 size_t layer_tile_iter;
301 int last_prop_type;
302 size_t last_prop_name_sz;
305 char tileset_name[RETROTILE_NAME_SZ_MAX + 1];
306 size_t pass_layer_iter;
312 size_t tiles_w;
313 size_t tiles_h;
314 struct RETROTILE* t;
315 retrotile_tj_parse_cb tj_parse_cb;
321 mparser_parse_token_cb custom_token_cb;
322 void* custom_token_cb_data;
323 struct MJSON_PARSER jparser;
324 struct MDATA_VECTOR* p_tile_defs;
325 char dirname[RETROFLAT_PATH_MAX + 1];
326 uint16_t layer_class;
327};
328
329/* State Idx JSONKeyWord Parent ParseMode */
330#define RETROTILE_PARSER_MSTATE_TABLE( f ) \
331 f( MTILESTATE_NONE, 0, "", 0, 0 ) \
332 f( MTILESTATE_HEIGHT, 1, "height", 0 , 0 ) \
333 f( MTILESTATE_WIDTH, 2, "width", 0 , 0 ) \
334 f( MTILESTATE_LAYERS, 3, "layers", 0 , 0 ) \
335 f( MTILESTATE_LAYER_DATA, 4, "data", 15 /* LAYER */ , 0 ) \
336 f( MTILESTATE_LAYER_NAME, 5, "name", 15 /* LAYER */ , 0 ) \
337 f( MTILESTATE_TILES, 6, "tiles", 0 , 1 ) \
338 f( MTILESTATE_TILES_ID, 7, "id", 6 /* TILES */ , 1 ) \
339 f( MTILESTATE_TILES_IMAGE, 8, "image", 6 /* TILES */ , 1 ) \
340 f( MTILESTATE_TILESETS, 9, "tilesets", 0 , 0 ) \
341 f( MTILESTATE_TILESETS_SRC, 10, "source", 9 /* TILESETS */ , 0 ) \
342 f( MTILESTATE_TILESETS_FGID, 11, "firstgid", 9 /* TILESETS */ , 0 ) \
343 f( MTILESTATE_TILESETS_PROP, 12, "firstgid", 9 /* TILESETS */ , 0 ) \
344 f( MTILESTATE_GRID, 13, "grid", 0 , 1 ) \
345 f( MTILESTATE_TILES_PROP, 14, "properties", 6 /* TILES */ , 1 ) \
346 f( MTILESTATE_LAYER, 15, "layers", /* [sic] */ 3 , 0 ) \
347 f( MTILESTATE_TILES_PROP_NAME, 16, "name", 14 /* TIL_PROP */ , 1 ) \
348 f( MTILESTATE_TILES_PROP_TYPE, 17, "type", 14 /* TIL_PROP */ , 1 ) \
349 f( MTILESTATE_TILES_PROP_VAL, 18, "value", 14 /* TIL_PROP */ , 1 ) \
350 f( MTILESTATE_PROP, 19, "properties", 0 /* NONE */ , 0 ) \
351 f( MTILESTATE_PROP_NAME, 20, "name", 19 /* PROP */ , 0 ) \
352 f( MTILESTATE_PROP_TYPE, 21, "type", 19 /* PROP */ , 0 ) \
353 f( MTILESTATE_PROP_VAL, 22, "value", 19 /* PROP */ , 0 ) \
354 f( MTILESTATE_LAYER_CLASS, 23, "class", 15 /* LAYER */ , 0 ) \
355 f( MTILESTATE_TILES_CLASS, 24, "type", 6 /* TILES */ , 1 ) \
356 f( MTILESTATE_NAME, 25, "name", 0 , 1 ) \
357 f( MTILESTATE_WANGSETS, 26, "wangsets", 0 , 1 ) \
358 f( MTILESTATE_TPROP, 27, "properties", 0 /* NONE */ , 1 ) \
359 f( MTILESTATE_TPROP_NAME, 28, "name", 27 /* PROP */ , 1 ) \
360 f( MTILESTATE_TPROP_TYPE, 29, "type", 27 /* PROP */ , 1 ) \
361 f( MTILESTATE_TPROP_VAL, 30, "value", 27 /* PROP */ , 1 )
362
363/* TODO: Mine wangsets for slowdown values, etc. */
364
366retrotile_parse_json_c( struct RETROTILE_PARSER* parser, char c );
367
372int retrotile_parse_prop_type( const char* token, size_t token_sz );
373
381mfix_t retrotile_static_rotation_from_dir( const char* dir );
382
408 const char* dirname, const char* filename, MAUG_MHANDLE* p_tilemap_h,
409 struct MDATA_VECTOR* p_tile_defs,
410 mparser_wait_cb_t wait_cb, void* wait_data,
411 mparser_parse_token_cb token_cb, void* token_cb_data, uint8_t passes,
412 uint8_t flags );
413 /* retrotile_parser */
415
421
422typedef MERROR_RETVAL (*retrotile_ani_cb)(
423 void* animation_cb_data, int16_t iter );
424
425typedef MERROR_RETVAL (*retrotile_gen_cb)(
426 struct RETROTILE* t, retroflat_tile_t min_z, retroflat_tile_t max_z,
427 uint32_t tuning, size_t layer_idx, uint8_t flags, void* data,
428 retrotile_ani_cb animation_cb, void* animation_cb_data );
429
440 struct RETROTILE* t, retroflat_tile_t min_z, retroflat_tile_t max_z,
441 uint32_t tuning, size_t layer_idx, uint8_t flags, void* data,
442 retrotile_ani_cb animation_cb, void* animation_cb_data );
443
455 struct RETROTILE* t, retroflat_tile_t min_z, retroflat_tile_t max_z,
456 uint32_t tuning, size_t layer_idx, uint8_t flags, void* data,
457 retrotile_ani_cb animation_cb, void* animation_cb_data );
458
467 struct RETROTILE* t, retroflat_tile_t min_z, retroflat_tile_t max_z,
468 uint32_t tuning, size_t layer_idx, uint8_t flags, void* data,
469 retrotile_ani_cb animation_cb, void* animation_cb_data );
470
478 struct RETROTILE* t, retroflat_tile_t min_z, retroflat_tile_t max_z,
479 uint32_t tuning, size_t layer_idx, uint8_t flags, void* data,
480 retrotile_ani_cb animation_cb, void* animation_cb_data );
481 /* retrotile_gen */
483
484struct RETROTILE_LAYER* retrotile_get_layer_p(
485 struct RETROTILE* tilemap, uint32_t layer_idx );
486
487MERROR_RETVAL retrotile_alloc(
488 MAUG_MHANDLE* p_tilemap_h, size_t w, size_t h, size_t layers_count,
489 const char* tilemap_name, const char* tileset_name );
490
491void retrotile_format_asset_path(
492 retroflat_asset_path path_out, const char* afile,
493 struct RETROTILE_PARSER* parser );
494
495MERROR_RETVAL retrotile_clear_refresh( retroflat_pxxy_t y_max );
496
497MERROR_RETVAL retrotile_topdown_draw(
498 struct RETROFLAT_BITMAP* target,
499 struct RETROTILE* t, struct MDATA_VECTOR* t_defs );
500
501#ifdef RETROTIL_C
502
503# include <mparser.h>
504
505/* TODO: Function names should be verb_noun! */
506
507#define retrotile_parser_mstate( parser, new_mstate ) \
508 parser->mstate = new_mstate; \
509 debug_printf( \
510 RETROTILE_TRACE_LVL, "parser mstate: %d", parser->mstate );
511
512# define RETROTILE_PARSER_MSTATE_TABLE_CONST( name, idx, tokn, parent, m ) \
513 MAUG_CONST uint8_t SEG_MCONST name = idx;
514
515RETROTILE_PARSER_MSTATE_TABLE( RETROTILE_PARSER_MSTATE_TABLE_CONST )
516
517#ifdef MPARSER_TRACE_NAMES
518# define RETROTILE_PARSER_MSTATE_TABLE_NAME( name, idx, tokn, parent, m ) \
519 #name,
520
521static MAUG_CONST char* SEG_MCONST gc_retrotile_mstate_names[] = {
522 RETROTILE_PARSER_MSTATE_TABLE( RETROTILE_PARSER_MSTATE_TABLE_NAME )
523 ""
524};
525#endif /* MPARSER_TRACE_NAMES */
526
527# define RETROTILE_PARSER_MSTATE_TABLE_TOKEN( name, idx, tokn, parent, m ) \
528 tokn,
529
530static MAUG_CONST char* SEG_MCONST gc_retrotile_mstate_tokens[] = {
531 RETROTILE_PARSER_MSTATE_TABLE( RETROTILE_PARSER_MSTATE_TABLE_TOKEN )
532 ""
533};
534
535# define RETROTILE_PARSER_MSTATE_TABLE_PARNT( name, idx, tokn, parent, m ) \
536 parent,
537
538static MAUG_CONST uint8_t SEG_MCONST gc_retrotile_mstate_parents[] = {
539 RETROTILE_PARSER_MSTATE_TABLE( RETROTILE_PARSER_MSTATE_TABLE_PARNT )
540 0
541};
542
543# define RETROTILE_PARSER_MSTATE_TABLE_MODE( name, idx, tokn, parent, m ) \
544 m,
545
546static MAUG_CONST uint8_t SEG_MCONST gc_retrotile_mstate_modes[] = {
547 RETROTILE_PARSER_MSTATE_TABLE( RETROTILE_PARSER_MSTATE_TABLE_MODE )
548 0
549};
550
551# define RETROTILE_CLASS_TABLE_CONSTS( A, a, i ) \
552 MAUG_CONST uint8_t SEG_MCONST RETROTILE_CLASS_ ## A = i;
553
554RETROTILE_CLASS_TABLE( RETROTILE_CLASS_TABLE_CONSTS )
555
556/* === */
557
558static void retrotile_parser_match_token(
559 const char* token, size_t token_sz, struct RETROTILE_PARSER* parser
560) {
561 size_t j = 1;
562
563 /* Figure out what the key is for. */
564 while( '\0' != gc_retrotile_mstate_tokens[j][0] ) {
565 if(
566 /* Make sure tokens match. */
567 maug_strlen( gc_retrotile_mstate_tokens[j] ) != token_sz ||
568 0 != strncmp(
569 token, gc_retrotile_mstate_tokens[j], token_sz
570 )
571 ) {
572 j++;
573 continue;
574
575 } else if(
576 /* This state can only be
577 * reached THROUGH that parent state. This allows us to have
578 * keys with the same name but different parents!
579 */
580 parser->mstate != gc_retrotile_mstate_parents[j]
581 ) {
582#ifdef RETROTILE_TRACE_TOKENS
583 debug_printf(
585 "found token \"%s\" "
586#ifdef MPARSER_TRACE_NAMES
587 "but incorrect parent %s (%d) (needs %s (%d))!",
588#else
589 "but incorrect parent %d (needs %d)!",
590#endif /* MPARSER_TRACE_NAMES */
591 token,
592#ifdef MPARSER_TRACE_NAMES
593 retrotile_mstate_name( parser->mstate ),
594 parser->mstate,
595 retrotile_mstate_name( gc_retrotile_mstate_parents[j] ),
596 gc_retrotile_mstate_parents[j]
597#else
598 parser->mstate,
599 gc_retrotile_mstate_parents[j]
600#endif /* MPARSER_TRACE_NAMES */
601 );
602#endif /* !RETROTILE_TRACE_TOKENS */
603 j++;
604 continue;
605
606 } else if( parser->mode != gc_retrotile_mstate_modes[j] ) {
607#ifdef RETROTILE_TRACE_TOKENS
608 debug_printf(
609 RETROTILE_TRACE_LVL, "found token %s but incorrect mode %u!",
610 token,
611 gc_retrotile_mstate_modes[j] );
612#endif /* !RETROTILE_TRACE_TOKENS */
613 j++;
614 continue;
615
616 } else {
617 /* Found it! */
618#ifdef RETROTILE_TRACE_TOKENS
619 debug_printf(
621 "found token \"%s\" "
622#ifdef MPARSER_TRACE_NAMES
623 "under correct parent %s (%d)!",
624#else
625 "under correct parent %d!",
626#endif /* MPARSER_TRACE_NAMES */
627 token,
628#ifdef MPARSER_TRACE_NAMES
629 retrotile_mstate_name( parser->mstate ),
630 parser->mstate
631#else
632 parser->mstate
633#endif /* MPARSER_TRACE_NAMES */
634 );
635#endif /* !RETROTILE_TRACE_TOKENS */
636
637 retrotile_parser_mstate( parser, j );
638 return;
639 }
640 }
641}
642
643/* === */
644
645MERROR_RETVAL retrotile_parser_parse_tiledef_token(
646 const char* token, size_t token_sz, void* parser_arg
647) {
648 MERROR_RETVAL retval = MERROR_OK;
649 struct RETROTILE_TILE_DEF* tile_def = NULL;
650 struct RETROTILE_PARSER* parser = (struct RETROTILE_PARSER*)parser_arg;
651 size_t tileset_id_parsed = 0;
652
653 /* We don't lock the vector right away, since we might reallocate tile defs
654 * later on, below.
655 */
656
657 /* Try the custom parser. */
658 if(
659 NULL != parser->custom_token_cb &&
660 /* MERROR_PREEMPT is dealt with in cleanup. */
661 MERROR_OK != (retval = parser->custom_token_cb(
662 token, token_sz, parser_arg ))
663 ) {
664 goto cleanup;
665 }
666
667 if( MJSON_PSTATE_OBJECT_VAL != mjson_parser_pstate( &(parser->jparser) ) ) {
668 /* Not a token we have to worry about! */
669 retrotile_parser_match_token( token, token_sz, parser );
670 goto cleanup;
671 }
672
673 if( MTILESTATE_TILES_ID == parser->mstate ) {
674 retrotile_parser_mstate( parser, MTILESTATE_TILES );
675 if( 0 == parser->pass ) {
676 /* Parse tile ID. */
677 tileset_id_parsed = maug_atou32( token, token_sz, 10 );
678 if( tileset_id_parsed > parser->tileset_id_cur ) {
679 parser->tileset_id_cur = tileset_id_parsed;
680 debug_printf(
682 "new highest tile ID: " SIZE_T_FMT, parser->tileset_id_cur );
683 }
684 } else {
685 assert( 0 < mdata_vector_ct( parser->p_tile_defs ) );
686 parser->tileset_id_cur = maug_atou32( token, token_sz, 10 );
687 debug_printf(
689 "next tile ID: " SIZE_T_FMT, parser->tileset_id_cur );
690 }
691 retrotile_parser_mstate( parser, MTILESTATE_TILES );
692
693 } else if( MTILESTATE_TILES_IMAGE == parser->mstate ) {
694 if( 1 == parser->pass ) {
695 /* Need this pass 1 so tile_defs vector is allocated. */
696 mdata_vector_lock( parser->p_tile_defs );
697 tile_def = mdata_vector_get(
698 parser->p_tile_defs, parser->tileset_id_cur,
699 struct RETROTILE_TILE_DEF );
700 assert( NULL != tile_def );
701
702 /* Parse tile image. */
703 if(
706 ) {
707 retroflat_assign_asset_path( tile_def->image_path, token );
708 } else {
709 retroflat_assign_asset_trim_ext( tile_def->image_path, token );
710 }
711
712 debug_printf(
713 RETROTILE_TRACE_LVL, "set tile ID " SIZE_T_FMT " to: %s",
714 parser->tileset_id_cur, tile_def->image_path );
715 }
716 retrotile_parser_mstate( parser, MTILESTATE_TILES );
717
718 } else if( MTILESTATE_TILES_CLASS == parser->mstate ) {
719 if( 1 == parser->pass ) {
720 /* Set the class from the pass 1 so the custom function can use
721 * it on pass 2 even if it's alphabetically out of order.
722 * (Tiled calls it "type" and puts it near the bottom!)
723 *
724 * We can't do this on pass 0 since tiles aren't allocated yet!
725 */
726 mdata_vector_lock( parser->p_tile_defs );
727 tile_def = mdata_vector_get(
728 parser->p_tile_defs, parser->tileset_id_cur,
729 struct RETROTILE_TILE_DEF );
730 assert( NULL != tile_def );
731 assert( 0 == tile_def->tile_class );
732
733 #define RETROTILE_CLASS_TABLE_SET( A, a, i ) \
734 } else if( 0 == strncmp( #a, token, strlen( #a ) + 1 ) ) { \
735 tile_def->tile_class = RETROTILE_CLASS_ ## A; \
736 debug_printf( RETROTILE_TRACE_LVL, \
737 "set tile " SIZE_T_FMT " type: " #a " (%u)", \
738 parser->tileset_id_cur, tile_def->tile_class );
739
740 if( 0 ) {
741 RETROTILE_CLASS_TABLE( RETROTILE_CLASS_TABLE_SET )
742 } else {
743 tile_def->tile_class = RETROTILE_CLASS_TILE;
744 debug_printf( RETROTILE_TRACE_LVL,
745 "set tile " SIZE_T_FMT " type: tile (%u)",
746 parser->tileset_id_cur, tile_def->tile_class );
747 }
748 }
749 retrotile_parser_mstate( parser, MTILESTATE_TILES );
750
751 } else if( MTILESTATE_TILES_PROP_NAME == parser->mstate ) {
752 debug_printf( RETROTILE_TRACE_LVL, "parsing property: %s", token );
754 maug_strncpy( parser->last_prop_name, token, RETROTILE_PROP_NAME_SZ_MAX );
755 retrotile_parser_mstate( parser, MTILESTATE_TILES_PROP );
756
757 } else if( MTILESTATE_TILES_PROP_TYPE == parser->mstate ) {
758 debug_printf( RETROTILE_TRACE_LVL, "property %s is type: %s",
759 parser->last_prop_name, token );
760 parser->last_prop_type = retrotile_parse_prop_type( token, token_sz );
761 retrotile_parser_mstate( parser, MTILESTATE_TILES_PROP );
762
763 } else if( MTILESTATE_TILES_PROP_VAL == parser->mstate ) {
764 /* This would be ideal to be handled in the custom token parser callback.
765 */
766
767 if( 1 == parser->pass ) {
768 mdata_vector_lock( parser->p_tile_defs );
769 tile_def = mdata_vector_get(
770 parser->p_tile_defs, parser->tileset_id_cur,
771 struct RETROTILE_TILE_DEF );
772 assert( NULL != tile_def );
773
774 if( 0 == strncmp( "warp_dest", parser->last_prop_name, 10 ) ) {
775 maug_mzero( tile_def->warp_dest, RETROTILE_NAME_SZ_MAX );
776 maug_strncpy( tile_def->warp_dest, token, RETROTILE_NAME_SZ_MAX );
777 debug_printf( 1, "set tile " SIZE_T_FMT " warp_dest: %s",
778 parser->tileset_id_cur, tile_def->warp_dest );
779
780 } else if( 0 == strncmp( "warp_x", parser->last_prop_name, 7 ) ) {
781 tile_def->warp_x = maug_atos32( token, token_sz );
782 debug_printf( 1, "set tile " SIZE_T_FMT " warp_x: %d",
783 parser->tileset_id_cur, tile_def->warp_x );
784
785 } else if( 0 == strncmp( "warp_y", parser->last_prop_name, 7 ) ) {
786 tile_def->warp_y = maug_atos32( token, token_sz );
787 debug_printf( 1, "set tile " SIZE_T_FMT " warp_y: %d",
788 parser->tileset_id_cur, tile_def->warp_y );
789
790 }
791 }
792
794 retrotile_parser_mstate( parser, MTILESTATE_TILES_PROP );
795
796 } else if( MTILESTATE_NAME == parser->mstate ) {
797 maug_strncpy( parser->tileset_name, token, RETROTILE_NAME_SZ_MAX );
798 debug_printf(
799 RETROTILE_TRACE_LVL, "tileset name: %s", parser->tileset_name );
800
801 retrotile_parser_mstate( parser, 0 );
802
803 } else if( MTILESTATE_TPROP_NAME == parser->mstate ) {
805 maug_strncpy(
807 parser->last_prop_name_sz = token_sz;
808 retrotile_parser_mstate( parser, MTILESTATE_TPROP );
809
810 } else if( MTILESTATE_TPROP_TYPE == parser->mstate ) {
811 debug_printf( RETROTILE_TRACE_LVL, "property %s is type: %s",
812 parser->last_prop_name, token );
813 parser->last_prop_type = retrotile_parse_prop_type( token, token_sz );
814 retrotile_parser_mstate( parser, MTILESTATE_TPROP );
815
816 } else if( MTILESTATE_TPROP_VAL == parser->mstate ) {
817 /* This should be handled in the custom_cb above! */
819 retrotile_parser_mstate( parser, MTILESTATE_TPROP );
820 }
821
822cleanup:
823
824 mdata_vector_unlock( parser->p_tile_defs );
825
826 if( MERROR_PREEMPT == retval ) {
827 /* Reset custom callback retval. */
828 retval = MERROR_OK;
829 }
830
831 return retval;
832}
833
834/* === */
835
836MERROR_RETVAL retrotile_parser_parse_token(
837 const char* token, size_t token_sz, void* parser_arg
838) {
839 MERROR_RETVAL retval = MERROR_OK;
840 struct RETROTILE_LAYER* tiles_layer = NULL;
841 struct RETROTILE_PARSER* parser = (struct RETROTILE_PARSER*)parser_arg;
842 retroflat_tile_t* tiles = NULL;
843
844 /* Try the custom parser. */
845 if(
846 NULL != parser->custom_token_cb &&
847 /* MERROR_PREEMPT is dealt with in cleanup. */
848 MERROR_OK != (retval = parser->custom_token_cb(
849 token, token_sz, parser_arg ))
850 ) {
851 goto cleanup;
852 }
853
854 if( MJSON_PSTATE_LIST == mjson_parser_pstate( &(parser->jparser) ) ) {
855 /* We're parsing a list. What lists do we care about? */
856
857 /* Please note that this is for new tokens encountered inside of a list.
858 * For dealing with the closing of a list, see
859 * retrotile_json_close_list()!
860 */
861
862 if(
863 1 == parser->pass &&
864 MTILESTATE_LAYER_DATA == parser->mstate
865 ) {
866 /* This is a list of layer data... AKA tiles! */
867
868 /*
869 assert( NULL != *(parser->p_tilemap_h) );
870 maug_mlock( *(parser->p_tilemap_h), tilemap );
871 maug_cleanup_if_null_alloc( struct RETROTILE*, tilemap );
872 */
873
874 /*
875 debug_printf( RETROTILE_TRACE_LVL,
876 "selecting layer " SIZE_T_FMT "...",
877 parser->pass_layer_iter );
878 */
879 assert( NULL != parser->t );
880 tiles_layer = retrotile_get_layer_p(
881 parser->t, parser->pass_layer_iter );
882 assert( NULL != tiles_layer );
883
884 /* Apply the class parsed previously. */
885 tiles_layer->layer_class = parser->layer_class;
886
887 tiles = retrotile_get_tiles_p( tiles_layer );
888
889 /* Parse tilemap tile. */
890 /*
891 debug_printf( RETROTILE_TRACE_LVL,
892 "layer " SIZE_T_FMT " tile: " SIZE_T_FMT " (tiles: %p)",
893 parser->pass_layer_iter, parser->layer_tile_iter,
894 tiles );
895 */
896 assert( NULL != token );
897 assert( NULL != parser );
898 assert( NULL != tiles );
899
900 if(
901 parser->layer_tile_iter >=
902 parser->t->tiles_w * parser->t->tiles_h
903 ) {
904 /* Tile is outside of layer! */
905 error_printf(
906 "tile " SIZE_T_FMT " outside of layer tile buffer size "
907 SIZE_T_FMT "!",
908 parser->layer_tile_iter,
909 parser->t->tiles_w * parser->t->tiles_h );
910 retval = MERROR_OVERFLOW;
911 goto cleanup;
912 }
913
914 assert( 0 == tiles[parser->layer_tile_iter] );
915
916 tiles[parser->layer_tile_iter] = atoi( token );
917
918 }
919 parser->layer_tile_iter++;
920 goto cleanup;
921
922 } else if(
923 MJSON_PSTATE_OBJECT_VAL ==
924 mjson_parser_pstate( &(parser->jparser) )
925 ) {
926
927 /* Pay attention to the retrotile_parser_mstate() calls here. We don't
928 * have a stack of states, and we really only track a few we're
929 * interested in. Even if we don't process their data here, we still
930 * use retrotile_parser_mstate() to move the parser mstate back up
931 * to the parent mstate when we're done with it. This kind of "fakes" a
932 * stack (in a well-formed file, of course).
933 */
934
935 if( MTILESTATE_TILESETS_FGID == parser->mstate ) {
936 if( 1 == parser->pass ) {
937 parser->t->tileset_fgid = maug_atou32( token, token_sz, 10 );
938 debug_printf(
939 RETROTILE_TRACE_LVL, "tileset FGID set to: " SIZE_T_FMT,
940 parser->t->tileset_fgid );
941 }
942 retrotile_parser_mstate( parser, MTILESTATE_TILESETS );
943
944 } else if( MTILESTATE_TILESETS_SRC == parser->mstate ) {
945 if( 1 == parser->pass ) {
946 debug_printf( RETROTILE_TRACE_LVL, "parsing %s...", token );
947 parser->tj_parse_cb(
948 parser->dirname, token, NULL, parser->p_tile_defs,
949 parser->wait_cb, parser->wait_data,
950 parser->custom_token_cb, parser->custom_token_cb_data,
951 parser->passes_max, parser->flags );
952 }
953 retrotile_parser_mstate( parser, MTILESTATE_TILESETS );
954
955 } else if( MTILESTATE_HEIGHT == parser->mstate ) {
956 if( 0 == parser->pass ) {
957 /* Need this to allocate on pass 1. */
958 parser->tiles_h = atoi( token );
959 debug_printf(
960 RETROTILE_TRACE_LVL, "tilemap height: " SIZE_T_FMT,
961 parser->tiles_h );
962 }
963 retrotile_parser_mstate( parser, MTILESTATE_NONE );
964
965 } else if( MTILESTATE_WIDTH == parser->mstate ) {
966 if( 0 == parser->pass ) {
967 /* Need this to allocate on pass 1. */
968 parser->tiles_w = atoi( token );
969 debug_printf(
970 RETROTILE_TRACE_LVL, "tilemap width: " SIZE_T_FMT,
971 parser->tiles_w );
972 }
973 retrotile_parser_mstate( parser, MTILESTATE_NONE );
974
975 } else if( MTILESTATE_LAYER_NAME == parser->mstate ) {
976 /* TODO: Store */
977 retrotile_parser_mstate( parser, MTILESTATE_LAYER );
978
979 } else if( MTILESTATE_LAYER_CLASS == parser->mstate ) {
980 /* TODO: Use the class table to create layers for e.g. crops, items. */
981 if( 0 == strncmp( "mobile", token, 7 ) ) {
982 debug_printf( RETROTILE_TRACE_LVL,
983 "layer " SIZE_T_FMT " type: mobile",
984 parser->pass_layer_iter );
985 parser->layer_class = RETROTILE_CLASS_MOBILE;
986 } else {
987 debug_printf( RETROTILE_TRACE_LVL,
988 "layer " SIZE_T_FMT " type: tile",
989 parser->pass_layer_iter );
990 parser->layer_class = RETROTILE_CLASS_TILE;
991 }
992 retrotile_parser_mstate( parser, MTILESTATE_LAYER );
993
994 } else if( MTILESTATE_PROP_NAME == parser->mstate ) {
996 maug_strncpy(
998 parser->last_prop_name_sz = token_sz;
999 retrotile_parser_mstate( parser, MTILESTATE_PROP );
1000
1001 } else if( MTILESTATE_PROP_TYPE == parser->mstate ) {
1002 debug_printf( RETROTILE_TRACE_LVL, "property %s is type: %s",
1003 parser->last_prop_name, token );
1004 parser->last_prop_type = retrotile_parse_prop_type( token, token_sz );
1005 retrotile_parser_mstate( parser, MTILESTATE_PROP );
1006
1007 } else if( MTILESTATE_PROP_VAL == parser->mstate ) {
1008 /* We're dealing with properties of the tilemap. */
1009 if( 0 == strncmp( parser->last_prop_name, "name", 5 ) ) {
1010 debug_printf( RETROTILE_TRACE_LVL, "tilemap name: %s", token );
1011 maug_strncpy( parser->tilemap_name, token, RETROTILE_NAME_SZ_MAX );
1012 }
1013
1015 retrotile_parser_mstate( parser, MTILESTATE_PROP );
1016 }
1017 goto cleanup;
1018 }
1019
1020 retrotile_parser_match_token( token, token_sz, parser );
1021
1022cleanup:
1023
1024 return retval;
1025}
1026
1027/* === */
1028
1029MERROR_RETVAL retrotile_json_close_list( void* parg ) {
1030 struct RETROTILE_PARSER* parser = (struct RETROTILE_PARSER*)parg;
1031
1032 if( MTILESTATE_LAYER_DATA == parser->mstate ) {
1033 assert( RETROTILE_PARSER_MODE_MAP == parser->mode );
1034 debug_printf( RETROTILE_TRACE_LVL, "parsed " SIZE_T_FMT " tiles!",
1035 parser->layer_tile_iter );
1036 assert( parser->layer_tile_iter > 0 );
1037 retrotile_parser_mstate( parser, MTILESTATE_LAYER );
1038
1039 } else if( MTILESTATE_LAYERS == parser->mstate ) {
1040 assert( RETROTILE_PARSER_MODE_MAP == parser->mode );
1041 retrotile_parser_mstate( parser, MTILESTATE_NONE );
1042
1043 } else if( MTILESTATE_TILESETS == parser->mstate ) {
1044 assert( RETROTILE_PARSER_MODE_MAP == parser->mode );
1045 retrotile_parser_mstate( parser, MTILESTATE_NONE );
1046
1047 } else if( MTILESTATE_TILES_PROP == parser->mstate ) {
1048 assert( RETROTILE_PARSER_MODE_DEFS == parser->mode );
1049 retrotile_parser_mstate( parser, MTILESTATE_TILES );
1050
1051 } else if( MTILESTATE_TILES == parser->mstate ) {
1052 assert( RETROTILE_PARSER_MODE_DEFS == parser->mode );
1053 retrotile_parser_mstate( parser, MTILESTATE_NONE );
1054
1055 } else if( MTILESTATE_PROP == parser->mstate ) {
1056 retrotile_parser_mstate( parser, MTILESTATE_NONE );
1057
1058 } else if( MTILESTATE_TPROP == parser->mstate ) {
1059 retrotile_parser_mstate( parser, MTILESTATE_NONE );
1060 }
1061
1062 return MERROR_OK;
1063}
1064
1065/* === */
1066
1067MERROR_RETVAL retrotile_json_open_obj( void* parg ) {
1068 struct RETROTILE_PARSER* parser = (struct RETROTILE_PARSER*)parg;
1069
1070 if( MTILESTATE_LAYERS == parser->mstate ) {
1071 assert( RETROTILE_PARSER_MODE_MAP == parser->mode );
1072 /* Reset on open so count is retained for allocating after first
1073 * pass. */
1074 parser->layer_tile_iter = 0;
1075 retrotile_parser_mstate( parser, MTILESTATE_LAYER );
1076 }
1077
1078 return MERROR_OK;
1079}
1080
1081/* === */
1082
1083MERROR_RETVAL retrotile_json_close_obj( void* parg ) {
1084 struct RETROTILE_PARSER* parser = (struct RETROTILE_PARSER*)parg;
1085
1086 if( MTILESTATE_LAYER == parser->mstate ) {
1087 assert( RETROTILE_PARSER_MODE_MAP == parser->mode );
1088 debug_printf( RETROTILE_TRACE_LVL,
1089 "incrementing pass layer to " SIZE_T_FMT " after " SIZE_T_FMT
1090 " tiles...",
1091 parser->pass_layer_iter + 1, parser->layer_tile_iter );
1092 parser->pass_layer_iter++;
1093 retrotile_parser_mstate( parser, MTILESTATE_LAYERS );
1094
1095 } else if( MTILESTATE_GRID == parser->mstate ) {
1096 retrotile_parser_mstate( parser, MTILESTATE_NONE );
1097 }
1098
1099 return MERROR_OK;
1100}
1101
1102/* === */
1103
1104int retrotile_parse_prop_type( const char* token, size_t token_sz ) {
1105 int out = RETROTILE_PROP_TYPE_OTHER;
1106
1107 if( 0 == strncmp( "string", token, 7 ) ) {
1109 } else if( 0 == strncmp( "file", token, 5 ) ) {
1111 } else if( 0 == strncmp( "int", token, 4 ) ) {
1113 }
1114
1115 return out;
1116}
1117
1118/* === */
1119
1120mfix_t retrotile_static_rotation_from_dir( const char* dir ) {
1121 mfix_t static_rotate_out = 0;
1122
1123 if( NULL == dir ) {
1124 return 0;
1125 }
1126
1127 /* Translate dir into rotation value. */
1128 if( 0 == strncmp( dir, "NW", 2 ) ) {
1129 static_rotate_out = mfix_from_f( 90.0f );
1130 } else if( 0 == strncmp( dir, "SW", 2 ) ) {
1131 static_rotate_out = mfix_from_f( 180.0f );
1132 } else if( 0 == strncmp( dir, "SE", 2 ) ) {
1133 static_rotate_out = mfix_from_f( 270.0f );
1134
1135 } else if( 0 == strncmp( dir, "W", 1 ) ) {
1136 static_rotate_out = mfix_from_f( 90.0f );
1137 } else if( 0 == strncmp( dir, "S", 1 ) ) {
1138 static_rotate_out = mfix_from_f( 180.0f );
1139 } else if( 0 == strncmp( dir, "E", 1 ) ) {
1140 static_rotate_out = mfix_from_f( 270.0f );
1141
1142 } else {
1143 static_rotate_out = 0;
1144 }
1145
1146 return static_rotate_out;
1147}
1148
1149/* === */
1150
1152 const char* dirname, const char* filename, MAUG_MHANDLE* p_tilemap_h,
1153 struct MDATA_VECTOR* p_tile_defs, mparser_wait_cb_t wait_cb, void* wait_data,
1154 mparser_parse_token_cb token_cb, void* token_cb_data, uint8_t passes,
1155 uint8_t flags
1156) {
1157 MERROR_RETVAL retval = MERROR_OK;
1158 MAUG_MHANDLE parser_h = (MAUG_MHANDLE)NULL;
1159 struct RETROTILE_PARSER* parser = NULL;
1160 char filename_path[RETROFLAT_PATH_MAX];
1161 mfile_t tile_file;
1162 char c;
1163 char* filename_ext = NULL;
1164
1165 /* Initialize parser. */
1166 parser_h = maug_malloc( 1, sizeof( struct RETROTILE_PARSER ) );
1167 maug_cleanup_if_null_alloc( MAUG_MHANDLE, parser_h );
1168
1169 maug_mlock( parser_h, parser );
1170 maug_cleanup_if_null_alloc( struct RETROTILE_PARSER*, parser );
1171 maug_mzero( parser, sizeof( struct RETROTILE_PARSER ) );
1172
1173 parser->flags = flags;
1174 parser->tj_parse_cb = retrotile_parse_json_file;
1175 parser->custom_token_cb = token_cb;
1176 parser->custom_token_cb_data = token_cb_data;
1177
1178 maug_strncpy( parser->dirname, dirname, RETROFLAT_PATH_MAX );
1179
1180 if( 2 > passes ) {
1181 debug_printf( RETROTILE_TRACE_LVL,
1182 "increasing parse passes to minimum, 2!" );
1183 passes = 2;
1184 }
1185
1186 parser->passes_max = passes;
1187
1188 /* Setup filename path. */
1189 memset( filename_path, '\0', RETROFLAT_PATH_MAX );
1190 /* TODO: Configurable path. */
1191 maug_snprintf(
1192 filename_path, RETROFLAT_PATH_MAX, "%s/%s", dirname, filename );
1193
1194 debug_printf( RETROTILE_TRACE_LVL, "opening %s...", filename_path );
1195
1196 retval = mfile_open_read( filename_path, &tile_file );
1197 maug_cleanup_if_not_ok();
1198
1199 /* Parse JSON and react to state. */
1200 for( parser->pass = 0 ; passes > parser->pass ; parser->pass++ ) {
1201 debug_printf( RETROTILE_TRACE_LVL, "beginning pass #%u...",
1202 parser->pass );
1203
1204 /* Reset tilemap parser. */
1205 parser->mstate = 0;
1206
1207 /* Reset JSON parser. */
1208 maug_mzero( &(parser->jparser.base), sizeof( struct MJSON_PARSER ) );
1209
1210 parser->wait_cb = wait_cb;
1211 parser->wait_data = wait_data;
1212 parser->jparser.base.wait_cb = wait_cb;
1213 parser->jparser.base.wait_data = wait_data;
1214
1215 /* Figure out if we're parsing a .tmj or .tsj. */
1216 filename_ext = maug_strrchr( filename, '.' );
1217 if( NULL == filename_ext ) {
1218 error_printf( "could not parse filename extension!" );
1219 retval = MERROR_FILE;
1220 goto cleanup;
1221 }
1222 if( 's' == filename_ext[2] ) {
1223 debug_printf( RETROTILE_TRACE_LVL,
1224 "(tile_defs pass %u)", parser->pass );
1226 parser->jparser.token_parser = retrotile_parser_parse_tiledef_token;
1227 parser->jparser.token_parser_arg = parser;
1228 parser->jparser.close_list = retrotile_json_close_list;
1229 parser->jparser.close_list_arg = parser;
1230 parser->jparser.close_obj = retrotile_json_close_obj;
1231 parser->jparser.close_obj_arg = parser;
1232 /*
1233 parser->jparser.base.close_val = retrotile_json_close_val;
1234 parser->jparser.base.close_val_arg = parser;
1235 */
1236 parser->p_tile_defs = p_tile_defs;
1237
1238 assert( NULL != p_tile_defs );
1239 if( 1 == parser->pass ) {
1240 /* Allocate tile defs based on highest tile ID found on
1241 * first pass.
1242 */
1243 assert( 0 < parser->tileset_id_cur );
1244 debug_printf(
1245 RETROTILE_TRACE_LVL, "allocating " SIZE_T_FMT " tile defs...",
1246 parser->tileset_id_cur + 1 );
1247 mdata_vector_fill(
1248 parser->p_tile_defs, parser->tileset_id_cur + 1,
1249 sizeof( struct RETROTILE_TILE_DEF ) );
1250 }
1251 } else {
1252 debug_printf( RETROTILE_TRACE_LVL, "(tilemap pass %u)", parser->pass );
1254
1255 parser->jparser.close_list = retrotile_json_close_list;
1256 parser->jparser.close_list_arg = parser;
1257 parser->jparser.open_obj = retrotile_json_open_obj;
1258 parser->jparser.open_obj_arg = parser;
1259 parser->jparser.close_obj = retrotile_json_close_obj;
1260 parser->jparser.close_obj_arg = parser;
1261 parser->jparser.token_parser = retrotile_parser_parse_token;
1262 parser->jparser.token_parser_arg = parser;
1263 parser->p_tile_defs = p_tile_defs;
1264
1265 assert( NULL != p_tilemap_h );
1266 assert( NULL != p_tile_defs );
1267 if( 1 == parser->pass ) {
1268 /* Allocate tiles for the new layers. */
1269 retval = retrotile_alloc(
1270 p_tilemap_h, parser->tiles_w, parser->tiles_h,
1271 parser->pass_layer_iter, parser->tilemap_name,
1272 parser->tileset_name );
1273 maug_cleanup_if_not_ok();
1274 maug_mlock( *p_tilemap_h, parser->t );
1275 }
1276 parser->pass_layer_iter = 0;
1277 }
1278
1279 while( tile_file.has_bytes( &tile_file ) ) {
1280 tile_file.read_int( &tile_file, (uint8_t*)&c, 1, 0 );
1281 debug_printf( RETROTILE_TRACE_CHARS, "%c", c );
1282 retval = mjson_parse_c( &(parser->jparser), c );
1283 if( MERROR_OK != retval ) {
1284 error_printf( "error parsing JSON!" );
1285 goto cleanup;
1286 }
1287 }
1288
1289 tile_file.seek( &tile_file, 0 );
1290
1291 filename_ext = maug_strrchr( filename, '.' );
1292 if( NULL == filename_ext ) {
1293 error_printf( "could not parse filename extension!" );
1294 retval = MERROR_FILE;
1295 goto cleanup;
1296 }
1297 if( 's' != filename_ext[2] ) {
1298 debug_printf( RETROTILE_TRACE_LVL,
1299 "pass %u found " SIZE_T_FMT " layers",
1300 parser->pass, parser->pass_layer_iter );
1301 }
1302 }
1303
1304 debug_printf(
1305 RETROTILE_TRACE_LVL, "finished parsing %s, retval: %d",
1306 filename_path, retval );
1307
1308cleanup:
1309
1310 if( NULL != parser ) {
1311 if( NULL != parser->t ) {
1312 maug_munlock( *p_tilemap_h, parser->t );
1313 }
1314 maug_munlock( parser_h, parser );
1315 }
1316
1317 if( NULL != parser_h ) {
1318 maug_mfree( parser_h );
1319 }
1320
1321 return retval;
1322}
1323
1324/* === */
1325
1326static retroflat_tile_t retrotile_gen_diamond_square_rand(
1327 retroflat_tile_t min_z, retroflat_tile_t max_z, uint32_t tuning,
1328 retroflat_tile_t top_left_z
1329) {
1330 retroflat_tile_t avg = top_left_z;
1331
1332 if( 8 > rand() % 10 ) {
1333 /* avg = min_z + (rand() % (max_z - min_z)); */
1334 avg -= (min_z / tuning) + (rand() % (max_z / tuning));
1335 /* } else {
1336 avg += (min_z / 10) + (rand() % (max_z / 10)); */
1337 }
1338
1339 /* Clamp the result. */
1340
1341 if( min_z > avg ) {
1342 avg = min_z;
1343 }
1344
1345 if( max_z < avg ) {
1346 avg = max_z;
1347 }
1348
1349 return avg;
1350}
1351
1352/* === */
1353
1354static void retrotile_gen_diamond_square_corners(
1355 int16_t corners_x[2][2], int16_t corners_y[2][2],
1357 uint32_t tuning, struct RETROTILE_DATA_DS* data_ds,
1358 struct RETROTILE_LAYER* layer, struct RETROTILE* t
1359) {
1360 int16_t iter_x = 0,
1361 iter_y = 0;
1362 retroflat_tile_t* tile_iter = NULL;
1363 retroflat_tile_t top_left_z = 0;
1364
1365 /* Generate missing corner data. Loop through X/Y coords stored in
1366 * corners_x/corners_y convenience arrays.
1367 */
1368 for( iter_y = 0 ; iter_y < 2 ; iter_y++ ) {
1369 for( iter_x = 0 ; iter_x < 2 ; iter_x++ ) {
1370
1371 /* Make sure corner X is in bounds. */
1372 corners_x[iter_x][iter_y] =
1373 (data_ds->sect_x - 1) + (iter_x * data_ds->sect_w);
1374 if( 0 > corners_x[iter_x][iter_y] ) {
1375 corners_x[iter_x][iter_y] += 1;
1376 }
1377
1378 /* Make sure corner Y is in bounds. */
1379 corners_y[iter_x][iter_y] =
1380 (data_ds->sect_y - 1) + (iter_y * data_ds->sect_h);
1381 if( 0 > corners_y[iter_x][iter_y] ) {
1382 corners_y[iter_x][iter_y] += 1;
1383 }
1384 }
1385 }
1386
1387 /* Should be handled by the check above. */
1388 assert( 0 <= corners_x[0][0] );
1389 assert( 0 <= corners_y[0][0] );
1390 assert( t->tiles_w > corners_x[0][0] );
1391 assert( t->tiles_h > corners_y[0][0] );
1392
1393 /* Grab the top-left Z-value to anchor generated corners to. */
1394 top_left_z = retrotile_get_tile(
1395 t, layer,
1396 corners_x[0][0],
1397 corners_y[0][0] );
1398
1399 if( 0 > top_left_z ) {
1400 retrotile_get_tile(
1401 t, layer,
1402 corners_x[0][0] >= 0 ? corners_x[0][0] : 0,
1403 corners_y[0][0] >= 0 ? corners_y[0][0] : 0 ) = max_z;
1404 top_left_z = max_z;
1405 }
1406
1407 /* Fill in missing corners. */
1408 for( iter_y = 0 ; iter_y < 2 ; iter_y++ ) {
1409 for( iter_x = 0 ; iter_x < 2 ; iter_x++ ) {
1410 /* Grab a pointer to the corner so we can modify it easily. */
1411 tile_iter = &(retrotile_get_tile(
1412 t, layer,
1413 corners_x[iter_x][iter_y],
1414 corners_y[iter_x][iter_y] ));
1415
1416 /* Check if corner is already filled in. */
1417 if( -1 != *tile_iter ) {
1418 debug_printf(
1419 RETROTILE_TRACE_LVL, "corner coord %d x %d present: %d",
1420 corners_x[iter_x][iter_y], corners_y[iter_x][iter_y],
1421 retrotile_get_tile(
1422 t, layer,
1423 corners_x[iter_x][iter_y],
1424 corners_y[iter_x][iter_y] ) );
1425 continue;
1426 }
1427
1428 /* Generate a new value for this corner. */
1429 *tile_iter = retrotile_gen_diamond_square_rand(
1430 min_z, max_z, tuning, top_left_z );
1431
1432 debug_printf( RETROTILE_TRACE_LVL,
1433 "missing corner coord %d x %d: %d",
1434 corners_x[iter_x][iter_y], corners_y[iter_x][iter_y],
1435 *tile_iter );
1436 }
1437 }
1438}
1439
1440/* === */
1441
1442static retroflat_tile_t retrotile_gen_diamond_square_avg(
1443 int16_t corners_x[2][2], int16_t corners_y[2][2],
1444 struct RETROTILE* t, struct RETROTILE_LAYER* layer
1445) {
1446 retroflat_tile_t* tile_iter = NULL;
1447 int16_t iter_x = 0,
1448 iter_y = 0;
1449 retroflat_tile_t avg = 0;
1450
1451 /* Average corner data. */
1452 for( iter_y = 0 ; 2 > iter_y ; iter_y++ ) {
1453 for( iter_x = 0 ; 2 > iter_x ; iter_x++ ) {
1454 tile_iter = &(retrotile_get_tile(
1455 t, layer,
1456 corners_x[iter_x][iter_y],
1457 corners_y[iter_x][iter_y] ));
1458 assert( -1 != *tile_iter );
1459 /*
1460 debug_printf(
1461 RETROTILE_TRACE_LVL, "%d: adding from coords %d x %d: %d",
1462 iter_depth,
1463 corners_x[iter_x][iter_y], corners_y[iter_x][iter_y],
1464 *tile_iter ); */
1465 avg += *tile_iter;
1466 }
1467 }
1468
1469 /* TODO: Use right shift? */
1470 avg /= 4;
1471
1472 return avg;
1473}
1474
1475/* === */
1476
1478 struct RETROTILE* t, retroflat_tile_t min_z, retroflat_tile_t max_z,
1479 uint32_t tuning, size_t layer_idx, uint8_t flags, void* data,
1480 retrotile_ani_cb animation_cb, void* animation_cb_data
1481) {
1482 int16_t iter_x = 0,
1483 iter_y = 0,
1484 iter_depth = 0;
1485 int16_t corners_x[2][2];
1486 int16_t corners_y[2][2];
1487 int32_t avg = 0;
1488 /* size_t tile_idx = 0; */
1489 struct RETROTILE_DATA_DS data_ds_sub;
1490 MAUG_MHANDLE data_ds_h = NULL;
1491 struct RETROTILE_DATA_DS* data_ds = NULL;
1492 /* retroflat_tile_t* tiles = NULL; */
1493 MERROR_RETVAL retval = MERROR_OK;
1494 struct RETROTILE_LAYER* layer = NULL;
1495 retroflat_tile_t* tile_iter = NULL;
1496 uint8_t free_ds_data = 0;
1497
1498 /*
1499 maug_mlock( t->tiles, tiles );
1500 maug_cleanup_if_null_alloc( struct GRIDCITY_TILE*, tiles );
1501 */
1502
1503 #define _retrotile_ds_update_statistics( data, tile ) \
1504 /* Gather statistics. */ \
1505 if( (data)->highest_generated < (tile) && 32767 > (tile) ) { \
1506 (data)->highest_generated = (tile); \
1507 } \
1508 if( (data)->lowest_generated > (tile) && 0 < (tile) ) { \
1509 (data)->lowest_generated = (tile); \
1510 }
1511
1512 layer = retrotile_get_layer_p( t, layer_idx );
1513
1514 if(
1515 NULL == data ||
1517 ) {
1518 /* This must be the first call, so initialize or allocate a new struct.
1519 */
1520 if( NULL == data ) {
1521 /* An internal struct needs to be allocated before initialization. */
1522 data_ds_h = maug_malloc( 1, sizeof( struct RETROTILE_DATA_DS ) );
1523 maug_cleanup_if_null_alloc( MAUG_MHANDLE, data_ds_h );
1524 free_ds_data = 1;
1525 maug_mlock( data_ds_h, data_ds );
1526 maug_cleanup_if_null_alloc( struct RETROTILE_DATA_DS*, data_ds );
1527 } else {
1528 data_ds = (struct RETROTILE_DATA_DS*)data;
1529 }
1530
1531 /* Initialize passed tilemap while we're handling first call stuff. */
1532 memset( retrotile_get_tiles_p( layer ), -1,
1533 t->tiles_w * t->tiles_h * sizeof( retroflat_tile_t ) );
1534
1535 /* Initialize DS struct from tilemap properties. */
1536 maug_mzero( data_ds, sizeof( struct RETROTILE_DATA_DS ) );
1537 data_ds->sect_w = t->tiles_w;
1538 data_ds->sect_h = t->tiles_h;
1539 data_ds->sect_w_half = data_ds->sect_w >> 1;
1540 data_ds->sect_h_half = data_ds->sect_h >> 1;
1541 data_ds->lowest_generated = 32767;
1542
1543 /* Disable this flag for subsequent calls. */
1545 } else {
1546 data_ds = (struct RETROTILE_DATA_DS*)data;
1547 }
1548 assert( NULL != data_ds );
1549
1550 /* Trivial case; end recursion. */
1551 if( 0 == data_ds->sect_w ) {
1552 debug_printf(
1553 RETROTILE_TRACE_LVL, "%d return: null sector", iter_depth );
1554 goto cleanup;
1555 }
1556
1557 if(
1558 data_ds->sect_x + data_ds->sect_w > t->tiles_w ||
1559 data_ds->sect_y + data_ds->sect_h > t->tiles_h
1560 ) {
1561 debug_printf(
1562 RETROTILE_TRACE_LVL, "%d return: overflow sector", iter_depth );
1563 goto cleanup;
1564 }
1565
1566 iter_depth = t->tiles_w / data_ds->sect_w;
1567
1568 /* Generate/grab corners before averaging them! */
1569 retrotile_gen_diamond_square_corners(
1570 corners_x, corners_y, min_z, max_z, tuning, data_ds, layer, t );
1571
1572 if( 2 == data_ds->sect_w || 2 == data_ds->sect_h ) {
1573 /* Nothing to average, this sector is just corners! */
1574 debug_printf(
1576 "%d return: reached innermost point", iter_depth );
1577 goto cleanup; /* Skip further descent regardless. */
1578 }
1579
1580 avg =
1581 retrotile_gen_diamond_square_avg( corners_x, corners_y, t, layer );
1582
1583 debug_printf( RETROTILE_TRACE_LVL, "avg: " S32_FMT, avg );
1584
1585 tile_iter = &(retrotile_get_tile(
1586 t, layer,
1587 data_ds->sect_x + data_ds->sect_w_half,
1588 data_ds->sect_y + data_ds->sect_h_half ));
1589 if( -1 != *tile_iter ) {
1590 debug_printf( RETROTILE_TRACE_LVL, "avg already present at %d x %d!",
1591 data_ds->sect_x + data_ds->sect_w_half,
1592 data_ds->sect_y + data_ds->sect_h_half );
1593 goto cleanup;
1594 }
1595 *tile_iter = avg;
1596 _retrotile_ds_update_statistics( data_ds, avg );
1597
1598 /* assert( 0 <= tiles[tile_idx].terrain );
1599
1600 maug_munlock( city->tiles, tiles );
1601 tiles = NULL; */
1602
1603 /* Recurse into subsectors. */
1604 for(
1605 iter_y = data_ds->sect_y ;
1606 iter_y < (data_ds->sect_y + data_ds->sect_h) ;
1607 iter_y++
1608 ) {
1609 for(
1610 iter_x = data_ds->sect_x ;
1611 iter_x < (data_ds->sect_x + data_ds->sect_w) ;
1612 iter_x++
1613 ) {
1614 data_ds_sub.sect_x = data_ds->sect_x + iter_x;
1615
1616 data_ds_sub.sect_y = data_ds->sect_y + iter_y;
1617
1618 data_ds_sub.sect_w = data_ds->sect_w_half;
1619 data_ds_sub.sect_h = data_ds->sect_h_half;
1620 data_ds_sub.sect_w_half = data_ds_sub.sect_w >> 1;
1621 data_ds_sub.sect_h_half = data_ds_sub.sect_h >> 1;
1622 data_ds_sub.lowest_generated = 32767;
1623 data_ds_sub.highest_generated = 0;
1624
1625 debug_printf(
1626 RETROTILE_TRACE_LVL, "%d: child sector at %d x %d, %d wide",
1627 iter_depth,
1628 data_ds_sub.sect_x, data_ds_sub.sect_y, data_ds_sub.sect_w );
1629
1631 t, min_z, max_z, tuning, layer_idx, flags, &data_ds_sub,
1632 animation_cb, animation_cb_data );
1633 maug_cleanup_if_not_ok();
1634
1635 _retrotile_ds_update_statistics(
1636 data_ds, data_ds_sub.highest_generated );
1637 _retrotile_ds_update_statistics(
1638 data_ds, data_ds_sub.lowest_generated );
1639 }
1640 }
1641
1642 if(
1643 data_ds->sect_w == t->tiles_w >> 1 &&
1644 NULL != animation_cb
1645 ) {
1646 retval = animation_cb( animation_cb_data, iter_y );
1647 maug_cleanup_if_not_ok();
1648 }
1649
1650 debug_printf(
1651 RETROTILE_TRACE_LVL, "%d return: all sectors complete", iter_depth );
1652
1653cleanup:
1654
1655 if( free_ds_data && NULL != data_ds ) {
1656 maug_munlock( data_ds_h, data_ds );
1657 }
1658
1659 if( free_ds_data && NULL != data_ds_h ) {
1660 maug_mfree( data_ds_h );
1661 }
1662
1663 return retval;
1664}
1665
1666/* === */
1667
1669 struct RETROTILE* t, retroflat_tile_t min_z, retroflat_tile_t max_z,
1670 uint32_t tuning, size_t layer_idx, uint8_t flags, void* data,
1671 retrotile_ani_cb animation_cb, void* animation_cb_data
1672) {
1673 size_t x = 0,
1674 y = 0;
1675 int16_t offset_x = 0,
1676 offset_y = 0,
1677 finished = 0;
1678 MERROR_RETVAL retval = MERROR_OK;
1679 struct RETROTILE_LAYER* layer = NULL;
1680 int16_t spb = RETROTILE_VORONOI_DEFAULT_SPB;
1681 int16_t drift = RETROTILE_VORONOI_DEFAULT_DRIFT;
1682 MAUG_MHANDLE temp_grid_h = (MAUG_MHANDLE)NULL;
1683 retroflat_tile_t* temp_grid = NULL;
1684 retroflat_tile_t* tiles = NULL;
1685 /* Only use 4 cardinal directions. */
1686 int8_t side_iter = 0;
1687
1688 layer = retrotile_get_layer_p( t, 0 );
1689
1690 tiles = retrotile_get_tiles_p( layer );
1691
1692 /* Initialize grid to empty. */
1693 memset( tiles, -1,
1694 t->tiles_w * t->tiles_h * sizeof( retroflat_tile_t ) );
1695
1696 /* Generate the initial sector starting points. */
1697 for( y = 0 ; t->tiles_w > y ; y += spb ) {
1698 for( x = 0 ; t->tiles_w > x ; x += spb ) {
1699 offset_x = x + ((drift * -1) + (rand() % drift));
1700 offset_y = y + ((drift * -1) + (rand() % drift));
1701
1702 /* Clamp sector offsets onto map borders. */
1703 if( 0 > offset_x ) {
1704 offset_x = 0;
1705 }
1706 if( offset_x >= t->tiles_w ) {
1707 offset_x = t->tiles_w - 1;
1708 }
1709 if( 0 > offset_y ) {
1710 offset_y = 0;
1711 }
1712 if( offset_y >= t->tiles_h ) {
1713 offset_y = t->tiles_h - 1;
1714 }
1715
1716 retrotile_get_tile( t, layer, offset_x, offset_y ) =
1717 min_z + (rand() % max_z);
1718 }
1719 }
1720
1721 temp_grid_h = maug_malloc(
1722 sizeof( retroflat_tile_t ), t->tiles_w * t->tiles_h );
1723 maug_cleanup_if_null_alloc( MAUG_MHANDLE, temp_grid_h );
1724
1725 maug_mlock( temp_grid_h, temp_grid );
1726 maug_cleanup_if_null_alloc( retroflat_tile_t*, temp_grid );
1727
1728 /* Grow the sector starting points. */
1729 while( !finished ) {
1730 if( NULL != animation_cb ) {
1731 retval = animation_cb( animation_cb_data, -1 );
1732 maug_cleanup_if_not_ok();
1733 }
1734
1735 /* Prepare sampling grid so we're working from unexpanded sections
1736 * below.
1737 */
1738 memcpy(
1739 temp_grid, tiles,
1740 sizeof( retroflat_tile_t ) * t->tiles_w * t->tiles_h );
1741
1742 /* Starting another pass, assume finished until proven otherwise. */
1743 finished = 1;
1744 for( y = 0 ; t->tiles_h > y ; y++ ) {
1745 for( x = 0 ; t->tiles_w > x ; x++ ) {
1746 if( -1 == retrotile_get_tile( t, layer, x, y ) ) {
1747 /* If there are still unfilled tiles, we're not finished
1748 * yet!
1749 */
1750 finished = 0;
1751
1752 /* Skip filled tile. */
1753 continue;
1754 }
1755
1756
1757 for( side_iter = 0 ; 4 > side_iter ; side_iter++ ) {
1758 debug_printf( RETROTILE_TRACE_LVL,
1759 SIZE_T_FMT " (%d), " SIZE_T_FMT " (%d) ("
1760 SIZE_T_FMT ", " SIZE_T_FMT ")",
1761 x,
1762 gc_retroflat_offsets4_x[side_iter],
1763 y,
1764 gc_retroflat_offsets4_y[side_iter],
1765 t->tiles_w, t->tiles_h );
1766
1767 /* Iterate through directions to expand. */
1768 /* TODO: Add tuning to select directional probability. */
1769 if(
1770 t->tiles_w > x + gc_retroflat_offsets4_x[side_iter] &&
1771 t->tiles_h > y + gc_retroflat_offsets4_y[side_iter] &&
1772 -1 == temp_grid[
1773 ((y + gc_retroflat_offsets4_y[side_iter]) *
1774 t->tiles_w) +
1775 (x + gc_retroflat_offsets4_x[side_iter])]
1776 ) {
1777 /* Copy center tile to this direction. */
1778 retrotile_get_tile( t, layer,
1779 x + gc_retroflat_offsets4_x[side_iter],
1780 y + gc_retroflat_offsets4_y[side_iter] ) =
1781 retrotile_get_tile( t, layer, x, y );
1782 break;
1783 }
1784 }
1785 }
1786 }
1787 }
1788
1789cleanup:
1790
1791 if( NULL != temp_grid ) {
1792 maug_munlock( temp_grid_h, temp_grid );
1793 }
1794
1795 if( NULL != temp_grid_h ) {
1796 maug_mfree( temp_grid_h );
1797 }
1798
1799 return retval;
1800}
1801
1802/* === */
1803
1805 struct RETROTILE* t, retroflat_tile_t min_z, retroflat_tile_t max_z,
1806 uint32_t tuning, size_t layer_idx, uint8_t flags, void* data,
1807 retrotile_ani_cb animation_cb, void* animation_cb_data
1808) {
1809 MERROR_RETVAL retval = MERROR_OK;
1810 size_t x = 0,
1811 y = 0;
1812 int16_t side_iter = 0,
1813 sides_avail = 0,
1814 sides_sum = 0;
1815 /* Sides start from 12 on the clock (up). */
1816 struct RETROTILE_LAYER* layer = NULL;
1817
1818 assert( NULL != t );
1819 layer = retrotile_get_layer_p( t, layer_idx );
1820 assert( NULL != layer );
1821
1822 for( y = 0 ; t->tiles_h > y ; y++ ) {
1823 if( NULL != animation_cb ) {
1824 retval = animation_cb( animation_cb_data, y );
1825 maug_cleanup_if_not_ok();
1826 }
1827 for( x = 0 ; t->tiles_w > x ; x++ ) {
1828 /* Reset average. */
1829 sides_avail = 0;
1830 sides_sum = 0;
1831
1832 /* Grab values for available sides. */
1833 for( side_iter = 0 ; 8 > side_iter ; side_iter++ ) {
1834 if(
1835 t->tiles_w <= x + gc_retroflat_offsets8_x[side_iter] ||
1836 t->tiles_h <= y + gc_retroflat_offsets8_y[side_iter]
1837 ) {
1838 continue;
1839 }
1840
1841 sides_avail++;
1842 debug_printf(
1844 "si %d: x, y: " SIZE_T_FMT " (+%d), " SIZE_T_FMT
1845 " (+%d) idx: " SIZE_T_FMT,
1846 side_iter,
1847 x + gc_retroflat_offsets8_x[side_iter],
1848 gc_retroflat_offsets8_x[side_iter],
1849 y + gc_retroflat_offsets8_y[side_iter],
1850 gc_retroflat_offsets8_y[side_iter],
1851 ((y + gc_retroflat_offsets8_y[side_iter]) * t->tiles_w) +
1852 x + gc_retroflat_offsets8_x[side_iter] );
1853 sides_sum += retrotile_get_tile(
1854 t, layer,
1855 x + gc_retroflat_offsets8_x[side_iter],
1856 y + gc_retroflat_offsets8_y[side_iter] );
1857 }
1858
1859 retrotile_get_tile( t, layer, x, y ) = sides_sum / sides_avail;
1860 }
1861 }
1862
1863cleanup:
1864
1865 return retval;
1866}
1867
1868/* === */
1869
1871 struct RETROTILE* t, retroflat_tile_t min_z, retroflat_tile_t max_z,
1872 uint32_t tuning, size_t layer_idx, uint8_t flags, void* data,
1873 retrotile_ani_cb animation_cb, void* animation_cb_data
1874) {
1875 MERROR_RETVAL retval = MERROR_OK;
1876 struct RETROTILE_DATA_BORDER* borders =
1877 (struct RETROTILE_DATA_BORDER*)data;
1878 size_t i = 0,
1879 x = 0,
1880 y = 0,
1881 x_plus_1 = 0,
1882 y_plus_1 = 0,
1883 side = 0;
1884 int16_t ctr_iter = 0,
1885 outside_iter = 0;
1886 struct RETROTILE_LAYER* layer = NULL;
1887
1888 assert( NULL != t );
1889 layer = retrotile_get_layer_p( t, layer_idx );
1890 assert( NULL != layer );
1891
1892 /* Reset tile counter for all defined borders. */
1893 for( i = 0 ; 0 <= borders[i].center ; i++ ) {
1894 borders[i].tiles_changed = 0;
1895 }
1896
1897 debug_printf( 1, "adding borders..." );
1898
1899 for( y = 0 ; t->tiles_h > y ; y++ ) {
1900 for( x = 0 ; t->tiles_w > x ; x++ ) {
1901 i = 0;
1902 while( 0 <= borders[i].center ) {
1903 /* Compare/grab current center tile. */
1904 ctr_iter = retrotile_get_tile( t, layer, x, y );
1905 debug_printf( RETROTILE_TRACE_LVL,
1906 "x: " SIZE_T_FMT ", y: " SIZE_T_FMT ", 0x%04x vs 0x%04x",
1907 x, y, ctr_iter, borders[i].center );
1908 if( ctr_iter != borders[i].center ) {
1909 i++;
1910 continue;
1911 }
1912
1913 debug_printf( RETROTILE_TRACE_LVL, "comparing sides..." );
1914
1915 /* Zeroth pass: look for stick-outs. */
1916 for( side = 0 ; 8 > side ; side += 2 ) {
1917 if(
1918 x + gc_retroflat_offsets8_x[side] > t->tiles_w ||
1919 y + gc_retroflat_offsets8_y[side] > t->tiles_h
1920 ) {
1921 /* Skip out-of-bounds. */
1922 continue;
1923 }
1924 /* Get the outside tile on this side. */
1925 outside_iter = retrotile_get_tile( t, layer,
1926 x + gc_retroflat_offsets8_x[side],
1927 y + gc_retroflat_offsets8_y[side] );
1928
1929 /* Get the outside tile next two clock-steps from this one.
1930 */
1931 if( side + 4 < 8 ) {
1932 x_plus_1 = x + gc_retroflat_offsets8_x[side + 4];
1933 y_plus_1 = y + gc_retroflat_offsets8_y[side + 4];
1934 } else {
1935 x_plus_1 = x + gc_retroflat_offsets8_x[side - 4];
1936 y_plus_1 = y + gc_retroflat_offsets8_y[side - 4];
1937 }
1938
1939 if(
1940 x_plus_1 < t->tiles_w && y_plus_1 < t->tiles_h &&
1941 outside_iter == borders[i].outside &&
1942 outside_iter == retrotile_get_tile( t, layer,
1943 x_plus_1, y_plus_1 )
1944 ) {
1945 /* This has the outside on two opposing sides, so just
1946 * erase it and use the outside. */
1947 retrotile_get_tile( t, layer, x, y ) =
1948 borders[i].outside;
1949 borders[i].tiles_changed++;
1950 goto tile_done;
1951 }
1952 }
1953
1954
1955 /* First pass: look for corners. */
1956 for( side = 0 ; 8 > side ; side += 2 ) {
1957 if(
1958 x + gc_retroflat_offsets8_x[side] > t->tiles_w ||
1959 y + gc_retroflat_offsets8_y[side] > t->tiles_h
1960 ) {
1961 /* Skip out-of-bounds. */
1962 continue;
1963 }
1964 /* Get the outside tile on this side. */
1965 outside_iter = retrotile_get_tile( t, layer,
1966 x + gc_retroflat_offsets8_x[side],
1967 y + gc_retroflat_offsets8_y[side] );
1968
1969 /* Get the outside tile next two clock-steps from this one.
1970 */
1971 if( side + 2 < 8 ) {
1972 x_plus_1 = x + gc_retroflat_offsets8_x[side + 2];
1973 y_plus_1 = y + gc_retroflat_offsets8_y[side + 2];
1974 } else {
1975 x_plus_1 = x + gc_retroflat_offsets8_x[0];
1976 y_plus_1 = y + gc_retroflat_offsets8_y[0];
1977 }
1978
1979 if(
1980 x_plus_1 < t->tiles_w && y_plus_1 < t->tiles_h &&
1981 outside_iter == borders[i].outside &&
1982 outside_iter == retrotile_get_tile( t, layer,
1983 x_plus_1, y_plus_1 )
1984 ) {
1985 /* This has the outside on two sides, so use a corner. */
1986 retrotile_get_tile( t, layer, x, y ) =
1987 borders[i].mod_to[side + 1 < 8 ? side + 1 : 0];
1988 borders[i].tiles_changed++;
1989 goto tile_done;
1990 }
1991 }
1992
1993 /* Second pass (if first pass fails): look for edges. */
1994 for( side = 0 ; 8 > side ; side += 2 ) {
1995 if(
1996 x + gc_retroflat_offsets8_x[side] > t->tiles_w ||
1997 y + gc_retroflat_offsets8_y[side] > t->tiles_h
1998 ) {
1999 /* Skip out-of-bounds. */
2000 continue;
2001 }
2002 /* Get the outside tile on this side. */
2003 outside_iter = retrotile_get_tile( t, layer,
2004 x + gc_retroflat_offsets8_x[side],
2005 y + gc_retroflat_offsets8_y[side] );
2006
2007 if( outside_iter == borders[i].outside ) {
2008 /* It only matches on this side. */
2009 debug_printf( RETROTILE_TRACE_LVL, "replacing..." );
2010 retrotile_get_tile( t, layer, x, y ) =
2011 borders[i].mod_to[side];
2012 borders[i].tiles_changed++;
2013 goto tile_done;
2014 }
2015 }
2016
2017tile_done:
2018 /* Tile replaced or not replaceable. */
2019 break;
2020 }
2021 }
2022 }
2023
2024 return retval;
2025}
2026
2027/* === */
2028
2029struct RETROTILE_LAYER* retrotile_get_layer_p(
2030 struct RETROTILE* tilemap, uint32_t layer_idx
2031) {
2032 struct RETROTILE_LAYER* layer_iter = NULL;
2033 uint8_t* tilemap_buf = (uint8_t*)tilemap;
2034
2035 if( 0 == tilemap->layers_count || layer_idx >= tilemap->layers_count ) {
2036 error_printf( "invalid layer " U32_FMT
2037 " requested (of " U32_FMT ")!",
2038 layer_idx, tilemap->layers_count );
2039 return NULL;
2040 }
2041
2042 /* Advance to first grid. */
2043 tilemap_buf += sizeof( struct RETROTILE );
2044 layer_iter = (struct RETROTILE_LAYER*)tilemap_buf;
2045 while( layer_idx > 0 ) {
2046 tilemap_buf += layer_iter->sz;
2047 layer_iter = (struct RETROTILE_LAYER*)tilemap_buf;
2048 layer_idx--;
2049 }
2050
2051 return layer_iter;
2052}
2053
2054/* === */
2055
2056MERROR_RETVAL retrotile_alloc(
2057 MAUG_MHANDLE* p_tilemap_h, size_t w, size_t h, size_t layers_count,
2058 const char* tilemap_name, const char* tileset_name
2059) {
2060 struct RETROTILE_LAYER* layer_iter = NULL;
2061 MERROR_RETVAL retval = MERROR_OK;
2062 size_t tilemap_sz = 0;
2063 struct RETROTILE* tilemap = NULL;
2064 size_t i = 0;
2065
2066 tilemap_sz = sizeof( struct RETROTILE ) +
2067 (layers_count * sizeof( struct RETROTILE_LAYER )) +
2068 (layers_count * (w * h * sizeof( retroflat_tile_t ) ));
2069
2070 debug_printf( 1, "allocating new tilemap " SIZE_T_FMT "x" SIZE_T_FMT
2071 " tiles, " SIZE_T_FMT " layers (" SIZE_T_FMT " bytes)...",
2072 w, h, layers_count, tilemap_sz );
2073
2074 *p_tilemap_h = maug_malloc( 1, tilemap_sz );
2075 maug_cleanup_if_null_alloc( MAUG_MHANDLE, *p_tilemap_h );
2076
2077 maug_mlock( *p_tilemap_h, tilemap );
2078 maug_cleanup_if_null_alloc( struct RETROTILE*, tilemap );
2079
2080 maug_mzero( tilemap, tilemap_sz );
2081 tilemap->sz = tilemap_sz;
2082 tilemap->layers_count = layers_count;
2083 tilemap->tiles_w = w;
2084 tilemap->tiles_h = h;
2086
2087 maug_strncpy( tilemap->name, tilemap_name, RETROTILE_NAME_SZ_MAX );
2088
2089 maug_strncpy( tilemap->tileset, tileset_name, RETROTILE_NAME_SZ_MAX );
2090
2091 for( i = 0 ; layers_count > i ; i++ ) {
2092 layer_iter = retrotile_get_layer_p( tilemap, i );
2093 assert( NULL != layer_iter );
2094 layer_iter->sz = sizeof( struct RETROTILE_LAYER ) +
2095 (w * h * sizeof( retroflat_tile_t ));
2096 maug_cleanup_if_not_ok();
2097 }
2098
2099cleanup:
2100
2101 if( NULL != tilemap ) {
2102 maug_munlock( *p_tilemap_h, tilemap );
2103 }
2104
2105 return retval;
2106}
2107
2108/* === */
2109
2110void retrotile_format_asset_path(
2111 retroflat_asset_path path_out, const char* afile,
2112 struct RETROTILE_PARSER* parser
2113) {
2114 /* Load the portrait. */
2115 maug_mzero( path_out, RETROFLAT_ASSETS_PATH_MAX + 1 );
2116 maug_snprintf( path_out, RETROFLAT_ASSETS_PATH_MAX, "%s/%s",
2117 parser->dirname, afile );
2118}
2119
2120/* === */
2121
2122MERROR_RETVAL retrotile_clear_refresh( retroflat_pxxy_t y_max ) {
2123 MERROR_RETVAL retval = MERROR_OK;
2124#ifndef RETROFLAT_NO_VIEWPORT_REFRESH
2125 int16_t x = 0,
2126 y = 0;
2127
2128 debug_printf( 1,
2129 "clearing " SIZE_T_FMT " vertical viewport pixels (" SIZE_T_FMT
2130 " rows)...",
2131 y_max, y_max / RETROFLAT_TILE_H );
2132
2133 retroflat_viewport_lock_refresh();
2134 for( y = 0 ; y_max > y ; y += RETROFLAT_TILE_H ) {
2135 for( x = 0 ; retroflat_viewport_screen_w() > x ; x += RETROFLAT_TILE_W ) {
2136 retroflat_viewport_set_refresh( x, y, -1 );
2137 }
2138 }
2139
2140cleanup:
2141
2142 retroflat_viewport_unlock_refresh();
2143
2144#endif /* !RETROFLAT_NO_VIEWPORT_REFRESH */
2145
2146 return retval;
2147}
2148
2149/* === */
2150
2151MERROR_RETVAL retrotile_topdown_draw(
2152 struct RETROFLAT_BITMAP* target,
2153 struct RETROTILE* t, struct MDATA_VECTOR* t_defs
2154) {
2155 int16_t x = 0,
2156 y = 0,
2157 x_tile = 0,
2158 y_tile = 0;
2159 retroflat_tile_t tile_id = 0;
2160 struct RETROTILE_LAYER* layer = NULL;
2161 struct RETROTILE_TILE_DEF* t_def = NULL;
2162 MERROR_RETVAL retval = MERROR_OK;
2163
2164 layer = retrotile_get_layer_p( t, 0 );
2165
2166 mdata_vector_lock( t_defs );
2167 /* TODO: Rework this so it uses viewport tile indexes and then calculates
2168 * screen pixel X/Y from those? For performance?
2169 */
2170 for(
2171 y = ((retroflat_viewport_world_y() >>
2172 RETROFLAT_TILE_H_BITS) << RETROFLAT_TILE_H_BITS) ;
2173 y < (int)retroflat_viewport_world_y() +
2174 (int)retroflat_viewport_screen_h() ;
2175 y += RETROFLAT_TILE_H
2176 ) {
2177 for(
2178 x = ((retroflat_viewport_world_x() >>
2179 RETROFLAT_TILE_W_BITS) << RETROFLAT_TILE_W_BITS) ;
2180 x < (int)retroflat_viewport_world_x() +
2181 (int)retroflat_viewport_screen_w() ;
2182 x += RETROFLAT_TILE_W
2183 ) {
2184 /* Limit to tiles that exist in the world. */
2185 if(
2186 -1 > x || -1 > y ||
2187 (int)retroflat_viewport_world_w() <= x ||
2188 (int)retroflat_viewport_world_h() <= y
2189 ) {
2190 continue;
2191 }
2192
2193 /* Divide by tile width (16), or shift by 2^4. */
2194 x_tile = x >> RETROFLAT_TILE_W_BITS;
2195 y_tile = y >> RETROFLAT_TILE_H_BITS;
2196
2197 tile_id = retrotile_get_tile( t, layer, x_tile, y_tile );
2198 t_def = mdata_vector_get(
2199 t_defs, tile_id - t->tileset_fgid, struct RETROTILE_TILE_DEF );
2200 if( NULL == t_def ) {
2201 error_printf(
2202 "invalid tile ID: %d (- " SIZE_T_FMT " = " SIZE_T_FMT ")",
2203 tile_id, t->tileset_fgid, tile_id - t->tileset_fgid );
2204 continue;
2205 }
2206 assert( NULL != t_def );
2207
2208#ifndef RETROFLAT_NO_VIEWPORT_REFRESH
2209 /* Check tile refresh buffer. */
2210 retroflat_viewport_lock_refresh();
2211 if( !retroflat_viewport_tile_is_stale(
2212 x - retroflat_viewport_world_x(),
2213 y - retroflat_viewport_world_y(), tile_id
2214 ) ) {
2215 retroflat_viewport_unlock_refresh();
2216 continue;
2217 }
2218 /* Noisy! */
2219 /*
2220 debug_printf( RETROTILE_TRACE_LVL, "redrawing tile: %u, %u",
2221 x - retroflat_viewport_world_x(),
2222 y - retroflat_viewport_world_y() );
2223 */
2224 retroflat_viewport_set_refresh(
2225 x - retroflat_viewport_world_x(),
2226 y - retroflat_viewport_world_y(), tile_id );
2227 retroflat_viewport_unlock_refresh();
2228#endif /* !RETROFLAT_NO_VIEWPORT_REFRESH */
2229
2230#ifdef RETROGXC_PRESENT
2231 retrogxc_blit_bitmap( target, t_def->image_cache_id,
2232 t_def->x, t_def->y,
2235 RETROFLAT_TILE_W, RETROFLAT_TILE_H,
2236 retroflat_instance_tile( tile_id ) );
2237#else
2238 retroflat_blit_bitmap( target, &(t_def->image),
2239 t_def->x, t_def->y,
2242 RETROFLAT_TILE_W, RETROFLAT_TILE_H,
2243 retroflat_instance_tile( tile_id ) );
2244#endif /* RETROGXC_PRESENT */
2245 }
2246 }
2247
2248cleanup:
2249
2250 mdata_vector_unlock( t_defs );
2251
2252 return retval;
2253}
2254
2255#else
2256
2257/* This is defined externally so custom token callbacks can reference it. */
2258
2259# define RETROTILE_PARSER_MSTATE_TABLE_CONST( name, idx, tokn, parent, m ) \
2260 extern MAUG_CONST uint8_t SEG_MCONST name;
2261
2262RETROTILE_PARSER_MSTATE_TABLE( RETROTILE_PARSER_MSTATE_TABLE_CONST )
2263
2264# define RETROTILE_CLASS_TABLE_CONSTS( A, a, i ) \
2265 extern MAUG_CONST uint8_t SEG_MCONST RETROTILE_CLASS_ ## A;
2266
2267RETROTILE_CLASS_TABLE( RETROTILE_CLASS_TABLE_CONSTS )
2268
2269
2270#endif /* RETROTIL_C */
2271 /* retrotile */
2273
2274#endif /* !RETROTIL_H */
2275
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:65
MERROR_RETVAL mfile_open_read(const char *filename, mfile_t *p_file)
Open a file and read it into memory or memory-map it.
char retroflat_asset_path[RETROFLAT_PATH_MAX+1]
Path/name used to load an asset from disk.
Definition retroflt.h:771
MERROR_RETVAL retroflat_blit_bitmap(struct RETROFLAT_BITMAP *target, struct RETROFLAT_BITMAP *src, size_t s_x, size_t s_y, int16_t d_x, int16_t d_y, size_t w, size_t h, int16_t instance)
Blit the contents of a ::RETROFLAT_BITMAP onto another ::RETROFLAT_BITMAP.
#define retroflat_instance_tile(instance)
Declare that a given instance ID is for a tile, rather than a sprite.
Definition retroflt.h:570
#define RETROFLAT_ASSETS_PATH_MAX
Maximum size of the assets path, to allow room for appending.
Definition retroflt.h:746
#define retroflat_viewport_screen_y(world_y)
Return the screenspace Y coordinate at which something at the given world coordinate should be drawn.
Definition retroflt.h:1519
#define retroflat_viewport_screen_x(world_x)
Return the screenspace X coordinate at which something at the given world coordinate should be drawn.
Definition retroflt.h:1512
size_t retroflat_pxxy_t
Type used for surface pixel coordinates.
Definition retroflt.h:895
#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:157
#define RETROTILE_PROP_TYPE_FILE
Value for RETROTILE_PARSER::last_prop_type indicating file path.
Definition retrotil.h:139
#define RETROTILE_PROP_TYPE_STRING
Value for RETROTILE_PARSER::last_prop_type indicating string.
Definition retrotil.h:133
#define RETROTILE_PROP_TYPE_INT
Value for RETROTILE_PARSER::last_prop_type indicating integer.
Definition retrotil.h:145
#define RETROTILE_PROP_TYPE_OTHER
Value for RETROTILE_PARSER::last_prop_type indicating other type.
Definition retrotil.h:127
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.
int retrotile_parse_prop_type(const char *token, size_t token_sz)
Convert a Tiled "type" field to an integer suitable for use with RETROTILE_PARSER::last_prop_type.
MERROR_RETVAL retrotile_parse_json_file(const char *dirname, const char *filename, MAUG_MHANDLE *p_tilemap_h, struct MDATA_VECTOR *p_tile_defs, mparser_wait_cb_t wait_cb, void *wait_data, mparser_parse_token_cb token_cb, void *token_cb_data, uint8_t passes, uint8_t flags)
Parse the JSON file at the given path into a heap-allocated tilemap with a RETROTILE struct header.
mfix_t retrotile_static_rotation_from_dir(const char *dir)
Convert a less-or-equal-to-two-character string to a direction in degrees.
#define RETROTILE_TILE_SCALE_DEFAULT
Default value for RETROTILE::tile_scale.
Definition retrotil.h:35
#define RETROTILE_PROP_NAME_SZ_MAX
Maximum number of chars in a parsed property name.
Definition retrotil.h:30
#define RETROTILE_PARSER_MODE_DEFS
Value for RETROTILE_PARSER::mode indicating the parser is currently parsing tile definitions.
Definition retrotil.h:78
#define RETROTILE_TRACE_LVL
If defined, bring debug printf statements up to this level.
Definition retrotil.h:40
#define RETROTILE_PARSER_MODE_MAP
Value for RETROTILE_PARSER::mode indicating the parser is currently parsing a tilemap.
Definition retrotil.h:71
int16_t retroflat_tile_t
Value for an individual tile in a RETROTILE_LAYER.
Definition retroflt.h:19
#define RETROTILE_NAME_SZ_MAX
Maximum number of chars in a RETROTILE::name.
Definition retrotil.h:25
#define RETROTILE_PARSER_FLAG_LITERAL_PATHS
Flag for RETROTILE_PARSER::flags indicating to use literal image asset paths.
Definition retrotil.h:63
A vector of uniformly-sized objects, stored contiguously.
Definition mdata.h:89
This is not currently used for anything, but is provided as a a convenience for game logic.
Definition retrotil.h:201
Definition retrotil.h:254
retroflat_tile_t mod_to[8]
If the center and outside match, use this mod-to.
Definition retrotil.h:259
Internal data structure used by retrotile_gen_diamond_square_iter().
Definition retrotil.h:237
int16_t sect_y
Starting Y of subsector in a given iteration.
Definition retrotil.h:241
int16_t sect_w
Width of subsector in a given iteration.
Definition retrotil.h:243
int16_t sect_w_half
Half of the width of subsector in a given iteration.
Definition retrotil.h:247
int16_t sect_x
Starting X of subsector in a given iteration.
Definition retrotil.h:239
int16_t sect_h
Height of subsector in a given iteration.
Definition retrotil.h:245
int16_t sect_h_half
Half of the height of subsector in a given iteration.
Definition retrotil.h:249
Definition retrotil.h:192
Definition retrotil.h:287
mparser_parse_token_cb custom_token_cb
Callback to parse engine-specific custom tokens from the tilemap JSON. Should return MERROR_PREEMPT i...
Definition retrotil.h:321
char tilemap_name[RETROTILE_NAME_SZ_MAX+1]
The name to give to the new tilemap.
Definition retrotil.h:304
char last_prop_name[RETROTILE_PROP_NAME_SZ_MAX+1]
The name of the last property key/value pair parsed.
Definition retrotil.h:300
uint8_t mode
Value indicating the current type of object being parsed into.
Definition retrotil.h:294
size_t tileset_id_cur
Highest tileset ID on first pass and next ID to be assigned on second.
Definition retrotil.h:311
Definition retrotil.h:164
mfix_t static_rotation
Field indicating how many degrees the tile should always be rotated before drawin on-screen....
Definition retrotil.h:176
A struct representing a tilemap.
Definition retrotil.h:213
size_t tiles_h
Height of all layers of the tilemap in tiles.
Definition retrotil.h:223
size_t tileset_fgid
First GID in the accompanying tileset.
Definition retrotil.h:221
uint32_t sz
Size of the tilemap in bytes (including this struct header).
Definition retrotil.h:217
float tile_scale
Amount by which to scale tiles (convenience property).
Definition retrotil.h:230
size_t tiles_w
Width of all layers of the tilemap in tiles.
Definition retrotil.h:225
uint32_t layers_count
Number of tile layers in this tilemap.
Definition retrotil.h:219