diff --git a/decode.c b/decode.c index 1d0b2af..1f76a29 100644 --- a/decode.c +++ b/decode.c @@ -1,7 +1,7 @@ #include "decode.h" -void decode_ega_spr(const uint8_t *src, int src_pitch, int w, int h, uint8_t *dst, int dst_pitch, int dst_x, int dst_y, int xflip) { +void decode_ega_spr(const uint8_t *src, int src_pitch, int w, int h, uint8_t *dst, int dst_pitch, int dst_x, int dst_y) { assert((src_pitch & 7) == 0); src_pitch /= 8; assert((w & 7) == 0); @@ -19,10 +19,8 @@ void decode_ega_spr(const uint8_t *src, int src_pitch, int w, int h, uint8_t *ds } } if (color != 0) { - const int x_offset = dst_x + (xflip ? (w * 8 - x * 8 - i) : (x * 8 + i)); - if (x_offset >= 0 && x_offset < 320 && y_offset >= 0 && y_offset < 200) { - dst[y_offset * dst_pitch + x_offset] = color; - } + const int x_offset = dst_x + (x * 8 + i); + dst[y_offset * dst_pitch + x_offset] = color; } } ++src; diff --git a/decode.h b/decode.h index a629eb9..07174f3 100644 --- a/decode.h +++ b/decode.h @@ -4,6 +4,6 @@ #include "intern.h" -extern void decode_ega_spr(const uint8_t *src, int src_pitch, int w, int h, uint8_t *dst, int dst_pitch, int dst_x, int dst_y, int xflip); +extern void decode_ega_spr(const uint8_t *src, int src_pitch, int w, int h, uint8_t *dst, int dst_pitch, int dst_x, int dst_y); #endif /* DECODE_H__ */ diff --git a/game.c b/game.c index 318822e..f1fa7bf 100644 --- a/game.c +++ b/game.c @@ -47,13 +47,14 @@ static void check_cheat_code() { void do_select_player() { int quit = 0; - int var9 = 0; + int fade = 0; int state = 0; int frame1 = 1; int frame2 = 1; const int color_rgb = 2; const int colors_count = 25; load_img("choix.sqz"); + screen_load_graphics(); screen_clear_sprites(); do { screen_unk4(); @@ -174,9 +175,9 @@ void do_select_player() { break; } // state_default_: - if (var9 == 0) { + if (!fade) { fade_in_palette(); - var9 = 1; + fade = 1; for (int i = 0; i < colors_count; ++i) { screen_adjust_palette_color( 3, color_rgb, 1); screen_adjust_palette_color(10, color_rgb, 1); @@ -208,16 +209,12 @@ void do_select_player() { } while (!quit && !g_sys.input.quit); } -static void do_inter_screen_helper(int a, int b, int c) { +static void do_inter_screen_helper(int xpos, int ypos, int c) { if (c != 0) { g_vars.screen_draw_offset ^= 0x2000; } - int xpos = a + 20; - int ypos = b - 20; for (int i = 0; i < 40; ++i) { - --xpos; - ++ypos; - screen_add_sprite(xpos, ypos, 125); + screen_add_sprite(xpos + 20 - 1 - i, ypos - 20 + 1 + i, 125); screen_clear_last_sprite(); screen_redraw_sprites(); if (c != 0) { @@ -226,9 +223,7 @@ static void do_inter_screen_helper(int a, int b, int c) { screen_clear_sprites(); } for (int i = 0; i < 40; ++i) { - ++xpos; - ++ypos; - screen_add_sprite(xpos, ypos, 125); + screen_add_sprite(xpos - 20 + 1 + i, ypos - 20 + 1 + i, 125); screen_clear_last_sprite(); screen_redraw_sprites(); if (c != 0) { @@ -288,7 +283,7 @@ void game_main() { screen_flip(); screen_init(); g_sys.set_screen_size(GAME_SCREEN_W, GAME_SCREEN_H, "Blues Brothers"); - g_vars.start_level = g_options.start_level; + g_vars.start_level = 0; load_sqv("sprite.sqv", g_res.spr_sqv, 0); do_title_screen(); while (g_sys.input.quit == 0) { @@ -297,7 +292,7 @@ void game_main() { g_vars.game_over_flag = 0; g_vars.play_level_flag = 1; if (!g_vars.play_demo_flag) { - g_vars.level = 0; + g_vars.level = g_options.start_level; do_select_player(); } else { g_vars.level = g_vars.start_level; diff --git a/game.h b/game.h index c308197..066cdd0 100644 --- a/game.h +++ b/game.h @@ -17,7 +17,6 @@ #define MAX_DOORS 30 #define MAX_LEVELS 6 #define MAX_OBJECTS 41 -#define MAX_SPRITES 40 #define SOUND_0 0 #define SOUND_2 2 @@ -47,17 +46,6 @@ struct options_t { extern struct options_t g_options; -struct sprite_t { - int16_t xpos; - int16_t ypos; - int16_t spr_w; - int16_t spr_h; - const uint8_t *frame; - struct sprite_t *next_sprite; - int16_t xflip; - int16_t unk16; -}; - struct door_t { uint16_t src_x16, src_y16; uint16_t dst_x16, dst_y16; @@ -96,7 +84,7 @@ struct object_t { int16_t unk1C; // xvelocity / 8 int16_t unk1E; // yvelocity / 8 uint8_t direction_lr; - uint8_t direction_ud; + int8_t direction_ud; int16_t xpos16; // tilemap_xpos int16_t ypos16; // tilemap_ypos // int16_t unk26; @@ -194,9 +182,6 @@ struct vars_t { uint16_t level_loop_counter; int triggers_counter; int update_objects_counter; - struct sprite_t sprites_table[MAX_SPRITES]; - struct sprite_t *sprites[MAX_SPRITES]; - int sprites_count; }; extern struct vars_t g_vars; @@ -243,7 +228,7 @@ extern void do_level_update_panel_lifes(struct object_t *); extern void do_level_enter_door(struct object_t *); extern void do_level_player_hit(struct object_t *); extern void do_level_drop_grabbed_object(struct object_t *); -extern void do_level_update_object38(struct object_t *obj); +extern void do_level_update_projectile(struct object_t *obj); extern void do_level(); /* opcodes.c */ @@ -259,7 +244,6 @@ extern void fade_in_palette(); extern void fade_out_palette(); extern void screen_adjust_palette_color(int color, int b, int c); extern void screen_vsync(); -extern void screen_set_palette(); extern void screen_draw_frame(const uint8_t *frame, int a, int b, int c, int d); extern void screen_flip(); extern void screen_unk4(); @@ -276,6 +260,7 @@ extern void screen_add_game_sprite1(int x, int y, int frame); extern void screen_add_game_sprite2(int x, int y, int frame); extern void screen_add_game_sprite3(int x, int y, int frame, int blinking_counter); extern void screen_add_game_sprite4(int x, int y, int frame, int blinking_counter); +extern void screen_load_graphics(); /* sound.c */ extern void sound_init(int rate); diff --git a/level.c b/level.c index 24044a4..7c500c5 100644 --- a/level.c +++ b/level.c @@ -1,7 +1,6 @@ /* level main loop */ -#include "decode.h" #include "game.h" #include "resource.h" #include "sys.h" @@ -66,13 +65,14 @@ void load_level_data(int num) { } load_bin(_levels[num].bin); load_avt(_levels[num].avt, g_res.avt_sqv, 0); - load_sqv(_levels[num].sqv, g_res.tmp, 146); + load_sqv(_levels[num].sqv, g_res.tmp, SPRITES_COUNT); memcpy(g_vars.level_xpos, _levels[num].xpos, MAX_OBJECTS * sizeof(int16_t)); memcpy(g_vars.level_ypos, _levels[num].ypos, MAX_OBJECTS * sizeof(int16_t)); if (g_vars.music_num != _levels[num].music) { play_music(_levels[num].music); g_vars.music_num = _levels[num].music; } + screen_load_graphics(); } static void init_level() { @@ -502,6 +502,64 @@ static void do_level_add_sprite1_case2(struct object_t *obj) { obj->anim_frame = anim_data[obj->anim_num]; } +static void do_level_add_sprite1_case3(struct object_t *obj) { + struct object_t *obj35 = &g_vars.objects[35]; + struct object_t *obj37 = &g_vars.objects[37]; + if (obj->type != 0 && g_vars.two_players_flag) { + obj35 = &g_vars.objects[34]; + obj37 = &g_vars.objects[36]; + } + if (obj37->type != 100 && obj37->grab_type != 0) { + do_level_drop_grabbed_object(obj); + } + obj->unk3D = 0; + if (obj35->type != 100) { + obj35->type = 100; + obj35->visible_flag = 0; + } + const uint8_t *anim_data = 0; + obj->unk3D = 0; + int tile_num = triggers_get_tile_type(obj->xpos16, obj->ypos16); + obj->xmaxvelocity = 8; + obj->xacc = 0; + obj->unk2D = 10; + if (obj->direction_lr != 0) { + if (obj->direction_ud != 0) { + if (tile_num == 6) { + anim_data = obj->animframes_ptr[56 / 4]; + } else { + anim_data = obj->animframes_ptr[0]; + } + } else { + if (tile_num == 6) { + anim_data = obj->animframes_ptr[68 / 4]; + } else { + anim_data = obj->animframes_ptr[4 / 4]; + } + } + } else { + if (obj->direction_ud != 0) { + if (tile_num == 6) { + anim_data = obj->animframes_ptr[56 / 4]; + } else { + anim_data = obj->animframes_ptr[0]; + } + } else { + if (tile_num == 6) { + anim_data = obj->animframes_ptr[68 / 4]; + } else { + anim_data = obj->animframes_ptr[4 / 4]; + } + } + } + if (obj->anim_num < anim_data[0]) { + obj->anim_num += (g_vars.level_loop_counter & 1); + } else { + obj->anim_num = 1; + } + obj->anim_frame = anim_data[obj->anim_num]; +} + // swimming static void do_level_add_sprite1_case4(struct object_t *obj) { struct object_t *obj35 = &g_vars.objects[35]; @@ -608,6 +666,9 @@ static void do_level_add_sprite1(struct object_t *obj) { case 2: do_level_add_sprite1_case2(obj); break; + case 3: + do_level_add_sprite1_case3(obj); + break; case 4: do_level_add_sprite1_case4(obj); break; @@ -1335,17 +1396,17 @@ static void do_level_update_object_bounds(struct object_t *obj) { // obj->unk41 = 0; } -void do_level_update_object38(struct object_t *obj) { +void do_level_update_projectile(struct object_t *obj) { struct object_t *obj38 = &g_vars.objects[38]; if (obj->unk42 == 0) { - if (obj->type == 100) { + if (obj38->type == 100) { obj->special_anim = 2; obj->unk42 = 1; } } else if (obj->unk42 == 1) { if (obj38->grab_type == 10 || obj->anim_num >= 7) { obj38->type = 2; - obj->visible_flag = 1; + obj38->visible_flag = 1; static const uint8_t data[MAX_OBJECTS] = { 0, 0, 0, 0, 4, 5, 0, 0, 0, 0, 0, 0, 0, 6, 23, 0, 9, 9, 0, 10, 0, 11, 0, 12, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, 0, 0 @@ -1375,6 +1436,11 @@ void do_level_update_object38(struct object_t *obj) { obj38->xpos = obj->xpos + 16 - (obj->facing_left << 5); obj38->xvelocity = obj38->yvelocity = 32 - (obj->facing_left << 6); break; + default: + obj38->ypos = obj->ypos - 18; + obj38->xpos = obj->xpos + 32 - (obj->facing_left << 6); + obj38->xvelocity = obj38->yvelocity = 64 - (obj->facing_left << 7); + break; } const int num = obj38->type * 116 / 4; assert(num < 1189); @@ -1842,7 +1908,17 @@ static void do_level_update_objects() { obj->direction_lr = 0; obj->direction_ud = 0; if (obj->type < 2) { - if (g_vars.inp_keyboard[0xC1] != 0) { // F7 + if (g_vars.inp_keyboard[0xC1] != 0) { // F7, change player + if (!g_vars.screen_unk1 && g_vars.two_players_flag) { + if (!g_vars.player2_scrolling_flag && g_vars.objects[OBJECT_NUM_PLAYER1].unk53 == 0) { + g_vars.player2_scrolling_flag = 1; + g_vars.screen_unk1 = 1; + } else if (g_vars.player2_scrolling_flag && g_vars.objects[OBJECT_NUM_PLAYER2].unk53 == 0) { + g_vars.player2_scrolling_flag = 1; + g_vars.screen_unk1 = 1; + } + } + g_vars.inp_keyboard[0xC1] = 0; } if (obj->blinking_counter != 0) { --obj->blinking_counter; @@ -1896,7 +1972,7 @@ static void do_level_update_objects() { if (obj->visible_flag != 0) { do_level_update_object_bounds(obj); if (obj->unk42 != 0) { - do_level_update_object38(obj); + do_level_update_projectile(obj); } obj->unk2D = 10; obj->unk1C = 0; @@ -2046,9 +2122,9 @@ static void draw_foreground_tiles() { } const int avt_num = g_res.triggers[num].foreground_tile_num; if (avt_num != 255) { - const int tile_y = j * 16 + 14; + const int tile_y = j * 16 + TILEMAP_OFFSET_Y; const int tile_x = i * 16; - decode_ega_spr(g_res.avt[avt_num], 16, 16, 16, g_res.vga, GAME_SCREEN_W, tile_x, tile_y, 0); + render_add_sprite(RENDER_SPR_FG, avt_num, tile_x, tile_y, 0); } } ++y; @@ -2098,7 +2174,7 @@ void do_level() { do { const uint32_t timestamp = g_sys.get_timestamp(); update_input(); - if (g_vars.inp_keyboard[0xBF] != 0) { // F5 + if (g_vars.inp_keyboard[0xBF] != 0) { // F5, quit game play_sound(SOUND_0); g_vars.inp_keyboard[0xBF] = 0; g_vars.quit_level_flag = 1; diff --git a/opcodes.c b/opcodes.c index 239245c..846fc2d 100644 --- a/opcodes.c +++ b/opcodes.c @@ -49,18 +49,18 @@ static void object_func_op2(struct object_t *obj) { obj->moving_direction ^= 1; } obj->direction_lr = obj->moving_direction + 1; - obj->xmaxvelocity = 40; + obj->xmaxvelocity = 24; if (obj->anim_num == 12 && obj->special_anim == 2) { obj->special_anim = 3; obj->anim_num = 1; } else if (obj->anim_num == 12 && obj->special_anim == 3) { obj->special_anim = 0; } - if (obj->player_ydist <= 0 && obj->player_xdist > -(TILEMAP_SCREEN_W / 4) && obj->player_xdist < (TILEMAP_SCREEN_W / 4) && obj->direction_ud > 196) { + if (obj->player_ydist <= 20 && obj->player_xdist > -(TILEMAP_SCREEN_W / 4) && obj->player_xdist < (TILEMAP_SCREEN_W / 4) && obj->direction_ud > -60) { if (obj->special_anim == 0 || obj->anim_num == 12) { obj->anim_num = 1; obj->unk42 = 0; - do_level_update_object38(obj); + do_level_update_projectile(obj); } obj->facing_left = (obj->player_xdist < 0) ? 1 : 0; } @@ -71,9 +71,9 @@ static void object_func_op2(struct object_t *obj) { static void object_func_op3(struct object_t *obj) { if (obj->player_ydist <= 20 && obj->player_ydist > -(TILEMAP_SCREEN_W / 2) && obj->player_xdist < (TILEMAP_SCREEN_W / 2) && g_vars.objects[38].type == 100) { - if (obj->direction_ud > 226) { + if (obj->direction_ud > -30) { if (obj->special_anim == 0 || obj->anim_num == 16) { - do_level_update_object38(obj); + do_level_update_projectile(obj); } obj->facing_left = (obj->player_xdist < 0) ? 1 : 0; } @@ -154,6 +154,37 @@ static void object_func_op5(struct object_t *obj) { obj->xmaxvelocity = 32; } +static void object_func_op7(struct object_t *obj) { + if (triggers_get_tile_type(obj->xpos16 + 2 - obj->facing_left * 4, obj->ypos16) == 12) { + obj->moving_direction ^= 1; + } + obj->direction_lr = obj->moving_direction + 1; + obj->xmaxvelocity = 32; + if (g_vars.objects[OBJECT_NUM_PLAYER1].special_anim != 18 && g_vars.objects[OBJECT_NUM_PLAYER2].special_anim != 18) { + if (obj->player_ydist <= 10 && obj->player_xdist > -(TILEMAP_SCREEN_W / 4) && obj->player_xdist < (TILEMAP_SCREEN_W / 4) && g_vars.objects[38].type == 100) { + if (obj->direction_ud > -30) { + if (obj->special_anim == 0 || obj->anim_num == 16) { + obj->anim_num = 1; + obj->unk42 = 0; + do_level_update_projectile(obj); + } + if (obj->player_xdist < 0) { + obj->facing_left = 1; + } else { + obj->facing_left = 0; + } + } + } else { + if (obj->anim_num == 16) { + obj->special_anim = 0; + } + } + } + if (obj->special_anim != 0) { + obj->direction_lr = 0; + } +} + // bird static void object_func_op8(struct object_t *obj) { if (triggers_get_tile_type(obj->xpos16 + 2 - obj->facing_left * 4, obj->ypos16) != 1) { @@ -163,10 +194,10 @@ static void object_func_op8(struct object_t *obj) { obj->xmaxvelocity = 48; obj->yacc = 0; obj->yvelocity = 0; - if (obj->player_ydist <= 100 && obj->player_xdist > -90 && obj->player_xdist < 90 && obj->direction_ud > 196 && obj->special_anim == 0) { + if (obj->player_ydist <= 100 && obj->player_xdist > -90 && obj->player_xdist < 90 && obj->direction_ud > -60 && obj->special_anim == 0) { obj->anim_num = 1; obj->unk42 = 1; - do_level_update_object38(obj); + do_level_update_projectile(obj); } } @@ -177,11 +208,11 @@ static void object_func_op10(struct object_t *obj) { obj->direction_lr = obj->moving_direction + 1; obj->xmaxvelocity = 32; if (obj->player_ydist <= 10 && obj->player_xdist > -(TILEMAP_SCREEN_W / 2) && obj->player_xdist < (TILEMAP_SCREEN_W / 2) && g_vars.objects[38].type == 100) { - if (obj->direction_ud > 226) { + if (obj->direction_ud > -30) { if (obj->special_anim == 0 || obj->anim_num == 16) { obj->anim_num = 1; obj->unk42 = 0; - do_level_update_object38(obj); + do_level_update_projectile(obj); } obj->facing_left = (obj->player_xdist < 0) ? 1 : 0; } @@ -436,6 +467,9 @@ void level_call_object_func(struct object_t *obj) { case 5: object_func_op5(obj); break; + case 7: + object_func_op7(obj); + break; case 8: object_func_op8(obj); break; diff --git a/resource.c b/resource.c index b27060e..0870be7 100644 --- a/resource.c +++ b/resource.c @@ -175,6 +175,7 @@ void load_avt(const char *filename, uint8_t *dst, int offset) { g_res.avt[offset + i] = ptr; ptr += 132; } + g_res.avt_count = count; } static const uint8_t *trigger_lookup_table1(uint8_t num) { @@ -259,6 +260,7 @@ void load_sqv(const char *filename, uint8_t *dst, int offset) { const uint8_t *ptr = dst; const int count = READ_LE_UINT16(ptr); ptr += 6; print_debug(DBG_RESOURCE, "sqv count %d", count); + assert(offset + count <= MAX_SPR_FRAMES); for (int i = 0; i < count; ++i) { g_res.spr_frames[offset + i] = ptr; const int h = READ_LE_UINT16(ptr - 4); @@ -268,6 +270,7 @@ void load_sqv(const char *filename, uint8_t *dst, int offset) { print_debug(DBG_RESOURCE, "sprite %d, dim %d,%d size %d", i, w, h, size); ptr += size; } + g_res.spr_count = offset + count; } void load_sql(const char *filename) { diff --git a/resource.h b/resource.h index 5aa4831..01970cc 100644 --- a/resource.h +++ b/resource.h @@ -25,6 +25,7 @@ struct trigger_t { #define MAX_SPR_FRAMES 200 #define MAX_TRIGGERS 256 #define SOUND_SIZE 29376 +#define SPRITES_COUNT 146 #define RESOURCE_FLAGS_DEMO (1 << 0) @@ -34,7 +35,9 @@ struct resource_data_t { uint8_t *spr_sqv; uint8_t *avt_sqv; uint8_t *tmp; + int avt_count; const uint8_t *avt[MAX_AVT]; + int spr_count; const uint8_t *spr_frames[MAX_SPR_FRAMES]; uint8_t palette[16 * 3]; struct trigger_t triggers[MAX_TRIGGERS]; diff --git a/screen.c b/screen.c index 2296a59..8f0f96a 100644 --- a/screen.c +++ b/screen.c @@ -5,49 +5,36 @@ #include "sys.h" #include "util.h" +#define MAX_SPRITESHEET_W 1024 +#define MAX_SPRITESHEET_H 512 + void screen_init() { - for (int i = 0; i < MAX_SPRITES; ++i) { - g_vars.sprites[i] = &g_vars.sprites_table[i]; - } - for (int i = 0; i < MAX_SPRITES - 1; ++i) { - g_vars.sprites[i]->next_sprite = g_vars.sprites[i] + 1; - } - g_vars.sprites[MAX_SPRITES - 1]->next_sprite = 0; } void screen_clear_sprites() { - g_vars.sprites_count = 0; + render_clear_sprites(); +} + +static void add_game_sprite(int x, int y, int frame, int xflip) { + const uint8_t *p = g_res.spr_frames[frame]; + const int h = READ_LE_UINT16(p - 4); + const int w = READ_LE_UINT16(p - 2); + int spr_type = RENDER_SPR_GAME; + if (frame >= SPRITES_COUNT) { + spr_type = RENDER_SPR_LEVEL; + frame -= SPRITES_COUNT; + } + render_add_sprite(spr_type, frame, x - w / 2, y - h, xflip); } void screen_add_sprite(int x, int y, int frame) { - assert(g_vars.sprites_count < MAX_SPRITES); - struct sprite_t *spr = g_vars.sprites[g_vars.sprites_count]; - spr->xpos = x; - spr->ypos = y; - spr->frame = g_res.spr_frames[frame]; - spr->xflip = 0; - spr->unk16 = 0; - spr->next_sprite = spr + 1; - ++g_vars.sprites_count; + add_game_sprite(x, y, frame, 0); } void screen_clear_last_sprite() { - assert(g_vars.sprites_count >= 0); - if (g_vars.sprites_count > 0) { - g_vars.sprites[g_vars.sprites_count - 1]->next_sprite = 0; - } } void screen_redraw_sprites() { - for (int i = 0; i < g_vars.sprites_count; ++i) { - struct sprite_t *spr = g_vars.sprites[i]; - const uint8_t *p = spr->frame; - const int h = READ_LE_UINT16(p - 4); - const int w = READ_LE_UINT16(p - 2); - const int y = spr->ypos - h; - const int x = spr->xpos - w / 2; - decode_ega_spr(p, w, w, h, g_res.vga, GAME_SCREEN_W, x, y, spr->xflip); - } } void fade_in_palette() { @@ -61,16 +48,12 @@ void fade_out_palette() { void screen_adjust_palette_color(int color, int b, int c) { g_res.palette[color * 3 + b] += c; screen_vsync(); - screen_set_palette(); + g_sys.set_screen_palette(g_res.palette, 16); } void screen_vsync() { } -void screen_set_palette() { - g_sys.set_screen_palette(g_res.palette, 16); -} - void screen_draw_frame(const uint8_t *frame, int a, int b, int c, int d) { const int h = READ_LE_UINT16(frame - 4); assert(a <= h); @@ -78,7 +61,7 @@ void screen_draw_frame(const uint8_t *frame, int a, int b, int c, int d) { assert(b <= w); const int x = c; const int y = d + a + 2; - decode_ega_spr(frame, w, b, h, g_res.vga, GAME_SCREEN_W, x, y, 0); + decode_ega_spr(frame, w, b, h, g_res.vga, GAME_SCREEN_W, x, y); } void screen_flip() { @@ -96,9 +79,9 @@ void screen_unk5() { } void screen_unk6() { - // _screen_draw_offset -= 12; + // g_vars.screen_draw_offset -= 12; // screen_do_transition2(); - // _screen_draw_offset += 12; + // g_vars.screen_draw_offset += 12; g_sys.update_screen(g_res.vga, 1); memset(g_res.vga, 0, GAME_SCREEN_W * GAME_SCREEN_H); } @@ -156,24 +139,90 @@ void screen_do_transition2() { void screen_draw_number(int num, int x, int y, int color) { extern const uint8_t font_data[]; y += TILEMAP_OFFSET_Y; - decode_ega_spr(font_data + (num / 10) * 32, 8, 8, 8, g_res.vga, GAME_SCREEN_W, x - 8, y, 0); - decode_ega_spr(font_data + (num % 10) * 32, 8, 8, 8, g_res.vga, GAME_SCREEN_W, x, y, 0); + decode_ega_spr(font_data + (num / 10) * 32, 8, 8, 8, g_res.vga, GAME_SCREEN_W, x - 8, y); + decode_ega_spr(font_data + (num % 10) * 32, 8, 8, 8, g_res.vga, GAME_SCREEN_W, x, y); } void screen_add_game_sprite1(int x, int y, int frame) { - screen_add_sprite(x, y + TILEMAP_OFFSET_Y, frame); - g_vars.sprites[g_vars.sprites_count - 1]->xflip = 0; + add_game_sprite(x, y + TILEMAP_OFFSET_Y, frame, 0); } void screen_add_game_sprite2(int x, int y, int frame) { - screen_add_sprite(x, y + TILEMAP_OFFSET_Y, frame); - g_vars.sprites[g_vars.sprites_count - 1]->xflip = 1; + add_game_sprite(x, y + TILEMAP_OFFSET_Y, frame, 1); } void screen_add_game_sprite3(int x, int y, int frame, int blinking_counter) { - print_warning("screen_add_game_sprite3"); +// print_warning("screen_add_game_sprite3"); } void screen_add_game_sprite4(int x, int y, int frame, int blinking_counter) { - print_warning("screen_add_game_sprite4"); +// print_warning("screen_add_game_sprite4"); +} + +static void decode_graphics(int spr_type, int start, int end) { + struct sys_rect_t r[MAX_SPR_FRAMES]; + 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; + for (int i = start; i < end; ++i) { + const uint8_t *ptr = g_res.spr_frames[i]; + 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) { + max_w = current_x; + } + current_x = 0; + max_h = h; + decode_ega_spr(ptr, w, w, h, data, MAX_SPRITESHEET_W, current_x, current_y); + r[j].x = current_x; + r[j].y = current_y; + } else { + decode_ega_spr(ptr, w, w, h, data, MAX_SPRITESHEET_W, current_x, current_y); + r[j].x = current_x; + r[j].y = current_y; + current_x += w; + if (h > max_h) { + max_h = h; + } + } + r[j].w = w; + r[j].h = h; + } + assert(max_w <= MAX_SPRITESHEET_W); + assert(current_y + max_h <= MAX_SPRITESHEET_H); + render_unload_sprites(spr_type); + render_load_sprites(spr_type, end - start, r, data, MAX_SPRITESHEET_W, current_y + max_h); + free(data); + } +} + +void screen_load_graphics() { + if (g_res.spr_count <= SPRITES_COUNT) { + decode_graphics(RENDER_SPR_GAME, 0, SPRITES_COUNT); + } else { + decode_graphics(RENDER_SPR_LEVEL, SPRITES_COUNT, g_res.spr_count); + struct sys_rect_t r[MAX_SPR_FRAMES]; + static const int FG_TILE_W = 16; + static const int FG_TILE_H = 16; + uint8_t *data = (uint8_t *)malloc(g_res.avt_count * FG_TILE_W * FG_TILE_H); + if (data) { + const int pitch = g_res.avt_count * FG_TILE_W; + for (int i = 0; i < g_res.avt_count; ++i) { + decode_ega_spr(g_res.avt[i], FG_TILE_W, FG_TILE_W, FG_TILE_H, data, pitch, i * FG_TILE_W, 0); + r[i].x = i * FG_TILE_W; + r[i].y = 0; + r[i].w = FG_TILE_W; + r[i].h = FG_TILE_H; + } + render_unload_sprites(RENDER_SPR_FG); + render_load_sprites(RENDER_SPR_FG, g_res.avt_count, r, data, g_res.avt_count * FG_TILE_W, FG_TILE_H); + free(data); + } + } } diff --git a/sound.c b/sound.c index 13bb8a8..a934f3a 100644 --- a/sound.c +++ b/sound.c @@ -32,7 +32,7 @@ static const struct { { 0x36a6, 0x0574 } }; -// Amiga, unexotica +// Amiga, ExoticA static const char *_modules[] = { "ALMOST", "mod.almost", "GUNN", "mod.bluesgunnbest", @@ -47,7 +47,7 @@ struct mixerchannel_t { uint32_t size; }; -static int _rate = 22050; +static const int _rate = SYS_AUDIO_FREQ; static struct mixerchannel_t _channel; static ModPlugFile *_mpf; @@ -74,7 +74,6 @@ static void mix(void *param, uint8_t *buf, int len) { } void sound_init(int rate) { - _rate = rate; ModPlug_Settings mp_settings; memset(&mp_settings, 0, sizeof(mp_settings)); ModPlug_GetSettings(&mp_settings); @@ -109,17 +108,60 @@ void play_music(int num) { ModPlug_Unload(_mpf); _mpf = 0; } - const char *filename = _modules[num * 2 + 1]; - if (fio_exists(filename)) { + const char *filename = _modules[num * 2]; + if (fio_exists(filename)) { // Amiga int slot = fio_open(filename, 1); int size = fio_size(slot); uint8_t *buf = (uint8_t *)malloc(size); if (buf) { fio_read(slot, buf, size); - _mpf = ModPlug_Load(buf, size); + + // append samples to the end of the buffer + static const int SONG_INFO_LEN = 20; + static const int NUM_SAMPLES = 8; + + int samples_size = 0; + int offset = SONG_INFO_LEN; + for (int i = 0; i < NUM_SAMPLES; ++i, offset += 30) { + const char *sample_name = (const char *)&buf[offset]; + if (sample_name[0]) { + samples_size += READ_BE_UINT16(&buf[offset + 22]) * 2; + } + } + + buf = (uint8_t *)realloc(buf, size + samples_size); + if (buf) { + offset = SONG_INFO_LEN; + for (int i = 0; i < NUM_SAMPLES; ++i, offset += 30) { + const char *sample_name = (const char *)&buf[offset]; + const int sample_size = READ_BE_UINT16(&buf[offset + 22]) * 2; + if (sample_name[0]) { + int sample_slot = fio_open(sample_name, 0); + if (!(sample_slot < 0)) { + fio_read(sample_slot, buf + size, sample_size); + size += sample_size; + } + fio_close(sample_slot); + } + } + _mpf = ModPlug_Load(buf, size); + } free(buf); } fio_close(slot); + } else { // ExoticA + filename = _modules[num * 2 + 1]; + if (fio_exists(filename)) { + int slot = fio_open(filename, 1); + int size = fio_size(slot); + uint8_t *buf = (uint8_t *)malloc(size); + if (buf) { + fio_read(slot, buf, size); + _mpf = ModPlug_Load(buf, size); + free(buf); + } + fio_close(slot); + } } g_sys.unlock_audio(); } diff --git a/sys.h b/sys.h index f092de4..a567581 100644 --- a/sys.h +++ b/sys.h @@ -23,6 +23,11 @@ struct input_t { typedef void (*sys_audio_cb)(void *, uint8_t *data, int len); +struct sys_rect_t { + int x, y; + int w, h; +}; + struct sys_t { struct input_t input; int (*init)(); @@ -45,4 +50,16 @@ struct sys_t { extern struct sys_t g_sys; +#define RENDER_SPR_GAME 0 /* player sprites */ +#define RENDER_SPR_LEVEL 1 /* level sprites */ +#define RENDER_SPR_FONT 2 /* font (digits) */ +#define RENDER_SPR_FG 3 /* foreground tiles */ + +extern void render_load_sprites(int spr_type, int count, const struct sys_rect_t *r, const uint8_t *data, int w, int h); +extern void render_unload_sprites(int spr_type); +extern void render_add_sprite(int spr_type, int frame, int x, int y, int xflip); +extern void render_clear_sprites(); +extern void render_draw_tilemap(int x, int y); +extern void render_draw_image(); + #endif /* SYS_H__ */ diff --git a/sys_sdl2.c b/sys_sdl2.c index ef5bfea..533220f 100644 --- a/sys_sdl2.c +++ b/sys_sdl2.c @@ -8,6 +8,24 @@ static const int SCALE = 2; static const int FADE_STEPS = 16; +struct spritesheet_t { + int count; + SDL_Rect *r; + SDL_Texture *texture; +}; + +static struct spritesheet_t _spritesheets[4]; + +struct sprite_t { + int sheet; + int num; + int x, y; + bool xflip; +}; + +static struct sprite_t _sprites[128]; +static int _sprites_count; + static int _screen_w; static int _screen_h; static SDL_Window *_window; @@ -62,7 +80,7 @@ static void sdl2_set_screen_size(int w, int h, const char *caption) { SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "0"); /* nearest pixel sampling */ const int window_w = w * SCALE; const int window_h = h * SCALE; // * 4 / 3; - _window = SDL_CreateWindow(caption, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, window_w, window_h, 0); + _window = SDL_CreateWindow(caption, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, window_w, window_h, SDL_WINDOW_RESIZABLE); _renderer = SDL_CreateRenderer(_window, -1, 0); print_debug(DBG_SYSTEM, "set_screen_size %d,%d", _screen_w, _screen_h); _screen_buffer = (uint32_t *)calloc(_screen_w * _screen_h, sizeof(uint32_t)); @@ -97,7 +115,7 @@ static void sdl2_set_copper_bars(const uint16_t *data) { _copper_color = (data[0] - 0x180) / 2; const uint16_t *src = data + 1; uint32_t *dst = _copper_palette; - for (int i = 0; i < 16; ++i) { + for (int i = 0; i < COPPER_BARS_H / 5; ++i) { const int j = i + 1; *dst++ = convert_amiga_color(src[j]); *dst++ = convert_amiga_color(src[i]); @@ -145,14 +163,17 @@ static void sdl2_fade_out_palette() { static void sdl2_update_screen(const uint8_t *p, int present) { if (_copper_color != -1) { for (int j = 0; j < _screen_h; ++j) { - for (int i = 0; i < _screen_w; ++i) { - if (*p == _copper_color && j / 2 < COPPER_BARS_H) { - _screen_buffer[j * _screen_w + i] = _copper_palette[j / 2]; - } else { - _screen_buffer[j * _screen_w + i] = _screen_palette[*p]; + if (j / 2 < COPPER_BARS_H) { + const uint32_t line_color = _copper_palette[j / 2]; + for (int i = 0; i < _screen_w; ++i) { + _screen_buffer[j * _screen_w + i] = (p[i] == _copper_color) ? line_color : _screen_palette[p[i]]; + } + } else { + for (int i = 0; i < _screen_w; ++i) { + _screen_buffer[j * _screen_w + i] = _screen_palette[p[i]]; } - ++p; } + p += _screen_w; } } else { for (int i = 0; i < _screen_w * _screen_h; ++i) { @@ -163,6 +184,23 @@ static void sdl2_update_screen(const uint8_t *p, int present) { if (present) { SDL_RenderClear(_renderer); SDL_RenderCopy(_renderer, _texture, 0, 0); + + // sprites + for (int i = 0; i < _sprites_count; ++i) { + struct sprite_t *spr = &_sprites[i]; + struct spritesheet_t *sheet = &_spritesheets[spr->sheet]; + SDL_Rect r; + r.x = spr->x * SCALE; + r.y = spr->y * SCALE; + r.w = sheet->r[spr->num].w * SCALE; + r.h = sheet->r[spr->num].h * SCALE; + if (!spr->xflip) { + SDL_RenderCopy(_renderer, sheet->texture, &sheet->r[spr->num], &r); + } else { + SDL_RenderCopyEx(_renderer, sheet->texture, &sheet->r[spr->num], &r, 0., 0, SDL_FLIP_HORIZONTAL); + } + } + SDL_RenderPresent(_renderer); } } @@ -283,3 +321,53 @@ struct sys_t g_sys = { .lock_audio = sdl2_lock_audio, .unlock_audio = sdl2_unlock_audio, }; + +void render_load_sprites(int spr_type, int count, const struct sys_rect_t *r, const uint8_t *data, int w, int h) { + assert(spr_type < ARRAYSIZE(_spritesheets)); + struct spritesheet_t *spr_sheet = &_spritesheets[spr_type]; + spr_sheet->count = count; + spr_sheet->r = (SDL_Rect *)malloc(count * sizeof(SDL_Rect)); + for (int i = 0; i < count; ++i) { + SDL_Rect *rect = &spr_sheet->r[i]; + rect->x = r[i].x; + rect->y = r[i].y; + rect->w = r[i].w; + rect->h = r[i].h; + } + uint32_t *argb = (uint32_t *)malloc(w * h * sizeof(uint32_t)); + if (!argb) { + print_warning("Failed to allocate RGB buffer for sprites type %d", spr_type); + } else { + spr_sheet->texture = SDL_CreateTexture(_renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STATIC, w, h); + SDL_SetTextureBlendMode(spr_sheet->texture, SDL_BLENDMODE_BLEND); + for (int i = 0; i < w * h; ++i) { + argb[i] = (data[i] == 0) ? 0 : (0xFF000000 | _screen_palette[data[i]]); + } + SDL_UpdateTexture(spr_sheet->texture, 0, argb, w * sizeof(uint32_t)); + free(argb); + } +} + +void render_unload_sprites(int spr_type) { + struct spritesheet_t *spr_sheet = &_spritesheets[spr_type]; + free(spr_sheet->r); + if (spr_sheet->texture) { + SDL_DestroyTexture(spr_sheet->texture); + } + memset(spr_sheet, 0, sizeof(struct spritesheet_t)); +} + +void render_add_sprite(int spr_type, int frame, int x, int y, int xflip) { + assert(_sprites_count < ARRAYSIZE(_sprites)); + struct sprite_t *spr = &_sprites[_sprites_count]; + spr->sheet = spr_type; + spr->num = frame; + spr->x = x; + spr->y = y; + spr->xflip = xflip; + ++_sprites_count; +} + +void render_clear_sprites() { + _sprites_count = 0; +}