From 5d1b8f82cf26fc215500a2039ec4f29326914df1 Mon Sep 17 00:00:00 2001 From: kichikuou Date: Sat, 11 Jan 2025 09:45:24 +0900 Subject: [PATCH] Refactor NACT_Sys1 introducing inheritance-based structure System1 commands have very different meanings from game to game (especially the Y and Z commands), so this moves game-specific logic to separate derived classes (e.g., NACT_LittleVampire, NACT_GakuenSenki) for better modularization. --- src/sys/game_id.h | 3 - src/sys/nact_sys1.cpp | 1044 +++++++++++++++++++++++------------------ 2 files changed, 591 insertions(+), 456 deletions(-) diff --git a/src/sys/game_id.h b/src/sys/game_id.h index 54b4a66..c9e5b43 100644 --- a/src/sys/game_id.h +++ b/src/sys/game_id.h @@ -90,9 +90,6 @@ struct GameId { bool is_sdps() const { return game == SDPS_MARIA || game == SDPS_TONO || game == SDPS_KAIZOKU; } - bool is_rance2() const { - return game == RANCE2 || game == RANCE2_HINT; - } bool is_rance4x() const { return game == RANCE41 || game == RANCE42; } diff --git a/src/sys/nact_sys1.cpp b/src/sys/nact_sys1.cpp index 959b73c..225496f 100644 --- a/src/sys/nact_sys1.cpp +++ b/src/sys/nact_sys1.cpp @@ -23,9 +23,9 @@ namespace { -class NACT_Sys1 final : public NACT { +class NACT_Sys1 : public NACT { public: - NACT_Sys1(const Config& config, const GameId& game_id); + NACT_Sys1(const Config& config, const GameId& game_id) : NACT(config, game_id) {} protected: void cmd_branch() override; @@ -50,21 +50,12 @@ protected: void cmd_w() override; void cmd_y() override; void cmd_z() override; - void opening() override; uint16 cali() override; + void exec_y(int cmd, int param); + private: void cmd_open_obj(int verb); - - // Intruder Zコマンド - int paint_x; - int paint_y; - int map_page; - - // GAKUEN - bool enable_graphics = true; - // Rance 2 - int last_paint_x = -1, last_paint_y = -1; }; #define WAIT(tm) \ @@ -110,58 +101,6 @@ private: } \ } -NACT_Sys1::NACT_Sys1(const Config& config, const GameId& game_id) - : NACT(config, game_id) -{ - if (game_id.is_system1_dps()) { - text_refresh = false; - strcpy(tvar[0], strings.dps_custom.c_str()); - strcpy(tvar[1], strings.dps_linus.c_str()); - strcpy(tvar[2], strings.dps_katsumi.c_str()); - strcpy(tvar[3], strings.dps_yumiko.c_str()); - strcpy(tvar[4], strings.dps_itsumi.c_str()); - strcpy(tvar[5], strings.dps_hitomi.c_str()); - strcpy(tvar[6], strings.dps_mariko.c_str()); - } else if (game_id.is(GameId::INTRUDER)) { - paint_x = paint_y = map_page = 0; - } -} - -void NACT_Sys1::opening() -{ - switch (game_id.game) { - case GameId::CRESCENT: - mako->play_music(2); - ags->load_cg(1, -1); - WAIT(3000); - ags->load_cg(81, -1); - break; - case GameId::INTRUDER: - ags->load_cg(77, -1); - WAIT(1667); - ags->load_cg(74, -1); - break; - case GameId::LITTLE_VAMPIRE: - mako->play_music(4); - ags->load_cg(3, -1); - WAIT(2000); - ags->load_cg(38, -1); - cmd_a(); - ags->draw_box(0); - break; - case GameId::GAKUEN: - mako->set_cd_track(1, 1); - ags->load_cg(302, -1); - WAIT(3000); - ags->load_cg(303, -1); - WAIT(3000); - mako->play_music(1); - ags->load_cg(304, -1); - WAIT(4000); - break; - } -} - void NACT_Sys1::cmd_branch() { int condition = cali(); @@ -460,28 +399,7 @@ void NACT_Sys1::cmd_g() TRACE("G %d:", page); - if (game_id.is(GameId::INTRUDER)) { - page = (page == 97) ? 96 : (page == 98) ? 97 : page; - } else if (game_id.is(GameId::GAKUEN)) { - page = (page == 3) ? 94 : page; - } - - if (enable_graphics) - ags->load_cg(page, -1); - - if (game_id.is(GameId::INTRUDER)) { - if(page == 94) { - WAIT(400); - } - if(page == 81 || page == 82) { - map_page = page; - } - } - - if (game_id.is_rance2()) { - last_paint_x = -1; - last_paint_y = -1; - } + ags->load_cg(page, -1); } void NACT_Sys1::cmd_h() @@ -582,15 +500,7 @@ void NACT_Sys1::cmd_u() TRACE("U %d,%d:", page, transparent); - if (game_id.is(GameId::INTRUDER)) { - ags->load_cg(page, 11); - - if(page == 5) { - WAIT(400); - } - } else { - ags->load_cg(page, transparent); - } + ags->load_cg(page, transparent); } void NACT_Sys1::cmd_v() @@ -611,374 +521,73 @@ void NACT_Sys1::cmd_y() int param = cali(); TRACE("Y %d,%d:", cmd, param); + exec_y(cmd, param); +} - if (game_id.is(GameId::INTRUDER)) { - switch(cmd) { - case 0: - ags->clear_text_window(text_window, true); - break; - case 1: - if(show_push) { - ags->draw_push(text_window); - } - for(;;) { - if(terminate) { - return; - } - if(get_key()) { - break; - } - sys_sleep(16); - } - sys_sleep(100); - for(;;) { - if(terminate) { - return; - } - if(!(get_key() & 0x18)) { - break; - } - sys_sleep(16); - } - ags->clear_text_window(text_window, true); - break; - case 2: - if(param == 0) { - quit(NACT_HALT); - } else { - WAIT(100) - } - break; - case 3: - WAIT(param * 1000) - break; - case 255: - quit(NACT_HALT); - break; - default: - WARNING("Unimplemented command: Y %d, %d", cmd, param); - break; - } - } else if (game_id.is(GameId::GAKUEN)) { - switch (cmd) { - case 1: - RND = (param == 0 || param == 1) ? 0 : random(param); - break; - case 2: - // Clear variables up to param, though in practice param is always 21. - for (int i = 0; i <= param; i++) { - var[i] = 0; - } - break; - case 3: - { - char buf[16]; - snprintf(buf, sizeof(buf), "%d", param); - ags->draw_text(buf); - } - break; - case 4: - if (param == 0) { - mako->play_music(1); - } else { - mako->stop_music(); - } - break; - case 5: - enable_graphics = (param == 0); - break; - case 6: - // Dummied. 0 - "Display text slowly," 1 - disable. - break; - case 7: - // Dummied. 0 - "Switch to interlace mode," 1 - disable. - break; - default: - WARNING("Unimplemented command: Y %d, %d", cmd, param); - break; - } - } else { - switch(cmd) { - case 0: - ags->clear_text_window(text_window, true); - break; - case 1: - { - char buf[16]; - buf[0] = '0' + (int)(param / 10000); - param %= 10000; - buf[1] = '0' + (int)(param / 1000); - param %= 1000; - buf[2] = '0' + (int)(param / 100); - param %= 100; - buf[3] = '0' + (int)(param / 10); - buf[4] = '0' + (param % 10); - buf[5] = '\0'; +void NACT_Sys1::exec_y(int cmd, int param) +{ + switch(cmd) { + case 0: + ags->clear_text_window(text_window, true); + break; + case 1: + { + char buf[16]; + buf[0] = '0' + (int)(param / 10000); + param %= 10000; + buf[1] = '0' + (int)(param / 1000); + param %= 1000; + buf[2] = '0' + (int)(param / 100); + param %= 100; + buf[3] = '0' + (int)(param / 10); + buf[4] = '0' + (param % 10); + buf[5] = '\0'; - int p = 0; - for(;;) { - if(buf[p] != '0') { - break; - } - p++; - } - ags->draw_text(&buf[p < 4 ? p : 4]); + int p = 0; + for(;;) { + if(buf[p] != '0') { + break; } - break; - case 2: - if (game_id.is(GameId::CRESCENT)) { - for(int i = 39; i <= 48; i++) { - var[i] = 0; - } - } else if (game_id.is_system1_dps()) { - for(int i = 0; i <= 20; i++) { - var[i] = 0; - } - } else { - for(int i = 0; i <= 16; i++) { - var[i] = 0; - } - } - break; - case 3: - WAIT(param * 1000 / 60) - break; - case 4: - RND = (param == 0 || param == 1) ? 0 : random(param); - break; - case 7: - if (game_id.is_system1_dps()) { - if(param == 1) { - ags->box_fill(0, 40, 8, 598, 270, 0); - } - } else if (game_id.is_rance2()) { - if (param == 0) { - // Unknown - } - else if (param == 6) { - ags->box_fill(0, 0, 0, 424, 276, 0); - } - else if (param == 7) { - ags->box_fill(0, 0, 0, 640, 276, 0); - } - } - break; - case 12: - if (game_id.is_rance2()) { - // Y 12, 0: is supposed to activate an automatic text - // progression feature, but it doesn't seem to work on the - // PC-98? - } - break; - case 13: - if (game_id.is_rance2()) { - // Sets the speed of the auto text progression feature (Y 12). - // The options in Rance 2 are 1 (the fastest), 5, and 10 (the - // slowest). Not available in the Hint Disk. - } - break; - case 20: - if (game_id.is_rance2()) { - ags->load_cg(param, -1); - } - break; - case 130: - if (game_id.is(GameId::TENGU)) { - WAIT(2000) - ags->load_cg(180, -1); - - WAIT(2000) - ags->draw_box(0); - ags->dest_screen = 1; - ags->load_cg(173, -1); - ags->dest_screen = 0; - ags->gcopy(0x1ecd, 0x3a2a, 8, 60, 3); - WAIT(667); - ags->gcopy(0x1b65, 0x262e, 8, 60, 3); - ags->gcopy(0x2e25, 0x38ee, 7, 9, 3); - WAIT(667); - ags->gcopy(0x1914, 0x1914, 8, 63, 3); - WAIT(667); - ags->gcopy(0x182c, 0x182c, 7, 50, 3); - WAIT(667); - ags->gcopy(0x0000, 0x1ecd, 32, 60, 1); - WAIT(667); - for(int i = 12; i <= 57; i++) { - ags->gcopy(i + 0x2306, i + 0x2306, 1, 165, 3); - } - WAIT_KEYQUIT(6000) - ags->draw_box(param); - } - break; - case 240: - ags->draw_hankaku = (param == 1) ? true : false; - break; - case 253: - quit(0); - break; - case 254: - RND = 0; - break; - case 255: - if (game_id.is_system1_dps()) { - quit(0); - } else { - quit(NACT_HALT); - } - break; - default: - WARNING("Unimplemented command: Y %d, %d", cmd, param); - break; + p++; + } + ags->draw_text(&buf[p < 4 ? p : 4]); } + break; + case 2: + for (int i = 0; i <= 16; i++) { + var[i] = 0; + } + break; + case 3: + WAIT(param * 1000 / 60) + break; + case 4: + RND = (param == 0 || param == 1) ? 0 : random(param); + break; + case 240: + ags->draw_hankaku = (param == 1) ? true : false; + break; + case 253: + quit(0); + break; + case 254: + RND = 0; + break; + case 255: + quit(NACT_HALT); + break; + default: + WARNING("Unimplemented command: Y %d, %d", cmd, param); + break; } } void NACT_Sys1::cmd_z() { - if (game_id.is(GameId::CRESCENT)) { - cmd_y(); - return; - } int cmd = cali(); int param = cali(); - TRACE("Z %d,%d:", cmd, param); - - switch (game_id.game) { - case GameId::INTRUDER: - if(cmd == 1 && 1 <= param && param <= 4) { - const char* buf[4] = { - "\x81\x83", // "<" in SJIS - "\x81\xC8", // "∧" in SJIS - "\x81\x84", // ">" in SJIS - "\x81\xC9", // "∨" in SJIS - }; - - // 矢印を消去する - if(map_page) { - ags->load_cg(map_page, -1); - if(paint_x) { - ags->paint(paint_x, paint_y * 2, 2 + 16); - } - } - - // 矢印を表示する - int x = ags->text_dest_x; - int y = ags->text_dest_y; - int color = ags->text_font_color; - ags->text_font_size = 24; - ags->text_font_color = 16; - - ags->text_dest_x = 456; - ags->text_dest_y = 103; - ags->draw_text(buf[param - 1]); - - ags->text_dest_x = x; - ags->text_dest_y = y; - ags->text_font_size = 16; - ags->text_font_color = color; - } else if(cmd == 2) { - if (param == 0) { - // Clear the screen - ags->box_fill(0, 0, 0, 640, 400, 0); - } else { - // Restore the screen - ags->load_cg(74, 8); - ags->load_cg(81, -1); - map_page = 81; - } - } else if(cmd == 3) { - RND = (param == 0 || param == 1) ? 0 : random(param); - } else if(460 <= cmd && cmd <= 625 && 20 <= param && param <= 55) { - if(paint_x) { - ags->paint(paint_x, paint_y * 2, 5 + 16); - } - ags->paint(cmd, param * 2, 2 + 16); - paint_x = cmd; - paint_y = param; - } else { - paint_x = 0; - } - break; - - case GameId::TENGU: - if(cmd == 10) { - // nop - } else if(cmd == 20) { - if(param == 20) { - ags->gcopy(8, 8, 46, 144, 2); - } else if(param == 21) { - ags->gcopy(8, 8, 46, 144, 3); - } - } - break; - - case GameId::GAKUEN: - if (param != 30 && (param < 34 || param > 46)) { - if (enable_graphics) { - ags->load_cg(param + 250, -1); - } - } - - // Display the Gakuen Senki party status bar for party members. The Z commands make the slots appear in reverse, so 39 and 45 draw - // the box for character 1, 38 and 44 draw for character 2, and so on. Z 1, 30: draws the first character slot, which is always occupied. - // - // One the actual MSX, this bar was constructed in an irregular fashion. This code constructs a more consistent version, where each square - // is 35px wide with 2px borders on each side. Since the borders are overlapped, this means each sub-sprite is placed 37px apart. - else if (param == 46) { - // Originally this would have loaded Image 296, the party status bar, to screen 1 for future reference. However, due to trouble with - // the palette, I decided to load it before using each subcommand, instead. - /*ags->dest_screen = 1; - ags->load_cg(296, -1); - ags->dest_screen = 0;*/ - } - else if (param == 30) { - // Load party status bar to screen 1. - ags->dest_screen = 1; - ags->load_cg(296, -1); - ags->dest_screen = 0; - - // Draw Wataru's icon. - ags->src_screen = 1; - ags->copy(10, 182, 48, 224, 7, 210); - ags->src_screen = 0; - } - else if (param >= 34 && param <= 39) { - int x = (39 - param) * 37 + 45; - - // Load party status bar to screen 1. - ags->dest_screen = 1; - ags->load_cg(296, -1); - ags->dest_screen = 0; - - // Draw a blank party icon to init or clear the party box. - ags->src_screen = 1; - ags->copy(269, 182, 307, 224, x, 210); - ags->src_screen = 0; - } - else { - int x = (45 - param) * 37 + 45; - - // Load party status bar to screen 1. - ags->dest_screen = 1; - ags->load_cg(296, -1); - ags->dest_screen = 0; - - // Draw a party member's icon to the party bar. - ags->src_screen = 1; - ags->copy(x + 2, 182, x + 38, 224, x, 210); - ags->src_screen = 0; - } - break; - case GameId::RANCE2: - case GameId::RANCE2_HINT: - if (last_paint_x != -1) - ags->paint(last_paint_x, last_paint_y, 0); - ags->paint(cmd, param, 3); - last_paint_x = cmd; - last_paint_y = param; - break; - } + WARNING("Unimplemented command: Z %d,%d:", cmd, param); } @@ -1046,10 +655,539 @@ uint16 NACT_Sys1::cali() return (uint16)(cali[1] & 0xffff); } +class NACT_LittleVampire : public NACT_Sys1 +{ +public: + NACT_LittleVampire(const Config& config, const GameId& game_id) + : NACT_Sys1(config, game_id) {} + + void opening() override { + mako->play_music(4); + ags->load_cg(3, -1); + WAIT(2000); + ags->load_cg(38, -1); + cmd_a(); + ags->draw_box(0); + } +}; + +class NACT_GakuenSenki : public NACT_Sys1 +{ +public: + NACT_GakuenSenki(const Config& config, const GameId& game_id) + : NACT_Sys1(config, game_id) {} + + void opening() override { + mako->set_cd_track(1, 1); + ags->load_cg(302, -1); + WAIT(3000); + ags->load_cg(303, -1); + WAIT(3000); + mako->play_music(1); + ags->load_cg(304, -1); + WAIT(4000); + } + + void cmd_g() override { + int page = sco.getd(); + TRACE("G %d:", page); + + page = (page == 3) ? 94 : page; + + if (enable_graphics) + ags->load_cg(page, -1); + } + + void cmd_y() override { + int cmd = cali(); + int param = cali(); + TRACE("Y %d,%d:", cmd, param); + + switch (cmd) { + case 1: + RND = (param == 0 || param == 1) ? 0 : random(param); + break; + case 2: + // Clear variables up to param, though in practice param is always 21. + for (int i = 0; i <= param; i++) { + var[i] = 0; + } + break; + case 3: + { + char buf[16]; + snprintf(buf, sizeof(buf), "%d", param); + ags->draw_text(buf); + } + break; + case 4: + if (param == 0) { + mako->play_music(1); + } else { + mako->stop_music(); + } + break; + case 5: + enable_graphics = (param == 0); + break; + case 6: + // Dummied. 0 - "Display text slowly," 1 - disable. + break; + case 7: + // Dummied. 0 - "Switch to interlace mode," 1 - disable. + break; + default: + WARNING("Unimplemented command: Y %d, %d", cmd, param); + break; + } + } + + void cmd_z() override { + int cmd = cali(); + int param = cali(); + TRACE("Z %d,%d:", cmd, param); + + if (param != 30 && (param < 34 || param > 46)) { + if (enable_graphics) { + ags->load_cg(param + 250, -1); + } + } + + // Display the Gakuen Senki party status bar for party members. The Z commands make the slots appear in reverse, so 39 and 45 draw + // the box for character 1, 38 and 44 draw for character 2, and so on. Z 1, 30: draws the first character slot, which is always occupied. + // + // One the actual MSX, this bar was constructed in an irregular fashion. This code constructs a more consistent version, where each square + // is 35px wide with 2px borders on each side. Since the borders are overlapped, this means each sub-sprite is placed 37px apart. + else if (param == 46) { + // Originally this would have loaded Image 296, the party status bar, to screen 1 for future reference. However, due to trouble with + // the palette, I decided to load it before using each subcommand, instead. + /*ags->dest_screen = 1; + ags->load_cg(296, -1); + ags->dest_screen = 0;*/ + } + else if (param == 30) { + // Load party status bar to screen 1. + ags->dest_screen = 1; + ags->load_cg(296, -1); + ags->dest_screen = 0; + + // Draw Wataru's icon. + ags->src_screen = 1; + ags->copy(10, 182, 48, 224, 7, 210); + ags->src_screen = 0; + } + else if (param >= 34 && param <= 39) { + int x = (39 - param) * 37 + 45; + + // Load party status bar to screen 1. + ags->dest_screen = 1; + ags->load_cg(296, -1); + ags->dest_screen = 0; + + // Draw a blank party icon to init or clear the party box. + ags->src_screen = 1; + ags->copy(269, 182, 307, 224, x, 210); + ags->src_screen = 0; + } else { + int x = (45 - param) * 37 + 45; + + // Load party status bar to screen 1. + ags->dest_screen = 1; + ags->load_cg(296, -1); + ags->dest_screen = 0; + + // Draw a party member's icon to the party bar. + ags->src_screen = 1; + ags->copy(x + 2, 182, x + 38, 224, x, 210); + ags->src_screen = 0; + } + } + +private: + bool enable_graphics = true; +}; + +class NACT_Intruder : public NACT_Sys1 +{ +public: + NACT_Intruder(const Config& config, const GameId& game_id) + : NACT_Sys1(config, game_id) {} + + void opening() override { + ags->load_cg(77, -1); + WAIT(1667); + ags->load_cg(74, -1); + } + + void cmd_g() override { + int page = sco.getd(); + TRACE("G %d:", page); + + page = (page == 97) ? 96 : (page == 98) ? 97 : page; + ags->load_cg(page, -1); + if (page == 94) + WAIT(400); + if (page == 81 || page == 82) + map_page = page; + } + + void cmd_u() override { + int page = sco.getd(); + int transparent = sco.getd(); + TRACE("U %d,%d:", page, transparent); + + ags->load_cg(page, 11); + if (page == 5) + WAIT(400); + } + + void cmd_y() override { + int cmd = cali(); + int param = cali(); + TRACE("Y %d,%d:", cmd, param); + + switch (cmd) { + case 0: + ags->clear_text_window(text_window, true); + break; + case 1: + if (show_push) { + ags->draw_push(text_window); + } + for (;;) { + if (terminate) { + return; + } + if (get_key()) { + break; + } + sys_sleep(16); + } + sys_sleep(100); + for (;;) { + if (terminate) { + return; + } + if (!(get_key() & 0x18)) { + break; + } + sys_sleep(16); + } + ags->clear_text_window(text_window, true); + break; + case 2: + if (param == 0) { + quit(NACT_HALT); + } else { + WAIT(100) + } + break; + case 3: + WAIT(param * 1000) + break; + case 255: + quit(NACT_HALT); + break; + default: + WARNING("Unimplemented command: Y %d, %d", cmd, param); + break; + } + } + + void cmd_z() override { + int cmd = cali(); + int param = cali(); + TRACE("Z %d,%d:", cmd, param); + + if (cmd == 1 && 1 <= param && param <= 4) { + const char* buf[4] = { + "\x81\x83", // "<" in SJIS + "\x81\xC8", // "∧" in SJIS + "\x81\x84", // ">" in SJIS + "\x81\xC9", // "∨" in SJIS + }; + + // 矢印を消去する + if (map_page) { + ags->load_cg(map_page, -1); + if (paint_x) { + ags->paint(paint_x, paint_y * 2, 2 + 16); + } + } + + // 矢印を表示する + int x = ags->text_dest_x; + int y = ags->text_dest_y; + int color = ags->text_font_color; + ags->text_font_size = 24; + ags->text_font_color = 16; + + ags->text_dest_x = 456; + ags->text_dest_y = 103; + ags->draw_text(buf[param - 1]); + + ags->text_dest_x = x; + ags->text_dest_y = y; + ags->text_font_size = 16; + ags->text_font_color = color; + } else if (cmd == 2) { + if (param == 0) { + // Clear the screen + ags->box_fill(0, 0, 0, 640, 400, 0); + } else { + // Restore the screen + ags->load_cg(74, 8); + ags->load_cg(81, -1); + map_page = 81; + } + } else if (cmd == 3) { + RND = (param == 0 || param == 1) ? 0 : random(param); + } else if (460 <= cmd && cmd <= 625 && 20 <= param && param <= 55) { + if (paint_x) { + ags->paint(paint_x, paint_y * 2, 5 + 16); + } + ags->paint(cmd, param * 2, 2 + 16); + paint_x = cmd; + paint_y = param; + } else { + paint_x = 0; + } + } + +private: + int paint_x = 0; + int paint_y = 0; + int map_page = 0; +}; + +class NACT_Crescent : public NACT_Sys1 +{ +public: + NACT_Crescent(const Config& config, const GameId& game_id) + : NACT_Sys1(config, game_id) {} + + void opening() override { + mako->play_music(2); + ags->load_cg(1, -1); + WAIT(3000); + ags->load_cg(81, -1); + } + + void cmd_y() override { + int cmd = cali(); + int param = cali(); + + TRACE("Y %d,%d:", cmd, param); + + switch (cmd) { + case 2: + for (int i = 39; i <= 48; i++) { + var[i] = 0; + } + default: + exec_y(cmd, param); + break; + } + } + + void cmd_z() override { + cmd_y(); + } +}; + +class NACT_Tengu : public NACT_Sys1 +{ +public: + NACT_Tengu(const Config& config, const GameId& game_id) + : NACT_Sys1(config, game_id) {} + + void cmd_y() override { + int cmd = cali(); + int param = cali(); + + TRACE("Y %d,%d:", cmd, param); + + switch (cmd) { + case 130: + WAIT(2000); + ags->load_cg(180, -1); + + WAIT(2000); + ags->draw_box(0); + ags->dest_screen = 1; + ags->load_cg(173, -1); + ags->dest_screen = 0; + ags->gcopy(0x1ecd, 0x3a2a, 8, 60, 3); + WAIT(667); + ags->gcopy(0x1b65, 0x262e, 8, 60, 3); + ags->gcopy(0x2e25, 0x38ee, 7, 9, 3); + WAIT(667); + ags->gcopy(0x1914, 0x1914, 8, 63, 3); + WAIT(667); + ags->gcopy(0x182c, 0x182c, 7, 50, 3); + WAIT(667); + ags->gcopy(0x0000, 0x1ecd, 32, 60, 1); + WAIT(667); + for (int i = 12; i <= 57; i++) { + ags->gcopy(i + 0x2306, i + 0x2306, 1, 165, 3); + } + WAIT_KEYQUIT(6000); + ags->draw_box(param); + break; + default: + exec_y(cmd, param); + break; + } + } + + void cmd_z() override { + int cmd = cali(); + int param = cali(); + + TRACE("Z %d,%d:", cmd, param); + + if (cmd == 10) { + // nop + } else if (cmd == 20) { + if (param == 20) { + ags->gcopy(8, 8, 46, 144, 2); + } else if (param == 21) { + ags->gcopy(8, 8, 46, 144, 3); + } + } + } +}; + +class NACT_DPS : public NACT_Sys1 +{ +public: + NACT_DPS(const Config& config, const GameId& game_id) + : NACT_Sys1(config, game_id) { + text_refresh = false; + strcpy(tvar[0], strings.dps_custom.c_str()); + strcpy(tvar[1], strings.dps_linus.c_str()); + strcpy(tvar[2], strings.dps_katsumi.c_str()); + strcpy(tvar[3], strings.dps_yumiko.c_str()); + strcpy(tvar[4], strings.dps_itsumi.c_str()); + strcpy(tvar[5], strings.dps_hitomi.c_str()); + strcpy(tvar[6], strings.dps_mariko.c_str()); + } + + void cmd_y() override { + int cmd = cali(); + int param = cali(); + + TRACE("Y %d,%d:", cmd, param); + + switch (cmd) { + case 2: + for(int i = 0; i <= 20; i++) { + var[i] = 0; + } + break; + case 7: + if (param == 1) { + ags->box_fill(0, 40, 8, 598, 270, 0); + } + break; + case 255: + quit(0); + break; + default: + exec_y(cmd, param); + break; + } + } +}; + +class NACT_Rance2 : public NACT_Sys1 +{ +public: + NACT_Rance2(const Config& config, const GameId& game_id) + : NACT_Sys1(config, game_id) {} + + void cmd_g() override { + NACT_Sys1::cmd_g(); + last_paint_x = -1; + last_paint_y = -1; + } + + void cmd_y() override { + int cmd = cali(); + int param = cali(); + + TRACE("Y %d,%d:", cmd, param); + + switch (cmd) { + case 7: + if (param == 0) { + // Unknown + } + else if (param == 6) { + ags->box_fill(0, 0, 0, 424, 276, 0); + } + else if (param == 7) { + ags->box_fill(0, 0, 0, 640, 276, 0); + } + break; + case 12: + // Y 12, 0: is supposed to activate an automatic text + // progression feature, but it doesn't seem to work on the + // PC-98? + break; + case 13: + // Sets the speed of the auto text progression feature (Y 12). + // The options in Rance 2 are 1 (the fastest), 5, and 10 (the + // slowest). Not available in the Hint Disk. + break; + case 20: + ags->load_cg(param, -1); + break; + default: + exec_y(cmd, param); + break; + } + } + + void cmd_z() override { + int x = cali(); + int y = cali(); + + TRACE("Z %d,%d:", x, y); + + if (last_paint_x != -1) + ags->paint(last_paint_x, last_paint_y, 0); + ags->paint(x, y, 3); + last_paint_x = x; + last_paint_y = y; + } + +private: + int last_paint_x = -1, last_paint_y = -1; +}; + } // namespace // static NACT* NACT::create_system1(const Config& config, const GameId& game_id) { - return new NACT_Sys1(config, game_id); + if (game_id.is_system1_dps()) + return new NACT_DPS(config, game_id); + switch (game_id.game) { + case GameId::LITTLE_VAMPIRE: + return new NACT_LittleVampire(config, game_id); + case GameId::GAKUEN: + return new NACT_GakuenSenki(config, game_id); + case GameId::INTRUDER: + return new NACT_Intruder(config, game_id); + case GameId::CRESCENT: + return new NACT_Crescent(config, game_id); + case GameId::TENGU: + return new NACT_Tengu(config, game_id); + case GameId::RANCE2: + case GameId::RANCE2_HINT: + return new NACT_Rance2(config, game_id); + default: + return new NACT_Sys1(config, game_id); + } }