From 910b2188d0c9449808e6831836a2326f72e4415d Mon Sep 17 00:00:00 2001 From: Coolthulhu Date: Sun, 18 Jun 2023 14:01:37 +0200 Subject: [PATCH] Move wielded item data to body part (#2864) * Move wielded item to body part * Remove unused --- src/avatar.cpp | 4 +-- src/bionics.cpp | 4 +-- src/bodypart.h | 11 +++++++ src/character.cpp | 53 ++++++++++++++++++++++---------- src/character.h | 8 +++-- src/character_functions.cpp | 4 +-- src/character_turn.cpp | 1 + src/consumption.cpp | 2 +- src/crafting.cpp | 2 +- src/creature.cpp | 31 +++++++++++-------- src/creature.h | 4 +-- src/debug_menu.cpp | 4 +-- src/magic_spell_effect.cpp | 2 +- src/mattack_actors.cpp | 6 +++- src/mattack_actors.h | 1 + src/mattack_common.h | 1 + src/melee.cpp | 44 +++++++++++++++++++++----- src/monattack.cpp | 6 ++-- src/monstergenerator.cpp | 6 ++++ src/newcharacter.cpp | 7 +++-- src/npc.cpp | 29 ++++++++--------- src/player.cpp | 4 ++- src/player_hardcoded_effects.cpp | 4 +-- src/savegame_json.cpp | 5 +-- src/suffer.cpp | 4 ++- 25 files changed, 169 insertions(+), 78 deletions(-) diff --git a/src/avatar.cpp b/src/avatar.cpp index b3f39b239825..d8c18ac98453 100644 --- a/src/avatar.cpp +++ b/src/avatar.cpp @@ -1237,9 +1237,9 @@ bool avatar::wield( item &target ) moves -= mv; if( has_item( target ) ) { - primary_weapon() = i_rem( &target ); + set_primary_weapon( i_rem( &target ) ); } else { - primary_weapon() = target; + set_primary_weapon( target ); } last_item = primary_weapon().typeId(); diff --git a/src/bionics.cpp b/src/bionics.cpp index 41aeb7775044..eaa5a7ae01f2 100644 --- a/src/bionics.cpp +++ b/src/bionics.cpp @@ -669,7 +669,7 @@ bool Character::activate_bionic( bionic &bio, bool eff_only ) } add_msg_activate(); - primary_weapon() = item( bio.info().fake_item ); + set_primary_weapon( item( bio.info().fake_item ) ); primary_weapon().invlet = '#'; if( is_player() && bio.ammo_count > 0 ) { primary_weapon().ammo_set( bio.ammo_loaded, bio.ammo_count ); @@ -1152,7 +1152,7 @@ bool Character::deactivate_bionic( bionic &bio, bool eff_only ) primary_weapon().ammo_data() != nullptr ? primary_weapon().ammo_data()->get_id() : itype_id::NULL_ID(); bio.ammo_count = static_cast( primary_weapon().ammo_remaining() ); - primary_weapon() = item(); + set_primary_weapon( item() ); invalidate_crafting_inventory(); } } else if( bio.id == bio_cqb ) { diff --git a/src/bodypart.h b/src/bodypart.h index 33d3c6627051..6761c13746a2 100644 --- a/src/bodypart.h +++ b/src/bodypart.h @@ -15,6 +15,7 @@ class JsonObject; class JsonIn; class JsonOut; +class item; struct body_part_type; template struct enum_traits; @@ -153,6 +154,12 @@ struct body_part_type { int bionic_slots_ = 0; }; +class wield_status +{ + public: + std::shared_ptr wielded; +}; + class bodypart { private: @@ -166,6 +173,10 @@ class bodypart int damage_bandaged = 0; int damage_disinfected = 0; + public: + // TODO: private + wield_status wielding; + public: bodypart(): id( bodypart_str_id( "num_bp" ) ), hp_cur( 0 ), hp_max( 0 ) {} bodypart( bodypart_str_id id ): id( id ), hp_cur( id->base_hp ), hp_max( id->base_hp ) {} diff --git a/src/character.cpp b/src/character.cpp index ccf4658f3139..bf4b3b2a26ad 100644 --- a/src/character.cpp +++ b/src/character.cpp @@ -437,6 +437,7 @@ Character::Character() : set_stim( 0 ); set_stamina( 10000 ); //Temporary value for stamina. It will be reset later from external json option. set_anatomy( anatomy_id("human_anatomy") ); + set_body(); update_type_of_scent( true ); pkill = 0; stored_calories = max_stored_kcal() - 100; @@ -1010,6 +1011,7 @@ void Character::mount_creature( monster &z ) } add_msg_if_player( m_good, _( "You hear your %s whir to life." ), z.get_name() ); } + add_msg_if_player( m_good, _( "You hear your %s whir to life." ), z.get_name() ); } // some rideable mechs have night-vision recalc_sight_limits(); @@ -1088,7 +1090,7 @@ void Character::forced_dismount() auto mon = mounted_creature.get(); if( mon->has_flag( MF_RIDEABLE_MECH ) && !mon->type->mech_weapon.is_empty() ) { mech = true; - remove_item( weapon ); + remove_item( primary_weapon() ); } mon->mounted_player_id = character_id(); mon->remove_effect( effect_ridden ); @@ -1193,6 +1195,7 @@ void Character::dismount() remove_effect( effect_riding ); monster *critter = mounted_creature.get(); critter->mounted_player_id = character_id(); + item &weapon = primary_weapon(); if( critter->has_flag( MF_RIDEABLE_MECH ) && !critter->type->mech_weapon.is_empty() && weapon.typeId() == critter->type->mech_weapon ) { remove_item( weapon ); @@ -1594,7 +1597,7 @@ void Character::recalc_hp() void Character::calc_all_parts_hp( float hp_mod, float hp_adjustment, int str_max ) { for( const std::pair &part : get_body() ) { - bodypart &bp = *get_part( part.first ); + bodypart &bp = get_part( part.first ); int new_max = ( part.first->base_hp + str_max * 3 + hp_adjustment ) * hp_mod; if( has_trait( trait_id( "GLASSJAW" ) ) && part.first == bodypart_str_id( "head" ) ) { @@ -2327,7 +2330,7 @@ item *Character::invlet_to_item( const int linvlet ) const item &Character::i_at( int position ) const { if( position == -1 ) { - return weapon; + return primary_weapon(); } if( position < -1 ) { int worn_index = worn_position_to_index( position ); @@ -2348,6 +2351,7 @@ item &Character::i_at( int position ) int Character::get_item_position( const item *it ) const { + const item &weapon = primary_weapon(); if( weapon.has_item( *it ) ) { return -1; } @@ -2367,8 +2371,8 @@ item Character::i_rem( int pos ) { item tmp; if( pos == -1 ) { - tmp = weapon; - weapon = item(); + tmp = primary_weapon(); + set_primary_weapon( item() ); return tmp; } else if( pos < -1 && pos > worn_position_to_index( worn.size() ) ) { auto iter = worn.begin(); @@ -2489,6 +2493,7 @@ invlets_bitset Character::allocated_invlets() const { invlets_bitset invlets = inv.allocated_invlets(); + const item &weapon = primary_weapon(); invlets.set( weapon.invlet ); for( const auto &w : worn ) { invlets.set( w.invlet ); @@ -2508,8 +2513,8 @@ bool Character::has_active_item( const itype_id &id ) const item Character::remove_weapon() { - item tmp = weapon; - weapon = item(); + item tmp = primary_weapon(); + set_primary_weapon( item() ); clear_npc_ai_info_cache( npc_ai_info::ideal_weapon_value ); return tmp; } @@ -2575,6 +2580,7 @@ units::mass Character::weight_carried_reduced_by( const excluded_stacks &without // Wielded item units::mass weaponweight = 0_gram; + const item &weapon = primary_weapon(); auto weapon_it = without.find( &weapon ); if( weapon_it == without.end() ) { weaponweight = weapon.weight(); @@ -2916,6 +2922,7 @@ ret_val Character::can_wear( const item &it, bool with_equip_change ) cons } // Check if we don't have both hands available before wearing a briefcase, shield, etc. Also occurs if we're already wearing one. + const item &weapon = primary_weapon(); if( it.has_flag( flag_RESTRICT_HANDS ) && ( worn_with_flag( flag_RESTRICT_HANDS ) || weapon.is_two_handed( *this ) ) ) { return ret_val::make_failure( ( is_player() ? _( "You don't have a hand free to wear that." ) @@ -2986,6 +2993,7 @@ Character::wear_possessed( item &to_wear, bool interactive ) bool was_weapon; item to_wear_copy( to_wear ); + item &weapon = primary_weapon(); if( &to_wear == &weapon ) { weapon = item(); was_weapon = true; @@ -3087,6 +3095,7 @@ ret_val Character::can_wield( const item &it ) const _( "You need at least one arm to even consider wielding something." ) ); } + const item &weapon = primary_weapon(); if( is_armed() && weapon.has_flag( "NO_UNWIELD" ) ) { return ret_val::make_failure( _( "The %s is preventing you from wielding the %s." ), character_funcs::fmt_wielded_weapon( *this ), it.tname() ); @@ -3126,6 +3135,7 @@ ret_val Character::can_unwield( const item &it ) const bool Character::unwield() { + item &weapon = primary_weapon(); if( weapon.is_null() ) { return true; } @@ -3203,8 +3213,10 @@ void Character::drop_invalid_inventory() bool Character::has_artifact_with( const art_effect_passive effect ) const { - if( weapon.has_effect_when_wielded( effect ) ) { - return true; + for( const item *weapon : wielded_items() ) { + if( weapon->has_effect_when_wielded( effect ) ) { + return true; + } } for( auto &i : worn ) { if( i.has_effect_when_worn( effect ) ) { @@ -3218,7 +3230,7 @@ bool Character::has_artifact_with( const art_effect_passive effect ) const bool Character::is_wielding( const item &target ) const { - return &weapon == ⌖ + return &primary_weapon() == ⌖ } bool Character::is_wearing( const item &itm ) const @@ -3312,6 +3324,7 @@ std::vector Character::get_overlay_ids() const // last weapon // TODO: might there be clothing that covers the weapon? + const item &weapon = primary_weapon(); if( is_armed() ) { rval.push_back( "wielded_" + weapon.typeId().str() ); } @@ -3686,6 +3699,7 @@ units::mass Character::get_weight() const ret += bodyweight(); // The base weight of the player's body ret += inv.weight(); // Weight of the stored inventory ret += wornWeight; // Weight of worn items + const item &weapon = primary_weapon(); ret += weapon.weight(); // Weight of wielded item ret += bionics_weight(); // Weight of installed bionics return ret; @@ -4605,12 +4619,12 @@ void Character::regen( int rate_multiplier ) } // remove effects if the limb was healed by other way - if( has_effect( effect_bandaged, bp->token ) && ( get_part( bp )->is_at_max_hp() ) ) { + if( has_effect( effect_bandaged, bp->token ) && ( get_part( bp ).is_at_max_hp() ) ) { damage_bandaged[i] = 0; remove_effect( effect_bandaged, bp->token ); add_msg_if_player( _( "Bandaged wounds on your %s healed." ), body_part_name( bp ) ); } - if( has_effect( effect_disinfected, bp->token ) && ( get_part( bp )->is_at_max_hp() ) ) { + if( has_effect( effect_disinfected, bp->token ) && ( get_part( bp ).is_at_max_hp() ) ) { damage_disinfected[i] = 0; remove_effect( effect_disinfected, bp->token ); add_msg_if_player( _( "Disinfected wounds on your %s healed." ), body_part_name( bp ) ); @@ -5313,6 +5327,7 @@ void Character::update_bodytemp( const map &m, const weather_manager &weather ) } } // If player is wielding something large, pockets are not usable + const item &weapon = primary_weapon(); if( weapon.volume() >= 500_ml ) { bonus_clothing_map[body_part_hand_l].clear(); bonus_clothing_map[body_part_hand_r].clear(); @@ -6530,6 +6545,7 @@ std::string Character::extended_description() const ss += "--\n"; ss += _( "Wielding:" ) + std::string( " " ); + const item &weapon = primary_weapon(); if( weapon.is_null() ) { ss += _( "Nothing" ); } else { @@ -7463,6 +7479,7 @@ int Character::item_handling_cost( const item &it, bool penalties, int base_cost mv += std::min( 200, it.volume() / 20_ml ); } + const item &weapon = primary_weapon(); if( weapon.typeId() == itype_e_handcuffs ) { mv *= 4; } else if( penalties && has_effect( effect_grabbed ) ) { @@ -8245,6 +8262,7 @@ void Character::on_dodge( Creature *source, int difficulty ) dodges_left--; // dodging throws of our aim unless we are either skilled at dodging or using a small weapon + const item &weapon = primary_weapon(); if( is_armed() && weapon.is_gun() ) { recoil += std::max( weapon.volume() / 250_ml - get_skill_level( skill_dodge ), 0 ) * rng( 0, 100 ); recoil = std::min( MAX_RECOIL, recoil ); @@ -8404,6 +8422,7 @@ void Character::apply_damage( Creature *source, bodypart_id hurt, int dam, mod_part_hp_cur( part_to_damage, - dam_to_bodypart ); get_event_bus().send( getID(), dam_to_bodypart ); + const item &weapon = primary_weapon(); if( !weapon.is_null() && !as_player()->can_wield( weapon ).success() && can_unwield( weapon ).success() ) { add_msg_if_player( _( "You are no longer able to wield your %s and drop it!" ), @@ -8538,6 +8557,7 @@ dealt_damage_instance Character::deal_damage( Creature *source, bodypart_id bp, // TODO: Scale with damage in a way that makes sense for power armors, plate armor and naked skin. + const item &weapon = primary_weapon(); recoil += recoil_mul * weapon.volume() / 250_ml; recoil = std::min( MAX_RECOIL, recoil ); //looks like this should be based off of dealt damages, not d as d has no damage reduction applied. @@ -8941,8 +8961,8 @@ int Character::shoe_type_count( const itype_id &it ) const std::vector Character::inv_dump() { std::vector ret; - if( is_armed() && can_unwield( weapon ).success() ) { - ret.push_back( &weapon ); + if( is_armed() && can_unwield( primary_weapon() ).success() ) { + ret.push_back( &primary_weapon() ); } for( auto &i : worn ) { ret.push_back( &i ); @@ -9612,7 +9632,7 @@ std::list Character::use_amount( itype_id it, int quantity, const std::function &filter ) { std::list ret; - if( weapon.use_amount( it, quantity, ret ) ) { + if( primary_weapon().use_amount( it, quantity, ret ) ) { remove_weapon(); } for( auto a = worn.begin(); a != worn.end() && quantity > 0; ) { @@ -9954,6 +9974,7 @@ const pathfinding_settings &Character::get_pathfinding_settings() const float Character::power_rating() const { + const item &weapon = primary_weapon(); int dmg = std::max( { weapon.damage_melee( DT_BASH ), weapon.damage_melee( DT_CUT ), weapon.damage_melee( DT_STAB ) @@ -10308,7 +10329,7 @@ std::vector Character::short_description_parts() const std::string gender = male ? _( "Male" ) : _( "Female" ); result.push_back( name + ", " + gender ); if( is_armed() ) { - result.push_back( _( "Wielding: " ) + weapon.tname() ); + result.push_back( _( "Wielding: " ) + primary_weapon().tname() ); } const std::string worn_str = enumerate_as_string( worn.begin(), worn.end(), []( const item & it ) { diff --git a/src/character.h b/src/character.h index f033a227bc15..73f531575b1f 100644 --- a/src/character.h +++ b/src/character.h @@ -1220,6 +1220,11 @@ class Character : public Creature, public visitable const item &primary_weapon() const; /*@}*/ + /** + * Use this when primary weapon might not exist yet. + */ + void set_primary_weapon( const item &new_weapon ); + /** * Try to find a container/s on character containing ammo of type it.typeId() and * add charges until the container is full. @@ -1575,8 +1580,7 @@ class Character : public Creature, public visitable std::optional destination_point; inventory inv; itype_id last_item; - private: - item weapon; + public: int scent = 0; diff --git a/src/character_functions.cpp b/src/character_functions.cpp index 69eafa2d1500..68c9821cb638 100644 --- a/src/character_functions.cpp +++ b/src/character_functions.cpp @@ -486,7 +486,7 @@ void add_pain_msg( const Character &who, int val, body_part bp ) void normalize( Character &who ) { who.martial_arts_data->reset_style(); - who.primary_weapon() = item(); + who.set_primary_weapon( item() ); who.set_body(); who.recalc_hp(); @@ -545,7 +545,7 @@ bool try_wield_contents( Character &who, item &container, item *internal_item, b who.inv.unsort(); } - who.primary_weapon() = std::move( *internal_item ); + who.set_primary_weapon( *internal_item ); container.remove_item( *internal_item ); container.on_contents_changed(); diff --git a/src/character_turn.cpp b/src/character_turn.cpp index 0e677cf0c1a1..c056abfce1c1 100644 --- a/src/character_turn.cpp +++ b/src/character_turn.cpp @@ -796,6 +796,7 @@ void Character::environmental_revert_effect() void Character::process_items() { + item &weapon = primary_weapon(); if( weapon.needs_processing() && weapon.process( as_player(), pos(), false ) ) { weapon = item(); } diff --git a/src/consumption.cpp b/src/consumption.cpp index 407c0df4415c..ec1a46c0a43e 100644 --- a/src/consumption.cpp +++ b/src/consumption.cpp @@ -1551,7 +1551,7 @@ void Character::consume( item_location loc ) } if( was_in_container && wielding ) { - add_msg_if_player( _( "You are now wielding an empty %s." ), weapon.tname() ); + add_msg_if_player( _( "You are now wielding an empty %s." ), primary_weapon().tname() ); } else if( was_in_container && worn ) { add_msg_if_player( _( "You are now wearing an empty %s." ), target.tname() ); } else if( was_in_container && !is_npc() ) { diff --git a/src/crafting.cpp b/src/crafting.cpp index 8e620e81eb40..97378b139a43 100644 --- a/src/crafting.cpp +++ b/src/crafting.cpp @@ -572,7 +572,7 @@ const inventory &Character::crafting_inventory( const tripoint &src_pos, int rad } cached_crafting_inventory.form_from_map( inv_pos, radius, this, false, clear_path ); cached_crafting_inventory += inv; - cached_crafting_inventory += weapon; + cached_crafting_inventory += primary_weapon(); cached_crafting_inventory += worn; for( const bionic &bio : *my_bionics ) { const bionic_data &bio_data = bio.info(); diff --git a/src/creature.cpp b/src/creature.cpp index cd9bd9547983..4a82a0b49f0e 100644 --- a/src/creature.cpp +++ b/src/creature.cpp @@ -1554,27 +1554,32 @@ const std::map &Creature::get_body() const void Creature::set_body() { body.clear(); - for( const bodypart_id &bp : get_anatomy()->get_bodyparts() ) { - body.emplace( bp.id(), bodypart( bp.id() ) ); + // TODO: Probably shouldn't be needed, but it's called from game::game() + if( get_anatomy().is_valid() ) { + for( const bodypart_id &bp : get_anatomy()->get_bodyparts() ) { + body.emplace( bp.id(), bodypart( bp.id() ) ); + } } } -bodypart *Creature::get_part( const bodypart_id &id ) +bodypart &Creature::get_part( const bodypart_id &id ) { auto found = body.find( id.id() ); if( found == body.end() ) { debugmsg( "Could not find bodypart %s in %s's body", id.id().c_str(), get_name() ); - return nullptr; + static bodypart nullpart; + return nullpart; } - return &found->second; + return found->second; } -bodypart Creature::get_part( const bodypart_id &id ) const +const bodypart &Creature::get_part( const bodypart_id &id ) const { auto found = body.find( id.id() ); if( found == body.end() ) { debugmsg( "Could not find bodypart %s in %s's body", id.id().c_str(), get_name() ); - return bodypart(); + static const bodypart nullpart; + return nullpart; } return found->second; } @@ -1596,32 +1601,32 @@ int Creature::get_part_healed_total( const bodypart_id &id ) const void Creature::set_part_hp_cur( const bodypart_id &id, int set ) { - get_part( id )->set_hp_cur( set ); + get_part( id ).set_hp_cur( set ); } void Creature::set_part_hp_max( const bodypart_id &id, int set ) { - get_part( id )->set_hp_max( set ); + get_part( id ).set_hp_max( set ); } void Creature::set_part_healed_total( const bodypart_id &id, int set ) { - get_part( id )->set_healed_total( set ); + get_part( id ).set_healed_total( set ); } void Creature::mod_part_hp_cur( const bodypart_id &id, int mod ) { - get_part( id )->mod_hp_cur( mod ); + get_part( id ).mod_hp_cur( mod ); } void Creature::mod_part_hp_max( const bodypart_id &id, int mod ) { - get_part( id )->mod_hp_max( mod ); + get_part( id ).mod_hp_max( mod ); } void Creature::mod_part_healed_total( const bodypart_id &id, int mod ) { - get_part( id )->mod_healed_total( mod ); + get_part( id ).mod_healed_total( mod ); } void Creature::set_all_parts_hp_cur( const int set ) diff --git a/src/creature.h b/src/creature.h index 4d9ecac480b5..f2ebc8b9398b 100644 --- a/src/creature.h +++ b/src/creature.h @@ -514,8 +514,8 @@ class Creature const std::map &get_body() const; void set_body(); - bodypart *get_part( const bodypart_id &id ); - bodypart get_part( const bodypart_id &id ) const; + bodypart &get_part( const bodypart_id &id ); + const bodypart &get_part( const bodypart_id &id ) const; int get_part_hp_cur( const bodypart_id &id ) const; int get_part_hp_max( const bodypart_id &id ) const; diff --git a/src/debug_menu.cpp b/src/debug_menu.cpp index d81be83be0e9..8266acc84f7f 100644 --- a/src/debug_menu.cpp +++ b/src/debug_menu.cpp @@ -649,7 +649,7 @@ void character_edit_menu( Character &c ) } p.worn.clear(); p.inv.clear(); - p.primary_weapon() = item(); + p.set_primary_weapon( item() ); break; case edit_character::item_worn: { item_location loc = game_menus::inv::titled_menu( g->u, _( "Make target equip" ) ); @@ -661,7 +661,7 @@ void character_edit_menu( Character &c ) p.on_item_wear( to_wear ); p.worn.push_back( to_wear ); } else if( !to_wear.is_null() ) { - p.primary_weapon() = to_wear; + p.set_primary_weapon( to_wear ); } } break; diff --git a/src/magic_spell_effect.cpp b/src/magic_spell_effect.cpp index 1b3743b54b75..1f6d1421962c 100644 --- a/src/magic_spell_effect.cpp +++ b/src/magic_spell_effect.cpp @@ -740,7 +740,7 @@ void spell_effect::spawn_ethereal_item( const spell &sp, Creature &caster, const granted.set_flag( "FIT" ); you.wear_item( granted, false ); } else if( !you.is_armed() ) { - you.primary_weapon() = granted; + you.set_primary_weapon( granted ); } else { you.i_add( granted ); } diff --git a/src/mattack_actors.cpp b/src/mattack_actors.cpp index 6c3e9f89cbd7..106825e778c1 100644 --- a/src/mattack_actors.cpp +++ b/src/mattack_actors.cpp @@ -179,6 +179,10 @@ void mon_spellcasting_actor::load_internal( const JsonObject &obj, const std::st to_translation( "%1$s casts %2$s at %3$s!" ) ); spell_data = intermediate.get_spell(); spell_data.set_message( monster_message ); +} + +void mon_spellcasting_actor::finalize() +{ avatar fake_player; move_cost = spell_data.casting_time( fake_player ); } @@ -669,7 +673,7 @@ void gun_actor::shoot( monster &z, const tripoint &target, const gun_mode_id &mo tmp.set_skill_level( pr.first, pr.second ); } - tmp.primary_weapon() = gun; + tmp.set_primary_weapon( gun ); tmp.i_add( item( "UPS_off", calendar::turn, 1000 ) ); if( g->u.sees( z ) ) { diff --git a/src/mattack_actors.h b/src/mattack_actors.h index 93b82991b187..5ac65e34c16d 100644 --- a/src/mattack_actors.h +++ b/src/mattack_actors.h @@ -58,6 +58,7 @@ class mon_spellcasting_actor : public mattack_actor void load_internal( const JsonObject &obj, const std::string &src ) override; bool call( monster & ) const override; std::unique_ptr clone() const override; + void finalize() override; }; class melee_actor : public mattack_actor diff --git a/src/mattack_common.h b/src/mattack_common.h index 6c14ba14d5a4..841902bca3b3 100644 --- a/src/mattack_common.h +++ b/src/mattack_common.h @@ -32,6 +32,7 @@ class mattack_actor virtual bool call( monster & ) const = 0; virtual std::unique_ptr clone() const = 0; virtual void load_internal( const JsonObject &jo, const std::string &src ) = 0; + virtual void finalize() {}; }; struct mtype_special_attack { diff --git a/src/melee.cpp b/src/melee.cpp index eb015b3b958b..1ea17223dacd 100644 --- a/src/melee.cpp +++ b/src/melee.cpp @@ -148,7 +148,19 @@ item &Character::used_weapon() const item &Character::primary_weapon() const { - return weapon; + if( get_body().find( body_part_arm_r ) == get_body().end() ) { + debugmsg( "primary_weapon called before set_anatomy" ); + return null_item_reference(); + } + + // TODO: Remove unconst hacks + const std::shared_ptr &wielded_const = get_part( body_part_arm_r ).wielding.wielded; + std::shared_ptr &wielded = const_cast &>( wielded_const ); + if( wielded == nullptr ) { + wielded = std::make_shared(); + } + + return *wielded; } item &Character::primary_weapon() @@ -156,10 +168,29 @@ item &Character::primary_weapon() return const_cast( const_cast( this )->primary_weapon() ); } +void Character::set_primary_weapon( const item &new_weapon ) +{ + auto &body = get_body(); + auto iter = body.find( body_part_arm_r ); + if( iter != body.end() ) { + bodypart &part = get_part( body_part_arm_r ); + if( part.wielding.wielded == nullptr ) { + part.wielding.wielded = std::make_shared( new_weapon ); + } else { + *part.wielding.wielded = new_weapon; + } + } +} + std::vector Character::wielded_items() { - if( !weapon.is_null() ) { - return {&weapon}; + if( get_body().find( body_part_arm_r ) == get_body().end() ) { + return {}; + } + const bodypart &right_arm = get_part( body_part_arm_r ); + const auto wielded = right_arm.wielding.wielded; + if( wielded != nullptr && !wielded->is_null() ) { + return {& *wielded}; } return {}; @@ -167,11 +198,8 @@ std::vector Character::wielded_items() std::vector Character::wielded_items() const { - if( !weapon.is_null() ) { - return {&weapon}; - } - - return {}; + const auto nonconst_ret = const_cast( this )->wielded_items(); + return std::vector( nonconst_ret.begin(), nonconst_ret.end() ); } bool Character::is_armed() const diff --git a/src/monattack.cpp b/src/monattack.cpp index 9fe4bc2bb3b4..a5f9615732c2 100644 --- a/src/monattack.cpp +++ b/src/monattack.cpp @@ -3350,7 +3350,7 @@ void mattack::rifle( monster *z, Creature *target ) add_msg( m_warning, _( "The %s opens up with its rifle!" ), z->name() ); } - tmp.primary_weapon() = item( "m4a1" ).ammo_set( ammo_type, z->ammo[ ammo_type ] ); + tmp.set_primary_weapon( item( "m4a1" ).ammo_set( ammo_type, z->ammo[ ammo_type ] ) ); int burst = std::max( tmp.primary_weapon().gun_get_mode( gun_mode_id( "AUTO" ) ).qty, 1 ); z->ammo[ ammo_type ] -= ranged::fire_gun( tmp, target->pos(), @@ -3411,7 +3411,7 @@ void mattack::frag( monster *z, Creature *target ) // This is for the bots, not add_msg( m_warning, _( "The %s's grenade launcher fires!" ), z->name() ); } - tmp.primary_weapon() = item( "mgl" ).ammo_set( ammo_type, z->ammo[ ammo_type ] ); + tmp.set_primary_weapon( item( "mgl" ).ammo_set( ammo_type, z->ammo[ ammo_type ] ) ); int burst = std::max( tmp.primary_weapon().gun_get_mode( gun_mode_id( "AUTO" ) ).qty, 1 ); z->ammo[ ammo_type ] -= ranged::fire_gun( tmp, target->pos(), @@ -3471,7 +3471,7 @@ void mattack::tankgun( monster *z, Creature *target ) if( g->u.sees( *z ) ) { add_msg( m_warning, _( "The %s's 120mm cannon fires!" ), z->name() ); } - tmp.primary_weapon() = item( "TANK" ).ammo_set( ammo_type, z->ammo[ ammo_type ] ); + tmp.set_primary_weapon( item( "TANK" ).ammo_set( ammo_type, z->ammo[ ammo_type ] ) ); int burst = std::max( tmp.primary_weapon().gun_get_mode( gun_mode_id( "AUTO" ) ).qty, 1 ); z->ammo[ ammo_type ] -= ranged::fire_gun( tmp, target->pos(), diff --git a/src/monstergenerator.cpp b/src/monstergenerator.cpp index 6530f852e68b..223befb4ccbd 100644 --- a/src/monstergenerator.cpp +++ b/src/monstergenerator.cpp @@ -393,6 +393,12 @@ void MonsterGenerator::finalize_mtypes() if( !mon.has_flag( MF_NOT_HALLU ) ) { hallucination_monsters.push_back( mon.id ); } + + for( auto &attack : mon.special_attacks ) { + const mattack_actor &actor = *attack.second; + mattack_actor &actor_nonconst_hack = const_cast( actor ); + actor_nonconst_hack.finalize(); + } } } diff --git a/src/newcharacter.cpp b/src/newcharacter.cpp index d216db4eb475..dd00a243883a 100644 --- a/src/newcharacter.cpp +++ b/src/newcharacter.cpp @@ -399,7 +399,10 @@ void avatar::randomize( const bool random_scenario, points_left &points, bool pl bool avatar::create( character_type type, const std::string &tempname ) { - primary_weapon() = item( "null", calendar::start_of_cataclysm ); + // TODO: This block should not be needed + if( get_body().find( body_part_arm_r ) != get_body().end() ) { + set_primary_weapon( item( "null", calendar::start_of_cataclysm ) ); + } prof = profession::generic(); g->scen = scenario::generic(); @@ -530,7 +533,7 @@ bool avatar::create( character_type type, const std::string &tempname ) scent = 300; } - primary_weapon() = item( "null", calendar::start_of_cataclysm ); + set_primary_weapon( item( "null", calendar::start_of_cataclysm ) ); // Grab the skills from the profession, if there are any // We want to do this before the recipes diff --git a/src/npc.cpp b/src/npc.cpp index bb4db4f316dd..d32a9a5cbe40 100644 --- a/src/npc.cpp +++ b/src/npc.cpp @@ -331,7 +331,8 @@ void npc::randomize( const npc_class_id &type ) setID( g->assign_npc_id() ); } - primary_weapon() = item( "null", calendar::start_of_cataclysm ); + set_primary_weapon( item( "null", calendar::start_of_cataclysm ) ); + inv.clear(); personality.aggression = rng( -10, 10 ); personality.bravery = rng( -3, 10 ); @@ -814,7 +815,7 @@ int npc::best_skill_level() const void npc::starting_weapon( const npc_class_id &type ) { if( item_group::group_is_defined( type->weapon_override ) ) { - primary_weapon() = item_group::item_from( type->weapon_override, calendar::turn ); + set_primary_weapon( item_group::item_from( type->weapon_override, calendar::turn ) ); return; } @@ -822,23 +823,23 @@ void npc::starting_weapon( const npc_class_id &type ) // if NPC has no suitable skills default to stabbing weapon if( !best || best == skill_stabbing ) { - primary_weapon() = random_item_from( type, "stabbing", item_group_id( "survivor_stabbing" ) ); + set_primary_weapon( random_item_from( type, "stabbing", item_group_id( "survivor_stabbing" ) ) ); } else if( best == skill_bashing ) { - primary_weapon() = random_item_from( type, "bashing", item_group_id( "survivor_bashing" ) ); + set_primary_weapon( random_item_from( type, "bashing", item_group_id( "survivor_bashing" ) ) ); } else if( best == skill_cutting ) { - primary_weapon() = random_item_from( type, "cutting", item_group_id( "survivor_cutting" ) ); + set_primary_weapon( random_item_from( type, "cutting", item_group_id( "survivor_cutting" ) ) ); } else if( best == skill_throw ) { - primary_weapon() = random_item_from( type, "throw" ); + set_primary_weapon( random_item_from( type, "throw" ) ); } else if( best == skill_archery ) { - primary_weapon() = random_item_from( type, "archery" ); + set_primary_weapon( random_item_from( type, "archery" ) ); } else if( best == skill_pistol ) { - primary_weapon() = random_item_from( type, "pistol", item_group_id( "guns_pistol_common" ) ); + set_primary_weapon( random_item_from( type, "pistol", item_group_id( "guns_pistol_common" ) ) ); } else if( best == skill_shotgun ) { - primary_weapon() = random_item_from( type, "shotgun", item_group_id( "guns_shotgun_common" ) ); + set_primary_weapon( random_item_from( type, "shotgun", item_group_id( "guns_shotgun_common" ) ) ); } else if( best == skill_smg ) { - primary_weapon() = random_item_from( type, "smg", item_group_id( "guns_smg_common" ) ); + set_primary_weapon( random_item_from( type, "smg", item_group_id( "guns_smg_common" ) ) ); } else if( best == skill_rifle ) { - primary_weapon() = random_item_from( type, "rifle", item_group_id( "guns_rifle_common" ) ); + set_primary_weapon( random_item_from( type, "rifle", item_group_id( "guns_rifle_common" ) ) ); } if( primary_weapon().is_gun() ) { @@ -1167,15 +1168,15 @@ bool npc::wield( item &it ) } if( it.is_null() ) { - primary_weapon() = item(); + set_primary_weapon( item() ); return true; } moves -= 15; if( has_item( it ) ) { - primary_weapon() = remove_item( it ); + set_primary_weapon( remove_item( it ) ); } else { - primary_weapon() = it; + set_primary_weapon( it ); } if( g->u.sees( pos() ) ) { diff --git a/src/player.cpp b/src/player.cpp index 16ac65d44336..794cf58c6fd7 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -131,7 +131,9 @@ player::player() vitamin_levels[ v.first ] = 0; } - if( g != nullptr && json_flag::is_ready() ) { + if( g != nullptr && json_flag::is_ready() && get_anatomy().is_valid() ) { + // TODO: Remove the set_body here + set_body(); recalc_sight_limits(); reset_encumbrance(); } diff --git a/src/player_hardcoded_effects.cpp b/src/player_hardcoded_effects.cpp index edb104f223a8..2f74c64ccf41 100644 --- a/src/player_hardcoded_effects.cpp +++ b/src/player_hardcoded_effects.cpp @@ -563,8 +563,8 @@ void Character::hardcoded_effects( effect &it ) } } else if( id == effect_evil ) { // Worn or wielded; diminished effects - bool lesserEvil = weapon.has_effect_when_wielded( AEP_EVIL ) || - weapon.has_effect_when_carried( AEP_EVIL ); + bool lesserEvil = primary_weapon().has_effect_when_wielded( AEP_EVIL ) || + primary_weapon().has_effect_when_carried( AEP_EVIL ); for( auto &w : worn ) { if( w.has_effect_when_worn( AEP_EVIL ) ) { lesserEvil = true; diff --git a/src/savegame_json.cpp b/src/savegame_json.cpp index 760338c4f113..c9f4bb3f1734 100644 --- a/src/savegame_json.cpp +++ b/src/savegame_json.cpp @@ -577,8 +577,8 @@ void Character::load( const JsonObject &data ) inv.json_load_items( *invin ); } - weapon = item( "null", calendar::start_of_cataclysm ); - data.read( "weapon", weapon ); + set_primary_weapon( item( "null", calendar::start_of_cataclysm ) ); + data.read( "weapon", primary_weapon() ); data.read( "move_mode", move_mode ); @@ -724,6 +724,7 @@ void Character::store( JsonOut &json ) const json.member( "fetch_data", things_to_fetch ); } + const item &weapon = primary_weapon(); if( !weapon.is_null() ) { json.member( "weapon", weapon ); // also saves contents } diff --git a/src/suffer.cpp b/src/suffer.cpp index ad1afdb39689..11f2fc85b0cb 100644 --- a/src/suffer.cpp +++ b/src/suffer.cpp @@ -435,6 +435,7 @@ void Character::suffer_from_chemimbalance() void Character::suffer_from_schizophrenia() { std::string i_name_w; + item &weapon = primary_weapon(); if( !weapon.is_null() ) { i_name_w = weapon.has_var( "item_label" ) ? weapon.get_var( "item_label" ) : //~ %1$s: weapon name @@ -924,7 +925,7 @@ void Character::suffer_from_sunburn() } } // Umbrellas can keep the sun off the skin - if( weapon.has_flag( "RAIN_PROTECT" ) ) { + if( primary_weapon().has_flag( "RAIN_PROTECT" ) ) { return; } @@ -1289,6 +1290,7 @@ void Character::suffer_from_bad_bionics() moves -= 150; mod_power_level( -bio_dis_shock->power_trigger ); + item &weapon = primary_weapon(); if( weapon.typeId() == itype_e_handcuffs && weapon.charges > 0 ) { weapon.charges -= rng( 1, 3 ) * 50; if( weapon.charges < 1 ) {