Skip to content

Commit

Permalink
Follower rules UI: Hotkeys,focus follows selection, can close window …
Browse files Browse the repository at this point in the history
…by pressing close button.

Also fixed this in mission ui
  • Loading branch information
RenechCDDA committed Jun 7, 2024
1 parent 56e356a commit 3933854
Show file tree
Hide file tree
Showing 3 changed files with 146 additions and 42 deletions.
2 changes: 1 addition & 1 deletion src/mission_ui.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ void mission_ui::draw_mission_ui()

p_impl.last_action = ctxt.handle_input();

if( p_impl.last_action == "QUIT" ) {
if( p_impl.last_action == "QUIT" || !p_impl.get_is_open() ) {
break;
}
}
Expand Down
183 changes: 143 additions & 40 deletions src/npctalk_rules.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,12 +58,14 @@ void follower_rules_ui::draw_follower_rules_ui( npc *guy )
input_context ctxt;
follower_rules_ui_impl p_impl;
p_impl.set_npc_pointer_to( guy );
p_impl.input_ptr = &ctxt;

ctxt.register_navigate_ui_list();
ctxt.register_action( "MOUSE_MOVE" );
ctxt.register_action( "CONFIRM", to_translation( "Set, toggle, or reset selected rule" ) );
ctxt.register_action( "HELP_KEYBINDINGS" );
ctxt.register_action( "QUIT" );
ctxt.register_action( "ANY_INPUT" );
// This is still bizarrely necessary for imgui
ctxt.set_timeout( 10 );

Expand All @@ -73,7 +75,7 @@ void follower_rules_ui::draw_follower_rules_ui( npc *guy )

p_impl.last_action = ctxt.handle_input();

if( p_impl.last_action == "QUIT" ) {
if( p_impl.last_action == "QUIT" || !p_impl.get_is_open() ) {
break;
}
}
Expand All @@ -92,6 +94,16 @@ std::string follower_rules_ui_impl::get_parsed( std::string initial_string )

}

void follower_rules_ui_impl::print_hotkey( input_event &hotkey )
{
// Padding spaces intentional, so it's obvious that the fake "Hotkey:" header refers to these.
// TODO: Just reimplement everything as a table...? Would avoid this sort of thing.
// But surely not *everything* needs to be a table...
draw_colored_text( string_format( " %s ", static_cast<char>( hotkey.sequence.front() ) ),
c_green );
ImGui::SameLine();
}

void follower_rules_ui_impl::draw_controls()
{
if( !guy ) {
Expand All @@ -102,20 +114,34 @@ void follower_rules_ui_impl::draw_controls()

ImGui::SetWindowSize( ImVec2( window_width, window_height ), ImGuiCond_Once );

ImGui::InvisibleButton( "TOP_OF_WINDOW_KB_SCROLL_SELECTABLE", ImVec2() );

const hotkey_queue &hotkeys = hotkey_queue::alphabets();
input_event assigned_hotkey = input_ptr->first_unassigned_hotkey( hotkeys );
input_event pressed_key = input_ptr->get_raw_input();

draw_colored_text( string_format( _( "Rules for your follower, %s" ), guy->disp_name() ) );
ImGui::Separator();
draw_colored_text( _( "Hotkey:" ) );
ImGui::NewLine();

if( ImGui::Button( _( "Default ALL" ) ) ) {
npc_follower_rules default_values; //Call the constructor and let *it* tell us what the default is
guy->rules = default_values;
print_hotkey( assigned_hotkey );
if( ImGui::Button( _( "Default ALL" ) ) || pressed_key == assigned_hotkey ) {
ImGui::SetKeyboardFocusHere( -1 );
if( query_yn( _( "Really set all of this follower's rules to their default values?" ) ) ) {
npc_follower_rules default_values; //Call the constructor and let *it* tell us what the default is
guy->rules = default_values;
}
}

int rule_number = 0;
/* Handle all of our regular, boolean rules */
for( std::pair<const std::string, ally_rule_data> rule_data : ally_rule_strs ) {

Check failure on line 139 in src/npctalk_rules.cpp

View workflow job for this annotation

GitHub Actions / build (src)

loop variable is copied but only used as const reference; consider making it a const reference [performance-for-range-copy,-warnings-as-errors]
ImGui::PushID( rule_number );
assigned_hotkey = input_ptr->next_unassigned_hotkey( hotkeys, assigned_hotkey );
rule_number++;
ImGui::NewLine();
print_hotkey( assigned_hotkey );
const ally_rule_data &this_rule = rule_data.second;
bool rule_enabled = false;
std::string rules_text;
Expand All @@ -125,7 +151,8 @@ void follower_rules_ui_impl::draw_controls()
} else {
rules_text = string_format( "%s", get_parsed( this_rule.rule_false_text ) );
}
if( ImGui::Button( _( "Change" ) ) ) {
if( ImGui::Button( _( "Change" ) ) || pressed_key == assigned_hotkey ) {
ImGui::SetKeyboardFocusHere( -1 );
guy->rules.toggle_flag( this_rule.rule );
guy->rules.toggle_specific_override_state( this_rule.rule, !rule_enabled );
}
Expand All @@ -138,44 +165,27 @@ void follower_rules_ui_impl::draw_controls()
draw_colored_text( rules_text );
ImGui::PopID();
}
/* Shows CBM rules, but only if the character has bionics*/
if( !guy->get_bionics().empty() ) {
ImGui::Separator();
ImGui::NewLine();
for( std::pair<const cbm_recharge_rule, std::string> recharge_rule : recharge_map ) {
ImGui::PushID( rule_number );
rule_number++;
int percent = static_cast<int>( recharge_rule.first );
std::string button_label = std::to_string( percent ) + "%";
ImGui::SameLine();
if( ImGui::Button( button_label.c_str() ) ) {
guy->rules.cbm_recharge = recharge_rule.first;
}
ImGui::PopID();
}
ImGui::NewLine();
draw_colored_text( string_format( "%s", get_parsed( recharge_map[guy->rules.cbm_recharge] ) ) );

ImGui::Separator();
ImGui::NewLine();
for( std::pair<const cbm_reserve_rule, std::string> reserve_rule : reserve_map ) {
ImGui::PushID( rule_number );
rule_number++;
int percent = static_cast<int>( reserve_rule.first );
std::string button_label = std::to_string( percent ) + "%";
ImGui::SameLine();
if( ImGui::Button( button_label.c_str() ) ) {
guy->rules.cbm_reserve = reserve_rule.first;
}
ImGui::PopID();
}
ImGui::NewLine();
draw_colored_text( string_format( "%s", get_parsed( reserve_map[guy->rules.cbm_reserve] ) ) );
}

// Engagement rules require their own set of buttons, each instruction is unique
ImGui::Separator();
ImGui::NewLine();
assigned_hotkey = input_ptr->next_unassigned_hotkey( hotkeys, assigned_hotkey );
print_hotkey( assigned_hotkey );
if( ImGui::InvisibleButton( "ENGAGEMENT_RULES", ImVec2() ) || pressed_key == assigned_hotkey ) {
combat_engagement &rule = guy->rules.engagement;
auto &this_map = engagement_rules;
// Wrapping the map. Means we can press the hotkey repeatedly to cycle through all of
// the available settings for this rule.
if( this_map.upper_bound( rule ) == this_map.end() ) {
// Then we have the *last* entry the map, so wrap to the first element
rule = this_map.begin()->first;
} else {
// Assign the next possible value contained in the map.
rule = this_map.upper_bound( rule )->first;
}
int offset = distance( this_map.begin(), this_map.find( rule ) );
ImGui::SetKeyboardFocusHere( offset );
}
int engagement_rule_number = 0;
for( std::pair<const combat_engagement, std::string> engagement_rule : engagement_rules ) {

Check failure on line 190 in src/npctalk_rules.cpp

View workflow job for this annotation

GitHub Actions / build (src)

loop variable is copied but only used as const reference; consider making it a const reference [performance-for-range-copy,-warnings-as-errors]
ImGui::PushID( rule_number );
Expand All @@ -186,15 +196,32 @@ void follower_rules_ui_impl::draw_controls()
ImGui::SameLine();
if( ImGui::Button( button_label.c_str() ) ) {
guy->rules.engagement = engagement_rule.first;
ImGui::SetKeyboardFocusHere( -1 );
}
ImGui::PopID();
}
ImGui::SameLine();
draw_colored_text( _( "Engagement rules" ), c_white );
ImGui::NewLine();
draw_colored_text( string_format( "%s", get_parsed( engagement_rules[guy->rules.engagement] ) ) );

// Lastly, aiming rule also has a non-boolean set of values
// Aiming rule also has a non-boolean set of values
ImGui::Separator();
ImGui::NewLine();
assigned_hotkey = input_ptr->next_unassigned_hotkey( hotkeys, assigned_hotkey );
print_hotkey( assigned_hotkey );
// This button implementation is a boilerplate copy of the engagement rules one. Look there for comments
if( ImGui::InvisibleButton( "AIMING_RULES", ImVec2() ) || pressed_key == assigned_hotkey ) {
aim_rule &rule = guy->rules.aim;
auto &this_map = aim_rule_map;
if( this_map.upper_bound( rule ) == this_map.end() ) {
rule = this_map.begin()->first;
} else {
rule = this_map.upper_bound( rule )->first;
}
int offset = distance( this_map.begin(), this_map.find( rule ) );
ImGui::SetKeyboardFocusHere( offset );
}
int aim_rule_number = 0;
for( std::pair<const aim_rule, std::string> aiming_rule : aim_rule_map ) {

Check failure on line 226 in src/npctalk_rules.cpp

View workflow job for this annotation

GitHub Actions / build (src)

loop variable is copied but only used as const reference; consider making it a const reference [performance-for-range-copy,-warnings-as-errors]
ImGui::PushID( rule_number );
Expand All @@ -205,9 +232,85 @@ void follower_rules_ui_impl::draw_controls()
ImGui::SameLine();
if( ImGui::Button( button_label.c_str() ) ) {
guy->rules.aim = aiming_rule.first;
ImGui::SetKeyboardFocusHere( -1 );
}
ImGui::PopID();
}
ImGui::SameLine();
draw_colored_text( _( "Aiming rules" ), c_white );
ImGui::NewLine();
draw_colored_text( string_format( "%s", get_parsed( aim_rule_map[guy->rules.aim] ) ) );

/* Shows CBM rules, but only if the character has bionics. Must be last because it will
only appear sometimes and we don't want hotkeys to be different depending on whether
the character has bionics. That's bad for muscle memory! */
if( !guy->get_bionics().empty() ) {
ImGui::Separator();
ImGui::NewLine();
assigned_hotkey = input_ptr->next_unassigned_hotkey( hotkeys, assigned_hotkey );
print_hotkey( assigned_hotkey );
// This button implementation is a boilerplate copy of the engagement rules one. Look there for comments
if( ImGui::InvisibleButton( "RECHARGE_RULES", ImVec2() ) || pressed_key == assigned_hotkey ) {
cbm_recharge_rule &rule = guy->rules.cbm_recharge;
auto &this_map = recharge_map;
if( this_map.upper_bound( rule ) == this_map.end() ) {
rule = this_map.begin()->first;
} else {
rule = this_map.upper_bound( rule )->first;
}
int offset = distance( this_map.begin(), this_map.find( rule ) );
ImGui::SetKeyboardFocusHere( offset );
}
for( std::pair<const cbm_recharge_rule, std::string> recharge_rule : recharge_map ) {

Check failure on line 264 in src/npctalk_rules.cpp

View workflow job for this annotation

GitHub Actions / build (src)

loop variable is copied but only used as const reference; consider making it a const reference [performance-for-range-copy,-warnings-as-errors]
ImGui::PushID( rule_number );
rule_number++;
int percent = static_cast<int>( recharge_rule.first );
std::string button_label = std::to_string( percent ) + "%";
ImGui::SameLine();
if( ImGui::Button( button_label.c_str() ) ) {
guy->rules.cbm_recharge = recharge_rule.first;
ImGui::SetKeyboardFocusHere( -1 );
}
ImGui::PopID();
}
ImGui::SameLine();
draw_colored_text( _( "CBM recharging rules" ), c_white );
ImGui::NewLine();
draw_colored_text( string_format( "%s", get_parsed( recharge_map[guy->rules.cbm_recharge] ) ) );

ImGui::Separator();
ImGui::NewLine();
assigned_hotkey = input_ptr->next_unassigned_hotkey( hotkeys, assigned_hotkey );
print_hotkey( assigned_hotkey );
// This button implementation is a boilerplate copy of the engagement rules one. Look there for comments
if( ImGui::InvisibleButton( "RESERVE_RULES", ImVec2() ) || pressed_key == assigned_hotkey ) {
cbm_reserve_rule &rule = guy->rules.cbm_reserve;
auto &this_map = reserve_map;
if( this_map.upper_bound( rule ) == this_map.end() ) {
rule = this_map.begin()->first;
} else {
rule = this_map.upper_bound( rule )->first;
}
int offset = distance( this_map.begin(), this_map.find( rule ) );
ImGui::SetKeyboardFocusHere( offset );
}
for( std::pair<const cbm_reserve_rule, std::string> reserve_rule : reserve_map ) {

Check failure on line 297 in src/npctalk_rules.cpp

View workflow job for this annotation

GitHub Actions / build (src)

loop variable is copied but only used as const reference; consider making it a const reference [performance-for-range-copy,-warnings-as-errors]
ImGui::PushID( rule_number );
rule_number++;
int percent = static_cast<int>( reserve_rule.first );
std::string button_label = std::to_string( percent ) + "%";
ImGui::SameLine();
if( ImGui::Button( button_label.c_str() ) ) {
guy->rules.cbm_reserve = reserve_rule.first;
ImGui::SetKeyboardFocusHere( -1 );
}
ImGui::PopID();
}
ImGui::SameLine();
draw_colored_text( _( "CBM reserve rules" ), c_white );
ImGui::NewLine();
draw_colored_text( string_format( "%s", get_parsed( reserve_map[guy->rules.cbm_reserve] ) ) );
}

ImGui::InvisibleButton( "BOTTOM_OF_WINDOW_KB_SCROLL_SELECTABLE", ImVec2() );
}
3 changes: 2 additions & 1 deletion src/npctalk_rules.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ class follower_rules_ui_impl : public cataimgui::window
{
public:
std::string last_action;
input_context *input_ptr = nullptr;
explicit follower_rules_ui_impl() : cataimgui::window( _( "Rules for your follower" ),
ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove ) {
}
Expand All @@ -42,10 +43,10 @@ class follower_rules_ui_impl : public cataimgui::window
private:
npc *guy = nullptr;
std::string get_parsed( std::string initial_string );
void print_hotkey( input_event &hotkey );

size_t window_width = str_width_to_pixels( TERMX ) / 2;
size_t window_height = str_height_to_pixels( TERMY ) / 2;
size_t table_column_width = window_width / 2;

protected:
void draw_controls() override;
Expand Down

0 comments on commit 3933854

Please sign in to comment.