From 23106c6954e7bc6aba42f48cf31e3a5f6e14a514 Mon Sep 17 00:00:00 2001 From: Nat Quayle Nelson Date: Wed, 6 Aug 2025 15:29:01 -0500 Subject: [PATCH] very messy support for recording 6-string dialog --- rsrc/dialogs/adventure-notes.xml | 18 ++++++---- src/dialogxml/dialogs/3choice.cpp | 51 ++++++++++++++++++++++++++-- src/dialogxml/dialogs/3choice.hpp | 17 +++++++++- src/dialogxml/dialogs/choicedlog.hpp | 2 +- src/game/boe.infodlg.cpp | 6 ++++ src/game/boe.specials.cpp | 10 +++--- 6 files changed, 89 insertions(+), 15 deletions(-) diff --git a/rsrc/dialogs/adventure-notes.xml b/rsrc/dialogs/adventure-notes.xml index 16a0800a..2dea6c55 100644 --- a/rsrc/dialogs/adventure-notes.xml +++ b/rsrc/dialogs/adventure-notes.xml @@ -4,16 +4,22 @@ - - + + + diff --git a/src/dialogxml/dialogs/3choice.cpp b/src/dialogxml/dialogs/3choice.cpp index fe45e1df..7ca62950 100644 --- a/src/dialogxml/dialogs/3choice.cpp +++ b/src/dialogxml/dialogs/3choice.cpp @@ -155,6 +155,13 @@ void cThreeChoice::init_buttons(cBasicButtonType btn1, cBasicButtonType btn2, cB me->add(btn, cur_btn_rect, sout.str()); cur_btn_rect.right = cur_btn_rect.left - 4; } + // Add a record button and hide it + cButton* record = new cButton(*me); + record->setText("Record"); + record->attachClickHandler(std::bind(&cThreeChoice::onRecord, this, _2)); + cur_btn_rect = {buttons_top,10,buttons_top + 23,buttons_right}; + me->add(record, cur_btn_rect, "record"); + record->hide(); } void cThreeChoice::init_pict(pic_num_t pic){ @@ -174,7 +181,7 @@ std::string cThreeChoice::show(){ return "**ERROR**"; // shouldn't be reached } -short custom_choice_dialog(std::array& strs,short pic_num,ePicType pic_type,std::array& buttons,bool anim_pict,short anim_loops, int anim_fps, cDialog* parent) { +short custom_choice_dialog(std::array& strs,short pic_num,ePicType pic_type,std::array& buttons,bool anim_pict,short anim_loops, int anim_fps, cUniverse* univ, cDialog* parent) { set_cursor(sword_curs); std::vector vec(strs.begin(), strs.end()); @@ -185,6 +192,28 @@ short custom_choice_dialog(std::array& strs,short pic_num,ePicTy if(anim_pict) setup_dialog_pict_anim(*(customDialog.operator->()), "pict", anim_loops, anim_fps); + if(univ != nullptr){ + customDialog.setRecordHandler([vec, univ](cDialog& dlg) -> void { + std::string combined; + for(std::string msg : vec){ + if(!msg.empty()){ + if(!combined.empty()){ + combined += " ||"; + } + combined += msg; + } + } + // It's not necessarily a NOTE_SCEN and location shouldn't be empty, + // but these things aren't actually shown to the party in the encounter notes dialog. + if(univ->party.record(NOTE_SCEN, combined, "")){ + give_help(58,0,dlg); + // TODO ASB is only in game source, but this function is in common source so the scenario editor can preview dialogs :( + /* + add_string_to_buf("Added to encounter notes."); + */ + } + }); + } std::string item_hit = customDialog.show(); for(int i = 0; i < 3; i++) { @@ -220,5 +249,23 @@ short once_dialog(cUniverse& univ, cSpecial& spec, eSpecCtxType cur_type, cDialo showError("Dialog box ended up with no buttons."); return -1; } - return custom_choice_dialog(strs, spec.pic, ePicType(spec.pictype), buttons, true, spec.ex1c, spec.ex2c, parent); + return custom_choice_dialog(strs, spec.pic, ePicType(spec.pictype), buttons, true, spec.ex1c, spec.ex2c, &univ, parent); +} + +cThreeChoice& cThreeChoice::setRecordHandler(record_callback_t rec){ + if(rec == nullptr){ + hasRecord = false; + dlg["record"].hide(); + }else{ + hasRecord = true; + rec_f = rec; + dlg["record"].show(); + } + return *this; +} + +bool cThreeChoice::onRecord(std::string id){ + if(hasRecord) rec_f(dlg); + else dlg[id].hide(); + return hasRecord; } \ No newline at end of file diff --git a/src/dialogxml/dialogs/3choice.hpp b/src/dialogxml/dialogs/3choice.hpp index 281222be..612b8c7d 100644 --- a/src/dialogxml/dialogs/3choice.hpp +++ b/src/dialogxml/dialogs/3choice.hpp @@ -37,6 +37,9 @@ namespace {cBasicButtonType null_btn = boost::none;} extern bbtt basic_buttons[71]; #endif +/// The signature of a record handler for cThreeChoice. +typedef std::function record_callback_t; + /// A choice dialog with several strings and up to three buttons. /// This is the class used for dialogs generated by special nodes. /// It generates the dialog dynamically from the given input. @@ -48,6 +51,9 @@ class cThreeChoice : public cChoiceDlog { void init_buttons(cBasicButtonType btn1, cBasicButtonType btn2, cBasicButtonType btn3); void init_pict(pic_num_t pic); const ePicType type; + record_callback_t rec_f; + bool hasRecord; + bool onRecord(std::string id); public: /// Create a dialog with just one button. /// @param strings A list of the strings to place in the dialog. @@ -70,12 +76,21 @@ public: /// @param t The type of the icon. /// @param parent Optionally, a parent dialog. cThreeChoice(std::vector& strings, std::array& buttons, pic_num_t pic, ePicType t, cDialog* parent = nullptr); + /// Set a record handler. + /// @param rec The handler. + /// @return This object, for method-call chaining. + /// @note Only one record handler can be set at a time. To remove it, set it to null. + /// @note The presence of the Record button is determined entirely by the presence of a record handler. + /// + /// A record handler should take one parameter, which is a reference to the dialog. + /// (That's the cDialog, not the cThreeChoice.) It should return void. + cThreeChoice& setRecordHandler(record_callback_t rec); /// @copydoc cChoiceDlog::show() /// @note The unique key in this case is the label specified in the button specification. std::string show(); }; -short custom_choice_dialog(std::array& strs,short pic_num,ePicType pic_type,std::array& buttons, bool anim_pict = false, short anim_loops = -1, int anim_fps = -1, cDialog* parent = nullptr); +short custom_choice_dialog(std::array& strs,short pic_num,ePicType pic_type,std::array& buttons, bool anim_pict = false, short anim_loops = -1, int anim_fps = -1, cUniverse* univ = nullptr, cDialog* parent = nullptr); short once_dialog(cUniverse& univ, cSpecial& spec, eSpecCtxType cur_type, cDialog* parent = nullptr); #endif diff --git a/src/dialogxml/dialogs/choicedlog.hpp b/src/dialogxml/dialogs/choicedlog.hpp index 318cca91..37e60c42 100644 --- a/src/dialogxml/dialogs/choicedlog.hpp +++ b/src/dialogxml/dialogs/choicedlog.hpp @@ -17,8 +17,8 @@ /// This class loads a definition from a file, so there can be any amount of other stuff in the dialog, /// and the buttons could be arranged in any fashion you want. class cChoiceDlog { - cDialog dlg; protected: + cDialog dlg; /// The click handler for the dialog's buttons. /// @param me A reference to the current dialog. /// @param id The unique key of the clicked control. diff --git a/src/game/boe.infodlg.cpp b/src/game/boe.infodlg.cpp index 3acd03a1..008bf740 100644 --- a/src/game/boe.infodlg.cpp +++ b/src/game/boe.infodlg.cpp @@ -512,10 +512,14 @@ static bool adventure_notes_event_filter(cDialog& me, std::string item_hit, eKey std::string n = boost::lexical_cast(i + 1); if(univ.party.special_notes.size() > store_page_on * 3+i) { me["str" + n].setText(univ.party.special_notes[store_page_on * 3+i].the_str); + me["str" + n].recalcRect(); + me["pane" + n].recalcRect(); me["del" + n].show(); } else { me["str" + n].setText(""); + me["str" + n].recalcRect(); + me["pane" + n].recalcRect(); me["del" + n].hide(); } } @@ -544,6 +548,8 @@ void adventure_notes() { std::string n = boost::lexical_cast(i + 1); if(univ.party.special_notes.size() > i) { encNotes["str" + n].setText(univ.party.special_notes[i].the_str); + encNotes["str" + n].recalcRect(); + encNotes["pane" + n].recalcRect(); encNotes["del" + n].show(); } else encNotes["del" + n].hide(); diff --git a/src/game/boe.specials.cpp b/src/game/boe.specials.cpp index 97036a6e..32b8aaaf 100644 --- a/src/game/boe.specials.cpp +++ b/src/game/boe.specials.cpp @@ -2637,7 +2637,7 @@ void oneshot_spec(const runtime_state& ctx) { univ.get_strs(strs, ctx.cur_spec_type, spec.m1); // Leave / Take buttons[0] = 9; buttons[1] = 19; - dlg_res = custom_choice_dialog(strs, spec.pic, ePicType(spec.pictype), buttons, true, spec.ex1c, spec.ex2c); + dlg_res = custom_choice_dialog(strs, spec.pic, ePicType(spec.pictype), buttons, true, spec.ex1c, spec.ex2c, &univ); if(dlg_res == 1) {set_sd = false; ctx.next_spec = -1;} else { store_i = univ.scenario.get_stored_item(spec.ex1a); @@ -2677,7 +2677,7 @@ void oneshot_spec(const runtime_state& ctx) { if((spec.m1 >= 0) || (spec.m2 >= 0)) { univ.get_strs(strs[0],strs[1], ctx.cur_spec_type, spec.m1, spec.m2); buttons[0] = 3; buttons[1] = 2; - dlg_res = custom_choice_dialog(strs,spec.pic,ePicType(spec.pictype),buttons, true, spec.ex1c, spec.ex2c); + dlg_res = custom_choice_dialog(strs,spec.pic,ePicType(spec.pictype),buttons, true, spec.ex1c, spec.ex2c, &univ); // TODO: Make custom_choice_dialog return string? } else dlg_res = cChoiceDlog("basic-trap",{"yes","no"}).show() == "no"; @@ -4006,7 +4006,7 @@ void townmode_spec(const runtime_state& ctx) { else { univ.get_strs(strs,ctx.cur_spec_type, spec.m1); buttons[0] = 9; buttons[1] = 35; - if(custom_choice_dialog(strs, spec.pic, ePicType(spec.pictype), buttons, true, spec.ex1c, spec.ex2c) == 1) + if(custom_choice_dialog(strs, spec.pic, ePicType(spec.pictype), buttons, true, spec.ex1c, spec.ex2c, &univ) == 1) ctx.next_spec = -1; else { int x = univ.party.get_ptr(10), y = univ.party.get_ptr(11); @@ -4037,7 +4037,7 @@ void townmode_spec(const runtime_state& ctx) { else { univ.get_strs(strs, ctx.cur_spec_type,spec.m1); buttons[0] = 9; buttons[1] = 8; - if(custom_choice_dialog(strs, spec.pic, ePicType(spec.pictype), buttons, true, spec.ex1c, spec.ex2c) == 1) { + if(custom_choice_dialog(strs, spec.pic, ePicType(spec.pictype), buttons, true, spec.ex1c, spec.ex2c, &univ) == 1) { ctx.next_spec = -1; if(ctx.which_mode == eSpecCtx::OUT_MOVE || ctx.which_mode == eSpecCtx::TOWN_MOVE || ctx.which_mode == eSpecCtx::COMBAT_MOVE) *ctx.ret_a = 1; @@ -4069,7 +4069,7 @@ void townmode_spec(const runtime_state& ctx) { else { univ.get_strs(strs,ctx.cur_spec_type, spec.m1); buttons[0] = 20; buttons[1] = 24; - int i = spec.ex2b == 1 ? 2 : custom_choice_dialog(strs, spec.pic, ePicType(spec.pictype), buttons); + int i = spec.ex2b == 1 ? 2 : custom_choice_dialog(strs, spec.pic, ePicType(spec.pictype), buttons, false, -1, -1, &univ); *ctx.ret_a = 1; if(i == 1) { ctx.next_spec = -1;