diff --git a/README.md b/README.md index 53617a7..51eaae7 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ 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 [Prehistorik 2](https://www.mobygames.com/game/prehistorik-2). +There is also some support for [Prehistorik 2](https://www.mobygames.com/game/prehistorik-2). ## Requirements diff --git a/p2/game.h b/p2/game.h index 5c7279a..2108ba0 100644 --- a/p2/game.h +++ b/p2/game.h @@ -50,7 +50,7 @@ struct monster_t { struct thing_t { void *ref; int16_t counter; - int16_t unkE; + int16_t y_velocity; }; struct object_t { @@ -62,13 +62,13 @@ struct object_t { 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; + struct thing_t t; /* objects[23..74] */ } data; uint8_t hit_counter; }; #define MONSTERS_COUNT 12 -#define OBJECTS_COUNT 108 +#define OBJECTS_COUNT 116 /* offset count 0 1 : club @@ -78,9 +78,11 @@ struct object_t { 11 12 : monsters 23 32 : secret bonuses 55 20 : items - 75 16 : bonus scores + 75 16 : scores 91 7 : decor 98 5 : boss level 5 (tree) + 103 5 : boss projectiles + 108 8 : boss energy bars */ struct boss_level5_proj_t { @@ -124,6 +126,11 @@ struct vars_t { uint32_t score; uint16_t score_extra_life; + uint16_t level_complete_secrets_count; + uint16_t level_complete_bonuses_count; + uint16_t level_current_secrets_count; + uint16_t level_current_bonuses_count; + uint8_t level_completed_flag; uint8_t restart_level_flag; @@ -133,7 +140,7 @@ struct vars_t { uint16_t shake_screen_voffset; uint8_t player_lifes; - uint8_t player_energy; + int8_t player_energy; uint8_t player_death_flag; uint8_t player_flying_flag; uint8_t player_flying_counter; @@ -206,28 +213,42 @@ struct vars_t { uint8_t type0_hdir; } monster; struct { - int16_t x_pos, y_pos; - uint8_t hdir; - int16_t x_dist; - } boss; + uint16_t draw_counter; + uint8_t unk_counter; + int16_t x_velocity, y_velocity; + bool hdir; /* facing to the right */ + int16_t x_dist; /* horizontal distance from player */ + int16_t state_counter; + uint8_t anim_num; + const uint8_t *prev_anim; + const uint8_t *next_anim; + const uint8_t *current_anim; + struct { + int16_t x_pos, y_pos; + uint16_t spr_num; + } parts[5]; + struct object_t *obj1; + struct object_t *obj2; + struct object_t *obj3; + } boss; /* gorilla */ struct { uint8_t unk1; uint8_t energy; uint8_t state; /* 3: boss dead */ - uint8_t unk4; /* spr103_pos */ - uint8_t unk5; /* spr106_pos */ + uint8_t spr103_pos; + uint8_t spr106_pos; uint8_t unk6; uint8_t counter; uint8_t unk8; struct boss_level5_proj_t proj_tbl[5]; } boss_level5; /* tree */ struct { - int16_t unk1; - uint8_t unk2; - uint8_t unk3; + uint16_t energy; + uint8_t seq_counter; + uint8_t hit_counter; const uint8_t *seq; const uint16_t *anim; - } boss_level9; /* minotaur statue */ + } boss_level9; /* minotaur */ struct { int16_t x_pos, y_pos; uint16_t spr_num; @@ -270,7 +291,9 @@ extern const uint8_t cos_tbl[256]; extern const uint8_t sin_tbl[256]; extern const uint16_t monster_spr_tbl[48]; extern const uint8_t monster_anim_tbl[1100]; -extern const uint8_t boss_minotaur_seq_data[742]; +extern const uint8_t boss_minotaur_seq_data[86]; +extern const uint16_t boss_gorilla_data[19 * 10]; +extern const uint16_t boss_gorilla_spr_tbl[46 * 3]; /* uint16_t: spr1_num, uint16_t: spr2_num, uint8_t: dx, uint8_t: dy */ /* game.c */ extern void update_input(); @@ -296,6 +319,8 @@ extern void video_copy_img(const uint8_t *src); extern void video_draw_panel(const uint8_t *src); extern void video_draw_panel_number(int offset, int num); extern void video_draw_number(int offset, int num); +extern void video_draw_character_spr(int offset, uint8_t chr); +extern void video_draw_string2(int offset, const char *str); extern void video_draw_tile(const uint8_t *src, int x, int y); extern void video_convert_tiles(uint8_t *data, int len); extern void video_load_front_tiles(); diff --git a/p2/level.c b/p2/level.c index 3eec898..280e695 100644 --- a/p2/level.c +++ b/p2/level.c @@ -156,6 +156,7 @@ static void load_level_data_fix_monsters_spr_num() { } static void load_level_data_init_secret_bonus_tiles() { + int count = 0; for (int i = 0; i < MAX_LEVEL_BONUSES; ++i) { struct level_bonus_t *bonus = &g_res.level.bonuses_tbl[i]; const uint16_t offset = bonus->pos; @@ -163,8 +164,10 @@ static void load_level_data_init_secret_bonus_tiles() { const uint8_t tile_num = level_get_tile(offset); level_set_tile(offset, bonus->tile_num0); bonus->tile_num1 = tile_num; + ++count; } } + g_vars.level_complete_secrets_count = count; } static int compare_level_item_x_pos(const void *item1, const void *item2) { @@ -217,7 +220,7 @@ static void load_level_data(int num) { print_debug(DBG_GAME, "tilemap offset %d", offset); load_leveldat(g_res.leveldat + offset, 0); video_convert_tiles(g_res.leveldat + g_vars.tilemap.size + 512, offset - (g_vars.tilemap.size + 512)); - print_debug(DBG_GAME, "start_pos %d,%d end_pos %d,%d", g_res.level.start_x_pos, g_res.level.start_y_pos, g_res.level.end_x_pos, g_res.level.end_y_pos); + print_debug(DBG_GAME, "start_pos %d,%d end_pos %d,%d", g_res.level.start_x_pos, g_res.level.start_y_pos, g_res.level.boss_x_pos, g_res.level.boss_y_pos); load_level_data_init_animated_tiles(); load_level_data_init_transparent_tiles(); g_vars.snow.counter = 0; @@ -238,8 +241,11 @@ static void load_level_data(int num) { } } } + g_vars.level_complete_bonuses_count = count; static const uint8_t type_tbl[] = { 0xFF, 0x0C, 0x0B, 0x0A, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x0E }; if (g_vars.level_num < 10 && type_tbl[g_vars.level_num] != 0xFF) { + g_vars.level_complete_bonuses_count *= 2; + g_vars.level_complete_secrets_count *= 2; } } @@ -890,6 +896,9 @@ static void level_update_tile1(uint16_t offset) { case 1: level_update_tile_type_1(offset); break; + case 2: + level_update_tile_type_2(offset); + break; default: print_warning("Unhandled level_update_tile1 type %d", type); break; @@ -1024,7 +1033,8 @@ static void level_add_object23_bonus(int x_vel, int y_vel, int count) { obj->x_pos = g_vars.current_bonus.x_pos; obj->y_pos = g_vars.current_bonus.y_pos; obj->x_velocity = x_vel; - obj->data.p.y_velocity = y_vel; + obj->data.t.y_velocity = y_vel; + obj->data.t.ref = 0; x_vel = -x_vel; ++counter; if ((counter & 1) == 0) { @@ -1113,12 +1123,11 @@ static void level_monster_die(struct object_t *obj, struct level_monster_t *m, s if ((m->flags & 1) == 0) { g_vars.current_bonus.x_pos = obj->x_pos; g_vars.current_bonus.y_pos = obj->y_pos; - g_vars.current_bonus.spr_num = 0x2046; + g_vars.current_bonus.spr_num = 0x2046; /* bones */ level_add_object23_bonus(48, -128, 6); } else { level_monster_update_anim(obj); - uint8_t al = m->flags & ~0x37; - if (al != 0x88) { + if ((m->flags & ~0x37) != 0x88) { m->flags &= ~8; } int dy = MIN(g_vars.player_club_power, 25); @@ -1209,6 +1218,7 @@ static bool level_handle_bonuses_found(struct object_t *obj, struct level_bonus_ g_vars.current_bonus.spr_num = 110 + num; g_vars.current_bonus.y_pos -= 112; level_add_object23_bonus(0, 0, 1); + ++g_vars.level_current_secrets_count; } else { static uint8_t draw_counter = 0; const int diff = abs(g_vars.level_draw_counter - draw_counter); @@ -1290,6 +1300,7 @@ static bool level_collide_axe_bonuses(struct object_t *obj) { continue; } memset(g_vars.level_bonuses_count_tbl, 0, 80); + int count = 0; const uint16_t pos = bonus->pos; for (int j = 0; j < MAX_LEVEL_BONUSES; ++j) { struct level_bonus_t *bonus2 = &g_res.level.bonuses_tbl[j]; @@ -1304,8 +1315,13 @@ static bool level_collide_axe_bonuses(struct object_t *obj) { } if (g_vars.level_bonuses_count_tbl[j] == 0) { ++g_vars.level_bonuses_count_tbl[j]; + ++g_vars.level_current_secrets_count; + ++count; } } + if (count == 0) { + return true; + } } return false; } @@ -1334,11 +1350,11 @@ static void level_update_objects_axe() { } } -static void level_clear_boss_objects(int count) { +static void level_update_objects_boss_energy(int count) { if (count > 8) { count = 8; } - if (count != 0 && 0) { + if (count != 0) { play_music(13); for (int i = 0; i < count; ++i) { struct object_t *obj = &g_vars.objects_tbl[108 + i]; @@ -1353,7 +1369,7 @@ static void level_clear_boss_objects(int count) { } } -static void level_update_objects_boss_level5_helper() { +static void level_update_objects_boss_hit_player() { --g_vars.bonus_energy_counter; if (g_vars.bonus_energy_counter < 0) { g_vars.bonus_energy_counter = 5; @@ -1366,54 +1382,462 @@ static void level_update_objects_boss_level5_helper() { } g_vars.current_bonus.x_pos = g_vars.objects_tbl[1].x_pos; g_vars.current_bonus.y_pos = g_vars.objects_tbl[1].y_pos - 48; - g_vars.current_bonus.spr_num = 0x2046; + g_vars.current_bonus.spr_num = 0x2046; /* bones */ const int x_vel = (g_vars.bonus_energy_counter & 1) != 0 ? -48 : 48; const int y_vel = -128; level_add_object23_bonus(x_vel, y_vel, 1); } -static void level_update_boss_gorilla() { - const int x = (g_vars.boss.x_pos >> 4) + g_res.level.end_x_pos; - if (x >= 0) { - if (g_res.level.boss_xmin <= x && g_res.level.boss_xmax >= x) { - g_res.level.end_x_pos = x; +static void level_update_boss_gorilla_collide_proj(struct object_t *obj_player, struct object_t *obj) { + if (obj->spr_num == 0xFFFF || (obj->spr_num & 0x2000) == 0) { + return; + } + if (!level_objects_collide(obj_player, obj)) { + return; + } + g_vars.boss.unk_counter -= 4 - g_res.level.boss_counter; + if (g_vars.boss.unk_counter < 0) { + g_vars.boss.unk_counter = 0; + } + level_update_objects_boss_hit_player(); + obj_player->hit_counter = 44; + g_vars.player_anim_0x40_flag = 0; + obj_player->data.p.y_velocity = -128; + obj_player->x_friction = 3; + obj_player->x_velocity = (g_vars.objects_tbl[1].x_pos >= g_res.level.boss_x_pos) ? 128 : -128; + g_vars.player_gravity_flag = 0; +} + +static void level_update_boss_gorilla_hit_player() { + if (g_vars.restart_level_flag != 0) { + return; + } + level_update_boss_gorilla_collide_proj(&g_vars.objects_tbl[1], g_vars.boss.obj2); + level_update_boss_gorilla_collide_proj(&g_vars.objects_tbl[1], g_vars.boss.obj1); + if (level_objects_collide(&g_vars.objects_tbl[1], g_vars.boss.obj3)) { + if (g_vars.player_jump_monster_flag != 0) { + g_vars.objects_tbl[1].data.p.y_velocity = (g_vars.input.key_up != 0) ? -128 : -64; } } - g_res.level.end_y_pos += (g_vars.boss.y_pos >> 4); - if (g_vars.boss.y_pos >= 0) { - const uint8_t bh = (g_res.level.end_y_pos >> 4); - const uint8_t bl = (g_res.level.end_x_pos >> 4); - const uint8_t tile_num = level_get_tile((bh << 8) | bl); +} + +static void level_update_boss_gorilla_tick() { + if (g_vars.boss.unk_counter < UCHAR_MAX) { + ++g_vars.boss.unk_counter; + } +} + +static uint16_t level_update_boss_gorilla_find_pos(uint16_t num1, uint16_t num2) { + const uint16_t *p = boss_gorilla_spr_tbl; + const uint16_t *end = &boss_gorilla_spr_tbl[138]; + while (p + 3 <= end) { + if (p[0] == num1 && p[1] == num2) { + return p[2]; /* dy,dx packed as a uint16_t */ + } + p += 3; + } + print_warning("boss_gorilla spr range (%d,%d) not found", num1, num2); + return 0xFFFF; +} + +static void level_update_boss_gorilla_init_objects(const uint16_t *p) { + g_vars.boss.parts[0].spr_num = *p++; + g_vars.boss.parts[1].spr_num = *p++; + uint16_t d; + int8_t dx, dy; + + d = level_update_boss_gorilla_find_pos(g_vars.boss.parts[0].spr_num, g_vars.boss.parts[1].spr_num); + dx = d & 255; + if (g_vars.boss.hdir) { + dx = -dx; + } + g_vars.boss.parts[1].x_pos = dx + g_vars.boss.parts[0].x_pos; + dy = d >> 8; + g_vars.boss.parts[1].y_pos = dy + g_vars.boss.parts[0].y_pos; + + g_vars.boss.parts[2].spr_num = *p++; + d = level_update_boss_gorilla_find_pos(g_vars.boss.parts[1].spr_num, g_vars.boss.parts[2].spr_num); + dx = d & 255; + if (g_vars.boss.hdir) { + dx = -dx; + } + g_vars.boss.parts[2].x_pos = dx + g_vars.boss.parts[1].x_pos; + dy = d >> 8; + g_vars.boss.parts[2].y_pos = dy + g_vars.boss.parts[1].y_pos; + + g_vars.boss.parts[3].spr_num = *p++; + d = level_update_boss_gorilla_find_pos(g_vars.boss.parts[1].spr_num, g_vars.boss.parts[3].spr_num); + dx = d & 255; + if (g_vars.boss.hdir) { + dx = -dx; + } + g_vars.boss.parts[3].x_pos = dx + g_vars.boss.parts[1].x_pos; + dy = d >> 8; + g_vars.boss.parts[3].y_pos = dy + g_vars.boss.parts[1].y_pos; + + g_vars.boss.parts[4].spr_num = *p++; + d = level_update_boss_gorilla_find_pos(g_vars.boss.parts[1].spr_num, g_vars.boss.parts[4].spr_num); + dx = d & 255; + if (g_vars.boss.hdir) { + dx = -dx; + } + g_vars.boss.parts[4].x_pos = dx + g_vars.boss.parts[1].x_pos; + dy = d >> 8; + g_vars.boss.parts[4].y_pos = dy + g_vars.boss.parts[1].y_pos; + + if (g_vars.boss.anim_num & 0x40) { + g_vars.boss.parts[1].y_pos += 2; + ++g_vars.boss.parts[2].y_pos; + ++g_vars.boss.parts[4].y_pos; + ++g_vars.boss.parts[3].y_pos; + } + if (g_vars.boss.hdir) { + g_vars.boss.parts[0].spr_num |= 0x8000; + g_vars.boss.parts[1].spr_num |= 0x8000; + g_vars.boss.parts[2].spr_num |= 0x8000; + g_vars.boss.parts[3].spr_num |= 0x8000; + g_vars.boss.parts[4].spr_num |= 0x8000; + } + for (int i = 0; i < 5; ++i) { + struct object_t *obj = &g_vars.objects_tbl[103 + i]; + const uint16_t addr = *p++; + if (addr == 0xA609) { + g_vars.boss.obj1 = obj; + } + if (addr == 0xA60F) { + g_vars.boss.obj2 = obj; + } + if (addr == 0xA603) { + g_vars.boss.obj3 = obj; + } + assert(addr == 0xA5F7 || addr == 0xA5FD || addr == 0xA603 || addr == 0xA609 || addr == 0xA60F); + const int num = (addr - 0xA5F7) / 6; + obj->x_pos = g_vars.boss.parts[num].x_pos; + obj->y_pos = g_vars.boss.parts[num].y_pos; + obj->spr_num = g_vars.boss.parts[num].spr_num; + } +} + +static bool level_update_boss_gorilla_collides_obj3(struct object_t *obj) { + if (obj->spr_num == 0xFFFF) { + return false; + } + const int spr_num = g_vars.boss.obj1->spr_num & 0x1FFF; + if ((spr_num == 0x196 || spr_num == 0x195) && level_objects_collide(obj, g_vars.boss.obj1)) { + obj->spr_num = 0xFFFF; + return false; + } + return level_objects_collide(obj, g_vars.boss.obj3); +} + +static void level_update_boss_gorilla_helper2() { + for (int i = 0; i < 6; ++i) { + if (i == 1) { + continue; + } + struct object_t *obj = &g_vars.objects_tbl[i]; + if (!level_update_boss_gorilla_collides_obj3(obj)) { + continue; + } + if (abs(g_vars.level_draw_counter - g_vars.boss.draw_counter) <= 22) { + break; + } + g_vars.boss.draw_counter = g_vars.level_draw_counter; + for (int j = 103; j <= 107; ++j) { + g_vars.objects_tbl[j].spr_num |= 0x4000; + } + g_vars.player_flying_flag = 0; + level_update_boss_gorilla_tick(); + g_res.level.boss_energy -= g_vars.player_club_power; + if (g_res.level.boss_energy < 0) { + g_res.level.boss_state = 6; + g_vars.boss.y_velocity = -240; + break; + } + if (g_vars.player_club_power > 20) { + g_vars.boss.x_velocity = (g_res.level.boss_x_pos >= g_vars.objects_tbl[1].x_pos) ? 48 : -48; + g_vars.boss.y_velocity = (g_vars.boss.y_velocity > 0) ? -128 : -64; + g_res.level.boss_state = 5; + } + g_vars.boss.state_counter = 0; + obj->spr_num = 0xFFFF; + break; + } +} + +static void level_update_boss_gorilla() { + const int x = (g_vars.boss.x_velocity >> 4) + g_res.level.boss_x_pos; + if (x >= 0) { + if (g_res.level.boss_xmin <= x && g_res.level.boss_xmax >= x) { + g_res.level.boss_x_pos = x; + } + } + g_res.level.boss_y_pos += (g_vars.boss.y_velocity >> 4); + if (g_vars.boss.y_velocity >= 0) { + const uint16_t pos = ((g_res.level.boss_y_pos >> 4) << 8) | (g_res.level.boss_x_pos >> 4); + const uint8_t tile_num = level_get_tile(pos); if (g_res.level.tile_attributes1[tile_num] != 0) { - g_res.level.end_y_pos &= ~15; - if (g_vars.boss.y_pos != 0) { + g_res.level.boss_y_pos &= ~15; + if (g_vars.boss.y_velocity != 0) { g_vars.shake_screen_counter = 7; } - g_vars.boss.y_pos = 0; - g_vars.boss.x_pos = 0; + g_vars.boss.y_velocity = 0; + g_vars.boss.x_velocity = 0; } else { - if (g_vars.boss.y_pos < 224) { - g_vars.boss.y_pos += 16; + if (g_vars.boss.y_velocity < 224) { + g_vars.boss.y_velocity += 16; } } } else { - if (g_vars.boss.y_pos < 224) { - g_vars.boss.y_pos += 16; + if (g_vars.boss.y_velocity < 224) { + g_vars.boss.y_velocity += 16; } } - g_vars.boss.hdir = (g_vars.objects_tbl[1].x_pos < g_res.level.end_x_pos) ? 1 : 0; - g_vars.boss.x_dist = abs(g_vars.objects_tbl[1].x_pos - g_res.level.end_x_pos); + g_vars.boss.hdir = (g_res.level.boss_x_pos < g_vars.objects_tbl[1].x_pos); + g_vars.boss.x_dist = abs(g_vars.objects_tbl[1].x_pos - g_res.level.boss_x_pos); if (g_vars.boss.x_dist > 400) { return; } - if (abs(g_vars.objects_tbl[1].y_pos - g_res.level.end_y_pos) > 250) { + if (abs(g_vars.objects_tbl[1].y_pos - g_res.level.boss_y_pos) > 250) { return; } - print_warning("level_update_boss unimplemented end_pos %d,%d", g_res.level.end_x_pos, g_res.level.end_y_pos); + if (g_res.level.boss_state == 0) { + if (g_vars.boss.x_dist >= 250) { + g_vars.boss.unk_counter = 0; + static const uint8_t data_st0[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0xF4 + }; + g_vars.boss.next_anim = data_st0; + } else { + g_vars.boss.state_counter = 0; + g_res.level.boss_state = 1; + } + } else if (g_res.level.boss_state == 1) { + if (g_vars.boss.x_dist >= 250) { + g_res.level.boss_state = 0; + } else { + ++g_vars.boss.state_counter; + if (g_res.level.boss_energy < 60) { + g_res.level.boss_state = 2; + } else { + static const uint8_t data_st1[] = { + 0x00, 0x00, 0x00, 0x40, 0x40, 0x40, 0xFA + }; + g_vars.boss.next_anim = data_st1; + if (g_vars.boss.state_counter >= 110) { + g_res.level.boss_state = 1; + level_update_boss_gorilla_tick(); + g_vars.boss.state_counter = 0; + } else if (g_vars.player_anim_0x40_flag != 0 && (g_vars.level_draw_counter & 7) == 0) { + level_update_boss_gorilla_tick(); + } else if (g_vars.boss.unk_counter >= 10) { + g_res.level.boss_state = 3; + g_vars.boss.state_counter = 0; + } else if (g_vars.boss.unk_counter > 3) { + g_res.level.boss_state = 2; + g_vars.boss.state_counter = 0; + } + } + } + } else if (g_res.level.boss_state == 2) { + ++g_vars.boss.state_counter; + if (g_vars.boss.state_counter != 64) { + if (g_vars.boss.state_counter > 64) { + if (g_vars.boss.y_velocity > 0) { + static const uint8_t data_st2a[] = { + 0x04, 0xFF + }; + static const uint8_t data_st2b[] = { + 0x07, 0xFF + }; + g_vars.boss.next_anim = (g_res.level.boss_energy >= 100) ? data_st2a : data_st2b; + } + if (g_vars.boss.x_dist <= 80) { + g_vars.boss.state_counter = 0; + g_res.level.boss_state = 3; + g_vars.boss.unk_counter = 11; + } else if (g_vars.boss.state_counter == 80) { + static const uint8_t data_st2[] = { + 0x03, 0xFF + }; + g_vars.boss.next_anim = data_st2; + int dx = g_res.level.boss_x_pos - g_vars.objects_tbl[1].x_pos; + int x_vel = 48; + int y_vel = abs(dx); + if (y_vel > g_res.level.boss_counter * 80) { + y_vel = -96; + } else { + y_vel = (y_vel / 14) << 4; + x_vel = y_vel >> 1; + y_vel = -y_vel; + } + g_vars.boss.y_velocity = y_vel; + if (dx >= 0) { + x_vel = -x_vel; + } + g_vars.boss.x_velocity = x_vel; + } else if (g_vars.boss.state_counter > 80) { + if (g_vars.boss.y_velocity == 0) { + g_vars.boss.unk_counter = 3; + g_vars.boss.state_counter = 0; + g_res.level.boss_state = 1; + } + } + } else { + level_update_boss_gorilla_hit_player(); + if (g_vars.boss.x_dist < 75) { + g_res.level.boss_state = 3; + g_vars.boss.state_counter = 0; + } else { + static const uint8_t data_st2[] = { + 0x10, 0x50, 0x10, 0x11, 0x51, 0x11, 0x10, 0x50, 0x10, 0x11, + 0x51, 0x11, 0x10, 0x50, 0x10, 0x11, 0x51, 0x11, 0x00, 0x00, + 0x40, 0x40, 0x00, 0x00, 0x40, 0x40, 0x00, 0x00, 0x40, 0x40, + 0x00, 0x00, 0x40, 0x40, 0xDE + }; + g_vars.boss.next_anim = data_st2; + } + } + } else { + level_update_boss_gorilla_hit_player(); + g_vars.boss.y_velocity = -224; + if (g_res.level.boss_energy < 60 && g_vars.boss.x_dist < 80) { + g_res.level.boss_state = 4; + g_vars.boss.state_counter = 0; + } + static const uint8_t data_st2[] = { + 0x03, 0xFF + }; + g_vars.boss.next_anim = data_st2; + } + } else if (g_res.level.boss_state == 3) { + level_update_boss_gorilla_hit_player(); + const int counter = (abs(g_res.level.boss_energy - 50) > 25) ? 66 : 154; + if (g_vars.boss.state_counter > counter) { + g_vars.boss.state_counter = 0; + g_res.level.boss_state = 2; + } else { + if (g_vars.boss.y_velocity == 0) { + if (g_vars.boss.x_dist < 100) { + if (g_vars.boss.x_dist <= 25) { + static const uint8_t data_st3[] = { + 0x0D, 0x0D, 0x0E, 0x0E, 0xFC + }; + g_vars.boss.next_anim = data_st3; + } else if (g_vars.boss.x_dist <= 35) { + g_res.level.boss_state = 4; + g_vars.boss.state_counter = 0; + } else { + if (g_vars.boss.unk_counter == 0) { + g_res.level.boss_state = 7; + g_vars.boss.state_counter = 0; + } + static const uint8_t data_st3[] = { + 0x0D, 0x0D, 0x0C, 0x0C, 0xFC + }; + g_vars.boss.next_anim = data_st3; + } + } else { + g_vars.boss.y_velocity = -81; + g_vars.boss.x_velocity = (g_vars.objects_tbl[1].x_pos > g_res.level.boss_x_pos) ? 80 : -80; + } + } else if (g_vars.boss.y_velocity < 0) { + static const uint8_t data_st3[] = { + 0x03, 0xFF + }; + g_vars.boss.next_anim = data_st3; + } else { + static const uint8_t data_st3[] = { + 0x0D, 0x0D, 0x0D, 0x0E, 0x0E, 0x0E, 0xFA + }; + g_vars.boss.next_anim = data_st3; + } + } + } else if (g_res.level.boss_state == 4) { + level_update_boss_gorilla_hit_player(); + ++g_vars.boss.state_counter; + if (g_vars.boss.state_counter > 66) { + g_res.level.boss_state = 3; + g_vars.boss.state_counter = 0; + } else { + static const uint8_t data_st4[] = { + 0x12, 0x12, 0x52, 0x12, 0x0E, 0x0E, 0x4E, 0x0E, 0xF8 + }; + g_vars.boss.next_anim = data_st4; + if (g_vars.boss.y_velocity == 0) { + g_vars.shake_screen_counter = 4; + } + } + } else if (g_res.level.boss_state == 5) { + ++g_vars.boss.state_counter; + if (g_vars.boss.state_counter > 19) { + g_vars.boss.state_counter = 0; + g_res.level.boss_state = 1; + } else { + static const uint8_t data_st5[] = { + 0x05, 0xFF + }; + g_vars.boss.next_anim = data_st5; + } + } else if (g_res.level.boss_state == 6) { + static const uint8_t data_st6[] = { + 0x05, 0x05, 0x0F, 0x0F, 0xFC + }; + g_vars.boss.next_anim = data_st6; + if (g_vars.boss.y_velocity >= 0) { + g_res.level.boss_state = 0xFF; + g_vars.level_completed_flag = 1; + g_vars.objects_tbl[103].spr_num = 0xFFFF; + g_vars.objects_tbl[104].spr_num = 0xFFFF; + g_vars.objects_tbl[105].spr_num = 0xFFFF; + g_vars.objects_tbl[106].spr_num = 0xFFFF; + g_vars.objects_tbl[107].spr_num = 0xFFFF; + return; + } + } else if (g_res.level.boss_state == 7) { + ++g_vars.boss.state_counter; + if (g_vars.boss.state_counter > 44) { + g_res.level.boss_state = 1; + } else { + static const uint8_t data_st7[] = { + 0x03, 0xFF + }; + g_vars.boss.next_anim = data_st7; + if (g_vars.boss.y_velocity == 0) { + g_vars.boss.y_velocity = -97; + g_vars.boss.x_velocity = (g_vars.objects_tbl[1].x_pos > g_res.level.boss_x_pos) ? -48 : 48; + } else if (g_vars.boss.y_velocity > 0) { + static const uint8_t data_st7[] = { + 0x04, 0xFF + }; + g_vars.boss.next_anim = data_st7; + } + } + } + if (g_vars.boss.prev_anim != g_vars.boss.next_anim) { + g_vars.boss.prev_anim = g_vars.boss.next_anim; + g_vars.boss.current_anim = g_vars.boss.next_anim; + } + const uint8_t *p = g_vars.boss.current_anim; + while (1) { + const uint8_t num = *p; + if (num & 0x80) { + p += (int8_t)num; + } else { + g_vars.boss.current_anim = p + 1; + g_vars.boss.anim_num = num; + g_vars.boss.parts[0].x_pos = g_res.level.boss_x_pos; + g_vars.boss.parts[0].y_pos = g_res.level.boss_y_pos; + const uint16_t *p = &boss_gorilla_data[(num & ~0x40) * 20 / sizeof(uint16_t)]; + level_update_boss_gorilla_init_objects(p); + level_update_boss_gorilla_helper2(); + break; + } + } } static void level_update_boss_tree() { - level_clear_boss_objects((2 - g_vars.boss_level5.state) << 1); + level_update_objects_boss_energy((2 - g_vars.boss_level5.state) << 1); struct object_t *obj_player = &g_vars.objects_tbl[1]; for (int i = 0; i < 5; ++i) { struct boss_level5_proj_t *prj = &g_vars.boss_level5.proj_tbl[i]; @@ -1427,7 +1851,7 @@ static void level_update_boss_tree() { obj_player->data.p.y_velocity = -128; obj_player->x_friction = 3; obj_player->x_velocity = -128; - level_update_objects_boss_level5_helper(); + level_update_objects_boss_hit_player(); prj->spr_num = 0xFFFF; obj->spr_num = 0xFFFF; } else { @@ -1474,7 +1898,7 @@ static void level_update_boss_tree() { 0x3B4, 0x79F, 0x1A7, 0x396, 0x78A, 0x1A8, 0x3B6, 0x79C, 0x1A9, 0x3E0, 0x795, 0x1B6, 0, 0, 0xFFFF }; - const uint16_t *p = &pos1_data[g_vars.boss_level5.unk5 * 6]; + const uint16_t *p = &pos1_data[g_vars.boss_level5.spr106_pos * 6]; g_vars.objects_tbl[107].x_pos = p[0]; g_vars.objects_tbl[107].y_pos = p[1]; g_vars.objects_tbl[107].spr_num = p[2]; @@ -1484,20 +1908,20 @@ static void level_update_boss_tree() { static const uint16_t pos3_data[] = { 0x3E3, 0x773, 0x1B0, 0x3E4, 0x771, 0x1B1, 0x3E4, 0x773, 0x1B2 }; - const uint16_t *q = &pos3_data[g_vars.boss_level5.unk4 * 3]; + const uint16_t *q = &pos3_data[g_vars.boss_level5.spr103_pos * 3]; g_vars.objects_tbl[103].x_pos = q[0]; g_vars.objects_tbl[103].y_pos = q[1]; g_vars.objects_tbl[103].spr_num = (g_vars.boss_level5.state != 0) ? 0xFFFF : q[2]; if ((g_vars.level_draw_counter & 3) == 0) { - ++g_vars.boss_level5.unk4; - if (g_vars.boss_level5.unk4 > 2) { - g_vars.boss_level5.unk4 = 0; + ++g_vars.boss_level5.spr103_pos; + if (g_vars.boss_level5.spr103_pos > 2) { + g_vars.boss_level5.spr103_pos = 0; } } if (g_vars.boss_level5.unk1 >= 1) { if (g_vars.boss_level5.unk1 == 1) { g_vars.boss_level5.unk6 = 2; - --g_vars.boss_level5.unk5; + --g_vars.boss_level5.spr106_pos; g_vars.boss_level5.counter += 5; } --g_vars.boss_level5.unk1; @@ -1506,12 +1930,12 @@ static void level_update_boss_tree() { if (g_vars.boss_level5.unk6 > 2) { g_vars.boss_level5.unk6 = 0; } - ++g_vars.boss_level5.unk5; - if (g_vars.boss_level5.unk5 > 2) { - g_vars.boss_level5.unk5 = 0; + ++g_vars.boss_level5.spr106_pos; + if (g_vars.boss_level5.spr106_pos > 2) { + g_vars.boss_level5.spr106_pos = 0; } uint8_t ah = 1; - if (g_vars.boss_level5.unk5 != 0) { + if (g_vars.boss_level5.spr106_pos != 0) { ah = 3; g_vars.shake_screen_counter = 4; obj_player->x_pos += 2; @@ -1547,9 +1971,9 @@ static void level_update_boss_tree() { g_vars.player_anim_0x40_flag = 0; obj_player->x_friction = 3; obj_player->x_velocity = -128; - level_update_objects_boss_level5_helper(); - level_update_objects_boss_level5_helper(); - level_update_objects_boss_level5_helper(); + level_update_objects_boss_hit_player(); + level_update_objects_boss_hit_player(); + level_update_objects_boss_hit_player(); } } } @@ -1577,7 +2001,7 @@ static void level_update_boss_tree() { g_vars.boss_level5.unk1 = 6; if (g_vars.boss_level5.state < 2) { g_vars.boss_level5.unk6 = 3; - g_vars.boss_level5.unk5 = 3; + g_vars.boss_level5.spr106_pos = 3; } --g_vars.boss_level5.energy; if (g_vars.boss_level5.energy == 0) { @@ -1596,7 +2020,7 @@ static void level_update_boss_tree() { obj_player->data.p.y_velocity = -144; obj_player->x_friction = 3; obj_player->x_velocity = -160; - level_update_objects_boss_level5_helper(); + level_update_objects_boss_hit_player(); } } if (g_vars.boss_level5.counter > 0) { @@ -1604,10 +2028,16 @@ static void level_update_boss_tree() { } } -static void level_update_boss_minotaur_update_tiles(int count) { +static void level_update_boss_minotaur_set_frame(int num) { + const int offset = (num * 6 + 20); + for (int y = 0; y < 7; ++y) { + for (int x = 0; x < 6; ++x) { + g_res.leveldat[(y << 8) + 12 + x] = g_res.leveldat[(y << 8) + offset + x]; + } + } } -static void level_update_boss_minotaur_helper() { +static void level_update_boss_minotaur_add_spr_0x137() { /* boss defeated, bonus */ g_vars.current_bonus.x_pos = 185; g_vars.current_bonus.y_pos = 30; g_vars.current_bonus.spr_num = 0x2137; @@ -1623,64 +2053,65 @@ static void level_update_boss_minotaur_helper() { } } -static void level_update_boss_minotaur_add_spr_0x1CA() { +static void level_update_boss_minotaur_add_spr_0x1CA() { /* rock */ for (int i = 0; i < 32; ++i) { struct object_t *obj = &g_vars.objects_tbl[23 + i]; - if (obj->spr_num == 0xFFFF) { + if (obj->spr_num != 0xFFFF) { continue; } obj->spr_num = 0x1CA; obj->x_pos = 200; obj->y_pos = 88; + obj->hit_counter = 0; obj->data.t.counter = 132; obj->data.t.ref = 0; obj->x_velocity = -((random_get_number() & 15) << 3); - obj->data.t.unkE = 0; + obj->data.t.y_velocity = 0; break; } } -static void level_update_boss_minotaur_add_spr_0x1CB() { +static void level_update_boss_minotaur_add_spr_0x1CB() { /* ceiling chandelier */ for (int i = 0; i < 32; ++i) { struct object_t *obj = &g_vars.objects_tbl[23 + i]; - if (obj->spr_num == 0xFFFF) { + if (obj->spr_num != 0xFFFF) { continue; } obj->spr_num = 0x1CB; obj->x_pos = (random_get_number() & 0x7F) - 16; obj->y_pos = 0; + obj->hit_counter = 0; obj->data.t.counter = 66; obj->data.t.ref = 0; obj->x_velocity = 0; - obj->data.t.unkE = 0; + obj->data.t.y_velocity = 0; break; } } static void level_update_boss_minotaur() { static const uint16_t data[] = { - 0xA70F, 5, 0xA74E, 1, 0xA70F, 3, 0xA74E, 2, 0xA70F, 2, 0xA734, 1, 0xFFFF, 0xFFE8 + 0xA70F, 5, 0xA74E, 1, 0xA70F, 3, 0xA74E, 2, 0xA70F, 2, 0xA734, 1, 0xFFFF }; if (!g_vars.boss_level9.seq) { g_vars.boss_level9.anim = data; - g_vars.boss_level9.unk1 = 24; - g_vars.boss_level9.unk2 = 0; - g_vars.boss_level9.unk3 = 3; + g_vars.boss_level9.energy = 24; + g_vars.boss_level9.seq_counter = 0; + g_vars.boss_level9.hit_counter = 3; } - level_clear_boss_objects(g_vars.boss_level9.unk1 >> 2); - if (g_vars.boss_level9.unk1 == 0) { - level_update_boss_minotaur_update_tiles(2); + level_update_objects_boss_energy(g_vars.boss_level9.energy >> 2); + if (g_vars.boss_level9.energy == 0) { + level_update_boss_minotaur_set_frame(2); return; } - if (g_vars.boss_level9.unk2 == 0) { + if (g_vars.boss_level9.seq_counter == 0) { const uint16_t *p = g_vars.boss_level9.anim; if (p[0] == 0xFFFF) { - p += (int16_t)p[1] / 2; - assert(p == data); + p = data; } assert(p[0] >= 0xA70F); g_vars.boss_level9.seq = &boss_minotaur_seq_data[p[0] - 0xA70F]; - g_vars.boss_level9.unk2 = p[1]; + g_vars.boss_level9.seq_counter = p[1]; g_vars.boss_level9.anim = p + 2; } for (int i = 0; i < 4; ++i) { @@ -1688,19 +2119,19 @@ static void level_update_boss_minotaur() { if (obj->spr_num == 0xFFFF) { continue; } - if ((obj->x_pos >= 235) || obj->y_pos >= 80) { + if (obj->x_pos >= 235 || obj->y_pos >= 80) { continue; } g_vars.boss_level9.seq = &boss_minotaur_seq_data[0x19]; - if (g_vars.boss_level9.unk1 > 0) { - --g_vars.boss_level9.unk1; + if (g_vars.boss_level9.energy > 0) { + --g_vars.boss_level9.energy; } - if (g_vars.boss_level9.unk1 == 0) { - level_update_boss_minotaur_helper(); + if (g_vars.boss_level9.energy == 0) { + level_update_boss_minotaur_add_spr_0x137(); } - ++g_vars.boss_level9.unk3; - g_vars.boss_level9.unk3 &= 3; - if (g_vars.boss_level9.unk3 == 0) { + ++g_vars.boss_level9.hit_counter; + g_vars.boss_level9.hit_counter &= 3; + if (g_vars.boss_level9.hit_counter == 0) { g_vars.boss_level9.seq = &boss_minotaur_seq_data[0x25]; } break; @@ -1718,11 +2149,11 @@ static void level_update_boss_minotaur() { ++p; } else if ((*p & 0x80) == 0) { ++g_vars.boss_level9.seq; - level_update_boss_minotaur_update_tiles(*p); + level_update_boss_minotaur_set_frame(*p); break; } else { - if (g_vars.boss_level9.unk2 > 0) { - --g_vars.boss_level9.unk2; + if (g_vars.boss_level9.seq_counter > 0) { + --g_vars.boss_level9.seq_counter; } p += (int8_t)*p; g_vars.boss_level9.seq = p; @@ -1808,7 +2239,7 @@ extern void monster_func1(int type, struct object_t *obj); /* update */ extern bool monster_func2(int type, struct level_monster_t *m); /* init */ static void level_update_objects_monsters() { - if (g_res.level.boss_flag != 0xFF) { + if (g_res.level.boss_state != 0xFF) { level_update_boss_gorilla(); } if (g_vars.level_num == 5 && (g_res.level.scrolling_mask & ~1) == 0) { @@ -1960,30 +2391,29 @@ static void level_update_objects_bonuses() { obj->x_pos = 0; obj->x_velocity = -obj->x_velocity; } - const int dy = obj->data.p.y_velocity >> 4; - obj->y_pos += dy; - int dx = obj->data.p.y_velocity + 9; - if (dx < 256) { - obj->data.p.y_velocity = dx; + obj->y_pos += (obj->data.t.y_velocity >> 4); + const int dy = obj->data.t.y_velocity + 9; + if (dy < 256) { + obj->data.t.y_velocity = dy; } const int num = obj->spr_num & 0x1FFF; if (num == 229) { continue; } - if (num == 300) { + if (num == 300 || num == 306 || num == 308 || num == 310) { if (obj->data.t.counter > 50) { obj->data.t.counter = 50; } continue; } - uint8_t bl = obj->x_pos >> 4; - uint8_t bh = obj->y_pos >> 4; + const uint16_t pos = ((obj->y_pos >> 4) << 8) | (obj->x_pos >> 4); if (dy > 0) { - const int tile_num = level_get_tile(bh * 256 + bl); + const int tile_num = level_get_tile(pos); uint8_t al = g_res.level.tile_attributes1[tile_num]; if (al != 0) { - obj->data.p.y_velocity = -obj->data.p.y_velocity; - obj->data.p.y_velocity >>= 1; + /* bounce on the ground */ + obj->data.t.y_velocity = -obj->data.t.y_velocity; + obj->data.t.y_velocity >>= 1; const bool x_direction = (obj->x_velocity < 0); const int dx = (obj->x_velocity < 0) ? -8 : 8; obj->x_velocity -= dx; @@ -1992,8 +2422,7 @@ static void level_update_objects_bonuses() { } } } else { - bh -= 1; - const int tile_num = level_get_tile(bh * 256 + bl); + const int tile_num = level_get_tile(pos - 0x100); uint8_t al = g_res.level.tile_attributes0[tile_num]; if (al != 0) { obj->x_velocity = -obj->x_velocity; @@ -2801,6 +3230,18 @@ update_pos: } } +static void level_add_object23_bones() { + g_vars.current_bonus.x_pos = g_vars.objects_tbl[1].x_pos; + g_vars.current_bonus.y_pos = g_vars.objects_tbl[1].y_pos - 48; + const int count = g_vars.player_energy * 6 + g_vars.bonus_energy_counter; + if (count != 0) { + g_vars.player_energy = 0; + g_vars.bonus_energy_counter = 0; + g_vars.current_bonus.spr_num = 0x2046; /* bones */ + level_add_object23_bonus(48, -128, count); + } +} + static void level_clear_item(struct object_t *obj) { struct level_item_t *item = obj->data.t.ref; if (item) { @@ -2912,8 +3353,20 @@ static void level_update_player_collision() { const int num = (obj->spr_num & 0x1FFF) - 53; obj->spr_num = 0xFFFF; if (num == 226) { + if (g_vars.level_num == 2) { + g_vars.level_num = 12; + } else if (g_vars.level_num == 13) { + g_vars.level_num = 2; + } else if (g_vars.level_num == 6) { + g_vars.level_num = 14; + } else if (g_vars.level_num == 15) { + g_vars.level_num = 6; + } g_vars.level_completed_flag = 1; return; + } else if (num == 258) { + g_vars.level_completed_flag = 0xFF; /* game completed */ + return; } else if (num == 228) { /* checkpoint */ g_vars.tilemap_start_x_pos = g_vars.objects_tbl[1].x_pos; g_vars.tilemap_start_y_pos = g_vars.objects_tbl[1].y_pos; @@ -2979,8 +3432,8 @@ static void level_update_player_collision() { level_clear_item(obj); } else if (num <= 64) { /* food */ play_sound(4); - if (obj->data.t.unkE >= 128) { - obj->data.t.unkE = -obj->data.t.unkE; + if (obj->data.t.y_velocity >= 128) { + obj->data.t.y_velocity = -obj->data.t.y_velocity; int x = 32; if ((random_get_number() & 1) != 0) { x = -x; @@ -2993,6 +3446,9 @@ static void level_update_player_collision() { ++g_vars.level_items_count_tbl[index]; ++g_vars.level_items_total_count; level_add_object75_score(obj, score_spr_lut[index] + 74); + if (obj->data.t.ref) { + ++g_vars.level_complete_bonuses_count; + } } } else if (num <= 74) { play_sound(8); @@ -3017,15 +3473,7 @@ static void level_update_player_collision() { g_vars.objects_tbl[1].hit_counter = 44; g_vars.objects_tbl[1].data.p.special_anim_num = 0; g_vars.objects_tbl[1].data.p.current_anim_num = 8; - g_vars.current_bonus.x_pos = g_vars.objects_tbl[1].x_pos; - g_vars.current_bonus.y_pos = g_vars.objects_tbl[1].y_pos - 48; - const int count = g_vars.player_energy * 6 + g_vars.bonus_energy_counter; - if (count != 0) { - g_vars.player_energy = 0; - g_vars.bonus_energy_counter = 0; - g_vars.current_bonus.spr_num = 0x2046; - level_add_object23_bonus(48, -128, count); - } + level_add_object23_bones(); g_vars.shake_screen_counter = 7; level_clear_item(obj); level_add_object75_score(obj, 228); @@ -3102,6 +3550,23 @@ static void level_update_player_collision() { g_vars.light.state = 0; } level_clear_item(obj); + } else if (num == 458 || num == 459) { /* boss projectiles */ + if (g_options.cheats & CHEATS_UNLIMITED_ENERGY) { + continue; + } + if (g_vars.player_energy < 1) { + level_player_die(); + } else { + play_sound(1); + g_vars.objects_tbl[1].hit_counter = 44; + g_vars.objects_tbl[1].data.p.special_anim_num = 0; + g_vars.objects_tbl[1].data.p.current_anim_num = 8; + --g_vars.player_energy; + const uint8_t energy = g_vars.player_energy; + g_vars.player_energy = 1; + level_add_object23_bones(); + g_vars.player_energy = energy; + } } } } @@ -3192,8 +3657,8 @@ static void level_update_gates() { g_vars.boss_level5.unk1 = 0; g_vars.boss_level5.energy = 10; g_vars.boss_level5.state = 0; - g_vars.boss_level5.unk4 = 0; - g_vars.boss_level5.unk5 = 1; + g_vars.boss_level5.spr103_pos = 0; + g_vars.boss_level5.spr106_pos = 1; g_vars.boss_level5.unk6 = 0; g_vars.boss_level5.counter = 110; g_vars.boss_level5.unk8 = 8; @@ -3416,12 +3881,18 @@ static void level_draw_objects() { int spr_y_pos = obj->y_pos - ((g_vars.tilemap.y << 4) + g_vars.tilemap.scroll_dy); int spr_h = spr_size_tbl[2 * spr_num + 1]; spr_y_pos -= spr_h; + if (spr_y_pos > TILEMAP_SCREEN_H || spr_y_pos + spr_h < 0) { + continue; + } int spr_x_pos = obj->x_pos - ((g_vars.tilemap.x << 4) + g_vars.tilemap.scroll_dx); int spr_w = spr_offs_tbl[2 * spr_num]; if (spr_hflip) { spr_w = spr_size_tbl[2 * spr_num] - spr_w; } spr_x_pos -= spr_w; + if (spr_x_pos > TILEMAP_SCREEN_W || spr_x_pos + spr_w < 0) { + continue; + } video_draw_sprite(spr_num, spr_x_pos, spr_y_pos, spr_hflip); obj->spr_num |= 0x2000; obj->spr_num &= ~0x4000; @@ -3525,13 +3996,32 @@ static void level_player_death_animation() { static void level_completed_bonuses_animation_draw_score() { const int score_digits = _score_8_digits ? 8 : 7; - const int offset = _score_8_digits ? 0x246 : 0x247; + video_draw_string2(0x230, "SCORE"); int score = g_vars.score * 10; for (int i = 0; i < score_digits; ++i) { const int digit = score % 10; score /= 10; - video_draw_number(offset + (score_digits - 1 - i) * 16 / 8, digit); + video_draw_number(0x23C + (score_digits - 1 - i) * 16 / 8, digit); } + if (g_res.dos_demo) { + return; + } + video_draw_string2(0x410, "LEVEL COMPLETED"); + int percentage = 100; + const int total = g_vars.level_complete_secrets_count + g_vars.level_complete_bonuses_count; + if (total != 0) { + const int current = g_vars.level_current_secrets_count + g_vars.level_current_bonuses_count; + percentage = (current * 100) / total; + } + for (int i = 0; i < 3; ++i) { + const int digit = percentage % 10; + percentage /= 10; + video_draw_number(0x430 + (2 - i) * 16 / 8, digit); + if (percentage == 0) { + break; + } + } + video_draw_character_spr(0x436, 0x1A); } static void level_completed_bonuses_animation_fixup_object4_spr_num() { @@ -3559,10 +4049,10 @@ static int level_completed_bonuses_animation_helper() { if (obj->spr_num == 0xFFFF) { continue; } - int y_velocity = obj->data.p.y_velocity; + int y_velocity = obj->data.t.y_velocity; if (y_velocity < 128) { y_velocity += 8; - obj->data.p.y_velocity = y_velocity; + obj->data.t.y_velocity = y_velocity; } obj->y_pos += y_velocity >> 4; ++bp; @@ -3691,7 +4181,7 @@ static void level_completed_bonuses_animation() { obj->spr_num = 110 + di; obj->x_pos = 155; obj->y_pos = 0; - obj->data.p.y_velocity = 0; + obj->data.t.y_velocity = 0; --g_vars.level_items_count_tbl[di]; ++di; break; diff --git a/p2/resource.c b/p2/resource.c index 4275fb1..c0653d1 100644 --- a/p2/resource.c +++ b/p2/resource.c @@ -251,9 +251,9 @@ void load_leveldat(const uint8_t *p, struct level_t *level) { g_res.level.boss_xmax = READ_LE_UINT16(p); p += 2; g_res.level.boss_counter = *p++; g_res.level.boss_energy = READ_LE_UINT16(p); p += 2; - g_res.level.boss_flag = *p++; - g_res.level.end_x_pos = READ_LE_UINT16(p); p += 2; - g_res.level.end_y_pos = READ_LE_UINT16(p); p += 2; + g_res.level.boss_state = *p++; + g_res.level.boss_x_pos = READ_LE_UINT16(p); p += 2; + g_res.level.boss_y_pos = READ_LE_UINT16(p); p += 2; const int total = p - g_res.leveldat; print_debug(DBG_RESOURCE, "level total offset %d", total); } diff --git a/p2/resource.h b/p2/resource.h index 24aa5b8..089e2b3 100644 --- a/p2/resource.h +++ b/p2/resource.h @@ -153,9 +153,9 @@ struct level_t { uint16_t boss_xmax; uint8_t boss_counter; uint16_t boss_energy; - uint8_t boss_flag; /* !=255: has boss */ - uint16_t end_x_pos; - uint16_t end_y_pos; + uint8_t boss_state; /* !=255: has boss */ + uint16_t boss_x_pos; + uint16_t boss_y_pos; }; struct resource_t { diff --git a/p2/screen.c b/p2/screen.c index c0a83f6..313a40c 100644 --- a/p2/screen.c +++ b/p2/screen.c @@ -88,6 +88,22 @@ void video_draw_number(int offset, int num) { decode_planar(fnt + num * 88, g_res.vga + y * GAME_SCREEN_W + x, GAME_SCREEN_W, 16, 11, 0); } +void video_draw_character_spr(int offset, uint8_t chr) { + const int y = (offset * 8) / 320; + const int x = (offset * 8) % 320; + video_draw_sprite(241 + chr, x, y, 0); +} + +void video_draw_string2(int offset, const char *s) { + while (*s) { + const uint8_t chr = *s++; + if (chr != ' ') { + video_draw_character_spr(offset, chr - 0x41); + } + offset += 2; + } +} + void video_clear() { memset(g_res.vga, 0, GAME_SCREEN_W * GAME_SCREEN_H); } diff --git a/p2/sound.c b/p2/sound.c index d2e6829..2c6f0da 100644 --- a/p2/sound.c +++ b/p2/sound.c @@ -57,6 +57,7 @@ struct mixerchannel_t { static const int _rate = SYS_AUDIO_FREQ; static struct mixerchannel_t _channel; static ModPlugFile *_mpf; +static int _music_num; static uint16_t sound_offsets_tbl[MAX_SOUNDS]; @@ -99,6 +100,7 @@ void sound_init() { sound_offsets_tbl[i] = offset; offset += sound_sizes_tbl[i]; } + _music_num = -1; g_sys.start_audio(mix, 0); } @@ -130,6 +132,9 @@ void play_music(int num) { if (g_res.dos_demo) { /* no .TRK files with demo */ return; } + if (_music_num == num) { + return; + } const char *filename = trk_names_tbl[num]; if (filename) { print_debug(DBG_MIXER, "play_music '%s'", filename); @@ -145,6 +150,7 @@ void play_music(int num) { print_debug(DBG_MIXER, "Loaded module '%s'", ModPlug_GetName(_mpf)); } free(data); + _music_num = num; } g_sys.unlock_audio(); } diff --git a/p2/staticres.c b/p2/staticres.c index e329c4d..3da754d 100644 --- a/p2/staticres.c +++ b/p2/staticres.c @@ -455,46 +455,30 @@ const uint8_t boss_minotaur_seq_data[] = { 0x03,0x03,0x03,0x03,0xF1,0x00,0x00,0xFF,0x03,0x03,0xFE,0x03,0x00,0x00,0x00,0x03, 0x03,0x03,0x03,0x00,0xFE,0x00,0x03,0x03,0x03,0x00,0x00,0x03,0x03,0x03,0xE7,0x04, 0x04,0x04,0x04,0x04,0x04,0x04,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x00,0x00,0x00, - 0xFF,0x00,0x00,0x00,0x00,0xEA,0xA2,0x01,0x9A,0x01,0x95,0x01,0x9F,0x01,0xA0,0x01, - 0x0F,0xA6,0x03,0xA6,0xFD,0xA5,0xF7,0xA5,0x09,0xA6,0xA2,0x01,0x9A,0x01,0x96,0x01, - 0x9D,0x01,0xA0,0x01,0x0F,0xA6,0x03,0xA6,0xFD,0xA5,0xF7,0xA5,0x09,0xA6,0xA2,0x01, - 0x9A,0x01,0x96,0x01,0x9C,0x01,0xA0,0x01,0x0F,0xA6,0x03,0xA6,0xFD,0xA5,0xF7,0xA5, - 0x09,0xA6,0xA1,0x01,0x99,0x01,0x96,0x01,0x9D,0x01,0x9D,0x81,0x0F,0xA6,0x03,0xA6, - 0xFD,0xA5,0xF7,0xA5,0x09,0xA6,0xA2,0x01,0x9B,0x01,0x96,0x01,0x9D,0x01,0x9D,0x81, - 0x0F,0xA6,0x03,0xA6,0xFD,0xA5,0xF7,0xA5,0x09,0xA6,0xA2,0x01,0x99,0x01,0x98,0x81, - 0x9E,0x01,0x9E,0x81,0x0F,0xA6,0x03,0xA6,0xFD,0xA5,0xF7,0xA5,0x09,0xA6,0xA2,0x01, - 0x9A,0x01,0x98,0x01,0x9C,0x01,0x9D,0x81,0x0F,0xA6,0x03,0xA6,0xFD,0xA5,0xF7,0xA5, - 0x09,0xA6,0xA2,0x01,0x9A,0x01,0x95,0x01,0x9E,0x01,0x9E,0x81,0x0F,0xA6,0x03,0xA6, - 0xFD,0xA5,0xF7,0xA5,0x09,0xA6,0xA2,0x01,0x99,0x01,0x95,0x01,0x9E,0x01,0x9D,0x81, - 0x0F,0xA6,0x03,0xA6,0xFD,0xA5,0xF7,0xA5,0x09,0xA6,0xA3,0x01,0x99,0x01,0x95,0x01, - 0x9E,0x01,0x9D,0x81,0x0F,0xA6,0x03,0xA6,0xFD,0xA5,0xF7,0xA5,0x09,0xA6,0xA2,0x01, - 0x99,0x01,0x95,0x01,0x9E,0x01,0xA0,0x01,0x0F,0xA6,0x03,0xA6,0xFD,0xA5,0xF7,0xA5, - 0x09,0xA6,0xA3,0x01,0x99,0x01,0x95,0x01,0x9D,0x01,0xA0,0x01,0x0F,0xA6,0x03,0xA6, - 0xFD,0xA5,0xF7,0xA5,0x09,0xA6,0xA3,0x01,0x9B,0x01,0x95,0x01,0x9E,0x01,0x9D,0x81, - 0x0F,0xA6,0x03,0xA6,0xFD,0xA5,0xF7,0xA5,0x09,0xA6,0xA3,0x01,0x9B,0x01,0x95,0x01, - 0x9D,0x01,0x9D,0x81,0x0F,0xA6,0x03,0xA6,0xFD,0xA5,0xF7,0xA5,0x09,0xA6,0xA3,0x01, - 0x9B,0x01,0x95,0x01,0x9D,0x01,0xA0,0x01,0x0F,0xA6,0x03,0xA6,0xFD,0xA5,0xF7,0xA5, - 0x09,0xA6,0xA2,0x01,0x9A,0x01,0x98,0x01,0x9C,0x01,0x9D,0x81,0x0F,0xA6,0x03,0xA6, - 0xFD,0xA5,0xF7,0xA5,0x09,0xA6,0xA2,0x01,0x9A,0x01,0x97,0x01,0x9E,0x01,0x9D,0x81, - 0x0F,0xA6,0x03,0xA6,0xFD,0xA5,0xF7,0xA5,0x09,0xA6,0xA2,0x01,0x9A,0x01,0x97,0x01, - 0x9D,0x01,0x9E,0x81,0x0F,0xA6,0x03,0xA6,0xFD,0xA5,0xF7,0xA5,0x09,0xA6,0xA3,0x01, - 0x9A,0x01,0x95,0x01,0xA0,0x81,0x9D,0x81,0x0F,0xA6,0x03,0xA6,0xFD,0xA5,0x09,0xA6, - 0xF7,0xA5,0xA2,0x01,0x99,0x01,0xF8,0xE9,0xA2,0x01,0x9A,0x01,0xFB,0xEB,0xA2,0x01, - 0x9B,0x01,0xF6,0xE8,0xA1,0x01,0x99,0x01,0xF6,0xDB,0xA1,0x01,0x9A,0x01,0x01,0xDD, - 0xA1,0x01,0x9B,0x01,0xF3,0xDA,0xA3,0x01,0x99,0x01,0xED,0xF0,0xA3,0x01,0x9A,0x01, - 0xF0,0xF2,0xA3,0x01,0x9B,0x01,0xEB,0xF0,0x99,0x01,0x95,0x01,0xFF,0xE8,0x9A,0x01, - 0x95,0x01,0xFB,0xEA,0x9B,0x01,0x95,0x01,0xF8,0xF7,0x99,0x01,0x96,0x01,0xFF,0xE8, - 0x9A,0x01,0x96,0x01,0xFB,0xEA,0x9B,0x01,0x96,0x01,0xF8,0xF7,0x99,0x01,0x97,0x01, - 0xFF,0xE8,0x9A,0x01,0x97,0x01,0xFB,0xEA,0x9B,0x01,0x97,0x01,0xF8,0xF7,0x99,0x01, - 0x98,0x01,0xFF,0xE8,0x9A,0x01,0x98,0x01,0xFB,0xEA,0x9B,0x01,0x98,0x01,0xF8,0xF7, - 0x99,0x01,0x98,0x81,0xFF,0xE8,0x9A,0x01,0x98,0x81,0xFB,0xEA,0x9B,0x01,0x98,0x81, - 0xF8,0xF7,0x99,0x01,0x9C,0x01,0xE2,0xEC,0x99,0x01,0x9D,0x01,0xDD,0xF1,0x99,0x01, - 0x9E,0x01,0xD6,0xE9,0x99,0x01,0x9F,0x01,0xE8,0x0C,0x9A,0x01,0x9C,0x01,0xE2,0xEC, - 0x9A,0x01,0x9D,0x01,0xE0,0xF1,0x9A,0x01,0x9E,0x01,0xD4,0xEA,0x9A,0x01,0x9F,0x01, - 0xEA,0x0F,0x9B,0x01,0x9C,0x01,0xDE,0xF8,0x9B,0x01,0x9D,0x01,0xDC,0x02,0x9B,0x01, - 0x9E,0x01,0xCE,0xFB,0x9B,0x01,0x9F,0x01,0xE6,0x1D,0x99,0x01,0x9D,0x81,0x21,0xEB, - 0x99,0x01,0x9E,0x81,0x2C,0xE4,0x99,0x01,0xA0,0x01,0x12,0x10,0x9A,0x01,0x9D,0x81, - 0x18,0xEE,0x9A,0x01,0x9E,0x81,0x29,0xEA,0x9A,0x01,0xA0,0x01,0x09,0x13,0x9B,0x01, - 0x9D,0x81,0x1A,0xF9,0x9B,0x01,0x9E,0x81,0x29,0xF3,0x9B,0x01,0xA0,0x01,0x0C,0x1E, - 0x9A,0x01,0xA0,0x81,0xEC,0x16 + 0xFF,0x00,0x00,0x00,0x00,0xEA +}; +const uint16_t boss_gorilla_data[] = { + 0x01A2,0x019A,0x0195,0x019F,0x01A0,0xA60F,0xA603,0xA5FD,0xA5F7,0xA609,0x01A2,0x019A,0x0196,0x019D,0x01A0,0xA60F, + 0xA603,0xA5FD,0xA5F7,0xA609,0x01A2,0x019A,0x0196,0x019C,0x01A0,0xA60F,0xA603,0xA5FD,0xA5F7,0xA609,0x01A1,0x0199, + 0x0196,0x019D,0x819D,0xA60F,0xA603,0xA5FD,0xA5F7,0xA609,0x01A2,0x019B,0x0196,0x019D,0x819D,0xA60F,0xA603,0xA5FD, + 0xA5F7,0xA609,0x01A2,0x0199,0x8198,0x019E,0x819E,0xA60F,0xA603,0xA5FD,0xA5F7,0xA609,0x01A2,0x019A,0x0198,0x019C, + 0x819D,0xA60F,0xA603,0xA5FD,0xA5F7,0xA609,0x01A2,0x019A,0x0195,0x019E,0x819E,0xA60F,0xA603,0xA5FD,0xA5F7,0xA609, + 0x01A2,0x0199,0x0195,0x019E,0x819D,0xA60F,0xA603,0xA5FD,0xA5F7,0xA609,0x01A3,0x0199,0x0195,0x019E,0x819D,0xA60F, + 0xA603,0xA5FD,0xA5F7,0xA609,0x01A2,0x0199,0x0195,0x019E,0x01A0,0xA60F,0xA603,0xA5FD,0xA5F7,0xA609,0x01A3,0x0199, + 0x0195,0x019D,0x01A0,0xA60F,0xA603,0xA5FD,0xA5F7,0xA609,0x01A3,0x019B,0x0195,0x019E,0x819D,0xA60F,0xA603,0xA5FD, + 0xA5F7,0xA609,0x01A3,0x019B,0x0195,0x019D,0x819D,0xA60F,0xA603,0xA5FD,0xA5F7,0xA609,0x01A3,0x019B,0x0195,0x019D, + 0x01A0,0xA60F,0xA603,0xA5FD,0xA5F7,0xA609,0x01A2,0x019A,0x0198,0x019C,0x819D,0xA60F,0xA603,0xA5FD,0xA5F7,0xA609, + 0x01A2,0x019A,0x0197,0x019E,0x819D,0xA60F,0xA603,0xA5FD,0xA5F7,0xA609,0x01A2,0x019A,0x0197,0x019D,0x819E,0xA60F, + 0xA603,0xA5FD,0xA5F7,0xA609,0x01A3,0x019A,0x0195,0x81A0,0x819D,0xA60F,0xA603,0xA5FD,0xA609,0xA5F7 +}; +const uint16_t boss_gorilla_spr_tbl[] = { + 0x01A2,0x0199,0xE9F8,0x01A2,0x019A,0xEBFB,0x01A2,0x019B,0xE8F6,0x01A1,0x0199,0xDBF6,0x01A1,0x019A,0xDD01,0x01A1, + 0x019B,0xDAF3,0x01A3,0x0199,0xF0ED,0x01A3,0x019A,0xF2F0,0x01A3,0x019B,0xF0EB,0x0199,0x0195,0xE8FF,0x019A,0x0195, + 0xEAFB,0x019B,0x0195,0xF7F8,0x0199,0x0196,0xE8FF,0x019A,0x0196,0xEAFB,0x019B,0x0196,0xF7F8,0x0199,0x0197,0xE8FF, + 0x019A,0x0197,0xEAFB,0x019B,0x0197,0xF7F8,0x0199,0x0198,0xE8FF,0x019A,0x0198,0xEAFB,0x019B,0x0198,0xF7F8,0x0199, + 0x8198,0xE8FF,0x019A,0x8198,0xEAFB,0x019B,0x8198,0xF7F8,0x0199,0x019C,0xECE2,0x0199,0x019D,0xF1DD,0x0199,0x019E, + 0xE9D6,0x0199,0x019F,0x0CE8,0x019A,0x019C,0xECE2,0x019A,0x019D,0xF1E0,0x019A,0x019E,0xEAD4,0x019A,0x019F,0x0FEA, + 0x019B,0x019C,0xF8DE,0x019B,0x019D,0x02DC,0x019B,0x019E,0xFBCE,0x019B,0x019F,0x1DE6,0x0199,0x819D,0xEB21,0x0199, + 0x819E,0xE42C,0x0199,0x01A0,0x1012,0x019A,0x819D,0xEE18,0x019A,0x819E,0xEA29,0x019A,0x01A0,0x1309,0x019B,0x819D, + 0xF91A,0x019B,0x819E,0xF329,0x019B,0x01A0,0x1E0C,0x019A,0x81A0,0x16EC };