/* bosses logic : gorilla, tree, minotaur */ #include "game.h" #include "resource.h" #include "util.h" static void level_update_objects_boss_energy(int count) { if (count > 8) { count = 8; } if (count != 0) { play_music(13); for (int i = 0; i < count; ++i) { struct object_t *obj = &g_vars.objects_tbl[108 + i]; obj->spr_num = 0x135; obj->x_pos = 8 + i * 5; obj->y_pos = 170; } } for (int i = count; i < 8; ++i) { g_vars.objects_tbl[108 + i].spr_num = 0xFFFF; } } 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; if ((g_options.cheats & CHEATS_UNLIMITED_ENERGY) == 0) { --g_vars.player_energy; if (g_vars.player_energy < 0) { level_player_die(); } } } 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; /* 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_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; } const int count = 4 - g_res.level.boss_speed; if (g_vars.boss.change_counter < count) { g_vars.boss.change_counter = 0; } else { g_vars.boss.change_counter -= count; } if (g_options.cheats & CHEATS_NO_HIT) { return; } level_update_objects_boss_hit_player(); play_sound(1); 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) { int y_vel = -64; if (g_vars.input.key_up != 0) { play_sound(3); y_vel = -128; } g_vars.objects_tbl[1].data.p.y_velocity = y_vel; } } } static void level_update_boss_gorilla_tick() { if (g_vars.boss.change_counter < UCHAR_MAX) { ++g_vars.boss.change_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; for (int i = 2; i <= 4; ++i) { g_vars.boss.parts[i].spr_num = *p++; d = level_update_boss_gorilla_find_pos(g_vars.boss.parts[1].spr_num, g_vars.boss.parts[i].spr_num); dx = d & 255; if (g_vars.boss.hdir) { dx = -dx; } g_vars.boss.parts[i].x_pos = dx + g_vars.boss.parts[1].x_pos; dy = d >> 8; g_vars.boss.parts[i].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) { for (int i = 0; i < 5; ++i) { g_vars.boss.parts[i].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++; switch (addr) { case 0xA609: g_vars.boss.obj1 = obj; break; case 0xA60F: g_vars.boss.obj2 = obj; break; case 0xA603: g_vars.boss.obj3 = obj; break; } 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; } play_sound(8); 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() { if (g_vars.objects_tbl[103].spr_num == 0xFFFF || g_res.level.boss_state == 6 || (g_vars.objects_tbl[103].spr_num & 0x2000) == 0) { level_update_objects_boss_energy(0); } else { level_update_objects_boss_energy(g_res.level.boss_energy >> 3); } 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.boss_y_pos &= ~15; if (g_vars.boss.y_velocity != 0) { g_vars.shake_screen_counter = 7; } g_vars.boss.y_velocity = 0; g_vars.boss.x_velocity = 0; } else { if (g_vars.boss.y_velocity < 224) { g_vars.boss.y_velocity += 16; } } } else { if (g_vars.boss.y_velocity < 224) { g_vars.boss.y_velocity += 16; } } 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.boss_y_pos) > 250) { return; } if (g_res.level.boss_state == 0) { /* waiting player to be near */ if (g_vars.boss.x_dist >= 250) { g_vars.boss.change_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 = 2; 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.change_counter >= 10) { g_res.level.boss_state = 3; g_vars.boss.state_counter = 0; } else if (g_vars.boss.change_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 != 44) { if (g_vars.boss.state_counter > 44) { 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.change_counter = 11; } else if (g_vars.boss.state_counter == 88) { 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_speed * 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 > 88) { if (g_vars.boss.y_velocity == 0) { g_vars.boss.change_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 < 40 && 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) { /* attack player */ 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.change_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) { /* hit by player */ ++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) { /* defeated */ 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; for (int i = 0; i < 5; ++i) { g_vars.objects_tbl[103 + i].spr_num = 0xFFFF; } g_vars.current_bonus.x_pos = g_vars.objects_tbl[103].x_pos; g_vars.current_bonus.y_pos = g_vars.objects_tbl[103].y_pos; level_add_bonuses_4x(); g_vars.current_bonus.x_pos += 32; g_vars.current_bonus.y_pos += 32; level_add_bonuses_4x(); g_vars.current_bonus.x_pos -= 64; level_add_bonuses_4x(); g_vars.current_bonus.x_pos -= 32; g_vars.current_bonus.y_pos -= 64; level_add_bonuses_4x(); g_vars.current_bonus.spr_num = 99; /* end of level lighter */ level_add_object23_bonus(0, 0, 1); 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_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_leaf_t *leaf = &g_vars.boss_level5.leaf_tbl[i]; struct object_t *obj = &g_vars.objects_tbl[98 + i]; if (leaf->spr_num == 0xFFFF) { continue; } if (g_vars.restart_level_flag == 0 && level_objects_collide(obj_player, obj)) { 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 = -128; level_update_objects_boss_hit_player(); leaf->spr_num = 0xFFFF; obj->spr_num = 0xFFFF; } else { leaf->y_pos += leaf->y_delta >> 4; leaf->x_delta += leaf->dir; int dx = leaf->x_delta; if (dx >= 32 || dx < -32) { leaf->dir = -leaf->dir; } uint16_t spr_num = leaf->spr_num; dx >>= 4; if (dx != 0) { spr_num += (dx > 0) ? 1 : 2; } leaf->x_pos += dx; obj->x_pos = leaf->x_pos; obj->y_pos = leaf->y_pos; if (obj->y_pos > 2008) { leaf->spr_num = 0xFFFF; obj->spr_num = 0xFFFF; } else { obj->spr_num = spr_num; } } } g_vars.objects_tbl[105].spr_num = 0xFFFF; g_vars.objects_tbl[104].spr_num = 0xFFFF; if (g_vars.boss_level5.state < 2) { static const uint16_t pos2_data[] = { 0x3D5, 0x7C1, 0x1AA, 0x3FA, 0x7B6, 0x1AB, 0x3E8, 0x7A8, 0x1AC, 0x3FF, 0x788, 0x1AD, 0x3D9, 0x79B, 0x1AE, 0x3FF, 0x7A8, 0x1AF, 0x405, 0x7A9, 0x1B7, 0, 0, 0xFFFF }; const uint16_t *p = &pos2_data[g_vars.boss_level5.state * 6]; g_vars.objects_tbl[105].x_pos = p[0]; g_vars.objects_tbl[105].y_pos = p[1]; g_vars.objects_tbl[105].spr_num = p[2]; g_vars.objects_tbl[104].x_pos = p[3]; g_vars.objects_tbl[104].y_pos = p[4]; g_vars.objects_tbl[104].spr_num = p[5]; } static const uint16_t pos1_data[] = { 0x3A9, 0x7D4, 0x1A4, 0x3D1, 0x7B9, 0x1A5, 0x3A2, 0x7A6, 0x1A6, 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.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]; g_vars.objects_tbl[106].x_pos = p[3]; g_vars.objects_tbl[106].y_pos = p[4]; g_vars.objects_tbl[106].spr_num = p[5]; 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.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.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.spr106_pos; g_vars.boss_level5.tick_counter += 5; } --g_vars.boss_level5.unk1; } else if (g_vars.boss_level5.tick_counter == 0) { ++g_vars.boss_level5.unk6; if (g_vars.boss_level5.unk6 > 2) { g_vars.boss_level5.unk6 = 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.spr106_pos != 1) { ah = 3; g_vars.shake_screen_counter = 4; obj_player->x_pos += 2; for (int i = 0; i < 5; ++i) { struct boss_level5_leaf_t *leaf = &g_vars.boss_level5.leaf_tbl[i]; if (leaf->spr_num == 0xFFFF) { leaf->spr_num = 0x1B3; leaf->y_pos = obj_player->y_pos - 150; const uint8_t r = random_get_number(); const int dx = r & 0x7C; leaf->x_pos = obj_player->x_pos + (((r & 4) != 0) ? dx : -dx); leaf->y_delta = ((r & 3) + 1) << 4; leaf->x_delta = 0; leaf->dir = 4; break; } } } g_vars.boss_level5.tick_counter = ah; --g_vars.boss_level5.idle_counter; if (g_vars.boss_level5.idle_counter == 0) { g_vars.boss_level5.idle_counter = (random_get_number() & 15) << 3; g_vars.boss_level5.tick_counter = 64 + ((random_get_number() & 15) << 3); g_vars.boss_level5.unk6 = 0; } if (g_vars.restart_level_flag == 0) { if (level_objects_collide(obj_player, &g_vars.objects_tbl[107]) || level_objects_collide(obj_player, &g_vars.objects_tbl[106])) { if (obj_player->y_pos <= 1979) { obj_player->data.p.y_velocity = -160; } else { obj_player->data.p.y_velocity = -160; obj_player->hit_counter = 44; g_vars.player_anim_0x40_flag = 0; obj_player->x_friction = 3; obj_player->x_velocity = -128; level_update_objects_boss_hit_player(); level_update_objects_boss_hit_player(); level_update_objects_boss_hit_player(); } } } } else if (g_vars.restart_level_flag == 0) { if (level_objects_collide(obj_player, &g_vars.objects_tbl[107]) && g_vars.player_jump_monster_flag) { obj_player->data.p.y_velocity = g_vars.input.key_up ? -128 : -64; } } if (g_vars.restart_level_flag == 0) { struct object_t *_si = &g_vars.objects_tbl[103 + g_vars.boss_level5.state * 2]; for (int i = 0; i < 6; ++i) { struct object_t *_di = &g_vars.objects_tbl[i]; if (_di->spr_num == 0xFFFF || _di == obj_player) { continue; } g_vars.player_using_club_flag = 1; const bool ret = level_objects_collide(_si, _di); g_vars.player_using_club_flag = 0; if (ret) { _si->spr_num ^= 0x4000; if (g_vars.boss_level5.state != 0) { _si[-1].spr_num ^= 0x4000; } _di->spr_num = 0xFFFF; g_vars.boss_level5.unk1 = 6; if (g_vars.boss_level5.state < 2) { g_vars.boss_level5.unk6 = 3; g_vars.boss_level5.spr106_pos = 3; } --g_vars.boss_level5.energy; if (g_vars.boss_level5.energy == 0) { g_vars.boss_level5.energy = 7; ++g_vars.boss_level5.state; if (g_vars.boss_level5.state == 3) { /* boss defeated */ g_vars.current_bonus.x_pos = g_vars.objects_tbl[103].x_pos - 200; g_vars.current_bonus.y_pos = g_vars.objects_tbl[103].y_pos; level_add_bonuses_4x(); g_vars.current_bonus.x_pos += 32; g_vars.current_bonus.y_pos += 32; level_add_bonuses_4x(); g_vars.current_bonus.x_pos -= 64; level_add_bonuses_4x(); g_vars.current_bonus.x_pos -= 32; g_vars.current_bonus.y_pos -= 64; level_add_bonuses_4x(); g_vars.current_bonus.spr_num = 99; /* end of level lighter */ level_add_object23_bonus(0, 0, 1); for (int i = 0; i < 5; ++i) { g_vars.objects_tbl[98 + i].spr_num = 0xFFFF; g_vars.objects_tbl[103 + i].spr_num = 0xFFFF; } } } break; } } if (obj_player->x_pos > 984) { obj_player->hit_counter = 44; g_vars.player_anim_0x40_flag = 0; obj_player->data.p.y_velocity = -144; obj_player->x_friction = 3; obj_player->x_velocity = -160; level_update_objects_boss_hit_player(); } } if (g_vars.boss_level5.tick_counter > 0) { --g_vars.boss_level5.tick_counter; } } 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_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; int x_vel = 32; int y_vel = -160; for (int i = 0; i < 4; ++i) { level_add_object23_bonus(x_vel, y_vel, 1); x_vel = -x_vel; if (x_vel >= 0) { x_vel -= 16; y_vel -= 16; } } } 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) { 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.y_velocity = 0; break; } } 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) { 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.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 }; if (!g_vars.boss_level9.seq) { g_vars.boss_level9.anim = data; g_vars.boss_level9.energy = 24; g_vars.boss_level9.seq_counter = 0; g_vars.boss_level9.hit_counter = 3; } 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.seq_counter == 0) { const uint16_t *p = g_vars.boss_level9.anim; if (p[0] == 0xFFFF) { p = data; } assert(p[0] >= 0xA70F); g_vars.boss_level9.seq = &boss_minotaur_seq_data[p[0] - 0xA70F]; g_vars.boss_level9.seq_counter = p[1]; g_vars.boss_level9.anim = p + 2; } for (int i = 0; i < 4; ++i) { struct object_t *obj = &g_vars.objects_tbl[2 + i]; if (obj->spr_num == 0xFFFF) { continue; } 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.energy > 0) { --g_vars.boss_level9.energy; } if (g_vars.boss_level9.energy == 0) { level_update_boss_minotaur_add_spr_0x137(); } ++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; } const uint8_t *p = g_vars.boss_level9.seq; while (1) { if (*p == 0xFF) { level_update_boss_minotaur_add_spr_0x1CA(); ++p; } else if (*p == 0xFE) { level_update_boss_minotaur_add_spr_0x1CB(); ++p; } else if (*p == 0xFD) { play_sound(2); ++p; } else if ((*p & 0x80) == 0) { ++g_vars.boss_level9.seq; level_update_boss_minotaur_set_frame(*p); break; } else { if (g_vars.boss_level9.seq_counter > 0) { --g_vars.boss_level9.seq_counter; } p += (int8_t)*p; g_vars.boss_level9.seq = p; } } } void boss_update() { if (g_res.level.boss_state != 0xFF) { level_update_boss_gorilla(); } if (g_vars.level_num == 5 && (g_res.level.scrolling_mask & ~1) == 0) { level_update_boss_tree(); } if (g_vars.level_num == 9) { level_update_boss_minotaur(); } }