Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add new safemode rule category for ignoring sounds #36786

Merged
merged 10 commits into from
Jan 23, 2020
Merged
168 changes: 121 additions & 47 deletions src/safemode_ui.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,13 +60,15 @@ void safemode::show( const std::string &custom_name_in, bool is_safemode_in )
COLUMN_ATTITUDE,
COLUMN_PROXIMITY,
COLUMN_WHITE_BLACKLIST,
COLUMN_CATEGORY
};

std::map<int, int> column_pos;
column_pos[COLUMN_RULE] = 4;
column_pos[COLUMN_ATTITUDE] = 48;
column_pos[COLUMN_PROXIMITY] = 59;
column_pos[COLUMN_WHITE_BLACKLIST] = 66;
column_pos[COLUMN_ATTITUDE] = column_pos[COLUMN_RULE] + 38;
column_pos[COLUMN_PROXIMITY] = column_pos[COLUMN_ATTITUDE] + 10;
column_pos[COLUMN_WHITE_BLACKLIST] = column_pos[COLUMN_PROXIMITY] + 6;
column_pos[COLUMN_CATEGORY] = column_pos[COLUMN_WHITE_BLACKLIST] + 11;

const int num_columns = column_pos.size();

Expand Down Expand Up @@ -133,6 +135,8 @@ void safemode::show( const std::string &custom_name_in, bool is_safemode_in )
mvwprintz( w_header, point( column_pos[COLUMN_ATTITUDE] + 2, 3 ), c_white, _( "Attitude" ) );
mvwprintz( w_header, point( column_pos[COLUMN_PROXIMITY] + 2, 3 ), c_white, _( "Dist" ) );
mvwprintz( w_header, point( column_pos[COLUMN_WHITE_BLACKLIST] + 2, 3 ), c_white, _( "B/W" ) );
mvwprintz( w_header, point( column_pos[COLUMN_CATEGORY] + 2, 3 ), c_white, pgettext( "category",
"Cat" ) );

wrefresh( w_header );
};
Expand Down Expand Up @@ -231,9 +235,13 @@ void safemode::show( const std::string &custom_name_in, bool is_safemode_in )
};

draw_column( COLUMN_RULE, ( rule.rule.empty() ) ? _( "<empty rule>" ) : rule.rule );
draw_column( COLUMN_ATTITUDE, Creature::get_attitude_ui_data( rule.attitude ).first.translated() );
draw_column( COLUMN_PROXIMITY, ( !rule.whitelist ) ? to_string( rule.proximity ) : "---" );
draw_column( COLUMN_WHITE_BLACKLIST, ( rule.whitelist ) ? _( "Whitelist" ) : _( "Blacklist" ) );
draw_column( COLUMN_ATTITUDE, ( rule.category == Categories::HOSTILE_SPOTTED ) ?
Creature::get_attitude_ui_data( rule.attitude ).first.translated() : "---" );
draw_column( COLUMN_PROXIMITY, ( ( rule.category == Categories::SOUND ) ||
!rule.whitelist ) ? to_string( rule.proximity ) : "---" );
draw_column( COLUMN_WHITE_BLACKLIST, rule.whitelist ? _( "Whitelist" ) : _( "Blacklist" ) );
draw_column( COLUMN_CATEGORY, ( rule.category == Categories::SOUND ) ? _( "Sound" ) :
_( "Hostile" ) );
}
}

Expand Down Expand Up @@ -273,12 +281,14 @@ void safemode::show( const std::string &custom_name_in, bool is_safemode_in )
} else if( action == "ADD_DEFAULT_RULESET" ) {
changes_made = true;
current_tab.push_back( rules_class( "*", true, false, Creature::A_HOSTILE,
get_option<int>( "SAFEMODEPROXIMITY" ) ) );
get_option<int>( "SAFEMODEPROXIMITY" )
, HOSTILE_SPOTTED ) );
current_tab.push_back( rules_class( "*", true, true, Creature::A_HOSTILE, 5, SOUND ) );
line = current_tab.size() - 1;
} else if( action == "ADD_RULE" ) {
changes_made = true;
current_tab.push_back( rules_class( "", true, false, Creature::A_HOSTILE,
get_option<int>( "SAFEMODEPROXIMITY" ) ) );
get_option<int>( "SAFEMODEPROXIMITY" ), HOSTILE_SPOTTED ) );
line = current_tab.size() - 1;
} else if( action == "REMOVE_RULE" && !current_tab.empty() ) {
changes_made = true;
Expand Down Expand Up @@ -310,19 +320,37 @@ void safemode::show( const std::string &custom_name_in, bool is_safemode_in )
} else if( action == "CONFIRM" && !current_tab.empty() ) {
changes_made = true;
if( column == COLUMN_RULE ) {
// NOLINTNEXTLINE(cata-use-named-point-constants)
fold_and_print( w_help, point( 1, 1 ), 999, c_white,
_(
"* is used as a Wildcard. A few Examples:\n"
"\n"
"human matches every NPC\n"
"zombie matches the monster name exactly\n"
"acidic zo* matches monsters beginning with 'acidic zo'\n"
"*mbie matches monsters ending with 'mbie'\n"
"*cid*zo*ie multiple * are allowed\n"
"AcI*zO*iE case insensitive search" )
);

switch( current_tab[line].category ) {
case Categories::HOSTILE_SPOTTED:
// NOLINTNEXTLINE(cata-use-named-point-constants)
fold_and_print( w_help, point( 1, 1 ), 999, c_white,
_(
"* is used as a Wildcard. A few Examples:\n"
"\n"
"human matches every NPC\n"
"zombie matches the monster name exactly\n"
"acidic zo* matches monsters beginning with 'acidic zo'\n"
"*mbie matches monsters ending with 'mbie'\n"
"*cid*zo*ie multiple * are allowed\n"
"AcI*zO*iE case insensitive search" )
);
break;
case Categories::SOUND:
// NOLINTNEXTLINE(cata-use-named-point-constants)
fold_and_print( w_help, point( 1, 1 ), 999, c_white,
_(
"* is used as a Wildcard. A few Examples:\n"
"\n"
"footsteps matches the sound name exactly\n"
"a loud ba* matches sounds beginning with 'a loud ba'\n"
"*losion! matches sounds ending with 'losion!'\n"
"a *oud*ba* multiple * are allowed\n"
"*LoU*bA* case insensitive search" )
);
break;
default:
break;
}
draw_border( w_help );
wrefresh( w_help );
current_tab[line].rule = wildcard_trim_rule( string_input_popup()
Expand All @@ -332,6 +360,12 @@ void safemode::show( const std::string &custom_name_in, bool is_safemode_in )
.query_string() );
} else if( column == COLUMN_WHITE_BLACKLIST ) {
current_tab[line].whitelist = !current_tab[line].whitelist;
} else if( column == COLUMN_CATEGORY ) {
if( current_tab[line].category == HOSTILE_SPOTTED ) {
current_tab[line].category = SOUND;
} else if( current_tab[line].category == SOUND ) {
current_tab[line].category = HOSTILE_SPOTTED;
}
} else if( column == COLUMN_ATTITUDE ) {
auto &attitude = current_tab[line].attitude;
switch( attitude ) {
Expand All @@ -347,7 +381,8 @@ void safemode::show( const std::string &custom_name_in, bool is_safemode_in )
case Creature::A_ANY:
attitude = Creature::A_HOSTILE;
}
} else if( column == COLUMN_PROXIMITY && !current_tab[line].whitelist ) {
} else if( column == COLUMN_PROXIMITY && ( current_tab[line].category == SOUND ||
!current_tab[line].whitelist ) ) {
const auto text = string_input_popup()
.title( _( "Proximity Distance (0=max view distance)" ) )
.width( 4 )
Expand Down Expand Up @@ -526,7 +561,7 @@ void safemode::add_rule( const std::string &rule_in, const Creature::Attitude at
const rule_state state_in )
{
character_rules.push_back( rules_class( rule_in, true, ( state_in == RULE_WHITELISTED ),
attitude_in, proximity_in ) );
attitude_in, proximity_in, HOSTILE_SPOTTED ) );
create_rules();

if( !get_option<bool>( "SAFEMODE" ) &&
Expand Down Expand Up @@ -569,8 +604,8 @@ bool safemode::empty() const

void safemode::create_rules()
{
safemode_rules.clear();

safemode_rules_hostile.clear();
safemode_rules_sound.clear();
//process include/exclude in order of rules, global first, then character specific
add_rules( global_rules );
add_rules( character_rules );
Expand All @@ -581,41 +616,60 @@ void safemode::add_rules( const std::vector<rules_class> &rules_in )
//if a specific monster is being added, all the rules need to be checked now
//may have some performance issues since exclusion needs to check all monsters also
for( auto &rule : rules_in ) {
if( !rule.whitelist ) {
//Check include patterns against all monster mtypes
for( const auto &mtype : MonsterGenerator::generator().get_all_mtypes() ) {
set_rule( rule, mtype.nname(), RULE_BLACKLISTED );
}
} else {
//exclude monsters from the existing mapping
for( const auto &safemode_rule : safemode_rules ) {
set_rule( rule, safemode_rule.first, RULE_WHITELISTED );
}
switch( rule.category ) {
case HOSTILE_SPOTTED:
if( !rule.whitelist ) {
//Check include patterns against all monster mtypes
for( const auto &mtype : MonsterGenerator::generator().get_all_mtypes() ) {
set_rule( rule, mtype.nname(), RULE_BLACKLISTED );
}
} else {
//exclude monsters from the existing mapping
for( const auto &safemode_rule : safemode_rules_hostile ) {
set_rule( rule, safemode_rule.first, RULE_WHITELISTED );
}
}
break;
case SOUND:
set_rule( rule, rule.rule, rule.whitelist ? RULE_WHITELISTED : RULE_BLACKLISTED );
break;
default:
break;
}
}
}

void safemode::set_rule( const rules_class &rule_in, const std::string &name_in, rule_state rs_in )
{
static std::vector<Creature::Attitude> attitude_any = {{Creature::A_HOSTILE, Creature::A_NEUTRAL, Creature::A_FRIENDLY}};

if( !rule_in.rule.empty() && rule_in.active && wildcard_match( name_in, rule_in.rule ) ) {
if( rule_in.attitude == Creature::A_ANY ) {
for( auto &att : attitude_any ) {
safemode_rules[ name_in ][ att ] = rule_state_class( rs_in, rule_in.proximity );
static std::vector<Creature::Attitude> attitude_any = { {Creature::A_HOSTILE, Creature::A_NEUTRAL, Creature::A_FRIENDLY} };
switch( rule_in.category ) {
case HOSTILE_SPOTTED:
if( !rule_in.rule.empty() && rule_in.active && wildcard_match( name_in, rule_in.rule ) ) {
if( rule_in.attitude == Creature::A_ANY ) {
for( auto &att : attitude_any ) {
safemode_rules_hostile[name_in][att] = rule_state_class( rs_in, rule_in.proximity,
HOSTILE_SPOTTED );
}
} else {
safemode_rules_hostile[name_in][rule_in.attitude] = rule_state_class( rs_in, rule_in.proximity,
HOSTILE_SPOTTED );
}
}
} else {
safemode_rules[ name_in ][ rule_in.attitude ] = rule_state_class( rs_in, rule_in.proximity );
}
break;
case SOUND:
safemode_rules_sound.push_back( rule_in );
break;
default:
break;
}
}

rule_state safemode::check_monster( const std::string &creature_name_in,
const Creature::Attitude attitude_in,
const int proximity_in ) const
{
const auto iter = safemode_rules.find( creature_name_in );
if( iter != safemode_rules.end() ) {
const auto iter = safemode_rules_hostile.find( creature_name_in );
if( iter != safemode_rules_hostile.end() ) {
const auto &tmp = ( iter->second )[static_cast<int>( attitude_in )];
if( tmp.state == RULE_BLACKLISTED ) {
if( tmp.proximity == 0 || proximity_in <= tmp.proximity ) {
Expand All @@ -630,6 +684,23 @@ rule_state safemode::check_monster( const std::string &creature_name_in,
return RULE_NONE;
}

bool safemode::is_sound_safe( const std::string &sound_name_in,
const int proximity_in ) const
{
bool sound_safe = false;
for( unsigned int i = 0; i < safemode_rules_sound.size(); i++ ) {
if( wildcard_match( sound_name_in, safemode_rules_sound[i].rule ) &&
proximity_in >= safemode_rules_sound[i].proximity ) {
if( safemode_rules_sound[i].whitelist ) {
sound_safe = true;
} else {
return false;
}
}
};
return sound_safe;
}

void safemode::clear_character_rules()
{
character_rules.clear();
Expand Down Expand Up @@ -715,6 +786,7 @@ void safemode::serialize( JsonOut &json ) const
json.member( "whitelist", elem.whitelist );
json.member( "attitude", elem.attitude );
json.member( "proximity", elem.proximity );
json.member( "category", elem.category );

json.end_object();
}
Expand All @@ -736,9 +808,11 @@ void safemode::deserialize( JsonIn &jsin )
const bool whitelist = jo.get_bool( "whitelist" );
const Creature::Attitude attitude = static_cast<Creature::Attitude>( jo.get_int( "attitude" ) );
const int proximity = jo.get_int( "proximity" );
const Categories cat = jo.has_member( "category" ) ? static_cast<Categories>
( jo.get_int( "category" ) ) : HOSTILE_SPOTTED;

temp_rules.push_back(
rules_class( rule, active, whitelist, attitude, proximity )
rules_class( rule, active, whitelist, attitude, proximity, cat )
);
}
}
19 changes: 14 additions & 5 deletions src/safemode_ui.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ class safemode
MAX_TAB
};

enum Categories : int {
HOSTILE_SPOTTED,
SOUND
};

class rules_class
{
public:
Expand All @@ -30,13 +35,14 @@ class safemode
bool whitelist;
Creature::Attitude attitude;
int proximity;
Categories category;

rules_class() : active( false ), whitelist( false ), attitude( Creature::A_HOSTILE ),
proximity( 0 ) {}
proximity( 0 ), category( Categories::HOSTILE_SPOTTED ) {}
rules_class( const std::string &rule_in, bool active_in, bool whitelist_in,
Creature::Attitude attitude_in, int proximity_in ) : rule( rule_in ),
Creature::Attitude attitude_in, int proximity_in, Categories cat ) : rule( rule_in ),
active( active_in ), whitelist( whitelist_in ),
attitude( attitude_in ), proximity( proximity_in ) {}
attitude( attitude_in ), proximity( proximity_in ), category( cat ) {}
};

class rule_state_class
Expand All @@ -46,7 +52,7 @@ class safemode
int proximity;

rule_state_class() : state( RULE_NONE ), proximity( 0 ) {}
rule_state_class( rule_state state_in, int proximity_in ) : state( state_in ),
rule_state_class( rule_state state_in, int proximity_in, Categories ) : state( state_in ),
proximity( proximity_in ) {}
};

Expand All @@ -57,7 +63,8 @@ class safemode
* is added as the key, with RULE_WHITELISTED or RULE_BLACKLISTED as the values.
* safemode_rules[ 'creature name' ][ 'attitude' ].rule_state_class('rule_state', 'proximity')
*/
std::unordered_map < std::string, std::array < rule_state_class, 3 > > safemode_rules;
std::unordered_map < std::string, std::array < rule_state_class, 3 > > safemode_rules_hostile;
std::vector < rules_class > safemode_rules_sound;

/**
* current rules for global and character tab
Expand Down Expand Up @@ -87,6 +94,8 @@ class safemode
rule_state check_monster( const std::string &creature_name_in, Creature::Attitude attitude_in,
int proximity_in ) const;

bool is_sound_safe( const std::string &sound_name_in, const int proximity_in ) const;

std::string npc_type_name();

void show();
Expand Down
4 changes: 3 additions & 1 deletion src/sounds.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
#include "type_id.h"
#include "point.h"
#include "string_id.h"
#include "safemode_ui.h"

#if defined(SDL_SOUND)
# if defined(_MSC_VER) && defined(USE_VCPKG)
Expand Down Expand Up @@ -431,7 +432,8 @@ void sounds::process_sound_markers( player *p )

// don't print our own noise or things without descriptions
if( !sound.ambient && ( pos != p->pos() ) && !g->m.pl_sees( pos, distance_to_sound ) ) {
if( !p->activity.is_distraction_ignored( distraction_type::noise ) ) {
if( !p->activity.is_distraction_ignored( distraction_type::noise ) &&
!get_safemode().is_sound_safe( sound.description, distance_to_sound ) ) {
const std::string query = string_format( _( "Heard %s!" ), description );
g->cancel_activity_or_ignore_query( distraction_type::noise, query );
}
Expand Down