Merge pull request #534 from NQNStudios:fix-479

Fixing text buffer texture/font corruption (#479)

* #479 demonstrates that the contents of the text buffer are NOT irrelevant for reproducing bugs. So I set up recording/replay for the burma shave easter egg. This also makes an easy way to mess with the buffer state when debugging (just mash &/\*/&/\*/&/\* n times)
* When a replay throws an error, it puts up a showError() dialog. If the next action is a control_click, the system will try to click that control on the error dialog--which is totally divergent from the replay's intended behavior. So we should just stop replaying when an error happens.
* If you have a long replay and want to run it very fast, but then slow down when you get to the sequence that reproduces your bug, now you can add a `<change_fps>` to your replay to achieve that.
* Fixes for the 2 legacy replay errors that I opened recently

Fix #479 
Fix #532 
Fix #533
This commit is contained in:
2025-01-20 09:10:17 -05:00
committed by GitHub
22 changed files with 165 additions and 43 deletions

View File

@@ -333,6 +333,9 @@ static void process_args(int argc, char* argv[]) {
cli.writeToStream(std::cout);
exit(0);
}
// This obsolete preference should always be true unless running an old replay
// (which will set it false after this line if it needs to)
set_pref("DrawTerrainFrills", true);
if(replay){
if(record_to){
std::cout << "Warning: flag --record conflicts with --replay and will be ignored." << std::endl;
@@ -502,6 +505,29 @@ static void handle_scenario_args() {
}
}
std::map<std::string, int> startup_button_indices = {
// Button layout since 11/30/24
{"Tutorial", 0}, {"Make New Party", 3},
{"Load Game", 1}, {"Start Scenario", 4},
{"Preferences", 2},
// Buttons that don't exist anymore
{"Custom Scenario", -1},
};
std::map<int, std::string> startup_button_names = {
{0, "Tutorial"}, {3, "Make New Party"},
{1, "Load Game"}, {4, "Start Scenario"},
{2, "Preferences"}, {5, ""},
};
// Map legacy int indices onto new string-mapped layout
std::map<int, std::string> startup_button_names_v1 = {
{0, "Load Game"}, {3, "Start Scenario"},
{1, "Make New Party"}, {4, "Custom Scenario"},
{2, "Preferences"},
};
void replay_action(Element& action) {
bool did_something = false, need_redraw = false, need_reprint = false;
@@ -512,10 +538,30 @@ void replay_action(Element& action) {
// NOTE: Action replay blocks need to return early unless the action advances time
if(overall_mode == MODE_STARTUP && t == "startup_button_click"){
auto info = info_from_action(action);
eStartButton btn = static_cast<eStartButton>(std::stoi(info["btn"]));
int btn_idx = -1;
try{
// Legacy replays use ints to encode startup buttons
btn_idx = std::stoi(info["btn"]);
}catch(std::invalid_argument& err){
// Newer replays use strings to encode startup buttons
btn_idx = startup_button_indices[info["btn"]];
}
// No-op button
if(btn_idx == -1){
return;
}
eStartButton btn = static_cast<eStartButton>(btn_idx);
eKeyMod mods = static_cast<eKeyMod>(std::stoi(info["mods"]));
handle_startup_button_click(btn, mods);
return;
}else if(t == "change_fps"){
extern boost::optional<cFramerateLimiter> replay_fps_limit;
// default new fps: slow the replay down substantially
int new_fps = 2;
if(!action.GetText().empty()){
new_fps = boost::lexical_cast<int>(action.GetText());
}
replay_fps_limit.emplace(new_fps);
}else if(t == "load_party"){
decode_file(action.GetText(), tempDir / "temp.exg");
load_party(tempDir / "temp.exg", univ);
@@ -832,12 +878,15 @@ void replay_action(Element& action) {
return;
}else if(t == "cancel_item_target"){
cancel_item_target(did_something, need_redraw, need_reprint);
}else if(t == "easter_egg"){
easter_egg(boost::lexical_cast<int>(action.GetText()));
}else if(t == "advance_time"){
// This is bad regardless of strictness, because visual changes may have occurred which won't get redrawn/reprinted
throw std::string { "Replay system internal error! advance_time() was supposed to be called by the last action, but wasn't: " } + _last_action_type;
}else{
std::ostringstream sstr;
sstr << "Couldn't replay action: " << action;
replaying = false;
throw sstr.str();
}
@@ -1009,6 +1058,10 @@ void handle_events() {
while(!All_Done) {
if(replaying && has_next_action()){
if(check_for_interrupt("confirm-interrupt-replay")){
replaying = false;
continue;
}
replay_next_action();
}else{
#ifdef __APPLE__