diff --git a/hyper.h b/hyper.h index 16ae61a7..f182f59f 100644 --- a/hyper.h +++ b/hyper.h @@ -676,6 +676,7 @@ namespace rg { static const char princess = 'p'; static const char daily = 'd'; static const char daily_off = 'D'; + static const char racing = 'R'; // wrongmode only -- marks 'global' achievements not related to the current mode static const char global = 'x'; @@ -4680,7 +4681,7 @@ ld sintick(int period, ld phase = 0); namespace racing { extern bool on, player_relative, track_ready; void generate_track(); - void show(); + void configure_race(); void prepare_subscreens(); extern vector track; extern int current_player; diff --git a/menus.cpp b/menus.cpp index 2df73efd..946ac9c2 100644 --- a/menus.cpp +++ b/menus.cpp @@ -494,6 +494,9 @@ void showChangeMode() { dialog::addBoolItem(XLAT("Yendor Challenge"), (yendor::on), 'y'); dialog::addBoolItem(XLAT("%1 Challenge", moPrincess), (princess::challenge), 'P'); dialog::addBoolItem(XLAT("random pattern mode"), (randomPatternsMode), 'r'); +#if CAP_RACING + dialog::addBoolItem(XLAT("racing mode"), racing::on, 'R'); +#endif #if CAP_DAILY dialog::addBoolItem(XLAT("Strange Challenge"), daily::on, 'z'); #endif @@ -539,6 +542,10 @@ void showChangeMode() { } else if(xuni == 'p') pushScreen(peace::showMenu); + #if CAP_RACING + else if(xuni == 'R') + racing::configure_race(); + #endif else if(xuni == 'i') dialog::do_if_confirmed([] { restart_game(rg::inv); }); diff --git a/quit.cpp b/quit.cpp index e9a09ae2..e0136475 100644 --- a/quit.cpp +++ b/quit.cpp @@ -299,6 +299,7 @@ void showMission() { tour::on ? (canmove ? XLAT("Tutorial") : XLAT("GAME OVER")) : #endif (cheater && !autocheat)? XLAT("It is a shame to cheat!") : + racing::on ? "racing mode" : showoff ? XLAT("Showoff mode") : (canmove && princess::challenge) ? XLAT("%1 Challenge", moPrincess) : canmove ? XLAT("Quest status") : @@ -307,9 +308,9 @@ void showMission() { ); keyhandler = handleKeyQuit; - if(!peace::on) + if(!peace::on && !racing::on) dialog::addInfo(XLAT("Your score: %1", its(gold()))); - if(!peace::on) + if(!peace::on && !racing::on) dialog::addInfo(XLAT("Enemies killed: %1", its(tkills()))); #if CAP_TOUR @@ -326,6 +327,7 @@ void showMission() { else if(tour::on) ; #endif + else if(racing::on) ; else if(princess::challenge) dialog::addInfo(XLAT("Follow the Mouse and escape with %the1!", moPrincess)); else if(gold() < R30) @@ -354,6 +356,7 @@ void showMission() { else if(tour::on) ; #endif else if(peace::on) ; + else if(racing::on) ; else if(tkills() < R100) dialog::addInfo(XLAT("Defeat %1 enemies to access the Graveyard", its(R100))); else if(kills[moVizier] == 0 && (items[itFernFlower] < U5 || items[itGold] < U5)) @@ -375,7 +378,7 @@ void showMission() { if(cheater && !autocheat) { dialog::addInfo(XLAT("you have cheated %1 times", its(cheater)), 0xFF2020); } - else { + else if(!racing::on) { dialog::addInfo(timeline(), dialog::dialogcolor); } @@ -439,6 +442,8 @@ void showMission() { dialog::addItem(XLAT("restart"), SDLK_F5); if(inv::on && items[itInventory]) dialog::addItem(XLAT("inventory"), 'i'); + if(racing::on) + dialog::addItem(XLAT("racing menu"), 'o'); #if ISMOBILE==0 dialog::addItem(XLAT(quitsaves() ? "save" : "quit"), SDLK_F10); #endif diff --git a/racing.cpp b/racing.cpp index 12a68e3b..9c53d751 100644 --- a/racing.cpp +++ b/racing.cpp @@ -13,10 +13,13 @@ bool on; bool player_relative = false; bool track_ready; -static const int LENGTH = 50; +static const int LENGTH = 250; static const int TWIDTH = 6; static const int DROP = 1; +int ghosts_to_show = 5; +int ghosts_to_save = 10; + struct race_cellinfo { cell *c; int from_track; @@ -95,8 +98,9 @@ void hwrite(hstream& hs, const ghost& gh) { } bool read_ghosts(string seed, int mcode) { - fhstream f; - f.f = fopen(ghost_filename(seed, mcode).c_str(), "rb"); + string fname = ghost_filename(seed, mcode); + println(hlog, "trying to read ghosts from: ", fname); + fhstream f(fname, "rb"); if(!f.f) return false; f.get (); hread(f, race_ghosts[{seed, mcode}]); @@ -446,9 +450,7 @@ int readArgs() { else if(argis("-racing")) { PHASEFROM(2); stop_game(); - shmup::on = true; - racing::on = true; - timerghost = false; + switch_game_mode(rg::racing); } else return 1; return 0; @@ -580,10 +582,7 @@ void track_chooser(string new_track) { dialog::addSelItem(XLAT1(linf[l].name), s, let++); dialog::add_action([l, new_track] () { stop_game(); - specialland = l; - racing::on = true; - shmup::on = true; - track_code = new_track; + if(!racing::on) switch_game_mode(rg::racing); start_game(); popScreenAll(); }); @@ -626,6 +625,27 @@ struct race_configurer { dialog::init(XLAT("Racing")); + dialog::addSelItem("track name", editing_track ? dialog::view_edited_string() : new_track, '/'); + dialog::add_action([this] () { + editing_track = !editing_track; + if(editing_track) dialog::start_editing(new_track); + }); + dialog::addItem("play the official track", 'o'); + dialog::add_action([this] () { new_track = "OFFICIAL"; }); + dialog::addItem("play a random track", 'r'); + dialog::add_action([this] () { new_track = random_track_name(); }); + + dialog::addItem(XLAT("select the track and start!"), 's'); + dialog::add_action([this] () { + if(race_ghosts[{new_track, modecode()}].empty()) + read_ghosts(new_track, modecode()); + else + println(hlog, "known ghosts: ", isize(race_ghosts[{new_track, modecode()}])); + pushScreen([this] () { track_chooser(new_track); }); + }); + + dialog::addBreak(100); + dialog::addBoolItem(XLAT("player relative"), player_relative, 'p'); dialog::add_action([] () { player_relative = !player_relative; @@ -674,22 +694,23 @@ struct race_configurer { } else dialog::addBreak(100); - dialog::addSelItem("track name", editing_track ? dialog::view_edited_string() : new_track, '/'); - dialog::add_action([this] () { - editing_track = !editing_track; - if(editing_track) dialog::start_editing(new_track); - }); - dialog::addItem("play the official track", 'o'); - dialog::add_action([this] () { new_track = "OFFICIAL"; }); - dialog::addItem("play a random track", 'r'); - dialog::add_action([this] () { new_track = random_track_name(); }); - - dialog::addItem(XLAT("select the track and start!"), 's'); - dialog::add_action([this] () { - if(race_ghosts[{new_track, modecode()}].empty()) - read_ghosts(new_track, modecode()); - pushScreen([this] () { track_chooser(new_track); }); - }); + dialog::addBreak(100); + + dialog::addSelItem(XLAT("best scores to show as ghosts"), its(ghosts_to_show), 'g'); + dialog::add_action([]() { dialog::editNumber(ghosts_to_show, 0, 100, 1, 5, "best scores to show as ghosts", ""); }); + + dialog::addSelItem(XLAT("best scores to save"), its(ghosts_to_save), 'b'); + dialog::add_action([]() { dialog::editNumber(ghosts_to_save, 0, 100, 1, 10, "best scores to save", ""); }); + + + if(racing::on) { + dialog::addItem(XLAT("disable the racing mode"), 'x'); + dialog::add_action([] () { + stop_game(); + switch_game_mode(rg::racing); + start_game(); + }); + } dialog::addBack(); dialog::display(); @@ -706,9 +727,13 @@ struct race_configurer { } }; +void configure_race() { + pushScreen(race_configurer()); + } + auto hooks1 = addHook(hooks_o_key, 90, [] { - if(racing::on) return named_dialog("race mode", race_configurer()); + if(racing::on) return named_dialog(XLAT("racing menu"), race_configurer()); else return named_functionality(); }); @@ -745,12 +770,19 @@ void race_won() { part(*x, 0) >>= 2; } - race_ghosts[{track_code, modecode()}] [specialland].emplace_back(ghost{gcs, ticks - race_start_tick, time(NULL), current_history[current_player]}); - write_ghosts(track_code, modecode()); + auto &subtrack = race_ghosts[{track_code, modecode()}] [specialland]; + + subtrack.emplace_back(ghost{gcs, ticks - race_start_tick, time(NULL), current_history[current_player]}); + sort(subtrack.begin(), subtrack.end(), [] (const ghost &g1, const ghost &g2) { return g1.result > g2.result; }); + if(isize(subtrack) > ghosts_to_save && ghosts_to_save > 0) + subtrack.resize(ghosts_to_save); + if(ghosts_to_save > 0) + write_ghosts(track_code, modecode()); } } void markers() { + if(!racing::on) return; if(racing::player_relative) { using namespace racing; cell *goal = NULL; @@ -760,7 +792,10 @@ void markers() { queuestr(H, vid.fsize, its(celldistance(cwt.at, track.back())), 0x10101 * int(128 - 100 * sintick(150))); addauraspecial(H, 0x10100, 0); } + int ghosts_left = ghosts_to_show; for(auto& ghost: race_ghosts[{track_code, modecode()}][specialland]) { + if(!ghosts_left) break; + ghosts_left--; auto p = std::find_if(ghost.history.begin(), ghost.history.end(), [] (const ghostmoment gm) { return gm.step > ticks - race_start_tick;} ); if(p == ghost.history.end()) p--; cell *w = rti[p->where_id].c; diff --git a/system.cpp b/system.cpp index ddb9571e..3f4a4a79 100644 --- a/system.cpp +++ b/system.cpp @@ -1192,6 +1192,7 @@ void switch_game_mode(char switchWhat) { peace::on = !peace::on; tactic::on = yendor::on = princess::challenge = randomPatternsMode = inv::on = false; + racing::on = false; break; case rg::inv: @@ -1199,6 +1200,7 @@ void switch_game_mode(char switchWhat) { if(tactic::on) firstland = laIce; tactic::on = yendor::on = princess::challenge = randomPatternsMode = peace::on = false; + racing::on = false; break; case rg::chaos: @@ -1207,6 +1209,7 @@ void switch_game_mode(char switchWhat) { need_reset_geometry = true; chaosmode = !chaosmode; if(bounded) set_geometry(gNormal); + racing::on = false; break; #if CAP_TOUR @@ -1219,6 +1222,7 @@ void switch_game_mode(char switchWhat) { shmup::on = false; need_reset_geometry = true; tour::on = !tour::on; + racing::on = false; break; #endif @@ -1230,8 +1234,19 @@ void switch_game_mode(char switchWhat) { princess::challenge = false; randomPatternsMode = false; chaosmode = false; + racing::on = false; if(!yendor::on) firstland = laIce; break; + + case rg::racing: + racing::on = !racing::on; + shmup::on = racing::on; + peace::on = false; + tour::on = false; + inv::on = false; + chaosmode = false; + princess::challenge = false; + break; case rg::tactic: tactic::on = !tactic::on; @@ -1240,6 +1255,7 @@ void switch_game_mode(char switchWhat) { inv::on = false; randomPatternsMode = false; princess::challenge = false; + racing::on = false; chaosmode = false; if(!tactic::on) firstland = laIce; break; @@ -1247,6 +1263,7 @@ void switch_game_mode(char switchWhat) { case rg::shmup: shmup::on = !shmup::on; princess::challenge = false; + if(!shmup::on) racing::on = false; break; case rg::randpattern: @@ -1266,6 +1283,7 @@ void switch_game_mode(char switchWhat) { yendor::on = false; chaosmode = false; inv::on = false; + racing::on = false; break; #if CAP_DAILY