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