diff --git a/data/json/body_parts.json b/data/json/body_parts.json index c193594dcea50..bc51329b825d7 100644 --- a/data/json/body_parts.json +++ b/data/json/body_parts.json @@ -109,6 +109,98 @@ ], "sub_parts": [ "torso_upper", "torso_neck", "torso_lower", "torso_hanging_front", "torso_hanging_back", "torso_waist" ] }, + { + "id": "big_head", + "type": "body_part", + "name": "big head", + "accusative": { "ctxt": "bodypart_accusative", "str": "head" }, + "heading": "big head", + "heading_multiple": "big heads", + "hp_bar_ui_text": "HEAD", + "encumbrance_text": "Dodging and blocking is hampered.", + "encumbrance_threshold": 10, + "main_part": "big_head", + "connected_to": "big_head", + "opposite_part": "big_head", + "limb_type": "head", + "limb_scores": [ [ "reaction", 0.3 ] ], + "is_vital": true, + "hit_size": 4, + "hit_difficulty": 1.2, + "side": "both", + "grabbing_effect": "grabbing_head", + "stylish_bonus": 3, + "hot_morale_mod": 2, + "cold_morale_mod": 2, + "fire_warmth_bonus": 150, + "squeamish_penalty": 7, + "base_hp": 60, + "drench_capacity": 700, + "smash_message": "You smash the %s with a firm headbutt.", + "smash_efficiency": 0.25, + "bionic_slots": 18, + "flags": [ "LIMB_UPPER" ], + "sub_parts": [ "head_forehead", "head_crown", "head_nape", "head_throat", "head_ear_r", "head_ear_l" ], + "encumbrance_per_weight": [ + { "weight": "1 mg", "encumbrance": 1 }, + { "weight": "300 g", "encumbrance": 4 }, + { "weight": "800 g", "encumbrance": 8 }, + { "weight": "1300 g", "encumbrance": 20 }, + { "weight": "1600 g", "encumbrance": 30 }, + { "weight": "2200 g", "encumbrance": 50 }, + { "weight": "5600 g", "encumbrance": 80 }, + { "weight": "10000 g", "encumbrance": 160 } + ], + "effects_on_hit": [ + { + "id": "bleed", + "dmg_type": "cut", + "dmg_threshold": 5, + "dmg_scale_increment": 2, + "duration": 60, + "duration_dmg_scaling": 30, + "max_duration": 960 + }, + { + "id": "bleed", + "dmg_type": "stab", + "dmg_threshold": 1, + "dmg_scale_increment": 1.5, + "duration": 60, + "duration_dmg_scaling": 60, + "max_duration": 1200 + }, + { + "id": "bleed", + "dmg_type": "bullet", + "dmg_threshold": 1, + "dmg_scale_increment": 1, + "duration": 60, + "duration_dmg_scaling": 60 + }, + { + "id": "stunned", + "dmg_type": "bash", + "dmg_threshold": 10, + "dmg_scale_increment": 5, + "chance": 10, + "chance_dmg_scaling": 10, + "duration": 1, + "duration_dmg_scaling": 0.5, + "max_duration": 4 + }, + { + "id": "stunned", + "dmg_threshold": 30, + "dmg_scale_increment": 10, + "chance": 10, + "chance_dmg_scaling": 10, + "duration": 1, + "duration_dmg_scaling": 0.5, + "max_duration": 4 + } + ] + }, { "id": "head", "type": "body_part", diff --git a/data/json/enchantments.json b/data/json/enchantments.json index db98d04c27a15..fbc4940726c55 100644 --- a/data/json/enchantments.json +++ b/data/json/enchantments.json @@ -21,6 +21,12 @@ { "lose": "hand_r" } ] }, + { + "type": "enchantment", + "id": "ENCH_DEBUG_BIG_HEAD", + "condition": "ALWAYS", + "modified_bodyparts": [ { "lose": "head" }, { "gain": "big_head" } ] + }, { "type": "enchantment", "id": "RM13", diff --git a/data/json/mutations/mutations.json b/data/json/mutations/mutations.json index 944111bbe6c51..5cfe857d41eb3 100644 --- a/data/json/mutations/mutations.json +++ b/data/json/mutations/mutations.json @@ -8181,6 +8181,16 @@ "enchantments": [ "ENCH_DEBUG_ONLY_HEAD" ], "debug": true }, + { + "type": "mutation", + "id": "DEBUG_BIG_HEAD", + "name": { "str": "Debug Big Head" }, + "points": 99, + "valid": false, + "description": "They said college what make me smart, this is a lot to lug around!", + "enchantments": [ "ENCH_DEBUG_BIG_HEAD" ], + "debug": true + }, { "type": "mutation", "id": "DEBUG_NIGHTVISION", diff --git a/src/creature.cpp b/src/creature.cpp index 2bf5559723f87..660c6eed985e0 100644 --- a/src/creature.cpp +++ b/src/creature.cpp @@ -1992,7 +1992,7 @@ bool Creature::has_part( const bodypart_id &id ) const bodypart *Creature::get_part( const bodypart_id &id ) { - auto found = body.find( id.id() ); + auto found = body.find( get_part_id( id ).id() ); if( found == body.end() ) { debugmsg( "Could not find bodypart %s in %s's body", id.id().c_str(), get_name() ); return nullptr; @@ -2002,7 +2002,7 @@ bodypart *Creature::get_part( const bodypart_id &id ) const bodypart *Creature::get_part( const bodypart_id &id ) const { - auto found = body.find( id.id() ); + auto found = body.find( get_part_id( id ).id() ); if( found == body.end() ) { debugmsg( "Could not find bodypart %s in %s's body", id.id().c_str(), get_name() ); return nullptr; @@ -2045,6 +2045,40 @@ static void set_part_helper( Creature &c, const bodypart_id &id, } } +bodypart_id Creature::get_part_id( const bodypart_id &id ) const +{ + auto found = body.find( id.id() ); + if( found == body.end() ) { + // try to find an equivalent part in the body map + for( const std::pair &bp : body ) { + if( id->part_side == bp.first->part_side && + id->primary_limb_type() == bp.first->primary_limb_type() ) { + return bp.first; + } + } + + // try to find the next best thing + std::pair best = { body_part_bp_null, 0.0 }; + for( const std::pair &bp : body ) { + for( const std::pair &mp : bp.first->limbtypes ) { + // if the secondary limb type matches and is better than the current + if( mp.first == id->primary_limb_type() && mp.second > best.second ) { + // give an inflated bonus if the part sides match + float bonus = id->part_side == bp.first->part_side ? 1.0 : 0.0; + best = { bp.first, mp.second + bonus }; + } + } + } + + if( best.first == body_part_bp_null ) { + debugmsg( "Could not find equivalent bodypart id %s in %s's body", id.id().c_str(), get_name() ); + } + + return best.first; + } + return found->first; +} + int Creature::get_part_hp_cur( const bodypart_id &id ) const { return get_part_helper( *this, id, &bodypart::get_hp_cur ); @@ -2281,7 +2315,7 @@ bodypart_id Creature::get_random_body_part( bool main ) const return main ? part->main_part.id() : part; } -static void sort_body_parts( std::vector &bps ) +static void sort_body_parts( std::vector &bps, const Creature *c ) { // We want to dynamically sort the parts based on their connections as // defined in json. @@ -2293,7 +2327,7 @@ static void sort_body_parts( std::vector &bps ) std::unordered_map> parts_connected_to; bodypart_id root_part; for( const bodypart_id &bp : bps ) { - bodypart_id conn = bp->connected_to; + bodypart_id conn = c->get_part_id( bp->connected_to ); if( conn == bp ) { root_part = bp; } else { @@ -2326,7 +2360,7 @@ static void sort_body_parts( std::vector &bps ) parts_with_no_connections.erase( last ); unaccounted_parts.erase( bp ); topo_sorted_parts.push_back( bp ); - bodypart_id conn = bp->connected_to; + bodypart_id conn = c->get_part_id( bp->connected_to ); if( conn == bp ) { break; } @@ -2410,7 +2444,7 @@ std::vector Creature::get_all_body_parts( get_body_part_flags flags } if( flags & get_body_part_flags::sorted ) { - sort_body_parts( all_bps ); + sort_body_parts( all_bps, this ); } return all_bps; @@ -2448,7 +2482,7 @@ std::vector Creature::get_all_body_parts_of_type( } if( flags & get_body_part_flags::sorted ) { - sort_body_parts( bodyparts ); + sort_body_parts( bodyparts, this ); } return bodyparts; diff --git a/src/creature.h b/src/creature.h index 1e7c9256efe5d..05a2ffd7355ea 100644 --- a/src/creature.h +++ b/src/creature.h @@ -766,6 +766,9 @@ class Creature : public viewer bodypart *get_part( const bodypart_id &id ); const bodypart *get_part( const bodypart_id &id ) const; + // get the body part id that matches for the character + bodypart_id get_part_id( 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/widget.cpp b/src/widget.cpp index 80af88e3d6c0b..32d85b9700205 100644 --- a/src/widget.cpp +++ b/src/widget.cpp @@ -705,7 +705,11 @@ void widget::set_default_var_range( const avatar &ava ) case widget_var::bp_hp: // HP for body part _var_min = 0; - _var_max = ava.get_part_hp_max( only_bp() ); + if( ava.has_part( only_bp() ) ) { + _var_max = ava.get_part_hp_max( only_bp() ); + } else { + _var_max = 0; + } break; case widget_var::bp_encumb: _var_min = 0;