From 9362af1343a3b85f49a3555794ee8b1690683fb8 Mon Sep 17 00:00:00 2001 From: KorGgenT Date: Sun, 13 Oct 2019 18:39:58 -0400 Subject: [PATCH 01/34] create character_mutations and change scope --- src/character.cpp | 19 ++--- src/character.h | 118 ++--------------------------- src/character_mutations.h | 155 ++++++++++++++++++++++++++++++++++++++ src/melee.cpp | 2 +- src/mutation.cpp | 44 +++++------ src/mutation_ui.cpp | 2 +- src/newcharacter.cpp | 6 +- src/player.cpp | 28 ++++--- src/player.h | 25 ------ 9 files changed, 218 insertions(+), 181 deletions(-) create mode 100644 src/character_mutations.h diff --git a/src/character.cpp b/src/character.cpp index 9c243146f0b14..a32014d038098 100644 --- a/src/character.cpp +++ b/src/character.cpp @@ -14,6 +14,7 @@ #include "avatar.h" #include "bionics.h" #include "cata_utility.h" +#include "character_mutations.h" #include "construction.h" #include "debug.h" #include "effect.h" @@ -3608,7 +3609,7 @@ bool Character::pour_into( vehicle &veh, item &liquid ) return true; } -resistances Character::mutation_armor( body_part bp ) const +resistances character_mutations::mutation_armor( body_part bp ) const { resistances res; for( auto &iter : my_mutations ) { @@ -3618,12 +3619,12 @@ resistances Character::mutation_armor( body_part bp ) const return res; } -float Character::mutation_armor( body_part bp, damage_type dt ) const +float character_mutations::mutation_armor( body_part bp, damage_type dt ) const { return mutation_armor( bp ).type_resist( dt ); } -float Character::mutation_armor( body_part bp, const damage_unit &du ) const +float character_mutations::mutation_armor( body_part bp, const damage_unit &du ) const { return mutation_armor( bp ).get_effective_resist( du ); } @@ -3819,7 +3820,7 @@ std::string Character::extended_description() const return replace_colors( ss.str() ); } -social_modifiers Character::get_mutation_social_mods() const +social_modifiers character_mutations::get_mutation_social_mods() const { social_modifiers mods; for( const mutation_branch *mut : cached_mutations ) { @@ -3897,7 +3898,7 @@ mutation_value_map = { { "skill_rust_multiplier", calc_mutation_value_multiplicative<&mutation_branch::skill_rust_multiplier> } }; -float Character::mutation_value( const std::string &val ) const +float character_mutations::mutation_value( const std::string &val ) const { // Syntax similar to tuple get() const auto found = mutation_value_map.find( val ); @@ -4421,7 +4422,7 @@ std::string get_stat_name( Character::stat Stat ) return pgettext( "fake stat there's an error", "ERR" ); } -void Character::build_mut_dependency_map( const trait_id &mut, +void character_mutations::build_mut_dependency_map( const trait_id &mut, std::unordered_map &dependency_map, int distance ) { // Skip base traits and traits we've seen with a lower distance @@ -4443,7 +4444,7 @@ void Character::build_mut_dependency_map( const trait_id &mut, } } -void Character::set_highest_cat_level() +void character_mutations::set_highest_cat_level() { mutation_category_level.clear(); @@ -4466,7 +4467,7 @@ void Character::set_highest_cat_level() } } -void Character::drench_mut_calc() +void character_mutations::drench_mut_calc() { for( const body_part bp : all_body_parts ) { int ignored = 0; @@ -4490,7 +4491,7 @@ void Character::drench_mut_calc() } /// Returns the mutation category with the highest strength -std::string Character::get_highest_category() const +std::string character_mutations::get_highest_category() const { int iLevel = 0; std::string sMaxCat; diff --git a/src/character.h b/src/character.h index a49843035d8d9..9288dae68ee06 100644 --- a/src/character.h +++ b/src/character.h @@ -19,6 +19,7 @@ #include "bodypart.h" #include "calendar.h" #include "character_id.h" +#include "character_mutations.h" #include "creature.h" #include "game_constants.h" #include "inventory.h" @@ -461,22 +462,6 @@ class Character : public Creature, public visitable /** Returns the id of a random starting trait that costs < 0 points */ trait_id random_bad_trait(); - // In mutation.cpp - /** Returns true if the player has the entered trait */ - bool has_trait( const trait_id &b ) const override; - /** Returns true if the player has the entered starting trait */ - bool has_base_trait( const trait_id &b ) const; - /** Returns true if player has a trait with a flag */ - bool has_trait_flag( const std::string &b ) const; - /** Returns the trait id with the given invlet, or an empty string if no trait has that invlet */ - trait_id trait_by_invlet( int ch ) const; - - /** Toggles a trait on the player and in their mutation list */ - void toggle_trait( const trait_id &flag ); - /** Add or removes a mutation on the player, but does not trigger mutation loss/gain effects. */ - void set_mutation( const trait_id &flag ); - void unset_mutation( const trait_id &flag ); - /** Converts a body_part to an hp_part */ static hp_part bp_to_hp( body_part bp ); /** Converts an hp_part to a body_part */ @@ -570,8 +555,6 @@ class Character : public Creature, public visitable position = p; } private: - /** Retrieves a stat mod of a mutation. */ - int get_mod( const trait_id &mut, const std::string &arg ) const; /** Applies skill-based boosts to stats **/ void apply_skill_boost(); protected: @@ -603,43 +586,6 @@ class Character : public Creature, public visitable // gets add and mult value from enchantment cache double calculate_by_enchantment( double modify, enchantment::mod value, bool round_output = false ) const; - /** Handles things like destruction of armor, etc. */ - void mutation_effect( const trait_id &mut ); - /** Handles what happens when you lose a mutation. */ - void mutation_loss_effect( const trait_id &mut ); - - bool has_active_mutation( const trait_id &b ) const; - /** Picks a random valid mutation and gives it to the Character, possibly removing/changing others along the way */ - void mutate(); - /** Returns true if the player doesn't have the mutation or a conflicting one and it complies with the force typing */ - bool mutation_ok( const trait_id &mutation, bool force_good, bool force_bad ) const; - /** Picks a random valid mutation in a category and mutate_towards() it */ - void mutate_category( const std::string &mut_cat ); - /** Mutates toward one of the given mutations, upgrading or removing conflicts if necessary */ - bool mutate_towards( std::vector muts, int num_tries = INT_MAX ); - /** Mutates toward the entered mutation, upgrading or removing conflicts if necessary */ - bool mutate_towards( const trait_id &mut ); - /** Removes a mutation, downgrading to the previous level if possible */ - void remove_mutation( const trait_id &mut, bool silent = false ); - /** Returns true if the player has the entered mutation child flag */ - bool has_child_flag( const trait_id &flag ) const; - /** Removes the mutation's child flag from the player's list */ - void remove_child_flag( const trait_id &flag ); - /** Recalculates mutation_category_level[] values for the player */ - void set_highest_cat_level(); - /** Returns the highest mutation category */ - std::string get_highest_category() const; - /** Recalculates mutation drench protection for all bodyparts (ignored/good/neutral stats) */ - void drench_mut_calc(); - /** Recursively traverses the mutation's prerequisites and replacements, building up a map */ - void build_mut_dependency_map( const trait_id &mut, - std::unordered_map &dependency_map, int distance ); - - /** - * Returns true if this category of mutation is allowed. - */ - bool is_category_allowed( const std::vector &category ) const; - bool is_category_allowed( const std::string &category ) const; bool is_weak_to_water() const; @@ -648,14 +594,6 @@ class Character : public Creature, public visitable bool can_install_cbm_on_bp( const std::vector &bps ) const; - /** - * Returns resistances on a body part provided by mutations - */ - // TODO: Cache this, it's kinda expensive to compute - resistances mutation_armor( body_part bp ) const; - float mutation_armor( body_part bp, damage_type dt ) const; - float mutation_armor( body_part bp, const damage_unit &du ) const; - // --------------- Bionic Stuff --------------- std::vector get_bionics() const; /** Returns true if the player has the entered bionic id */ @@ -1011,17 +949,6 @@ class Character : public Creature, public visitable */ float healing_rate_medicine( float at_rest_quality, body_part bp ) const; - /** - * Goes over all mutations, gets min and max of a value with given name - * @return min( 0, lowest ) + max( 0, highest ) - */ - float mutation_value( const std::string &val ) const; - - /** - * Goes over all mutations, returning the sum of the social modifiers - */ - social_modifiers get_mutation_social_mods() const; - /** Color's character's tile's background */ nc_color symbol_color() const override; @@ -1031,15 +958,9 @@ class Character : public Creature, public visitable void empty_skills(); /** Returns a random name from NAMES_* */ void pick_name( bool bUseDefault = false ); - /** Get the idents of all base traits. */ - std::vector get_base_traits() const; - /** Get the idents of all traits/mutations. */ - std::vector get_mutations( bool include_hidden = true ) const; const std::bitset &get_vision_modes() const { return vision_mode_cache; } - /** Empties the trait list */ - void empty_traits(); /** * Adds mandatory scenario and profession traits unless you already have them * And if you do already have them, refunds the points for the trait @@ -1159,8 +1080,6 @@ class Character : public Creature, public visitable // the amount healed per bodypart per day std::array healed_total; - std::map mutation_category_level; - void spores(); void blossoms(); @@ -1201,24 +1120,15 @@ class Character : public Creature, public visitable double footwear_factor() const; /** Returns true if the player is wearing something on their feet that is not SKINTIGHT */ bool is_wearing_shoes( const side &which_side = side::BOTH ) const; + + character_mutations mutations; + + void spores(); + void blossoms(); protected: Character(); Character( Character && ); Character &operator=( Character && ); - struct trait_data { - /** Whether the mutation is activated. */ - bool powered = false; - /** Key to select the mutation in the UI. */ - char key = ' '; - /** - * Time (in turns) until the mutation increase hunger/thirst/fatigue according - * to its cost (@ref mutation_branch::cost). When those costs have been paid, this - * is reset to @ref mutation_branch::cooldown. - */ - int charge = 0; - void serialize( JsonOut &json ) const; - void deserialize( JsonIn &jsin ); - }; // The player's position on the local map. tripoint position; @@ -1242,22 +1152,6 @@ class Character : public Creature, public visitable std::array encumbrance_cache; mutable std::map cached_info; - /** - * Traits / mutations of the character. Key is the mutation id (it's also a valid - * key into @ref mutation_data), the value describes the status of the mutation. - * If there is not entry for a mutation, the character does not have it. If the map - * contains the entry, the character has the mutation. - */ - std::unordered_map my_mutations; - /** - * Contains mutation ids of the base traits. - */ - std::unordered_set my_traits; - /** - * Pointers to mutation branches in @ref my_mutations. - */ - std::vector cached_mutations; - void store( JsonOut &json ) const; void load( JsonObject &data ); diff --git a/src/character_mutations.h b/src/character_mutations.h new file mode 100644 index 0000000000000..5939f2dde28dc --- /dev/null +++ b/src/character_mutations.h @@ -0,0 +1,155 @@ +#pragma once +#ifndef CHARACTER_MUTATIONS_H +#define CHARACTER_MUTATIONS_H + +#include "mutation.h" +#include "player.h" +#include "type_id.h" + +// contains all of the mutation data a character has +class character_mutations +{ + public: + struct trait_data { + /** Whether the mutation is activated. */ + bool powered = false; + /** Key to select the mutation in the UI. */ + char key = ' '; + /** + * Time (in turns) until the mutation increase hunger/thirst/fatigue according + * to its cost (@ref mutation_branch::cost). When those costs have been paid, this + * is reset to @ref mutation_branch::cooldown. + */ + int charge = 0; + void serialize( JsonOut &json ) const; + void deserialize( JsonIn &jsin ); + }; + /** Get the idents of all base traits. */ + std::vector get_base_traits() const; + /** Get the idents of all traits/mutations. */ + std::vector get_mutations( bool include_hidden = true ) const; + /** + * Goes over all mutations, gets min and max of a value with given name + * @return min( 0, lowest ) + max( 0, highest ) + */ + float mutation_value( const std::string &val ) const; + + /** + * Goes over all mutations, returning the sum of the social modifiers + */ + social_modifiers get_mutation_social_mods() const; + /** + * Returns resistances on a body part provided by mutations + */ + // TODO: Cache this, it's kinda expensive to compute + resistances mutation_armor( body_part bp ) const; + float mutation_armor( body_part bp, damage_type dt ) const; + float mutation_armor( body_part bp, const damage_unit &du ) const; + /** Handles things like destruction of armor, etc. */ + void mutation_effect( const trait_id &mut ); + /** Handles what happens when you lose a mutation. */ + void mutation_loss_effect( const trait_id &mut ); + + bool has_active_mutation( const trait_id &b ) const; + /** Picks a random valid mutation and gives it to the Character, possibly removing/changing others along the way */ + void mutate(); + /** Returns true if the player doesn't have the mutation or a conflicting one and it complies with the force typing */ + bool mutation_ok( const trait_id &mutation, bool force_good, bool force_bad ) const; + /** Picks a random valid mutation in a category and mutate_towards() it */ + void mutate_category( const std::string &mut_cat ); + /** Mutates toward one of the given mutations, upgrading or removing conflicts if necessary */ + bool mutate_towards( std::vector muts, int num_tries = INT_MAX ); + /** Mutates toward the entered mutation, upgrading or removing conflicts if necessary */ + bool mutate_towards( const trait_id &mut ); + /** Removes a mutation, downgrading to the previous level if possible */ + void remove_mutation( const trait_id &mut, bool silent = false ); + /** Returns true if the player has the entered mutation child flag */ + bool has_child_flag( const trait_id &flag ) const; + /** Removes the mutation's child flag from the player's list */ + void remove_child_flag( const trait_id &flag ); + /** Recalculates mutation_category_level[] values for the player */ + void set_highest_cat_level(); + /** Returns the highest mutation category */ + std::string get_highest_category() const; + /** Recalculates mutation drench protection for all bodyparts (ignored/good/neutral stats) */ + void drench_mut_calc(); + /** Recursively traverses the mutation's prerequisites and replacements, building up a map */ + void build_mut_dependency_map( const trait_id &mut, + std::unordered_map &dependency_map, int distance ); + + /** + * Returns true if this category of mutation is allowed. + */ + bool is_category_allowed( const std::vector &category ) const; + bool is_category_allowed( const std::string &category ) const; + + // In mutation.cpp + /** Returns true if the player has the entered trait */ + bool has_trait( const trait_id &b ) const; + /** Returns true if the player has the entered starting trait */ + bool has_base_trait( const trait_id &b ) const; + /** Returns true if player has a trait with a flag */ + bool has_trait_flag( const std::string &b ) const; + /** Returns the trait id with the given invlet, or an empty string if no trait has that invlet */ + trait_id trait_by_invlet( int ch ) const; + + /** Toggles a trait on the player and in their mutation list */ + void toggle_trait( const trait_id &flag ); + /** Add or removes a mutation on the player, but does not trigger mutation loss/gain effects. */ + void set_mutation( const trait_id &flag ); + void unset_mutation( const trait_id &flag ); + /** Returns true if the player has a conflicting trait to the entered trait + * Uses has_opposite_trait(), has_lower_trait(), and has_higher_trait() to determine conflicts. + */ + bool has_conflicting_trait( const trait_id &flag ) const; + /** Returns true if the player has a trait which cancels the entered trait */ + bool has_opposite_trait( const trait_id &flag ) const; + /** Returns true if the player has a trait which upgrades into the entered trait */ + bool has_lower_trait( const trait_id &flag ) const; + /** Returns true if the player has a trait which is an upgrade of the entered trait */ + bool has_higher_trait( const trait_id &flag ) const; + /** Returns true if the player has a trait that shares a type with the entered trait */ + bool has_same_type_trait( const trait_id &flag ) const; + /** Returns true if the player has crossed a mutation threshold + * Player can only cross one mutation threshold. + */ + bool crossed_threshold() const; + /** Returns true if the entered trait may be purified away + * Defaults to true + */ + bool purifiable( const trait_id &flag ) const; + + void power_mutations(); + /** Returns a vector of valid mutation attacks */ + std::vector mutation_attacks( Creature &t ) const; + /** Retrieves a stat mod of a mutation. */ + int get_mod( const trait_id &mut, const std::string &arg ) const; + /** Empties the trait list */ + void empty_traits(); + /** Correction factor of the body temperature due to traits and mutations **/ + int bodytemp_modifier_traits( bool overheated ) const; + /** Correction factor of the body temperature due to traits and mutations for player lying on the floor **/ + int bodytemp_modifier_traits_floor() const; + /** Returns an enumeration of visible mutations with colors */ + std::string visible_mutations( int visibility_cap ) const; + private: + /** + * Traits / mutations of the character. Key is the mutation id (it's also a valid + * key into @ref mutation_data), the value describes the status of the mutation. + * If there is not entry for a mutation, the character does not have it. If the map + * contains the entry, the character has the mutation. + */ + std::unordered_map my_mutations; + /** + * Contains mutation ids of the base traits. + */ + std::unordered_set my_traits; + /** + * Pointers to mutation branches in @ref my_mutations. + */ + std::vector cached_mutations; + + std::map mutation_category_level; +}; + +#endif diff --git a/src/melee.cpp b/src/melee.cpp index 9ef696cf0d5a0..a048a01f49f5d 100644 --- a/src/melee.cpp +++ b/src/melee.cpp @@ -1843,7 +1843,7 @@ static damage_instance hardcoded_mutation_attack( const player &u, const trait_i return damage_instance(); } -std::vector player::mutation_attacks( Creature &t ) const +std::vector character_mutations::mutation_attacks( Creature &t ) const { std::vector ret; diff --git a/src/mutation.cpp b/src/mutation.cpp index 07108c56cb6da..966e83e1ece58 100644 --- a/src/mutation.cpp +++ b/src/mutation.cpp @@ -9,6 +9,7 @@ #include "event_bus.h" #include "field.h" #include "game.h" +#include "character_mutations.h" #include "item.h" #include "itype.h" #include "map.h" @@ -77,12 +78,12 @@ std::string enum_to_string( mutagen_technique data ) } // namespace io -bool Character::has_trait( const trait_id &b ) const +bool character_mutations::has_trait( const trait_id &b ) const { return my_mutations.count( b ) > 0; } -bool Character::has_trait_flag( const std::string &b ) const +bool character_mutations::has_trait_flag( const std::string &b ) const { // UGLY, SLOW, should be cached as my_mutation_flags or something for( const auto &mut : my_mutations ) { @@ -95,13 +96,13 @@ bool Character::has_trait_flag( const std::string &b ) const return false; } -bool Character::has_base_trait( const trait_id &b ) const +bool character_mutations::has_base_trait( const trait_id &b ) const { // Look only at base traits return my_traits.find( b ) != my_traits.end(); } -void Character::toggle_trait( const trait_id &flag ) +void character_mutations::toggle_trait( const trait_id &flag ) { const auto titer = my_traits.find( flag ); if( titer == my_traits.end() ) { @@ -119,7 +120,7 @@ void Character::toggle_trait( const trait_id &flag ) } } -void Character::set_mutation( const trait_id &flag ) +void character_mutations::set_mutation( const trait_id &flag ) { const auto iter = my_mutations.find( flag ); if( iter == my_mutations.end() ) { @@ -132,7 +133,7 @@ void Character::set_mutation( const trait_id &flag ) reset_encumbrance(); } -void Character::unset_mutation( const trait_id &flag ) +void character_mutations::unset_mutation( const trait_id &flag ) { const auto iter = my_mutations.find( flag ); if( iter != my_mutations.end() ) { @@ -147,7 +148,7 @@ void Character::unset_mutation( const trait_id &flag ) reset_encumbrance(); } -int Character::get_mod( const trait_id &mut, const std::string &arg ) const +int character_mutations::get_mod( const trait_id &mut, const std::string &arg ) const { auto &mod_data = mut->mods; int ret = 0; @@ -198,7 +199,7 @@ const resistances &mutation_branch::damage_resistance( body_part bp ) const return iter->second; } -void Character::mutation_effect( const trait_id &mut ) +void character_mutations::mutation_effect( const trait_id &mut ) { if( mut == "GLASSJAW" ) { recalc_hp(); @@ -270,7 +271,7 @@ void Character::mutation_effect( const trait_id &mut ) on_mutation_gain( mut ); } -void Character::mutation_loss_effect( const trait_id &mut ) +void character_mutations::mutation_loss_effect( const trait_id &mut ) { if( mut == "GLASSJAW" ) { recalc_hp(); @@ -312,13 +313,13 @@ void Character::mutation_loss_effect( const trait_id &mut ) on_mutation_loss( mut ); } -bool Character::has_active_mutation( const trait_id &b ) const +bool character_mutations::has_active_mutation( const trait_id &b ) const { const auto iter = my_mutations.find( b ); return iter != my_mutations.end() && iter->second.powered; } -bool Character::is_category_allowed( const std::vector &category ) const +bool character_mutations::is_category_allowed( const std::vector &category ) const { bool allowed = false; bool restricted = false; @@ -341,7 +342,7 @@ bool Character::is_category_allowed( const std::vector &category ) } -bool Character::is_category_allowed( const std::string &category ) const +bool character_mutations::is_category_allowed( const std::string &category ) const { bool allowed = false; bool restricted = false; @@ -577,7 +578,7 @@ void player::deactivate_mutation( const trait_id &mut ) recalc_sight_limits(); } -trait_id Character::trait_by_invlet( const int ch ) const +trait_id character_mutations::trait_by_invlet( const int ch ) const { for( auto &mut : my_mutations ) { if( mut.second.key == ch ) { @@ -587,7 +588,8 @@ trait_id Character::trait_by_invlet( const int ch ) const return trait_id::NULL_ID(); } -bool Character::mutation_ok( const trait_id &mutation, bool force_good, bool force_bad ) const +bool character_mutations::mutation_ok( const trait_id &mutation, bool force_good, + bool force_bad ) const { if( !is_category_allowed( mutation->category ) ) { return false; @@ -613,7 +615,7 @@ bool Character::mutation_ok( const trait_id &mutation, bool force_good, bool for return true; } -void Character::mutate() +void character_mutations::mutate() { bool force_bad = one_in( 3 ); bool force_good = false; @@ -770,7 +772,7 @@ void Character::mutate() } } -void Character::mutate_category( const std::string &cat ) +void character_mutations::mutate_category( const std::string &cat ) { // Hacky ID comparison is better than separate hardcoded branch used before // TODO: Turn it into the null id @@ -819,7 +821,7 @@ static std::vector get_all_mutation_prereqs( const trait_id &id ) return ret; } -bool Character::mutate_towards( std::vector muts, int num_tries ) +bool character_mutations::mutate_towards( std::vector muts, int num_tries ) { while( !muts.empty() && num_tries > 0 ) { int i = rng( 0, muts.size() - 1 ); @@ -835,7 +837,7 @@ bool Character::mutate_towards( std::vector muts, int num_tries ) return false; } -bool Character::mutate_towards( const trait_id &mut ) +bool character_mutations::mutate_towards( const trait_id &mut ) { if( has_child_flag( mut ) ) { remove_child_flag( mut ); @@ -1067,7 +1069,7 @@ bool Character::mutate_towards( const trait_id &mut ) return true; } -void Character::remove_mutation( const trait_id &mut, bool silent ) +void character_mutations::remove_mutation( const trait_id &mut, bool silent ) { const auto &mdata = mut.obj(); // Check if there's a prerequisite we should shrink back into @@ -1219,7 +1221,7 @@ void Character::remove_mutation( const trait_id &mut, bool silent ) drench_mut_calc(); } -bool Character::has_child_flag( const trait_id &flag ) const +bool character_mutations::has_child_flag( const trait_id &flag ) const { for( const trait_id &elem : flag->replacements ) { const trait_id &tmp = elem; @@ -1230,7 +1232,7 @@ bool Character::has_child_flag( const trait_id &flag ) const return false; } -void Character::remove_child_flag( const trait_id &flag ) +void character_mutations::remove_child_flag( const trait_id &flag ) { for( auto &elem : flag->replacements ) { const trait_id &tmp = elem; diff --git a/src/mutation_ui.cpp b/src/mutation_ui.cpp index c342b7f71a8a0..a2fe7e6585c6b 100644 --- a/src/mutation_ui.cpp +++ b/src/mutation_ui.cpp @@ -56,7 +56,7 @@ static void show_mutations_titlebar( const catacurses::window &window, wrefresh( window ); } -void player::power_mutations() +void character_mutations::power_mutations() { if( !is_player() ) { // TODO: Implement NPCs activating mutations diff --git a/src/newcharacter.cpp b/src/newcharacter.cpp index aab5060eb080e..8d3beb02cfdcf 100644 --- a/src/newcharacter.cpp +++ b/src/newcharacter.cpp @@ -2484,12 +2484,12 @@ tab_direction set_description( const catacurses::window &w, avatar &you, const b } while( true ); } -std::vector Character::get_base_traits() const +std::vector character_mutations::get_base_traits() const { return std::vector( my_traits.begin(), my_traits.end() ); } -std::vector Character::get_mutations( bool include_hidden ) const +std::vector character_mutations::get_mutations( bool include_hidden ) const { std::vector result; for( auto &t : my_mutations ) { @@ -2500,7 +2500,7 @@ std::vector Character::get_mutations( bool include_hidden ) const return result; } -void Character::empty_traits() +void character_mutations::empty_traits() { for( auto &mut : my_mutations ) { on_mutation_loss( mut.first ); diff --git a/src/player.cpp b/src/player.cpp index 9b0d714fea04e..00dddc3676a37 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -1391,7 +1391,7 @@ int player::floor_warmth( const tripoint &pos ) const return ( item_warmth + bedding_warmth + floor_mut_warmth ); } -int player::bodytemp_modifier_traits( bool overheated ) const +int character_mutations::bodytemp_modifier_traits( bool overheated ) const { int mod = 0; for( auto &iter : my_mutations ) { @@ -1401,7 +1401,7 @@ int player::bodytemp_modifier_traits( bool overheated ) const return mod; } -int player::bodytemp_modifier_traits_floor() const +int character_mutations::bodytemp_modifier_traits_floor() const { int mod = 0; for( auto &iter : my_mutations ) { @@ -1960,13 +1960,13 @@ time_duration player::estimate_effect_dur( const skill_id &relevant_skill, return estimate; } -bool player::has_conflicting_trait( const trait_id &flag ) const +bool character_mutations::has_conflicting_trait( const trait_id &flag ) const { return ( has_opposite_trait( flag ) || has_lower_trait( flag ) || has_higher_trait( flag ) || has_same_type_trait( flag ) ); } -bool player::has_opposite_trait( const trait_id &flag ) const +bool character_mutations::has_opposite_trait( const trait_id &flag ) const { for( auto &i : flag->cancels ) { if( has_trait( i ) ) { @@ -1976,7 +1976,7 @@ bool player::has_opposite_trait( const trait_id &flag ) const return false; } -bool player::has_lower_trait( const trait_id &flag ) const +bool character_mutations::has_lower_trait( const trait_id &flag ) const { for( auto &i : flag->prereqs ) { if( has_trait( i ) || has_lower_trait( i ) ) { @@ -1986,7 +1986,7 @@ bool player::has_lower_trait( const trait_id &flag ) const return false; } -bool player::has_higher_trait( const trait_id &flag ) const +bool character_mutations::has_higher_trait( const trait_id &flag ) const { for( auto &i : flag->replacements ) { if( has_trait( i ) || has_higher_trait( i ) ) { @@ -1996,7 +1996,7 @@ bool player::has_higher_trait( const trait_id &flag ) const return false; } -bool player::has_same_type_trait( const trait_id &flag ) const +bool character_mutations::has_same_type_trait( const trait_id &flag ) const { for( auto &i : get_mutations_in_types( flag->types ) ) { if( has_trait( i ) && flag != i ) { @@ -2006,7 +2006,17 @@ bool player::has_same_type_trait( const trait_id &flag ) const return false; } -bool player::purifiable( const trait_id &flag ) const +bool character_mutations::crossed_threshold() const +{ + for( auto &mut : my_mutations ) { + if( mut.first->threshold ) { + return true; + } + } + return false; +} + +bool character_mutations::purifiable( const trait_id &flag ) const { return flag->purifiable; } @@ -10376,7 +10386,7 @@ float player::hearing_ability() const return volume_multiplier; } -std::string player::visible_mutations( const int visibility_cap ) const +std::string character_mutations::visible_mutations( const int visibility_cap ) const { const std::string trait_str = enumerate_as_string( my_mutations.begin(), my_mutations.end(), [visibility_cap ]( const std::pair &pr ) -> std::string { diff --git a/src/player.h b/src/player.h index 7a9d1d4fb7e2f..5a3e826d1e669 100644 --- a/src/player.h +++ b/src/player.h @@ -201,8 +201,6 @@ class player : public Character /** Returns what color the player should be drawn as */ nc_color basic_symbol_color() const override; - /** Returns an enumeration of visible mutations with colors */ - std::string visible_mutations( int visibility_cap ) const; std::vector short_description_parts() const; std::string short_description() const; int print_info( const catacurses::window &w, int vStart, int vLines, int column ) const override; @@ -266,22 +264,6 @@ class player : public Character /** Returns if the player has hibernation mutation and is asleep and well fed */ bool is_hibernating() const; - /** Returns true if the player has a conflicting trait to the entered trait - * Uses has_opposite_trait(), has_lower_trait(), and has_higher_trait() to determine conflicts. - */ - bool has_conflicting_trait( const trait_id &flag ) const; - /** Returns true if the player has a trait which cancels the entered trait */ - bool has_opposite_trait( const trait_id &flag ) const; - /** Returns true if the player has a trait which upgrades into the entered trait */ - bool has_lower_trait( const trait_id &flag ) const; - /** Returns true if the player has a trait which is an upgrade of the entered trait */ - bool has_higher_trait( const trait_id &flag ) const; - /** Returns true if the player has a trait that shares a type with the entered trait */ - bool has_same_type_trait( const trait_id &flag ) const; - /** Returns true if the entered trait may be purified away - * Defaults to true - */ - bool purifiable( const trait_id &flag ) const; /** Returns a dream's description selected randomly from the player's highest mutation category */ std::string get_category_dream( const std::string &cat, int strength ) const; @@ -338,7 +320,6 @@ class player : public Character bool has_enough_anesth( const itype *cbm, player &patient ); /** Generates and handles the UI for player interaction with installed bionics */ void power_bionics(); - void power_mutations(); /** Handles bionic activation effects of the entered bionic, returns if anything activated */ bool activate_bionic( int b, bool eff_only = false ); /** Handles bionic deactivation effects of the entered bionic, returns if anything deactivated */ @@ -624,8 +605,6 @@ class player : public Character /** Performs special attacks and their effects (poisonous, stinger, etc.) */ void perform_special_attacks( Creature &t ); - /** Returns a vector of valid mutation attacks */ - std::vector mutation_attacks( Creature &t ) const; /** Handles combat effects, returns a string of any valid combat effect messages */ std::string melee_special_effects( Creature &t, damage_instance &d, item &weap ); /** Returns Creature::get_dodge_base modified by the player's skill level */ @@ -1611,10 +1590,6 @@ class player : public Character static int floor_item_warmth( const tripoint &pos ); /** Final warmth from the floor **/ int floor_warmth( const tripoint &pos ) const; - /** Correction factor of the body temperature due to traits and mutations **/ - int bodytemp_modifier_traits( bool overheated ) const; - /** Correction factor of the body temperature due to traits and mutations for player lying on the floor **/ - int bodytemp_modifier_traits_floor() const; /** Value of the body temperature corrected by climate control **/ int temp_corrected_by_climate_control( int temperature ) const; /** Define blood loss (in percents) */ From 910d733a3acd1e010986a9e43fe42a89fa332e2b Mon Sep 17 00:00:00 2001 From: KorGgenT Date: Sun, 13 Oct 2019 18:56:37 -0400 Subject: [PATCH 02/34] remove Creature::has_trait() --- src/creature.cpp | 6 ------ src/creature.h | 3 --- 2 files changed, 9 deletions(-) diff --git a/src/creature.cpp b/src/creature.cpp index b77f0018a4313..1f937acc828aa 100644 --- a/src/creature.cpp +++ b/src/creature.cpp @@ -1205,12 +1205,6 @@ bool Creature::resists_effect( const effect &e ) return false; } -bool Creature::has_trait( const trait_id &flag ) const -{ - ( void )flag; - return false; -} - // Methods for setting/getting misc key/value pairs. void Creature::set_value( const std::string &key, const std::string &value ) { diff --git a/src/creature.h b/src/creature.h index dd4329eb75eca..c34a8f2b1ebce 100644 --- a/src/creature.h +++ b/src/creature.h @@ -370,9 +370,6 @@ class Creature /** Processes through all the effects on the Creature. */ virtual void process_effects(); - /** Returns true if the player has the entered trait, returns false for non-humans */ - virtual bool has_trait( const trait_id &flag ) const; - // not-quite-stats, maybe group these with stats later virtual void mod_pain( int npain ); virtual void mod_pain_noresist( int npain ); From 4ee35bf06b7ccc825eb2fcf35e8127dc14159336 Mon Sep 17 00:00:00 2001 From: KorGgenT Date: Sun, 13 Oct 2019 18:57:58 -0400 Subject: [PATCH 03/34] player::has_trait to player:::mutations::has_trait --- src/character.cpp | 126 +++++----- src/mutation.cpp | 28 +-- src/player.cpp | 573 ++++++++++++++++++++++++---------------------- 3 files changed, 373 insertions(+), 354 deletions(-) diff --git a/src/character.cpp b/src/character.cpp index a32014d038098..9d00e3233554f 100644 --- a/src/character.cpp +++ b/src/character.cpp @@ -217,16 +217,16 @@ character_id Character::getID() const field_type_id Character::bloodType() const { - if( has_trait( trait_ACIDBLOOD ) ) { + if( mutations.has_trait( trait_ACIDBLOOD ) ) { return fd_acid; } - if( has_trait( trait_THRESH_PLANT ) ) { + if( mutations.has_trait( trait_THRESH_PLANT ) ) { return fd_blood_veggy; } - if( has_trait( trait_THRESH_INSECT ) || has_trait( trait_THRESH_SPIDER ) ) { + if( mutations.has_trait( trait_THRESH_INSECT ) || mutations.has_trait( trait_THRESH_SPIDER ) ) { return fd_blood_insect; } - if( has_trait( trait_THRESH_CEPHALOPOD ) ) { + if( mutations.has_trait( trait_THRESH_CEPHALOPOD ) ) { return fd_blood_invertebrate; } return fd_blood; @@ -274,7 +274,7 @@ void Character::mod_stat( const std::string &stat, float modifier ) int Character::get_fat_to_hp() const { float mut_fat_hp = 0; - for( const trait_id &mut : get_mutations() ) { + for( const trait_id &mut : mutations.get_mutations() ) { mut_fat_hp += mut.obj().fat_to_max_hp; } @@ -638,7 +638,7 @@ bool Character::has_two_arms() const // and still not count if they have low enough hitpoints int Character::get_working_arm_count() const { - if( has_active_mutation( trait_id( "SHELL2" ) ) ) { + if( mutations.has_active_mutation( trait_id( "SHELL2" ) ) ) { return 0; } @@ -948,14 +948,15 @@ void Character::recalc_hp() str_boost_val = str_boost->calc_bonus( skill_total ); } // Mutated toughness stacks with starting, by design. - float hp_mod = 1.0f + mutation_value( "hp_modifier" ) + mutation_value( "hp_modifier_secondary" ); - float hp_adjustment = mutation_value( "hp_adjustment" ) + ( str_boost_val * 3 ); + float hp_mod = 1.0f + mutations.mutation_value( "hp_modifier" ) + + mutations.mutation_value( "hp_modifier_secondary" ); + float hp_adjustment = mutations.mutation_value( "hp_adjustment" ) + ( str_boost_val * 3 ); for( auto &elem : new_max_hp ) { /** @EFFECT_STR_MAX increases base hp */ elem = 60 + str_max * 3 + hp_adjustment + get_fat_to_hp(); elem *= hp_mod; } - if( has_trait( trait_GLASSJAW ) ) { + if( mutations.has_trait( trait_GLASSJAW ) ) { new_max_hp[hp_head] *= 0.8; } for( int i = 0; i < num_hp_parts; i++ ) { @@ -988,24 +989,24 @@ void Character::recalc_sight_limits() vision_mode_cache.reset(); // Set sight_max. - if( is_blind() || ( in_sleep_state() && !has_trait( trait_SEESLEEP ) ) || + if( is_blind() || ( in_sleep_state() && !mutations.has_trait( trait_SEESLEEP ) ) || has_effect( effect_narcosis ) ) { sight_max = 0; - } else if( has_effect( effect_boomered ) && ( !( has_trait( trait_PER_SLIME_OK ) ) ) ) { + } else if( has_effect( effect_boomered ) && ( !( mutations.has_trait( trait_PER_SLIME_OK ) ) ) ) { sight_max = 1; vision_mode_cache.set( BOOMERED ); } else if( has_effect( effect_in_pit ) || has_effect( effect_no_sight ) || ( underwater && !has_bionic( bionic_id( "bio_membrane" ) ) && - !has_trait( trait_MEMBRANE ) && !worn_with_flag( "SWIM_GOGGLES" ) && - !has_trait( trait_CEPH_EYES ) && !has_trait( trait_PER_SLIME_OK ) ) ) { + !mutations.has_trait( trait_MEMBRANE ) && !worn_with_flag( "SWIM_GOGGLES" ) && + !mutations.has_trait( trait_CEPH_EYES ) && !mutations.has_trait( trait_PER_SLIME_OK ) ) ) { sight_max = 1; - } else if( has_active_mutation( trait_SHELL2 ) ) { + } else if( mutations.has_active_mutation( trait_SHELL2 ) ) { // You can kinda see out a bit. sight_max = 2; - } else if( ( has_trait( trait_MYOPIC ) || has_trait( trait_URSINE_EYE ) ) && + } else if( ( mutations.has_trait( trait_MYOPIC ) || mutations.has_trait( trait_URSINE_EYE ) ) && !worn_with_flag( "FIX_NEARSIGHT" ) && !has_effect( effect_contacts ) ) { sight_max = 4; - } else if( has_trait( trait_PER_SLIME ) ) { + } else if( mutations.has_trait( trait_PER_SLIME ) ) { sight_max = 6; } else if( has_effect( effect_darkness ) ) { vision_mode_cache.set( DARKNESS ); @@ -1013,45 +1014,45 @@ void Character::recalc_sight_limits() } // Debug-only NV, by vache's request - if( has_trait( trait_DEBUG_NIGHTVISION ) ) { + if( mutations.has_trait( trait_DEBUG_NIGHTVISION ) ) { vision_mode_cache.set( DEBUG_NIGHTVISION ); } if( has_nv() ) { vision_mode_cache.set( NV_GOGGLES ); } - if( has_active_mutation( trait_NIGHTVISION3 ) || is_wearing( "rm13_armor_on" ) || + if( mutations.has_active_mutation( trait_NIGHTVISION3 ) || is_wearing( "rm13_armor_on" ) || ( is_mounted() && mounted_creature->has_flag( MF_MECH_RECON_VISION ) ) ) { vision_mode_cache.set( NIGHTVISION_3 ); } - if( has_active_mutation( trait_ELFA_FNV ) ) { + if( mutations.has_active_mutation( trait_ELFA_FNV ) ) { vision_mode_cache.set( FULL_ELFA_VISION ); } - if( has_active_mutation( trait_CEPH_VISION ) ) { + if( mutations.has_active_mutation( trait_CEPH_VISION ) ) { vision_mode_cache.set( CEPH_VISION ); } - if( has_active_mutation( trait_ELFA_NV ) ) { + if( mutations.has_active_mutation( trait_ELFA_NV ) ) { vision_mode_cache.set( ELFA_VISION ); } - if( has_active_mutation( trait_NIGHTVISION2 ) ) { + if( mutations.has_active_mutation( trait_NIGHTVISION2 ) ) { vision_mode_cache.set( NIGHTVISION_2 ); } - if( has_active_mutation( trait_FEL_NV ) ) { + if( mutations.has_active_mutation( trait_FEL_NV ) ) { vision_mode_cache.set( FELINE_VISION ); } - if( has_active_mutation( trait_URSINE_EYE ) ) { + if( mutations.has_active_mutation( trait_URSINE_EYE ) ) { vision_mode_cache.set( URSINE_VISION ); } - if( has_active_mutation( trait_NIGHTVISION ) ) { + if( mutations.has_active_mutation( trait_NIGHTVISION ) ) { vision_mode_cache.set( NIGHTVISION_1 ); } - if( has_trait( trait_BIRD_EYE ) ) { + if( mutations.has_trait( trait_BIRD_EYE ) ) { vision_mode_cache.set( BIRD_EYE ); } // Not exactly a sight limit thing, but related enough if( has_active_bionic( bionic_id( "bio_infrared" ) ) || - has_trait( trait_id( "INFRARED" ) ) || - has_trait( trait_id( "LIZ_IR" ) ) || + mutations.has_trait( trait_id( "INFRARED" ) ) || + mutations.has_trait( trait_id( "LIZ_IR" ) ) || worn_with_flag( "IR_EFFECT" ) || ( is_mounted() && mounted_creature->has_flag( MF_MECH_RECON_VISION ) ) ) { vision_mode_cache.set( IR_VISION ); @@ -1848,7 +1849,7 @@ units::volume Character::volume_carried_with_tweaks( const item_tweaks &tweaks ) units::mass Character::weight_capacity() const { - if( has_trait( trait_id( "DEBUG_STORAGE" ) ) ) { + if( mutations.has_trait( trait_id( "DEBUG_STORAGE" ) ) ) { // Infinite enough return units::mass_max; } @@ -1857,7 +1858,7 @@ units::mass Character::weight_capacity() const units::mass ret = Creature::weight_capacity(); /** @EFFECT_STR increases carrying capacity */ ret += get_str() * 4_kilogram; - ret *= mutation_value( "weight_capacity_modifier" ); + ret *= mutations.mutation_value( "weight_capacity_modifier" ); units::mass worn_weight_bonus = 0_gram; for( const item &it : worn ) { @@ -1898,7 +1899,7 @@ units::volume Character::volume_capacity() const units::volume Character::volume_capacity_reduced_by( const units::volume &mod, const std::map &without_items ) const { - if( has_trait( trait_id( "DEBUG_STORAGE" ) ) ) { + if( mutations.has_trait( trait_id( "DEBUG_STORAGE" ) ) ) { return units::volume_max; } @@ -1911,16 +1912,16 @@ units::volume Character::volume_capacity_reduced_by( if( has_bionic( bionic_id( "bio_storage" ) ) ) { ret += 2000_ml; } - if( has_trait( trait_SHELL ) ) { + if( mutations.has_trait( trait_SHELL ) ) { ret += 4000_ml; } - if( has_trait( trait_SHELL2 ) && !has_active_mutation( trait_SHELL2 ) ) { + if( mutations.has_trait( trait_SHELL2 ) && !mutations.has_active_mutation( trait_SHELL2 ) ) { ret += 6000_ml; } - if( has_trait( trait_PACKMULE ) ) { + if( mutations.has_trait( trait_PACKMULE ) ) { ret = ret * 1.4; } - if( has_trait( trait_DISORGANIZED ) ) { + if( mutations.has_trait( trait_DISORGANIZED ) ) { ret = ret * 0.6; } return std::max( ret, 0_ml ); @@ -1937,7 +1938,7 @@ bool Character::can_pickWeight( const item &it, bool safe ) const { if( !safe ) { // Character can carry up to four times their maximum weight - return ( weight_carried() + it.weight() <= ( has_trait( trait_id( "DEBUG_STORAGE" ) ) ? + return ( weight_carried() + it.weight() <= ( mutations.has_trait( trait_id( "DEBUG_STORAGE" ) ) ? units::mass_max : weight_capacity() * 4 ) ); } else { return ( weight_carried() + it.weight() <= weight_capacity() ); @@ -2172,7 +2173,7 @@ void Character::make_bleed( body_part bp, time_duration duration, int intensity, bool permanent, bool force, bool defferred ) { int b_resist = 0; - for( const trait_id &mut : get_mutations() ) { + for( const trait_id &mut : mutations.get_mutations() ) { b_resist += mut.obj().bleed_resist; } @@ -2256,8 +2257,8 @@ void Character::reset_stats() mod_int_bonus( get_mod_stat_from_bionic( INTELLIGENCE ) ); // Trait / mutation buffs - mod_str_bonus( std::floor( mutation_value( "str_modifier" ) ) ); - mod_dodge_bonus( std::floor( mutation_value( "dodge_modifier" ) ) ); + mod_str_bonus( std::floor( mutations.mutation_value( "str_modifier" ) ) ); + mod_dodge_bonus( std::floor( mutations.mutation_value( "dodge_modifier" ) ) ); /** @EFFECT_STR_MAX above 15 decreases Dodge bonus by 1 (NEGATIVE) */ if( str_max >= 16 ) { @@ -2596,8 +2597,8 @@ void Character::mut_cbm_encumb( std::array &vals ) con // Lower penalty for bps covered only by XL armor const auto oversize = exclusive_flag_coverage( "OVERSIZE" ); - for( const auto &mut_pair : my_mutations ) { - apply_mut_encumbrance( vals, *mut_pair.first, oversize ); + for( const trait_id &mut : mutations.get_mutations() ) { + apply_mut_encumbrance( vals, mut.obj(), oversize ); } } @@ -2777,7 +2778,7 @@ void Character::set_healthy( int nhealthy ) void Character::mod_healthy( int nhealthy ) { float mut_rate = 1.0f; - for( const trait_id &mut : get_mutations() ) { + for( const trait_id &mut : mutations.get_mutations() ) { mut_rate *= mut.obj().healthy_rate; } healthy += nhealthy * mut_rate; @@ -3428,13 +3429,13 @@ nc_color Character::symbol_color() const bool Character::is_immune_field( const field_type_id fid ) const { // Obviously this makes us invincible - if( has_trait( debug_nodmg ) ) { + if( mutations.has_trait( debug_nodmg ) ) { return true; } // Check to see if we are immune const field_type &ft = fid.obj(); for( const trait_id &t : ft.immunity_data_traits ) { - if( has_trait( t ) ) { + if( mutations.has_trait( t ) ) { return true; } } @@ -3920,7 +3921,7 @@ float Character::healing_rate( float at_rest_quality ) const } else { heal_rate = get_option< float >( "NPC_HEALING_RATE" ); } - float awake_rate = heal_rate * mutation_value( "healing_awake" ); + float awake_rate = heal_rate * mutations.mutation_value( "healing_awake" ); float final_rate = 0.0f; if( awake_rate > 0.0f ) { final_rate += awake_rate; @@ -3930,7 +3931,8 @@ float Character::healing_rate( float at_rest_quality ) const } float asleep_rate = 0.0f; if( at_rest_quality > 0.0f ) { - asleep_rate = at_rest_quality * heal_rate * ( 1.0f + mutation_value( "healing_resting" ) ); + asleep_rate = at_rest_quality * heal_rate * ( 1.0f + + mutations.mutation_value( "healing_resting" ) ); } if( asleep_rate > 0.0f ) { final_rate += asleep_rate * ( 1.0f + get_healthy() / 200.0f ); @@ -3944,7 +3946,7 @@ float Character::healing_rate( float at_rest_quality ) const return 0.0f; } - float primary_hp_mod = mutation_value( "hp_modifier" ); + float primary_hp_mod = mutations.mutation_value( "hp_modifier" ); if( primary_hp_mod < 0.0f ) { // HP mod can't get below -1.0 final_rate *= 1.0f + primary_hp_mod; @@ -3985,7 +3987,7 @@ float Character::healing_rate_medicine( float at_rest_quality, const body_part b } rate_medicine += bandaged_rate + disinfected_rate; - rate_medicine *= 1.0f + mutation_value( "healing_resting" ); + rate_medicine *= 1.0f + mutations.mutation_value( "healing_resting" ); rate_medicine *= 1.0f + at_rest_quality; // increase healing if character has both effects @@ -3998,7 +4000,7 @@ float Character::healing_rate_medicine( float at_rest_quality, const body_part b } else { rate_medicine *= 1.0f + get_healthy() / 400.0f; } - float primary_hp_mod = mutation_value( "hp_modifier" ); + float primary_hp_mod = mutations.mutation_value( "hp_modifier" ); if( primary_hp_mod < 0.0f ) { // HP mod can't get below -1.0 rate_medicine *= 1.0f + primary_hp_mod; @@ -4224,17 +4226,17 @@ int Character::get_shout_volume() const int shout_multiplier = 2; // Mutations make shouting louder, they also define the default message - if( has_trait( trait_SHOUT3 ) ) { + if( mutations.has_trait( trait_SHOUT3 ) ) { shout_multiplier = 4; base = 20; - } else if( has_trait( trait_SHOUT2 ) ) { + } else if( mutations.has_trait( trait_SHOUT2 ) ) { base = 15; shout_multiplier = 3; } // You can't shout without your face - if( has_trait( trait_PROF_FOODP ) && !( is_wearing( itype_id( "foodperson_mask" ) ) || - is_wearing( itype_id( "foodperson_mask_on" ) ) ) ) { + if( mutations.has_trait( trait_PROF_FOODP ) && !( is_wearing( itype_id( "foodperson_mask" ) ) || + is_wearing( itype_id( "foodperson_mask_on" ) ) ) ) { base = 0; shout_multiplier = 0; } @@ -4267,20 +4269,20 @@ void Character::shout( std::string msg, bool order ) std::string shout; // You can't shout without your face - if( has_trait( trait_PROF_FOODP ) && !( is_wearing( itype_id( "foodperson_mask" ) ) || - is_wearing( itype_id( "foodperson_mask_on" ) ) ) ) { + if( mutations.has_trait( trait_PROF_FOODP ) && !( is_wearing( itype_id( "foodperson_mask" ) ) || + is_wearing( itype_id( "foodperson_mask_on" ) ) ) ) { add_msg_if_player( m_warning, _( "You try to shout but you have no face!" ) ); return; } // Mutations make shouting louder, they also define the default message - if( has_trait( trait_SHOUT3 ) ) { + if( mutations.has_trait( trait_SHOUT3 ) ) { base = 20; if( msg.empty() ) { msg = is_player() ? _( "yourself let out a piercing howl!" ) : _( "a piercing howl!" ); shout = "howl"; } - } else if( has_trait( trait_SHOUT2 ) ) { + } else if( mutations.has_trait( trait_SHOUT2 ) ) { base = 15; if( msg.empty() ) { msg = is_player() ? _( "yourself scream loudly!" ) : _( "a loud scream!" ); @@ -4306,7 +4308,7 @@ void Character::shout( std::string msg, bool order ) // Screaming underwater is not good for oxygen and harder to do overall if( underwater ) { - if( !has_trait( trait_GILLS ) && !has_trait( trait_GILLS_CEPH ) ) { + if( !mutations.has_trait( trait_GILLS ) && !mutations.has_trait( trait_GILLS_CEPH ) ) { mod_stat( "oxygen", -noise ); } } @@ -4725,10 +4727,10 @@ void Character::absorb_hit( body_part bp, damage_instance &dam ) passive_absorb_hit( bp, elem ); if( elem.type == DT_BASH ) { - if( has_trait( trait_LIGHT_BONES ) ) { + if( mutations.has_trait( trait_LIGHT_BONES ) ) { elem.amount *= 1.4; } - if( has_trait( trait_HOLLOW_BONES ) ) { + if( mutations.has_trait( trait_HOLLOW_BONES ) ) { elem.amount *= 1.8; } } @@ -4914,7 +4916,7 @@ void Character::healall( int dam ) void Character::hurtall( int dam, Creature *source, bool disturb /*= true*/ ) { - if( is_dead_state() || has_trait( trait_DEBUG_NODMG ) || dam <= 0 ) { + if( is_dead_state() || mutations.has_trait( trait_DEBUG_NODMG ) || dam <= 0 ) { return; } @@ -4946,7 +4948,7 @@ int Character::hitall( int dam, int vary, Creature *source ) void Character::on_hurt( Creature *source, bool disturb /*= true*/ ) { - if( has_trait( trait_ADRENALINE ) && !has_effect( effect_adrenaline ) && + if( mutations.has_trait( trait_ADRENALINE ) && !has_effect( effect_adrenaline ) && ( hp_cur[hp_head] < 25 || hp_cur[hp_torso] < 15 ) ) { add_effect( effect_adrenaline, 20_minutes ); } diff --git a/src/mutation.cpp b/src/mutation.cpp index 966e83e1ece58..b9f9f88a2c34f 100644 --- a/src/mutation.cpp +++ b/src/mutation.cpp @@ -162,11 +162,11 @@ int character_mutations::get_mod( const trait_id &mut, const std::string &arg ) void Character::apply_mods( const trait_id &mut, bool add_remove ) { int sign = add_remove ? 1 : -1; - int str_change = get_mod( mut, "STR" ); + int str_change = mutations.get_mod( mut, "STR" ); str_max += sign * str_change; - per_max += sign * get_mod( mut, "PER" ); - dex_max += sign * get_mod( mut, "DEX" ); - int_max += sign * get_mod( mut, "INT" ); + per_max += sign * mutations.get_mod( mut, "PER" ); + dex_max += sign * mutations.get_mod( mut, "DEX" ); + int_max += sign * mutations.get_mod( mut, "INT" ); if( str_change != 0 ) { recalc_hp(); @@ -362,8 +362,8 @@ bool character_mutations::is_category_allowed( const std::string &category ) con bool Character::is_weak_to_water() const { - for( const auto &mut : my_mutations ) { - if( mut.first.obj().weakness_to_water > 0 ) { + for( const trait_id &mut : mutations.get_mutations() ) { + if( mut.obj().weakness_to_water > 0 ) { return true; } } @@ -377,11 +377,11 @@ bool Character::can_use_heal_item( const item &med ) const bool can_use = false; bool got_restriction = false; - for( const trait_id &mut : get_mutations() ) { - if( !mut.obj().can_only_heal_with.empty() ) { + for( const trait_id &mut : mutations.get_mutations() ) { + if( !mut->can_only_heal_with.empty() ) { got_restriction = true; } - if( mut.obj().can_only_heal_with.count( heal_id ) ) { + if( mut->can_only_heal_with.count( heal_id ) ) { can_use = true; break; } @@ -391,8 +391,8 @@ bool Character::can_use_heal_item( const item &med ) const } if( !can_use ) { - for( const trait_id &mut : get_mutations() ) { - if( mut.obj().can_heal_with.count( heal_id ) ) { + for( const trait_id &mut : mutations.get_mutations() ) { + if( mut->can_heal_with.count( heal_id ) ) { can_use = true; break; } @@ -405,9 +405,9 @@ bool Character::can_use_heal_item( const item &med ) const bool Character::can_install_cbm_on_bp( const std::vector &bps ) const { bool can_install = true; - for( const trait_id &mut : get_mutations() ) { + for( const trait_id &mut : mutations.get_mutations() ) { for( const body_part bp : bps ) { - if( mut.obj().no_cbm_on_bp.count( bp ) ) { + if( mut->no_cbm_on_bp.count( bp ) ) { can_install = false; break; } @@ -529,7 +529,7 @@ void player::activate_mutation( const trait_id &mut ) return; } - if( has_trait( trait_ROOTS2 ) || has_trait( trait_ROOTS3 ) ) { + if( mutations.has_trait( trait_ROOTS2 ) || mutationshas_trait( trait_ROOTS3 ) ) { add_msg_if_player( _( "You reach out to the trees with your roots." ) ); } else { add_msg_if_player( diff --git a/src/player.cpp b/src/player.cpp index 00dddc3676a37..e317afa081214 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -438,13 +438,13 @@ stat_mod player::get_pain_penalty() const int stat_penalty = std::floor( std::pow( pain, 0.8f ) / 10.0f ); - bool ceno = has_trait( trait_CENOBITE ); + bool ceno = mutations.has_trait( trait_CENOBITE ); if( !ceno ) { ret.strength = stat_penalty; ret.dexterity = stat_penalty; } - if( !has_trait( trait_INT_SLIME ) ) { + if( !mutations.has_trait( trait_INT_SLIME ) ) { ret.intelligence = 1 + stat_penalty; } else { ret.intelligence = 1 + pain / 5; @@ -509,7 +509,7 @@ player::player() : next_expected_position = cata::nullopt; death_drops = true; - empty_traits(); + mutations.empty_traits(); temp_cur.fill( BODYTEMP_NORM ); frostbite_timer.fill( 0 ); @@ -584,7 +584,7 @@ void player::process_turn() mod_stored_kcal( -1 ); mod_power_level( 1_kJ ); } - if( has_trait( trait_DEBUG_BIONIC_POWER ) ) { + if( mutations.has_trait( trait_DEBUG_BIONIC_POWER ) ) { mod_power_level( get_max_power_level() ); } @@ -598,25 +598,26 @@ void player::process_turn() // Set our scent towards the norm int norm_scent = 500; - if( has_trait( trait_WEAKSCENT ) ) { + if( mutations.has_trait( trait_WEAKSCENT ) ) { norm_scent = 300; } - if( has_trait( trait_SMELLY ) ) { + if( mutations.has_trait( trait_SMELLY ) ) { norm_scent = 800; } - if( has_trait( trait_SMELLY2 ) ) { + if( mutations.has_trait( trait_SMELLY2 ) ) { norm_scent = 1200; } // Not so much that you don't have a scent // but that you smell like a plant, rather than // a human. When was the last time you saw a critter // attack a bluebell or an apple tree? - if( ( has_trait( trait_FLOWERS ) ) && ( !( has_trait( trait_CHLOROMORPH ) ) ) ) { + if( ( mutations.has_trait( trait_FLOWERS ) ) && + ( !( mutations.has_trait( trait_CHLOROMORPH ) ) ) ) { norm_scent -= 200; } // You *are* a plant. Unless someone hunts triffids by scent, // you don't smell like prey. - if( has_trait( trait_CHLOROMORPH ) ) { + if( mutations.has_trait( trait_CHLOROMORPH ) ) { norm_scent = 0; } @@ -632,7 +633,7 @@ void player::process_turn() scent--; } - for( const trait_id &mut : get_mutations() ) { + for( const trait_id &mut : mutations.get_mutations() ) { scent *= mut.obj().scent_modifier; } @@ -659,7 +660,8 @@ void player::process_turn() } // Update time spent conscious in this overmap tile for the Nomad traits. - if( ( has_trait( trait_NOMAD ) || has_trait( trait_NOMAD2 ) || has_trait( trait_NOMAD3 ) ) && + if( ( mutations.has_trait( trait_NOMAD ) || mutations.has_trait( trait_NOMAD2 ) || + mutations.has_trait( trait_NOMAD3 ) ) && !has_effect( effect_sleep ) && !has_effect( effect_narcosis ) ) { const tripoint ompos = global_omt_location(); const point pos = ompos.xy(); @@ -674,11 +676,11 @@ void player::process_turn() const tripoint ompos = global_omt_location(); const time_point now = calendar::turn; time_duration decay_time = 0_days; - if( has_trait( trait_NOMAD ) ) { + if( mutations.has_trait( trait_NOMAD ) ) { decay_time = 7_days; - } else if( has_trait( trait_NOMAD2 ) ) { + } else if( mutations.has_trait( trait_NOMAD2 ) ) { decay_time = 14_days; - } else if( has_trait( trait_NOMAD3 ) ) { + } else if( mutations.has_trait( trait_NOMAD3 ) ) { decay_time = 28_days; } auto it = overmap_time.begin(); @@ -724,7 +726,7 @@ void player::update_morale() void player::apply_persistent_morale() { // Hoarders get a morale penalty if they're not carrying a full inventory. - if( has_trait( trait_HOARDER ) ) { + if( mutations.has_trait( trait_HOARDER ) ) { int pen = ( volume_capacity() - volume_carried() ) / 125_ml; if( pen > 70 ) { pen = 70; @@ -742,7 +744,8 @@ void player::apply_persistent_morale() } } // Nomads get a morale penalty if they stay near the same overmap tiles too long. - if( has_trait( trait_NOMAD ) || has_trait( trait_NOMAD2 ) || has_trait( trait_NOMAD3 ) ) { + if( mutations.has_trait( trait_NOMAD ) || mutations.has_trait( trait_NOMAD2 ) || + mutations.has_trait( trait_NOMAD3 ) ) { const tripoint ompos = global_omt_location(); float total_time = 0; // Check how long we've stayed in any overmap tile within 5 of us. @@ -762,11 +765,11 @@ void player::apply_persistent_morale() // Characters with higher tiers of Nomad suffer worse morale penalties, faster. int max_unhappiness; float min_time, max_time; - if( has_trait( trait_NOMAD ) ) { + if( mutations.has_trait( trait_NOMAD ) ) { max_unhappiness = 20; min_time = to_moves( 12_hours ); max_time = to_moves( 1_days ); - } else if( has_trait( trait_NOMAD2 ) ) { + } else if( mutations.has_trait( trait_NOMAD2 ) ) { max_unhappiness = 40; min_time = to_moves( 4_hours ); max_time = to_moves( 8_hours ); @@ -783,7 +786,7 @@ void player::apply_persistent_morale() } } - if( has_trait( trait_PROF_FOODP ) ) { + if( mutations.has_trait( trait_PROF_FOODP ) ) { // Loosing your face is distressing if( !( is_wearing( itype_id( "foodperson_mask" ) ) || is_wearing( itype_id( "foodperson_mask_on" ) ) ) ) { @@ -838,7 +841,7 @@ HURRICANE : 185 mph (880 hPa) [Ref: Hurricane Wilma] void player::update_bodytemp() { - if( has_trait( trait_DEBUG_NOTEMP ) ) { + if( mutations.has_trait( trait_DEBUG_NOTEMP ) ) { temp_cur.fill( BODYTEMP_NORM ); temp_conv.fill( BODYTEMP_NORM ); return; @@ -860,11 +863,11 @@ void player::update_bodytemp() pos(), g->weather.winddirection, sheltered ); // Let's cache this not to check it num_bp times - const bool has_bark = has_trait( trait_BARK ); + const bool has_bark = mutations.has_trait( trait_BARK ); const bool has_sleep = has_effect( effect_sleep ); const bool has_sleep_state = has_sleep || in_sleep_state(); const bool has_heatsink = has_bionic( bio_heatsink ) || is_wearing( "rm13_armor_on" ) || - has_trait( trait_M_SKIN2 ) || has_trait( trait_M_SKIN3 ); + mutations.has_trait( trait_M_SKIN2 ) || mutations.has_trait( trait_M_SKIN3 ); const bool has_common_cold = has_effect( effect_common_cold ); const bool has_climate_control = in_climate_control(); const bool use_floor_warmth = can_use_floor_warmth(); @@ -902,8 +905,8 @@ void player::update_bodytemp() // Correction of body temperature due to traits and mutations // Lower heat is applied always - const int mutation_heat_low = bodytemp_modifier_traits( false ); - const int mutation_heat_high = bodytemp_modifier_traits( true ); + const int mutation_heat_low = mutations.bodytemp_modifier_traits( false ); + const int mutation_heat_high = mutations.bodytemp_modifier_traits( true ); // Difference between high and low is the "safe" heat - one we only apply if it's beneficial const int mutation_heat_bonus = mutation_heat_high - mutation_heat_low; @@ -969,7 +972,7 @@ void player::update_bodytemp() blister_count += h_radiation - 111 > 0 ? std::max( static_cast( sqrt( h_radiation - 111 ) ), 0 ) : 0; - const bool pyromania = has_trait( trait_PYROMANIA ); + const bool pyromania = mutations.has_trait( trait_PYROMANIA ); // BLISTERS : Skin gets blisters from intense heat exposure. // Fire protection protects from blisters. // Heatsinks give near-immunity. @@ -1382,10 +1385,10 @@ int player::floor_warmth( const tripoint &pos ) const int bedding_warmth = floor_bedding_warmth( pos ); // If the PC has fur, etc, that will apply too - int floor_mut_warmth = bodytemp_modifier_traits_floor(); + int floor_mut_warmth = mutations.bodytemp_modifier_traits_floor(); // DOWN does not provide floor insulation, though. // Better-than-light fur or being in one's shell does. - if( ( !( has_trait( trait_DOWN ) ) ) && ( floor_mut_warmth >= 200 ) ) { + if( ( !( mutations.has_trait( trait_DOWN ) ) ) && ( floor_mut_warmth >= 200 ) ) { bedding_warmth = std::max( 0, bedding_warmth ); } return ( item_warmth + bedding_warmth + floor_mut_warmth ); @@ -1499,7 +1502,7 @@ void player::recalc_speed_bonus() { // Minus some for weight... int carry_penalty = 0; - if( weight_carried() > weight_capacity() && !has_trait( trait_id( "DEBUG_STORAGE" ) ) ) { + if( weight_carried() > weight_capacity() && !mutations.has_trait( trait_id( "DEBUG_STORAGE" ) ) ) { carry_penalty = 25 * ( weight_carried() - weight_capacity() ) / ( weight_capacity() ); } mod_speed_bonus( -carry_penalty ); @@ -1526,13 +1529,13 @@ void player::recalc_speed_bonus() // Ectothermic/COLDBLOOD4 is intended to buff folks in the Summer // Threshold-crossing has its charms ;-) if( g != nullptr ) { - if( has_trait( trait_SUNLIGHT_DEPENDENT ) && !g->is_in_sunlight( pos() ) ) { + if( mutations.has_trait( trait_SUNLIGHT_DEPENDENT ) && !g->is_in_sunlight( pos() ) ) { mod_speed_bonus( -( g->light_level( posz() ) >= 12 ? 5 : 10 ) ); } - const float temperature_speed_modifier = mutation_value( "temperature_speed_modifier" ); + const float temperature_speed_modifier = mutations.mutation_value( "temperature_speed_modifier" ); if( temperature_speed_modifier != 0 ) { const auto player_local_temp = g->weather.get_temperature( pos() ); - if( has_trait( trait_COLDBLOOD4 ) || player_local_temp < 65 ) { + if( mutations.has_trait( trait_COLDBLOOD4 ) || player_local_temp < 65 ) { mod_speed_bonus( ( player_local_temp - 65 ) * temperature_speed_modifier ); } } @@ -1545,7 +1548,7 @@ void player::recalc_speed_bonus() mod_speed_bonus( -20 ); } - float speed_modifier = Character::mutation_value( "speed_modifier" ); + float speed_modifier = Character::mutations.mutation_value( "speed_modifier" ); set_speed_bonus( static_cast( get_speed() * speed_modifier ) - get_speed_base() ); if( has_bionic( bio_speed ) ) { // multiply by 1.1 @@ -1572,12 +1575,12 @@ int player::run_cost( int base_cost, bool diag ) const if( !is_mounted() ) { if( movecost > 100 ) { - movecost *= Character::mutation_value( "movecost_obstacle_modifier" ); + movecost *= Character::mutations.mutation_value( "movecost_obstacle_modifier" ); if( movecost < 100 ) { movecost = 100; } } - if( has_trait( trait_M_IMMUNE ) && on_fungus ) { + if( mutations.has_trait( trait_M_IMMUNE ) && on_fungus ) { if( movecost > 75 ) { movecost = 75; // Mycal characters are faster on their home territory, even through things like shrubs @@ -1590,11 +1593,11 @@ int player::run_cost( int base_cost, bool diag ) const if( is_limb_hindered( hp_leg_r ) ) { movecost += 25; } - movecost *= Character::mutation_value( "movecost_modifier" ); + movecost *= Character::mutations.mutation_value( "movecost_modifier" ); if( flatground ) { - movecost *= Character::mutation_value( "movecost_flatground_modifier" ); + movecost *= Character::mutations.mutation_value( "movecost_flatground_modifier" ); } - if( has_trait( trait_PADDED_FEET ) && !footwear_factor() ) { + if( mutations.has_trait( trait_PADDED_FEET ) && !footwear_factor() ) { movecost *= .9f; } if( has_active_bionic( bio_jointservo ) ) { @@ -1646,8 +1649,10 @@ int player::run_cost( int base_cost, bool diag ) const // ROOTS3 does slow you down as your roots are probing around for nutrients, // whether you want them to or not. ROOTS1 is just too squiggly without shoes // to give you some stability. Plants are a bit of a slow-mover. Deal. - const bool mutfeet = has_trait( trait_LEG_TENTACLES ) || has_trait( trait_PADDED_FEET ) || - has_trait( trait_HOOVES ) || has_trait( trait_TOUGH_FEET ) || has_trait( trait_ROOTS2 ); + const bool mutfeet = mutations.has_trait( trait_LEG_TENTACLES ) || + mutations.has_trait( trait_PADDED_FEET ) || + mutations.has_trait( trait_HOOVES ) || mutations.has_trait( trait_TOUGH_FEET ) || + mutations.has_trait( trait_ROOTS2 ); if( !is_wearing_shoes( side::LEFT ) && !mutfeet ) { movecost += 8; } @@ -1655,7 +1660,7 @@ int player::run_cost( int base_cost, bool diag ) const movecost += 8; } - if( has_trait( trait_ROOTS3 ) && g->m.has_flag( "DIGGABLE", pos() ) ) { + if( mutations.has_trait( trait_ROOTS3 ) && g->m.has_flag( "DIGGABLE", pos() ) ) { movecost += 10 * footwear_factor(); } // Both walk and run speed drop to half their maximums as stamina approaches 0. @@ -1698,18 +1703,18 @@ int player::swim_speed() const float hand_bonus_mult = ( usable.test( bp_hand_l ) ? 0.5f : 0.0f ) + ( usable.test( bp_hand_r ) ? 0.5f : 0.0f ); - if( !has_trait( trait_AMPHIBIAN ) ) { + if( !mutations.has_trait( trait_AMPHIBIAN ) ) { ret = 440 + weight_carried() / 60_gram - 50 * get_skill_level( skill_swimming ); /** AMPHIBIAN increases base swim speed */ } else { ret = 200 + weight_carried() / 120_gram - 50 * get_skill_level( skill_swimming ); } /** @EFFECT_STR increases swim speed bonus from PAWS */ - if( has_trait( trait_PAWS ) ) { + if( mutations.has_trait( trait_PAWS ) ) { ret -= hand_bonus_mult * ( 20 + str_cur * 3 ); } /** @EFFECT_STR increases swim speed bonus from PAWS_LARGE */ - if( has_trait( trait_PAWS_LARGE ) ) { + if( mutations.has_trait( trait_PAWS_LARGE ) ) { ret -= hand_bonus_mult * ( 20 + str_cur * 4 ); } /** @EFFECT_STR increases swim speed bonus from swim_fins */ @@ -1721,20 +1726,20 @@ int player::swim_speed() const } } /** @EFFECT_STR increases swim speed bonus from WEBBED */ - if( has_trait( trait_WEBBED ) ) { + if( mutations.has_trait( trait_WEBBED ) ) { ret -= hand_bonus_mult * ( 60 + str_cur * 5 ); } /** @EFFECT_STR increases swim speed bonus from TAIL_FIN */ - if( has_trait( trait_TAIL_FIN ) ) { + if( mutations.has_trait( trait_TAIL_FIN ) ) { ret -= 100 + str_cur * 10; } - if( has_trait( trait_SLEEK_SCALES ) ) { + if( mutations.has_trait( trait_SLEEK_SCALES ) ) { ret -= 100; } - if( has_trait( trait_LEG_TENTACLES ) ) { + if( mutations.has_trait( trait_LEG_TENTACLES ) ) { ret -= 60; } - if( has_trait( trait_FAT ) ) { + if( mutations.has_trait( trait_FAT ) ) { ret -= 30; } /** @EFFECT_SWIMMING increases swim speed */ @@ -1792,16 +1797,18 @@ bool player::is_elec_immune() const bool player::is_immune_effect( const efftype_id &eff ) const { if( eff == effect_downed ) { - return is_throw_immune() || ( has_trait( trait_LEG_TENT_BRACE ) && footwear_factor() == 0 ); + return is_throw_immune() || ( mutations.has_trait( trait_LEG_TENT_BRACE ) && + footwear_factor() == 0 ); } else if( eff == effect_onfire ) { return is_immune_damage( DT_HEAT ); } else if( eff == effect_deaf ) { return worn_with_flag( "DEAF" ) || worn_with_flag( "PARTIAL_DEAF" ) || has_bionic( bio_ears ) || is_wearing( "rm13_armor_on" ); } else if( eff == effect_corroding ) { - return is_immune_damage( DT_ACID ) || has_trait( trait_SLIMY ) || has_trait( trait_VISCOUS ); + return is_immune_damage( DT_ACID ) || mutations.has_trait( trait_SLIMY ) || + mutations.has_trait( trait_VISCOUS ); } else if( eff == effect_nausea ) { - return has_trait( trait_STRONGSTOMACH ); + return mutations.has_trait( trait_STRONGSTOMACH ); } return false; @@ -1836,15 +1843,15 @@ bool player::is_immune_damage( const damage_type dt ) const return has_effect_with_flag( "EFFECT_CUT_IMMUNE" ) || worn_with_flag( "CUT_IMMUNE" ); case DT_ACID: - return has_trait( trait_ACIDPROOF ) || + return mutations.has_trait( trait_ACIDPROOF ) || has_effect_with_flag( "EFFECT_ACID_IMMUNE" ) || worn_with_flag( "ACID_IMMUNE" ); case DT_STAB: return has_effect_with_flag( "EFFECT_STAB_IMMUNE" ) || worn_with_flag( "STAB_IMMUNE" ); case DT_HEAT: - return has_trait( trait_M_SKIN2 ) || - has_trait( trait_M_SKIN3 ) || + return mutations.has_trait( trait_M_SKIN2 ) || + mutations.has_trait( trait_M_SKIN3 ) || has_effect_with_flag( "EFFECT_HEAT_IMMUNE" ) || worn_with_flag( "HEAT_IMMUNE" ); case DT_COLD: @@ -1906,14 +1913,14 @@ nc_color player::basic_symbol_color() const if( has_effect( effect_boomered ) ) { return c_pink; } - if( has_active_mutation( trait_id( "SHELL2" ) ) ) { + if( mutations.has_active_mutation( trait_id( "SHELL2" ) ) ) { return c_magenta; } if( underwater ) { return c_blue; } if( has_active_bionic( bio_cloak ) || has_artifact_with( AEP_INVISIBLE ) || - is_wearing_active_optcloak() || has_trait( trait_DEBUG_CLOAK ) ) { + is_wearing_active_optcloak() || mutations.has_trait( trait_DEBUG_CLOAK ) ) { return c_dark_gray; } if( move_mode == CMM_RUN ) { @@ -2048,7 +2055,7 @@ bool player::in_climate_control() if( has_active_bionic( bio_climate ) ) { return true; } - if( has_trait( trait_M_SKIN3 ) && g->m.has_flag_ter_or_furn( "FUNGUS", pos() ) && + if( mutations.has_trait( trait_M_SKIN3 ) && g->m.has_flag_ter_or_furn( "FUNGUS", pos() ) && in_sleep_state() ) { return true; } @@ -2247,9 +2254,9 @@ int player::overmap_sight_range( int light_level ) const // The higher up you are, the farther you can see. sight += std::max( 0, posz() ) * 2; // Mutations like Scout and Topographagnosia affect how far you can see. - sight += Character::mutation_value( "overmap_sight" ); + sight += Character::mutations.mutation_value( "overmap_sight" ); - float multiplier = Character::mutation_value( "overmap_multiplier" ); + float multiplier = Character::mutations.mutation_value( "overmap_multiplier" ); // Binoculars double your sight range. const bool has_optic = ( has_item_with_flag( "ZOOM" ) || has_bionic( bio_eye_optic ) || ( is_mounted() && @@ -2284,15 +2291,15 @@ bool player::sight_impaired() const { return ( ( ( has_effect( effect_boomered ) || has_effect( effect_no_sight ) || has_effect( effect_darkness ) ) && - ( !( has_trait( trait_PER_SLIME_OK ) ) ) ) || - ( underwater && !has_bionic( bio_membrane ) && !has_trait( trait_MEMBRANE ) && - !worn_with_flag( "SWIM_GOGGLES" ) && !has_trait( trait_PER_SLIME_OK ) && - !has_trait( trait_CEPH_EYES ) && !has_trait( trait_SEESLEEP ) ) || - ( ( has_trait( trait_MYOPIC ) || has_trait( trait_URSINE_EYE ) ) && + ( !( mutations.has_trait( trait_PER_SLIME_OK ) ) ) ) || + ( underwater && !has_bionic( bio_membrane ) && !mutations.has_trait( trait_MEMBRANE ) && + !worn_with_flag( "SWIM_GOGGLES" ) && !mutations.has_trait( trait_PER_SLIME_OK ) && + !mutations.has_trait( trait_CEPH_EYES ) && !mutations.has_trait( trait_SEESLEEP ) ) || + ( ( mutations.has_trait( trait_MYOPIC ) || mutations.has_trait( trait_URSINE_EYE ) ) && !worn_with_flag( "FIX_NEARSIGHT" ) && !has_effect( effect_contacts ) && !has_bionic( bio_eye_optic ) ) || - has_trait( trait_PER_SLIME ) ); + mutations.has_trait( trait_PER_SLIME ) ); } bool player::avoid_trap( const tripoint &pos, const trap &tr ) const @@ -2308,11 +2315,11 @@ bool player::avoid_trap( const tripoint &pos, const trap &tr ) const traproll = dice( 6, tr.get_avoidance() ); } - if( has_trait( trait_LIGHTSTEP ) ) { + if( mutations.has_trait( trait_LIGHTSTEP ) ) { myroll += dice( 2, 6 ); } - if( has_trait( trait_CLUMSY ) ) { + if( mutations.has_trait( trait_CLUMSY ) ) { myroll -= dice( 2, 6 ); } @@ -2479,15 +2486,15 @@ int player::read_speed( bool return_stat_effect ) const ret *= .85; } - if( has_trait( trait_FASTREADER ) ) { + if( mutations.has_trait( trait_FASTREADER ) ) { ret *= .8; } - if( has_trait( trait_PROF_DICEMASTER ) ) { + if( mutations.has_trait( trait_PROF_DICEMASTER ) ) { ret *= .9; } - if( has_trait( trait_SLOWREADER ) ) { + if( mutations.has_trait( trait_SLOWREADER ) ) { ret *= 1.3; } @@ -2510,7 +2517,7 @@ int player::rust_rate( bool return_stat_effect ) const int ret = ( ( get_option( "SKILL_RUST" ) == "vanilla" || get_option( "SKILL_RUST" ) == "capped" ) ? 500 : 500 - 35 * ( intel - 8 ) ); - ret *= mutation_value( "skill_rust_multiplier" ); + ret *= mutations.mutation_value( "skill_rust_multiplier" ); if( ret < 0 ) { ret = 0; @@ -2619,24 +2626,24 @@ void player::on_hit( Creature *source, body_part bp_hit, source->deal_damage( this, bp_torso, ods_shock_damage ); } if( !wearing_something_on( bp_hit ) && - ( has_trait( trait_SPINES ) || has_trait( trait_QUILLS ) ) ) { - int spine = rng( 1, has_trait( trait_QUILLS ) ? 20 : 8 ); + ( mutations.has_trait( trait_SPINES ) || mutations.has_trait( trait_QUILLS ) ) ) { + int spine = rng( 1, mutations.has_trait( trait_QUILLS ) ? 20 : 8 ); if( !is_player() ) { if( u_see ) { add_msg( _( "%1$s's %2$s puncture %3$s in mid-attack!" ), name, - ( has_trait( trait_QUILLS ) ? _( "quills" ) : _( "spines" ) ), + ( mutations.has_trait( trait_QUILLS ) ? _( "quills" ) : _( "spines" ) ), source->disp_name() ); } } else { add_msg( m_good, _( "Your %1$s puncture %2$s in mid-attack!" ), - ( has_trait( trait_QUILLS ) ? _( "quills" ) : _( "spines" ) ), + ( mutations.has_trait( trait_QUILLS ) ? _( "quills" ) : _( "spines" ) ), source->disp_name() ); } damage_instance spine_damage; spine_damage.add_damage( DT_STAB, spine ); source->deal_damage( this, bp_torso, spine_damage ); } - if( ( !( wearing_something_on( bp_hit ) ) ) && ( has_trait( trait_THORNS ) ) && + if( ( !( wearing_something_on( bp_hit ) ) ) && ( mutations.has_trait( trait_THORNS ) ) && ( !( source->has_weapon() ) ) ) { if( !is_player() ) { if( u_see ) { @@ -2653,7 +2660,7 @@ void player::on_hit( Creature *source, body_part bp_hit, // so safer to target the torso source->deal_damage( this, bp_torso, thorn_damage ); } - if( ( !( wearing_something_on( bp_hit ) ) ) && ( has_trait( trait_CF_HAIR ) ) ) { + if( ( !( wearing_something_on( bp_hit ) ) ) && ( mutations.has_trait( trait_CF_HAIR ) ) ) { if( !is_player() ) { if( u_see ) { add_msg( _( "%1$s gets a load of %2$s's %3$s stuck in!" ), source->disp_name(), @@ -2672,13 +2679,13 @@ void player::on_hit( Creature *source, body_part bp_hit, if( worn_with_flag( "ROLLER_ONE" ) ) { rolls += 2; } - if( has_trait( trait_PROF_SKATER ) ) { + if( mutations.has_trait( trait_PROF_SKATER ) ) { rolls--; } - if( has_trait( trait_DEFT ) ) { + if( mutations.has_trait( trait_DEFT ) ) { rolls--; } - if( has_trait( trait_CLUMSY ) ) { + if( mutations.has_trait( trait_CLUMSY ) ) { rolls++; } @@ -2714,7 +2721,7 @@ int player::get_num_crafting_helpers( int max ) const bool player::immune_to( body_part bp, damage_unit dam ) const { - if( has_trait( trait_DEBUG_NODMG ) || is_immune_damage( dam.type ) ) { + if( mutations.has_trait( trait_DEBUG_NODMG ) || is_immune_damage( dam.type ) ) { return true; } @@ -2732,7 +2739,7 @@ bool player::immune_to( body_part bp, damage_unit dam ) const dealt_damage_instance player::deal_damage( Creature *source, body_part bp, const damage_instance &d ) { - if( has_trait( trait_DEBUG_NODMG ) ) { + if( mutations.has_trait( trait_DEBUG_NODMG ) ) { return dealt_damage_instance(); } @@ -2772,7 +2779,7 @@ dealt_damage_instance player::deal_damage( Creature *source, body_part bp, } // And slimespawners too - if( ( has_trait( trait_SLIMESPAWNER ) ) && ( dam >= 10 ) && one_in( 20 - dam ) ) { + if( ( mutations.has_trait( trait_SLIMESPAWNER ) ) && ( dam >= 10 ) && one_in( 20 - dam ) ) { if( monster *const slime = g->place_critter_around( mon_player_blob, pos(), 1 ) ) { slime->friendly = -1; add_msg_if_player( m_warning, _( "Slime is torn from you, and moves on its own!" ) ); @@ -2782,7 +2789,7 @@ dealt_damage_instance player::deal_damage( Creature *source, body_part bp, //Acid blood effects. bool u_see = g->u.sees( *this ); int cut_dam = dealt_dams.type_damage( DT_CUT ); - if( source && has_trait( trait_ACIDBLOOD ) && !one_in( 3 ) && + if( source && mutations.has_trait( trait_ACIDBLOOD ) && !one_in( 3 ) && ( dam >= 4 || cut_dam > 0 ) && ( rl_dist( g->u.pos(), source->pos() ) <= 1 ) ) { if( is_player() ) { add_msg( m_good, _( "Your acidic blood splashes %s in mid-attack!" ), @@ -2901,23 +2908,23 @@ dealt_damage_instance player::deal_damage( Creature *source, body_part bp, void player::mod_pain( int npain ) { if( npain > 0 ) { - if( has_trait( trait_NOPAIN ) || has_effect( effect_narcosis ) ) { + if( mutations.has_trait( trait_NOPAIN ) || has_effect( effect_narcosis ) ) { return; } // always increase pain gained by one from these bad mutations - if( has_trait( trait_MOREPAIN ) ) { + if( mutations.has_trait( trait_MOREPAIN ) ) { npain += std::max( 1, roll_remainder( npain * 0.25 ) ); - } else if( has_trait( trait_MOREPAIN2 ) ) { + } else if( mutations.has_trait( trait_MOREPAIN2 ) ) { npain += std::max( 1, roll_remainder( npain * 0.5 ) ); - } else if( has_trait( trait_MOREPAIN3 ) ) { + } else if( mutations.has_trait( trait_MOREPAIN3 ) ) { npain += std::max( 1, roll_remainder( npain * 1.0 ) ); } if( npain > 1 ) { // if it's 1 it'll just become 0, which is bad - if( has_trait( trait_PAINRESIST_TROGLO ) ) { + if( mutations.has_trait( trait_PAINRESIST_TROGLO ) ) { npain = roll_remainder( npain * 0.5 ); - } else if( has_trait( trait_PAINRESIST ) ) { + } else if( mutations.has_trait( trait_PAINRESIST ) ) { npain = roll_remainder( npain * 0.67 ); } } @@ -2984,9 +2991,9 @@ void player::react_to_felt_pain( int intensity ) if( has_effect( effect_sleep ) && !has_effect( effect_narcosis ) ) { int pain_thresh = rng( 3, 5 ); - if( has_trait( trait_HEAVYSLEEPER ) ) { + if( mutations.has_trait( trait_HEAVYSLEEPER ) ) { pain_thresh += 2; - } else if( has_trait( trait_HEAVYSLEEPER2 ) ) { + } else if( mutations.has_trait( trait_HEAVYSLEEPER2 ) ) { pain_thresh += 5; } @@ -3024,7 +3031,7 @@ int player::reduce_healing_effect( const efftype_id &eff_id, int remove_med, bod */ void player::apply_damage( Creature *source, body_part hurt, int dam, const bool bypass_med ) { - if( is_dead_state() || has_trait( trait_DEBUG_NODMG ) ) { + if( is_dead_state() || mutations.has_trait( trait_DEBUG_NODMG ) ) { // don't do any more damage if we're already dead // Or if we're debugging and don't want to die return; @@ -3091,7 +3098,7 @@ float player::fall_damage_mod() const // 100% damage at 0, 75% at 10, 50% at 20 and so on ret *= ( 100.0f - ( dex_dodge * 4.0f ) ) / 100.0f; - if( has_trait( trait_PARKOUR ) ) { + if( mutations.has_trait( trait_PARKOUR ) ) { ret *= 2.0f / 3.0f; } @@ -3349,7 +3356,7 @@ void player::update_body( const time_point &from, const time_point &to ) reset_activity_level(); } // Radiation kills health even at low doses - update_health( has_trait( trait_RADIOGENIC ) ? 0 : -radiation ); + update_health( mutations.has_trait( trait_RADIOGENIC ) ? 0 : -radiation ); get_sick(); } @@ -3375,12 +3382,12 @@ void player::update_stomach( const time_point &from, const time_point &to ) { const needs_rates rates = calc_needs_rates(); // No food/thirst/fatigue clock at all - const bool debug_ls = has_trait( trait_DEBUG_LS ); + const bool debug_ls = mutations.has_trait( trait_DEBUG_LS ); // No food/thirst, capped fatigue clock (only up to tired) const bool npc_no_food = is_npc() && get_option( "NO_NPC_FOOD" ); const bool foodless = debug_ls || npc_no_food; - const bool mouse = has_trait( trait_NO_THIRST ); - const bool mycus = has_trait( trait_M_DEPENDENT ); + const bool mouse = mutations.has_trait( trait_NO_THIRST ); + const bool mycus = mutations.has_trait( trait_M_DEPENDENT ); const float kcal_per_time = get_bmr() / ( 12.0f * 24.0f ); const int five_mins = ticks_between( from, to, 5_minutes ); @@ -3458,7 +3465,7 @@ void player::update_stomach( const time_point &from, const time_point &to ) void player::get_sick() { // NPCs are too dumb to handle infections now - if( is_npc() || has_trait( trait_DISIMMUNE ) ) { + if( is_npc() || mutations.has_trait( trait_DISIMMUNE ) ) { // In a shocking twist, disease immunity prevents diseases. return; } @@ -3471,7 +3478,7 @@ void player::get_sick() // Normal people get sick about 2-4 times/year. int base_diseases_per_year = 3; - if( has_trait( trait_DISRESISTANT ) ) { + if( mutations.has_trait( trait_DISRESISTANT ) ) { // Disease resistant people only get sick once a year. base_diseases_per_year = 1; } @@ -3508,7 +3515,7 @@ void player::check_needs_extremes() g->events().send( getID(), efftype_id() ); hp_cur[hp_torso] = 0; } else if( has_effect( effect_jetinjector ) && get_effect_dur( effect_jetinjector ) > 40_minutes ) { - if( !( has_trait( trait_NOPAIN ) ) ) { + if( !( mutations.has_trait( trait_NOPAIN ) ) ) { add_msg_if_player( m_bad, _( "Your heart spasms painfully and stops." ) ); } else { add_msg_if_player( _( "Your heart spasms and stops." ) ); @@ -3705,13 +3712,13 @@ needs_rates player::calc_needs_rates() add_msg_if_player( m_debug, "Metabolic rate: %.2f", rates.hunger ); rates.thirst = get_option< float >( "PLAYER_THIRST_RATE" ); - rates.thirst *= 1.0f + mutation_value( "thirst_modifier" ); + rates.thirst *= 1.0f + mutations.mutation_value( "thirst_modifier" ); if( worn_with_flag( "SLOWS_THIRST" ) ) { rates.thirst *= 0.7f; } rates.fatigue = get_option< float >( "PLAYER_FATIGUE_RATE" ); - rates.fatigue *= 1.0f + mutation_value( "fatigue_modifier" ); + rates.fatigue *= 1.0f + mutations.mutation_value( "fatigue_modifier" ); // Note: intentionally not in metabolic rate if( has_recycler ) { @@ -3721,7 +3728,7 @@ needs_rates player::calc_needs_rates() } if( asleep ) { - rates.recovery = 1.0f + mutation_value( "fatigue_regen_modifier" ); + rates.recovery = 1.0f + mutations.mutation_value( "fatigue_regen_modifier" ); if( !is_hibernating() ) { // Hunger and thirst advance more slowly while we sleep. This is the standard rate. rates.hunger *= 0.5f; @@ -3746,14 +3753,14 @@ needs_rates player::calc_needs_rates() if( has_activity( activity_id( "ACT_TREE_COMMUNION" ) ) ) { // Much of the body's needs are taken care of by the trees. // Hair Roots dont provide any bodily needs. - if( has_trait( trait_ROOTS2 ) || has_trait( trait_ROOTS3 ) ) { + if( mutations.has_trait( trait_ROOTS2 ) || mutations.has_trait( trait_ROOTS3 ) ) { rates.hunger *= 0.5f; rates.thirst *= 0.5f; rates.fatigue *= 0.5f; } } - if( has_trait( trait_TRANSPIRATION ) ) { + if( mutations.has_trait( trait_TRANSPIRATION ) ) { // Transpiration, the act of moving nutrients with evaporating water, can take a very heavy toll on your thirst when it's really hot. rates.thirst *= ( ( g->weather.get_temperature( pos() ) - 32.5f ) / 40.0f ); } @@ -3771,7 +3778,7 @@ void player::update_needs( int rate_multiplier ) // Hunger, thirst, & fatigue up every 5 minutes effect &sleep = get_effect( effect_sleep ); // No food/thirst/fatigue clock at all - const bool debug_ls = has_trait( trait_DEBUG_LS ); + const bool debug_ls = mutations.has_trait( trait_DEBUG_LS ); // No food/thirst, capped fatigue clock (only up to tired) const bool npc_no_food = is_npc() && get_option( "NO_NPC_FOOD" ); const bool asleep = !sleep.is_null(); @@ -3877,7 +3884,7 @@ void player::update_needs( int rate_multiplier ) } // Huge folks take penalties for cramming themselves in vehicles - if( in_vehicle && ( has_trait( trait_HUGE ) || has_trait( trait_HUGE_OK ) ) ) { + if( in_vehicle && ( mutations.has_trait( trait_HUGE ) || mutations.has_trait( trait_HUGE_OK ) ) ) { vehicle *veh = veh_pointer_or_null( g->m.veh_at( pos() ) ); // it's painful to work the controls, but passengers in open topped vehicles are fine if( veh && ( veh->enclosed_at( pos() ) || veh->player_in_control( *this ) ) ) { @@ -3961,7 +3968,7 @@ void player::update_stamina( int turns ) // Recover some stamina every turn. // Mutated stamina works even when winded float stamina_multiplier = ( !has_effect( effect_winded ) ? 1.0f : 0.1f ) + - mutation_value( "stamina_regen_modifier" ); + mutations.mutation_value( "stamina_regen_modifier" ); // But mouth encumbrance interferes, even with mutated stamina. stamina_recovery += stamina_multiplier * std::max( 1.0f, get_option( "PLAYER_BASE_STAMINA_REGEN_RATE" ) - ( encumb( bp_mouth ) / 5.0f ) ); @@ -4011,7 +4018,7 @@ bool player::is_hibernating() const // life like that--but since there's much less fluid reserve than food reserve, // simply using the same numbers won't work. return has_effect( effect_sleep ) && get_kcal_percent() > 0.8f && - get_thirst() <= 80 && has_active_mutation( trait_id( "HIBERNATE" ) ); + get_thirst() <= 80 && mutations.has_active_mutation( trait_id( "HIBERNATE" ) ); } void player::add_addiction( add_type type, int strength ) @@ -4020,10 +4027,10 @@ void player::add_addiction( add_type type, int strength ) return; } time_duration timer = 2_hours; - if( has_trait( trait_ADDICTIVE ) ) { + if( mutations.has_trait( trait_ADDICTIVE ) ) { strength *= 2; timer = 1_hours; - } else if( has_trait( trait_NONADDICTIVE ) ) { + } else if( mutations.has_trait( trait_NONADDICTIVE ) ) { strength /= 2; timer = 6_hours; } @@ -4140,7 +4147,7 @@ void player::cough( bool harmful, int loudness ) void player::add_pain_msg( int val, body_part bp ) const { - if( has_trait( trait_NOPAIN ) ) { + if( mutations.has_trait( trait_NOPAIN ) ) { return; } if( bp == num_bp ) { @@ -4181,7 +4188,7 @@ void player::print_health() const return; } int current_health = get_healthy(); - if( has_trait( trait_SELFAWARE ) ) { + if( mutations.has_trait( trait_SELFAWARE ) ) { add_msg_if_player( _( "Your current health value is %d." ), current_health ); } @@ -4315,13 +4322,13 @@ void player::process_one_effect( effect &it, bool is_new ) if( val != 0 ) { mod = 1; if( it.get_sizing( "PAIN" ) ) { - if( has_trait( trait_FAT ) ) { + if( mutations.has_trait( trait_FAT ) ) { mod *= 1.5; } - if( has_trait( trait_LARGE ) || has_trait( trait_LARGE_OK ) ) { + if( mutations.has_trait( trait_LARGE ) || mutations.has_trait( trait_LARGE_OK ) ) { mod *= 2; } - if( has_trait( trait_HUGE ) || has_trait( trait_HUGE_OK ) ) { + if( mutations.has_trait( trait_HUGE ) || mutations.has_trait( trait_HUGE_OK ) ) { mod *= 3; } } @@ -4339,13 +4346,13 @@ void player::process_one_effect( effect &it, bool is_new ) if( val != 0 ) { mod = 1; if( it.get_sizing( "HURT" ) ) { - if( has_trait( trait_FAT ) ) { + if( mutations.has_trait( trait_FAT ) ) { mod *= 1.5; } - if( has_trait( trait_LARGE ) || has_trait( trait_LARGE_OK ) ) { + if( mutations.has_trait( trait_LARGE ) || mutations.has_trait( trait_LARGE_OK ) ) { mod *= 2; } - if( has_trait( trait_HUGE ) || has_trait( trait_HUGE_OK ) ) { + if( mutations.has_trait( trait_HUGE ) || mutations.has_trait( trait_HUGE_OK ) ) { mod *= 3; } } @@ -4422,15 +4429,15 @@ void player::process_effects() if( has_effect( effect_darkness ) && g->is_in_sunlight( pos() ) ) { remove_effect( effect_darkness ); } - if( has_trait( trait_M_IMMUNE ) && has_effect( effect_fungus ) ) { + if( mutations.has_trait( trait_M_IMMUNE ) && has_effect( effect_fungus ) ) { vomit(); remove_effect( effect_fungus ); add_msg_if_player( m_bad, _( "We have mistakenly colonized a local guide! Purging now." ) ); } - if( has_trait( trait_PARAIMMUNE ) && ( has_effect( effect_dermatik ) || - has_effect( effect_tapeworm ) || - has_effect( effect_bloodworms ) || has_effect( effect_brainworms ) || - has_effect( effect_paincysts ) ) ) { + if( mutations.has_trait( trait_PARAIMMUNE ) && ( has_effect( effect_dermatik ) || + has_effect( effect_tapeworm ) || + has_effect( effect_bloodworms ) || has_effect( effect_brainworms ) || + has_effect( effect_paincysts ) ) ) { remove_effect( effect_dermatik ); remove_effect( effect_tapeworm ); remove_effect( effect_bloodworms ); @@ -4438,19 +4445,20 @@ void player::process_effects() remove_effect( effect_paincysts ); add_msg_if_player( m_good, _( "Something writhes and inside of you as it dies." ) ); } - if( has_trait( trait_ACIDBLOOD ) && ( has_effect( effect_dermatik ) || - has_effect( effect_bloodworms ) || - has_effect( effect_brainworms ) ) ) { + if( mutations.has_trait( trait_ACIDBLOOD ) && ( has_effect( effect_dermatik ) || + has_effect( effect_bloodworms ) || + has_effect( effect_brainworms ) ) ) { remove_effect( effect_dermatik ); remove_effect( effect_bloodworms ); remove_effect( effect_brainworms ); } - if( has_trait( trait_EATHEALTH ) && has_effect( effect_tapeworm ) ) { + if( mutations.has_trait( trait_EATHEALTH ) && has_effect( effect_tapeworm ) ) { remove_effect( effect_tapeworm ); add_msg_if_player( m_good, _( "Your bowels gurgle as something inside them dies." ) ); } - if( has_trait( trait_INFIMMUNE ) && ( has_effect( effect_bite ) || has_effect( effect_infected ) || - has_effect( effect_recover ) ) ) { + if( mutations.has_trait( trait_INFIMMUNE ) && ( has_effect( effect_bite ) || + has_effect( effect_infected ) || + has_effect( effect_recover ) ) ) { remove_effect( effect_bite ); remove_effect( effect_infected ); remove_effect( effect_recover ); @@ -4472,16 +4480,16 @@ double player::vomit_mod() if( has_effect( effect_weed_high ) ) { mod *= .1; } - if( has_trait( trait_STRONGSTOMACH ) ) { + if( mutations.has_trait( trait_STRONGSTOMACH ) ) { mod *= .5; } - if( has_trait( trait_WEAKSTOMACH ) ) { + if( mutations.has_trait( trait_WEAKSTOMACH ) ) { mod *= 2; } - if( has_trait( trait_NAUSEA ) ) { + if( mutations.has_trait( trait_NAUSEA ) ) { mod *= 3; } - if( has_trait( trait_VOMITOUS ) ) { + if( mutations.has_trait( trait_VOMITOUS ) ) { mod *= 3; } // If you're already nauseous, any food in your stomach greatly @@ -4553,7 +4561,7 @@ void player::suffer() } if( underwater ) { - if( !has_trait( trait_GILLS ) && !has_trait( trait_GILLS_CEPH ) ) { + if( !mutations.has_trait( trait_GILLS ) && !mutations.has_trait( trait_GILLS_CEPH ) ) { oxygen--; } if( oxygen < 12 && worn_with_flag( "REBREATHER" ) ) { @@ -4568,18 +4576,18 @@ void player::suffer() apply_damage( nullptr, bp_torso, rng( 1, 4 ) ); } } - if( has_trait( trait_FRESHWATEROSMOSIS ) && !g->m.has_flag_ter( "SALT_WATER", pos() ) && + if( mutations.has_trait( trait_FRESHWATEROSMOSIS ) && !g->m.has_flag_ter( "SALT_WATER", pos() ) && get_thirst() > -60 ) { mod_thirst( -1 ); } } - if( has_trait( trait_SHARKTEETH ) && one_turn_in( 24_hours ) ) { + if( mutations.has_trait( trait_SHARKTEETH ) && one_turn_in( 24_hours ) ) { add_msg_if_player( m_neutral, _( "You shed a tooth!" ) ); g->m.spawn_item( pos(), "bone", 1 ); } - if( has_active_mutation( trait_id( "WINGS_INSECT" ) ) ) { + if( mutations.has_active_mutation( trait_id( "WINGS_INSECT" ) ) ) { //~Sound of buzzing Insect Wings sounds::sound( pos(), 10, sounds::sound_t::movement, _( "BZZZZZ" ), false, "misc", "insect_wings" ); } @@ -4587,7 +4595,7 @@ void player::suffer() bool wearing_shoes = is_wearing_shoes( side::LEFT ) || is_wearing_shoes( side::RIGHT ); int root_vitamins = 0; int root_water = 0; - if( has_trait( trait_ROOTS3 ) && g->m.has_flag( "PLOWABLE", pos() ) && !wearing_shoes ) { + if( mutations.has_trait( trait_ROOTS3 ) && g->m.has_flag( "PLOWABLE", pos() ) && !wearing_shoes ) { root_vitamins += 1; if( get_thirst() <= -2000 ) { root_water += 51; @@ -4608,9 +4616,9 @@ void player::suffer() } time_duration timer = -6_hours; - if( has_trait( trait_ADDICTIVE ) ) { + if( mutations.has_trait( trait_ADDICTIVE ) ) { timer = -10_hours; - } else if( has_trait( trait_NONADDICTIVE ) ) { + } else if( mutations.has_trait( trait_NONADDICTIVE ) ) { timer = -3_hours; } for( auto &cur_addiction : addictions ) { @@ -4632,15 +4640,16 @@ void player::suffer() } if( !in_sleep_state() ) { - if( !has_trait( trait_id( "DEBUG_STORAGE" ) ) && ( weight_carried() > 4 * weight_capacity() ) ) { + if( !mutations.has_trait( trait_id( "DEBUG_STORAGE" ) ) && + ( weight_carried() > 4 * weight_capacity() ) ) { if( has_effect( effect_downed ) ) { add_effect( effect_downed, 1_turns, num_bp, false, 0, true ); } else { add_effect( effect_downed, 2_turns, num_bp, false, 0, true ); } } - if( has_trait( trait_CHEMIMBALANCE ) ) { - if( one_turn_in( 6_hours ) && !has_trait( trait_NOPAIN ) ) { + if( mutations.has_trait( trait_CHEMIMBALANCE ) ) { + if( one_turn_in( 6_hours ) && !mutations.has_trait( trait_NOPAIN ) ) { add_msg_if_player( m_bad, _( "You suddenly feel sharp pain for no reason." ) ); mod_pain( 3 * rng( 1, 3 ) ); } @@ -4648,7 +4657,7 @@ void player::suffer() int pkilladd = 5 * rng( -1, 2 ); if( pkilladd > 0 ) { add_msg_if_player( m_bad, _( "You suddenly feel numb." ) ); - } else if( ( pkilladd < 0 ) && ( !( has_trait( trait_NOPAIN ) ) ) ) { + } else if( ( pkilladd < 0 ) && ( !( mutations.has_trait( trait_NOPAIN ) ) ) ) { add_msg_if_player( m_bad, _( "You suddenly ache." ) ); } mod_painkiller( pkilladd ); @@ -4700,7 +4709,7 @@ void player::suffer() } } } - if( ( has_trait( trait_SCHIZOPHRENIC ) || has_artifact_with( AEP_SCHIZO ) ) && + if( ( mutations.has_trait( trait_SCHIZOPHRENIC ) || has_artifact_with( AEP_SCHIZO ) ) && !has_effect( effect_took_thorazine ) ) { if( is_player() ) { bool done_effect = false; @@ -4886,14 +4895,14 @@ void player::suffer() } } - if( ( has_trait( trait_NARCOLEPTIC ) || has_artifact_with( AEP_SCHIZO ) ) ) { + if( ( mutations.has_trait( trait_NARCOLEPTIC ) || has_artifact_with( AEP_SCHIZO ) ) ) { if( one_turn_in( 8_hours ) ) { add_msg( m_bad, _( "You're suddenly overcome with the urge to sleep and you pass out." ) ); add_effect( effect_lying_down, 20_minutes ); } } - if( has_trait( trait_JITTERY ) && !has_effect( effect_shakes ) ) { + if( mutations.has_trait( trait_JITTERY ) && !has_effect( effect_shakes ) ) { if( stim > 50 && one_in( to_turns( 30_minutes ) - ( stim * 6 ) ) ) { add_effect( effect_shakes, 30_minutes + 1_turns * stim ); } else if( ( get_hunger() > 80 || get_kcal_percent() < 1.0f ) && get_hunger() > 0 && @@ -4902,7 +4911,7 @@ void player::suffer() } } - if( has_trait( trait_MOODSWINGS ) && one_turn_in( 6_hours ) ) { + if( mutations.has_trait( trait_MOODSWINGS ) && one_turn_in( 6_hours ) ) { if( rng( 1, 20 ) > 9 ) { // 55% chance add_morale( MORALE_MOODSWING, -100, -500 ); } else { // 45% chance @@ -4910,28 +4919,28 @@ void player::suffer() } } - if( has_trait( trait_VOMITOUS ) && one_turn_in( 7_hours ) ) { + if( mutations.has_trait( trait_VOMITOUS ) && one_turn_in( 7_hours ) ) { vomit(); } - if( has_trait( trait_SHOUT1 ) && one_turn_in( 6_hours ) ) { + if( mutations.has_trait( trait_SHOUT1 ) && one_turn_in( 6_hours ) ) { shout(); } - if( has_trait( trait_SHOUT2 ) && one_turn_in( 4_hours ) ) { + if( mutations.has_trait( trait_SHOUT2 ) && one_turn_in( 4_hours ) ) { shout(); } - if( has_trait( trait_SHOUT3 ) && one_turn_in( 3_hours ) ) { + if( mutations.has_trait( trait_SHOUT3 ) && one_turn_in( 3_hours ) ) { shout(); } - if( has_trait( trait_M_SPORES ) && one_turn_in( 4_hours ) ) { + if( mutations.has_trait( trait_M_SPORES ) && one_turn_in( 4_hours ) ) { spores(); } - if( has_trait( trait_M_BLOSSOMS ) && one_turn_in( 3_hours ) ) { + if( mutations.has_trait( trait_M_BLOSSOMS ) && one_turn_in( 3_hours ) ) { blossoms(); } } // Done with while-awake-only effects - if( has_trait( trait_ASTHMA ) && !has_effect( effect_adrenaline ) && + if( mutations.has_trait( trait_ASTHMA ) && !has_effect( effect_adrenaline ) && !has_effect( effect_datura ) && one_in( ( to_turns( 6_hours ) - stim * 300 ) * ( has_effect( effect_sleep ) ? 10 : 1 ) ) ) { @@ -5027,10 +5036,10 @@ void player::suffer() double sleeve_factor = armwear_factor(); const bool has_hat = wearing_something_on( bp_head ); - const bool leafy = has_trait( trait_LEAVES ) || has_trait( trait_LEAVES2 ) || - has_trait( trait_LEAVES3 ); - const bool leafier = has_trait( trait_LEAVES2 ) || has_trait( trait_LEAVES3 ); - const bool leafiest = has_trait( trait_LEAVES3 ); + const bool leafy = mutations.has_trait( trait_LEAVES ) || mutations.has_trait( trait_LEAVES2 ) || + mutations.has_trait( trait_LEAVES3 ); + const bool leafier = mutations.has_trait( trait_LEAVES2 ) || mutations.has_trait( trait_LEAVES3 ); + const bool leafiest = mutations.has_trait( trait_LEAVES3 ); int sunlight_nutrition = 0; if( leafy && g->m.is_outside( pos() ) && ( g->light_level( pos().z ) >= 40 ) ) { const float weather_factor = ( g->weather.weather == WEATHER_CLEAR || @@ -5059,18 +5068,18 @@ void player::suffer() } if( get_pain() > 0 ) { - if( has_trait( trait_PAINREC1 ) && one_turn_in( 1_hours ) ) { + if( mutations.has_trait( trait_PAINREC1 ) && one_turn_in( 1_hours ) ) { mod_pain( -1 ); } - if( has_trait( trait_PAINREC2 ) && one_turn_in( 30_minutes ) ) { + if( mutations.has_trait( trait_PAINREC2 ) && one_turn_in( 30_minutes ) ) { mod_pain( -1 ); } - if( has_trait( trait_PAINREC3 ) && one_turn_in( 15_minutes ) ) { + if( mutations.has_trait( trait_PAINREC3 ) && one_turn_in( 15_minutes ) ) { mod_pain( -1 ); } } - if( ( has_trait( trait_ALBINO ) || has_effect( effect_datura ) ) && + if( ( mutations.has_trait( trait_ALBINO ) || has_effect( effect_datura ) ) && g->is_in_sunlight( pos() ) && one_turn_in( 1_minutes ) ) { // Umbrellas can keep the sun off the skin and sunglasses - off the eyes. if( !weapon.has_flag( "RAIN_PROTECT" ) ) { @@ -5157,8 +5166,8 @@ void player::suffer() } } } - for( const auto &m : my_mutations ) { - const mutation_branch &mdata = m.first.obj(); + for( const trait_id &m : mutations.get_mutations() ) { + const mutation_branch &mdata = m.obj(); for( const body_part bp : all_body_parts ) { if( calendar::once_every( 1_minutes ) ) { // 0.0 - 1.0 @@ -5177,7 +5186,7 @@ void player::suffer() } } - if( has_trait( trait_SUNBURN ) && g->is_in_sunlight( pos() ) && one_in( 10 ) ) { + if( mutations.has_trait( trait_SUNBURN ) && g->is_in_sunlight( pos() ) && one_in( 10 ) ) { if( !( weapon.has_flag( "RAIN_PROTECT" ) ) ) { add_msg_if_player( m_bad, _( "The sunlight burns your skin!" ) ); if( has_effect( effect_sleep ) && !has_effect( effect_narcosis ) ) { @@ -5188,7 +5197,7 @@ void player::suffer() } } - if( ( has_trait( trait_TROGLO ) || has_trait( trait_TROGLO2 ) ) && + if( ( mutations.has_trait( trait_TROGLO ) || mutations.has_trait( trait_TROGLO2 ) ) && g->is_in_sunlight( pos() ) && g->weather.weather == WEATHER_SUNNY ) { mod_str_bonus( -1 ); mod_dex_bonus( -1 ); @@ -5196,14 +5205,14 @@ void player::suffer() mod_int_bonus( -1 ); mod_per_bonus( -1 ); } - if( has_trait( trait_TROGLO2 ) && g->is_in_sunlight( pos() ) ) { + if( mutations.has_trait( trait_TROGLO2 ) && g->is_in_sunlight( pos() ) ) { mod_str_bonus( -1 ); mod_dex_bonus( -1 ); add_miss_reason( _( "The sunlight distracts you." ), 1 ); mod_int_bonus( -1 ); mod_per_bonus( -1 ); } - if( has_trait( trait_TROGLO3 ) && g->is_in_sunlight( pos() ) ) { + if( mutations.has_trait( trait_TROGLO3 ) && g->is_in_sunlight( pos() ) ) { mod_str_bonus( -4 ); mod_dex_bonus( -4 ); add_miss_reason( _( "You can't stand the sunlight!" ), 4 ); @@ -5211,7 +5220,7 @@ void player::suffer() mod_per_bonus( -4 ); } - if( has_trait( trait_SORES ) ) { + if( mutations.has_trait( trait_SORES ) ) { for( const body_part bp : all_body_parts ) { if( bp == bp_head ) { continue; @@ -5223,7 +5232,7 @@ void player::suffer() } } //Web Weavers...weave web - if( has_active_mutation( trait_WEB_WEAVER ) && !in_vehicle ) { + if( mutations.has_active_mutation( trait_WEB_WEAVER ) && !in_vehicle ) { // this adds intensity to if its not already there. g->m.add_field( pos(), fd_web, 1 ); @@ -5231,7 +5240,7 @@ void player::suffer() // Blind/Deaf for brief periods about once an hour, // and visuals about once every 30 min. - if( has_trait( trait_PER_SLIME ) ) { + if( mutations.has_trait( trait_PER_SLIME ) ) { if( one_turn_in( 1_hours ) && !has_effect( effect_deaf ) ) { add_msg_if_player( m_bad, _( "Suddenly, you can't hear anything!" ) ); add_effect( effect_deaf, rng( 20_minutes, 60_minutes ) ); @@ -5248,26 +5257,28 @@ void player::suffer() } } - if( has_trait( trait_WEB_SPINNER ) && !in_vehicle && one_in( 3 ) ) { + if( mutations.has_trait( trait_WEB_SPINNER ) && !in_vehicle && one_in( 3 ) ) { // this adds intensity to if its not already there. g->m.add_field( pos(), fd_web, 1 ); } - if( has_trait( trait_UNSTABLE ) && !has_trait( trait_CHAOTIC_BAD ) && one_turn_in( 48_hours ) ) { - mutate(); + if( mutations.has_trait( trait_UNSTABLE ) && !mutations.has_trait( trait_CHAOTIC_BAD ) && + one_turn_in( 48_hours ) ) { + mutations.mutate(); } - if( ( has_trait( trait_CHAOTIC ) || has_trait( trait_CHAOTIC_BAD ) ) && one_turn_in( 12_hours ) ) { - mutate(); + if( ( mutations.has_trait( trait_CHAOTIC ) || mutations.has_trait( trait_CHAOTIC_BAD ) ) && + one_turn_in( 12_hours ) ) { + mutations.mutate(); } if( has_artifact_with( AEP_MUTAGENIC ) && one_turn_in( 48_hours ) ) { - mutate(); + mutations.mutate(); } if( has_artifact_with( AEP_FORCE_TELEPORT ) && one_turn_in( 1_hours ) ) { teleport::teleport( *this ); } const bool needs_fire = !has_morale( MORALE_PYROMANIA_NEARFIRE ) && !has_morale( MORALE_PYROMANIA_STARTFIRE ); - if( has_trait( trait_PYROMANIA ) && needs_fire && !in_sleep_state() && + if( mutations.has_trait( trait_PYROMANIA ) && needs_fire && !in_sleep_state() && calendar::once_every( 2_hours ) ) { add_morale( MORALE_PYROMANIA_NOFIRE, -1, -30, 24_hours, 24_hours, true ); if( calendar::once_every( 4_hours ) ) { @@ -5275,7 +5286,7 @@ void player::suffer() add_msg_if_player( m_bad, _( smokin_hot_fiyah ) ); } } - if( has_trait( trait_KILLER ) && !has_morale( MORALE_KILLER_HAS_KILLED ) && + if( mutations.has_trait( trait_KILLER ) && !has_morale( MORALE_KILLER_HAS_KILLED ) && calendar::once_every( 2_hours ) ) { add_morale( MORALE_KILLER_NEED_TO_KILL, -1, -30, 24_hours, 24_hours ); if( calendar::once_every( 4_hours ) ) { @@ -5292,11 +5303,11 @@ void player::suffer() float rads = map_radiation / 100.0f + item_radiation / 10.0f; int rad_mut = 0; - if( has_trait( trait_RADIOACTIVE3 ) ) { + if( mutations.has_trait( trait_RADIOACTIVE3 ) ) { rad_mut = 3; - } else if( has_trait( trait_RADIOACTIVE2 ) ) { + } else if( mutations.has_trait( trait_RADIOACTIVE2 ) ) { rad_mut = 2; - } else if( has_trait( trait_RADIOACTIVE1 ) ) { + } else if( mutations.has_trait( trait_RADIOACTIVE1 ) ) { rad_mut = 1; } @@ -5344,7 +5355,7 @@ void player::suffer() radiation = 2000; } if( get_option( "RAD_MUTATION" ) && rng( 100, 10000 ) < radiation ) { - mutate(); + mutations.mutate(); radiation -= 50; } else if( radiation > 50 && rng( 1, 3000 ) < radiation && ( stomach.contains() > 0_ml || radiation_increasing || !in_sleep_state() ) ) { @@ -5353,7 +5364,7 @@ void player::suffer() } } - const bool radiogenic = has_trait( trait_RADIOGENIC ); + const bool radiogenic = mutations.has_trait( trait_RADIOGENIC ); if( radiogenic && calendar::once_every( 30_minutes ) && radiation > 0 ) { // At 200 irradiation, twice as fast as REGEN if( x_in_y( radiation, 200 ) ) { @@ -5708,11 +5719,11 @@ void player::suffer() bool player::irradiate( float rads, bool bypass ) { int rad_mut = 0; - if( has_trait( trait_RADIOACTIVE3 ) ) { + if( mutations.has_trait( trait_RADIOACTIVE3 ) ) { rad_mut = 3; - } else if( has_trait( trait_RADIOACTIVE2 ) ) { + } else if( mutations.has_trait( trait_RADIOACTIVE2 ) ) { rad_mut = 2; - } else if( has_trait( trait_RADIOACTIVE1 ) ) { + } else if( mutations.has_trait( trait_RADIOACTIVE1 ) ) { rad_mut = 1; } @@ -5819,7 +5830,7 @@ void player::mend( int rate_multiplier ) healing_factor *= addiction_scaling( 0.25f, 0.75f, addiction_level( ADD_ALCOHOL ) ); } - if( radiation > 0 && !has_trait( trait_RADIOGENIC ) ) { + if( radiation > 0 && !mutations.has_trait( trait_RADIOGENIC ) ) { healing_factor *= clamp( ( 1000.0f - radiation ) / 1000.0f, 0.0f, 1.0f ); } @@ -5846,16 +5857,16 @@ void player::mend( int rate_multiplier ) // Mutagenic healing factor! bool needs_splint = true; - if( has_trait( trait_REGEN_LIZ ) ) { + if( mutations.has_trait( trait_REGEN_LIZ ) ) { healing_factor *= 20.0; needs_splint = false; - } else if( has_trait( trait_REGEN ) ) { + } else if( mutations.has_trait( trait_REGEN ) ) { healing_factor *= 16.0; - } else if( has_trait( trait_FASTHEALER2 ) ) { + } else if( mutations.has_trait( trait_FASTHEALER2 ) ) { healing_factor *= 4.0; - } else if( has_trait( trait_FASTHEALER ) ) { + } else if( mutations.has_trait( trait_FASTHEALER ) ) { healing_factor *= 2.0; - } else if( has_trait( trait_SLOWHEALER ) ) { + } else if( mutations.has_trait( trait_SLOWHEALER ) ) { healing_factor *= 0.5; } @@ -5952,7 +5963,8 @@ void player::drench( int saturation, const body_part_set &flags, bool ignore_wat } // OK, water gets in your AEP suit or whatever. It wasn't built to keep you dry. - if( has_trait( trait_DEBUG_NOTEMP ) || has_active_mutation( trait_id( "SHELL2" ) ) || + if( mutations.has_trait( trait_DEBUG_NOTEMP ) || + mutations.has_active_mutation( trait_id( "SHELL2" ) ) || ( !ignore_waterproof && is_waterproof( flags ) ) ) { return; } @@ -6085,12 +6097,14 @@ void player::update_body_wetness( const w_point &weather ) delay += ( ( weather.humidity - 66 ) - ( weather.temperature - 65 ) ) / 100; delay = std::max( 0.1, delay ); // Fur/slime retains moisture - if( has_trait( trait_LIGHTFUR ) || has_trait( trait_FUR ) || has_trait( trait_FELINE_FUR ) || - has_trait( trait_LUPINE_FUR ) || has_trait( trait_CHITIN_FUR ) || has_trait( trait_CHITIN_FUR2 ) || - has_trait( trait_CHITIN_FUR3 ) ) { + if( mutations.has_trait( trait_LIGHTFUR ) || mutations.has_trait( trait_FUR ) || + mutations.has_trait( trait_FELINE_FUR ) || + mutations.has_trait( trait_LUPINE_FUR ) || mutations.has_trait( trait_CHITIN_FUR ) || + mutations.has_trait( trait_CHITIN_FUR2 ) || + mutations.has_trait( trait_CHITIN_FUR3 ) ) { delay = delay * 6 / 5; } - if( has_trait( trait_URSINE_FUR ) || has_trait( trait_SLIMY ) ) { + if( mutations.has_trait( trait_URSINE_FUR ) || mutations.has_trait( trait_SLIMY ) ) { delay *= 1.5; } @@ -6179,8 +6193,8 @@ void player::check_and_recover_morale() test_morale.on_item_wear( wit ); } - for( const auto &mut : my_mutations ) { - test_morale.on_mutation_gain( mut.first ); + for( const trait_id &mut : mutations.get_mutations() ) { + test_morale.on_mutation_gain( mut ); } for( auto &elem : *effects ) { @@ -6757,7 +6771,7 @@ bool player::consume_item( item &target ) add_msg_if_player( m_info, _( "You do not have that item." ) ); return false; } - if( is_underwater() && !has_trait( trait_WATERSLEEP ) ) { + if( is_underwater() && !mutations.has_trait( trait_WATERSLEEP ) ) { add_msg_if_player( m_info, _( "You can't do that while underwater." ) ); return false; } @@ -7298,17 +7312,17 @@ ret_val player::can_wear( const item &it ) const : string_format( _( "%s can't wear that much on their head!" ), name ) ) ); } - if( has_trait( trait_WOOLALLERGY ) && ( it.made_of( material_id( "wool" ) ) || - it.item_tags.count( "wooled" ) ) ) { + if( mutations.has_trait( trait_WOOLALLERGY ) && ( it.made_of( material_id( "wool" ) ) || + it.item_tags.count( "wooled" ) ) ) { return ret_val::make_failure( _( "Can't wear that, it's made of wool!" ) ); } - if( it.is_filthy() && has_trait( trait_SQUEAMISH ) ) { + if( it.is_filthy() && mutations.has_trait( trait_SQUEAMISH ) ) { return ret_val::make_failure( _( "Can't wear that, it's filthy!" ) ); } if( !it.has_flag( "OVERSIZE" ) && !it.has_flag( "SEMITANGIBLE" ) ) { - for( const trait_id &mut : get_mutations() ) { + for( const trait_id &mut : mutations.get_mutations() ) { const auto &branch = mut.obj(); if( branch.conflicts_with_item( it ) ) { return ret_val::make_failure( _( "Your %s mutation prevents you from wearing your %s." ), @@ -7318,11 +7332,11 @@ ret_val player::can_wear( const item &it ) const if( it.covers( bp_head ) && !it.has_flag( "SEMITANGIBLE" ) && !it.made_of( material_id( "wool" ) ) && !it.made_of( material_id( "cotton" ) ) && !it.made_of( material_id( "nomex" ) ) && !it.made_of( material_id( "leather" ) ) && - ( has_trait( trait_HORNS_POINTED ) || has_trait( trait_ANTENNAE ) || - has_trait( trait_ANTLERS ) ) ) { + ( mutations.has_trait( trait_HORNS_POINTED ) || mutations.has_trait( trait_ANTENNAE ) || + mutations.has_trait( trait_ANTLERS ) ) ) { return ret_val::make_failure( _( "Cannot wear a helmet over %s." ), - ( has_trait( trait_HORNS_POINTED ) ? _( "horns" ) : - ( has_trait( trait_ANTENNAE ) ? _( "antennae" ) : _( "antlers" ) ) ) ); + ( mutations.has_trait( trait_HORNS_POINTED ) ? _( "horns" ) : + ( mutations.has_trait( trait_ANTENNAE ) ? _( "antennae" ) : _( "antlers" ) ) ) ); } } @@ -7609,7 +7623,7 @@ bool player::dispose_item( item_location &&obj, const std::string &prompt ) void player::mend_item( item_location &&obj, bool interactive ) { - if( g->u.has_trait( trait_DEBUG_HS ) ) { + if( g->u.mutations.has_trait( trait_DEBUG_HS ) ) { uilist menu; menu.text = _( "Toggle which fault?" ); std::vector> opts; @@ -7859,8 +7873,8 @@ player::wear_item( const item &to_wear, bool interactive ) } const bool was_deaf = is_deaf(); - const bool supertinymouse = g->u.has_trait( trait_id( "SMALL2" ) ) || - g->u.has_trait( trait_id( "SMALL_OK" ) ); + const bool supertinymouse = g->u.mutations.has_trait( trait_id( "SMALL2" ) ) || + g->u.mutations.has_trait( trait_id( "SMALL_OK" ) ); last_item = to_wear.typeId(); std::list::iterator position = position_to_wear_new_item( to_wear ); @@ -8678,7 +8692,7 @@ bool player::gunmod_remove( item &gun, item &mod ) std::pair player::gunmod_installation_odds( const item &gun, const item &mod ) const { // Mods with INSTALL_DIFFICULT have a chance to fail, potentially damaging the gun - if( !mod.has_flag( "INSTALL_DIFFICULT" ) || has_trait( trait_DEBUG_HS ) ) { + if( !mod.has_flag( "INSTALL_DIFFICULT" ) || mutations.has_trait( trait_DEBUG_HS ) ) { return std::make_pair( 100, 0 ); } @@ -8721,7 +8735,7 @@ void player::gunmod_add( item &gun, item &mod ) } // first check at least the minimum requirements are met - if( !has_trait( trait_DEBUG_HS ) && !can_use( mod, gun ) ) { + if( !mutations.has_trait( trait_DEBUG_HS ) && !can_use( mod, gun ) ) { return; } @@ -8782,7 +8796,7 @@ void player::gunmod_add( item &gun, item &mod ) actions[ prompt.ret ](); } - const int turns = !has_trait( trait_DEBUG_HS ) ? mod.type->gunmod->install_time : 0; + const int turns = !mutations.has_trait( trait_DEBUG_HS ) ? mod.type->gunmod->install_time : 0; const int moves = to_moves( time_duration::from_turns( turns ) ); assign_activity( activity_id( "ACT_GUNMOD_ADD" ), moves, -1, get_item_position( &gun ), tool ); @@ -8799,7 +8813,7 @@ void player::toolmod_add( item_location tool, item_location mod ) return; } // first check at least the minimum requirements are met - if( !has_trait( trait_DEBUG_HS ) && !can_use( *mod, *tool ) ) { + if( !mutations.has_trait( trait_DEBUG_HS ) && !can_use( *mod, *tool ) ) { return; } @@ -8818,11 +8832,11 @@ void player::toolmod_add( item_location tool, item_location mod ) bool player::fun_to_read( const item &book ) const { // If you don't have a problem with eating humans, To Serve Man becomes rewarding - if( ( has_trait( trait_CANNIBAL ) || has_trait( trait_PSYCHOPATH ) || - has_trait( trait_SAPIOVORE ) ) && + if( ( mutations.has_trait( trait_CANNIBAL ) || mutations.has_trait( trait_PSYCHOPATH ) || + mutations.has_trait( trait_SAPIOVORE ) ) && book.typeId() == "cookbook_human" ) { return true; - } else if( has_trait( trait_SPIRITUAL ) && book.has_flag( "INSPIRATIONAL" ) ) { + } else if( mutations.has_trait( trait_SPIRITUAL ) && book.has_flag( "INSPIRATIONAL" ) ) { return true; } else { return book_fun_for( book, *this ) > 0; @@ -8838,17 +8852,17 @@ int player::book_fun_for( const item &book, const player &p ) const } // If you don't have a problem with eating humans, To Serve Man becomes rewarding - if( ( p.has_trait( trait_CANNIBAL ) || p.has_trait( trait_PSYCHOPATH ) || - p.has_trait( trait_SAPIOVORE ) ) && + if( ( p.mutations.has_trait( trait_CANNIBAL ) || p.mutations.has_trait( trait_PSYCHOPATH ) || + p.mutations.has_trait( trait_SAPIOVORE ) ) && book.typeId() == "cookbook_human" ) { fun_bonus = abs( fun_bonus ); - } else if( p.has_trait( trait_SPIRITUAL ) && book.has_flag( "INSPIRATIONAL" ) ) { + } else if( p.mutations.has_trait( trait_SPIRITUAL ) && book.has_flag( "INSPIRATIONAL" ) ) { fun_bonus = abs( fun_bonus * 3 ); } - if( has_trait( trait_LOVES_BOOKS ) ) { + if( mutations.has_trait( trait_LOVES_BOOKS ) ) { fun_bonus++; - } else if( has_trait( trait_HATES_BOOKS ) ) { + } else if( mutations.has_trait( trait_HATES_BOOKS ) ) { if( book.type->book->fun > 0 ) { fun_bonus = 0; } else { @@ -8958,7 +8972,7 @@ void player::try_to_sleep( const time_duration &dur ) bool websleeping = false; bool in_shell = false; bool watersleep = false; - if( has_trait( trait_CHLOROMORPH ) ) { + if( mutations.has_trait( trait_CHLOROMORPH ) ) { plantsleep = true; if( ( ter_at_pos == t_dirt || ter_at_pos == t_pit || ter_at_pos == t_dirtmound || ter_at_pos == t_pit_shallow || @@ -8973,19 +8987,19 @@ void player::try_to_sleep( const time_duration &dur ) } else { // Floor problems add_msg_if_player( m_bad, _( "Your roots scrabble ineffectively at the unyielding surface." ) ); } - } else if( has_trait( trait_M_SKIN3 ) ) { + } else if( mutations.has_trait( trait_M_SKIN3 ) ) { fungaloid_cosplay = true; if( g->m.has_flag_ter_or_furn( "FUNGUS", pos() ) ) { add_msg_if_player( m_good, _( "Our fibers meld with the ground beneath us. The gills on our neck begin to seed the air with spores as our awareness fades." ) ); } } - if( has_trait( trait_WEB_WALKER ) ) { + if( mutations.has_trait( trait_WEB_WALKER ) ) { websleep = true; } // Not sure how one would get Arachnid w/o web-making, but Just In Case - if( has_trait( trait_THRESH_SPIDER ) && ( has_trait( trait_WEB_SPINNER ) || - ( has_trait( trait_WEB_WEAVER ) ) ) ) { + if( mutations.has_trait( trait_THRESH_SPIDER ) && ( mutations.has_trait( trait_WEB_SPINNER ) || + ( mutations.has_trait( trait_WEB_WEAVER ) ) ) ) { webforce = true; } if( websleep || webforce ) { @@ -9013,11 +9027,11 @@ void player::try_to_sleep( const time_duration &dur ) } } } - if( has_active_mutation( trait_SHELL2 ) ) { + if( mutations.has_active_mutation( trait_SHELL2 ) ) { // Your shell's interior is a comfortable place to sleep. in_shell = true; } - if( has_trait( trait_WATERSLEEP ) ) { + if( mutations.has_trait( trait_WATERSLEEP ) ) { if( underwater ) { add_msg_if_player( m_good, _( "You lay beneath the waves' embrace, gazing up through the water's surface…" ) ); @@ -9076,13 +9090,14 @@ comfort_level player::base_comfort_value( const tripoint &p ) const // as arachnids who sleep in webs will find most places comfortable for instance. int comfort = 0; - bool plantsleep = has_trait( trait_CHLOROMORPH ); - bool fungaloid_cosplay = has_trait( trait_M_SKIN3 ); - bool websleep = has_trait( trait_WEB_WALKER ); - bool webforce = has_trait( trait_THRESH_SPIDER ) && ( has_trait( trait_WEB_SPINNER ) || - ( has_trait( trait_WEB_WEAVER ) ) ); - bool in_shell = has_active_mutation( trait_SHELL2 ); - bool watersleep = has_trait( trait_WATERSLEEP ); + bool plantsleep = mutations.has_trait( trait_CHLOROMORPH ); + bool fungaloid_cosplay = mutations.has_trait( trait_M_SKIN3 ); + bool websleep = mutations.has_trait( trait_WEB_WALKER ); + bool webforce = mutations.has_trait( trait_THRESH_SPIDER ) && + ( mutations.has_trait( trait_WEB_SPINNER ) || + ( mutations.has_trait( trait_WEB_WEAVER ) ) ); + bool in_shell = mutations.has_active_mutation( trait_SHELL2 ); + bool watersleep = mutations.has_trait( trait_WATERSLEEP ); const optional_vpart_position vp = g->m.veh_at( p ); const maptile tile = g->m.maptile_at( p ); @@ -9199,16 +9214,16 @@ int player::sleep_spot( const tripoint &p ) const { comfort_level base_level = base_comfort_value( p ); int sleepy = static_cast( base_level ); - bool watersleep = has_trait( trait_WATERSLEEP ); + bool watersleep = mutations.has_trait( trait_WATERSLEEP ); if( has_addiction( ADD_SLEEP ) ) { sleepy -= 4; } - if( has_trait( trait_INSOMNIA ) ) { + if( mutations.has_trait( trait_INSOMNIA ) ) { // 12.5 points is the difference between "tired" and "dead tired" sleepy -= 12; } - if( has_trait( trait_EASYSLEEPER ) ) { + if( mutations.has_trait( trait_EASYSLEEPER ) ) { // Low fatigue (being rested) has a much stronger effect than high fatigue // so it's OK for the value to be that much higher sleepy += 24; @@ -9216,7 +9231,7 @@ int player::sleep_spot( const tripoint &p ) const if( has_active_bionic( bio_soporific ) ) { sleepy += 30; } - if( has_trait( trait_EASYSLEEPER2 ) ) { + if( mutations.has_trait( trait_EASYSLEEPER2 ) ) { // Mousefolk can sleep just about anywhere. sleepy += 40; } @@ -9230,7 +9245,7 @@ int player::sleep_spot( const tripoint &p ) const sleepy += static_cast( ( get_fatigue() - TIRED + 1 ) / 16 ); } - if( stim > 0 || !has_trait( trait_INSOMNIA ) ) { + if( stim > 0 || !mutations.has_trait( trait_INSOMNIA ) ) { sleepy -= 2 * stim; } else { // Make it harder for insomniac to get around the trait @@ -9291,7 +9306,7 @@ float player::fine_detail_vision_mod( const tripoint &p ) const // it's annoying rather than limiting. if( is_blind() || ( ( has_effect( effect_boomered ) || has_effect( effect_darkness ) ) && - !has_trait( trait_PER_SLIME_OK ) ) ) { + !mutations.has_trait( trait_PER_SLIME_OK ) ) ) { return 11.0; } // Scale linearly as light level approaches LIGHT_AMBIENT_LIT. @@ -9333,7 +9348,7 @@ int player::get_wind_resistance( body_part bp ) const } // Your shell provides complete wind protection if you're inside it - if( has_active_mutation( trait_SHELL2 ) ) { + if( mutations.has_active_mutation( trait_SHELL2 ) ) { totalCoverage = 100; return totalCoverage; } @@ -9429,7 +9444,7 @@ int player::get_armor_type( damage_type dt, body_part bp ) const } } - ret += mutation_armor( bp, dt ); + ret += mutations.mutation_armor( bp, dt ); return ret; } case DT_NULL: @@ -9469,7 +9484,7 @@ int player::get_armor_bash_base( body_part bp ) const ret += 3; } - ret += mutation_armor( bp, DT_BASH ); + ret += mutations.mutation_armor( bp, DT_BASH ); return ret; } @@ -9496,7 +9511,7 @@ int player::get_armor_cut_base( body_part bp ) const ret += 3; } - ret += mutation_armor( bp, DT_CUT ); + ret += mutations.mutation_armor( bp, DT_CUT ); return ret; } @@ -9527,7 +9542,7 @@ int player::get_env_resist( body_part bp ) const } } - if( bp == bp_eyes && has_trait( trait_SEESLEEP ) ) { + if( bp == bp_eyes && mutations.has_trait( trait_SEESLEEP ) ) { ret += 8; } return ret; @@ -9596,13 +9611,13 @@ int player::shoe_type_count( const itype_id &it ) const int player::adjust_for_focus( int amount ) const { int effective_focus = focus_pool; - if( has_trait( trait_FASTLEARNER ) ) { + if( mutations.has_trait( trait_FASTLEARNER ) ) { effective_focus += 15; } if( has_active_bionic( bio_memory ) ) { effective_focus += 10; } - if( has_trait( trait_SLOWLEARNER ) ) { + if( mutations.has_trait( trait_SLOWLEARNER ) ) { effective_focus -= 15; } double tmp = amount * ( effective_focus / 100.0 ); @@ -9632,26 +9647,26 @@ void player::practice( const skill_id &id, int amount, int cap, bool suppress_wa return result.first; }; - const bool isSavant = has_trait( trait_SAVANT ); + const bool isSavant = mutations.has_trait( trait_SAVANT ); const skill_id savantSkill = isSavant ? highest_skill() : skill_id::NULL_ID(); amount = adjust_for_focus( amount ); - if( has_trait( trait_PACIFIST ) && skill.is_combat_skill() ) { + if( mutations.has_trait( trait_PACIFIST ) && skill.is_combat_skill() ) { if( !one_in( 3 ) ) { amount = 0; } } - if( has_trait( trait_PRED2 ) && skill.is_combat_skill() ) { + if( mutations.has_trait( trait_PRED2 ) && skill.is_combat_skill() ) { if( one_in( 3 ) ) { amount *= 2; } } - if( has_trait( trait_PRED3 ) && skill.is_combat_skill() ) { + if( mutations.has_trait( trait_PRED3 ) && skill.is_combat_skill() ) { amount *= 2; } - if( has_trait( trait_PRED4 ) && skill.is_combat_skill() ) { + if( mutations.has_trait( trait_PRED4 ) && skill.is_combat_skill() ) { amount *= 3; } @@ -9682,7 +9697,7 @@ void player::practice( const skill_id &id, int amount, int cap, bool suppress_wa focus_pool -= chance_to_drop / 100; // Apex Predators don't think about much other than killing. // They don't lose Focus when practicing combat skills. - if( ( rng( 1, 100 ) <= ( chance_to_drop % 100 ) ) && ( !( has_trait( trait_PRED4 ) && + if( ( rng( 1, 100 ) <= ( chance_to_drop % 100 ) ) && ( !( mutations.has_trait( trait_PRED4 ) && skill.is_combat_skill() ) ) ) { focus_pool--; } @@ -10092,12 +10107,12 @@ bool player::has_weapon() const m_size player::get_size() const { - if( has_trait( trait_id( "SMALL2" ) ) || has_trait( trait_id( "SMALL_OK" ) ) || - has_trait( trait_id( "SMALL" ) ) ) { + if( mutations.has_trait( trait_id( "SMALL2" ) ) || mutations.has_trait( trait_id( "SMALL_OK" ) ) || + mutations.has_trait( trait_id( "SMALL" ) ) ) { return MS_SMALL; - } else if( has_trait( trait_LARGE ) || has_trait( trait_LARGE_OK ) ) { + } else if( mutations.has_trait( trait_LARGE ) || mutations.has_trait( trait_LARGE_OK ) ) { return MS_LARGE; - } else if( has_trait( trait_HUGE ) || has_trait( trait_HUGE_OK ) ) { + } else if( mutations.has_trait( trait_HUGE ) || mutations.has_trait( trait_HUGE_OK ) ) { return MS_HUGE; } return MS_MEDIUM; @@ -10140,7 +10155,7 @@ int player::get_hp_max( hp_part bp ) const int player::get_stamina_max() const { int maxStamina = get_option< int >( "PLAYER_MAX_STAMINA" ); - maxStamina *= Character::mutation_value( "max_stamina_modifier" ); + maxStamina *= Character::mutations.mutation_value( "max_stamina_modifier" ); return maxStamina; } @@ -10167,7 +10182,7 @@ void player::burn_move_stamina( int moves ) add_msg( m_debug, "Stamina burn: %d", -( ( moves * burn_ratio ) / 100 ) ); // Chance to suffer pain if overburden and stamina runs out or has trait BADBACK // Starts at 1 in 25, goes down by 5 for every 50% more carried - if( ( current_weight > max_weight ) && ( has_trait( trait_BADBACK ) || stamina == 0 ) && + if( ( current_weight > max_weight ) && ( mutations.has_trait( trait_BADBACK ) || stamina == 0 ) && one_in( 35 - 5 * current_weight / ( max_weight / 2 ) ) ) { add_msg_if_player( m_bad, _( "Your body strains under the weight!" ) ); // 1 more pain for every 800 grams more (5 per extra STR needed) @@ -10247,7 +10262,7 @@ bool player::sees( const Creature &critter ) const { // This handles only the player/npc specific stuff (monsters don't have traits or bionics). const int dist = rl_dist( pos(), critter.pos() ); - if( dist <= 3 && has_active_mutation( trait_ANTENNAE ) ) { + if( dist <= 3 && mutations.has_active_mutation( trait_ANTENNAE ) ) { return true; } if( critter.digging() && has_active_bionic( bio_ground_sonar ) ) { @@ -10337,9 +10352,11 @@ void player::add_known_trap( const tripoint &pos, const trap &t ) bool player::is_deaf() const { - return get_effect_int( effect_deaf ) > 2 || worn_with_flag( "DEAF" ) || has_trait( trait_DEAF ) || + return get_effect_int( effect_deaf ) > 2 || worn_with_flag( "DEAF" ) || + mutations.has_trait( trait_DEAF ) || ( has_active_bionic( bio_earplugs ) && !has_active_bionic( bio_ears ) ) || - ( has_trait( trait_M_SKIN3 ) && g->m.has_flag_ter_or_furn( "FUNGUS", pos() ) && in_sleep_state() ); + ( mutations.has_trait( trait_M_SKIN3 ) && g->m.has_flag_ter_or_furn( "FUNGUS", pos() ) && + in_sleep_state() ); } bool player::can_hear( const tripoint &source, const int volume ) const @@ -10365,14 +10382,14 @@ float player::hearing_ability() const if( has_active_bionic( bio_ears ) && !has_active_bionic( bio_earplugs ) ) { volume_multiplier *= 3.5; } - if( has_trait( trait_PER_SLIME ) ) { + if( mutations.has_trait( trait_PER_SLIME ) ) { // Random hearing :-/ // (when it's working at all, see player.cpp) // changed from 0.5 to fix Mac compiling error volume_multiplier *= ( rng( 1, 2 ) ); } - volume_multiplier *= Character::mutation_value( "hearing_modifier" ); + volume_multiplier *= Character::mutations.mutation_value( "hearing_modifier" ); if( has_effect( effect_deaf ) ) { // Scale linearly up to 30 minutes @@ -10417,7 +10434,7 @@ std::vector player::short_description_parts() const result.push_back( _( "Wearing: " ) + worn_str ); } const int visibility_cap = 0; // no cap - const auto trait_str = visible_mutations( visibility_cap ); + const std::string trait_str = mutations.visible_mutations( visibility_cap ); if( !trait_str.empty() ) { result.push_back( _( "Traits: " ) + trait_str ); } @@ -10550,7 +10567,7 @@ void player::place_corpse( const tripoint &om_target ) bool player::sees_with_infrared( const Creature &critter ) const { // electroreceptors grants vision of robots and electric monsters through walls - if( has_trait( trait_ELECTRORECEPTORS ) && + if( mutations.has_trait( trait_ELECTRORECEPTORS ) && ( critter.in_species( ROBOT ) || critter.has_flag( MF_ELECTRIC ) ) ) { return true; } @@ -10585,7 +10602,7 @@ float player::power_rating() const } else if( dmg > 12 ) { ret = 3; // Melee weapon or weapon-y tool } - if( has_trait( trait_HUGE ) || has_trait( trait_HUGE_OK ) ) { + if( mutations.has_trait( trait_HUGE ) || mutations.has_trait( trait_HUGE_OK ) ) { ret += 1; } if( is_wearing_power_armor( nullptr ) ) { @@ -10772,9 +10789,9 @@ void player::do_skill_rust() SkillLevel &skill_level_obj = pair.second; if( aSkill.is_combat_skill() && - ( ( has_trait( trait_PRED2 ) && one_in( 4 ) ) || - ( has_trait( trait_PRED3 ) && one_in( 2 ) ) || - ( has_trait( trait_PRED4 ) && x_in_y( 2, 3 ) ) ) ) { + ( ( mutations.has_trait( trait_PRED2 ) && one_in( 4 ) ) || + ( mutations.has_trait( trait_PRED3 ) && one_in( 2 ) ) || + ( mutations.has_trait( trait_PRED4 ) && x_in_y( 2, 3 ) ) ) ) { // Their brain is optimized to remember this if( one_in( 15600 ) ) { // They've already passed the roll to avoid rust at @@ -10884,7 +10901,7 @@ std::pair player::get_pain_description() const pain_color = c_light_red; } // get pain string - if( ( has_trait( trait_SELFAWARE ) || has_effect( effect_got_checked ) ) && + if( ( mutations.has_trait( trait_SELFAWARE ) || has_effect( effect_got_checked ) ) && get_perceived_pain() > 0 ) { pain_string = string_format( "%s %d", _( "Pain " ), get_perceived_pain() ); } else if( get_perceived_pain() > 0 ) { From bd4df5bbd8de3ada18ebf8f41cd257e7cb95e0a9 Mon Sep 17 00:00:00 2001 From: KorGgenT Date: Sun, 13 Oct 2019 20:47:41 -0400 Subject: [PATCH 04/34] get_trait_data --- src/character_mutations.cpp | 6 ++++++ src/character_mutations.h | 2 ++ src/mutation.cpp | 2 +- src/player.cpp | 8 ++++---- 4 files changed, 13 insertions(+), 5 deletions(-) create mode 100644 src/character_mutations.cpp diff --git a/src/character_mutations.cpp b/src/character_mutations.cpp new file mode 100644 index 0000000000000..cf59d294dcf29 --- /dev/null +++ b/src/character_mutations.cpp @@ -0,0 +1,6 @@ +#include "character_mutations.h" + +character_mutations::trait_data &character_mutations::get_trait_data( const trait_id &mut ) const +{ + return &my_mutations[mut]; +} diff --git a/src/character_mutations.h b/src/character_mutations.h index 5939f2dde28dc..87152bd6c14b8 100644 --- a/src/character_mutations.h +++ b/src/character_mutations.h @@ -132,6 +132,8 @@ class character_mutations int bodytemp_modifier_traits_floor() const; /** Returns an enumeration of visible mutations with colors */ std::string visible_mutations( int visibility_cap ) const; + + trait_data &get_trait_data( const trait_id &mut ) const; private: /** * Traits / mutations of the character. Key is the mutation id (it's also a valid diff --git a/src/mutation.cpp b/src/mutation.cpp index b9f9f88a2c34f..2619eceb592dc 100644 --- a/src/mutation.cpp +++ b/src/mutation.cpp @@ -419,7 +419,7 @@ bool Character::can_install_cbm_on_bp( const std::vector &bps ) const void player::activate_mutation( const trait_id &mut ) { const mutation_branch &mdata = mut.obj(); - auto &tdata = my_mutations[mut]; + const character_mutations::trait_data &tdata = mutations.get_trait_data( mut ); int cost = mdata.cost; // Preserve the fake weapon used to initiate ranged mutation firing static item mut_ranged( weapon ); diff --git a/src/player.cpp b/src/player.cpp index e317afa081214..d7e5049922853 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -4516,12 +4516,12 @@ void player::suffer() } } - for( auto &mut : my_mutations ) { - auto &tdata = mut.second; + for( const trait_id &mut : mutations.get_mutations() ) { + character_mutations::trait_data &tdata = mutations.get_trait_data( mut ); if( !tdata.powered ) { continue; } - const auto &mdata = mut.first.obj(); + const mutation_branch &mdata = mut.obj(); if( tdata.powered && tdata.charge > 0 ) { // Already-on units just lose a bit of charge tdata.charge--; @@ -4555,7 +4555,7 @@ void player::suffer() } if( !tdata.powered ) { - apply_mods( mut.first, false ); + apply_mods( mut, false ); } } } From aa76cee0ca0622bfeb256c1227ce2811567c5160 Mon Sep 17 00:00:00 2001 From: KorGgenT Date: Sun, 13 Oct 2019 21:23:34 -0400 Subject: [PATCH 05/34] add Character to function parameters --- src/character.h | 7 +- src/character_mutations.h | 22 ++-- src/mutation.cpp | 229 +++++++++++++++++++------------------- src/player.h | 6 +- 4 files changed, 132 insertions(+), 132 deletions(-) diff --git a/src/character.h b/src/character.h index 9288dae68ee06..2b3fa487f131c 100644 --- a/src/character.h +++ b/src/character.h @@ -558,9 +558,8 @@ class Character : public Creature, public visitable /** Applies skill-based boosts to stats **/ void apply_skill_boost(); protected: - /** Applies stat mods to character. */ - void apply_mods( const trait_id &mut, bool add_remove ); + void deactivate_mutation( const trait_id &mut ); /** Recalculate encumbrance for all body parts. */ std::array calc_encumbrance() const; /** Recalculate encumbrance for all body parts as if `new_item` was also worn. */ @@ -581,6 +580,8 @@ class Character : public Creature, public visitable std::array, num_bp> mut_drench; public: + /** Applies stat mods to character. */ + void apply_mods( const trait_id &mut, bool add_remove ); // recalculates enchantment cache by iterating through all held, worn, and wielded items void recalculate_enchantment_cache(); // gets add and mult value from enchantment cache @@ -1056,9 +1057,9 @@ class Character : public Creature, public visitable protected: void on_stat_change( const std::string &, int ) override {} void on_damage_of_type( int adjusted_damage, damage_type type, body_part bp ) override; + public: virtual void on_mutation_gain( const trait_id & ) {} virtual void on_mutation_loss( const trait_id & ) {} - public: virtual void on_item_wear( const item & ) {} virtual void on_item_takeoff( const item & ) {} virtual void on_worn_item_washed( const item & ) {} diff --git a/src/character_mutations.h b/src/character_mutations.h index 87152bd6c14b8..af1c4d76894e4 100644 --- a/src/character_mutations.h +++ b/src/character_mutations.h @@ -46,27 +46,27 @@ class character_mutations float mutation_armor( body_part bp, damage_type dt ) const; float mutation_armor( body_part bp, const damage_unit &du ) const; /** Handles things like destruction of armor, etc. */ - void mutation_effect( const trait_id &mut ); + void mutation_effect( Character &owner, const trait_id &mut ); /** Handles what happens when you lose a mutation. */ - void mutation_loss_effect( const trait_id &mut ); + void mutation_loss_effect( Character &owner, const trait_id &mut ); bool has_active_mutation( const trait_id &b ) const; /** Picks a random valid mutation and gives it to the Character, possibly removing/changing others along the way */ - void mutate(); + void mutate( Character &owner ); /** Returns true if the player doesn't have the mutation or a conflicting one and it complies with the force typing */ bool mutation_ok( const trait_id &mutation, bool force_good, bool force_bad ) const; /** Picks a random valid mutation in a category and mutate_towards() it */ - void mutate_category( const std::string &mut_cat ); + void mutate_category( Character &owner, const std::string &mut_cat ); /** Mutates toward one of the given mutations, upgrading or removing conflicts if necessary */ - bool mutate_towards( std::vector muts, int num_tries = INT_MAX ); + bool mutate_towards( Character &owner, std::vector muts, int num_tries = INT_MAX ); /** Mutates toward the entered mutation, upgrading or removing conflicts if necessary */ - bool mutate_towards( const trait_id &mut ); + bool mutate_towards( Character &owner, const trait_id &mut ); /** Removes a mutation, downgrading to the previous level if possible */ - void remove_mutation( const trait_id &mut, bool silent = false ); + void remove_mutation( Character &owner, const trait_id &mut, bool silent = false ); /** Returns true if the player has the entered mutation child flag */ bool has_child_flag( const trait_id &flag ) const; /** Removes the mutation's child flag from the player's list */ - void remove_child_flag( const trait_id &flag ); + void remove_child_flag( Character &owner, const trait_id &flag ); /** Recalculates mutation_category_level[] values for the player */ void set_highest_cat_level(); /** Returns the highest mutation category */ @@ -94,10 +94,10 @@ class character_mutations trait_id trait_by_invlet( int ch ) const; /** Toggles a trait on the player and in their mutation list */ - void toggle_trait( const trait_id &flag ); + void toggle_trait( Character &owner, const trait_id &flag ); /** Add or removes a mutation on the player, but does not trigger mutation loss/gain effects. */ - void set_mutation( const trait_id &flag ); - void unset_mutation( const trait_id &flag ); + void set_mutation( Character &owner, const trait_id &flag ); + void unset_mutation( Character &owner, const trait_id &flag ); /** Returns true if the player has a conflicting trait to the entered trait * Uses has_opposite_trait(), has_lower_trait(), and has_higher_trait() to determine conflicts. */ diff --git a/src/mutation.cpp b/src/mutation.cpp index 2619eceb592dc..6254c36047a08 100644 --- a/src/mutation.cpp +++ b/src/mutation.cpp @@ -9,6 +9,7 @@ #include "event_bus.h" #include "field.h" #include "game.h" +#include "character.h" #include "character_mutations.h" #include "item.h" #include "itype.h" @@ -102,7 +103,7 @@ bool character_mutations::has_base_trait( const trait_id &b ) const return my_traits.find( b ) != my_traits.end(); } -void character_mutations::toggle_trait( const trait_id &flag ) +void character_mutations::toggle_trait( Character &owner, const trait_id &flag ) { const auto titer = my_traits.find( flag ); if( titer == my_traits.end() ) { @@ -112,15 +113,15 @@ void character_mutations::toggle_trait( const trait_id &flag ) } const auto miter = my_mutations.find( flag ); if( miter == my_mutations.end() ) { - set_mutation( flag ); - mutation_effect( flag ); + set_mutation( owner, flag ); + mutation_effect( owner, flag ); } else { - unset_mutation( flag ); - mutation_loss_effect( flag ); + unset_mutation( owner, flag ); + mutation_loss_effect( owner, flag ); } } -void character_mutations::set_mutation( const trait_id &flag ) +void character_mutations::set_mutation( Character &owner, const trait_id &flag ) { const auto iter = my_mutations.find( flag ); if( iter == my_mutations.end() ) { @@ -129,11 +130,11 @@ void character_mutations::set_mutation( const trait_id &flag ) } else { return; } - recalc_sight_limits(); - reset_encumbrance(); + owner.recalc_sight_limits(); + owner.reset_encumbrance(); } -void character_mutations::unset_mutation( const trait_id &flag ) +void character_mutations::unset_mutation( Character &owner, const trait_id &flag ) { const auto iter = my_mutations.find( flag ); if( iter != my_mutations.end() ) { @@ -144,8 +145,8 @@ void character_mutations::unset_mutation( const trait_id &flag ) } else { return; } - recalc_sight_limits(); - reset_encumbrance(); + owner.recalc_sight_limits(); + owner.reset_encumbrance(); } int character_mutations::get_mod( const trait_id &mut, const std::string &arg ) const @@ -199,46 +200,46 @@ const resistances &mutation_branch::damage_resistance( body_part bp ) const return iter->second; } -void character_mutations::mutation_effect( const trait_id &mut ) +void character_mutations::mutation_effect( Character &owner, const trait_id &mut ) { if( mut == "GLASSJAW" ) { - recalc_hp(); + owner.recalc_hp(); } else if( mut == trait_STR_ALPHA ) { - if( str_max < 16 ) { - str_max = 8 + str_max / 2; + if( owner.str_max < 16 ) { + owner.str_max = 8 + owner.str_max / 2; } - apply_mods( mut, true ); - recalc_hp(); + owner.apply_mods( mut, true ); + owner.recalc_hp(); } else if( mut == trait_DEX_ALPHA ) { - if( dex_max < 16 ) { - dex_max = 8 + dex_max / 2; + if( owner.dex_max < 16 ) { + owner.dex_max = 8 + owner.dex_max / 2; } - apply_mods( mut, true ); + owner.apply_mods( mut, true ); } else if( mut == trait_INT_ALPHA ) { - if( int_max < 16 ) { - int_max = 8 + int_max / 2; + if( owner.int_max < 16 ) { + owner.int_max = 8 + owner.int_max / 2; } - apply_mods( mut, true ); + owner.apply_mods( mut, true ); } else if( mut == trait_INT_SLIME ) { - int_max *= 2; // Now, can you keep it? :-) + owner.int_max *= 2; // Now, can you keep it? :-) } else if( mut == trait_PER_ALPHA ) { - if( per_max < 16 ) { - per_max = 8 + per_max / 2; + if( owner.per_max < 16 ) { + owner.per_max = 8 + owner.per_max / 2; } - apply_mods( mut, true ); + owner.apply_mods( mut, true ); } else { - apply_mods( mut, true ); + owner.apply_mods( mut, true ); } const auto &branch = mut.obj(); if( branch.hp_modifier != 0.0f || branch.hp_modifier_secondary != 0.0f || branch.hp_adjustment != 0.0f ) { - recalc_hp(); + owner.recalc_hp(); } - remove_worn_items_with( [&]( item & armor ) { + owner.remove_worn_items_with( [&]( item & armor ) { static const std::string mutation_safe = "OVERSIZE"; if( armor.has_flag( mutation_safe ) ) { return false; @@ -247,19 +248,19 @@ void character_mutations::mutation_effect( const trait_id &mut ) return false; } if( branch.destroys_gear ) { - add_msg_player_or_npc( m_bad, + owner.add_msg_player_or_npc( m_bad, _( "Your %s is destroyed!" ), _( "'s %s is destroyed!" ), armor.tname() ); for( item &remain : armor.contents ) { - g->m.add_item_or_charges( pos(), remain ); + g->m.add_item_or_charges( owner.pos(), remain ); } } else { - add_msg_player_or_npc( m_bad, + owner.add_msg_player_or_npc( m_bad, _( "Your %s is pushed off!" ), _( "'s %s is pushed off!" ), armor.tname() ); - g->m.add_item_or_charges( pos(), armor ); + g->m.add_item_or_charges( owner.pos(), armor ); } return true; } ); @@ -268,49 +269,49 @@ void character_mutations::mutation_effect( const trait_id &mut ) my_mutations[mut].powered = true; } - on_mutation_gain( mut ); + owner.on_mutation_gain( mut ); } -void character_mutations::mutation_loss_effect( const trait_id &mut ) +void character_mutations::mutation_loss_effect( Character &owner, const trait_id &mut ) { if( mut == "GLASSJAW" ) { - recalc_hp(); + owner.recalc_hp(); } else if( mut == trait_STR_ALPHA ) { - apply_mods( mut, false ); - if( str_max < 16 ) { - str_max = 2 * ( str_max - 8 ); + owner.apply_mods( mut, false ); + if( owner.str_max < 16 ) { + owner.str_max = 2 * ( owner.str_max - 8 ); } - recalc_hp(); + owner.recalc_hp(); } else if( mut == trait_DEX_ALPHA ) { - apply_mods( mut, false ); - if( dex_max < 16 ) { - dex_max = 2 * ( dex_max - 8 ); + owner.apply_mods( mut, false ); + if( owner.dex_max < 16 ) { + owner.dex_max = 2 * ( owner.dex_max - 8 ); } } else if( mut == trait_INT_ALPHA ) { - apply_mods( mut, false ); - if( int_max < 16 ) { - int_max = 2 * ( int_max - 8 ); + owner.apply_mods( mut, false ); + if( owner.int_max < 16 ) { + owner.int_max = 2 * ( owner.int_max - 8 ); } } else if( mut == trait_INT_SLIME ) { - int_max /= 2; // In case you have a freak accident with the debug menu ;-) + owner.int_max /= 2; // In case you have a freak accident with the debug menu ;-) } else if( mut == trait_PER_ALPHA ) { - apply_mods( mut, false ); - if( per_max < 16 ) { - per_max = 2 * ( per_max - 8 ); + owner.apply_mods( mut, false ); + if( owner.per_max < 16 ) { + owner.per_max = 2 * ( owner.per_max - 8 ); } } else { - apply_mods( mut, false ); + owner.apply_mods( mut, false ); } const auto &branch = mut.obj(); if( branch.hp_modifier != 0.0f || branch.hp_modifier_secondary != 0.0f || branch.hp_adjustment != 0.0f ) { - recalc_hp(); + owner.recalc_hp(); } - on_mutation_loss( mut ); + owner.on_mutation_loss( mut ); } bool character_mutations::has_active_mutation( const trait_id &b ) const @@ -419,7 +420,7 @@ bool Character::can_install_cbm_on_bp( const std::vector &bps ) const void player::activate_mutation( const trait_id &mut ) { const mutation_branch &mdata = mut.obj(); - const character_mutations::trait_data &tdata = mutations.get_trait_data( mut ); + character_mutations::trait_data &tdata = mutations.get_trait_data( mut ); int cost = mdata.cost; // Preserve the fake weapon used to initiate ranged mutation firing static item mut_ranged( weapon ); @@ -529,7 +530,7 @@ void player::activate_mutation( const trait_id &mut ) return; } - if( mutations.has_trait( trait_ROOTS2 ) || mutationshas_trait( trait_ROOTS3 ) ) { + if( mutations.has_trait( trait_ROOTS2 ) || mutations.has_trait( trait_ROOTS3 ) ) { add_msg_if_player( _( "You reach out to the trees with your roots." ) ); } else { add_msg_if_player( @@ -538,8 +539,8 @@ void player::activate_mutation( const trait_id &mut ) assign_activity( activity_id( "ACT_TREE_COMMUNION" ) ); - if( has_trait( trait_ROOTS2 ) || has_trait( trait_ROOTS3 ) ) { - const time_duration startup_time = has_trait( trait_ROOTS3 ) ? rng( 15_minutes, + if( mutations.has_trait( trait_ROOTS2 ) || mutations.has_trait( trait_ROOTS3 ) ) { + const time_duration startup_time = mutations.has_trait( trait_ROOTS3 ) ? rng( 15_minutes, 30_minutes ) : rng( 60_minutes, 90_minutes ); activity.values.push_back( to_turns( startup_time ) ); return; @@ -569,9 +570,9 @@ void player::activate_mutation( const trait_id &mut ) } } -void player::deactivate_mutation( const trait_id &mut ) +void Character::deactivate_mutation( const trait_id &mut ) { - my_mutations[mut].powered = false; + mutations.get_trait_data( mut ).powered = false; // Handle stat changes from deactivation apply_mods( mut, false ); @@ -615,7 +616,7 @@ bool character_mutations::mutation_ok( const trait_id &mutation, bool force_good return true; } -void character_mutations::mutate() +void character_mutations::mutate( Character &owner ) { bool force_bad = one_in( 3 ); bool force_good = false; @@ -705,7 +706,7 @@ void character_mutations::mutate() size_t roll = rng( 0, upgrades.size() + 4 ); if( roll < upgrades.size() ) { // We got a valid upgrade index, so use it and return. - mutate_towards( upgrades[roll] ); + mutate_towards( owner, upgrades[roll] ); return; } } @@ -714,7 +715,7 @@ void character_mutations::mutate() if( !downgrades.empty() && !cat.empty() ) { size_t roll = rng( 0, downgrades.size() + 4 ); if( roll < downgrades.size() ) { - remove_mutation( downgrades[roll] ); + remove_mutation( owner, downgrades[roll] ); return; } } @@ -764,20 +765,20 @@ void character_mutations::mutate() return; } - if( mutate_towards( random_entry( valid ) ) ) { + if( mutate_towards( owner, random_entry( valid ) ) ) { return; } else { // if mutation failed (errors, post-threshold pick), try again once. - mutate_towards( random_entry( valid ) ); + mutate_towards( owner, random_entry( valid ) ); } } -void character_mutations::mutate_category( const std::string &cat ) +void character_mutations::mutate_category( Character &owner, const std::string &cat ) { // Hacky ID comparison is better than separate hardcoded branch used before // TODO: Turn it into the null id if( cat == "ANY" ) { - mutate(); + mutate( owner ); return; } @@ -802,7 +803,7 @@ void character_mutations::mutate_category( const std::string &cat ) } } - mutate_towards( valid, 2 ); + mutate_towards( owner, valid, 2 ); } static std::vector get_all_mutation_prereqs( const trait_id &id ) @@ -821,12 +822,12 @@ static std::vector get_all_mutation_prereqs( const trait_id &id ) return ret; } -bool character_mutations::mutate_towards( std::vector muts, int num_tries ) +bool character_mutations::mutate_towards( Character &owner, std::vector muts, int num_tries ) { while( !muts.empty() && num_tries > 0 ) { int i = rng( 0, muts.size() - 1 ); - if( mutate_towards( muts[i] ) ) { + if( mutate_towards( owner, muts[i] ) ) { return true; } @@ -837,10 +838,10 @@ bool character_mutations::mutate_towards( std::vector muts, int num_tr return false; } -bool character_mutations::mutate_towards( const trait_id &mut ) +bool character_mutations::mutate_towards( Character &owner, const trait_id &mut ) { if( has_child_flag( mut ) ) { - remove_child_flag( mut ); + remove_child_flag( owner, mut ); return true; } const mutation_branch &mdata = mut.obj(); @@ -877,12 +878,12 @@ bool character_mutations::mutate_towards( const trait_id &mut ) for( size_t i = 0; i < cancel.size(); i++ ) { if( !cancel.empty() ) { trait_id removed = cancel[i]; - remove_mutation( removed ); + remove_mutation( owner, removed ); cancel.erase( cancel.begin() + i ); i--; // This checks for cases where one trait knocks out several others // Probably a better way, but gets it Fixed Now--KA101 - return mutate_towards( mut ); + return mutate_towards( owner, mut ); } } @@ -904,9 +905,9 @@ bool character_mutations::mutate_towards( const trait_id &mut ) if( !has_prereqs && ( !prereq.empty() || !prereqs2.empty() ) ) { if( !prereq1 && !prereq.empty() ) { - return mutate_towards( prereq ); + return mutate_towards( owner, prereq ); } else if( !prereq2 && !prereqs2.empty() ) { - return mutate_towards( prereqs2 ); + return mutate_towards( owner, prereqs2 ); } } @@ -919,7 +920,7 @@ bool character_mutations::mutate_towards( const trait_id &mut ) // It shouldn't pick a Threshold anyway--they're supposed to be non-Valid // and aren't categorized. This can happen if someone makes a threshold mutation into a prerequisite. if( threshold ) { - add_msg_if_player( _( "You feel something straining deep inside you, yearning to be free…" ) ); + owner.add_msg_if_player( _( "You feel something straining deep inside you, yearning to be free..." ) ); return false; } if( profession ) { @@ -935,7 +936,7 @@ bool character_mutations::mutate_towards( const trait_id &mut ) // No crossing The Threshold by simply not having it if( !has_threshreq && !threshreq.empty() ) { - add_msg_if_player( _( "You feel something straining deep inside you, yearning to be free…" ) ); + owner.add_msg_if_player( _( "You feel something straining deep inside you, yearning to be free..." ) ); return false; } @@ -969,7 +970,7 @@ bool character_mutations::mutate_towards( const trait_id &mut ) } } - set_mutation( mut ); + set_mutation( owner, mut ); bool mutation_replaced = false; @@ -989,15 +990,15 @@ bool character_mutations::mutate_towards( const trait_id &mut ) //  TODO: Limit this to visible mutations // TODO: In case invisible mutation turns into visible or vice versa // print only the visible mutation appearing/disappearing - add_msg_player_or_npc( rating, + owner.add_msg_player_or_npc( rating, _( "Your %1$s mutation turns into %2$s!" ), _( "'s %1$s mutation turns into %2$s!" ), replace_mdata.name(), mdata.name() ); - g->events().send( getID(), replace_mdata.id, mdata.id ); - unset_mutation( replacing ); - mutation_loss_effect( replacing ); - mutation_effect( mut ); + g->events().send( owner.getID(), replace_mdata.id, mdata.id ); + unset_mutation( owner, replacing ); + mutation_loss_effect( owner, replacing ); + mutation_effect( owner, mut ); mutation_replaced = true; } if( replacing2 ) { @@ -1011,14 +1012,14 @@ bool character_mutations::mutate_towards( const trait_id &mut ) } else { rating = m_neutral; } - add_msg_player_or_npc( rating, + owner.add_msg_player_or_npc( rating, _( "Your %1$s mutation turns into %2$s!" ), _( "'s %1$s mutation turns into %2$s!" ), replace_mdata.name(), mdata.name() ); - g->events().send( getID(), replace_mdata.id, mdata.id ); - unset_mutation( replacing2 ); - mutation_loss_effect( replacing2 ); - mutation_effect( mut ); + g->events().send( owner.getID(), replace_mdata.id, mdata.id ); + unset_mutation( owner, replacing2 ); + mutation_loss_effect( owner, replacing2 ); + mutation_effect( owner, mut ); mutation_replaced = true; } for( const auto &i : canceltrait ) { @@ -1035,14 +1036,14 @@ bool character_mutations::mutate_towards( const trait_id &mut ) rating = m_mixed; } // If this new mutation cancels a base trait, remove it and add the mutation at the same time - add_msg_player_or_npc( rating, + owner.add_msg_player_or_npc( rating, _( "Your innate %1$s trait turns into %2$s!" ), _( "'s innate %1$s trait turns into %2$s!" ), cancel_mdata.name(), mdata.name() ); - g->events().send( getID(), cancel_mdata.id, mdata.id ); - unset_mutation( i ); - mutation_loss_effect( i ); - mutation_effect( mut ); + g->events().send( owner.getID(), cancel_mdata.id, mdata.id ); + unset_mutation( owner, i ); + mutation_loss_effect( owner, i ); + mutation_effect( owner, mut ); mutation_replaced = true; } if( !mutation_replaced ) { @@ -1056,12 +1057,12 @@ bool character_mutations::mutate_towards( const trait_id &mut ) rating = m_neutral; } // TODO: Limit to visible mutations - add_msg_player_or_npc( rating, + owner.add_msg_player_or_npc( rating, _( "You gain a mutation called %s!" ), _( " gains a mutation called %s!" ), mdata.name() ); - g->events().send( getID(), mdata.id ); - mutation_effect( mut ); + g->events().send( owner.getID(), mdata.id ); + mutation_effect( owner, mut ); } set_highest_cat_level(); @@ -1069,7 +1070,7 @@ bool character_mutations::mutate_towards( const trait_id &mut ) return true; } -void character_mutations::remove_mutation( const trait_id &mut, bool silent ) +void character_mutations::remove_mutation( Character &owner, const trait_id &mut, bool silent ) { const auto &mdata = mut.obj(); // Check if there's a prerequisite we should shrink back into @@ -1148,7 +1149,7 @@ void character_mutations::remove_mutation( const trait_id &mut, bool silent ) } // This should revert back to a removed base trait rather than simply removing the mutation - unset_mutation( mut ); + unset_mutation( owner, mut ); bool mutation_replaced = false; @@ -1166,14 +1167,14 @@ void character_mutations::remove_mutation( const trait_id &mut, bool silent ) rating = m_neutral; } if( !silent ) { - add_msg_player_or_npc( rating, + owner.add_msg_player_or_npc( rating, _( "Your %1$s mutation turns into %2$s." ), _( "'s %1$s mutation turns into %2$s." ), mdata.name(), replace_mdata.name() ); } - set_mutation( replacing ); - mutation_loss_effect( mut ); - mutation_effect( replacing ); + set_mutation( owner, replacing ); + mutation_loss_effect( owner, mut ); + mutation_effect( owner, replacing ); mutation_replaced = true; } if( replacing2 ) { @@ -1188,14 +1189,14 @@ void character_mutations::remove_mutation( const trait_id &mut, bool silent ) rating = m_neutral; } if( !silent ) { - add_msg_player_or_npc( rating, + owner.add_msg_player_or_npc( rating, _( "Your %1$s mutation turns into %2$s." ), _( "'s %1$s mutation turns into %2$s." ), mdata.name(), replace_mdata.name() ); } - set_mutation( replacing2 ); - mutation_loss_effect( mut ); - mutation_effect( replacing2 ); + set_mutation( owner, replacing2 ); + mutation_loss_effect( owner, mut ); + mutation_effect( owner, replacing2 ); mutation_replaced = true; } if( !mutation_replaced ) { @@ -1209,12 +1210,12 @@ void character_mutations::remove_mutation( const trait_id &mut, bool silent ) rating = m_neutral; } if( !silent ) { - add_msg_player_or_npc( rating, + owner.add_msg_player_or_npc( rating, _( "You lose your %s mutation." ), _( " loses their %s mutation." ), mdata.name() ); } - mutation_loss_effect( mut ); + mutation_loss_effect( owner, mut ); } set_highest_cat_level(); @@ -1232,15 +1233,15 @@ bool character_mutations::has_child_flag( const trait_id &flag ) const return false; } -void character_mutations::remove_child_flag( const trait_id &flag ) +void character_mutations::remove_child_flag( Character &owner, const trait_id &flag ) { for( auto &elem : flag->replacements ) { const trait_id &tmp = elem; if( has_trait( tmp ) ) { - remove_mutation( tmp ); + remove_mutation( owner, tmp ); return; } else if( has_child_flag( tmp ) ) { - remove_child_flag( tmp ); + remove_child_flag( owner, tmp ); return; } } @@ -1248,7 +1249,7 @@ void character_mutations::remove_child_flag( const trait_id &flag ) static mutagen_rejection try_reject_mutagen( player &p, const item &it, bool strong ) { - if( p.has_trait( trait_MUTAGEN_AVOID ) ) { + if( p.mutations.has_trait( trait_MUTAGEN_AVOID ) ) { p.add_msg_if_player( m_warning, //~ "Uh-uh" is a sound used for "nope", "no", etc. _( "After what happened that last time? uh-uh. You're not drinking that chemical stuff." ) ); @@ -1265,7 +1266,7 @@ static mutagen_rejection try_reject_mutagen( player &p, const item &it, bool str return mutagen_rejection::accepted; } - if( p.has_trait( trait_THRESH_MYCUS ) ) { + if( p.mutations.has_trait( trait_THRESH_MYCUS ) ) { p.add_msg_if_player( m_info, _( "This is a contaminant. We reject it from the Mycus." ) ); if( p.has_trait( trait_M_SPORES ) || p.has_trait( trait_M_FERTILE ) || p.has_trait( trait_M_BLOSSOMS ) || p.has_trait( trait_M_BLOOM ) ) { diff --git a/src/player.h b/src/player.h index 5a3e826d1e669..59b465f14fc11 100644 --- a/src/player.h +++ b/src/player.h @@ -1555,6 +1555,8 @@ class player : public Character // magic mod known_magic magic; + // Trigger and disable mutations that can be so toggled. + void activate_mutation( const trait_id &mutation ); protected: trap_map known_traps; @@ -1564,7 +1566,6 @@ class player : public Character /** Processes human-specific effects of an effect. */ void process_one_effect( effect &it, bool is_new ) override; - private: /** Check if an area-of-effect technique has valid targets */ @@ -1595,9 +1596,6 @@ class player : public Character /** Define blood loss (in percents) */ int blood_loss( body_part bp ) const; - // Trigger and disable mutations that can be so toggled. - void activate_mutation( const trait_id &mutation ); - void deactivate_mutation( const trait_id &mut ); bool has_fire( int quantity ) const; void use_fire( int quantity ); From bef01c77262411c6878a87c1aedf25e9aecce417 Mon Sep 17 00:00:00 2001 From: KorGgenT Date: Wed, 16 Oct 2019 12:15:07 -0400 Subject: [PATCH 06/34] add character to function parameters --- src/player.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/player.cpp b/src/player.cpp index d7e5049922853..e3155158bcfef 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -5264,14 +5264,14 @@ void player::suffer() if( mutations.has_trait( trait_UNSTABLE ) && !mutations.has_trait( trait_CHAOTIC_BAD ) && one_turn_in( 48_hours ) ) { - mutations.mutate(); + mutations.mutate( *this ); } if( ( mutations.has_trait( trait_CHAOTIC ) || mutations.has_trait( trait_CHAOTIC_BAD ) ) && one_turn_in( 12_hours ) ) { - mutations.mutate(); + mutations.mutate( *this ); } if( has_artifact_with( AEP_MUTAGENIC ) && one_turn_in( 48_hours ) ) { - mutations.mutate(); + mutations.mutate( *this ); } if( has_artifact_with( AEP_FORCE_TELEPORT ) && one_turn_in( 1_hours ) ) { teleport::teleport( *this ); @@ -5355,7 +5355,7 @@ void player::suffer() radiation = 2000; } if( get_option( "RAD_MUTATION" ) && rng( 100, 10000 ) < radiation ) { - mutations.mutate(); + mutations.mutate( *this ); radiation -= 50; } else if( radiation > 50 && rng( 1, 3000 ) < radiation && ( stomach.contains() > 0_ml || radiation_increasing || !in_sleep_state() ) ) { From b484134cd8366cb37e78b2c3877f860f6591203f Mon Sep 17 00:00:00 2001 From: KorGgenT Date: Wed, 16 Oct 2019 12:18:58 -0400 Subject: [PATCH 07/34] add mutations. to get members --- src/character.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/character.cpp b/src/character.cpp index 9d00e3233554f..6b24ebe59ad38 100644 --- a/src/character.cpp +++ b/src/character.cpp @@ -3542,7 +3542,7 @@ bool Character::is_invisible() const has_active_bionic( str_bio_cloak ) || has_active_bionic( str_bio_night ) || is_wearing_active_optcloak() || - has_trait( trait_DEBUG_CLOAK ) || + mutations.has_trait( trait_DEBUG_CLOAK ) || has_artifact_with( AEP_INVISIBLE ) ); } @@ -3555,7 +3555,7 @@ int Character::visibility( bool, int ) const } // TODO: // if ( dark_clothing() && light check ... - int stealth_modifier = std::floor( mutation_value( "stealth_modifier" ) ); + int stealth_modifier = std::floor( mutations.mutation_value( "stealth_modifier" ) ); return clamp( 100 - stealth_modifier, 40, 160 ); } @@ -4550,9 +4550,9 @@ void Character::passive_absorb_hit( body_part bp, damage_unit &du ) const if( du.type == DT_STAB ) { damage_unit du_copy = du; du_copy.type = DT_CUT; - du.amount -= mutation_armor( bp, du_copy ); + du.amount -= mutations.mutation_armor( bp, du_copy ); } else { - du.amount -= mutation_armor( bp, du ); + du.amount -= mutations.mutation_armor( bp, du ); } } du.amount -= bionic_armor_bonus( bp, du.type ); //Check for passive armor bionics @@ -4975,8 +4975,8 @@ void Character::on_hurt( Creature *source, bool disturb /*= true*/ ) bool Character::crossed_threshold() const { - for( const std::pair &mut : my_mutations ) { - if( mut.first->threshold ) { + for( const trait_id &mut : mutations.get_mutations() ) { + if( mut->threshold ) { return true; } } From e08341d539fdc6e9316254c0bd79d64b04ef93f3 Mon Sep 17 00:00:00 2001 From: KorGgenT Date: Wed, 16 Oct 2019 17:57:14 -0400 Subject: [PATCH 08/34] astyle mutation.cpp --- src/mutation.cpp | 63 +++++++++++++++++++++++++----------------------- 1 file changed, 33 insertions(+), 30 deletions(-) diff --git a/src/mutation.cpp b/src/mutation.cpp index 6254c36047a08..f158a2d9c49c4 100644 --- a/src/mutation.cpp +++ b/src/mutation.cpp @@ -249,17 +249,17 @@ void character_mutations::mutation_effect( Character &owner, const trait_id &mut } if( branch.destroys_gear ) { owner.add_msg_player_or_npc( m_bad, - _( "Your %s is destroyed!" ), - _( "'s %s is destroyed!" ), - armor.tname() ); + _( "Your %s is destroyed!" ), + _( "'s %s is destroyed!" ), + armor.tname() ); for( item &remain : armor.contents ) { g->m.add_item_or_charges( owner.pos(), remain ); } } else { owner.add_msg_player_or_npc( m_bad, - _( "Your %s is pushed off!" ), - _( "'s %s is pushed off!" ), - armor.tname() ); + _( "Your %s is pushed off!" ), + _( "'s %s is pushed off!" ), + armor.tname() ); g->m.add_item_or_charges( owner.pos(), armor ); } return true; @@ -822,7 +822,8 @@ static std::vector get_all_mutation_prereqs( const trait_id &id ) return ret; } -bool character_mutations::mutate_towards( Character &owner, std::vector muts, int num_tries ) +bool character_mutations::mutate_towards( Character &owner, std::vector muts, + int num_tries ) { while( !muts.empty() && num_tries > 0 ) { int i = rng( 0, muts.size() - 1 ); @@ -920,7 +921,8 @@ bool character_mutations::mutate_towards( Character &owner, const trait_id &mut // It shouldn't pick a Threshold anyway--they're supposed to be non-Valid // and aren't categorized. This can happen if someone makes a threshold mutation into a prerequisite. if( threshold ) { - owner.add_msg_if_player( _( "You feel something straining deep inside you, yearning to be free..." ) ); + owner.add_msg_if_player( + _( "You feel something straining deep inside you, yearning to be free..." ) ); return false; } if( profession ) { @@ -936,7 +938,8 @@ bool character_mutations::mutate_towards( Character &owner, const trait_id &mut // No crossing The Threshold by simply not having it if( !has_threshreq && !threshreq.empty() ) { - owner.add_msg_if_player( _( "You feel something straining deep inside you, yearning to be free..." ) ); + owner.add_msg_if_player( + _( "You feel something straining deep inside you, yearning to be free..." ) ); return false; } @@ -991,9 +994,9 @@ bool character_mutations::mutate_towards( Character &owner, const trait_id &mut // TODO: In case invisible mutation turns into visible or vice versa // print only the visible mutation appearing/disappearing owner.add_msg_player_or_npc( rating, - _( "Your %1$s mutation turns into %2$s!" ), - _( "'s %1$s mutation turns into %2$s!" ), - replace_mdata.name(), mdata.name() ); + _( "Your %1$s mutation turns into %2$s!" ), + _( "'s %1$s mutation turns into %2$s!" ), + replace_mdata.name(), mdata.name() ); g->events().send( owner.getID(), replace_mdata.id, mdata.id ); unset_mutation( owner, replacing ); @@ -1013,9 +1016,9 @@ bool character_mutations::mutate_towards( Character &owner, const trait_id &mut rating = m_neutral; } owner.add_msg_player_or_npc( rating, - _( "Your %1$s mutation turns into %2$s!" ), - _( "'s %1$s mutation turns into %2$s!" ), - replace_mdata.name(), mdata.name() ); + _( "Your %1$s mutation turns into %2$s!" ), + _( "'s %1$s mutation turns into %2$s!" ), + replace_mdata.name(), mdata.name() ); g->events().send( owner.getID(), replace_mdata.id, mdata.id ); unset_mutation( owner, replacing2 ); mutation_loss_effect( owner, replacing2 ); @@ -1037,9 +1040,9 @@ bool character_mutations::mutate_towards( Character &owner, const trait_id &mut } // If this new mutation cancels a base trait, remove it and add the mutation at the same time owner.add_msg_player_or_npc( rating, - _( "Your innate %1$s trait turns into %2$s!" ), - _( "'s innate %1$s trait turns into %2$s!" ), - cancel_mdata.name(), mdata.name() ); + _( "Your innate %1$s trait turns into %2$s!" ), + _( "'s innate %1$s trait turns into %2$s!" ), + cancel_mdata.name(), mdata.name() ); g->events().send( owner.getID(), cancel_mdata.id, mdata.id ); unset_mutation( owner, i ); mutation_loss_effect( owner, i ); @@ -1058,9 +1061,9 @@ bool character_mutations::mutate_towards( Character &owner, const trait_id &mut } // TODO: Limit to visible mutations owner.add_msg_player_or_npc( rating, - _( "You gain a mutation called %s!" ), - _( " gains a mutation called %s!" ), - mdata.name() ); + _( "You gain a mutation called %s!" ), + _( " gains a mutation called %s!" ), + mdata.name() ); g->events().send( owner.getID(), mdata.id ); mutation_effect( owner, mut ); } @@ -1168,9 +1171,9 @@ void character_mutations::remove_mutation( Character &owner, const trait_id &mut } if( !silent ) { owner.add_msg_player_or_npc( rating, - _( "Your %1$s mutation turns into %2$s." ), - _( "'s %1$s mutation turns into %2$s." ), - mdata.name(), replace_mdata.name() ); + _( "Your %1$s mutation turns into %2$s." ), + _( "'s %1$s mutation turns into %2$s." ), + mdata.name(), replace_mdata.name() ); } set_mutation( owner, replacing ); mutation_loss_effect( owner, mut ); @@ -1190,9 +1193,9 @@ void character_mutations::remove_mutation( Character &owner, const trait_id &mut } if( !silent ) { owner.add_msg_player_or_npc( rating, - _( "Your %1$s mutation turns into %2$s." ), - _( "'s %1$s mutation turns into %2$s." ), - mdata.name(), replace_mdata.name() ); + _( "Your %1$s mutation turns into %2$s." ), + _( "'s %1$s mutation turns into %2$s." ), + mdata.name(), replace_mdata.name() ); } set_mutation( owner, replacing2 ); mutation_loss_effect( owner, mut ); @@ -1211,9 +1214,9 @@ void character_mutations::remove_mutation( Character &owner, const trait_id &mut } if( !silent ) { owner.add_msg_player_or_npc( rating, - _( "You lose your %s mutation." ), - _( " loses their %s mutation." ), - mdata.name() ); + _( "You lose your %s mutation." ), + _( " loses their %s mutation." ), + mdata.name() ); } mutation_loss_effect( owner, mut ); } From 5a2276aca2af0da86e431fe9edfb913d665f59c1 Mon Sep 17 00:00:00 2001 From: KorGgenT Date: Wed, 16 Oct 2019 18:03:14 -0400 Subject: [PATCH 09/34] fix try_reject_mutagen() --- src/mutation.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/mutation.cpp b/src/mutation.cpp index f158a2d9c49c4..f3ad0acc75ff7 100644 --- a/src/mutation.cpp +++ b/src/mutation.cpp @@ -1250,7 +1250,7 @@ void character_mutations::remove_child_flag( Character &owner, const trait_id &f } } -static mutagen_rejection try_reject_mutagen( player &p, const item &it, bool strong ) +static mutagen_rejection try_reject_mutagen( Character &p, const item &it, bool strong ) { if( p.mutations.has_trait( trait_MUTAGEN_AVOID ) ) { p.add_msg_if_player( m_warning, @@ -1271,8 +1271,8 @@ static mutagen_rejection try_reject_mutagen( player &p, const item &it, bool str if( p.mutations.has_trait( trait_THRESH_MYCUS ) ) { p.add_msg_if_player( m_info, _( "This is a contaminant. We reject it from the Mycus." ) ); - if( p.has_trait( trait_M_SPORES ) || p.has_trait( trait_M_FERTILE ) || - p.has_trait( trait_M_BLOSSOMS ) || p.has_trait( trait_M_BLOOM ) ) { + if( p.mutations.has_trait( trait_M_SPORES ) || p.mutations.has_trait( trait_M_FERTILE ) || + p.mutations.has_trait( trait_M_BLOSSOMS ) || p.mutations.has_trait( trait_M_BLOOM ) ) { p.add_msg_if_player( m_good, _( "We decontaminate it with spores." ) ); g->m.ter_set( p.pos(), t_fungus ); if( p.is_avatar() ) { @@ -1288,7 +1288,7 @@ static mutagen_rejection try_reject_mutagen( player &p, const item &it, bool str } } - if( p.has_trait( trait_THRESH_MARLOSS ) ) { + if( p.mutations.has_trait( trait_THRESH_MARLOSS ) ) { p.add_msg_player_or_npc( m_warning, _( "The %s sears your insides white-hot, and you collapse to the ground!" ), _( " writhes in agony and collapses to the ground!" ), @@ -1299,10 +1299,10 @@ static mutagen_rejection try_reject_mutagen( player &p, const item &it, bool str p.hurtall( rng( 20, 35 ) + ( strong ? 10 : 0 ), nullptr ); // Hope you were eating someplace safe. Mycus v. Goo in your guts is no joke. p.fall_asleep( 5_hours - 1_minutes * ( p.int_cur + ( strong ? 100 : 0 ) ) ); - p.set_mutation( trait_MUTAGEN_AVOID ); + p.mutations.set_mutation( p, trait_MUTAGEN_AVOID ); // Injected mutagen purges marloss, ingested doesn't if( strong ) { - p.unset_mutation( trait_THRESH_MARLOSS ); + p.mutations.unset_mutation( p, trait_THRESH_MARLOSS ); p.add_msg_if_player( m_warning, _( "It was probably that marloss -- how did you know to call it \"marloss\" anyway?" ) ); p.add_msg_if_player( m_warning, _( "Best to stay clear of that alien crap in future." ) ); From c891bc55e140fca4990c681463f227ecc74d5e1d Mon Sep 17 00:00:00 2001 From: KorGgenT Date: Wed, 16 Oct 2019 18:03:44 -0400 Subject: [PATCH 10/34] fix mutagen_common_checks() --- src/mutation.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mutation.cpp b/src/mutation.cpp index f3ad0acc75ff7..01a6fd659db03 100644 --- a/src/mutation.cpp +++ b/src/mutation.cpp @@ -1330,7 +1330,7 @@ static mutagen_rejection try_reject_mutagen( Character &p, const item &it, bool return mutagen_rejection::accepted; } -mutagen_attempt mutagen_common_checks( player &p, const item &it, bool strong, +mutagen_attempt mutagen_common_checks( Character &p, const item &it, bool strong, const mutagen_technique technique ) { g->events().send( p.getID(), technique ); From 25a32ea7248cbbf8ce205fae66b9395c21c0d97c Mon Sep 17 00:00:00 2001 From: KorGgenT Date: Thu, 17 Oct 2019 18:17:06 -0400 Subject: [PATCH 11/34] remove accidentally re-added function declaration --- src/character.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/character.h b/src/character.h index 2b3fa487f131c..9af87bba6fd92 100644 --- a/src/character.h +++ b/src/character.h @@ -1123,9 +1123,6 @@ class Character : public Creature, public visitable bool is_wearing_shoes( const side &which_side = side::BOTH ) const; character_mutations mutations; - - void spores(); - void blossoms(); protected: Character(); Character( Character && ); From d71cdc5d317c1cbd57c842ff7f1ece72932b8951 Mon Sep 17 00:00:00 2001 From: KorGgenT Date: Thu, 17 Oct 2019 18:38:38 -0400 Subject: [PATCH 12/34] fix includes --- src/character_mutations.h | 23 +++++++++++++++++++++-- src/mutation.h | 4 ++-- src/player.h | 5 ----- 3 files changed, 23 insertions(+), 9 deletions(-) diff --git a/src/character_mutations.h b/src/character_mutations.h index af1c4d76894e4..5522ac1c96b34 100644 --- a/src/character_mutations.h +++ b/src/character_mutations.h @@ -2,10 +2,29 @@ #ifndef CHARACTER_MUTATIONS_H #define CHARACTER_MUTATIONS_H -#include "mutation.h" -#include "player.h" +#include "damage.h" #include "type_id.h" +#include +#include +#include +#include + +class Creature; +class Character; +class JsonOut; +class JsonIn; + +enum body_part; + +struct resistances; +struct social_modifiers; + +struct special_attack { + std::string text; + damage_instance damage; +}; + // contains all of the mutation data a character has class character_mutations { diff --git a/src/mutation.h b/src/mutation.h index a38af7610bedc..77b5da9596dbc 100644 --- a/src/mutation.h +++ b/src/mutation.h @@ -12,7 +12,6 @@ #include "bodypart.h" #include "calendar.h" -#include "character.h" #include "damage.h" #include "string_id.h" #include "hash_utils.h" @@ -20,6 +19,7 @@ #include "type_id.h" #include "point.h" +class Character; class nc_color; class JsonObject; class player; @@ -466,7 +466,7 @@ struct mutagen_attempt { int charges_used; }; -mutagen_attempt mutagen_common_checks( player &p, const item &it, bool strong, +mutagen_attempt mutagen_common_checks( Character &p, const item &it, bool strong, mutagen_technique technique ); void test_crossing_threshold( Character &guy, const mutation_category_trait &m_category ); diff --git a/src/player.h b/src/player.h index 59b465f14fc11..c4133df074da7 100644 --- a/src/player.h +++ b/src/player.h @@ -129,11 +129,6 @@ enum class comfort_level { very_comfortable = 10 }; -struct special_attack { - std::string text; - damage_instance damage; -}; - class player_morale; // The maximum level recoil will ever reach. From a8de1ab58fb93403386f1fe41edf5e00141d0cd6 Mon Sep 17 00:00:00 2001 From: KorGgenT Date: Thu, 17 Oct 2019 18:39:58 -0400 Subject: [PATCH 13/34] fix call to get_mutations() --- src/consumption.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/consumption.cpp b/src/consumption.cpp index ac948fe60229a..633f75d1f430d 100644 --- a/src/consumption.cpp +++ b/src/consumption.cpp @@ -334,8 +334,8 @@ time_duration player::vitamin_rate( const vitamin_id &vit ) const { time_duration res = vit.obj().rate(); - for( const auto &m : get_mutations() ) { - const auto &mut = m.obj(); + for( const trait_id &m : mutations.get_mutations() ) { + const mutation_branch &mut = m.obj(); auto iter = mut.vitamin_rates.find( vit ); if( iter != mut.vitamin_rates.end() ) { res += iter->second; @@ -536,7 +536,7 @@ ret_val player::can_eat( const item &food ) const _( "The thought of eating that makes you feel sick." ) ); } - for( const trait_id &mut : get_mutations() ) { + for( const trait_id &mut : mutations.get_mutations() ) { if( !food.made_of_any( mut.obj().can_only_eat ) && !mut.obj().can_only_eat.empty() ) { return ret_val::make_failure( INEDIBLE_MUTATION, _( "You can't eat this." ) ); } From a73a60baadebe318cd7b047489556c5356edbc6e Mon Sep 17 00:00:00 2001 From: KorGgenT Date: Thu, 17 Oct 2019 18:42:31 -0400 Subject: [PATCH 14/34] fix get_trait_data() --- src/character_mutations.cpp | 2 +- src/character_mutations.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/character_mutations.cpp b/src/character_mutations.cpp index cf59d294dcf29..2bb97c935b9b4 100644 --- a/src/character_mutations.cpp +++ b/src/character_mutations.cpp @@ -1,6 +1,6 @@ #include "character_mutations.h" -character_mutations::trait_data &character_mutations::get_trait_data( const trait_id &mut ) const +character_mutations::trait_data &character_mutations::get_trait_data( const trait_id &mut ) { return &my_mutations[mut]; } diff --git a/src/character_mutations.h b/src/character_mutations.h index 5522ac1c96b34..39f2a0efd072c 100644 --- a/src/character_mutations.h +++ b/src/character_mutations.h @@ -151,8 +151,8 @@ class character_mutations int bodytemp_modifier_traits_floor() const; /** Returns an enumeration of visible mutations with colors */ std::string visible_mutations( int visibility_cap ) const; - - trait_data &get_trait_data( const trait_id &mut ) const; + // gets modifiable trait data + trait_data &get_trait_data( const trait_id &mut ); private: /** * Traits / mutations of the character. Key is the mutation id (it's also a valid From e9bb02d9e4f886e20699b2a144ce7278b6ecc04c Mon Sep 17 00:00:00 2001 From: KorGgenT Date: Thu, 17 Oct 2019 18:47:14 -0400 Subject: [PATCH 15/34] get_trait_data --- src/character.cpp | 4 ++-- src/character_mutations.cpp | 12 +++++++++++- src/character_mutations.h | 1 + 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/character.cpp b/src/character.cpp index 6b24ebe59ad38..d98e34502a770 100644 --- a/src/character.cpp +++ b/src/character.cpp @@ -2047,8 +2047,8 @@ std::vector Character::get_overlay_ids() const } // then get mutations - for( const auto &mut : my_mutations ) { - overlay_id = ( mut.second.powered ? "active_" : "" ) + mut.first.str(); + for( const trait_id &mut : mutations.get_mutations() ) { + overlay_id = ( mutations.get_trait_data( mut ).powered ? "active_" : "" ) + mut.first.str(); order = get_overlay_order_of_mutation( overlay_id ); mutation_sorting.insert( std::pair( order, overlay_id ) ); } diff --git a/src/character_mutations.cpp b/src/character_mutations.cpp index 2bb97c935b9b4..6af3eac911108 100644 --- a/src/character_mutations.cpp +++ b/src/character_mutations.cpp @@ -2,5 +2,15 @@ character_mutations::trait_data &character_mutations::get_trait_data( const trait_id &mut ) { - return &my_mutations[mut]; + return my_mutations[mut]; +} + +character_mutations::trait_data character_mutations::get_trait_data( const trait_id &mut ) const +{ + const auto iter = my_mutations.find( mut ); + if( iter == my_mutations.cend() ) { + return trait_data(); + } else { + return iter->second; + } } diff --git a/src/character_mutations.h b/src/character_mutations.h index 39f2a0efd072c..bb7d8877f7954 100644 --- a/src/character_mutations.h +++ b/src/character_mutations.h @@ -153,6 +153,7 @@ class character_mutations std::string visible_mutations( int visibility_cap ) const; // gets modifiable trait data trait_data &get_trait_data( const trait_id &mut ); + trait_data get_trait_data( const trait_id &mut ) const; private: /** * Traits / mutations of the character. Key is the mutation id (it's also a valid From b17060a36d962b534aedef1640b48dea702bdcbc Mon Sep 17 00:00:00 2001 From: KorGgenT Date: Thu, 17 Oct 2019 18:54:56 -0400 Subject: [PATCH 16/34] fix function calls --- src/activity_handlers.cpp | 11 +-- src/character.cpp | 2 +- src/iuse.cpp | 160 +++++++++++++++++++------------------- 3 files changed, 88 insertions(+), 85 deletions(-) diff --git a/src/activity_handlers.cpp b/src/activity_handlers.cpp index 1e2187422f6fb..e3d505a8e85ae 100644 --- a/src/activity_handlers.cpp +++ b/src/activity_handlers.cpp @@ -526,8 +526,9 @@ static void set_up_butchery( player_activity &act, player &u, butcher_type actio // applies to all butchery actions const bool is_human = corpse.id == mtype_id::NULL_ID() || ( corpse.in_species( HUMAN ) && !corpse.in_species( ZOMBIE ) ); - if( is_human && !( u.has_trait_flag( "CANNIBAL" ) || u.has_trait_flag( "PSYCHOPATH" ) || - u.has_trait_flag( "SAPIOVORE" ) ) ) { + if( is_human && !( u.mutations.has_trait_flag( "CANNIBAL" ) || + u.mutations.has_trait_flag( "PSYCHOPATH" ) || + u.mutations.has_trait_flag( "SAPIOVORE" ) ) ) { if( u.is_player() ) { if( query_yn( _( "Would you dare desecrate the mortal remains of a fellow human being?" ) ) ) { @@ -1696,7 +1697,7 @@ void activity_handlers::pickaxe_finish( player_activity *act, player *p ) // Betcha wish you'd opted for the J-Hammer ;P p->mod_stored_nutr( 15 - ( helpersize * 3 ) ); p->mod_thirst( 15 - ( helpersize * 3 ) ); - if( p->has_trait( trait_id( "STOCKY_TROGLO" ) ) ) { + if( p->mutations.has_trait( trait_id( "STOCKY_TROGLO" ) ) ) { p->mod_fatigue( 20 - ( helpersize * 3 ) ); // Yep, dwarves can dig longer before tiring } else { p->mod_fatigue( 30 - ( helpersize * 3 ) ); @@ -4161,7 +4162,7 @@ void activity_handlers::tree_communion_do_turn( player_activity *act, player *p if( act->values.front() > 0 ) { act->values.front() -= 1; if( act->values.front() == 0 ) { - if( p->has_trait( trait_id( "SPIRITUAL" ) ) ) { + if( p->mutations.has_trait( trait_id( "SPIRITUAL" ) ) ) { p->add_msg_if_player( m_good, _( "The ancient tree spirits answer your call." ) ); } else { p->add_msg_if_player( m_good, _( "Your communion with the trees has begun." ) ); @@ -4185,7 +4186,7 @@ void activity_handlers::tree_communion_do_turn( player_activity *act, player *p while( !q.empty() ) { tripoint tpt = q.front(); if( overmap_buffer.reveal( tpt, 3, filter ) ) { - if( p->has_trait( trait_id( "SPIRITUAL" ) ) ) { + if( p->mutations.has_trait( trait_id( "SPIRITUAL" ) ) ) { p->add_morale( MORALE_TREE_COMMUNION, 2, 30, 8_hours, 6_hours ); } else { p->add_morale( MORALE_TREE_COMMUNION, 1, 15, 2_hours, 1_hours ); diff --git a/src/character.cpp b/src/character.cpp index d98e34502a770..984be624217fd 100644 --- a/src/character.cpp +++ b/src/character.cpp @@ -2048,7 +2048,7 @@ std::vector Character::get_overlay_ids() const // then get mutations for( const trait_id &mut : mutations.get_mutations() ) { - overlay_id = ( mutations.get_trait_data( mut ).powered ? "active_" : "" ) + mut.first.str(); + overlay_id = ( mutations.get_trait_data( mut ).powered ? "active_" : "" ) + mut.str(); order = get_overlay_order_of_mutation( overlay_id ); mutation_sorting.insert( std::pair( order, overlay_id ) ); } diff --git a/src/iuse.cpp b/src/iuse.cpp index 1e6a9a25509c3..11b1667519814 100644 --- a/src/iuse.cpp +++ b/src/iuse.cpp @@ -354,7 +354,7 @@ int iuse::sewage( player *p, item *it, bool, const tripoint & ) g->events().send(); p->vomit(); if( one_in( 4 ) ) { - p->mutate(); + p->mutations.mutate( *p ); } return it->type->charges_to_use(); } @@ -405,7 +405,7 @@ static int alcohol( player &p, const item &it, const int strength ) /** @EFFECT_STR_MAX reduces drunkenness duration */ time_duration duration = alc_strength( strength, 34_minutes, 68_minutes, 90_minutes ) - ( alc_strength( strength, 36_seconds, 1_minutes, 72_seconds ) * p.str_max ); - if( p.has_trait( trait_ALCMET ) ) { + if( p.mutations.has_trait( trait_ALCMET ) ) { duration = alc_strength( strength, 9_minutes, 18_minutes, 25_minutes ) - ( alc_strength( strength, 36_seconds, 1_minutes, 1_minutes ) * p.str_max ); // Metabolizing the booze improves the nutritional value; @@ -413,9 +413,9 @@ static int alcohol( player &p, const item &it, const int strength ) p.stomach.mod_nutr( -( abs( it.get_comestible() ? it.type->comestible->stim : 0 ) ) ); // Metabolizing it cancels out the depressant p.stim += abs( it.get_comestible() ? it.get_comestible()->stim : 0 ); - } else if( p.has_trait( trait_TOLERANCE ) ) { + } else if( p.mutations.has_trait( trait_TOLERANCE ) ) { duration -= alc_strength( strength, 12_minutes, 30_minutes, 45_minutes ); - } else if( p.has_trait( trait_LIGHTWEIGHT ) ) { + } else if( p.mutations.has_trait( trait_LIGHTWEIGHT ) ) { duration += alc_strength( strength, 12_minutes, 30_minutes, 45_minutes ); } p.add_effect( effect_drunk, duration ); @@ -666,7 +666,7 @@ int iuse::antiparasitic( player *p, item *it, bool, const tripoint & ) if( p->has_effect( effect_tapeworm ) ) { p->remove_effect( effect_tapeworm ); p->guts.mod_nutr( -1 ); // You just digested the tapeworm. - if( p->has_trait( trait_NOPAIN ) ) { + if( p->mutations.has_trait( trait_NOPAIN ) ) { p->add_msg_if_player( m_good, _( "Your bowels clench as something inside them dies." ) ); } else { p->add_msg_if_player( m_mixed, _( "Your bowels spasm painfully as something inside them dies." ) ); @@ -679,7 +679,7 @@ int iuse::antiparasitic( player *p, item *it, bool, const tripoint & ) } if( p->has_effect( effect_brainworms ) ) { p->remove_effect( effect_brainworms ); - if( p->has_trait( trait_NOPAIN ) ) { + if( p->mutations.has_trait( trait_NOPAIN ) ) { p->add_msg_if_player( m_good, _( "The pressure inside your head feels better already." ) ); } else { p->add_msg_if_player( m_mixed, @@ -689,7 +689,7 @@ int iuse::antiparasitic( player *p, item *it, bool, const tripoint & ) } if( p->has_effect( effect_paincysts ) ) { p->remove_effect( effect_paincysts ); - if( p->has_trait( trait_NOPAIN ) ) { + if( p->mutations.has_trait( trait_NOPAIN ) ) { p->add_msg_if_player( m_good, _( "The stiffness in your joints goes away." ) ); } else { p->add_msg_if_player( m_good, _( "The pain in your joints goes away." ) ); @@ -703,10 +703,10 @@ int iuse::anticonvulsant( player *p, item *it, bool, const tripoint & ) p->add_msg_if_player( _( "You take some anticonvulsant medication." ) ); /** @EFFECT_STR reduces duration of anticonvulsant medication */ time_duration duration = 8_hours - p->str_cur * rng( 0_turns, 10_minutes ); - if( p->has_trait( trait_TOLERANCE ) ) { + if( p->mutations.has_trait( trait_TOLERANCE ) ) { duration -= 1_hours; } - if( p->has_trait( trait_LIGHTWEIGHT ) ) { + if( p->mutations.has_trait( trait_LIGHTWEIGHT ) ) { duration += 2_hours; } p->add_effect( effect_valium, duration ); @@ -724,10 +724,10 @@ int iuse::weed_cake( player *p, item *it, bool, const tripoint & ) p->add_msg_if_player( _( "You start scarfing down the delicious cake. It tastes a little funny though…" ) ); time_duration duration = 12_minutes; - if( p->has_trait( trait_TOLERANCE ) ) { + if( p->mutations.has_trait( trait_TOLERANCE ) ) { duration = 9_minutes; } - if( p->has_trait( trait_LIGHTWEIGHT ) ) { + if( p->mutations.has_trait( trait_LIGHTWEIGHT ) ) { duration = 15_minutes; } p->mod_hunger( 2 ); @@ -748,10 +748,10 @@ int iuse::coke( player *p, item *it, bool, const tripoint & ) p->add_msg_if_player( _( "You snort a bump of coke." ) ); /** @EFFECT_STR reduces duration of coke */ time_duration duration = 20_minutes - 1_seconds * p->str_cur + rng( 0_minutes, 1_minutes ); - if( p->has_trait( trait_TOLERANCE ) ) { + if( p->mutations.has_trait( trait_TOLERANCE ) ) { duration -= 1_minutes; // Symmetry would cause problems :-/ } - if( p->has_trait( trait_LIGHTWEIGHT ) ) { + if( p->mutations.has_trait( trait_LIGHTWEIGHT ) ) { duration += 2_minutes; } p->mod_hunger( -8 ); @@ -767,10 +767,10 @@ int iuse::meth( player *p, item *it, bool, const tripoint & ) p->add_msg_if_player( m_neutral, _( "You smoke your meth." ) ); p->add_msg_if_player( m_good, _( "The world seems to sharpen." ) ); p->mod_fatigue( -375 ); - if( p->has_trait( trait_TOLERANCE ) ) { + if( p->mutations.has_trait( trait_TOLERANCE ) ) { duration *= 1.2; } else { - duration *= ( p->has_trait( trait_LIGHTWEIGHT ) ? 1.8 : 1.5 ); + duration *= ( p->mutations.has_trait( trait_LIGHTWEIGHT ) ? 1.8 : 1.5 ); } // breathe out some smoke for( int i = 0; i < 3; i++ ) { @@ -820,7 +820,7 @@ int iuse::flu_vaccine( player *p, item *it, bool, const tripoint & ) int iuse::poison( player *p, item *it, bool, const tripoint & ) { - if( ( p->has_trait( trait_EATDEAD ) ) ) { + if( ( p->mutations.has_trait( trait_EATDEAD ) ) ) { return it->type->charges_to_use(); } @@ -832,7 +832,7 @@ int iuse::poison( player *p, item *it, bool, const tripoint & ) return 0; } /** @EFFECT_STR increases EATPOISON trait effectiveness (50-90%) */ - if( ( p->has_trait( trait_EATPOISON ) ) && ( !( one_in( p->str_cur / 2 ) ) ) ) { + if( ( p->mutations.has_trait( trait_EATPOISON ) ) && ( !( one_in( p->str_cur / 2 ) ) ) ) { return it->type->charges_to_use(); } p->add_effect( effect_poison, 1_hours ); @@ -849,7 +849,7 @@ int iuse::meditate( player *p, item *it, bool t, const tripoint & ) p->add_msg_if_player( m_info, _( "You cannot do that while mounted." ) ); return 0; } - if( p->has_trait( trait_SPIRITUAL ) ) { + if( p->mutations.has_trait( trait_SPIRITUAL ) ) { const int moves = to_moves( 20_minutes ); p->assign_activity( activity_id( "ACT_MEDITATE" ), moves ); } else { @@ -914,7 +914,7 @@ int iuse::datura( player *p, item *it, bool, const tripoint & ) p->add_effect( effect_datura, rng( 3_hours, 13_hours ) ); p->add_msg_if_player( _( "You eat the datura seed." ) ); - if( p->has_trait( trait_SPIRITUAL ) ) { + if( p->mutations.has_trait( trait_SPIRITUAL ) ) { p->add_morale( MORALE_FOOD_GOOD, 36, 72, 2_hours, 1_hours, false, it->type ); } return it->type->charges_to_use(); @@ -984,8 +984,8 @@ int iuse::blech( player *p, item *it, bool, const tripoint & ) } } - if( it->has_flag( "ACID" ) && ( p->has_trait( trait_ACIDPROOF ) || - p->has_trait( trait_ACIDBLOOD ) ) ) { + if( it->has_flag( "ACID" ) && ( p->mutations.has_trait( trait_ACIDPROOF ) || + p->mutations.has_trait( trait_ACIDBLOOD ) ) ) { p->add_msg_if_player( m_bad, _( "Blech, that tastes gross!" ) ); //reverse the harmful values of drinking this acid. double multiplier = -1; @@ -1008,9 +1008,9 @@ int iuse::blech( player *p, item *it, bool, const tripoint & ) int iuse::plantblech( player *p, item *it, bool, const tripoint &pos ) { - if( p->has_trait( trait_THRESH_PLANT ) ) { + if( p->mutations.has_trait( trait_THRESH_PLANT ) ) { double multiplier = -1; - if( p->has_trait( trait_CHLOROMORPH ) ) { + if( p->mutations.has_trait( trait_CHLOROMORPH ) ) { multiplier = -3; p->add_msg_if_player( m_good, _( "The meal is revitalizing." ) ); } else { @@ -1041,7 +1041,7 @@ static void do_purify( player &p ) { std::vector valid; // Which flags the player has for( auto &traits_iter : mutation_branch::get_all() ) { - if( p.has_trait( traits_iter.id ) && !p.has_base_trait( traits_iter.id ) ) { + if( p.mutations.has_trait( traits_iter.id ) && !p.mutations.has_base_trait( traits_iter.id ) ) { //Looks for active mutation valid.push_back( traits_iter.id ); } @@ -1054,8 +1054,8 @@ static void do_purify( player &p ) num_cured = std::min( 4, num_cured ); for( int i = 0; i < num_cured && !valid.empty(); i++ ) { const trait_id id = random_entry_removed( valid ); - if( p.purifiable( id ) ) { - p.remove_mutation( id ); + if( p.mutations.purifiable( id ) ) { + p.mutations.remove_mutation( p, id ); } else { p.add_msg_if_player( m_warning, _( "You feel a slight itching inside, but it passes." ) ); } @@ -1084,7 +1084,7 @@ int iuse::purify_iv( player *p, item *it, bool, const tripoint & ) std::vector valid; // Which flags the player has for( auto &traits_iter : mutation_branch::get_all() ) { - if( p->has_trait( traits_iter.id ) && !p->has_base_trait( traits_iter.id ) ) { + if( p->mutations.has_trait( traits_iter.id ) && !p->mutations.has_base_trait( traits_iter.id ) ) { //Looks for active mutation valid.push_back( traits_iter.id ); } @@ -1100,12 +1100,12 @@ int iuse::purify_iv( player *p, item *it, bool, const tripoint & ) } for( int i = 0; i < num_cured && !valid.empty(); i++ ) { const trait_id id = random_entry_removed( valid ); - if( p->purifiable( id ) ) { - p->remove_mutation( id ); + if( p->mutations.purifiable( id ) ) { + p->mutations.remove_mutation( *p, id ); } else { p->add_msg_if_player( m_warning, _( "You feel a distinct burning inside, but it passes." ) ); } - if( !( p->has_trait( trait_NOPAIN ) ) ) { + if( !( p->mutations.has_trait( trait_NOPAIN ) ) ) { p->mod_pain( 2 * num_cured ); //Hurts worse as it fixes more p->add_msg_if_player( m_warning, _( "Feels like you're on fire, but you're OK." ) ); } @@ -1127,9 +1127,9 @@ int iuse::purify_smart( player *p, item *it, bool, const tripoint & ) std::vector valid; // Which flags the player has std::vector valid_names; // Which flags the player has for( auto &traits_iter : mutation_branch::get_all() ) { - if( p->has_trait( traits_iter.id ) && - !p->has_base_trait( traits_iter.id ) && - p->purifiable( traits_iter.id ) ) { + if( p->mutations.has_trait( traits_iter.id ) && + !p->mutations.has_base_trait( traits_iter.id ) && + p->mutations.purifiable( traits_iter.id ) ) { //Looks for active mutation valid.push_back( traits_iter.id ); valid_names.push_back( traits_iter.id->name() ); @@ -1148,15 +1148,15 @@ int iuse::purify_smart( player *p, item *it, bool, const tripoint & ) p->add_msg_if_player( _( "You inject the purifier. The liquid thrashes inside the tube and goes down reluctantly." ) ); - p->remove_mutation( valid[mutation_index] ); + p->mutations.remove_mutation( *p, valid[mutation_index] ); valid.erase( valid.begin() + mutation_index ); // and one or two more untargeted purifications. if( !valid.empty() ) { - p->remove_mutation( random_entry_removed( valid ) ); + p->mutations.remove_mutation( *p, random_entry_removed( valid ) ); } if( !valid.empty() && one_in( 2 ) ) { - p->remove_mutation( random_entry_removed( valid ) ); + p->mutations.remove_mutation( *p, random_entry_removed( valid ) ); } p->mod_pain( 3 ); @@ -1197,7 +1197,7 @@ static void marloss_common( player &p, item &it, const trait_id ¤t_color ) } }; - if( p.has_trait( current_color ) || p.has_trait( trait_THRESH_MARLOSS ) ) { + if( p.mutations.has_trait( current_color ) || p.mutations.has_trait( trait_THRESH_MARLOSS ) ) { p.add_msg_if_player( m_good, _( "As you eat the %s, you have a near-religious experience, feeling at one with your surroundings…" ), it.tname() ); @@ -1215,7 +1215,7 @@ static void marloss_common( player &p, item &it, const trait_id ¤t_color ) int marloss_count = std::count_if( mycus_colors.begin(), mycus_colors.end(), [&p]( const std::pair &pr ) { - return p.has_trait( pr.first ); + return p.mutations.has_trait( pr.first ); } ); /* If we're not already carriers of current type of Marloss, roll for a random effect: @@ -1232,7 +1232,7 @@ static void marloss_common( player &p, item &it, const trait_id ¤t_color ) int effect = rng( 1, 12 ); if( effect <= 3 ) { p.add_msg_if_player( _( "It tastes extremely strange!" ) ); - p.mutate(); + p.mutations.mutate( p ); // Gruss dich, mutation drain, missed you! p.mod_pain( 2 * rng( 1, 5 ) ); p.mod_stored_nutr( 10 ); @@ -1278,22 +1278,22 @@ static void marloss_common( player &p, item &it, const trait_id ¤t_color ) p.fall_asleep( 10_hours - p.int_cur * 1_minutes ); // Hope you were eating someplace safe. Mycus v. Goo in your guts is no joke. for( const std::pair &pr : mycus_colors ) { - p.unset_mutation( pr.first ); + p.mutations.unset_mutation( p, pr.first ); p.rem_addiction( pr.second ); } - p.set_mutation( - trait_MARLOSS_AVOID ); // And if you survive it's etched in your RNA, so you're unlikely to repeat the experiment. + p.mutations.set_mutation( p, + trait_MARLOSS_AVOID ); // And if you survive it's etched in your RNA, so you're unlikely to repeat the experiment. } else if( marloss_count >= 2 ) { p.add_msg_if_player( m_bad, _( "You feel a familiar warmth, but suddenly it surges into painful burning as you convulse and collapse to the ground…" ) ); /** @EFFECT_INT reduces sleep duration when eating wrong color marloss */ p.fall_asleep( 40_minutes - 1_minutes * p.int_cur / 2 ); for( const std::pair &pr : mycus_colors ) { - p.unset_mutation( pr.first ); + p.mutations.unset_mutation( p, pr.first ); p.rem_addiction( pr.second ); } - p.set_mutation( trait_THRESH_MARLOSS ); + p.mutations.set_mutation( p, trait_THRESH_MARLOSS ); g->m.ter_set( p.pos(), t_marloss ); g->events().send( p.getID() ); p.add_msg_if_player( m_good, @@ -1302,8 +1302,8 @@ static void marloss_common( player &p, item &it, const trait_id ¤t_color ) //~ Beginning to hear the Mycus while conscious: that's it speaking _( "unity. together we have reached the door. we provide the final key. now to pass through…" ) ); } else { - p.add_msg_if_player( _( "You feel a strange warmth spreading throughout your body…" ) ); - p.set_mutation( current_color ); + p.add_msg_if_player( _( "You feel a strange warmth spreading throughout your body..." ) ); + p.mutations.set_mutation( p, current_color ); // Give us addictions to the other two colors, but cure one for current color for( const std::pair &pr : mycus_colors ) { if( pr.first == current_color ) { @@ -1320,13 +1320,13 @@ static bool marloss_prevented( const player &p ) if( p.is_npc() ) { return true; } - if( p.has_trait( trait_MARLOSS_AVOID ) ) { + if( p.mutations.has_trait( trait_MARLOSS_AVOID ) ) { p.add_msg_if_player( m_warning, //~ "Uh-uh" is a sound used for "nope", "no", etc. _( "After what happened that last time? uh-uh. You're not eating that alien poison." ) ); return true; } - if( p.has_trait( trait_THRESH_MYCUS ) ) { + if( p.mutations.has_trait( trait_THRESH_MYCUS ) ) { p.add_msg_if_player( m_info, _( "We no longer require this scaffolding. We reserve it for other uses." ) ); return true; @@ -1382,7 +1382,7 @@ int iuse::mycus( player *p, item *it, bool t, const tripoint &pos ) return it->type->charges_to_use(); } // Welcome our guide. Welcome. To. The Mycus. - if( p->has_trait( trait_THRESH_MARLOSS ) ) { + if( p->mutations.has_trait( trait_THRESH_MARLOSS ) ) { g->events().send( p->getID() ); p->add_msg_if_player( m_neutral, _( "It tastes amazing, and you finish it quickly." ) ); @@ -1398,8 +1398,8 @@ int iuse::mycus( player *p, item *it, bool t, const tripoint &pos ) _( "Your eyes roll back in your head. Everything dissolves into a blissful haze…" ) ); /** @EFFECT_INT slightly reduces sleep duration when eating mycus */ p->fall_asleep( 5_hours - p->int_cur * 1_minutes ); - p->unset_mutation( trait_THRESH_MARLOSS ); - p->set_mutation( trait_THRESH_MYCUS ); + p->mutations.unset_mutation( *p, trait_THRESH_MARLOSS ); + p->mutations.set_mutation( *p, trait_THRESH_MYCUS ); g->refresh_all(); //~ The Mycus does not use the term (or encourage the concept of) "you". The PC is a local/native organism, but is now the Mycus. //~ It still understands the concept, but uninitelligent fungaloids and mind-bent symbiotes should not need it. @@ -1441,23 +1441,23 @@ int iuse::mycus( player *p, item *it, bool t, const tripoint &pos ) p->rem_addiction( ADD_MARLOSS_R ); p->rem_addiction( ADD_MARLOSS_B ); p->rem_addiction( ADD_MARLOSS_Y ); - } else if( p->has_trait( trait_THRESH_MYCUS ) && - !p->has_trait( trait_M_DEPENDENT ) ) { // OK, now set the hook. + } else if( p->mutations.has_trait( trait_THRESH_MYCUS ) && + !p->mutations.has_trait( trait_M_DEPENDENT ) ) { // OK, now set the hook. if( !one_in( 3 ) ) { - p->mutate_category( "MYCUS" ); + p->mutations.mutate_category( *p, "MYCUS" ); p->mod_stored_nutr( 10 ); p->mod_thirst( 10 ); p->mod_fatigue( 5 ); p->add_morale( MORALE_MARLOSS, 25, 200 ); // still covers up mutation pain } - } else if( p->has_trait( trait_THRESH_MYCUS ) ) { + } else if( p->mutations.has_trait( trait_THRESH_MYCUS ) ) { p->mod_painkiller( 5 ); p->stim += 5; } else { // In case someone gets one without having been adapted first. // Marloss is the Mycus' method of co-opting humans. Mycus fruit is for symbiotes' maintenance and development. p->add_msg_if_player( - _( "This tastes really weird! You're not sure it's good for you…" ) ); - p->mutate(); + _( "This tastes really weird! You're not sure it's good for you..." ) ); + p->mutations.mutate( *p ); p->mod_pain( 2 * rng( 1, 5 ) ); p->mod_stored_nutr( 10 ); p->mod_thirst( 10 ); @@ -2026,8 +2026,8 @@ static int cauterize_elec( player &p, item &it ) p.add_msg_if_player( m_info, _( "You need batteries to cauterize wounds." ) ); return 0; } else if( !p.has_effect( effect_bite ) && !p.has_effect( effect_bleed ) && !p.is_underwater() ) { - if( ( p.has_trait( trait_MASOCHIST ) || p.has_trait( trait_MASOCHIST_MED ) || - p.has_trait( trait_CENOBITE ) ) && + if( ( p.mutations.has_trait( trait_MASOCHIST ) || p.mutations.has_trait( trait_MASOCHIST_MED ) || + p.mutations.has_trait( trait_CENOBITE ) ) && p.query_yn( _( "Cauterize yourself for fun?" ) ) ) { return cauterize_actor::cauterize_effect( p, it, true ) ? it.type->charges_to_use() : 0; } else { @@ -2742,19 +2742,20 @@ int iuse::dig( player *p, item *it, bool t, const tripoint & ) } if( grave ) { - if( g->u.has_trait( trait_id( "SPIRITUAL" ) ) && !g->u.has_trait( trait_id( "PSYCHOPATH" ) ) && + if( g->u.mutations.has_trait( trait_id( "SPIRITUAL" ) ) && + !g->u.mutations.has_trait( trait_id( "PSYCHOPATH" ) ) && g->u.query_yn( _( "Would you really touch the sacred resting place of the dead?" ) ) ) { add_msg( m_info, _( "Exhuming a grave is really against your beliefs." ) ); g->u.add_morale( MORALE_GRAVEDIGGER, -50, -100, 48_hours, 12_hours ); if( one_in( 3 ) ) { g->u.vomit(); } - } else if( g->u.has_trait( trait_id( "PSYCHOPATH" ) ) ) { + } else if( g->u.mutations.has_trait( trait_id( "PSYCHOPATH" ) ) ) { p->add_msg_if_player( m_good, _( "Exhuming a grave is fun now, where there is no one to object." ) ); g->u.add_morale( MORALE_GRAVEDIGGER, 25, 50, 2_hours, 1_hours ); - } else if( !g->u.has_trait( trait_id( "EATDEAD" ) ) && - !g->u.has_trait( trait_id( "SAPROVORE" ) ) ) { + } else if( !g->u.mutations.has_trait( trait_id( "EATDEAD" ) ) && + !g->u.mutations.has_trait( trait_id( "SAPROVORE" ) ) ) { p->add_msg_if_player( m_bad, _( "Exhuming this grave is utterly disgusting!" ) ); g->u.add_morale( MORALE_GRAVEDIGGER, -25, -50, 2_hours, 1_hours ); if( one_in( 5 ) ) { @@ -4322,7 +4323,7 @@ int iuse::portable_game( player *p, item *it, bool, const tripoint & ) p->add_msg_if_player( m_info, _( "You can't do that while underwater." ) ); return 0; } - if( p->has_trait( trait_ILLITERATE ) ) { + if( p->mutations.has_trait( trait_ILLITERATE ) ) { p->add_msg_if_player( m_info, _( "You're illiterate!" ) ); return 0; } else if( it->ammo_remaining() < 15 ) { @@ -4436,7 +4437,7 @@ int iuse::vibe( player *p, item *it, bool, const tripoint & ) p->add_msg_if_player( m_info, _( "You cannot do… that while mounted." ) ); return 0; } - if( ( p->is_underwater() ) && ( !( ( p->has_trait( trait_GILLS ) ) || + if( ( p->is_underwater() ) && ( !( ( p->mutations.has_trait( trait_GILLS ) ) || ( p->is_wearing( "rebreather_on" ) ) || ( p->is_wearing( "rebreather_xl_on" ) ) || ( p->is_wearing( "mask_h20survivor_on" ) ) ) ) ) { p->add_msg_if_player( m_info, _( "It's waterproof, but oxygen maybe?" ) ); @@ -4564,7 +4565,7 @@ int iuse::blood_draw( player *p, item *it, bool, const tripoint & ) if( !drew_blood && query_yn( _( "Draw your own blood?" ) ) ) { p->add_msg_if_player( m_info, _( "You drew your own blood…" ) ); drew_blood = true; - if( p->has_trait( trait_ACIDBLOOD ) ) { + if( p->mutations.has_trait( trait_ACIDBLOOD ) ) { acid_blood = true; } p->mod_stored_nutr( 10 ); @@ -5308,7 +5309,7 @@ int iuse::artifact( player *p, item *it, bool, const tripoint & ) case AEA_MUTATE: if( !one_in( 3 ) ) { - p->mutate(); + p->mutations.mutate( *p ); } break; @@ -5551,8 +5552,9 @@ int iuse::hotplate( player *p, item *it, bool, const tripoint & ) int choice = 0; if( ( p->has_effect( effect_bite ) || p->has_effect( effect_bleed ) || - p->has_trait( trait_MASOCHIST ) || - p->has_trait( trait_MASOCHIST_MED ) || p->has_trait( trait_CENOBITE ) ) && !p->is_underwater() ) { + p->mutations.has_trait( trait_MASOCHIST ) || + p->mutations.has_trait( trait_MASOCHIST_MED ) || p->mutations.has_trait( trait_CENOBITE ) ) && + !p->is_underwater() ) { //Might want to cauterize choice = uilist( _( "Using hotplate:" ), { _( "Heat food" ), _( "Cauterize wound" ) @@ -5809,8 +5811,8 @@ int iuse::contacts( player *p, item *it, bool, const tripoint & ) p->add_msg_if_player( _( "You don't do anything with your %s." ), it->tname() ); return 0; } - } else if( p->has_trait( trait_HYPEROPIC ) || p->has_trait( trait_MYOPIC ) || - p->has_trait( trait_URSINE_EYE ) ) { + } else if( p->mutations.has_trait( trait_HYPEROPIC ) || p->mutations.has_trait( trait_MYOPIC ) || + p->mutations.has_trait( trait_URSINE_EYE ) ) { p->moves -= to_moves( 20_seconds ); p->add_msg_if_player( _( "You put the %s in your eyes." ), it->tname() ); p->add_effect( effect_contacts, duration ); @@ -6103,12 +6105,12 @@ int iuse::robotcontrol( player *p, item *it, bool, const tripoint & ) return 0; } - if( p->has_trait( trait_ILLITERATE ) ) { + if( p->mutations.has_trait( trait_ILLITERATE ) ) { p->add_msg_if_player( _( "You cannot read a computer screen." ) ); return 0; } - if( p->has_trait( trait_HYPEROPIC ) && !p->worn_with_flag( "FIX_FARSIGHT" ) && + if( p->mutations.has_trait( trait_HYPEROPIC ) && !p->worn_with_flag( "FIX_FARSIGHT" ) && !p->has_effect( effect_contacts ) && !p->has_bionic( bionic_id( "bio_eye_optic" ) ) ) { p->add_msg_if_player( m_info, _( "You'll need to put on reading glasses before you can see the screen." ) ); @@ -6475,11 +6477,11 @@ int iuse::einktabletpc( player *p, item *it, bool t, const tripoint &pos ) p->add_msg_if_player( m_info, _( "You can't do that while underwater." ) ); return 0; } - if( p->has_trait( trait_ILLITERATE ) ) { + if( p->mutations.has_trait( trait_ILLITERATE ) ) { p->add_msg_if_player( m_info, _( "You cannot read a computer screen." ) ); return 0; } - if( p->has_trait( trait_HYPEROPIC ) && !p->worn_with_flag( "FIX_FARSIGHT" ) && + if( p->mutations.has_trait( trait_HYPEROPIC ) && !p->worn_with_flag( "FIX_FARSIGHT" ) && !p->has_effect( effect_contacts ) && !p->has_bionic( bionic_id( "bio_eye_optic" ) ) ) { p->add_msg_if_player( m_info, _( "You'll need to put on reading glasses before you can see the screen." ) ); @@ -6548,7 +6550,7 @@ int iuse::einktabletpc( player *p, item *it, bool t, const tripoint &pos ) p->moves -= to_moves( rng( 3_seconds, 7_seconds ) ); - if( p->has_trait( trait_PSYCHOPATH ) ) { + if( p->mutations.has_trait( trait_PSYCHOPATH ) ) { p->add_msg_if_player( m_info, _( "Wasted time, these pictures do not provoke your senses." ) ); } else { p->add_morale( MORALE_PHOTOS, rng( 15, 30 ), 100 ); @@ -8333,7 +8335,7 @@ int iuse::remoteveh( player *p, item *it, bool t, const tripoint &pos ) } if( choice == 0 ) { - if( g->u.has_trait( trait_id( "WAYFARER" ) ) ) { + if( g->u.mutations.has_trait( trait_id( "WAYFARER" ) ) ) { add_msg( m_info, _( "Despite using a controller, you still refuse to take control of this vehicle." ) ); } else { @@ -8562,7 +8564,7 @@ int iuse::multicooker( player *p, item *it, bool t, const tripoint &pos ) return 0; } - if( p->has_trait( trait_ILLITERATE ) ) { + if( p->mutations.has_trait( trait_ILLITERATE ) ) { p->add_msg_if_player( m_info, _( "You cannot read, and don't understand the screen or the buttons!" ) ); return 0; @@ -8574,7 +8576,7 @@ int iuse::multicooker( player *p, item *it, bool t, const tripoint &pos ) } } - if( p->has_trait( trait_HYPEROPIC ) && !p->worn_with_flag( "FIX_FARSIGHT" ) && + if( p->mutations.has_trait( trait_HYPEROPIC ) && !p->worn_with_flag( "FIX_FARSIGHT" ) && !p->has_effect( effect_contacts ) ) { p->add_msg_if_player( m_info, _( "You'll need to put on reading glasses before you can see the screen." ) ); From 0397d5de2be7f29e8e807b1900ba91b3f8668d58 Mon Sep 17 00:00:00 2001 From: KorGgenT Date: Thu, 17 Oct 2019 18:58:59 -0400 Subject: [PATCH 17/34] get_cat_level() --- src/character_mutations.cpp | 11 +++++++++++ src/character_mutations.h | 2 ++ src/iuse.cpp | 6 +----- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/character_mutations.cpp b/src/character_mutations.cpp index 6af3eac911108..ceb26a8952b17 100644 --- a/src/character_mutations.cpp +++ b/src/character_mutations.cpp @@ -14,3 +14,14 @@ character_mutations::trait_data character_mutations::get_trait_data( const trait return iter->second; } } + +int character_mutations::get_cat_level( const std::string &category ) const +{ + const auto iter = mutation_category_level.find( category ); + if ( iter == mutation_category_level.cend() ) + { + return 0; + } else { + return iter->second; + } +} diff --git a/src/character_mutations.h b/src/character_mutations.h index bb7d8877f7954..1a7e07640978d 100644 --- a/src/character_mutations.h +++ b/src/character_mutations.h @@ -154,6 +154,8 @@ class character_mutations // gets modifiable trait data trait_data &get_trait_data( const trait_id &mut ); trait_data get_trait_data( const trait_id &mut ) const; + + int get_cat_level( const std::string &category ) const; private: /** * Traits / mutations of the character. Key is the mutation id (it's also a valid diff --git a/src/iuse.cpp b/src/iuse.cpp index 11b1667519814..772972b045655 100644 --- a/src/iuse.cpp +++ b/src/iuse.cpp @@ -6065,11 +6065,7 @@ int iuse::bell( player *p, item *it, bool, const tripoint & ) sounds::sound( p->pos(), 12, sounds::sound_t::music, _( "Clank! Clank!" ), true, "misc", "cow_bell" ); if( !p->is_deaf() ) { - const int cow_factor = 1 + ( p->mutation_category_level.find( "CATTLE" ) == - p->mutation_category_level.end() ? - 0 : - ( p->mutation_category_level.find( "CATTLE" )->second ) / 8 - ); + const int cow_factor = 1 + p->mutations.get_cat_level( "CATTLE" ) / 8; if( x_in_y( cow_factor, 1 + cow_factor ) ) { p->add_morale( MORALE_MUSIC, 1, 15 * ( cow_factor > 10 ? 10 : cow_factor ) ); } From e05b17f28cbb98203bd20f7747d1c03ca0e8173d Mon Sep 17 00:00:00 2001 From: KorGgenT Date: Thu, 17 Oct 2019 19:11:39 -0400 Subject: [PATCH 18/34] fix function calls --- src/newcharacter.cpp | 80 ++++++++++++++++++++++---------------------- 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/src/newcharacter.cpp b/src/newcharacter.cpp index 8d3beb02cfdcf..be3a90077fc11 100644 --- a/src/newcharacter.cpp +++ b/src/newcharacter.cpp @@ -283,8 +283,8 @@ void avatar::randomize( const bool random_scenario, points_left &points, bool pl int num_btraits = 0; int tries = 0; add_traits( points ); // adds mandatory profession/scenario traits. - for( const auto &mut : my_mutations ) { - const mutation_branch &mut_info = mut.first.obj(); + for( const trait_id &mut : mutations.get_mutations() ) { + const mutation_branch &mut_info = mut.obj(); if( mut_info.profession ) { continue; } @@ -308,11 +308,11 @@ void avatar::randomize( const bool random_scenario, points_left &points, bool pl do { rn = random_bad_trait(); tries++; - } while( ( has_trait( rn ) || num_btraits - rn->points > max_trait_points ) && + } while( ( mutations.has_trait( rn ) || num_btraits - rn->points > max_trait_points ) && tries < 5 ); - if( tries < 5 && !has_conflicting_trait( rn ) ) { - toggle_trait( rn ); + if( tries < 5 && !mutations.has_conflicting_trait( rn ) ) { + mutations.toggle_trait( *this, rn ); points.trait_points -= rn->points; num_btraits -= rn->points; } @@ -360,9 +360,9 @@ void avatar::randomize( const bool random_scenario, points_left &points, bool pl if( allow_traits ) { rn = random_good_trait(); auto &mdata = rn.obj(); - if( !has_trait( rn ) && points.trait_points_left() >= mdata.points && - num_gtraits + mdata.points <= max_trait_points && !has_conflicting_trait( rn ) ) { - toggle_trait( rn ); + if( !mutations.has_trait( rn ) && points.trait_points_left() >= mdata.points && + num_gtraits + mdata.points <= max_trait_points && !mutations.has_conflicting_trait( rn ) ) { + mutations.toggle_trait( *this, rn ); points.trait_points -= mdata.points; num_gtraits += mdata.points; } @@ -550,10 +550,10 @@ bool avatar::create( character_type type, const std::string &tempname ) hp_cur[i] = hp_max[i]; } - if( has_trait( trait_id( "SMELLY" ) ) ) { + if( mutations.has_trait( trait_id( "SMELLY" ) ) ) { scent = 800; } - if( has_trait( trait_id( "WEAKSCENT" ) ) ) { + if( mutations.has_trait( trait_id( "WEAKSCENT" ) ) ) { scent = 300; } @@ -568,13 +568,13 @@ bool avatar::create( character_type type, const std::string &tempname ) // setup staring bank money cash = rng( -200000, 200000 ); - if( has_trait( trait_id( "XS" ) ) ) { + if( mutations.has_trait( trait_id( "XS" ) ) ) { set_stored_kcal( 10000 ); - toggle_trait( trait_id( "XS" ) ); + mutations.toggle_trait( *this, trait_id( "XS" ) ); } - if( has_trait( trait_id( "XXXL" ) ) ) { + if( mutations.has_trait( trait_id( "XXXL" ) ) ) { set_stored_kcal( 125000 ); - toggle_trait( trait_id( "XXXL" ) ); + mutations.toggle_trait( *this, trait_id( "XXXL" ) ); } // Learn recipes @@ -587,7 +587,7 @@ bool avatar::create( character_type type, const std::string &tempname ) for( mtype_id elem : prof->pets() ) { starting_pets.push_back( elem ); } - std::list prof_items = prof->items( male, get_mutations() ); + std::list prof_items = prof->items( male, mutations.get_mutations() ); for( item &it : prof_items ) { if( it.has_flag( "WET" ) ) { @@ -628,7 +628,7 @@ bool avatar::create( character_type type, const std::string &tempname ) // Adjust current energy level to maximum set_power_level( get_max_power_level() ); - for( auto &t : get_base_traits() ) { + for( const trait_id &t : mutations.get_base_traits() ) { std::vector styles; for( auto &s : t->initial_ma_styles ) { if( !has_martialart( s ) ) { @@ -645,10 +645,10 @@ bool avatar::create( character_type type, const std::string &tempname ) } // Activate some mutations right from the start. - for( const trait_id &mut : get_mutations() ) { - const auto &branch = mut.obj(); + for( const trait_id &mut : mutations.get_mutations() ) { + const mutation_branch &branch = mut.obj(); if( branch.starts_active ) { - my_mutations[mut].powered = true; + mutations.get_trait_data( mut ).powered = true; } } @@ -1046,13 +1046,13 @@ tab_direction set_traits( const catacurses::window &w, avatar &u, points_left &p if( traits_iter.points > 0 ) { vStartingTraits[0].push_back( traits_iter.id ); - if( u.has_trait( traits_iter.id ) ) { + if( u.mutations.has_trait( traits_iter.id ) ) { num_good += traits_iter.points; } } else if( traits_iter.points < 0 ) { vStartingTraits[1].push_back( traits_iter.id ); - if( u.has_trait( traits_iter.id ) ) { + if( u.mutations.has_trait( traits_iter.id ) ) { num_bad += traits_iter.points; } } else { @@ -1164,23 +1164,23 @@ tab_direction set_traits( const catacurses::window &w, avatar &u, points_left &p cLine = col_off_act; if( cur_line_y == i ) { cLine = hi_off; - if( u.has_conflicting_trait( cur_trait ) ) { + if( u.mutations.has_conflicting_trait( cur_trait ) ) { cLine = hilite( c_dark_gray ); - } else if( u.has_trait( cur_trait ) ) { + } else if( u.mutations.has_trait( cur_trait ) ) { cLine = hi_on; } } else { - if( u.has_conflicting_trait( cur_trait ) || g->scen->is_forbidden_trait( cur_trait ) ) { + if( u.mutations.has_conflicting_trait( cur_trait ) || g->scen->is_forbidden_trait( cur_trait ) ) { cLine = c_dark_gray; - } else if( u.has_trait( cur_trait ) ) { + } else if( u.mutations.has_trait( cur_trait ) ) { cLine = col_on_act; } } - } else if( u.has_trait( cur_trait ) ) { + } else if( u.mutations.has_trait( cur_trait ) ) { cLine = col_on_pas; - } else if( u.has_conflicting_trait( cur_trait ) || g->scen->is_forbidden_trait( cur_trait ) ) { + } else if( u.mutations.has_conflicting_trait( cur_trait ) || g->scen->is_forbidden_trait( cur_trait ) ) { cLine = c_light_gray; } @@ -1225,7 +1225,7 @@ tab_direction set_traits( const catacurses::window &w, avatar &u, points_left &p int inc_type = 0; const trait_id cur_trait = vStartingTraits[iCurWorkingPage][iCurrentLine[iCurWorkingPage]]; const mutation_branch &mdata = cur_trait.obj(); - if( u.has_trait( cur_trait ) ) { + if( u.mutations.has_trait( cur_trait ) ) { inc_type = -1; @@ -1238,7 +1238,7 @@ tab_direction set_traits( const catacurses::window &w, avatar &u, points_left &p popup( _( "Your profession of %s prevents you from removing this trait." ), u.prof->gender_appropriate_name( u.male ) ); } - } else if( u.has_conflicting_trait( cur_trait ) ) { + } else if( u.mutations.has_conflicting_trait( cur_trait ) ) { popup( _( "You already picked a conflicting trait!" ) ); } else if( g->scen->is_forbidden_trait( cur_trait ) ) { popup( _( "The scenario you picked prevents you from taking this trait!" ) ); @@ -1260,7 +1260,7 @@ tab_direction set_traits( const catacurses::window &w, avatar &u, points_left &p //inc_type is either -1 or 1, so we can just multiply by it to invert if( inc_type != 0 ) { - u.toggle_trait( cur_trait ); + u.mutations.toggle_trait( u, cur_trait ); points.trait_points -= mdata.points * inc_type; if( iCurWorkingPage == 0 ) { num_good += mdata.points * inc_type; @@ -1481,7 +1481,7 @@ tab_direction set_profession( const catacurses::window &w, avatar &u, points_lef } // Profession items - const auto prof_items = sorted_profs[cur_id]->items( u.male, u.get_mutations() ); + const auto prof_items = sorted_profs[cur_id]->items( u.male, u.mutations.get_mutations() ); buffer << colorize( _( "Profession items:" ), c_light_blue ) << "\n"; if( prof_items.empty() ) { buffer << pgettext( "set_profession_item", "None" ) << "\n"; @@ -1603,7 +1603,7 @@ tab_direction set_profession( const catacurses::window &w, avatar &u, points_lef } else if( action == "CONFIRM" ) { // Remove traits from the previous profession for( const trait_id &old_trait : u.prof->get_locked_traits() ) { - u.toggle_trait( old_trait ); + u.mutations.toggle_trait( u, old_trait ); } u.prof = &sorted_profs[cur_id].obj(); // Add traits for the new profession (and perhaps scenario, if, for example, @@ -2272,12 +2272,12 @@ tab_direction set_description( const catacurses::window &w, avatar &you, const b wrefresh( w_stats ); mvwprintz( w_traits, point_zero, COL_HEADER, _( "Traits: " ) ); - std::vector current_traits = you.get_base_traits(); + const std::vector current_traits = you.mutations.get_base_traits(); if( current_traits.empty() ) { wprintz( w_traits, c_light_red, _( "None!" ) ); } else { for( size_t i = 0; i < current_traits.size(); i++ ) { - const auto current_trait = current_traits[i]; + const trait_id current_trait = current_traits[i]; trim_and_print( w_traits, point( 0, i + 1 ), getmaxx( w_traits ) - 1, current_trait->get_display_color(), current_trait->name() ); } @@ -2527,15 +2527,15 @@ void Character::add_traits( points_left &points ) { // TODO: get rid of using g->u here, use `this` instead for( const trait_id &tr : g->u.prof->get_locked_traits() ) { - if( !has_trait( tr ) ) { - toggle_trait( tr ); + if( !mutations.has_trait( tr ) ) { + mutations.toggle_trait( *this, tr ); } else { points.trait_points += tr->points; } } for( const trait_id &tr : g->scen->get_locked_traits() ) { - if( !has_trait( tr ) ) { - toggle_trait( tr ); + if( !mutations.has_trait( tr ) ) { + mutations.toggle_trait( *this, tr ); } } } @@ -2685,9 +2685,9 @@ void reset_scenario( avatar &u, const scenario *scen ) u.per_max = 8; g->scen = scen; u.prof = &default_prof.obj(); - for( auto &t : u.get_mutations() ) { + for( const trait_id &t : u.mutations.get_mutations() ) { if( t.obj().hp_modifier != 0 ) { - u.toggle_trait( t ); + u.mutations.toggle_trait( u, t ); } } u.empty_traits(); From 052faeee611b8456801700c71af2781f41cc3ef0 Mon Sep 17 00:00:00 2001 From: KorGgenT Date: Thu, 17 Oct 2019 19:12:31 -0400 Subject: [PATCH 19/34] on_mutation_loss() --- src/character_mutations.h | 2 +- src/newcharacter.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/character_mutations.h b/src/character_mutations.h index 1a7e07640978d..247e3b6d73efb 100644 --- a/src/character_mutations.h +++ b/src/character_mutations.h @@ -144,7 +144,7 @@ class character_mutations /** Retrieves a stat mod of a mutation. */ int get_mod( const trait_id &mut, const std::string &arg ) const; /** Empties the trait list */ - void empty_traits(); + void empty_traits( Character &guy ); /** Correction factor of the body temperature due to traits and mutations **/ int bodytemp_modifier_traits( bool overheated ) const; /** Correction factor of the body temperature due to traits and mutations for player lying on the floor **/ diff --git a/src/newcharacter.cpp b/src/newcharacter.cpp index be3a90077fc11..89cbf7e6d379c 100644 --- a/src/newcharacter.cpp +++ b/src/newcharacter.cpp @@ -2500,10 +2500,10 @@ std::vector character_mutations::get_mutations( bool include_hidden ) return result; } -void character_mutations::empty_traits() +void character_mutations::empty_traits( Character &guy ) { for( auto &mut : my_mutations ) { - on_mutation_loss( mut.first ); + guy.on_mutation_loss( mut.first ); } my_traits.clear(); my_mutations.clear(); From d028958c2c4aa7a6b1fbdc5b9cb3aacce361b6b6 Mon Sep 17 00:00:00 2001 From: KorGgenT Date: Thu, 17 Oct 2019 19:18:44 -0400 Subject: [PATCH 20/34] fix function calls --- src/newcharacter.cpp | 2 +- src/player_display.cpp | 31 +++++++++++++++---------------- 2 files changed, 16 insertions(+), 17 deletions(-) diff --git a/src/newcharacter.cpp b/src/newcharacter.cpp index 89cbf7e6d379c..37aa82982f0a1 100644 --- a/src/newcharacter.cpp +++ b/src/newcharacter.cpp @@ -2690,7 +2690,7 @@ void reset_scenario( avatar &u, const scenario *scen ) u.mutations.toggle_trait( u, t ); } } - u.empty_traits(); + u.mutations.empty_traits( u ); u.recalc_hp(); u.empty_skills(); u.add_traits(); diff --git a/src/player_display.cpp b/src/player_display.cpp index 85f2a2c324bf2..9aac12a61cffb 100644 --- a/src/player_display.cpp +++ b/src/player_display.cpp @@ -431,7 +431,7 @@ static void draw_encumbrance_tab( const catacurses::window &w_encumb, static void draw_traits_tab( const catacurses::window &w_traits, const catacurses::window &w_info, unsigned int &line, int &curtab, input_context &ctxt, bool &done, - std::string &action, std::vector &traitslist, + std::string &action, const std::vector &traitslist, const size_t trait_win_size_y ) { werase( w_traits ); @@ -920,7 +920,7 @@ static void draw_initial_windows( const catacurses::window &w_stats, const catacurses::window &w_encumb, const catacurses::window &w_traits, const catacurses::window &w_bionics, const catacurses::window &w_effects, const catacurses::window &w_skills, const catacurses::window &w_speed, player &you, - unsigned int &line, std::vector &traitslist, std::vector &bionicslist, + unsigned int &line, const std::vector &traitslist, std::vector &bionicslist, std::vector> &effect_name_and_text, std::vector &skillslist, const size_t bionics_win_size_y, const size_t effect_win_size_y, const size_t trait_win_size_y, @@ -1037,19 +1037,19 @@ static void draw_initial_windows( const catacurses::window &w_stats, ( pen < 10 ? " " : "" ), pen ); line++; } - if( you.has_trait( trait_id( "SUNLIGHT_DEPENDENT" ) ) && !g->is_in_sunlight( you.pos() ) ) { + if( you.mutations.has_trait( trait_id( "SUNLIGHT_DEPENDENT" ) ) && !g->is_in_sunlight( you.pos() ) ) { pen = ( g->light_level( you.posz() ) >= 12 ? 5 : 10 ); mvwprintz( w_speed, point( 1, line ), c_red, _( "Out of Sunlight -%s%d%%" ), ( pen < 10 ? " " : "" ), pen ); line++; } - const float temperature_speed_modifier = you.mutation_value( "temperature_speed_modifier" ); + const float temperature_speed_modifier = you.mutations.mutation_value( "temperature_speed_modifier" ); if( temperature_speed_modifier != 0 ) { nc_color pen_color; std::string pen_sign; const auto player_local_temp = g->weather.get_temperature( you.pos() ); - if( you.has_trait( trait_id( "COLDBLOOD4" ) ) && player_local_temp > 65 ) { + if( you.mutations.has_trait( trait_id( "COLDBLOOD4" ) ) && player_local_temp > 65 ) { pen_color = c_green; pen_sign = "+"; } else if( player_local_temp < 65 ) { @@ -1066,11 +1066,11 @@ static void draw_initial_windows( const catacurses::window &w_stats, int quick_bonus = static_cast( newmoves - ( newmoves / 1.1 ) ); int bio_speed_bonus = quick_bonus; - if( you.has_trait( trait_id( "QUICK" ) ) && you.has_bionic( bionic_id( "bio_speed" ) ) ) { + if( you.mutations.has_trait( trait_id( "QUICK" ) ) && you.has_bionic( bionic_id( "bio_speed" ) ) ) { bio_speed_bonus = static_cast( newmoves / 1.1 - ( newmoves / 1.1 / 1.1 ) ); std::swap( quick_bonus, bio_speed_bonus ); } - if( you.has_trait( trait_id( "QUICK" ) ) ) { + if( you.mutations.has_trait( trait_id( "QUICK" ) ) ) { mvwprintz( w_speed, point( 1, line ), c_green, _( "Quick +%s%d%%" ), ( quick_bonus < 10 ? " " : "" ), quick_bonus ); line++; @@ -1145,20 +1145,20 @@ void player::disp_info() effect_name_and_text.push_back( { starvation_name, starvation_text.str() } ); } - if( ( has_trait( trait_id( "TROGLO" ) ) && g->is_in_sunlight( pos() ) && + if( ( mutations.has_trait( trait_id( "TROGLO" ) ) && g->is_in_sunlight( pos() ) && g->weather.weather == WEATHER_SUNNY ) || - ( has_trait( trait_id( "TROGLO2" ) ) && g->is_in_sunlight( pos() ) && + ( mutations.has_trait( trait_id( "TROGLO2" ) ) && g->is_in_sunlight( pos() ) && g->weather.weather != WEATHER_SUNNY ) ) { effect_name_and_text.push_back( { _( "In Sunlight" ), _( "The sunlight irritates you.\n" "Strength - 1; Dexterity - 1; Intelligence - 1; Perception - 1" ) } ); - } else if( has_trait( trait_id( "TROGLO2" ) ) && g->is_in_sunlight( pos() ) ) { + } else if( mutations.has_trait( trait_id( "TROGLO2" ) ) && g->is_in_sunlight( pos() ) ) { effect_name_and_text.push_back( { _( "In Sunlight" ), _( "The sunlight irritates you badly.\n" "Strength - 2; Dexterity - 2; Intelligence - 2; Perception - 2" ) } ); - } else if( has_trait( trait_id( "TROGLO3" ) ) && g->is_in_sunlight( pos() ) ) { + } else if( mutations.has_trait( trait_id( "TROGLO3" ) ) && g->is_in_sunlight( pos() ) ) { effect_name_and_text.push_back( { _( "In Sunlight" ), _( "The sunlight irritates you terribly.\n" "Strength - 4; Dexterity - 4; Intelligence - 4; Perception - 4" ) @@ -1175,7 +1175,7 @@ void player::disp_info() unsigned int effect_win_size_y = 1 + static_cast( effect_name_and_text.size() ); - std::vector traitslist = get_mutations( false ); + const std::vector traitslist = mutations.get_mutations( false ); unsigned int trait_win_size_y = 1 + static_cast( traitslist.size() ); std::vector bionicslist = *my_bionics; @@ -1283,10 +1283,9 @@ void player::disp_info() // Post-humanity trumps your pre-Cataclysm life. if( crossed_threshold() ) { std::string race; - for( auto &mut : my_mutations ) { - const auto &mdata = mut.first.obj(); - if( mdata.threshold ) { - race = mdata.name(); + for( const trait_id &mut : mutations.get_mutations() ) { + if( mut->threshold ) { + race = mut->name(); break; } } From 8b413e790f2d2192194ae956af62f627a5f6081e Mon Sep 17 00:00:00 2001 From: KorGgenT Date: Thu, 17 Oct 2019 19:56:48 -0400 Subject: [PATCH 21/34] astyle --- src/newcharacter.cpp | 3 ++- src/player_display.cpp | 6 ++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/newcharacter.cpp b/src/newcharacter.cpp index 37aa82982f0a1..bbf097ec6cdbb 100644 --- a/src/newcharacter.cpp +++ b/src/newcharacter.cpp @@ -1180,7 +1180,8 @@ tab_direction set_traits( const catacurses::window &w, avatar &u, points_left &p } else if( u.mutations.has_trait( cur_trait ) ) { cLine = col_on_pas; - } else if( u.mutations.has_conflicting_trait( cur_trait ) || g->scen->is_forbidden_trait( cur_trait ) ) { + } else if( u.mutations.has_conflicting_trait( cur_trait ) || + g->scen->is_forbidden_trait( cur_trait ) ) { cLine = c_light_gray; } diff --git a/src/player_display.cpp b/src/player_display.cpp index 9aac12a61cffb..8b74e9cbd27a3 100644 --- a/src/player_display.cpp +++ b/src/player_display.cpp @@ -1037,14 +1037,16 @@ static void draw_initial_windows( const catacurses::window &w_stats, ( pen < 10 ? " " : "" ), pen ); line++; } - if( you.mutations.has_trait( trait_id( "SUNLIGHT_DEPENDENT" ) ) && !g->is_in_sunlight( you.pos() ) ) { + if( you.mutations.has_trait( trait_id( "SUNLIGHT_DEPENDENT" ) ) && + !g->is_in_sunlight( you.pos() ) ) { pen = ( g->light_level( you.posz() ) >= 12 ? 5 : 10 ); mvwprintz( w_speed, point( 1, line ), c_red, _( "Out of Sunlight -%s%d%%" ), ( pen < 10 ? " " : "" ), pen ); line++; } - const float temperature_speed_modifier = you.mutations.mutation_value( "temperature_speed_modifier" ); + const float temperature_speed_modifier = + you.mutations.mutation_value( "temperature_speed_modifier" ); if( temperature_speed_modifier != 0 ) { nc_color pen_color; std::string pen_sign; From 7add60c9391949907ef152a484d76856d774a062 Mon Sep 17 00:00:00 2001 From: KorGgenT Date: Thu, 17 Oct 2019 19:57:10 -0400 Subject: [PATCH 22/34] save/load mutations --- src/character_mutations.cpp | 28 +++++++++- src/character_mutations.h | 11 ++++ src/savegame.cpp | 2 +- src/savegame_json.cpp | 100 ++++++++++++++++++------------------ 4 files changed, 87 insertions(+), 54 deletions(-) diff --git a/src/character_mutations.cpp b/src/character_mutations.cpp index ceb26a8952b17..7baebeab0aa2d 100644 --- a/src/character_mutations.cpp +++ b/src/character_mutations.cpp @@ -1,5 +1,30 @@ #include "character_mutations.h" +#include "character.h" +#include "debug.h" +#include "json.h" + +void character_mutations::serialize( JsonOut &json ) const +{ + json.member( "mutations", my_mutations ); + json.member( "traits", my_traits ); +} + +void character_mutations::load_cache_data( Character &guy ) +{ + for( auto it = my_mutations.begin(); it != my_mutations.end(); ) { + const auto &mid = it->first; + if( mid.is_valid() ) { + guy.on_mutation_gain( mid ); + cached_mutations.push_back( &mid.obj() ); + ++it; + } else { + debugmsg( "character %s has invalid mutation %s, it will be ignored", guy.name, mid.c_str() ); + my_mutations.erase( it++ ); + } + } +} + character_mutations::trait_data &character_mutations::get_trait_data( const trait_id &mut ) { return my_mutations[mut]; @@ -18,8 +43,7 @@ character_mutations::trait_data character_mutations::get_trait_data( const trait int character_mutations::get_cat_level( const std::string &category ) const { const auto iter = mutation_category_level.find( category ); - if ( iter == mutation_category_level.cend() ) - { + if( iter == mutation_category_level.cend() ) { return 0; } else { return iter->second; diff --git a/src/character_mutations.h b/src/character_mutations.h index 247e3b6d73efb..bd3f029f5e6c0 100644 --- a/src/character_mutations.h +++ b/src/character_mutations.h @@ -43,6 +43,17 @@ class character_mutations void serialize( JsonOut &json ) const; void deserialize( JsonIn &jsin ); }; + + character_mutations( const std::unordered_map + &my_mutations, const std::unordered_set &my_traits ) { + this->my_mutations = my_mutations; + this->my_traits = my_traits; + } + void serialize( JsonOut &json ) const; + void deserialize( JsonIn &jsin ); + + void load_cache_data( Character &guy ); + /** Get the idents of all base traits. */ std::vector get_base_traits() const; /** Get the idents of all traits/mutations. */ diff --git a/src/savegame.cpp b/src/savegame.cpp index 9e3da8f3f8d6f..8afbdf503d2f2 100644 --- a/src/savegame.cpp +++ b/src/savegame.cpp @@ -49,7 +49,7 @@ extern std::map> quick_shortcuts_map; * Changes that break backwards compatibility should bump this number, so the game can * load a legacy format loader. */ -const int savegame_version = 25; +const int savegame_version = 26; /* * This is a global set by detected version header in .sav, maps.txt, or overmap. diff --git a/src/savegame_json.cpp b/src/savegame_json.cpp index 74879fda1982e..2f90163c3a7fb 100644 --- a/src/savegame_json.cpp +++ b/src/savegame_json.cpp @@ -31,6 +31,7 @@ #include "basecamp.h" #include "bionics.h" #include "calendar.h" +#include "character_mutations.h" #include "debug.h" #include "effect.h" #include "game.h" @@ -341,7 +342,7 @@ void character_id::deserialize( JsonIn &jsin ) //////////////////////////////////////////////////////////////////////////////////////////////////// ///// Character.h, avatar + npc -void Character::trait_data::serialize( JsonOut &json ) const +void character_mutations::trait_data::serialize( JsonOut &json ) const { json.start_object(); json.member( "key", key ); @@ -350,7 +351,7 @@ void Character::trait_data::serialize( JsonOut &json ) const json.end_object(); } -void Character::trait_data::deserialize( JsonIn &jsin ) +void character_mutations::trait_data::deserialize( JsonIn &jsin ) { JsonObject data = jsin.get_object(); data.read( "key", key ); @@ -416,47 +417,56 @@ void Character::load( JsonObject &data ) JsonArray parray; data.read( "underwater", underwater ); + if( savegame_loading_version <= 25 ) { - data.read( "traits", my_traits ); - for( auto it = my_traits.begin(); it != my_traits.end(); ) { - const auto &tid = *it; - if( tid.is_valid() ) { - ++it; - } else { - debugmsg( "character %s has invalid trait %s, it will be ignored", name, tid.c_str() ); - my_traits.erase( it++ ); - } - } + std::unordered_map my_mutations; + std::unordered_set my_traits; - if( savegame_loading_version <= 23 ) { - std::unordered_set old_my_mutations; - data.read( "mutations", old_my_mutations ); - for( const auto &mut : old_my_mutations ) { - my_mutations[mut]; // Creates a new entry with default values - } - std::map trait_keys; - data.read( "mutation_keys", trait_keys ); - for( const auto &k : trait_keys ) { - my_mutations[k.first].key = k.second; - } - std::set active_muts; - data.read( "active_mutations_hacky", active_muts ); - for( const auto &mut : active_muts ) { - my_mutations[mut].powered = true; + data.read( "traits", my_traits ); + for( auto it = my_traits.begin(); it != my_traits.end(); ) { + const auto &tid = *it; + if( tid.is_valid() ) { + ++it; + } else { + debugmsg( "character %s has invalid trait %s, it will be ignored", name, tid.c_str() ); + my_traits.erase( it++ ); + } } - } else { - data.read( "mutations", my_mutations ); - } - for( auto it = my_mutations.begin(); it != my_mutations.end(); ) { - const auto &mid = it->first; - if( mid.is_valid() ) { - on_mutation_gain( mid ); - cached_mutations.push_back( &mid.obj() ); - ++it; + + if( savegame_loading_version <= 23 ) { + std::unordered_set old_my_mutations; + data.read( "mutations", old_my_mutations ); + for( const auto &mut : old_my_mutations ) { + my_mutations[mut]; // Creates a new entry with default values + } + std::map trait_keys; + data.read( "mutation_keys", trait_keys ); + for( const auto &k : trait_keys ) { + my_mutations[k.first].key = k.second; + } + std::set active_muts; + data.read( "active_mutations_hacky", active_muts ); + for( const auto &mut : active_muts ) { + my_mutations[mut].powered = true; + } } else { - debugmsg( "character %s has invalid mutation %s, it will be ignored", name, mid.c_str() ); - my_mutations.erase( it++ ); + data.read( "mutations", my_mutations ); } + mutations = character_mutations( my_mutations, my_traits ); + } else { + data.read( "mutations", mutations ); + } + mutations.load_cache_data( *this ); + mutations.set_highest_cat_level(); + mutations.drench_mut_calc(); + + // Fixes bugged characters for telescopic eyes CBM. + if( has_bionic( bionic_id( "bio_eye_optic" ) ) && mutations.has_trait( trait_HYPEROPIC ) ) { + mutations.remove_mutation( *this, trait_HYPEROPIC ); + } + + if( has_bionic( bionic_id( "bio_eye_optic" ) ) && mutations.has_trait( trait_MYOPIC ) ) { + mutations.remove_mutation( *this, trait_MYOPIC ); } data.read( "my_bionics", *my_bionics ); @@ -590,8 +600,7 @@ void Character::store( JsonOut &json ) const json.member( "oxygen", oxygen ); // traits: permanent 'mutations' more or less - json.member( "traits", my_traits ); - json.member( "mutations", my_mutations ); + json.member( "mutations", mutations ); // "Fracking Toasters" - Saul Tigh, toaster json.member( "my_bionics", *my_bionics ); @@ -787,15 +796,6 @@ void player::load( JsonObject &data ) add_bionic( bionic_id( "bio_blindfold" ) ); } - // Fixes bugged characters for telescopic eyes CBM. - if( has_bionic( bionic_id( "bio_eye_optic" ) ) && has_trait( trait_HYPEROPIC ) ) { - remove_mutation( trait_HYPEROPIC ); - } - - if( has_bionic( bionic_id( "bio_eye_optic" ) ) && has_trait( trait_MYOPIC ) ) { - remove_mutation( trait_MYOPIC ); - } - if( has_bionic( bionic_id( "bio_solar" ) ) ) { remove_bionic( bionic_id( "bio_solar" ) ); } @@ -1002,8 +1002,6 @@ void avatar::load( JsonObject &data ) data.read( "stamina", stamina ); data.read( "magic", magic ); - set_highest_cat_level(); - drench_mut_calc(); std::string scen_ident = "(null)"; if( data.read( "scenario", scen_ident ) && string_id( scen_ident ).is_valid() ) { g->scen = &string_id( scen_ident ).obj(); From eb480b68950cc92f78074f190e411240a57fc43b Mon Sep 17 00:00:00 2001 From: KorGgenT Date: Thu, 17 Oct 2019 21:10:17 -0400 Subject: [PATCH 23/34] fix function calls --- src/character.cpp | 8 +-- src/condition.cpp | 6 +- src/consumption.cpp | 138 ++++++++++++++++++++++---------------------- src/magic.cpp | 14 ++--- src/monattack.cpp | 39 +++++++------ src/mutation.cpp | 16 ++--- src/mutation.h | 1 + src/npc.cpp | 38 ++++++------ src/npctalk.cpp | 22 +++---- src/weather.cpp | 8 +-- src/wish.cpp | 46 +++++++-------- 11 files changed, 169 insertions(+), 167 deletions(-) diff --git a/src/character.cpp b/src/character.cpp index 984be624217fd..41f1aa1e4bb21 100644 --- a/src/character.cpp +++ b/src/character.cpp @@ -4451,7 +4451,7 @@ void character_mutations::set_highest_cat_level() mutation_category_level.clear(); // For each of our mutations... - for( const std::pair &mut : my_mutations ) { + for( const std::pair &mut : my_mutations ) { // ...build up a map of all prerequisite/replacement mutations along the tree, along with their distance from the current mutation std::unordered_map dependency_map; build_mut_dependency_map( mut.first, dependency_map, 0 ); @@ -4486,9 +4486,9 @@ void character_mutations::drench_mut_calc() } } - mut_drench[bp][WT_GOOD] = good; - mut_drench[bp][WT_NEUTRAL] = neutral; - mut_drench[bp][WT_IGNORED] = ignored; + mut_drench[bp][Character::water_tolerance::WT_GOOD] = Character::water_tolerance::good; + mut_drench[bp][Character::water_tolerance::WT_NEUTRAL] = Character::water_tolerance::neutral; + mut_drench[bp][Character::water_tolerance::WT_IGNORED] = Character::water_tolerance::ignored; } } diff --git a/src/condition.cpp b/src/condition.cpp index ab7059a992b89..56011859b0151 100644 --- a/src/condition.cpp +++ b/src/condition.cpp @@ -84,7 +84,7 @@ void conditional_t::set_has_any_trait( JsonObject &jo, const std::string &mem actor = dynamic_cast( d.beta ); } for( const auto &trait : traits_to_check ) { - if( actor->has_trait( trait ) ) { + if( actor->mutations.has_trait( trait ) ) { return true; } } @@ -101,7 +101,7 @@ void conditional_t::set_has_trait( JsonObject &jo, const std::string &member, if( is_npc ) { actor = dynamic_cast( d.beta ); } - return actor->has_trait( trait_id( trait_to_check ) ); + return actor->mutations.has_trait( trait_id( trait_to_check ) ); }; } @@ -117,7 +117,7 @@ void conditional_t::set_has_trait_flag( JsonObject &jo, const std::string &me if( trait_flag_to_check == "MUTATION_THRESHOLD" ) { return actor->crossed_threshold(); } - return actor->has_trait_flag( trait_flag_to_check ); + return actor->mutations.has_trait_flag( trait_flag_to_check ); }; } diff --git a/src/consumption.cpp b/src/consumption.cpp index 633f75d1f430d..70d930a5c2236 100644 --- a/src/consumption.cpp +++ b/src/consumption.cpp @@ -87,15 +87,15 @@ const std::map plut_charges = { int player::stomach_capacity() const { - if( has_trait( trait_id( "GIZZARD" ) ) ) { + if( mutations.has_trait( trait_id( "GIZZARD" ) ) ) { return 0; } - if( has_active_mutation( trait_id( "HIBERNATE" ) ) ) { + if( mutations.has_active_mutation( trait_id( "HIBERNATE" ) ) ) { return -620; } - if( has_trait( trait_id( "GOURMAND" ) ) || has_trait( trait_id( "HIBERNATE" ) ) ) { + if( mutations.has_trait( trait_id( "GOURMAND" ) ) || mutations.has_trait( trait_id( "HIBERNATE" ) ) ) { return -60; } @@ -130,11 +130,11 @@ int player::kcal_for( const item &comest ) const kcal = comest.get_comestible()->get_calories(); } - if( has_trait( trait_GIZZARD ) ) { + if( mutations.has_trait( trait_GIZZARD ) ) { kcal *= 0.6f; } - if( has_trait( trait_CARNIVORE ) && comest.has_flag( flag_CARNIVORE_OK ) && + if( mutations.has_trait( trait_CARNIVORE ) && comest.has_flag( flag_CARNIVORE_OK ) && comest.has_any_flag( carnivore_blacklist ) ) { // TODO: Comment pizza scrapping kcal *= 0.5f; @@ -142,7 +142,7 @@ int player::kcal_for( const item &comest ) const const float relative_rot = comest.get_relative_rot(); // Saprophages get full nutrition from rotting food - if( relative_rot > 1.0f && !has_trait( trait_SAPROPHAGE ) ) { + if( relative_rot > 1.0f && !mutations.has_trait( trait_SAPROPHAGE ) ) { // everyone else only gets a portion of the nutrition // Scaling linearly from 100% at just-rotten to 0 at halfway-rotten-away const float rottedness = clamp( 2 * relative_rot - 2.0f, 0.1f, 1.0f ); @@ -190,7 +190,7 @@ std::pair player::fun_for( const item &comest ) const } // Rotten food should be pretty disgusting const float relative_rot = comest.get_relative_rot(); - if( relative_rot > 1.0f && !has_trait( trait_SAPROPHAGE ) && !has_trait( trait_SAPROVORE ) ) { + if( relative_rot > 1.0f && !mutations.has_trait( trait_SAPROPHAGE ) && !mutations.has_trait( trait_SAPROVORE ) ) { const float rottedness = clamp( 2 * relative_rot - 2.0f, 0.1f, 1.0f ); // Three effects: // penalty for rot goes from -2 to -20 @@ -223,15 +223,15 @@ std::pair player::fun_for( const item &comest ) const } } - if( ( comest.has_flag( flag_LUPINE ) && has_trait( trait_LUPINE ) ) || - ( comest.has_flag( flag_FELINE ) && has_trait( trait_FELINE ) ) ) { + if( ( comest.has_flag( flag_LUPINE ) && mutations.has_trait( trait_LUPINE ) ) || + ( comest.has_flag( flag_FELINE ) && mutations.has_trait( trait_FELINE ) ) ) { if( fun < 0 ) { fun = -fun; fun /= 2; } } - if( has_trait( trait_GOURMAND ) ) { + if( mutations.has_trait( trait_GOURMAND ) ) { if( fun < -1 ) { fun_max = fun; fun /= 2; @@ -259,7 +259,7 @@ std::map player::vitamins_from( const itype_id &id ) const static std::list mut_vitamin_absorb_modify( const player &p ) { std::list traits; - for( auto &m : p.get_mutations() ) { + for( const trait_id &m : p.mutations.get_mutations() ) { const auto &mut = m.obj(); if( !mut.vitamin_absorb_multi.empty() ) { traits.push_back( m ); @@ -397,7 +397,7 @@ bool player::vitamin_set( const vitamin_id &vit, int qty ) float Character::metabolic_rate_base() const { float hunger_rate = get_option< float >( "PLAYER_HUNGER_RATE" ); - return hunger_rate * ( 1.0f + mutation_value( "metabolism_modifier" ) ); + return hunger_rate * ( 1.0f + mutations.mutation_value( "metabolism_modifier" ) ); } // TODO: Make this less chaotic to let NPC retroactive catch up work here @@ -439,7 +439,7 @@ morale_type player::allergy_type( const item &food ) const }; for( const auto &tp : allergy_tuples ) { - if( has_trait( std::get<0>( tp ) ) && + if( mutations.has_trait( std::get<0>( tp ) ) && food.has_flag( std::get<1>( tp ) ) ) { return std::get<2>( tp ); } @@ -457,10 +457,10 @@ ret_val player::can_eat( const item &food ) const } if( food.has_flag( "INEDIBLE" ) ) { - if( ( food.has_flag( "CATTLE" ) && !has_trait( trait_id( "THRESH_CATTLE" ) ) ) || - ( food.has_flag( "FELINE" ) && !has_trait( trait_id( "THRESH_FELINE" ) ) ) || - ( food.has_flag( "LUPINE" ) && !has_trait( trait_id( "THRESH_LUPINE" ) ) ) || - ( food.has_flag( "BIRD" ) && !has_trait( trait_id( "THRESH_BIRD" ) ) ) ) { + if( ( food.has_flag( "CATTLE" ) && !mutations.has_trait( trait_id( "THRESH_CATTLE" ) ) ) || + ( food.has_flag( "FELINE" ) && !mutations.has_trait( trait_id( "THRESH_FELINE" ) ) ) || + ( food.has_flag( "LUPINE" ) && !mutations.has_trait( trait_id( "THRESH_LUPINE" ) ) ) || + ( food.has_flag( "BIRD" ) && !mutations.has_trait( trait_id( "THRESH_BIRD" ) ) ) ) { return ret_val::make_failure( _( "That doesn't look edible to you." ) ); } } @@ -480,7 +480,7 @@ ret_val player::can_eat( const item &food ) const // TODO: This condition occurs way too often. Unify it. // update Sep. 26 2018: this apparently still occurs way too often. yay! - if( is_underwater() && !has_trait( trait_id( "WATERSLEEP" ) ) ) { + if( is_underwater() && !mutations.has_trait( trait_id( "WATERSLEEP" ) ) ) { return ret_val::make_failure( _( "You can't do that while underwater." ) ); } @@ -514,22 +514,22 @@ ret_val player::can_eat( const item &food ) const } // For all those folks who loved eating marloss berries. D:< mwuhahaha - if( has_trait( trait_id( "M_DEPENDENT" ) ) && !food.has_flag( "MYCUS_OK" ) ) { + if( mutations.has_trait( trait_id( "M_DEPENDENT" ) ) && !food.has_flag( "MYCUS_OK" ) ) { return ret_val::make_failure( INEDIBLE_MUTATION, _( "We can't eat that. It's not right for us." ) ); } // Here's why PROBOSCIS is such a negative trait. - if( has_trait( trait_id( "PROBOSCIS" ) ) && !( drinkable || food.is_medication() ) ) { + if( mutations.has_trait( trait_id( "PROBOSCIS" ) ) && !( drinkable || food.is_medication() ) ) { return ret_val::make_failure( INEDIBLE_MUTATION, _( "Ugh, you can't drink that!" ) ); } - if( has_trait( trait_id( "CARNIVORE" ) ) && nutrition_for( food ) > 0 && + if( mutations.has_trait( trait_id( "CARNIVORE" ) ) && nutrition_for( food ) > 0 && food.has_any_flag( carnivore_blacklist ) && !food.has_flag( "CARNIVORE_OK" ) ) { return ret_val::make_failure( INEDIBLE_MUTATION, _( "Eww. Inedible plant stuff!" ) ); } - if( ( has_trait( trait_id( "HERBIVORE" ) ) || has_trait( trait_id( "RUMINANT" ) ) ) && + if( ( mutations.has_trait( trait_id( "HERBIVORE" ) ) || mutations.has_trait( trait_id( "RUMINANT" ) ) ) && food.has_any_flag( herbivore_blacklist ) ) { // Like non-cannibal, but more strict! return ret_val::make_failure( INEDIBLE_MUTATION, @@ -560,18 +560,18 @@ ret_val player::will_eat( const item &food, bool interactive ) co consequences.emplace_back( ret_val::make_failure( code, msg ) ); }; - const bool saprophage = has_trait( trait_id( "SAPROPHAGE" ) ); + const bool saprophage = mutations.has_trait( trait_id( "SAPROPHAGE" ) ); const auto &comest = food.get_comestible(); if( food.rotten() ) { - const bool saprovore = has_trait( trait_id( "SAPROVORE" ) ); + const bool saprovore = mutations.has_trait( trait_id( "SAPROVORE" ) ); if( !saprophage && !saprovore ) { add_consequence( _( "This is rotten and smells awful!" ), ROTTEN ); } } - const bool carnivore = has_trait( trait_id( "CARNIVORE" ) ); - if( food.has_flag( "CANNIBALISM" ) && !has_trait_flag( "CANNIBAL" ) ) { + const bool carnivore = mutations.has_trait( trait_id( "CARNIVORE" ) ); + if( food.has_flag( "CANNIBALISM" ) && !mutations.has_trait_flag( "CANNIBAL" ) ) { add_consequence( _( "The thought of eating human flesh makes you feel sick." ), CANNIBALISM ); } @@ -657,7 +657,7 @@ bool player::eat( item &food, bool force ) // Note: the block below assumes we decided to eat it // No coming back from here - const bool hibernate = has_active_mutation( trait_id( "HIBERNATE" ) ); + const bool hibernate = mutations.has_active_mutation( trait_id( "HIBERNATE" ) ); const int nutr = nutrition_for( food ); const int quench = food.get_comestible()->quench; const bool spoiled = food.rotten(); @@ -679,10 +679,10 @@ bool player::eat( item &food, bool force ) rng( units::to_milliliter( stomach.capacity() ) / 2, units::to_milliliter( stomach.contains() ) ) > units::to_milliliter( stomach.capacity() ); - const bool saprophage = has_trait( trait_id( "SAPROPHAGE" ) ); + const bool saprophage = mutations.has_trait( trait_id( "SAPROPHAGE" ) ); if( spoiled && !saprophage ) { - add_msg_if_player( m_bad, _( "Ick, this %s doesn't taste so good…" ), food.tname() ); - if( !has_trait( trait_id( "SAPROVORE" ) ) && !has_trait( trait_id( "EATDEAD" ) ) && + add_msg_if_player( m_bad, _( "Ick, this %s doesn't taste so good..." ), food.tname() ); + if( !mutations.has_trait( trait_id( "SAPROVORE" ) ) && !mutations.has_trait( trait_id( "EATDEAD" ) ) && ( !has_bionic( bio_digestion ) || one_in( 3 ) ) ) { add_effect( effect_foodpoison, rng( 6_minutes, ( nutr + 1 ) * 6_minutes ) ); } @@ -698,25 +698,25 @@ bool player::eat( item &food, bool force ) } food.mod_charges( -1 ); - const bool amorphous = has_trait( trait_id( "AMORPHOUS" ) ); + const bool amorphous = mutations.has_trait( trait_id( "AMORPHOUS" ) ); int mealtime = 250; if( drinkable || chew ) { // Those bonuses/penalties only apply to food // Not to smoking weed or applying bandages! - if( has_trait( trait_id( "MOUTH_TENTACLES" ) ) || has_trait( trait_id( "MANDIBLES" ) ) || - has_trait( trait_id( "FANGS_SPIDER" ) ) ) { + if( mutations.has_trait( trait_id( "MOUTH_TENTACLES" ) ) || mutations.has_trait( trait_id( "MANDIBLES" ) ) || + mutations.has_trait( trait_id( "FANGS_SPIDER" ) ) ) { mealtime /= 2; - } else if( has_trait( trait_id( "SHARKTEETH" ) ) ) { + } else if( mutations.has_trait( trait_id( "SHARKTEETH" ) ) ) { //SHARKBAIT! HOO HA HA! mealtime /= 3; - } else if( has_trait( trait_id( "GOURMAND" ) ) ) { + } else if( mutations.has_trait( trait_id( "GOURMAND" ) ) ) { // Don't stack those two - that would be 25 moves per item mealtime -= 100; } - if( has_trait( trait_id( "BEAK_HUM" ) ) && !drinkable ) { + if( mutations.has_trait( trait_id( "BEAK_HUM" ) ) && !drinkable ) { mealtime += 200; // Much better than PROBOSCIS but still optimized for fluids - } else if( has_trait( trait_id( "SABER_TEETH" ) ) ) { + } else if( mutations.has_trait( trait_id( "SABER_TEETH" ) ) ) { mealtime += 250; // They get In The Way } @@ -731,8 +731,8 @@ bool player::eat( item &food, bool force ) // If it's poisonous... poison us. // TODO: Move this to a flag - if( food.poison > 0 && !has_trait( trait_id( "EATPOISON" ) ) && - !has_trait( trait_id( "EATDEAD" ) ) ) { + if( food.poison > 0 && !mutations.has_trait( trait_id( "EATPOISON" ) ) && + !mutations.has_trait( trait_id( "EATDEAD" ) ) ) { if( food.poison >= rng( 2, 4 ) ) { add_effect( effect_poison, food.poison * 10_minutes ); } @@ -740,7 +740,7 @@ bool player::eat( item &food, bool force ) add_effect( effect_foodpoison, food.poison * 30_minutes ); } - const bool spiritual = has_trait( trait_id( "SPIRITUAL" ) ); + const bool spiritual = mutations.has_trait( trait_id( "SPIRITUAL" ) ); if( food.has_flag( "HIDDEN_HALLU" ) ) { if( spiritual ) { add_morale( MORALE_FOOD_GOOD, 36, 72, 2_hours, 1_hours, false ); @@ -756,7 +756,7 @@ bool player::eat( item &food, bool force ) add_msg_player_or_npc( _( "You assimilate your %s." ), _( " assimilates a %s." ), food.tname() ); } else if( drinkable ) { - if( ( has_trait( trait_id( "SCHIZOPHRENIC" ) ) || has_artifact_with( AEP_SCHIZO ) ) && + if( ( mutations.has_trait( trait_id( "SCHIZOPHRENIC" ) ) || has_artifact_with( AEP_SCHIZO ) ) && one_in( 50 ) && !spoiled && food.goes_bad() && is_player() ) { add_msg( m_bad, _( "Ick, this %s (rotten) doesn't taste so good…" ), food.tname() ); @@ -766,7 +766,7 @@ bool player::eat( item &food, bool force ) food.tname() ); } } else if( chew ) { - if( ( has_trait( trait_id( "SCHIZOPHRENIC" ) ) || has_artifact_with( AEP_SCHIZO ) ) && + if( ( mutations.has_trait( trait_id( "SCHIZOPHRENIC" ) ) || has_artifact_with( AEP_SCHIZO ) ) && one_in( 50 ) && !spoiled && food.goes_bad() && is_player() ) { add_msg( m_bad, _( "Ick, this %s (rotten) doesn't taste so good…" ), food.tname() ); @@ -789,14 +789,14 @@ bool player::eat( item &food, bool force ) } } if( has_chair_nearby && has_table_nearby ) { - if( has_trait( trait_id( "TABLEMANNERS" ) ) ) { + if( mutations.has_trait( trait_id( "TABLEMANNERS" ) ) ) { rem_morale( MORALE_ATE_WITHOUT_TABLE ); add_morale( MORALE_ATE_WITH_TABLE, 3, 3, 3_hours, 2_hours, true ); } else { add_morale( MORALE_ATE_WITH_TABLE, 1, 1, 3_hours, 2_hours, true ); } } else { - if( has_trait( trait_id( "TABLEMANNERS" ) ) ) { + if( mutations.has_trait( trait_id( "TABLEMANNERS" ) ) ) { rem_morale( MORALE_ATE_WITH_TABLE ); add_morale( MORALE_ATE_WITHOUT_TABLE, -2, -4, 3_hours, 2_hours, true ); } @@ -828,9 +828,9 @@ bool player::eat( item &food, bool force ) // Sapiovores don't recognize humans as the same species. // But let them possibly feel cool about eating sapient stuff - treat like psycho // However, spiritual sapiovores should still recognize humans as having a soul or special for religious reasons - const bool cannibal = has_trait( trait_id( "CANNIBAL" ) ); - const bool psycho = has_trait( trait_id( "PSYCHOPATH" ) ); - const bool sapiovore = has_trait( trait_id( "SAPIOVORE" ) ); + const bool cannibal = mutations.has_trait( trait_id( "CANNIBAL" ) ); + const bool psycho = mutations.has_trait( trait_id( "PSYCHOPATH" ) ); + const bool sapiovore = mutations.has_trait( trait_id( "SAPIOVORE" ) ); if( ( cannibal || sapiovore ) && psycho && spiritual ) { add_msg_if_player( m_good, _( "You feast upon the human flesh, and in doing so, devour their spirit." ) ); @@ -862,7 +862,7 @@ bool player::eat( item &food, bool force ) } } - if( food.has_flag( "FUNGAL_VECTOR" ) && !has_trait( trait_id( "M_IMMUNE" ) ) ) { + if( food.has_flag( "FUNGAL_VECTOR" ) && !mutations.has_trait( trait_id( "M_IMMUNE" ) ) ) { add_effect( effect_fungus, 1_turns, num_bp, true ); } @@ -887,11 +887,11 @@ bool player::eat( item &food, bool force ) add_morale( allergy, -75, -400, 30_minutes, 24_minutes ); } if( food.has_flag( "ALLERGEN_JUNK" ) ) { - if( has_trait( trait_id( "PROJUNK" ) ) ) { + if( mutations.has_trait( trait_id( "PROJUNK" ) ) ) { add_msg_if_player( m_good, _( "Mmm, junk food." ) ); add_morale( MORALE_SWEETTOOTH, 5, 30, 30_minutes, 24_minutes ); } - if( has_trait( trait_id( "PROJUNK2" ) ) ) { + if( mutations.has_trait( trait_id( "PROJUNK2" ) ) ) { if( !one_in( 100 ) ) { add_msg_if_player( m_good, _( "When life's got you down, there's always sugar." ) ); } else { @@ -902,23 +902,23 @@ bool player::eat( item &food, bool force ) } // Carnivores CAN eat junk food, but they won't like it much. // Pizza-scraping happens in consume_effects. - if( has_trait( trait_id( "CARNIVORE" ) ) && food.has_flag( "ALLERGEN_JUNK" ) && + if( mutations.has_trait( trait_id( "CARNIVORE" ) ) && food.has_flag( "ALLERGEN_JUNK" ) && !food.has_flag( "CARNIVORE_OK" ) ) { add_msg_if_player( m_bad, _( "Your stomach begins gurgling and you feel bloated and ill." ) ); add_morale( MORALE_NO_DIGEST, -25, -125, 30_minutes, 24_minutes ); } - if( !spoiled && chew && has_trait( trait_id( "SAPROPHAGE" ) ) ) { + if( !spoiled && chew && mutations.has_trait( trait_id( "SAPROPHAGE" ) ) ) { // It's OK to *drink* things that haven't rotted. Alternative is to ban water. D: add_msg_if_player( m_bad, _( "Your stomach begins gurgling and you feel bloated and ill." ) ); add_morale( MORALE_NO_DIGEST, -75, -400, 30_minutes, 24_minutes ); } if( food.has_flag( "URSINE_HONEY" ) && ( !crossed_threshold() || - has_trait( trait_id( "THRESH_URSINE" ) ) ) && - mutation_category_level["URSINE"] > 40 ) { + mutations.has_trait( trait_id( "THRESH_URSINE" ) ) ) && + mutations.get_cat_level( "URSINE" ) > 40 ) { //Need at least 5 bear mutations for effect to show, to filter out mutations in common with other categories - int honey_fun = has_trait( trait_id( "THRESH_URSINE" ) ) ? - std::min( mutation_category_level["URSINE"] / 8, 20 ) : - mutation_category_level["URSINE"] / 12; + int honey_fun = mutations.has_trait( trait_id( "THRESH_URSINE" ) ) ? + std::min( mutations.get_cat_level( "URSINE" ) / 8, 20 ) : + mutations.get_cat_level( "URSINE" ) / 12; if( honey_fun < 10 ) { add_msg_if_player( m_good, _( "You find the sweet taste of honey surprisingly palatable." ) ); } else { @@ -928,17 +928,17 @@ bool player::eat( item &food, bool force ) } // chance to become parasitised - if( !will_vomit && !( has_bionic( bio_digestion ) || has_trait( trait_id( "PARAIMMUNE" ) ) ) ) { + if( !will_vomit && !( has_bionic( bio_digestion ) || mutations.has_trait( trait_id( "PARAIMMUNE" ) ) ) ) { if( food.get_comestible()->parasites > 0 && !food.has_flag( "NO_PARASITES" ) && one_in( food.get_comestible()->parasites ) ) { switch( rng( 0, 3 ) ) { case 0: - if( !has_trait( trait_id( "EATHEALTH" ) ) ) { + if( !mutations.has_trait( trait_id( "EATHEALTH" ) ) ) { add_effect( effect_tapeworm, 1_turns, num_bp, true ); } break; case 1: - if( !has_trait( trait_id( "ACIDBLOOD" ) ) ) { + if( !mutations.has_trait( trait_id( "ACIDBLOOD" ) ) ) { add_effect( effect_bloodworms, 1_turns, num_bp, true ); } break; @@ -975,7 +975,7 @@ void player::modify_stimulation( const islot_comestible &comest ) stim = std::min( comest.stim * 3, stim + comest.stim ); } } - if( has_trait( trait_id( "STIMBOOST" ) ) && ( stim > 30 ) && ( ( comest.add == ADD_CAFFEINE ) + if( mutations.has_trait( trait_id( "STIMBOOST" ) ) && ( stim > 30 ) && ( ( comest.add == ADD_CAFFEINE ) || ( comest.add == ADD_SPEED ) || ( comest.add == ADD_COKE ) || ( comest.add == ADD_CRACK ) ) ) { int hallu_duration = ( stim - comest.stim < 30 ) ? stim - 30 : comest.stim; add_effect( effect_visuals, hallu_duration * 30_minutes ); @@ -1031,11 +1031,11 @@ bool player::consume_effects( item &food ) return false; } - if( has_trait( trait_id( "THRESH_PLANT" ) ) && food.type->can_use( "PLANTBLECH" ) ) { + if( mutations.has_trait( trait_id( "THRESH_PLANT" ) ) && food.type->can_use( "PLANTBLECH" ) ) { // used to cap nutrition and thirst, but no longer return false; } - if( ( has_trait( trait_id( "HERBIVORE" ) ) || has_trait( trait_id( "RUMINANT" ) ) ) && + if( ( mutations.has_trait( trait_id( "HERBIVORE" ) ) || mutations.has_trait( trait_id( "RUMINANT" ) ) ) && food.has_any_flag( herbivore_blacklist ) ) { // No good can come of this. return false; @@ -1045,8 +1045,8 @@ bool player::consume_effects( item &food ) // Rotten food causes health loss const float relative_rot = food.get_relative_rot(); - if( relative_rot > 1.0f && !has_trait( trait_id( "SAPROPHAGE" ) ) && - !has_trait( trait_id( "SAPROVORE" ) ) && !has_bionic( bio_digestion ) ) { + if( relative_rot > 1.0f && !mutations.has_trait( trait_id( "SAPROPHAGE" ) ) && + !mutations.has_trait( trait_id( "SAPROVORE" ) ) && !has_bionic( bio_digestion ) ) { const float rottedness = clamp( 2 * relative_rot - 2.0f, 0.1f, 1.0f ); // ~-1 health per 1 nutrition at halfway-rotten-away, ~0 at "just got rotten" // But always round down @@ -1056,7 +1056,7 @@ bool player::consume_effects( item &food ) } const auto nutr = nutrition_for( food ); // used in hibernation messages. - const bool skip_health = has_trait( trait_id( "PROJUNK2" ) ) && comest.healthy < 0; + const bool skip_health = mutations.has_trait( trait_id( "PROJUNK2" ) ) && comest.healthy < 0; if( !skip_health ) { // we can handle junk just fine modify_health( comest ); } @@ -1064,7 +1064,7 @@ bool player::consume_effects( item &food ) modify_addiction( comest ); modify_morale( food, nutr ); - const bool hibernate = has_active_mutation( trait_id( "HIBERNATE" ) ); + const bool hibernate = mutations.has_active_mutation( trait_id( "HIBERNATE" ) ); if( hibernate ) { if( ( nutr > 0 && get_hunger() < -60 ) || ( comest.quench > 0 && get_thirst() < -60 ) ) { //Tell the player what's going on @@ -1101,7 +1101,7 @@ bool player::consume_effects( item &food ) int capacity = stomach_capacity(); // Moved here and changed a bit - it was too complex // Incredibly minor stuff like this shouldn't require complexity - if( !is_npc() && has_trait( trait_id( "SLIMESPAWNER" ) ) && + if( !is_npc() && mutations.has_trait( trait_id( "SLIMESPAWNER" ) ) && ( get_healthy_kcal() < get_stored_kcal() + 4000 && get_thirst() - stomach.get_water() / 5_ml < 40 ) ) { add_msg_if_player( m_mixed, @@ -1121,7 +1121,7 @@ bool player::consume_effects( item &food ) } // Last thing that happens before capping hunger - if( get_hunger() < capacity && has_trait( trait_id( "EATHEALTH" ) ) ) { + if( get_hunger() < capacity && mutations.has_trait( trait_id( "EATHEALTH" ) ) ) { int excess_food = capacity - get_hunger(); add_msg_player_or_npc( _( "You feel the %s filling you out." ), _( " looks better after eating the %s." ), diff --git a/src/magic.cpp b/src/magic.cpp index b1f77d4639985..323f9a9f837b8 100644 --- a/src/magic.cpp +++ b/src/magic.cpp @@ -581,7 +581,7 @@ bool spell::can_learn( const player &p ) const if( type->spell_class == trait_id( "NONE" ) ) { return true; } - return p.has_trait( type->spell_class ); + return p.mutations.has_trait( type->spell_class ); } int spell::energy_cost( const player &p ) const @@ -1254,7 +1254,7 @@ void known_magic::learn_spell( const spell_type *sp, player &p, bool force ) return; } if( !force && sp->spell_class != trait_id( "NONE" ) ) { - if( can_learn_spell( p, sp->id ) && !p.has_trait( sp->spell_class ) ) { + if( can_learn_spell( p, sp->id ) && !p.mutations.has_trait( sp->spell_class ) ) { std::string trait_cancel; for( const trait_id &cancel : sp->spell_class->cancels ) { if( cancel == sp->spell_class->cancels.back() && @@ -1275,7 +1275,7 @@ void known_magic::learn_spell( const spell_type *sp, player &p, bool force ) if( query_yn( _( "Learning this spell will make you a\n\n%s: %s\n\nand lock you out of\n\n%s\n\nContinue?" ), sp->spell_class->name(), sp->spell_class->desc(), trait_cancel ) ) { - p.set_mutation( sp->spell_class ); + p.mutations.set_mutation( p, sp->spell_class ); p.on_mutation_gain( sp->spell_class ); p.add_msg_if_player( sp->spell_class.obj().desc() ); } else { @@ -1312,7 +1312,7 @@ bool known_magic::can_learn_spell( const player &p, const spell_id &sp ) const if( sp_t.spell_class == trait_id( "NONE" ) ) { return true; } - return !p.has_opposite_trait( sp_t.spell_class ); + return !p.mutations.has_opposite_trait( sp_t.spell_class ); } spell &known_magic::get_spell( const spell_id &sp ) @@ -1352,8 +1352,8 @@ int known_magic::max_mana( const player &p ) const { const float int_bonus = ( ( 0.2f + p.get_int() * 0.1f ) - 1.0f ) * mana_base; const float unaugmented_mana = std::max( 0.0f, - ( ( mana_base + int_bonus ) * p.mutation_value( "mana_multiplier" ) ) + - p.mutation_value( "mana_modifier" ) - units::to_kilojoule( p.get_power_level() ) ); + ( ( mana_base + int_bonus ) * p.mutations.mutation_value( "mana_multiplier" ) ) + + p.mutations.mutation_value( "mana_modifier" ) - units::to_kilojoule( p.get_power_level() ) ); return p.calculate_by_enchantment( unaugmented_mana, enchantment::mod::MAX_MANA, true ); } @@ -1363,7 +1363,7 @@ void known_magic::update_mana( const player &p, float turns ) const float full_replenish = to_turns( 8_hours ); const float ratio = turns / full_replenish; mod_mana( p, floor( ratio * p.calculate_by_enchantment( max_mana( p ) * - p.mutation_value( "mana_regen_multiplier" ), enchantment::mod::REGEN_MANA ) ) ); + p.mutations.mutation_value( "mana_regen_multiplier" ), enchantment::mod::REGEN_MANA ) ) ); } std::vector known_magic::spells() const diff --git a/src/monattack.cpp b/src/monattack.cpp index df63297f225c7..ff8bc8804286a 100644 --- a/src/monattack.cpp +++ b/src/monattack.cpp @@ -1234,7 +1234,7 @@ bool mattack::science( monster *const z ) // I said SCIENCE again! if( rad_proof ) { target->add_msg_if_player( m_good, _( "Your armor protects you from the radiation!" ) ); } else if( one_in( att_rad_mutate_chance ) ) { - foe->mutate(); + foe->mutations.mutate( *foe ); } else { target->add_msg_if_player( m_bad, _( "You get pins and needles all over." ) ); } @@ -1578,7 +1578,7 @@ bool mattack::fungus( monster *z ) { // TODO: Infect NPCs? z->moves -= 200; // It takes a while - if( g->u.has_trait( trait_THRESH_MYCUS ) ) { + if( g->u.mutations.has_trait( trait_THRESH_MYCUS ) ) { z->friendly = 100; } //~ the sound of a fungus releasing spores @@ -1711,11 +1711,11 @@ bool mattack::fungus_inject( monster *z ) return false; } - if( g->u.has_trait( trait_THRESH_MARLOSS ) || g->u.has_trait( trait_THRESH_MYCUS ) ) { + if( g->u.mutations.has_trait( trait_THRESH_MARLOSS ) || g->u.mutations.has_trait( trait_THRESH_MYCUS ) ) { z->friendly = 1; return true; } - if( ( g->u.has_trait( trait_MARLOSS ) ) && ( g->u.has_trait( trait_MARLOSS_BLUE ) ) && + if( ( g->u.mutations.has_trait( trait_MARLOSS ) ) && ( g->u.mutations.has_trait( trait_MARLOSS_BLUE ) ) && !g->u.crossed_threshold() ) { add_msg( m_info, _( "The %s seems to wave you toward the tower…" ), z->name() ); z->anger = 0; @@ -1768,7 +1768,7 @@ bool mattack::fungus_inject( monster *z ) bool mattack::fungus_bristle( monster *z ) { - if( g->u.has_trait( trait_THRESH_MARLOSS ) || g->u.has_trait( trait_THRESH_MYCUS ) ) { + if( g->u.mutations.has_trait( trait_THRESH_MARLOSS ) || g->u.mutations.has_trait( trait_THRESH_MYCUS ) ) { z->friendly = 1; } Creature *target = z->attack_target(); @@ -1865,10 +1865,10 @@ bool mattack::fungus_fortify( monster *z ) Creature *target = &g->u; bool mycus = false; bool peaceful = true; - if( g->u.has_trait( trait_THRESH_MARLOSS ) || g->u.has_trait( trait_THRESH_MYCUS ) ) { + if( g->u.mutations.has_trait( trait_THRESH_MARLOSS ) || g->u.mutations.has_trait( trait_THRESH_MYCUS ) ) { mycus = true; //No nifty support effects. Yet. This lets it rebuild hedges. } - if( ( g->u.has_trait( trait_MARLOSS ) ) && ( g->u.has_trait( trait_MARLOSS_BLUE ) ) && + if( ( g->u.mutations.has_trait( trait_MARLOSS ) ) && ( g->u.mutations.has_trait( trait_MARLOSS_BLUE ) ) && !g->u.crossed_threshold() && !mycus ) { // You have the other two. Is it really necessary for us to fight? add_msg( m_info, _( "The %s spreads its tendrils. It seems as though it's expecting you…" ), @@ -1881,9 +1881,9 @@ bool mattack::fungus_fortify( monster *z ) g->u.hurtall( 1, z ); add_msg( m_warning, _( "You see a clear golden liquid pump through the tendrils--and then lose consciousness." ) ); - g->u.unset_mutation( trait_MARLOSS ); - g->u.unset_mutation( trait_MARLOSS_BLUE ); - g->u.set_mutation( trait_THRESH_MARLOSS ); + g->u.mutations.unset_mutation( g->u, trait_MARLOSS ); + g->u.mutations.unset_mutation( g->u, trait_MARLOSS_BLUE ); + g->u.mutations.set_mutation( g->u, trait_THRESH_MARLOSS ); g->m.ter_set( g->u.pos(), t_marloss ); // We only show you the door. You walk through it on your own. g->memorial().add( @@ -2084,7 +2084,7 @@ bool mattack::dermatik( monster *z ) int swat_skill = ( foe->get_skill_level( skill_melee ) + foe->get_skill_level( skill_unarmed ) * 2 ) / 3; int player_swat = dice( swat_skill, 10 ); - if( foe->has_trait( trait_TAIL_CATTLE ) ) { + if( foe->mutations.has_trait( trait_TAIL_CATTLE ) ) { target->add_msg_if_player( _( "You swat at the %s with your tail!" ), z->name() ); ///\EFFECT_DEX increases chance of deflecting dermatik attack with TAIL_CATTLE @@ -2119,7 +2119,7 @@ bool mattack::dermatik( monster *z ) target->add_msg_if_player( m_bad, _( "The %1$s sinks its ovipositor into your %2$s!" ), z->name(), body_part_name_accusative( targeted ) ); - if( !foe->has_trait( trait_PARAIMMUNE ) || !foe->has_trait( trait_ACIDBLOOD ) ) { + if( !foe->mutations.has_trait( trait_PARAIMMUNE ) || !foe->mutations.has_trait( trait_ACIDBLOOD ) ) { foe->add_effect( effect_dermatik, 1_turns, targeted, true ); g->events().send( foe->getID() ); } @@ -2710,7 +2710,8 @@ bool mattack::gene_sting( monster *z ) bool hit = sting_shoot( z, target, dam, range ); if( hit ) { //Add checks if previous NPC/player conditions are removed - dynamic_cast( target )->mutate(); + Character *guy = target->as_character(); + guy->mutations.mutate( *guy ); } return true; @@ -3023,7 +3024,7 @@ bool mattack::photograph( monster *z ) // Badges should NOT be swappable between roles. // Hence separate checking. // If you are in fact listed as a police officer - if( g->u.has_trait( trait_id( "PROF_POLICE" ) ) ) { + if( g->u.mutations.has_trait( trait_id( "PROF_POLICE" ) ) ) { // And you're wearing your badge if( g->u.is_wearing( "badge_deputy" ) ) { if( one_in( 3 ) ) { @@ -3043,7 +3044,7 @@ bool mattack::photograph( monster *z ) } } - if( g->u.has_trait( trait_id( "PROF_PD_DET" ) ) ) { + if( g->u.mutations.has_trait( trait_id( "PROF_PD_DET" ) ) ) { // And you have your shield on if( g->u.is_wearing( "badge_detective" ) ) { if( one_in( 4 ) ) { @@ -3061,7 +3062,7 @@ bool mattack::photograph( monster *z ) return true; } } - } else if( g->u.has_trait( trait_id( "PROF_SWAT" ) ) ) { + } else if( g->u.mutations.has_trait( trait_id( "PROF_SWAT" ) ) ) { // And you're wearing your badge if( g->u.is_wearing( "badge_swat" ) ) { if( one_in( 3 ) ) { @@ -3078,7 +3079,7 @@ bool mattack::photograph( monster *z ) return true; } } - } else if( g->u.has_trait( trait_id( "PROF_CYBERCOP" ) ) ) { + } else if( g->u.mutations.has_trait( trait_id( "PROF_CYBERCOP" ) ) ) { // And you're wearing your badge if( g->u.is_wearing( "badge_cybercop" ) ) { if( one_in( 3 ) ) { @@ -3098,7 +3099,7 @@ bool mattack::photograph( monster *z ) } } - if( g->u.has_trait( trait_id( "PROF_FED" ) ) ) { + if( g->u.mutations.has_trait( trait_id( "PROF_FED" ) ) ) { // And you're wearing your badge if( g->u.is_wearing( "badge_marshal" ) ) { add_msg( m_info, _( "The %s flashes a LED and departs. The Feds got this." ), z->name() ); @@ -3234,7 +3235,7 @@ void mattack::frag( monster *z, Creature *target ) // This is for the bots, not if( target == &g->u ) { if( !z->has_effect( effect_targeted ) ) { - if( g->u.has_trait( trait_id( "PROF_CHURL" ) ) ) { + if( g->u.mutations.has_trait( trait_id( "PROF_CHURL" ) ) ) { //~ Potential grenading detected. add_msg( m_warning, _( "Thee eye o dat divil be upon me!" ) ); } else { diff --git a/src/mutation.cpp b/src/mutation.cpp index 01a6fd659db03..00b4b80f73f9f 100644 --- a/src/mutation.cpp +++ b/src/mutation.cpp @@ -1361,11 +1361,11 @@ void test_crossing_threshold( Character &guy, const mutation_category_trait &m_c std::string mutation_category = m_category.id; int total = 0; for( const auto &iter : mutation_category_trait::get_all() ) { - total += guy.mutation_category_level[ iter.first ]; + total += guy.mutations.get_cat_level( iter.first ); } // Threshold-breaching - const std::string &primary = guy.get_highest_category(); - int breach_power = guy.mutation_category_level[primary]; + const std::string &primary = guy.mutations.get_highest_category(); + int breach_power = guy.mutations.get_cat_level( primary ); // Only if you were pushing for more in your primary category. // You wanted to be more like it and less human. // That said, you're required to have hit third-stage dreams first. @@ -1381,18 +1381,18 @@ void test_crossing_threshold( Character &guy, const mutation_category_trait &m_c int breacher = breach_power + booster; if( x_in_y( breacher, total ) ) { guy.add_msg_if_player( m_good, - _( "Something strains mightily for a moment… and then… you're… FREE!" ) ); - guy.set_mutation( mutation_thresh ); + _( "Something strains mightily for a moment... and then... you're... FREE!" ) ); + guy.mutations.set_mutation( guy, mutation_thresh ); g->events().send( guy.getID(), m_category.id ); // Manually removing Carnivore, since it tends to creep in // This is because carnivore is a prerequisite for the // predator-style post-threshold mutations. - if( mutation_category == "URSINE" && guy.has_trait( trait_CARNIVORE ) ) { - guy.unset_mutation( trait_CARNIVORE ); + if( mutation_category == "URSINE" && guy.mutations.has_trait( trait_CARNIVORE ) ) { + guy.mutations.unset_mutation( guy, trait_CARNIVORE ); guy.add_msg_if_player( _( "Your appetite for blood fades." ) ); } } - } else if( guy.has_trait( trait_NOPAIN ) ) { + } else if( guy.mutations.has_trait( trait_NOPAIN ) ) { //~NOPAIN is a post-Threshold trait, so you shouldn't //~legitimately have it and get here! guy.add_msg_if_player( m_bad, _( "You feel extremely Bugged." ) ); diff --git a/src/mutation.h b/src/mutation.h index 77b5da9596dbc..6a923e025b3d7 100644 --- a/src/mutation.h +++ b/src/mutation.h @@ -12,6 +12,7 @@ #include "bodypart.h" #include "calendar.h" +#include "character.h" #include "damage.h" #include "string_id.h" #include "hash_utils.h" diff --git a/src/npc.cpp b/src/npc.cpp index a16191d446ccd..4ce7015e8bfc5 100644 --- a/src/npc.cpp +++ b/src/npc.cpp @@ -414,18 +414,18 @@ void npc::randomize( const npc_class_id &type ) starting_clothes( *this, myclass, male ); starting_inv( *this, myclass ); has_new_items = true; - empty_traits(); + mutations.empty_traits( *this ); // Add fixed traits - for( const auto &tid : trait_group::traits_from( myclass->traits ) ) { - set_mutation( tid ); + for( const trait_id &tid : trait_group::traits_from( myclass->traits ) ) { + mutations.set_mutation( *this, tid ); } // Run mutation rounds for( const auto &mr : type->mutation_rounds ) { int rounds = mr.second.roll(); for( int i = 0; i < rounds; ++i ) { - mutate_category( mr.first ); + mutations.mutate_category( *this, mr.first ); } } // Add bionics @@ -841,9 +841,9 @@ bool npc::can_read( const item &book, std::vector &fail_reasons ) } // Check for conditions that disqualify us - if( type->intel > 0 && has_trait( trait_ILLITERATE ) ) { + if( type->intel > 0 && mutations.has_trait( trait_ILLITERATE ) ) { fail_reasons.emplace_back( _( "I can't read!" ) ); - } else if( has_trait( trait_HYPEROPIC ) && !worn_with_flag( "FIX_FARSIGHT" ) && + } else if( mutations.has_trait( trait_HYPEROPIC ) && !worn_with_flag( "FIX_FARSIGHT" ) && !has_effect( effect_contacts ) && !has_bionic( bio_eye_optic ) ) { fail_reasons.emplace_back( _( "I can't read without my glasses." ) ); } else if( fine_detail_vision_mod() > 4 ) { @@ -867,7 +867,7 @@ int npc::time_to_read( const item &book, const player &reader ) const int retval = type->time * reading_speed; retval *= std::min( fine_detail_vision_mod(), reader.fine_detail_vision_mod() ); - if( type->intel > reader.get_int() && !reader.has_trait( trait_PROF_DICEMASTER ) ) { + if( type->intel > reader.get_int() && !reader.mutations.has_trait( trait_PROF_DICEMASTER ) ) { retval += type->time * ( type->intel - reader.get_int() ) * 100; } return retval; @@ -947,7 +947,7 @@ void npc::finish_read( item &book ) } if( ( skill_level == reading->level || !skill_level.can_train() ) || - ( ( has_trait( trait_id( "SCHIZOPHRENIC" ) ) || + ( ( mutations.has_trait( trait_id( "SCHIZOPHRENIC" ) ) || has_artifact_with( AEP_SCHIZO ) ) && one_in( 25 ) ) ) { if( display_messages ) { add_msg( m_info, _( "%s can no longer learn from %s." ), disp_name(), book.type_name() ); @@ -1221,15 +1221,15 @@ void npc::form_opinion( const player &u ) } } - if( u.has_trait( trait_SAPIOVORE ) ) { + if( u.mutations.has_trait( trait_SAPIOVORE ) ) { op_of_u.fear += 10; // Sapiovores = Scary } - if( u.has_trait( trait_TERRIFYING ) ) { + if( u.mutations.has_trait( trait_TERRIFYING ) ) { op_of_u.fear += 6; } int u_ugly = 0; - for( trait_id &mut : u.get_mutations() ) { + for( const trait_id &mut : u.mutations.get_mutations() ) { u_ugly += mut.obj().ugliness; } op_of_u.fear += u_ugly / 2; @@ -1501,7 +1501,7 @@ void npc::say( const std::string &line, const int priority ) const { std::string formatted_line = line; parse_tags( formatted_line, g->u, *this ); - if( has_trait( trait_id( "MUTE" ) ) ) { + if( mutations.has_trait( trait_id( "MUTE" ) ) ) { return; } @@ -2221,7 +2221,7 @@ int npc::print_info( const catacurses::window &w, int line, int vLines, int colu visibility_cap = round( dist * dist / 20.0 / ( per - 1 ) ); } - const auto trait_str = visible_mutations( visibility_cap ); + const std::string trait_str = mutations.visible_mutations( visibility_cap ); if( !trait_str.empty() ) { std::string mutations = _( "Traits: " ) + remove_color_tags( trait_str ); enumerate_print( mutations, c_green ); @@ -2391,9 +2391,9 @@ void npc::die( Creature *nkiller ) } if( killer == &g->u && ( !guaranteed_hostile() || hit_by_player ) ) { - bool cannibal = g->u.has_trait( trait_CANNIBAL ); - bool psycho = g->u.has_trait( trait_PSYCHOPATH ); - if( g->u.has_trait( trait_SAPIOVORE ) || psycho ) { + bool cannibal = g->u.mutations.has_trait( trait_CANNIBAL ); + bool psycho = g->u.mutations.has_trait( trait_PSYCHOPATH ); + if( g->u.mutations.has_trait( trait_SAPIOVORE ) || psycho ) { // No morale effect } else if( cannibal ) { g->u.add_morale( MORALE_KILLED_INNOCENT, -5, 0, 2_days, 3_hours ); @@ -2685,7 +2685,7 @@ void npc::on_load() disp_name() ); } } - if( has_trait( trait_id( "HALLUCINATION" ) ) ) { + if( mutations.has_trait( trait_id( "HALLUCINATION" ) ) ) { hallucination = true; } } @@ -2868,7 +2868,7 @@ bool npc::will_accept_from_player( const item &it ) const return false; } - if( is_minion() || g->u.has_trait( trait_id( "DEBUG_MIND_CONTROL" ) ) || + if( is_minion() || g->u.mutations.has_trait( trait_id( "DEBUG_MIND_CONTROL" ) ) || it.has_flag( "NPC_SAFE" ) ) { return true; } @@ -2952,7 +2952,7 @@ mfaction_id npc::get_monster_faction() const return player_fac.id(); } - if( has_trait( trait_id( "BEE" ) ) ) { + if( mutations.has_trait( trait_id( "BEE" ) ) ) { return bee_fac.id(); } diff --git a/src/npctalk.cpp b/src/npctalk.cpp index 9087759227bb5..f133a002b6496 100644 --- a/src/npctalk.cpp +++ b/src/npctalk.cpp @@ -362,7 +362,7 @@ void game::chat() } ); const int guard_count = guards.size(); - if( g->u.has_trait( trait_PROF_FOODP ) && !( g->u.is_wearing( itype_id( "foodperson_mask" ) ) || + if( g->u.mutations.has_trait( trait_PROF_FOODP ) && !( g->u.is_wearing( itype_id( "foodperson_mask" ) ) || g->u.is_wearing( itype_id( "foodperson_mask_on" ) ) ) ) { g->u.add_msg_if_player( m_warning, _( "You can't speak without your face!" ) ); return; @@ -658,7 +658,7 @@ void npc::talk_to_u( bool text_only, bool radio_contact ) set_attitude( NPCATT_NULL ); return; } - const bool has_mind_control = g->u.has_trait( trait_DEBUG_MIND_CONTROL ); + const bool has_mind_control = g->u.mutations.has_trait( trait_DEBUG_MIND_CONTROL ); // This is necessary so that we don't bug the player over and over if( get_attitude() == NPCATT_TALK ) { set_attitude( NPCATT_NULL ); @@ -766,12 +766,12 @@ void npc::talk_to_u( bool text_only, bool radio_contact ) } } - if( g->u.has_trait( trait_PROF_FOODP ) && !( g->u.is_wearing( itype_id( "foodperson_mask" ) ) || + if( g->u.mutations.has_trait( trait_PROF_FOODP ) && !( g->u.is_wearing( itype_id( "foodperson_mask" ) ) || g->u.is_wearing( itype_id( "foodperson_mask_on" ) ) ) ) { d.add_topic( "TALK_NOFACE" ); } - if( has_trait( trait_PROF_FOODP ) && !( is_wearing( itype_id( "foodperson_mask" ) ) || + if( mutations.has_trait( trait_PROF_FOODP ) && !( is_wearing( itype_id( "foodperson_mask" ) ) || is_wearing( itype_id( "foodperson_mask_on" ) ) ) ) { d.add_topic( "TALK_NPC_NOFACE" ); } @@ -1222,7 +1222,7 @@ void dialogue::gen_responses( const talk_topic &the_topic ) add_response_done( _( "Let's keep moving." ) ); } - if( g->u.has_trait( trait_DEBUG_MIND_CONTROL ) && !p->is_player_ally() ) { + if( g->u.mutations.has_trait( trait_DEBUG_MIND_CONTROL ) && !p->is_player_ally() ) { add_response( _( "OBEY ME!" ), "TALK_MIND_CONTROL" ); add_response_done( _( "Bye." ) ); } @@ -1269,10 +1269,10 @@ static int parse_mod( const dialogue &d, const std::string &attribute, const int int talk_trial::calc_chance( const dialogue &d ) const { player &u = *d.alpha; - if( u.has_trait( trait_DEBUG_MIND_CONTROL ) ) { + if( u.mutations.has_trait( trait_DEBUG_MIND_CONTROL ) ) { return 100; } - const social_modifiers &u_mods = u.get_mutation_social_mods(); + const social_modifiers &u_mods = u.mutations.get_mutation_social_mods(); npc &p = *d.beta; int chance = difficulty; @@ -1342,7 +1342,7 @@ int talk_trial::calc_chance( const dialogue &d ) const bool talk_trial::roll( dialogue &d ) const { player &u = *d.alpha; - if( type == TALK_TRIAL_NONE || u.has_trait( trait_DEBUG_MIND_CONTROL ) ) { + if( type == TALK_TRIAL_NONE || u.mutations.has_trait( trait_DEBUG_MIND_CONTROL ) ) { return true; } const int chance = calc_chance( d ); @@ -1814,7 +1814,7 @@ void talk_effect_fun_t::set_add_trait( JsonObject jo, const std::string &member, if( is_npc ) { actor = dynamic_cast( d.beta ); } - actor->set_mutation( trait_id( new_trait ) ); + actor->mutations.set_mutation( *actor, trait_id( new_trait ) ); }; } @@ -1826,7 +1826,7 @@ void talk_effect_fun_t::set_remove_trait( JsonObject jo, const std::string &memb if( is_npc ) { actor = dynamic_cast( d.beta ); } - actor->unset_mutation( trait_id( old_trait ) ); + actor->mutations.unset_mutation( *actor, trait_id( old_trait ) ); }; } @@ -3168,7 +3168,7 @@ std::string give_item_to( npc &p, bool allow_use, bool allow_carry ) return _( "How?" ); } - if( given.is_dangerous() && !g->u.has_trait( trait_DEBUG_MIND_CONTROL ) ) { + if( given.is_dangerous() && !g->u.mutations.has_trait( trait_DEBUG_MIND_CONTROL ) ) { return _( "Are you insane!?" ); } diff --git a/src/weather.cpp b/src/weather.cpp index 8c986551436cf..b1ded22028f14 100644 --- a/src/weather.cpp +++ b/src/weather.cpp @@ -88,7 +88,7 @@ void weather_effect::glare( sun_intensity intensity ) //apply final glare effect if( dur > 0_turns && effect != nullptr ) { //enhance/reduce by some traits - if( g->u.has_trait( trait_CEPH_VISION ) ) { + if( g->u.mutations.has_trait( trait_CEPH_VISION ) ) { dur = dur * 2; } g->u.add_env_effect( *effect, bp_eyes, 2, dur ); @@ -356,7 +356,7 @@ static void fill_water_collectors( int mmPerHour, bool acid ) static void wet_player( int amount ) { if( !is_player_outside() || - g->u.has_trait( trait_FEATHERS ) || + g->u.mutations.has_trait( trait_FEATHERS ) || g->u.weapon.has_flag( "RAIN_PROTECT" ) || ( !one_in( 50 ) && g->u.worn_with_flag( "RAINPROOF" ) ) ) { return; @@ -449,10 +449,10 @@ void weather_effect::thunder() add_msg( _( "You hear a distant rumble of thunder." ) ); sfx::play_variant_sound( "environment", "thunder_far", 80, rng( 0, 359 ) ); } else if( one_in( std::max( roll_remainder( 2.0f * g->get_levz() / - g->u.mutation_value( "hearing_modifier" ) ), 1 ) ) ) { + g->u.mutations.mutation_value( "hearing_modifier" ) ), 1 ) ) ) { add_msg( _( "You hear a rumble of thunder from above." ) ); sfx::play_variant_sound( "environment", "thunder_far", - ( 80 * g->u.mutation_value( "hearing_modifier" ) ), rng( 0, 359 ) ); + ( 80 * g->u.mutations.mutation_value( "hearing_modifier" ) ), rng( 0, 359 ) ); } } } diff --git a/src/wish.cpp b/src/wish.cpp index b1aa704bc68b2..4f1304abd1e2b 100644 --- a/src/wish.cpp +++ b/src/wish.cpp @@ -57,17 +57,17 @@ class wish_mutate_callback: public uilist_callback wish_mutate_callback() = default; bool key( const input_context &, const input_event &event, int entnum, uilist *menu ) override { - if( event.get_first_input() == 't' && p->has_trait( vTraits[ entnum ] ) ) { - if( p->has_base_trait( vTraits[ entnum ] ) ) { - p->toggle_trait( vTraits[ entnum ] ); - p->unset_mutation( vTraits[ entnum ] ); + if( event.get_first_input() == 't' && p->mutations.has_trait( vTraits[ entnum ] ) ) { + if( p->mutations.has_base_trait( vTraits[ entnum ] ) ) { + p->mutations.toggle_trait( *p, vTraits[ entnum ] ); + p->mutations.unset_mutation( *p, vTraits[ entnum ] ); } else { - p->set_mutation( vTraits[ entnum ] ); - p->toggle_trait( vTraits[ entnum ] ); + p->mutations.set_mutation( *p, vTraits[ entnum ] ); + p->mutations.toggle_trait( *p, vTraits[ entnum ] ); } - menu->entries[ entnum ].text_color = p->has_trait( vTraits[ entnum ] ) ? c_green : menu->text_color; - menu->entries[ entnum ].extratxt.txt = p->has_base_trait( vTraits[ entnum ] ) ? "T" : ""; + menu->entries[ entnum ].text_color = p->mutations.has_trait( vTraits[ entnum ] ) ? c_green : menu->text_color; + menu->entries[ entnum ].extratxt.txt = p->mutations.has_base_trait( vTraits[ entnum ] ) ? "T" : ""; return true; } return false; @@ -79,7 +79,7 @@ class wish_mutate_callback: public uilist_callback padding = std::string( menu->pad_right - 1, ' ' ); for( auto &traits_iter : mutation_branch::get_all() ) { vTraits.push_back( traits_iter.id ); - pTraits[traits_iter.id] = p->has_trait( traits_iter.id ); + pTraits[traits_iter.id] = p->mutations.has_trait( traits_iter.id ); } } const mutation_branch &mdata = vTraits[entnum].obj(); @@ -210,9 +210,9 @@ void debug_menu::wishmutate( player *p ) wmenu.entries[ c ].extratxt.left = 1; wmenu.entries[ c ].extratxt.txt.clear(); wmenu.entries[ c ].extratxt.color = c_light_green; - if( p->has_trait( traits_iter.id ) ) { + if( p->mutations.has_trait( traits_iter.id ) ) { wmenu.entries[ c ].text_color = c_green; - if( p->has_base_trait( traits_iter.id ) ) { + if( p->mutations.has_base_trait( traits_iter.id ) ) { wmenu.entries[ c ].extratxt.txt = "T"; } } @@ -236,27 +236,27 @@ void debug_menu::wishmutate( player *p ) const bool profession = mdata.profession; // Manual override for the threshold-gaining if( threshold || profession ) { - if( p->has_trait( mstr ) ) { + if( p->mutations.has_trait( mstr ) ) { do { - p->remove_mutation( mstr ); + p->mutations.remove_mutation( *p, mstr ); rc++; - } while( p->has_trait( mstr ) && rc < 10 ); + } while( p->mutations.has_trait( mstr ) && rc < 10 ); } else { do { - p->set_mutation( mstr ); + p->mutations.set_mutation( *p, mstr ); rc++; - } while( !p->has_trait( mstr ) && rc < 10 ); + } while( !p->mutations.has_trait( mstr ) && rc < 10 ); } - } else if( p->has_trait( mstr ) ) { + } else if( p->mutations.has_trait( mstr ) ) { do { - p->remove_mutation( mstr ); + p->mutations.remove_mutation( *p, mstr ); rc++; - } while( p->has_trait( mstr ) && rc < 10 ); + } while( p->mutations.has_trait( mstr ) && rc < 10 ); } else { do { - p->mutate_towards( mstr ); + p->mutations.mutate_towards( *p, mstr ); rc++; - } while( !p->has_trait( mstr ) && rc < 10 ); + } while( !p->mutations.has_trait( mstr ) && rc < 10 ); } cb.msg = string_format( _( "%s Mutation changes: %d" ), mstr.c_str(), rc ); uistate.wishmutate_selected = wmenu.selected; @@ -264,10 +264,10 @@ void debug_menu::wishmutate( player *p ) for( size_t i = 0; i < cb.vTraits.size(); i++ ) { uilist_entry &entry = wmenu.entries[ i ]; entry.extratxt.txt.clear(); - if( p->has_trait( cb.vTraits[ i ] ) ) { + if( p->mutations.has_trait( cb.vTraits[ i ] ) ) { entry.text_color = c_green; cb.pTraits[ cb.vTraits[ i ] ] = true; - if( p->has_base_trait( cb.vTraits[ i ] ) ) { + if( p->mutations.has_base_trait( cb.vTraits[ i ] ) ) { entry.extratxt.txt = "T"; } } else { From 5a33e08d53d3f48db3041ccaed90e6d38f53c309 Mon Sep 17 00:00:00 2001 From: KorGgenT Date: Thu, 17 Oct 2019 21:48:35 -0400 Subject: [PATCH 24/34] water tolerance --- src/character_mutations.cpp | 4 ++++ src/character_mutations.h | 12 ++++++++++++ 2 files changed, 16 insertions(+) diff --git a/src/character_mutations.cpp b/src/character_mutations.cpp index 7baebeab0aa2d..513452a0dacad 100644 --- a/src/character_mutations.cpp +++ b/src/character_mutations.cpp @@ -49,3 +49,7 @@ int character_mutations::get_cat_level( const std::string &category ) const return iter->second; } } + +std::array character_mutations::get_mut_drench( body_part bp ) const { + return mut_drench[bp]; +} diff --git a/src/character_mutations.h b/src/character_mutations.h index bd3f029f5e6c0..a9b891be10d56 100644 --- a/src/character_mutations.h +++ b/src/character_mutations.h @@ -2,6 +2,7 @@ #ifndef CHARACTER_MUTATIONS_H #define CHARACTER_MUTATIONS_H +#include "bodypart.h" #include "damage.h" #include "type_id.h" @@ -43,7 +44,15 @@ class character_mutations void serialize( JsonOut &json ) const; void deserialize( JsonIn &jsin ); }; + // Drench cache + enum water_tolerance { + WT_IGNORED = 0, + WT_NEUTRAL, + WT_GOOD, + NUM_WATER_TOLERANCE + }; + character_mutations() = default; character_mutations( const std::unordered_map &my_mutations, const std::unordered_set &my_traits ) { this->my_mutations = my_mutations; @@ -167,6 +176,7 @@ class character_mutations trait_data get_trait_data( const trait_id &mut ) const; int get_cat_level( const std::string &category ) const; + std::array get_mut_drench( body_part bp ) const; private: /** * Traits / mutations of the character. Key is the mutation id (it's also a valid @@ -185,6 +195,8 @@ class character_mutations std::vector cached_mutations; std::map mutation_category_level; + + std::array, num_bp> mut_drench; }; #endif From 52cff4a0f59378696355a2c26c1f6b42c23ece5b Mon Sep 17 00:00:00 2001 From: KorGgenT Date: Thu, 17 Oct 2019 21:56:29 -0400 Subject: [PATCH 25/34] fix function calls --- src/character.cpp | 10 +- src/character.h | 9 - src/character_mutations.cpp | 4 +- src/consumption.cpp | 30 +- src/game.cpp | 2629 +++++++++++++++++------------------ src/monattack.cpp | 18 +- src/npctalk.cpp | 12 +- src/player.cpp | 8 +- src/veh_interact.cpp | 14 +- src/wish.cpp | 3 +- 10 files changed, 1355 insertions(+), 1382 deletions(-) diff --git a/src/character.cpp b/src/character.cpp index 41f1aa1e4bb21..7ce30c99535b0 100644 --- a/src/character.cpp +++ b/src/character.cpp @@ -4486,9 +4486,9 @@ void character_mutations::drench_mut_calc() } } - mut_drench[bp][Character::water_tolerance::WT_GOOD] = Character::water_tolerance::good; - mut_drench[bp][Character::water_tolerance::WT_NEUTRAL] = Character::water_tolerance::neutral; - mut_drench[bp][Character::water_tolerance::WT_IGNORED] = Character::water_tolerance::ignored; + mut_drench[bp][water_tolerance::WT_GOOD] = good; + mut_drench[bp][water_tolerance::WT_NEUTRAL] = neutral; + mut_drench[bp][water_tolerance::WT_IGNORED] = ignored; } } @@ -5040,7 +5040,7 @@ void Character::update_vitamins( const vitamin_id &vit ) void Character::rooted_message() const { bool wearing_shoes = is_wearing_shoes( side::LEFT ) || is_wearing_shoes( side::RIGHT ); - if( ( has_trait( trait_ROOTS2 ) || has_trait( trait_ROOTS3 ) ) && + if( ( mutations.has_trait( trait_ROOTS2 ) || mutations.has_trait( trait_ROOTS3 ) ) && g->m.has_flag( "PLOWABLE", pos() ) && !wearing_shoes ) { add_msg( m_info, _( "You sink your roots into the soil." ) ); @@ -5051,7 +5051,7 @@ void Character::rooted() // Should average a point every two minutes or so; ground isn't uniformly fertile { double shoe_factor = footwear_factor(); - if( ( has_trait( trait_ROOTS2 ) || has_trait( trait_ROOTS3 ) ) && + if( ( mutations.has_trait( trait_ROOTS2 ) || mutations.has_trait( trait_ROOTS3 ) ) && g->m.has_flag( "PLOWABLE", pos() ) && shoe_factor != 1.0 ) { if( one_in( 96 ) ) { vitamin_mod( vitamin_id( "iron" ), 1, true ); diff --git a/src/character.h b/src/character.h index 9af87bba6fd92..3bf1ef905e0b2 100644 --- a/src/character.h +++ b/src/character.h @@ -526,13 +526,6 @@ class Character : public Creature, public visitable bool made_of( const material_id &m ) const override; bool made_of_any( const std::set &ms ) const override; - // Drench cache - enum water_tolerance { - WT_IGNORED = 0, - WT_NEUTRAL, - WT_GOOD, - NUM_WATER_TOLERANCE - }; inline int posx() const override { return position.x; } @@ -577,8 +570,6 @@ class Character : public Creature, public visitable * is added to existing work items. */ void item_encumb( std::array &vals, const item &new_item ) const; - - std::array, num_bp> mut_drench; public: /** Applies stat mods to character. */ void apply_mods( const trait_id &mut, bool add_remove ); diff --git a/src/character_mutations.cpp b/src/character_mutations.cpp index 513452a0dacad..3c2b566f79958 100644 --- a/src/character_mutations.cpp +++ b/src/character_mutations.cpp @@ -50,6 +50,8 @@ int character_mutations::get_cat_level( const std::string &category ) const } } -std::array character_mutations::get_mut_drench( body_part bp ) const { +std::array +character_mutations::get_mut_drench( body_part bp ) const +{ return mut_drench[bp]; } diff --git a/src/consumption.cpp b/src/consumption.cpp index 70d930a5c2236..97f0fbd4079c1 100644 --- a/src/consumption.cpp +++ b/src/consumption.cpp @@ -95,7 +95,8 @@ int player::stomach_capacity() const return -620; } - if( mutations.has_trait( trait_id( "GOURMAND" ) ) || mutations.has_trait( trait_id( "HIBERNATE" ) ) ) { + if( mutations.has_trait( trait_id( "GOURMAND" ) ) || + mutations.has_trait( trait_id( "HIBERNATE" ) ) ) { return -60; } @@ -190,7 +191,8 @@ std::pair player::fun_for( const item &comest ) const } // Rotten food should be pretty disgusting const float relative_rot = comest.get_relative_rot(); - if( relative_rot > 1.0f && !mutations.has_trait( trait_SAPROPHAGE ) && !mutations.has_trait( trait_SAPROVORE ) ) { + if( relative_rot > 1.0f && !mutations.has_trait( trait_SAPROPHAGE ) && + !mutations.has_trait( trait_SAPROVORE ) ) { const float rottedness = clamp( 2 * relative_rot - 2.0f, 0.1f, 1.0f ); // Three effects: // penalty for rot goes from -2 to -20 @@ -529,7 +531,8 @@ ret_val player::can_eat( const item &food ) const _( "Eww. Inedible plant stuff!" ) ); } - if( ( mutations.has_trait( trait_id( "HERBIVORE" ) ) || mutations.has_trait( trait_id( "RUMINANT" ) ) ) && + if( ( mutations.has_trait( trait_id( "HERBIVORE" ) ) || + mutations.has_trait( trait_id( "RUMINANT" ) ) ) && food.has_any_flag( herbivore_blacklist ) ) { // Like non-cannibal, but more strict! return ret_val::make_failure( INEDIBLE_MUTATION, @@ -682,7 +685,8 @@ bool player::eat( item &food, bool force ) const bool saprophage = mutations.has_trait( trait_id( "SAPROPHAGE" ) ); if( spoiled && !saprophage ) { add_msg_if_player( m_bad, _( "Ick, this %s doesn't taste so good..." ), food.tname() ); - if( !mutations.has_trait( trait_id( "SAPROVORE" ) ) && !mutations.has_trait( trait_id( "EATDEAD" ) ) && + if( !mutations.has_trait( trait_id( "SAPROVORE" ) ) && + !mutations.has_trait( trait_id( "EATDEAD" ) ) && ( !has_bionic( bio_digestion ) || one_in( 3 ) ) ) { add_effect( effect_foodpoison, rng( 6_minutes, ( nutr + 1 ) * 6_minutes ) ); } @@ -703,7 +707,8 @@ bool player::eat( item &food, bool force ) if( drinkable || chew ) { // Those bonuses/penalties only apply to food // Not to smoking weed or applying bandages! - if( mutations.has_trait( trait_id( "MOUTH_TENTACLES" ) ) || mutations.has_trait( trait_id( "MANDIBLES" ) ) || + if( mutations.has_trait( trait_id( "MOUTH_TENTACLES" ) ) || + mutations.has_trait( trait_id( "MANDIBLES" ) ) || mutations.has_trait( trait_id( "FANGS_SPIDER" ) ) ) { mealtime /= 2; } else if( mutations.has_trait( trait_id( "SHARKTEETH" ) ) ) { @@ -913,12 +918,12 @@ bool player::eat( item &food, bool force ) add_morale( MORALE_NO_DIGEST, -75, -400, 30_minutes, 24_minutes ); } if( food.has_flag( "URSINE_HONEY" ) && ( !crossed_threshold() || - mutations.has_trait( trait_id( "THRESH_URSINE" ) ) ) && + mutations.has_trait( trait_id( "THRESH_URSINE" ) ) ) && mutations.get_cat_level( "URSINE" ) > 40 ) { //Need at least 5 bear mutations for effect to show, to filter out mutations in common with other categories int honey_fun = mutations.has_trait( trait_id( "THRESH_URSINE" ) ) ? std::min( mutations.get_cat_level( "URSINE" ) / 8, 20 ) : - mutations.get_cat_level( "URSINE" ) / 12; + mutations.get_cat_level( "URSINE" ) / 12; if( honey_fun < 10 ) { add_msg_if_player( m_good, _( "You find the sweet taste of honey surprisingly palatable." ) ); } else { @@ -928,7 +933,8 @@ bool player::eat( item &food, bool force ) } // chance to become parasitised - if( !will_vomit && !( has_bionic( bio_digestion ) || mutations.has_trait( trait_id( "PARAIMMUNE" ) ) ) ) { + if( !will_vomit && !( has_bionic( bio_digestion ) || + mutations.has_trait( trait_id( "PARAIMMUNE" ) ) ) ) { if( food.get_comestible()->parasites > 0 && !food.has_flag( "NO_PARASITES" ) && one_in( food.get_comestible()->parasites ) ) { switch( rng( 0, 3 ) ) { @@ -975,8 +981,9 @@ void player::modify_stimulation( const islot_comestible &comest ) stim = std::min( comest.stim * 3, stim + comest.stim ); } } - if( mutations.has_trait( trait_id( "STIMBOOST" ) ) && ( stim > 30 ) && ( ( comest.add == ADD_CAFFEINE ) - || ( comest.add == ADD_SPEED ) || ( comest.add == ADD_COKE ) || ( comest.add == ADD_CRACK ) ) ) { + if( mutations.has_trait( trait_id( "STIMBOOST" ) ) && ( stim > 30 ) && + ( ( comest.add == ADD_CAFFEINE ) + || ( comest.add == ADD_SPEED ) || ( comest.add == ADD_COKE ) || ( comest.add == ADD_CRACK ) ) ) { int hallu_duration = ( stim - comest.stim < 30 ) ? stim - 30 : comest.stim; add_effect( effect_visuals, hallu_duration * 30_minutes ); std::vector stimboost_msg{ _( "The shadows are getting ever closer." ), @@ -1035,7 +1042,8 @@ bool player::consume_effects( item &food ) // used to cap nutrition and thirst, but no longer return false; } - if( ( mutations.has_trait( trait_id( "HERBIVORE" ) ) || mutations.has_trait( trait_id( "RUMINANT" ) ) ) && + if( ( mutations.has_trait( trait_id( "HERBIVORE" ) ) || + mutations.has_trait( trait_id( "RUMINANT" ) ) ) && food.has_any_flag( herbivore_blacklist ) ) { // No good can come of this. return false; diff --git a/src/game.cpp b/src/game.cpp index 79c4c3936ac2c..d086490390455 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -789,9 +789,9 @@ bool game::start_game() } //Create mutation_category_level - u.set_highest_cat_level(); + u.mutations.set_highest_cat_level(); //Calculate mutation drench protection stats - u.drench_mut_calc(); + u.mutations.drench_mut_calc(); if( scen->has_flag( "FIRE_START" ) ) { start_loc.burn( omtstart, 3, 3 ); } @@ -961,7 +961,7 @@ void game::create_starting_npcs() //This sets the NPC mission. This NPC remains in the starting location. tmp->mission = NPC_MISSION_SHELTER; tmp->chatbin.first_topic = "TALK_SHELTER"; - tmp->toggle_trait( trait_id( "NPC_STARTING_NPC" ) ); + tmp->mutations.toggle_trait( *tmp, trait_id( "NPC_STARTING_NPC" ) ); tmp->set_fac( faction_id( "no_faction" ) ); //One random starting NPC mission tmp->add_new_mission( mission::reserve_random( ORIGIN_OPENER_NPC, tmp->global_omt_location(), @@ -996,7 +996,8 @@ bool game::cleanup_at_end() if( u.has_amount( "holybook_bible1", 1 ) || u.has_amount( "holybook_bible2", 1 ) || u.has_amount( "holybook_bible3", 1 ) ) { - if( !( u.has_trait( trait_id( "CANNIBAL" ) ) || u.has_trait( trait_id( "PSYCHOPATH" ) ) ) ) { + if( !( u.mutations.has_trait( trait_id( "CANNIBAL" ) ) || + u.mutations.has_trait( trait_id( "PSYCHOPATH" ) ) ) ) { vRip.emplace_back( " _______ ___" ); vRip.emplace_back( " < `/ |" ); vRip.emplace_back( " > _ _ (" ); @@ -1495,7 +1496,7 @@ bool game::do_turn() // No-scent debug mutation has to be processed here or else it takes time to start working if( !u.has_active_bionic( bionic_id( "bio_scent_mask" ) ) && - !u.has_trait( trait_id( "DEBUG_NOSCENT" ) ) ) { + !u.mutations.has_trait( trait_id( "DEBUG_NOSCENT" ) ) ) { scent.set( u.pos(), u.scent ); overmap_buffer.set_scent( u.global_omt_location(), u.scent ); } @@ -3954,7 +3955,7 @@ void game::mon_info( const catacurses::window &w, int hor_padding ) monster &critter = *new_seen_mon.back(); cancel_activity_or_ignore_query( distraction_type::hostile_spotted, string_format( _( "%s spotted!" ), critter.name() ) ); - if( u.has_trait( trait_id( "M_DEFENDER" ) ) && critter.type->in_species( PLANT ) ) { + if( u.mutations.has_trait( trait_id( "M_DEFENDER" ) ) && critter.type->in_species( PLANT ) ) { add_msg( m_warning, _( "We have detected a %s - an enemy of the Mycus!" ), critter.name() ); if( !u.has_effect( effect_adrenaline_mycus ) ) { u.add_effect( effect_adrenaline_mycus, 30_minutes ); @@ -4436,7 +4437,7 @@ void game::knockback( std::vector &traj, int force, int stun, int dam_ targ->name ); } } else if( u.posx() == traj_front.x && u.posy() == traj_front.y && - ( u.has_trait( trait_LEG_TENT_BRACE ) && ( !u.footwear_factor() || + ( u.mutations.has_trait( trait_LEG_TENT_BRACE ) && ( !u.footwear_factor() || ( u.footwear_factor() == .5 && one_in( 2 ) ) ) ) ) { add_msg( _( "%s collided with you, and barely dislodges your tentacles!" ), targ->name ); force_remaining = 1; @@ -4531,7 +4532,7 @@ void game::knockback( std::vector &traj, int force, int stun, int dam_ void game::use_computer( const tripoint &p ) { - if( u.has_trait( trait_id( "ILLITERATE" ) ) ) { + if( u.mutations.has_trait( trait_id( "ILLITERATE" ) ) ) { add_msg( m_info, _( "You can not read a computer screen!" ) ); return; } @@ -4540,7 +4541,7 @@ void game::use_computer( const tripoint &p ) add_msg( m_info, _( "You can not see a computer screen!" ) ); return; } - if( u.has_trait( trait_id( "HYPEROPIC" ) ) && !u.worn_with_flag( "FIX_FARSIGHT" ) && + if( u.mutations.has_trait( trait_id( "HYPEROPIC" ) ) && !u.worn_with_flag( "FIX_FARSIGHT" ) && !u.has_effect( effect_contacts ) && !u.has_bionic( bionic_id( "bio_eye_optic" ) ) ) { add_msg( m_info, _( "You'll need to put on reading glasses before you can see the screen." ) ); return; @@ -5868,7 +5869,7 @@ void game::print_terrain_info( const tripoint &lp, const catacurses::window &w_l if( !signage.empty() ) { trim_and_print( w_look, point( column, ++lines ), max_width, c_dark_gray, // NOLINTNEXTLINE(cata-text-style): the question mark does not end a sentence - u.has_trait( trait_ILLITERATE ) ? _( "Sign: ???" ) : _( "Sign: %s" ), signage ); + u.mutations.has_trait( trait_ILLITERATE ) ? _( "Sign: ???" ) : _( "Sign: %s" ), signage ); } if( m.has_zlevels() && lp.z > -OVERMAP_DEPTH && !m.has_floor( lp ) ) { @@ -8405,7 +8406,8 @@ void game::eat( item_location( *menu )( player &p ) ) void game::eat( item_location( *menu )( player &p ), int pos ) { - if( ( u.has_active_mutation( trait_RUMINANT ) || u.has_active_mutation( trait_GRAZER ) ) && + if( ( u.mutations.has_active_mutation( trait_RUMINANT ) || + u.mutations.has_active_mutation( trait_GRAZER ) ) && ( m.ter( u.pos() ) == t_underbrush || m.ter( u.pos() ) == t_shrub ) ) { if( u.get_hunger() < 20 ) { add_msg( _( "You're too full to eat the leaves from the %s." ), m.ter( u.pos() )->name() ); @@ -8419,7 +8421,7 @@ void game::eat( item_location( *menu )( player &p ), int pos ) return; } } - if( u.has_active_mutation( trait_GRAZER ) && ( m.ter( u.pos() ) == t_grass || + if( u.mutations.has_active_mutation( trait_GRAZER ) && ( m.ter( u.pos() ) == t_grass || m.ter( u.pos() ) == t_grass_long || m.ter( u.pos() ) == t_grass_tall ) ) { if( u.get_hunger() < 8 ) { add_msg( _( "You're too full to graze." ) ); @@ -8440,7 +8442,7 @@ void game::eat( item_location( *menu )( player &p ), int pos ) return; } } - if( u.has_active_mutation( trait_GRAZER ) ) { + if( u.mutations.has_active_mutation( trait_GRAZER ) ) { if( m.ter( u.pos() ) == t_grass_golf ) { add_msg( _( "This grass is too short to graze." ) ); return; @@ -8846,7 +8848,7 @@ bool game::check_safe_mode_allowed( bool repeat_safe_mode_warnings ) if( u.has_effect( effect_laserlocked ) ) { // Automatic and mandatory safemode. Make BLOODY sure the player notices! - if( u.get_int_base() < 5 || u.has_trait( trait_id( "PROF_CHURL" ) ) ) { + if( u.get_int_base() < 5 || u.mutations.has_trait( trait_id( "PROF_CHURL" ) ) ) { add_msg( game_message_params{ m_warning, gmf_bypass_cooldown }, _( "There's an angry red dot on your body, %s to brush it off." ), msg_ignore ); } else { @@ -9175,9 +9177,10 @@ bool game::walk_move( const tripoint &dest_loc ) const bool fungus = m.has_flag_ter_or_furn( "FUNGUS", u.pos() ) || m.has_flag_ter_or_furn( "FUNGUS", dest_loc ); //fungal furniture has no slowing effect on mycus characters - const bool slowed = ( ( !u.has_trait( trait_PARKOUR ) && ( mcost_to > 2 || mcost_from > 2 ) ) || + const bool slowed = ( ( !u.mutations.has_trait( trait_PARKOUR ) && ( mcost_to > 2 || + mcost_from > 2 ) ) || mcost_to > 4 || mcost_from > 4 ) && - !( u.has_trait( trait_M_IMMUNE ) && fungus ); + !( u.mutations.has_trait( trait_M_IMMUNE ) && fungus ); if( slowed && !u.is_mounted() ) { // Unless u.pos() has a higher movecost than dest_loc, state that dest_loc is the cause if( mcost_to >= mcost_from ) { @@ -9200,7 +9203,7 @@ bool game::walk_move( const tripoint &dest_loc ) } } } - if( !u.is_mounted() && u.has_trait( trait_id( "LEG_TENT_BRACE" ) ) && + if( !u.is_mounted() && u.mutations.has_trait( trait_id( "LEG_TENT_BRACE" ) ) && ( !u.footwear_factor() || ( u.footwear_factor() == .5 && one_in( 2 ) ) ) ) { // DX and IN are long suits for Cephalopods, @@ -9215,9 +9218,9 @@ bool game::walk_move( const tripoint &dest_loc ) u.mod_fatigue( 1 ); } } - if( !u.has_artifact_with( AEP_STEALTH ) && !u.has_trait( trait_id( "DEBUG_SILENT" ) ) ) { + if( !u.has_artifact_with( AEP_STEALTH ) && !u.mutations.has_trait( trait_id( "DEBUG_SILENT" ) ) ) { int volume = u.is_stealthy() ? 3 : 6; - volume *= u.mutation_value( "noise_modifier" ); + volume *= u.mutations.mutation_value( "noise_modifier" ); if( volume > 0 ) { if( u.is_wearing( "rm13_armor_on" ) ) { volume = 2; @@ -9324,14 +9327,14 @@ point game::place_player( const tripoint &dest_loc ) } std::string signage = m.get_signage( dest_loc ); if( !signage.empty() ) { - if( !u.has_trait( trait_ILLITERATE ) ) { + if( !u.mutations.has_trait( trait_ILLITERATE ) ) { add_msg( m_info, _( "The sign says: %s" ), signage ); } else { add_msg( m_info, _( "There is a sign here, but you are unable to read it." ) ); } } if( m.has_graffiti_at( dest_loc ) ) { - if( !u.has_trait( trait_ILLITERATE ) ) { + if( !u.mutations.has_trait( trait_ILLITERATE ) ) { add_msg( m_info, _( "Written here: %s" ), m.graffiti_at( dest_loc ) ); } else { add_msg( m_info, _( "Something is written here, but you are unable to read it." ) ); @@ -9354,7 +9357,7 @@ point game::place_player( const tripoint &dest_loc ) } ///\EFFECT_DEX increases chance of avoiding cuts on sharp terrain if( m.has_flag( "SHARP", dest_loc ) && !one_in( 3 ) && !x_in_y( 1 + u.dex_cur / 2.0, 40 ) && - ( !u.in_vehicle && !g->m.veh_at( dest_loc ) ) && ( !u.has_trait( trait_PARKOUR ) || + ( !u.in_vehicle && !g->m.veh_at( dest_loc ) ) && ( !u.mutations.has_trait( trait_PARKOUR ) || one_in( 4 ) ) ) { if( u.is_mounted() ) { add_msg( _( "Your %s gets cut!" ), u.mounted_creature->get_name() ); @@ -9367,9 +9370,10 @@ point game::place_player( const tripoint &dest_loc ) body_part_name_accusative( bp ), m.has_flag_ter( "SHARP", dest_loc ) ? m.tername( dest_loc ) : m.furnname( dest_loc ) ); - if( ( u.has_trait( trait_INFRESIST ) ) && ( one_in( 1024 ) ) ) { + if( ( u.mutations.has_trait( trait_INFRESIST ) ) && ( one_in( 1024 ) ) ) { u.add_effect( effect_tetanus, 1_turns, num_bp, true ); - } else if( ( !u.has_trait( trait_INFIMMUNE ) || !u.has_trait( trait_INFRESIST ) ) && + } else if( ( !u.mutations.has_trait( trait_INFIMMUNE ) || + !u.mutations.has_trait( trait_INFRESIST ) ) && ( one_in( 256 ) ) ) { u.add_effect( effect_tetanus, 1_turns, num_bp, true ); } @@ -9632,7 +9636,7 @@ point game::place_player( const tripoint &dest_loc ) if( vp1.part_with_feature( "CONTROLS", true ) && u.in_vehicle && !u.is_mounted() ) { add_msg( _( "There are vehicle controls here." ) ); - if( !u.has_trait( trait_id( "WAYFARER" ) ) ) { + if( !u.mutations.has_trait( trait_id( "WAYFARER" ) ) ) { add_msg( m_info, _( "%s to drive." ), press_x( ACTION_CONTROL_VEHICLE ) ); } } else if( vp1.part_with_feature( "CONTROLS", true ) && u.in_vehicle && @@ -10562,7 +10566,7 @@ cata::optional game::find_or_make_stairs( map &mp, const int z_after, return cata::nullopt; } - if( u.has_trait( trait_id( "WEB_RAPPEL" ) ) ) { + if( u.mutations.has_trait( trait_id( "WEB_RAPPEL" ) ) ) { if( query_yn( _( "There is a sheer drop halfway down. Web-descend?" ) ) ) { rope_ladder = true; if( ( rng( 4, 8 ) ) < u.get_skill_level( skill_dodge ) ) { @@ -10573,1495 +10577,1454 @@ cata::optional game::find_or_make_stairs( map &mp, const int z_after, } else { return cata::nullopt; } - } else if( u.has_trait( trait_VINES2 ) || u.has_trait( trait_VINES3 ) ) { + } else if( u.mutations.has_trait( trait_VINES2 ) || u.mutations.has_trait( trait_VINES3 ) ) { if( query_yn( _( "There is a sheer drop halfway down. Use your vines to descend?" ) ) ) { + <<< <<< < HEAD if( u.has_trait( trait_VINES2 ) ) { if( query_yn( _( "Detach a vine? It'll hurt, but you'll be able to climb back up…" ) ) ) { + == == == = + if( u.mutations.has_trait( trait_VINES2 ) ) { + if( query_yn( _( "Detach a vine? It'll hurt, but you'll be able to climb back up..." ) ) ) { + >>> >>> > fix function calls + rope_ladder = true; + add_msg( m_bad, _( "You descend on your vines, though leaving a part of you behind stings." ) ); + u.mod_pain( 5 ); + u.apply_damage( nullptr, bp_torso, 5 ); + u.mod_stored_nutr( 10 ); + u.mod_thirst( 10 ); + } else { + add_msg( _( "You gingerly descend using your vines." ) ); + } + } else { + add_msg( _( "You effortlessly lower yourself and leave a vine rooted for future use." ) ); + rope_ladder = true; + u.mod_stored_nutr( 10 ); + u.mod_thirst( 10 ); + } + } else { + return cata::nullopt; + } + } else if( u.has_amount( "grapnel", 1 ) ) { + if( query_yn( _( "There is a sheer drop halfway down. Climb your grappling hook down?" ) ) ) { rope_ladder = true; - add_msg( m_bad, _( "You descend on your vines, though leaving a part of you behind stings." ) ); - u.mod_pain( 5 ); - u.apply_damage( nullptr, bp_torso, 5 ); - u.mod_stored_nutr( 10 ); - u.mod_thirst( 10 ); + u.use_amount( "grapnel", 1 ); } else { - add_msg( _( "You gingerly descend using your vines." ) ); + return cata::nullopt; } - } else { - add_msg( _( "You effortlessly lower yourself and leave a vine rooted for future use." ) ); - rope_ladder = true; - u.mod_stored_nutr( 10 ); - u.mod_thirst( 10 ); + } else if( u.has_amount( "rope_30", 1 ) ) { + if( query_yn( _( "There is a sheer drop halfway down. Climb your rope down?" ) ) ) { + rope_ladder = true; + u.use_amount( "rope_30", 1 ); + } else { + return cata::nullopt; + } + } else if( !query_yn( _( "There is a sheer drop halfway down. Jump?" ) ) ) { + return cata::nullopt; } - } else { - return cata::nullopt; - } - } else if( u.has_amount( "grapnel", 1 ) ) { - if( query_yn( _( "There is a sheer drop halfway down. Climb your grappling hook down?" ) ) ) { - rope_ladder = true; - u.use_amount( "grapnel", 1 ); - } else { - return cata::nullopt; - } - } else if( u.has_amount( "rope_30", 1 ) ) { - if( query_yn( _( "There is a sheer drop halfway down. Climb your rope down?" ) ) ) { - rope_ladder = true; - u.use_amount( "rope_30", 1 ); - } else { - return cata::nullopt; - } - } else if( !query_yn( _( "There is a sheer drop halfway down. Jump?" ) ) ) { - return cata::nullopt; - } - - return stairs; -} -void game::vertical_shift( const int z_after ) -{ - if( z_after < -OVERMAP_DEPTH || z_after > OVERMAP_HEIGHT ) { - debugmsg( "Tried to get z-level %d outside allowed range of %d-%d", - z_after, -OVERMAP_DEPTH, OVERMAP_HEIGHT ); - return; - } + return stairs; + } - // TODO: Implement dragging stuff up/down - u.grab( OBJECT_NONE ); + void game::vertical_shift( const int z_after ) { + if( z_after < -OVERMAP_DEPTH || z_after > OVERMAP_HEIGHT ) { + debugmsg( "Tried to get z-level %d outside allowed range of %d-%d", + z_after, -OVERMAP_DEPTH, OVERMAP_HEIGHT ); + return; + } - scent.reset(); + // TODO: Implement dragging stuff up/down + u.grab( OBJECT_NONE ); - u.setz( z_after ); - const int z_before = get_levz(); - if( !m.has_zlevels() ) { - m.clear_vehicle_cache( z_before ); - m.access_cache( z_before ).vehicle_list.clear(); - m.access_cache( z_before ).zone_vehicles.clear(); - m.set_transparency_cache_dirty( z_before ); - m.set_outside_cache_dirty( z_before ); - m.load( tripoint( get_levx(), get_levy(), z_after ), true ); - shift_monsters( tripoint( 0, 0, z_after - z_before ) ); - reload_npcs(); - } else { - // Shift the map itself - m.vertical_shift( z_after ); - } + scent.reset(); + + u.setz( z_after ); + const int z_before = get_levz(); + if( !m.has_zlevels() ) { + m.clear_vehicle_cache( z_before ); + m.access_cache( z_before ).vehicle_list.clear(); + m.access_cache( z_before ).zone_vehicles.clear(); + m.set_transparency_cache_dirty( z_before ); + m.set_outside_cache_dirty( z_before ); + m.load( tripoint( get_levx(), get_levy(), z_after ), true ); + shift_monsters( tripoint( 0, 0, z_after - z_before ) ); + reload_npcs(); + } else { + // Shift the map itself + m.vertical_shift( z_after ); + } - m.spawn_monsters( true ); - // this may be required after a vertical shift if z-levels are not enabled - // the critter is unloaded/loaded, and it needs to reconstruct its rider data after being reloaded. - validate_mounted_npcs(); - vertical_notes( z_before, z_after ); -} + m.spawn_monsters( true ); + // this may be required after a vertical shift if z-levels are not enabled + // the critter is unloaded/loaded, and it needs to reconstruct its rider data after being reloaded. + validate_mounted_npcs(); + vertical_notes( z_before, z_after ); + } -void game::vertical_notes( int z_before, int z_after ) -{ - if( z_before == z_after || !get_option( "AUTO_NOTES" ) || - !get_option( "AUTO_NOTES_STAIRS" ) ) { - return; - } + void game::vertical_notes( int z_before, int z_after ) { + if( z_before == z_after || !get_option( "AUTO_NOTES" ) || + !get_option( "AUTO_NOTES_STAIRS" ) ) { + return; + } - if( !m.inbounds_z( z_before ) || !m.inbounds_z( z_after ) ) { - debugmsg( "game::vertical_notes invalid arguments: z_before == %d, z_after == %d", - z_before, z_after ); - return; - } - // Figure out where we know there are up/down connectors - // Fill in all the tiles we know about (e.g. subway stations) - static const int REVEAL_RADIUS = 40; - for( const tripoint &p : points_in_radius( u.global_omt_location(), REVEAL_RADIUS ) ) { - const tripoint cursp_before( p.xy(), z_before ); - const tripoint cursp_after( p.xy(), z_after ); + if( !m.inbounds_z( z_before ) || !m.inbounds_z( z_after ) ) { + debugmsg( "game::vertical_notes invalid arguments: z_before == %d, z_after == %d", + z_before, z_after ); + return; + } + // Figure out where we know there are up/down connectors + // Fill in all the tiles we know about (e.g. subway stations) + static const int REVEAL_RADIUS = 40; + for( const tripoint &p : points_in_radius( u.global_omt_location(), REVEAL_RADIUS ) ) { + const tripoint cursp_before( p.xy(), z_before ); + const tripoint cursp_after( p.xy(), z_after ); - if( !overmap_buffer.seen( cursp_before ) ) { - continue; - } - if( overmap_buffer.has_note( cursp_after ) ) { - // Already has a note -> never add an AUTO-note - continue; - } - const oter_id &ter = overmap_buffer.ter( cursp_before ); - const oter_id &ter2 = overmap_buffer.ter( cursp_after ); - if( z_after > z_before && ter->has_flag( known_up ) && - !ter2->has_flag( known_down ) ) { - overmap_buffer.set_seen( cursp_after, true ); - overmap_buffer.add_note( cursp_after, string_format( ">:W;%s", _( "AUTO: goes down" ) ) ); - } else if( z_after < z_before && ter->has_flag( known_down ) && - !ter2->has_flag( known_up ) ) { - overmap_buffer.set_seen( cursp_after, true ); - overmap_buffer.add_note( cursp_after, string_format( "<:W;%s", _( "AUTO: goes up" ) ) ); + if( !overmap_buffer.seen( cursp_before ) ) { + continue; + } + if( overmap_buffer.has_note( cursp_after ) ) { + // Already has a note -> never add an AUTO-note + continue; + } + const oter_id &ter = overmap_buffer.ter( cursp_before ); + const oter_id &ter2 = overmap_buffer.ter( cursp_after ); + if( z_after > z_before && ter->has_flag( known_up ) && + !ter2->has_flag( known_down ) ) { + overmap_buffer.set_seen( cursp_after, true ); + overmap_buffer.add_note( cursp_after, string_format( ">:W;%s", _( "AUTO: goes down" ) ) ); + } else if( z_after < z_before && ter->has_flag( known_down ) && + !ter2->has_flag( known_up ) ) { + overmap_buffer.set_seen( cursp_after, true ); + overmap_buffer.add_note( cursp_after, string_format( "<:W;%s", _( "AUTO: goes up" ) ) ); + } + } } - } -} -point game::update_map( player &p ) -{ - int x = p.posx(); - int y = p.posy(); - return update_map( x, y ); -} + point game::update_map( player & p ) { + int x = p.posx(); + int y = p.posy(); + return update_map( x, y ); + } -point game::update_map( int &x, int &y ) -{ - point shift; + point game::update_map( int &x, int &y ) { + point shift; - while( x < HALF_MAPSIZE_X ) { - x += SEEX; - shift.x--; - } - while( x >= HALF_MAPSIZE_X + SEEX ) { - x -= SEEX; - shift.x++; - } - while( y < HALF_MAPSIZE_Y ) { - y += SEEY; - shift.y--; - } - while( y >= HALF_MAPSIZE_Y + SEEY ) { - y -= SEEY; - shift.y++; - } - - if( shift == point_zero ) { - // adjust player position - u.setpos( tripoint( x, y, get_levz() ) ); - // Not actually shifting the submaps, all the stuff below would do nothing - return point_zero; - } + while( x < HALF_MAPSIZE_X ) { + x += SEEX; + shift.x--; + } + while( x >= HALF_MAPSIZE_X + SEEX ) { + x -= SEEX; + shift.x++; + } + while( y < HALF_MAPSIZE_Y ) { + y += SEEY; + shift.y--; + } + while( y >= HALF_MAPSIZE_Y + SEEY ) { + y -= SEEY; + shift.y++; + } - // this handles loading/unloading submaps that have scrolled on or off the viewport - m.shift( shift ); + if( shift == point_zero ) { + // adjust player position + u.setpos( tripoint( x, y, get_levz() ) ); + // Not actually shifting the submaps, all the stuff below would do nothing + return point_zero; + } - // Shift monsters - shift_monsters( tripoint( shift, 0 ) ); - const point shift_ms = sm_to_ms_copy( shift ); - u.shift_destination( -shift_ms ); + // this handles loading/unloading submaps that have scrolled on or off the viewport + m.shift( shift ); - // Shift NPCs - for( auto it = active_npc.begin(); it != active_npc.end(); ) { - ( *it )->shift( shift.x, shift.y ); - if( ( *it )->posx() < 0 - SEEX * 2 || ( *it )->posy() < 0 - SEEX * 2 || - ( *it )->posx() > SEEX * ( MAPSIZE + 2 ) || ( *it )->posy() > SEEY * ( MAPSIZE + 2 ) ) { - //Remove the npc from the active list. It remains in the overmap list. - ( *it )->on_unload(); - it = active_npc.erase( it ); - } else { - it++; - } - } + // Shift monsters + shift_monsters( tripoint( shift, 0 ) ); + const point shift_ms = sm_to_ms_copy( shift ); + u.shift_destination( -shift_ms ); - scent.shift( shift_ms.x, shift_ms.y ); + // Shift NPCs + for( auto it = active_npc.begin(); it != active_npc.end(); ) { + ( *it )->shift( shift.x, shift.y ); + if( ( *it )->posx() < 0 - SEEX * 2 || ( *it )->posy() < 0 - SEEX * 2 || + ( *it )->posx() > SEEX * ( MAPSIZE + 2 ) || ( *it )->posy() > SEEY * ( MAPSIZE + 2 ) ) { + //Remove the npc from the active list. It remains in the overmap list. + ( *it )->on_unload(); + it = active_npc.erase( it ); + } else { + it++; + } + } - // Also ensure the player is on current z-level - // get_levz() should later be removed, when there is no longer such a thing - // as "current z-level" - u.setpos( tripoint( x, y, get_levz() ) ); + scent.shift( shift_ms.x, shift_ms.y ); - // Only do the loading after all coordinates have been shifted. + // Also ensure the player is on current z-level + // get_levz() should later be removed, when there is no longer such a thing + // as "current z-level" + u.setpos( tripoint( x, y, get_levz() ) ); - // Check for overmap saved npcs that should now come into view. - // Put those in the active list. - load_npcs(); + // Only do the loading after all coordinates have been shifted. - // Make sure map cache is consistent since it may have shifted. - if( m.has_zlevels() ) { - for( int zlev = -OVERMAP_DEPTH; zlev <= OVERMAP_HEIGHT; ++zlev ) { - m.invalidate_map_cache( zlev ); - } - } else { - m.invalidate_map_cache( get_levz() ); - } - m.build_map_cache( get_levz() ); + // Check for overmap saved npcs that should now come into view. + // Put those in the active list. + load_npcs(); - // Spawn monsters if appropriate - // This call will generate new monsters in addition to loading, so it's placed after NPC loading - m.spawn_monsters( false ); // Static monsters + // Make sure map cache is consistent since it may have shifted. + if( m.has_zlevels() ) { + for( int zlev = -OVERMAP_DEPTH; zlev <= OVERMAP_HEIGHT; ++zlev ) { + m.invalidate_map_cache( zlev ); + } + } else { + m.invalidate_map_cache( get_levz() ); + } + m.build_map_cache( get_levz() ); - // Update what parts of the world map we can see - update_overmap_seen(); + // Spawn monsters if appropriate + // This call will generate new monsters in addition to loading, so it's placed after NPC loading + m.spawn_monsters( false ); // Static monsters - return shift; -} + // Update what parts of the world map we can see + update_overmap_seen(); -void game::update_overmap_seen() -{ - const tripoint ompos = u.global_omt_location(); - const int dist = u.overmap_sight_range( light_level( u.posz() ) ); - const int dist_squared = dist * dist; - // We can always see where we're standing - overmap_buffer.set_seen( ompos, true ); - for( const tripoint &p : points_in_radius( ompos, dist ) ) { - const point delta = p.xy() - ompos.xy(); - const int h_squared = delta.x * delta.x + delta.y * delta.y; - if( trigdist && h_squared > dist_squared ) { - continue; + return shift; } - // If circular distances are enabled, scale overmap distances by the diagonality of the sight line. - const float multiplier = trigdist ? std::sqrt( h_squared ) / std::max( std::abs( delta.x ), - std::abs( delta.y ) ) : 1; - const std::vector line = line_to( ompos, p, 0 ); - float sight_points = dist; - for( auto it = line.begin(); - it != line.end() && sight_points >= 0; ++it ) { - const oter_id &ter = overmap_buffer.ter( *it ); - sight_points -= static_cast( ter->get_see_cost() ) * multiplier; - } - if( sight_points >= 0 ) { - tripoint seen( p ); - do { - overmap_buffer.set_seen( seen, true ); - --seen.z; - } while( seen.z >= 0 ); - } - } -} -void game::replace_stair_monsters() -{ - for( auto &elem : coming_to_stairs ) { - elem.staircount = 0; - const tripoint pnt( elem.pos().xy(), get_levz() ); - place_critter_around( std::make_shared( elem ), pnt, 10 ); - } - - coming_to_stairs.clear(); -} - -// TODO: abstract out the location checking code -// TODO: refactor so zombies can follow up and down stairs instead of this mess -void game::update_stair_monsters() -{ - // Search for the stairs closest to the player. - std::vector stairx; - std::vector stairy; - std::vector stairdist; - - const bool from_below = monstairz < get_levz(); - - if( coming_to_stairs.empty() ) { - return; - } + void game::update_overmap_seen() { + const tripoint ompos = u.global_omt_location(); + const int dist = u.overmap_sight_range( light_level( u.posz() ) ); + const int dist_squared = dist * dist; + // We can always see where we're standing + overmap_buffer.set_seen( ompos, true ); + for( const tripoint &p : points_in_radius( ompos, dist ) ) { + const point delta = p.xy() - ompos.xy(); + const int h_squared = delta.x * delta.x + delta.y * delta.y; + if( trigdist && h_squared > dist_squared ) { + continue; + } + // If circular distances are enabled, scale overmap distances by the diagonality of the sight line. + const float multiplier = trigdist ? std::sqrt( h_squared ) / std::max( std::abs( delta.x ), + std::abs( delta.y ) ) : 1; + const std::vector line = line_to( ompos, p, 0 ); + float sight_points = dist; + for( auto it = line.begin(); + it != line.end() && sight_points >= 0; ++it ) { + const oter_id &ter = overmap_buffer.ter( *it ); + sight_points -= static_cast( ter->get_see_cost() ) * multiplier; + } + if( sight_points >= 0 ) { + tripoint seen( p ); + do { + overmap_buffer.set_seen( seen, true ); + --seen.z; + } while( seen.z >= 0 ); + } + } + } - if( m.has_zlevels() ) { - debugmsg( "%d monsters coming to stairs on a map with z-levels", - coming_to_stairs.size() ); - coming_to_stairs.clear(); - } + void game::replace_stair_monsters() { + for( auto &elem : coming_to_stairs ) { + elem.staircount = 0; + const tripoint pnt( elem.pos().xy(), get_levz() ); + place_critter_around( std::make_shared( elem ), pnt, 10 ); + } - for( const tripoint &dest : m.points_on_zlevel( u.posz() ) ) { - if( ( from_below && m.has_flag( "GOES_DOWN", dest ) ) || - ( !from_below && m.has_flag( "GOES_UP", dest ) ) ) { - stairx.push_back( dest.x ); - stairy.push_back( dest.y ); - stairdist.push_back( rl_dist( dest, u.pos() ) ); + coming_to_stairs.clear(); } - } - if( stairdist.empty() ) { - return; // Found no stairs? - } - // Find closest stairs. - size_t si = 0; - for( size_t i = 0; i < stairdist.size(); i++ ) { - if( stairdist[i] < stairdist[si] ) { - si = i; - } - } + // TODO: abstract out the location checking code + // TODO: refactor so zombies can follow up and down stairs instead of this mess + void game::update_stair_monsters() { + // Search for the stairs closest to the player. + std::vector stairx; + std::vector stairy; + std::vector stairdist; - // Find up to 4 stairs for distance stairdist[si] +1 - std::vector nearest; - nearest.push_back( si ); - for( size_t i = 0; i < stairdist.size() && nearest.size() < 4; i++ ) { - if( ( i != si ) && ( stairdist[i] <= stairdist[si] + 1 ) ) { - nearest.push_back( i ); - } - } - // Randomize the stair choice - si = random_entry_ref( nearest ); + const bool from_below = monstairz < get_levz(); - // Attempt to spawn zombies. - for( size_t i = 0; i < coming_to_stairs.size(); i++ ) { - int mposx = stairx[si]; - int mposy = stairy[si]; - monster &critter = coming_to_stairs[i]; - const tripoint dest { - mposx, mposy, g->get_levz() - }; + if( coming_to_stairs.empty() ) { + return; + } - // We might be not be visible. - if( ( critter.posx() < 0 - ( MAPSIZE_X ) / 6 || - critter.posy() < 0 - ( MAPSIZE_Y ) / 6 || - critter.posx() > ( MAPSIZE_X * 7 ) / 6 || - critter.posy() > ( MAPSIZE_Y * 7 ) / 6 ) ) { - continue; - } + if( m.has_zlevels() ) { + debugmsg( "%d monsters coming to stairs on a map with z-levels", + coming_to_stairs.size() ); + coming_to_stairs.clear(); + } - critter.staircount -= 4; - // Let the player know zombies are trying to come. - if( u.sees( dest ) ) { - std::stringstream dump; - if( critter.staircount > 4 ) { - dump << string_format( _( "You see a %s on the stairs" ), critter.name() ); - } else { - if( critter.staircount > 0 ) { - dump << ( from_below ? - //~ The is almost at the of the ! - string_format( _( "The %1$s is almost at the top of the %2$s!" ), - critter.name(), - m.tername( dest ) ) : - string_format( _( "The %1$s is almost at the bottom of the %2$s!" ), - critter.name(), - m.tername( dest ) ) ); + for( const tripoint &dest : m.points_on_zlevel( u.posz() ) ) { + if( ( from_below && m.has_flag( "GOES_DOWN", dest ) ) || + ( !from_below && m.has_flag( "GOES_UP", dest ) ) ) { + stairx.push_back( dest.x ); + stairy.push_back( dest.y ); + stairdist.push_back( rl_dist( dest, u.pos() ) ); } } + if( stairdist.empty() ) { + return; // Found no stairs? + } - add_msg( m_warning, dump.str() ); - } else { - sounds::sound( dest, 5, sounds::sound_t::movement, - _( "a sound nearby from the stairs!" ), true, "misc", "stairs_movement" ); - } - - if( critter.staircount > 0 ) { - continue; - } + // Find closest stairs. + size_t si = 0; + for( size_t i = 0; i < stairdist.size(); i++ ) { + if( stairdist[i] < stairdist[si] ) { + si = i; + } + } - if( is_empty( dest ) ) { - critter.spawn( dest ); - critter.staircount = 0; - place_critter_at( std::make_shared( critter ), dest ); - if( u.sees( dest ) ) { - if( !from_below ) { - add_msg( m_warning, _( "The %1$s comes down the %2$s!" ), - critter.name(), - m.tername( dest ) ); - } else { - add_msg( m_warning, _( "The %1$s comes up the %2$s!" ), - critter.name(), - m.tername( dest ) ); + // Find up to 4 stairs for distance stairdist[si] +1 + std::vector nearest; + nearest.push_back( si ); + for( size_t i = 0; i < stairdist.size() && nearest.size() < 4; i++ ) { + if( ( i != si ) && ( stairdist[i] <= stairdist[si] + 1 ) ) { + nearest.push_back( i ); } } - coming_to_stairs.erase( coming_to_stairs.begin() + i ); - continue; - } else if( u.pos() == dest ) { - // Monster attempts to push player of stairs - int pushx = -1; - int pushy = -1; - int tries = 0; - - // the critter is now right on top of you and will attack unless - // it can find a square to push you into with one of his tries. - const int creature_push_attempts = 9; - const int player_throw_resist_chance = 3; - - critter.spawn( dest ); - while( tries < creature_push_attempts ) { - tries++; - pushx = rng( -1, 1 ); - pushy = rng( -1, 1 ); - int iposx = mposx + pushx; - int iposy = mposy + pushy; - tripoint pos( iposx, iposy, get_levz() ); - if( ( pushx != 0 || pushy != 0 ) && !critter_at( pos ) && - critter.can_move_to( pos ) ) { - bool resiststhrow = ( u.is_throw_immune() ) || - ( u.has_trait( trait_LEG_TENT_BRACE ) ); - if( resiststhrow && one_in( player_throw_resist_chance ) ) { - u.moves -= 25; // small charge for avoiding the push altogether - add_msg( _( "The %s fails to push you back!" ), - critter.name() ); - return; //judo or leg brace prevent you from getting pushed at all - } - // Not accounting for tentacles latching on, so.. - // Something is about to happen, lets charge half a move - u.moves -= 50; - if( resiststhrow && ( u.is_throw_immune() ) ) { - //we have a judoka who isn't getting pushed but counterattacking now. - mattack::thrown_by_judo( &critter ); - return; - } - std::string msg; - ///\EFFECT_DODGE reduces chance of being downed when pushed off the stairs - if( !( resiststhrow ) && ( u.get_dodge() + rng( 0, 3 ) < 12 ) ) { - // dodge 12 - never get downed - // 11.. avoid 75%; 10.. avoid 50%; 9.. avoid 25% - u.add_effect( effect_downed, 2_turns ); - msg = _( "The %s pushed you back hard!" ); + // Randomize the stair choice + si = random_entry_ref( nearest ); + + // Attempt to spawn zombies. + for( size_t i = 0; i < coming_to_stairs.size(); i++ ) { + int mposx = stairx[si]; + int mposy = stairy[si]; + monster &critter = coming_to_stairs[i]; + const tripoint dest { + mposx, mposy, g->get_levz() + }; + + // We might be not be visible. + if( ( critter.posx() < 0 - ( MAPSIZE_X ) / 6 || + critter.posy() < 0 - ( MAPSIZE_Y ) / 6 || + critter.posx() > ( MAPSIZE_X * 7 ) / 6 || + critter.posy() > ( MAPSIZE_Y * 7 ) / 6 ) ) { + continue; + } + + critter.staircount -= 4; + // Let the player know zombies are trying to come. + if( u.sees( dest ) ) { + std::stringstream dump; + if( critter.staircount > 4 ) { + dump << string_format( _( "You see a %s on the stairs" ), critter.name() ); } else { - msg = _( "The %s pushed you back!" ); + if( critter.staircount > 0 ) { + dump << ( from_below ? + //~ The is almost at the of the ! + string_format( _( "The %1$s is almost at the top of the %2$s!" ), + critter.name(), + m.tername( dest ) ) : + string_format( _( "The %1$s is almost at the bottom of the %2$s!" ), + critter.name(), + m.tername( dest ) ) ); + } } - add_msg( m_warning, msg.c_str(), critter.name() ); - u.setx( u.posx() + pushx ); - u.sety( u.posy() + pushy ); - return; + + add_msg( m_warning, dump.str() ); + } else { + sounds::sound( dest, 5, sounds::sound_t::movement, + _( "a sound nearby from the stairs!" ), true, "misc", "stairs_movement" ); } - } - add_msg( m_warning, - _( "The %s tried to push you back but failed! It attacks you!" ), - critter.name() ); - critter.melee_attack( u ); - u.moves -= 50; - return; - } else if( monster *const mon_ptr = critter_at( dest ) ) { - // Monster attempts to displace a monster from the stairs - monster &other = *mon_ptr; - critter.spawn( dest ); - - // the critter is now right on top of another and will push it - // if it can find a square to push it into inside of his tries. - const int creature_push_attempts = 9; - const int creature_throw_resist = 4; - - int tries = 0; - int pushx = 0; - int pushy = 0; - while( tries < creature_push_attempts ) { - tries++; - pushx = rng( -1, 1 ); - pushy = rng( -1, 1 ); - int iposx = mposx + pushx; - int iposy = mposy + pushy; - tripoint pos( iposx, iposy, get_levz() ); - if( ( pushx == 0 && pushy == 0 ) || ( ( iposx == u.posx() ) && ( iposy == u.posy() ) ) ) { + + if( critter.staircount > 0 ) { continue; } - if( !critter_at( pos ) && other.can_move_to( pos ) ) { - other.setpos( tripoint( iposx, iposy, get_levz() ) ); - other.moves -= 50; - std::string msg; - if( one_in( creature_throw_resist ) ) { - other.add_effect( effect_downed, 2_turns ); - msg = _( "The %1$s pushed the %2$s hard." ); - } else { - msg = _( "The %1$s pushed the %2$s." ); + + if( is_empty( dest ) ) { + critter.spawn( dest ); + critter.staircount = 0; + place_critter_at( std::make_shared( critter ), dest ); + if( u.sees( dest ) ) { + if( !from_below ) { + add_msg( m_warning, _( "The %1$s comes down the %2$s!" ), + critter.name(), + m.tername( dest ) ); + } else { + add_msg( m_warning, _( "The %1$s comes up the %2$s!" ), + critter.name(), + m.tername( dest ) ); + } + } + coming_to_stairs.erase( coming_to_stairs.begin() + i ); + continue; + } else if( u.pos() == dest ) { + // Monster attempts to push player of stairs + int pushx = -1; + int pushy = -1; + int tries = 0; + + // the critter is now right on top of you and will attack unless + // it can find a square to push you into with one of his tries. + const int creature_push_attempts = 9; + const int player_throw_resist_chance = 3; + + critter.spawn( dest ); + while( tries < creature_push_attempts ) { + tries++; + pushx = rng( -1, 1 ); + pushy = rng( -1, 1 ); + int iposx = mposx + pushx; + int iposy = mposy + pushy; + tripoint pos( iposx, iposy, get_levz() ); + if( ( pushx != 0 || pushy != 0 ) && !critter_at( pos ) && + critter.can_move_to( pos ) ) { + bool resiststhrow = ( u.is_throw_immune() ) || + ( u.mutations.has_trait( trait_LEG_TENT_BRACE ) ); + if( resiststhrow && one_in( player_throw_resist_chance ) ) { + u.moves -= 25; // small charge for avoiding the push altogether + add_msg( _( "The %s fails to push you back!" ), + critter.name() ); + return; //judo or leg brace prevent you from getting pushed at all + } + // Not accounting for tentacles latching on, so.. + // Something is about to happen, lets charge half a move + u.moves -= 50; + if( resiststhrow && ( u.is_throw_immune() ) ) { + //we have a judoka who isn't getting pushed but counterattacking now. + mattack::thrown_by_judo( &critter ); + return; + } + std::string msg; + ///\EFFECT_DODGE reduces chance of being downed when pushed off the stairs + if( !( resiststhrow ) && ( u.get_dodge() + rng( 0, 3 ) < 12 ) ) { + // dodge 12 - never get downed + // 11.. avoid 75%; 10.. avoid 50%; 9.. avoid 25% + u.add_effect( effect_downed, 2_turns ); + msg = _( "The %s pushed you back hard!" ); + } else { + msg = _( "The %s pushed you back!" ); + } + add_msg( m_warning, msg.c_str(), critter.name() ); + u.setx( u.posx() + pushx ); + u.sety( u.posy() + pushy ); + return; + } + } + add_msg( m_warning, + _( "The %s tried to push you back but failed! It attacks you!" ), + critter.name() ); + critter.melee_attack( u ); + u.moves -= 50; + return; + } else if( monster *const mon_ptr = critter_at( dest ) ) { + // Monster attempts to displace a monster from the stairs + monster &other = *mon_ptr; + critter.spawn( dest ); + + // the critter is now right on top of another and will push it + // if it can find a square to push it into inside of his tries. + const int creature_push_attempts = 9; + const int creature_throw_resist = 4; + + int tries = 0; + int pushx = 0; + int pushy = 0; + while( tries < creature_push_attempts ) { + tries++; + pushx = rng( -1, 1 ); + pushy = rng( -1, 1 ); + int iposx = mposx + pushx; + int iposy = mposy + pushy; + tripoint pos( iposx, iposy, get_levz() ); + if( ( pushx == 0 && pushy == 0 ) || ( ( iposx == u.posx() ) && ( iposy == u.posy() ) ) ) { + continue; + } + if( !critter_at( pos ) && other.can_move_to( pos ) ) { + other.setpos( tripoint( iposx, iposy, get_levz() ) ); + other.moves -= 50; + std::string msg; + if( one_in( creature_throw_resist ) ) { + other.add_effect( effect_downed, 2_turns ); + msg = _( "The %1$s pushed the %2$s hard." ); + } else { + msg = _( "The %1$s pushed the %2$s." ); + } + add_msg( m_neutral, msg, critter.name(), other.name() ); + return; + } } - add_msg( m_neutral, msg, critter.name(), other.name() ); return; } } - return; } - } -} - -void game::despawn_monster( monster &critter ) -{ - if( !critter.is_hallucination() ) { - // hallucinations aren't stored, they come and go as they like, - overmap_buffer.despawn_monster( critter ); - } - critter.on_unload(); - remove_zombie( critter ); - // simulate it being dead so further processing of it (e.g. in monmove) will yield - critter.set_hp( 0 ); -} + void game::despawn_monster( monster & critter ) { + if( !critter.is_hallucination() ) { + // hallucinations aren't stored, they come and go as they like, + overmap_buffer.despawn_monster( critter ); + } -void game::shift_monsters( const tripoint &shift ) -{ - // If either shift argument is non-zero, we're shifting. - if( shift == tripoint_zero ) { - return; - } - for( monster &critter : all_monsters() ) { - if( shift.xy() != point_zero ) { - critter.shift( shift.xy() ); + critter.on_unload(); + remove_zombie( critter ); + // simulate it being dead so further processing of it (e.g. in monmove) will yield + critter.set_hp( 0 ); } - if( m.inbounds( critter.pos() ) && ( shift.z == 0 || m.has_zlevels() ) ) { - // We're inbounds, so don't despawn after all. - // No need to shift Z-coordinates, they are absolute - continue; - } - // Either a vertical shift or the critter is now outside of the reality bubble, - // anyway: it must be saved and removed. - despawn_monster( critter ); - } - // The order in which zombies are shifted may cause zombies to briefly exist on - // the same square. This messes up the mon_at cache, so we need to rebuild it. - critter_tracker->rebuild_cache(); -} + void game::shift_monsters( const tripoint & shift ) { + // If either shift argument is non-zero, we're shifting. + if( shift == tripoint_zero ) { + return; + } + for( monster &critter : all_monsters() ) { + if( shift.xy() != point_zero ) { + critter.shift( shift.xy() ); + } -void game::perhaps_add_random_npc() -{ - if( !calendar::once_every( 1_hours ) ) { - return; - } - // Create a new NPC? - // Only allow NPCs on 0 z-level, otherwise they can bug out due to lack of spots - if( !get_option( "RANDOM_NPC" ) || ( !m.has_zlevels() && get_levz() != 0 ) ) { - return; - } + if( m.inbounds( critter.pos() ) && ( shift.z == 0 || m.has_zlevels() ) ) { + // We're inbounds, so don't despawn after all. + // No need to shift Z-coordinates, they are absolute + continue; + } + // Either a vertical shift or the critter is now outside of the reality bubble, + // anyway: it must be saved and removed. + despawn_monster( critter ); + } + // The order in which zombies are shifted may cause zombies to briefly exist on + // the same square. This messes up the mon_at cache, so we need to rebuild it. + critter_tracker->rebuild_cache(); + } - float density = get_option( "NPC_DENSITY" ); - // TODO: This is inaccurate when the player is near a overmap border, and it will - //immediately spawn new npcs upon entering a new overmap. Rather use number of npcs *nearby*. - const int npc_num = get_cur_om().get_npcs().size(); - if( npc_num > 0 ) { - // 100%, 80%, 64%, 52%, 41%, 33%... - density *= powf( 0.8f, npc_num ); - } + void game::perhaps_add_random_npc() { + if( !calendar::once_every( 1_hours ) ) { + return; + } + // Create a new NPC? + // Only allow NPCs on 0 z-level, otherwise they can bug out due to lack of spots + if( !get_option( "RANDOM_NPC" ) || ( !m.has_zlevels() && get_levz() != 0 ) ) { + return; + } - if( !x_in_y( density, 100 ) ) { - return; - } + float density = get_option( "NPC_DENSITY" ); + // TODO: This is inaccurate when the player is near a overmap border, and it will + //immediately spawn new npcs upon entering a new overmap. Rather use number of npcs *nearby*. + const int npc_num = get_cur_om().get_npcs().size(); + if( npc_num > 0 ) { + // 100%, 80%, 64%, 52%, 41%, 33%... + density *= powf( 0.8f, npc_num ); + } - //tmp->stock_missions(); - // Create the NPC in one of the outermost submaps, - // hopefully far away to be invisible to the player, - // to prevent NPCs appearing out of thin air. - // This can be changed to let the NPC spawn further away, - // so it does not became active immediately. - int msx = get_levx(); - int msy = get_levy(); - switch( rng( 0, 4 ) ) { // on which side of the map to spawn - case 0: - msy += rng( 0, MAPSIZE - 1 ); - break; - case 1: - msx += MAPSIZE - 1; - msy += rng( 0, MAPSIZE - 1 ); - break; - case 2: - msx += rng( 0, MAPSIZE - 1 ); - break; - case 3: - msy += MAPSIZE - 1; - msx += rng( 0, MAPSIZE - 1 ); - break; - default: - break; - } - tripoint omt_pos = sm_to_omt_copy( tripoint( msx, msy, 0 ) ); - const auto oter = overmap_buffer.ter( omt_pos ); - // shouldnt spawn on lakes or rivers. - if( is_river_or_lake( oter ) ) { - return; - } - std::shared_ptr tmp = std::make_shared(); - tmp->normalize(); - tmp->randomize(); - std::string new_fac_id = "solo_"; - new_fac_id += tmp->name; - // create a new "lone wolf" faction for this one NPC - faction *new_solo_fac = faction_manager_ptr->add_new_faction( tmp->name, faction_id( new_fac_id ), - faction_id( "no_faction" ) ); - tmp->set_fac( new_solo_fac ? new_solo_fac->id : faction_id( "no_faction" ) ); - // adds the npc to the correct overmap. - tmp->spawn_at_sm( msx, msy, 0 ); - overmap_buffer.insert_npc( tmp ); - tmp->form_opinion( u ); - tmp->mission = NPC_MISSION_NULL; - tmp->add_new_mission( mission::reserve_random( ORIGIN_ANY_NPC, tmp->global_omt_location(), - tmp->getID() ) ); - // This will make the new NPC active - load_npcs(); -} + if( !x_in_y( density, 100 ) ) { + return; + } -bool game::display_overlay_state( const action_id action ) -{ - const auto it = displaying_overlays.find( action ); - if( it == displaying_overlays.end() ) { - return false; - } + //tmp->stock_missions(); + // Create the NPC in one of the outermost submaps, + // hopefully far away to be invisible to the player, + // to prevent NPCs appearing out of thin air. + // This can be changed to let the NPC spawn further away, + // so it does not became active immediately. + int msx = get_levx(); + int msy = get_levy(); + switch( rng( 0, 4 ) ) { // on which side of the map to spawn + case 0: + msy += rng( 0, MAPSIZE - 1 ); + break; + case 1: + msx += MAPSIZE - 1; + msy += rng( 0, MAPSIZE - 1 ); + break; + case 2: + msx += rng( 0, MAPSIZE - 1 ); + break; + case 3: + msy += MAPSIZE - 1; + msx += rng( 0, MAPSIZE - 1 ); + break; + default: + break; + } + tripoint omt_pos = sm_to_omt_copy( tripoint( msx, msy, 0 ) ); + const auto oter = overmap_buffer.ter( omt_pos ); + // shouldnt spawn on lakes or rivers. + if( is_river_or_lake( oter ) ) { + return; + } + std::shared_ptr tmp = std::make_shared(); + tmp->normalize(); + tmp->randomize(); + std::string new_fac_id = "solo_"; + new_fac_id += tmp->name; + // create a new "lone wolf" faction for this one NPC + faction *new_solo_fac = faction_manager_ptr->add_new_faction( tmp->name, faction_id( new_fac_id ), + faction_id( "no_faction" ) ); + tmp->set_fac( new_solo_fac ? new_solo_fac->id : faction_id( "no_faction" ) ); + // adds the npc to the correct overmap. + tmp->spawn_at_sm( msx, msy, 0 ); + overmap_buffer.insert_npc( tmp ); + tmp->form_opinion( u ); + tmp->mission = NPC_MISSION_NULL; + tmp->add_new_mission( mission::reserve_random( ORIGIN_ANY_NPC, tmp->global_omt_location(), + tmp->getID() ) ); + // This will make the new NPC active + load_npcs(); + } - return displaying_overlays[action]; -} + bool game::display_overlay_state( const action_id action ) { + const auto it = displaying_overlays.find( action ); + if( it == displaying_overlays.end() ) { + return false; + } -void game::display_toggle_overlay( const action_id action ) -{ - const auto it = displaying_overlays.find( action ); - if( it == displaying_overlays.end() ) { - return; - } + return displaying_overlays[action]; + } - const bool action_flag = it->second; - std::for_each( displaying_overlays.begin(), displaying_overlays.end(), []( auto & p ) { - p.second = false; - } ); - displaying_overlays[action] = !action_flag; -} + void game::display_toggle_overlay( const action_id action ) { + const auto it = displaying_overlays.find( action ); + if( it == displaying_overlays.end() ) { + return; + } -void game::display_scent() -{ - if( use_tiles ) { - display_toggle_overlay( ACTION_DISPLAY_SCENT ); - } else { - int div; - bool got_value = query_int( div, _( "Set the Scent Map sensitivity to (0 to cancel)?" ) ); - if( !got_value || div < 1 ) { - add_msg( _( "Never mind." ) ); - return; + const bool action_flag = it->second; + std::for_each( displaying_overlays.begin(), displaying_overlays.end(), []( auto & p ) { + p.second = false; + } ); + displaying_overlays[action] = !action_flag; } - draw_ter(); - scent.draw( w_terrain, div * 2, u.pos() + u.view_offset ); - wrefresh( w_terrain ); - draw_panels(); - inp_mngr.wait_for_any_key(); - } -} - -void game::display_temperature() -{ - if( use_tiles ) { - display_toggle_overlay( ACTION_DISPLAY_TEMPERATURE ); - } -} -void game::display_visibility() -{ - if( use_tiles ) { - display_toggle_overlay( ACTION_DISPLAY_VISIBILITY ); - if( display_overlay_state( ACTION_DISPLAY_VISIBILITY ) ) { - std::vector< tripoint > locations; - uilist creature_menu; - int num_creatures = 0; - creature_menu.addentry( num_creatures++, true, MENU_AUTOASSIGN, "%s", _( "You" ) ); - locations.emplace_back( g->u.pos() ); // add player first. - for( const Creature &critter : g->all_creatures() ) { - if( critter.is_player() ) { - continue; + void game::display_scent() { + if( use_tiles ) { + display_toggle_overlay( ACTION_DISPLAY_SCENT ); + } else { + int div; + bool got_value = query_int( div, _( "Set the Scent Map sensitivity to (0 to cancel)?" ) ); + if( !got_value || div < 1 ) { + add_msg( _( "Never mind." ) ); + return; } - creature_menu.addentry( num_creatures++, true, MENU_AUTOASSIGN, critter.disp_name() ); - locations.emplace_back( critter.pos() ); + draw_ter(); + scent.draw( w_terrain, div * 2, u.pos() + u.view_offset ); + wrefresh( w_terrain ); + draw_panels(); + inp_mngr.wait_for_any_key(); } + } - pointmenu_cb callback( locations ); - creature_menu.callback = &callback; - creature_menu.w_y = 0; - creature_menu.query(); - if( creature_menu.ret >= 0 && static_cast( creature_menu.ret ) < locations.size() ) { - Creature *creature = critter_at( locations[creature_menu.ret] ); - displaying_visibility_creature = creature; + void game::display_temperature() { + if( use_tiles ) { + display_toggle_overlay( ACTION_DISPLAY_TEMPERATURE ); } - } else { - displaying_visibility_creature = nullptr; } - } -} -void game::display_lighting() -{ - if( use_tiles ) { - display_toggle_overlay( ACTION_DISPLAY_LIGHTING ); - if( !g->display_overlay_state( ACTION_DISPLAY_LIGHTING ) ) { - return; - } - uilist lighting_menu; - std::vector lighting_menu_strings{ - "Global lighting conditions" - }; + void game::display_visibility() { + if( use_tiles ) { + display_toggle_overlay( ACTION_DISPLAY_VISIBILITY ); + if( display_overlay_state( ACTION_DISPLAY_VISIBILITY ) ) { + std::vector< tripoint > locations; + uilist creature_menu; + int num_creatures = 0; + creature_menu.addentry( num_creatures++, true, MENU_AUTOASSIGN, "%s", _( "You" ) ); + locations.emplace_back( g->u.pos() ); // add player first. + for( const Creature &critter : g->all_creatures() ) { + if( critter.is_player() ) { + continue; + } + creature_menu.addentry( num_creatures++, true, MENU_AUTOASSIGN, critter.disp_name() ); + locations.emplace_back( critter.pos() ); + } - int count = 0; - for( const auto &menu_str : lighting_menu_strings ) { - lighting_menu.addentry( count++, true, MENU_AUTOASSIGN, "%s", menu_str ); + pointmenu_cb callback( locations ); + creature_menu.callback = &callback; + creature_menu.w_y = 0; + creature_menu.query(); + if( creature_menu.ret >= 0 && static_cast( creature_menu.ret ) < locations.size() ) { + Creature *creature = critter_at( locations[creature_menu.ret] ); + displaying_visibility_creature = creature; + } + } else { + displaying_visibility_creature = nullptr; + } + } } - lighting_menu.w_y = 0; - lighting_menu.query(); - if( ( lighting_menu.ret >= 0 ) && - ( static_cast( lighting_menu.ret ) < lighting_menu_strings.size() ) ) { - g->displaying_lighting_condition = lighting_menu.ret; + void game::display_lighting() { + if( use_tiles ) { + display_toggle_overlay( ACTION_DISPLAY_LIGHTING ); + if( !g->display_overlay_state( ACTION_DISPLAY_LIGHTING ) ) { + return; + } + uilist lighting_menu; + std::vector lighting_menu_strings{ + "Global lighting conditions" + }; + + int count = 0; + for( const auto &menu_str : lighting_menu_strings ) { + lighting_menu.addentry( count++, true, MENU_AUTOASSIGN, "%s", menu_str ); + } + + lighting_menu.w_y = 0; + lighting_menu.query(); + if( ( lighting_menu.ret >= 0 ) && + ( static_cast( lighting_menu.ret ) < lighting_menu_strings.size() ) ) { + g->displaying_lighting_condition = lighting_menu.ret; + } + } } - } -} -void game::display_radiation() -{ - if( use_tiles ) { - display_toggle_overlay( ACTION_DISPLAY_RADIATION ); - } -} + void game::display_radiation() { + if( use_tiles ) { + display_toggle_overlay( ACTION_DISPLAY_RADIATION ); + } + } -void game::init_autosave() -{ - moves_since_last_save = 0; - last_save_timestamp = time( nullptr ); -} + void game::init_autosave() { + moves_since_last_save = 0; + last_save_timestamp = time( nullptr ); + } -void game::quicksave() -{ - //Don't autosave if the player hasn't done anything since the last autosave/quicksave, - if( !moves_since_last_save ) { - return; - } - add_msg( m_info, _( "Saving game, this may take a while" ) ); - popup_nowait( _( "Saving game, this may take a while" ) ); + void game::quicksave() { + //Don't autosave if the player hasn't done anything since the last autosave/quicksave, + if( !moves_since_last_save ) { + return; + } + add_msg( m_info, _( "Saving game, this may take a while" ) ); + popup_nowait( _( "Saving game, this may take a while" ) ); - time_t now = time( nullptr ); //timestamp for start of saving procedure + time_t now = time( nullptr ); //timestamp for start of saving procedure - //perform save - save(); - //Now reset counters for autosaving, so we don't immediately autosave after a quicksave or autosave. - moves_since_last_save = 0; - last_save_timestamp = now; -} + //perform save + save(); + //Now reset counters for autosaving, so we don't immediately autosave after a quicksave or autosave. + moves_since_last_save = 0; + last_save_timestamp = now; + } -void game::quickload() -{ - const WORLDPTR active_world = world_generator->active_world; - if( active_world == nullptr ) { - return; - } + void game::quickload() { + const WORLDPTR active_world = world_generator->active_world; + if( active_world == nullptr ) { + return; + } - if( active_world->save_exists( save_t::from_player_name( u.name ) ) ) { - if( moves_since_last_save != 0 ) { // See if we need to reload anything - MAPBUFFER.reset(); - overmap_buffer.clear(); - try { - setup(); - } catch( const std::exception &err ) { - debugmsg( "Error: %s", err.what() ); + if( active_world->save_exists( save_t::from_player_name( u.name ) ) ) { + if( moves_since_last_save != 0 ) { // See if we need to reload anything + MAPBUFFER.reset(); + overmap_buffer.clear(); + try { + setup(); + } catch( const std::exception &err ) { + debugmsg( "Error: %s", err.what() ); + } + load( save_t::from_player_name( u.name ) ); + } + } else { + popup_getkey( _( "No saves for %s yet." ), u.name ); } - load( save_t::from_player_name( u.name ) ); } - } else { - popup_getkey( _( "No saves for %s yet." ), u.name ); - } -} -void game::autosave() -{ - //Don't autosave if the min-autosave interval has not passed since the last autosave/quicksave. - if( time( nullptr ) < last_save_timestamp + 60 * get_option( "AUTOSAVE_MINUTES" ) ) { - return; - } - quicksave(); //Driving checks are handled by quicksave() -} - -void intro() -{ - int maxy = getmaxy( catacurses::stdscr ); - int maxx = getmaxx( catacurses::stdscr ); - const int minHeight = FULL_SCREEN_HEIGHT; - const int minWidth = FULL_SCREEN_WIDTH; - catacurses::window tmp = catacurses::newwin( minHeight, minWidth, point_zero ); - - while( maxy < minHeight || maxx < minWidth ) { - werase( tmp ); - if( maxy < minHeight && maxx < minWidth ) { - fold_and_print( tmp, point_zero, maxx, c_white, - _( "Whoa! Your terminal is tiny! This game requires a minimum terminal size of " - "%dx%d to work properly. %dx%d just won't do. Maybe a smaller font would help?" ), - minWidth, minHeight, maxx, maxy ); - } else if( maxx < minWidth ) { - fold_and_print( tmp, point_zero, maxx, c_white, - _( "Oh! Hey, look at that. Your terminal is just a little too narrow. This game " - "requires a minimum terminal size of %dx%d to function. It just won't work " - "with only %dx%d. Can you stretch it out sideways a bit?" ), - minWidth, minHeight, maxx, maxy ); - } else { - fold_and_print( tmp, point_zero, maxx, c_white, - _( "Woah, woah, we're just a little short on space here. The game requires a " - "minimum terminal size of %dx%d to run. %dx%d isn't quite enough! Can you " - "make the terminal just a smidgen taller?" ), - minWidth, minHeight, maxx, maxy ); - } - wrefresh( tmp ); - inp_mngr.wait_for_any_key(); - maxy = getmaxy( catacurses::stdscr ); - maxx = getmaxx( catacurses::stdscr ); - } - werase( tmp ); + void game::autosave() { + //Don't autosave if the min-autosave interval has not passed since the last autosave/quicksave. + if( time( nullptr ) < last_save_timestamp + 60 * get_option( "AUTOSAVE_MINUTES" ) ) { + return; + } + quicksave(); //Driving checks are handled by quicksave() + } + + void intro() { + int maxy = getmaxy( catacurses::stdscr ); + int maxx = getmaxx( catacurses::stdscr ); + const int minHeight = FULL_SCREEN_HEIGHT; + const int minWidth = FULL_SCREEN_WIDTH; + catacurses::window tmp = catacurses::newwin( minHeight, minWidth, point_zero ); + + while( maxy < minHeight || maxx < minWidth ) { + werase( tmp ); + if( maxy < minHeight && maxx < minWidth ) { + fold_and_print( tmp, point_zero, maxx, c_white, + _( "Whoa! Your terminal is tiny! This game requires a minimum terminal size of " + "%dx%d to work properly. %dx%d just won't do. Maybe a smaller font would help?" ), + minWidth, minHeight, maxx, maxy ); + } else if( maxx < minWidth ) { + fold_and_print( tmp, point_zero, maxx, c_white, + _( "Oh! Hey, look at that. Your terminal is just a little too narrow. This game " + "requires a minimum terminal size of %dx%d to function. It just won't work " + "with only %dx%d. Can you stretch it out sideways a bit?" ), + minWidth, minHeight, maxx, maxy ); + } else { + fold_and_print( tmp, point_zero, maxx, c_white, + _( "Woah, woah, we're just a little short on space here. The game requires a " + "minimum terminal size of %dx%d to run. %dx%d isn't quite enough! Can you " + "make the terminal just a smidgen taller?" ), + minWidth, minHeight, maxx, maxy ); + } + wrefresh( tmp ); + inp_mngr.wait_for_any_key(); + maxy = getmaxy( catacurses::stdscr ); + maxx = getmaxx( catacurses::stdscr ); + } + werase( tmp ); #if !(defined(_WIN32) || defined(TILES)) - // Check whether LC_CTYPE supports the UTF-8 encoding - // and show a warning if it doesn't - if( std::strcmp( nl_langinfo( CODESET ), "UTF-8" ) != 0 ) { - const char *unicode_error_msg = - _( "You don't seem to have a valid Unicode locale. You may see some weird " - "characters (e.g. empty boxes or question marks). You have been warned." ); - fold_and_print( tmp, point_zero, maxx, c_white, unicode_error_msg, minWidth, minHeight, - maxx, maxy ); - wrefresh( tmp ); - inp_mngr.wait_for_any_key(); - werase( tmp ); - } + // Check whether LC_CTYPE supports the UTF-8 encoding + // and show a warning if it doesn't + if( std::strcmp( nl_langinfo( CODESET ), "UTF-8" ) != 0 ) { + const char *unicode_error_msg = + _( "You don't seem to have a valid Unicode locale. You may see some weird " + "characters (e.g. empty boxes or question marks). You have been warned." ); + fold_and_print( tmp, point_zero, maxx, c_white, unicode_error_msg, minWidth, minHeight, + maxx, maxy ); + wrefresh( tmp ); + inp_mngr.wait_for_any_key(); + werase( tmp ); + } #endif - wrefresh( tmp ); - catacurses::erase(); -} - -void game::process_artifact( item &it, player &p ) -{ - const bool worn = p.is_worn( it ); - const bool wielded = ( &it == &p.weapon ); - std::vector effects = it.type->artifact->effects_carried; - if( worn ) { - const std::vector &ew = it.type->artifact->effects_worn; - effects.insert( effects.end(), ew.begin(), ew.end() ); - } - if( wielded ) { - const std::vector &ew = it.type->artifact->effects_wielded; - effects.insert( effects.end(), ew.begin(), ew.end() ); - } - - if( it.is_tool() ) { - // Recharge it if necessary - if( it.ammo_remaining() < it.ammo_capacity() && calendar::once_every( 1_minutes ) ) { - //Before incrementing charge, check that any extra requirements are met - if( check_art_charge_req( it ) ) { - switch( it.type->artifact->charge_type ) { - case ARTC_NULL: - case NUM_ARTCS: - break; // dummy entries - case ARTC_TIME: - // Once per hour - if( calendar::once_every( 1_hours ) ) { - it.charges++; + wrefresh( tmp ); + catacurses::erase(); + } + + void game::process_artifact( item & it, player & p ) { + const bool worn = p.is_worn( it ); + const bool wielded = ( &it == &p.weapon ); + std::vector effects = it.type->artifact->effects_carried; + if( worn ) { + const std::vector &ew = it.type->artifact->effects_worn; + effects.insert( effects.end(), ew.begin(), ew.end() ); + } + if( wielded ) { + const std::vector &ew = it.type->artifact->effects_wielded; + effects.insert( effects.end(), ew.begin(), ew.end() ); + } + + if( it.is_tool() ) { + // Recharge it if necessary + if( it.ammo_remaining() < it.ammo_capacity() && calendar::once_every( 1_minutes ) ) { + //Before incrementing charge, check that any extra requirements are met + if( check_art_charge_req( it ) ) { + switch( it.type->artifact->charge_type ) { + case ARTC_NULL: + case NUM_ARTCS: + break; // dummy entries + case ARTC_TIME: + // Once per hour + if( calendar::once_every( 1_hours ) ) { + it.charges++; + } + break; + case ARTC_SOLAR: + if( calendar::once_every( 10_minutes ) && + is_in_sunlight( p.pos() ) ) { + it.charges++; + } + break; + // Artifacts can inflict pain even on Deadened folks. + // Some weird Lovecraftian thing. ;P + // (So DON'T route them through mod_pain!) + case ARTC_PAIN: + if( calendar::once_every( 1_minutes ) ) { + add_msg( m_bad, _( "You suddenly feel sharp pain for no reason." ) ); + p.mod_pain_noresist( 3 * rng( 1, 3 ) ); + it.charges++; + } + break; + case ARTC_HP: + if( calendar::once_every( 1_minutes ) ) { + add_msg( m_bad, _( "You feel your body decaying." ) ); + p.hurtall( 1, nullptr ); + it.charges++; + } + break; + case ARTC_FATIGUE: + if( calendar::once_every( 1_minutes ) ) { + add_msg( m_bad, _( "You feel fatigue seeping into your body." ) ); + u.mod_fatigue( 3 * rng( 1, 3 ) ); + u.mod_stat( "stamina", -90 * rng( 1, 3 ) * rng( 1, 3 ) * rng( 2, 3 ) ); + it.charges++; + } + break; + // Portals are energetic enough to charge the item. + // Tears in reality are consumed too, but can't charge it. + case ARTC_PORTAL: + for( const tripoint &dest : m.points_in_radius( p.pos(), 1 ) ) { + m.remove_field( dest, fd_fatigue ); + if( m.tr_at( dest ).loadid == tr_portal ) { + add_msg( m_good, _( "The portal collapses!" ) ); + m.remove_trap( dest ); + it.charges++; + break; + } + } + break; } + } + } + } + + for( const art_effect_passive &i : effects ) { + switch( i ) { + case AEP_STR_UP: + p.mod_str_bonus( +4 ); break; - case ARTC_SOLAR: - if( calendar::once_every( 10_minutes ) && - is_in_sunlight( p.pos() ) ) { - it.charges++; - } + case AEP_DEX_UP: + p.mod_dex_bonus( +4 ); break; - // Artifacts can inflict pain even on Deadened folks. - // Some weird Lovecraftian thing. ;P - // (So DON'T route them through mod_pain!) - case ARTC_PAIN: - if( calendar::once_every( 1_minutes ) ) { - add_msg( m_bad, _( "You suddenly feel sharp pain for no reason." ) ); - p.mod_pain_noresist( 3 * rng( 1, 3 ) ); - it.charges++; - } + case AEP_PER_UP: + p.mod_per_bonus( +4 ); break; - case ARTC_HP: - if( calendar::once_every( 1_minutes ) ) { - add_msg( m_bad, _( "You feel your body decaying." ) ); - p.hurtall( 1, nullptr ); - it.charges++; - } + case AEP_INT_UP: + p.mod_int_bonus( +4 ); break; - case ARTC_FATIGUE: - if( calendar::once_every( 1_minutes ) ) { - add_msg( m_bad, _( "You feel fatigue seeping into your body." ) ); - u.mod_fatigue( 3 * rng( 1, 3 ) ); - u.mod_stat( "stamina", -90 * rng( 1, 3 ) * rng( 1, 3 ) * rng( 2, 3 ) ); - it.charges++; - } + case AEP_ALL_UP: + p.mod_str_bonus( +2 ); + p.mod_dex_bonus( +2 ); + p.mod_per_bonus( +2 ); + p.mod_int_bonus( +2 ); break; - // Portals are energetic enough to charge the item. - // Tears in reality are consumed too, but can't charge it. - case ARTC_PORTAL: - for( const tripoint &dest : m.points_in_radius( p.pos(), 1 ) ) { - m.remove_field( dest, fd_fatigue ); - if( m.tr_at( dest ).loadid == tr_portal ) { - add_msg( m_good, _( "The portal collapses!" ) ); - m.remove_trap( dest ); - it.charges++; - break; - } - } + case AEP_SPEED_UP: // Handled in player::current_speed() break; - } - } - } - } - for( const art_effect_passive &i : effects ) { - switch( i ) { - case AEP_STR_UP: - p.mod_str_bonus( +4 ); - break; - case AEP_DEX_UP: - p.mod_dex_bonus( +4 ); - break; - case AEP_PER_UP: - p.mod_per_bonus( +4 ); - break; - case AEP_INT_UP: - p.mod_int_bonus( +4 ); - break; - case AEP_ALL_UP: - p.mod_str_bonus( +2 ); - p.mod_dex_bonus( +2 ); - p.mod_per_bonus( +2 ); - p.mod_int_bonus( +2 ); - break; - case AEP_SPEED_UP: // Handled in player::current_speed() - break; - - case AEP_PBLUE: - if( p.radiation > 0 ) { - p.radiation--; - } - break; + case AEP_PBLUE: + if( p.radiation > 0 ) { + p.radiation--; + } + break; - case AEP_SMOKE: - if( one_in( 10 ) ) { - tripoint pt( p.posx() + rng( -1, 1 ), - p.posy() + rng( -1, 1 ), - p.posz() ); - m.add_field( pt, fd_smoke, rng( 1, 3 ) ); - } - break; + case AEP_SMOKE: + if( one_in( 10 ) ) { + tripoint pt( p.posx() + rng( -1, 1 ), + p.posy() + rng( -1, 1 ), + p.posz() ); + m.add_field( pt, fd_smoke, rng( 1, 3 ) ); + } + break; - case AEP_SNAKES: - break; // Handled in player::hit() + case AEP_SNAKES: + break; // Handled in player::hit() - case AEP_EXTINGUISH: - for( const tripoint &dest : m.points_in_radius( p.pos(), 1 ) ) { - m.mod_field_age( dest, fd_fire, -1_turns ); - } - break; + case AEP_EXTINGUISH: + for( const tripoint &dest : m.points_in_radius( p.pos(), 1 ) ) { + m.mod_field_age( dest, fd_fire, -1_turns ); + } + break; - case AEP_FUN: - //Bonus fluctuates, wavering between 0 and 30-ish - usually around 12 - p.add_morale( MORALE_FEELING_GOOD, rng( 1, 2 ) * rng( 2, 3 ), 0, 3_turns, 0_turns, false ); - break; + case AEP_FUN: + //Bonus fluctuates, wavering between 0 and 30-ish - usually around 12 + p.add_morale( MORALE_FEELING_GOOD, rng( 1, 2 ) * rng( 2, 3 ), 0, 3_turns, 0_turns, false ); + break; - case AEP_HUNGER: - if( one_in( 100 ) ) { - p.mod_hunger( 1 ); - } - break; + case AEP_HUNGER: + if( one_in( 100 ) ) { + p.mod_hunger( 1 ); + } + break; - case AEP_THIRST: - if( one_in( 120 ) ) { - p.mod_thirst( 1 ); - } - break; + case AEP_THIRST: + if( one_in( 120 ) ) { + p.mod_thirst( 1 ); + } + break; - case AEP_EVIL: - if( one_in( 150 ) ) { // Once every 15 minutes, on average - p.add_effect( effect_evil, 30_minutes ); - if( it.is_armor() ) { - if( !worn ) { - add_msg( _( "You have an urge to wear the %s." ), - it.tname() ); + case AEP_EVIL: + if( one_in( 150 ) ) { // Once every 15 minutes, on average + p.add_effect( effect_evil, 30_minutes ); + if( it.is_armor() ) { + if( !worn ) { + add_msg( _( "You have an urge to wear the %s." ), + it.tname() ); + } + } else if( !wielded ) { + add_msg( _( "You have an urge to wield the %s." ), + it.tname() ); + } } - } else if( !wielded ) { - add_msg( _( "You have an urge to wield the %s." ), - it.tname() ); - } - } - break; + break; - case AEP_SCHIZO: - break; // Handled in player::suffer() + case AEP_SCHIZO: + break; // Handled in player::suffer() - case AEP_RADIOACTIVE: - if( one_in( 4 ) ) { - p.irradiate( 1.0f ); - } - break; + case AEP_RADIOACTIVE: + if( one_in( 4 ) ) { + p.irradiate( 1.0f ); + } + break; - case AEP_STR_DOWN: - p.mod_str_bonus( -3 ); - break; + case AEP_STR_DOWN: + p.mod_str_bonus( -3 ); + break; - case AEP_DEX_DOWN: - p.mod_dex_bonus( -3 ); - break; + case AEP_DEX_DOWN: + p.mod_dex_bonus( -3 ); + break; - case AEP_PER_DOWN: - p.mod_per_bonus( -3 ); - break; + case AEP_PER_DOWN: + p.mod_per_bonus( -3 ); + break; - case AEP_INT_DOWN: - p.mod_int_bonus( -3 ); - break; + case AEP_INT_DOWN: + p.mod_int_bonus( -3 ); + break; - case AEP_ALL_DOWN: - p.mod_str_bonus( -2 ); - p.mod_dex_bonus( -2 ); - p.mod_per_bonus( -2 ); - p.mod_int_bonus( -2 ); - break; + case AEP_ALL_DOWN: + p.mod_str_bonus( -2 ); + p.mod_dex_bonus( -2 ); + p.mod_per_bonus( -2 ); + p.mod_int_bonus( -2 ); + break; - case AEP_SPEED_DOWN: - break; // Handled in player::current_speed() + case AEP_SPEED_DOWN: + break; // Handled in player::current_speed() - default: - //Suppress warnings - break; - } - } - // Recalculate, as it might have changed (by mod_*_bonus above) - p.str_cur = p.get_str(); - p.int_cur = p.get_int(); - p.dex_cur = p.get_dex(); - p.per_cur = p.get_per(); -} -//Check if an artifact's extra charge requirements are currently met -bool check_art_charge_req( item &it ) -{ - player &p = g->u; - bool reqsmet = true; - const bool worn = p.is_worn( it ); - const bool wielded = ( &it == &p.weapon ); - const bool heldweapon = ( wielded && !it.is_armor() ); //don't charge wielded clothes - switch( it.type->artifact->charge_req ) { - case( ACR_NULL ): - case( NUM_ACRS ): - break; - case( ACR_EQUIP ): - //Generated artifacts won't both be wearable and have charges, but nice for mods - reqsmet = ( worn || heldweapon ); - break; - case( ACR_SKIN ): - //As ACR_EQUIP, but also requires nothing worn on bodypart wielding or wearing item - if( !worn && !heldweapon ) { - reqsmet = false; - break; + default: + //Suppress warnings + break; + } } - for( const body_part bp : all_body_parts ) { - if( it.covers( bp ) || ( heldweapon && ( bp == bp_hand_r || bp == bp_hand_l ) ) ) { - reqsmet = true; - for( auto &i : p.worn ) { - if( i.covers( bp ) && ( &it != &i ) && i.get_coverage() > 50 ) { - reqsmet = false; - break; //This one's no good, check the next body part + // Recalculate, as it might have changed (by mod_*_bonus above) + p.str_cur = p.get_str(); + p.int_cur = p.get_int(); + p.dex_cur = p.get_dex(); + p.per_cur = p.get_per(); + } + //Check if an artifact's extra charge requirements are currently met + bool check_art_charge_req( item & it ) { + player &p = g->u; + bool reqsmet = true; + const bool worn = p.is_worn( it ); + const bool wielded = ( &it == &p.weapon ); + const bool heldweapon = ( wielded && !it.is_armor() ); //don't charge wielded clothes + switch( it.type->artifact->charge_req ) { + case( ACR_NULL ): + case( NUM_ACRS ): + break; + case( ACR_EQUIP ): + //Generated artifacts won't both be wearable and have charges, but nice for mods + reqsmet = ( worn || heldweapon ); + break; + case( ACR_SKIN ): + //As ACR_EQUIP, but also requires nothing worn on bodypart wielding or wearing item + if( !worn && !heldweapon ) { + reqsmet = false; + break; + } + for( const body_part bp : all_body_parts ) { + if( it.covers( bp ) || ( heldweapon && ( bp == bp_hand_r || bp == bp_hand_l ) ) ) { + reqsmet = true; + for( auto &i : p.worn ) { + if( i.covers( bp ) && ( &it != &i ) && i.get_coverage() > 50 ) { + reqsmet = false; + break; //This one's no good, check the next body part + } + } + if( reqsmet ) { + break; //Only need skin contact on one bodypart + } } } - if( reqsmet ) { - break; //Only need skin contact on one bodypart + break; + case( ACR_SLEEP ): + reqsmet = p.has_effect( effect_sleep ); + break; + case( ACR_RAD ): + reqsmet = ( ( g->m.get_radiation( p.pos() ) > 0 ) || ( p.radiation > 0 ) ); + break; + case( ACR_WET ): + reqsmet = std::any_of( p.body_wetness.begin(), p.body_wetness.end(), + []( const int w ) { + return w != 0; + } ); + if( !reqsmet && sum_conditions( calendar::turn - 1_turns, calendar::turn, p.pos() ).rain_amount > 0 + && !( p.in_vehicle && g->m.veh_at( p.pos() )->is_inside() ) ) { + reqsmet = true; } - } - } - break; - case( ACR_SLEEP ): - reqsmet = p.has_effect( effect_sleep ); - break; - case( ACR_RAD ): - reqsmet = ( ( g->m.get_radiation( p.pos() ) > 0 ) || ( p.radiation > 0 ) ); - break; - case( ACR_WET ): - reqsmet = std::any_of( p.body_wetness.begin(), p.body_wetness.end(), - []( const int w ) { - return w != 0; - } ); - if( !reqsmet && sum_conditions( calendar::turn - 1_turns, calendar::turn, p.pos() ).rain_amount > 0 - && !( p.in_vehicle && g->m.veh_at( p.pos() )->is_inside() ) ) { - reqsmet = true; + break; + case( ACR_SKY ): + reqsmet = ( p.posz() > 0 ); + break; } - break; - case( ACR_SKY ): - reqsmet = ( p.posz() > 0 ); - break; - } - return reqsmet; -} - -void game::start_calendar() -{ - const bool scen_season = scen->has_flag( "SPR_START" ) || scen->has_flag( "SUM_START" ) || - scen->has_flag( "AUT_START" ) || scen->has_flag( "WIN_START" ) || - scen->has_flag( "SUM_ADV_START" ); - - if( scen_season ) { - // Configured starting date overridden by scenario, calendar::start is left as Spring 1 - calendar::start_of_cataclysm = calendar::turn_zero + 1_hours * get_option( "INITIAL_TIME" ); - calendar::turn = calendar::turn_zero + 1_hours * get_option( "INITIAL_TIME" ); - if( scen->has_flag( "SPR_START" ) ) { - calendar::initial_season = SPRING; - } else if( scen->has_flag( "SUM_START" ) ) { - calendar::initial_season = SUMMER; - calendar::turn += calendar::season_length(); - } else if( scen->has_flag( "AUT_START" ) ) { - calendar::initial_season = AUTUMN; - calendar::turn += calendar::season_length() * 2; - } else if( scen->has_flag( "WIN_START" ) ) { - calendar::initial_season = WINTER; - calendar::turn += calendar::season_length() * 3; - } else if( scen->has_flag( "SUM_ADV_START" ) ) { - calendar::initial_season = SUMMER; - calendar::turn += calendar::season_length() * 5; - } else { - debugmsg( "The Unicorn" ); - } - } else { - // No scenario, so use the starting date+time configured in world options - const int initial_days = get_option( "INITIAL_DAY" ); - calendar::start_of_cataclysm = calendar::turn_zero + 1_days * initial_days; - - // Determine the season based off how long the seasons are set to be - // First mod by length of season to get number of seasons elapsed, then mod by 4 to force a 0-3 range of values - const int season_number = ( initial_days % get_option( "SEASON_LENGTH" ) ) % 4; - if( season_number == 0 ) { - calendar::initial_season = SPRING; - } else if( season_number == 1 ) { - calendar::initial_season = SUMMER; - } else if( season_number == 2 ) { - calendar::initial_season = AUTUMN; - } else { - calendar::initial_season = WINTER; - } + return reqsmet; + } + + void game::start_calendar() { + const bool scen_season = scen->has_flag( "SPR_START" ) || scen->has_flag( "SUM_START" ) || + scen->has_flag( "AUT_START" ) || scen->has_flag( "WIN_START" ) || + scen->has_flag( "SUM_ADV_START" ); + + if( scen_season ) { + // Configured starting date overridden by scenario, calendar::start is left as Spring 1 + calendar::start_of_cataclysm = calendar::turn_zero + 1_hours * get_option( "INITIAL_TIME" ); + calendar::turn = calendar::turn_zero + 1_hours * get_option( "INITIAL_TIME" ); + if( scen->has_flag( "SPR_START" ) ) { + calendar::initial_season = SPRING; + } else if( scen->has_flag( "SUM_START" ) ) { + calendar::initial_season = SUMMER; + calendar::turn += calendar::season_length(); + } else if( scen->has_flag( "AUT_START" ) ) { + calendar::initial_season = AUTUMN; + calendar::turn += calendar::season_length() * 2; + } else if( scen->has_flag( "WIN_START" ) ) { + calendar::initial_season = WINTER; + calendar::turn += calendar::season_length() * 3; + } else if( scen->has_flag( "SUM_ADV_START" ) ) { + calendar::initial_season = SUMMER; + calendar::turn += calendar::season_length() * 5; + } else { + debugmsg( "The Unicorn" ); + } + } else { + // No scenario, so use the starting date+time configured in world options + const int initial_days = get_option( "INITIAL_DAY" ); + calendar::start_of_cataclysm = calendar::turn_zero + 1_days * initial_days; + + // Determine the season based off how long the seasons are set to be + // First mod by length of season to get number of seasons elapsed, then mod by 4 to force a 0-3 range of values + const int season_number = ( initial_days % get_option( "SEASON_LENGTH" ) ) % 4; + if( season_number == 0 ) { + calendar::initial_season = SPRING; + } else if( season_number == 1 ) { + calendar::initial_season = SUMMER; + } else if( season_number == 2 ) { + calendar::initial_season = AUTUMN; + } else { + calendar::initial_season = WINTER; + } - calendar::turn = calendar::start_of_cataclysm - + 1_hours * get_option( "INITIAL_TIME" ) - + 1_days * get_option( "SPAWN_DELAY" ); - } + calendar::turn = calendar::start_of_cataclysm + + 1_hours * get_option( "INITIAL_TIME" ) + + 1_days * get_option( "SPAWN_DELAY" ); + } -} + } -void game::add_artifact_messages( const std::vector &effects ) -{ - int net_str = 0; - int net_dex = 0; - int net_per = 0; - int net_int = 0; - int net_speed = 0; + void game::add_artifact_messages( const std::vector &effects ) { + int net_str = 0; + int net_dex = 0; + int net_per = 0; + int net_int = 0; + int net_speed = 0; - for( auto &i : effects ) { - switch( i ) { - case AEP_STR_UP: - net_str += 4; - break; - case AEP_DEX_UP: - net_dex += 4; - break; - case AEP_PER_UP: - net_per += 4; - break; - case AEP_INT_UP: - net_int += 4; - break; - case AEP_ALL_UP: - net_str += 2; - net_dex += 2; - net_per += 2; - net_int += 2; - break; - case AEP_STR_DOWN: - net_str -= 3; - break; - case AEP_DEX_DOWN: - net_dex -= 3; - break; - case AEP_PER_DOWN: - net_per -= 3; - break; - case AEP_INT_DOWN: - net_int -= 3; - break; - case AEP_ALL_DOWN: - net_str -= 2; - net_dex -= 2; - net_per -= 2; - net_int -= 2; - break; + for( auto &i : effects ) { + switch( i ) { + case AEP_STR_UP: + net_str += 4; + break; + case AEP_DEX_UP: + net_dex += 4; + break; + case AEP_PER_UP: + net_per += 4; + break; + case AEP_INT_UP: + net_int += 4; + break; + case AEP_ALL_UP: + net_str += 2; + net_dex += 2; + net_per += 2; + net_int += 2; + break; + case AEP_STR_DOWN: + net_str -= 3; + break; + case AEP_DEX_DOWN: + net_dex -= 3; + break; + case AEP_PER_DOWN: + net_per -= 3; + break; + case AEP_INT_DOWN: + net_int -= 3; + break; + case AEP_ALL_DOWN: + net_str -= 2; + net_dex -= 2; + net_per -= 2; + net_int -= 2; + break; - case AEP_SPEED_UP: - net_speed += 20; - break; - case AEP_SPEED_DOWN: - net_speed -= 20; - break; + case AEP_SPEED_UP: + net_speed += 20; + break; + case AEP_SPEED_DOWN: + net_speed -= 20; + break; - case AEP_PBLUE: - break; // No message + case AEP_PBLUE: + break; // No message - case AEP_SNAKES: - add_msg( m_warning, _( "Your skin feels slithery." ) ); - break; + case AEP_SNAKES: + add_msg( m_warning, _( "Your skin feels slithery." ) ); + break; - case AEP_INVISIBLE: - add_msg( m_good, _( "You fade into invisibility!" ) ); - break; + case AEP_INVISIBLE: + add_msg( m_good, _( "You fade into invisibility!" ) ); + break; - case AEP_CLAIRVOYANCE: - case AEP_CLAIRVOYANCE_PLUS: - add_msg( m_good, _( "You can see through walls!" ) ); - break; + case AEP_CLAIRVOYANCE: + case AEP_CLAIRVOYANCE_PLUS: + add_msg( m_good, _( "You can see through walls!" ) ); + break; - case AEP_SUPER_CLAIRVOYANCE: - add_msg( m_good, _( "You can see through everything!" ) ); - break; + case AEP_SUPER_CLAIRVOYANCE: + add_msg( m_good, _( "You can see through everything!" ) ); + break; - case AEP_STEALTH: - add_msg( m_good, _( "Your steps stop making noise." ) ); - break; + case AEP_STEALTH: + add_msg( m_good, _( "Your steps stop making noise." ) ); + break; - case AEP_GLOW: - add_msg( _( "A glow of light forms around you." ) ); - break; + case AEP_GLOW: + add_msg( _( "A glow of light forms around you." ) ); + break; - case AEP_PSYSHIELD: - add_msg( m_good, _( "Your mental state feels protected." ) ); - break; + case AEP_PSYSHIELD: + add_msg( m_good, _( "Your mental state feels protected." ) ); + break; - case AEP_RESIST_ELECTRICITY: - add_msg( m_good, _( "You feel insulated." ) ); - break; + case AEP_RESIST_ELECTRICITY: + add_msg( m_good, _( "You feel insulated." ) ); + break; - case AEP_CARRY_MORE: - add_msg( m_good, _( "Your back feels strengthened." ) ); - break; + case AEP_CARRY_MORE: + add_msg( m_good, _( "Your back feels strengthened." ) ); + break; - case AEP_FUN: - add_msg( m_good, _( "You feel a pleasant tingle." ) ); - break; + case AEP_FUN: + add_msg( m_good, _( "You feel a pleasant tingle." ) ); + break; - case AEP_HUNGER: - add_msg( m_warning, _( "You feel hungry." ) ); - break; + case AEP_HUNGER: + add_msg( m_warning, _( "You feel hungry." ) ); + break; - case AEP_THIRST: - add_msg( m_warning, _( "You feel thirsty." ) ); - break; + case AEP_THIRST: + add_msg( m_warning, _( "You feel thirsty." ) ); + break; - case AEP_EVIL: - add_msg( m_warning, _( "You feel an evil presence…" ) ); - break; + case AEP_EVIL: + add_msg( m_warning, _( "You feel an evil presence…" ) ); + break; - case AEP_SCHIZO: - add_msg( m_bad, _( "You feel a tickle of insanity." ) ); - break; + case AEP_SCHIZO: + add_msg( m_bad, _( "You feel a tickle of insanity." ) ); + break; - case AEP_RADIOACTIVE: - add_msg( m_warning, _( "Your skin prickles with radiation." ) ); - break; + case AEP_RADIOACTIVE: + add_msg( m_warning, _( "Your skin prickles with radiation." ) ); + break; - case AEP_MUTAGENIC: - add_msg( m_bad, _( "You feel your genetic makeup degrading." ) ); - break; + case AEP_MUTAGENIC: + add_msg( m_bad, _( "You feel your genetic makeup degrading." ) ); + break; - case AEP_ATTENTION: - add_msg( m_warning, _( "You feel an otherworldly attention upon you…" ) ); - break; + case AEP_ATTENTION: + add_msg( m_warning, _( "You feel an otherworldly attention upon you…" ) ); + break; - case AEP_FORCE_TELEPORT: - add_msg( m_bad, _( "You feel a force pulling you inwards." ) ); - break; + case AEP_FORCE_TELEPORT: + add_msg( m_bad, _( "You feel a force pulling you inwards." ) ); + break; - case AEP_MOVEMENT_NOISE: - add_msg( m_warning, _( "You hear a rattling noise coming from inside yourself." ) ); - break; + case AEP_MOVEMENT_NOISE: + add_msg( m_warning, _( "You hear a rattling noise coming from inside yourself." ) ); + break; - case AEP_BAD_WEATHER: - add_msg( m_warning, _( "You feel storms coming." ) ); - break; + case AEP_BAD_WEATHER: + add_msg( m_warning, _( "You feel storms coming." ) ); + break; - case AEP_SICK: - add_msg( m_bad, _( "You feel unwell." ) ); - break; + case AEP_SICK: + add_msg( m_bad, _( "You feel unwell." ) ); + break; - case AEP_SMOKE: - add_msg( m_warning, _( "A cloud of smoke appears." ) ); - break; - default: - //Suppress warnings - break; - } - } + case AEP_SMOKE: + add_msg( m_warning, _( "A cloud of smoke appears." ) ); + break; + default: + //Suppress warnings + break; + } + } - std::string stat_info; - if( net_str != 0 ) { - stat_info += string_format( _( "Str %s%d! " ), - ( net_str > 0 ? "+" : "" ), net_str ); - } - if( net_dex != 0 ) { - stat_info += string_format( _( "Dex %s%d! " ), - ( net_dex > 0 ? "+" : "" ), net_dex ); - } - if( net_int != 0 ) { - stat_info += string_format( _( "Int %s%d! " ), - ( net_int > 0 ? "+" : "" ), net_int ); - } - if( net_per != 0 ) { - stat_info += string_format( _( "Per %s%d! " ), - ( net_per > 0 ? "+" : "" ), net_per ); - } + std::string stat_info; + if( net_str != 0 ) { + stat_info += string_format( _( "Str %s%d! " ), + ( net_str > 0 ? "+" : "" ), net_str ); + } + if( net_dex != 0 ) { + stat_info += string_format( _( "Dex %s%d! " ), + ( net_dex > 0 ? "+" : "" ), net_dex ); + } + if( net_int != 0 ) { + stat_info += string_format( _( "Int %s%d! " ), + ( net_int > 0 ? "+" : "" ), net_int ); + } + if( net_per != 0 ) { + stat_info += string_format( _( "Per %s%d! " ), + ( net_per > 0 ? "+" : "" ), net_per ); + } - if( !stat_info.empty() ) { - add_msg( m_neutral, stat_info ); - } + if( !stat_info.empty() ) { + add_msg( m_neutral, stat_info ); + } - if( net_speed != 0 ) { - add_msg( m_info, _( "Speed %s%d!" ), ( net_speed > 0 ? "+" : "" ), net_speed ); - } -} + if( net_speed != 0 ) { + add_msg( m_info, _( "Speed %s%d!" ), ( net_speed > 0 ? "+" : "" ), net_speed ); + } + } -void game::add_artifact_dreams( ) -{ - //If player is sleeping, get a dream from a carried artifact - //Don't need to check that player is sleeping here, that's done before calling - std::list art_items = g->u.get_artifact_items(); - std::vector valid_arts; - std::vector> - valid_dreams; // Tracking separately so we only need to check its req once - //Pull the list of dreams - add_msg( m_debug, "Checking %s carried artifacts", art_items.size() ); - for( auto &it : art_items ) { - //Pick only the ones with an applicable dream - auto art = it->type->artifact; - if( art.has_value() && art->charge_req != ACR_NULL && - ( it->ammo_remaining() < it->ammo_capacity() || - it->ammo_capacity() == 0 ) ) { //or max 0 in case of wacky mod shenanigans - add_msg( m_debug, "Checking artifact %s", it->tname() ); - if( check_art_charge_req( *it ) ) { - add_msg( m_debug, " Has freq %s,%s", art->dream_freq_met, art->dream_freq_unmet ); - if( art->dream_freq_met > 0 && x_in_y( art->dream_freq_met, 100 ) ) { - add_msg( m_debug, "Adding met dream from %s", it->tname() ); - valid_arts.push_back( it ); - valid_dreams.push_back( art->dream_msg_met ); + void game::add_artifact_dreams( ) { + //If player is sleeping, get a dream from a carried artifact + //Don't need to check that player is sleeping here, that's done before calling + std::list art_items = g->u.get_artifact_items(); + std::vector valid_arts; + std::vector> + valid_dreams; // Tracking separately so we only need to check its req once + //Pull the list of dreams + add_msg( m_debug, "Checking %s carried artifacts", art_items.size() ); + for( auto &it : art_items ) { + //Pick only the ones with an applicable dream + auto art = it->type->artifact; + if( art.has_value() && art->charge_req != ACR_NULL && + ( it->ammo_remaining() < it->ammo_capacity() || + it->ammo_capacity() == 0 ) ) { //or max 0 in case of wacky mod shenanigans + add_msg( m_debug, "Checking artifact %s", it->tname() ); + if( check_art_charge_req( *it ) ) { + add_msg( m_debug, " Has freq %s,%s", art->dream_freq_met, art->dream_freq_unmet ); + if( art->dream_freq_met > 0 && x_in_y( art->dream_freq_met, 100 ) ) { + add_msg( m_debug, "Adding met dream from %s", it->tname() ); + valid_arts.push_back( it ); + valid_dreams.push_back( art->dream_msg_met ); + } + } else { + add_msg( m_debug, " Has freq %s,%s", art->dream_freq_met, art->dream_freq_unmet ); + if( art->dream_freq_unmet > 0 && x_in_y( art->dream_freq_unmet, 100 ) ) { + add_msg( m_debug, "Adding unmet dream from %s", it->tname() ); + valid_arts.push_back( it ); + valid_dreams.push_back( art->dream_msg_unmet ); + } + } } + } + if( !valid_dreams.empty() ) { + add_msg( m_debug, "Found %s valid artifact dreams", valid_dreams.size() ); + const int selected = rng( 0, valid_arts.size() - 1 ); + auto it = valid_arts[selected]; + auto msg = random_entry( valid_dreams[selected] ); + const std::string &dream = string_format( _( msg ), it->tname() ); + add_msg( dream ); } else { - add_msg( m_debug, " Has freq %s,%s", art->dream_freq_met, art->dream_freq_unmet ); - if( art->dream_freq_unmet > 0 && x_in_y( art->dream_freq_unmet, 100 ) ) { - add_msg( m_debug, "Adding unmet dream from %s", it->tname() ); - valid_arts.push_back( it ); - valid_dreams.push_back( art->dream_msg_unmet ); - } + add_msg( m_debug, "Didn't have any dreams, sorry" ); } } - } - if( !valid_dreams.empty() ) { - add_msg( m_debug, "Found %s valid artifact dreams", valid_dreams.size() ); - const int selected = rng( 0, valid_arts.size() - 1 ); - auto it = valid_arts[selected]; - auto msg = random_entry( valid_dreams[selected] ); - const std::string &dream = string_format( _( msg ), it->tname() ); - add_msg( dream ); - } else { - add_msg( m_debug, "Didn't have any dreams, sorry" ); - } -} -int game::get_levx() const -{ - return m.get_abs_sub().x; -} + int game::get_levx() const { + return m.get_abs_sub().x; + } -int game::get_levy() const -{ - return m.get_abs_sub().y; -} + int game::get_levy() const { + return m.get_abs_sub().y; + } -int game::get_levz() const -{ - return m.get_abs_sub().z; -} + int game::get_levz() const { + return m.get_abs_sub().z; + } -overmap &game::get_cur_om() const -{ - // The player is located in the middle submap of the map. - const tripoint sm = m.get_abs_sub() + tripoint( HALF_MAPSIZE, HALF_MAPSIZE, 0 ); - const tripoint pos_om = sm_to_om_copy( sm ); - return overmap_buffer.get( pos_om.xy() ); -} + overmap &game::get_cur_om() const { + // The player is located in the middle submap of the map. + const tripoint sm = m.get_abs_sub() + tripoint( HALF_MAPSIZE, HALF_MAPSIZE, 0 ); + const tripoint pos_om = sm_to_om_copy( sm ); + return overmap_buffer.get( pos_om.xy() ); + } -std::vector game::allies() -{ - return get_npcs_if( [&]( const npc & guy ) { - if( !guy.is_hallucination() ) { - return guy.is_ally( g->u ); - } else { - return false; + std::vector game::allies() { + return get_npcs_if( [&]( const npc & guy ) { + if( !guy.is_hallucination() ) { + return guy.is_ally( g->u ); + } else { + return false; + } + } ); } - } ); -} -std::vector game::get_creatures_if( const std::function - &pred ) -{ - std::vector result; - for( Creature &critter : all_creatures() ) { - if( pred( critter ) ) { - result.push_back( &critter ); + std::vector game::get_creatures_if( const std::function + &pred ) { + std::vector result; + for( Creature &critter : all_creatures() ) { + if( pred( critter ) ) { + result.push_back( &critter ); + } + } + return result; } - } - return result; -} -std::vector game::get_npcs_if( const std::function &pred ) -{ - std::vector result; - for( npc &guy : all_npcs() ) { - if( pred( guy ) ) { - result.push_back( &guy ); + std::vector game::get_npcs_if( const std::function &pred ) { + std::vector result; + for( npc &guy : all_npcs() ) { + if( pred( guy ) ) { + result.push_back( &guy ); + } + } + return result; } - } - return result; -} -template<> -bool game::non_dead_range::iterator::valid() -{ - current = iter->lock(); - return current && !current->is_dead(); -} + template<> + bool game::non_dead_range::iterator::valid() { + current = iter->lock(); + return current && !current->is_dead(); + } -template<> -bool game::non_dead_range::iterator::valid() -{ - current = iter->lock(); - return current && !current->is_dead(); -} + template<> + bool game::non_dead_range::iterator::valid() { + current = iter->lock(); + return current && !current->is_dead(); + } -template<> -bool game::non_dead_range::iterator::valid() -{ - current = iter->lock(); - // There is no Creature::is_dead function, so we can't write - // return current && !current->is_dead(); - if( !current ) { - return false; - } - const Creature *const critter = current.get(); - if( critter->is_monster() ) { - return !static_cast( critter )->is_dead(); - } - if( critter->is_npc() ) { - return !static_cast( critter )->is_dead(); - } - return true; // must be g->u -} + template<> + bool game::non_dead_range::iterator::valid() { + current = iter->lock(); + // There is no Creature::is_dead function, so we can't write + // return current && !current->is_dead(); + if( !current ) { + return false; + } + const Creature *const critter = current.get(); + if( critter->is_monster() ) { + return !static_cast( critter )->is_dead(); + } + if( critter->is_npc() ) { + return !static_cast( critter )->is_dead(); + } + return true; // must be g->u + } -game::monster_range::monster_range( game &g ) -{ - const auto &monsters = g.critter_tracker->get_monsters_list(); - items.insert( items.end(), monsters.begin(), monsters.end() ); -} + game::monster_range::monster_range( game & g ) { + const auto &monsters = g.critter_tracker->get_monsters_list(); + items.insert( items.end(), monsters.begin(), monsters.end() ); + } -game::Creature_range::Creature_range( game &g ) : u( &g.u, []( player * ) { } ) -{ - const auto &monsters = g.critter_tracker->get_monsters_list(); - items.insert( items.end(), monsters.begin(), monsters.end() ); - items.insert( items.end(), g.active_npc.begin(), g.active_npc.end() ); - items.push_back( u ); -} + game::Creature_range::Creature_range( game & g ) : u( &g.u, []( player * ) { } ) { + const auto &monsters = g.critter_tracker->get_monsters_list(); + items.insert( items.end(), monsters.begin(), monsters.end() ); + items.insert( items.end(), g.active_npc.begin(), g.active_npc.end() ); + items.push_back( u ); + } -game::npc_range::npc_range( game &g ) -{ - items.insert( items.end(), g.active_npc.begin(), g.active_npc.end() ); -} + game::npc_range::npc_range( game & g ) { + items.insert( items.end(), g.active_npc.begin(), g.active_npc.end() ); + } -game::Creature_range game::all_creatures() -{ - return Creature_range( *this ); -} + game::Creature_range game::all_creatures() { + return Creature_range( *this ); + } -game::monster_range game::all_monsters() -{ - return monster_range( *this ); -} + game::monster_range game::all_monsters() { + return monster_range( *this ); + } -game::npc_range game::all_npcs() -{ - return npc_range( *this ); -} + game::npc_range game::all_npcs() { + return npc_range( *this ); + } -Creature *game::get_creature_if( const std::function &pred ) -{ - for( Creature &critter : all_creatures() ) { - if( pred( critter ) ) { - return &critter; + Creature *game::get_creature_if( const std::function &pred ) { + for( Creature &critter : all_creatures() ) { + if( pred( critter ) ) { + return &critter; + } + } + return nullptr; } - } - return nullptr; -} -std::string game::get_player_base_save_path() const -{ - return get_world_base_save_path() + "/" + base64_encode( u.name ); -} + std::string game::get_player_base_save_path() const { + return get_world_base_save_path() + "/" + base64_encode( u.name ); + } -std::string game::get_world_base_save_path() const -{ - if( world_generator->active_world == nullptr ) { - return FILENAMES["savedir"]; - } - return world_generator->active_world->folder_path(); -} + std::string game::get_world_base_save_path() const { + if( world_generator->active_world == nullptr ) { + return FILENAMES["savedir"]; + } + return world_generator->active_world->folder_path(); + } diff --git a/src/monattack.cpp b/src/monattack.cpp index ff8bc8804286a..f2f65b9ff57b5 100644 --- a/src/monattack.cpp +++ b/src/monattack.cpp @@ -1711,11 +1711,13 @@ bool mattack::fungus_inject( monster *z ) return false; } - if( g->u.mutations.has_trait( trait_THRESH_MARLOSS ) || g->u.mutations.has_trait( trait_THRESH_MYCUS ) ) { + if( g->u.mutations.has_trait( trait_THRESH_MARLOSS ) || + g->u.mutations.has_trait( trait_THRESH_MYCUS ) ) { z->friendly = 1; return true; } - if( ( g->u.mutations.has_trait( trait_MARLOSS ) ) && ( g->u.mutations.has_trait( trait_MARLOSS_BLUE ) ) && + if( ( g->u.mutations.has_trait( trait_MARLOSS ) ) && + ( g->u.mutations.has_trait( trait_MARLOSS_BLUE ) ) && !g->u.crossed_threshold() ) { add_msg( m_info, _( "The %s seems to wave you toward the tower…" ), z->name() ); z->anger = 0; @@ -1768,7 +1770,8 @@ bool mattack::fungus_inject( monster *z ) bool mattack::fungus_bristle( monster *z ) { - if( g->u.mutations.has_trait( trait_THRESH_MARLOSS ) || g->u.mutations.has_trait( trait_THRESH_MYCUS ) ) { + if( g->u.mutations.has_trait( trait_THRESH_MARLOSS ) || + g->u.mutations.has_trait( trait_THRESH_MYCUS ) ) { z->friendly = 1; } Creature *target = z->attack_target(); @@ -1865,10 +1868,12 @@ bool mattack::fungus_fortify( monster *z ) Creature *target = &g->u; bool mycus = false; bool peaceful = true; - if( g->u.mutations.has_trait( trait_THRESH_MARLOSS ) || g->u.mutations.has_trait( trait_THRESH_MYCUS ) ) { + if( g->u.mutations.has_trait( trait_THRESH_MARLOSS ) || + g->u.mutations.has_trait( trait_THRESH_MYCUS ) ) { mycus = true; //No nifty support effects. Yet. This lets it rebuild hedges. } - if( ( g->u.mutations.has_trait( trait_MARLOSS ) ) && ( g->u.mutations.has_trait( trait_MARLOSS_BLUE ) ) && + if( ( g->u.mutations.has_trait( trait_MARLOSS ) ) && + ( g->u.mutations.has_trait( trait_MARLOSS_BLUE ) ) && !g->u.crossed_threshold() && !mycus ) { // You have the other two. Is it really necessary for us to fight? add_msg( m_info, _( "The %s spreads its tendrils. It seems as though it's expecting you…" ), @@ -2119,7 +2124,8 @@ bool mattack::dermatik( monster *z ) target->add_msg_if_player( m_bad, _( "The %1$s sinks its ovipositor into your %2$s!" ), z->name(), body_part_name_accusative( targeted ) ); - if( !foe->mutations.has_trait( trait_PARAIMMUNE ) || !foe->mutations.has_trait( trait_ACIDBLOOD ) ) { + if( !foe->mutations.has_trait( trait_PARAIMMUNE ) || + !foe->mutations.has_trait( trait_ACIDBLOOD ) ) { foe->add_effect( effect_dermatik, 1_turns, targeted, true ); g->events().send( foe->getID() ); } diff --git a/src/npctalk.cpp b/src/npctalk.cpp index f133a002b6496..7e59eb5f8a51f 100644 --- a/src/npctalk.cpp +++ b/src/npctalk.cpp @@ -362,8 +362,9 @@ void game::chat() } ); const int guard_count = guards.size(); - if( g->u.mutations.has_trait( trait_PROF_FOODP ) && !( g->u.is_wearing( itype_id( "foodperson_mask" ) ) || - g->u.is_wearing( itype_id( "foodperson_mask_on" ) ) ) ) { + if( g->u.mutations.has_trait( trait_PROF_FOODP ) && + !( g->u.is_wearing( itype_id( "foodperson_mask" ) ) || + g->u.is_wearing( itype_id( "foodperson_mask_on" ) ) ) ) { g->u.add_msg_if_player( m_warning, _( "You can't speak without your face!" ) ); return; } @@ -766,13 +767,14 @@ void npc::talk_to_u( bool text_only, bool radio_contact ) } } - if( g->u.mutations.has_trait( trait_PROF_FOODP ) && !( g->u.is_wearing( itype_id( "foodperson_mask" ) ) || - g->u.is_wearing( itype_id( "foodperson_mask_on" ) ) ) ) { + if( g->u.mutations.has_trait( trait_PROF_FOODP ) && + !( g->u.is_wearing( itype_id( "foodperson_mask" ) ) || + g->u.is_wearing( itype_id( "foodperson_mask_on" ) ) ) ) { d.add_topic( "TALK_NOFACE" ); } if( mutations.has_trait( trait_PROF_FOODP ) && !( is_wearing( itype_id( "foodperson_mask" ) ) || - is_wearing( itype_id( "foodperson_mask_on" ) ) ) ) { + is_wearing( itype_id( "foodperson_mask_on" ) ) ) ) { d.add_topic( "TALK_NPC_NOFACE" ); } diff --git a/src/player.cpp b/src/player.cpp index e3155158bcfef..cb8bd71ecabb7 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -6024,10 +6024,10 @@ void player::apply_wetness_morale( int temperature ) continue; } - const auto &part_arr = mut_drench[bp]; - const int part_ignored = part_arr[WT_IGNORED]; - const int part_neutral = part_arr[WT_NEUTRAL]; - const int part_good = part_arr[WT_GOOD]; + const auto &part_arr = mutations.get_mut_drench( bp ); + const int part_ignored = part_arr[character_mutations::water_tolerance::WT_IGNORED]; + const int part_neutral = part_arr[character_mutations::water_tolerance::WT_NEUTRAL]; + const int part_good = part_arr[character_mutations::water_tolerance::WT_GOOD]; if( part_ignored >= part_drench ) { continue; diff --git a/src/veh_interact.cpp b/src/veh_interact.cpp index 884d12c672166..a925160419bf0 100644 --- a/src/veh_interact.cpp +++ b/src/veh_interact.cpp @@ -121,7 +121,7 @@ player_activity veh_interact::serialize_activity() time = vp->removal_time( g->u ); break; } - if( g->u.has_trait( trait_id( "DEBUG_HS" ) ) ) { + if( g->u.mutations.has_trait( trait_id( "DEBUG_HS" ) ) ) { time = 1; } player_activity res( activity_id( "ACT_VEHICLE" ), time, static_cast( sel_cmd ) ); @@ -512,7 +512,7 @@ task_reason veh_interact::cant_do( char mode ) case 'm': { // mend mode enough_morale = g->u.has_morale_to_craft(); - const bool toggling = g->u.has_trait( trait_DEBUG_HS ); + const bool toggling = g->u.mutations.has_trait( trait_DEBUG_HS ); valid_target = std::any_of( veh->parts.begin(), veh->parts.end(), [toggling]( const vehicle_part & pt ) { if( toggling ) { @@ -772,7 +772,7 @@ bool veh_interact::can_install_part() // NOLINTNEXTLINE(cata-use-named-point-constants) fold_and_print( w_msg, point( 1, 0 ), getmaxx( w_msg ) - 2, c_light_gray, msg.str() ); wrefresh( w_msg ); - return ok || g->u.has_trait( trait_DEBUG_HS ); + return ok || g->u.mutations.has_trait( trait_DEBUG_HS ); } /** @@ -1184,7 +1184,7 @@ bool veh_interact::do_mend( std::string &msg ) set_title( _( "Choose a part here to mend:" ) ); - const bool toggling = g->u.has_trait( trait_DEBUG_HS ); + const bool toggling = g->u.mutations.has_trait( trait_DEBUG_HS ); auto sel = [toggling]( const vehicle_part & pt ) { if( toggling ) { return !pt.faults_potential().empty(); @@ -1695,7 +1695,7 @@ bool veh_interact::can_remove_part( int idx, const player &p ) // NOLINTNEXTLINE(cata-use-named-point-constants) fold_and_print( w_msg, point( 1, 0 ), getmaxx( w_msg ) - 2, c_light_gray, msg.str() ); wrefresh( w_msg ); - return ok || g->u.has_trait( trait_DEBUG_HS ); + return ok || g->u.mutations.has_trait( trait_DEBUG_HS ); } bool veh_interact::do_remove( std::string &msg ) @@ -1923,7 +1923,7 @@ int veh_interact::part_at( const point &d ) */ bool veh_interact::can_potentially_install( const vpart_info &vpart ) { - return g->u.has_trait( trait_DEBUG_HS ) || + return g->u.mutations.has_trait( trait_DEBUG_HS ) || vpart.install_requirements().can_make_with_inventory( crafting_inv, is_crafting_component ); } @@ -2796,7 +2796,7 @@ void veh_interact::complete_vehicle( player &p ) } } if( base.is_null() ) { - if( !p.has_trait( trait_DEBUG_HS ) ) { + if( !p.mutations.has_trait( trait_DEBUG_HS ) ) { add_msg( m_info, _( "Could not find base part in requirements for %s." ), vpinfo.name() ); break; } else { diff --git a/src/wish.cpp b/src/wish.cpp index 4f1304abd1e2b..8de2ffeb12331 100644 --- a/src/wish.cpp +++ b/src/wish.cpp @@ -66,7 +66,8 @@ class wish_mutate_callback: public uilist_callback p->mutations.set_mutation( *p, vTraits[ entnum ] ); p->mutations.toggle_trait( *p, vTraits[ entnum ] ); } - menu->entries[ entnum ].text_color = p->mutations.has_trait( vTraits[ entnum ] ) ? c_green : menu->text_color; + menu->entries[ entnum ].text_color = p->mutations.has_trait( vTraits[ entnum ] ) ? c_green : + menu->text_color; menu->entries[ entnum ].extratxt.txt = p->mutations.has_base_trait( vTraits[ entnum ] ) ? "T" : ""; return true; } From 8174887cf3acb9b6dd3684f50cad8315d4cad9d6 Mon Sep 17 00:00:00 2001 From: KorGgenT Date: Thu, 17 Oct 2019 22:13:44 -0400 Subject: [PATCH 26/34] fix function calls --- src/bionics.cpp | 31 +++++++++++---------- src/item.cpp | 30 ++++++++++---------- src/iuse_actor.cpp | 58 +++++++++++++++++++-------------------- src/mapgen.cpp | 4 +-- src/melee.cpp | 50 ++++++++++++++++----------------- src/mission_companion.cpp | 30 ++++++++++---------- src/monster.cpp | 28 +++++++++---------- src/npctalk_funcs.cpp | 8 +++--- src/tutorial.cpp | 2 +- 9 files changed, 122 insertions(+), 119 deletions(-) diff --git a/src/bionics.cpp b/src/bionics.cpp index 5f07dcc1ff24f..50765ee7c8562 100644 --- a/src/bionics.cpp +++ b/src/bionics.cpp @@ -1102,7 +1102,7 @@ void player::bionics_uninstall_failure( int difficulty, int success, float adjus add_msg( m_neutral, _( "The removal is a failure." ) ); switch( fail_type ) { case 1: - if( !has_trait( trait_id( "NOPAIN" ) ) ) { + if( !mutations.has_trait( trait_id( "NOPAIN" ) ) ) { add_msg_if_player( m_bad, _( "It really hurts!" ) ); mod_pain( rng( failure_level * 3, failure_level * 6 ) ); } @@ -1177,7 +1177,7 @@ void player::bionics_uninstall_failure( monster &installer, player &patient, int } switch( fail_type ) { case 1: - if( !has_trait( trait_id( "NOPAIN" ) ) ) { + if( !mutations.has_trait( trait_id( "NOPAIN" ) ) ) { patient.add_msg_if_player( m_bad, _( "It really hurts!" ) ); patient.mod_pain( rng( failure_level * 3, failure_level * 6 ) ); } @@ -1219,8 +1219,8 @@ bool player::has_enough_anesth( const itype *cbm, player &patient ) return false; } - if( has_bionic( bionic_id( "bio_painkiller" ) ) || has_trait( trait_NOPAIN ) || - has_trait( trait_id( "DEBUG_BIONICS" ) ) ) { + if( has_bionic( bionic_id( "bio_painkiller" ) ) || mutations.has_trait( trait_NOPAIN ) || + mutations.has_trait( trait_id( "DEBUG_BIONICS" ) ) ) { return true; } @@ -1266,14 +1266,14 @@ int player::bionics_pl_skill( const skill_id &most_important_skill, const skill_ } // Medical residents have some idea what they're doing - if( has_trait( trait_PROF_MED ) ) { + if( mutations.has_trait( trait_PROF_MED ) ) { pl_skill += 3; add_msg_player_or_npc( m_neutral, _( "You prep to begin surgery." ), _( " prepares for surgery." ) ); } // People trained in bionics gain an additional advantage towards using it - if( has_trait( trait_PROF_AUTODOC ) ) { + if( mutations.has_trait( trait_PROF_AUTODOC ) ) { pl_skill += 7; add_msg( m_neutral, _( "A lifetime of augmentation has taught %s a thing or two…" ), disp_name() ); @@ -1285,7 +1285,7 @@ int player::bionics_pl_skill( const skill_id &most_important_skill, const skill_ int bionic_manip_cos( float adjusted_skill, bool autodoc, int bionic_difficulty ) { if( ( autodoc && get_option < bool > ( "SAFE_AUTODOC" ) ) || - g->u.has_trait( trait_id( "DEBUG_BIONICS" ) ) ) { + g->u.mutations.has_trait( trait_id( "DEBUG_BIONICS" ) ) ) { return 100; } @@ -1698,7 +1698,8 @@ bool player::install_bionics( const itype &type, player &installer, bool autodoc activity.str_values.push_back( "" ); activity.str_values.push_back( "" ); } - if( installer.has_trait( trait_PROF_MED ) || installer.has_trait( trait_PROF_AUTODOC ) ) { + if( installer.mutations.has_trait( trait_PROF_MED ) || + installer.mutations.has_trait( trait_PROF_AUTODOC ) ) { activity.str_values.push_back( installer.disp_name( true ) ); } else { activity.str_values.push_back( "NOT_MED" ); @@ -1713,7 +1714,7 @@ bool player::install_bionics( const itype &type, player &installer, bool autodoc add_effect( effect_under_op, difficulty * 20_minutes, elem.first, true, difficulty ); } for( const trait_id &mid : bioid->canceled_mutations ) { - if( has_trait( mid ) ) { + if( mutations.has_trait( mid ) ) { activity.str_values.push_back( mid.c_str() ); } } @@ -1740,7 +1741,7 @@ void player::perform_install( bionic_id bid, bionic_id upbid, int difficulty, in if( !trait_to_rem.empty() ) { for( trait_id tid : trait_to_rem ) { - remove_mutation( tid ); + mutations.remove_mutation( *this, tid ); } } @@ -1790,7 +1791,7 @@ void player::bionics_install_failure( bionic_id bid, std::string installer, int switch( fail_type ) { case 1: - if( !( has_trait( trait_id( "NOPAIN" ) ) ) ) { + if( !( mutations.has_trait( trait_id( "NOPAIN" ) ) ) ) { add_msg_if_player( m_bad, _( "It really hurts!" ) ); mod_pain( rng( failure_level * 3, failure_level * 6 ) ); } @@ -2274,7 +2275,7 @@ void player::introduce_into_anesthesia( const time_duration &duration, player &i _( " settles into position, sliding their wrist into the couch's strap." ) ); if( needs_anesthesia ) { //post-threshold medical mutants do not fear operations. - if( has_trait( trait_THRESH_MEDICAL ) ) { + if( mutations.has_trait( trait_THRESH_MEDICAL ) ) { add_msg_if_player( m_mixed, _( "You feel excited as the operation starts." ) ); } @@ -2285,7 +2286,7 @@ void player::introduce_into_anesthesia( const time_duration &duration, player &i //post-threshold medical mutants with Deadened don't need anesthesia due to their inability to feel pain } else { //post-threshold medical mutants do not fear operations. - if( has_trait( trait_THRESH_MEDICAL ) ) { + if( mutations.has_trait( trait_THRESH_MEDICAL ) ) { add_msg_if_player( m_mixed, _( "You feel excited as the Autodoc slices painlessly into you. You enjoy the sight of scalpels slicing you apart, but as operation proceeds you suddenly feel tired and pass out." ) ); } else { @@ -2295,8 +2296,8 @@ void player::introduce_into_anesthesia( const time_duration &duration, player &i } //Pain junkies feel sorry about missed pain from operation. - if( has_trait( trait_MASOCHIST ) || has_trait( trait_MASOCHIST_MED ) || - has_trait( trait_CENOBITE ) ) { + if( mutations.has_trait( trait_MASOCHIST ) || mutations.has_trait( trait_MASOCHIST_MED ) || + mutations.has_trait( trait_CENOBITE ) ) { add_msg_if_player( m_mixed, _( "As your conciousness slips away, you feel regret that you won't be able to enjoy the operation." ) ); } diff --git a/src/item.cpp b/src/item.cpp index a66c167b1fd8d..1d4af4ba191a6 100644 --- a/src/item.cpp +++ b/src/item.cpp @@ -991,11 +991,11 @@ static std::string get_freshness_description( const item &food_item ) item::sizing item::get_sizing( const Character &p, bool wearable ) const { if( wearable ) { - const bool small = p.has_trait( trait_small2 ) || - p.has_trait( trait_small_ok ); + const bool small = p.mutations.has_trait( trait_small2 ) || + p.mutations.has_trait( trait_small_ok ); - const bool big = p.has_trait( trait_huge ) || - p.has_trait( trait_huge_ok ); + const bool big = p.mutations.has_trait( trait_huge ) || + p.mutations.has_trait( trait_huge_ok ); // due to the iterative nature of these features, something can fit and be undersized/oversized // but that is fine because we have separate logic to adjust encumberance per each. One day we @@ -1364,7 +1364,8 @@ std::string item::info( std::vector &info, const iteminfo_query *parts abs( static_cast( food_item->charges ) * batch ) ) ); } if( food_item->corpse != nullptr && ( debug || ( g != nullptr && - ( g->u.has_bionic( bionic_id( "bio_scent_vision" ) ) || g->u.has_trait( trait_id( "CARNIVORE" ) ) || + ( g->u.has_bionic( bionic_id( "bio_scent_vision" ) ) || + g->u.mutations.has_trait( trait_id( "CARNIVORE" ) ) || g->u.has_artifact_with( AEP_SUPER_CLAIRVOYANCE ) ) ) ) && parts->test( iteminfo_parts::FOOD_SMELL ) ) { info.push_back( iteminfo( "FOOD", _( "Smells like: " ) + food_item->corpse->nname() ) ); @@ -1389,7 +1390,7 @@ std::string item::info( std::vector &info, const iteminfo_query *parts } if( food_item->has_flag( "CANNIBALISM" ) && parts->test( iteminfo_parts::FOOD_CANNIBALISM ) ) { - if( !g->u.has_trait_flag( "CANNIBAL" ) ) { + if( !g->u.mutations.has_trait_flag( "CANNIBAL" ) ) { info.emplace_back( "DESCRIPTION", _( "* This food contains human flesh." ) ); } else { info.emplace_back( "DESCRIPTION", _( "* This food contains human flesh." ) ); @@ -1442,7 +1443,7 @@ std::string item::info( std::vector &info, const iteminfo_query *parts if( g->u.has_bionic( bionic_id( "bio_digestion" ) ) ) { info.push_back( iteminfo( "DESCRIPTION", _( "This food has started to rot, but your bionic digestion can tolerate it." ) ) ); - } else if( g->u.has_trait( trait_id( "SAPROVORE" ) ) ) { + } else if( g->u.mutations.has_trait( trait_id( "SAPROVORE" ) ) ) { info.push_back( iteminfo( "DESCRIPTION", _( "This food has started to rot, but you can tolerate it." ) ) ); } else { @@ -2568,8 +2569,9 @@ std::string item::info( std::vector &info, const iteminfo_query *parts insert_separation_line(); } - if( is_armor() && u.has_trait( trait_id( "WOOLALLERGY" ) ) && ( made_of( material_id( "wool" ) ) || - item_tags.count( "wooled" ) ) ) { + if( is_armor() && u.mutations.has_trait( trait_id( "WOOLALLERGY" ) ) && + ( made_of( material_id( "wool" ) ) || + item_tags.count( "wooled" ) ) ) { info.push_back( iteminfo( "DESCRIPTION", _( "* This clothing will give you an allergic reaction." ) ) ); } @@ -3110,7 +3112,7 @@ nc_color item::color_in_inventory() const ret = c_cyan; } else if( has_flag( "LITCIG" ) ) { ret = c_red; - } else if( is_armor() && u.has_trait( trait_id( "WOOLALLERGY" ) ) && + } else if( is_armor() && u.mutations.has_trait( trait_id( "WOOLALLERGY" ) ) && ( made_of( material_id( "wool" ) ) || item_tags.count( "wooled" ) ) ) { ret = c_red; } else if( is_filthy() || item_tags.count( "DIRTY" ) ) { @@ -8289,9 +8291,9 @@ bool item::process_litcig( player *carrier, const tripoint &pos ) // if carried by someone: if( carrier != nullptr ) { time_duration duration = 15_seconds; - if( carrier->has_trait( trait_id( "TOLERANCE" ) ) ) { + if( carrier->mutations.has_trait( trait_id( "TOLERANCE" ) ) ) { duration = 7_seconds; - } else if( carrier->has_trait( trait_id( "LIGHTWEIGHT" ) ) ) { + } else if( carrier->mutations.has_trait( trait_id( "LIGHTWEIGHT" ) ) ) { duration = 30_seconds; } carrier->add_msg_if_player( m_neutral, _( "You take a puff of your %s." ), tname() ); @@ -8303,7 +8305,7 @@ bool item::process_litcig( player *carrier, const tripoint &pos ) carrier->moves -= 15; if( ( carrier->has_effect( effect_shakes ) && one_in( 10 ) ) || - ( carrier->has_trait( trait_id( "JITTERY" ) ) && one_in( 200 ) ) ) { + ( carrier->mutations.has_trait( trait_id( "JITTERY" ) ) && one_in( 200 ) ) ) { carrier->add_msg_if_player( m_bad, _( "Your shaking hand causes you to drop your %s." ), tname() ); g->m.add_item_or_charges( pos + point( rng( -1, 1 ), rng( -1, 1 ) ), *this ); @@ -8980,7 +8982,7 @@ skill_id item::contextualize_skill( const skill_id &id ) const bool item::is_filthy() const { return has_flag( "FILTHY" ) && ( get_option( "FILTHY_MORALE" ) || - g->u.has_trait( trait_id( "SQUEAMISH" ) ) ); + g->u.mutations.has_trait( trait_id( "SQUEAMISH" ) ) ); } bool item::on_drop( const tripoint &pos ) diff --git a/src/iuse_actor.cpp b/src/iuse_actor.cpp index 1dde63366c69c..0d933c7f4f326 100644 --- a/src/iuse_actor.cpp +++ b/src/iuse_actor.cpp @@ -692,9 +692,9 @@ int consume_drug_iuse::use( player &p, item &it, bool, const tripoint & ) const // Apply the various effects. for( const auto &eff : effects ) { time_duration dur = eff.duration; - if( p.has_trait( trait_TOLERANCE ) ) { + if( p.mutations.has_trait( trait_TOLERANCE ) ) { dur *= .8; - } else if( p.has_trait( trait_LIGHTWEIGHT ) ) { + } else if( p.mutations.has_trait( trait_LIGHTWEIGHT ) ) { dur *= 1.2; } p.add_effect( eff.id, dur, eff.bp, eff.permanent ); @@ -1254,7 +1254,7 @@ bool firestarter_actor::prep_firestarter_use( const player &p, tripoint &pos ) void firestarter_actor::resolve_firestarter_use( player &p, const tripoint &pos ) { if( g->m.add_field( pos, fd_fire, 1, 10_minutes ) ) { - if( !p.has_trait( trait_PYROMANIA ) ) { + if( !p.mutations.has_trait( trait_PYROMANIA ) ) { p.add_msg_if_player( _( "You successfully light a fire." ) ); } else { if( one_in( 4 ) ) { @@ -1748,7 +1748,7 @@ bool cauterize_actor::cauterize_effect( player &p, item &it, bool force ) hp_part hpart = dummy.use_healing_item( p, p, it, force ); if( hpart != num_hp_parts ) { p.add_msg_if_player( m_neutral, _( "You cauterize yourself." ) ); - if( !( p.has_trait( trait_NOPAIN ) ) ) { + if( !( p.mutations.has_trait( trait_NOPAIN ) ) ) { p.mod_pain( 15 ); p.add_msg_if_player( m_bad, _( "It hurts like hell!" ) ); } else { @@ -1781,8 +1781,8 @@ int cauterize_actor::use( player &p, item &it, bool t, const tripoint & ) const if( has_disease ) { did_cauterize = cauterize_effect( p, it, false ); } else { - const bool can_have_fun = p.has_trait( trait_MASOCHIST ) || p.has_trait( trait_MASOCHIST_MED ) || - p.has_trait( trait_CENOBITE ); + const bool can_have_fun = p.mutations.has_trait( trait_MASOCHIST ) || p.mutations.has_trait( trait_MASOCHIST_MED ) || + p.mutations.has_trait( trait_CENOBITE ); if( can_have_fun && query_yn( _( "Cauterize yourself for fun?" ) ) ) { did_cauterize = cauterize_effect( p, it, true ); @@ -1807,9 +1807,9 @@ ret_val cauterize_actor::can_use( const player &p, const item &it, bool, { if( !p.has_effect( effect_bite ) && !p.has_effect( effect_bleed ) && - !p.has_trait( trait_MASOCHIST ) && - !p.has_trait( trait_MASOCHIST_MED ) && - !p.has_trait( trait_CENOBITE ) ) { + !p.mutations.has_trait( trait_MASOCHIST ) && + !p.mutations.has_trait( trait_MASOCHIST_MED ) && + !p.mutations.has_trait( trait_CENOBITE ) ) { return ret_val::make_failure( _( "You are not bleeding or bitten, there is no need to cauterize yourself." ) ); @@ -1873,11 +1873,11 @@ int enzlave_actor::use( player &p, item &it, bool t, const tripoint & ) const } int tolerance_level = 9; - if( p.has_trait( trait_PSYCHOPATH ) || p.has_trait( trait_SAPIOVORE ) ) { + if( p.mutations.has_trait( trait_PSYCHOPATH ) || p.mutations.has_trait( trait_SAPIOVORE ) ) { tolerance_level = 0; - } else if( p.has_trait( trait_PRED4 ) ) { + } else if( p.mutations.has_trait( trait_PRED4 ) ) { tolerance_level = 5; - } else if( p.has_trait( trait_PRED3 ) ) { + } else if( p.mutations.has_trait( trait_PRED3 ) ) { tolerance_level = 7; } @@ -1918,12 +1918,12 @@ int enzlave_actor::use( player &p, item &it, bool t, const tripoint & ) const time_duration duration = 30_minutes * ( 5.0 / p.get_skill_level( skill_survival ) ); time_duration decayDelay = 3_minutes * ( 5.0 / p.get_skill_level( skill_survival ) ); - if( p.has_trait( trait_PACIFIST ) ) { + if( p.mutations.has_trait( trait_PACIFIST ) ) { moraleMalus *= 5; maxMalus *= 3; - } else if( p.has_trait( trait_PRED1 ) ) { + } else if( p.mutations.has_trait( trait_PRED1 ) ) { moraleMalus /= 4; - } else if( p.has_trait( trait_PRED2 ) ) { + } else if( p.mutations.has_trait( trait_PRED2 ) ) { moraleMalus /= 5; } @@ -3092,7 +3092,7 @@ bool repair_item_actor::can_repair_target( player &pl, const item &fix, } const bool resizing_matters = fix.get_encumber( pl ) != 0; - const bool small = pl.has_trait( trait_SMALL2 ) || pl.has_trait( trait_SMALL_OK ); + const bool small = pl.mutations.has_trait( trait_SMALL2 ) || pl.mutations.has_trait( trait_SMALL_OK ); const bool can_resize = small != fix.has_flag( "UNDERSIZE" ); if( can_be_refitted && resizing_matters && can_resize ) { return true; @@ -3181,19 +3181,19 @@ repair_item_actor::repair_type repair_item_actor::default_action( const item &fi return RT_REFIT; } - const bool smol = g->u.has_trait( trait_id( "SMALL2" ) ) || - g->u.has_trait( trait_id( "SMALL_OK" ) ); + const bool small = g->u.mutations.has_trait( trait_id( "SMALL2" ) ) || + g->u.mutations.has_trait( trait_id( "SMALL_OK" ) ); const bool is_undersized = fix.has_flag( "UNDERSIZE" ); const bool is_oversized = fix.has_flag( "OVERSIZE" ); const bool resizing_matters = fix.get_encumber( g->u ) != 0; - const bool too_big_while_smol = smol && !is_undersized && !is_oversized; + const bool too_big_while_smol = small && !is_undersized && !is_oversized; if( too_big_while_smol && can_be_refitted && resizing_matters ) { return RT_DOWNSIZING; } - const bool too_small_while_big = !smol && is_undersized && !is_oversized; + const bool too_small_while_big = !small && is_undersized && !is_oversized; if( too_small_while_big && can_be_refitted && resizing_matters ) { return RT_UPSIZING; } @@ -3660,7 +3660,7 @@ static hp_part pick_part_to_heal( const bool bite = bite_chance > 0.0f; const bool infect = infect_chance > 0.0f; const bool precise = &healer == &patient ? - patient.has_trait( trait_SELFAWARE ) : + patient.mutations.has_trait( trait_SELFAWARE ) : /** @EFFECT_PER slightly increases precision when using first aid on someone else */ /** @EFFECT_FIRSTAID increases precision when using first aid on someone else */ @@ -4099,9 +4099,9 @@ ret_val install_bionic_actor::can_use( const player &p, const item &it, bo return ret_val::make_failure( _( "You can't install bionics while mounted." ) ); } if( !get_option( "MANUAL_BIONIC_INSTALLATION" ) && - !p.has_trait( trait_id( "DEBUG_BIONICS" ) ) ) { + !p.mutations.has_trait( trait_id( "DEBUG_BIONICS" ) ) ) { return ret_val::make_failure( _( "You can't self-install bionics." ) ); - } else if( !p.has_trait( trait_id( "DEBUG_BIONICS" ) ) ) { + } else if( !p.mutations.has_trait( trait_id( "DEBUG_BIONICS" ) ) ) { if( it.has_flag( "FILTHY" ) ) { return ret_val::make_failure( _( "You can't install a filthy CBM!" ) ); } else if( it.has_flag( "NO_STERILE" ) ) { @@ -4233,8 +4233,8 @@ int mutagen_actor::use( player &p, item &it, bool, const tripoint & ) const const mutation_category_trait &m_category = mutation_category_trait::get_category( mutation_category ); - if( p.has_trait( trait_MUT_JUNKIE ) ) { - p.add_msg_if_player( m_good, _( "You quiver with anticipation…" ) ); + if( p.mutations.has_trait( trait_MUT_JUNKIE ) ) { + p.add_msg_if_player( m_good, _( "You quiver with anticipation..." ) ); p.add_morale( MORALE_MUTAGEN, 5, 50 ); } @@ -4250,7 +4250,7 @@ int mutagen_actor::use( player &p, item &it, bool, const tripoint & ) const int mut_count = 1 + ( is_strong ? one_in( 3 ) : 0 ); for( int i = 0; i < mut_count; i++ ) { - p.mutate_category( m_category.id ); + p.mutations.mutate_category( p, m_category.id ); p.mod_pain( m_category.mutagen_pain * rng( 1, 5 ) ); } // burn calories directly @@ -4283,7 +4283,7 @@ int mutagen_iv_actor::use( player &p, item &it, bool, const tripoint & ) const const mutation_category_trait &m_category = mutation_category_trait::get_category( mutation_category ); - if( p.has_trait( trait_MUT_JUNKIE ) ) { + if( p.mutations.has_trait( trait_MUT_JUNKIE ) ) { p.add_msg_if_player( m_category.junkie_message() ); } else { p.add_msg_if_player( m_category.iv_message() ); @@ -4293,7 +4293,7 @@ int mutagen_iv_actor::use( player &p, item &it, bool, const tripoint & ) const test_crossing_threshold( p, m_category ); // TODO: Remove the "is_player" part, implement NPC screams - if( p.is_player() && !( p.has_trait( trait_NOPAIN ) ) && m_category.iv_sound ) { + if( p.is_player() && !( p.mutations.has_trait( trait_NOPAIN ) ) && m_category.iv_sound ) { p.mod_pain( m_category.iv_pain ); /** @EFFECT_STR increases volume of painful shouting when using IV mutagen */ sounds::sound( p.pos(), m_category.iv_noise + p.str_cur, sounds::sound_t::alert, @@ -4308,7 +4308,7 @@ int mutagen_iv_actor::use( player &p, item &it, bool, const tripoint & ) const } for( int i = 0; i < mut_count; i++ ) { - p.mutate_category( m_category.id ); + p.mutations.mutate_category( p, m_category.id ); p.mod_pain( m_category.iv_pain * rng( 1, 5 ) ); } diff --git a/src/mapgen.cpp b/src/mapgen.cpp index 46befa276ecdd..6821f2bf09979 100644 --- a/src/mapgen.cpp +++ b/src/mapgen.cpp @@ -785,7 +785,7 @@ class jmapgen_npc : public jmapgen_piece npc *p = g->find_npc( npc_id ); if( p != nullptr ) { for( const std::string &new_trait : traits ) { - p->set_mutation( trait_id( new_trait ) ); + p->mutations.set_mutation( *p, trait_id( new_trait ) ); } } } @@ -6361,7 +6361,7 @@ character_id map::place_npc( const point &p, const string_id &type temp->normalize(); temp->load_npc_template( type ); temp->spawn_at_precise( { abs_sub.xy() }, { p, abs_sub.z } ); - temp->toggle_trait( trait_id( "NPC_STATIC_NPC" ) ); + temp->mutations.toggle_trait( *temp, trait_id( "NPC_STATIC_NPC" ) ); overmap_buffer.insert_npc( temp ); return temp->getID(); } diff --git a/src/melee.cpp b/src/melee.cpp index a048a01f49f5d..e7741b6b62e8b 100644 --- a/src/melee.cpp +++ b/src/melee.cpp @@ -302,13 +302,13 @@ float player::hit_roll() const // Dexterity, skills, weapon and martial arts float hit = get_hit(); // Drunken master makes us hit better - if( has_trait( trait_DRUNKEN ) ) { + if( mutations.has_trait( trait_DRUNKEN ) ) { hit += to_turns( get_effect_dur( effect_drunk ) ) / ( used_weapon().is_null() ? 300.0f : 400.0f ); } // Farsightedness makes us hit worse - if( has_trait( trait_HYPEROPIC ) && !worn_with_flag( "FIX_FARSIGHT" ) && + if( mutations.has_trait( trait_HYPEROPIC ) && !worn_with_flag( "FIX_FARSIGHT" ) && !has_effect( effect_contacts ) ) { hit -= 2.0f; } @@ -342,7 +342,7 @@ std::string player::get_miss_reason() add_miss_reason( _( "Your torso encumbrance throws you off-balance." ), roll_remainder( encumb( bp_torso ) / 10.0 ) ); - const int farsightedness = 2 * ( has_trait( trait_HYPEROPIC ) && + const int farsightedness = 2 * ( mutations.has_trait( trait_HYPEROPIC ) && !worn_with_flag( "FIX_FARSIGHT" ) && !has_effect( effect_contacts ) ); add_miss_reason( @@ -564,7 +564,7 @@ void player::melee_attack( Creature &t, bool allow_special, const matec_id &forc /** @EFFECT_STR reduces stamina cost for melee attack with heavier weapons */ const int weight_cost = cur_weapon.weight() / ( 2_gram * std::max( 1, str_cur ) ); const int encumbrance_cost = roll_remainder( ( encumb( bp_arm_l ) + encumb( bp_arm_r ) ) * 2.0f ); - const int deft_bonus = hit_spread < 0 && has_trait( trait_DEFT ) ? 50 : 0; + const int deft_bonus = hit_spread < 0 && mutations.has_trait( trait_DEFT ) ? 50 : 0; /** @EFFECT_MELEE reduces stamina cost of melee attacks */ const int mod_sta = ( weight_cost + encumbrance_cost - melee - deft_bonus + 50 ) * -1; mod_stat( "stamina", std::min( -50, mod_sta ) ); @@ -642,7 +642,7 @@ void player::reach_attack( const tripoint &p ) int stumble( player &u, const item &weap ) { - if( u.has_trait( trait_DEFT ) ) { + if( u.mutations.has_trait( trait_DEFT ) ) { return 0; } @@ -771,7 +771,7 @@ float player::get_dodge() const if( worn_with_flag( "ROLLER_INLINE" ) || worn_with_flag( "ROLLER_QUAD" ) || worn_with_flag( "ROLLER_ONE" ) ) { - ret /= has_trait( trait_PROF_SKATER ) ? 2 : 5; + ret /= mutations.has_trait( trait_PROF_SKATER ) ? 2 : 5; } if( has_effect( effect_bouldering ) ) { @@ -824,7 +824,7 @@ void player::roll_bash_damage( bool crit, damage_instance &di, bool average, stat_bonus += mabuff_damage_bonus( DT_BASH ); // Drunken Master damage bonuses - if( has_trait( trait_DRUNKEN ) && has_effect( effect_drunk ) ) { + if( mutations.has_trait( trait_DRUNKEN ) && has_effect( effect_drunk ) ) { // Remember, a single drink gives 600 levels of "drunk" int mindrunk = 0; int maxdrunk = 0; @@ -899,23 +899,23 @@ void player::roll_cut_damage( bool crit, damage_instance &di, bool average, cons weap.is_null(); if( left_empty || right_empty ) { float per_hand = 0.0f; - if( has_trait( trait_CLAWS ) || ( has_active_mutation( trait_CLAWS_RETRACT ) ) ) { + if( mutations.has_trait( trait_CLAWS ) || ( mutations.has_active_mutation( trait_CLAWS_RETRACT ) ) ) { per_hand += 3; } if( has_bionic( bionic_id( "bio_razors" ) ) ) { per_hand += 2; } - if( has_trait( trait_TALONS ) ) { + if( mutations.has_trait( trait_TALONS ) ) { /** @EFFECT_UNARMED increases cutting damage with TALONS */ per_hand += 3 + ( unarmed_skill > 8 ? 4 : unarmed_skill / 2 ); } // Stainless Steel Claws do stabbing damage, too. - if( has_trait( trait_CLAWS_RAT ) || has_trait( trait_CLAWS_ST ) ) { + if( mutations.has_trait( trait_CLAWS_RAT ) || mutations.has_trait( trait_CLAWS_ST ) ) { /** @EFFECT_UNARMED increases cutting damage with CLAWS_RAT and CLAWS_ST */ per_hand += 1 + ( unarmed_skill > 8 ? 4 : unarmed_skill / 2 ); } // TODO: add acidproof check back to slime hands (probably move it elsewhere) - if( has_trait( trait_SLIME_HANDS ) ) { + if( mutations.has_trait( trait_SLIME_HANDS ) ) { /** @EFFECT_UNARMED increases cutting damage with SLIME_HANDS */ per_hand += average ? 2.5f : rng( 2, 3 ); } @@ -972,11 +972,11 @@ void player::roll_stab_damage( bool crit, damage_instance &di, bool average, weap.is_null(); if( left_empty || right_empty ) { float per_hand = 0.0f; - if( has_trait( trait_CLAWS ) || has_active_mutation( trait_CLAWS_RETRACT ) ) { + if( mutations.has_trait( trait_CLAWS ) || mutations.has_active_mutation( trait_CLAWS_RETRACT ) ) { per_hand += 3; } - if( has_trait( trait_NAILS ) ) { + if( mutations.has_trait( trait_NAILS ) ) { per_hand += .5; } @@ -984,11 +984,11 @@ void player::roll_stab_damage( bool crit, damage_instance &di, bool average, per_hand += 2; } - if( has_trait( trait_THORNS ) ) { + if( mutations.has_trait( trait_THORNS ) ) { per_hand += 2; } - if( has_trait( trait_CLAWS_ST ) ) { + if( mutations.has_trait( trait_CLAWS_ST ) ) { /** @EFFECT_UNARMED increases stabbing damage with CLAWS_ST */ per_hand += 3 + unarmed_skill / 2.0; } @@ -1660,7 +1660,7 @@ void player::perform_special_attacks( Creature &t ) { bool can_poison = false; - std::vector special_attacks = mutation_attacks( t ); + std::vector special_attacks = mutations.mutation_attacks( t ); std::string target = t.disp_name(); @@ -1692,11 +1692,11 @@ void player::perform_special_attacks( Creature &t ) dealt_dam.type_damage( DT_STAB ) > 0; } - if( can_poison && ( has_trait( trait_POISONOUS ) || has_trait( trait_POISONOUS2 ) ) ) { - if( has_trait( trait_POISONOUS ) ) { + if( can_poison && ( mutations.has_trait( trait_POISONOUS ) || mutations.has_trait( trait_POISONOUS2 ) ) ) { + if( mutations.has_trait( trait_POISONOUS ) ) { add_msg_if_player( m_good, _( "You poison %s!" ), target ); t.add_effect( effect_poison, 6_turns ); - } else if( has_trait( trait_POISONOUS2 ) ) { + } else if( mutations.has_trait( trait_POISONOUS2 ) ) { add_msg_if_player( m_good, _( "You inject your venom into %s!" ), target ); t.add_effect( effect_badpoison, 6_turns ); } @@ -1818,7 +1818,7 @@ static damage_instance hardcoded_mutation_attack( const player &u, const trait_i return damage_instance(); } - const bool rake = u.has_trait( trait_CLAWS_TENTACLE ); + const bool rake = u.mutations.has_trait( trait_CLAWS_TENTACLE ); /** @EFFECT_STR increases damage with ARM_TENTACLES* */ damage_instance ret; @@ -2059,7 +2059,7 @@ void player_hit_message( player *attacker, const std::string &message, msgtype = m_neutral; } else if( crit ) { //Player won't see exact numbers of damage dealt by NPC unless player has DEBUG_NIGHTVISION trait - if( attacker->is_npc() && !g->u.has_trait( trait_DEBUG_NIGHTVISION ) ) { + if( attacker->is_npc() && !g->u.mutations.has_trait( trait_DEBUG_NIGHTVISION ) ) { //~ NPC hits something (critical) msg = string_format( _( "%s. Critical!" ), message ); } else { @@ -2069,7 +2069,7 @@ void player_hit_message( player *attacker, const std::string &message, sSCTmod = _( "Critical!" ); gmtSCTcolor = m_critical; } else { - if( attacker->is_npc() && !g->u.has_trait( trait_DEBUG_NIGHTVISION ) ) { + if( attacker->is_npc() && !g->u.mutations.has_trait( trait_DEBUG_NIGHTVISION ) ) { //~ NPC hits something msg = string_format( _( "%s." ), message ); } else { @@ -2131,7 +2131,7 @@ int player::attack_speed( const item &weap ) const move_cost *= ma_mult; move_cost += ma_move_cost; - move_cost *= mutation_value( "attackcost_modifier" ); + move_cost *= mutations.mutation_value( "attackcost_modifier" ); if( move_cost < 25 ) { return 25; @@ -2312,10 +2312,10 @@ void avatar::steal( npc &target ) if( !is_armed() ) { my_roll += dice( 4, 3 ); } - if( has_trait( trait_DEFT ) ) { + if( mutations.has_trait( trait_DEFT ) ) { my_roll += dice( 2, 6 ); } - if( has_trait( trait_CLUMSY ) ) { + if( mutations.has_trait( trait_CLUMSY ) ) { my_roll -= dice( 4, 6 ); } diff --git a/src/mission_companion.cpp b/src/mission_companion.cpp index 51d0592228ee8..6f051bd0218e5 100644 --- a/src/mission_companion.cpp +++ b/src/mission_companion.cpp @@ -120,7 +120,7 @@ void talk_function::companion_mission( npc &p ) if( role_id == "SCAVENGER" ) { title = _( "Junk Shop Missions" ); scavenger_patrol( mission_key, p ); - if( p.has_trait( trait_NPC_MISSION_LEV_1 ) ) { + if( p.mutations.has_trait( trait_NPC_MISSION_LEV_1 ) ) { scavenger_raid( mission_key, p ); } } else if( role_id == "COMMUNE CROPS" ) { @@ -131,7 +131,7 @@ void talk_function::companion_mission( npc &p ) } else if( role_id == "FOREMAN" ) { title = _( "Construction Missions" ); commune_menial( mission_key, p ); - if( p.has_trait( trait_NPC_MISSION_LEV_1 ) ) { + if( p.mutations.has_trait( trait_NPC_MISSION_LEV_1 ) ) { commune_carpentry( mission_key, p ); } } else if( role_id == "REFUGEE MERCHANT" ) { @@ -228,7 +228,7 @@ void talk_function::commune_carpentry( mission_data &mission_key, npc &p ) void talk_function::commune_farmfield( mission_data &mission_key, npc &p ) { - if( !p.has_trait( trait_NPC_CONSTRUCTION_LEV_1 ) ) { + if( !p.mutations.has_trait( trait_NPC_CONSTRUCTION_LEV_1 ) ) { std::string entry = _( "Cost: $1000\n\n\n" " .........\n" // NOLINT(cata-text-style) " .........\n" // NOLINT(cata-text-style) @@ -247,7 +247,7 @@ void talk_function::commune_farmfield( mission_data &mission_key, npc &p ) "willing to liquidate it." ); mission_key.add( "Purchase East Field", _( "Purchase East Field" ), entry ); } - if( p.has_trait( trait_NPC_CONSTRUCTION_LEV_1 ) && !p.has_trait( trait_NPC_CONSTRUCTION_LEV_2 ) ) { + if( p.mutations.has_trait( trait_NPC_CONSTRUCTION_LEV_1 ) && !p.mutations.has_trait( trait_NPC_CONSTRUCTION_LEV_2 ) ) { std::string entry = _( "Cost: $5500\n\n" "\n ........." // NOLINT(cata-text-style) "\n ........." // NOLINT(cata-text-style) @@ -264,7 +264,7 @@ void talk_function::commune_farmfield( mission_data &mission_key, npc &p ) mission_key.add( "Upgrade East Field I", _( "Upgrade East Field I" ), entry ); } - if( p.has_trait( trait_NPC_CONSTRUCTION_LEV_1 ) ) { + if( p.mutations.has_trait( trait_NPC_CONSTRUCTION_LEV_1 ) ) { std::string entry = _( "Cost: $3.00/plot\n\n" "\n ........." // NOLINT(cata-text-style) "\n ........." // NOLINT(cata-text-style) @@ -920,7 +920,7 @@ void talk_function::field_build_1( npc &p ) popup( _( "I'm sorry, you don't have enough money." ) ); return; } - p.set_mutation( trait_NPC_CONSTRUCTION_LEV_1 ); + p.mutations.set_mutation( p, trait_NPC_CONSTRUCTION_LEV_1 ); g->u.cash += -100000; const tripoint site = overmap_buffer.find_closest( g->u.global_omt_location(), "ranch_camp_63", 20, false ); @@ -944,7 +944,7 @@ void talk_function::field_build_2( npc &p ) popup( _( "I'm sorry, you don't have enough money." ) ); return; } - p.set_mutation( trait_NPC_CONSTRUCTION_LEV_2 ); + p.mutations.set_mutation( p, trait_NPC_CONSTRUCTION_LEV_2 ); g->u.cash += -550000; const tripoint site = overmap_buffer.find_closest( g->u.global_omt_location(), "ranch_camp_63", 20, false ); @@ -1107,7 +1107,7 @@ void talk_function::field_harvest( npc &p, const std::string &place ) int number_plants = 0; int number_seeds = 0; int skillLevel = 2; - if( p.has_trait( trait_NPC_CONSTRUCTION_LEV_2 ) ) { + if( p.mutations.has_trait( trait_NPC_CONSTRUCTION_LEV_2 ) ) { skillLevel += 2; } @@ -1239,8 +1239,8 @@ bool talk_function::scavenging_patrol_return( npc &p ) p.name, comp->name ); g->u.cash += 10000; } - if( one_in( 10 ) && !p.has_trait( trait_NPC_MISSION_LEV_1 ) ) { - p.set_mutation( trait_NPC_MISSION_LEV_1 ); + if( one_in( 10 ) && !p.mutations.has_trait( trait_NPC_MISSION_LEV_1 ) ) { + p.mutations.set_mutation( p, trait_NPC_MISSION_LEV_1 ); popup( _( "%s feels more confident in your abilities and is willing to let you " "participate in daring raids." ), p.name ); } @@ -1326,8 +1326,8 @@ bool talk_function::labor_return( npc &p ) popup( _( "%s returns from working as a laborer having earned $%d and a bit of experience…" ), comp->name, money ); companion_return( *comp ); - if( hours >= 8 && one_in( 8 ) && !p.has_trait( trait_NPC_MISSION_LEV_1 ) ) { - p.set_mutation( trait_NPC_MISSION_LEV_1 ); + if( hours >= 8 && one_in( 8 ) && !p.mutations.has_trait( trait_NPC_MISSION_LEV_1 ) ) { + p.mutations.set_mutation( p, trait_NPC_MISSION_LEV_1 ); popup( _( "%s feels more confident in your companions and is willing to let them " "participate in advanced tasks." ), p.name ); } @@ -1474,8 +1474,8 @@ bool talk_function::forage_return( npc &p ) popup( _( "%s returned with a %s for you!" ), comp->name, result.tname() ); g->u.i_add( result ); } - if( one_in( 6 ) && !p.has_trait( trait_NPC_MISSION_LEV_1 ) ) { - p.set_mutation( trait_NPC_MISSION_LEV_1 ); + if( one_in( 6 ) && !p.mutations.has_trait( trait_NPC_MISSION_LEV_1 ) ) { + p.mutations.set_mutation( p, trait_NPC_MISSION_LEV_1 ); popup( _( "%s feels more confident in your companions and is willing to let them " "participate in advanced tasks." ), p.name ); } @@ -2008,7 +2008,7 @@ npc_ptr talk_function::companion_choose_return( const tripoint &omt_pos, ( by_mission && c_mission.mission_id != mission_id ) || c_mission.role_id != role_id ) { continue; } - if( g->u.has_trait( trait_id( "DEBUG_HS" ) ) ) { + if( g->u.mutations.has_trait( trait_id( "DEBUG_HS" ) ) ) { available.push_back( guy ); } else if( deadline == calendar::before_time_starts ) { if( guy->companion_mission_time_ret <= calendar::turn ) { diff --git a/src/monster.cpp b/src/monster.cpp index be5ee152aca33..c7dbb2f653404 100644 --- a/src/monster.cpp +++ b/src/monster.cpp @@ -983,47 +983,47 @@ monster_attitude monster::attitude( const Character *u ) const static const trait_id mycus_friend( "MYCUS_FRIEND" ); static const trait_id terrifying( "TERRIFYING" ); if( faction == faction_bee ) { - if( u->has_trait( trait_BEE ) ) { + if( u->mutations.has_trait( trait_BEE ) ) { return MATT_FRIEND; - } else if( u->has_trait( trait_FLOWERS ) ) { + } else if( u->mutations.has_trait( trait_FLOWERS ) ) { effective_anger -= 10; } } - if( type->in_species( FUNGUS ) && ( u->has_trait( mycus_thresh ) || - u->has_trait( mycus_friend ) ) ) { + if( type->in_species( FUNGUS ) && ( u->mutations.has_trait( mycus_thresh ) || + u->mutations.has_trait( mycus_friend ) ) ) { return MATT_FRIEND; } if( effective_anger >= 10 && - ( ( type->in_species( MAMMAL ) && u->has_trait( pheromone_mammal ) ) || - ( type->in_species( INSECT ) && u->has_trait( pheromone_insect ) ) ) ) { + ( ( type->in_species( MAMMAL ) && u->mutations.has_trait( pheromone_mammal ) ) || + ( type->in_species( INSECT ) && u->mutations.has_trait( pheromone_insect ) ) ) ) { effective_anger -= 20; } - if( u->has_trait( terrifying ) ) { + if( u->mutations.has_trait( terrifying ) ) { effective_morale -= 10; } if( has_flag( MF_ANIMAL ) ) { - if( u->has_trait( trait_ANIMALEMPATH ) ) { + if( u->mutations.has_trait( trait_ANIMALEMPATH ) ) { effective_anger -= 10; if( effective_anger < 10 ) { effective_morale += 55; } - } else if( u->has_trait( trait_ANIMALEMPATH2 ) ) { + } else if( u->mutations.has_trait( trait_ANIMALEMPATH2 ) ) { effective_anger -= 20; if( effective_anger < 20 ) { effective_morale += 80; } - } else if( u->has_trait( trait_ANIMALDISCORD ) ) { + } else if( u->mutations.has_trait( trait_ANIMALDISCORD ) ) { if( effective_anger >= 10 ) { effective_anger += 10; } if( effective_anger < 10 ) { effective_morale -= 5; } - } else if( u->has_trait( trait_ANIMALDISCORD2 ) ) { + } else if( u->mutations.has_trait( trait_ANIMALDISCORD2 ) ) { if( effective_anger >= 20 ) { effective_anger += 20; } @@ -1033,7 +1033,7 @@ monster_attitude monster::attitude( const Character *u ) const } } - for( const trait_id &mut : u->get_mutations() ) { + for( const trait_id &mut : u->mutations.get_mutations() ) { for( const species_id &spe : mut.obj().ignored_by ) { if( type->in_species( spe ) ) { return MATT_IGNORE; @@ -2095,13 +2095,13 @@ void monster::die( Creature *nkiller ) // TODO: should actually be class Character player *ch = dynamic_cast( get_killer() ); if( !is_hallucination() && ch != nullptr ) { - if( ( has_flag( MF_GUILT ) && ch->is_player() ) || ( ch->has_trait( trait_PACIFIST ) && + if( ( has_flag( MF_GUILT ) && ch->is_player() ) || ( ch->mutations.has_trait( trait_PACIFIST ) && has_flag( MF_HUMAN ) ) ) { // has guilt flag or player is pacifist && monster is humanoid mdeath::guilt( *this ); } g->events().send( ch->getID(), type->id ); - if( ch->is_player() && ch->has_trait( trait_KILLER ) ) { + if( ch->is_player() && ch->mutations.has_trait( trait_KILLER ) ) { if( one_in( 4 ) ) { std::string snip = SNIPPET.random_from_category( "killer_on_kill" ); ch->add_msg_if_player( m_good, _( snip ) ); diff --git a/src/npctalk_funcs.cpp b/src/npctalk_funcs.cpp index 42ca01f8ac939..d875a5c0f0be0 100644 --- a/src/npctalk_funcs.cpp +++ b/src/npctalk_funcs.cpp @@ -600,7 +600,7 @@ static void generic_barber( const std::string &mut_type ) std::vector hair_muts = get_mutations_in_type( mut_type ); trait_id cur_hair; for( auto elem : hair_muts ) { - if( g->u.has_trait( elem ) ) { + if( g->u.mutations.has_trait( elem ) ) { cur_hair = elem; } index += 1; @@ -609,10 +609,10 @@ static void generic_barber( const std::string &mut_type ) hair_menu.query(); int choice = hair_menu.ret; if( choice != 0 ) { - if( g->u.has_trait( cur_hair ) ) { - g->u.remove_mutation( cur_hair, true ); + if( g->u.mutations.has_trait( cur_hair ) ) { + g->u.mutations.remove_mutation( g->u, cur_hair, true ); } - g->u.set_mutation( hair_muts[ choice - 1 ] ); + g->u.mutations.set_mutation( g->u, hair_muts[ choice - 1 ] ); add_msg( m_info, _( "You get a trendy new cut!" ) ); } } diff --git a/src/tutorial.cpp b/src/tutorial.cpp index 306a6994e5d0f..ada1c48096abf 100644 --- a/src/tutorial.cpp +++ b/src/tutorial.cpp @@ -79,7 +79,7 @@ bool tutorial_game::init() starting_om.ter_set( lp + tripoint_below, oter_id( "tutorial" ) ); starting_om.clear_mon_groups(); - g->u.toggle_trait( trait_id( "QUICK" ) ); + g->u.mutations.toggle_trait( g->u, trait_id( "QUICK" ) ); item lighter( "lighter", 0 ); lighter.invlet = 'e'; g->u.inv.add_item( lighter, true, false ); From 864474e9fa8b059e4060e405ab778b3a0a1e8c63 Mon Sep 17 00:00:00 2001 From: KorGgenT Date: Fri, 18 Oct 2019 19:06:38 -0400 Subject: [PATCH 27/34] fix bad rebase --- src/game.cpp | 2575 +++++++++++++++++++------------------ src/iuse_actor.cpp | 8 +- src/melee.cpp | 6 +- src/mission_companion.cpp | 3 +- 4 files changed, 1319 insertions(+), 1273 deletions(-) diff --git a/src/game.cpp b/src/game.cpp index d086490390455..4d3c27ab3a1f9 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -10579,1452 +10579,1493 @@ cata::optional game::find_or_make_stairs( map &mp, const int z_after, } } else if( u.mutations.has_trait( trait_VINES2 ) || u.mutations.has_trait( trait_VINES3 ) ) { if( query_yn( _( "There is a sheer drop halfway down. Use your vines to descend?" ) ) ) { - <<< <<< < HEAD - if( u.has_trait( trait_VINES2 ) ) { - if( query_yn( _( "Detach a vine? It'll hurt, but you'll be able to climb back up…" ) ) ) { - == == == = - if( u.mutations.has_trait( trait_VINES2 ) ) { - if( query_yn( _( "Detach a vine? It'll hurt, but you'll be able to climb back up..." ) ) ) { - >>> >>> > fix function calls - rope_ladder = true; - add_msg( m_bad, _( "You descend on your vines, though leaving a part of you behind stings." ) ); - u.mod_pain( 5 ); - u.apply_damage( nullptr, bp_torso, 5 ); - u.mod_stored_nutr( 10 ); - u.mod_thirst( 10 ); - } else { - add_msg( _( "You gingerly descend using your vines." ) ); - } - } else { - add_msg( _( "You effortlessly lower yourself and leave a vine rooted for future use." ) ); - rope_ladder = true; - u.mod_stored_nutr( 10 ); - u.mod_thirst( 10 ); - } - } else { - return cata::nullopt; - } - } else if( u.has_amount( "grapnel", 1 ) ) { - if( query_yn( _( "There is a sheer drop halfway down. Climb your grappling hook down?" ) ) ) { + if( u.mutations.has_trait( trait_VINES2 ) ) { + if( query_yn( _( "Detach a vine? It'll hurt, but you'll be able to climb back up..." ) ) ) { rope_ladder = true; - u.use_amount( "grapnel", 1 ); + add_msg( m_bad, _( "You descend on your vines, though leaving a part of you behind stings." ) ); + u.mod_pain( 5 ); + u.apply_damage( nullptr, bp_torso, 5 ); + u.mod_stored_nutr( 10 ); + u.mod_thirst( 10 ); } else { - return cata::nullopt; + add_msg( _( "You gingerly descend using your vines." ) ); } - } else if( u.has_amount( "rope_30", 1 ) ) { - if( query_yn( _( "There is a sheer drop halfway down. Climb your rope down?" ) ) ) { - rope_ladder = true; - u.use_amount( "rope_30", 1 ); - } else { - return cata::nullopt; - } - } else if( !query_yn( _( "There is a sheer drop halfway down. Jump?" ) ) ) { - return cata::nullopt; + } else { + add_msg( _( "You effortlessly lower yourself and leave a vine rooted for future use." ) ); + rope_ladder = true; + u.mod_stored_nutr( 10 ); + u.mod_thirst( 10 ); } - - return stairs; + } else { + return cata::nullopt; } + } else if( u.has_amount( "grapnel", 1 ) ) { + if( query_yn( _( "There is a sheer drop halfway down. Climb your grappling hook down?" ) ) ) { + rope_ladder = true; + u.use_amount( "grapnel", 1 ); + } else { + return cata::nullopt; + } + } else if( u.has_amount( "rope_30", 1 ) ) { + if( query_yn( _( "There is a sheer drop halfway down. Climb your rope down?" ) ) ) { + rope_ladder = true; + u.use_amount( "rope_30", 1 ); + } else { + return cata::nullopt; + } + } else if( !query_yn( _( "There is a sheer drop halfway down. Jump?" ) ) ) { + return cata::nullopt; + } - void game::vertical_shift( const int z_after ) { - if( z_after < -OVERMAP_DEPTH || z_after > OVERMAP_HEIGHT ) { - debugmsg( "Tried to get z-level %d outside allowed range of %d-%d", - z_after, -OVERMAP_DEPTH, OVERMAP_HEIGHT ); - return; - } - - // TODO: Implement dragging stuff up/down - u.grab( OBJECT_NONE ); + return stairs; +} - scent.reset(); - - u.setz( z_after ); - const int z_before = get_levz(); - if( !m.has_zlevels() ) { - m.clear_vehicle_cache( z_before ); - m.access_cache( z_before ).vehicle_list.clear(); - m.access_cache( z_before ).zone_vehicles.clear(); - m.set_transparency_cache_dirty( z_before ); - m.set_outside_cache_dirty( z_before ); - m.load( tripoint( get_levx(), get_levy(), z_after ), true ); - shift_monsters( tripoint( 0, 0, z_after - z_before ) ); - reload_npcs(); - } else { - // Shift the map itself - m.vertical_shift( z_after ); - } +void game::vertical_shift( const int z_after ) +{ + if( z_after < -OVERMAP_DEPTH || z_after > OVERMAP_HEIGHT ) { + debugmsg( "Tried to get z-level %d outside allowed range of %d-%d", + z_after, -OVERMAP_DEPTH, OVERMAP_HEIGHT ); + return; + } - m.spawn_monsters( true ); - // this may be required after a vertical shift if z-levels are not enabled - // the critter is unloaded/loaded, and it needs to reconstruct its rider data after being reloaded. - validate_mounted_npcs(); - vertical_notes( z_before, z_after ); - } + // TODO: Implement dragging stuff up/down + u.grab( OBJECT_NONE ); - void game::vertical_notes( int z_before, int z_after ) { - if( z_before == z_after || !get_option( "AUTO_NOTES" ) || - !get_option( "AUTO_NOTES_STAIRS" ) ) { - return; - } + scent.reset(); - if( !m.inbounds_z( z_before ) || !m.inbounds_z( z_after ) ) { - debugmsg( "game::vertical_notes invalid arguments: z_before == %d, z_after == %d", - z_before, z_after ); - return; - } - // Figure out where we know there are up/down connectors - // Fill in all the tiles we know about (e.g. subway stations) - static const int REVEAL_RADIUS = 40; - for( const tripoint &p : points_in_radius( u.global_omt_location(), REVEAL_RADIUS ) ) { - const tripoint cursp_before( p.xy(), z_before ); - const tripoint cursp_after( p.xy(), z_after ); + u.setz( z_after ); + const int z_before = get_levz(); + if( !m.has_zlevels() ) { + m.clear_vehicle_cache( z_before ); + m.access_cache( z_before ).vehicle_list.clear(); + m.access_cache( z_before ).zone_vehicles.clear(); + m.set_transparency_cache_dirty( z_before ); + m.set_outside_cache_dirty( z_before ); + m.load( tripoint( get_levx(), get_levy(), z_after ), true ); + shift_monsters( tripoint( 0, 0, z_after - z_before ) ); + reload_npcs(); + } else { + // Shift the map itself + m.vertical_shift( z_after ); + } - if( !overmap_buffer.seen( cursp_before ) ) { - continue; - } - if( overmap_buffer.has_note( cursp_after ) ) { - // Already has a note -> never add an AUTO-note - continue; - } - const oter_id &ter = overmap_buffer.ter( cursp_before ); - const oter_id &ter2 = overmap_buffer.ter( cursp_after ); - if( z_after > z_before && ter->has_flag( known_up ) && - !ter2->has_flag( known_down ) ) { - overmap_buffer.set_seen( cursp_after, true ); - overmap_buffer.add_note( cursp_after, string_format( ">:W;%s", _( "AUTO: goes down" ) ) ); - } else if( z_after < z_before && ter->has_flag( known_down ) && - !ter2->has_flag( known_up ) ) { - overmap_buffer.set_seen( cursp_after, true ); - overmap_buffer.add_note( cursp_after, string_format( "<:W;%s", _( "AUTO: goes up" ) ) ); - } - } - } + m.spawn_monsters( true ); + // this may be required after a vertical shift if z-levels are not enabled + // the critter is unloaded/loaded, and it needs to reconstruct its rider data after being reloaded. + validate_mounted_npcs(); + vertical_notes( z_before, z_after ); +} - point game::update_map( player & p ) { - int x = p.posx(); - int y = p.posy(); - return update_map( x, y ); - } +void game::vertical_notes( int z_before, int z_after ) +{ + if( z_before == z_after || !get_option( "AUTO_NOTES" ) || + !get_option( "AUTO_NOTES_STAIRS" ) ) { + return; + } - point game::update_map( int &x, int &y ) { - point shift; + if( !m.inbounds_z( z_before ) || !m.inbounds_z( z_after ) ) { + debugmsg( "game::vertical_notes invalid arguments: z_before == %d, z_after == %d", + z_before, z_after ); + return; + } + // Figure out where we know there are up/down connectors + // Fill in all the tiles we know about (e.g. subway stations) + static const int REVEAL_RADIUS = 40; + for( const tripoint &p : points_in_radius( u.global_omt_location(), REVEAL_RADIUS ) ) { + const tripoint cursp_before( p.xy(), z_before ); + const tripoint cursp_after( p.xy(), z_after ); - while( x < HALF_MAPSIZE_X ) { - x += SEEX; - shift.x--; - } - while( x >= HALF_MAPSIZE_X + SEEX ) { - x -= SEEX; - shift.x++; - } - while( y < HALF_MAPSIZE_Y ) { - y += SEEY; - shift.y--; - } - while( y >= HALF_MAPSIZE_Y + SEEY ) { - y -= SEEY; - shift.y++; - } + if( !overmap_buffer.seen( cursp_before ) ) { + continue; + } + if( overmap_buffer.has_note( cursp_after ) ) { + // Already has a note -> never add an AUTO-note + continue; + } + const oter_id &ter = overmap_buffer.ter( cursp_before ); + const oter_id &ter2 = overmap_buffer.ter( cursp_after ); + if( z_after > z_before && ter->has_flag( known_up ) && + !ter2->has_flag( known_down ) ) { + overmap_buffer.set_seen( cursp_after, true ); + overmap_buffer.add_note( cursp_after, string_format( ">:W;%s", _( "AUTO: goes down" ) ) ); + } else if( z_after < z_before && ter->has_flag( known_down ) && + !ter2->has_flag( known_up ) ) { + overmap_buffer.set_seen( cursp_after, true ); + overmap_buffer.add_note( cursp_after, string_format( "<:W;%s", _( "AUTO: goes up" ) ) ); + } + } +} - if( shift == point_zero ) { - // adjust player position - u.setpos( tripoint( x, y, get_levz() ) ); - // Not actually shifting the submaps, all the stuff below would do nothing - return point_zero; - } +point game::update_map( player &p ) +{ + int x = p.posx(); + int y = p.posy(); + return update_map( x, y ); +} - // this handles loading/unloading submaps that have scrolled on or off the viewport - m.shift( shift ); +point game::update_map( int &x, int &y ) +{ + point shift; - // Shift monsters - shift_monsters( tripoint( shift, 0 ) ); - const point shift_ms = sm_to_ms_copy( shift ); - u.shift_destination( -shift_ms ); + while( x < HALF_MAPSIZE_X ) { + x += SEEX; + shift.x--; + } + while( x >= HALF_MAPSIZE_X + SEEX ) { + x -= SEEX; + shift.x++; + } + while( y < HALF_MAPSIZE_Y ) { + y += SEEY; + shift.y--; + } + while( y >= HALF_MAPSIZE_Y + SEEY ) { + y -= SEEY; + shift.y++; + } - // Shift NPCs - for( auto it = active_npc.begin(); it != active_npc.end(); ) { - ( *it )->shift( shift.x, shift.y ); - if( ( *it )->posx() < 0 - SEEX * 2 || ( *it )->posy() < 0 - SEEX * 2 || - ( *it )->posx() > SEEX * ( MAPSIZE + 2 ) || ( *it )->posy() > SEEY * ( MAPSIZE + 2 ) ) { - //Remove the npc from the active list. It remains in the overmap list. - ( *it )->on_unload(); - it = active_npc.erase( it ); - } else { - it++; - } - } + if( shift == point_zero ) { + // adjust player position + u.setpos( tripoint( x, y, get_levz() ) ); + // Not actually shifting the submaps, all the stuff below would do nothing + return point_zero; + } - scent.shift( shift_ms.x, shift_ms.y ); + // this handles loading/unloading submaps that have scrolled on or off the viewport + m.shift( shift ); - // Also ensure the player is on current z-level - // get_levz() should later be removed, when there is no longer such a thing - // as "current z-level" - u.setpos( tripoint( x, y, get_levz() ) ); + // Shift monsters + shift_monsters( tripoint( shift, 0 ) ); + const point shift_ms = sm_to_ms_copy( shift ); + u.shift_destination( -shift_ms ); - // Only do the loading after all coordinates have been shifted. + // Shift NPCs + for( auto it = active_npc.begin(); it != active_npc.end(); ) { + ( *it )->shift( shift.x, shift.y ); + if( ( *it )->posx() < 0 - SEEX * 2 || ( *it )->posy() < 0 - SEEX * 2 || + ( *it )->posx() > SEEX * ( MAPSIZE + 2 ) || ( *it )->posy() > SEEY * ( MAPSIZE + 2 ) ) { + //Remove the npc from the active list. It remains in the overmap list. + ( *it )->on_unload(); + it = active_npc.erase( it ); + } else { + it++; + } + } - // Check for overmap saved npcs that should now come into view. - // Put those in the active list. - load_npcs(); + scent.shift( shift_ms.x, shift_ms.y ); - // Make sure map cache is consistent since it may have shifted. - if( m.has_zlevels() ) { - for( int zlev = -OVERMAP_DEPTH; zlev <= OVERMAP_HEIGHT; ++zlev ) { - m.invalidate_map_cache( zlev ); - } - } else { - m.invalidate_map_cache( get_levz() ); - } - m.build_map_cache( get_levz() ); + // Also ensure the player is on current z-level + // get_levz() should later be removed, when there is no longer such a thing + // as "current z-level" + u.setpos( tripoint( x, y, get_levz() ) ); - // Spawn monsters if appropriate - // This call will generate new monsters in addition to loading, so it's placed after NPC loading - m.spawn_monsters( false ); // Static monsters + // Only do the loading after all coordinates have been shifted. - // Update what parts of the world map we can see - update_overmap_seen(); + // Check for overmap saved npcs that should now come into view. + // Put those in the active list. + load_npcs(); - return shift; + // Make sure map cache is consistent since it may have shifted. + if( m.has_zlevels() ) { + for( int zlev = -OVERMAP_DEPTH; zlev <= OVERMAP_HEIGHT; ++zlev ) { + m.invalidate_map_cache( zlev ); } + } else { + m.invalidate_map_cache( get_levz() ); + } + m.build_map_cache( get_levz() ); - void game::update_overmap_seen() { - const tripoint ompos = u.global_omt_location(); - const int dist = u.overmap_sight_range( light_level( u.posz() ) ); - const int dist_squared = dist * dist; - // We can always see where we're standing - overmap_buffer.set_seen( ompos, true ); - for( const tripoint &p : points_in_radius( ompos, dist ) ) { - const point delta = p.xy() - ompos.xy(); - const int h_squared = delta.x * delta.x + delta.y * delta.y; - if( trigdist && h_squared > dist_squared ) { - continue; - } - // If circular distances are enabled, scale overmap distances by the diagonality of the sight line. - const float multiplier = trigdist ? std::sqrt( h_squared ) / std::max( std::abs( delta.x ), - std::abs( delta.y ) ) : 1; - const std::vector line = line_to( ompos, p, 0 ); - float sight_points = dist; - for( auto it = line.begin(); - it != line.end() && sight_points >= 0; ++it ) { - const oter_id &ter = overmap_buffer.ter( *it ); - sight_points -= static_cast( ter->get_see_cost() ) * multiplier; - } - if( sight_points >= 0 ) { - tripoint seen( p ); - do { - overmap_buffer.set_seen( seen, true ); - --seen.z; - } while( seen.z >= 0 ); - } - } - } + // Spawn monsters if appropriate + // This call will generate new monsters in addition to loading, so it's placed after NPC loading + m.spawn_monsters( false ); // Static monsters - void game::replace_stair_monsters() { - for( auto &elem : coming_to_stairs ) { - elem.staircount = 0; - const tripoint pnt( elem.pos().xy(), get_levz() ); - place_critter_around( std::make_shared( elem ), pnt, 10 ); - } + // Update what parts of the world map we can see + update_overmap_seen(); + + return shift; +} - coming_to_stairs.clear(); +void game::update_overmap_seen() +{ + const tripoint ompos = u.global_omt_location(); + const int dist = u.overmap_sight_range( light_level( u.posz() ) ); + const int dist_squared = dist * dist; + // We can always see where we're standing + overmap_buffer.set_seen( ompos, true ); + for( const tripoint &p : points_in_radius( ompos, dist ) ) { + const point delta = p.xy() - ompos.xy(); + const int h_squared = delta.x * delta.x + delta.y * delta.y; + if( trigdist && h_squared > dist_squared ) { + continue; } + // If circular distances are enabled, scale overmap distances by the diagonality of the sight line. + const float multiplier = trigdist ? std::sqrt( h_squared ) / std::max( std::abs( delta.x ), + std::abs( delta.y ) ) : 1; + const std::vector line = line_to( ompos, p, 0 ); + float sight_points = dist; + for( auto it = line.begin(); + it != line.end() && sight_points >= 0; ++it ) { + const oter_id &ter = overmap_buffer.ter( *it ); + sight_points -= static_cast( ter->get_see_cost() ) * multiplier; + } + if( sight_points >= 0 ) { + tripoint seen( p ); + do { + overmap_buffer.set_seen( seen, true ); + --seen.z; + } while( seen.z >= 0 ); + } + } +} - // TODO: abstract out the location checking code - // TODO: refactor so zombies can follow up and down stairs instead of this mess - void game::update_stair_monsters() { - // Search for the stairs closest to the player. - std::vector stairx; - std::vector stairy; - std::vector stairdist; +void game::replace_stair_monsters() +{ + for( auto &elem : coming_to_stairs ) { + elem.staircount = 0; + const tripoint pnt( elem.pos().xy(), get_levz() ); + place_critter_around( std::make_shared( elem ), pnt, 10 ); + } - const bool from_below = monstairz < get_levz(); + coming_to_stairs.clear(); +} - if( coming_to_stairs.empty() ) { - return; - } +// TODO: abstract out the location checking code +// TODO: refactor so zombies can follow up and down stairs instead of this mess +void game::update_stair_monsters() +{ + // Search for the stairs closest to the player. + std::vector stairx; + std::vector stairy; + std::vector stairdist; - if( m.has_zlevels() ) { - debugmsg( "%d monsters coming to stairs on a map with z-levels", - coming_to_stairs.size() ); - coming_to_stairs.clear(); - } + const bool from_below = monstairz < get_levz(); - for( const tripoint &dest : m.points_on_zlevel( u.posz() ) ) { - if( ( from_below && m.has_flag( "GOES_DOWN", dest ) ) || - ( !from_below && m.has_flag( "GOES_UP", dest ) ) ) { - stairx.push_back( dest.x ); - stairy.push_back( dest.y ); - stairdist.push_back( rl_dist( dest, u.pos() ) ); - } - } - if( stairdist.empty() ) { - return; // Found no stairs? - } + if( coming_to_stairs.empty() ) { + return; + } - // Find closest stairs. - size_t si = 0; - for( size_t i = 0; i < stairdist.size(); i++ ) { - if( stairdist[i] < stairdist[si] ) { - si = i; - } - } + if( m.has_zlevels() ) { + debugmsg( "%d monsters coming to stairs on a map with z-levels", + coming_to_stairs.size() ); + coming_to_stairs.clear(); + } - // Find up to 4 stairs for distance stairdist[si] +1 - std::vector nearest; - nearest.push_back( si ); - for( size_t i = 0; i < stairdist.size() && nearest.size() < 4; i++ ) { - if( ( i != si ) && ( stairdist[i] <= stairdist[si] + 1 ) ) { - nearest.push_back( i ); - } - } - // Randomize the stair choice - si = random_entry_ref( nearest ); + for( const tripoint &dest : m.points_on_zlevel( u.posz() ) ) { + if( ( from_below && m.has_flag( "GOES_DOWN", dest ) ) || + ( !from_below && m.has_flag( "GOES_UP", dest ) ) ) { + stairx.push_back( dest.x ); + stairy.push_back( dest.y ); + stairdist.push_back( rl_dist( dest, u.pos() ) ); + } + } + if( stairdist.empty() ) { + return; // Found no stairs? + } - // Attempt to spawn zombies. - for( size_t i = 0; i < coming_to_stairs.size(); i++ ) { - int mposx = stairx[si]; - int mposy = stairy[si]; - monster &critter = coming_to_stairs[i]; - const tripoint dest { - mposx, mposy, g->get_levz() - }; + // Find closest stairs. + size_t si = 0; + for( size_t i = 0; i < stairdist.size(); i++ ) { + if( stairdist[i] < stairdist[si] ) { + si = i; + } + } - // We might be not be visible. - if( ( critter.posx() < 0 - ( MAPSIZE_X ) / 6 || - critter.posy() < 0 - ( MAPSIZE_Y ) / 6 || - critter.posx() > ( MAPSIZE_X * 7 ) / 6 || - critter.posy() > ( MAPSIZE_Y * 7 ) / 6 ) ) { - continue; - } + // Find up to 4 stairs for distance stairdist[si] +1 + std::vector nearest; + nearest.push_back( si ); + for( size_t i = 0; i < stairdist.size() && nearest.size() < 4; i++ ) { + if( ( i != si ) && ( stairdist[i] <= stairdist[si] + 1 ) ) { + nearest.push_back( i ); + } + } + // Randomize the stair choice + si = random_entry_ref( nearest ); - critter.staircount -= 4; - // Let the player know zombies are trying to come. - if( u.sees( dest ) ) { - std::stringstream dump; - if( critter.staircount > 4 ) { - dump << string_format( _( "You see a %s on the stairs" ), critter.name() ); - } else { - if( critter.staircount > 0 ) { - dump << ( from_below ? - //~ The is almost at the of the ! - string_format( _( "The %1$s is almost at the top of the %2$s!" ), - critter.name(), - m.tername( dest ) ) : - string_format( _( "The %1$s is almost at the bottom of the %2$s!" ), - critter.name(), - m.tername( dest ) ) ); - } - } + // Attempt to spawn zombies. + for( size_t i = 0; i < coming_to_stairs.size(); i++ ) { + int mposx = stairx[si]; + int mposy = stairy[si]; + monster &critter = coming_to_stairs[i]; + const tripoint dest { + mposx, mposy, g->get_levz() + }; - add_msg( m_warning, dump.str() ); - } else { - sounds::sound( dest, 5, sounds::sound_t::movement, - _( "a sound nearby from the stairs!" ), true, "misc", "stairs_movement" ); - } + // We might be not be visible. + if( ( critter.posx() < 0 - ( MAPSIZE_X ) / 6 || + critter.posy() < 0 - ( MAPSIZE_Y ) / 6 || + critter.posx() > ( MAPSIZE_X * 7 ) / 6 || + critter.posy() > ( MAPSIZE_Y * 7 ) / 6 ) ) { + continue; + } + critter.staircount -= 4; + // Let the player know zombies are trying to come. + if( u.sees( dest ) ) { + std::stringstream dump; + if( critter.staircount > 4 ) { + dump << string_format( _( "You see a %s on the stairs" ), critter.name() ); + } else { if( critter.staircount > 0 ) { - continue; + dump << ( from_below ? + //~ The is almost at the of the ! + string_format( _( "The %1$s is almost at the top of the %2$s!" ), + critter.name(), + m.tername( dest ) ) : + string_format( _( "The %1$s is almost at the bottom of the %2$s!" ), + critter.name(), + m.tername( dest ) ) ); } + } - if( is_empty( dest ) ) { - critter.spawn( dest ); - critter.staircount = 0; - place_critter_at( std::make_shared( critter ), dest ); - if( u.sees( dest ) ) { - if( !from_below ) { - add_msg( m_warning, _( "The %1$s comes down the %2$s!" ), - critter.name(), - m.tername( dest ) ); - } else { - add_msg( m_warning, _( "The %1$s comes up the %2$s!" ), - critter.name(), - m.tername( dest ) ); - } - } - coming_to_stairs.erase( coming_to_stairs.begin() + i ); - continue; - } else if( u.pos() == dest ) { - // Monster attempts to push player of stairs - int pushx = -1; - int pushy = -1; - int tries = 0; - - // the critter is now right on top of you and will attack unless - // it can find a square to push you into with one of his tries. - const int creature_push_attempts = 9; - const int player_throw_resist_chance = 3; - - critter.spawn( dest ); - while( tries < creature_push_attempts ) { - tries++; - pushx = rng( -1, 1 ); - pushy = rng( -1, 1 ); - int iposx = mposx + pushx; - int iposy = mposy + pushy; - tripoint pos( iposx, iposy, get_levz() ); - if( ( pushx != 0 || pushy != 0 ) && !critter_at( pos ) && - critter.can_move_to( pos ) ) { - bool resiststhrow = ( u.is_throw_immune() ) || - ( u.mutations.has_trait( trait_LEG_TENT_BRACE ) ); - if( resiststhrow && one_in( player_throw_resist_chance ) ) { - u.moves -= 25; // small charge for avoiding the push altogether - add_msg( _( "The %s fails to push you back!" ), - critter.name() ); - return; //judo or leg brace prevent you from getting pushed at all - } - // Not accounting for tentacles latching on, so.. - // Something is about to happen, lets charge half a move - u.moves -= 50; - if( resiststhrow && ( u.is_throw_immune() ) ) { - //we have a judoka who isn't getting pushed but counterattacking now. - mattack::thrown_by_judo( &critter ); - return; - } - std::string msg; - ///\EFFECT_DODGE reduces chance of being downed when pushed off the stairs - if( !( resiststhrow ) && ( u.get_dodge() + rng( 0, 3 ) < 12 ) ) { - // dodge 12 - never get downed - // 11.. avoid 75%; 10.. avoid 50%; 9.. avoid 25% - u.add_effect( effect_downed, 2_turns ); - msg = _( "The %s pushed you back hard!" ); - } else { - msg = _( "The %s pushed you back!" ); - } - add_msg( m_warning, msg.c_str(), critter.name() ); - u.setx( u.posx() + pushx ); - u.sety( u.posy() + pushy ); - return; - } + add_msg( m_warning, dump.str() ); + } else { + sounds::sound( dest, 5, sounds::sound_t::movement, + _( "a sound nearby from the stairs!" ), true, "misc", "stairs_movement" ); + } + + if( critter.staircount > 0 ) { + continue; + } + + if( is_empty( dest ) ) { + critter.spawn( dest ); + critter.staircount = 0; + place_critter_at( std::make_shared( critter ), dest ); + if( u.sees( dest ) ) { + if( !from_below ) { + add_msg( m_warning, _( "The %1$s comes down the %2$s!" ), + critter.name(), + m.tername( dest ) ); + } else { + add_msg( m_warning, _( "The %1$s comes up the %2$s!" ), + critter.name(), + m.tername( dest ) ); + } + } + coming_to_stairs.erase( coming_to_stairs.begin() + i ); + continue; + } else if( u.pos() == dest ) { + // Monster attempts to push player of stairs + int pushx = -1; + int pushy = -1; + int tries = 0; + + // the critter is now right on top of you and will attack unless + // it can find a square to push you into with one of his tries. + const int creature_push_attempts = 9; + const int player_throw_resist_chance = 3; + + critter.spawn( dest ); + while( tries < creature_push_attempts ) { + tries++; + pushx = rng( -1, 1 ); + pushy = rng( -1, 1 ); + int iposx = mposx + pushx; + int iposy = mposy + pushy; + tripoint pos( iposx, iposy, get_levz() ); + if( ( pushx != 0 || pushy != 0 ) && !critter_at( pos ) && + critter.can_move_to( pos ) ) { + bool resiststhrow = ( u.is_throw_immune() ) || + ( u.mutations.has_trait( trait_LEG_TENT_BRACE ) ); + if( resiststhrow && one_in( player_throw_resist_chance ) ) { + u.moves -= 25; // small charge for avoiding the push altogether + add_msg( _( "The %s fails to push you back!" ), + critter.name() ); + return; //judo or leg brace prevent you from getting pushed at all } - add_msg( m_warning, - _( "The %s tried to push you back but failed! It attacks you!" ), - critter.name() ); - critter.melee_attack( u ); + // Not accounting for tentacles latching on, so.. + // Something is about to happen, lets charge half a move u.moves -= 50; + if( resiststhrow && ( u.is_throw_immune() ) ) { + //we have a judoka who isn't getting pushed but counterattacking now. + mattack::thrown_by_judo( &critter ); + return; + } + std::string msg; + ///\EFFECT_DODGE reduces chance of being downed when pushed off the stairs + if( !( resiststhrow ) && ( u.get_dodge() + rng( 0, 3 ) < 12 ) ) { + // dodge 12 - never get downed + // 11.. avoid 75%; 10.. avoid 50%; 9.. avoid 25% + u.add_effect( effect_downed, 2_turns ); + msg = _( "The %s pushed you back hard!" ); + } else { + msg = _( "The %s pushed you back!" ); + } + add_msg( m_warning, msg.c_str(), critter.name() ); + u.setx( u.posx() + pushx ); + u.sety( u.posy() + pushy ); return; - } else if( monster *const mon_ptr = critter_at( dest ) ) { - // Monster attempts to displace a monster from the stairs - monster &other = *mon_ptr; - critter.spawn( dest ); - - // the critter is now right on top of another and will push it - // if it can find a square to push it into inside of his tries. - const int creature_push_attempts = 9; - const int creature_throw_resist = 4; - - int tries = 0; - int pushx = 0; - int pushy = 0; - while( tries < creature_push_attempts ) { - tries++; - pushx = rng( -1, 1 ); - pushy = rng( -1, 1 ); - int iposx = mposx + pushx; - int iposy = mposy + pushy; - tripoint pos( iposx, iposy, get_levz() ); - if( ( pushx == 0 && pushy == 0 ) || ( ( iposx == u.posx() ) && ( iposy == u.posy() ) ) ) { - continue; - } - if( !critter_at( pos ) && other.can_move_to( pos ) ) { - other.setpos( tripoint( iposx, iposy, get_levz() ) ); - other.moves -= 50; - std::string msg; - if( one_in( creature_throw_resist ) ) { - other.add_effect( effect_downed, 2_turns ); - msg = _( "The %1$s pushed the %2$s hard." ); - } else { - msg = _( "The %1$s pushed the %2$s." ); - } - add_msg( m_neutral, msg, critter.name(), other.name() ); - return; - } + } + } + add_msg( m_warning, + _( "The %s tried to push you back but failed! It attacks you!" ), + critter.name() ); + critter.melee_attack( u ); + u.moves -= 50; + return; + } else if( monster *const mon_ptr = critter_at( dest ) ) { + // Monster attempts to displace a monster from the stairs + monster &other = *mon_ptr; + critter.spawn( dest ); + + // the critter is now right on top of another and will push it + // if it can find a square to push it into inside of his tries. + const int creature_push_attempts = 9; + const int creature_throw_resist = 4; + + int tries = 0; + int pushx = 0; + int pushy = 0; + while( tries < creature_push_attempts ) { + tries++; + pushx = rng( -1, 1 ); + pushy = rng( -1, 1 ); + int iposx = mposx + pushx; + int iposy = mposy + pushy; + tripoint pos( iposx, iposy, get_levz() ); + if( ( pushx == 0 && pushy == 0 ) || ( ( iposx == u.posx() ) && ( iposy == u.posy() ) ) ) { + continue; + } + if( !critter_at( pos ) && other.can_move_to( pos ) ) { + other.setpos( tripoint( iposx, iposy, get_levz() ) ); + other.moves -= 50; + std::string msg; + if( one_in( creature_throw_resist ) ) { + other.add_effect( effect_downed, 2_turns ); + msg = _( "The %1$s pushed the %2$s hard." ); + } else { + msg = _( "The %1$s pushed the %2$s." ); } + add_msg( m_neutral, msg, critter.name(), other.name() ); return; } } + return; } + } +} - void game::despawn_monster( monster & critter ) { - if( !critter.is_hallucination() ) { - // hallucinations aren't stored, they come and go as they like, - overmap_buffer.despawn_monster( critter ); - } +void game::despawn_monster( monster &critter ) +{ + if( !critter.is_hallucination() ) { + // hallucinations aren't stored, they come and go as they like, + overmap_buffer.despawn_monster( critter ); + } - critter.on_unload(); - remove_zombie( critter ); - // simulate it being dead so further processing of it (e.g. in monmove) will yield - critter.set_hp( 0 ); - } + critter.on_unload(); + remove_zombie( critter ); + // simulate it being dead so further processing of it (e.g. in monmove) will yield + critter.set_hp( 0 ); +} - void game::shift_monsters( const tripoint & shift ) { - // If either shift argument is non-zero, we're shifting. - if( shift == tripoint_zero ) { - return; - } - for( monster &critter : all_monsters() ) { - if( shift.xy() != point_zero ) { - critter.shift( shift.xy() ); - } +void game::shift_monsters( const tripoint &shift ) +{ + // If either shift argument is non-zero, we're shifting. + if( shift == tripoint_zero ) { + return; + } + for( monster &critter : all_monsters() ) { + if( shift.xy() != point_zero ) { + critter.shift( shift.xy() ); + } - if( m.inbounds( critter.pos() ) && ( shift.z == 0 || m.has_zlevels() ) ) { - // We're inbounds, so don't despawn after all. - // No need to shift Z-coordinates, they are absolute - continue; - } - // Either a vertical shift or the critter is now outside of the reality bubble, - // anyway: it must be saved and removed. - despawn_monster( critter ); - } - // The order in which zombies are shifted may cause zombies to briefly exist on - // the same square. This messes up the mon_at cache, so we need to rebuild it. - critter_tracker->rebuild_cache(); + if( m.inbounds( critter.pos() ) && ( shift.z == 0 || m.has_zlevels() ) ) { + // We're inbounds, so don't despawn after all. + // No need to shift Z-coordinates, they are absolute + continue; } + // Either a vertical shift or the critter is now outside of the reality bubble, + // anyway: it must be saved and removed. + despawn_monster( critter ); + } + // The order in which zombies are shifted may cause zombies to briefly exist on + // the same square. This messes up the mon_at cache, so we need to rebuild it. + critter_tracker->rebuild_cache(); +} - void game::perhaps_add_random_npc() { - if( !calendar::once_every( 1_hours ) ) { - return; - } - // Create a new NPC? - // Only allow NPCs on 0 z-level, otherwise they can bug out due to lack of spots - if( !get_option( "RANDOM_NPC" ) || ( !m.has_zlevels() && get_levz() != 0 ) ) { - return; - } +void game::perhaps_add_random_npc() +{ + if( !calendar::once_every( 1_hours ) ) { + return; + } + // Create a new NPC? + // Only allow NPCs on 0 z-level, otherwise they can bug out due to lack of spots + if( !get_option( "RANDOM_NPC" ) || ( !m.has_zlevels() && get_levz() != 0 ) ) { + return; + } - float density = get_option( "NPC_DENSITY" ); - // TODO: This is inaccurate when the player is near a overmap border, and it will - //immediately spawn new npcs upon entering a new overmap. Rather use number of npcs *nearby*. - const int npc_num = get_cur_om().get_npcs().size(); - if( npc_num > 0 ) { - // 100%, 80%, 64%, 52%, 41%, 33%... - density *= powf( 0.8f, npc_num ); - } + float density = get_option( "NPC_DENSITY" ); + // TODO: This is inaccurate when the player is near a overmap border, and it will + //immediately spawn new npcs upon entering a new overmap. Rather use number of npcs *nearby*. + const int npc_num = get_cur_om().get_npcs().size(); + if( npc_num > 0 ) { + // 100%, 80%, 64%, 52%, 41%, 33%... + density *= powf( 0.8f, npc_num ); + } - if( !x_in_y( density, 100 ) ) { - return; - } + if( !x_in_y( density, 100 ) ) { + return; + } - //tmp->stock_missions(); - // Create the NPC in one of the outermost submaps, - // hopefully far away to be invisible to the player, - // to prevent NPCs appearing out of thin air. - // This can be changed to let the NPC spawn further away, - // so it does not became active immediately. - int msx = get_levx(); - int msy = get_levy(); - switch( rng( 0, 4 ) ) { // on which side of the map to spawn - case 0: - msy += rng( 0, MAPSIZE - 1 ); - break; - case 1: - msx += MAPSIZE - 1; - msy += rng( 0, MAPSIZE - 1 ); - break; - case 2: - msx += rng( 0, MAPSIZE - 1 ); - break; - case 3: - msy += MAPSIZE - 1; - msx += rng( 0, MAPSIZE - 1 ); - break; - default: - break; - } - tripoint omt_pos = sm_to_omt_copy( tripoint( msx, msy, 0 ) ); - const auto oter = overmap_buffer.ter( omt_pos ); - // shouldnt spawn on lakes or rivers. - if( is_river_or_lake( oter ) ) { - return; - } - std::shared_ptr tmp = std::make_shared(); - tmp->normalize(); - tmp->randomize(); - std::string new_fac_id = "solo_"; - new_fac_id += tmp->name; - // create a new "lone wolf" faction for this one NPC - faction *new_solo_fac = faction_manager_ptr->add_new_faction( tmp->name, faction_id( new_fac_id ), - faction_id( "no_faction" ) ); - tmp->set_fac( new_solo_fac ? new_solo_fac->id : faction_id( "no_faction" ) ); - // adds the npc to the correct overmap. - tmp->spawn_at_sm( msx, msy, 0 ); - overmap_buffer.insert_npc( tmp ); - tmp->form_opinion( u ); - tmp->mission = NPC_MISSION_NULL; - tmp->add_new_mission( mission::reserve_random( ORIGIN_ANY_NPC, tmp->global_omt_location(), - tmp->getID() ) ); - // This will make the new NPC active - load_npcs(); - } + //tmp->stock_missions(); + // Create the NPC in one of the outermost submaps, + // hopefully far away to be invisible to the player, + // to prevent NPCs appearing out of thin air. + // This can be changed to let the NPC spawn further away, + // so it does not became active immediately. + int msx = get_levx(); + int msy = get_levy(); + switch( rng( 0, 4 ) ) { // on which side of the map to spawn + case 0: + msy += rng( 0, MAPSIZE - 1 ); + break; + case 1: + msx += MAPSIZE - 1; + msy += rng( 0, MAPSIZE - 1 ); + break; + case 2: + msx += rng( 0, MAPSIZE - 1 ); + break; + case 3: + msy += MAPSIZE - 1; + msx += rng( 0, MAPSIZE - 1 ); + break; + default: + break; + } + tripoint omt_pos = sm_to_omt_copy( tripoint( msx, msy, 0 ) ); + const auto oter = overmap_buffer.ter( omt_pos ); + // shouldnt spawn on lakes or rivers. + if( is_river_or_lake( oter ) ) { + return; + } + std::shared_ptr tmp = std::make_shared(); + tmp->normalize(); + tmp->randomize(); + std::string new_fac_id = "solo_"; + new_fac_id += tmp->name; + // create a new "lone wolf" faction for this one NPC + faction *new_solo_fac = faction_manager_ptr->add_new_faction( tmp->name, faction_id( new_fac_id ), + faction_id( "no_faction" ) ); + tmp->set_fac( new_solo_fac ? new_solo_fac->id : faction_id( "no_faction" ) ); + // adds the npc to the correct overmap. + tmp->spawn_at_sm( msx, msy, 0 ); + overmap_buffer.insert_npc( tmp ); + tmp->form_opinion( u ); + tmp->mission = NPC_MISSION_NULL; + tmp->add_new_mission( mission::reserve_random( ORIGIN_ANY_NPC, tmp->global_omt_location(), + tmp->getID() ) ); + // This will make the new NPC active + load_npcs(); +} - bool game::display_overlay_state( const action_id action ) { - const auto it = displaying_overlays.find( action ); - if( it == displaying_overlays.end() ) { - return false; - } +bool game::display_overlay_state( const action_id action ) +{ + const auto it = displaying_overlays.find( action ); + if( it == displaying_overlays.end() ) { + return false; + } - return displaying_overlays[action]; - } + return displaying_overlays[action]; +} - void game::display_toggle_overlay( const action_id action ) { - const auto it = displaying_overlays.find( action ); - if( it == displaying_overlays.end() ) { - return; - } +void game::display_toggle_overlay( const action_id action ) +{ + const auto it = displaying_overlays.find( action ); + if( it == displaying_overlays.end() ) { + return; + } - const bool action_flag = it->second; - std::for_each( displaying_overlays.begin(), displaying_overlays.end(), []( auto & p ) { - p.second = false; - } ); - displaying_overlays[action] = !action_flag; + const bool action_flag = it->second; + std::for_each( displaying_overlays.begin(), displaying_overlays.end(), []( auto & p ) { + p.second = false; + } ); + displaying_overlays[action] = !action_flag; +} + +void game::display_scent() +{ + if( use_tiles ) { + display_toggle_overlay( ACTION_DISPLAY_SCENT ); + } else { + int div; + bool got_value = query_int( div, _( "Set the Scent Map sensitivity to (0 to cancel)?" ) ); + if( !got_value || div < 1 ) { + add_msg( _( "Never mind." ) ); + return; } + draw_ter(); + scent.draw( w_terrain, div * 2, u.pos() + u.view_offset ); + wrefresh( w_terrain ); + draw_panels(); + inp_mngr.wait_for_any_key(); + } +} - void game::display_scent() { - if( use_tiles ) { - display_toggle_overlay( ACTION_DISPLAY_SCENT ); - } else { - int div; - bool got_value = query_int( div, _( "Set the Scent Map sensitivity to (0 to cancel)?" ) ); - if( !got_value || div < 1 ) { - add_msg( _( "Never mind." ) ); - return; +void game::display_temperature() +{ + if( use_tiles ) { + display_toggle_overlay( ACTION_DISPLAY_TEMPERATURE ); + } +} + +void game::display_visibility() +{ + if( use_tiles ) { + display_toggle_overlay( ACTION_DISPLAY_VISIBILITY ); + if( display_overlay_state( ACTION_DISPLAY_VISIBILITY ) ) { + std::vector< tripoint > locations; + uilist creature_menu; + int num_creatures = 0; + creature_menu.addentry( num_creatures++, true, MENU_AUTOASSIGN, "%s", _( "You" ) ); + locations.emplace_back( g->u.pos() ); // add player first. + for( const Creature &critter : g->all_creatures() ) { + if( critter.is_player() ) { + continue; } - draw_ter(); - scent.draw( w_terrain, div * 2, u.pos() + u.view_offset ); - wrefresh( w_terrain ); - draw_panels(); - inp_mngr.wait_for_any_key(); + creature_menu.addentry( num_creatures++, true, MENU_AUTOASSIGN, critter.disp_name() ); + locations.emplace_back( critter.pos() ); } - } - void game::display_temperature() { - if( use_tiles ) { - display_toggle_overlay( ACTION_DISPLAY_TEMPERATURE ); + pointmenu_cb callback( locations ); + creature_menu.callback = &callback; + creature_menu.w_y = 0; + creature_menu.query(); + if( creature_menu.ret >= 0 && static_cast( creature_menu.ret ) < locations.size() ) { + Creature *creature = critter_at( locations[creature_menu.ret] ); + displaying_visibility_creature = creature; } + } else { + displaying_visibility_creature = nullptr; } + } +} - void game::display_visibility() { - if( use_tiles ) { - display_toggle_overlay( ACTION_DISPLAY_VISIBILITY ); - if( display_overlay_state( ACTION_DISPLAY_VISIBILITY ) ) { - std::vector< tripoint > locations; - uilist creature_menu; - int num_creatures = 0; - creature_menu.addentry( num_creatures++, true, MENU_AUTOASSIGN, "%s", _( "You" ) ); - locations.emplace_back( g->u.pos() ); // add player first. - for( const Creature &critter : g->all_creatures() ) { - if( critter.is_player() ) { - continue; - } - creature_menu.addentry( num_creatures++, true, MENU_AUTOASSIGN, critter.disp_name() ); - locations.emplace_back( critter.pos() ); - } - - pointmenu_cb callback( locations ); - creature_menu.callback = &callback; - creature_menu.w_y = 0; - creature_menu.query(); - if( creature_menu.ret >= 0 && static_cast( creature_menu.ret ) < locations.size() ) { - Creature *creature = critter_at( locations[creature_menu.ret] ); - displaying_visibility_creature = creature; - } - } else { - displaying_visibility_creature = nullptr; - } - } +void game::display_lighting() +{ + if( use_tiles ) { + display_toggle_overlay( ACTION_DISPLAY_LIGHTING ); + if( !g->display_overlay_state( ACTION_DISPLAY_LIGHTING ) ) { + return; } + uilist lighting_menu; + std::vector lighting_menu_strings{ + "Global lighting conditions" + }; - void game::display_lighting() { - if( use_tiles ) { - display_toggle_overlay( ACTION_DISPLAY_LIGHTING ); - if( !g->display_overlay_state( ACTION_DISPLAY_LIGHTING ) ) { - return; - } - uilist lighting_menu; - std::vector lighting_menu_strings{ - "Global lighting conditions" - }; - - int count = 0; - for( const auto &menu_str : lighting_menu_strings ) { - lighting_menu.addentry( count++, true, MENU_AUTOASSIGN, "%s", menu_str ); - } - - lighting_menu.w_y = 0; - lighting_menu.query(); - if( ( lighting_menu.ret >= 0 ) && - ( static_cast( lighting_menu.ret ) < lighting_menu_strings.size() ) ) { - g->displaying_lighting_condition = lighting_menu.ret; - } - } + int count = 0; + for( const auto &menu_str : lighting_menu_strings ) { + lighting_menu.addentry( count++, true, MENU_AUTOASSIGN, "%s", menu_str ); } - void game::display_radiation() { - if( use_tiles ) { - display_toggle_overlay( ACTION_DISPLAY_RADIATION ); - } + lighting_menu.w_y = 0; + lighting_menu.query(); + if( ( lighting_menu.ret >= 0 ) && + ( static_cast( lighting_menu.ret ) < lighting_menu_strings.size() ) ) { + g->displaying_lighting_condition = lighting_menu.ret; } + } +} - void game::init_autosave() { - moves_since_last_save = 0; - last_save_timestamp = time( nullptr ); - } +void game::display_radiation() +{ + if( use_tiles ) { + display_toggle_overlay( ACTION_DISPLAY_RADIATION ); + } +} - void game::quicksave() { - //Don't autosave if the player hasn't done anything since the last autosave/quicksave, - if( !moves_since_last_save ) { - return; - } - add_msg( m_info, _( "Saving game, this may take a while" ) ); - popup_nowait( _( "Saving game, this may take a while" ) ); +void game::init_autosave() +{ + moves_since_last_save = 0; + last_save_timestamp = time( nullptr ); +} - time_t now = time( nullptr ); //timestamp for start of saving procedure +void game::quicksave() +{ + //Don't autosave if the player hasn't done anything since the last autosave/quicksave, + if( !moves_since_last_save ) { + return; + } + add_msg( m_info, _( "Saving game, this may take a while" ) ); + popup_nowait( _( "Saving game, this may take a while" ) ); - //perform save - save(); - //Now reset counters for autosaving, so we don't immediately autosave after a quicksave or autosave. - moves_since_last_save = 0; - last_save_timestamp = now; - } + time_t now = time( nullptr ); //timestamp for start of saving procedure - void game::quickload() { - const WORLDPTR active_world = world_generator->active_world; - if( active_world == nullptr ) { - return; - } + //perform save + save(); + //Now reset counters for autosaving, so we don't immediately autosave after a quicksave or autosave. + moves_since_last_save = 0; + last_save_timestamp = now; +} - if( active_world->save_exists( save_t::from_player_name( u.name ) ) ) { - if( moves_since_last_save != 0 ) { // See if we need to reload anything - MAPBUFFER.reset(); - overmap_buffer.clear(); - try { - setup(); - } catch( const std::exception &err ) { - debugmsg( "Error: %s", err.what() ); - } - load( save_t::from_player_name( u.name ) ); - } - } else { - popup_getkey( _( "No saves for %s yet." ), u.name ); +void game::quickload() +{ + const WORLDPTR active_world = world_generator->active_world; + if( active_world == nullptr ) { + return; + } + + if( active_world->save_exists( save_t::from_player_name( u.name ) ) ) { + if( moves_since_last_save != 0 ) { // See if we need to reload anything + MAPBUFFER.reset(); + overmap_buffer.clear(); + try { + setup(); + } catch( const std::exception &err ) { + debugmsg( "Error: %s", err.what() ); } + load( save_t::from_player_name( u.name ) ); } + } else { + popup_getkey( _( "No saves for %s yet." ), u.name ); + } +} - void game::autosave() { - //Don't autosave if the min-autosave interval has not passed since the last autosave/quicksave. - if( time( nullptr ) < last_save_timestamp + 60 * get_option( "AUTOSAVE_MINUTES" ) ) { - return; - } - quicksave(); //Driving checks are handled by quicksave() - } - - void intro() { - int maxy = getmaxy( catacurses::stdscr ); - int maxx = getmaxx( catacurses::stdscr ); - const int minHeight = FULL_SCREEN_HEIGHT; - const int minWidth = FULL_SCREEN_WIDTH; - catacurses::window tmp = catacurses::newwin( minHeight, minWidth, point_zero ); - - while( maxy < minHeight || maxx < minWidth ) { - werase( tmp ); - if( maxy < minHeight && maxx < minWidth ) { - fold_and_print( tmp, point_zero, maxx, c_white, - _( "Whoa! Your terminal is tiny! This game requires a minimum terminal size of " - "%dx%d to work properly. %dx%d just won't do. Maybe a smaller font would help?" ), - minWidth, minHeight, maxx, maxy ); - } else if( maxx < minWidth ) { - fold_and_print( tmp, point_zero, maxx, c_white, - _( "Oh! Hey, look at that. Your terminal is just a little too narrow. This game " - "requires a minimum terminal size of %dx%d to function. It just won't work " - "with only %dx%d. Can you stretch it out sideways a bit?" ), - minWidth, minHeight, maxx, maxy ); - } else { - fold_and_print( tmp, point_zero, maxx, c_white, - _( "Woah, woah, we're just a little short on space here. The game requires a " - "minimum terminal size of %dx%d to run. %dx%d isn't quite enough! Can you " - "make the terminal just a smidgen taller?" ), - minWidth, minHeight, maxx, maxy ); - } - wrefresh( tmp ); - inp_mngr.wait_for_any_key(); - maxy = getmaxy( catacurses::stdscr ); - maxx = getmaxx( catacurses::stdscr ); - } - werase( tmp ); +void game::autosave() +{ + //Don't autosave if the min-autosave interval has not passed since the last autosave/quicksave. + if( time( nullptr ) < last_save_timestamp + 60 * get_option( "AUTOSAVE_MINUTES" ) ) { + return; + } + quicksave(); //Driving checks are handled by quicksave() +} + +void intro() +{ + int maxy = getmaxy( catacurses::stdscr ); + int maxx = getmaxx( catacurses::stdscr ); + const int minHeight = FULL_SCREEN_HEIGHT; + const int minWidth = FULL_SCREEN_WIDTH; + catacurses::window tmp = catacurses::newwin( minHeight, minWidth, point_zero ); + + while( maxy < minHeight || maxx < minWidth ) { + werase( tmp ); + if( maxy < minHeight && maxx < minWidth ) { + fold_and_print( tmp, point_zero, maxx, c_white, + _( "Whoa! Your terminal is tiny! This game requires a minimum terminal size of " + "%dx%d to work properly. %dx%d just won't do. Maybe a smaller font would help?" ), + minWidth, minHeight, maxx, maxy ); + } else if( maxx < minWidth ) { + fold_and_print( tmp, point_zero, maxx, c_white, + _( "Oh! Hey, look at that. Your terminal is just a little too narrow. This game " + "requires a minimum terminal size of %dx%d to function. It just won't work " + "with only %dx%d. Can you stretch it out sideways a bit?" ), + minWidth, minHeight, maxx, maxy ); + } else { + fold_and_print( tmp, point_zero, maxx, c_white, + _( "Woah, woah, we're just a little short on space here. The game requires a " + "minimum terminal size of %dx%d to run. %dx%d isn't quite enough! Can you " + "make the terminal just a smidgen taller?" ), + minWidth, minHeight, maxx, maxy ); + } + wrefresh( tmp ); + inp_mngr.wait_for_any_key(); + maxy = getmaxy( catacurses::stdscr ); + maxx = getmaxx( catacurses::stdscr ); + } + werase( tmp ); #if !(defined(_WIN32) || defined(TILES)) - // Check whether LC_CTYPE supports the UTF-8 encoding - // and show a warning if it doesn't - if( std::strcmp( nl_langinfo( CODESET ), "UTF-8" ) != 0 ) { - const char *unicode_error_msg = - _( "You don't seem to have a valid Unicode locale. You may see some weird " - "characters (e.g. empty boxes or question marks). You have been warned." ); - fold_and_print( tmp, point_zero, maxx, c_white, unicode_error_msg, minWidth, minHeight, - maxx, maxy ); - wrefresh( tmp ); - inp_mngr.wait_for_any_key(); - werase( tmp ); - } + // Check whether LC_CTYPE supports the UTF-8 encoding + // and show a warning if it doesn't + if( std::strcmp( nl_langinfo( CODESET ), "UTF-8" ) != 0 ) { + const char *unicode_error_msg = + _( "You don't seem to have a valid Unicode locale. You may see some weird " + "characters (e.g. empty boxes or question marks). You have been warned." ); + fold_and_print( tmp, point_zero, maxx, c_white, unicode_error_msg, minWidth, minHeight, + maxx, maxy ); + wrefresh( tmp ); + inp_mngr.wait_for_any_key(); + werase( tmp ); + } #endif - wrefresh( tmp ); - catacurses::erase(); - } - - void game::process_artifact( item & it, player & p ) { - const bool worn = p.is_worn( it ); - const bool wielded = ( &it == &p.weapon ); - std::vector effects = it.type->artifact->effects_carried; - if( worn ) { - const std::vector &ew = it.type->artifact->effects_worn; - effects.insert( effects.end(), ew.begin(), ew.end() ); - } - if( wielded ) { - const std::vector &ew = it.type->artifact->effects_wielded; - effects.insert( effects.end(), ew.begin(), ew.end() ); - } - - if( it.is_tool() ) { - // Recharge it if necessary - if( it.ammo_remaining() < it.ammo_capacity() && calendar::once_every( 1_minutes ) ) { - //Before incrementing charge, check that any extra requirements are met - if( check_art_charge_req( it ) ) { - switch( it.type->artifact->charge_type ) { - case ARTC_NULL: - case NUM_ARTCS: - break; // dummy entries - case ARTC_TIME: - // Once per hour - if( calendar::once_every( 1_hours ) ) { - it.charges++; - } - break; - case ARTC_SOLAR: - if( calendar::once_every( 10_minutes ) && - is_in_sunlight( p.pos() ) ) { - it.charges++; - } - break; - // Artifacts can inflict pain even on Deadened folks. - // Some weird Lovecraftian thing. ;P - // (So DON'T route them through mod_pain!) - case ARTC_PAIN: - if( calendar::once_every( 1_minutes ) ) { - add_msg( m_bad, _( "You suddenly feel sharp pain for no reason." ) ); - p.mod_pain_noresist( 3 * rng( 1, 3 ) ); - it.charges++; - } - break; - case ARTC_HP: - if( calendar::once_every( 1_minutes ) ) { - add_msg( m_bad, _( "You feel your body decaying." ) ); - p.hurtall( 1, nullptr ); - it.charges++; - } - break; - case ARTC_FATIGUE: - if( calendar::once_every( 1_minutes ) ) { - add_msg( m_bad, _( "You feel fatigue seeping into your body." ) ); - u.mod_fatigue( 3 * rng( 1, 3 ) ); - u.mod_stat( "stamina", -90 * rng( 1, 3 ) * rng( 1, 3 ) * rng( 2, 3 ) ); - it.charges++; - } - break; - // Portals are energetic enough to charge the item. - // Tears in reality are consumed too, but can't charge it. - case ARTC_PORTAL: - for( const tripoint &dest : m.points_in_radius( p.pos(), 1 ) ) { - m.remove_field( dest, fd_fatigue ); - if( m.tr_at( dest ).loadid == tr_portal ) { - add_msg( m_good, _( "The portal collapses!" ) ); - m.remove_trap( dest ); - it.charges++; - break; - } - } - break; - } - } - } - } + wrefresh( tmp ); + catacurses::erase(); +} - for( const art_effect_passive &i : effects ) { - switch( i ) { - case AEP_STR_UP: - p.mod_str_bonus( +4 ); - break; - case AEP_DEX_UP: - p.mod_dex_bonus( +4 ); - break; - case AEP_PER_UP: - p.mod_per_bonus( +4 ); - break; - case AEP_INT_UP: - p.mod_int_bonus( +4 ); +void game::process_artifact( item &it, player &p ) +{ + const bool worn = p.is_worn( it ); + const bool wielded = ( &it == &p.weapon ); + std::vector effects = it.type->artifact->effects_carried; + if( worn ) { + const std::vector &ew = it.type->artifact->effects_worn; + effects.insert( effects.end(), ew.begin(), ew.end() ); + } + if( wielded ) { + const std::vector &ew = it.type->artifact->effects_wielded; + effects.insert( effects.end(), ew.begin(), ew.end() ); + } + + if( it.is_tool() ) { + // Recharge it if necessary + if( it.ammo_remaining() < it.ammo_capacity() && calendar::once_every( 1_minutes ) ) { + //Before incrementing charge, check that any extra requirements are met + if( check_art_charge_req( it ) ) { + switch( it.type->artifact->charge_type ) { + case ARTC_NULL: + case NUM_ARTCS: + break; // dummy entries + case ARTC_TIME: + // Once per hour + if( calendar::once_every( 1_hours ) ) { + it.charges++; + } break; - case AEP_ALL_UP: - p.mod_str_bonus( +2 ); - p.mod_dex_bonus( +2 ); - p.mod_per_bonus( +2 ); - p.mod_int_bonus( +2 ); + case ARTC_SOLAR: + if( calendar::once_every( 10_minutes ) && + is_in_sunlight( p.pos() ) ) { + it.charges++; + } break; - case AEP_SPEED_UP: // Handled in player::current_speed() + // Artifacts can inflict pain even on Deadened folks. + // Some weird Lovecraftian thing. ;P + // (So DON'T route them through mod_pain!) + case ARTC_PAIN: + if( calendar::once_every( 1_minutes ) ) { + add_msg( m_bad, _( "You suddenly feel sharp pain for no reason." ) ); + p.mod_pain_noresist( 3 * rng( 1, 3 ) ); + it.charges++; + } break; - - case AEP_PBLUE: - if( p.radiation > 0 ) { - p.radiation--; + case ARTC_HP: + if( calendar::once_every( 1_minutes ) ) { + add_msg( m_bad, _( "You feel your body decaying." ) ); + p.hurtall( 1, nullptr ); + it.charges++; } break; - - case AEP_SMOKE: - if( one_in( 10 ) ) { - tripoint pt( p.posx() + rng( -1, 1 ), - p.posy() + rng( -1, 1 ), - p.posz() ); - m.add_field( pt, fd_smoke, rng( 1, 3 ) ); + case ARTC_FATIGUE: + if( calendar::once_every( 1_minutes ) ) { + add_msg( m_bad, _( "You feel fatigue seeping into your body." ) ); + u.mod_fatigue( 3 * rng( 1, 3 ) ); + u.mod_stat( "stamina", -90 * rng( 1, 3 ) * rng( 1, 3 ) * rng( 2, 3 ) ); + it.charges++; } break; - - case AEP_SNAKES: - break; // Handled in player::hit() - - case AEP_EXTINGUISH: + // Portals are energetic enough to charge the item. + // Tears in reality are consumed too, but can't charge it. + case ARTC_PORTAL: for( const tripoint &dest : m.points_in_radius( p.pos(), 1 ) ) { - m.mod_field_age( dest, fd_fire, -1_turns ); + m.remove_field( dest, fd_fatigue ); + if( m.tr_at( dest ).loadid == tr_portal ) { + add_msg( m_good, _( "The portal collapses!" ) ); + m.remove_trap( dest ); + it.charges++; + break; + } } break; + } + } + } + } - case AEP_FUN: - //Bonus fluctuates, wavering between 0 and 30-ish - usually around 12 - p.add_morale( MORALE_FEELING_GOOD, rng( 1, 2 ) * rng( 2, 3 ), 0, 3_turns, 0_turns, false ); - break; + for( const art_effect_passive &i : effects ) { + switch( i ) { + case AEP_STR_UP: + p.mod_str_bonus( +4 ); + break; + case AEP_DEX_UP: + p.mod_dex_bonus( +4 ); + break; + case AEP_PER_UP: + p.mod_per_bonus( +4 ); + break; + case AEP_INT_UP: + p.mod_int_bonus( +4 ); + break; + case AEP_ALL_UP: + p.mod_str_bonus( +2 ); + p.mod_dex_bonus( +2 ); + p.mod_per_bonus( +2 ); + p.mod_int_bonus( +2 ); + break; + case AEP_SPEED_UP: // Handled in player::current_speed() + break; - case AEP_HUNGER: - if( one_in( 100 ) ) { - p.mod_hunger( 1 ); - } - break; + case AEP_PBLUE: + if( p.radiation > 0 ) { + p.radiation--; + } + break; - case AEP_THIRST: - if( one_in( 120 ) ) { - p.mod_thirst( 1 ); - } - break; + case AEP_SMOKE: + if( one_in( 10 ) ) { + tripoint pt( p.posx() + rng( -1, 1 ), + p.posy() + rng( -1, 1 ), + p.posz() ); + m.add_field( pt, fd_smoke, rng( 1, 3 ) ); + } + break; + + case AEP_SNAKES: + break; // Handled in player::hit() + + case AEP_EXTINGUISH: + for( const tripoint &dest : m.points_in_radius( p.pos(), 1 ) ) { + m.mod_field_age( dest, fd_fire, -1_turns ); + } + break; + + case AEP_FUN: + //Bonus fluctuates, wavering between 0 and 30-ish - usually around 12 + p.add_morale( MORALE_FEELING_GOOD, rng( 1, 2 ) * rng( 2, 3 ), 0, 3_turns, 0_turns, false ); + break; + + case AEP_HUNGER: + if( one_in( 100 ) ) { + p.mod_hunger( 1 ); + } + break; + + case AEP_THIRST: + if( one_in( 120 ) ) { + p.mod_thirst( 1 ); + } + break; - case AEP_EVIL: - if( one_in( 150 ) ) { // Once every 15 minutes, on average - p.add_effect( effect_evil, 30_minutes ); - if( it.is_armor() ) { - if( !worn ) { - add_msg( _( "You have an urge to wear the %s." ), - it.tname() ); - } - } else if( !wielded ) { - add_msg( _( "You have an urge to wield the %s." ), - it.tname() ); - } + case AEP_EVIL: + if( one_in( 150 ) ) { // Once every 15 minutes, on average + p.add_effect( effect_evil, 30_minutes ); + if( it.is_armor() ) { + if( !worn ) { + add_msg( _( "You have an urge to wear the %s." ), + it.tname() ); } - break; + } else if( !wielded ) { + add_msg( _( "You have an urge to wield the %s." ), + it.tname() ); + } + } + break; - case AEP_SCHIZO: - break; // Handled in player::suffer() + case AEP_SCHIZO: + break; // Handled in player::suffer() - case AEP_RADIOACTIVE: - if( one_in( 4 ) ) { - p.irradiate( 1.0f ); - } - break; + case AEP_RADIOACTIVE: + if( one_in( 4 ) ) { + p.irradiate( 1.0f ); + } + break; - case AEP_STR_DOWN: - p.mod_str_bonus( -3 ); - break; + case AEP_STR_DOWN: + p.mod_str_bonus( -3 ); + break; - case AEP_DEX_DOWN: - p.mod_dex_bonus( -3 ); - break; + case AEP_DEX_DOWN: + p.mod_dex_bonus( -3 ); + break; - case AEP_PER_DOWN: - p.mod_per_bonus( -3 ); - break; + case AEP_PER_DOWN: + p.mod_per_bonus( -3 ); + break; - case AEP_INT_DOWN: - p.mod_int_bonus( -3 ); - break; + case AEP_INT_DOWN: + p.mod_int_bonus( -3 ); + break; - case AEP_ALL_DOWN: - p.mod_str_bonus( -2 ); - p.mod_dex_bonus( -2 ); - p.mod_per_bonus( -2 ); - p.mod_int_bonus( -2 ); - break; + case AEP_ALL_DOWN: + p.mod_str_bonus( -2 ); + p.mod_dex_bonus( -2 ); + p.mod_per_bonus( -2 ); + p.mod_int_bonus( -2 ); + break; - case AEP_SPEED_DOWN: - break; // Handled in player::current_speed() + case AEP_SPEED_DOWN: + break; // Handled in player::current_speed() - default: - //Suppress warnings - break; - } + default: + //Suppress warnings + break; + } + } + // Recalculate, as it might have changed (by mod_*_bonus above) + p.str_cur = p.get_str(); + p.int_cur = p.get_int(); + p.dex_cur = p.get_dex(); + p.per_cur = p.get_per(); +} +//Check if an artifact's extra charge requirements are currently met +bool check_art_charge_req( item &it ) +{ + player &p = g->u; + bool reqsmet = true; + const bool worn = p.is_worn( it ); + const bool wielded = ( &it == &p.weapon ); + const bool heldweapon = ( wielded && !it.is_armor() ); //don't charge wielded clothes + switch( it.type->artifact->charge_req ) { + case( ACR_NULL ): + case( NUM_ACRS ): + break; + case( ACR_EQUIP ): + //Generated artifacts won't both be wearable and have charges, but nice for mods + reqsmet = ( worn || heldweapon ); + break; + case( ACR_SKIN ): + //As ACR_EQUIP, but also requires nothing worn on bodypart wielding or wearing item + if( !worn && !heldweapon ) { + reqsmet = false; + break; } - // Recalculate, as it might have changed (by mod_*_bonus above) - p.str_cur = p.get_str(); - p.int_cur = p.get_int(); - p.dex_cur = p.get_dex(); - p.per_cur = p.get_per(); - } - //Check if an artifact's extra charge requirements are currently met - bool check_art_charge_req( item & it ) { - player &p = g->u; - bool reqsmet = true; - const bool worn = p.is_worn( it ); - const bool wielded = ( &it == &p.weapon ); - const bool heldweapon = ( wielded && !it.is_armor() ); //don't charge wielded clothes - switch( it.type->artifact->charge_req ) { - case( ACR_NULL ): - case( NUM_ACRS ): - break; - case( ACR_EQUIP ): - //Generated artifacts won't both be wearable and have charges, but nice for mods - reqsmet = ( worn || heldweapon ); - break; - case( ACR_SKIN ): - //As ACR_EQUIP, but also requires nothing worn on bodypart wielding or wearing item - if( !worn && !heldweapon ) { - reqsmet = false; - break; - } - for( const body_part bp : all_body_parts ) { - if( it.covers( bp ) || ( heldweapon && ( bp == bp_hand_r || bp == bp_hand_l ) ) ) { - reqsmet = true; - for( auto &i : p.worn ) { - if( i.covers( bp ) && ( &it != &i ) && i.get_coverage() > 50 ) { - reqsmet = false; - break; //This one's no good, check the next body part - } - } - if( reqsmet ) { - break; //Only need skin contact on one bodypart - } + for( const body_part bp : all_body_parts ) { + if( it.covers( bp ) || ( heldweapon && ( bp == bp_hand_r || bp == bp_hand_l ) ) ) { + reqsmet = true; + for( auto &i : p.worn ) { + if( i.covers( bp ) && ( &it != &i ) && i.get_coverage() > 50 ) { + reqsmet = false; + break; //This one's no good, check the next body part } } - break; - case( ACR_SLEEP ): - reqsmet = p.has_effect( effect_sleep ); - break; - case( ACR_RAD ): - reqsmet = ( ( g->m.get_radiation( p.pos() ) > 0 ) || ( p.radiation > 0 ) ); - break; - case( ACR_WET ): - reqsmet = std::any_of( p.body_wetness.begin(), p.body_wetness.end(), - []( const int w ) { - return w != 0; - } ); - if( !reqsmet && sum_conditions( calendar::turn - 1_turns, calendar::turn, p.pos() ).rain_amount > 0 - && !( p.in_vehicle && g->m.veh_at( p.pos() )->is_inside() ) ) { - reqsmet = true; + if( reqsmet ) { + break; //Only need skin contact on one bodypart } - break; - case( ACR_SKY ): - reqsmet = ( p.posz() > 0 ); - break; - } - return reqsmet; - } - - void game::start_calendar() { - const bool scen_season = scen->has_flag( "SPR_START" ) || scen->has_flag( "SUM_START" ) || - scen->has_flag( "AUT_START" ) || scen->has_flag( "WIN_START" ) || - scen->has_flag( "SUM_ADV_START" ); - - if( scen_season ) { - // Configured starting date overridden by scenario, calendar::start is left as Spring 1 - calendar::start_of_cataclysm = calendar::turn_zero + 1_hours * get_option( "INITIAL_TIME" ); - calendar::turn = calendar::turn_zero + 1_hours * get_option( "INITIAL_TIME" ); - if( scen->has_flag( "SPR_START" ) ) { - calendar::initial_season = SPRING; - } else if( scen->has_flag( "SUM_START" ) ) { - calendar::initial_season = SUMMER; - calendar::turn += calendar::season_length(); - } else if( scen->has_flag( "AUT_START" ) ) { - calendar::initial_season = AUTUMN; - calendar::turn += calendar::season_length() * 2; - } else if( scen->has_flag( "WIN_START" ) ) { - calendar::initial_season = WINTER; - calendar::turn += calendar::season_length() * 3; - } else if( scen->has_flag( "SUM_ADV_START" ) ) { - calendar::initial_season = SUMMER; - calendar::turn += calendar::season_length() * 5; - } else { - debugmsg( "The Unicorn" ); } - } else { - // No scenario, so use the starting date+time configured in world options - const int initial_days = get_option( "INITIAL_DAY" ); - calendar::start_of_cataclysm = calendar::turn_zero + 1_days * initial_days; - - // Determine the season based off how long the seasons are set to be - // First mod by length of season to get number of seasons elapsed, then mod by 4 to force a 0-3 range of values - const int season_number = ( initial_days % get_option( "SEASON_LENGTH" ) ) % 4; - if( season_number == 0 ) { - calendar::initial_season = SPRING; - } else if( season_number == 1 ) { - calendar::initial_season = SUMMER; - } else if( season_number == 2 ) { - calendar::initial_season = AUTUMN; - } else { - calendar::initial_season = WINTER; - } - - calendar::turn = calendar::start_of_cataclysm - + 1_hours * get_option( "INITIAL_TIME" ) - + 1_days * get_option( "SPAWN_DELAY" ); } - + break; + case( ACR_SLEEP ): + reqsmet = p.has_effect( effect_sleep ); + break; + case( ACR_RAD ): + reqsmet = ( ( g->m.get_radiation( p.pos() ) > 0 ) || ( p.radiation > 0 ) ); + break; + case( ACR_WET ): + reqsmet = std::any_of( p.body_wetness.begin(), p.body_wetness.end(), + []( const int w ) { + return w != 0; + } ); + if( !reqsmet && sum_conditions( calendar::turn - 1_turns, calendar::turn, p.pos() ).rain_amount > 0 + && !( p.in_vehicle && g->m.veh_at( p.pos() )->is_inside() ) ) { + reqsmet = true; + } + break; + case( ACR_SKY ): + reqsmet = ( p.posz() > 0 ); + break; + } + return reqsmet; +} + +void game::start_calendar() +{ + const bool scen_season = scen->has_flag( "SPR_START" ) || scen->has_flag( "SUM_START" ) || + scen->has_flag( "AUT_START" ) || scen->has_flag( "WIN_START" ) || + scen->has_flag( "SUM_ADV_START" ); + + if( scen_season ) { + // Configured starting date overridden by scenario, calendar::start is left as Spring 1 + calendar::start_of_cataclysm = calendar::turn_zero + 1_hours * get_option( "INITIAL_TIME" ); + calendar::turn = calendar::turn_zero + 1_hours * get_option( "INITIAL_TIME" ); + if( scen->has_flag( "SPR_START" ) ) { + calendar::initial_season = SPRING; + } else if( scen->has_flag( "SUM_START" ) ) { + calendar::initial_season = SUMMER; + calendar::turn += calendar::season_length(); + } else if( scen->has_flag( "AUT_START" ) ) { + calendar::initial_season = AUTUMN; + calendar::turn += calendar::season_length() * 2; + } else if( scen->has_flag( "WIN_START" ) ) { + calendar::initial_season = WINTER; + calendar::turn += calendar::season_length() * 3; + } else if( scen->has_flag( "SUM_ADV_START" ) ) { + calendar::initial_season = SUMMER; + calendar::turn += calendar::season_length() * 5; + } else { + debugmsg( "The Unicorn" ); + } + } else { + // No scenario, so use the starting date+time configured in world options + const int initial_days = get_option( "INITIAL_DAY" ); + calendar::start_of_cataclysm = calendar::turn_zero + 1_days * initial_days; + + // Determine the season based off how long the seasons are set to be + // First mod by length of season to get number of seasons elapsed, then mod by 4 to force a 0-3 range of values + const int season_number = ( initial_days % get_option( "SEASON_LENGTH" ) ) % 4; + if( season_number == 0 ) { + calendar::initial_season = SPRING; + } else if( season_number == 1 ) { + calendar::initial_season = SUMMER; + } else if( season_number == 2 ) { + calendar::initial_season = AUTUMN; + } else { + calendar::initial_season = WINTER; } - void game::add_artifact_messages( const std::vector &effects ) { - int net_str = 0; - int net_dex = 0; - int net_per = 0; - int net_int = 0; - int net_speed = 0; - - for( auto &i : effects ) { - switch( i ) { - case AEP_STR_UP: - net_str += 4; - break; - case AEP_DEX_UP: - net_dex += 4; - break; - case AEP_PER_UP: - net_per += 4; - break; - case AEP_INT_UP: - net_int += 4; - break; - case AEP_ALL_UP: - net_str += 2; - net_dex += 2; - net_per += 2; - net_int += 2; - break; - case AEP_STR_DOWN: - net_str -= 3; - break; - case AEP_DEX_DOWN: - net_dex -= 3; - break; - case AEP_PER_DOWN: - net_per -= 3; - break; - case AEP_INT_DOWN: - net_int -= 3; - break; - case AEP_ALL_DOWN: - net_str -= 2; - net_dex -= 2; - net_per -= 2; - net_int -= 2; - break; + calendar::turn = calendar::start_of_cataclysm + + 1_hours * get_option( "INITIAL_TIME" ) + + 1_days * get_option( "SPAWN_DELAY" ); + } - case AEP_SPEED_UP: - net_speed += 20; - break; - case AEP_SPEED_DOWN: - net_speed -= 20; - break; +} - case AEP_PBLUE: - break; // No message +void game::add_artifact_messages( const std::vector &effects ) +{ + int net_str = 0; + int net_dex = 0; + int net_per = 0; + int net_int = 0; + int net_speed = 0; - case AEP_SNAKES: - add_msg( m_warning, _( "Your skin feels slithery." ) ); - break; + for( auto &i : effects ) { + switch( i ) { + case AEP_STR_UP: + net_str += 4; + break; + case AEP_DEX_UP: + net_dex += 4; + break; + case AEP_PER_UP: + net_per += 4; + break; + case AEP_INT_UP: + net_int += 4; + break; + case AEP_ALL_UP: + net_str += 2; + net_dex += 2; + net_per += 2; + net_int += 2; + break; + case AEP_STR_DOWN: + net_str -= 3; + break; + case AEP_DEX_DOWN: + net_dex -= 3; + break; + case AEP_PER_DOWN: + net_per -= 3; + break; + case AEP_INT_DOWN: + net_int -= 3; + break; + case AEP_ALL_DOWN: + net_str -= 2; + net_dex -= 2; + net_per -= 2; + net_int -= 2; + break; - case AEP_INVISIBLE: - add_msg( m_good, _( "You fade into invisibility!" ) ); - break; + case AEP_SPEED_UP: + net_speed += 20; + break; + case AEP_SPEED_DOWN: + net_speed -= 20; + break; - case AEP_CLAIRVOYANCE: - case AEP_CLAIRVOYANCE_PLUS: - add_msg( m_good, _( "You can see through walls!" ) ); - break; + case AEP_PBLUE: + break; // No message - case AEP_SUPER_CLAIRVOYANCE: - add_msg( m_good, _( "You can see through everything!" ) ); - break; + case AEP_SNAKES: + add_msg( m_warning, _( "Your skin feels slithery." ) ); + break; - case AEP_STEALTH: - add_msg( m_good, _( "Your steps stop making noise." ) ); - break; + case AEP_INVISIBLE: + add_msg( m_good, _( "You fade into invisibility!" ) ); + break; - case AEP_GLOW: - add_msg( _( "A glow of light forms around you." ) ); - break; + case AEP_CLAIRVOYANCE: + case AEP_CLAIRVOYANCE_PLUS: + add_msg( m_good, _( "You can see through walls!" ) ); + break; - case AEP_PSYSHIELD: - add_msg( m_good, _( "Your mental state feels protected." ) ); - break; + case AEP_SUPER_CLAIRVOYANCE: + add_msg( m_good, _( "You can see through everything!" ) ); + break; - case AEP_RESIST_ELECTRICITY: - add_msg( m_good, _( "You feel insulated." ) ); - break; + case AEP_STEALTH: + add_msg( m_good, _( "Your steps stop making noise." ) ); + break; - case AEP_CARRY_MORE: - add_msg( m_good, _( "Your back feels strengthened." ) ); - break; + case AEP_GLOW: + add_msg( _( "A glow of light forms around you." ) ); + break; - case AEP_FUN: - add_msg( m_good, _( "You feel a pleasant tingle." ) ); - break; + case AEP_PSYSHIELD: + add_msg( m_good, _( "Your mental state feels protected." ) ); + break; - case AEP_HUNGER: - add_msg( m_warning, _( "You feel hungry." ) ); - break; + case AEP_RESIST_ELECTRICITY: + add_msg( m_good, _( "You feel insulated." ) ); + break; - case AEP_THIRST: - add_msg( m_warning, _( "You feel thirsty." ) ); - break; + case AEP_CARRY_MORE: + add_msg( m_good, _( "Your back feels strengthened." ) ); + break; - case AEP_EVIL: - add_msg( m_warning, _( "You feel an evil presence…" ) ); - break; + case AEP_FUN: + add_msg( m_good, _( "You feel a pleasant tingle." ) ); + break; - case AEP_SCHIZO: - add_msg( m_bad, _( "You feel a tickle of insanity." ) ); - break; + case AEP_HUNGER: + add_msg( m_warning, _( "You feel hungry." ) ); + break; - case AEP_RADIOACTIVE: - add_msg( m_warning, _( "Your skin prickles with radiation." ) ); - break; + case AEP_THIRST: + add_msg( m_warning, _( "You feel thirsty." ) ); + break; - case AEP_MUTAGENIC: - add_msg( m_bad, _( "You feel your genetic makeup degrading." ) ); - break; + case AEP_EVIL: + add_msg( m_warning, _( "You feel an evil presence…" ) ); + break; - case AEP_ATTENTION: - add_msg( m_warning, _( "You feel an otherworldly attention upon you…" ) ); - break; + case AEP_SCHIZO: + add_msg( m_bad, _( "You feel a tickle of insanity." ) ); + break; - case AEP_FORCE_TELEPORT: - add_msg( m_bad, _( "You feel a force pulling you inwards." ) ); - break; + case AEP_RADIOACTIVE: + add_msg( m_warning, _( "Your skin prickles with radiation." ) ); + break; - case AEP_MOVEMENT_NOISE: - add_msg( m_warning, _( "You hear a rattling noise coming from inside yourself." ) ); - break; + case AEP_MUTAGENIC: + add_msg( m_bad, _( "You feel your genetic makeup degrading." ) ); + break; - case AEP_BAD_WEATHER: - add_msg( m_warning, _( "You feel storms coming." ) ); - break; + case AEP_ATTENTION: + add_msg( m_warning, _( "You feel an otherworldly attention upon you…" ) ); + break; - case AEP_SICK: - add_msg( m_bad, _( "You feel unwell." ) ); - break; + case AEP_FORCE_TELEPORT: + add_msg( m_bad, _( "You feel a force pulling you inwards." ) ); + break; - case AEP_SMOKE: - add_msg( m_warning, _( "A cloud of smoke appears." ) ); - break; - default: - //Suppress warnings - break; - } - } + case AEP_MOVEMENT_NOISE: + add_msg( m_warning, _( "You hear a rattling noise coming from inside yourself." ) ); + break; - std::string stat_info; - if( net_str != 0 ) { - stat_info += string_format( _( "Str %s%d! " ), - ( net_str > 0 ? "+" : "" ), net_str ); - } - if( net_dex != 0 ) { - stat_info += string_format( _( "Dex %s%d! " ), - ( net_dex > 0 ? "+" : "" ), net_dex ); - } - if( net_int != 0 ) { - stat_info += string_format( _( "Int %s%d! " ), - ( net_int > 0 ? "+" : "" ), net_int ); - } - if( net_per != 0 ) { - stat_info += string_format( _( "Per %s%d! " ), - ( net_per > 0 ? "+" : "" ), net_per ); - } + case AEP_BAD_WEATHER: + add_msg( m_warning, _( "You feel storms coming." ) ); + break; - if( !stat_info.empty() ) { - add_msg( m_neutral, stat_info ); - } + case AEP_SICK: + add_msg( m_bad, _( "You feel unwell." ) ); + break; - if( net_speed != 0 ) { - add_msg( m_info, _( "Speed %s%d!" ), ( net_speed > 0 ? "+" : "" ), net_speed ); - } + case AEP_SMOKE: + add_msg( m_warning, _( "A cloud of smoke appears." ) ); + break; + default: + //Suppress warnings + break; } + } - void game::add_artifact_dreams( ) { - //If player is sleeping, get a dream from a carried artifact - //Don't need to check that player is sleeping here, that's done before calling - std::list art_items = g->u.get_artifact_items(); - std::vector valid_arts; - std::vector> - valid_dreams; // Tracking separately so we only need to check its req once - //Pull the list of dreams - add_msg( m_debug, "Checking %s carried artifacts", art_items.size() ); - for( auto &it : art_items ) { - //Pick only the ones with an applicable dream - auto art = it->type->artifact; - if( art.has_value() && art->charge_req != ACR_NULL && - ( it->ammo_remaining() < it->ammo_capacity() || - it->ammo_capacity() == 0 ) ) { //or max 0 in case of wacky mod shenanigans - add_msg( m_debug, "Checking artifact %s", it->tname() ); - if( check_art_charge_req( *it ) ) { - add_msg( m_debug, " Has freq %s,%s", art->dream_freq_met, art->dream_freq_unmet ); - if( art->dream_freq_met > 0 && x_in_y( art->dream_freq_met, 100 ) ) { - add_msg( m_debug, "Adding met dream from %s", it->tname() ); - valid_arts.push_back( it ); - valid_dreams.push_back( art->dream_msg_met ); - } - } else { - add_msg( m_debug, " Has freq %s,%s", art->dream_freq_met, art->dream_freq_unmet ); - if( art->dream_freq_unmet > 0 && x_in_y( art->dream_freq_unmet, 100 ) ) { - add_msg( m_debug, "Adding unmet dream from %s", it->tname() ); - valid_arts.push_back( it ); - valid_dreams.push_back( art->dream_msg_unmet ); - } - } + std::string stat_info; + if( net_str != 0 ) { + stat_info += string_format( _( "Str %s%d! " ), + ( net_str > 0 ? "+" : "" ), net_str ); + } + if( net_dex != 0 ) { + stat_info += string_format( _( "Dex %s%d! " ), + ( net_dex > 0 ? "+" : "" ), net_dex ); + } + if( net_int != 0 ) { + stat_info += string_format( _( "Int %s%d! " ), + ( net_int > 0 ? "+" : "" ), net_int ); + } + if( net_per != 0 ) { + stat_info += string_format( _( "Per %s%d! " ), + ( net_per > 0 ? "+" : "" ), net_per ); + } + + if( !stat_info.empty() ) { + add_msg( m_neutral, stat_info ); + } + + if( net_speed != 0 ) { + add_msg( m_info, _( "Speed %s%d!" ), ( net_speed > 0 ? "+" : "" ), net_speed ); + } +} + +void game::add_artifact_dreams( ) +{ + //If player is sleeping, get a dream from a carried artifact + //Don't need to check that player is sleeping here, that's done before calling + std::list art_items = g->u.get_artifact_items(); + std::vector valid_arts; + std::vector> + valid_dreams; // Tracking separately so we only need to check its req once + //Pull the list of dreams + add_msg( m_debug, "Checking %s carried artifacts", art_items.size() ); + for( auto &it : art_items ) { + //Pick only the ones with an applicable dream + auto art = it->type->artifact; + if( art.has_value() && art->charge_req != ACR_NULL && + ( it->ammo_remaining() < it->ammo_capacity() || + it->ammo_capacity() == 0 ) ) { //or max 0 in case of wacky mod shenanigans + add_msg( m_debug, "Checking artifact %s", it->tname() ); + if( check_art_charge_req( *it ) ) { + add_msg( m_debug, " Has freq %s,%s", art->dream_freq_met, art->dream_freq_unmet ); + if( art->dream_freq_met > 0 && x_in_y( art->dream_freq_met, 100 ) ) { + add_msg( m_debug, "Adding met dream from %s", it->tname() ); + valid_arts.push_back( it ); + valid_dreams.push_back( art->dream_msg_met ); } - } - if( !valid_dreams.empty() ) { - add_msg( m_debug, "Found %s valid artifact dreams", valid_dreams.size() ); - const int selected = rng( 0, valid_arts.size() - 1 ); - auto it = valid_arts[selected]; - auto msg = random_entry( valid_dreams[selected] ); - const std::string &dream = string_format( _( msg ), it->tname() ); - add_msg( dream ); } else { - add_msg( m_debug, "Didn't have any dreams, sorry" ); + add_msg( m_debug, " Has freq %s,%s", art->dream_freq_met, art->dream_freq_unmet ); + if( art->dream_freq_unmet > 0 && x_in_y( art->dream_freq_unmet, 100 ) ) { + add_msg( m_debug, "Adding unmet dream from %s", it->tname() ); + valid_arts.push_back( it ); + valid_dreams.push_back( art->dream_msg_unmet ); + } } } + } + if( !valid_dreams.empty() ) { + add_msg( m_debug, "Found %s valid artifact dreams", valid_dreams.size() ); + const int selected = rng( 0, valid_arts.size() - 1 ); + auto it = valid_arts[selected]; + auto msg = random_entry( valid_dreams[selected] ); + const std::string &dream = string_format( _( msg ), it->tname() ); + add_msg( dream ); + } else { + add_msg( m_debug, "Didn't have any dreams, sorry" ); + } +} - int game::get_levx() const { - return m.get_abs_sub().x; - } +int game::get_levx() const +{ + return m.get_abs_sub().x; +} - int game::get_levy() const { - return m.get_abs_sub().y; - } +int game::get_levy() const +{ + return m.get_abs_sub().y; +} - int game::get_levz() const { - return m.get_abs_sub().z; - } +int game::get_levz() const +{ + return m.get_abs_sub().z; +} - overmap &game::get_cur_om() const { - // The player is located in the middle submap of the map. - const tripoint sm = m.get_abs_sub() + tripoint( HALF_MAPSIZE, HALF_MAPSIZE, 0 ); - const tripoint pos_om = sm_to_om_copy( sm ); - return overmap_buffer.get( pos_om.xy() ); - } +overmap &game::get_cur_om() const +{ + // The player is located in the middle submap of the map. + const tripoint sm = m.get_abs_sub() + tripoint( HALF_MAPSIZE, HALF_MAPSIZE, 0 ); + const tripoint pos_om = sm_to_om_copy( sm ); + return overmap_buffer.get( pos_om.xy() ); +} - std::vector game::allies() { - return get_npcs_if( [&]( const npc & guy ) { - if( !guy.is_hallucination() ) { - return guy.is_ally( g->u ); - } else { - return false; - } - } ); +std::vector game::allies() +{ + return get_npcs_if( [&]( const npc & guy ) { + if( !guy.is_hallucination() ) { + return guy.is_ally( g->u ); + } else { + return false; } + } ); +} - std::vector game::get_creatures_if( const std::function - &pred ) { - std::vector result; - for( Creature &critter : all_creatures() ) { - if( pred( critter ) ) { - result.push_back( &critter ); - } - } - return result; +std::vector game::get_creatures_if( const std::function + &pred ) +{ + std::vector result; + for( Creature &critter : all_creatures() ) { + if( pred( critter ) ) { + result.push_back( &critter ); } + } + return result; +} - std::vector game::get_npcs_if( const std::function &pred ) { - std::vector result; - for( npc &guy : all_npcs() ) { - if( pred( guy ) ) { - result.push_back( &guy ); - } - } - return result; +std::vector game::get_npcs_if( const std::function &pred ) +{ + std::vector result; + for( npc &guy : all_npcs() ) { + if( pred( guy ) ) { + result.push_back( &guy ); } + } + return result; +} - template<> - bool game::non_dead_range::iterator::valid() { - current = iter->lock(); - return current && !current->is_dead(); - } +template<> +bool game::non_dead_range::iterator::valid() +{ + current = iter->lock(); + return current && !current->is_dead(); +} - template<> - bool game::non_dead_range::iterator::valid() { - current = iter->lock(); - return current && !current->is_dead(); - } +template<> +bool game::non_dead_range::iterator::valid() +{ + current = iter->lock(); + return current && !current->is_dead(); +} - template<> - bool game::non_dead_range::iterator::valid() { - current = iter->lock(); - // There is no Creature::is_dead function, so we can't write - // return current && !current->is_dead(); - if( !current ) { - return false; - } - const Creature *const critter = current.get(); - if( critter->is_monster() ) { - return !static_cast( critter )->is_dead(); - } - if( critter->is_npc() ) { - return !static_cast( critter )->is_dead(); - } - return true; // must be g->u - } +template<> +bool game::non_dead_range::iterator::valid() +{ + current = iter->lock(); + // There is no Creature::is_dead function, so we can't write + // return current && !current->is_dead(); + if( !current ) { + return false; + } + const Creature *const critter = current.get(); + if( critter->is_monster() ) { + return !static_cast( critter )->is_dead(); + } + if( critter->is_npc() ) { + return !static_cast( critter )->is_dead(); + } + return true; // must be g->u +} - game::monster_range::monster_range( game & g ) { - const auto &monsters = g.critter_tracker->get_monsters_list(); - items.insert( items.end(), monsters.begin(), monsters.end() ); - } +game::monster_range::monster_range( game &g ) +{ + const auto &monsters = g.critter_tracker->get_monsters_list(); + items.insert( items.end(), monsters.begin(), monsters.end() ); +} - game::Creature_range::Creature_range( game & g ) : u( &g.u, []( player * ) { } ) { - const auto &monsters = g.critter_tracker->get_monsters_list(); - items.insert( items.end(), monsters.begin(), monsters.end() ); - items.insert( items.end(), g.active_npc.begin(), g.active_npc.end() ); - items.push_back( u ); - } +game::Creature_range::Creature_range( game &g ) : u( &g.u, []( player * ) { } ) +{ + const auto &monsters = g.critter_tracker->get_monsters_list(); + items.insert( items.end(), monsters.begin(), monsters.end() ); + items.insert( items.end(), g.active_npc.begin(), g.active_npc.end() ); + items.push_back( u ); +} - game::npc_range::npc_range( game & g ) { - items.insert( items.end(), g.active_npc.begin(), g.active_npc.end() ); - } +game::npc_range::npc_range( game &g ) +{ + items.insert( items.end(), g.active_npc.begin(), g.active_npc.end() ); +} - game::Creature_range game::all_creatures() { - return Creature_range( *this ); - } +game::Creature_range game::all_creatures() +{ + return Creature_range( *this ); +} - game::monster_range game::all_monsters() { - return monster_range( *this ); - } +game::monster_range game::all_monsters() +{ + return monster_range( *this ); +} - game::npc_range game::all_npcs() { - return npc_range( *this ); - } +game::npc_range game::all_npcs() +{ + return npc_range( *this ); +} - Creature *game::get_creature_if( const std::function &pred ) { - for( Creature &critter : all_creatures() ) { - if( pred( critter ) ) { - return &critter; - } - } - return nullptr; +Creature *game::get_creature_if( const std::function &pred ) +{ + for( Creature &critter : all_creatures() ) { + if( pred( critter ) ) { + return &critter; } + } + return nullptr; +} - std::string game::get_player_base_save_path() const { - return get_world_base_save_path() + "/" + base64_encode( u.name ); - } +std::string game::get_player_base_save_path() const +{ + return get_world_base_save_path() + "/" + base64_encode( u.name ); +} - std::string game::get_world_base_save_path() const { - if( world_generator->active_world == nullptr ) { - return FILENAMES["savedir"]; - } - return world_generator->active_world->folder_path(); - } +std::string game::get_world_base_save_path() const +{ + if( world_generator->active_world == nullptr ) { + return FILENAMES["savedir"]; + } + return world_generator->active_world->folder_path(); +} diff --git a/src/iuse_actor.cpp b/src/iuse_actor.cpp index 0d933c7f4f326..75ccd0f6c741b 100644 --- a/src/iuse_actor.cpp +++ b/src/iuse_actor.cpp @@ -1781,7 +1781,8 @@ int cauterize_actor::use( player &p, item &it, bool t, const tripoint & ) const if( has_disease ) { did_cauterize = cauterize_effect( p, it, false ); } else { - const bool can_have_fun = p.mutations.has_trait( trait_MASOCHIST ) || p.mutations.has_trait( trait_MASOCHIST_MED ) || + const bool can_have_fun = p.mutations.has_trait( trait_MASOCHIST ) || + p.mutations.has_trait( trait_MASOCHIST_MED ) || p.mutations.has_trait( trait_CENOBITE ); if( can_have_fun && query_yn( _( "Cauterize yourself for fun?" ) ) ) { @@ -3092,7 +3093,8 @@ bool repair_item_actor::can_repair_target( player &pl, const item &fix, } const bool resizing_matters = fix.get_encumber( pl ) != 0; - const bool small = pl.mutations.has_trait( trait_SMALL2 ) || pl.mutations.has_trait( trait_SMALL_OK ); + const bool small = pl.mutations.has_trait( trait_SMALL2 ) || + pl.mutations.has_trait( trait_SMALL_OK ); const bool can_resize = small != fix.has_flag( "UNDERSIZE" ); if( can_be_refitted && resizing_matters && can_resize ) { return true; @@ -3182,7 +3184,7 @@ repair_item_actor::repair_type repair_item_actor::default_action( const item &fi } const bool small = g->u.mutations.has_trait( trait_id( "SMALL2" ) ) || - g->u.mutations.has_trait( trait_id( "SMALL_OK" ) ); + g->u.mutations.has_trait( trait_id( "SMALL_OK" ) ); const bool is_undersized = fix.has_flag( "UNDERSIZE" ); const bool is_oversized = fix.has_flag( "OVERSIZE" ); diff --git a/src/melee.cpp b/src/melee.cpp index e7741b6b62e8b..705f390b900a5 100644 --- a/src/melee.cpp +++ b/src/melee.cpp @@ -899,7 +899,8 @@ void player::roll_cut_damage( bool crit, damage_instance &di, bool average, cons weap.is_null(); if( left_empty || right_empty ) { float per_hand = 0.0f; - if( mutations.has_trait( trait_CLAWS ) || ( mutations.has_active_mutation( trait_CLAWS_RETRACT ) ) ) { + if( mutations.has_trait( trait_CLAWS ) || + ( mutations.has_active_mutation( trait_CLAWS_RETRACT ) ) ) { per_hand += 3; } if( has_bionic( bionic_id( "bio_razors" ) ) ) { @@ -1692,7 +1693,8 @@ void player::perform_special_attacks( Creature &t ) dealt_dam.type_damage( DT_STAB ) > 0; } - if( can_poison && ( mutations.has_trait( trait_POISONOUS ) || mutations.has_trait( trait_POISONOUS2 ) ) ) { + if( can_poison && ( mutations.has_trait( trait_POISONOUS ) || + mutations.has_trait( trait_POISONOUS2 ) ) ) { if( mutations.has_trait( trait_POISONOUS ) ) { add_msg_if_player( m_good, _( "You poison %s!" ), target ); t.add_effect( effect_poison, 6_turns ); diff --git a/src/mission_companion.cpp b/src/mission_companion.cpp index 6f051bd0218e5..e97d26cf73417 100644 --- a/src/mission_companion.cpp +++ b/src/mission_companion.cpp @@ -247,7 +247,8 @@ void talk_function::commune_farmfield( mission_data &mission_key, npc &p ) "willing to liquidate it." ); mission_key.add( "Purchase East Field", _( "Purchase East Field" ), entry ); } - if( p.mutations.has_trait( trait_NPC_CONSTRUCTION_LEV_1 ) && !p.mutations.has_trait( trait_NPC_CONSTRUCTION_LEV_2 ) ) { + if( p.mutations.has_trait( trait_NPC_CONSTRUCTION_LEV_1 ) && + !p.mutations.has_trait( trait_NPC_CONSTRUCTION_LEV_2 ) ) { std::string entry = _( "Cost: $5500\n\n" "\n ........." // NOLINT(cata-text-style) "\n ........." // NOLINT(cata-text-style) From 555e54ee0c8e04f7d70b4e85a6c9b5121006a9a8 Mon Sep 17 00:00:00 2001 From: KorGgenT Date: Fri, 18 Oct 2019 19:15:28 -0400 Subject: [PATCH 28/34] add Character parameter to mutation_attacks() --- src/character_mutations.h | 2 +- src/melee.cpp | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/character_mutations.h b/src/character_mutations.h index a9b891be10d56..f336c083c45ad 100644 --- a/src/character_mutations.h +++ b/src/character_mutations.h @@ -160,7 +160,7 @@ class character_mutations void power_mutations(); /** Returns a vector of valid mutation attacks */ - std::vector mutation_attacks( Creature &t ) const; + std::vector mutation_attacks( Character &owner, Creature &t ) const; /** Retrieves a stat mod of a mutation. */ int get_mod( const trait_id &mut, const std::string &arg ) const; /** Empties the trait list */ diff --git a/src/melee.cpp b/src/melee.cpp index 705f390b900a5..66c102ccc55bb 100644 --- a/src/melee.cpp +++ b/src/melee.cpp @@ -1661,7 +1661,7 @@ void player::perform_special_attacks( Creature &t ) { bool can_poison = false; - std::vector special_attacks = mutations.mutation_attacks( t ); + std::vector special_attacks = mutations.mutation_attacks( *this, t ); std::string target = t.disp_name(); @@ -1845,14 +1845,14 @@ static damage_instance hardcoded_mutation_attack( const player &u, const trait_i return damage_instance(); } -std::vector character_mutations::mutation_attacks( Creature &t ) const +std::vector character_mutations::mutation_attacks( Character &owner, Creature &t ) const { std::vector ret; std::string target = t.disp_name(); - const auto usable_body_parts = exclusive_flag_coverage( "ALLOWS_NATURAL_ATTACKS" ); - const int unarmed = get_skill_level( skill_unarmed ); + const auto usable_body_parts = owner.exclusive_flag_coverage( "ALLOWS_NATURAL_ATTACKS" ); + const int unarmed = owner.get_skill_level( skill_unarmed ); for( const auto &pr : my_mutations ) { const auto &branch = pr.first.obj(); @@ -1866,7 +1866,7 @@ std::vector character_mutations::mutation_attacks( Creature &t ) /** @EFFECT_DEX increases chance of attacking with mutated body parts */ // Calculate actor ability value to be compared against mutation attack difficulty and add debug message - const int proc_value = get_dex() + unarmed; + const int proc_value = owner.get_dex() + unarmed; add_msg( m_debug, "%s proc chance: %d in %d", pr.first.c_str(), proc_value, mut_atk.chance ); // If the mutation attack fails to proc, bail out if( !x_in_y( proc_value, mut_atk.chance ) ) { @@ -1895,19 +1895,19 @@ std::vector character_mutations::mutation_attacks( Creature &t ) // Ugly special case: player's strings have only 1 variable, NPC have 2 // Can't use here // TODO: Fix - if( is_player() ) { + if( owner.is_player() ) { tmp.text = string_format( _( mut_atk.attack_text_u ), target ); } else { - tmp.text = string_format( _( mut_atk.attack_text_npc ), name, target ); + tmp.text = string_format( _( mut_atk.attack_text_npc ), owner.name, target ); } // Attack starts here if( mut_atk.hardcoded_effect ) { - tmp.damage = hardcoded_mutation_attack( *this, pr.first ); + tmp.damage = hardcoded_mutation_attack( *owner.as_player(), pr.first ); } else { damage_instance dam = mut_atk.base_damage; damage_instance scaled = mut_atk.strength_damage; - scaled.mult_damage( std::min( 15.0f, get_str() ), true ); + scaled.mult_damage( std::min( 15.0f, owner.get_str() ), true ); dam.add( scaled ); tmp.damage = dam; From 7c9b653de89c5b50328b96aca056dec8b55c966f Mon Sep 17 00:00:00 2001 From: KorGgenT Date: Fri, 18 Oct 2019 19:19:31 -0400 Subject: [PATCH 29/34] astyle - mutation_attacks --- src/melee.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/melee.cpp b/src/melee.cpp index 66c102ccc55bb..3be175c159a50 100644 --- a/src/melee.cpp +++ b/src/melee.cpp @@ -1845,7 +1845,8 @@ static damage_instance hardcoded_mutation_attack( const player &u, const trait_i return damage_instance(); } -std::vector character_mutations::mutation_attacks( Character &owner, Creature &t ) const +std::vector character_mutations::mutation_attacks( Character &owner, + Creature &t ) const { std::vector ret; From 7d30db8a63226caa705ed1601757980a6a47a9cc Mon Sep 17 00:00:00 2001 From: KorGgenT Date: Fri, 18 Oct 2019 19:19:39 -0400 Subject: [PATCH 30/34] fix function calls --- src/handle_action.cpp | 48 +++++++++++++++++++++---------------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/src/handle_action.cpp b/src/handle_action.cpp index c85e026dceb64..ac8eb89577eb2 100644 --- a/src/handle_action.cpp +++ b/src/handle_action.cpp @@ -928,9 +928,9 @@ static void sleep() active.push_back( info.name.translated() ); } } - for( auto &mut : u.get_mutations() ) { - const auto &mdata = mut.obj(); - if( mdata.cost > 0 && u.has_active_mutation( mut ) ) { + for( const trait_id &mut : u.mutations.get_mutations() ) { + const mutation_branch &mdata = mut.obj(); + if( mdata.cost > 0 && u.mutations.has_active_mutation( mut ) ) { active.push_back( mdata.name() ); } } @@ -969,7 +969,7 @@ static void sleep() time_duration try_sleep_dur = 24_hours; if( u.has_alarm_clock() ) { /* Reuse menu to ask player whether they want to set an alarm. */ - bool can_hibernate = u.get_hunger() < -60 && u.has_active_mutation( trait_HIBERNATE ); + bool can_hibernate = u.get_hunger() < -60 && u.mutations.has_active_mutation( trait_HIBERNATE ); as_m.reset(); as_m.text = can_hibernate @@ -1767,7 +1767,7 @@ bool game::handle_action() break; case ACTION_OPEN: - if( u.has_active_mutation( trait_SHELL2 ) ) { + if( u.mutations.has_active_mutation( trait_SHELL2 ) ) { add_msg( m_info, _( "You can't open things while you're in your shell." ) ); } else if( u.is_mounted() ) { add_msg( m_info, _( "You can't open things while you're riding." ) ); @@ -1777,7 +1777,7 @@ bool game::handle_action() break; case ACTION_CLOSE: - if( u.has_active_mutation( trait_SHELL2 ) ) { + if( u.mutations.has_active_mutation( trait_SHELL2 ) ) { add_msg( m_info, _( "You can't close things while you're in your shell." ) ); } else if( u.is_mounted() ) { auto mon = u.mounted_creature.get(); @@ -1794,7 +1794,7 @@ bool game::handle_action() case ACTION_SMASH: if( veh_ctrl ) { handbrake(); - } else if( u.has_active_mutation( trait_SHELL2 ) ) { + } else if( u.mutations.has_active_mutation( trait_SHELL2 ) ) { add_msg( m_info, _( "You can't smash things while you're in your shell." ) ); } else { smash(); @@ -1802,7 +1802,7 @@ bool game::handle_action() break; case ACTION_EXAMINE: - if( u.has_active_mutation( trait_SHELL2 ) ) { + if( u.mutations.has_active_mutation( trait_SHELL2 ) ) { add_msg( m_info, _( "You can't examine your surroundings while you're in your shell." ) ); } else if( mouse_target ) { examine( *mouse_target ); @@ -1812,7 +1812,7 @@ bool game::handle_action() break; case ACTION_ADVANCEDINV: - if( u.has_active_mutation( trait_SHELL2 ) ) { + if( u.mutations.has_active_mutation( trait_SHELL2 ) ) { add_msg( m_info, _( "You can't move mass quantities while you're in your shell." ) ); } else if( u.is_mounted() ) { add_msg( m_info, _( "You can't move mass quantities while you're riding." ) ); @@ -1822,7 +1822,7 @@ bool game::handle_action() break; case ACTION_PICKUP: - if( u.has_active_mutation( trait_SHELL2 ) ) { + if( u.mutations.has_active_mutation( trait_SHELL2 ) ) { add_msg( m_info, _( "You can't pick anything up while you're in your shell." ) ); } else if( u.is_mounted() ) { add_msg( m_info, _( "You can't pick anything up while you're riding." ) ); @@ -1834,7 +1834,7 @@ bool game::handle_action() break; case ACTION_PICKUP_FEET: - if( u.has_active_mutation( trait_SHELL2 ) ) { + if( u.mutations.has_active_mutation( trait_SHELL2 ) ) { add_msg( m_info, _( "You can't pick anything up while you're in your shell." ) ); } else { pickup_feet(); @@ -1842,7 +1842,7 @@ bool game::handle_action() break; case ACTION_GRAB: - if( u.has_active_mutation( trait_SHELL2 ) ) { + if( u.mutations.has_active_mutation( trait_SHELL2 ) ) { add_msg( m_info, _( "You can't grab things while you're in your shell." ) ); } else if( u.is_mounted() ) { add_msg( m_info, _( "You can't grab things while you're riding." ) ); @@ -1852,7 +1852,7 @@ bool game::handle_action() break; case ACTION_HAUL: - if( u.has_active_mutation( trait_SHELL2 ) ) { + if( u.mutations.has_active_mutation( trait_SHELL2 ) ) { add_msg( m_info, _( "You can't haul things while you're in your shell." ) ); } else if( u.is_mounted() ) { add_msg( m_info, _( "You can't haul things while you're riding." ) ); @@ -1862,7 +1862,7 @@ bool game::handle_action() break; case ACTION_BUTCHER: - if( u.has_active_mutation( trait_SHELL2 ) ) { + if( u.mutations.has_active_mutation( trait_SHELL2 ) ) { add_msg( m_info, _( "You can't butcher while you're in your shell." ) ); } else if( u.is_mounted() ) { add_msg( m_info, _( "You can't butcher while you're riding." ) ); @@ -1876,7 +1876,7 @@ bool game::handle_action() break; case ACTION_PEEK: - if( u.has_active_mutation( trait_SHELL2 ) ) { + if( u.mutations.has_active_mutation( trait_SHELL2 ) ) { add_msg( m_info, _( "You can't peek around corners while you're in your shell." ) ); } else if( u.is_mounted() ) { add_msg( m_info, _( "You can't peek around corners while you're riding." ) ); @@ -2008,7 +2008,7 @@ bool game::handle_action() break; case ACTION_DIR_DROP: - if( u.has_active_mutation( trait_SHELL2 ) ) { + if( u.mutations.has_active_mutation( trait_SHELL2 ) ) { add_msg( m_info, _( "You can't drop things to another tile while you're in your shell." ) ); } else { drop_in_direction(); @@ -2019,7 +2019,7 @@ bool game::handle_action() refresh_all(); break; case ACTION_MUTATIONS: - u.power_mutations(); + u.mutations.power_mutations(); refresh_all(); break; @@ -2033,7 +2033,7 @@ bool game::handle_action() break; case ACTION_CRAFT: - if( u.has_active_mutation( trait_SHELL2 ) ) { + if( u.mutations.has_active_mutation( trait_SHELL2 ) ) { add_msg( m_info, _( "You can't craft while you're in your shell." ) ); } else if( u.is_mounted() ) { add_msg( m_info, _( "You can't craft while you're riding." ) ); @@ -2043,7 +2043,7 @@ bool game::handle_action() break; case ACTION_RECRAFT: - if( u.has_active_mutation( trait_SHELL2 ) ) { + if( u.mutations.has_active_mutation( trait_SHELL2 ) ) { add_msg( m_info, _( "You can't craft while you're in your shell." ) ); } else if( u.is_mounted() ) { add_msg( m_info, _( "You can't craft while you're riding." ) ); @@ -2053,7 +2053,7 @@ bool game::handle_action() break; case ACTION_LONGCRAFT: - if( u.has_active_mutation( trait_SHELL2 ) ) { + if( u.mutations.has_active_mutation( trait_SHELL2 ) ) { add_msg( m_info, _( "You can't craft while you're in your shell." ) ); } else if( u.is_mounted() ) { add_msg( m_info, _( "You can't craft while you're riding." ) ); @@ -2076,7 +2076,7 @@ bool game::handle_action() case ACTION_CONSTRUCT: if( u.in_vehicle ) { add_msg( m_info, _( "You can't construct while in a vehicle." ) ); - } else if( u.has_active_mutation( trait_SHELL2 ) ) { + } else if( u.mutations.has_active_mutation( trait_SHELL2 ) ) { add_msg( m_info, _( "You can't construct while you're in your shell." ) ); } else if( u.is_mounted() ) { add_msg( m_info, _( "You can't construct while you're riding." ) ); @@ -2096,9 +2096,9 @@ bool game::handle_action() break; case ACTION_CONTROL_VEHICLE: - if( u.has_active_mutation( trait_SHELL2 ) ) { + if( u.mutations.has_active_mutation( trait_SHELL2 ) ) { add_msg( m_info, _( "You can't operate a vehicle while you're in your shell." ) ); - } else if( u.has_trait( trait_id( "WAYFARER" ) ) ) { + } else if( u.mutations.has_trait( trait_id( "WAYFARER" ) ) ) { add_msg( m_info, _( "You refuse to take control of this vehicle." ) ); } else if( u.is_mounted() ) { u.dismount(); @@ -2146,7 +2146,7 @@ bool game::handle_action() } set_safe_mode( SAFE_MODE_ON ); } else if( u.has_effect( effect_laserlocked ) ) { - if( u.has_trait( trait_id( "PROF_CHURL" ) ) ) { + if( u.mutations.has_trait( trait_id( "PROF_CHURL" ) ) ) { add_msg( m_warning, _( "You make the sign of the cross." ) ); } else { add_msg( m_info, _( "Ignoring laser targeting!" ) ); From 61e534b8e8f0b0ae2e3e46b703c9bedae1ba0e02 Mon Sep 17 00:00:00 2001 From: KorGgenT Date: Fri, 18 Oct 2019 19:49:33 -0400 Subject: [PATCH 31/34] fix function calls --- src/addiction.cpp | 2 +- src/advanced_inv.cpp | 2 +- src/avatar.cpp | 46 +++++++++++----------- src/avatar_action.cpp | 8 ++-- src/character.cpp | 2 +- src/construction.cpp | 16 ++++---- src/craft_command.cpp | 2 +- src/crafting.cpp | 14 +++---- src/descriptions.cpp | 5 ++- src/effect.cpp | 4 +- src/explosion.cpp | 6 +-- src/fungal_effects.cpp | 4 +- src/game_inventory.cpp | 10 ++--- src/iexamine.cpp | 65 +++++++++++++++++--------------- src/magic_spell_effect.cpp | 6 +-- src/map.cpp | 3 +- src/map_field.cpp | 19 +++++----- src/mattack_actors.cpp | 2 +- src/memorial_logger.cpp | 10 ++--- src/mondeath.cpp | 13 ++++--- src/overmap_ui.cpp | 4 +- src/panels.cpp | 10 ++--- src/player.cpp | 2 +- src/player.h | 4 +- src/player_hardcoded_effects.cpp | 53 +++++++++++++------------- src/ranged.cpp | 4 +- src/requirements.cpp | 8 ++-- src/sounds.cpp | 10 ++--- src/stomach.cpp | 8 ++-- src/trap.cpp | 4 +- src/trapfunc.cpp | 40 +++++++++++--------- src/visitable.cpp | 10 +++-- 32 files changed, 208 insertions(+), 188 deletions(-) diff --git a/src/addiction.cpp b/src/addiction.cpp index 9541362c6408c..374f6abe315ea 100644 --- a/src/addiction.cpp +++ b/src/addiction.cpp @@ -202,7 +202,7 @@ void addict_effect( player &u, addiction &add ) } case ADD_MUTAGEN: - if( u.has_trait( trait_id( "MUT_JUNKIE" ) ) ) { + if( u.mutations.has_trait( trait_id( "MUT_JUNKIE" ) ) ) { if( one_in( 600 - 50 * in ) ) { u.add_msg_if_player( m_warning, rng( 0, 6 ) < in ? _( "You so miss the exquisite rainbow of post-humanity." ) : diff --git a/src/advanced_inv.cpp b/src/advanced_inv.cpp index 232fa1c4b89c1..e5f845afee710 100644 --- a/src/advanced_inv.cpp +++ b/src/advanced_inv.cpp @@ -2205,7 +2205,7 @@ bool advanced_inventory::query_charges( aim_location destarea, const advanced_in // Inventory has a weight capacity, map and vehicle don't have that if( destarea == AIM_INVENTORY || destarea == AIM_WORN ) { const units::mass unitweight = it.weight() / ( by_charges ? it.charges : 1 ); - const units::mass max_weight = g->u.has_trait( trait_id( "DEBUG_STORAGE" ) ) ? + const units::mass max_weight = g->u.mutations.has_trait( trait_id( "DEBUG_STORAGE" ) ) ? units::mass_max : g->u.weight_capacity() * 4 - g->u.weight_carried(); if( unitweight > 0_gram && unitweight * amount > max_weight ) { const int weightmax = max_weight / unitweight; diff --git a/src/avatar.cpp b/src/avatar.cpp index 5625d480717f8..6ac043c5416af 100644 --- a/src/avatar.cpp +++ b/src/avatar.cpp @@ -159,7 +159,7 @@ size_t avatar::max_memorized_tiles() const if( current_map_memory_turn != calendar::turn ) { current_map_memory_turn = calendar::turn; float map_memory_capacity_multiplier = - mutation_value( "map_memory_capacity_multiplier" ); + mutations.mutation_value( "map_memory_capacity_multiplier" ); if( has_active_bionic( bio_memory ) ) { map_memory_capacity_multiplier = 50; } @@ -272,9 +272,9 @@ const player *avatar::get_book_reader( const item &book, std::vectorintel > 0 && has_trait( trait_ILLITERATE ) ) { + if( type->intel > 0 && mutations.has_trait( trait_ILLITERATE ) ) { reasons.emplace_back( _( "You're illiterate!" ) ); - } else if( has_trait( trait_HYPEROPIC ) && !worn_with_flag( "FIX_FARSIGHT" ) && + } else if( mutations.has_trait( trait_HYPEROPIC ) && !worn_with_flag( "FIX_FARSIGHT" ) && !has_effect( effect_contacts ) && !has_bionic( bio_eye_optic ) ) { reasons.emplace_back( _( "Your eyes won't focus without reading glasses." ) ); } else if( fine_detail_vision_mod() > 4 ) { @@ -297,14 +297,15 @@ const player *avatar::get_book_reader( const item &book, std::vectorintel > 0 && elem->has_trait( trait_ILLITERATE ) ) { + if( type->intel > 0 && elem->mutations.has_trait( trait_ILLITERATE ) ) { reasons.push_back( string_format( _( "%s is illiterate!" ), elem->disp_name() ) ); } else if( skill && elem->get_skill_level( skill ) < type->req && has_identified( book.typeId() ) ) { reasons.push_back( string_format( _( "%s %d needed to understand. %s has %d" ), skill.obj().name(), type->req, elem->disp_name(), elem->get_skill_level( skill ) ) ); - } else if( elem->has_trait( trait_HYPEROPIC ) && !elem->worn_with_flag( "FIX_FARSIGHT" ) && + } else if( elem->mutations.has_trait( trait_HYPEROPIC ) && + !elem->worn_with_flag( "FIX_FARSIGHT" ) && !elem->has_effect( effect_contacts ) ) { reasons.push_back( string_format( _( "%s needs reading glasses!" ), elem->disp_name() ) ); @@ -347,7 +348,7 @@ int avatar::time_to_read( const item &book, const player &reader, const player * retval *= std::min( fine_detail_vision_mod(), reader.fine_detail_vision_mod() ); const int effective_int = std::min( { get_int(), reader.get_int(), learner ? learner->get_int() : INT_MAX } ); - if( type->intel > effective_int && !reader.has_trait( trait_PROF_DICEMASTER ) ) { + if( type->intel > effective_int && !reader.mutations.has_trait( trait_PROF_DICEMASTER ) ) { retval += type->time * ( type->intel - effective_int ) * 100; } if( !has_identified( book.typeId() ) ) { @@ -602,7 +603,7 @@ bool avatar::read( int inventory_position, const bool continuous ) const int intelligence = get_int(); const bool complex_penalty = type->intel > std::min( intelligence, reader->get_int() ) && - !reader->has_trait( trait_PROF_DICEMASTER ); + !reader->mutations.has_trait( trait_PROF_DICEMASTER ); const player *complex_player = reader->get_int() < intelligence ? reader : this; if( complex_penalty && !continuous ) { add_msg( m_warning, @@ -806,7 +807,7 @@ void avatar::do_read( item &book ) } if( ( skill_level == reading->level || !skill_level.can_train() ) || - ( ( learner->has_trait( trait_id( "SCHIZOPHRENIC" ) ) || + ( ( learner->mutations.has_trait( trait_id( "SCHIZOPHRENIC" ) ) || learner->has_artifact_with( AEP_SCHIZO ) ) && one_in( 25 ) ) ) { if( learner->is_player() ) { add_msg( m_info, _( "You can no longer learn from %s." ), book.type_name() ); @@ -962,7 +963,7 @@ void avatar::disp_morale() } int pain_penalty = 0; - if( get_perceived_pain() && !has_trait( trait_CENOBITE ) ) { + if( get_perceived_pain() && !mutations.has_trait( trait_CENOBITE ) ) { pain_penalty = calc_focus_equilibrium( true ) - equilibrium - fatigue_penalty; } @@ -988,7 +989,7 @@ int avatar::calc_focus_equilibrium( bool ignore_pain ) const int eff_morale = get_morale_level(); // Factor in perceived pain, since it's harder to rest your mind while your body hurts. // Cenobites don't mind, though - if( !ignore_pain && !has_trait( trait_CENOBITE ) ) { + if( !ignore_pain && !mutations.has_trait( trait_CENOBITE ) ) { eff_morale = eff_morale - get_perceived_pain(); } @@ -1086,19 +1087,20 @@ void avatar::update_mental_focus() void avatar::reset_stats() { // Trait / mutation buffs - if( has_trait( trait_THICK_SCALES ) ) { + if( mutations.has_trait( trait_THICK_SCALES ) ) { add_miss_reason( _( "Your thick scales get in the way." ), 2 ); } - if( has_trait( trait_CHITIN2 ) || has_trait( trait_CHITIN3 ) || has_trait( trait_CHITIN_FUR3 ) ) { + if( mutations.has_trait( trait_CHITIN2 ) || mutations.has_trait( trait_CHITIN3 ) || + mutations.has_trait( trait_CHITIN_FUR3 ) ) { add_miss_reason( _( "Your chitin gets in the way." ), 1 ); } - if( has_trait( trait_COMPOUND_EYES ) && !wearing_something_on( bp_eyes ) ) { + if( mutations.has_trait( trait_COMPOUND_EYES ) && !wearing_something_on( bp_eyes ) ) { mod_per_bonus( 1 ); } - if( has_trait( trait_INSECT_ARMS ) ) { + if( mutations.has_trait( trait_INSECT_ARMS ) ) { add_miss_reason( _( "Your insect limbs get in the way." ), 2 ); } - if( has_trait( trait_INSECT_ARMS_OK ) ) { + if( mutations.has_trait( trait_INSECT_ARMS_OK ) ) { if( !wearing_something_on( bp_torso ) ) { mod_dex_bonus( 1 ); } else { @@ -1106,13 +1108,13 @@ void avatar::reset_stats() add_miss_reason( _( "Your clothing restricts your insect arms." ), 1 ); } } - if( has_trait( trait_WEBBED ) ) { + if( mutations.has_trait( trait_WEBBED ) ) { add_miss_reason( _( "Your webbed hands get in the way." ), 1 ); } - if( has_trait( trait_ARACHNID_ARMS ) ) { + if( mutations.has_trait( trait_ARACHNID_ARMS ) ) { add_miss_reason( _( "Your arachnid limbs get in the way." ), 4 ); } - if( has_trait( trait_ARACHNID_ARMS_OK ) ) { + if( mutations.has_trait( trait_ARACHNID_ARMS_OK ) ) { if( !wearing_something_on( bp_torso ) ) { mod_dex_bonus( 2 ); } else if( !exclusive_flag_coverage( "OVERSIZE" ).test( bp_torso ) ) { @@ -1159,7 +1161,7 @@ void avatar::reset_stats() // Stimulants set_fake_effect_dur( effect_stim, 1_turns * stim ); set_fake_effect_dur( effect_depressants, 1_turns * -stim ); - if( has_trait( trait_STIMBOOST ) ) { + if( mutations.has_trait( trait_STIMBOOST ) ) { set_fake_effect_dur( effect_stim_overdose, 1_turns * ( stim - 60 ) ); } else { set_fake_effect_dur( effect_stim_overdose, 1_turns * ( stim - 30 ) ); @@ -1194,10 +1196,10 @@ void avatar::reset_stats() mod_dodge_bonus( mabuff_dodge_bonus() - ( encumb( bp_leg_l ) + encumb( bp_leg_r ) ) / 20.0f - encumb( bp_torso ) / 10.0f ); // Whiskers don't work so well if they're covered - if( has_trait( trait_WHISKERS ) && !wearing_something_on( bp_mouth ) ) { + if( mutations.has_trait( trait_WHISKERS ) && !wearing_something_on( bp_mouth ) ) { mod_dodge_bonus( 1 ); } - if( has_trait( trait_WHISKERS_RAT ) && !wearing_something_on( bp_mouth ) ) { + if( mutations.has_trait( trait_WHISKERS_RAT ) && !wearing_something_on( bp_mouth ) ) { mod_dodge_bonus( 2 ); } // depending on mounts size, attacks will hit the mount and use their dodge rating. @@ -1206,7 +1208,7 @@ void avatar::reset_stats() mod_dodge_bonus( -4 ); } // Spider hair is basically a full-body set of whiskers, once you get the brain for it - if( has_trait( trait_CHITIN_FUR3 ) ) { + if( mutations.has_trait( trait_CHITIN_FUR3 ) ) { static const std::array parts{ { bp_head, bp_arm_r, bp_arm_l, bp_leg_r, bp_leg_l } }; for( auto bp : parts ) { if( !wearing_something_on( bp ) ) { diff --git a/src/avatar_action.cpp b/src/avatar_action.cpp index a7d6c9a5126d9..69842cf552ef0 100644 --- a/src/avatar_action.cpp +++ b/src/avatar_action.cpp @@ -63,8 +63,8 @@ static const efftype_id effect_harnessed( "harnessed" ); bool avatar_action::move( avatar &you, map &m, int dx, int dy, int dz ) { - if( ( !g->check_safe_mode_allowed() ) || you.has_active_mutation( trait_SHELL2 ) ) { - if( you.has_active_mutation( trait_SHELL2 ) ) { + if( ( !g->check_safe_mode_allowed() ) || you.mutations.has_active_mutation( trait_SHELL2 ) ) { + if( you.mutations.has_active_mutation( trait_SHELL2 ) ) { add_msg( m_warning, _( "You can't move while in your shell. Deactivate it to go mobile." ) ); } return false; @@ -104,7 +104,7 @@ bool avatar_action::move( avatar &you, map &m, int dx, int dy, int dz ) return true; } } - if( you.has_trait( trait_BURROW ) ) { + if( you.mutations.has_trait( trait_BURROW ) ) { item burrowing_item( itype_id( "fake_burrowing" ) ); you.invoke_item( &burrowing_item, "BURROW", dest_loc ); you.defer_move( dest_loc ); // don't move into the tile until done mining @@ -831,7 +831,7 @@ bool avatar_action::fire( avatar &you, map &m, item &weapon, int bp_cost ) void avatar_action::plthrow( avatar &you, int pos, const cata::optional &blind_throw_from_pos ) { - if( you.has_active_mutation( trait_SHELL2 ) ) { + if( you.mutations.has_active_mutation( trait_SHELL2 ) ) { add_msg( m_info, _( "You can't effectively throw while you're in your shell." ) ); return; } diff --git a/src/character.cpp b/src/character.cpp index 7ce30c99535b0..69e68bcc5068a 100644 --- a/src/character.cpp +++ b/src/character.cpp @@ -5227,7 +5227,7 @@ void Character::fall_asleep() add_msg_if_player( _( "You use your %s to keep warm." ), item_name ); } } - if( has_active_mutation( trait_id( "HIBERNATE" ) ) && + if( mutations.has_active_mutation( trait_id( "HIBERNATE" ) ) && get_kcal_percent() > 0.8f ) { if( is_avatar() ) { g->memorial().add( pgettext( "memorial_male", "Entered hibernation." ), diff --git a/src/construction.cpp b/src/construction.cpp index 97eec9d3e4fb4..cb94ddc3d1a00 100644 --- a/src/construction.cpp +++ b/src/construction.cpp @@ -176,7 +176,7 @@ static void draw_grid( const catacurses::window &w, const int list_width ) static nc_color construction_color( const std::string &con_name, bool highlight ) { nc_color col = c_dark_gray; - if( g->u.has_trait( trait_id( "DEBUG_HS" ) ) ) { + if( g->u.mutations.has_trait( trait_id( "DEBUG_HS" ) ) ) { col = c_white; } else if( can_construct( con_name ) ) { construction *con_first = nullptr; @@ -663,7 +663,7 @@ int construction_menu( bool blueprint ) } if( !blueprint ) { if( player_can_build( g->u, total_inv, constructs[select] ) ) { - if( g->u.fine_detail_vision_mod() > 4 && !g->u.has_trait( trait_DEBUG_HS ) ) { + if( g->u.fine_detail_vision_mod() > 4 && !g->u.mutations.has_trait( trait_DEBUG_HS ) ) { add_msg( m_info, _( "It is too dark to construct right now." ) ); } else { place_construction( constructs[select] ); @@ -709,7 +709,7 @@ bool player_can_build( player &p, const inventory &inv, const std::string &desc bool player_can_build( player &p, const inventory &inv, const construction &con ) { - if( p.has_trait( trait_DEBUG_HS ) ) { + if( p.mutations.has_trait( trait_DEBUG_HS ) ) { return true; } @@ -1013,7 +1013,7 @@ void construct::done_grave( const tripoint &p ) if( it.is_corpse() ) { if( it.get_corpse_name().empty() ) { if( it.get_mtype()->has_flag( MF_HUMAN ) ) { - if( g->u.has_trait( trait_SPIRITUAL ) ) { + if( g->u.mutations.has_trait( trait_SPIRITUAL ) ) { g->u.add_morale( MORALE_FUNERAL, 50, 75, 1_days, 1_hours ); add_msg( m_good, _( "You feel relieved after providing last rites for this human being, whose name is lost in the Cataclysm." ) ); @@ -1022,7 +1022,7 @@ void construct::done_grave( const tripoint &p ) } } } else { - if( g->u.has_trait( trait_SPIRITUAL ) ) { + if( g->u.mutations.has_trait( trait_SPIRITUAL ) ) { g->u.add_morale( MORALE_FUNERAL, 50, 75, 1_days, 1_hours ); add_msg( m_good, _( "You feel sadness, but also relief after providing last rites for %s, whose name you will keep in your memory." ), @@ -1162,7 +1162,8 @@ void construct::done_digormine_stair( const tripoint &p, bool dig ) tmpmap.load( tripoint( pos_sm.xy(), pos_sm.z - 1 ), false ); const tripoint local_tmp = tmpmap.getlocal( abs_pos ); - bool dig_muts = g->u.has_trait( trait_PAINRESIST_TROGLO ) || g->u.has_trait( trait_STOCKY_TROGLO ); + bool dig_muts = g->u.mutations.has_trait( trait_PAINRESIST_TROGLO ) || + g->u.mutations.has_trait( trait_STOCKY_TROGLO ); int no_mut_penalty = dig_muts ? 10 : 0; int mine_penalty = dig ? 0 : 10; @@ -1235,7 +1236,8 @@ void construct::done_mine_upstair( const tripoint &p ) return; } - bool dig_muts = g->u.has_trait( trait_PAINRESIST_TROGLO ) || g->u.has_trait( trait_STOCKY_TROGLO ); + bool dig_muts = g->u.mutations.has_trait( trait_PAINRESIST_TROGLO ) || + g->u.mutations.has_trait( trait_STOCKY_TROGLO ); int no_mut_penalty = dig_muts ? 15 : 0; g->u.mod_stored_nutr( 20 + no_mut_penalty ); diff --git a/src/craft_command.cpp b/src/craft_command.cpp index 53fd1264319b9..20347197fca19 100644 --- a/src/craft_command.cpp +++ b/src/craft_command.cpp @@ -209,7 +209,7 @@ item craft_command::create_in_progress_craft() // Use up the components and tools std::list used; std::vector comps_used; - if( crafter->has_trait( trait_id( "DEBUG_HS" ) ) ) { + if( crafter->mutations.has_trait( trait_id( "DEBUG_HS" ) ) ) { return item( rec, batch_size, used, comps_used ); } diff --git a/src/crafting.cpp b/src/crafting.cpp index 7d4725523323b..88bbae9bb57f4 100644 --- a/src/crafting.cpp +++ b/src/crafting.cpp @@ -551,7 +551,7 @@ const inventory &player::crafting_inventory( const tripoint &src_pos, int radius calendar::turn, units::to_kilojoule( get_power_level() ) ); } } - if( has_trait( trait_BURROW ) ) { + if( mutations.has_trait( trait_BURROW ) ) { cached_crafting_inventory += item( "pickaxe", calendar::turn ); cached_crafting_inventory += item( "shovel", calendar::turn ); } @@ -921,7 +921,7 @@ double player::crafting_success_roll( const recipe &making ) const // farsightedness can impose a penalty on electronics and tailoring success // it's equivalent to a 2-rank electronics penalty, 1-rank tailoring - if( has_trait( trait_id( "HYPEROPIC" ) ) && !worn_with_flag( "FIX_FARSIGHT" ) && + if( mutations.has_trait( trait_id( "HYPEROPIC" ) ) && !worn_with_flag( "FIX_FARSIGHT" ) && !has_effect( effect_contacts ) ) { int main_rank_penalty = 0; if( making.skill_used == skill_id( "electronics" ) ) { @@ -934,9 +934,9 @@ double player::crafting_success_roll( const recipe &making ) const // It's tough to craft with paws. Fortunately it's just a matter of grip and fine-motor, // not inability to see what you're doing - if( has_trait( trait_PAWS ) || has_trait( trait_PAWS_LARGE ) ) { + if( mutations.has_trait( trait_PAWS ) || mutations.has_trait( trait_PAWS_LARGE ) ) { int paws_rank_penalty = 0; - if( has_trait( trait_PAWS_LARGE ) ) { + if( mutations.has_trait( trait_PAWS_LARGE ) ) { paws_rank_penalty += 1; } if( making.skill_used == skill_id( "electronics" ) @@ -1444,7 +1444,7 @@ comp_selection player::select_item_component( const std::vector player::consume_items( map &m, const comp_selection & { std::list ret; - if( has_trait( trait_DEBUG_HS ) ) { + if( mutations.has_trait( trait_DEBUG_HS ) ) { return ret; } @@ -1770,7 +1770,7 @@ void player::consume_tools( const comp_selection &tool, int batch ) void player::consume_tools( map &m, const comp_selection &tool, int batch, const tripoint &origin, int radius, basecamp *bcp ) { - if( has_trait( trait_DEBUG_HS ) ) { + if( mutations.has_trait( trait_DEBUG_HS ) ) { return; } diff --git a/src/descriptions.cpp b/src/descriptions.cpp index 770cbe9b58130..d71e056f47c3c 100644 --- a/src/descriptions.cpp +++ b/src/descriptions.cpp @@ -99,8 +99,9 @@ void game::extended_description( const tripoint &p ) std::string signage = m.get_signage( p ); if( !signage.empty() ) { // NOLINTNEXTLINE(cata-text-style): the question mark does not end a sentence - desc += u.has_trait( trait_ILLITERATE ) ? _( "\nSign: ???" ) : string_format( _( "\nSign: %s" ), - signage ); + desc += u.mutations.has_trait( trait_ILLITERATE ) ? _( "\nSign: ???" ) : string_format( + _( "\nSign: %s" ), + signage ); } werase( w_main ); diff --git a/src/effect.cpp b/src/effect.cpp index 82847a6899415..08c540d6c8720 100644 --- a/src/effect.cpp +++ b/src/effect.cpp @@ -155,9 +155,9 @@ void weed_msg( player &p ) case 1: // Real Life p.add_msg_if_player( _( "Man, a cheeseburger sounds SO awesome right now." ) ); p.mod_hunger( 4 ); - if( p.has_trait( trait_id( "VEGETARIAN" ) ) ) { + if( p.mutations.has_trait( trait_id( "VEGETARIAN" ) ) ) { p.add_msg_if_player( _( "Eh… maybe not." ) ); - } else if( p.has_trait( trait_id( "LACTOSE" ) ) ) { + } else if( p.mutations.has_trait( trait_id( "LACTOSE" ) ) ) { p.add_msg_if_player( _( "I guess, maybe, without the cheese… yeah." ) ); } return; diff --git a/src/explosion.cpp b/src/explosion.cpp index 2615e07576217..11b0693349195 100644 --- a/src/explosion.cpp +++ b/src/explosion.cpp @@ -533,11 +533,11 @@ void flashbang( const tripoint &p, bool player_immune ) } if( g->m.sees( g->u.pos(), p, 8 ) ) { int flash_mod = 0; - if( g->u.has_trait( trait_id( "PER_SLIME" ) ) ) { + if( g->u.mutations.has_trait( trait_id( "PER_SLIME" ) ) ) { if( one_in( 2 ) ) { flash_mod = 3; // Yay, you weren't looking! } - } else if( g->u.has_trait( trait_id( "PER_SLIME_OK" ) ) ) { + } else if( g->u.mutations.has_trait( trait_id( "PER_SLIME_OK" ) ) ) { flash_mod = 8; // Just retract those and extrude fresh eyes } else if( g->u.has_bionic( bionic_id( "bio_sunglasses" ) ) || g->u.is_wearing( "rm13_armor_on" ) ) { @@ -590,7 +590,7 @@ void shockwave( const tripoint &p, int radius, int force, int stun, int dam_mult } } if( rl_dist( g->u.pos(), p ) <= radius && !ignore_player && - ( !g->u.has_trait( trait_id( "LEG_TENT_BRACE" ) ) || g->u.footwear_factor() == 1 || + ( !g->u.mutations.has_trait( trait_id( "LEG_TENT_BRACE" ) ) || g->u.footwear_factor() == 1 || ( g->u.footwear_factor() == .5 && one_in( 2 ) ) ) ) { add_msg( m_bad, _( "You're caught in the shockwave!" ) ); g->knockback( p, g->u.pos(), force, stun, dam_mult ); diff --git a/src/fungal_effects.cpp b/src/fungal_effects.cpp index 70ca0671dd8b8..2d7720b2c449e 100644 --- a/src/fungal_effects.cpp +++ b/src/fungal_effects.cpp @@ -60,7 +60,7 @@ void fungal_effects::fungalize( const tripoint &p, Creature *origin, double spor ///\EFFECT_DEX increases chance of knocking fungal spores away with your TAIL_CATTLE ///\EFFECT_MELEE increases chance of knocking fungal sports away with your TAIL_CATTLE - if( pl.has_trait( trait_id( "TAIL_CATTLE" ) ) && + if( pl.mutations.has_trait( trait_id( "TAIL_CATTLE" ) ) && one_in( 20 - pl.dex_cur - pl.get_skill_level( skill_id( "melee" ) ) ) ) { pl.add_msg_if_player( _( "The spores land on you, but you quickly swat them off with your tail!" ) ); @@ -83,7 +83,7 @@ void fungal_effects::fungalize( const tripoint &p, Creature *origin, double spor if( origin_mon != nullptr ) { spore->make_ally( *origin_mon ); } else if( origin != nullptr && origin->is_player() && - gm.u.has_trait( trait_id( "THRESH_MYCUS" ) ) ) { + gm.u.mutations.has_trait( trait_id( "THRESH_MYCUS" ) ) ) { spore->friendly = 1000; } } diff --git a/src/game_inventory.cpp b/src/game_inventory.cpp index 86b52793f789e..4bf0398c7a469 100644 --- a/src/game_inventory.cpp +++ b/src/game_inventory.cpp @@ -600,7 +600,7 @@ class comestible_inventory_preset : public inventory_selector_preset if( time > 0_turns && !( loc->type->container && loc->type->container->preserves ) ) { return 0; } else if( get_consumable_item( loc ).rotten() ) { - if( p.has_trait( trait_SAPROPHAGE ) || p.has_trait( trait_SAPROVORE ) ) { + if( p.mutations.has_trait( trait_SAPROPHAGE ) || p.mutations.has_trait( trait_SAPROVORE ) ) { return 1; } else { return 4; @@ -1513,7 +1513,7 @@ static item_location autodoc_internal( player &u, player &patient, int drug_count = 0; if( !surgeon ) {//surgeon use their own anesthetic, player just need money - if( patient.has_trait( trait_NOPAIN ) ) { + if( patient.mutations.has_trait( trait_NOPAIN ) ) { hint = _( "Patient has Deadened nerves. Anesthesia unneeded." ); } else if( patient.has_bionic( bionic_id( "bio_painkiller" ) ) ) { hint = _( "Patient has Sensory Dulling CBM installed. Anesthesia unneeded." ); @@ -1674,7 +1674,7 @@ class bionic_install_preset: public inventory_selector_preset -1 ); if( ( get_option < bool > ( "SAFE_AUTODOC" ) ) || - g->u.has_trait( trait_id( "DEBUG_BIONICS" ) ) ) { + g->u.mutations.has_trait( trait_id( "DEBUG_BIONICS" ) ) ) { chance_of_failure = 0; } else { float skill_difficulty_parameter = static_cast( ( adjusted_skill + assist_bonus ) / @@ -1785,7 +1785,7 @@ class bionic_install_surgeon_preset : public inventory_selector_preset 20 );//override player's skills with surgeon skill if( ( get_option < bool >( "SAFE_AUTODOC" ) ) || - g->u.has_trait( trait_id( "DEBUG_BIONICS" ) ) ) { + g->u.mutations.has_trait( trait_id( "DEBUG_BIONICS" ) ) ) { chance_of_failure = 0; } else { float skill_difficulty_parameter = static_cast( ( adjusted_skill + assist_bonus ) / @@ -1876,7 +1876,7 @@ class bionic_uninstall_preset : public inventory_selector_preset -1 ); if( ( get_option < bool >( "SAFE_AUTODOC" ) ) || - g->u.has_trait( trait_id( "DEBUG_BIONICS" ) ) ) { + g->u.mutations.has_trait( trait_id( "DEBUG_BIONICS" ) ) ) { chance_of_failure = 0; } else { float skill_difficulty_parameter = static_cast( ( adjusted_skill + assist_bonus ) / diff --git a/src/iexamine.cpp b/src/iexamine.cpp index 4cf51780fb23b..61601f9fdec28 100644 --- a/src/iexamine.cpp +++ b/src/iexamine.cpp @@ -871,7 +871,7 @@ void iexamine::intercom( player &p, const tripoint &examp ) void iexamine::rubble( player &p, const tripoint &examp ) { int moves; - if( p.has_quality( quality_id( "DIG" ), 3 ) || p.has_trait( trait_BURROW ) ) { + if( p.has_quality( quality_id( "DIG" ), 3 ) || p.mutations.has_trait( trait_BURROW ) ) { moves = to_moves( 1_minutes ); } else if( p.has_quality( quality_id( "DIG" ), 2 ) ) { moves = to_moves( 2_minutes ); @@ -901,26 +901,27 @@ void iexamine::chainfence( player &p, const tripoint &examp ) return; } - if( g->m.has_flag( "CLIMB_SIMPLE", examp ) && p.has_trait( trait_PARKOUR ) ) { + if( g->m.has_flag( "CLIMB_SIMPLE", examp ) && p.mutations.has_trait( trait_PARKOUR ) ) { add_msg( _( "You vault over the obstacle with ease." ) ); p.moves -= 100; // Not tall enough to warrant spider-climbing, so only relevant trait. } else if( g->m.has_flag( "CLIMB_SIMPLE", examp ) ) { add_msg( _( "You vault over the obstacle." ) ); p.moves -= 300; // Most common move cost for barricades pre-change. - } else if( p.has_trait( trait_ARACHNID_ARMS_OK ) && !p.wearing_something_on( bp_torso ) ) { + } else if( p.mutations.has_trait( trait_ARACHNID_ARMS_OK ) && + !p.wearing_something_on( bp_torso ) ) { add_msg( _( "Climbing this obstacle is trivial for one such as you." ) ); p.moves -= 75; // Yes, faster than walking. 6-8 limbs are impressive. - } else if( p.has_trait( trait_INSECT_ARMS_OK ) && !p.wearing_something_on( bp_torso ) ) { + } else if( p.mutations.has_trait( trait_INSECT_ARMS_OK ) && !p.wearing_something_on( bp_torso ) ) { add_msg( _( "You quickly scale the fence." ) ); p.moves -= 90; - } else if( p.has_trait( trait_PARKOUR ) ) { + } else if( p.mutations.has_trait( trait_PARKOUR ) ) { add_msg( _( "This obstacle is no match for your freerunning abilities." ) ); p.moves -= 100; } else { p.moves -= 400; ///\EFFECT_DEX decreases chances of slipping while climbing int climb = p.dex_cur; - if( p.has_trait( trait_BADKNEES ) ) { + if( p.mutations.has_trait( trait_BADKNEES ) ) { climb = climb / 2; } if( one_in( climb ) ) { @@ -950,7 +951,7 @@ void iexamine::chainfence( player &p, const tripoint &examp ) */ void iexamine::bars( player &p, const tripoint &examp ) { - if( !( p.has_trait( trait_AMORPHOUS ) ) ) { + if( !( p.mutations.has_trait( trait_AMORPHOUS ) ) ) { none( p, examp ); return; } @@ -1461,8 +1462,8 @@ static bool dead_plant( bool flower, player &p, const tripoint &examp ) */ static bool can_drink_nectar( const player &p ) { - return ( p.has_active_mutation( trait_id( "PROBOSCIS" ) ) || - p.has_active_mutation( trait_id( "BEAK_HUM" ) ) ) && + return ( p.mutations.has_active_mutation( trait_id( "PROBOSCIS" ) ) || + p.mutations.has_active_mutation( trait_id( "BEAK_HUM" ) ) ) && ( ( p.get_hunger() ) > 0 ) && ( !( p.wearing_something_on( bp_mouth ) ) ); } @@ -1598,7 +1599,7 @@ void iexamine::flower_dahlia( player &p, const tripoint &examp ) return; } - bool can_get_root = p.has_quality( quality_id( "DIG" ) ) || p.has_trait( trait_BURROW ); + bool can_get_root = p.has_quality( quality_id( "DIG" ) ) || p.mutations.has_trait( trait_BURROW ); if( can_get_root ) { if( !query_yn( _( "Pick %s?" ), g->m.furnname( examp ) ) ) { @@ -2013,16 +2014,18 @@ void iexamine::harvest_plant( player &p, const tripoint &examp, bool from_activi } else if( seedType == "marloss_seed" ) { fungus( p, examp ); g->m.i_clear( examp ); - if( p.has_trait( trait_M_DEPENDENT ) && ( p.get_kcal_percent() < 0.8f || p.get_thirst() > 300 ) ) { + if( p.mutations.has_trait( trait_M_DEPENDENT ) && ( p.get_kcal_percent() < 0.8f || + p.get_thirst() > 300 ) ) { g->m.ter_set( examp, t_marloss ); add_msg( m_info, _( "We have altered this unit's configuration to extract and provide local nutriment. The Mycus provides." ) ); - } else if( ( p.has_trait( trait_M_DEFENDER ) ) || ( ( p.has_trait( trait_M_SPORES ) || - p.has_trait( trait_M_FERTILE ) ) && - one_in( 2 ) ) ) { + } else if( ( p.mutations.has_trait( trait_M_DEFENDER ) ) || + ( ( p.mutations.has_trait( trait_M_SPORES ) || + p.mutations.has_trait( trait_M_FERTILE ) ) && + one_in( 2 ) ) ) { g->place_critter_at( mon_fungal_blossom, examp ); add_msg( m_info, _( "The seed blooms forth! We have brought true beauty to this world." ) ); - } else if( ( p.has_trait( trait_THRESH_MYCUS ) ) || one_in( 4 ) ) { + } else if( ( p.mutations.has_trait( trait_THRESH_MYCUS ) ) || one_in( 4 ) ) { g->m.furn_set( examp, f_flower_marloss ); add_msg( m_info, _( "The seed blossoms rather rapidly…" ) ); } else { @@ -3286,9 +3289,9 @@ void iexamine::tree_maple_tapped( player &p, const tripoint &examp ) void iexamine::shrub_marloss( player &p, const tripoint &examp ) { - if( p.has_trait( trait_THRESH_MYCUS ) ) { + if( p.mutations.has_trait( trait_THRESH_MYCUS ) ) { pick_plant( p, examp, "mycus_fruit", t_shrub_fungal ); - } else if( p.has_trait( trait_THRESH_MARLOSS ) ) { + } else if( p.mutations.has_trait( trait_THRESH_MARLOSS ) ) { g->m.spawn_item( p.pos(), "mycus_fruit", 1, 0, calendar::turn ); g->m.ter_set( examp, t_fungus ); add_msg( m_info, _( "The shrub offers up a fruit, then crumbles into a fungal bed." ) ); @@ -3299,16 +3302,16 @@ void iexamine::shrub_marloss( player &p, const tripoint &examp ) void iexamine::tree_marloss( player &p, const tripoint &examp ) { - if( p.has_trait( trait_THRESH_MYCUS ) ) { + if( p.mutations.has_trait( trait_THRESH_MYCUS ) ) { pick_plant( p, examp, "mycus_fruit", t_tree_fungal ); - if( p.has_trait( trait_M_DEPENDENT ) && one_in( 3 ) ) { + if( p.mutations.has_trait( trait_M_DEPENDENT ) && one_in( 3 ) ) { // Folks have a better shot at keeping fed. add_msg( m_info, _( "We have located a particularly vital nutrient deposit underneath this location." ) ); add_msg( m_good, _( "Additional nourishment is available." ) ); g->m.ter_set( examp, t_marloss_tree ); } - } else if( p.has_trait( trait_THRESH_MARLOSS ) ) { + } else if( p.mutations.has_trait( trait_THRESH_MARLOSS ) ) { g->m.spawn_item( p.pos(), "mycus_fruit", 1, 0, calendar::turn ); g->m.ter_set( examp, t_tree_fungal ); add_msg( m_info, _( "The tree offers up a fruit, then shrivels into a fungal tree." ) ); @@ -3660,7 +3663,7 @@ void iexamine::sign( player &p, const tripoint &examp ) bool previous_signage_exists = !existing_signage.empty(); // Display existing message, or lack thereof. - if( p.has_trait( trait_ILLITERATE ) ) { + if( p.mutations.has_trait( trait_ILLITERATE ) ) { popup( _( "You're illiterate, and can't read the message on the sign." ) ); } else if( previous_signage_exists ) { popup( existing_signage.c_str() ); @@ -3785,7 +3788,7 @@ static int findBestGasDiscount( player &p ) static std::string str_to_illiterate_str( std::string s ) { - if( !g->u.has_trait( trait_ILLITERATE ) ) { + if( !g->u.mutations.has_trait( trait_ILLITERATE ) ) { return s; } else { for( auto &i : s ) { @@ -3914,7 +3917,7 @@ void iexamine::pay_gas( player &p, const tripoint &examp ) const int hack = 3; const int refund = 4; - if( p.has_trait( trait_ILLITERATE ) ) { + if( p.mutations.has_trait( trait_ILLITERATE ) ) { popup( _( "You're illiterate, and can't read the screen." ) ); } @@ -3947,8 +3950,9 @@ void iexamine::pay_gas( player &p, const tripoint &examp ) int pricePerUnit = getGasPricePerLiter( discount ); - bool can_hack = ( !p.has_trait( trait_ILLITERATE ) && ( ( p.has_charges( "electrohack", 25 ) ) || - ( p.has_bionic( bionic_id( "bio_fingerhack" ) ) && p.get_power_level() > 24_kJ ) ) ); + bool can_hack = ( !p.mutations.has_trait( trait_ILLITERATE ) && + ( ( p.has_charges( "electrohack", 25 ) ) || + ( p.has_bionic( bionic_id( "bio_fingerhack" ) ) && p.get_power_level() > 24_kJ ) ) ); uilist amenu; amenu.selected = 1; @@ -4367,7 +4371,8 @@ void iexamine::autodoc( player &p, const tripoint &examp ) std::vector anesth_kit; int drug_count = 0; - if( patient.has_trait( trait_NOPAIN ) || patient.has_bionic( bionic_id( "bio_painkiller" ) ) || + if( patient.mutations.has_trait( trait_NOPAIN ) || + patient.has_bionic( bionic_id( "bio_painkiller" ) ) || amenu.ret > 1 ) { needs_anesthesia = false; } else { @@ -5502,7 +5507,7 @@ void iexamine::workbench_internal( player &p, const tripoint &examp, const option choice = static_cast