Merge pull request #551 from NQNStudios:keys

Refactor Debug Keys

* Collect debug keys into a central location so that the debug help dialog can automatically list every defined debug key.
* Add buttons so that debug actions can be taken directly from the debug help dialog.
* Add new debug keys to immediately fight an encounter from the current outdoor section.
This commit is contained in:
2025-02-02 13:56:38 -05:00
committed by GitHub
11 changed files with 290 additions and 158 deletions

View File

@@ -3,6 +3,6 @@
<dialog defbtn='okay'> <dialog defbtn='okay'>
<field name="number" type='int' top='33' left='90' width='75' height='16'/> <field name="number" type='int' top='33' left='90' width='75' height='16'/>
<pict type='dlog' num='2' top='8' left='8'/> <pict type='dlog' num='2' top='8' left='8'/>
<text name='prompt' size='large' top='8' left='49' width='163' height='16'>How many?</text> <text name='prompt' size='large' top='8' left='49' width='193' height='16'>How many?</text>
<button name='okay' type='regular' top='63' left='141'>OK</button> <button name='okay' type='regular' top='63' left='141'>OK</button>
</dialog> </dialog>

View File

@@ -3,31 +3,41 @@
<dialog defbtn='okay'> <dialog defbtn='okay'>
<pict type='dlog' num='16' top='8' left='8'/> <pict type='dlog' num='16' top='8' left='8'/>
<text top='8' left='50' width='400' height='16'>DEBUG MODE HELP</text> <text top='8' left='50' width='400' height='16'>DEBUG MODE HELP</text>
<text top='22' left='50' width='400' height='250'> <text top='22' left='50' width='400' height='50'>
Debug mode is intended to aid you in testing your scenario. Debug mode is intended to aid you in testing your scenario.
While in debug mode, monsters don't move in combat and are killed in one hit. While in debug mode, monsters don't move in combat and are killed in one hit.
In addition, you have access to a lot of additional hotkeys.<br/><br/> In addition, you have access to a lot of additional hotkeys.<br/><br/>
Debug hot keys<br/> Debug hot keys:<br/>
B - Leave town<br/>
C - Get cleaned up (lose negative status effects)<br/>
D - Toggle Debug mode<br/>
E - Stealth, Detect Life, Firewalk<br/>
F - Flight<br/>
G - Toggle Ghost mode (letting you pass through walls)<br/>
H - Heal<br/>
K - Kill everything<br/>
N - End Scenario<br/>
O - Location<br/>
Q - Magic map<br/>
R - Return to Start<br/>
S - Set stuff done flags<br/>
T - Enter Town<br/>
W - Refresh jobs/shops<br/>
= - Heal, increase magic skills<br/>
&lt; - Make one day pass<br/>
&gt; - Reset towns (excludes the one you're in, if any)<br/>
! - Toggle Special Node Step-through Mode<br/>
/ - Bring up this list<br/>
</text> </text>
<button name='okay' type='regular' top='280' left='387'>OK</button> <button name='btn1' type='tiny' relative='pos-in pos' rel-anchor='prev' left='0' top='3'></button>
<button name='btn2' type='tiny' relative='pos-in pos' rel-anchor='prev' left='0' top='1'></button>
<button name='btn3' type='tiny' relative='pos-in pos' rel-anchor='prev' left='0' top='1'></button>
<button name='btn4' type='tiny' relative='pos-in pos' rel-anchor='prev' left='0' top='1'></button>
<button name='btn5' type='tiny' relative='pos-in pos' rel-anchor='prev' left='0' top='1'></button>
<button name='btn6' type='tiny' relative='pos-in pos' rel-anchor='prev' left='0' top='1'></button>
<button name='btn7' type='tiny' relative='pos-in pos' rel-anchor='prev' left='0' top='1'></button>
<button name='btn8' type='tiny' relative='pos-in pos' rel-anchor='prev' left='0' top='1'></button>
<button name='btn9' type='tiny' relative='pos-in pos' rel-anchor='prev' left='0' top='1'></button>
<button name='btn10' type='tiny' relative='pos-in pos' rel-anchor='prev' left='0' top='1'></button>
<button name='btn11' type='tiny' relative='pos-in pos' rel-anchor='prev' left='0' top='1'></button>
<button name='btn12' type='tiny' relative='pos-in pos' rel-anchor='prev' left='0' top='1'></button>
<button name='btn13' type='tiny' relative='pos-in pos' rel-anchor='prev' left='0' top='1'></button>
<button name='btn14' type='tiny' relative='pos-in pos' rel-anchor='prev' left='0' top='1'></button>
<button name='btn15' type='tiny' relative='pos-in pos' rel-anchor='prev' left='0' top='1'></button>
<button name='btn16' type='tiny' relative='pos-in pos' rel-anchor='prev' left='0' top='1'></button>
<button name='btn17' type='tiny' relative='pos-in pos' rel-anchor='prev' left='0' top='1'></button>
<button name='btn18' type='tiny' relative='pos-in pos' rel-anchor='prev' left='0' top='1'></button>
<button name='btn19' type='tiny' relative='pos-in pos' rel-anchor='prev' left='0' top='1'></button>
<button name='btn20' type='tiny' relative='pos-in pos' rel-anchor='prev' left='0' top='1'></button>
<button name='btn21' type='tiny' relative='pos-in pos' rel-anchor='prev' left='0' top='1'></button>
<button name='btn22' type='tiny' relative='pos-in pos' rel-anchor='prev' left='0' top='1'></button>
<button name='btn23' type='tiny' relative='pos-in pos' rel-anchor='prev' left='0' top='1'></button>
<button name='btn24' type='tiny' relative='pos-in pos' rel-anchor='prev' left='0' top='1'></button>
<button name='btn25' type='tiny' relative='pos-in pos' rel-anchor='prev' left='0' top='1'></button>
<button name='btn26' type='tiny' relative='pos-in pos' rel-anchor='prev' left='0' top='1'></button>
<button name='btn27' type='tiny' relative='pos-in pos' rel-anchor='prev' left='0' top='1'></button>
<button name='btn28' type='tiny' relative='pos-in pos' rel-anchor='prev' left='0' top='1'></button>
<button name='btn29' type='tiny' relative='pos-in pos' rel-anchor='prev' left='0' top='1'></button>
<button name='btn30' type='tiny' relative='pos-in pos' rel-anchor='prev' left='0' top='1'></button>
<button name='okay' type='regular' top='400' left='387'>OK</button>
</dialog> </dialog>

View File

@@ -12,10 +12,10 @@
<text top='41' left='68' width='111' height='14'>World height:</text> <text top='41' left='68' width='111' height='14'>World height:</text>
<text name='height' top='41' left='186' width='37'/> <text name='height' top='41' left='186' width='37'/>
<text name='y' framed='true' top='88' left='142' width='75' height='16'/> <text name='y' framed='true' top='88' left='142' width='75' height='16'/>
<button name='xminus' type='small' top='59' left='77'>-</button> <button name='xminus' type='small' top='59' left='77' def-key='left'>-</button>
<button name='xplus' type='small' top='59' left='106'>+</button> <button name='xplus' type='small' top='59' left='106' def-key='right'>+</button>
<button name='yminus' type='small' top='84' left='77'>-</button> <button name='yminus' type='small' top='84' left='77' def-key='up'>-</button>
<button name='yplus' type='small' top='84' left='106'>+</button> <button name='yplus' type='small' top='84' left='106' def-key='down'>+</button>
<text name='title' framed='true' top='111' left='142' width='150' height='16'/> <text name='title' framed='true' top='111' left='142' width='150' height='16'/>
<button name='choose' type='regular' top='111' left='66'>Choose</button> <button name='choose' type='regular' top='111' left='66'>Choose</button>
</dialog> </dialog>

View File

@@ -5,7 +5,9 @@
* Created by Celtic Minstrel on 11/05/09. * Created by Celtic Minstrel on 11/05/09.
* *
*/ */
#include <sstream>
#include <codecvt>
#include <locale>
#include "keycodes.hpp" #include "keycodes.hpp"
//#include <sstream> //#include <sstream>
//#include "dialog.hpp" //#include "dialog.hpp"
@@ -18,6 +20,54 @@
//#include "cursors.hpp" //#include "cursors.hpp"
#include "keymods.hpp" #include "keymods.hpp"
// Windows won't compile if this conversion is not explicit.
// (Wildly enough, it claims that char->wchar_t is a NARROWING conversion).
static wchar_t w(char ch) {
#ifdef SFML_SYSTEM_WINDOWS
std::ostringstream sstr;
sstr << ch;
std::wstring_convert<std::codecvt_utf8<wchar_t>> converter;
std::wstring wide_str = converter.from_bytes(sstr.str());
return wide_str[0];
#else
return ch;
#endif
}
// NOTE: This only supports a few characters including the ones
// that are currently used for debug key shortcuts!
// It is also very hard-coded to the key layout on my laptop (which may only be standard in the US!)
cKey charToKey(char ch) {
if(ch >= 'a' && ch <= 'z'){
return {false, w(ch)};
}else if(ch >= 'A' && ch <= 'Z'){
return {false, w(tolower(ch)), mod_shift};
}
switch(ch){
case '=': case '/':
return {false, w(ch)};
case '<':
return {false, w(','), mod_shift};
case '>':
return {false, w('.'), mod_shift};
case '!':
return {false, w('1'), mod_shift};
case '@':
return {false, w('2'), mod_shift};
case '#':
return {false, w('3'), mod_shift};
case '$':
return {false, w('4'), mod_shift};
case '%':
return {false, w('5'), mod_shift};
case '^':
return {false, w('6'), mod_shift};
case '?':
return {false, w('/'), mod_shift};
}
throw "Tried to convert unsupported char to cKey!";
}
eKeyMod operator + (eKeyMod lhs, eKeyMod rhs){ eKeyMod operator + (eKeyMod lhs, eKeyMod rhs){
if(lhs == rhs) return lhs; if(lhs == rhs) return lhs;
else if(lhs == mod_none) return rhs; else if(lhs == mod_none) return rhs;

View File

@@ -57,6 +57,8 @@ struct cKey {
eKeyMod mod; eKeyMod mod;
}; };
cKey charToKey(char ch);
/// Combine two key modifiers. /// Combine two key modifiers.
/// @param lhs @param rhs /// @param lhs @param rhs
/// @return lhs + rhs /// @return lhs + rhs

View File

@@ -27,6 +27,7 @@
#include "boe.ui.hpp" #include "boe.ui.hpp"
#include "mathutil.hpp" #include "mathutil.hpp"
#include "fileio/fileio.hpp" #include "fileio/fileio.hpp"
#include "fileio/resmgr/res_dialog.hpp"
#include "dialogxml/dialogs/choicedlog.hpp" #include "dialogxml/dialogs/choicedlog.hpp"
#include "dialogxml/dialogs/dialog.hpp" #include "dialogxml/dialogs/dialog.hpp"
#include "dialogxml/widgets/scrollbar.hpp" #include "dialogxml/widgets/scrollbar.hpp"
@@ -1887,9 +1888,24 @@ void handle_menu_spell(eSpell spell_picked) {
advance_time(did_something, need_redraw, need_reprint); advance_time(did_something, need_redraw, need_reprint);
} }
void initiate_outdoor_combat(short i) { static void set_up_combat() {
location to_place; location to_place;
for(cPlayer& pc : univ.party)
if(pc.main_status == eMainStatus::ALIVE)
to_place = pc.combat_pos;
else for(cItem& item : pc.items)
if(item.variety != eItemType::NO_ITEM) {
place_item(item,to_place);
item.variety = eItemType::NO_ITEM;
}
overall_mode = MODE_COMBAT;
center = univ.current_pc().combat_pos;
draw_terrain();
}
void initiate_outdoor_combat(short i) {
// Make sure the player can see the monster they stepped next to
draw_terrain(); draw_terrain();
// Is combat too easy? // Is combat too easy?
@@ -1900,24 +1916,11 @@ void initiate_outdoor_combat(short i) {
return; return;
} }
// Delay((long) 100,&dummy);
start_outdoor_combat(univ.party.out_c[i], univ.party.out_loc,count_walls(univ.party.out_loc)); start_outdoor_combat(univ.party.out_c[i], univ.party.out_loc,count_walls(univ.party.out_loc));
univ.party.out_c[i].exists = false; univ.party.out_c[i].exists = false;
for(cPlayer& pc : univ.party) set_up_combat();
if(pc.main_status == eMainStatus::ALIVE)
to_place = pc.combat_pos;
else for(cItem& item : pc.items)
if(item.variety != eItemType::NO_ITEM) {
place_item(item,to_place);
item.variety = eItemType::NO_ITEM;
}
overall_mode = MODE_COMBAT;
center = univ.current_pc().combat_pos;
draw_terrain();
} }
void show_inventory() { void show_inventory() {
@@ -1955,6 +1958,37 @@ void toggle_debug_mode() {
print_buf(); print_buf();
} }
void debug_fight_encounter(bool wandering) {
if(recording){
record_action("debug_fight_encounter", bool_to_str(wandering));
}
if(!is_out()){
ASB("Debug outdoor encounter: You have to be");
ASB("outdoors!");
print_buf();
return;
}
std::string prompt = "Which ";
if(wandering){
prompt += "wandering encounter?";
}else{
prompt += "special encounter?";
}
int i = get_num_response(0, 3, prompt);
cOutdoors::cWandering encounter;
if(wandering){
encounter = univ.out->wandering[i];
}else{
encounter = univ.out->special_enc[i];
}
start_outdoor_combat(encounter, univ.party.out_loc, count_walls(univ.party.out_loc));
set_up_combat();
}
void debug_give_item() { void debug_give_item() {
if(recording){ if(recording){
record_action("debug_give_item", ""); record_action("debug_give_item", "");
@@ -2265,6 +2299,111 @@ void easter_egg(int idx) {
print_buf(); print_buf();
} }
std::map<char,key_action_t> debug_actions;
std::vector<std::vector<char>> debug_actions_help_order;
void add_debug_action(std::vector<char> keys, std::string name, void (*action)()) {
key_action_t shortcut = {keys, name, action};
for(char ch: keys){
debug_actions[ch] = shortcut;
}
debug_actions_help_order.push_back(keys);
}
void show_debug_help() {
if(recording){
record_action("show_debug_help", "");
}
cDialog debug_panel(*ResMgr::dialogs.get("help-debug"));
int idx;
for(idx = 0; idx < debug_actions_help_order.size(); ++idx){
std::ostringstream btn_name;
btn_name << "btn" << (idx+1);
cControl& button = debug_panel[btn_name.str()];
key_action_t action = debug_actions[debug_actions_help_order[idx][0]];
std::ostringstream btn_text;
btn_text << action.keys[0];
for(int key_idx = 1; key_idx < action.keys.size(); ++key_idx){
btn_text << ", " << action.keys[key_idx];
}
btn_text << " - " << action.name;
button.setText(btn_text.str());
// TODO it's unfortunate that the button can only have one key if actions might have
// more than one
button.attachKey(charToKey(action.keys[0]));
// Don't recursively open the panel
if(action.action != &show_debug_help){
button.attachClickHandler([action](cDialog& me, std::string, eKeyMod) -> bool {
me.toast(false);
action.action();
return true;
});
}
}
for(; idx < 30; ++idx){
std::ostringstream btn_name;
btn_name << "btn" << (idx+1);
cControl& button = debug_panel[btn_name.str()];
button.hide();
}
debug_panel.attachClickHandlers([](cDialog& me, std::string, eKeyMod) -> bool {
me.toast(false);
return true;
}, {"okay"});
debug_panel.run();
}
// Non-comprehensive list of unused keys:
// JUXYZ chijklnoqvy @#$-_+[]{},.'"`~/\|;:
void init_debug_actions() {
add_debug_action({'B'}, "Leave town", debug_leave_town);
add_debug_action({'C'}, "Get cleaned up (lose negative status effects)", debug_clean_up);
add_debug_action({'D'}, "Toggle Debug mode", toggle_debug_mode);
add_debug_action({'E'}, "Stealth, Detect Life, Firewalk", debug_stealth_detect_life_firewalk);
add_debug_action({'F'}, "Flight", debug_fly);
add_debug_action({'G'}, "Toggle Ghost mode (letting you pass through walls)", debug_ghost_mode);
add_debug_action({'H'}, "Heal", debug_heal);
// This one was missing from the old help dialog:
add_debug_action({'I'}, "Give item", debug_give_item);
add_debug_action({'K'}, "Kill everything", debug_kill);
add_debug_action({'N'}, "End scenario", []() -> void {handle_victory(true);});
add_debug_action({'O'}, "Print your location", debug_print_location);
add_debug_action({'Q'}, "Magic map", debug_magic_map);
add_debug_action({'R'}, "Return to start", debug_return_to_start);
add_debug_action({'S'}, "Set stuff done flags", []() -> void {
// edit_stuff_done() is used in the character editor which
// doesn't have replays, so its replay action is recorded
// external to the function definition unlike most actions.
if(recording){
record_action("edit_stuff_done", "");
}
edit_stuff_done();
});
add_debug_action({'T'}, "Enter town", debug_enter_town);
add_debug_action({'W'}, "Refresh jobs/shops", debug_refresh_stores);
add_debug_action({'='}, "Heal, increase magic skills", debug_heal_plus_extra);
add_debug_action({'<'}, "Make one day pass", debug_increase_age);
add_debug_action({'>'}, "Reset towns (excludes the one you're in, if any)", debug_towns_forget);
add_debug_action({'!'}, "Toggle Special Node Step-through Mode", debug_step_through);
// std::bind won't work here for reasons
add_debug_action({'%'}, "Fight wandering encounter from this section", []() -> void {debug_fight_encounter(true);});
add_debug_action({'^'}, "Fight special encounter from this section", []() -> void {debug_fight_encounter(false);});
add_debug_action({'/', '?'}, "Bring up this window", show_debug_help);
}
// Later we might want to know whether the key is used or not
bool handle_debug_key(char key) {
if(!univ.debug_mode)
return false;
if(debug_actions.find(key) != debug_actions.end()){
debug_actions[key].action();
return true;
}
return false;
}
bool handle_keystroke(const sf::Event& event, cFramerateLimiter& fps_limiter){ bool handle_keystroke(const sf::Event& event, cFramerateLimiter& fps_limiter){
bool are_done = false; bool are_done = false;
location pass_point; // TODO: This isn't needed location pass_point; // TODO: This isn't needed
@@ -2443,6 +2582,11 @@ bool handle_keystroke(const sf::Event& event, cFramerateLimiter& fps_limiter){
break; break;
case '?': case '?':
// In debug mode, override basic help menus with a debug mode cheat-sheet
if(univ.debug_mode){
show_debug_help();
break;
}
if(overall_mode == MODE_SHOPPING) { if(overall_mode == MODE_SHOPPING) {
give_help_and_record(226,27); give_help_and_record(226,27);
break; break;
@@ -2488,116 +2632,15 @@ bool handle_keystroke(const sf::Event& event, cFramerateLimiter& fps_limiter){
} }
break; break;
case 'D': case 'D':
toggle_debug_mode(); toggle_debug_mode();
break; break;
// 'z', Really? 'i' is not used
case 'z': case 'z':
show_inventory(); show_inventory();
break; break;
case '=':
if(!univ.debug_mode) break;
debug_heal_plus_extra();
break;
case 'B':
if(!univ.debug_mode) break;
debug_leave_town();
break;
case 'C':
if(!univ.debug_mode) break;
debug_clean_up();
break;
case 'E':
if(!univ.debug_mode) break;
debug_stealth_detect_life_firewalk();
break;
case 'F':
if(!univ.debug_mode) break;
debug_fly();
break;
case 'G':
if(!univ.debug_mode) break;
debug_ghost_mode();
break;
case 'H':
if(!univ.debug_mode) break;
debug_heal();
break;
case 'K':
if(!univ.debug_mode) break;
debug_kill();
break;
case 'N':
if(!univ.debug_mode) break;
handle_victory(true);
break;
case 'O':
if(!univ.debug_mode) break;
debug_print_location();
break;
case 'I':
if(!univ.debug_mode) break;
debug_give_item();
break;
case 'Q':
if(!univ.debug_mode) break;
debug_magic_map();
break;
case 'R':
if(!univ.debug_mode) break;
debug_return_to_start();
break;
case 'S':
if(!univ.debug_mode) break;
// edit_stuff_done() is used in the character editor which
// doesn't have replays, so its replay action is recorded
// external to the function definition unlike most actions.
if(recording){
record_action("edit_stuff_done", "");
}
edit_stuff_done();
break;
case 'T':
if(!univ.debug_mode) break;
debug_enter_town();
break;
case 'W':
if(!univ.debug_mode) break;
debug_refresh_stores();
break;
case '<':
if(!univ.debug_mode) break;
debug_increase_age();
break;
case '>':
if(!univ.debug_mode) break;
debug_towns_forget();
break;
case '!':
if(!univ.debug_mode) break;
debug_step_through();
break;
case '/':
if(!univ.debug_mode) break;
show_dialog_action("help-debug");
break;
case 'a': // Show automap case 'a': // Show automap
display_map(); display_map();
break; break;
@@ -2704,6 +2747,11 @@ bool handle_keystroke(const sf::Event& event, cFramerateLimiter& fps_limiter){
advance_time(did_something, need_redraw, need_reprint); advance_time(did_something, need_redraw, need_reprint);
} }
break; break;
// Debug action keyboard shortcuts:
default:
handle_debug_key(chr);
break;
} }
spell_forced = false; spell_forced = false;
return are_done; return are_done;

View File

@@ -2,11 +2,18 @@
#ifndef BOE_GAME_ACTIONS_H #ifndef BOE_GAME_ACTIONS_H
#define BOE_GAME_ACTIONS_H #define BOE_GAME_ACTIONS_H
#include <vector>
#include <SFML/Window/Event.hpp> #include <SFML/Window/Event.hpp>
#include "location.hpp" #include "location.hpp"
#include "dialogxml/keycodes.hpp" #include "dialogxml/keycodes.hpp"
#include "tools/framerate_limiter.hpp" #include "tools/framerate_limiter.hpp"
struct key_action_t {
std::vector<char> keys;
std::string name;
void (*action)();
};
void init_screen_locs(); void init_screen_locs();
bool prime_time(); bool prime_time();
bool handle_action(const sf::Event& event, cFramerateLimiter& fps_limiter); bool handle_action(const sf::Event& event, cFramerateLimiter& fps_limiter);
@@ -75,6 +82,9 @@ void handle_use_space_select(bool& need_reprint);
void handle_use_space(location destination, bool& did_something, bool& need_redraw); void handle_use_space(location destination, bool& did_something, bool& need_redraw);
void show_inventory(); void show_inventory();
void toggle_debug_mode(); void toggle_debug_mode();
void init_debug_actions();
void show_debug_help();
void debug_fight_encounter(bool wandering);
void debug_give_item(); void debug_give_item();
void debug_print_location(); void debug_print_location();
void debug_step_through(); void debug_step_through();

View File

@@ -251,11 +251,7 @@ effect_pat_type field[8] = {
bool center_on_monst; bool center_on_monst;
void start_outdoor_combat(cOutdoors::cWandering encounter,location where,short num_walls) {
void start_outdoor_combat(cOutdoors::cCreature encounter,location where,short num_walls) {
short how_many,num_tries = 0; short how_many,num_tries = 0;
short low[10] = {15,7,4,3,2,1,1,7,2,1}; short low[10] = {15,7,4,3,2,1,1,7,2,1};
short high[10] = {30,10,6,5,3,2,1,10,4,1}; short high[10] = {30,10,6,5,3,2,1,10,4,1};
@@ -266,7 +262,7 @@ void start_outdoor_combat(cOutdoors::cCreature encounter,location where,short nu
nums[i] = get_ran(1,low[i],high[i]); nums[i] = get_ran(1,low[i],high[i]);
for(short i = 0; i < 3; i++) for(short i = 0; i < 3; i++)
nums[i + 7] = get_ran(1,low[i + 7],high[i + 7]); nums[i + 7] = get_ran(1,low[i + 7],high[i + 7]);
notify_out_combat_began(encounter.what_monst,nums); notify_out_combat_began(encounter,nums);
print_buf(); print_buf();
play_sound(23); play_sound(23);
@@ -286,15 +282,15 @@ void start_outdoor_combat(cOutdoors::cCreature encounter,location where,short nu
for(short i = 0; i < 7; i++) { for(short i = 0; i < 7; i++) {
how_many = nums[i]; how_many = nums[i];
if(encounter.what_monst.monst[i] != 0) if(encounter.monst[i] != 0)
for(short j = 0; j < how_many; j++) for(short j = 0; j < how_many; j++)
set_up_monst(eAttitude::HOSTILE_A,encounter.what_monst.monst[i]); set_up_monst(eAttitude::HOSTILE_A,encounter.monst[i]);
} }
for(short i = 0; i < 3; i++) { for(short i = 0; i < 3; i++) {
how_many = nums[i + 7]; how_many = nums[i + 7];
if(encounter.what_monst.friendly[i] != 0) if(encounter.friendly[i] != 0)
for(short j = 0; j < how_many; j++) for(short j = 0; j < how_many; j++)
set_up_monst(eAttitude::FRIENDLY,encounter.what_monst.friendly[i]); set_up_monst(eAttitude::FRIENDLY,encounter.friendly[i]);
} }
// place PCs // place PCs
@@ -371,6 +367,10 @@ void start_outdoor_combat(cOutdoors::cCreature encounter,location where,short nu
} }
void start_outdoor_combat(cOutdoors::cCreature encounter,location where,short num_walls) {
start_outdoor_combat(encounter.what_monst, where, num_walls);
}
bool pc_combat_move(location destination) { bool pc_combat_move(location destination) {
std::string create_line; std::string create_line;
short s1; short s1;

View File

@@ -9,6 +9,9 @@
#include "boe.global.hpp" #include "boe.global.hpp"
#include "spell.hpp" #include "spell.hpp"
// For debug purposes, allow directly starting an outdoor encounter without
// a wandering monster
void start_outdoor_combat(cOutdoors::cWandering encounter,location where,short num_walls);
void start_outdoor_combat(cOutdoors::cCreature encounter,location where,short num_walls); void start_outdoor_combat(cOutdoors::cCreature encounter,location where,short num_walls);
bool pc_combat_move(location destination); bool pc_combat_move(location destination);
void char_parry(); void char_parry();

View File

@@ -873,6 +873,10 @@ static void replay_action(Element& action) {
cancel_item_target(did_something, need_redraw, need_reprint); cancel_item_target(did_something, need_redraw, need_reprint);
}else if(t == "easter_egg"){ }else if(t == "easter_egg"){
easter_egg(boost::lexical_cast<int>(action.GetText())); easter_egg(boost::lexical_cast<int>(action.GetText()));
}else if(t == "show_debug_panel"){
show_debug_help();
}else if(t == "debug_fight_encounter"){
debug_fight_encounter(str_to_bool(action.GetText()));
}else if(t == "advance_time"){ }else if(t == "advance_time"){
// This is bad regardless of strictness, because visual changes may have occurred which won't get redrawn/reprinted // 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; throw std::string { "Replay system internal error! advance_time() was supposed to be called by the last action, but wasn't: " } + _last_action_type;
@@ -942,6 +946,7 @@ void init_boe(int argc, char* argv[]) {
cUniverse::print_result = iLiving::print_result = add_string_to_buf; cUniverse::print_result = iLiving::print_result = add_string_to_buf;
cPlayer::give_help = give_help; cPlayer::give_help = give_help;
init_fileio(); init_fileio();
init_debug_actions();
init_spell_menus(); init_spell_menus();
init_mini_map(); init_mini_map();
redraw_screen(REFRESH_NONE); redraw_screen(REFRESH_NONE);

View File

@@ -41,9 +41,12 @@ using bitmap = std::array<std::bitset<y>, x>;
class cOutdoors : public cArea { class cOutdoors : public cArea {
cScenario* scenario; cScenario* scenario;
public: public:
// Definition of an outdoor combat encounter
class cWandering { // formerly out_wandering_type class cWandering { // formerly out_wandering_type
public: public:
// max 7 types of monster per encounter
std::array<mon_num_t,7> monst; std::array<mon_num_t,7> monst;
// max 3 types of friendly npc per encounter
std::array<mon_num_t,3> friendly; std::array<mon_num_t,3> friendly;
short spec_on_meet,spec_on_win,spec_on_flee; short spec_on_meet,spec_on_win,spec_on_flee;
short end_spec1,end_spec2; short end_spec1,end_spec2;
@@ -55,6 +58,7 @@ public:
void readFrom(const cTagFile_Page& page); void readFrom(const cTagFile_Page& page);
cWandering(); cWandering();
}; };
// Instantiation of an outdoor wandering monster on the map
class cCreature { // formerly outdoor_creature_type class cCreature { // formerly outdoor_creature_type
public: public:
bool exists = false; bool exists = false;