diff --git a/README.md b/README.md index 2aa0044..e8babf1 100644 --- a/README.md +++ b/README.md @@ -5,13 +5,13 @@ This is a rewrite of the [Blues Brothers](https://www.mobygames.com/game/blues-b ![Screenshot1](blues1.png) ![Screenshot2](bbja2.png) -There is also early support for [Prehistork 2](https://www.mobygames.com/game/prehistorik-2). +There is also early support for [Prehistorik 2](https://www.mobygames.com/game/prehistorik-2). ## Requirements ### Blues Brothers -The game data files of the DOS or Amiga version are required. +The data files of the Amiga or DOS version, full game or [demo](https://archive.org/details/TheBluesBrothers_1020), are required. ``` *.BIN, *.CK1, *.CK2, *.SQL, *.SQV, *.SQZ @@ -21,7 +21,7 @@ For sounds and music, the Amiga version files or the [ExoticA](https://www.exoti ### Jukebox Adventure -The game data files of the DOS version are required. +The data files of the DOS version are required. ``` *.EAT, *.MOD @@ -29,7 +29,7 @@ The game data files of the DOS version are required. ### Prehistorik 2 -The game data files of the DOS version are required. +The data files of the DOS version, full game or [demo](http://cd.textfiles.com/ccbcurrsh1/demos/pre2.zip), are required. ``` *.SQZ, *.TRK diff --git a/bb/game.h b/bb/game.h index bab0c6f..a1807b8 100644 --- a/bb/game.h +++ b/bb/game.h @@ -87,16 +87,16 @@ struct object_t { int16_t unk2E; int8_t unk2F; uint8_t op; - uint8_t grab_state; // 1:carry_crate + uint8_t grab_state; /* 1:carry_crate */ uint8_t grab_type; - uint8_t special_anim; // 2:flying + uint8_t special_anim; /* 2:flying */ // uint8_t unk35; int16_t player_xdist; int16_t player_ydist; uint8_t sprite3_counter; uint8_t visible_flag; - uint8_t moving_direction; // 0:right 1:left - uint8_t unk3D; // 1:umbrella 2:balloon + uint8_t moving_direction; /* 0:right 1:left */ + uint8_t unk3D; /* 1:umbrella 2:balloon */ uint8_t carry_crate_flag; // int16_t unk3F; // uint8_t unk41; @@ -105,12 +105,12 @@ struct object_t { uint8_t tile1_flags; uint8_t tile2_flags; int16_t tile012_xpos; - int8_t elevator_direction; // -1,1 + int8_t elevator_direction; /* -1,1 */ const uint8_t *trigger3; uint8_t trigger3_num; // int16_t unk4E; int16_t unk50; - uint8_t data51; // health for obj39/40, horizontal direction for other objects + uint8_t data51; /* health for obj39/40, horizontal direction for other objects */ uint8_t scrolling_lock_flag; uint8_t unk54; uint8_t unk55; @@ -121,10 +121,10 @@ struct object_t { // 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 data5F; /* music instrument number for obj39/40, counter for other objects */ uint8_t unk60; uint8_t lifes_count; - uint8_t level_complete_flag; // set if music instrument was found + uint8_t level_complete_flag; /* set if music instrument was found */ uint8_t restart_level_flag; }; diff --git a/ja/game.h b/ja/game.h index 2e7cf3b..4e748d0 100644 --- a/ja/game.h +++ b/ja/game.h @@ -18,17 +18,17 @@ extern struct options_t g_options; #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_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 +#define SOUND_14 14 /* player lost life */ +#define SOUND_15 15 /* running out time */ struct object_t { int16_t x_pos; @@ -66,20 +66,20 @@ enum { #define object_blinking_counter(obj) (obj)->data[6].b[1] // 0x13, shield, jump -// star +/* star */ #define object2_spr_count(obj) (obj)->data[0].b[0] #define object2_spr_tick(obj) (obj)->data[0].b[1] -// vinyl +/* 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 +/* crate */ #define object64_counter(obj) (obj)->data[1].w #define object64_yvelocity(obj) (obj)->data[2].w -// monster +/* 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 @@ -111,16 +111,18 @@ struct player_t { #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) +/* + offset count + 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; @@ -128,7 +130,7 @@ struct vars_t { 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 + 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]; int16_t player_xscroll, player_map_offset; @@ -147,7 +149,7 @@ struct vars_t { 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_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; diff --git a/p2/game.c b/p2/game.c index e622518..f85cac5 100644 --- a/p2/game.c +++ b/p2/game.c @@ -78,11 +78,28 @@ static void do_credits() { g_sys.update_screen(g_res.vga, 1); } +static void update_screen_img(const uint8_t *src) { + const int size = GAME_SCREEN_W * GAME_SCREEN_H; + if (size < 64000) { + return; + } else if (size == 64000) { + g_sys.update_screen(src, 0); + } else { + memset(g_res.vga, 0, size); + const int y_offs = (GAME_SCREEN_H - 200) / 2; + const int x_offs = (GAME_SCREEN_W - 320) / 2; + for (int y = 0; y < 200; ++y) { + memcpy(g_res.vga + (y_offs + y) * GAME_SCREEN_W + x_offs, src + y * 320, 320); + } + g_sys.update_screen(g_res.vga, 0); + } +} + static void do_titus_screen() { uint8_t *data = load_file("TITUS.SQZ"); if (data) { g_sys.set_screen_palette(data, 0, 256, 6); - g_sys.update_screen(data + 768, 0); + update_screen_img(data + 768); fade_in_palette(); wait_input(70); fade_out_palette(); @@ -94,7 +111,7 @@ static void do_present_screen() { uint8_t *data = load_file("PRESENT.SQZ"); if (data) { g_sys.set_screen_palette(data, 0, 256, 6); - g_sys.update_screen(data + 768, 0); + update_screen_img(data + 768); fade_in_palette(); free(data); } @@ -112,7 +129,7 @@ static void do_menu() { uint8_t *data = load_file("MENU.SQZ"); if (data) { g_sys.set_screen_palette(data, 0, 256, 6); - g_sys.update_screen(data + 768, 0); + update_screen_img(data + 768); fade_in_palette(); free(data); memset(g_vars.input.keystate, 0, sizeof(g_vars.input.keystate)); diff --git a/p2/game.h b/p2/game.h index a906464..424c1af 100644 --- a/p2/game.h +++ b/p2/game.h @@ -24,33 +24,33 @@ struct club_anim_t { }; struct player_t { - int16_t hdir; /* left:-1 right:1 */ // 0x9 - uint8_t current_anim_num; // 0xB - const uint8_t *anim; // 0xC - int16_t y_velocity; // 0xE - uint8_t special_anim_num; /* idle, exhausted */ // 0x10 + int16_t hdir; /* left:-1 right:1 */ + uint8_t current_anim_num; + const uint8_t *anim; + int16_t y_velocity; + uint8_t special_anim_num; /* idle, exhausted */ }; struct club_projectile_t { - const uint8_t *anim; // 0xC - int16_t y_velocity; // 0xE + const uint8_t *anim; + int16_t y_velocity; }; struct monster_t { uint8_t unk5; - void *ref; // 0x6 - int16_t x_velocity; // 0x8 - int16_t y_velocity; // 0xA - const uint8_t *anim; // 0xC - uint8_t state; // 0xE - uint8_t energy; // 0xF - uint8_t unk10; // 0x10 + void *ref; + int16_t x_velocity; + int16_t y_velocity; + const uint8_t *anim; + uint8_t state; + uint8_t energy; + uint8_t unk10; /* score */ }; struct thing_t { - void *ref; // 0x9 - int16_t counter; // 0xC - int16_t unkE; // 0xE + void *ref; + int16_t counter; + int16_t unkE; }; struct object_t { @@ -58,34 +58,36 @@ struct object_t { uint16_t spr_num; int16_t x_velocity; uint8_t x_friction; - union { // 0x9 - 0x10 + union { struct player_t p; /* objects[1] */ struct club_projectile_t c; /* objects[2..5] */ struct monster_t m; /* objects[11..22] */ struct thing_t t; } data; - uint8_t hit_counter; // 0x11 -}; // sizeof == 18 + uint8_t hit_counter; +}; #define MONSTERS_COUNT 12 #define OBJECTS_COUNT 108 -// offset count -// 0 1 : club -// 1 1 : player -// 2 4 : axe -// 6 5 : club hitting decor frames -// 11 12 : monsters -// 23 32 : secret bonuses -// 55 20 : items -// 75 16 : bonus scores -// 91 7 : decor +/* + offset count + 0 1 : club + 1 1 : player + 2 4 : axe + 6 5 : club hitting decor frames + 11 12 : monsters + 23 32 : secret bonuses + 55 20 : items + 75 16 : bonus scores + 91 7 : decor +*/ struct rotation_t { uint16_t x_pos; uint16_t y_pos; uint8_t index_tbl; /* cos_/sin_tbl */ uint8_t radius; -}; // sizeof == 6 +}; struct collision_t { uint16_t x_pos; @@ -94,7 +96,7 @@ struct collision_t { uint8_t unk5; uint8_t unk6; uint8_t unk7; -}; // sizeof == 8 +}; struct vars_t { uint32_t starttime; @@ -140,6 +142,8 @@ struct vars_t { uint8_t player_nojump_counter; uint8_t player_jumping_counter; uint8_t player_action_counter; + int16_t player_hit_monster_counter; + uint8_t player_jump_monster_flag; uint8_t player_club_type; uint8_t player_club_power; uint8_t player_club_powerup_duration; @@ -186,6 +190,8 @@ struct vars_t { struct { struct object_t *current_object; + uint8_t type10_dist; + uint8_t hit_mask; } monster; struct { int16_t x_pos, y_pos; @@ -243,6 +249,10 @@ extern void game_main(); /* level.c */ extern void do_level(); +/* monsters.c */ +extern void monster_change_next_anim(struct object_t *obj); +extern void monster_change_prev_anim(struct object_t *obj); + /* screen.c */ extern void video_draw_string(int offset, int hspace, const char *s); extern void video_clear(); diff --git a/p2/level.c b/p2/level.c index dae13da..450004f 100644 --- a/p2/level.c +++ b/p2/level.c @@ -258,7 +258,16 @@ static void level_update_tilemap() { return; } } - memcpy(g_res.vga, g_res.background, 320 * 200); + if (GAME_SCREEN_W * GAME_SCREEN_H == 64000) { + memcpy(g_res.vga, g_res.background, 320 * 200); + } else { + memset(g_res.vga, 0, GAME_SCREEN_W * GAME_SCREEN_H); + for (int y = 0; y < MIN(200, GAME_SCREEN_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)); + } + } + } g_vars.tile_attr2_flags = 0; g_vars.animated_tiles_flag = 0; uint16_t offset = (g_vars.tilemap_y << 8) | g_vars.tilemap_x; @@ -896,8 +905,8 @@ static void level_reset_objects() { obj->spr_num = 0xFFFF; obj->hit_counter = 0; } - g_vars.objects_tbl[1].x_pos = (g_options.start_xpos16 < 0) ? g_res.level.start_x_pos : g_options.start_xpos16; - g_vars.objects_tbl[1].y_pos = (g_options.start_ypos16 < 0) ? g_res.level.start_y_pos : g_options.start_ypos16; + g_vars.objects_tbl[1].x_pos = (g_options.start_xpos16 < 0) ? g_res.level.start_x_pos : (g_options.start_xpos16 << 4); + g_vars.objects_tbl[1].y_pos = (g_options.start_ypos16 < 0) ? g_res.level.start_y_pos : (g_options.start_ypos16 << 4); } static void level_reset() { @@ -1059,6 +1068,14 @@ static bool level_objects_collide(const struct object_t *si, const struct object return (a + b > d); } +static void level_monster_update_anim(struct object_t *obj) { + const uint8_t *p = obj->data.m.anim; + do { + p += 2; + } while (READ_LE_UINT16(p) != 0x7D00); + obj->data.m.anim = p + 2; +} + static void level_monster_die(struct object_t *obj, struct level_monster_t *m) { const int num = m->unk8 + 74; static const uint8_t data[] = { 1, 2, 3, 4, 6, 8 }; @@ -1082,6 +1099,19 @@ static void level_monster_die(struct object_t *obj, struct level_monster_t *m) { g_vars.current_bonus.spr_num = 0x2046; level_add_object23_bonus(48, -128, 6); } else { + level_monster_update_anim(obj); + uint8_t al = m->flags & ~0x37; + if (al != 0x88) { + m->flags &= ~8; + } + int dy = MIN(g_vars.player_club_power, 25); + dy = (-dy) << 3; + obj->data.m.y_velocity = dy; + int dx = dy >> 1; + if ((obj->data.m.unk5 & 0x80) == 0) { + dx = -dx; + } + obj->data.m.x_velocity = dx; } } @@ -1280,6 +1310,76 @@ static void level_update_objects_axe() { static void level_update_monsters_state() { } +static int level_get_tile_monster_offset(uint8_t tile_num, struct object_t *obj) { + const uint8_t attr = g_res.level.tile_attributes1[tile_num]; + if ((attr & 0x30) == 0) { + return attr; + } + int x = (obj->x_pos & 15) / 3; + if (attr & 0x10) { + x = (attr & 15) + x; + } else { + x = (attr & 15) - x; + } + return x; +} + +static void level_update_monster_pos(struct object_t *obj, struct level_monster_t *m) { + const uint16_t pos = ((obj->y_pos >> 4) << 8) | (obj->x_pos >> 4); + uint8_t dl = level_get_tile(pos); + uint8_t dh = level_get_tile(pos - 0x100); + int x_vel = obj->data.m.x_velocity; + if (x_vel != 0 && x_vel <= 1) { + x_vel = -x_vel; + } + uint8_t al = level_get_tile(pos + x_vel - 0x100); + al = g_res.level.tile_attributes1[al]; + if ((m->flags & 0x40) != 0 && (m->flags & 0x80) != 0) { + if (al != 0) { + obj->x_pos -= obj->data.m.x_velocity >> 4; + } else { + monster_change_prev_anim(obj); + obj->data.m.y_velocity = 0; + m->flags &= ~0x80; + obj->y_pos -= 16; + } + } else { + if (al != 0) { + if (m->flags & 0x40) { + monster_change_next_anim(obj); + m->flags |= 0x80; + obj->data.m.y_velocity &= ~15; + obj->x_pos += obj->data.m.x_velocity >> 2; + return; + } + obj->data.m.x_velocity = -obj->data.m.x_velocity; + obj->x_pos += obj->data.m.x_velocity >> 4; + } + obj->y_pos -= 16; + SWAP(dl, dh); + al = g_res.level.tile_attributes1[dl]; + if (al == 0) { + obj->y_pos += 16; + al = g_res.level.tile_attributes1[dh]; + if (al == 0) { + if (obj->data.m.y_velocity < 256) { + obj->data.m.y_velocity += 16; + } + return; + } + } + obj->y_pos &= ~15; + obj->y_pos += level_get_tile_monster_offset(dl, obj); + if ((m->flags & 0x20) == 0) { + int dy = (-obj->data.m.y_velocity) >> 1; + if (abs(dy) <= 32) { + dy = 0; + } + obj->data.m.y_velocity = dy; + } + } +} + extern void monster_func1(int type, struct object_t *obj); /* update */ extern bool monster_func2(int type, struct level_monster_t *m); /* init */ @@ -1298,12 +1398,14 @@ static void level_update_objects_monsters() { if (obj->spr_num == 0xFFFF) { continue; } + print_debug(DBG_GAME, "monster #%d pos %d,%d spr %d", i, obj->x_pos, obj->y_pos, obj->spr_num); obj->y_pos += obj->data.m.y_velocity >> 4; if (obj->data.m.x_velocity != -1) { obj->x_pos += obj->data.m.x_velocity >> 4; } struct level_monster_t *m = obj->data.m.ref; if (m->flags & 8) { + level_update_monster_pos(obj, m); } const uint8_t *p = obj->data.m.anim; int16_t num; @@ -1315,6 +1417,7 @@ static void level_update_objects_monsters() { p += num; } uint16_t dx = 305 + (num & 0x1FFF); + g_vars.monster.hit_mask = ((num >> 8) & 0xE0) | g_vars.player_monsters_unk_counter; if (g_vars.player_monsters_unk_counter == 0) { obj->data.m.anim = p + 2; } else { @@ -2030,6 +2133,13 @@ static void level_update_player_anim_5(uint8_t al) { level_update_player_club_power(); } +static void level_update_player_anim_8(uint8_t al) { + level_update_screen_x_velocity(); + level_update_player_x_velocity(); + const uint8_t *p = level_update_player_anim2_num(al, al * 2); + level_update_object_anim(p); +} + static void level_update_player_decor() { const int y_pos = (g_vars.objects_tbl[1].y_pos >> 4) - 1; const int spr_num = g_vars.objects_tbl[1].spr_num & 0x1FFF; @@ -2216,6 +2326,9 @@ static void level_update_player() { case 5: level_update_player_anim_5(al); break; + case 8: + level_update_player_anim_8(al); + break; default: print_warning("Unhandled anim_lut %d mask %d", al, mask); break; @@ -2271,6 +2384,9 @@ static void level_update_player_collision() { if (obj->spr_num == 0xFFFF || (obj->spr_num & 0x2000) == 0) { continue; } + if (g_options.cheats & CHEATS_NO_HIT) { + continue; + } struct level_monster_t *m = obj->data.m.ref; if (m->flags & 0x10) { continue; @@ -2281,7 +2397,64 @@ static void level_update_player_collision() { if (!level_objects_collide(obj_player, obj)) { continue; } - print_warning("Unhandled level_update_player_collision 6A43"); + if (g_vars.player_hit_monster_counter != 0) { + level_monster_die(obj, m); + continue; + } + if (!g_vars.player_jump_monster_flag && g_vars.objects_tbl[1].data.p.y_velocity < 0) { + m->flags &= ~1; + g_vars.objects_tbl[1].hit_counter = 44; + g_vars.player_anim_0x40_flag = 0; + g_vars.objects_tbl[1].data.p.y_velocity = -128; + g_vars.objects_tbl[1].x_velocity = -(g_vars.objects_tbl[1].x_velocity << 2); + g_vars.player_gravity_flag = 0; + if (g_vars.player_flying_flag != 0) { + g_vars.player_flying_flag = 0; + } else { + --g_vars.player_energy; + if (g_vars.player_energy < 0) { + level_player_die(); + } + } + return; + } + if (g_vars.player_gravity_flag == 0) { + int num = obj->data.m.unk10 >> 2; + if (num != 11) { + obj->data.m.unk10 += 4; + ++num; + if ((num & 1) == 0) { + static const uint16_t data[] = { 0xFF46, 0xE0, 0xE1, 0x12D, 0x12E, 0x12F }; + + level_add_object75_score(obj_player, data[num >> 1]); + } + } + g_vars.objects_tbl[1].data.p.y_velocity = (g_vars.input.key_up != 0) ? -224 : -64; + g_vars.player_jumping_counter = 0; + } else if (g_vars.objects_tbl[1].data.p.y_velocity <= 32) { + g_vars.objects_tbl[1].data.p.y_velocity = -96; + g_vars.player_jumping_counter = 0; + } else { + int num = (obj->data.m.unk10) & 3; + level_add_object75_score(obj_player, 82 + (num << 1)); + obj->spr_num |= 0x4000; + if (num == 2) { + obj->data.m.state = 0xFF; + level_monster_update_anim(obj); + m->flags &= ~8; + obj->data.m.y_velocity = -200; + int delta = abs(g_vars.objects_tbl[1].data.p.y_velocity); + if (obj->x_pos <= g_vars.objects_tbl[1].x_pos) { + delta = -delta; + } + obj->data.m.x_velocity = delta * 3; + } else { + ++obj->data.m.unk10; + g_vars.objects_tbl[1].data.p.y_velocity = -96; + g_vars.player_jumping_counter = 0; + } + } + return; } } /* bonuses */ @@ -2430,10 +2603,14 @@ static void level_update_player_collision() { if (obj->spr_num == 0xFFFF) { continue; } + struct level_monster_t *m = obj->data.m.ref; + if (m->flags & 0x10) { + continue; + } if ((obj->spr_num & 0x2000) == 0) { continue; } - print_warning("Unhandled bonus num 169"); + level_monster_die(obj, m); } g_vars.shake_screen_counter = 9; level_clear_item(obj); @@ -3114,6 +3291,7 @@ void do_level() { level_clear_panel(); level_draw_panel(); level_sync(); + g_vars.monster.type10_dist = 0; random_reset(); while (!g_sys.input.quit) { level_update_objects_hit_animation(); diff --git a/p2/monsters.c b/p2/monsters.c index fe4968b..9383a96 100644 --- a/p2/monsters.c +++ b/p2/monsters.c @@ -5,6 +5,15 @@ #include "resource.h" #include "util.h" +static void monster_reset(struct object_t *obj, struct level_monster_t *m) { + m->current_tick = 0; + obj->spr_num = 0xFFFF; + m->flags &= ~4; + if ((m->flags & 2) == 0) { + m->spr_num = 0xFFFF; + } +} + static void monster_func1_helper(struct object_t *obj, int16_t x_pos, int16_t y_pos) { struct level_monster_t *m = obj->data.m.ref; if (obj->data.m.state == 0xFF) { @@ -25,12 +34,7 @@ static void monster_func1_helper(struct object_t *obj, int16_t x_pos, int16_t y_ m->flags &= ~4; m->current_tick = 0; } else { - m->current_tick = 0; - obj->spr_num = 0xFFFF; - m->flags &= ~4; - if ((m->flags & 2) == 0) { - m->spr_num = 0xFFFF; - } + monster_reset(obj, m); } } @@ -41,7 +45,7 @@ static bool monster_next_tick(struct level_monster_t *m) { return ((m->current_tick >> 2) < m->total_ticks); } -static void monster_change_next_anim(struct object_t *obj) { +void monster_change_next_anim(struct object_t *obj) { const uint8_t *p = obj->data.m.anim; while ((int16_t)READ_LE_UINT16(p) >= 0) { p += 2; @@ -49,7 +53,7 @@ static void monster_change_next_anim(struct object_t *obj) { obj->data.m.anim = p + 2; } -static void monster_change_prev_anim(struct object_t *obj) { +void monster_change_prev_anim(struct object_t *obj) { const uint8_t *p = obj->data.m.anim; do { p -= 2; @@ -67,7 +71,7 @@ static struct rotation_t *find_rotation() { return 0; } -static void monster_rotate_platform(struct level_monster_t *m, int index, int step) { +static void monster_rotate_tiles(struct level_monster_t *m, int index, int step) { step >>= 2; uint8_t radius = step; for (int i = 0; i < 3; ++i) { @@ -83,9 +87,9 @@ static void monster_rotate_platform(struct level_monster_t *m, int index, int st } static void monster_rotate_pos(struct object_t *obj, struct level_monster_t *m) { - obj->x_pos = m->x_pos + ((m->type4.unkD * (cos_tbl[m->type4.angle] >> 2)) >> 4); - obj->y_pos = m->y_pos + ((m->type4.unkD * (sin_tbl[m->type4.angle] >> 2)) >> 4); - monster_rotate_platform(m, m->type4.angle, m->type4.unkD); + obj->x_pos = m->x_pos + ((m->type4.radius * (((int8_t)cos_tbl[m->type4.angle]) >> 2)) >> 4); + obj->y_pos = m->y_pos + ((m->type4.radius * (((int8_t)sin_tbl[m->type4.angle]) >> 2)) >> 4); + monster_rotate_tiles(m, m->type4.angle, m->type4.radius); } static void monster_update_y_velocity(struct object_t *obj, struct level_monster_t *m) { @@ -94,12 +98,7 @@ static void monster_update_y_velocity(struct object_t *obj, struct level_monster obj->data.m.y_velocity += 15; } } else { - m->current_tick = 0; - obj->spr_num = 0xFFFF; - m->flags &= ~4; - if ((m->flags & 2) == 0) { - m->spr_num = 0xFFFF; - } + monster_reset(obj, m); } } @@ -108,7 +107,7 @@ static void monster_func1_type2(struct object_t *obj) { struct level_monster_t *m = obj->data.m.ref; const uint8_t state = obj->data.m.state; if (state < 2) { - monster_rotate_platform(m, 0, obj->y_pos - m->y_pos); + monster_rotate_tiles(m, 0, obj->y_pos - m->y_pos); if (state == 0) { obj->data.m.y_velocity = m->type2.unkE << 4; const int dy = obj->y_pos - m->y_pos; @@ -118,7 +117,7 @@ static void monster_func1_type2(struct object_t *obj) { obj->data.m.state = 1; monster_change_next_anim(obj); } else if (state == 1) { - obj->data.m.y_velocity = m->type2.unkE << 4; + obj->data.m.y_velocity = -(m->type2.unkE << 4); const int dy = obj->y_pos - m->y_pos; if (dy >= 0) { return; @@ -140,8 +139,8 @@ static void monster_func1_type4(struct object_t *obj) { return; } const int dy = obj->y_pos - m->y_pos; - monster_rotate_platform(m, 0, dy); - if (m->type4.unkD > dy) { + monster_rotate_tiles(m, 0, dy); + if (m->type4.radius > dy) { obj->y_pos += 2; } else { obj->data.m.state = 1; @@ -154,6 +153,7 @@ static void monster_func1_type4(struct object_t *obj) { obj->data.m.state = 2; } } else if (state == 2) { + monster_rotate_pos(obj, m); if (m->type4.angle & 0x80) { ++m->type4.unk10; } else { @@ -245,6 +245,65 @@ static void monster_func1_type9(struct object_t *obj) { } } +static void monster_func1_type10(struct object_t *obj) { + monster_func1_helper(obj, obj->x_pos, obj->y_pos); + struct level_monster_t *m = obj->data.m.ref; + const uint8_t state = obj->data.m.state; + if (g_vars.level_num == 5 && g_vars.shake_screen_counter != 0 && state != 3 && state != 0xFF) { + m->current_tick = 1; + } + if (state == 0) { + m->flags |= 0x18; + if (obj->data.m.y_velocity == 0) { + obj->data.m.state = 1; + } + } else if (state == 1) { + if (obj->data.m.y_velocity != 0) { + obj->data.m.state = 0; + } else { + m->current_tick = 30; + obj->data.m.state = 2; + monster_change_next_anim(obj); + } + } else if (state == 2) { + const int dx = abs(obj->data.m.x_velocity); + if (dx < 16) { + if (g_vars.monster.hit_mask == 0) { + return; + } + m->flags = 0xF; + int x_vel = m->type10.unkD; + if (obj->x_pos >= g_vars.objects_tbl[1].x_pos) { + x_vel = -x_vel; + } + obj->data.m.x_velocity = x_vel; + } + if ((g_vars.level_draw_counter & 3) == 0) { + --m->current_tick; + if (m->current_tick == 0) { + obj->data.m.state = 3; + obj->data.m.y_velocity = 0; + obj->data.m.x_velocity >>= 15; + m->flags = 0x36; + monster_change_next_anim(obj); + } + } + } else if (state == 3) { + if (g_vars.monster.hit_mask == 0) { + return; + } + monster_reset(obj, m); + } else if (state == 0xFF) { + if ((m->flags & 1) != 0 || (obj->data.m.unk5 & 0x20) != 0 || g_vars.objects_tbl[1].y_pos >= obj->y_pos) { + if (obj->data.m.y_velocity < 240) { + obj->data.m.y_velocity += 15; + } + } else { + monster_reset(obj, m); + } + } +} + void monster_func1(int type, struct object_t *obj) { switch (type) { case 1: @@ -262,6 +321,9 @@ void monster_func1(int type, struct object_t *obj) { case 9: monster_func1_type9(obj); break; + case 10: + monster_func1_type10(obj); + break; default: print_warning("monster_func1 unhandled monster type %d", type); break; @@ -310,9 +372,7 @@ static bool monster_is_visible(int x_pos, int y_pos) { } static bool monster_func2_type1(struct level_monster_t *m) { - const int16_t x_pos = m->x_pos; - const int16_t y_pos = m->y_pos; - if (!monster_is_visible(x_pos, y_pos)) { + if (!monster_is_visible(m->x_pos, m->y_pos)) { return false; } return monster_init_object(m); @@ -345,6 +405,8 @@ static bool monster_func2_type4(struct level_monster_t *m) { return false; } m->flags = 5; + m->type4.angle = 0; + m->type4.unk10 = 0; return true; } @@ -379,20 +441,62 @@ static bool monster_func2_type10(struct level_monster_t *m) { if (m->total_ticks > (m->current_tick >> 2)) { return false; } - const int dx = (g_vars.objects_tbl[1].x_pos >> 4) - m->x_pos; + const uint16_t x = m->x_pos; + const uint16_t y = m->y_pos; + const int dx = (g_vars.objects_tbl[1].x_pos >> 4) - (x & 255); if (dx < 0) { return false; } - const int dy = (g_vars.objects_tbl[1].y_pos >> 4) - m->y_pos; + if ((y & 255) < dx) { + return false; + } + const int dy = (g_vars.objects_tbl[1].y_pos >> 4) - (x >> 8); if (dy < 0) { return false; } + if ((y >> 8) < dy) { + return false; + } struct object_t *obj = find_object_monster(); if (!obj) { return false; } - print_warning("monster_func2_type10 6579"); - return true; + obj->data.m.unk10 = 0; + g_vars.monster.current_object = obj; + static const int16_t dist_tbl[] = { 120, 100, -90, 110, -120, 40, 80, 60 }; + obj->x_pos = g_vars.objects_tbl[1].x_pos + dist_tbl[g_vars.monster.type10_dist & 7]; + ++g_vars.monster.type10_dist; + obj->data.m.x_velocity = 0; + uint8_t bh = (g_vars.objects_tbl[1].y_pos >> 4) + 4; + uint8_t bl = (obj->x_pos >> 4); + uint16_t bp = (bh << 8) | bl; + for (int i = 0; i < 10; ++i) { + if (bp < (g_vars.tilemap_h << 8)) { + bool init_spr = true; + for (int j = 0; j < 3; ++j) { + const uint8_t tile_num = g_res.leveldat[bp - j * 0x100]; + if (g_res.level.tile_attributes1[tile_num] != 0) { + init_spr = false; + break; + } + } + if (init_spr) { + obj->y_pos = bh << 4; + obj->spr_num = m->spr_num; + obj->data.m.ref = m; + m->flags = 0x17; + obj->data.m.state = 0; + obj->data.m.y_velocity = 0; + obj->data.m.energy = m->energy; + return true; + } + } + bp -= 0x100; + if (bp < 0x300) { + return false; + } + } + return false; } static bool monster_func2_type11(struct level_monster_t *m) { diff --git a/p2/resource.c b/p2/resource.c index 60b25b7..532168c 100644 --- a/p2/resource.c +++ b/p2/resource.c @@ -130,26 +130,36 @@ void load_leveldat(const uint8_t *p, struct level_t *level) { m->x_pos = READ_LE_UINT16(p + 0x9); m->y_pos = READ_LE_UINT16(p + 0xB); switch (type) { - case 2: + case 2: /* vertical (eg. spider) */ + assert(len == 15); m->type2.y_range = p[0xD]; m->type2.unkE = p[0xE]; - case 4: - m->type4.unkD = p[0xD]; + break; + case 4: /* rotate (eg. spider) */ + assert(len == 17); + m->type4.radius = p[0xD]; m->type4.unkE = p[0xE]; m->type4.angle = p[0xF]; m->type4.unk10 = p[0x10]; break; - case 8: + case 8: /* jumps (eg. leopard) */ + assert(len == 17); m->type8.x_range = p[0xD]; m->type8.unkE = p[0xE]; m->type8.unkF = p[0xF]; m->type8.y_range = p[0x10]; break; - case 9: + case 9: /* horizontal */ + assert(len == 19); m->type9.unkD = READ_LE_UINT16(p + 0xD); m->type9.unkF = READ_LE_UINT16(p + 0xF); m->type9.unk11 = p[0x11]; m->type9.unk12 = p[0x12]; + break; + case 10: /* come out of the ground */ + assert(len == 14); + m->type10.unkD = p[0xD]; + break; default: break; } diff --git a/p2/resource.h b/p2/resource.h index 620a6b9..f5ae5f2 100644 --- a/p2/resource.h +++ b/p2/resource.h @@ -5,11 +5,11 @@ #include "intern.h" struct level_gate_t { - uint16_t enter_pos; // (y << 8) | x + uint16_t enter_pos; uint16_t tilemap_pos; uint16_t dst_pos; uint8_t scroll_flag; -}; // sizeof == 7 +}; struct level_platform_t { uint16_t tilemap_pos; @@ -17,22 +17,22 @@ struct level_platform_t { uint8_t h; uint16_t unk4; uint16_t unk6; - uint8_t unk8; // y_offs + uint8_t unk8; uint8_t unk9; -}; // sizeof == 10 +}; struct level_bonus_t { uint8_t tile_num0; /* new tile */ uint8_t tile_num1; /* original tile */ uint8_t count; - uint16_t pos; // (y << 8) | x -}; // sizeof == 5 + uint16_t pos; +}; struct level_item_t { int16_t x_pos, y_pos; uint16_t spr_num; int8_t y_delta; -}; // sizeof == 7 +}; struct level_trigger_t { uint16_t x_pos; @@ -44,9 +44,9 @@ struct level_trigger_t { int8_t unk7; uint8_t unk8; uint8_t unk9; - uint8_t state; // 0xA - uint16_t y_delta; // 0xB - uint8_t counter; // 0xD + uint8_t state; + uint16_t y_delta; + uint8_t counter; } type8; struct { int16_t unk7; @@ -56,42 +56,45 @@ struct level_trigger_t { } other; }; uint8_t unkE; -}; // sizeof == 15 +}; struct level_monster_t { uint8_t len; uint8_t type; - uint16_t spr_num; // 0x2 - uint8_t flags; // 0x4 - uint8_t energy; // 0x5 + uint16_t spr_num; + uint8_t flags; + uint8_t energy; uint8_t total_ticks; uint8_t current_tick; uint8_t unk8; - uint16_t x_pos; // 0x9 - uint16_t y_pos; // 0xB + uint16_t x_pos; + uint16_t y_pos; union { struct { - uint8_t y_range; // 0xD - int8_t unkE; // 0xE, cbw + uint8_t y_range; + int8_t unkE; } type2; struct { - uint8_t unkD; + uint8_t radius; uint8_t unkE; uint8_t angle; uint8_t unk10; } type4; struct { - uint8_t x_range; // 0xD - int8_t unkE; // 0xE, cbw - int8_t unkF; // 0xF, cbw - uint8_t y_range; // 0x10 + uint8_t x_range; + int8_t unkE; + int8_t unkF; + uint8_t y_range; } type8; struct { - int16_t unkD; // 0xD - int16_t unkF; // 0xF - int8_t unk11; // 0x11 - uint8_t unk12; // 0x12 + int16_t unkD; + int16_t unkF; + int8_t unk11; + uint8_t unk12; } type9; + struct { + uint8_t unkD; + } type10; }; }; diff --git a/p2/screen.c b/p2/screen.c index 6d8d83e..765e699 100644 --- a/p2/screen.c +++ b/p2/screen.c @@ -76,12 +76,16 @@ void video_draw_string(int offset, int hspace, const char *s) { void video_draw_panel_number(int offset, int num) { const uint8_t *fnt = g_res.allfonts + 48 * 41 + 160 * 23; - decode_planar(fnt + num * 96, g_res.vga + offset * 8, 320, 16, 12, 0); + const int y = (offset * 8) / 320 + (GAME_SCREEN_H - 200); + const int x = (offset * 8) % 320 + (GAME_SCREEN_W - 320) / 2; + decode_planar(fnt + num * 96, g_res.vga + y * GAME_SCREEN_W + x, GAME_SCREEN_W, 16, 12, 0); } void video_draw_number(int offset, int num) { const uint8_t *fnt = g_res.allfonts + 0x1C70; - decode_planar(fnt + num * 88, g_res.vga + offset * 8, 320, 16, 11, 0); + const int y = (offset * 8) / 320; + const int x = (offset * 8) % 320; + decode_planar(fnt + num * 88, g_res.vga + y * GAME_SCREEN_W + x, GAME_SCREEN_W, 16, 11, 0); } void video_clear() { @@ -93,7 +97,9 @@ void video_copy_img(const uint8_t *src) { } void video_draw_panel(const uint8_t *src) { - decode_planar(src, g_res.vga + TILEMAP_SCREEN_H * 320, 320, 320, 23, 0xFF); + const int h = GAME_SCREEN_H - PANEL_H; + const int x = (GAME_SCREEN_W - 320) / 2; + decode_planar(src, g_res.vga + h * GAME_SCREEN_W + x, GAME_SCREEN_W, 320, 23, 0xFF); } void video_draw_tile(const uint8_t *src, int x_offset, int y_offset) {