From ffb07ac8a834f09c00fc55bd24f56e7772608b80 Mon Sep 17 00:00:00 2001 From: osuphobia Date: Thu, 19 Dec 2024 23:07:32 +0800 Subject: [PATCH 1/7] Replace dead mutation cache code. --- src/character.cpp | 25 +------------------------ src/character.h | 2 -- src/inventory.cpp | 20 -------------------- src/inventory.h | 3 --- src/item.cpp | 34 ---------------------------------- src/item.h | 1 - src/magic_enchantment.cpp | 2 ++ 7 files changed, 3 insertions(+), 84 deletions(-) diff --git a/src/character.cpp b/src/character.cpp index df3afb07a74c2..70d76b995da81 100644 --- a/src/character.cpp +++ b/src/character.cpp @@ -7689,8 +7689,7 @@ void Character::recalculate_bodyparts() void Character::recalculate_enchantment_cache() { - // start by resetting the cache to all inventory items - *enchantment_cache = inv->get_active_enchantment_cache( *this ); + enchantment_cache->clear(); cache_visit_items_with( "is_relic", &item::is_relic, [this]( const item & it ) { for( const enchant_cache &ench : it.get_proc_enchantments() ) { @@ -9840,12 +9839,6 @@ void Character::on_item_wear( const item &it ) { invalidate_inventory_validity_cache(); invalidate_leak_level_cache(); - for( const trait_id &mut : it.mutations_from_wearing( *this ) ) { - // flag these mutations to be added at the start of the next turn - // without doing this you still count as wearing the item providing - // the traits so most the calcs don't work in mutation_loss_effect - mutations_to_add.push_back( mut ); - } morale->on_item_wear( it ); } @@ -9854,25 +9847,9 @@ void Character::on_item_takeoff( const item &it ) invalidate_inventory_validity_cache(); invalidate_weight_carried_cache(); invalidate_leak_level_cache(); - for( const trait_id &mut : it.mutations_from_wearing( *this, true ) ) { - // flag these mutations to be removed at the start of the next turn - // without doing this you still count as wearing the item providing - // the traitssdd so most the calcs don't work in mutation_loss_effect - mutations_to_remove.push_back( mut ); - - } morale->on_item_takeoff( it ); } -void Character::enchantment_wear_change() -{ - recalc_sight_limits(); - calc_encumbrance(); - if( get_stamina() > get_stamina_max() ) { - set_stamina( get_stamina_max() ); - } -} - void Character::on_item_acquire( const item &it ) { bool check_for_zoom = is_avatar(); diff --git a/src/character.h b/src/character.h index 67d845a6a8169..5a2d2fbf67fe0 100644 --- a/src/character.h +++ b/src/character.h @@ -3183,8 +3183,6 @@ class Character : public Creature, public visitable void on_item_wear( const item &it ); /** Called when an item is taken off */ void on_item_takeoff( const item &it ); - // things to call when mutations enchantments change - void enchantment_wear_change(); /** Called when an item is washed */ void on_worn_item_washed( const item &it ); /** Called when an item is acquired (picked up, worn, or wielded) */ diff --git a/src/inventory.cpp b/src/inventory.cpp index 7dd4ddd1dfe3e..ff2997d0c7606 100644 --- a/src/inventory.cpp +++ b/src/inventory.cpp @@ -971,26 +971,6 @@ units::volume inventory::volume_without( const std::map &with return ret; } -enchant_cache inventory::get_active_enchantment_cache( const Character &owner ) const -{ - enchant_cache temp_cache; - for( const std::list &elem : items ) { - for( const item &check_item : elem ) { - for( const enchant_cache &ench : check_item.get_proc_enchantments() ) { - if( ench.is_active( owner, check_item ) ) { - temp_cache.force_add( ench ); - } - } - for( const enchantment &ench : check_item.get_defined_enchantments() ) { - if( ench.is_active( owner, check_item ) ) { - temp_cache.force_add( ench, owner ); - } - } - } - } - return temp_cache; -} - int inventory::count_item( const itype_id &item_type ) const { int num = 0; diff --git a/src/inventory.h b/src/inventory.h index 0b48b71cdb61d..aaaf57689d65f 100644 --- a/src/inventory.h +++ b/src/inventory.h @@ -250,9 +250,6 @@ class inventory : public visitable void copy_invlet_of( const inventory &other ); - // gets a singular enchantment that is an amalgamation of all items that have active enchantments - enchant_cache get_active_enchantment_cache( const Character &owner ) const; - int count_item( const itype_id &item_type ) const; book_proficiency_bonuses get_book_proficiency_bonuses() const; diff --git a/src/item.cpp b/src/item.cpp index 4ef8b13eafa3e..f7d7401c8bb6c 100644 --- a/src/item.cpp +++ b/src/item.cpp @@ -13409,40 +13409,6 @@ void item::reset_temp_check() last_temp_check = calendar::turn; } -std::vector item::mutations_from_wearing( const Character &guy, bool removing ) const -{ - if( !is_relic() ) { - return std::vector {}; - } - std::vector muts; - - for( const enchant_cache &ench : relic_data->get_proc_enchantments() ) { - for( const trait_id &mut : ench.get_mutations() ) { - // this may not be perfectly accurate due to conditions - muts.push_back( mut ); - } - } - if( type->relic_data ) { - for( const enchantment &ench : type->relic_data->get_defined_enchantments() ) { - for( const trait_id &mut : ench.get_mutations() ) { - // this may not be perfectly accurate due to conditions - muts.push_back( mut ); - } - } - } - for( const trait_id &char_mut : guy.get_mutations( true, removing ) ) { - for( auto iter = muts.begin(); iter != muts.end(); ) { - if( char_mut == *iter ) { - iter = muts.erase( iter ); - } else { - ++iter; - } - } - } - - return muts; -} - void item::overwrite_relic( const relic &nrelic ) { this->relic_data = cata::make_value( nrelic ); diff --git a/src/item.h b/src/item.h index e3ea202ce2ccc..62aeb62a2b71d 100644 --- a/src/item.h +++ b/src/item.h @@ -1889,7 +1889,6 @@ class item : public visitable bool use_relic( Character &guy, const tripoint_bub_ms &pos ); bool has_relic_recharge() const; bool has_relic_activation() const; - std::vector mutations_from_wearing( const Character &guy, bool removing = false ) const; /** * Name of the item type (not the item), with proper plural. diff --git a/src/magic_enchantment.cpp b/src/magic_enchantment.cpp index 0f279dcb57265..7f096af848f38 100644 --- a/src/magic_enchantment.cpp +++ b/src/magic_enchantment.cpp @@ -1462,6 +1462,8 @@ void enchant_cache::clear() hit_me_effect.clear(); hit_you_effect.clear(); ench_effects.clear(); + mutations.clear(); + modified_bodyparts.clear(); } bool enchant_cache::operator==( const enchant_cache &rhs ) const From eaacb1b4efcb38a93164203fcbe9e89ac87c97b4 Mon Sep 17 00:00:00 2001 From: osuphobia Date: Fri, 20 Dec 2024 01:07:59 +0800 Subject: [PATCH 2/7] Make enchantment mutations work again. --- src/bionics.cpp | 2 +- src/cata_tiles.cpp | 2 +- src/character.cpp | 197 +++++++++++++++++++++++++------- src/character.h | 40 +++++-- src/character_armor.cpp | 2 +- src/character_attire.cpp | 2 +- src/character_body.cpp | 26 ----- src/character_morale.cpp | 2 +- src/consumption.cpp | 6 +- src/crafting.cpp | 2 +- src/handle_action.cpp | 2 +- src/iuse_actor.cpp | 2 +- src/magic_enchantment.cpp | 12 +- src/magic_enchantment.h | 6 +- src/melee.cpp | 2 +- src/memorial_logger.cpp | 4 +- src/monster.cpp | 2 +- src/mood_face.cpp | 2 +- src/mutation.cpp | 179 +++++++++++++++++------------ src/mutation_ui.cpp | 33 +++--- src/newcharacter.cpp | 62 ++++++---- src/npc.cpp | 6 +- src/player_difficulty.cpp | 2 +- src/savegame_json.cpp | 42 ++++++- src/suffer.cpp | 2 +- src/talker_npc.cpp | 2 +- src/visitable.cpp | 2 +- src/wish.cpp | 12 +- tests/consumption_time_test.cpp | 2 +- 29 files changed, 424 insertions(+), 233 deletions(-) diff --git a/src/bionics.cpp b/src/bionics.cpp index 181f28d4088af..250197f49d1b6 100644 --- a/src/bionics.cpp +++ b/src/bionics.cpp @@ -2716,7 +2716,7 @@ int Character::get_total_bionics_slots( const bodypart_id &bp ) const { const bodypart_str_id &id = bp.id(); int mut_bio_slots = 0; - for( const trait_id &mut : get_mutations() ) { + for( const trait_id &mut : get_functioning_mutations() ) { mut_bio_slots += mut->bionic_slot_bonus( id ); } return bp->bionic_slots() + mut_bio_slots; diff --git a/src/cata_tiles.cpp b/src/cata_tiles.cpp index bc8290aa1edde..ddcc7cf23dac4 100644 --- a/src/cata_tiles.cpp +++ b/src/cata_tiles.cpp @@ -4352,7 +4352,7 @@ void cata_tiles::draw_zlevel_overlay( const tripoint &p, const lit_level ll, int void cata_tiles::draw_entity_with_overlays( const Character &ch, const tripoint &p, lit_level ll, int &height_3d ) { - std::vector override_look_muts = ch.get_mutations( true, + std::vector override_look_muts = ch.get_functioning_mutations( true, false, []( const mutation_branch & mut ) { return mut.override_look.has_value(); } ); diff --git a/src/character.cpp b/src/character.cpp index 70d76b995da81..426ff59e4db0b 100644 --- a/src/character.cpp +++ b/src/character.cpp @@ -2388,7 +2388,7 @@ void Character::process_turn() } } - for( const trait_id &mut : get_mutations() ) { + for( const trait_id &mut : get_functioning_mutations() ) { mutation_reflex_trigger( mut ); } @@ -2424,7 +2424,7 @@ void Character::process_turn() int norm_scent = 500; int temp_norm_scent = INT_MIN; bool found_intensity = false; - for( const trait_id &mut : get_mutations() ) { + for( const trait_id &mut : get_functioning_mutations() ) { const std::optional &scent_intensity = mut->scent_intensity; if( scent_intensity ) { found_intensity = true; @@ -3434,22 +3434,13 @@ std::vector> Character::get_overlay_ids() co } // then get mutations - const std::vector &all_mutations = get_mutations(); - for( const trait_id &mut : all_mutations ) { - // get the data if it exists - const auto mut_data = my_mutations.find( mut ); - if( mut_data == my_mutations.end() ) { - // this entry is from an enchantment and doesn't have any character dependent info - overlay_id = mut.str(); - - } else { - if( !mut_data->second.show_sprite ) { - continue; - } - overlay_id = ( mut_data->second.powered ? "active_" : "" ) + mut.str(); - if( mut_data->second.variant != nullptr ) { - variant = mut_data->second.variant->id; - } + for( const auto &mut : cached_mutations ) { + if( mut.second.corrupted > 0 || !mut.second.show_sprite ) { + continue; + } + overlay_id = ( mut.second.powered ? "active_" : "" ) + mut.first.str(); + if( mut.second.variant != nullptr ) { + variant = mut.second.variant->id; } order = get_overlay_order_of_mutation( overlay_id ); mutation_sorting.emplace( order, std::pair { overlay_id, variant } ); @@ -3721,7 +3712,7 @@ std::pair Character::best_part_to_smash() const int tmp_bash_armor = 0; for( const bodypart_id &bp : get_all_body_parts() ) { tmp_bash_armor += worn.damage_resist( damage_bash, bp ); - for( const trait_id &mut : get_mutations() ) { + for( const trait_id &mut : get_functioning_mutations() ) { const resistances &res = mut->damage_resistance( bp ); tmp_bash_armor += std::floor( res.type_resist( damage_bash ) ); } @@ -4169,7 +4160,7 @@ int Character::encumb( const bodypart_id &bp ) const void Character::apply_mut_encumbrance( std::map &vals ) const { - const std::vector all_muts = get_mutations(); + const std::vector all_muts = get_functioning_mutations(); std::map total_enc; // Lower penalty for bps covered only by XL or unrestricted armor @@ -5980,7 +5971,7 @@ float Character::active_light() const lumination = static_cast( maxlum ); float mut_lum = 0.0f; - for( const trait_id &mut : get_mutations() ) { + for( const trait_id &mut : get_functioning_mutations() ) { float curr_lum = 0.0f; for( const std::pair &elem : mut->lumination ) { const float lum_coverage = worn.coverage_with_flags_exclude( elem.first.id(), @@ -6206,7 +6197,7 @@ std::vector Character::extended_description() const social_modifiers Character::get_mutation_bionic_social_mods() const { social_modifiers mods; - for( const mutation_branch *mut : cached_mutations ) { + for( const trait_id &mut : get_functioning_mutations() ) { mods += mut->social_mods; } for( const bionic &bio : *my_bionics ) { @@ -7582,7 +7573,7 @@ void Character::calc_mutation_levels() // from the same root mutation as having full height // For each of our mutations... - for( const trait_id &mut : get_mutations() ) { + for( const trait_id &mut : get_mutations( true, true ) ) { const mutation_branch &mdata = mut.obj(); if( mdata.threshold ) { // Thresholds are worth 10, and technically support multiple categories @@ -7619,7 +7610,7 @@ void Character::drench_mut_calc() int neutral = 0; int good = 0; - for( const trait_id &iter : get_mutations() ) { + for( const trait_id &iter : get_functioning_mutations() ) { const mutation_branch &mdata = iter.obj(); const auto wp_iter = mdata.protection.find( bp.id() ); if( wp_iter != mdata.protection.end() ) { @@ -7704,17 +7695,6 @@ void Character::recalculate_enchantment_cache() } } ); - // get from traits/ mutations - for( const std::pair &mut_map : my_mutations ) { - const mutation_branch &mut = mut_map.first.obj(); - - for( const enchantment_id &ench_id : mut.enchantments ) { - const enchantment &ench = ench_id.obj(); - if( ench.is_active( *this, mut.activated && mut_map.second.powered ) ) { - enchantment_cache->force_add( ench, *this ); - } - } - } for( const bionic &bio : *my_bionics ) { const bionic_id &bid = bio.id; @@ -7737,12 +7717,147 @@ void Character::recalculate_enchantment_cache() } } + for( const std::pair &mut_map : my_mutations ) { + if( mut_map.second.corrupted == 0 ) { + const mutation_branch &mut = mut_map.first.obj(); + for( const enchantment_id &ench_id : mut.enchantments ) { + const enchantment &ench = ench_id.obj(); + if( ench.is_active( *this, mut.activated && mut_map.second.powered ) ) { + enchantment_cache->force_add_mutation( ench, *this ); + } + } + } + } + new_mutation_cache->mutations = enchantment_cache->mutations; + update_cached_mutations(); + for( const std::pair &mut_map : cached_mutations ) { + if( mut_map.second.corrupted == 0 ) { + const mutation_branch &mut = mut_map.first.obj(); + for( const enchantment_id &ench_id : mut.enchantments ) { + const enchantment &ench = ench_id.obj(); + if( ench.is_active( *this, mut.activated && mut_map.second.powered ) ) { + enchantment_cache->force_add( ench, *this ); + } + } + } + } + if( enchantment_cache->modifies_bodyparts() ) { recalculate_bodyparts(); } recalc_hp(); } +void Character::update_cached_mutations() +{ + const std::vector &new_muts = new_mutation_cache->get_mutations(); + const std::vector &old_muts = old_mutation_cache->get_mutations(); + if( my_mutations_dirty.empty() ) { + if( cached_mutations.empty() ) { + // migration from old save + cached_mutations = my_mutations; + } + if( new_muts.empty() && old_muts.empty() ) { + // Quick bail out as no changes happened. + return; + } + } + + if( !old_muts.empty() ) { + for( const trait_id &it : old_muts ) { + auto remains = find( new_muts.begin(), new_muts.end(), it ); + if( remains == new_muts.end() ) { + mutations_to_remove.insert( it ); + } else { + for( auto &iter : my_mutations_dirty ) { + if( are_opposite_traits( it, iter.first ) || b_is_lower_trait_of_a( it, iter.first ) + || are_same_type_traits( it, iter.first ) ) { + ++iter.second.corrupted; + ++my_mutations[it].corrupted; + } + } + } + } + for( auto &iter : my_mutations_dirty ) { + cached_mutations.emplace( iter ); + if( iter.second.corrupted == 0 ) { + mutation_effect( iter.first, false ); + do_mutation_updates(); + } + } + for( const trait_id &it : new_muts ) { + auto exists = find( old_muts.begin(), old_muts.end(), it ); + if( exists == old_muts.end() ) { + mutations_to_add.insert( it ); + } + } + } else { + for( auto &it : my_mutations_dirty ) { + cached_mutations.emplace( it ); + mutation_effect( it.first, false ); + do_mutation_updates(); + } + for( const trait_id &it : new_muts ) { + if( !cached_mutations.count( it ) ) { + mutations_to_add.insert( it ); + } + } + } + my_mutations_dirty.clear(); + old_mutation_cache = new_mutation_cache; + new_mutation_cache->clear(); + if( mutations_to_add.empty() && mutations_to_remove.empty() ) { + // Quick bail out as no changes happened. + return; + } + + const std::vector ¤t_traits = get_mutations(); + for( const trait_id &mut : mutations_to_remove ) { + // check if the player still has a mutation + // since a trait from an item might be provided by another item as well + auto it = std::find( current_traits.begin(), current_traits.end(), mut ); + if( it == current_traits.end() ) { + for( auto &iter : cached_mutations ) { + if( ( are_opposite_traits( iter.first, mut ) || b_is_higher_trait_of_a( iter.first, mut ) + || are_same_type_traits( iter.first, mut ) ) && iter.second.corrupted > 0 ) { + --iter.second.corrupted; + if( my_mutations.count( mut ) ) { + --my_mutations[mut].corrupted; + } + if( iter.second.corrupted == 0 ) { + mutation_effect( mut, false ); + do_mutation_updates(); + } + } + } + cached_mutations.erase( mut ); + mutation_loss_effect( mut ); + do_mutation_updates(); + } + } + for( const trait_id &mut : mutations_to_add ) { + for( auto &iter : cached_mutations ) { + if( are_opposite_traits( iter.first, mut ) || b_is_higher_trait_of_a( iter.first, mut ) + || are_same_type_traits( iter.first, mut ) ) { + ++iter.second.corrupted; + if( my_mutations.count( mut ) ) { + ++my_mutations[mut].corrupted; + } + if( iter.second.corrupted == 1 ) { + mutation_loss_effect( mut ); + do_mutation_updates(); + } + } + } + cached_mutations.emplace( mut, trait_data() ); + mutation_effect( mut, true ); + do_mutation_updates(); + } + mutations_to_add.clear(); + mutations_to_remove.clear(); + trait_flag_cache.clear(); +} + void Character::passive_absorb_hit( const bodypart_id &bp, damage_unit &du ) const { // >0 check because some mutations provide negative armor @@ -8408,7 +8523,7 @@ bool Character::crossed_threshold() const mutation_category_id Character::get_threshold_category() const { - for( const trait_id &mut : get_mutations() ) { + for( const trait_id &mut : get_functioning_mutations() ) { if( mut->threshold && !mut->category.empty() ) { return mut->category[0]; } @@ -8419,7 +8534,7 @@ mutation_category_id Character::get_threshold_category() const void Character::update_type_of_scent( bool init ) { scenttype_id new_scent = scent_sc_human; - for( const trait_id &mut : get_mutations() ) { + for( const trait_id &mut : get_functioning_mutations() ) { if( mut.obj().scent_typeid ) { new_scent = mut.obj().scent_typeid.value(); } @@ -9182,7 +9297,7 @@ units::temperature_delta Character::floor_warmth( const tripoint_bub_ms &pos ) c units::temperature_delta Character::bodytemp_modifier_traits( bool overheated ) const { units::temperature_delta mod = 0_C_delta; - for( const trait_id &iter : get_mutations() ) { + for( const trait_id &iter : get_functioning_mutations() ) { mod += overheated ? iter->bodytemp_min : iter->bodytemp_max; } return mod; @@ -9888,7 +10003,6 @@ void Character::on_mutation_gain( const trait_id &mid ) morale->on_mutation_gain( mid ); magic->on_mutation_gain( mid, *this ); update_type_of_scent( mid ); - recalculate_enchantment_cache(); // mutations can have enchantments effect_on_conditions::process_reactivate( *this ); if( is_avatar() ) { as_avatar()->character_mood_face( true ); @@ -9900,7 +10014,6 @@ void Character::on_mutation_loss( const trait_id &mid ) morale->on_mutation_loss( mid ); magic->on_mutation_loss( mid ); update_type_of_scent( mid, false ); - recalculate_enchantment_cache(); // mutations can have enchantments effect_on_conditions::process_reactivate( *this ); if( is_avatar() ) { as_avatar()->character_mood_face( true ); @@ -9925,7 +10038,7 @@ std::unordered_set Character::get_opposite_traits( const trait_id &fla traits.insert( i ); } } - for( const std::pair &mut : my_mutations ) { + for( const std::pair &mut : cached_mutations ) { for( const trait_id &canceled_trait : mut.first->cancels ) { if( canceled_trait == flag ) { traits.insert( mut.first ); @@ -11134,7 +11247,7 @@ bool Character::can_sleep() const comfort_data &Character::get_comfort_data_for( const tripoint_bub_ms &p ) const { const comfort_data *worst = nullptr; - for( const trait_id trait : get_mutations() ) { + for( const trait_id trait : get_functioning_mutations() ) { for( const comfort_data &data : trait->comfort ) { if( data.are_conditions_true( *this, p.raw() ) ) { if( worst == nullptr || worst->base_comfort > data.base_comfort ) { diff --git a/src/character.h b/src/character.h index 5a2d2fbf67fe0..ad95cc80a2926 100644 --- a/src/character.h +++ b/src/character.h @@ -943,7 +943,7 @@ class Character : public Creature, public visitable /** Updates the stomach to give accurate hunger messages */ void update_stomach( const time_point &from, const time_point &to ); /** Updates the mutations from enchantments */ - void update_enchantment_mutations(); + void update_cached_mutations(); /** Returns true if character needs food, false if character is an NPC with NO_NPC_FOOD set */ bool needs_food() const; /** Increases hunger, thirst, sleepiness and stimulants wearing off. `rate_multiplier` is for retroactive updates. */ @@ -1376,10 +1376,12 @@ class Character : public Creature, public visitable 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 mutation_category_id &cat, int strength ) const; - /** 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 trait in cached_mutations */ + bool has_trait( const trait_id &b, bool ignore_disabled = true ) const; /** Returns true if the player has the entered trait with the desired variant */ bool has_trait_variant( const trait_and_var & ) const; + /** Returns true if the player has the entered trait in my_mutations */ + bool has_permanent_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 */ @@ -1445,7 +1447,8 @@ class Character : public Creature, public visitable void mutation_reflex_trigger( const trait_id &mut ); // Trigger and disable mutations that can be so toggled. - void activate_mutation( const trait_id &mutation ); + void activate_mutation( const trait_id &mut ); + void activate_cached_mutation( const trait_id &mut ); void deactivate_mutation( const trait_id &mut ); bool can_mount( const monster &critter ) const; @@ -2672,6 +2675,11 @@ class Character : public Creature, public visitable bool include_hidden = true, bool ignore_enchantment = false, const std::function &filter = nullptr ) const; + /** Get the idents of all traits/mutations with corrupted = 0. */ + std::vector get_functioning_mutations( + bool include_hidden = true, + bool ignore_enchantment = false, + const std::function &filter = nullptr ) const; /** Same as above, but also grab the variant ids (or empty string if none) */ std::vector get_mutations_variants( bool include_hidden = true, bool ignore_enchantment = false ) const; @@ -3881,6 +3889,12 @@ class Character : public Creature, public visitable void swap_character( Character &other ); public: struct trait_data { + /** + * Updated in Character::update_cached_mutations(), + * corrupted > 0 means that the mutation is not functioning, + * because of other conflicting enchantment mutations. + */ + int corrupted = 0; /** Whether the mutation is activated. */ bool powered = false; /** Key to select the mutation in the UI. */ @@ -3969,14 +3983,18 @@ class Character : public Creature, public visitable // Mutations that have been turned unpurifiable via EoC std::unordered_set my_intrinsic_mutations; /** - * Pointers to mutation branches in @ref my_mutations. + * Cache containing my_mutations and enchantment mutations. + */ + std::unordered_map cached_mutations; + /** + * Cache newly mutated mutations in my_mutations. */ - std::vector cached_mutations; + std::unordered_map my_mutations_dirty; // if the player puts on and takes off items these mutations - // are added or removed at the beginning of the next - std::vector mutations_to_remove; - std::vector mutations_to_add; + // are added or removed at the beginning of the next turn. + std::set mutations_to_remove; + std::set mutations_to_add; /** * The amount of weight the Character is carrying. * If it is nullopt, needs to be recalculated @@ -4156,6 +4174,10 @@ class Character : public Creature, public visitable mutable time_point next_climate_control_check; mutable bool last_climate_control_ret; + /* cached enchantment mutations */ + pimpl old_mutation_cache; + pimpl new_mutation_cache; + private: /* cached recipes, which are invalidated if the turn changes */ mutable time_point cached_recipe_turn; diff --git a/src/character_armor.cpp b/src/character_armor.cpp index 766f8f2f3d846..ee86c452e66c2 100644 --- a/src/character_armor.cpp +++ b/src/character_armor.cpp @@ -54,7 +54,7 @@ bool Character::can_interface_armor() const resistances Character::mutation_armor( bodypart_id bp ) const { resistances res; - for( const trait_id &iter : get_mutations() ) { + for( const trait_id &iter : get_functioning_mutations() ) { res += iter->damage_resistance( bp ); } diff --git a/src/character_attire.cpp b/src/character_attire.cpp index ae1bddd0a0bfb..2f95c3107f4d2 100644 --- a/src/character_attire.cpp +++ b/src/character_attire.cpp @@ -145,7 +145,7 @@ ret_val Character::can_wear( const item &it, bool with_equip_change ) cons if( !it.has_flag( flag_OVERSIZE ) && !it.has_flag( flag_INTEGRATED ) && !it.has_flag( flag_SEMITANGIBLE ) && !it.has_flag( flag_MORPHIC ) && !it.has_flag( flag_UNRESTRICTED ) ) { - for( const trait_id &mut : get_mutations() ) { + for( const trait_id &mut : get_functioning_mutations() ) { const mutation_branch &branch = mut.obj(); if( branch.conflicts_with_item( it ) ) { return ret_val::make_failure( is_avatar() ? diff --git a/src/character_body.cpp b/src/character_body.cpp index 42e5754e50dc3..478294da563b5 100644 --- a/src/character_body.cpp +++ b/src/character_body.cpp @@ -249,7 +249,6 @@ void Character::update_body( const time_point &from, const time_point &to ) } update_stomach( from, to ); recalculate_enchantment_cache(); - update_enchantment_mutations(); if( ticks_between( from, to, 3_minutes ) > 0 ) { magic->update_mana( *this, to_turns( 3_minutes ) ); } @@ -381,31 +380,6 @@ void Character::update_body( const time_point &from, const time_point &to ) } } -void Character::update_enchantment_mutations() -{ - // after recalcing the enchantment cache can properly remove and add mutations - const std::vector ¤t_traits = get_mutations(); - for( const trait_id &mut : mutations_to_remove ) { - // check if the player still has a mutation - // since a trait from an item might be provided by another item as well - auto it = std::find( current_traits.begin(), current_traits.end(), mut ); - if( it == current_traits.end() ) { - const mutation_branch &mut_b = *mut; - cached_mutations.erase( std::remove( cached_mutations.begin(), cached_mutations.end(), &mut_b ), - cached_mutations.end() ); - mutation_loss_effect( mut ); - enchantment_wear_change(); - } - } - for( const trait_id &mut : mutations_to_add ) { - cached_mutations.push_back( &mut.obj() ); - mutation_effect( mut, true ); - enchantment_wear_change(); - } - mutations_to_add.clear(); - mutations_to_remove.clear(); -} - /* Here lies the intended effects of body temperature Assumption 1 : a naked person is comfortable at 19C/66.2F (31C/87.8F at rest). diff --git a/src/character_morale.cpp b/src/character_morale.cpp index 6ca948876ae5d..0e4a545e742a8 100644 --- a/src/character_morale.cpp +++ b/src/character_morale.cpp @@ -184,7 +184,7 @@ void Character::check_and_recover_morale() worn.check_and_recover_morale( test_morale ); - for( const trait_id &mut : get_mutations() ) { + for( const trait_id &mut : get_functioning_mutations() ) { test_morale.on_mutation_gain( mut ); } diff --git a/src/consumption.cpp b/src/consumption.cpp index 7fd4eabd18fd8..060c150f7553f 100644 --- a/src/consumption.cpp +++ b/src/consumption.cpp @@ -272,7 +272,7 @@ static std::map compute_default_effective_vitamins( vit.second = vit.first->RDA_to_default( vit.second ); } - for( const trait_id &trait : you.get_mutations() ) { + for( const trait_id &trait : you.get_functioning_mutations() ) { const mutation_branch &mut = trait.obj(); // make sure to iterate over every material defined for vitamin absorption // TODO: put this loop into a function and utilize it again for bionics @@ -606,7 +606,7 @@ time_duration Character::vitamin_rate( const vitamin_id &vit ) const { time_duration res = vit.obj().rate(); - for( const auto &m : get_mutations() ) { + for( const auto &m : get_functioning_mutations() ) { const mutation_branch &mut = m.obj(); auto iter = mut.vitamin_rates.find( vit ); if( iter != mut.vitamin_rates.end() && iter->second != 0_turns ) { @@ -941,7 +941,7 @@ ret_val Character::can_eat( const item &food ) const _( "You're still not going to eat animal products." ) ); } - for( const trait_id &mut : get_mutations() ) { + for( const trait_id &mut : get_functioning_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." ) ); } diff --git a/src/crafting.cpp b/src/crafting.cpp index 932d545b4ed08..d22abf3b0d758 100644 --- a/src/crafting.cpp +++ b/src/crafting.cpp @@ -1122,7 +1122,7 @@ float Character::get_recipe_weighted_skill_average( const recipe &making ) const } // Mutations can define specific skill bonuses and penalties. Gotta include those. - for( const trait_id &mut : get_mutations() ) { + for( const trait_id &mut : get_functioning_mutations() ) { for( const std::pair &skill_bonuses : mut->craft_skill_bonus ) { if( making.skill_used == skill_bonuses.first ) { total_skill_modifiers += skill_bonuses.second * 1.0f; diff --git a/src/handle_action.cpp b/src/handle_action.cpp index 80704eb911ea3..dcce28f55bbf6 100644 --- a/src/handle_action.cpp +++ b/src/handle_action.cpp @@ -1376,7 +1376,7 @@ static void sleep() active.push_back( info.name.translated() ); } } - for( auto &mut : player_character.get_mutations() ) { + for( auto &mut : player_character.get_functioning_mutations() ) { const mutation_branch &mdata = mut.obj(); if( mdata.cost > 0 && player_character.has_active_mutation( mut ) ) { active.push_back( player_character.mutation_name( mut ) ); diff --git a/src/iuse_actor.cpp b/src/iuse_actor.cpp index bec793fffd336..c07ee32f17114 100644 --- a/src/iuse_actor.cpp +++ b/src/iuse_actor.cpp @@ -386,7 +386,7 @@ ret_val iuse_transform::can_use( const Character &p, const item &it, item tmp = item( target ); if( !tmp.has_flag( flag_OVERSIZE ) && !tmp.has_flag( flag_INTEGRATED ) && !tmp.has_flag( flag_SEMITANGIBLE ) ) { - for( const trait_id &mut : p.get_mutations() ) { + for( const trait_id &mut : p.get_functioning_mutations() ) { const mutation_branch &branch = mut.obj(); if( branch.conflicts_with_item( tmp ) ) { return ret_val::make_failure( _( "Your %1$s mutation prevents you from doing that." ), diff --git a/src/magic_enchantment.cpp b/src/magic_enchantment.cpp index 7f096af848f38..8eaff6e332de9 100644 --- a/src/magic_enchantment.cpp +++ b/src/magic_enchantment.cpp @@ -777,6 +777,14 @@ bool enchant_cache::add( const enchant_cache &rhs ) return true; } +void enchant_cache::force_add_mutation( const enchantment &rhs, const Character &guy ) +{ + for( const trait_id &branch : rhs.mutations ) { + mutations.push_back( branch ); + } +} + + void enchant_cache::force_add( const enchant_cache &rhs ) { for( const std::pair &pair_values : @@ -851,7 +859,7 @@ void enchant_cache::force_add( const enchant_cache &rhs ) } for( const trait_id &branch : rhs.mutations ) { - mutations.emplace( branch ); + mutations.push_back( branch ); } for( const std::pair> &act_pair : @@ -998,7 +1006,7 @@ void enchant_cache::force_add_with_dialogue( const enchantment &rhs, const const } for( const trait_id &branch : rhs.mutations ) { - mutations.emplace( branch ); + mutations.push_back( branch ); } for( const std::pair> &act_pair : diff --git a/src/magic_enchantment.h b/src/magic_enchantment.h index d3871fd062ee6..a63c8b93d59f3 100644 --- a/src/magic_enchantment.h +++ b/src/magic_enchantment.h @@ -194,7 +194,7 @@ class enchantment bool was_loaded = false; - const std::set &get_mutations() const { + const std::vector &get_mutations() const { return mutations; } double get_value_add( enchant_vals::mod value, const Character &guy ) const; @@ -213,7 +213,7 @@ class enchantment }; std::vector modified_bodyparts; - std::set mutations; + std::vector mutations; std::optional emitter; std::map ench_effects; @@ -303,6 +303,8 @@ class enchant_cache : public enchantment void force_add( const enchant_cache &rhs ); void force_add_with_dialogue( const enchantment &rhs, const const_dialogue &d, bool evaluate = true ); + // adds enchantment mutations to the cache + void force_add_mutation( const enchantment &rhs, const Character &guy ); // modifies character stats, or does other passive effects void activate_passive( Character &guy ) const; diff --git a/src/melee.cpp b/src/melee.cpp index 87c789ba59a39..f951063e269f8 100644 --- a/src/melee.cpp +++ b/src/melee.cpp @@ -2370,7 +2370,7 @@ std::vector Character::mutation_attacks( Creature &t ) const const body_part_set usable_body_parts = exclusive_flag_coverage( flag_ALLOWS_NATURAL_ATTACKS ); const int unarmed = get_skill_level( skill_unarmed ); - for( const trait_id &pr : get_mutations() ) { + for( const trait_id &pr : get_functioning_mutations() ) { const mutation_branch &branch = pr.obj(); for( const mut_attack &mut_atk : branch.attacks_granted ) { // Covered body part diff --git a/src/memorial_logger.cpp b/src/memorial_logger.cpp index 6850385ceea96..eaf19f545c48c 100644 --- a/src/memorial_logger.cpp +++ b/src/memorial_logger.cpp @@ -328,10 +328,10 @@ void memorial_logger::write_text_memorial( std::ostream &file, //Traits file << _( "Traits:" ) << eol; - for( const trait_id &mut : u.get_mutations() ) { + for( const trait_id &mut : u.get_functioning_mutations() ) { file << indent << u.mutation_name( mut ) << eol; } - if( u.get_mutations().empty() ) { + if( u.get_functioning_mutations().empty() ) { file << indent << _( "(None)" ) << eol; } file << eol; diff --git a/src/monster.cpp b/src/monster.cpp index 175bfd95695b8..cf4b3671cfee7 100644 --- a/src/monster.cpp +++ b/src/monster.cpp @@ -1726,7 +1726,7 @@ monster_attitude monster::attitude( const Character *u ) const } } - for( const trait_id &mut : u->get_mutations() ) { + for( const trait_id &mut : u->get_functioning_mutations() ) { const mutation_branch &branch = *mut; if( branch.ignored_by.empty() && branch.anger_relations.empty() ) { continue; diff --git a/src/mood_face.cpp b/src/mood_face.cpp index 02c7773682551..0f6968e7990f2 100644 --- a/src/mood_face.cpp +++ b/src/mood_face.cpp @@ -79,7 +79,7 @@ const mood_face_id &avatar::character_mood_face( bool clear_cache ) const mood_face_horizontal = option_horizontal; std::string face_type; - for( const trait_id &mut : get_mutations() ) { + for( const trait_id &mut : get_functioning_mutations() ) { if( !mut->threshold ) { continue; } diff --git a/src/mutation.cpp b/src/mutation.cpp index abf819a605d5b..c0cd1b8f95ee7 100644 --- a/src/mutation.cpp +++ b/src/mutation.cpp @@ -115,15 +115,24 @@ std::string enum_to_string( mutagen_technique data ) } // namespace io -bool Character::has_trait( const trait_id &b ) const +bool Character::has_trait( const trait_id &b, bool ignore_disabled ) const { - return my_mutations.count( b ) || enchantment_cache->get_mutations().count( b ); + auto mutit = cached_mutations.find( b ); + if( mutit != cached_mutations.end() ) { + return ignore_disabled ? mutit->second.corrupted == 0 : true; + } + return false; +} + +bool Character::has_permanent_trait( const trait_id &b ) const +{ + return my_mutations.count( b ); } bool Character::has_trait_variant( const trait_and_var &test ) const { - auto mutit = my_mutations.find( test.trait ); - if( mutit != my_mutations.end() ) { + auto mutit = cached_mutations.find( test.trait ); + if( mutit != cached_mutations.end() ) { if( mutit->second.variant != nullptr ) { return mutit->second.variant->id == test.variant; } else { @@ -141,7 +150,7 @@ bool Character::has_trait_flag( const json_character_flag &b ) const return iter->second; } - for( const trait_id &mut : get_mutations() ) { + for( const trait_id &mut : get_functioning_mutations() ) { const mutation_branch &mut_data = mut.obj(); if( mut_data.flags.count( b ) > 0 ) { trait_flag_cache[b] = true; @@ -164,7 +173,7 @@ int Character::count_trait_flag( const json_character_flag &b ) const { int ret = 0; // UGLY, SLOW, should be cached as my_mutation_flags or something - for( const trait_id &mut : get_mutations() ) { + for( const trait_id &mut : get_functioning_mutations() ) { const mutation_branch &mut_data = mut.obj(); if( mut_data.flags.count( b ) > 0 ) { ret++; @@ -191,7 +200,7 @@ int Character::get_instability_per_category( const mutation_category_id &categ ) int mut_count = 0; bool robust = has_flag( json_flag_ROBUST_GENETIC ); // For each and every trait we have... - for( const trait_id &mut : get_mutations() ) { + for( const trait_id &mut : get_mutations( true, true ) ) { // only count muts that have 0 or more points, aren't a threshold, have a category, and aren't a base trait. if( mut.obj().points > -1 && !mut.obj().threshold && !mut.obj().category.empty() && !has_base_trait( mut ) ) { @@ -233,7 +242,7 @@ int Character::get_total_in_category_char_has( const mutation_category_id &categ int mut_count = 0; std::vector list = get_in_category( categ, count_type ); for( const trait_id mut : list ) { - if( has_trait( mut ) ) { + if( has_permanent_trait( mut ) ) { mut_count++; } } @@ -305,12 +314,12 @@ void Character::set_mutation_unsafe( const trait_id &trait, const mutation_varia variant = trait->pick_variant(); } my_mutations.emplace( trait, trait_data{variant} ); - cached_mutations.push_back( &trait.obj() ); - if( !trait.obj().vanity ) { - mutation_effect( trait, false ); + my_mutations_dirty.emplace( trait, trait_data{variant} ); + if( !trait.obj().enchantments.empty() ) { + recalculate_enchantment_cache(); + } else { + update_cached_mutations(); } - - trait_flag_cache.clear(); } void Character::do_mutation_updates() @@ -329,20 +338,21 @@ void Character::set_mutations( const std::vector &traits ) for( const trait_id &trait : traits ) { set_mutation_unsafe( trait ); } - do_mutation_updates(); } void Character::set_mutation( const trait_id &trait, const mutation_variant *variant ) { set_mutation_unsafe( trait, variant ); - do_mutation_updates(); } void Character::set_mut_variant( const trait_id &trait, const mutation_variant *variant ) { - auto mutit = my_mutations.find( trait ); - if( mutit != my_mutations.end() ) { + auto mutit = cached_mutations.find( trait ); + if( mutit != cached_mutations.end() ) { mutit->second.variant = variant; + if( my_mutations.count( trait ) ) { + my_mutations[trait].variant = variant; + } trait_flag_cache.clear(); } } @@ -361,14 +371,15 @@ void Character::unset_mutation( const trait_id &trait_ ) return; } const mutation_branch &mut = *trait; - cached_mutations.erase( std::remove( cached_mutations.begin(), cached_mutations.end(), &mut ), - cached_mutations.end() ); my_mutations.erase( iter ); - if( !mut.vanity ) { - mutation_loss_effect( trait ); + auto exists = find( old_mutation_cache->mutations.begin(), old_mutation_cache->mutations.end(), trait_ ); + if( exists == old_mutation_cache->mutations.end() ) { + cached_mutations.erase( trait_ ); + if( !mut.enchantments.empty() ) { + recalculate_enchantment_cache(); + mutation_loss_effect( trait ); + } } - trait_flag_cache.clear(); - do_mutation_updates(); } void Character::switch_mutations( const trait_id &switched, const trait_id &target, @@ -382,7 +393,7 @@ void Character::switch_mutations( const trait_id &switched, const trait_id &targ set_mutation( target ); } if( has_trait( target ) ) { - my_mutations[target].powered = start_powered; + cached_mutations[target].powered = start_powered; } trait_flag_cache.clear(); } @@ -547,11 +558,15 @@ void Character::recalculate_size() void Character::mutation_effect( const trait_id &mut, const bool worn_destroyed_override ) { + if( mut.obj().vanity ) { + return; + } + if( mut == trait_GLASSJAW ) { recalc_hp(); } reset(); - + trait_flag_cache.clear(); recalculate_size(); const mutation_branch &branch = mut.obj(); @@ -619,19 +634,25 @@ void Character::mutation_effect( const trait_id &mut, const bool worn_destroyed_ } if( branch.starts_active ) { - my_mutations[mut].powered = true; + cached_mutations[mut].powered = true; + if( my_mutations.count( mut ) ) { + my_mutations[mut].powered = true; + } } - trait_flag_cache.clear(); on_mutation_gain( mut ); } void Character::mutation_loss_effect( const trait_id &mut ) { + if( mut.obj().vanity ) { + return; + } + if( mut == trait_GLASSJAW ) { recalc_hp(); } reset(); - + trait_flag_cache.clear(); recalculate_size(); const mutation_branch &branch = mut.obj(); @@ -643,7 +664,6 @@ void Character::mutation_loss_effect( const trait_id &mut ) } if( !branch.enchantments.empty() ) { - recalculate_enchantment_cache(); recalculate_bodyparts(); } @@ -651,22 +671,21 @@ void Character::mutation_loss_effect( const trait_id &mut ) remove_moncam( moncam.first ); } - trait_flag_cache.clear(); on_mutation_loss( mut ); } bool Character::has_active_mutation( const trait_id &b ) const { - const auto iter = my_mutations.find( b ); - return iter != my_mutations.end() && iter->second.powered; + const auto iter = cached_mutations.find( b ); + return iter != cached_mutations.end() && iter->second.powered; } time_duration Character::get_cost_timer( const trait_id &mut ) const { - const auto iter = my_mutations.find( mut ); - const std::vector &all_mut = get_mutations(); + const auto iter = cached_mutations.find( mut ); + const std::vector &all_mut = get_functioning_mutations(); const auto all_iter = std::find( all_mut.begin(), all_mut.end(), mut ); - if( iter != my_mutations.end() ) { + if( iter != cached_mutations.end() ) { return iter->second.charge; } else if( all_iter == all_mut.end() ) { // dont have the mutation personally and can't find it in enchantments (this shouldn't happen) @@ -678,10 +697,10 @@ time_duration Character::get_cost_timer( const trait_id &mut ) const void Character::set_cost_timer( const trait_id &mut, time_duration set ) { - const auto iter = my_mutations.find( mut ); - const std::vector &all_mut = get_mutations(); + const auto iter = cached_mutations.find( mut ); + const std::vector &all_mut = get_functioning_mutations(); const auto all_iter = std::find( all_mut.begin(), all_mut.end(), mut ); - if( iter != my_mutations.end() ) { + if( iter != cached_mutations.end() ) { iter->second.charge = set; trait_flag_cache.clear(); } else if( all_iter == all_mut.end() ) { @@ -699,7 +718,7 @@ bool Character::is_category_allowed( const std::vector &ca { bool allowed = false; bool restricted = false; - for( const trait_id &mut : get_mutations() ) { + for( const trait_id &mut : get_mutations( true, true ) ) { if( !mut.obj().allowed_category.empty() ) { restricted = true; } @@ -722,7 +741,7 @@ bool Character::is_category_allowed( const mutation_category_id &category ) cons { bool allowed = false; bool restricted = false; - for( const trait_id &mut : get_mutations() ) { + for( const trait_id &mut : get_mutations( true, true ) ) { for( const mutation_category_id &Ch_cat : mut.obj().allowed_category ) { restricted = true; if( Ch_cat == category ) { @@ -743,7 +762,7 @@ 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() ) { + for( const trait_id &mut : get_functioning_mutations() ) { if( !mut.obj().can_only_heal_with.empty() ) { got_restriction = true; } @@ -757,7 +776,7 @@ bool Character::can_use_heal_item( const item &med ) const } if( !can_use ) { - for( const trait_id &mut : get_mutations() ) { + for( const trait_id &mut : get_functioning_mutations() ) { if( mut.obj().can_heal_with.count( heal_id ) ) { can_use = true; break; @@ -771,7 +790,7 @@ 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 : get_functioning_mutations() ) { for( const bodypart_id &bp : bps ) { if( mut.obj().no_cbm_on_bp.count( bp.id() ) ) { can_install = false; @@ -783,9 +802,17 @@ bool Character::can_install_cbm_on_bp( const std::vector &bps ) con } void Character::activate_mutation( const trait_id &mut ) +{ + activate_cached_mutation( mut ); + if( my_mutations.count( mut ) ) { + my_mutations[mut] = cached_mutations[mut]; + } +} + +void Character::activate_cached_mutation( const trait_id &mut ) { const mutation_branch &mdata = mut.obj(); - trait_data &tdata = my_mutations[mut]; + trait_data &tdata = cached_mutations[mut]; int cost = mdata.cost; // You can take yourself halfway to Near Death levels of hunger/thirst. // Sleepiness can go to Exhausted. @@ -967,7 +994,7 @@ void Character::activate_mutation( const trait_id &mut ) void Character::deactivate_mutation( const trait_id &mut ) { - my_mutations[mut].powered = false; + cached_mutations[mut].powered = false; trait_flag_cache.clear(); recalc_sight_limits(); @@ -1000,7 +1027,7 @@ void Character::deactivate_mutation( const trait_id &mut ) trait_id Character::trait_by_invlet( const int ch ) const { - for( const std::pair &mut : my_mutations ) { + for( const std::pair &mut : cached_mutations ) { if( mut.second.key == ch ) { return mut.first; } @@ -1030,7 +1057,7 @@ bool Character::mutation_ok( const trait_id &mutation, bool allow_good, bool all if( mutation_branch::trait_is_blacklisted( mutation ) ) { return false; } - if( has_trait( mutation ) || has_child_flag( mutation ) ) { + if( has_permanent_trait( mutation ) || has_child_flag( mutation ) ) { // We already have this mutation or something that replaces it. add_msg_debug( debugmode::DF_MUTATION, "mutation_ok( %s ): failed, trait or child already present", mutation.c_str() ); @@ -1183,7 +1210,7 @@ void Character::mutate( const int &true_random_chance, bool use_vitamins ) // ...those we don't have are valid. if( base_mdata.valid && is_category_allowed( base_mdata.category ) && - !has_trait( base_mutation ) && !base_mdata.dummy ) { + !has_permanent_trait( base_mutation ) && !base_mdata.dummy ) { add_msg_debug( debugmode::DF_MUTATION, "mutate: trait %s added to valid trait list", base_mdata.id.c_str() ); valid.push_back( base_mdata.id ); @@ -1197,7 +1224,7 @@ void Character::mutate( const int &true_random_chance, bool use_vitamins ) } // ...for those that we have... - if( has_trait( base_mutation ) ) { + if( has_permanent_trait( base_mutation ) ) { // ...consider the mutations that replace it. for( const trait_id &mutation : base_mdata.replacements ) { @@ -1404,7 +1431,7 @@ bool Character::mutation_selector( const std::vector &prospective_trai std::vector prereqs1 = mdata.prereqs; bool c_has_prereq1 = prereqs1.empty() ? true : false; for( size_t i = 0; !c_has_prereq1 && i < prereqs1.size(); i++ ) { - if( has_trait( prereqs1[i] ) ) { + if( has_permanent_trait( prereqs1[i] ) ) { c_has_prereq1 = true; } } @@ -1416,7 +1443,7 @@ bool Character::mutation_selector( const std::vector &prospective_trai std::vector prereqs2 = mdata.prereqs2; bool c_has_prereq2 = prereqs2.empty() ? true : false; for( size_t i = 0; !c_has_prereq2 && i < prereqs2.size(); i++ ) { - if( has_trait( prereqs2[i] ) ) { + if( has_permanent_trait( prereqs2[i] ) ) { c_has_prereq2 = true; } } @@ -1428,7 +1455,7 @@ bool Character::mutation_selector( const std::vector &prospective_trai std::vector threshreq = mdata.threshreq; bool c_has_threshreq = threshreq.empty() ? true : false; for( size_t i = 0; !c_has_threshreq && i < threshreq.size(); i++ ) { - if( has_trait( threshreq[i] ) ) { + if( has_permanent_trait( threshreq[i] ) ) { c_has_threshreq = true; } } @@ -1538,7 +1565,7 @@ bool Character::mutate_towards( const trait_id &mut, const mutation_category_id std::vector cancel_recheck = cancel; for( size_t i = 0; i < cancel.size(); i++ ) { - if( !has_trait( cancel[i] ) ) { + if( !has_permanent_trait( cancel[i] ) ) { cancel.erase( cancel.begin() + i ); i--; } else if( !purifiable( cancel[i] ) ) { @@ -1581,7 +1608,7 @@ bool Character::mutate_towards( const trait_id &mut, const mutation_category_id //If we still have anything to cancel, we aren't done pruning, but should stop for now for( const trait_id &trait : cancel_recheck ) { - if( has_trait( trait ) ) { + if( has_permanent_trait( trait ) ) { add_msg_debug( debugmode::DF_MUTATION, "mutate_towards: bailed out on cancel_recheck because of trait %s still existing", trait.c_str() ); return true; @@ -1598,13 +1625,13 @@ bool Character::mutate_towards( const trait_id &mut, const mutation_category_id } for( size_t i = 0; ( !c_has_prereq1 ) && i < prereqs1.size(); i++ ) { - if( has_trait( prereqs1[i] ) ) { + if( has_permanent_trait( prereqs1[i] ) ) { c_has_prereq1 = true; } } for( size_t i = 0; ( !c_has_prereq2 ) && i < prereqs2.size(); i++ ) { - if( has_trait( prereqs2[i] ) ) { + if( has_permanent_trait( prereqs2[i] ) ) { c_has_prereq2 = true; } } @@ -1684,13 +1711,13 @@ bool Character::mutate_towards( const trait_id &mut, const mutation_category_id } for( size_t i = 0; !c_has_threshreq && i < threshreq.size(); i++ ) { - if( has_trait( threshreq[i] ) ) { + if( has_permanent_trait( threshreq[i] ) ) { add_msg_debug( debugmode::DF_MUTATION, "mutate_towards: necessary threshold %s found", threshreq[i].c_str() ); c_has_threshreq = true; } for( const trait_id &subst : threshreq[i]->threshold_substitutes ) { - if( has_trait( subst ) ) { + if( has_permanent_trait( subst ) ) { add_msg_debug( debugmode::DF_MUTATION, "mutate_towards: substitute threshold %s found", subst.c_str() ); if( mdata.strict_threshreq ) { @@ -1738,7 +1765,7 @@ bool Character::mutate_towards( const trait_id &mut, const mutation_category_id trait_id replacing = trait_id::NULL_ID(); prereqs1 = mdata.prereqs; // Reset it for( auto &elem : prereqs1 ) { - if( has_trait( elem ) ) { + if( has_permanent_trait( elem ) ) { const trait_id &pre = elem; const mutation_branch &p = pre.obj(); for( size_t j = 0; !replacing && j < p.replacements.size(); j++ ) { @@ -1755,7 +1782,7 @@ bool Character::mutate_towards( const trait_id &mut, const mutation_category_id trait_id replacing2 = trait_id::NULL_ID(); prereqs1 = mdata.prereqs2; // Reset it for( auto &elem : prereqs1 ) { - if( has_trait( elem ) ) { + if( has_permanent_trait( elem ) ) { const trait_id &pre2 = elem; const mutation_branch &p = pre2.obj(); for( size_t j = 0; !replacing2 && j < p.replacements.size(); j++ ) { @@ -1953,13 +1980,13 @@ std::unordered_set Character::get_lower_traits( const trait_id &flag ) { std::unordered_set traits; for( const trait_id &i : flag->prereqs ) { - if( has_trait( i ) ) { + if( has_permanent_trait( i ) ) { traits.insert( i ); } traits = traits << ( get_lower_traits( i ) ); } for( const trait_id &i : flag->prereqs2 ) { - if( has_trait( i ) ) { + if( has_permanent_trait( i ) ) { traits.insert( i ); } traits = traits << ( get_lower_traits( i ) ); @@ -1976,7 +2003,7 @@ std::unordered_set Character::get_replacement_traits( const trait_id & { std::unordered_set traits; for( const trait_id &i : flag->replacements ) { - if( has_trait( i ) ) { + if( has_permanent_trait( i ) ) { traits.insert( i ); } else { traits = traits << ( get_replacement_traits( i ) ); @@ -1994,7 +2021,7 @@ std::unordered_set Character::get_addition_traits( const trait_id &fla { std::unordered_set traits; for( const trait_id &i : flag->additions ) { - if( has_trait( i ) ) { + if( has_permanent_trait( i ) ) { traits.insert( i ); } else { traits = traits << ( get_addition_traits( i ) ); @@ -2012,7 +2039,7 @@ std::unordered_set Character::get_same_type_traits( const trait_id &fl { std::unordered_set traits; for( auto &i : get_mutations_in_types( flag->types ) ) { - if( has_trait( i ) && flag != i ) { + if( has_permanent_trait( i ) && flag != i ) { traits.insert( i ); } } @@ -2083,7 +2110,7 @@ void Character::remove_mutation( const trait_id &mut, bool silent ) //Check each mutation until we reach the end or find a trait to revert to for( const mutation_branch &iter : mutation_branch::get_all() ) { //See if it's in our list of base traits but not active - if( has_base_trait( iter.id ) && !has_trait( iter.id ) ) { + if( has_base_trait( iter.id ) && !has_permanent_trait( iter.id ) ) { //See if that base trait cancels the mutation we are using std::vector traitcheck = iter.cancels; for( size_t j = 0; !replacing && j < traitcheck.size(); j++ ) { @@ -2103,7 +2130,7 @@ void Character::remove_mutation( const trait_id &mut, bool silent ) //Check each mutation until we reach the end or find a trait to revert to for( const mutation_branch &iter : mutation_branch::get_all() ) { //See if it's in our list of base traits but not active - if( has_base_trait( iter.id ) && !has_trait( iter.id ) ) { + if( has_base_trait( iter.id ) && !has_permanent_trait( iter.id ) ) { //See if that base trait cancels the mutation we are using std::vector traitcheck = iter.cancels; for( size_t j = 0; !replacing2 && j < traitcheck.size(); j++ ) { @@ -2255,7 +2282,7 @@ bool Character::has_child_flag( const trait_id &flag ) const { for( const trait_id &elem : flag->replacements ) { const trait_id &tmp = elem; - if( has_trait( tmp ) || has_child_flag( tmp ) ) { + if( has_permanent_trait( tmp ) || has_child_flag( tmp ) ) { return true; } } @@ -2266,7 +2293,7 @@ void Character::remove_child_flag( const trait_id &flag ) { for( const auto &elem : flag->replacements ) { const trait_id &tmp = elem; - if( has_trait( tmp ) ) { + if( has_permanent_trait( tmp ) ) { remove_mutation( tmp ); return; } else if( has_child_flag( tmp ) ) { @@ -2380,7 +2407,7 @@ void Character::give_all_mutations( const mutation_category_trait &category, void Character::unset_all_mutations() { - for( const trait_id &mut : get_mutations() ) { + for( const trait_id &mut : get_mutations( true, true ) ) { get_event_bus().send( getID(), mut ); unset_mutation( mut ); } @@ -2388,8 +2415,8 @@ void Character::unset_all_mutations() std::string Character::mutation_name( const trait_id &mut ) const { - auto it = my_mutations.find( mut ); - if( it != my_mutations.end() && it->second.variant != nullptr ) { + auto it = cached_mutations.find( mut ); + if( it != cached_mutations.end() && it->second.variant != nullptr ) { return mut->name( it->second.variant->id ); } @@ -2398,8 +2425,8 @@ std::string Character::mutation_name( const trait_id &mut ) const std::string Character::mutation_desc( const trait_id &mut ) const { - auto it = my_mutations.find( mut ); - if( it != my_mutations.end() && it->second.variant != nullptr ) { + auto it = cached_mutations.find( mut ); + if( it != cached_mutations.end() && it->second.variant != nullptr ) { return mut->desc( it->second.variant->id ); } @@ -2468,7 +2495,7 @@ void Character::customize_appearance( customize_appearance_choice choice ) amenu.query(); if( amenu.ret >= 0 ) { const trait_id &trait_selected = traits[amenu.ret]; - if( has_trait( current_trait ) ) { + if( has_permanent_trait( current_trait ) ) { remove_mutation( current_trait ); } if( !trait_selected->variants.empty() ) { @@ -2485,7 +2512,7 @@ void Character::customize_appearance( customize_appearance_choice choice ) std::string Character::visible_mutations( const int visibility_cap ) const { - const std::vector &my_muts = get_mutations(); + const std::vector &my_muts = get_functioning_mutations(); return enumerate_as_string( my_muts.begin(), my_muts.end(), [this, visibility_cap ]( const trait_id & pr ) -> std::string { const mutation_branch &mut_branch = pr.obj(); diff --git a/src/mutation_ui.cpp b/src/mutation_ui.cpp index 3502c5212404e..5d00cefdeb0c6 100644 --- a/src/mutation_ui.cpp +++ b/src/mutation_ui.cpp @@ -101,7 +101,10 @@ void avatar::power_mutations() { std::vector passive; std::vector active; - for( std::pair &mut : my_mutations ) { + for( std::pair &mut : cached_mutations ) { + if( mut.second.corrupted > 0 ) { + continue; + } if( !mut.first->player_display ) { continue; } @@ -226,10 +229,10 @@ void avatar::power_mutations() ctxt.register_action( "QUIT" ); #if defined(__ANDROID__) for( const auto &p : passive ) { - ctxt.register_manual_key( my_mutations[p].key, p.obj().name() ); + ctxt.register_manual_key( cached_mutations[p].key, p.obj().name() ); } for( const auto &a : active ) { - ctxt.register_manual_key( my_mutations[a].key, a.obj().name() ); + ctxt.register_manual_key( cached_mutations[a].key, a.obj().name() ); } #endif @@ -259,7 +262,7 @@ void avatar::power_mutations() } else { for( int i = scroll_position; static_cast( i ) < passive.size(); i++ ) { const mutation_branch &md = passive[i].obj(); - const trait_data &td = my_mutations[passive[i]]; + const trait_data &td = cached_mutations[passive[i]]; const bool is_highlighted = cursor == static_cast( i ); if( i - scroll_position == list_height ) { break; @@ -283,7 +286,7 @@ void avatar::power_mutations() } else { for( int i = scroll_position; static_cast( i ) < active.size(); i++ ) { const mutation_branch &md = active[i].obj(); - const trait_data &td = my_mutations[active[i]]; + const trait_data &td = cached_mutations[active[i]]; const bool is_highlighted = cursor == static_cast( i ); if( i - scroll_position == list_height ) { break; @@ -408,9 +411,9 @@ void avatar::power_mutations() if( mutation_chars.valid( newch ) ) { const trait_id other_mut_id = trait_by_invlet( newch ); if( !other_mut_id.is_null() ) { - std::swap( my_mutations[mut_id].key, my_mutations[other_mut_id].key ); + std::swap( cached_mutations[mut_id].key, cached_mutations[other_mut_id].key ); } else { - my_mutations[mut_id].key = newch; + cached_mutations[mut_id].key = newch; } pop_exit = true; pop_handled = true; @@ -434,7 +437,7 @@ void avatar::power_mutations() } case mutation_menu_mode::activating: { if( mut_data.activated ) { - if( my_mutations[mut_id].powered ) { + if( cached_mutations[mut_id].powered ) { add_msg_if_player( m_neutral, _( "You stop using your %s." ), mutation_name( mut_data.id ) ); // Reset menu in advance ui.reset(); @@ -458,7 +461,7 @@ void avatar::power_mutations() } else { popup( _( "You cannot activate %1$s! To read a description of " "%1$s, press '%2$s', then '%3$c'." ), - mutation_name( mut_data.id ), ctxt.get_desc( "TOGGLE_EXAMINE" ), my_mutations[mut_id].key ); + mutation_name( mut_data.id ), ctxt.get_desc( "TOGGLE_EXAMINE" ), cached_mutations[mut_id].key ); } break; } @@ -467,7 +470,7 @@ void avatar::power_mutations() examine_id = mut_id; break; case mutation_menu_mode::hiding: - my_mutations[mut_id].show_sprite = !my_mutations[mut_id].show_sprite; + cached_mutations[mut_id].show_sprite = !cached_mutations[mut_id].show_sprite; break; } handled = true; @@ -579,14 +582,14 @@ void avatar::power_mutations() if( mutation_chars.valid( newch ) ) { const trait_id other_mut_id = trait_by_invlet( newch ); if( !other_mut_id.is_null() ) { - std::swap( my_mutations[mut_id].key, my_mutations[other_mut_id].key ); + std::swap( cached_mutations[mut_id].key, cached_mutations[other_mut_id].key ); } else { - my_mutations[mut_id].key = newch; + cached_mutations[mut_id].key = newch; } pop_exit = true; pop_handled = true; } else if( newch == ' ' ) { - my_mutations[mut_id].key = newch; + cached_mutations[mut_id].key = newch; pop_exit = true; pop_handled = true; } @@ -609,7 +612,7 @@ void avatar::power_mutations() } case mutation_menu_mode::activating: { if( mut_data.activated ) { - if( my_mutations[mut_id].powered ) { + if( cached_mutations[mut_id].powered ) { add_msg_if_player( m_neutral, _( "You stop using your %s." ), mutation_name( mut_data.id ) ); // Reset menu in advance ui.reset(); @@ -642,7 +645,7 @@ void avatar::power_mutations() examine_id = mut_id; break; case mutation_menu_mode::hiding: - my_mutations[mut_id].show_sprite = !my_mutations[mut_id].show_sprite; + cached_mutations[mut_id].show_sprite = !cached_mutations[mut_id].show_sprite; break; } } diff --git a/src/newcharacter.cpp b/src/newcharacter.cpp index a7c6029b0f4f0..4b17c912b9092 100644 --- a/src/newcharacter.cpp +++ b/src/newcharacter.cpp @@ -996,7 +996,7 @@ void Character::initialize( bool learn_recipes ) for( const trait_id &mut : get_mutations() ) { const mutation_branch &branch = mut.obj(); if( branch.starts_active ) { - my_mutations[mut].powered = true; + cached_mutations[mut].powered = true; } } trait_flag_cache.clear(); @@ -4772,7 +4772,7 @@ std::vector Character::get_mutations( bool include_hidden, bool ignore_enchantments, const std::function &filter ) const { std::vector result; - result.reserve( my_mutations.size() + enchantment_cache->get_mutations().size() ); + result.reserve( my_mutations.size() + old_mutation_cache->get_mutations().size() ); for( const std::pair &t : my_mutations ) { const mutation_branch &mut = t.first.obj(); if( include_hidden || mut.player_display ) { @@ -4782,7 +4782,7 @@ std::vector Character::get_mutations( bool include_hidden, } } if( !ignore_enchantments ) { - for( const trait_id &ench_trait : enchantment_cache->get_mutations() ) { + for( const trait_id &ench_trait : old_mutation_cache->get_mutations() ) { if( include_hidden || ench_trait->player_display ) { bool found = false; for( const trait_id &exist : result ) { @@ -4802,30 +4802,36 @@ std::vector Character::get_mutations( bool include_hidden, return result; } +std::vector Character::get_functioning_mutations( bool include_hidden, + bool ignore_enchantments, const std::function &filter ) const +{ + std::vector result; + auto &test = ignore_enchantments ? my_mutations : cached_mutations; + result.reserve( test.size() ); + for( const std::pair &t : test ) { + if( t.second.corrupted == 0 ) { + const mutation_branch &mut = t.first.obj(); + if( include_hidden || mut.player_display ) { + if( filter == nullptr || filter( mut ) ) { + result.push_back( t.first ); + } + } + } + } + return result; +} + std::vector Character::get_mutations_variants( bool include_hidden, bool ignore_enchantments ) const { std::vector result; - result.reserve( my_mutations.size() + enchantment_cache->get_mutations().size() ); - for( const std::pair &t : my_mutations ) { - if( include_hidden || t.first.obj().player_display ) { - const std::string &variant = t.second.variant != nullptr ? t.second.variant->id : ""; - result.emplace_back( t.first, variant ); - } - } - if( !ignore_enchantments ) { - for( const trait_id &ench_trait : enchantment_cache->get_mutations() ) { - if( include_hidden || ench_trait->player_display ) { - bool found = false; - for( const trait_and_var &exist : result ) { - if( exist.trait == ench_trait ) { - found = true; - break; - } - } - if( !found ) { - result.emplace_back( ench_trait, "" ); - } + auto &test = ignore_enchantments ? my_mutations : cached_mutations; + result.reserve( test.size() ); + for( const std::pair &t : test ) { + if( t.second.corrupted == 0 ) { + if( include_hidden || t.first.obj().player_display ) { + const std::string &variant = t.second.variant != nullptr ? t.second.variant->id : ""; + result.emplace_back( t.first, variant ); } } } @@ -4838,11 +4844,17 @@ void Character::clear_mutations() my_traits.erase( *my_traits.begin() ); } while( !my_mutations.empty() ) { - const trait_id trait = my_mutations.begin()->first; my_mutations.erase( my_mutations.begin() ); + } + while( !my_mutations_dirty.empty() ) { + my_mutations_dirty.erase( my_mutations_dirty.begin() ); + } + while( !cached_mutations.empty() ) { + const trait_id trait = cached_mutations.begin()->first; + cached_mutations.erase( cached_mutations.begin() ); mutation_loss_effect( trait ); } - cached_mutations.clear(); + old_mutation_cache->clear(); recalc_sight_limits(); calc_encumbrance(); } diff --git a/src/npc.cpp b/src/npc.cpp index 9426d4c7af9b6..9de99ed9c810b 100644 --- a/src/npc.cpp +++ b/src/npc.cpp @@ -737,7 +737,7 @@ void npc::catchup_skills() void npc::clear_personality_traits() { - for( const trait_id &trait : get_mutations() ) { + for( const trait_id &trait : get_functioning_mutations() ) { if( trait.obj().personality_score ) { unset_mutation( trait ); } @@ -773,7 +773,7 @@ void npc::randomize_personality() void npc::learn_ma_styles_from_traits() { - for( const trait_id &iter : get_mutations() ) { + for( const trait_id &iter : get_functioning_mutations() ) { if( !iter->initial_ma_styles.empty() ) { std::vector shuffled_trait_styles = iter->initial_ma_styles; std::shuffle( shuffled_trait_styles.begin(), shuffled_trait_styles.end(), rng_get_engine() ); @@ -1655,7 +1655,7 @@ npc_opinion npc::get_opinion_values( const Character &you ) const } int u_ugly = 0; - for( trait_id &mut : you.get_mutations() ) { + for( trait_id &mut : you.get_functioning_mutations() ) { u_ugly += mut.obj().ugliness; } for( const bodypart_id &bp : you.get_all_body_parts() ) { diff --git a/src/player_difficulty.cpp b/src/player_difficulty.cpp index f280f8d5600cd..89a164c97bf7d 100644 --- a/src/player_difficulty.cpp +++ b/src/player_difficulty.cpp @@ -39,7 +39,7 @@ void player_difficulty::npc_from_avatar( const avatar &u, npc &dummy ) dummy.hobbies = u.hobbies; // set mutations - for( const trait_id &t : u.get_mutations( true ) ) { + for( const trait_id &t : u.get_functioning_mutations( true ) ) { dummy.set_mutation( t ); } diff --git a/src/savegame_json.cpp b/src/savegame_json.cpp index 16530138ab7f5..e29904024a691 100644 --- a/src/savegame_json.cpp +++ b/src/savegame_json.cpp @@ -535,6 +535,7 @@ void effect_source::deserialize( const JsonObject &data ) void Character::trait_data::serialize( JsonOut &json ) const { json.start_object(); + json.member( "corrupted", corrupted ); json.member( "key", key ); json.member( "charge", charge ); json.member( "powered", powered ); @@ -549,6 +550,7 @@ void Character::trait_data::serialize( JsonOut &json ) const void Character::trait_data::deserialize( const JsonObject &data ) { data.allow_omitted_members(); + data.read( "corrupted", corrupted ); data.read( "key", key ); data.read( "charge", charge ); data.read( "powered", powered ); @@ -818,13 +820,41 @@ void Character::load( const JsonObject &data ) for( const std::pair &add : muts_to_add ) { my_mutations.emplace( add.first, add.second ); } - // We need to ensure that my_mutations contains no invalid mutations before we do this + + data.read( "cached_mutations", cached_mutations ); + + std::map caches_to_add; + for( auto it = cached_mutations.begin(); it != cached_mutations.end(); ) { + const trait_id &mid = it->first; + if( mid.is_valid() ) { + ++it; + continue; + } + + const trait_replacement &rules = mutation_branch::trait_migration( mid ); + if( rules.prof ) { + add_proficiency( *rules.prof ); + } else if( rules.trait ) { + const trait_id &added = rules.trait->trait; + const std::string &added_var = rules.trait->variant; + auto add_it = caches_to_add.emplace( added, it->second ).first; + add_it->second.variant = added->variant( added_var ); + } else { + if( rules.error ) { + debugmsg( "character %s has invalid mutation %s, it will be ignored", get_name(), mid.str() ); + } + } + it = cached_mutations.erase( it ); + } + for( const std::pair &add : caches_to_add ) { + cached_mutations.emplace( add.first, add.second ); + on_mutation_gain( add.first ); + } + // We need to ensure that cached_mutations contains no invalid mutations before we do this // As every time we add a mutation, we rebuild the enchantment cache, causing errors if // we have invalid mutations. - for( const std::pair &mut : my_mutations ) { - on_mutation_gain( mut.first ); - cached_mutations.push_back( &mut.first.obj() ); - } + recalculate_enchantment_cache(); + recalculate_size(); data.read( "my_bionics", *my_bionics ); @@ -1275,7 +1305,6 @@ void Character::load( const JsonObject &data ) queued_effect_on_conditions.push( temp ); } data.read( "inactive_eocs", inactive_effect_on_condition_vector ); - update_enchantment_mutations(); } /** @@ -1371,6 +1400,7 @@ void Character::store( JsonOut &json ) const // traits: permanent 'mutations' more or less json.member( "traits", my_traits ); json.member( "mutations", my_mutations ); + json.member( "cached_mutations", cached_mutations ); json.member( "moncams", moncams ); json.member( "magic", magic ); json.member( "martial_arts_data", martial_arts_data ); diff --git a/src/suffer.cpp b/src/suffer.cpp index ce290f1c640ad..a2070295b40f1 100644 --- a/src/suffer.cpp +++ b/src/suffer.cpp @@ -1680,7 +1680,7 @@ void Character::suffer() process_bionic( bio ); } - for( const trait_id &mut_id : get_mutations() ) { + for( const trait_id &mut_id : get_functioning_mutations() ) { if( calendar::once_every( 1_seconds ) && enchantment_cache->modify_value( enchant_vals::mod::WEAKNESS_TO_WATER, 0 ) != 0 ) { diff --git a/src/talker_npc.cpp b/src/talker_npc.cpp index 9ff75f8758984..0cbdb3c4f4166 100644 --- a/src/talker_npc.cpp +++ b/src/talker_npc.cpp @@ -699,7 +699,7 @@ std::string talker_npc_const::view_personality_traits() const std::string assessment = "&"; assessment += _( " seems to be:" ); bool found_personality_trait = false; - for( const auto &trait_data_pairs : me_npc->my_mutations ) { + for( const auto &trait_data_pairs : me_npc->cached_mutations ) { const mutation_branch &mdata = trait_data_pairs.first.obj(); if( mdata.personality_score ) { found_personality_trait = true; diff --git a/src/visitable.cpp b/src/visitable.cpp index 760bdcc9fef96..8994664f040a4 100644 --- a/src/visitable.cpp +++ b/src/visitable.cpp @@ -274,7 +274,7 @@ int Character::max_quality( const quality_id &qual ) const } if( qual == qual_BUTCHER ) { - for( const trait_id &mut : get_mutations() ) { + for( const trait_id &mut : get_functioning_mutations() ) { res = std::max( res, mut->butchering_quality ); } } diff --git a/src/wish.cpp b/src/wish.cpp index c211692bdb5cb..f8dac0ded4990 100644 --- a/src/wish.cpp +++ b/src/wish.cpp @@ -279,27 +279,27 @@ void debug_menu::wishmutate( Character *you ) const bool profession = mdata.profession; // Manual override for the threshold-gaining if( threshold || profession ) { - if( you->has_trait( mstr ) ) { + if( you->has_permanent_trait( mstr ) ) { do { you->remove_mutation( mstr ); rc++; - } while( you->has_trait( mstr ) && rc < 10 ); + } while( you->has_permanent_trait( mstr ) && rc < 10 ); } else { do { you->set_mutation( mstr ); rc++; - } while( !you->has_trait( mstr ) && rc < 10 ); + } while( !you->has_permanent_trait( mstr ) && rc < 10 ); } - } else if( you->has_trait( mstr ) ) { + } else if( you->has_permanent_trait( mstr ) ) { do { you->remove_mutation( mstr ); rc++; - } while( you->has_trait( mstr ) && rc < 10 ); + } while( you->has_permanent_trait( mstr ) && rc < 10 ); } else { do { you->mutate_towards( mstr ); rc++; - } while( !you->has_trait( mstr ) && rc < 10 ); + } while( !you->has_permanent_trait( mstr ) && rc < 10 ); } cb.msg = string_format( _( "%s Mutation changes: %d" ), mstr.c_str(), rc ); uistate.wishmutate_selected = wmenu.selected; diff --git a/tests/consumption_time_test.cpp b/tests/consumption_time_test.cpp index b1e435de7b188..b11b722255d02 100644 --- a/tests/consumption_time_test.cpp +++ b/tests/consumption_time_test.cpp @@ -7,7 +7,7 @@ TEST_CASE( "characters_with_no_mutations_take_at_least_1_second_to_consume_comes { GIVEN( "a character with no mutations and a comestible" ) { avatar character; - REQUIRE( character.my_mutations.empty() ); + REQUIRE( character.cached_mutations.empty() ); item mustard( "mustard" ); REQUIRE( mustard.is_comestible() ); From 83bd4573a3ac54eb0a071960bceb2bca91ee4a64 Mon Sep 17 00:00:00 2001 From: osuphobia Date: Fri, 20 Dec 2024 01:14:46 +0800 Subject: [PATCH 3/7] Remove redundants due to #74994. --- data/mods/Magiclysm/mutations/magical.json | 52 ++--------- .../Xedra_Evolved/mutations/mutations.json | 15 +--- .../mutations/shapeshifters.json | 15 +--- .../Xedra_Evolved/mutations/temporary.json | 89 ++----------------- 4 files changed, 17 insertions(+), 154 deletions(-) diff --git a/data/mods/Magiclysm/mutations/magical.json b/data/mods/Magiclysm/mutations/magical.json index 692cc6c69f0ad..3b921036bc39f 100644 --- a/data/mods/Magiclysm/mutations/magical.json +++ b/data/mods/Magiclysm/mutations/magical.json @@ -26,7 +26,6 @@ "starting_trait": false, "purifiable": false, "player_display": false, - "//": "The second enchantment takes into account the effects of the mutations added by the first enchantment, whose own enchantments do not transfer over due to bug #74984. If that bug is fixed, it can be deleted.", "enchantments": [ { "condition": "ALWAYS", @@ -43,7 +42,6 @@ ], "mutations": [ "FANGS", "MUZZLE_BEAR", "TAIL_STUB", "PAWS_LARGE", "URSINE_FUR", "URSINE_EARS", "PRED3" ] }, - { "condition": "ALWAYS", "values": [ { "value": "HEARING_MULT", "multiply": 0.25 } ] }, { "condition": { "and": [ { "u_has_flag": "QUADRUPED_CROUCH" }, { "u_has_flag": "QUADRUPED_RUN" }, { "not": "u_can_drop_weapon" } ] }, "values": [ { "value": "MOVE_COST", "multiply": -0.15 } ], @@ -55,19 +53,8 @@ "values": [ { "value": "MELEE_DAMAGE", "multiply": -1 }, { "value": "RANGE", "multiply": -1 } ] } ], - "flags": [ - "MUTE", - "MYOPIC_IN_LIGHT", - "NO_SPELLCASTING", - "PRED3", - "QUADRUPED_CROUCH", - "QUADRUPED_RUN", - "TOUGH_FEET", - "TEMPORARY_SHAPESHIFT", - "SHAPESHIFT_SIZE_HUGE" - ], - "override_look": { "id": "mon_bear", "tile_category": "monster" }, - "integrated_armor": [ "integrated_claws_st", "integrated_fangs", "integrated_ursine_fur" ] + "flags": [ "MUTE", "NO_SPELLCASTING", "QUADRUPED_RUN", "TOUGH_FEET", "TEMPORARY_SHAPESHIFT", "SHAPESHIFT_SIZE_HUGE" ], + "override_look": { "id": "mon_bear", "tile_category": "monster" } }, { "type": "mutation", @@ -96,7 +83,6 @@ "purifiable": false, "valid": false, "player_display": false, - "//": "The second enchantment takes into account the effects of the mutations added by the first enchantment, whose own enchantments do not transfer over due to bug #74984. If that bug is fixed, it can be deleted.", "enchantments": [ { "condition": "ALWAYS", @@ -113,14 +99,6 @@ ], "mutations": [ "FANGS", "TAIL_LONG", "PAWS", "FELINE_FUR", "FELINE_EARS", "PRED3", "WHISKERS", "FELINE_LEAP" ] }, - { - "condition": "ALWAYS", - "values": [ - { "value": "HEARING_MULT", "multiply": 0.25 }, - { "value": "COMBAT_CATCHUP", "multiply": 2 }, - { "value": "DODGE_CHANCE", "add": 5 } - ] - }, { "condition": { "and": [ { "u_has_flag": "QUADRUPED_CROUCH" }, { "u_has_flag": "QUADRUPED_RUN" }, { "not": "u_can_drop_weapon" } ] }, "values": [ { "value": "MOVE_COST", "multiply": -0.15 } ], @@ -132,9 +110,8 @@ "values": [ { "value": "MELEE_DAMAGE", "multiply": -1 }, { "value": "RANGE", "multiply": -1 } ] } ], - "flags": [ "MUTE", "NO_SPELLCASTING", "PRED3", "QUADRUPED_CROUCH", "QUADRUPED_RUN", "TOUGH_FEET", "TEMPORARY_SHAPESHIFT" ], - "override_look": { "id": "mon_cougar", "tile_category": "monster" }, - "integrated_armor": [ "integrated_fangs", "integrated_feline_fur", "integrated_claws" ] + "flags": [ "MUTE", "NO_SPELLCASTING", "QUADRUPED_RUN", "TOUGH_FEET", "TEMPORARY_SHAPESHIFT" ], + "override_look": { "id": "mon_cougar", "tile_category": "monster" } }, { "type": "mutation", @@ -187,15 +164,8 @@ "values": [ { "value": "MELEE_DAMAGE", "multiply": -1 }, { "value": "RANGE", "multiply": -1 } ] } ], - "attacks": { - "attack_text_u": "You kick %s with your hooves!", - "attack_text_npc": "%1$s kicks %2$s with their hooves!", - "chance": 15, - "strength_damage": { "damage_type": "bash", "amount": 3 } - }, - "flags": [ "MUTE", "NO_SPELLCASTING", "QUADRUPED_CROUCH", "QUADRUPED_RUN", "TOUGH_FEET", "TEMPORARY_SHAPESHIFT" ], - "override_look": { "id": "mon_deer", "tile_category": "monster" }, - "integrated_armor": [ "integrated_antlers", "integrated_feline_fur" ] + "flags": [ "MUTE", "NO_SPELLCASTING", "QUADRUPED_RUN", "TOUGH_FEET", "TEMPORARY_SHAPESHIFT" ], + "override_look": { "id": "mon_deer", "tile_category": "monster" } }, { "type": "mutation", @@ -293,7 +263,6 @@ "purifiable": false, "valid": false, "player_display": false, - "//": "The second enchantment, as well as bodytemp modifiers, takes into account the effects of the mutations added by the first enchantment, whose own enchantments do not transfer over due to bug #74984. If that bug is fixed, it can be deleted.", "bodytemp_modifiers": [ 300, 800 ], "enchantments": [ { @@ -311,15 +280,6 @@ "skills": [ { "value": "dodge", "add": 6 } ], "mutations": [ "BIRD_EYE", "DOWN", "EAGLEEYED", "LIGHTEATER" ] }, - { - "condition": "ALWAYS", - "values": [ - { "value": "PERCEPTION", "add": 4 }, - { "value": "BODYTEMP_SLEEP", "add": 0.5 }, - { "value": "OVERMAP_SIGHT", "add": 4 }, - { "value": "METABOLISM", "multiply": -0.333 } - ] - }, { "condition": "u_has_weapon", "values": [ { "value": "MELEE_DAMAGE", "multiply": -1 }, { "value": "RANGE", "multiply": -1 } ] diff --git a/data/mods/Xedra_Evolved/mutations/mutations.json b/data/mods/Xedra_Evolved/mutations/mutations.json index cd195beb375d6..c70214424d7d3 100644 --- a/data/mods/Xedra_Evolved/mutations/mutations.json +++ b/data/mods/Xedra_Evolved/mutations/mutations.json @@ -685,7 +685,6 @@ "purifiable": false, "valid": false, "player_display": false, - "//": "The second enchantment takes into account the effects of the mutations added by the first enchantment, whose own enchantments do not transfer over due to bug #74994. If that bug is fixed, it can be deleted.", "enchantments": [ { "condition": "ALWAYS", @@ -699,15 +698,6 @@ ], "mutations": [ "FANGS", "MUZZLE", "TAIL_FLUFFY", "PAWS", "LUPINE_FUR", "LUPINE_EARS", "PERSISTENCE_HUNTER2", "PRED3" ] }, - { - "condition": "ALWAYS", - "values": [ - { "value": "STAMINA_REGEN_MOD", "add": 0.2 }, - { "value": "HEARING_MULT", "multiply": 0.75 }, - { "value": "COMBAT_CATCHUP", "multiply": 2 }, - { "value": "DODGE_CHANCE", "add": 4 } - ] - }, { "condition": { "and": [ { "not": "u_can_drop_weapon" } ] }, "values": [ { "value": "MOVE_COST", "multiply": -0.15 } ], @@ -719,9 +709,8 @@ "values": [ { "value": "MELEE_DAMAGE", "multiply": -1 }, { "value": "RANGE", "multiply": -1 } ] } ], - "flags": [ "MUTE", "PRED3", "QUADRUPED_CROUCH", "QUADRUPED_RUN", "TOUGH_FEET", "TEMPORARY_SHAPESHIFT" ], - "override_look": { "id": "mon_wolf", "tile_category": "monster" }, - "integrated_armor": [ "integrated_fangs", "integrated_lupine_fur" ] + "flags": [ "MUTE", "QUADRUPED_RUN", "TOUGH_FEET", "TEMPORARY_SHAPESHIFT" ], + "override_look": { "id": "mon_wolf", "tile_category": "monster" } }, { "type": "mutation", diff --git a/data/mods/Xedra_Evolved/mutations/shapeshifters.json b/data/mods/Xedra_Evolved/mutations/shapeshifters.json index 441642cf49b85..6ea5278ce29c6 100644 --- a/data/mods/Xedra_Evolved/mutations/shapeshifters.json +++ b/data/mods/Xedra_Evolved/mutations/shapeshifters.json @@ -87,7 +87,6 @@ "starting_trait": false, "purifiable": false, "player_display": false, - "//": "The second enchantment takes into account the effects of the mutations added by the first enchantment, whose own enchantments do not transfer over due to bug #74994. If that bug is fixed, it can be deleted.", "enchantments": [ { "condition": "ALWAYS", @@ -108,23 +107,13 @@ ], "mutations": [ "MUZZLE", "TAIL_FLUFFY", "PAWS", "LUPINE_FUR", "LUPINE_EARS", "PERSISTENCE_HUNTER2", "PRED3" ] }, - { - "condition": "ALWAYS", - "values": [ - { "value": "STAMINA_REGEN_MOD", "add": 0.2 }, - { "value": "HEARING_MULT", "multiply": 0.75 }, - { "value": "COMBAT_CATCHUP", "multiply": 2 }, - { "value": "DODGE_CHANCE", "add": 4 } - ] - }, { "condition": { "and": [ { "not": "is_day" }, { "math": [ "moon_phase() == 4" ] } ] }, "values": [ { "value": "DEXTERITY", "add": 2 }, { "value": "STRENGTH", "add": 2 }, { "value": "SPEED", "multiply": 0.05 } ] } ], - "flags": [ "NO_SPELLCASTING", "PRED3", "TOUGH_FEET", "TEMPORARY_SHAPESHIFT", "SHAPESHIFT_SIZE_HUGE" ], - "override_look": { "id": "mon_loup_garou", "tile_category": "monster" }, - "integrated_armor": [ "integrated_claws_werewolf", "integrated_werewolf_teeth", "integrated_lupine_fur" ] + "flags": [ "NO_SPELLCASTING", "TOUGH_FEET", "TEMPORARY_SHAPESHIFT", "SHAPESHIFT_SIZE_HUGE" ], + "override_look": { "id": "mon_loup_garou", "tile_category": "monster" } }, { "type": "effect_on_condition", diff --git a/data/mods/Xedra_Evolved/mutations/temporary.json b/data/mods/Xedra_Evolved/mutations/temporary.json index 4f88eb2c0d16d..aed221401a367 100644 --- a/data/mods/Xedra_Evolved/mutations/temporary.json +++ b/data/mods/Xedra_Evolved/mutations/temporary.json @@ -149,7 +149,6 @@ "starting_trait": false, "purifiable": false, "player_display": false, - "//": "The second enchantment takes into account the effects of the mutations added by the first enchantment, whose own enchantments do not transfer over due to bug #74994. If that bug is fixed, it can be deleted.", "enchantments": [ { "condition": "ALWAYS", @@ -166,7 +165,6 @@ ], "mutations": [ "FANGS", "MUZZLE_BEAR", "TAIL_STUB", "PAWS_LARGE", "URSINE_FUR", "URSINE_EARS", "PRED3" ] }, - { "condition": "ALWAYS", "values": [ { "value": "HEARING_MULT", "multiply": 0.25 } ] }, { "condition": { "and": [ { "u_has_flag": "QUADRUPED_CROUCH" }, { "u_has_flag": "QUADRUPED_RUN" }, { "not": "u_can_drop_weapon" } ] }, "values": [ { "value": "MOVE_COST", "multiply": -0.15 } ], @@ -178,19 +176,8 @@ "values": [ { "value": "MELEE_DAMAGE", "multiply": -1 }, { "value": "RANGE", "multiply": -1 } ] } ], - "flags": [ - "MUTE", - "MYOPIC_IN_LIGHT", - "NO_SPELLCASTING", - "PRED3", - "QUADRUPED_CROUCH", - "QUADRUPED_RUN", - "TOUGH_FEET", - "TEMPORARY_SHAPESHIFT", - "SHAPESHIFT_SIZE_HUGE" - ], - "override_look": { "id": "mon_bear", "tile_category": "monster" }, - "integrated_armor": [ "integrated_claws_st", "integrated_fangs", "integrated_ursine_fur" ] + "flags": [ "MUTE", "NO_SPELLCASTING", "QUADRUPED_RUN", "TOUGH_FEET", "TEMPORARY_SHAPESHIFT", "SHAPESHIFT_SIZE_HUGE" ], + "override_look": { "id": "mon_bear", "tile_category": "monster" } }, { "type": "mutation", @@ -229,15 +216,8 @@ "values": [ { "value": "MELEE_DAMAGE", "multiply": -1 }, { "value": "RANGE", "multiply": -1 } ] } ], - "attacks": { - "attack_text_u": "You kick %s with your hooves!", - "attack_text_npc": "%1$s kicks %2$s with their hooves!", - "chance": 15, - "strength_damage": { "damage_type": "bash", "amount": 3 } - }, - "flags": [ "MUTE", "NO_SPELLCASTING", "QUADRUPED_CROUCH", "QUADRUPED_RUN", "TOUGH_FEET", "TEMPORARY_SHAPESHIFT" ], - "override_look": { "id": "mon_deer", "tile_category": "monster" }, - "integrated_armor": [ "integrated_antlers", "integrated_feline_fur" ] + "flags": [ "MUTE", "NO_SPELLCASTING", "QUADRUPED_RUN", "TOUGH_FEET", "TEMPORARY_SHAPESHIFT" ], + "override_look": { "id": "mon_deer", "tile_category": "monster" } }, { "type": "mutation", @@ -252,7 +232,6 @@ "purifiable": false, "valid": false, "player_display": false, - "//": "The second enchantment takes into account the effects of the mutations added by the first enchantment, whose own enchantments do not transfer over due to bug #74994. If that bug is fixed, it can be deleted.", "enchantments": [ { "condition": "ALWAYS", @@ -269,14 +248,6 @@ ], "mutations": [ "FANGS", "TAIL_LONG", "PAWS", "FELINE_FUR", "FELINE_EARS", "PRED3", "WHISKERS", "FELINE_LEAP" ] }, - { - "condition": "ALWAYS", - "values": [ - { "value": "HEARING_MULT", "multiply": 0.25 }, - { "value": "COMBAT_CATCHUP", "multiply": 2 }, - { "value": "DODGE_CHANCE", "add": 5 } - ] - }, { "condition": { "and": [ { "u_has_flag": "QUADRUPED_CROUCH" }, { "u_has_flag": "QUADRUPED_RUN" }, { "not": "u_can_drop_weapon" } ] }, "values": [ { "value": "MOVE_COST", "multiply": -0.15 } ], @@ -288,9 +259,8 @@ "values": [ { "value": "MELEE_DAMAGE", "multiply": -1 }, { "value": "RANGE", "multiply": -1 } ] } ], - "flags": [ "MUTE", "NO_SPELLCASTING", "PRED3", "QUADRUPED_CROUCH", "QUADRUPED_RUN", "TOUGH_FEET", "TEMPORARY_SHAPESHIFT" ], - "override_look": { "id": "mon_cougar", "tile_category": "monster" }, - "integrated_armor": [ "integrated_fangs", "integrated_feline_fur", "integrated_claws" ] + "flags": [ "MUTE", "NO_SPELLCASTING", "QUADRUPED_RUN", "TOUGH_FEET", "TEMPORARY_SHAPESHIFT" ], + "override_look": { "id": "mon_cougar", "tile_category": "monster" } }, { "type": "mutation", @@ -305,7 +275,6 @@ "purifiable": false, "valid": false, "player_display": false, - "//": "The second enchantment, as well as bodytemp modifiers, takes into account the effects of the mutations added by the first enchantment, whose own enchantments do not transfer over due to bug #74994. If that bug is fixed, it can be deleted.", "bodytemp_modifiers": [ 500, 1200 ], "enchantments": [ { @@ -323,15 +292,6 @@ "skills": [ { "value": "dodge", "add": 4 } ], "mutations": [ "BIRD_EYE", "DOWN", "EAGLEEYED", "LIGHTEATER" ] }, - { - "condition": "ALWAYS", - "values": [ - { "value": "PERCEPTION", "add": 4 }, - { "value": "BODYTEMP_SLEEP", "add": 0.5 }, - { "value": "OVERMAP_SIGHT", "add": 4 }, - { "value": "METABOLISM", "multiply": -0.333 } - ] - }, { "condition": "u_has_weapon", "values": [ { "value": "MELEE_DAMAGE", "multiply": -1 }, { "value": "RANGE", "multiply": -1 } ] @@ -360,7 +320,6 @@ "purifiable": false, "valid": false, "player_display": false, - "//": "The second enchantment, as well as bodytemp modifiers, takes into account the effects of the mutations added by the first enchantment, whose own enchantments do not transfer over due to bug #74994. If that bug is fixed, it can be deleted.", "bodytemp_modifiers": [ 500, 1200 ], "enchantments": [ { @@ -376,33 +335,9 @@ ], "skills": [ { "value": "dodge", "add": 4 }, { "value": "unarmed", "add": 1 } ], "mutations": [ "BEAK", "BIRD_EYE", "BIRD_LEGS", "DOWN", "EAGLEEYED", "HOLLOW_BONES", "LIGHTEATER", "SCREECH", "WINGS_BIRD" ] - }, - { - "condition": "ALWAYS", - "incoming_damage_mod_post_absorbed": [ { "type": "bash", "multiply": 0.8 } ], - "values": [ - { "value": "PERCEPTION", "add": 4 }, - { "value": "BODYTEMP_SLEEP", "add": 0.5 }, - { "value": "OVERMAP_SIGHT", "add": 4 }, - { "value": "METABOLISM", "multiply": -0.2 }, - { "value": "MOVE_COST", "multiply": -0.2 }, - { "value": "ATTACK_SPEED", "multiply": -0.2 }, - { "value": "CARRY_WEIGHT", "multiply": -0.1 }, - { "value": "SOCIAL_INTIMIDATE", "add": 10 }, - { "value": "SOCIAL_LIE", "add": -20 }, - { "value": "SOCIAL_PERSUADE", "add": -10 } - ] } ], - "attacks": { - "attack_text_u": "You rip into %s with your beak!", - "attack_text_npc": "%1$s rips into %2$s with their beak!", - "body_part": "mouth", - "chance": 15, - "strength_damage": { "damage_type": "cut", "amount": 3 } - }, - "flags": [ "TEMPORARY_SHAPESHIFT", "SHAPESHIFT_SIZE_LARGE", "WINGS_2", "WING_GLIDE" ], - "integrated_armor": [ "integrated_feathers", "integrated_talons" ] + "flags": [ "TEMPORARY_SHAPESHIFT", "SHAPESHIFT_SIZE_LARGE" ] }, { "type": "mutation", @@ -417,7 +352,6 @@ "purifiable": false, "valid": false, "player_display": false, - "//": "The second enchantment, as well as bodytemp modifiers, takes into account the effects of the mutations added by the first enchantment, whose own enchantments do not transfer over due to bug #74994. If that bug is fixed, it can be deleted.", "bodytemp_modifiers": [ 300, 800 ], "enchantments": [ { @@ -435,15 +369,6 @@ "skills": [ { "value": "dodge", "add": 6 } ], "mutations": [ "BIRD_EYE", "DOWN", "EAGLEEYED", "LIGHTEATER" ] }, - { - "condition": "ALWAYS", - "values": [ - { "value": "PERCEPTION", "add": 4 }, - { "value": "BODYTEMP_SLEEP", "add": 0.5 }, - { "value": "OVERMAP_SIGHT", "add": 4 }, - { "value": "METABOLISM", "multiply": -0.333 } - ] - }, { "condition": "u_has_weapon", "values": [ { "value": "MELEE_DAMAGE", "multiply": -1 }, { "value": "RANGE", "multiply": -1 } ] From 1af0dd8e976ed3df78839fade787a1fca6a16708 Mon Sep 17 00:00:00 2001 From: osuphobia <78858975+osuphobia@users.noreply.github.com> Date: Fri, 20 Dec 2024 01:55:24 +0800 Subject: [PATCH 4/7] Apply suggestions from code review astyle Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- src/character.cpp | 4 ++-- src/mutation.cpp | 4 ++-- src/savegame_json.cpp | 1 - 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/character.cpp b/src/character.cpp index 426ff59e4db0b..08dae7700cf3d 100644 --- a/src/character.cpp +++ b/src/character.cpp @@ -7819,7 +7819,7 @@ void Character::update_cached_mutations() if( it == current_traits.end() ) { for( auto &iter : cached_mutations ) { if( ( are_opposite_traits( iter.first, mut ) || b_is_higher_trait_of_a( iter.first, mut ) - || are_same_type_traits( iter.first, mut ) ) && iter.second.corrupted > 0 ) { + || are_same_type_traits( iter.first, mut ) ) && iter.second.corrupted > 0 ) { --iter.second.corrupted; if( my_mutations.count( mut ) ) { --my_mutations[mut].corrupted; @@ -7841,7 +7841,7 @@ void Character::update_cached_mutations() || are_same_type_traits( iter.first, mut ) ) { ++iter.second.corrupted; if( my_mutations.count( mut ) ) { - ++my_mutations[mut].corrupted; + ++my_mutations[mut].corrupted; } if( iter.second.corrupted == 1 ) { mutation_loss_effect( mut ); diff --git a/src/mutation.cpp b/src/mutation.cpp index c0cd1b8f95ee7..dce64c59e8928 100644 --- a/src/mutation.cpp +++ b/src/mutation.cpp @@ -372,7 +372,8 @@ void Character::unset_mutation( const trait_id &trait_ ) } const mutation_branch &mut = *trait; my_mutations.erase( iter ); - auto exists = find( old_mutation_cache->mutations.begin(), old_mutation_cache->mutations.end(), trait_ ); + auto exists = find( old_mutation_cache->mutations.begin(), old_mutation_cache->mutations.end(), + trait_ ); if( exists == old_mutation_cache->mutations.end() ) { cached_mutations.erase( trait_ ); if( !mut.enchantments.empty() ) { @@ -561,7 +562,6 @@ void Character::mutation_effect( const trait_id &mut, const bool worn_destroyed_ if( mut.obj().vanity ) { return; } - if( mut == trait_GLASSJAW ) { recalc_hp(); } diff --git a/src/savegame_json.cpp b/src/savegame_json.cpp index e29904024a691..9c519a3be229e 100644 --- a/src/savegame_json.cpp +++ b/src/savegame_json.cpp @@ -854,7 +854,6 @@ void Character::load( const JsonObject &data ) // As every time we add a mutation, we rebuild the enchantment cache, causing errors if // we have invalid mutations. recalculate_enchantment_cache(); - recalculate_size(); data.read( "my_bionics", *my_bionics ); From 66a481ebcf6a5cfa8f63c6ac2284ba1d41d9382f Mon Sep 17 00:00:00 2001 From: osuphobia Date: Fri, 20 Dec 2024 02:17:08 +0800 Subject: [PATCH 5/7] Fix overloaded-virtual --- src/character.h | 2 +- src/mutation.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/character.h b/src/character.h index ad95cc80a2926..b91a286edcd6c 100644 --- a/src/character.h +++ b/src/character.h @@ -1377,7 +1377,7 @@ class Character : public Creature, public visitable /** Returns a dream's description selected randomly from the player's highest mutation category */ std::string get_category_dream( const mutation_category_id &cat, int strength ) const; /** Returns true if the player has the entered trait in cached_mutations */ - bool has_trait( const trait_id &b, bool ignore_disabled = true ) const; + bool has_trait( const trait_id &b ) const override; /** Returns true if the player has the entered trait with the desired variant */ bool has_trait_variant( const trait_and_var & ) const; /** Returns true if the player has the entered trait in my_mutations */ diff --git a/src/mutation.cpp b/src/mutation.cpp index dce64c59e8928..20c5383172da1 100644 --- a/src/mutation.cpp +++ b/src/mutation.cpp @@ -115,11 +115,11 @@ std::string enum_to_string( mutagen_technique data ) } // namespace io -bool Character::has_trait( const trait_id &b, bool ignore_disabled ) const +bool Character::has_trait( const trait_id &b ) const { auto mutit = cached_mutations.find( b ); if( mutit != cached_mutations.end() ) { - return ignore_disabled ? mutit->second.corrupted == 0 : true; + return mutit->second.corrupted == 0; } return false; } From a291de1877d9e6c76cd2d2598cfbadceede8854a Mon Sep 17 00:00:00 2001 From: osuphobia <78858975+osuphobia@users.noreply.github.com> Date: Fri, 20 Dec 2024 08:47:31 +0800 Subject: [PATCH 6/7] clang-tidy --- src/magic_enchantment.cpp | 2 +- src/magic_enchantment.h | 2 +- src/newcharacter.cpp | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/magic_enchantment.cpp b/src/magic_enchantment.cpp index 8eaff6e332de9..46f1bd6a39a14 100644 --- a/src/magic_enchantment.cpp +++ b/src/magic_enchantment.cpp @@ -777,7 +777,7 @@ bool enchant_cache::add( const enchant_cache &rhs ) return true; } -void enchant_cache::force_add_mutation( const enchantment &rhs, const Character &guy ) +void enchant_cache::force_add_mutation( const enchantment &rhs ) { for( const trait_id &branch : rhs.mutations ) { mutations.push_back( branch ); diff --git a/src/magic_enchantment.h b/src/magic_enchantment.h index a63c8b93d59f3..6ffc374ed0c9c 100644 --- a/src/magic_enchantment.h +++ b/src/magic_enchantment.h @@ -304,7 +304,7 @@ class enchant_cache : public enchantment void force_add_with_dialogue( const enchantment &rhs, const const_dialogue &d, bool evaluate = true ); // adds enchantment mutations to the cache - void force_add_mutation( const enchantment &rhs, const Character &guy ); + void force_add_mutation( const enchantment &rhs ); // modifies character stats, or does other passive effects void activate_passive( Character &guy ) const; diff --git a/src/newcharacter.cpp b/src/newcharacter.cpp index 4b17c912b9092..cac92e9d95bf8 100644 --- a/src/newcharacter.cpp +++ b/src/newcharacter.cpp @@ -4806,7 +4806,7 @@ std::vector Character::get_functioning_mutations( bool include_hidden, bool ignore_enchantments, const std::function &filter ) const { std::vector result; - auto &test = ignore_enchantments ? my_mutations : cached_mutations; + const auto &test = ignore_enchantments ? my_mutations : cached_mutations; result.reserve( test.size() ); for( const std::pair &t : test ) { if( t.second.corrupted == 0 ) { @@ -4825,7 +4825,7 @@ std::vector Character::get_mutations_variants( bool include_hidde bool ignore_enchantments ) const { std::vector result; - auto &test = ignore_enchantments ? my_mutations : cached_mutations; + const auto &test = ignore_enchantments ? my_mutations : cached_mutations; result.reserve( test.size() ); for( const std::pair &t : test ) { if( t.second.corrupted == 0 ) { From 8e9b8ae58cd82c44d239dc51849a269e373fc836 Mon Sep 17 00:00:00 2001 From: osuphobia <78858975+osuphobia@users.noreply.github.com> Date: Fri, 20 Dec 2024 08:49:13 +0800 Subject: [PATCH 7/7] clang-tidy --- src/character.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/character.cpp b/src/character.cpp index 08dae7700cf3d..5bb800933b8a8 100644 --- a/src/character.cpp +++ b/src/character.cpp @@ -7723,7 +7723,7 @@ void Character::recalculate_enchantment_cache() for( const enchantment_id &ench_id : mut.enchantments ) { const enchantment &ench = ench_id.obj(); if( ench.is_active( *this, mut.activated && mut_map.second.powered ) ) { - enchantment_cache->force_add_mutation( ench, *this ); + enchantment_cache->force_add_mutation( ench ); } } }