diff --git a/Makefile b/Makefile index c5af10c..e632707 100644 --- a/Makefile +++ b/Makefile @@ -2,16 +2,26 @@ SDL_CFLAGS := `sdl2-config --cflags` SDL_LIBS := `sdl2-config --libs` -SRCS := decode.c fileio.c game.c level.c main.c opcodes.c resource.c screen.c sound.c staticres.c sys_sdl2.c triggers.c unpack.c util.c +BB := decode.c fileio.c game.c level.c objects.c resource.c screen.c sound.c staticres.c tiles.c unpack.c +JA := game.c level.c resource.c screen.c sound.c staticres.c unpack.c + +BB_SRCS := $(foreach f,$(BB),bb/$f) +JA_SRCS := $(foreach f,$(JA),ja/$f) +SRCS := $(BB_SRCS) $(JA_SRCS) OBJS := $(SRCS:.c=.o) DEPS := $(SRCS:.c=.d) -CPPFLAGS := -Wall -Wpedantic -MMD $(SDL_CFLAGS) -g +CPPFLAGS := -Wall -Wpedantic -MMD $(SDL_CFLAGS) -I. -g -blues: $(OBJS) - $(CC) $(LDFLAGS) -o $@ $(OBJS) $(SDL_LIBS) -lmodplug +all: blues bbja + +blues: main.o sys_sdl2.o util.o $(BB_SRCS:.c=.o) + $(CC) $(LDFLAGS) -o $@ $^ $(SDL_LIBS) -lmodplug + +bbja: main.o sys_sdl2.o util.o $(JA_SRCS:.c=.o) + $(CC) $(LDFLAGS) -o $@ $^ $(SDL_LIBS) -lmodplug clean: - rm -f *.o *.d + rm -f $(OBJS) $(DEPS) -include $(DEPS) diff --git a/README.md b/README.md index 00d02da..e5035e2 100644 --- a/README.md +++ b/README.md @@ -1,33 +1,36 @@ # Blues Brothers -This is a rewrite of the game [Blues Brothers](https://www.mobygames.com/game/blues-brothers) developed by [Titus](https://www.mobygames.com/company/titus-interactive-sa). +This is a rewrite of the [Blues Brothers](https://www.mobygames.com/game/blues-brothers) and [Blues Brothers: Jukebox Adventure](https://www.mobygames.com/game/blues-brothers-jukebox-adventure) game engines developed by [Titus Interactive](https://www.mobygames.com/company/titus-interactive-sa). -![Screenshot1](blues1.png) ![Screenshot2](blues2.png) +![Screenshot1](blues1.png) ![Screenshot2](bbja2.png) ## Requirements +### Blues Brothers + The game data files of the DOS or Amiga version are required. ``` *.BIN, *.CK1, *.CK2, *.SQL, *.SQV, *.SQZ ``` -For sounds and music, the Amiga version files need to be copied. +For sounds and music, the Amiga version files or the [ExoticA](https://www.exotica.org.uk/wiki/The_Blues_Brothers) set need to be copied. + +### Jukebox Adventure + +The game data files of the DOS version are required. ``` -SOUND, ALMOST, GUNN, EVERY, SHOT +*.EAT, *.MOD ``` -The [ExoticA](https://www.exotica.org.uk/wiki/The_Blues_Brothers) modules set can also be used. - ## Running By default, the executable loads the data files from the current directory. - -This can be changed by using command line switch. +This can be changed using command line switches. ``` Usage: blues [OPTIONS]... @@ -36,8 +39,8 @@ Usage: blues [OPTIONS]... --cheats=MASK Cheats bitmask --startpos=XxY Start at position (X,Y) --fullscreen Enable fullscreen - --scale Graphics scaling factor (default 2) - --filter Graphics scaling filter + --scale=N Graphics scaling factor (default 2) + --filter=NAME Graphics scaling filter (default 'nearest') --screensize=WxH Graphics screen size (default 320x200) --cga Enable CGA colors ``` diff --git a/decode.c b/bb/decode.c similarity index 100% rename from decode.c rename to bb/decode.c diff --git a/decode.h b/bb/decode.h similarity index 100% rename from decode.h rename to bb/decode.h diff --git a/fileio.c b/bb/fileio.c similarity index 100% rename from fileio.c rename to bb/fileio.c diff --git a/fileio.h b/bb/fileio.h similarity index 100% rename from fileio.h rename to bb/fileio.h diff --git a/game.c b/bb/game.c similarity index 92% rename from game.c rename to bb/game.c index f884a2f..4e832c2 100644 --- a/game.c +++ b/bb/game.c @@ -31,7 +31,7 @@ void update_input() { // g_vars.inp_key_tab = g_vars.inp_keyboard[0xF] || g_vars.inp_keyboard[0x78]; } -void do_title_screen() { +static void do_title_screen() { const uint32_t timestamp = g_sys.get_timestamp() + 20 * 1000; load_img(g_res.amiga_data ? "blues.lbm" : "pres.sqz", GAME_SCREEN_W, g_options.cga_colors ? 0 : -1); fade_in_palette(); @@ -40,13 +40,33 @@ void do_title_screen() { if (g_sys.input.space || g_sys.input.quit) { break; } + g_sys.sleep(10); } while (g_sys.get_timestamp() < timestamp); play_sound(SOUND_0); fade_out_palette(); g_sys.input.space = 0; } -void do_select_player() { +static void do_demo_screens() { + static const char *filenames[] = { "text1.sqz", "maq1.sqz", "maq2.sqz", "maq3.sqz", "maq4.sqz", "maq5.sqz", 0 }; + for (int i = 0; filenames[i]; ++i) { + load_img(filenames[i], GAME_SCREEN_W, -1); + fade_in_palette(); + while (1) { + update_input(); + if (g_sys.input.space) { + break; + } + if (g_sys.input.quit) { + return; + } + g_sys.sleep(10); + } + fade_out_palette(); + } +} + +static void do_select_player() { int quit = 0; int fade = 0; int state = 0; @@ -58,7 +78,6 @@ void do_select_player() { screen_load_graphics(g_options.cga_colors ? g_res.cga_lut_sqv : 0, 0); screen_clear_sprites(); do { - screen_copy_img(); update_input(); const uint32_t timestamp = g_sys.get_timestamp(); switch (state) { @@ -300,7 +319,7 @@ void game_main() { g_res.spr_frames[144] = icon778e; } if (g_options.cga_colors) { - g_sys.set_screen_palette(_colors_cga, 4); + g_sys.set_screen_palette(_colors_cga, 0, 4, 8); } else if (g_options.amiga_colors) { uint16_t palette[16]; for (int i = 0; i < 16; ++i) { @@ -328,6 +347,23 @@ void game_main() { } load_level_data(g_vars.level); do_level(); + if (g_res.dos_demo && g_vars.level > 0) { + do_demo_screens(); + return; + } } } } + +static void game_run(const char *data_path) { + res_init(data_path, GAME_SCREEN_W * GAME_SCREEN_H); + sound_init(); + game_main(); + sound_fini(); + res_fini(); +} + +struct game_t game = { + "Blues Brothers", + game_run +}; diff --git a/game.h b/bb/game.h similarity index 91% rename from game.h rename to bb/game.h index 1be8e5b..88f3c18 100644 --- a/game.h +++ b/bb/game.h @@ -4,9 +4,6 @@ #include "intern.h" -#define GAME_SCREEN_W g_options.screen_w -#define GAME_SCREEN_H g_options.screen_h - #define TILEMAP_OFFSET_Y 14 #define TILEMAP_SCREEN_W GAME_SCREEN_W #define TILEMAP_SCREEN_H (GAME_SCREEN_H - 40) @@ -38,20 +35,6 @@ #define CHEATS_UNLIMITED_LIFES (1 << 1) #define CHEATS_UNLIMITED_ENERGY (1 << 2) -struct options_t { - uint32_t cheats; - int start_level; - int start_xpos16; - int start_ypos16; - int screen_w; - int screen_h; - bool amiga_copper_bars; - bool amiga_colors; - bool amiga_status_bar; - bool dos_scrolling; - bool cga_colors; -}; - extern struct options_t g_options; struct door_t { @@ -134,15 +117,15 @@ struct object_t { uint8_t unk56; int16_t vinyls_count; uint8_t collide_flag; - int16_t unk5A; // always zero - uint8_t unk5C; + // int16_t unk5A; // always zero + // uint8_t unk5C; // never read uint8_t unk5D; uint8_t blinking_counter; uint8_t data5F; // music instrument number for obj39/40, counter for other objects uint8_t unk60; uint8_t lifes_count; - uint8_t flag_end_level; // level flag, set if music instrument was found - uint8_t unk63; // restart_level_flag + uint8_t level_complete_flag; // set if music instrument was found + uint8_t restart_level_flag; }; struct vars_t { @@ -220,8 +203,6 @@ extern uint8_t *level_tilesdata_1e8c[]; /* game.c */ extern void update_input(); extern void game_main(); -extern void do_title_screen(); -extern void do_select_player(); /* level.c */ extern void load_level_data(int num); @@ -233,7 +214,7 @@ extern void do_level_drop_grabbed_object(struct object_t *); extern void do_level_update_projectile(struct object_t *obj); extern void do_level(); -/* opcodes.c */ +/* objects.c */ extern void level_call_object_func(struct object_t *); /* screen.c */ @@ -247,7 +228,6 @@ extern void screen_adjust_palette_color(int color, int b, int c); extern void screen_vsync(); extern void screen_draw_frame(const uint8_t *frame, int fh, int fw, int x, int y); extern void screen_flip(); -extern void screen_copy_img(); extern void screen_unk5(); extern void screen_do_transition1(int a); extern void screen_do_transition2(); @@ -266,14 +246,14 @@ extern void sound_fini(); extern void play_sound(int num); extern void play_music(int num); -/* triggers.c */ +/* tiles.c */ extern uint16_t triggers_get_tile_type(int x, int y); extern uint16_t triggers_get_next_tile_flags(int x, int y); extern uint16_t triggers_get_tile_data(struct object_t *obj); extern uint16_t triggers_get_next_tile_num(int x, int y); extern void level_call_trigger_func(struct object_t *obj, int y); extern void triggers_update_tiles1(struct object_t *obj); -extern int16_t triggers_get_dy(struct object_t *obj); +extern int16_t triggers_tile_get_yoffset(struct object_t *obj); extern void triggers_update_tiles2(struct object_t *obj); #endif /* GAME_H__ */ diff --git a/level.c b/bb/level.c similarity index 97% rename from level.c rename to bb/level.c index da3f043..4f2c8f9 100644 --- a/level.c +++ b/bb/level.c @@ -94,6 +94,8 @@ void load_level_data(int num) { if (g_options.cga_colors) { } else if (g_options.amiga_colors) { g_sys.set_palette_amiga(_colors_data + g_vars.level * 16, 0); + } else { + g_sys.set_screen_palette(g_res.palette, 0, 16, 8); } screen_load_graphics(g_options.cga_colors ? g_res.cga_lut_sqv : 0, g_options.cga_colors ? g_res.cga_lut_avt : 0); } @@ -229,13 +231,13 @@ static void init_level() { obj->trigger3 = 0; obj->unk50 = 0; // obj->unk4E = 0; - if (obj->unk63 == 0 && obj->flag_end_level == 0 && (g_vars.level == 0 || g_vars.play_demo_flag)) { + if (obj->restart_level_flag == 0 && obj->level_complete_flag == 0 && (g_vars.level == 0 || g_vars.play_demo_flag)) { g_vars.player1_dead_flag = 0; g_vars.player2_scrolling_flag = 0; obj->lifes_count = 3; obj->data51 = 3; obj->vinyls_count = 0; - } else if (obj->unk63 != 0) { + } else if (obj->restart_level_flag != 0) { --obj->lifes_count; obj->data51 = 3; obj->vinyls_count = 0; @@ -244,15 +246,15 @@ static void init_level() { obj->lifes_count = 3; obj->data51 = 3; } - obj->unk63 = 0; - obj->flag_end_level = 0; + obj->restart_level_flag = 0; + obj->level_complete_flag = 0; obj->unk53 = 0; obj->unk54 = 0; obj->unk55 = 0; obj->unk56 = 0; obj->collide_flag = 0; - obj->unk5A = 0; - obj->unk5C = 1; + // obj->unk5A = 0; + // obj->unk5C = 1; obj->unk5D = 0; if (obj->ypos > g_vars.screen_tilemap_h) { obj->unk53 = 1; @@ -789,17 +791,17 @@ static void do_level_add_sprite3(struct object_t *obj) { obj->anim_num = 1; obj->special_anim = 0; obj->sprite3_counter = 0; - } else if (obj->unk5A != 0) { - obj->unk5C = 4; + // } else if (obj->unk5A != 0) { + // obj->unk5C = 4; } } if (obj->special_anim == 18 && obj->anim_num > 8 && (g_vars.inp_key_up != 0 /* || g_inp_key_T != 0*/)) { obj->anim_num = 1; obj->special_anim = 0; obj->sprite3_counter = 0; - if (obj->unk5A != 0) { - obj->unk5C = 4; - } + // if (obj->unk5A != 0) { + // obj->unk5C = 4; + // } } } if (obj->type != 100) { @@ -998,7 +1000,7 @@ void do_level_update_panel_lifes(struct object_t *obj) { } if (obj->data51 == 0) { // health if (!g_vars.two_players_flag) { - obj->unk63 = 1; + obj->restart_level_flag = 1; g_vars.game_over_flag = 1; } else { if (obj->lifes_count != 1) { @@ -1151,12 +1153,6 @@ void do_level_enter_door(struct object_t *obj) { } obj->screen_xpos = obj->xpos - g_vars.screen_tilemap_xorigin; obj->screen_ypos = obj->ypos - g_vars.screen_tilemap_yorigin; - draw_level_panel(); - do_level_update_panel_vinyls(obj); - if (g_vars.two_players_flag) { - do_level_update_panel_2nd_player(); - } - screen_flip(); do_level_redraw_tilemap(g_vars.screen_tilemap_xorigin, g_vars.screen_tilemap_yorigin); do_level_update_scrolling2(); draw_level_panel(); @@ -1164,8 +1160,8 @@ void do_level_enter_door(struct object_t *obj) { if (g_vars.two_players_flag) { do_level_update_panel_2nd_player(); } - screen_do_transition1(1); screen_flip(); + screen_do_transition1(1); obj->unk3D = 0; obj->sprite_type = 0; obj->yacc = 2; @@ -1456,7 +1452,7 @@ static void do_level_fixup_object_position(struct object_t *obj) { switch (num) { case 2: case 14: - ret = triggers_get_dy(obj); + ret = triggers_tile_get_yoffset(obj); obj->ypos = ret + (obj->ypos & ~15); obj->ypos16 = obj->ypos >> 4; obj->screen_ypos = obj->ypos - g_vars.screen_tilemap_yorigin; @@ -1464,7 +1460,7 @@ static void do_level_fixup_object_position(struct object_t *obj) { case 3: case 15: --obj->ypos16; - ret = triggers_get_dy(obj); + ret = triggers_tile_get_yoffset(obj); obj->ypos = ret + (obj->ypos & ~15) - 16; obj->ypos16 = obj->ypos >> 4; obj->screen_ypos = obj->ypos - g_vars.screen_tilemap_yorigin; @@ -1909,7 +1905,6 @@ static void do_level_update_objects() { } } else if (g_vars.two_players_flag) { if (!g_vars.player1_dead_flag) { - // sub_176D7(obj); do_level_update_object_unk2E(obj); do_level_update_panel_2nd_player(); } @@ -1918,8 +1913,8 @@ static void do_level_update_objects() { } } } else if (obj->type > 2 && obj->collide_flag == 0) { - const int x = g_vars.level_xpos[i]; // _si - const int y = g_vars.level_ypos[i]; // _di + const int x = g_vars.level_xpos[i]; + const int y = g_vars.level_ypos[i]; if (obj->type == 32) { obj->unk5D = i; } @@ -1992,16 +1987,16 @@ static void do_level_update_objects() { } } } - if (!g_vars.two_players_flag && g_vars.objects[OBJECT_NUM_PLAYER1].flag_end_level != 0) { + if (!g_vars.two_players_flag && g_vars.objects[OBJECT_NUM_PLAYER1].level_complete_flag != 0) { g_vars.level_completed_flag = 1; } else { - if (g_vars.objects[OBJECT_NUM_PLAYER1].flag_end_level != 0 && g_vars.objects[OBJECT_NUM_PLAYER2].flag_end_level != 0) { + if (g_vars.objects[OBJECT_NUM_PLAYER1].level_complete_flag != 0 && g_vars.objects[OBJECT_NUM_PLAYER2].level_complete_flag != 0) { g_vars.level_completed_flag = 1; } - if (g_vars.objects[OBJECT_NUM_PLAYER1].flag_end_level != 0 && g_vars.player1_dead_flag) { + if (g_vars.objects[OBJECT_NUM_PLAYER1].level_complete_flag != 0 && g_vars.player1_dead_flag) { g_vars.level_completed_flag = 1; } - if (g_vars.objects[OBJECT_NUM_PLAYER2].flag_end_level != 0 && g_vars.player2_dead_flag) { + if (g_vars.objects[OBJECT_NUM_PLAYER2].level_complete_flag != 0 && g_vars.player2_dead_flag) { g_vars.level_completed_flag = 1; } } @@ -2019,8 +2014,8 @@ static void do_level_update_objects() { g_vars.objects[OBJECT_NUM_PLAYER2].unk60 = 0; g_vars.objects[OBJECT_NUM_PLAYER1].data5F = 0; g_vars.objects[OBJECT_NUM_PLAYER2].data5F = 0; - g_vars.objects[OBJECT_NUM_PLAYER1].flag_end_level = 0; - g_vars.objects[OBJECT_NUM_PLAYER2].flag_end_level = 0; + g_vars.objects[OBJECT_NUM_PLAYER1].level_complete_flag = 0; + g_vars.objects[OBJECT_NUM_PLAYER2].level_complete_flag = 0; // g_vars.screen_draw_offset -= TILEMAP_OFFSET_Y * 40; screen_unk5(); // g_vars.screen_draw_offset += TILEMAP_OFFSET_Y * 40; @@ -2034,10 +2029,10 @@ static void do_level_update_objects() { g_vars.objects[OBJECT_NUM_PLAYER2].unk60 = 0; g_vars.objects[OBJECT_NUM_PLAYER1].data5F = 0; g_vars.objects[OBJECT_NUM_PLAYER2].data5F = 0; - g_vars.objects[OBJECT_NUM_PLAYER1].unk63 = 0; - g_vars.objects[OBJECT_NUM_PLAYER2].unk63 = 0; - g_vars.objects[OBJECT_NUM_PLAYER1].flag_end_level = 0; - g_vars.objects[OBJECT_NUM_PLAYER2].flag_end_level = 0; + g_vars.objects[OBJECT_NUM_PLAYER1].restart_level_flag = 0; + g_vars.objects[OBJECT_NUM_PLAYER2].restart_level_flag = 0; + g_vars.objects[OBJECT_NUM_PLAYER1].level_complete_flag = 0; + g_vars.objects[OBJECT_NUM_PLAYER2].level_complete_flag = 0; screen_vsync(); } if (g_vars.objects[35].screen_ypos > 90) { diff --git a/opcodes.c b/bb/objects.c similarity index 100% rename from opcodes.c rename to bb/objects.c diff --git a/resource.c b/bb/resource.c similarity index 96% rename from resource.c rename to bb/resource.c index 00f1255..2b52815 100644 --- a/resource.c +++ b/bb/resource.c @@ -7,7 +7,8 @@ struct resource_data_t g_res; -void res_init(int vga_size) { +void res_init(const char *datapath, int vga_size) { + fio_init(datapath); static const int SQL_SIZE = 640 * 25; g_res.sql = (uint8_t *)malloc(SQL_SIZE); if (!g_res.sql) { @@ -56,6 +57,7 @@ void res_init(int vga_size) { } void res_fini() { + fio_fini(); free(g_res.sql); g_res.sql = 0; free(g_res.spr_sqv); @@ -102,7 +104,7 @@ static void copy_cga(uint8_t *dst, int dst_pitch, uint8_t *dither_lut) { for (int y = 0; y < 200; ++y) { const uint8_t *p = dither_lut + (y & 1) * 0x10; for (int x = 0; x < 320; ++x) { - dst[x] = p[dst[x]] & 3; + dst[x] = p[dst[x]]; } dst += dst_pitch; } @@ -280,9 +282,6 @@ void load_ck(const char *filename, uint16_t offset, int dither_pattern) { break; } load_iff(g_res.tmp, size, g_res.tiles + offset, 640, dither_pattern); - if (dither_pattern < 0) { - g_sys.set_screen_palette(g_res.palette, 16); - } } void load_img(const char *filename, int screen_w, int dither_pattern) { @@ -294,12 +293,11 @@ void load_img(const char *filename, int screen_w, int dither_pattern) { size = read_compressed_file(filename, g_res.tmp); } assert(size <= 32000); - load_iff(g_res.tmp, size, g_res.tmp + 32000, screen_w, dither_pattern); + load_iff(g_res.tmp, size, g_res.vga, screen_w, dither_pattern); if (dither_pattern < 0) { - g_sys.set_screen_palette(g_res.palette, 16); + g_sys.set_screen_palette(g_res.palette, 0, 16, 8); } - g_sys.update_screen(g_res.tmp + 32000, 0); - memcpy(g_res.vga, g_res.tmp + 32000, g_res.vga_size); + g_sys.update_screen(g_res.vga, 0); } void load_m(const char *filename) { diff --git a/resource.h b/bb/resource.h similarity index 94% rename from resource.h rename to bb/resource.h index e7ebba0..c9bfef8 100644 --- a/resource.h +++ b/bb/resource.h @@ -12,7 +12,7 @@ struct trigger_t { int16_t tile_type; int16_t tile_flags; uint8_t op_func; - const uint8_t *op_table1; // dy for (x&15) + const uint8_t *op_table1; // tile_yoffset const uint8_t *op_table2; // tile_animation int16_t unk10; const uint8_t *op_table3; // tile_trigger @@ -51,7 +51,7 @@ struct resource_data_t { extern struct resource_data_t g_res; -extern void res_init(int vga_size); +extern void res_init(const char *datapath, int vga_size); extern void res_fini(); extern int read_file(const char *filename, uint8_t *dst, int size); extern int read_compressed_file(const char *filename, uint8_t *dst); diff --git a/screen.c b/bb/screen.c similarity index 97% rename from screen.c rename to bb/screen.c index 1cf8e8a..2013806 100644 --- a/screen.c +++ b/bb/screen.c @@ -56,7 +56,7 @@ void screen_adjust_palette_color(int color, int b, int c) { if (!g_options.cga_colors) { g_res.palette[color * 3 + b] += c; screen_vsync(); - g_sys.set_screen_palette(g_res.palette, 16); + g_sys.set_screen_palette(g_res.palette, 0, 16, 8); } } @@ -90,24 +90,20 @@ void screen_flip() { g_sys.update_screen(g_res.vga, 1); } -void screen_copy_img() { - memcpy(g_res.vga, g_res.tmp + 32000, GAME_SCREEN_W * GAME_SCREEN_H); -} - void screen_unk5() { // screen_do_transition2(); screen_clear(0); } void screen_do_transition1(int a) { - print_warning("screen_do_transition1 %d", a); + // print_warning("screen_do_transition1 %d", a); if (a) { g_sys.transition_screen(TRANSITION_CURTAIN, true); } } void screen_do_transition2() { - print_warning("screen_do_transition2"); + // print_warning("screen_do_transition2"); g_sys.transition_screen(TRANSITION_SQUARE, true); } @@ -207,7 +203,7 @@ static void dither_graphics(uint8_t *dst, int dst_pitch, int w, int h, const uin if (color == 0) { dst[x] = color_key; } else { - dst[x] = p[color] & 3; + dst[x] = p[color]; } } dst += dst_pitch; @@ -232,7 +228,6 @@ static void decode_graphics(int spr_type, int start, int end, const uint8_t *dit } const int h = READ_LE_UINT16(ptr - 4); const int w = READ_LE_UINT16(ptr - 2); - const int j = i - start; if (current_x + w > MAX_SPRITESHEET_W) { current_y += max_h; if (current_x > max_w) { @@ -249,6 +244,7 @@ static void decode_graphics(int spr_type, int start, int end, const uint8_t *dit if (dither_lut) { dither_graphics(data + current_y * MAX_SPRITESHEET_W + current_x, MAX_SPRITESHEET_W, w, h, dither_lut, color_key); } + const int j = i - start; r[j].x = current_x; r[j].y = current_y; r[j].w = w; diff --git a/sound.c b/bb/sound.c similarity index 99% rename from sound.c rename to bb/sound.c index 85bbfa4..572252f 100644 --- a/sound.c +++ b/bb/sound.c @@ -145,8 +145,8 @@ void play_music(int num) { } } _mpf = ModPlug_Load(buf, size); + free(buf); } - free(buf); } fio_close(slot); } else { // ExoticA diff --git a/staticres.c b/bb/staticres.c similarity index 100% rename from staticres.c rename to bb/staticres.c diff --git a/triggers.c b/bb/tiles.c similarity index 97% rename from triggers.c rename to bb/tiles.c index b60c634..034616c 100644 --- a/triggers.c +++ b/bb/tiles.c @@ -12,9 +12,7 @@ uint16_t triggers_get_tile_type(int x, int y) { } uint16_t triggers_get_next_tile_flags(int x, int y) { - const uint8_t *p = lookup_sql(x, y); - int num = p[0]; - num = g_res.triggers[num].unk16; + const int num = triggers_get_next_tile_num(x, y); return g_res.triggers[num].tile_flags; } @@ -470,7 +468,7 @@ static void trigger_func_op30(struct object_t *obj) { static void trigger_func_op31(struct object_t *obj) { if (obj->yvelocity > 0 && obj->unk2B == 0) { obj->yvelocity = 0; - obj->ypos = (obj->ypos & ~15) + triggers_get_dy(obj); + obj->ypos = (obj->ypos & ~15) + triggers_tile_get_yoffset(obj); obj->ypos16 = obj->ypos >> 4; obj->screen_ypos = obj->ypos - g_vars.screen_tilemap_yorigin; obj->sprite_type = 1; @@ -482,7 +480,7 @@ static void trigger_func_op31(struct object_t *obj) { static void trigger_func_op32(struct object_t *obj) { if (obj->yvelocity > 0 && obj->unk2B == 0) { obj->yvelocity = 0; - obj->ypos = (obj->ypos & ~15) + triggers_get_dy(obj); + obj->ypos = (obj->ypos & ~15) + triggers_tile_get_yoffset(obj); obj->ypos16 = obj->ypos >> 4; obj->screen_ypos = obj->ypos - g_vars.screen_tilemap_yorigin; obj->sprite_type = 1; @@ -508,7 +506,7 @@ static void trigger_func_op33(struct object_t *obj) { static void trigger_func_op34(struct object_t *obj) { if (obj->yvelocity > 0 && obj->unk2B == 0) { obj->yvelocity = 0; - obj->ypos = (obj->ypos & ~15) + triggers_get_dy(obj); + obj->ypos = (obj->ypos & ~15) + triggers_tile_get_yoffset(obj); obj->ypos16 = obj->ypos >> 4; obj->screen_ypos = obj->ypos - g_vars.screen_tilemap_yorigin; obj->sprite_type = 1; @@ -534,7 +532,7 @@ static void trigger_func_op35(struct object_t *obj) { static void trigger_func_op36(struct object_t *obj) { if (obj->yvelocity > 0 && obj->unk2B == 0) { obj->yvelocity = 0; - obj->ypos = (obj->ypos & ~15) + triggers_get_dy(obj); + obj->ypos = (obj->ypos & ~15) + triggers_tile_get_yoffset(obj); obj->ypos16 = obj->ypos >> 4; obj->screen_ypos = obj->ypos - g_vars.screen_tilemap_yorigin; obj->sprite_type = 4; @@ -556,7 +554,7 @@ static void trigger_func_op37(struct object_t *obj) { static void trigger_func_op38(struct object_t *obj) { if (obj->yvelocity > 0 && obj->unk2B == 0) { obj->yvelocity = 0; - obj->ypos = (obj->ypos & ~15) + triggers_get_dy(obj); + obj->ypos = (obj->ypos & ~15) + triggers_tile_get_yoffset(obj); obj->ypos16 = obj->ypos >> 4; obj->screen_ypos = obj->ypos - g_vars.screen_tilemap_yorigin; obj->sprite_type = 4; @@ -599,7 +597,7 @@ static void trigger_func_op40(struct object_t *obj) { if (counter == 400) { if (!g_vars.two_players_flag) { g_vars.level_completed_flag = 1; - } else if (g_vars.objects[OBJECT_NUM_PLAYER1].flag_end_level != 0 && g_vars.objects[OBJECT_NUM_PLAYER2].flag_end_level != 0) { + } else if (g_vars.objects[OBJECT_NUM_PLAYER1].level_complete_flag != 0 && g_vars.objects[OBJECT_NUM_PLAYER2].level_complete_flag != 0) { g_vars.level_completed_flag = 1; } } @@ -764,9 +762,9 @@ void triggers_update_tiles1(struct object_t *obj) { } if (p[8] != 0) { if (obj->data5F == g_vars.level + 1) { // music instrument must have been found to complete the level - if (obj->flag_end_level == 0) { + if (obj->level_complete_flag == 0) { play_sound(SOUND_13); - obj->flag_end_level = 1; + obj->level_complete_flag = 1; if (_di == 0) { do_level_update_tile(obj->xpos16, obj->ypos16, p[2]); } else { @@ -784,10 +782,8 @@ void triggers_update_tiles1(struct object_t *obj) { } g_vars.triggers_counter = 0; } - } else if (g_vars.triggers_counter == 0) { g_vars.triggers_counter = 1; - } } else { if (_di == 0) { @@ -890,7 +886,7 @@ void triggers_update_tiles1(struct object_t *obj) { } } -int16_t triggers_get_dy(struct object_t *obj) { +int16_t triggers_tile_get_yoffset(struct object_t *obj) { const uint8_t *p = lookup_sql(obj->xpos16, obj->ypos16); const int num = p[0]; return g_res.triggers[num].op_table1[obj->xpos & 15] - 1; diff --git a/unpack.c b/bb/unpack.c similarity index 100% rename from unpack.c rename to bb/unpack.c diff --git a/unpack.h b/bb/unpack.h similarity index 100% rename from unpack.h rename to bb/unpack.h diff --git a/bbja1.png b/bbja1.png new file mode 100644 index 0000000..67b3f4e Binary files /dev/null and b/bbja1.png differ diff --git a/bbja2.png b/bbja2.png new file mode 100644 index 0000000..1c20962 Binary files /dev/null and b/bbja2.png differ diff --git a/intern.h b/intern.h index 6ea6678..55c9760 100644 --- a/intern.h +++ b/intern.h @@ -9,20 +9,58 @@ #include #include -#ifndef ARRAYSIZE #define ARRAYSIZE(a) (sizeof(a)/sizeof(a[0])) -#endif +#define SWAP(x, y) do { typeof(x) tmp = x; x = y; y = tmp; } while(0) -static inline uint16_t READ_BE_UINT16(const uint8_t *p) { - return (p[0] << 8) | p[1]; +#undef MIN +static inline int MIN(int a, int b) { + return (a < b) ? a : b; +} + +#undef MAX +static inline int MAX(int a, int b) { + return (a > b) ? a : b; } static inline uint32_t READ_BE_UINT32(const uint8_t *p) { return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]; } +static inline uint16_t READ_BE_UINT16(const uint8_t *p) { + return (p[0] << 8) | p[1]; +} + static inline uint16_t READ_LE_UINT16(const uint8_t *p) { return p[0] | (p[1] << 8); } +static inline void WRITE_LE_UINT16(uint8_t *p, uint16_t value) { + p[0] = value & 0xFF; + p[1] = value >> 8; +} + +#define GAME_SCREEN_W g_options.screen_w +#define GAME_SCREEN_H g_options.screen_h + +struct options_t { + uint32_t cheats; + int start_level; + int start_xpos16; + int start_ypos16; + int screen_w; + int screen_h; + // 'bb' only options + bool amiga_copper_bars; + bool amiga_colors; + bool amiga_status_bar; + bool dos_scrolling; + bool cga_colors; +}; + +struct game_t { + const char *name; + //int (*detect)(const char *data_path); + void (*run)(const char *data_path); +}; + #endif diff --git a/ja/game.c b/ja/game.c new file mode 100644 index 0000000..4d01073 --- /dev/null +++ b/ja/game.c @@ -0,0 +1,361 @@ + +/* game screens */ + +#include "game.h" +#include "resource.h" +#include "sys.h" + +struct vars_t g_vars; + +void update_input() { + g_sys.process_events(); + + g_vars.input_key_left = (g_sys.input.direction & INPUT_DIRECTION_LEFT) != 0 ? 0xFF : 0; + g_vars.input_key_right = (g_sys.input.direction & INPUT_DIRECTION_RIGHT) != 0 ? 0xFF : 0; + g_vars.input_key_up = (g_sys.input.direction & INPUT_DIRECTION_UP) != 0 ? 0xFF : 0; + g_vars.input_key_down = (g_sys.input.direction & INPUT_DIRECTION_DOWN) != 0 ? 0xFF : 0; + g_vars.input_key_space = g_sys.input.space ? 0xFF : 0; + + g_vars.input_keystate[2] = g_sys.input.digit1; + g_vars.input_keystate[3] = g_sys.input.digit2; + g_vars.input_keystate[4] = g_sys.input.digit3; +} + +static void wait_input(int timeout) { + const uint32_t end = g_sys.get_timestamp() + timeout * 10; + while (g_sys.get_timestamp() < end) { + g_sys.process_events(); + if (g_sys.input.quit || g_sys.input.space) { + break; + } + g_sys.sleep(20); + } +} + +static void do_splash_screen() { + load_file("titus.eat"); + video_copy_vga(0x7D00); + fade_in_palette(); + fade_out_palette(); + load_file("tiny.eat"); + video_copy_vga(0x7D00); + fade_in_palette(); + fade_out_palette(); +} + +static void scroll_screen_palette() { + g_vars.level_time += 3; + if (g_vars.level_time >= 90) { + g_vars.level_time = 0; + } + const int count = 90 - g_vars.level_time; + for (int i = 0; i < count; i += 3) { + g_sys.set_palette_color(225 + i / 3, g_res.tmp + 225 * 3 + g_vars.level_time + i); + } + g_sys.update_screen(g_res.vga, 1); +} + +static void do_select_screen_scroll_palette(int start, int end, int step, int count) { + uint8_t *palette_buffer = g_res.tmp; + do { + for (int i = start * 3; i < end * 3; ++i) { + int color = g_vars.palette_buffer[i]; + if ((step > 0 && color != palette_buffer[i]) || (step < 0 && color != 0)) { + color += step; + } + g_vars.palette_buffer[i] = color; + } + g_sys.set_screen_palette(g_vars.palette_buffer + start * 3, start, end - start + 1, 6); + g_sys.update_screen(g_res.vga, 1); + g_sys.sleep(20); + } while (--count != 0); +} + +static void do_select_screen_scroll_palette_pattern1() { + do_select_screen_scroll_palette(0x10, 0x4F, -1, 0x19); +} + +static void do_select_screen_scroll_palette_pattern2() { + do_select_screen_scroll_palette(0x60, 0x9F, -1, 0x19); +} + +static void do_select_screen_scroll_palette_pattern3() { + do_select_screen_scroll_palette(0x10, 0x4F, 1, 0x19); +} + +static void do_select_screen_scroll_palette_pattern4() { + do_select_screen_scroll_palette(0x60, 0x9F, 1, 0x19); +} + +static void do_select_screen() { + load_file("select.eat"); + video_copy_vga(0x7D00); + fade_in_palette(); + memcpy(g_vars.palette_buffer, g_res.tmp, 256 * 3); + do_select_screen_scroll_palette_pattern2(); + int bl = 2; + while (!g_sys.input.quit) { + int bh = bl; + video_vsync(60); + if (g_sys.input.direction & INPUT_DIRECTION_RIGHT) { + g_sys.input.direction &= ~INPUT_DIRECTION_RIGHT; + ++bl; + bl &= 3; + if (bl == 0) { + bl = 1; + } + video_vsync(60); + } + if (g_sys.input.direction & INPUT_DIRECTION_LEFT) { + g_sys.input.direction &= ~INPUT_DIRECTION_LEFT; + --bl; + bl &= 3; + if (bl == 0) { + bl = 3; + } + video_vsync(60); + } + bh ^= bl; + if (bh & 1) { + if (bl & 1) { + do_select_screen_scroll_palette_pattern4(); + } else { + do_select_screen_scroll_palette_pattern2(); + } + } + if (bh & 2) { + if (bl & 2) { + do_select_screen_scroll_palette_pattern3(); + } else { + do_select_screen_scroll_palette_pattern1(); + } + } + if (g_sys.input.space) { + assert(bl == 1 || bl == 2); + g_sys.input.space = 0; + g_vars.player = 1 - ((bl & 3) - 1); + fade_out_palette(); + break; + } + update_input(); + g_sys.sleep(30); + } +} + +void do_difficulty_screen() { + char name[16]; + snprintf(name, sizeof(name), "dif%02d.eat", (g_vars.level >> 3) + 1); + load_file(name); + video_copy_vga(0x7D00); + fade_in_palette(); + wait_input(560); + fade_out_palette(); +} + +void do_level_number_screen() { + load_file("fond.eat"); + video_draw_string("LEVEL NUMBER", 0x5E0C, 11); + char buf[8]; + snprintf(buf, sizeof(buf), "%02d", g_vars.level); + video_draw_string(buf, 0x5E9B, 11); + video_copy_vga(0x7D00); + fade_in_palette(); + fade_out_palette(); +} + +static uint16_t get_password(uint16_t ax) { + // ax = read(0xF000:0xFFFF, 16) // bios + ax ^= 0xAA31; + // rol ax, cpu_speed + return ax; +} + +void do_level_password_screen() { + load_file("password.eat"); + uint16_t ax = get_password(g_vars.player * 50 + g_vars.level - 1); + char str[5]; + for (int i = 0; i < 4; ++i, ax <<= 4) { + static const uint8_t rev_bits[] = { + 0x0, 0x8, 0x4, 0xC, 0x2, 0xA, 0x6, 0xE, 0x1, 0x9, 0x5, 0xD, 0x3, 0xB, 0x7, 0xF + }; + const uint8_t dx = rev_bits[(ax >> 15) & 15] + '0'; + str[i] = (dx <= '9') ? dx : (dx + 7); + } + str[4] = 0; + video_draw_string("STAGE NUMBER", 0x7E96, 11); + video_draw_string(str, 0xABB4, 20); + video_copy_vga(0x7D00); + fade_in_palette(); + scroll_screen_palette(); + wait_input(64000); + fade_out_palette(); +} + +static void do_password_screen() { + load_file("password.eat"); + video_draw_string("ENTER PASSWORD", 0x7E96, 11); + fade_in_palette(); + char str[5] = "0000"; + video_draw_string(str, 0xABB4, 20); +} + +static int do_menu_screen() { + load_file("menu.eat"); + video_copy_vga(0x7D00); + fade_in_palette(); + memset(g_vars.input_keystate, 0, sizeof(g_vars.input_keystate)); + g_vars.level_time = 0; + while (!g_sys.input.quit) { + scroll_screen_palette(); + if (g_vars.input_keystate[2] || g_vars.input_keystate[0x4F] || g_sys.input.space) { + g_sys.input.space = 0; + fade_out_palette(); + return 1; + } + if (g_vars.input_keystate[3] || g_vars.input_keystate[0x50]) { + fade_out_palette(); + return 2; + } + if (g_vars.input_keystate[4] || g_vars.input_keystate[0x51]) { + return 3; + } + update_input(); + g_sys.sleep(30); + } + return 0; +} + +static int do_options_screen() { + fade_out_palette(); + load_file("fond.eat"); + video_draw_string("GAME SPEED", 0x3EE9, 11); + video_draw_string("1 FAST", 0x647E, 11); + video_draw_string("2 NORMAL", 0x89FE, 11); + video_copy_vga(0x7D00); + fade_in_palette(); + memset(g_vars.input_keystate, 0, sizeof(g_vars.input_keystate)); + while (!g_sys.input.quit) { + scroll_screen_palette(); + if (g_vars.input_keystate[2] || g_vars.input_keystate[0x4F]) { + fade_out_palette(); + return 1; + } + if (g_vars.input_keystate[3] || g_vars.input_keystate[0x50]) { + fade_out_palette(); + return 2; + } + update_input(); + g_sys.sleep(30); + } + return 0; +} + +void do_game_over_screen() { + load_file("fond.eat"); + video_draw_string("GAME OVER", 0x5E2E, 11); + video_copy_vga(0x7D00); + fade_in_palette(); + wait_input(64000); + fade_out_palette(); +} + +void do_game_win_screen() { + load_file("win.eat"); + video_copy_vga(0x7D00); + fade_in_palette(); + fade_out_palette(); + load_file("end.eat"); + video_copy_vga(0x7D00); + static const int count = 5; + static const struct { + uint16_t offset; + const char *str; + } text[] = { + { 0x0F68, "CONGRATULATION" }, + { 0x3B34, "YOU HAVE BEATEN" }, + { 0x5CE8, "THE EVIL JUKEBOXE" }, + { 0x7EB1, "NOW YOU ARE FREE" }, + { 0xA072, "AND YOUR CONCERT" }, + { 0xC22D, "WILL BE A SUCCESS" }, + { 0xFFFF, 0 }, + { 0x33BE, "PC CONVERSION" }, + { 0x5590, "ERIC ZMIRO" }, + { 0x864D, "PC GRAPHICS" }, + { 0xA7FC, "DIDIER CARRERE" }, + { 0xFFFF, 0 }, + { 0x33AF, "ORIGINAL VERSION" }, + { 0x5594, "ERIC CAEN" }, + { 0x862B, "ORIGINAL GRAPHICS" }, + { 0xA835, "BOB" }, + { 0xFFFF, 0 }, + { 0x33B1, "ORIGINAL MUSICS" }, + { 0x559A, "DIMITRI" }, + { 0x8653, "PC MUSICS" }, + { 0xA7F2, "MICHAEL KNAEPEN" }, + { 0xFFFF, 0 }, + { 0x5A8F, "THANK YOU" }, + { 0x8008, "FOR PLAYING" }, + { 0xFFFF, 0 } + }; + int i = 0; + for (int j = 0; j < count; ++j) { + for (; text[i].str; ++i) { + video_draw_string(text[i].str, text[i].offset, 11); + } + ++i; + video_copy_vga(0x7D00); + fade_in_palette(); + wait_input(64000); + fade_out_palette(); + video_copy_vga(0x7D00); + } +} + +void game_main() { + play_music(0); + do_splash_screen(); + g_sys.set_screen_palette(common_palette_data, 0, 128, 6); + video_load_sprites(); + render_set_sprites_clipping_rect(0, 0, TILEMAP_SCREEN_W, TILEMAP_SCREEN_H); + while (!g_sys.input.quit) { + update_input(); + g_vars.level = g_options.start_level; + if (g_vars.level == 0) { + g_vars.level = 1; + } + const int ret = do_menu_screen(); + g_vars.players_table[0].lifes_count = 3; + g_vars.players_table[1].lifes_count = 3; + if (ret == 0) { + break; + } else if (ret == 1) { + do_select_screen(); + do_difficulty_screen(); + } else if (ret == 2) { + g_vars.level = -1; + do_password_screen(); + if (g_vars.level < 0) { + continue; + } + ++g_vars.level; + do_difficulty_screen(); + } else { + do_options_screen(); + continue; + } + do_level(); + } +} + +static void game_run(const char *data_path) { + res_init(data_path, GAME_SCREEN_W * GAME_SCREEN_H); + sound_init(); + game_main(); + sound_fini(); + res_fini(); +} + +struct game_t game = { + "Blues Brothers : Jukebox Adventure", + game_run +}; diff --git a/ja/game.h b/ja/game.h new file mode 100644 index 0000000..12b7872 --- /dev/null +++ b/ja/game.h @@ -0,0 +1,225 @@ + +#ifndef GAME_H__ +#define GAME_H__ + +#include "intern.h" + +#define CHEATS_OBJECT_NO_HIT (1 << 0) +#define CHEATS_ONE_HIT_VINYL (1 << 1) +#define CHEATS_DECOR_NO_HIT (1 << 2) + +extern struct options_t g_options; + +#define TILEMAP_SCREEN_W GAME_SCREEN_W +#define TILEMAP_SCREEN_H (GAME_SCREEN_H - PANEL_H) + +#define PANEL_H 24 + +#define SOUND_0 0 +#define SOUND_1 1 // monster knocked out +#define SOUND_2 2 // bouncing mushroom +#define SOUND_3 3 // decor hit +#define SOUND_4 4 // player hit +#define SOUND_5 5 // grab vinyl +#define SOUND_8 8 // no vinyl +#define SOUND_9 9 // throw vinyl +#define SOUND_10 10 // "rock,rock,rock and roll" +#define SOUND_11 11 +#define SOUND_14 14 // player lost life +#define SOUND_15 15 // running out time + +struct object_t { + int16_t x_pos; + int16_t y_pos; + uint16_t spr_num; + union { + uint8_t b[2]; + int16_t w; + const uint8_t *p; + } data[11]; // 0x6..0x1C +}; + +enum { + PLAYER_FLAGS_STAIRS = 2, + PLAYER_FLAGS_FACING_LEFT = 4, + PLAYER_FLAGS_THROW_VINYL = 8, + PLAYER_FLAGS_POWERUP = 0x20, + PLAYER_FLAGS_JAKE = 0x80 +}; + +#define player_obj_num(obj) (obj)->data[0].w // 0x6 +#define player_prev_spr_num(obj) (obj)->data[1].w // 0x8 +#define player_anim_data(obj) (obj)->data[2].p // 0xA +#define player_idle_counter(obj) (obj)->data[3].b[0] // 0xC +#define player_power(obj) (obj)->data[3].b[1] // 0xD +#define player_x_delta(obj) (obj)->data[4].w // 0xE +#define player_y_delta(obj) (obj)->data[5].w // 0x10 +#define player_flags(obj) (obj)->data[6].b[0] // 0x12 +#define player_jump_counter(obj) (obj)->data[6].b[1] // 0x13 +#define player_bounce_counter(obj) (obj)->data[7].b[0] // 0x14 +#define player_tilemap_offset(obj) (obj)->data[8].w // 0x16 +#define player_hit_counter(obj) (obj)->data[9].b[0] // 0x18 +#define player_throw_counter(obj) (obj)->data[9].b[1] // 0x19 +#define player_flags2(obj) (obj)->data[10].b[0] // 0x1A, 8:dead 1:blocked + +#define object_blinking_counter(obj) (obj)->data[6].b[1] // 0x13, shield, jump + +// star +#define object2_spr_count(obj) (obj)->data[0].b[0] +#define object2_spr_tick(obj) (obj)->data[0].b[1] + +// vinyl +#define object22_xvelocity(obj) (obj)->data[0].w +#define object22_damage(obj) (obj)->data[1].w +#define object22_player_num(obj) (obj)->data[2].w + +// crate +#define object64_counter(obj) (obj)->data[1].w +#define object64_yvelocity(obj) (obj)->data[2].w + +// monster +#define object82_state(obj) (obj)->data[0].b[0] +#define object82_type(obj) (obj)->data[0].b[1] +#define object82_anim_data(obj) (obj)->data[1].p +#define object82_hflip(obj) (obj)->data[3].b[0] +#define object82_counter(obj) (obj)->data[3].b[1] +#define object82_x_delta(obj) (obj)->data[4].w +#define object82_y_delta(obj) (obj)->data[5].w +#define object82_energy(obj) (obj)->data[8].w +#define object82_type0_init_data(obj) (obj)->data[4].p +#define object82_type0_player_num(obj) (obj)->data[5].w +#define object82_type0_x_delta(obj) (obj)->data[6].b[0] // signed +#define object82_type0_y_delta(obj) (obj)->data[7].b[0] // signed +#define object82_type1_hdir(obj) (obj)->data[6].b[0] // signed + +#define object102_x_delta(obj) (obj)->data[0].w +#define object102_y_delta(obj) (obj)->data[1].w + +struct player_t { + struct object_t obj; + int16_t unk_counter; // 0x1D always zero + int16_t change_hdir_counter; + uint8_t lifes_count; + int16_t vinyls_count; + int8_t energy; + uint8_t dir_mask; // 0x25 + uint8_t ticks_counter; // 0x26 +}; + +#define TRIGGERS_COUNT 36 +#define LEVELS_COUNT 30 +#define OBJECTS_COUNT 165 +// offset | count | comment +// 2 20 animated tiles/vinyls +// 22 10 vinyls +// 32 8 * 4 (rotating) platforms +// 64 8 crates +// 72 10 bonuses spr_num:190 +// 82 20 monsters +// 102 10 +// 112 9 vertical bars +// 121 8 dragon monster (level 26) + +struct vars_t { + int level; + int player; + bool input_keystate[128]; + uint32_t timestamp; + uint8_t input_key_left, input_key_right, input_key_down, input_key_up, input_key_space; + uint16_t buffer[128 * 2]; // level objects state 0xFFFF, 0xFF20 or g_vars.objects_table index + int16_t dragon_coords[1 + 128]; + struct player_t players_table[2]; + uint8_t player_hit_counter; + int16_t player_xscroll, player_map_offset; + struct object_t objects_table[OBJECTS_COUNT]; + int level_start_1p_x_pos, level_start_1p_y_pos; + int level_start_2p_player1_x_pos, level_start_2p_player1_y_pos, level_start_2p_player2_x_pos, level_start_2p_player2_y_pos; + int level_loop_counter; + int throw_vinyl_counter; + uint8_t level_num; + uint16_t level_time, level_time2; + uint8_t level_time_counter; + uint8_t triggers_table[19 + TRIGGERS_COUNT * 16]; + int tilemap_x, tilemap_y, tilemap_w, tilemap_h; + int tilemap_end_x, tilemap_end_y; + int tilemap_scroll_dx, tilemap_scroll_dy, tilemap_scroll_xoffset, tilemap_scroll_yoffset; + uint8_t *tilemap_data; + uint16_t level_pos_num; + uint8_t tilemap_type, tilemap_flags; + uint8_t tilemap_lut_type[0x100]; // type:0,1,2 + uint8_t tilemap_lut_init[6 * 0x100]; + uint8_t tilemap_lut_init2[0x100]; + const uint8_t *tilemap_current_lut; + uint8_t tile_palette_table[0x100]; + const uint8_t *level_tiles_lut; + uint8_t palette_buffer[256 * 3]; + bool change_next_level_flag; + bool reset_palette_flag; +}; + +extern struct vars_t g_vars; + +/* staticres.c */ +extern const uint16_t sound_offsets[]; +extern const uint16_t sprite_offsets[]; +extern const uint16_t sprite_sizes[]; +extern const uint8_t sprite_palettes[]; +extern const uint8_t level_data1p[]; +extern const uint8_t level_data2p[]; +extern const uint8_t level_data3[]; +extern const uint8_t level_data4[]; +extern const uint8_t monster_spr_anim_data0[]; +extern const uint8_t monster_spr_anim_data1[]; +extern const uint8_t monster_spr_anim_data2[]; +extern const uint8_t monster_spr_anim_data3[]; +extern const uint8_t monster_spr_anim_data4[]; +extern const uint8_t monster_spr_anim_data6[]; +extern const uint8_t monster_spr_anim_data8[]; +extern const uint8_t monster_spr_anim_data9[]; +extern const uint8_t common_palette_data[]; +extern const uint8_t levels_palette_data[]; +extern const uint8_t tile_lut_data0[]; +extern const uint8_t tile_lut_data1[]; +extern const uint8_t tile_lut_data2[]; +extern const uint8_t tile_lut_data3[]; +extern const uint16_t tile_funcs0[]; +extern const uint16_t tile_funcs1[]; +extern const uint16_t tile_funcs2[]; +extern const uint16_t tile_funcs3[]; +extern const uint8_t level_data_tiles_lut[]; +extern const uint8_t player_anim_dy[]; +extern const uint8_t player_anim_lut[]; +extern const uint8_t *player_anim_table[]; + +/* game.c */ +extern void update_input(); +extern void game_main(); +extern void do_game_over_screen(); +extern void do_game_win_screen(); +extern void do_difficulty_screen(); +extern void do_level_password_screen(); +extern void do_level_number_screen(); + +/* level.c */ +extern void do_level(); + +/* screen.c */ +extern void video_draw_dot_pattern(int offset); +extern void video_draw_sprite(int num, int x, int y, int flag); +extern void video_draw_string(const char *s, int offset, int hspace); +extern void video_copy_vga(int size); +extern void video_vsync(int delay); +extern void fade_in_palette(); +extern void fade_out_palette(); +extern void ja_decode_spr(const uint8_t *src, int w, int h, uint8_t *dst, int dst_pitch, uint8_t pal_mask); +extern void ja_decode_chr(const uint8_t *buffer, const int size, uint8_t *dst, int dst_pitch); +extern void ja_decode_tile(const uint8_t *buffer, uint8_t pal_mask, uint8_t *dst, int dst_pitch, int x, int y); +extern void video_load_sprites(); + +/* sound.c */ +extern void sound_init(); +extern void sound_fini(); +extern void play_sound(int num); +extern void play_music(int num); + +#endif /* GAME_H__ */ diff --git a/ja/level.c b/ja/level.c new file mode 100644 index 0000000..d9b7a59 --- /dev/null +++ b/ja/level.c @@ -0,0 +1,3039 @@ + +/* level main loop */ + +#include "game.h" +#include "resource.h" +#include "sys.h" +#include "util.h" + +static const char *_background[] = { + "back0.eat", "back1.eat", "back2.eat", "back3.eat" +}; + +static const char *_decor[] = { + "jardin.eat", "prison.eat", "entrepot.eat", "egout.eat" +}; + +static const uint16_t _vinyl_throw_delay = 5; +static const uint16_t _object_respawn_delay = 224; +static const uint16_t _undefined = 0x55AA; + +static void level_player_draw_powerdown_animation(struct player_t *player); +static void level_player_draw_powerup_animation(struct player_t *player, struct object_t *obj); +static void level_update_tiles(struct object_t *obj, int ax, int dx, int bp); + +static void level_player_die(struct player_t *player) { + g_vars.player_xscroll = 0; + player_flags2(&player->obj) |= 8; + level_player_draw_powerdown_animation(player); + if (player_flags(&player->obj) & 0x40) { + // player_flags2(&player->other_player->obj) &= ~1; + } + --player->lifes_count; + play_sound(SOUND_14); +} + +static void level_reset_palette() { + if (g_vars.reset_palette_flag) { + return; + } + g_vars.reset_palette_flag = 1; + memcpy(g_vars.palette_buffer, common_palette_data, 384); + const uint8_t *pal = levels_palette_data + g_vars.level_num * 144 * 3; + memcpy(g_vars.palette_buffer + 384, pal, 384); pal += 384; + if ((g_vars.level & 1) != 0) { + memcpy(g_vars.palette_buffer + 384 - 48, pal, 48); + } + for (int i = 0; i < 8; ++i) { + uint8_t *dst = g_vars.palette_buffer + 384 + 48 * i; + dst[0] = 0; + dst[1] = 0x20; + dst[2] = 0x3F; + } + g_sys.set_screen_palette(g_vars.palette_buffer, 0, 256, 6); +} + +static void level_update_palette() { + if (g_vars.level_time == 12 || g_vars.level_time2 == g_vars.level_time) { + return; + } + if (g_vars.level_time2 < g_vars.level_time) { + g_vars.level_time2 = g_vars.level_time; + g_vars.reset_palette_flag = 0; + level_reset_palette(); + return; + } + g_vars.level_time2 = g_vars.level_time; + if (g_vars.level_time < 5) { + play_sound(SOUND_15); + uint8_t *palette_buffer = g_vars.palette_buffer + 128 * 3; + for (int i = 0; i < 128 * 3; ++i) { + const int color = palette_buffer[i]; + palette_buffer[i] = MAX(0, color - 8); + } + g_sys.set_screen_palette(palette_buffer, 128, 128, 6); + if (g_vars.level_time == 0) { + level_player_die(&g_vars.players_table[0]); + level_player_die(&g_vars.players_table[1]); + play_sound(SOUND_14); + } + } +} + +static uint8_t level_lookup_tile(uint8_t num) { + uint8_t _al = g_vars.tilemap_lut_type[num]; + if (_al == 2) { + if ((g_vars.tilemap_flags & 2) != 0 && (g_vars.level_loop_counter & 0x20) != 0) { + return 0; + } + if ((g_vars.tilemap_flags & 4) == 0) { + const uint8_t flags = g_vars.tilemap_lut_init2[num] ^ g_vars.tilemap_flags; + if (flags & 1) { + return 0; + } + } + } + _al = g_vars.tilemap_current_lut[num]; + if (_al == 248 && (g_vars.tilemap_flags & 1) != 0) { + ++_al; + } + return _al; +} + +static uint8_t level_get_tile(int offset) { + if (offset < 0 || offset >= g_vars.tilemap_w * g_vars.tilemap_h) { + print_warning("Invalid tilemap offset %d", offset); + return 0; + } + int num = g_vars.tilemap_data[offset]; + num = level_lookup_tile(num); + return g_vars.level_tiles_lut[num]; +} + +static void level_draw_tile(uint8_t tile_num, int x, int y, int dx, int dy) { + tile_num = level_lookup_tile(tile_num); + // video_set_palette_index(g_vars.tile_palette_table[tile_num]); + ja_decode_tile(g_res.tmp + tile_num * 128, 0x80 | (g_vars.tile_palette_table[tile_num] * 0x10), g_res.vga, GAME_SCREEN_W, x * 16 - dx, y * 16 - dy); +} + +static void level_draw_tilemap() { + for (int y = 0; y < MIN(200, GAME_SCREEN_H) - PANEL_H; ++y) { + for (int x = 0; x < GAME_SCREEN_W; x += 320) { + memcpy(g_res.vga + y * GAME_SCREEN_W + x, g_res.background + y * 320, MIN(320, GAME_SCREEN_W - x)); + } + } + const uint8_t *current_lut = g_vars.tilemap_current_lut + 0x100; + if (current_lut >= &g_vars.tilemap_lut_init[0x600]) { + g_vars.tilemap_current_lut = g_vars.tilemap_lut_init; + } else { + g_vars.tilemap_current_lut = current_lut; + } + for (int y = 0; y < TILEMAP_SCREEN_H / 16 + 1; ++y) { + for (int x = 0; x < TILEMAP_SCREEN_W / 16 + 1; ++x) { + const int offset = (g_vars.tilemap_y + y) * g_vars.tilemap_w + (g_vars.tilemap_x + x); + const uint8_t num = g_vars.tilemap_data[offset]; + level_draw_tile(num, x, y, g_vars.tilemap_scroll_dx << 2, g_vars.tilemap_scroll_dy); + } + } +} + +static void level_adjust_hscroll_right(int dx) { + if (g_vars.tilemap_x >= g_vars.tilemap_end_x) { + const int8_t _al = 3 - g_vars.tilemap_scroll_dx; + if (_al < dx) { + dx = _al; + } + } + g_vars.tilemap_scroll_dx += dx; + if (g_vars.tilemap_scroll_dx < 4) { + return; + } + g_vars.tilemap_scroll_dx -= 4; + ++g_vars.tilemap_x; + g_vars.tilemap_scroll_xoffset += 4; + if (g_vars.tilemap_scroll_xoffset > 84) { + g_vars.tilemap_scroll_xoffset -= 84; + } +} + +static void level_adjust_hscroll_left(int dx) { + g_vars.tilemap_scroll_dx -= dx; + if (g_vars.tilemap_scroll_dx >= 0) { + return; + } + g_vars.tilemap_scroll_dx += 4; + --g_vars.tilemap_x; + if (g_vars.tilemap_x < 0) { + g_vars.tilemap_x = 0; + g_vars.tilemap_scroll_dx = 0; + g_vars.tilemap_scroll_xoffset = 0; + } else { + g_vars.tilemap_scroll_xoffset -= 4; + if (g_vars.tilemap_scroll_xoffset < 0) { + g_vars.tilemap_scroll_xoffset += 84; + } + } +} + +static void level_adjust_vscroll_down(int dy) { + if (g_vars.tilemap_y >= g_vars.tilemap_end_y) { + const int8_t _al = 15 - g_vars.tilemap_scroll_dy; + if (_al < dy) { + dy = _al; + } + } + g_vars.tilemap_scroll_dy += dy; + if (g_vars.tilemap_scroll_dy < 16) { + return; + } + g_vars.tilemap_scroll_dy -= 16; + ++g_vars.tilemap_scroll_yoffset; + ++g_vars.tilemap_y; + if (g_vars.tilemap_scroll_yoffset >= 12) { + g_vars.tilemap_scroll_yoffset -= 12; + } +} + +static void level_adjust_vscroll_up(int dy) { + g_vars.tilemap_scroll_dy -= dy; + if (g_vars.tilemap_scroll_dy >= 0) { + return; + } + g_vars.tilemap_scroll_dy += 16; + --g_vars.tilemap_y; + if (g_vars.tilemap_y < 0) { + g_vars.tilemap_scroll_dy = 0; + g_vars.tilemap_y = 0; + g_vars.tilemap_scroll_yoffset = 0; + } else { + --g_vars.tilemap_scroll_yoffset; + if (g_vars.tilemap_scroll_yoffset < 0) { + g_vars.tilemap_scroll_yoffset += 12; + } + } +} + +static void level_hit_player(struct player_t *p) { + player_flags(&g_vars.players_table[0].obj) &= ~0x40; + player_flags(&g_vars.players_table[1].obj) &= ~0x40; + player_flags2(&g_vars.players_table[0].obj) &= ~1; + player_flags2(&g_vars.players_table[1].obj) &= ~1; + g_vars.player_hit_counter = 7; +} + +static void level_player_hit_object(struct player_t *p, struct object_t *obj) { + if (g_options.cheats & CHEATS_OBJECT_NO_HIT) { + return; + } + if (object_blinking_counter(obj) != 0 || g_vars.level_loop_counter <= 20) { + return; + } + play_sound(SOUND_4); + if ((player_flags(&p->obj) & PLAYER_FLAGS_POWERUP) == 0 && (--p->energy < 0)) { + p->energy = 0; + level_player_die(p); + } + player_hit_counter(&p->obj) = 8; + player_y_delta(&p->obj) = -40; + if (player_flags(&p->obj) & PLAYER_FLAGS_POWERUP) { + level_player_draw_powerdown_animation(p); + } + level_hit_player(p); + object_blinking_counter(obj) = 33; +} + +static void level_player_update_xvelocity(struct player_t *player, int16_t x) { + if ((player_flags2(&player->obj) & 2) == 0) { + x *= 8; + if (x == 0) { + if (player_x_delta(&player->obj) != 0) { + player_x_delta(&player->obj) += (player_x_delta(&player->obj) >= 0) ? -8 : 8; + const int dx = abs(player_x_delta(&player->obj)); + if (dx < 8) { + player_x_delta(&player->obj) = 0; + } + } + } else if (x < 0) { + if (player_x_delta(&player->obj) + x < -32) { + player_x_delta(&player->obj) = -32; + } else { + player_x_delta(&player->obj) += x; + } + } else { + if (player_x_delta(&player->obj) + x > 32) { + player_x_delta(&player->obj) = 32; + } else { + player_x_delta(&player->obj) += x; + } + } + } +} + +static struct object_t *find_object(int offset, int count, int stride) { + for (int i = 0; i < count; ++i) { + struct object_t *obj = &g_vars.objects_table[offset + i * stride]; + if (obj->spr_num == 0xFFFF) { + return obj; + } + } + print_warning("No free object %d count %d", offset, count / stride); + return 0; +} + +// small stars +static void level_init_object_spr_num_46(int x_pos, int y_pos) { + struct object_t *obj = find_object(2, 20, 1); + if (obj) { + obj->x_pos = x_pos; + obj->y_pos = y_pos; + obj->spr_num = 46; + object2_spr_count(obj) = 4; + object2_spr_tick(obj) = 1; + object_blinking_counter(obj) = 0; + } +} + +// large star +static void level_init_object_spr_num_92(int x_pos, int y_pos) { + struct object_t *obj = find_object(2, 20, 1); + if (obj) { + obj->x_pos = x_pos; + obj->y_pos = y_pos; + obj->spr_num = 92; + object2_spr_count(obj) = 4; + object2_spr_tick(obj) = 1; + object_blinking_counter(obj) = 0; + } +} + +// light +static void level_init_object_spr_num_98(int x_pos, int y_pos) { + struct object_t *obj = find_object(2, 20, 1); + if (obj) { + obj->x_pos = x_pos; + obj->y_pos = y_pos; + obj->spr_num = 98; + object2_spr_count(obj) = 4; + object2_spr_tick(obj) = 1; + object_blinking_counter(obj) = 0; + } +} + +// light +static void level_init_object_spr_num_98_align_pos(int x_pos, int y_pos) { + struct object_t *obj = find_object(2, 20, 1); + if (obj) { + obj->x_pos = (x_pos << 4) + 8; + obj->y_pos = (y_pos << 4) + 12; + obj->spr_num = 98; + object2_spr_count(obj) = 6; + object2_spr_tick(obj) = 1; + object_blinking_counter(obj) = 0; + } +} + +// vinyl fade animation +static void level_init_object_spr_num_104(int x_pos, int y_pos) { + struct object_t *obj = find_object(2, 20, 1); + if (obj) { + obj->x_pos = (x_pos << 4) + 8; + obj->y_pos = (y_pos << 4) + 8; + obj->spr_num = 104; + object2_spr_count(obj) = 6; + object2_spr_tick(obj) = 3; + object_blinking_counter(obj) = 0; + } +} + +static void level_update_bouncing_tiles(struct player_t *player) { + const int offset = player_tilemap_offset(&player->obj); + uint8_t *p = &g_vars.tilemap_data[offset]; + int num = *p; + int adj = 2; + if (num > 0x20 && (num <= 0x22 || num > 0x9C)) { + adj = -adj; + } + if ((num & 1) == 0) { + --p; + } + p[0] += adj; + p[1] += adj; + p[g_vars.tilemap_w] += adj; + p[g_vars.tilemap_w + 1] += adj; +} + +static int level_player_update_flags(struct player_t *player) { + if (player_flags2(&player->obj) & 0x40) { + return 1; + } + if (abs(player_x_delta(&player->obj)) < 24) { + return 0; + } + player_flags(&player->obj) &= ~PLAYER_FLAGS_FACING_LEFT; + if (player_x_delta(&player->obj) < 0) { + player_flags(&player->obj) |= PLAYER_FLAGS_FACING_LEFT; + } + if (g_vars.level_loop_counter - player->ticks_counter < 4) { + return 1; + } + player->ticks_counter = g_vars.level_loop_counter; + if (player_flags2(&player->obj) & 2) { + return 1; + } + level_init_object_spr_num_98(player->obj.x_pos, player->obj.y_pos); + play_sound(SOUND_11); + return 1; +} + +static void level_player_update_hdirection(struct player_t *player) { + if (g_vars.input_key_right) { + level_player_update_xvelocity(player, 2); + } else if (g_vars.input_key_left) { + level_player_update_xvelocity(player, -2); + } else { + level_player_update_xvelocity(player, 0); + } +} + +static void level_update_player_anim_1(struct player_t *player) { + player->obj.spr_num = 1; + level_player_update_xvelocity(player, 0); + if (player_throw_counter(&player->obj) != 0) { + player->obj.spr_num = 0; + } +} + +static void level_update_player_anim_5(struct player_t *player) { + level_player_update_hdirection(player); + player->obj.spr_num = 5; + if (player_jump_counter(&player->obj) != 0 && (player_flags2(&player->obj) & 1) == 0 && g_vars.input_key_up) { + --player_jump_counter(&player->obj); + const int dy = (int8_t)player_anim_dy[player_jump_counter(&player->obj)] + player_y_delta(&player->obj); + if (dy >= -70) { + player_y_delta(&player->obj) = dy; + } + } +} + +static void level_update_player_anim_2(struct player_t *player) { + if (player_jump_counter(&player->obj) != 7) { + level_update_player_anim_5(player); + } else { + player->obj.spr_num = 2; + level_player_update_hdirection(player); + } +} + +static void level_update_player_anim_3(struct player_t *player) { + level_player_update_hdirection(player); + player->obj.spr_num = 3; +} + +static void level_update_player_anim_4(struct player_t *player) { + player->obj.spr_num = 4; + level_player_update_xvelocity(player, 0); +} + +static void level_update_player_anim_34(struct player_t *player) { + player->obj.spr_num = 0; + if (level_player_update_flags(player)) { + player->obj.spr_num = 34; + } + level_player_update_xvelocity(player, 0); +} + +static void level_update_player_anim_0(struct player_t *player) { + if (player_flags2(&player->obj) & 1) { + level_update_player_anim_34(player); + } else if (player_jump_counter(&player->obj) != 7) { + level_update_player_anim_5(player); + } else if (player->obj.spr_num == 1) { + level_update_player_anim_1(player); + } else if (player_idle_counter(&player->obj) != 90) { + level_update_player_anim_34(player); + } else { + player->obj.spr_num = 1; + level_player_update_xvelocity(player, 0); + } +} + +// throw vinyl +static void level_update_player_anim_6(struct player_t *player) { + if (player->vinyls_count == 0) { + play_sound(SOUND_8); + level_update_player_anim_0(player); + } else { + level_player_update_xvelocity(player, 0); + player->obj.spr_num = 6; + if (player_throw_counter(&player->obj) == 0) { + return; + } + if (g_vars.level_loop_counter - g_vars.throw_vinyl_counter <= _vinyl_throw_delay) { + return; + } + g_vars.throw_vinyl_counter = g_vars.level_loop_counter; + struct object_t *obj = find_object(22, 10, 1); + if (obj) { + obj->spr_num = 42; // vinyl + object_blinking_counter(obj) = 0; + object22_player_num(obj) = player - &g_vars.players_table[0]; + obj->x_pos = player->obj.x_pos; + obj->y_pos = player->obj.y_pos - 12; + object22_xvelocity(obj) = (player_flags(&player->obj) & PLAYER_FLAGS_FACING_LEFT) ? -24 : 24; + int ax = ((player_flags(&player->obj) & PLAYER_FLAGS_POWERUP) != 0) ? 0x200 : 0x100; + if (player_power(&player->obj) >= 33) { + ax = 0x4000; + player->vinyls_count -= 4; + } + --player->vinyls_count; + object22_damage(obj) = ax; + player_flags(&player->obj) |= PLAYER_FLAGS_THROW_VINYL; + player_power(&player->obj) = 0; + play_sound(SOUND_9); + } + } +} + +static void level_update_player_anim_7(struct player_t *player) { + if (player->vinyls_count == 0) { + play_sound(SOUND_8); + level_update_player_anim_0(player); + } else { + player->obj.x_pos += 12; + level_update_player_anim_6(player); + player->obj.x_pos -= 12; + } + player->obj.spr_num = 7; +} + +static void level_update_player_anim_8(struct player_t *player) { + player->obj.spr_num = 8; + player->obj.y_pos -= 2; + player_jump_counter(&player->obj) = 7; + player_x_delta(&player->obj) = 0; + player_y_delta(&player->obj) = 0; +} + +static void level_update_player_anim_9(struct player_t *player) { + player->obj.spr_num = 9; + player_jump_counter(&player->obj) = 7; + player_x_delta(&player->obj) = 0; + player_y_delta(&player->obj) = 0; +} + +static void level_update_player_anim_10(struct player_t *player, int spr_num) { + player->obj.spr_num = spr_num; + player->obj.y_pos += 2; + const int x = (player->obj.x_pos & 15) - 8; + if (x < 0) { + ++player->obj.x_pos; + } else if (x > 0) { + --player->obj.x_pos; + } + player_jump_counter(&player->obj) = 7; + player_x_delta(&player->obj) = 0; + player_y_delta(&player->obj) = 0; +} + +static void level_update_player_anim_11(struct player_t *player) { + player->obj.spr_num = 9; + --player->obj.x_pos; + player_jump_counter(&player->obj) = 7; + player_x_delta(&player->obj) = 0; + player_y_delta(&player->obj) = 0; +} + +static void level_update_player_anim_12(struct player_t *player) { + player->obj.spr_num = 9; + ++player->obj.x_pos; + player_jump_counter(&player->obj) = 7; + player_x_delta(&player->obj) = 0; + player_y_delta(&player->obj) = 0; +} + +static void level_update_player_anim_18(struct player_t *player) { + level_player_update_hdirection(player); + player->obj.spr_num = 18; + if (player_jump_counter(&player->obj) != 0 && g_vars.input_key_up) { + --player_jump_counter(&player->obj); + player_y_delta(&player->obj) += (int8_t)player_anim_dy[player_jump_counter(&player->obj)]; + } +} + +static void level_update_player_anim_15(struct player_t *player) { + if (player_jump_counter(&player->obj) != 7) { + level_update_player_anim_18(player); + } else { + player->obj.spr_num = 15; + if (g_vars.input_key_right) { + level_player_update_xvelocity(player, 2); + } else if (g_vars.input_key_left) { + level_player_update_xvelocity(player, -2); + } else { + level_player_update_xvelocity(player, 0); + } + } +} + +static void level_update_player_anim_13(struct player_t *player) { + if (player_jump_counter(&player->obj) != 7) { + level_update_player_anim_18(player); + } else { + player->obj.spr_num = 13; + level_player_update_flags(player); + level_player_update_xvelocity(player, 0); + } +} + +static void level_update_player_anim_19(struct player_t *player) { + if (player->vinyls_count == 0) { + play_sound(SOUND_8); + level_update_player_anim_13(player); + } else { + level_update_player_anim_6(player); + player->obj.spr_num = 19; + } +} + +static void level_update_player_anim_26(struct player_t *player) { + level_player_update_hdirection(player); + player->obj.spr_num = 26; + if (player_jump_counter(&player->obj) == 0 || !g_vars.input_key_up) { + return; + } + --player_jump_counter(&player->obj); + player_y_delta(&player->obj) += (int8_t)player_anim_dy[player_jump_counter(&player->obj)]; +} + +static void level_update_player_anim_27(struct player_t *player) { + g_vars.player_hit_counter = 7; + player->obj.spr_num = 27; + level_player_update_xvelocity(player, 0); + if (player_throw_counter(&player->obj) == 0) { + return; + } + if (player_flags(&player->obj) & PLAYER_FLAGS_POWERUP) { + player->obj.spr_num = 35; + } + player_flags(&player->obj) |= PLAYER_FLAGS_THROW_VINYL; + player_flags(&player->obj) &= ~0x40; + // struct player_t *other_player = player->other_player; +} + +static void level_update_player_anim_28(struct player_t *player) { + player->obj.y_pos += 12; + level_update_player_anim_27(player); + player->obj.y_pos -= 12; + if (player_throw_counter(&player->obj) != 0 && (player_flags(&player->obj) & PLAYER_FLAGS_POWERUP) != 0) { + player_obj_num(&player->obj) = 35; + } else { + player_obj_num(&player->obj) = 28; + } +} + +static void level_update_player_anim_29(struct player_t *player) { + player->obj.spr_num = 29; + player->obj.y_pos -= 2; + const int x = (player->obj.x_pos & 15) - 8; + if (x < 0) { + ++player->obj.x_pos; + } else if (x > 0) { + --player->obj.x_pos; + } + player_jump_counter(&player->obj) = 7; + player_x_delta(&player->obj) = 0; + player_y_delta(&player->obj) = 0; +} + +static void level_update_player_anim_30(struct player_t *player) { + player->obj.spr_num = 30; + player_jump_counter(&player->obj) = 7; + player_x_delta(&player->obj) = 0; + player_y_delta(&player->obj) = 0; +} + +static void level_update_player_anim_32(struct player_t *player) { + player->obj.spr_num = 30; + --player->obj.x_pos; + player_jump_counter(&player->obj) = 7; + player_x_delta(&player->obj) = 0; + player_y_delta(&player->obj) = 0; +} + +static void level_update_player_anim_33(struct player_t *player) { + player->obj.spr_num = 30; + ++player->obj.x_pos; + player_jump_counter(&player->obj) = 7; + player_x_delta(&player->obj) = 0; + player_y_delta(&player->obj) = 0; +} + +static void level_update_player_from_input(struct player_t *player) { + if (player_flags2(&player->obj) & 8) { // dead + if ((player_flags2(&player->obj) & 1) == 0) { + player_y_delta(&player->obj) = -120; + player_x_delta(&player->obj) = (player->obj.x_pos < (g_vars.tilemap_x * 16 + TILEMAP_SCREEN_W / 2)) ? 24 : -24; + player_flags2(&player->obj) |= 1; + } + player->obj.x_pos += player_x_delta(&player->obj) >> 3; + player->obj.y_pos += player_y_delta(&player->obj) >> 3; + player_y_delta(&player->obj) = MIN(player_y_delta(&player->obj) + 7, 120); + if ((player->obj.y_pos >> 4) - g_vars.tilemap_y > TILEMAP_SCREEN_H / 16 + 3) { + player_flags2(&player->obj) |= 0x10; + } + player_flags(&player->obj) &= ~PLAYER_FLAGS_THROW_VINYL; + if (g_vars.input_key_space) { + player_flags(&player->obj) |= PLAYER_FLAGS_THROW_VINYL; + } + return; + } + if (player->unk_counter != 0) { + --player->unk_counter; + } + if (player->change_hdir_counter != 0) { + --player->change_hdir_counter; + } + player_flags2(&player->obj) &= ~4; + player_flags2(&player->obj) |= (g_vars.input_key_down & 4); + if ((player_flags(&player->obj) & PLAYER_FLAGS_THROW_VINYL) != 0 && player->vinyls_count > 4) { + if (player_power(&player->obj) < 255) { + ++player_power(&player->obj); + } + } else { + player_power(&player->obj) = 0; + } + uint8_t flags = player_flags(&player->obj); + if (g_vars.input_key_right) { + player_flags(&player->obj) &= ~PLAYER_FLAGS_FACING_LEFT; + } + if (g_vars.input_key_left) { + player_flags(&player->obj) |= PLAYER_FLAGS_FACING_LEFT; + } + if (((flags ^ player_flags(&player->obj)) & PLAYER_FLAGS_FACING_LEFT) != 0) { // horizontal direction changed + player->change_hdir_counter += 4; + } + // flags = player_flags(&player->obj); + uint8_t mask = 0; + if (flags & PLAYER_FLAGS_STAIRS) { + mask |= 4; + } + if (flags & 0x40) { + mask |= 2; + } + if (flags & PLAYER_FLAGS_POWERUP) { + mask |= 1; + } + flags = g_vars.input_key_space & 1; + if (flags == 0) { + if (player_power(&player->obj) >= 33 && (player_flags(&player->obj) & PLAYER_FLAGS_THROW_VINYL) != 0) { + flags = 1; + } else { + player_flags(&player->obj) &= ~PLAYER_FLAGS_THROW_VINYL; + } + } else { + if (player_flags(&player->obj) & PLAYER_FLAGS_THROW_VINYL) { + flags = 0; + } + } + mask = (mask << 1) | flags; + if (player_flags2(&player->obj) & 1) { + mask <<= 4; + mask |= 2; + } else { + mask <<= 4; + if ((player_flags(&player->obj) & 0x10) == 0) { + mask |= (g_vars.input_key_up & 8); + } + mask |= (g_vars.input_key_right & 4); + if (player_jump_counter(&player->obj) == 7) { + mask |= ((player_flags(&player->obj) & 0x10) == 0) ? (g_vars.input_key_down & 2) : 2; + } + mask |= (g_vars.input_key_left & 1); + } + if (player_flags2(&player->obj) & 0x40) { + player->dir_mask = mask; + mask = 0; + } + switch (player_anim_lut[mask]) { + case 0: + level_update_player_anim_0(player); + break; + case 1: + level_update_player_anim_1(player); + break; + case 2: + level_update_player_anim_2(player); + break; + case 3: + level_update_player_anim_3(player); + break; + case 4: + level_update_player_anim_4(player); + break; + case 5: + level_update_player_anim_5(player); + break; + case 6: + level_update_player_anim_6(player); + break; + case 7: + level_update_player_anim_7(player); + break; + case 8: + level_update_player_anim_8(player); + break; + case 9: + level_update_player_anim_9(player); + break; + case 10: + level_update_player_anim_10(player, 10); + break; + case 11: + level_update_player_anim_11(player); + break; + case 12: + level_update_player_anim_12(player); + break; + case 13: + case 35: + level_update_player_anim_13(player); + break; + case 15: + level_update_player_anim_15(player); + break; + case 17: + if (player_y_delta(&player->obj) != 0) { + level_update_player_anim_13(player); + } else { + player->obj.spr_num = 17; + level_player_update_xvelocity(player, 0); + } + break; + case 18: + level_update_player_anim_18(player); + break; + case 19: + level_update_player_anim_19(player); + break; + case 20: + if (player->vinyls_count == 0) { + play_sound(SOUND_8); + level_update_player_anim_13(player); + } else { + player->obj.y_pos += 12; + level_update_player_anim_6(player); + player->obj.y_pos -= 12; + } + player->obj.spr_num = 20; + break; + case 21: + if (player_jump_counter(&player->obj) != 7) { + level_update_player_anim_26(player); + } else { + player->obj.spr_num = 21; + level_player_update_flags(player); + level_player_update_xvelocity(player, 0); + } + break; + case 23: + if (player_jump_counter(&player->obj) != 7) { + level_update_player_anim_26(player); + } else { + player->obj.spr_num = 23; + if (g_vars.input_key_right) { + level_player_update_xvelocity(player, 2); + } else if (g_vars.input_key_left) { + level_player_update_xvelocity(player, -2); + } else { + level_player_update_xvelocity(player, 0); + } + } + break; + case 25: + if (player_y_delta(&player->obj) != 0) { + level_update_player_anim_0(player); + } else { + player->obj.spr_num = 25; + level_player_update_xvelocity(player, 0); + } + break; + case 26: + level_update_player_anim_26(player); + break; + case 27: + level_update_player_anim_27(player); + break; + case 28: + level_update_player_anim_28(player); + break; + case 29: + level_update_player_anim_29(player); + break; + case 30: + level_update_player_anim_30(player); + break; + case 31: + level_update_player_anim_10(player, 31); + break; + case 32: + level_update_player_anim_32(player); + break; + case 33: + level_update_player_anim_33(player); + break; + default: + print_warning("Unhandled anim_lut %d mask %d", player_anim_lut[mask], mask); + break; + } + if (g_vars.tilemap_h <= (player->obj.y_pos >> 4)) { + level_player_die(player); + } + int ax = player_x_delta(&player->obj) >> 3; + player->obj.x_pos += ax; + int dx = g_vars.tilemap_w << 4; + if (player->obj.x_pos >= dx || player->obj.x_pos < 0) { + player->obj.x_pos -= ax; + ax = 0; + } + if (g_vars.player_xscroll != 0) { + g_vars.player_xscroll += ax; + } + player->obj.y_pos += player_y_delta(&player->obj) >> 3; + int x = (g_vars.tilemap_x << 4) + 4; + if (player_x_delta(&player->obj) <= 0 && player->obj.x_pos < x) { + player->obj.x_pos = x; + } + if (player_x_delta(&player->obj) >= 0) { + x += TILEMAP_SCREEN_W - 4; + if (player->obj.x_pos > x) { + player->obj.x_pos = x; + } + } +} + +static void level_update_player_position() { + assert(g_vars.player != 2); + struct player_t *bp = &g_vars.players_table[0]; + if ((player_flags2(&bp->obj) & 8) == 0) { + if ((g_vars.input_key_right | g_vars.input_key_left) == 0) { + } + } + assert(g_vars.player != 2); + struct object_t *obj = &g_vars.players_table[0].obj; + if (player_flags2(obj) & 8) { + return; + } + if (player_flags2(obj) & 1) { + obj = &g_vars.players_table[1].obj; + } + if ((player_flags(obj) & 1) == 0 && (player_x_delta(obj) == 0)) { + g_vars.player_xscroll = 0; + } + if (g_vars.player_xscroll != 0) { + const int dx = MIN(abs(g_vars.player_xscroll) >> 4, 2); + if (dx == 0) { + g_vars.player_xscroll = obj->x_pos - (g_vars.tilemap_x << 4) - TILEMAP_SCREEN_W / 2; + } else { + if (g_vars.player_xscroll > 0) { + g_vars.player_xscroll -= dx << 2; + if (g_vars.player_xscroll < 0) { + g_vars.player_xscroll = 0; + } + level_adjust_hscroll_right(dx); + } else { + g_vars.player_xscroll += dx << 2; + if (g_vars.player_xscroll > 0) { + g_vars.player_xscroll = 0; + } + level_adjust_hscroll_left(dx); + } + } + } else { + g_vars.player_xscroll += obj->x_pos - (g_vars.tilemap_x << 4) - TILEMAP_SCREEN_W / 2; + } + const int y = (g_vars.tilemap_y << 4) + g_vars.tilemap_scroll_dy + 88 - obj->y_pos; + if (y >= 0) { + level_adjust_vscroll_up((int8_t)level_data4[0x88 + y]); + } else { + level_adjust_vscroll_down((int8_t)level_data4[0x88 - y]); + } +} + +static void level_update_input() { + if (g_vars.input_keystate[0x19]) { + } + if (g_vars.input_keystate[0x3B]) { + if (player_flags2(&g_vars.players_table[0].obj) & 8) { + level_player_die(&g_vars.players_table[0]); + } + assert(g_vars.player != 2); + } + if (g_vars.input_keystate[0x3C]) { + g_vars.players_table[0].lifes_count = 0; + g_vars.players_table[1].lifes_count = 0; + player_flags2(&g_vars.players_table[0].obj) = 8; + player_flags2(&g_vars.players_table[1].obj) = 8; + } + level_update_player_from_input(&g_vars.players_table[0]); + level_update_player_position(); +} + +static int level_collide_objects(const struct object_t *si, const struct object_t *di) { + if (di->spr_num == 0xFFFF) { + return 0; + } + const int dx = abs(si->x_pos - di->x_pos); + if (dx >= 80) { + return 0; + } + const int dy = abs(si->y_pos - di->y_pos); + if (dy >= 70) { + return 0; + } + // if (g_vars.input_keystate[0x2A]) { + int d = di->y_pos + (sprite_offsets[di->spr_num & 0x1FFF] >> 8) - (sprite_sizes[di->spr_num & 0x1FFF] >> 8); + int a = si->y_pos + (sprite_offsets[si->spr_num & 0x1FFF] >> 8) - (sprite_sizes[si->spr_num & 0x1FFF] >> 8); + if (a < d) { + SWAP(a, d); + SWAP(si, di); + } + a -= sprite_offsets[si->spr_num & 0x1FFF] >> 8; + if (a >= d) { + return 0; + } + d = si->x_pos - (sprite_sizes[si->spr_num & 0x1FFF] & 255); + a = di->x_pos - (sprite_sizes[di->spr_num & 0x1FFF] & 255); + int b = sprite_offsets[di->spr_num & 0x1FFF] & 255; + if (a >= d) { + SWAP(a, d); + b = sprite_offsets[si->spr_num & 0x1FFF] & 255; + } + return (a + b >= d); +} + +static int level_is_object_visible(int x_pos, int y_pos) { + const int x = abs((x_pos >> 4) - g_vars.tilemap_x - (TILEMAP_SCREEN_W / 32)); + if (x < (TILEMAP_SCREEN_W / 32 + 4)) { + const int y = abs((y_pos >> 4) - g_vars.tilemap_y - (TILEMAP_SCREEN_H / 32)); + return (y < 10) ? 1 : 0; + } + return 0; +} + +static int level_is_respawn_object_visible(const uint8_t *data) { + const int x_pos = READ_LE_UINT16(data); + const int y_pos = READ_LE_UINT16(data + 2); + if (g_vars.level_loop_counter != 0) { + const int x = abs((x_pos >> 4) - g_vars.tilemap_x - (TILEMAP_SCREEN_W / 32)); + if (x <= (TILEMAP_SCREEN_W / 32 + 2)) { + const int y = (y_pos >> 4) - g_vars.tilemap_y; + return (y >= 0 && y <= 12) ? 1 : 0; + } + } + return !level_is_object_visible(x_pos, y_pos); +} + +static void init_object82(int type, int x_pos, int y_pos, struct object_t *obj, uint16_t *ptr) { + obj->data[2].w = ptr - g_vars.buffer; + object82_state(obj) = 0; + object82_type(obj) = type; + obj->x_pos = x_pos; + obj->y_pos = y_pos; + *ptr = obj - g_vars.objects_table; + object_blinking_counter(obj) = 0; +} + +static uint8_t get_random_number() { + static uint8_t random_a = 5; + static uint8_t random_b = 34; + static uint8_t random_c = 134; + static uint16_t random_d = 58765; + + random_d += random_a; + random_a += 3 + (random_d >> 8); + + random_b += random_c; + random_b *= 2; + random_b += random_a; + + random_c ^= random_a; + random_c ^= random_b; + + return random_b; +} + +static void level_init_object102_1(struct object_t *obj22, int x, int y, int num) { + struct object_t *obj = find_object(102, 10, 1); + if (obj) { + obj->x_pos = x; + obj->y_pos = y; + obj->spr_num = num; + object_blinking_counter(obj) = 0; + const int n = (get_random_number() & 7) << 3; + object102_x_delta(obj) = (object22_xvelocity(obj22) < 0) ? -n : n; + object102_y_delta(obj) = -(((get_random_number() & 7) + 4) << 3); + } +} + +static void level_init_object102_2(struct object_t *obj22, int x, int y) { + struct object_t *obj = find_object(102, 10, 1); + if (obj) { + obj->x_pos = x + 8; + obj->y_pos = y + 12; + obj->spr_num = (get_random_number() & 1) + 96; + object_blinking_counter(obj) = 0; + object102_x_delta(obj) = (object22_xvelocity(obj22) < 0) ? -16 : 16; + object102_y_delta(obj) = -80; + } +} + +static void level_player_collide_object(struct player_t *player, struct object_t *obj) { + if ((player_flags2(&player->obj) & 0x20) == 0) { + return; + } + if ((obj->y_pos & 15) < 12) { + return; + } + struct object_t *player_obj = &g_vars.objects_table[player_obj_num(&player->obj)]; + if (level_collide_objects(obj, player_obj)) { + level_hit_player(player); + } +} + +static bool level_collide_vinyl_decor(int x, int y, struct object_t *obj) { + const int offset = (y >> 4) * g_vars.tilemap_w + (x >> 4); + uint8_t al = g_vars.tilemap_data[offset]; + al = g_vars.level_tiles_lut[al]; + if (al == 3) { + object22_damage(obj) = 0; + obj->spr_num = 0xFFFF; + g_vars.tilemap_flags ^= 1; + play_sound(SOUND_3); + } else if (al == 9) { + g_vars.tilemap_data[offset] = 0; + level_init_object102_2(obj, x, y); + play_sound(SOUND_1); + } else if (al == 10) { + obj->spr_num = 0xFFFF; + } else { + return false; + } + if (object22_damage(obj) <= 0x200) { + obj->spr_num = 0xFFFF; + } + level_init_object_spr_num_98_align_pos(x, y); + return true; +} + +static struct object_t *level_collide_vinyl_objects82(struct object_t *obj) { + for (int i = 0; i < 20; ++i) { + struct object_t *obj2 = &g_vars.objects_table[82 + i]; + if (obj2->x_pos != -1 && object82_type(obj2) != 16 && object82_type(obj2) != 7) { + if (level_collide_objects(obj, obj2)) { + return obj2; + } + } + } + return 0; +} + +static struct object_t *level_collide_vinyl_objects64(struct object_t *obj) { + for (int i = 0; i < 8; ++i) { + struct object_t *obj2 = &g_vars.objects_table[64 + i]; + if (obj2->x_pos != -1 && (obj2->spr_num & 0x1FFF) == 199) { + if (level_collide_objects(obj, obj2)) { + return obj2; + } + } + } + return 0; +} + +static int level_update_object82_position(struct object_t *obj, int8_t dx, int8_t dy) { + int ret = 0; + object82_hflip(obj) = dx; + obj->x_pos += dx >> 3; + if (obj->x_pos < 0) { + obj->x_pos = 0; + ++ret; + } + if (g_vars.tilemap_w <= (obj->x_pos >> 4)) { + obj->x_pos -= 8; + ++ret; + } + obj->y_pos += dy >> 3; + if (obj->y_pos < 0) { + obj->y_pos = 0; + } + return ret; +} + +static struct player_t *level_get_closest_player(struct object_t *obj, int x_dist) { + struct player_t *player = &g_vars.players_table[0]; + int dx = abs(obj->x_pos - player->obj.x_pos); + if (dx < x_dist) { + return player; + } + if (g_vars.objects_table[1].spr_num != 0xFFFF) { + ++player; + dx = abs(obj->x_pos - player->obj.x_pos); + if (dx < x_dist) { + return player; + } + } + return 0; +} + +static void level_update_object82_type0(struct object_t *obj) { + if (object82_anim_data(obj) != &monster_spr_anim_data0[2] && !level_is_object_visible(obj->x_pos, obj->y_pos)) { + obj->spr_num = 0xFFFF; + g_vars.buffer[obj->data[2].w] = 0xFFFF; + object82_counter(obj) = 0; + g_vars.buffer[128 + obj->data[2].w] = g_vars.level_loop_counter; + } else { + const int state = object82_state(obj); + if (state == 0) { + const int num = object82_counter(obj) << 1; + int16_t ax1 = ((int16_t)READ_LE_UINT16(level_data4 + 0x1AE + num) * 60) >> 8; + object82_hflip(obj) = ax1 >> 8; + int16_t ax2 = ((int16_t)READ_LE_UINT16(level_data4 + 0x12C + num) * 60) >> 8; + object82_counter(obj) += 2; + const uint8_t *p = object82_type0_init_data(obj); + obj->x_pos = ax1 + READ_LE_UINT16(p); + obj->y_pos = ax2 + READ_LE_UINT16(p + 2); + struct player_t *player = level_get_closest_player(obj, 80); + if (player) { + object82_type0_player_num(obj) = player - &g_vars.players_table[0]; + object82_state(obj) = 1; + obj->data[6].b[0] = 0; + obj->data[7].b[0] = 0; + } + } else if (state == 1) { + struct player_t *player = &g_vars.players_table[object82_type0_player_num(obj)]; + int16_t x_pos = player->obj.x_pos; + if ((player_flags2(&player->obj) & 0x40) != 0) { + x_pos += 60; + if ((player->dir_mask & 4) == 0) { + x_pos -= 120; + if ((player->dir_mask & 1) == 0) { + x_pos += 60; + if ((player_flags(&player->obj) & PLAYER_FLAGS_FACING_LEFT) != 0) { + x_pos -= 20; + } else { + x_pos = player->obj.x_pos; + } + } + } + } + x_pos -= obj->x_pos; + x_pos = (x_pos >> 8) | (1 + object82_type0_x_delta(obj)); + int dx = 32; + if (x_pos < dx) { + dx = -dx; + if (x_pos > dx) { + dx = x_pos; + } + } + object82_type0_x_delta(obj) = dx; + int16_t y_pos = player->obj.y_pos; + if ((player_flags2(&player->obj) & 0x40) != 0) { + y_pos -= 60; + if ((player->dir_mask & 8) == 0) { + y_pos += 120; + if ((player->dir_mask & 2) == 0) { + y_pos -= 60; + } + } + } + y_pos -= obj->y_pos; + y_pos = (y_pos >> 8) | (1 + object82_type0_y_delta(obj)); + int dy = 32; + if (y_pos < dy) { + dy = -dy; + if (y_pos > dy) { + dy = y_pos; + } + } + object82_type0_y_delta(obj) = dy; + if (level_update_object82_position(obj, object82_type0_x_delta(obj), object82_type0_y_delta(obj)) != 0) { + object82_type0_x_delta(obj) = -object82_type0_x_delta(obj); + } + } + } +} + +// snail +static void level_update_object82_type1(struct object_t *obj) { + if (!level_is_object_visible(obj->x_pos, obj->y_pos)) { + obj->spr_num = 0xFFFF; + g_vars.buffer[obj->data[2].w] = 0xFFFF; + object82_counter(obj) = 0; + } else { + int dx = object82_x_delta(obj) + (int8_t)object82_type1_hdir(obj); + if (dx < 128 && dx > -128) { + object82_x_delta(obj) += dx; + } + if ((g_vars.level_loop_counter & 3) == 0) { + ++object82_counter(obj); + if (object82_counter(obj) >= 75) { + object82_type1_hdir(obj) = -object82_type1_hdir(obj); + object82_counter(obj) = 0; + } + } + dx = obj->data[7].b[0] + object82_x_delta(obj); + obj->data[7].b[0] = dx; + obj->x_pos += (dx >> 8); + object82_hflip(obj) = object82_type1_hdir(obj); + level_update_tiles(obj, obj->x_pos, obj->y_pos, _undefined); + obj->y_pos += object82_y_delta(obj) >> 3; + } +} + +static void level_update_object82_type3(struct object_t *obj) { + if (!level_is_object_visible(obj->x_pos, obj->y_pos)) { + obj->spr_num = 0xFFFF; + g_vars.buffer[obj->data[2].w] = 0xFFFF; + object82_counter(obj) = 0; + } else { + const int al = object82_state(obj); + if (al == 0) { + if (level_get_closest_player(obj, 160)) { + object82_state(obj) = 1; + object82_anim_data(obj) = monster_spr_anim_data4; + object82_counter(obj) = 40; + } + } else if (al == 1) { + level_update_tiles(obj, obj->x_pos, obj->y_pos, _undefined); + level_update_object82_position(obj, 0, object82_y_delta(obj)); + if (--object82_counter(obj) == 0) { + object82_counter(obj) = 40; + object82_y_delta(obj) = -80; + } + } + } +} + +static int level_is_object_on_tile_5dc8(struct object_t *obj) { + uint8_t tile_num; + const int offset = g_vars.player_map_offset; + tile_num = level_get_tile(offset - g_vars.tilemap_w); + if (tile_funcs2[tile_num] == 0x5DC8) { + return 0; + } + tile_num = level_get_tile(offset - g_vars.tilemap_w - 1); + if (tile_funcs2[tile_num] == 0x5DC8) { + return 1; + } + tile_num = level_get_tile(offset - g_vars.tilemap_w + 1); + if (tile_funcs2[tile_num] == 0x5DC8) { + return 1; + } + obj->data[0].b[1] = 0x10; + return 1; +} + +static void level_update_object82_type2(struct object_t *obj) { + if (!level_is_object_visible(obj->x_pos, obj->y_pos)) { + obj->spr_num = 0xFFFF; + g_vars.buffer[obj->data[2].w] = 0xFFFF; + object82_counter(obj) = 0; + } else { + int state = object82_state(obj); + if (state == 0) { + struct player_t *player = level_get_closest_player(obj, 160); + if (player) { + object82_state(obj) = 1; + obj->data[5].w = -56; + obj->data[4].b[0] = obj->data[4].b[1]; + obj->data[4].b[1] = -obj->data[4].b[1]; + obj->data[1].b[0] += 2; + object82_counter(obj) = 4; + } + } else { + if (state == 1) { + if (obj->data[5].w == 0 && --object82_counter(obj) == 0) { + object82_state(obj) = state = 2; + object82_counter(obj) = 20; + } + } + if (state == 2) { + object82_anim_data(obj) = monster_spr_anim_data3; + if (--object82_counter(obj) != 0) { + goto update_tiles; // do not update position + } + object82_state(obj) = 0; + obj->data[5].w = 0; + obj->data[4].b[0] = 0; + } + } + if (level_update_object82_position(obj, obj->data[4].b[0], 0) != 0) { + obj->data[4].b[0] = -obj->data[4].b[0]; + } +update_tiles: + level_update_tiles(obj, obj->x_pos, obj->y_pos, _undefined); + if (level_is_object_on_tile_5dc8(obj)) { + object82_x_delta(obj) = -object82_x_delta(obj); + if (level_update_object82_position(obj, obj->data[4].b[0], 0) != 0) { + obj->data[4].b[0] = -obj->data[4].b[0]; + } + } + obj->y_pos += object82_y_delta(obj) >> 3; + } +} + +// grass cutter +static void level_update_object82_type4_6(struct object_t *obj) { + if (!level_is_object_visible(obj->x_pos, obj->y_pos)) { + obj->spr_num = 0xFFFF; + g_vars.buffer[obj->data[2].w] = 0xFFFF; + object82_counter(obj) = 0; + } else { + const int state = object82_state(obj); + if (state == 0) { + struct player_t *player = level_get_closest_player(obj, 300); + if (player) { + object82_x_delta(obj) = (obj->x_pos < player->obj.x_pos) ? 8 : -8; + object82_state(obj) = 1; + } + } else if (state == 1) { // head to player + struct player_t *player = level_get_closest_player(obj, 300); + if (player) { + object82_x_delta(obj) <<= 2; // faster + object82_anim_data(obj) = (object82_type(obj) == 4) ? (monster_spr_anim_data6 + 10) : (monster_spr_anim_data8 + 26); + object82_state(obj) = 2; + } + } + level_update_tiles(obj, obj->x_pos, obj->y_pos, _undefined); + if (level_is_object_on_tile_5dc8(obj)) { + object82_x_delta(obj) = -object82_x_delta(obj); + } + object82_hflip(obj) = object82_x_delta(obj) >> 8; + if (level_update_object82_position(obj, object82_x_delta(obj), object82_y_delta(obj)) != 0) { + object82_x_delta(obj) = -object82_x_delta(obj); + } + } +} + +// dragon +static void level_update_object82_type7(struct object_t *obj) { + level_update_object82_type0(obj); + int num = g_vars.dragon_coords[0]; + const int x_pos = obj->x_pos; + const int y_pos = obj->y_pos; + g_vars.dragon_coords[(num + 2) / 2] = x_pos; + g_vars.dragon_coords[(num + 4) / 2] = y_pos; + if (player_flags2(&g_vars.players_table[0].obj) & 0x40) { + g_vars.players_table[0].obj.x_pos = x_pos; + g_vars.players_table[0].obj.y_pos = y_pos; + int spr_num = 6; + if (obj->spr_num & 0x8000) { + spr_num |= 0x8000; + } + if (player_flags(&g_vars.players_table[0].obj) & PLAYER_FLAGS_JAKE) { + spr_num += 50; + } + const int dx = (int8_t)obj->data[6].b[0]; + player_x_delta(&g_vars.players_table[0].obj) = (dx >> 2) + dx; + } + if (player_flags2(&g_vars.players_table[1].obj) & 0x40) { + g_vars.players_table[1].obj.x_pos = x_pos; + g_vars.players_table[1].obj.y_pos = y_pos; + const int dx = (int8_t)obj->data[6].b[0]; + player_x_delta(&g_vars.players_table[1].obj) = (dx >> 2) + dx; + } + num = (num + 4) & 0x7F; + g_vars.dragon_coords[0] = num; + for (int i = 0; i < 8; ++i) { + obj = &g_vars.objects_table[121 + i]; + num -= 12; + if (num < 0) { + num += 128; + } + obj->x_pos = g_vars.dragon_coords[(num + 2) / 2]; + obj->y_pos = g_vars.dragon_coords[(num + 4) / 2]; + object_blinking_counter(obj) = 0; + } +} + +// dead monster +static void level_update_object82_type16(struct object_t *obj) { + if (!level_is_object_visible(obj->x_pos, obj->y_pos)) { + obj->spr_num = 0xFFFF; + g_vars.buffer[obj->data[2].w] = 0xFFFF; + object82_counter(obj) = 0; + g_vars.buffer[128 + obj->data[2].w] = g_vars.level_loop_counter; + } else { + obj->y_pos += object82_energy(obj) >> 3; + const int y_vel = object82_energy(obj) + 4; + if (y_vel < 128) { + object82_energy(obj) = y_vel; + } + } +} + +static void level_update_triggers() { + const int count = g_vars.triggers_table[18]; + const uint8_t *start = g_vars.triggers_table + 19; + const uint8_t *data = start; + uint16_t *ptr = g_vars.buffer; + for (int i = 0; i < count; ++i, data += 16, ++ptr) { + if (*ptr != 0xFFFF) { + continue; + } + const int x_pos = READ_LE_UINT16(data); + const int y_pos = READ_LE_UINT16(data + 2); + if (READ_LE_UINT16(data + 4) == 0 && READ_LE_UINT16(data + 6) == 9) { + const uint16_t num = READ_LE_UINT16(data + 8); + if (num == 12 || num == 25 || num == 8) { + if (!level_is_object_visible(x_pos, y_pos)) { + continue; + } + struct object_t *obj = find_object(32, 8, 4); + if (obj) { + int dx = 205; + if (num != 8) { + dx = 224; // ball with spikes + if (num == 12) { + WRITE_LE_UINT16(&g_vars.triggers_table[19] + i * 16 + 14, 0x260); + } + } + obj[0].spr_num = dx; + object_blinking_counter(&obj[0]) = 0; + obj[0].data[1].w = READ_LE_UINT16(data + 14); + obj[1].spr_num = 204; + object_blinking_counter(&obj[1]) = 0; + obj[2].spr_num = 204; + object_blinking_counter(&obj[2]) = 0; + obj[3].spr_num = 204; + object_blinking_counter(&obj[3]) = 0; + obj->data[2].b[1] = 0x80; // rotation table index + obj->data[2].b[0] = 0; + obj->data[4].w = 0x3A00; // radius + obj->data[5].w = 8; // angle step + *ptr = obj - g_vars.objects_table; + obj->data[0].w = data - start; + } + + } else if (num == 2 || num == 24) { + if (level_is_respawn_object_visible(data)) { + continue; + } + struct object_t *obj = find_object(64, 8, 1); + if (obj) { + obj->spr_num = (num == 2) ? 199 : 205; // crate + obj->x_pos = x_pos; + obj->y_pos = y_pos; + object64_counter(obj) = READ_LE_UINT16(data + 14); + object_blinking_counter(obj) = 0; + *ptr = obj - g_vars.objects_table; + obj->data[0].w = data - start; + object64_yvelocity(obj) = 0; + } + } else if (num == 15) { + struct object_t *obj = find_object(82, 20, 1); + if (obj) { + obj->spr_num = 198; // jukebox + init_object82(5, x_pos, y_pos, obj, ptr); + object82_anim_data(obj) = monster_spr_anim_data9; + } + } else if (num == 19 || num == 3 || num == 16) { + if (abs((int16_t)ptr[128] - g_vars.level_loop_counter) < _object_respawn_delay) { + continue; + } + if (level_is_respawn_object_visible(data)) { + continue; + } + struct object_t *obj = find_object(82, 20, 1); + if (obj) { + obj->spr_num = 214; // mosquito + object82_anim_data(obj) = monster_spr_anim_data1; + int type = 0; + if (num == 3) { + obj->spr_num = 230; // dragon + object82_anim_data(obj) = monster_spr_anim_data0; + for (int i = 0; i < 128; ++i) { + g_vars.dragon_coords[1 + i] = -1; + } + g_vars.dragon_coords[0] = 0; + g_vars.objects_table[121].spr_num = 233; + g_vars.objects_table[122].spr_num = 233; + g_vars.objects_table[123].spr_num = 233; + g_vars.objects_table[124].spr_num = 233; + g_vars.objects_table[125].spr_num = 233; + g_vars.objects_table[126].spr_num = 233; + g_vars.objects_table[127].spr_num = 234; + g_vars.objects_table[128].spr_num = 235; + type = 7; // fall-through + obj->y_pos -= 60; + } + init_object82(type, x_pos, y_pos, obj, ptr); + object82_energy(obj) = 128; + object82_counter(obj) = 0; + object82_type0_init_data(obj) = data; + } + } else if (num == 20) { + if (level_is_respawn_object_visible(data)) { + continue; + } + struct object_t *obj = find_object(82, 20, 1); + if (obj) { + obj->spr_num = 217; // snail + init_object82(1, x_pos, y_pos, obj, ptr); + object82_anim_data(obj) = monster_spr_anim_data2; + object82_type1_hdir(obj) = -32; + object82_x_delta(obj) = 0; + obj->data[7].w = 0; + object82_counter(obj) = 0; + object82_y_delta(obj) = 0; + object82_energy(obj) = 512; + } + } else if (num == 21 || num == 18) { + if (abs((int16_t)ptr[128] - g_vars.level_loop_counter) < _object_respawn_delay) { + continue; + } + if (level_is_respawn_object_visible(data)) { + continue; + } + struct object_t *obj = find_object(82, 20, 1); + if (obj) { + uint8_t type; + if (num == 21) { + obj->spr_num = 228; // closed trap + type = 3; + object82_anim_data(obj) = monster_spr_anim_data4 + 18; + } else { + obj->spr_num = 225; // open trap + object82_anim_data(obj) = monster_spr_anim_data3; + type = 2; + } + init_object82(type, x_pos, y_pos, obj, ptr); + object82_y_delta(obj) = 0; + obj->data[4].b[0] = 0; + obj->data[7].p = data; + obj->data[4].b[1] = ((data[11] & 1) != 0) ? -40 : 40; + object82_energy(obj) = 768; + } + } else if (num == 26 || num == 5 || num == 17) { + if (abs((int16_t)ptr[128] - g_vars.level_loop_counter) < _object_respawn_delay) { + continue; + } + if (level_is_respawn_object_visible(data)) { + continue; + } + struct object_t *obj = find_object(82, 20, 1); + if (obj) { + obj->spr_num = 225; // grass cutter + uint8_t type; + if (num == 17) { + type = 4; + object82_anim_data(obj) = monster_spr_anim_data6; + } else { + type = 6; + object82_anim_data(obj) = monster_spr_anim_data8; + } + init_object82(type, x_pos, y_pos, obj, ptr); + obj->data[5].w = 0; + obj->data[4].b[0] = 0; + object82_energy(obj) = 512; + } + } else if (num == 14) { + if (!level_is_object_visible(x_pos, y_pos)) { + continue; + } + struct object_t *obj = find_object(112, 9, 1); + if (obj) { + obj->spr_num = 242; // vertical bar base + obj->x_pos = x_pos; + obj->y_pos = y_pos + 16; + obj->data[3].w = obj->y_pos; + obj->data[0].w = READ_LE_UINT16(data + 14); + object_blinking_counter(obj) = 0; + obj->data[2].w = 0; + *ptr = obj - g_vars.objects_table; + obj->data[1].w = data - start; + } + } + } else if (READ_LE_UINT16(data + 4) == 2 && level_is_object_visible(x_pos, y_pos)) { // bonus + struct object_t *obj = find_object(72, 10, 1); + if (obj) { + const uint16_t num = READ_LE_UINT16(data + 8); + obj->spr_num = 190 + level_data4[0x3B0 + num]; + object_blinking_counter(obj) = 0; + obj->x_pos = x_pos; + obj->y_pos = y_pos; + *ptr = obj - g_vars.objects_table; + obj->data[0].w = data - start; + } + } + } + for (int i = 0; i < 8; ++i) { + struct object_t *obj = &g_vars.objects_table[32 + i * 4]; + if (obj->spr_num == 0xFFFF) { + continue; + } + obj->data[1].w += (obj->data[2].w & 0x8000) ? -3 : 3; + const uint8_t *_di = start + obj->data[0].w; + if (!level_is_object_visible(READ_LE_UINT16(_di), READ_LE_UINT16(_di + 2))) { + obj[0].spr_num = 0xFFFF; + obj[1].spr_num = 0xFFFF; + obj[2].spr_num = 0xFFFF; + obj[3].spr_num = 0xFFFF; + g_vars.buffer[obj->data[0].w / 16] = 0xFFFF; + } else { + if (READ_LE_UINT16(_di + 8) == 25) { + obj->data[4].w += obj->data[5].w; + if (obj->data[4].w == 24000 || obj->data[4].w == 10240) { + obj->data[5].w = -obj->data[5].w; + } + obj->data[1].w = (-(obj->data[4].w - 30000)) >> 4; + } + const int x_pos = obj->x_pos; + obj[0].x_pos = obj[1].x_pos = obj[2].x_pos = obj[3].x_pos = READ_LE_UINT16(_di); + obj[0].y_pos = obj[1].y_pos = obj[2].y_pos = obj[3].y_pos = READ_LE_UINT16(_di + 2); + int dx, ax = obj[0].data[1].w * 2; + obj[0].data[2].w += ax; + ax = obj[0].data[4].b[1] * (int16_t)READ_LE_UINT16(level_data4 + 0x1AE + obj[0].data[2].b[1] * 2); + ax >>= 8; + obj[0].x_pos += ax; + ax >>= 1; + dx = ax; + obj[2].x_pos += ax; + ax >>= 1; + dx += ax; + obj[3].x_pos += ax; + obj[1].x_pos += dx; + + ax = obj[0].data[4].b[1] * (int16_t)READ_LE_UINT16(level_data4 + 0x12C + obj[0].data[2].b[1] * 2); + ax >>= 8; + obj[0].y_pos += ax; + ax >>= 1; + dx = ax; + obj[2].y_pos += ax; + ax >>= 1; + dx += ax; + obj[3].y_pos += ax; + obj[1].y_pos += dx; + + obj[0].data[3].w = x_pos - obj[0].x_pos; + } + } + for (int i = 0; i < 8; ++i) { + struct object_t *obj = &g_vars.objects_table[64 + i]; + if (obj->spr_num == 0xFFFF) { + continue; + } + const int timer = object64_counter(obj); + if (timer == 0) { // fall + ++object64_yvelocity(obj); + if (obj->y_pos < 2048) { + obj->y_pos += object64_yvelocity(obj); + } + } else if (timer <= 20) { + obj->x_pos ^= (g_vars.level_loop_counter & 1); + } + const uint8_t *p = start + obj->data[0].w; + if (!level_is_object_visible(READ_LE_UINT16(p), READ_LE_UINT16(p + 2))) { + obj->spr_num = 0xFFFF; + g_vars.buffer[obj->data[0].w / 16] = 0xFFFF; + } + } + for (int i = 0; i < 10; ++i) { + struct object_t *obj = &g_vars.objects_table[72 + i]; + if (obj->spr_num == 0xFFFF) { + continue; + } + const uint8_t *p = start + obj->data[0].w; + if (!level_is_object_visible(READ_LE_UINT16(p), READ_LE_UINT16(p + 2))) { + obj->spr_num = 0xFFFF; + g_vars.buffer[obj->data[0].w / 16] = 0xFFFF; + } + } + for (int i = 0; i < 20; ++i) { + struct object_t *obj = &g_vars.objects_table[2 + i]; + if (obj->spr_num == 0xFFFF) { + continue; + } + if ((object2_spr_tick(obj) & g_vars.level_loop_counter) == 0) { + --object2_spr_count(obj); + if (object2_spr_count(obj) != 0) { + ++obj->spr_num; + } else { + obj->spr_num = 0xFFFF; + } + } + } + for (int i = 0; i < 9; ++i) { + struct object_t *obj = &g_vars.objects_table[112 + i]; + if (obj->spr_num == 0xFFFF) { + continue; + } + int offset; + while (1) { + obj->data[2].w += obj->data[0].w; + obj->y_pos += obj->data[2].w >> 8; + obj->data[2].w &= 0xFF; + offset = (obj->y_pos >> 4) * g_vars.tilemap_w + (obj->x_pos >> 4); + if (offset < 0) { + print_warning("Invalid object #112 position %d,%d", obj->x_pos, obj->y_pos); + break; + } + if (obj->data[0].w < 0) { + offset -= g_vars.tilemap_w; + if (obj->y_pos > obj->data[3].w) { + break; + } + } else { + level_player_collide_object(&g_vars.players_table[0], obj); + level_player_collide_object(&g_vars.players_table[1], obj); + uint8_t ah = g_vars.level_tiles_lut[g_vars.tilemap_data[offset]]; + uint8_t al = g_vars.level_tiles_lut[g_vars.tilemap_data[offset + 1]]; + if (ah != 1 && ah != 2 && al != 1 && al != 2) { + break; + } + } + obj->data[0].w = -obj->data[0].w; + } + if (obj->data[0].w < 0) { + offset += g_vars.tilemap_w; + if (g_vars.tilemap_data[offset + 1] != 0x95 && g_vars.tilemap_data[offset] != 0x95) { + continue; + } + WRITE_LE_UINT16(g_vars.tilemap_data + offset - 2, 0); + WRITE_LE_UINT16(g_vars.tilemap_data + offset, 0); + } else { + offset -= g_vars.tilemap_w; + uint16_t tiles = READ_LE_UINT16(g_vars.tilemap_data + offset - 2); + if (tiles != 0x9594 && tiles != 0) { + level_init_object102_2(obj, offset % g_vars.tilemap_w - 2, offset / g_vars.tilemap_w); + } + WRITE_LE_UINT16(g_vars.tilemap_data + offset - 2, 0x9594); + tiles = READ_LE_UINT16(g_vars.tilemap_data + offset); + if (tiles != 0x9695 && tiles != 0) { + level_init_object102_2(obj, offset % g_vars.tilemap_w, offset / g_vars.tilemap_w); + } + WRITE_LE_UINT16(g_vars.tilemap_data + offset, 0x9695); + } + } + for (int i = 0; i < 10; ++i) { + struct object_t *obj = &g_vars.objects_table[102 + i]; + if (obj->spr_num != 0xFFFF) { + object102_y_delta(obj) += 8; + obj->x_pos += object102_x_delta(obj) >> 3; + obj->y_pos += object102_y_delta(obj) >> 3; + const int y = (obj->y_pos >> 4) - g_vars.tilemap_y; + if (y < 0 || y > TILEMAP_SCREEN_H / 16 + 3) { + obj->spr_num = 0xFFFF; + } + } + } + for (int i = 0; i < 10; ++i) { // vinyls + struct object_t *obj = &g_vars.objects_table[22 + i]; + if (obj->spr_num == 0xFFFF) { + continue; + } + if (level_collide_vinyl_decor(obj->x_pos - 8, obj->y_pos - 8, obj)) { + continue; + } + int16_t ax, dx; + struct object_t *obj2 = level_collide_vinyl_objects82(obj); + if (obj2 && object_blinking_counter(obj2) == 0) { + level_init_object_spr_num_92(obj->x_pos, obj->y_pos); + object82_y_delta(obj2) = -48; + const int num = object82_type(obj2); + if (num == 6 || num == 4 || num == 1) { + ax = abs(object82_x_delta(obj2)); + object82_x_delta(obj2) = (obj->data[0].w < 0) ? -ax : ax; + } else if (num == 2) { + ax = abs((int8_t)obj2->data[4].b[0]); + obj2->data[4].b[0] = (obj->data[0].w < 0) ? -ax : ax; + } + object_blinking_counter(obj2) = 3; + if (obj2->spr_num == 198) { + obj->spr_num = 0xFFFF; + continue; + } + dx = object82_energy(obj2); + ax = object82_energy(obj2) - object22_damage(obj); + if (ax <= 0 || (g_options.cheats & CHEATS_ONE_HIT_VINYL) != 0) { + play_sound(SOUND_1); + ax = -80; + object82_type(obj2) = 16; + // seek to ko animation + const uint8_t *p = object82_anim_data(obj2); + while (READ_LE_UINT16(p) != 0x7D00) { + p += 2; + } + object82_anim_data(obj2) = p + 2; + } + object82_energy(obj2) = ax; + object22_damage(obj) -= dx; + if (object22_damage(obj) <= 0) { + obj->spr_num = 0xFFFF; + object22_damage(obj) = 0; + continue; + } + } + obj2 = level_collide_vinyl_objects64(obj); + if (obj2) { + play_sound(SOUND_0); + level_init_object102_1(obj, obj->x_pos, obj->y_pos, 200); + level_init_object102_1(obj, obj->x_pos, obj->y_pos, 201); + level_init_object102_1(obj, obj->x_pos, obj->y_pos, 202); + level_init_object102_1(obj, obj->x_pos, obj->y_pos, 203); + obj2->spr_num = 0xFFFF; + g_vars.buffer[obj->data[0].w / 16] = 0xFFFF; + if (object22_damage(obj) > 0x200) { + level_init_object_spr_num_46(obj->x_pos, obj->y_pos); + } else { + obj->spr_num = 0xFFFF; + continue; + } + } else { + assert(g_vars.player != 2); + if (object22_damage(obj) > 0x200) { + level_init_object_spr_num_46(obj->x_pos, obj->y_pos); + } + } + if ((g_vars.level_loop_counter & 1) == 0) { + int num = (obj->spr_num & 0x1FFF) + 1; + if (num > 45) { // rolling vinyl + num = 42; + } + obj->spr_num = num; + object_blinking_counter(obj) = 0; + } + ax = object22_xvelocity(obj); + dx = 4; + if (ax < 0) { + ax = -ax; + dx = -dx; + } + ax += 4; + if (ax > 96) { + dx = 0; + } + object22_xvelocity(obj) += dx; + obj->x_pos += object22_xvelocity(obj) >> 3; + ax = abs((obj->x_pos >> 4) - g_vars.tilemap_x - (TILEMAP_SCREEN_W / 32)); + if (ax >= (TILEMAP_SCREEN_W / 32 + 2)) { + obj->spr_num = 0xFFFF; + } + } + if (player_bounce_counter(&g_vars.players_table[0].obj) != 0) { + --player_bounce_counter(&g_vars.players_table[0].obj); + level_update_bouncing_tiles(&g_vars.players_table[0]); + } + assert(g_vars.player != 2); + // update monsters + for (int i = 0; i < 20; ++i) { + struct object_t *obj = &g_vars.objects_table[82 + i]; + if (obj->spr_num != 0xFFFF) { + const uint8_t *p = object82_anim_data(obj); + int num; + while (1) { + num = (int16_t)READ_LE_UINT16(p); + if (num >= 0) { + break; + } + p += num << 1; // loop + } + obj->spr_num = num | ((object82_hflip(obj) & 0x80) << 8); // hflip + object82_anim_data(obj) = p + 2; + switch (object82_type(obj)) { + case 0: + level_update_object82_type0(obj); + break; + case 1: + level_update_object82_type1(obj); + break; + case 2: + level_update_object82_type2(obj); + break; + case 3: + level_update_object82_type3(obj); + break; + case 4: + case 6: + level_update_object82_type4_6(obj); + break; + case 5: + level_update_tiles(obj, obj->x_pos, obj->y_pos, _undefined); + level_update_object82_position(obj, 0, obj->data[5].b[0]); + break; + case 7: + level_update_object82_type7(obj); + break; + case 16: + level_update_object82_type16(obj); + break; + default: + print_warning("object %d unhandled type %d", 82 + i, object82_type(obj)); + break; + } + } + } + // collision with monsters + for (int i = 0; i < 20; ++i) { + struct object_t *obj = &g_vars.objects_table[82 + i]; + if (obj->spr_num == 0xFFFF) { + continue; + } + struct player_t *player = &g_vars.players_table[0]; + if (object82_type(obj) == 5) { // jukebox for end of level + if ((player_flags2(&player->obj) & 8) == 0) { + if (level_collide_objects(obj, &g_vars.objects_table[0])) { + // do_end_of_level(); + g_vars.change_next_level_flag = 1; + continue; + } + } + } else { + if ((player_flags2(&player->obj) & 9) == 0) { + if (object82_type(obj) == 16) { // monster ko + continue; + } + if (player_hit_counter(&player->obj) == 0 && level_collide_objects(obj, &g_vars.objects_table[0])) { + if (object82_type(obj) != 7) { + level_player_hit_object(player, &g_vars.objects_table[0]); + } else if (player_y_delta(&player->obj) > 0 && obj->y_pos > player->obj.y_pos) { + player_flags2(&player->obj) |= 0x40; + } + } + } + assert(g_vars.player != 2); + } + } +} + +static void level_update_player(struct player_t *player) { + if (player_flags2(&player->obj) & 8) { + return; + } + struct object_t *player_obj = &g_vars.objects_table[player_obj_num(&player->obj)]; + player_flags(&player->obj) &= ~1; + player_obj->y_pos += 8; + for (int i = 0; i < 8; ++i) { // platforms + struct object_t *obj = &g_vars.objects_table[32 + i * 4]; + const uint16_t spr_num = player_obj->spr_num; + player_obj->spr_num = 0; + const bool ret = level_collide_objects(player_obj, obj); + player_obj->spr_num = spr_num; + if (!ret) { + continue; + } + if ((obj->spr_num & 0x1FFF) == 224) { // ball with spikes + player_x_delta(&player->obj) = (obj->x_pos <= player_obj->x_pos) ? 40 : -40; + player_flags2(&player->obj) |= 2; + level_player_hit_object(player, obj); + continue; + } else { // rotating platforms + if (player_y_delta(&player->obj) < 0) { + break; // 10D51 + } + if (obj->y_pos < player_obj->y_pos) { + continue; + } + player->obj.x_pos -= obj->data[3].w; + player->obj.y_pos = obj->y_pos - 15; + player_obj->y_pos = player->obj.y_pos; + if (player_hit_counter(&player->obj) == 0) { + player_flags2(&player->obj) &= ~2; + } + player_y_delta(&player->obj) = 0; + player_flags(&player->obj) |= 1; + player_jump_counter(&player->obj) = 7; + goto bonus_objects; // 10D51 + } + } + if (player_y_delta(&player->obj) >= 0) { + for (int i = 0; i < 8; ++i) { // crates + struct object_t *obj = &g_vars.objects_table[64 + i]; + if (obj->y_pos < player_obj->y_pos) { + continue; + } + const int num = obj->spr_num; + obj->spr_num = 0; + const int ret = level_collide_objects(player_obj, obj); + obj->spr_num = num; + if (!ret) { + continue; + } + const int dl = sprite_offsets[obj->spr_num & 0x1FFF] >> 8; + if (dl > 16 && player->obj.y_pos > obj->y_pos - 16) { + continue; + } + player->obj.y_pos = obj->y_pos - dl; + player_obj->y_pos = player->obj.y_pos; + if (player_hit_counter(&player->obj) == 0) { + player_flags2(&player->obj) &= ~2; + } + player_y_delta(&player->obj) = 0; + player_flags(&player->obj) |= 1; + player_jump_counter(&player->obj) = 7; + if (object64_counter(obj) != 0) { + --object64_counter(obj); + } + goto bonus_objects; // 10D51; + } + } + player_obj->y_pos -= 8; +bonus_objects: + for (int i = 0; i < 10; ++i) { // bonuses + struct object_t *obj = &g_vars.objects_table[72 + i]; + if (obj->spr_num == 0xFFFF || !level_collide_objects(player_obj, obj)) { + continue; + } + const int spr_num = (obj->spr_num & 0x1FFF); + if (spr_num == 194) { // cake, power up + level_player_draw_powerup_animation(player, obj); + } else { + play_sound(SOUND_10); + if (spr_num == 197) { // checkpoint + g_vars.level_start_1p_x_pos = obj->x_pos; + g_vars.level_start_1p_y_pos = obj->y_pos; + g_vars.level_start_2p_player1_x_pos = obj->x_pos; + g_vars.level_start_2p_player1_y_pos = obj->y_pos; + g_vars.level_start_2p_player2_x_pos = obj->x_pos; + g_vars.level_start_2p_player2_y_pos = obj->y_pos; + } else if (spr_num == 195) { // shield + struct object_t *player_obj = &g_vars.objects_table[player_obj_num(&player->obj)]; + object_blinking_counter(player_obj) = 240; + } else if (spr_num == 190) { + ++player->lifes_count; + } else if (spr_num == 193) { + g_vars.level_time += 50; + } else if (spr_num == 210) { + player->vinyls_count += 20; + } else if (spr_num == 192) { + if (player->energy < 4) { + ++player->energy; + } + } else if (spr_num == 191) { + player->energy = 4; + } else if (spr_num == 196) { + player->vinyls_count += 20; + } + obj->spr_num = 0xFFFF; + } + } +} + +static void level_update_players() { + if ((player_flags2(&g_vars.players_table[0].obj) & 1) == 0) { + level_update_player(&g_vars.players_table[0]); + } + if (g_vars.player == 2 && (player_flags2(&g_vars.players_table[1].obj) & 1) == 0) { + level_update_player(&g_vars.players_table[1]); + } +} + +static void init_panel(int x_offset) { + for (int y = 0; y < PANEL_H; ++y) { + memcpy(g_res.vga + (GAME_SCREEN_H - PANEL_H + y) * GAME_SCREEN_W + x_offset, g_res.board + y * 320, 320); + } + if (g_vars.player == 1) { + video_draw_dot_pattern(x_offset); + } else if (g_vars.player == 0) { + video_draw_dot_pattern(x_offset + 44 * 4); + } +} + +static void draw_panel_helper(int a) { +} + +static void draw_panel_energy(int di, int count, int x_offset) { + const uint8_t *src = g_res.board + 0x2840 + count * 40; + const int y_dst = (GAME_SCREEN_H - PANEL_H) + di / 80; + const int x_dst = (di % 80) * 4 + x_offset; + for (int y = 0; y < 6; ++y) { + memcpy(g_res.vga + (y_dst + y) * GAME_SCREEN_W + x_dst, src, 40); + src += 320; + } +} + +static void draw_panel_number(int bp, int di, int num, int x_offset) { + int y_dst = (GAME_SCREEN_H - PANEL_H) + di / 80; + int x_dst = (di % 80) * 4 + x_offset; + int digits[3]; + int count = 0; + do { + digits[count++] = num % 10; + num /= 10; + } while (num != 0 && count < 3); + if (bp == 0x1E40) { // center time + x_dst += (3 - count) * 8 / 2; + } + for (; count > 0; --count) { + const uint8_t *src = g_res.board + bp + digits[count - 1] * 8; + for (int y = 0; y < 8; ++y) { + memcpy(g_res.vga + (y_dst + y) * GAME_SCREEN_W + x_dst, src, 8); + src += 320; + } + x_dst += 8; + } +} + +static void draw_panel() { + const int x_offset = (GAME_SCREEN_W - 320) / 2; + init_panel(x_offset); + draw_panel_number(0x1E40, 0x435, MIN(g_vars.level_time, 999), x_offset); + const int bp = 0x1E90; + if (g_vars.player != 1) { + draw_panel_number(bp, 0x29F, MIN(g_vars.players_table[0].lifes_count, 99), x_offset); + draw_panel_number(bp, 0x288, g_vars.players_table[0].vinyls_count, x_offset); + draw_panel_energy(0x41E, g_vars.players_table[0].energy, x_offset); + const uint8_t al = player_power(&g_vars.players_table[0].obj); + if (al > 8) { + uint8_t cl = 0; + if (al < 33) { + ++cl; + if (al < 20) { + ++cl; + } + } + draw_panel_helper((g_vars.level_loop_counter >> cl) & 3); + } + } + if (g_vars.player != 0) { + struct player_t *player = &g_vars.players_table[0]; + if (g_vars.player != 1) { + player = &g_vars.players_table[1]; + } + draw_panel_number(bp, 0x2CB, MIN(player->lifes_count, 99), x_offset); + draw_panel_number(bp, 0x2B4, player->vinyls_count, x_offset); + draw_panel_energy(0x44A, player->energy, x_offset); + const uint8_t al = player_power(&player->obj); + if (al > 8) { + uint8_t cl = 0; + if (al < 33) { + ++cl; + if (al < 20) { + ++cl; + } + } + draw_panel_helper((g_vars.level_loop_counter >> cl) & 3); + } + } +} + +static void level_draw_objects() { + for (int i = OBJECTS_COUNT - 1; i >= 0; --i) { + struct object_t *obj = &g_vars.objects_table[i]; + if (obj->spr_num == 0xFFFF) { + continue; + } + const uint16_t num = obj->spr_num & 0x1FFF; + if (object_blinking_counter(obj) != 0) { + --object_blinking_counter(obj); + } + if (object_blinking_counter(obj) != 0 && (g_vars.level_loop_counter & 1) != 0) { + // video_set_palette_index(1); + continue; + } else { + // video_set_palette_index(sprite_palettes[num]); + } + const int spr_flag = ((obj->spr_num & 0x8000) != 0) ? 1 : 0; + const int spr_y_pos = obj->y_pos - ((g_vars.tilemap_y << 4) + g_vars.tilemap_scroll_dy) - (sprite_offsets[num] >> 8); + int _dx = obj->x_pos - ((g_vars.tilemap_x << 4) + (g_vars.tilemap_scroll_dx << 2)); + int _cl = (sprite_sizes[num] & 255); + if (spr_flag) { + _cl = (sprite_offsets[num] & 255) - _cl; + } + const int spr_x_pos = _dx - _cl; + video_draw_sprite(num, spr_x_pos, spr_y_pos, spr_flag); + } +} + +static int16_t level_player_update_anim(struct player_t *player) { + const uint8_t *p; + if (player_prev_spr_num(&player->obj) != player->obj.spr_num) { + player_idle_counter(&player->obj) = 0; + player_prev_spr_num(&player->obj) = player->obj.spr_num; + p = player_anim_table[player_prev_spr_num(&player->obj)]; + } else { + ++player_idle_counter(&player->obj); + p = player_anim_data(&player->obj); + } + int num; + while ((num = (int16_t)READ_LE_UINT16(p)) < 0) { + p += num * 2; + } + if (num & 0x2000) { + ++player_throw_counter(&player->obj); + } + player_anim_data(&player->obj) = p + 2; + return num; +} + +static void level_player_update_spr(struct player_t *player, struct object_t *obj) { + int num; + player_throw_counter(&player->obj) = 0; + if ((player_flags2(&player->obj) & 1) == 0) { + if (player->obj.spr_num != 6 && player->obj.spr_num != 19 && player_y_delta(&player->obj) > 64) { + num = abs(player_x_delta(&player->obj)); + num = (num == 32) ? 0 : 4; + if (player_flags(&player->obj) & PLAYER_FLAGS_POWERUP) { + num |= 2; + } + if (player_flags(&player->obj) & 0x40) { + num |= 1; + } + static const uint8_t data[] = { 0x0B, 0x93, 0x24, 0x93, 0x0C, 0x94, 0x24, 0x94 }; + num = data[num]; + } else { + if (player_hit_counter(&player->obj) != 0) { + --player_hit_counter(&player->obj); + num = 19; + } else { + num = level_player_update_anim(player); + } + } + } else { + num = level_player_update_anim(player); + } + const int _dx = num & 0x4000; + num &= 0x1FFF; + num |= (player_flags(&player->obj) & PLAYER_FLAGS_FACING_LEFT) << 13; + if (_dx) { + num ^= 0x8000; + } + obj->spr_num = num; +} + +// crates +static void level_tile_func_5dc9(struct object_t *obj, int bp) { + if (bp != _undefined && (player_flags2(obj) & 0x40) != 0) { + level_init_object_spr_num_92(obj->x_pos, obj->y_pos); + play_sound(SOUND_4); + player_flags2(obj) &= ~0x40; + } + g_vars.player_xscroll = 0; + obj->x_pos -= player_x_delta(obj) >> 3; + player_x_delta(obj) = 0; + if (bp == _undefined) { + return; + } + const int offset = g_vars.player_map_offset; + uint8_t al; + al = g_vars.tilemap_data[offset - g_vars.tilemap_w]; + al = g_vars.level_tiles_lut[al]; + al = level_data4[0x3C5 + al]; + if (al == 0) { + return; + } + al = g_vars.tilemap_data[offset - g_vars.tilemap_w - 1]; + al = g_vars.level_tiles_lut[al]; + al = level_data4[0x3C5 + al]; + if (al == 0) { + obj->x_pos -= 2; + return; + } + al = g_vars.tilemap_data[offset - g_vars.tilemap_w + 1]; + al = g_vars.level_tiles_lut[al]; + al = level_data4[0x3C5 + al]; + if (al != 0) { + if ((player_flags(obj) & 0x10) == 0) { + return; + } + level_player_die((struct player_t *)obj); + } + obj->x_pos += 2; +} + +static void level_tile_func_5daa(struct object_t *obj, int bp) { + if (bp != _undefined && (player_flags2(obj) & 0x40) != 0) { + return; + } + player_y_delta(obj) = MIN(player_y_delta(obj) + 7, 120); +} + +static void level_tile_func_5e58(struct object_t *obj, int bp) { + if (bp != _undefined) { + player_flags2(obj) &= ~0x40; + level_init_object_spr_num_92(obj->x_pos, obj->y_pos - 24); + play_sound(SOUND_4); + } + player_y_delta(obj) = 0; + if (player_flags(obj) & PLAYER_FLAGS_STAIRS) { + obj->y_pos += 2; + } +} + +static void level_tile_func_5e83(struct object_t *obj, int bp) { + if (bp != _undefined) { + player_flags2(obj) &= ~0x40; + } + if (player_y_delta(obj) < 0) { + level_tile_func_5daa(obj, bp); + } else { + player_flags2(obj) &= ~2; + player_y_delta(obj) = 0; + obj->y_pos &= ~15; + if (bp == _undefined) { // (obj < &g_vars.players_table[0]) + return; + } + if (player_jump_counter(obj) != 7) { + player_jump_counter(obj) = 7; + player_x_delta(obj) >>= 1; + } + } +} + +// stairs +static void level_tile_func_5d3e(struct object_t *obj, int bp) { + if (bp == _undefined) { + level_tile_func_5daa(obj, bp); + return; + } + player_flags2(obj) &= ~2; + if (player_flags2(obj) & 0x40) { + return; + } + if (player_hit_counter(obj) != 0) { + level_tile_func_5daa(obj, bp); + return; + } + if (player_x_delta(obj) != 0 && player_y_delta(obj) < 0) { + level_tile_func_5daa(obj, bp); + return; + } + const int offset = g_vars.player_map_offset; + uint8_t ah = (player_flags(obj) & 0x40) | (player_flags2(obj) & 1); + uint8_t al = g_vars.tilemap_data[offset - g_vars.tilemap_w]; + al = g_vars.level_tiles_lut[al]; + if (al == 8) { + if (ah != 0) { + level_tile_func_5daa(obj, bp); + return; + } + } else { + player_y_delta(obj) = 0; + if (ah != 0 || (player_flags2(obj) & 4) == 0) { + level_tile_func_5e83(obj, bp); + return; + } + } + player_y_delta(obj) = 0; + player_flags(obj) |= PLAYER_FLAGS_STAIRS; +} + +// bouncing mushrooms +static void level_tile_func_5f16(struct object_t *obj, int bp) { + if (player_y_delta(obj) < 0) { + return; + } else if (player_y_delta(obj) == 0) { + level_tile_func_5e83(obj, bp); + } else { + if (obj != &g_vars.players_table[0].obj) { + print_warning("Unexpected object #%d spr %d on tile 5f16", obj - g_vars.objects_table, obj->spr_num); + return; + } + player_flags2(obj) &= ~2; + obj->y_pos &= ~15; + player_y_delta(obj) = MAX(-player_y_delta(obj) - 16, -112); + player_jump_counter(obj) = 0; + player_bounce_counter(obj) = 7; + player_tilemap_offset(obj) = g_vars.player_map_offset; + const int x = (obj->x_pos >> 4) - g_vars.tilemap_x; + if (x < 0 || x > TILEMAP_SCREEN_W / 16) { + return; + } + const int y = (obj->y_pos >> 4) - g_vars.tilemap_y; + if (y < 0 || y > (TILEMAP_SCREEN_H / 16) + 1) { + return; + } + play_sound(SOUND_2); + } +} + +static void level_tile_func_5eec(struct object_t *obj, int bp) { + assert(bp != _undefined); + struct player_t *player = (struct player_t *)obj; + const int offset = g_vars.player_map_offset; + g_vars.tilemap_data[offset] = 0; + ++player->vinyls_count; + level_init_object_spr_num_104(offset % g_vars.tilemap_w, offset / g_vars.tilemap_w); + play_sound(SOUND_5); + level_tile_func_5daa(obj, bp); +} + +static void level_tile_func_5f0e(struct object_t *obj, int bp) { + level_tile_func_5f16(obj, bp); + obj->data[7].b[0] = 0; +} + +// sloppy tiles +static void level_tile_func_5f7b(struct object_t *obj, int bp, int bx) { + if (bp != _undefined) { + player_flags2(obj) &= ~0x40; + } + if (player_y_delta(obj) < 0) { + return; + } + player_flags2(obj) &= ~2; + player_y_delta(obj) = 0; + obj->y_pos &= ~15; + if (bp == _undefined) { // (obj < &g_vars.players_table[0]) + return; + } + struct player_t *player = (struct player_t *)obj; + if (player_jump_counter(&player->obj) != 7) { + player_jump_counter(&player->obj) = 7; + player_x_delta(&player->obj) >>= 1; + } + const uint8_t *p = level_data4 + 0x3F0 + ((bx - 18) << 3); + player->obj.y_pos += (int8_t)p[player->obj.x_pos & 15]; +} + +// electricity +static void level_tile_func_5f94(struct object_t *obj) { + if (g_options.cheats & CHEATS_DECOR_NO_HIT) { + return; + } + level_player_die((struct player_t *)obj); +} + +// spikes +static void level_tile_func_5f9e(struct object_t *obj, int bp) { + level_player_hit_object(&g_vars.players_table[0], &g_vars.objects_table[0]); +} + +static void level_tile_func_5fa7(struct object_t *obj, int bp) { + level_player_hit_object(&g_vars.players_table[0], &g_vars.objects_table[0]); + level_tile_func_5e83(obj, bp); +} + +static void level_tile_func(int ax, struct object_t *obj, int bp, int bx) { + switch (ax) { + case 0x5D3E: + level_tile_func_5d3e(obj, bp); + break; + case 0x5DC8: + break; + case 0x5DC9: + level_tile_func_5dc9(obj, bp); + break; + case 0x5DAA: + level_tile_func_5daa(obj, bp); + break; + case 0x5E58: + level_tile_func_5e58(obj, bp); + break; + case 0x5E83: + level_tile_func_5e83(obj, bp); + break; + case 0x5EEC: + level_tile_func_5eec(obj, bp); + break; + case 0x5F0E: + level_tile_func_5f0e(obj, bp); + break; + case 0x5F16: + level_tile_func_5f16(obj, bp); + break; + case 0x5F73: + level_tile_func_5f7b(obj, bp, 18); + break; + case 0x5F78: + level_tile_func_5f7b(obj, bp, 20); + break; + case 0x5F7B: + level_tile_func_5f7b(obj, bp, bx); + break; + case 0x5F94: + level_tile_func_5f94(obj); + break; + case 0x5F9E: + level_tile_func_5f9e(obj, bp); + break; + case 0x5FA7: + level_tile_func_5fa7(obj, bp); + break; + default: + print_warning("Unimplemented tile func %x", ax); + break; + } +} + +static void level_update_tiles(struct object_t *obj, int ax, int dx, int bp) { + const int level_update_tiles_x = ax; + const int level_update_tiles_y = dx; + g_vars.player_map_offset = (dx >> 4) * g_vars.tilemap_w + (ax >> 4); + uint8_t _al = 0; + while (1) { + const int offset = (dx >> 4) * g_vars.tilemap_w + (ax >> 4); + _al = level_get_tile(offset); + if (_al != 5) { + break; + } + obj->y_pos -= 16; + dx = obj->y_pos; + } + const int num = _al << 1; + ax = 0x5DAA; + if (obj->y_pos > 0) { + if (bp == _undefined) { + ax = tile_funcs0[_al]; + } else { + ax = tile_funcs1[_al]; // player + } + if (ax == 0 && (obj->data[6].b[0] & 1) != 0) { + return; + } + } + level_tile_func(ax, obj, bp, num); + if (bp == _undefined) { + return; // not player + } + ax = level_update_tiles_x + ((player_x_delta(obj) < 0) ? -6 : 6); + dx = level_update_tiles_y; + const int offset = (dx >> 4) * g_vars.tilemap_w + (ax >> 4); + _al = level_get_tile(offset - g_vars.tilemap_w); + level_tile_func(tile_funcs2[_al], obj, bp, _al * 2); + if (obj->spr_num == 3) { + _al = level_get_tile(offset - g_vars.tilemap_w * 2); + level_tile_func(tile_funcs2[_al], obj, bp, _al * 2); + } + player_flags(obj) &= ~0x10; + player_flags2(obj) &= ~0x20; + if (player_jump_counter(obj) == 7) { + _al = level_get_tile(offset - g_vars.tilemap_w * 2); + if (_al == 10) { + player_flags2(obj) |= 0x20; + } + if (tile_funcs3[_al] == 0x5E58) { + player_flags(obj) |= 0x10; + player_flags2(obj) &= ~1; + } + } + if ((player_flags(obj) & PLAYER_FLAGS_STAIRS) == 0 && player_y_delta(obj) >= 0) { + return; + } + _al = level_get_tile(offset - g_vars.tilemap_w * 2); + level_tile_func(tile_funcs3[_al], obj, bp, _al * 2); +} + +static bool sub_15D99(struct player_t *player, int ax, int dx) { + if ((player_flags2(&player->obj) & 1) == 0) { + return false; + } + if (player->change_hdir_counter < 30) { + const int offset = (dx >> 4) * g_vars.tilemap_w + (ax >> 4); + uint8_t _al = level_get_tile(offset - g_vars.tilemap_w); + if (tile_funcs3[_al] != 0x5E58) { + return true; + } + } + level_hit_player(player); + return true; +} + +static void sub_15BA5(struct player_t *player) { + struct object_t *obj = &g_vars.objects_table[player_obj_num(&player->obj)]; + obj->x_pos = player->obj.x_pos; + obj->y_pos = player->obj.y_pos; + if (player_flags2(&player->obj) & 8) { + obj->spr_num = 19; + } else { + level_player_update_spr(player, obj); + const int tile_x = (player->obj.x_pos >> 4); + const int tile_y = (player->obj.y_pos >> 4); + int offset = tile_y * g_vars.tilemap_w + tile_x; + const int spr_h = sprite_offsets[player->obj.spr_num & 0x1FFF] >> 8; + for (int i = 0; i < (spr_h >> 4); ++i) { + offset -= g_vars.tilemap_w; + if (offset < 0) { + break; + } + uint8_t _al = g_vars.tilemap_data[offset]; + _al = g_vars.level_tiles_lut[_al]; + if (_al == 6) { + g_vars.tilemap_data[offset] = 0; + ++player->vinyls_count; + level_init_object_spr_num_104(tile_x, tile_y - i - 1); + play_sound(SOUND_5); + } + } + player_flags(&player->obj) &= ~PLAYER_FLAGS_STAIRS; + if (!sub_15D99(player, player->obj.x_pos, player->obj.y_pos)) { + level_update_tiles(&player->obj, player->obj.x_pos, player->obj.y_pos, 0); + } + } + obj->y_pos = player->obj.y_pos; + const int spr_num = obj->spr_num & 0x1FFF; + if ((player_flags(&player->obj) & 0x60) == 0x60) { + obj->spr_num += (spr_num <= 148) ? 13 : 12; + } + if ((player_flags(&player->obj) & PLAYER_FLAGS_JAKE) != 0) { + int d = (spr_num < 50) ? 50 : 25; + if (spr_num == 138) { + d = 1; + } + obj->spr_num += d; + } +} + +static void level_adjust_player_position() { + if (g_vars.player_hit_counter != 0) { + --g_vars.player_hit_counter; + } + sub_15BA5(&g_vars.players_table[0]); + assert(g_vars.player < 2); +} + +static void level_sync() { + ++g_vars.level_time_counter; + if (g_vars.level_time_counter >= 28) { + g_vars.level_time_counter = 0; + --g_vars.level_time; + } + g_sys.update_screen(g_res.vga, 1); + render_clear_sprites(); + const int diff = (g_vars.timestamp + (1000 / 30)) - g_sys.get_timestamp(); + g_sys.sleep(MAX(diff, 10)); + g_vars.timestamp = g_sys.get_timestamp(); +} + +static void load_level_data(uint16_t level) { + g_vars.level_num = (level >> 8); + const uint8_t triggers_count = (level & 255); + g_vars.level_tiles_lut = (level & 0xFF00) + level_data_tiles_lut; + load_file(_background[g_vars.level_num]); + video_copy_vga(0xB500); + load_file(_decor[g_vars.level_num]); + const uint8_t *buffer = g_res.tmp; + memcpy(g_vars.tile_palette_table, buffer + 0x8000, 0x100); + int bp = 0; + const uint8_t *data = buffer + 0x8100; + for (int i = 0; i < triggers_count; ++i) { + const int num = data[18] * 16; + data += num + 24; + const int w = (int16_t)READ_LE_UINT16(data - 3); + const int h = (int16_t)READ_LE_UINT16(data - 5); + print_debug(DBG_GAME, "level len:%d w:%d h:%d", num, w, h); + bp += w * h; + } + memcpy(g_vars.triggers_table, data, 19); data += 19; + uint8_t *dst = g_vars.triggers_table + 19; + int count = data[-1]; // data[18] + print_debug(DBG_GAME, "level 0x%x count %d", level, count); + assert(count <= TRIGGERS_COUNT); + const int stride = count * sizeof(int16_t); + for (int i = 0; i < count; ++i, data += 2) { + for (int i = 0; i < 8; ++i) { + memcpy(dst, data + stride * i, sizeof(int16_t)); + dst += 2; + } + } + data += 7 * stride; // 14 * count + if (g_debug_mask & DBG_GAME) { + for (int i = 0; i < g_vars.triggers_table[18]; ++i) { + const uint8_t *obj_data = g_vars.triggers_table + 19 + i * 16; + const int x_pos = READ_LE_UINT16(obj_data); + const int y_pos = READ_LE_UINT16(obj_data + 2); + const int type1 = READ_LE_UINT16(obj_data + 4); + const int type2 = READ_LE_UINT16(obj_data + 6); + const int num = READ_LE_UINT16(obj_data + 8); + print_debug(DBG_GAME, "trigger #%d pos %d,%d type %d,%d num %d", i, x_pos, y_pos, type1, type2, num); + } + } + g_vars.tilemap_h = READ_LE_UINT16(data); data += 2; + g_vars.tilemap_w = READ_LE_UINT16(data); data += 2; + g_vars.tilemap_type = *data++; + print_debug(DBG_GAME, "tilemap w:%d h:%d type:%d", g_vars.tilemap_w, g_vars.tilemap_h, g_vars.tilemap_type); + data = buffer + 0x8100; + int offset = 0; + count = level_data3[g_vars.level_num]; + print_debug(DBG_GAME, "level_data[3] %d bp %d", count, bp); + for (int i = 0; i < count; ++i) { + const int num = data[offset + 18] * 16; + offset += num + 24; + } + offset += bp; + assert(offset >= g_vars.tilemap_h); + g_vars.tilemap_data = g_res.tmp + 0x8100 + offset; + for (int i = 0; i < 0x600; ++i) { + g_vars.tilemap_lut_init[i] = (i & 0xFF); + } + g_vars.tilemap_current_lut = g_vars.tilemap_lut_init; + const uint8_t *tiles_lut = 0; + switch (g_vars.tilemap_type & 0x3F) { + case 0: tiles_lut = tile_lut_data0; break; + case 1: tiles_lut = tile_lut_data1; break; + case 2: tiles_lut = tile_lut_data2; break; + case 3: tiles_lut = tile_lut_data3; break; + default: + print_error("load_level_data(): Invalid tilemap type %d", g_vars.tilemap_type); + break; + } + for (int i = 0; i < tiles_lut[0]; ++i) { + const uint8_t start = tiles_lut[i * 3 + 1]; + const uint8_t count = tiles_lut[i * 3 + 2]; + const uint8_t value = tiles_lut[i * 3 + 3]; + print_debug(DBG_GAME, "tiles start:%d count:%d value:0x%x", start, count, value); + memset(g_vars.tilemap_lut_init2 + start, value, count); + } + const uint8_t *src = tiles_lut; + int total_count = src[0]; // total_count + while (1) { + const uint8_t start = src[1]; + const uint8_t count = src[2]; + src += 3; + --total_count; + if (total_count < 0) { + break; + } + uint8_t bh = 0; + do { + uint8_t ch = 0; + do { + assert(bh < 6); + uint8_t dh = ch; + for (int i = 0; i < count; ++i) { + g_vars.tilemap_lut_init[(bh << 8) | (start + i)] = start + dh; + ++dh; + if (dh == count) { + dh = 0; + } + } + ++bh; + ++ch; + if (bh == 6) { + break; + } + } while (bh != count); + } while (bh == 3); + } + const uint8_t *p = g_vars.tilemap_lut_init; + for (int i = 0; i < 256; ++i) { + const uint8_t num = p[i]; + if (p[0x100 + i] != num) { + if (p[0x300 + i] == num) { + g_vars.tilemap_lut_type[i] = 2; + } else { + g_vars.tilemap_lut_type[i] = 1; + } + } else { + g_vars.tilemap_lut_type[i] = 0; + } + } + g_vars.tilemap_lut_type[0xF8] = 1; + g_vars.tilemap_end_x = g_vars.tilemap_w - (TILEMAP_SCREEN_W / 16 + 1); + g_vars.tilemap_end_y = g_vars.tilemap_h - (TILEMAP_SCREEN_H / 16 + 1); + g_vars.players_table[0].vinyls_count = READ_LE_UINT16(g_vars.triggers_table + 0xC); + g_vars.players_table[1].vinyls_count = READ_LE_UINT16(g_vars.triggers_table + 0xE); + g_vars.level_time = g_vars.level_time2 = READ_LE_UINT16(g_vars.triggers_table + 0x10); + g_vars.level_time_counter = 0; + const uint16_t num = (g_vars.level_num << 8) | triggers_count; + if (num != g_vars.level_pos_num) { + g_vars.level_start_2p_player1_x_pos = READ_LE_UINT16(g_vars.triggers_table); + g_vars.level_start_2p_player1_y_pos = READ_LE_UINT16(g_vars.triggers_table + 0x4); + g_vars.level_start_2p_player2_x_pos = READ_LE_UINT16(g_vars.triggers_table + 0x2); + g_vars.level_start_2p_player2_y_pos = READ_LE_UINT16(g_vars.triggers_table + 0x6); + g_vars.level_start_1p_x_pos = READ_LE_UINT16(g_vars.triggers_table + 0x8); + g_vars.level_start_1p_y_pos = READ_LE_UINT16(g_vars.triggers_table + 0xA); + } + g_vars.level_pos_num = num; + g_vars.level_loop_counter = 0; +} + +static void reset_level_data() { + memset(g_vars.objects_table, 0xFF, sizeof(g_vars.objects_table)); + g_vars.change_next_level_flag = 0; + g_vars.player_hit_counter = 0; + g_vars.player_xscroll = 0; + g_vars.player_map_offset = 0; + g_vars.throw_vinyl_counter = 0; + g_vars.tilemap_flags = 0; + memset(&g_vars.objects_table[32], 0xFF, 40 * sizeof(struct object_t)); + g_vars.tilemap_flags |= g_vars.tilemap_type >> 5; + player_flags(&g_vars.players_table[0].obj) = 0; + player_prev_spr_num(&g_vars.players_table[0].obj) = 0; + player_flags(&g_vars.players_table[1].obj) = 0; + player_prev_spr_num(&g_vars.players_table[1].obj) = 0; +} + +static void level_init_players() { + for (int i = 0; i < 128; ++i) { + g_vars.buffer[i] = 0xFFFF; + g_vars.buffer[128 + i] = 0xFF20; + } + object_blinking_counter(&g_vars.objects_table[0]) = 0; + object_blinking_counter(&g_vars.objects_table[1]) = 0; + assert(g_vars.player != 2); + struct player_t *player = &g_vars.players_table[0]; + player->obj.x_pos = g_vars.level_start_1p_x_pos; + player->obj.y_pos = g_vars.level_start_1p_y_pos; + player_prev_spr_num(&player->obj) = _undefined; + player_obj_num(&player->obj) = 0; + player_jump_counter(&player->obj) = 7; + player_x_delta(&player->obj) = 0; + player_y_delta(&player->obj) = 0; + player->unk_counter = 0; + player->change_hdir_counter = 0; + player_bounce_counter(&player->obj) = 0; + player_hit_counter(&player->obj) = 0; + player_flags2(&player->obj) = 0; + const uint8_t flag = ((g_vars.player & 1) != 0) ? PLAYER_FLAGS_JAKE : 0; + player_flags(&player->obj) |= flag; + player_flags2(&player->obj) = flag; + player->energy = 4; + g_vars.objects_table[1].spr_num = 0xFFFF; +} + +static void level_init_tilemap() { + g_vars.tilemap_x = 0; + g_vars.tilemap_y = 0; + g_vars.tilemap_scroll_xoffset = 0; + g_vars.tilemap_scroll_yoffset = 0; + g_vars.tilemap_scroll_dx = 0; + g_vars.tilemap_scroll_dy = 0; + while (1) { + const int current_y = g_vars.tilemap_y; + const int _dx = g_vars.tilemap_y + 11; + const int _ax = g_vars.players_table[0].obj.y_pos >> 4; + if (_dx > _ax) { + break; + } + level_adjust_vscroll_down(16); + if (g_vars.tilemap_y == current_y) { + break; + } + } + level_adjust_vscroll_down(16); + level_adjust_vscroll_down(16); + int bp = _undefined; + while (1) { + const int _dx = g_vars.tilemap_x + 10; + const int _ax = g_vars.players_table[0].obj.x_pos >> 4; + if (_dx >= _ax || bp == _dx) { + break; + } + level_adjust_hscroll_right(4); + bp = _dx; + } +} + +static void level_player_draw_powerdown_animation(struct player_t *player) { + if (player_flags(&player->obj) & PLAYER_FLAGS_POWERUP) { + player_flags(&player->obj) &= ~PLAYER_FLAGS_POWERUP; + struct object_t *player_obj = &g_vars.objects_table[player_obj_num(&player->obj)]; + player_jump_counter(player_obj) = 0; + static const uint8_t data[] = { // death animation sprites + 0x7A, 0x7A, 0x7A, 0x7A, 0x7A, 0x7A, 0x7A, 0x79, 0x7B, 0x7B, 0x7B, + 0x7B, 0x7B, 0x7B, 0x7A, 0x7A, 0x7C, 0x7C, 0x7C, 0x7C, 0x7C, 0x7B, + 0x7B, 0x7B, 0x7D, 0x7D, 0x7D, 0x7C, 0x7C, 0x7C, 0x7C, 0x7C, 0x7D, + 0x7D, 0x7D, 0x7C, 0x8A, 0xFF + }; + for (int i = 0; data[i] != 0xFF; ++i) { + level_draw_tilemap(); + level_draw_objects(); + level_sync(); + player_obj->spr_num = data[i]; + if (player_flags(&player->obj) & PLAYER_FLAGS_JAKE) { + player_obj->spr_num += 11; + } + } + } +} + +static void level_player_draw_powerup_animation(struct player_t *player, struct object_t *obj) { + if ((player_flags(&player->obj) & PLAYER_FLAGS_POWERUP) == 0) { + play_sound(SOUND_10); + if (player_flags(&player->obj) & 0x40) { + player_flags(&player->obj) &= ~0x40; + } + player_flags(&player->obj) |= PLAYER_FLAGS_POWERUP; + obj->spr_num = 0xFFFF; + static const uint8_t data[] = { // power animation sprites + 0x7D, 0x7D, 0x7D, 0x7D, 0x7D, 0x74, 0x75, 0x74, 0x75, 0x7C, 0x7C, + 0x7C, 0x7C, 0x75, 0x75, 0x76, 0x75, 0x76, 0x7B, 0x7B, 0x7B, 0x7B, + 0x76, 0x76, 0x77, 0x76, 0x77, 0x7A, 0x7A, 0x7A, 0x77, 0x77, 0x77, + 0x78, 0x77, 0x78, 0x7E, 0x78, 0x78, 0x78, 0x78, 0x79, 0x77, 0x79, + 0x79, 0xFF + }; + struct object_t *player_obj = &g_vars.objects_table[player_obj_num(&player->obj)]; + for (int i = 0; data[i] != 0xFF; ++i) { + level_draw_tilemap(); + level_draw_objects(); + level_sync(); + player_obj->spr_num = data[i]; + if (player_flags(&player->obj) & PLAYER_FLAGS_JAKE) { + player_obj->spr_num += 11; + } + } + } +} + +static void init_level(uint16_t level) { + load_level_data(level); + reset_level_data(); + level_init_players(); + level_init_tilemap(); + level_draw_tilemap(); + g_vars.reset_palette_flag = 0; +} + +static bool start_level() { + const uint8_t *data = (g_vars.player == 2) ? level_data2p : level_data1p; + const uint16_t level = READ_BE_UINT16(data + (g_vars.level - 1) * 2); + if (level == 0xFFFF) { + do_game_win_screen(); + return false; + } else { + init_level(level); + return true; + } +} + +static bool change_level() { + if ((g_vars.level & 7) == 0 && (g_vars.level >> 3) < 4) { + do_difficulty_screen(); + } + if ((g_vars.level & 3) == 0 && g_vars.level < LEVELS_COUNT) { + do_level_password_screen(); + } + do_level_number_screen(); + const int num = ((g_vars.level >> 3) & 3) + 1; + play_music(num); + return start_level(); +} + +void do_level() { + change_level(); + while (!g_sys.input.quit) { + g_vars.timestamp = g_sys.get_timestamp(); + update_input(); + level_update_palette(); + level_update_input(); + level_update_triggers(); + level_update_players(); + level_draw_tilemap(); + draw_panel(); + level_reset_palette(); + level_draw_objects(); + level_adjust_player_position(); + level_sync(); + ++g_vars.level_loop_counter; + if (g_vars.change_next_level_flag) { + ++g_vars.level; + if (change_level()) { + continue; + } + break; + } + assert(g_vars.player != 2); + if ((player_flags2(&g_vars.players_table[0].obj) & 0x10) == 0) { + continue; + } + // player fell or no energy left + fade_out_palette(); + assert(g_vars.player != 2); + if (g_vars.players_table[0].lifes_count != 0) { + start_level(); + continue; + } + do_game_over_screen(); + break; + } +} diff --git a/ja/resource.c b/ja/resource.c new file mode 100644 index 0000000..3802237 --- /dev/null +++ b/ja/resource.c @@ -0,0 +1,88 @@ + +#include +#include "resource.h" +#include "unpack.h" +#include "util.h" + +static const int TMP_SIZE = 72728; + +static const int BACKGROUND_SIZE = 320 * 200; + +static const char *_datapath; + +struct resource_t g_res; + +void res_init(const char *path, int vga_size) { + _datapath = path; + + char filepath[MAXPATHLEN]; + + static const int FONT_SIZE = 7264; + g_res.font = (uint8_t *)malloc(FONT_SIZE); + if (!g_res.font) { + print_error("Failed to allocate font buffer, %d bytes", FONT_SIZE); + } + snprintf(filepath, sizeof(filepath), "%s/font256.eat", _datapath); + unpack(filepath, g_res.font, FONT_SIZE); + + static const int BOARD_SIZE = 12800; + g_res.board = (uint8_t *)malloc(BOARD_SIZE); + if (!g_res.board) { + print_error("Failed to allocate board buffer, %d bytes", BOARD_SIZE); + } + snprintf(filepath, sizeof(filepath), "%s/board.eat", _datapath); + unpack(filepath, g_res.board, BOARD_SIZE); + + static const int SPRITES_SIZE = 108044; + g_res.sprites = (uint8_t *)malloc(SPRITES_SIZE); + if (!g_res.sprites) { + print_error("Failed to allocate sprites buffer, %d bytes", SPRITES_SIZE); + } + snprintf(filepath, sizeof(filepath), "%s/sprites.eat", _datapath); + unpack(filepath, g_res.sprites, SPRITES_SIZE); + + static const int SAMPLES_SIZE = 49398; + g_res.samples = (uint8_t *)malloc(SAMPLES_SIZE); + if (!g_res.samples) { + print_error("Failed to allocate samples buffer, %d bytes", SAMPLES_SIZE); + } + snprintf(filepath, sizeof(filepath), "%s/samples.eat", _datapath); + unpack(filepath, g_res.samples, SAMPLES_SIZE); + + g_res.tmp = (uint8_t *)malloc(TMP_SIZE); + if (!g_res.tmp) { + print_error("Failed to allocate tmp buffer, %d bytes", TMP_SIZE); + } + + g_res.background = (uint8_t *)malloc(BACKGROUND_SIZE); + if (!g_res.background) { + print_error("Failed to allocate tmp buffer, %d bytes", BACKGROUND_SIZE); + } + + g_res.vga = (uint8_t *)malloc(vga_size); + if (!g_res.vga) { + print_error("Failed to allocate vga buffer, %d bytes", vga_size); + } +} + +void res_fini() { + free(g_res.font); + g_res.font = 0; + free(g_res.board); + g_res.board = 0; + free(g_res.sprites); + g_res.sprites = 0; + free(g_res.tmp); + g_res.tmp = 0; + free(g_res.background); + g_res.background = 0; + free(g_res.vga); + g_res.vga = 0; +} + +int load_file(const char *filename) { + char filepath[MAXPATHLEN]; + snprintf(filepath, sizeof(filepath), "%s/%s", _datapath, filename); + print_debug(DBG_RESOURCE, "load_file '%s'", filepath); + return unpack(filepath, g_res.tmp, TMP_SIZE); +} diff --git a/ja/resource.h b/ja/resource.h new file mode 100644 index 0000000..cfea669 --- /dev/null +++ b/ja/resource.h @@ -0,0 +1,23 @@ + +#ifndef RESOURCE_H__ +#define RESOURCE_H__ + +#include "intern.h" + +struct resource_t { + uint8_t *font; + uint8_t *board; + uint8_t *sprites; + uint8_t *samples; + uint8_t *tmp; + uint8_t *background; + uint8_t *vga; +}; + +extern struct resource_t g_res; + +extern void res_init(const char *datapath, int vga_size); +extern void res_fini(); +extern int load_file(const char *filename); + +#endif diff --git a/ja/screen.c b/ja/screen.c new file mode 100644 index 0000000..7d8eeb5 --- /dev/null +++ b/ja/screen.c @@ -0,0 +1,225 @@ + +/* screen drawing */ + +#include "game.h" +#include "resource.h" +#include "sys.h" +#include "util.h" + +#define MAX_SPRITES 288 +#define MAX_SPRITESHEET_W 1024 +#define MAX_SPRITESHEET_H 512 + +void video_draw_dot_pattern(int offset) { + static const int W = 144; + uint8_t *dst = g_res.vga + (GAME_SCREEN_H - PANEL_H) * GAME_SCREEN_W + offset; + for (int y = 0; y < PANEL_H; ++y) { + for (int x = 0; x < W; x += 2) { + dst[x + (y & 1)] = 0; + } + dst += GAME_SCREEN_W; + } +} + +void video_draw_sprite(int num, int x, int y, int flag) { + render_add_sprite(RENDER_SPR_GAME, num, x, y, flag != 0); +} + +void video_draw_string(const char *s, int offset, int hspace) { + while (*s) { + uint8_t code = *s++; + if (code != 0x20) { + if (code >= 0x41) { + code -= 0x41; + } else { + code -= 0x16; + } + ja_decode_chr(g_res.font + code * 200, 200, g_res.tmp + 768 + offset, 320); + } + offset += hspace; + } +} + +void video_copy_vga(int size) { + if (size == 0xB500) { + memcpy(g_res.background, g_res.tmp + 768, 64000); + } else { + g_sys.set_screen_palette(g_res.tmp, 0, 256, 6); + assert(size == 0x7D00); + const uint8_t *src = g_res.tmp + 768; + if (GAME_SCREEN_W * GAME_SCREEN_H == 64000) { + memcpy(g_res.vga, src, 64000); + } else { + memset(g_res.vga, 0, GAME_SCREEN_W * GAME_SCREEN_H); + for (int y = 0; y < MIN(200, GAME_SCREEN_H); ++y) { + memcpy(g_res.vga + y * GAME_SCREEN_W, src, MIN(320, GAME_SCREEN_W)); + src += 320; + } + } + g_sys.update_screen(g_res.vga, 0); + } +} + +void video_vsync(int delay) { +} + +void fade_in_palette() { + if (!g_sys.input.quit) { + g_sys.fade_in_palette(); + } +} + +void fade_out_palette() { + if (!g_sys.input.quit) { + g_sys.fade_out_palette(); + } +} + +void ja_decode_spr(const uint8_t *src, int w, int h, uint8_t *dst, int dst_pitch, uint8_t pal_mask) { + const int bitplane_size = w * h; + for (int y = 0; y < h; ++y) { + for (int x = 0; x < w; ++x) { + for (int i = 0; i < 8; ++i) { + int color = 0; + const int mask = 1 << (7 - i); + for (int bit = 0; bit < 4; ++bit) { + if (src[bit * bitplane_size] & mask) { + color |= 1 << bit; + } + } + if (color != 0) { + dst[x * 8 + i] = pal_mask | color; + } + } + ++src; + } + dst += dst_pitch; + } +} + +void ja_decode_chr(const uint8_t *buffer, const int size, uint8_t *dst, int dst_pitch) { + for (int y = 0; y < 25; ++y) { + const int offset = y * 2; + const uint16_t p[] = { + READ_BE_UINT16(buffer + offset), + READ_BE_UINT16(buffer + offset + 50), + READ_BE_UINT16(buffer + offset + 100), + READ_BE_UINT16(buffer + offset + 150) + }; + for (int x = 0; x < 16; ++x) { + const uint16_t mask = 1 << (15 - x); + uint8_t color = 0; + for (int b = 0; b < 4; ++b) { + if (p[b] & mask) { + color |= (1 << b); + } + } + if (color != 0) { + dst[x] = 0xD0 | color; + } + } + dst += dst_pitch; + } +} + +void ja_decode_tile(const uint8_t *buffer, uint8_t pal_mask, uint8_t *dst, int dst_pitch, int x_offset, int y_offset) { + int tile_w = 16; + if (x_offset < 0) { + tile_w += x_offset; + buffer -= x_offset / 2; + x_offset = 0; + } + if (x_offset + tile_w > TILEMAP_SCREEN_W) { + tile_w = TILEMAP_SCREEN_W - x_offset; + } + if (tile_w <= 0) { + return; + } + int tile_h = 16; + if (y_offset < 0) { + tile_h += y_offset; + buffer -= y_offset * 8; + y_offset = 0; + } + if (y_offset + tile_h > TILEMAP_SCREEN_H) { + tile_h = TILEMAP_SCREEN_H - y_offset; + } + if (tile_h <= 0) { + return; + } + dst += y_offset * dst_pitch + x_offset; + for (int y = 0; y < tile_h; ++y) { + uint8_t *p = dst; + for (int x = 0; x < tile_w / 2; ++x) { + const uint8_t color1 = buffer[x] >> 4; + const uint8_t color2 = buffer[x] & 15; + if (color1 != 0) { + *p = pal_mask | color1; + } + ++p; + if (color2 != 0) { + *p = pal_mask | color2; + } + ++p; + } + buffer += 8; + dst += dst_pitch; + } +} + +void video_load_sprites() { + struct sys_rect_t r[MAX_SPRITES]; + uint8_t *data = (uint8_t *)calloc(MAX_SPRITESHEET_W * MAX_SPRITESHEET_H, 1); + if (data) { + int current_x = 0; + int max_w = 0; + int current_y = 0; + int max_h = 0; + + int total_size = 0; + int count = 0; + uint8_t value; + for (int i = 0; (value = sprite_offsets[i] & 255) != 0; ++i, ++count) { + + value = (value >> 3) | ((value & 7) << 5); + if ((value & 0xE0) != 0) { + value &= ~0xE0; + ++value; + } + const int h = sprite_offsets[i] >> 8; + const int w = value * 8; + assert((sprite_offsets[i] & 255) == w); + const int size = (h * value) * 4; + + if (current_x + w > MAX_SPRITESHEET_W) { + current_y += max_h; + if (current_x > max_w) { + max_w = current_x; + } + current_x = 0; + max_h = h; + } else { + if (h > max_h) { + max_h = h; + } + } + ja_decode_spr(g_res.sprites + total_size, w / 8, h, data + current_y * MAX_SPRITESHEET_W + current_x, MAX_SPRITESHEET_W, sprite_palettes[i] * 0x10); + total_size += size; + + r[i].x = current_x; + r[i].y = current_y; + r[i].w = w; + r[i].h = h; + current_x += w; + if (h > max_h) { + max_h = h; + } + } + assert(count <= MAX_SPRITES); + assert(max_w <= MAX_SPRITESHEET_W); + assert(current_y + max_h <= MAX_SPRITESHEET_H); + render_unload_sprites(RENDER_SPR_GAME); + render_load_sprites(RENDER_SPR_GAME, count, r, data, MAX_SPRITESHEET_W, current_y + max_h, 0, 0x0); + free(data); + } +} diff --git a/ja/sound.c b/ja/sound.c new file mode 100644 index 0000000..428adfc --- /dev/null +++ b/ja/sound.c @@ -0,0 +1,123 @@ + +#include "game.h" +#include "resource.h" +#include "sys.h" +#include "util.h" + +#include + +#define MAX_SOUNDS 16 + +static const char *_modules[] = { + "almost.mod", + "every.mod", + "bartende.mod", + "petergun.mod", + "shoot.mod" +}; + +struct mixerchannel_t { + uint8_t *data; + uint32_t pos; + uint32_t step; + uint32_t size; + const int8_t *seq; +}; + +static struct { + uint16_t offset; + uint16_t size; +} _samples[MAX_SOUNDS]; + +static const int _rate = SYS_AUDIO_FREQ; +static struct mixerchannel_t _channel; +static ModPlugFile *_mpf; + +static void mix(void *param, uint8_t *buf, int len) { + memset(buf, 0, len); + if (_mpf) { + const int count = ModPlug_Read(_mpf, buf, len); + if (count == 0) { + ModPlug_SeekOrder(_mpf, 0); + } + } + if (_channel.data) { + for (int i = 0; i < len; i += sizeof(int16_t)) { + int pos = _channel.pos >> 16; + if (pos >= _channel.size) { + const int next = _channel.seq ? *_channel.seq++ : -1; + if (next < 0) { + _channel.data = 0; + break; + } + _channel.data = g_res.samples + _samples[next].offset; + _channel.pos = pos = 0; + _channel.size = _samples[next].size; + } + const int sample = *(int16_t *)(buf + i) + ((int8_t)_channel.data[pos]) * 256; + *(int16_t *)(buf + i) = (sample < -32768 ? -32768 : (sample > 32767 ? 32767 : sample)); + _channel.pos += _channel.step; + } + } +} + +void sound_init() { + ModPlug_Settings mp_settings; + memset(&mp_settings, 0, sizeof(mp_settings)); + ModPlug_GetSettings(&mp_settings); + mp_settings.mFlags = MODPLUG_ENABLE_OVERSAMPLING | MODPLUG_ENABLE_NOISE_REDUCTION; + mp_settings.mChannels = 1; + mp_settings.mBits = 16; + mp_settings.mFrequency = _rate; + mp_settings.mResamplingMode = MODPLUG_RESAMPLE_FIR; + mp_settings.mLoopCount = -1; + ModPlug_SetSettings(&mp_settings); + g_sys.start_audio(mix, 0); + uint16_t offset = 0; + for (int i = 0; i < MAX_SOUNDS; ++i) { + const int num = i; + _samples[i].size = sound_offsets[num]; + _samples[i].offset = offset; + offset += _samples[i].size; + } +} + +void sound_fini() { + g_sys.stop_audio(); +} + +void play_sound(int num) { + assert(num < MAX_SOUNDS); + const int sample_offset = _samples[num].offset; + const int sample_size = _samples[num].size; + print_debug(DBG_MIXER, "sample num %d offset 0x%x size %d", num, sample_offset, sample_size); + if (sample_size == 0) { + return; + } + g_sys.lock_audio(); + _channel.data = g_res.samples + sample_offset; + _channel.pos = 0; + _channel.step = (8000 << 16) / _rate; + _channel.size = sample_size; + if (num == 10) { + static const int8_t seq[] = { 10, 10, 10, 6, -1 }; + _channel.seq = seq + 1; + } else { + _channel.seq = 0; + } + g_sys.unlock_audio(); +} + +void play_music(int num) { + g_sys.lock_audio(); + if (_mpf) { + ModPlug_Unload(_mpf); + _mpf = 0; + } + const int size = load_file(_modules[num]); + _mpf = ModPlug_Load(g_res.tmp, size); + if (_mpf) { + print_debug(DBG_MIXER, "Loaded module '%s'", ModPlug_GetName(_mpf)); + } + g_sys.unlock_audio(); +} diff --git a/ja/staticres.c b/ja/staticres.c new file mode 100644 index 0000000..bec990e --- /dev/null +++ b/ja/staticres.c @@ -0,0 +1,670 @@ +#include "intern.h" + +const uint16_t sound_offsets[] = { + 0x04CA,0x185C,0x0F4C,0x05EC,0x0762,0x0898,0x1188,0x0000,0x0CC4,0x1378,0x089C,0x0606,0x0000,0x0000,0x1254,0x2BE4 +}; +const uint16_t sprite_offsets[] = { + 0x2510,0x2420,0x2518,0x2518,0x2420,0x2518,0x1C18,0x1528,0x1528,0x1420,0x2618,0x2620,0x2420,0x2818,0x2318,0x2320, + 0x1F28,0x2418,0x2320,0x2128,0x2418,0x2418,0x2418,0x1820,0x1E20,0x2620,0x2720,0x2718,0x2618,0x2718,0x2720,0x2520, + 0x2A18,0x2020,0x1A20,0x2820,0x2820,0x1E20,0x2520,0x2520,0x2520,0x1C28,0x1010,0x1010,0x1010,0x1010,0x1010,0x0F10, + 0x0808,0x0A08,0x2018,0x2010,0x1F18,0x2010,0x2018,0x1E20,0x1620,0x1720,0x1620,0x1620,0x2218,0x1E18,0x1F18,0x2218, + 0x1E18,0x1E20,0x1E28,0x1F20,0x1C18,0x2220,0x1F18,0x1F18,0x1F18,0x1420,0x1820,0x2018,0x2118,0x2120,0x2020,0x2120, + 0x2118,0x2120,0x2518,0x1C20,0x1B20,0x2420,0x2220,0x1B20,0x2120,0x2120,0x2120,0x1D20,0x0F10,0x1018,0x1010,0x1010, + 0x0A10,0x0610,0x0608,0x0808,0x0910,0x0B10,0x0B10,0x0A08,0x0C10,0x0C10,0x0C10,0x0D10,0x0E10,0x0D10,0x0C10,0x0D10, + 0x0E10,0x0E10,0x0E10,0x0C10,0x2D20,0x2D28,0x3330,0x2F38,0x2A30,0x2328,0x2128,0x2420,0x2320,0x2418,0x2528,0x2720, + 0x2D30,0x2D30,0x2D30,0x2A30,0x2028,0x1D28,0x1F20,0x2020,0x1F18,0x2028,0x2320,0x1E18,0x2218,0x2318,0x2318,0x2220, + 0x2318,0x2318,0x2518,0x2418,0x2220,0x1C18,0x2218,0x2218,0x2218,0x2718,0x2818,0x2820,0x2720,0x2820,0x2818,0x2920, + 0x2920,0x2120,0x2820,0x2820,0x2720,0x1918,0x1A18,0x1A18,0x1918,0x1A18,0x1A18,0x1C18,0x1C18,0x1818,0x1720,0x1A18, + 0x1A18,0x1A18,0x2020,0x2120,0x2120,0x2018,0x2120,0x2120,0x2420,0x2220,0x1D20,0x2120,0x2120,0x2220,0x0F10,0x0C10, + 0x0610,0x0F10,0x0D10,0x1010,0x1010,0x1010,0x3028,0x2030,0x2020,0x0E08,0x0D10,0x2010,0x1010,0x1040,0x1D20,0x1B30, + 0x1A20,0x1920,0x1D20,0x1D20,0x1F28,0x1D20,0x1718,0x1518,0x1518,0x1828,0x1628,0x1628,0x2828,0x0828,0x0A28,0x0A28, + 0x2020,0x0B30,0x1830,0x1F20,0x1F10,0x1828,0x2428,0x2528,0x1F28,0x1010,0x0D10,0x0808,0x1D30,0x1E30,0x2030,0x2030, + 0x1618,0x1718,0x1040,0x0F10,0x0F10,0x0F10,0x0F10,0x0F10,0x0F10,0x0F10,0x0F10,0x0F08,0x0F10,0x0F10,0x0F10,0x0F10, + 0x0F10,0x0F10,0x0F10,0x0F10,0x0F10,0x0F10,0x0F10,0x0F10,0x0F10,0x0F10,0x0F10,0x0F10,0x0F10,0x0F08,0x0F10,0x0408, + 0x0410,0x0F10,0x0F08,0x0F10,0x0F10,0x0F10,0x0F10,0x0F10,0x0F10,0x0F10,0x0F10,0x0000 +}; +const uint16_t sprite_sizes[] = { + 0x2508,0x2410,0x250C,0x250C,0x2410,0x250C,0x1C0C,0x1514,0x1514,0x1410,0x260C,0x2610,0x2410,0x280C,0x270D,0x2310, + 0x1F14,0x240E,0x2316,0x2114,0x240C,0x240C,0x240C,0x1810,0x1E10,0x2610,0x2710,0x270C,0x260C,0x270C,0x2710,0x2510, + 0x2A0C,0x2010,0x1A10,0x2810,0x2810,0x1E10,0x2510,0x2510,0x2510,0x1C14,0x1008,0x1008,0x1008,0x1008,0x1008,0x0F08, + 0x0804,0x0A04,0x200C,0x2008,0x1F0C,0x2008,0x200C,0x1E10,0x1610,0x1710,0x1610,0x1610,0x220C,0x1E0C,0x1F0C,0x220C, + 0x1E0C,0x1E10,0x1E14,0x1F10,0x1C0C,0x2210,0x1F0C,0x1F0C,0x1F0C,0x1410,0x1810,0x200C,0x210C,0x2110,0x2010,0x2110, + 0x210C,0x2110,0x250C,0x1C10,0x1B10,0x2410,0x2210,0x1B10,0x2110,0x2110,0x2110,0x1D10,0x0F08,0x100C,0x1008,0x1008, + 0x0A08,0x0608,0x0604,0x0804,0x0908,0x0B08,0x0B08,0x0A04,0x0C08,0x0C08,0x0C08,0x0D08,0x0E08,0x0D08,0x0C08,0x0D08, + 0x0E08,0x0E08,0x0E08,0x0C08,0x2D10,0x2D14,0x3118,0x2F1C,0x2C18,0x2A14,0x2A14,0x2A10,0x2910,0x290C,0x2913,0x2B10, + 0x2E18,0x2C18,0x3019,0x2B17,0x2714,0x2714,0x2710,0x2710,0x270C,0x2714,0x2310,0x1E0C,0x220C,0x230C,0x230C,0x2210, + 0x230C,0x230C,0x250C,0x240C,0x2210,0x1C0C,0x220C,0x220C,0x220C,0x270C,0x280C,0x2810,0x2710,0x2810,0x280C,0x2910, + 0x2910,0x2110,0x2810,0x2810,0x2710,0x190C,0x1A0C,0x1A0C,0x190C,0x1A0C,0x1A0C,0x1C0C,0x1C0C,0x180C,0x1710,0x1A0C, + 0x1A0C,0x1A0C,0x2010,0x2110,0x2110,0x200C,0x2110,0x2110,0x2410,0x2210,0x1D10,0x2110,0x2110,0x2210,0x0F08,0x0C08, + 0x0608,0x0F08,0x0D08,0x1008,0x1008,0x1008,0x3014,0x2018,0x2010,0x0E04,0x0D08,0x2008,0x1008,0x1020,0x1D10,0x1B18, + 0x1A10,0x1910,0x1D10,0x1D10,0x1F14,0x1D10,0x170C,0x150C,0x150C,0x1814,0x1614,0x1614,0x2814,0x0814,0x0A14,0x0A14, + 0x2010,0x0B18,0x1818,0x1F10,0x1F08,0x1814,0x120D,0x120E,0x120F,0x0608,0x0507,0x0304,0x1D18,0x1E18,0x2018,0x2018, + 0x160C,0x170C,0x1020,0x0F08,0x0F08,0x0F08,0x0F08,0x0F08,0x0F08,0x0F08,0x0F08,0x0F04,0x0F08,0x0F08,0x0F08,0x0F08, + 0x0F08,0x0F08,0x0F08,0x0F08,0x0F08,0x0F08,0x0F08,0x0F08,0x0F08,0x0F08,0x0F08,0x0F08,0x0F08,0x0F04,0x0F08,0x0404, + 0x0408,0x0F08,0x0F04,0x0F08,0x0F08,0x0F08,0x0F08,0x0F08,0x0F08,0x0F08,0x0F08,0x0000 +}; +const uint8_t sprite_palettes[] = { + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x01, + 0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x03,0x03, + 0x03,0x03,0x03,0x03,0x03,0x03,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04, + 0x05,0x06,0x06,0x06,0x06,0x06,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07, + 0x07,0x07,0x0E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +}; +const uint8_t level_data1p[] = { + 0x00,0x02,0x00,0x05,0x01,0x00,0x01,0x06,0x02,0x00,0x02,0x01,0x00,0x00,0x01,0x07, + 0x02,0x02,0x03,0x01,0x00,0x04,0x00,0x03,0x01,0x01,0x01,0x03,0x02,0x03,0x02,0x04, + 0x03,0x06,0x03,0x05,0x00,0x06,0x00,0x08,0x00,0x07,0x01,0x04,0x01,0x05,0x02,0x05, + 0x02,0x06,0x02,0x07,0x02,0x08,0x03,0x02,0x03,0x03,0x03,0x04,0x01,0x08,0xFF,0xFF +}; +const uint8_t level_data2p[] = { + 0x00,0x02,0x00,0x05,0x01,0x00,0x01,0x06,0x02,0x00,0x02,0x01,0x00,0x00,0x01,0x07, + 0x02,0x02,0x03,0x01,0x00,0x04,0x00,0x03,0x01,0x01,0x01,0x03,0x02,0x03,0x02,0x04, + 0x03,0x06,0x03,0x05,0x00,0x08,0x00,0x07,0x01,0x04,0x01,0x05,0x02,0x05,0x02,0x06, + 0x02,0x08,0x03,0x02,0x03,0x03,0x03,0x04,0x01,0x08,0xFF,0xFF +}; +const uint8_t level_data3[] = { + 0x09,0x09,0x09,0x07,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00 +}; +const uint8_t level_data4[] = { + 0x2D,0x2D,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x30,0x2D,0x2D,0x2D,0x2D, + 0x41,0x2D,0x45,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x41,0x2D, + 0x44,0x46,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x43,0x2D, + 0x42,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D, + 0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x37,0x38,0x39,0x2D,0x34,0x35,0x36,0x2D,0x31, + 0x32,0x33,0x30,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D, + 0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D, + 0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D, + 0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x2D,0x00,0x00,0x00,0x01,0x01,0x01,0x01,0x01, + 0x01,0x01,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02, + 0x02,0x02,0x02,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03, + 0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x04,0x04,0x04,0x04,0x04, + 0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x05, + 0x05,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x07,0x07,0x07,0x07,0x07,0x07,0x08,0x08, + 0x08,0x09,0x09,0x09,0x0A,0x0A,0x0B,0x0B,0x0C,0x0D,0x0E,0x10,0x10,0x10,0x10,0x10, + 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10, + 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10, + 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10, + 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x01,0xFF,0x01,0xFF, + 0x01,0xFF,0x01,0xFF,0x02,0xFF,0x02,0xFF,0x03,0xFF,0x04,0xFF,0x05,0xFF,0x07,0xFF, + 0x08,0xFF,0x0A,0xFF,0x0C,0xFF,0x0D,0xFF,0x0F,0xFF,0x12,0xFF,0x14,0xFF,0x16,0xFF, + 0x19,0xFF,0x1C,0xFF,0x1F,0xFF,0x22,0xFF,0x25,0xFF,0x28,0xFF,0x2C,0xFF,0x2F,0xFF, + 0x33,0xFF,0x37,0xFF,0x3B,0xFF,0x3F,0xFF,0x43,0xFF,0x47,0xFF,0x4B,0xFF,0x50,0xFF, + 0x55,0xFF,0x59,0xFF,0x5E,0xFF,0x63,0xFF,0x68,0xFF,0x6D,0xFF,0x72,0xFF,0x78,0xFF, + 0x7D,0xFF,0x82,0xFF,0x88,0xFF,0x8D,0xFF,0x93,0xFF,0x99,0xFF,0x9F,0xFF,0xA4,0xFF, + 0xAA,0xFF,0xB0,0xFF,0xB6,0xFF,0xBC,0xFF,0xC2,0xFF,0xC8,0xFF,0xCF,0xFF,0xD5,0xFF, + 0xDB,0xFF,0xE1,0xFF,0xE7,0xFF,0xEE,0xFF,0xF4,0xFF,0xFA,0xFF,0x00,0x00,0x00,0x00, + 0x06,0x00,0x0C,0x00,0x12,0x00,0x19,0x00,0x1F,0x00,0x25,0x00,0x2B,0x00,0x31,0x00, + 0x38,0x00,0x3E,0x00,0x44,0x00,0x4A,0x00,0x50,0x00,0x56,0x00,0x5C,0x00,0x61,0x00, + 0x67,0x00,0x6D,0x00,0x73,0x00,0x78,0x00,0x7E,0x00,0x83,0x00,0x88,0x00,0x8E,0x00, + 0x93,0x00,0x98,0x00,0x9D,0x00,0xA2,0x00,0xA7,0x00,0xAB,0x00,0xB0,0x00,0xB5,0x00, + 0xB9,0x00,0xBD,0x00,0xC1,0x00,0xC5,0x00,0xC9,0x00,0xCD,0x00,0xD1,0x00,0xD4,0x00, + 0xD8,0x00,0xDB,0x00,0xDE,0x00,0xE1,0x00,0xE4,0x00,0xE7,0x00,0xEA,0x00,0xEC,0x00, + 0xEE,0x00,0xF1,0x00,0xF3,0x00,0xF4,0x00,0xF6,0x00,0xF8,0x00,0xF9,0x00,0xFB,0x00, + 0xFC,0x00,0xFD,0x00,0xFE,0x00,0xFE,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00, + 0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFE,0x00,0xFE,0x00,0xFD,0x00,0xFC,0x00,0xFB,0x00, + 0xF9,0x00,0xF8,0x00,0xF6,0x00,0xF4,0x00,0xF3,0x00,0xF1,0x00,0xEE,0x00,0xEC,0x00, + 0xEA,0x00,0xE7,0x00,0xE4,0x00,0xE1,0x00,0xDE,0x00,0xDB,0x00,0xD8,0x00,0xD4,0x00, + 0xD1,0x00,0xCD,0x00,0xC9,0x00,0xC5,0x00,0xC1,0x00,0xBD,0x00,0xB9,0x00,0xB5,0x00, + 0xB0,0x00,0xAB,0x00,0xA7,0x00,0xA2,0x00,0x9D,0x00,0x98,0x00,0x93,0x00,0x8E,0x00, + 0x88,0x00,0x83,0x00,0x7E,0x00,0x78,0x00,0x73,0x00,0x6D,0x00,0x67,0x00,0x61,0x00, + 0x5C,0x00,0x56,0x00,0x50,0x00,0x4A,0x00,0x44,0x00,0x3E,0x00,0x38,0x00,0x31,0x00, + 0x2B,0x00,0x25,0x00,0x1F,0x00,0x19,0x00,0x12,0x00,0x0C,0x00,0x06,0x00,0x00,0x00, + 0xFA,0xFF,0xF4,0xFF,0xEE,0xFF,0xE7,0xFF,0xE1,0xFF,0xDB,0xFF,0xD5,0xFF,0xCF,0xFF, + 0xC8,0xFF,0xC2,0xFF,0xBC,0xFF,0xB6,0xFF,0xB0,0xFF,0xAA,0xFF,0xA4,0xFF,0x9F,0xFF, + 0x99,0xFF,0x93,0xFF,0x8D,0xFF,0x88,0xFF,0x82,0xFF,0x7D,0xFF,0x78,0xFF,0x72,0xFF, + 0x6D,0xFF,0x68,0xFF,0x63,0xFF,0x5E,0xFF,0x59,0xFF,0x55,0xFF,0x50,0xFF,0x4B,0xFF, + 0x47,0xFF,0x43,0xFF,0x3F,0xFF,0x3B,0xFF,0x37,0xFF,0x33,0xFF,0x2F,0xFF,0x2C,0xFF, + 0x28,0xFF,0x25,0xFF,0x22,0xFF,0x1F,0xFF,0x1C,0xFF,0x19,0xFF,0x16,0xFF,0x14,0xFF, + 0x12,0xFF,0x0F,0xFF,0x0D,0xFF,0x0C,0xFF,0x0A,0xFF,0x08,0xFF,0x07,0xFF,0x05,0xFF, + 0x04,0xFF,0x03,0xFF,0x02,0xFF,0x02,0xFF,0x01,0xFF,0x01,0xFF,0x01,0xFF,0x01,0xFF, + 0x01,0xFF,0x01,0xFF,0x01,0xFF,0x02,0xFF,0x02,0xFF,0x03,0xFF,0x04,0xFF,0x05,0xFF, + 0x07,0xFF,0x08,0xFF,0x0A,0xFF,0x0C,0xFF,0x0D,0xFF,0x0F,0xFF,0x12,0xFF,0x14,0xFF, + 0x16,0xFF,0x19,0xFF,0x1C,0xFF,0x1F,0xFF,0x22,0xFF,0x25,0xFF,0x28,0xFF,0x2C,0xFF, + 0x2F,0xFF,0x33,0xFF,0x37,0xFF,0x3B,0xFF,0x3F,0xFF,0x43,0xFF,0x47,0xFF,0x4B,0xFF, + 0x50,0xFF,0x55,0xFF,0x59,0xFF,0x5E,0xFF,0x63,0xFF,0x68,0xFF,0x6D,0xFF,0x72,0xFF, + 0x78,0xFF,0x7D,0xFF,0x82,0xFF,0x88,0xFF,0x8D,0xFF,0x93,0xFF,0x99,0xFF,0x9F,0xFF, + 0xA4,0xFF,0xAA,0xFF,0xB0,0xFF,0xB6,0xFF,0xBC,0xFF,0xC2,0xFF,0xC8,0xFF,0xCF,0xFF, + 0xD5,0xFF,0xDB,0xFF,0xE1,0xFF,0xE7,0xFF,0xEE,0xFF,0xF4,0xFF,0xFA,0xFF,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x02,0x03,0x04, + 0x05,0x00,0x06,0x00,0x07,0x00,0x01,0x01,0x00,0x00,0x00,0x00,0x01,0x01,0x01,0x01, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x01, + 0x0F,0x0E,0x0D,0x0C,0x0B,0x0A,0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x02,0x01,0x00, + 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01, + 0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02, + 0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03, + 0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04, + 0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x05, + 0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06, + 0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07, + 0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08, + 0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x09, + 0x0A,0x0A,0x0A,0x0A,0x0A,0x0A,0x0A,0x0A,0x0A,0x0A,0x0A,0x0A,0x0A,0x0A,0x0A,0x0A, + 0x0B,0x0B,0x0B,0x0B,0x0B,0x0B,0x0B,0x0B,0x0B,0x0B,0x0B,0x0B,0x0B,0x0B,0x0B,0x0B, + 0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C, + 0x0D,0x0D,0x0D,0x0D,0x0D,0x0D,0x0D,0x0D,0x0D,0x0D,0x0D,0x0D,0x0D,0x0D,0x0D,0x0D, + 0x09,0x09,0x08,0x08,0x08,0x07,0x07,0x07,0x06,0x06,0x06,0x05,0x05,0x05,0x04,0x04, + 0x04,0x04,0x05,0x05,0x05,0x06,0x06,0x06,0x07,0x07,0x07,0x08,0x08,0x08,0x09,0x09, + 0x04,0x03,0x03,0x03,0x02,0x02,0x02,0x01,0x01,0x01,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x01,0x02,0x02,0x02,0x03,0x03,0x03,0x04, + 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x0F,0x0F,0x0F,0x0E, + 0x0E,0x0E,0x0D,0x0D,0x0D,0x0C,0x0C,0x0C,0x0B,0x0B,0x0B,0x0A,0x0A,0x0A,0x09,0x09, + 0x09,0x09,0x0A,0x0A,0x0A,0x0B,0x0B,0x0B,0x0C,0x0C,0x0C,0x0D,0x0D,0x0D,0x0E,0x0E, + 0x0E,0x0F,0x0F,0x0F,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10, + 0x00,0x00,0x00,0x00,0x00,0x1D,0x1E,0x00,0x04,0x01,0x01,0x01,0x00,0x00,0x00,0x0B, + 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x19,0x1B,0x05,0x00,0x05,0x00,0x01,0x01, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1F,0x20,0x00,0x07, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x05,0x1C,0x1A,0x05,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00, + 0x00,0x0B,0x0C,0x0D,0x0E,0x0F,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x0B,0x0C,0x0D,0x0E,0x0F,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x00,0x00, + 0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x21,0x22,0x21,0x22,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1B,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x2A,0x2A,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x23,0x00,0x1F,0x20,0x00,0x00,0x1D,0x1E,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1C,0x1A, + 0x19,0x1B,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x06,0x06,0x06,0x06,0x06,0x06, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x02,0x02,0x02, + 0x02,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x02,0x00,0x00,0x00,0x00, + 0x02,0x02,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x02, + 0x00,0x02,0x02,0x00,0x02,0x02,0x02,0x00,0x00,0x00,0x01,0x01,0x01,0x00,0x00,0x00, + 0x00,0x02,0x02,0x00,0x00,0x02,0x02,0x00,0x02,0x02,0x02,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x02,0x02,0x02,0x02,0x00,0x00,0x24,0x24,0x24,0x25,0x25,0x25,0x02,0x00,0x00, + 0x00,0x00,0x00,0x00,0x0A,0x0A,0x0A,0x02,0x02,0x00,0x00,0x21,0x22,0x21,0x22,0x0A, + 0x0A,0x0A,0x00,0x00,0x00,0x00,0x00,0x00,0x09,0x09,0x09,0x09,0x09,0x02,0x02,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x00,0x00,0x00,0x00, + 0x01,0x01,0x08,0x01,0x01,0x26,0x26,0x26,0x27,0x27,0x27,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x00,0x00,0x09,0x09,0x09,0x09,0x09,0x09,0x09, + 0x09,0x09,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x03,0x06,0x06,0x06,0x06,0x06,0x06, + 0x00,0x02,0x02,0x02,0x02,0x02,0x02,0x01,0x08,0x01,0x02,0x02,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x02,0x02,0x02,0x02,0x23,0x23,0x00,0x08,0x00,0x01,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x02, + 0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x02,0x02,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x02, + 0x00,0x00,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x23,0x23,0x02,0x08,0x02,0x02,0x00, + 0x00,0x00,0x02,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x23,0x23,0x08, + 0x08,0x02,0x02,0x00,0x00,0x02,0x00,0x00,0x23,0x23,0x23,0x23,0x00,0x00,0x00,0x00, + 0x24,0x24,0x24,0x25,0x25,0x25,0x02,0x26,0x26,0x26,0x27,0x27,0x27,0x28,0x28,0x28, + 0x00,0x00,0x08,0x00,0x02,0x02,0x00,0x23,0x00,0x00,0x00,0x21,0x22,0x21,0x22,0x08, + 0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x06,0x06,0x06,0x06,0x06, + 0x00,0x01,0x01,0x00,0x00,0x01,0x01,0x01,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x21, + 0x22,0x21,0x22,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x02,0x02,0x02,0x02,0x02,0x02, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x02,0x02,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x02,0x02,0x00,0x00,0x00,0x00,0x02,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x02,0x02,0x02,0x02,0x02,0x00,0x00,0x00,0x00,0x00,0x02,0x02,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x02,0x02,0x02,0x02,0x02,0x23,0x23,0x23,0x23,0x23,0x23, + 0x23,0x23,0x23,0x23,0x02,0x02,0x02,0x02,0x23,0x00,0x00,0x00,0x00,0x02,0x02,0x02, + 0x02,0x02,0x02,0x02,0x23,0x02,0x02,0x02,0x02,0x02,0x23,0x02,0x23,0x00,0x00,0x00, + 0x02,0x02,0x23,0x02,0x02,0x02,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x02,0x02,0x02,0x02,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x06,0x06,0x06,0x06,0x06, + 0xAA,0x5D,0x83,0x5E,0x83,0x5E,0xAA,0x5D,0x73,0x5F,0xAA,0x5D,0xAA,0x5D,0x78,0x5F, + 0xAA,0x5D,0x83,0x5E,0x83,0x5E,0x7B,0x5F,0x7B,0x5F,0x7B,0x5F,0x7B,0x5F,0x7B,0x5F, + 0x7B,0x5F,0x7B,0x5F,0x7B,0x5F,0x7B,0x5F,0x7B,0x5F,0x7B,0x5F,0x7B,0x5F,0x7B,0x5F, + 0x7B,0x5F,0x7B,0x5F,0x7B,0x5F,0x7B,0x5F,0x7B,0x5F,0x7B,0x5F,0x7B,0x5F,0x7B,0x5F, + 0x7B,0x5F,0x0E,0x5F,0x0E,0x5F,0x83,0x5E,0xAA,0x5D,0xAA,0x5D,0xAA,0x5D,0xAA,0x5D, + 0xAA,0x5D,0xAA,0x5D,0x0E,0x5F,0xAA,0x5D,0x83,0x5E,0x83,0x5E,0xAA,0x5D,0x73,0x5F, + 0xAA,0x5D,0xEC,0x5E,0x78,0x5F,0x3E,0x5D,0x83,0x5E,0x83,0x5E,0x7B,0x5F,0x7B,0x5F, + 0x7B,0x5F,0x7B,0x5F,0x7B,0x5F,0x7B,0x5F,0x7B,0x5F,0x7B,0x5F,0x7B,0x5F,0x7B,0x5F, + 0x7B,0x5F,0x7B,0x5F,0x7B,0x5F,0x7B,0x5F,0x7B,0x5F,0x7B,0x5F,0x7B,0x5F,0x7B,0x5F, + 0x7B,0x5F,0x7B,0x5F,0x7B,0x5F,0x7B,0x5F,0x16,0x5F,0x16,0x5F,0xA7,0x5F,0x94,0x5F, + 0x94,0x5F,0x94,0x5F,0x94,0x5F,0x94,0x5F,0x94,0x5F,0x0E,0x5F,0xC8,0x5D,0xC8,0x5D, + 0xC9,0x5D,0xC8,0x5D,0xC8,0x5D,0xC8,0x5D,0xC8,0x5D,0xC8,0x5D,0xC8,0x5D,0xC9,0x5D, + 0xC9,0x5D,0xC8,0x5D,0xC8,0x5D,0xC8,0x5D,0xC8,0x5D,0xC8,0x5D,0xC8,0x5D,0xC8,0x5D, + 0xC8,0x5D,0xC8,0x5D,0xC8,0x5D,0xC8,0x5D,0xC8,0x5D,0xC8,0x5D,0xC8,0x5D,0xC8,0x5D, + 0xC8,0x5D,0xC8,0x5D,0xC8,0x5D,0xC8,0x5D,0xC8,0x5D,0xC8,0x5D,0xC8,0x5D,0xC8,0x5D, + 0xC8,0x5D,0x9E,0x5F,0x94,0x5F,0x94,0x5F,0x94,0x5F,0x94,0x5F,0x94,0x5F,0x94,0x5F, + 0xC8,0x5D,0xC8,0x5D,0xC8,0x5D,0x58,0x5E,0xC8,0x5D,0xC8,0x5D,0xC8,0x5D,0xC8,0x5D, + 0xC8,0x5D,0xC8,0x5D,0x58,0x5E,0x58,0x5E,0x7B,0x5F,0x7B,0x5F,0x7B,0x5F,0x7B,0x5F, + 0x7B,0x5F,0x7B,0x5F,0x7B,0x5F,0x7B,0x5F,0x7B,0x5F,0x7B,0x5F,0x7B,0x5F,0x7B,0x5F, + 0x7B,0x5F,0x7B,0x5F,0x7B,0x5F,0x7B,0x5F,0x7B,0x5F,0x7B,0x5F,0x7B,0x5F,0x7B,0x5F, + 0x7B,0x5F,0x7B,0x5F,0xC8,0x5D,0xC8,0x5D,0x9E,0x5F,0x94,0x5F,0x94,0x5F,0x94,0x5F, + 0x94,0x5F,0x94,0x5F,0x94,0x5F,0xC8,0x5D +}; +const uint8_t monster_spr_anim_data0[] = { + 0xE6,0x00,0xFF,0xFF +}; +const uint8_t monster_spr_anim_data1[] = { + 0xD6,0x00,0xD7,0x00,0xFE,0xFF,0x00,0x7D,0xD8,0x00,0xFF,0xFF +}; +const uint8_t monster_spr_anim_data2[] = { + 0xD9,0x00,0xD9,0x00,0xD9,0x00,0xDB,0x00,0xDB,0x00,0xDB,0x00,0xDA,0x00,0xDA,0x00, + 0xDA,0x00,0xF7,0xFF,0x00,0x7D,0xDC,0x00,0xFF,0xFF +}; +const uint8_t monster_spr_anim_data3[] = { + 0xE1,0x00,0xFF,0xFF +}; +const uint8_t monster_spr_anim_data4[] = { + 0xE1,0x00,0xE1,0x00,0xE2,0x00,0xE3,0x00,0xE3,0x00,0xE2,0x00,0xE1,0x00,0xE1,0x00, + 0xF8,0xFF,0xE4,0x00,0xFF,0xFF,0x00,0x7D,0xE5,0x00,0xFF,0xFF +}; +const uint8_t monster_spr_anim_data6[] = { + 0xEC,0x00,0xEC,0x00,0xED,0x00,0xED,0x00,0xFC,0xFF,0xEE,0x00,0xFF,0xFF,0x00,0x7D, + 0xEF,0x00,0xFF,0xFF +}; +const uint8_t monster_spr_anim_data8[] = { + 0xCE,0x00,0xCE,0x00,0xCE,0x00,0xCF,0x00,0xCF,0x00,0xCF,0x00,0xD0,0x00,0xD0,0x00, + 0xD0,0x00,0xD1,0x00,0xD1,0x00,0xD1,0x00,0xF4,0xFF,0xCE,0x00,0xCE,0x00,0xCF,0x00, + 0xCF,0x00,0xD0,0x00,0xD0,0x00,0xD1,0x00,0xD1,0x00,0xF8,0xFF,0x00,0x7D,0xD4,0x00, + 0xFF,0xFF +}; +const uint8_t monster_spr_anim_data9[] = { + 0xC6,0x00,0xFF,0xFF +}; +const uint8_t common_palette_data[] = { + 0x00,0x00,0x00,0x0C,0x14,0x10,0x04,0x0C,0x08,0x3C,0x3C,0x3C,0x1C,0x20,0x28,0x10, + 0x14,0x20,0x0C,0x10,0x1C,0x08,0x0C,0x18,0x04,0x08,0x14,0x04,0x04,0x04,0x3C,0x38, + 0x30,0x3C,0x30,0x24,0x30,0x24,0x1C,0x24,0x14,0x0C,0x20,0x28,0x24,0x10,0x18,0x14, + 0x3F,0x3F,0x3F,0x08,0x08,0x18,0x18,0x10,0x20,0x18,0x18,0x30,0x20,0x20,0x38,0x38, + 0x38,0x38,0x3F,0x3E,0x00,0x2F,0x2E,0x00,0x30,0x18,0x10,0x20,0x10,0x00,0x18,0x00, + 0x00,0x1C,0x00,0x00,0x27,0x00,0x00,0x33,0x00,0x00,0x3F,0x10,0x10,0x00,0x00,0x00, + 0x04,0x28,0x00,0x20,0x1C,0x20,0x1C,0x14,0x1C,0x18,0x0C,0x18,0x10,0x08,0x10,0x0C, + 0x04,0x0C,0x08,0x00,0x08,0x04,0x00,0x04,0x24,0x10,0x04,0x1C,0x0C,0x04,0x18,0x08, + 0x04,0x14,0x04,0x00,0x0C,0x00,0x00,0x08,0x00,0x00,0x00,0x04,0x08,0x00,0x00,0x00, + 0x06,0x07,0x08,0x3C,0x3C,0x3C,0x24,0x2C,0x28,0x2C,0x14,0x10,0x24,0x10,0x0C,0x1C, + 0x0C,0x08,0x14,0x08,0x08,0x0C,0x04,0x04,0x04,0x00,0x00,0x0C,0x14,0x10,0x3C,0x38, + 0x30,0x3C,0x30,0x24,0x30,0x24,0x1C,0x24,0x14,0x0C,0x20,0x28,0x24,0x10,0x18,0x14, + 0x14,0x10,0x0C,0x3C,0x3C,0x3C,0x1C,0x20,0x28,0x10,0x14,0x20,0x0C,0x10,0x1C,0x08, + 0x0C,0x18,0x04,0x08,0x14,0x04,0x04,0x10,0x18,0x10,0x18,0x18,0x20,0x1C,0x20,0x18, + 0x20,0x0C,0x14,0x10,0x08,0x10,0x0C,0x2C,0x28,0x2C,0x04,0x0C,0x08,0x00,0x08,0x04, + 0x14,0x00,0x14,0x10,0x3C,0x3C,0x10,0x34,0x34,0x0C,0x30,0x30,0x0C,0x28,0x28,0x08, + 0x24,0x24,0x08,0x1C,0x1C,0x08,0x18,0x18,0x04,0x10,0x10,0x04,0x0C,0x0C,0x00,0x04, + 0x04,0x00,0x00,0x00,0x24,0x24,0x24,0x14,0x14,0x14,0x0C,0x0C,0x0C,0x30,0x34,0x34, + 0x14,0x14,0x14,0x24,0x1C,0x1C,0x20,0x18,0x18,0x1C,0x14,0x14,0x18,0x10,0x10,0x14, + 0x0C,0x0C,0x10,0x08,0x08,0x08,0x04,0x04,0x18,0x18,0x14,0x04,0x04,0x04,0x10,0x10, + 0x0C,0x00,0x30,0x00,0x00,0x24,0x00,0x00,0x14,0x00,0x3C,0x3C,0x3C,0x2C,0x1C,0x28, + 0x14,0x1C,0x0C,0x3C,0x3C,0x3C,0x28,0x1C,0x1C,0x20,0x14,0x14,0x1C,0x0C,0x0C,0x18, + 0x08,0x08,0x10,0x04,0x04,0x0C,0x0C,0x04,0x14,0x14,0x10,0x18,0x18,0x1C,0x18,0x18, + 0x14,0x14,0x14,0x18,0x10,0x10,0x14,0x10,0x10,0x08,0x08,0x0C,0x10,0x00,0x08,0x04 +}; +const uint8_t levels_palette_data[] = { + 0x00,0x34,0x38,0x20,0x28,0x10,0x1C,0x28,0x00,0x18,0x20,0x0C,0x10,0x14,0x04,0x08, + 0x0C,0x04,0x14,0x18,0x10,0x14,0x14,0x08,0x00,0x08,0x00,0x04,0x0C,0x14,0x08,0x10, + 0x18,0x0C,0x14,0x24,0x10,0x1C,0x28,0x14,0x2C,0x34,0x0C,0x20,0x00,0x10,0x24,0x00, + 0x00,0x34,0x38,0x30,0x34,0x2C,0x38,0x20,0x10,0x38,0x24,0x20,0x0C,0x20,0x00,0x10, + 0x24,0x00,0x14,0x2C,0x00,0x18,0x34,0x00,0x3C,0x30,0x14,0x38,0x10,0x10,0x2D,0x00, + 0x2D,0x21,0x00,0x21,0x16,0x00,0x16,0x00,0x10,0x38,0x28,0x3C,0x10,0x28,0x3C,0x10, + 0x00,0x34,0x38,0x00,0x28,0x38,0x38,0x2C,0x20,0x34,0x24,0x18,0x2C,0x20,0x18,0x20, + 0x18,0x08,0x1C,0x14,0x08,0x00,0x00,0x00,0x00,0x0C,0x00,0x00,0x14,0x00,0x00,0x18, + 0x00,0x00,0x20,0x00,0x1C,0x2C,0x00,0x3C,0x38,0x20,0x28,0x38,0x10,0x20,0x2C,0x00, + 0x00,0x34,0x38,0x38,0x3C,0x38,0x00,0x14,0x00,0x00,0x18,0x00,0x00,0x20,0x00,0x08, + 0x2C,0x00,0x00,0x00,0x00,0x3C,0x38,0x20,0x18,0x0C,0x08,0x20,0x10,0x08,0x20,0x14, + 0x08,0x28,0x18,0x10,0x34,0x20,0x14,0x3C,0x28,0x0C,0x3C,0x2C,0x08,0x00,0x34,0x38, + 0x00,0x34,0x38,0x28,0x2C,0x30,0x28,0x00,0x04,0x1C,0x00,0x04,0x00,0x18,0x04,0x00, + 0x2C,0x04,0x34,0x38,0x3C,0x10,0x28,0x34,0x28,0x30,0x34,0x34,0x00,0x04,0x20,0x20, + 0x24,0x1C,0x1C,0x20,0x14,0x14,0x18,0x10,0x10,0x14,0x0C,0x0C,0x10,0x3C,0x3C,0x3C, + 0x00,0x24,0x26,0x14,0x10,0x0C,0x10,0x0C,0x08,0x0C,0x08,0x04,0x08,0x04,0x00,0x00, + 0x00,0x00,0x22,0x07,0x00,0x2B,0x17,0x04,0x35,0x29,0x0A,0x3F,0x3F,0x12,0x00,0x00, + 0x06,0x03,0x04,0x11,0x0B,0x0C,0x1D,0x18,0x18,0x28,0x29,0x29,0x33,0x3F,0x3F,0x3F, + 0x00,0x24,0x26,0x2C,0x1C,0x14,0x2A,0x18,0x0C,0x26,0x16,0x10,0x20,0x12,0x06,0x1B, + 0x10,0x03,0x16,0x0C,0x01,0x12,0x08,0x00,0x04,0x00,0x00,0x3C,0x3C,0x3C,0x1E,0x22, + 0x26,0x17,0x1C,0x20,0x11,0x16,0x1B,0x0C,0x10,0x15,0x08,0x0C,0x10,0x04,0x08,0x0C, + 0x38,0x2E,0x3F,0x3A,0x3C,0x3E,0x35,0x38,0x3E,0x31,0x35,0x3D,0x2E,0x30,0x3F,0x28, + 0x2E,0x3C,0x24,0x2B,0x3C,0x20,0x28,0x3B,0x1C,0x25,0x3B,0x17,0x22,0x3B,0x13,0x1F, + 0x3A,0x10,0x1C,0x3A,0x0C,0x19,0x39,0x38,0x32,0x3F,0x32,0x2C,0x3F,0x32,0x2E,0x3F, + 0x3F,0x38,0x34,0x3B,0x38,0x31,0x3B,0x37,0x30,0x3B,0x36,0x2F,0x3F,0x35,0x2E,0x3F, + 0x33,0x2C,0x3E,0x31,0x2A,0x3D,0x2F,0x29,0x3D,0x2D,0x28,0x3C,0x2C,0x27,0x3C,0x2A, + 0x25,0x3B,0x28,0x24,0x3B,0x27,0x23,0x3F,0x37,0x39,0x3F,0x33,0x2E,0x3F,0x35,0x2E, + 0x00,0x34,0x38,0x20,0x28,0x10,0x1C,0x28,0x00,0x18,0x20,0x0C,0x10,0x14,0x04,0x08, + 0x0C,0x04,0x14,0x18,0x10,0x14,0x14,0x08,0x00,0x08,0x00,0x04,0x0C,0x14,0x08,0x10, + 0x18,0x0C,0x14,0x24,0x10,0x1C,0x28,0x14,0x2C,0x34,0x0C,0x20,0x00,0x10,0x24,0x00, + 0x1C,0x0C,0x24,0x38,0x3C,0x28,0x1C,0x20,0x00,0x08,0x10,0x00,0x3C,0x3C,0x3C,0x0C, + 0x04,0x0C,0x08,0x00,0x08,0x04,0x00,0x04,0x28,0x22,0x1E,0x1C,0x18,0x14,0x14,0x10, + 0x0C,0x10,0x0C,0x08,0x0C,0x08,0x04,0x00,0x00,0x00,0x00,0x04,0x08,0x00,0x00,0x00, + 0x1C,0x0C,0x24,0x3E,0x36,0x30,0x3E,0x20,0x00,0x32,0x00,0x00,0x3C,0x3C,0x3C,0x0C, + 0x04,0x0C,0x08,0x04,0x00,0x00,0x00,0x00,0x28,0x22,0x1E,0x1C,0x18,0x14,0x14,0x10, + 0x0C,0x10,0x0C,0x08,0x0C,0x08,0x04,0x00,0x00,0x00,0x00,0x04,0x08,0x00,0x00,0x00, + 0x00,0x20,0x00,0x10,0x1C,0x20,0x0C,0x18,0x1C,0x08,0x14,0x18,0x04,0x10,0x14,0x04, + 0x0C,0x10,0x00,0x08,0x0C,0x00,0x04,0x08,0x30,0x28,0x0C,0x28,0x1E,0x08,0x22,0x16, + 0x04,0x1C,0x0E,0x02,0x14,0x08,0x00,0x0E,0x06,0x00,0x08,0x04,0x00,0x00,0x00,0x00, + 0x24,0x34,0x38,0x24,0x10,0x04,0x1C,0x0C,0x04,0x18,0x08,0x04,0x14,0x04,0x00,0x0C, + 0x00,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x14,0x18,0x14,0x10,0x14, + 0x10,0x0C,0x10,0x0C,0x08,0x0C,0x08,0x04,0x08,0x04,0x00,0x04,0x00,0x00,0x00,0x00, + 0x00,0x24,0x26,0x14,0x10,0x0C,0x10,0x0C,0x08,0x0C,0x08,0x04,0x08,0x04,0x00,0x00, + 0x00,0x00,0x22,0x06,0x00,0x2A,0x16,0x04,0x34,0x28,0x0A,0x3E,0x3E,0x12,0x00,0x00, + 0x06,0x02,0x04,0x10,0x0A,0x0C,0x1C,0x18,0x18,0x28,0x28,0x28,0x32,0x3E,0x3E,0x3E, + 0x00,0x24,0x26,0x30,0x28,0x0C,0x28,0x1E,0x08,0x22,0x16,0x04,0x1C,0x0E,0x02,0x14, + 0x08,0x00,0x0E,0x06,0x00,0x08,0x04,0x00,0x0E,0x18,0x18,0x0C,0x14,0x14,0x0A,0x10, + 0x10,0x08,0x0E,0x0C,0x06,0x0A,0x0A,0x04,0x06,0x06,0x02,0x02,0x02,0x00,0x00,0x00, + 0x00,0x00,0x00,0x0A,0x07,0x0A,0x0D,0x0A,0x0E,0x10,0x0E,0x12,0x14,0x12,0x16,0x17, + 0x1A,0x1D,0x1B,0x1C,0x1F,0x21,0x1B,0x16,0x1B,0x15,0x11,0x17,0x0F,0x0D,0x12,0x0A, + 0x09,0x0D,0x08,0x08,0x3A,0x00,0x34,0x3A,0x00,0x34,0x3A,0x00,0x34,0x3A,0x00,0x34, + 0x00,0x00,0x00,0x06,0x0C,0x10,0x09,0x10,0x17,0x0E,0x13,0x1D,0x10,0x14,0x1F,0x16, + 0x19,0x25,0x12,0x15,0x21,0x24,0x18,0x20,0x1D,0x13,0x1A,0x16,0x0D,0x15,0x0C,0x0C, + 0x11,0x0A,0x0A,0x0E,0x3A,0x00,0x34,0x3A,0x00,0x34,0x3A,0x00,0x34,0x3A,0x00,0x34, + 0x00,0x24,0x26,0x14,0x10,0x0C,0x10,0x0C,0x08,0x0C,0x08,0x04,0x08,0x04,0x00,0x00, + 0x00,0x00,0x22,0x07,0x00,0x2B,0x17,0x04,0x35,0x29,0x0A,0x3F,0x3F,0x12,0x00,0x00, + 0x06,0x03,0x04,0x11,0x0B,0x0C,0x1D,0x18,0x18,0x28,0x29,0x29,0x33,0x3F,0x3F,0x3F, + 0x1C,0x0C,0x24,0x38,0x3C,0x28,0x1C,0x20,0x00,0x08,0x10,0x00,0x3C,0x3C,0x3C,0x0C, + 0x04,0x0C,0x08,0x00,0x08,0x04,0x00,0x04,0x28,0x22,0x1E,0x1C,0x18,0x14,0x14,0x10, + 0x0C,0x10,0x0C,0x08,0x0C,0x08,0x04,0x00,0x00,0x00,0x00,0x04,0x08,0x00,0x00,0x00, + 0x0C,0x2C,0x04,0x28,0x22,0x1E,0x20,0x28,0x1C,0x20,0x24,0x1C,0x18,0x20,0x14,0x14, + 0x18,0x0C,0x00,0x18,0x10,0x00,0x00,0x00,0x20,0x1C,0x18,0x1C,0x18,0x14,0x18,0x14, + 0x10,0x14,0x10,0x0C,0x10,0x0C,0x08,0x0C,0x08,0x04,0x00,0x00,0x00,0x38,0x38,0x3C, + 0x00,0x1C,0x00,0x16,0x14,0x00,0x12,0x11,0x00,0x0F,0x0E,0x00,0x0B,0x0A,0x00,0x07, + 0x07,0x00,0x04,0x03,0x00,0x00,0x00,0x00,0x0A,0x08,0x03,0x11,0x0E,0x06,0x19,0x13, + 0x09,0x18,0x14,0x0A,0x1E,0x1A,0x10,0x1A,0x1E,0x08,0x24,0x20,0x12,0x2E,0x26,0x1E, + 0x24,0x34,0x38,0x20,0x10,0x04,0x18,0x0C,0x04,0x14,0x08,0x04,0x10,0x04,0x00,0x08, + 0x04,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x1C,0x20,0x0C,0x18, + 0x1C,0x08,0x14,0x18,0x04,0x10,0x14,0x04,0x0C,0x10,0x00,0x08,0x0C,0x00,0x04,0x08, + 0x24,0x34,0x38,0x10,0x3C,0x3C,0x10,0x34,0x34,0x0C,0x30,0x30,0x0C,0x28,0x28,0x08, + 0x24,0x24,0x08,0x1C,0x1C,0x08,0x18,0x18,0x04,0x10,0x10,0x04,0x0C,0x0C,0x00,0x04, + 0x04,0x00,0x00,0x00,0x24,0x24,0x24,0x14,0x14,0x14,0x0C,0x0C,0x0C,0x30,0x34,0x34, + 0x3F,0x3F,0x3F,0x3A,0x3D,0x3E,0x36,0x3C,0x3E,0x32,0x3A,0x3E,0x2E,0x39,0x3E,0x2A, + 0x37,0x3E,0x26,0x36,0x3F,0x22,0x35,0x3F,0x1E,0x33,0x3F,0x1A,0x32,0x3F,0x16,0x31, + 0x3F,0x12,0x2F,0x3F,0x0E,0x2E,0x3F,0x0A,0x2C,0x3F,0x06,0x2B,0x3F,0x02,0x2A,0x3F, + 0x00,0x00,0x00,0x0A,0x03,0x03,0x0F,0x05,0x03,0x14,0x07,0x03,0x1A,0x0B,0x03,0x1F, + 0x10,0x02,0x23,0x16,0x00,0x0F,0x0B,0x07,0x14,0x11,0x0B,0x1A,0x19,0x10,0x1E,0x1F, + 0x15,0x22,0x25,0x1C,0x01,0x3D,0x00,0x01,0x3D,0x00,0x01,0x3D,0x00,0x01,0x3D,0x00, + 0x00,0x00,0x00,0x03,0x03,0x05,0x07,0x08,0x0D,0x09,0x0A,0x10,0x0C,0x0D,0x14,0x0F, + 0x0F,0x17,0x12,0x12,0x1A,0x0D,0x03,0x00,0x10,0x06,0x00,0x14,0x09,0x00,0x18,0x0D, + 0x00,0x1C,0x12,0x00,0x01,0x3D,0x00,0x01,0x3D,0x00,0x01,0x3D,0x00,0x01,0x3D,0x00, + 0x00,0x18,0x00,0x20,0x1C,0x18,0x1C,0x18,0x14,0x18,0x14,0x10,0x14,0x10,0x0C,0x10, + 0x0C,0x08,0x0C,0x08,0x04,0x08,0x04,0x00,0x00,0x00,0x00,0x18,0x1C,0x18,0x14,0x18, + 0x14,0x10,0x14,0x10,0x0C,0x10,0x0C,0x08,0x0C,0x08,0x04,0x08,0x04,0x00,0x04,0x00, + 0x00,0x00,0x00,0x1C,0x14,0x14,0x18,0x10,0x10,0x14,0x0C,0x0C,0x10,0x08,0x08,0x0C, + 0x04,0x04,0x08,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x1C,0x20,0x1C,0x18,0x1C, + 0x18,0x14,0x18,0x14,0x10,0x14,0x10,0x0C,0x10,0x0C,0x08,0x0C,0x08,0x04,0x08,0x04, + 0x00,0x00,0x00,0x20,0x1C,0x18,0x1C,0x18,0x14,0x18,0x14,0x10,0x14,0x10,0x0C,0x10, + 0x0C,0x08,0x0C,0x08,0x04,0x08,0x04,0x00,0x00,0x00,0x00,0x08,0x08,0x18,0x18,0x18, + 0x28,0x1C,0x20,0x30,0x24,0x28,0x3C,0x00,0x08,0x00,0x00,0x28,0x28,0x38,0x38,0x34, + 0x00,0x00,0x00,0x3D,0x3F,0x2E,0x3A,0x3F,0x27,0x38,0x3F,0x1F,0x34,0x3F,0x17,0x31, + 0x3F,0x10,0x2D,0x3F,0x08,0x28,0x3F,0x00,0x00,0x00,0x00,0x07,0x08,0x17,0x06,0x07, + 0x13,0x04,0x05,0x0F,0x03,0x04,0x0B,0x02,0x02,0x08,0x01,0x01,0x04,0x00,0x00,0x00, + 0x00,0x18,0x00,0x08,0x08,0x18,0x18,0x10,0x20,0x18,0x18,0x30,0x20,0x20,0x38,0x38, + 0x38,0x38,0x28,0x28,0x28,0x23,0x1F,0x1F,0x1E,0x18,0x18,0x1A,0x11,0x11,0x15,0x0C, + 0x0C,0x10,0x07,0x07,0x0C,0x04,0x04,0x20,0x2A,0x20,0x38,0x38,0x18,0x00,0x00,0x00, + 0x00,0x00,0x00,0x14,0x10,0x0C,0x10,0x0C,0x08,0x0C,0x08,0x04,0x08,0x04,0x00,0x00, + 0x00,0x00,0x22,0x07,0x00,0x2B,0x17,0x04,0x35,0x29,0x0A,0x3F,0x3F,0x12,0x00,0x00, + 0x06,0x03,0x04,0x11,0x0B,0x0C,0x1D,0x18,0x18,0x28,0x29,0x29,0x33,0x3F,0x3F,0x3F, + 0x37,0x00,0x00,0x37,0x00,0x00,0x37,0x00,0x00,0x37,0x00,0x00,0x37,0x00,0x00,0x37, + 0x00,0x00,0x37,0x00,0x00,0x37,0x00,0x00,0x37,0x00,0x00,0x37,0x00,0x00,0x37,0x00, + 0x00,0x37,0x00,0x00,0x37,0x00,0x00,0x37,0x00,0x00,0x37,0x00,0x00,0x37,0x00,0x00, + 0x00,0x00,0x00,0x31,0x24,0x10,0x0B,0x0A,0x12,0x2C,0x1D,0x12,0x25,0x12,0x0E,0x1F, + 0x0B,0x0D,0x19,0x08,0x0E,0x12,0x05,0x0D,0x0C,0x03,0x0B,0x37,0x2D,0x1E,0x1A,0x1C, + 0x18,0x07,0x14,0x1D,0x0F,0x12,0x12,0x0B,0x0D,0x11,0x0B,0x08,0x10,0x04,0x1F,0x23, + 0x00,0x00,0x00,0x14,0x23,0x17,0x12,0x0E,0x0D,0x14,0x20,0x17,0x11,0x1C,0x18,0x0F, + 0x19,0x18,0x0D,0x13,0x15,0x0A,0x0D,0x12,0x08,0x08,0x0B,0x1D,0x23,0x17,0x19,0x1E, + 0x11,0x19,0x14,0x13,0x08,0x12,0x04,0x08,0x0D,0x0A,0x04,0x09,0x04,0x22,0x1B,0x1A +}; +const uint8_t tile_lut_data0[] = { + 0x01,0xFA,0x06,0x00 +}; +const uint8_t tile_lut_data1[] = { + 0x05,0xFA,0x06,0x00,0x87,0x03,0x01,0x8A,0x03,0x02,0xD5,0x03,0x01,0xD8,0x03,0x02 +}; +const uint8_t tile_lut_data2[] = { + 0x06,0xFA,0x06,0x00,0x80,0x03,0x01,0x83,0x03,0x02,0x87,0x03,0x02,0x8A,0x03,0x01, + 0x8D,0x03,0x02 +}; +const uint8_t tile_lut_data3[] = { + 0x01,0xFA,0x06,0x00 +}; +const uint16_t tile_funcs0[] = { + 0x5DAA,0x5E83,0x5E83,0x5DAA,0x5F73,0x5DAA,0x5DAA,0x5F78,0x5DAA,0x5E83,0x5E83,0x5F7B,0x5F7B,0x5F7B,0x5F7B,0x5F7B, + 0x5F7B,0x5F7B,0x5F7B,0x5F7B,0x5F7B,0x5F7B,0x5F7B,0x5F7B,0x5F7B,0x5F7B,0x5F7B,0x5F7B,0x5F7B,0x5F7B,0x5F7B,0x5F7B, + 0x5F7B,0x5F0E,0x5F0E,0x5E83,0x5DAA,0x5DAA,0x5DAA,0x5DAA,0x5DAA,0x5DAA,0x5F0E +}; +const uint16_t tile_funcs1[] = { + 0x5DAA,0x5E83,0x5E83,0x5DAA,0x5F73,0x5DAA,0x5EEC,0x5F78,0x5D3E,0x5E83,0x5E83,0x5F7B,0x5F7B,0x5F7B,0x5F7B,0x5F7B, + 0x5F7B,0x5F7B,0x5F7B,0x5F7B,0x5F7B,0x5F7B,0x5F7B,0x5F7B,0x5F7B,0x5F7B,0x5F7B,0x5F7B,0x5F7B,0x5F7B,0x5F7B,0x5F7B, + 0x5F7B,0x5F16,0x5F16,0x5FA7,0x5F94,0x5F94,0x5F94,0x5F94,0x5F94,0x5F94,0x5F0E +}; +const uint16_t tile_funcs2[] = { + 0x5DC8,0x5DC8,0x5DC9,0x5DC8,0x5DC8,0x5DC8,0x5DC8,0x5DC8,0x5DC8,0x5DC9,0x5DC9,0x5DC8,0x5DC8,0x5DC8,0x5DC8,0x5DC8, + 0x5DC8,0x5DC8,0x5DC8,0x5DC8,0x5DC8,0x5DC8,0x5DC8,0x5DC8,0x5DC8,0x5DC8,0x5DC8,0x5DC8,0x5DC8,0x5DC8,0x5DC8,0x5DC8, + 0x5DC8,0x5DC8,0x5DC8,0x5F9E,0x5F94,0x5F94,0x5F94,0x5F94,0x5F94,0x5F94,0x5DC8 +}; +const uint16_t tile_funcs3[] = { + 0x5DC8,0x5DC8,0x5E58,0x5DC8,0x5DC8,0x5DC8,0x5DC8,0x5DC8,0x5DC8,0x5E58,0x5E58,0x5F7B,0x5F7B,0x5F7B,0x5F7B,0x5F7B, + 0x5F7B,0x5F7B,0x5F7B,0x5F7B,0x5F7B,0x5F7B,0x5F7B,0x5F7B,0x5F7B,0x5F7B,0x5F7B,0x5F7B,0x5F7B,0x5F7B,0x5F7B,0x5F7B, + 0x5F7B,0x5DC8,0x5DC8,0x5F9E,0x5F94,0x5F94,0x5F94,0x5F94,0x5F94,0x5F94,0x5DC8 +}; +const uint8_t level_data_tiles_lut[] = { + 0x00,0x00,0x00,0x00,0x00,0x1D,0x1E,0x00,0x04,0x01,0x01,0x01,0x00,0x00,0x00,0x0B, + 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x19,0x1B,0x05,0x00,0x05,0x00,0x01,0x01, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1F,0x20,0x00,0x07, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x05,0x1C,0x1A,0x05,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00, + 0x00,0x0B,0x0C,0x0D,0x0E,0x0F,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x0B,0x0C,0x0D,0x0E,0x0F,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x00,0x00, + 0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x21,0x22,0x21,0x22,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1B,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x2A,0x2A,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x23,0x00,0x1F,0x20,0x00,0x00,0x1D,0x1E,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1C,0x1A, + 0x19,0x1B,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x06,0x06,0x06,0x06,0x06,0x06, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x02,0x02,0x02, + 0x02,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x02,0x00,0x00,0x00,0x00, + 0x02,0x02,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x02, + 0x00,0x02,0x02,0x00,0x02,0x02,0x02,0x00,0x00,0x00,0x01,0x01,0x01,0x00,0x00,0x00, + 0x00,0x02,0x02,0x00,0x00,0x02,0x02,0x00,0x02,0x02,0x02,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x02,0x02,0x02,0x02,0x00,0x00,0x24,0x24,0x24,0x25,0x25,0x25,0x02,0x00,0x00, + 0x00,0x00,0x00,0x00,0x0A,0x0A,0x0A,0x02,0x02,0x00,0x00,0x21,0x22,0x21,0x22,0x0A, + 0x0A,0x0A,0x00,0x00,0x00,0x00,0x00,0x00,0x09,0x09,0x09,0x09,0x09,0x02,0x02,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x00,0x00,0x00,0x00, + 0x01,0x01,0x08,0x01,0x01,0x26,0x26,0x26,0x27,0x27,0x27,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x00,0x00,0x09,0x09,0x09,0x09,0x09,0x09,0x09, + 0x09,0x09,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x03,0x06,0x06,0x06,0x06,0x06,0x06, + 0x00,0x02,0x02,0x02,0x02,0x02,0x02,0x01,0x08,0x01,0x02,0x02,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x02,0x02,0x02,0x02,0x23,0x23,0x00,0x08,0x00,0x01,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x02, + 0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x02,0x02,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x02, + 0x00,0x00,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x23,0x23,0x02,0x08,0x02,0x02,0x00, + 0x00,0x00,0x02,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x23,0x23,0x08, + 0x08,0x02,0x02,0x00,0x00,0x02,0x00,0x00,0x23,0x23,0x23,0x23,0x00,0x00,0x00,0x00, + 0x24,0x24,0x24,0x25,0x25,0x25,0x02,0x26,0x26,0x26,0x27,0x27,0x27,0x28,0x28,0x28, + 0x00,0x00,0x08,0x00,0x02,0x02,0x00,0x23,0x00,0x00,0x00,0x21,0x22,0x21,0x22,0x08, + 0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x06,0x06,0x06,0x06,0x06, + 0x00,0x01,0x01,0x00,0x00,0x01,0x01,0x01,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x21, + 0x22,0x21,0x22,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x02,0x02,0x02,0x02,0x02,0x02, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x02,0x02,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x02,0x02,0x00,0x00,0x00,0x00,0x02,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x02,0x02,0x02,0x02,0x02,0x00,0x00,0x00,0x00,0x00,0x02,0x02,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x02,0x02,0x02,0x02,0x02,0x23,0x23,0x23,0x23,0x23,0x23, + 0x23,0x23,0x23,0x23,0x02,0x02,0x02,0x02,0x23,0x00,0x00,0x00,0x00,0x02,0x02,0x02, + 0x02,0x02,0x02,0x02,0x23,0x02,0x02,0x02,0x02,0x02,0x23,0x02,0x23,0x00,0x00,0x00, + 0x02,0x02,0x23,0x02,0x02,0x02,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x02,0x02,0x02,0x02,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x06,0x06,0x06,0x06,0x06 +}; +const uint8_t player_anim_dy[] = { + 0xFF,0xFE,0xFC,0xF9,0xF2,0xEA,0xDA,0x00 +}; +const uint8_t player_anim_lut[] = { + 0x00,0x02,0x04,0x03,0x02,0x00,0x03,0x00,0x05,0x05,0x00,0x00,0x05,0x00,0x00,0x00, + 0x06,0x06,0x07,0x07,0x06,0x06,0x07,0x00,0x06,0x06,0x00,0x00,0x06,0x00,0x00,0x00, + 0x0D,0x0F,0x11,0x11,0x0F,0x0D,0x11,0x0D,0x12,0x12,0x0D,0x0D,0x12,0x0D,0x0D,0x0D, + 0x13,0x13,0x14,0x14,0x13,0x13,0x14,0x0D,0x13,0x13,0x0D,0x0D,0x13,0x0D,0x0D,0x0D, + 0x15,0x17,0x19,0x19,0x17,0x15,0x19,0x15,0x1A,0x1A,0x15,0x15,0x1A,0x15,0x15,0x15, + 0x1B,0x1B,0x1C,0x1C,0x1B,0x1B,0x1C,0x15,0x1B,0x1B,0x15,0x15,0x1B,0x15,0x15,0x15, + 0x15,0x17,0x19,0x19,0x17,0x15,0x19,0x15,0x1A,0x1A,0x15,0x15,0x1A,0x15,0x15,0x15, + 0x1B,0x1B,0x1C,0x1C,0x1B,0x1B,0x1C,0x15,0x1B,0x1B,0x15,0x15,0x1B,0x15,0x15,0x15, + 0x09,0x0B,0x0A,0x0A,0x0C,0x09,0x0A,0x0A,0x08,0x05,0x09,0x09,0x05,0x08,0x09,0x09, + 0x06,0x06,0x06,0x06,0x06,0x09,0x06,0x09,0x06,0x06,0x09,0x09,0x06,0x09,0x09,0x09, + 0x1E,0x20,0x1F,0x1F,0x21,0x1E,0x1F,0x1F,0x1D,0x12,0x1E,0x1E,0x12,0x1D,0x1E,0x1E, + 0x13,0x13,0x13,0x13,0x13,0x1E,0x13,0x1E,0x13,0x13,0x1E,0x1E,0x13,0x1E,0x1E,0x1E +}; +static const uint8_t tab6278[] = { + 0x14,0x00,0x14,0x00,0x14,0x00,0x14,0x00,0x14,0x00,0x14,0x00,0x14,0x00,0x14,0x00, + 0x14,0x00,0x14,0x00,0x14,0x00,0x14,0x00,0x14,0x00,0x14,0x00,0x14,0x00,0x14,0x00, + 0x14,0x00,0x14,0x00,0x14,0x00,0x14,0x00,0x14,0x00,0x14,0x00,0x14,0x00,0x14,0x00, + 0x14,0x00,0x15,0x00,0x15,0x00,0x15,0x00,0x15,0x00,0x15,0x00,0x16,0x00,0x16,0x00, + 0x16,0x00,0x16,0x00,0x16,0x00,0xF1,0xFF +}; +static const uint8_t tab62c0[] = { + 0x11,0x00,0x11,0x00,0x11,0x00,0x11,0x00,0x11,0x00,0x11,0x00,0x12,0x00,0x12,0x00, + 0x12,0x00,0x12,0x00,0x12,0x00,0x12,0x00,0x12,0x00,0x12,0x00,0x12,0x00,0x12,0x00, + 0x11,0x00,0x11,0x00,0x11,0x00,0x12,0x00,0x12,0x00,0x12,0x00,0x11,0x00,0x11,0x00, + 0x11,0x00,0x12,0x00,0x12,0x00,0x12,0x00,0x12,0x40,0x12,0x40,0x12,0x40,0x12,0x40, + 0x12,0x40,0x12,0x40,0x11,0x40,0x11,0x40,0x11,0x40,0x11,0x40,0x11,0x40,0x11,0x40, + 0x12,0x00,0x12,0x00,0x12,0x00,0x12,0x00,0x12,0x00,0x12,0x00,0x12,0x00,0x12,0x00, + 0x12,0x00,0x12,0x00,0x11,0x00,0x11,0x00,0x11,0x00,0x12,0x00,0x12,0x00,0x12,0x00, + 0x11,0x00,0x11,0x00,0x11,0x00,0x12,0x00,0x12,0x00,0x12,0x00,0x11,0x40,0x11,0x40, + 0x11,0x40,0x11,0x40,0x11,0x40,0x11,0x40,0x12,0x40,0x12,0x40,0x12,0x40,0x12,0x40, + 0x12,0x40,0x12,0x40,0x11,0x40,0x11,0x40,0x11,0x40,0x11,0x40,0x11,0x40,0x11,0x40, + 0x12,0x40,0x12,0x40,0x12,0x40,0x12,0x40,0x12,0x40,0x12,0x40,0x11,0x40,0x11,0x40, + 0x11,0x40,0x11,0x40,0x11,0x40,0x11,0x40,0x12,0x40,0x12,0x40,0x12,0x40,0x11,0x40, + 0x11,0x40,0x11,0x40,0x12,0x40,0x12,0x40,0x12,0x40,0x11,0x00,0x11,0x00,0x11,0x00, + 0x11,0x00,0x11,0x00,0x11,0x00,0x12,0x00,0x12,0x00,0x12,0x00,0x12,0x00,0x12,0x00, + 0x12,0x00,0x11,0x00,0x11,0x00,0x11,0x00,0x11,0x00,0x11,0x00,0x11,0x00,0x12,0x00, + 0x12,0x00,0x12,0x00,0x12,0x00,0x12,0x00,0x12,0x00,0x11,0x00,0x11,0x00,0x11,0x00, + 0x11,0x00,0x11,0x00,0x11,0x00,0x12,0x00,0x12,0x00,0x12,0x00,0x12,0x00,0x12,0x00, + 0x12,0x00,0x11,0x00,0x11,0x00,0x11,0x00,0x11,0x00,0x11,0x00,0x11,0x00,0x12,0x00, + 0x12,0x00,0x12,0x00,0x12,0x00,0x12,0x00,0x12,0x00,0x14,0x20,0xFF,0xFF +}; +static const uint8_t tab63ee[] = { + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x01,0x00,0x01,0x00,0x01,0x00, + 0x02,0x00,0x02,0x00,0x02,0x00,0x02,0x00,0x03,0x00,0x03,0x00,0x03,0x00,0x03,0x00, + 0x04,0x00,0x04,0x00,0x04,0x00,0x04,0x00,0x05,0x00,0x05,0x00,0x05,0x00,0x05,0x00, + 0xE8,0xFF +}; +static const uint8_t tab6420[] = { + 0x06,0x00,0x06,0x00,0x07,0x00,0x07,0x00,0x07,0x00,0x08,0x00,0x08,0x00,0x08,0x00, + 0x09,0x00,0x09,0x00,0x09,0x00,0xF7,0xFF +}; +static const uint8_t tab6438[] = { + 0x06,0x00,0xFF,0xFF +}; +static const uint8_t tab643c[] = { + 0x0A,0x00,0xFF,0xFF +}; +static const uint8_t tab6440[] = { + 0x10,0x00,0x10,0x20,0x10,0x00,0xFD,0xFF +}; +static const uint8_t tab6448[] = { + 0x17,0x00,0x17,0x20,0x17,0x00,0xFD,0xFF +}; +static const uint8_t tab6266[] = { + 0x0D,0x00,0x0D,0x00,0x0E,0x00,0x0E,0x00,0x0D,0x40,0x0D,0x40,0x0E,0x40,0x0E,0x40, + 0xF8,0xFF +}; +static const uint8_t tab6262[] = { + 0x0D,0x00,0xFF,0xFF +}; +static const uint8_t tab6450[] = { + 0x26,0x00,0x26,0x00,0x26,0x00,0x26,0x00,0x26,0x00,0x26,0x00,0x26,0x00,0x27,0x00, + 0x27,0x00,0x27,0x00,0x27,0x00,0x27,0x00,0x27,0x00,0x27,0x00,0x28,0x00,0x28,0x00, + 0x28,0x00,0x28,0x00,0x28,0x00,0x28,0x00,0x28,0x00,0x27,0x00,0x27,0x00,0x27,0x00, + 0x27,0x00,0x27,0x00,0x27,0x00,0x27,0x00,0xE4,0xFF +}; +static const uint8_t tab648a[] = { + 0x19,0x00,0x19,0x00,0x19,0x00,0x19,0x00,0x1A,0x00,0x1A,0x00,0x1A,0x00,0x1A,0x00, + 0x1B,0x00,0x1B,0x00,0x1B,0x00,0x1B,0x00,0x1C,0x00,0x1C,0x00,0x1C,0x00,0x1C,0x00, + 0x1D,0x00,0x1D,0x00,0x1D,0x00,0x1D,0x00,0x1E,0x00,0x1E,0x00,0x1E,0x00,0x1E,0x00, + 0xE8,0xFF +}; +static const uint8_t tab64bc[] = { + 0x21,0x00,0xFF,0xFF +}; +static const uint8_t tab64c0[] = { + 0x23,0x00,0xFF,0xFF +}; +static const uint8_t tab64c4[] = { + 0x29,0x00,0x29,0x20,0xFF,0xFF +}; +static const uint8_t tab64ca[] = { + 0x22,0x00,0x22,0x20,0xFF,0xFF +}; +static const uint8_t tab64e6[] = { + 0x96,0x00,0x96,0x00,0x96,0x00,0x96,0x00,0x96,0x00,0x96,0x00,0x96,0x00,0x97,0x00, + 0x97,0x00,0x97,0x00,0x97,0x00,0x97,0x00,0x97,0x00,0x97,0x00,0x98,0x00,0x98,0x00, + 0x98,0x00,0x98,0x00,0x98,0x00,0x98,0x00,0x98,0x00,0x97,0x00,0x97,0x00,0x97,0x00, + 0x97,0x00,0x97,0x00,0x97,0x00,0x97,0x00,0xE4,0xFF +}; +static const uint8_t tab6520[] = { + 0x8C,0x00,0x8C,0x00,0x8C,0x00,0x8C,0x00,0x8D,0x00,0x8D,0x00,0x8D,0x00,0x8D,0x00, + 0x8E,0x00,0x8E,0x00,0x8E,0x00,0x8E,0x00,0x8F,0x00,0x8F,0x00,0x8F,0x00,0x8F,0x00, + 0x90,0x00,0x90,0x00,0x90,0x00,0x90,0x00,0x91,0x00,0x91,0x00,0x91,0x00,0x91,0x00, + 0xE8,0xFF +}; +static const uint8_t tab6552[] = { + 0x95,0x00,0xFF,0xFF +}; +static const uint8_t tab6556[] = { + 0x92,0x00,0xFF,0xFF +}; +static const uint8_t tab655a[] = { + 0x18,0x20,0xFF,0xFF +}; +static const uint8_t tab655e[] = { + 0x17,0x20,0xFF,0xFF +}; +static const uint8_t tab64d4[] = { + 0x1F,0x00,0x1F,0x00,0x20,0x00,0x20,0x00,0x1F,0x40,0x1F,0x40,0x20,0x40,0x20,0x40, + 0xF8,0xFF +}; +static const uint8_t tab64d0[] = { + 0x1F,0x00,0xFF,0xFF +}; +static const uint8_t tab625e[] = { + 0x8A,0x00,0xFF,0xFF +}; +static const uint8_t tab6562[] = { + 0x25,0x00,0xFF,0xFF +}; +const uint8_t *player_anim_table[] = { + tab6278,tab62c0,tab63ee,tab6420, + tab6438,tab643c,tab6440,tab6448, + tab6266,tab6262,tab6266,tab6266, + tab6266,tab6450, 0,tab648a, + 0,tab64bc,tab64c0,tab64c4, + tab64ca,tab64e6, 0,tab6520, + 0,tab6552,tab6556,tab655a, + tab655e,tab64d4,tab64d0,tab64d4, + tab64d4,tab64d4,tab625e,tab6562, +}; diff --git a/ja/unpack.c b/ja/unpack.c new file mode 100644 index 0000000..7c0a492 --- /dev/null +++ b/ja/unpack.c @@ -0,0 +1,129 @@ + +/* uncompress data packed with DIET by T.Matsumoto */ + +#include "unpack.h" +#include "util.h" + +struct unpack_eat_t { + FILE *fp; + uint8_t len; + uint16_t bits; + uint8_t *dst; +}; + +static struct unpack_eat_t g_unpack; + +static uint16_t fread_le16(FILE *fp) { + const uint16_t val = fgetc(fp); + return val | (fgetc(fp) << 8); +} + +static int next_bit(struct unpack_eat_t *u) { + const int bit = (u->bits & (1 << (16 - u->len))) != 0; + --u->len; + if (u->len == 0) { + u->bits = fread_le16(u->fp); + u->len = 16; + } + return bit; +} + +static int zero_bits(struct unpack_eat_t *u, int count) { + int i = 0; + for (; i < count; ++i) { + if (next_bit(u)) { + break; + } + } + return i; +} + +static uint8_t get_bits(struct unpack_eat_t *u, int count) { + assert(count < 8); + uint8_t val = 0; + for (int i = 0; i < count; ++i) { + val = (val << 1) | next_bit(u); + } + return val; +} + +static void copy_reference(struct unpack_eat_t *u, int count, int offset_hi, int offset_lo) { + const int16_t offset = offset_hi * 256 + offset_lo; + for (int i = 0; i < count; ++i) { + const uint8_t value = u->dst[offset]; + *u->dst++ = value; + } +} + +static int unpack_eat(struct unpack_eat_t *u, uint8_t *output_buffer, int output_buffer_size) { + uint8_t buffer[17]; + const int header_size = fread(buffer, 1, sizeof(buffer), u->fp); + if (header_size != 17 || READ_LE_UINT16(buffer + 4) != 0x899D || READ_LE_UINT16(buffer + 6) != 0x6C64) { + print_error("Unexpected signature for .eat file"); + return 0; + } + const uint16_t crc = READ_LE_UINT16(buffer + 12); + const int output_size = (buffer[14] << 14) + READ_LE_UINT16(buffer + 15); + print_debug(DBG_UNPACK, "uncompressed size %d crc 0x%04x", output_size, crc); + if (output_size > output_buffer_size) { + print_error("Invalid output buffer size %d, need %d", output_buffer_size, output_size); + return 0; + } + + u->dst = output_buffer; + u->len = 16; + u->bits = fread_le16(u->fp); + + while (1) { + while (next_bit(u)) { + *u->dst++ = fgetc(u->fp); + } + const int b = next_bit(u); + const int offset_lo = fgetc(u->fp); + if (b) { + int offset_hi = 0xFE | next_bit(u); + if (!next_bit(u)) { + int i = 1; + for (; i < 4 && !next_bit(u); ++i) { + offset_hi = (offset_hi << 1) | next_bit(u); + } + offset_hi -= (1 << i); + } + const int n = zero_bits(u, 4); + if (n != 4) { + copy_reference(u, n + 3, offset_hi, offset_lo); + } else if (next_bit(u)) { + copy_reference(u, next_bit(u) + 7, offset_hi, offset_lo); + } else if (!next_bit(u)) { + copy_reference(u, get_bits(u, 3) + 9, offset_hi, offset_lo); + } else { + copy_reference(u, fgetc(u->fp) + 17, offset_hi, offset_lo); + } + } else { + if (next_bit(u)) { + const int offset_hi = (0xF8 | get_bits(u, 3)) - 1; + copy_reference(u, 2, offset_hi, offset_lo); + } else if (offset_lo == 0xFF) { + break; + } else { + copy_reference(u, 2, 0xFF, offset_lo); + } + } + } + assert((u->dst - output_buffer) == output_size); + return output_size; +} + +int unpack(const char *filename, uint8_t *dst, int size) { + int uncompressed_size = 0; + FILE *fp = fopen(filename, "rb"); + if (fp) { + memset(&g_unpack, 0, sizeof(struct unpack_eat_t)); + g_unpack.fp = fp; + uncompressed_size = unpack_eat(&g_unpack, dst, size); + fclose(fp); + } else { + print_error("Unable to open '%s'", filename); + } + return uncompressed_size; +} diff --git a/ja/unpack.h b/ja/unpack.h new file mode 100644 index 0000000..0c86458 --- /dev/null +++ b/ja/unpack.h @@ -0,0 +1,9 @@ + +#ifndef UNPACK_H__ +#define UNPACK_H__ + +#include "intern.h" + +extern int unpack(const char *filename, uint8_t *dst, int size); + +#endif /* UNPACK_H__ */ diff --git a/main.c b/main.c index 79fcec0..0b86846 100644 --- a/main.c +++ b/main.c @@ -1,10 +1,6 @@ #include #include - -#include "fileio.h" -#include "game.h" -#include "resource.h" #include "sys.h" #include "util.h" @@ -23,12 +19,33 @@ static const char *USAGE = " --cheats=MASK Cheats mask\n" " --startpos=XxY Start at position (X,Y)\n" " --fullscreen Enable fullscreen\n" - " --scale Graphics scaling factor (default 2)\n" - " --filter Graphics scaling filter\n" + " --scale=N Graphics scaling factor (default 2)\n" + " --filter=NAME Graphics scaling filter (default 'nearest')\n" " --screensize=WxH Graphics screen size (default 320x200)\n" " --cga Enable CGA colors\n" ; +static struct game_t *detect_game(const char *data_path) { +#if 0 + extern struct game_t bb_game; + extern struct game_t ja_game; + static struct game_t *games[] = { + &bb_game, + &ja_game, + 0 + }; + for (int i = 0; games[i]; ++i) { + if (games[i]->detect(data_path)) { + return games[i]; + } + } + return 0; +#else + extern struct game_t game; + return &game; +#endif +} + int main(int argc, char *argv[]) { g_options.start_xpos16 = -1; g_options.start_ypos16 = -1; @@ -97,7 +114,7 @@ int main(int argc, char *argv[]) { if (sscanf(optarg, "%dx%d", &g_options.screen_w, &g_options.screen_h) == 2) { // align to tile 16x16 g_options.screen_w = (g_options.screen_w + 15) & ~15; - g_options.screen_h = ((g_options.screen_h + 15) & ~15) + 40; + g_options.screen_h = ((g_options.screen_h + 15) & ~15) + 40; // PANEL_H } break; case 10: @@ -108,16 +125,15 @@ int main(int argc, char *argv[]) { return -1; } } - fio_init(data_path); - res_init(GAME_SCREEN_W * GAME_SCREEN_H); - g_sys.init(); - g_sys.set_screen_size(GAME_SCREEN_W, GAME_SCREEN_H, "Blues Brothers", scale_factor, scale_filter, fullscreen); - sound_init(); - game_main(); - sound_fini(); - g_sys.fini(); - res_fini(); - fio_fini(); + struct game_t *game = detect_game(data_path); + if (!game) { + fprintf(stdout, "No data files found\n"); + } else { + g_sys.init(); + g_sys.set_screen_size(GAME_SCREEN_W, GAME_SCREEN_H, game->name, scale_factor, scale_filter, fullscreen); + game->run(data_path); + g_sys.fini(); + } if (data_path != DEFAULT_DATA_PATH) { free((char *)data_path); } diff --git a/sys.h b/sys.h index 2696010..31120bc 100644 --- a/sys.h +++ b/sys.h @@ -16,6 +16,7 @@ struct input_t { bool quit; bool escape; bool space; + bool digit1, digit2, digit3; }; typedef void (*sys_audio_cb)(void *, uint8_t *data, int len); @@ -35,9 +36,10 @@ struct sys_t { int (*init)(); void (*fini)(); void (*set_screen_size)(int w, int h, const char *caption, int scale, const char *filter, bool fullscreen); - void (*set_screen_palette)(const uint8_t *colors, int); + void (*set_screen_palette)(const uint8_t *colors, int offset, int count, int depth); void (*set_palette_amiga)(const uint16_t *colors, int offset); void (*set_copper_bars)(const uint16_t *data); + void (*set_palette_color)(int i, const uint8_t *colors); void (*fade_in_palette)(); void (*fade_out_palette)(); void (*update_screen)(const uint8_t *p, int present); diff --git a/sys_sdl2.c b/sys_sdl2.c index cfe1e5f..1018b6c 100644 --- a/sys_sdl2.c +++ b/sys_sdl2.c @@ -33,7 +33,7 @@ static SDL_Window *_window; static SDL_Renderer *_renderer; static SDL_Texture *_texture; static SDL_PixelFormat *_fmt; -static uint32_t _screen_palette[48]; +static uint32_t _screen_palette[256]; static uint32_t *_screen_buffer; static struct input_t *_input = &g_sys.input; static int _copper_color; @@ -158,22 +158,41 @@ static void sdl2_set_copper_bars(const uint16_t *data) { } } -static void sdl2_set_screen_palette(const uint8_t *colors, int count) { +static void sdl2_set_screen_palette(const uint8_t *colors, int offset, int count, int depth) { + const int shift = 8 - depth; for (int i = 0; i < count; ++i) { - _screen_palette[i] = SDL_MapRGB(_fmt, colors[0], colors[1], colors[2]); + int r = colors[0]; + int g = colors[1]; + int b = colors[2]; + if (depth != 8) { + r = (r << shift) | (r >> (depth - shift)); + g = (g << shift) | (g >> (depth - shift)); + b = (b << shift) | (b >> (depth - shift)); + } + _screen_palette[offset + i] = SDL_MapRGB(_fmt, r, g, b); colors += 3; } } +static void sdl2_set_palette_color(int i, const uint8_t *colors) { + int r = colors[0]; + r = (r << 2) | (r >> 4); + int g = colors[1]; + g = (g << 2) | (g >> 4); + int b = colors[2]; + b = (b << 2) | (b >> 4); + _screen_palette[i] = SDL_MapRGB(_fmt, r, g, b); +} + static void fade_palette_helper(int in) { SDL_SetRenderDrawBlendMode(_renderer, SDL_BLENDMODE_BLEND); SDL_Rect r; r.x = r.y = 0; SDL_GetRendererOutputSize(_renderer, &r.w, &r.h); - for (int i = 1; i <= FADE_STEPS; ++i) { - int alpha = 256 * i / FADE_STEPS; + for (int i = 0; i <= FADE_STEPS; ++i) { + int alpha = 255 * i / FADE_STEPS; if (in) { - alpha = 256 - alpha; + alpha = 255 - alpha; } SDL_SetRenderDrawColor(_renderer, 0, 0, 0, alpha); SDL_RenderClear(_renderer); @@ -313,6 +332,15 @@ static void handle_keyevent(int keysym, bool keydown) { case SDLK_ESCAPE: _input->escape = keydown; break; + case SDLK_1: + _input->digit1 = keydown; + break; + case SDLK_2: + _input->digit2 = keydown; + break; + case SDLK_3: + _input->digit3 = keydown; + break; } } @@ -484,18 +512,19 @@ static void sdl2_unlock_audio() { } struct sys_t g_sys = { - .init = sdl2_init, - .fini = sdl2_fini, - .set_screen_size = sdl2_set_screen_size, - .set_screen_palette = sdl2_set_screen_palette, - .set_palette_amiga = sdl2_set_palette_amiga, - .set_copper_bars = sdl2_set_copper_bars, - .fade_in_palette = sdl2_fade_in_palette, - .fade_out_palette = sdl2_fade_out_palette, - .update_screen = sdl2_update_screen, + .init = sdl2_init, + .fini = sdl2_fini, + .set_screen_size = sdl2_set_screen_size, + .set_screen_palette = sdl2_set_screen_palette, + .set_palette_amiga = sdl2_set_palette_amiga, + .set_copper_bars = sdl2_set_copper_bars, + .set_palette_color = sdl2_set_palette_color, + .fade_in_palette = sdl2_fade_in_palette, + .fade_out_palette = sdl2_fade_out_palette, + .update_screen = sdl2_update_screen, .transition_screen = sdl2_transition_screen, - .process_events = sdl2_process_events, - .sleep = sdl2_sleep, + .process_events = sdl2_process_events, + .sleep = sdl2_sleep, .get_timestamp = sdl2_get_timestamp, .start_audio = sdl2_start_audio, .stop_audio = sdl2_stop_audio,