diff --git a/data/json/body_parts.json b/data/json/body_parts.json index bc51329b825d7..6c5f6a3343f34 100644 --- a/data/json/body_parts.json +++ b/data/json/body_parts.json @@ -19,7 +19,6 @@ "hit_difficulty": 1, "side": "both", "legacy_id": "TORSO", - "grabbing_effect": "grabbing_torso", "stylish_bonus": 6, "hot_morale_mod": 2, "cold_morale_mod": 2, @@ -128,7 +127,6 @@ "hit_size": 4, "hit_difficulty": 1.2, "side": "both", - "grabbing_effect": "grabbing_head", "stylish_bonus": 3, "hot_morale_mod": 2, "cold_morale_mod": 2, @@ -221,7 +219,6 @@ "hit_difficulty": 1.2, "side": "both", "legacy_id": "HEAD", - "grabbing_effect": "grabbing_head", "stylish_bonus": 3, "hot_morale_mod": 2, "cold_morale_mod": 2, @@ -312,7 +309,6 @@ "side": "both", "legacy_id": "EYES", "//": "Shouldn't happen, but consistency is nice", - "grabbing_effect": "grabbing_eyes", "stylish_bonus": 2, "squeamish_penalty": 8, "base_hp": 60, @@ -350,7 +346,6 @@ "limb_scores": [ [ "breathing", 1.0 ], [ "manip", 0.05, 0.2 ] ], "side": "both", "legacy_id": "MOUTH", - "grabbing_effect": "grabbing_mouth", "stylish_bonus": 2, "hot_morale_mod": 2, "cold_morale_mod": 2, @@ -387,7 +382,6 @@ "limb_scores": [ [ "manip", 0.1, 0.2 ], [ "lift", 0.5 ], [ "balance", 0.15 ], [ "block", 1.0 ], [ "swim", 0.1 ], [ "crawl", 0.3 ] ], "side": "left", "legacy_id": "ARM_L", - "grabbing_effect": "grabbing_arm_l", "hot_morale_mod": 0.5, "cold_morale_mod": 0.5, "fire_warmth_bonus": 600, @@ -472,7 +466,6 @@ "limb_scores": [ [ "manip", 0.1, 0.2 ], [ "lift", 0.5 ], [ "balance", 0.15 ], [ "block", 1.0 ], [ "swim", 0.1 ], [ "crawl", 0.3 ] ], "side": "right", "legacy_id": "ARM_R", - "grabbing_effect": "grabbing_arm_r", "hot_morale_mod": 0.5, "cold_morale_mod": 0.5, "fire_warmth_bonus": 600, @@ -554,7 +547,6 @@ "limb_scores": [ [ "grip", 0.5 ], [ "manip", 0.5, 1.0 ], [ "swim", 0.15 ] ], "side": "left", "legacy_id": "HAND_L", - "grabbing_effect": "grabbing_hand_l", "stylish_bonus": 0.5, "hot_morale_mod": 0.5, "cold_morale_mod": 0.5, @@ -586,7 +578,6 @@ "limb_scores": [ [ "grip", 0.5 ], [ "manip", 0.5, 1.0 ], [ "swim", 0.15 ] ], "side": "right", "legacy_id": "HAND_R", - "grabbing_effect": "grabbing_hand_r", "stylish_bonus": 0.5, "hot_morale_mod": 0.5, "cold_morale_mod": 0.5, @@ -621,7 +612,6 @@ "limb_scores": [ [ "manip", 0.1, 0.2 ], [ "move_speed", 0.5 ], [ "swim", 0.15 ], [ "block", 1 ], [ "crawl", 0.2 ] ], "side": "left", "legacy_id": "LEG_L", - "grabbing_effect": "grabbing_leg_l", "stylish_bonus": 1, "hot_morale_mod": 0.5, "cold_morale_mod": 0.5, @@ -715,7 +705,6 @@ "limb_scores": [ [ "manip", 0.1, 0.2 ], [ "move_speed", 0.5 ], [ "swim", 0.15 ], [ "block", 1 ], [ "crawl", 0.2 ] ], "side": "right", "legacy_id": "LEG_R", - "grabbing_effect": "grabbing_leg_r", "stylish_bonus": 1, "hot_morale_mod": 0.5, "cold_morale_mod": 0.5, @@ -807,7 +796,6 @@ "limb_scores": [ [ "manip", 0.1, 0.2 ], [ "footing", 0.5 ], [ "swim", 0.1 ] ], "side": "left", "legacy_id": "FOOT_L", - "grabbing_effect": "grabbing_foot_l", "stylish_bonus": 0.5, "hot_morale_mod": 0.5, "cold_morale_mod": 0.5, @@ -841,7 +829,6 @@ "limb_scores": [ [ "manip", 0.1, 0.2 ], [ "footing", 0.5 ], [ "swim", 0.1 ] ], "side": "right", "legacy_id": "FOOT_R", - "grabbing_effect": "grabbing_foot_r", "stylish_bonus": 0.5, "hot_morale_mod": 0.5, "cold_morale_mod": 0.5, @@ -878,7 +865,6 @@ "accusative": { "ctxt": "bodypart_accusative", "str": "debug tail" }, "hp_bar_ui_text": "DBG TAIL", "base_hp": 20, - "grabbing_effect": "grabbing_tail", "flags": [ "ALWAYS_BLOCK", "NONSTANDARD_BLOCK", "MEND_LIMB", "WALL_CLING" ], "armor": { "bash": 10 }, "unarmed_damage": [ { "damage_type": "acid", "amount": 10 }, { "damage_type": "bullet", "amount": 5, "armor_penetration": 100 } ], diff --git a/data/json/effects.json b/data/json/effects.json index 23dfbdc61d9b5..749b1672048fc 100644 --- a/data/json/effects.json +++ b/data/json/effects.json @@ -305,143 +305,6 @@ "desc": [ "AI tag used for robot monsters losing mechanical fluid. This is a bug if you have it." ], "show_in_info": true }, - { - "type": "effect_type", - "id": "grabbing_head", - "name": [ "Grabbing Head" ], - "show_in_info": true, - "//": "Grab filter effect for dedicated removal, per-bptype", - "base_mods": { "dodge_mod": [ -8 ] }, - "desc": [ "You're grabbing the head of somebody." ], - "flags": [ "GRAB_FILTER" ] - }, - { - "type": "effect_type", - "id": "grabbing_eyes", - "name": [ "Grabbing Eyes" ], - "show_in_info": true, - "base_mods": { "dodge_mod": [ -8 ] }, - "desc": [ "You've caught the eye of somebody." ], - "flags": [ "GRAB_FILTER" ] - }, - { - "type": "effect_type", - "id": "grabbing_mouth", - "name": [ "Grabbing Mouth" ], - "show_in_info": true, - "base_mods": { "dodge_mod": [ -8 ] }, - "desc": [ "You're grabbing the mouth of somebody. Somehow." ], - "flags": [ "GRAB_FILTER" ] - }, - { - "type": "effect_type", - "id": "grabbing_torso", - "name": [ "Grabbing Torso" ], - "show_in_info": true, - "base_mods": { "dodge_mod": [ -8 ] }, - "desc": [ "You're hugging somebody." ], - "flags": [ "GRAB_FILTER" ] - }, - { - "type": "effect_type", - "id": "grabbing_arm_l", - "name": [ "Grabbing Left Arm" ], - "show_in_info": true, - "base_mods": { "dodge_mod": [ -8 ] }, - "desc": [ "You're grabbing the left arm of somebody." ], - "flags": [ "GRAB_FILTER" ] - }, - { - "type": "effect_type", - "id": "grabbing_arm_r", - "name": [ "Grabbing Right Arm" ], - "show_in_info": true, - "base_mods": { "dodge_mod": [ -8 ] }, - "desc": [ "You're grabbing the right arm of somebody." ], - "flags": [ "GRAB_FILTER" ] - }, - { - "type": "effect_type", - "id": "grabbing_hand_l", - "name": [ "Grabbing Left Hand" ], - "show_in_info": true, - "base_mods": { "dodge_mod": [ -8 ] }, - "desc": [ "You're holding hands(left)." ], - "flags": [ "GRAB_FILTER" ] - }, - { - "type": "effect_type", - "id": "grabbing_hand_r", - "name": [ "Grabbing Right Hand" ], - "show_in_info": true, - "base_mods": { "dodge_mod": [ -8 ] }, - "desc": [ "You're holding hands(right)." ], - "flags": [ "GRAB_FILTER" ] - }, - { - "type": "effect_type", - "id": "grabbing_leg_l", - "name": [ "Grabbing Left Leg" ], - "show_in_info": true, - "base_mods": { "dodge_mod": [ -8 ] }, - "desc": [ "You're grabbing the left leg of somebody." ], - "flags": [ "GRAB_FILTER" ] - }, - { - "type": "effect_type", - "id": "grabbing_leg_r", - "name": [ "Grabbing Right Leg" ], - "show_in_info": true, - "base_mods": { "dodge_mod": [ -8 ] }, - "desc": [ "You're grabbing the right leg of somebody." ], - "flags": [ "GRAB_FILTER" ] - }, - { - "type": "effect_type", - "id": "grabbing_foot_l", - "name": [ "Grabbing Left Foot" ], - "show_in_info": true, - "base_mods": { "dodge_mod": [ -8 ] }, - "desc": [ "You're grabbing the left foot of somebody." ], - "flags": [ "GRAB_FILTER" ] - }, - { - "type": "effect_type", - "id": "grabbing_foot_r", - "name": [ "Grabbing Right Foot" ], - "show_in_info": true, - "base_mods": { "dodge_mod": [ -8 ] }, - "desc": [ "You're grabbing the right foot of somebody." ], - "flags": [ "GRAB_FILTER" ] - }, - { - "type": "effect_type", - "id": "grabbing_tail", - "name": [ "Grabbing Tail" ], - "show_in_info": true, - "base_mods": { "dodge_mod": [ -8 ] }, - "desc": [ "You're grabbing the tail of somebody." ], - "flags": [ "GRAB_FILTER" ] - }, - { - "type": "effect_type", - "id": "grabbing_gastropod_foot", - "name": [ "Grabbing G. Foot" ], - "show_in_info": true, - "base_mods": { "dodge_mod": [ -8 ] }, - "desc": [ "You're grabbing the snail like lower body of somebody." ], - "flags": [ "GRAB_FILTER" ] - }, - { - "type": "effect_type", - "id": "grabbing_appendix", - "name": [ "Grabbing" ], - "show_in_info": true, - "base_mods": { "dodge_mod": [ -8 ] }, - "//": "Null-BP grabbing effect for safety and monsters", - "desc": [ "This sounds pretty invasive." ], - "flags": [ "GRAB_FILTER" ] - }, { "type": "effect_type", "id": "staggered", @@ -451,23 +314,7 @@ "apply_message": "You're staggered off-balance.", "max_duration": "5 s", "base_mods": { "speed_mod": [ -20 ] }, - "removes_effects": [ - "grabbing", - "grabbing_2", - "grabbing_head", - "grabbing_eyes", - "grabbing_mouth", - "grabbing_hand_l", - "grabbing_hand_r", - "grabbing_arm_l", - "grabbing_arm_r", - "grabbing_leg_l", - "grabbing_leg_r", - "grabbing_foot_l", - "grabbing_foot_r", - "grabbing_appendix", - "grabbing_tail" - ], + "removes_effects": [ "grabbing", "grabbing_2" ], "rating": "bad", "show_in_info": true }, diff --git a/data/json/mutations/mutation_limbs.json b/data/json/mutations/mutation_limbs.json index a8648c238dd26..e57a3c9212af4 100644 --- a/data/json/mutations/mutation_limbs.json +++ b/data/json/mutations/mutation_limbs.json @@ -17,7 +17,6 @@ "hit_size": 30, "hit_difficulty": 1.2, "side": "both", - "grabbing_effect": "grabbing_gastropod_foot", "stylish_bonus": 3, "hot_morale_mod": 2, "cold_morale_mod": 2, diff --git a/data/mods/Xedra_Evolved/body_parts.json b/data/mods/Xedra_Evolved/body_parts.json index b02dff459c3c7..0d06f3887beca 100644 --- a/data/mods/Xedra_Evolved/body_parts.json +++ b/data/mods/Xedra_Evolved/body_parts.json @@ -119,7 +119,6 @@ "limb_type": "sensor", "limb_scores": [ [ "vision", 2.0 ], [ "night_vis", 12 ], [ "reaction", 1.4 ] ], "side": "both", - "grabbing_effect": "grabbing_eyes", "base_hp": 60, "drench_capacity": 0, "flags": [ "IGNORE_TEMP", "LIMB_UPPER" ], diff --git a/src/bodypart.cpp b/src/bodypart.cpp index 64593ab6d20fd..28311f5973ed8 100644 --- a/src/bodypart.cpp +++ b/src/bodypart.cpp @@ -29,8 +29,6 @@ const bodypart_str_id body_part_leg_r( "leg_r" ); const bodypart_str_id body_part_mouth( "mouth" ); const bodypart_str_id body_part_torso( "torso" ); -static const efftype_id effect_grabbing_appendix( "grabbing_appendix" ); - const sub_bodypart_str_id sub_body_part_sub_limb_debug( "sub_limb_debug" ); side opposite_side( side s ) @@ -404,7 +402,6 @@ void body_part_type::load( const JsonObject &jo, const std::string_view ) optional( jo, was_loaded, "flags", flags ); optional( jo, was_loaded, "conditional_flags", conditional_flags ); - optional( jo, was_loaded, "grabbing_effect", grabbing_effect, effect_grabbing_appendix ); optional( jo, was_loaded, "encumbrance_threshold", encumbrance_threshold, 0 ); optional( jo, was_loaded, "encumbrance_limit", encumbrance_limit, 100 ); diff --git a/src/bodypart.h b/src/bodypart.h index 4dbf3ab378c4a..cd59a0a7d1734 100644 --- a/src/bodypart.h +++ b/src/bodypart.h @@ -241,9 +241,6 @@ struct body_part_type { // Effects to trigger on getting hit std::vector effects_on_hit; - // Monster effect added to mobs grabbing this limb (for dedicated removal) - efftype_id grabbing_effect; - // Those are stored untranslated translation name; translation name_multiple; diff --git a/src/character_escape.cpp b/src/character_escape.cpp index 2b55423fdec57..286503047b5d3 100644 --- a/src/character_escape.cpp +++ b/src/character_escape.cpp @@ -201,7 +201,7 @@ bool Character::try_remove_grab( bool attacking ) monster *grabber = nullptr; for( const tripoint loc : surrounding ) { monster *mon = creatures.creature_at( loc ); - if( mon && mon->has_effect( eff.get_bp()->grabbing_effect ) ) { + if( mon && mon->is_grabbing( eff.get_bp().id() ) ) { add_msg_debug( debugmode::DF_MATTACK, "Grabber %s found", mon->name() ); grabber = mon; break; @@ -266,9 +266,9 @@ bool Character::try_remove_grab( bool attacking ) // Every attempt burns some stamina - maybe some moves? mod_stamina( -5 * eff.get_intensity() ); if( x_in_y( escape_chance, grabber_roll ) ) { - grabber->remove_effect( eff.get_bp()->grabbing_effect ); - add_msg_debug( debugmode::DF_MATTACK, "Removed grab filter effect %s from monster %s", - eff.get_bp()->grabbing_effect.c_str(), grabber->name() ); + grabber->remove_grab( eff.get_bp().id() ); + add_msg_debug( debugmode::DF_MATTACK, "Removed grab effect %s from monster %s", + eff.get_bp()->name, grabber->name() ); if( grab_break_factor > 0 ) { add_msg_if_player( m_info, martial_arts_data->get_grab_break( *this ).avatar_message.translated(), diff --git a/src/mattack_actors.cpp b/src/mattack_actors.cpp index 4f7ea3ffba130..e1291af66c72f 100644 --- a/src/mattack_actors.cpp +++ b/src/mattack_actors.cpp @@ -567,9 +567,9 @@ int melee_actor::do_grab( monster &z, Creature *target, bodypart_id bp_id ) cons if( grab_data.grab_effect != effect_null ) { if( foe ) { - z.add_effect( bp_id->grabbing_effect, 1_days, true, 1 ); - add_msg_debug( debugmode::DF_MATTACK, "Added grabbing filter effect %s", - bp_id->grabbing_effect.c_str() ); + z.add_grab( bp_id.id() ); + add_msg_debug( debugmode::DF_MATTACK, "Added grabbing on %s", + bp_id->name ); // Add grabbed - permanent, removal handled in try_remove_grab on move/wait target->add_effect( grab_data.grab_effect, 1_days, bp_id, true, eff_grab_strength ); } else { @@ -751,7 +751,7 @@ bool melee_actor::call( monster &z ) const monster *mon = creatures.creature_at( loc ); if( mon && mon->has_effect_with_flag( json_flag_GRAB_FILTER ) && mon->attack_target() == target ) { if( target->is_monster() || ( !target->is_monster() && - mon->has_effect( eff.get_bp()->grabbing_effect ) ) ) { + mon->is_grabbing( eff.get_bp().id() ) ) ) { grabber = mon; break; } diff --git a/src/melee.cpp b/src/melee.cpp index f911bcb78f463..9efcdabababcc 100644 --- a/src/melee.cpp +++ b/src/melee.cpp @@ -1906,11 +1906,14 @@ void Character::perform_technique( const ma_technique &technique, Creature &t, // Remove our grab if we knocked back our grabber (if we can do so is handled by tech conditions) if( has_flag( json_flag_GRAB ) && t.has_effect_with_flag( json_flag_GRAB_FILTER ) ) { for( const effect &eff : get_effects_with_flag( json_flag_GRAB ) ) { - if( t.has_effect( eff.get_bp()->grabbing_effect ) ) { - t.remove_effect( eff.get_bp()->grabbing_effect ); - remove_effect( eff.get_id(), eff.get_bp() ); - add_msg_debug( debugmode::DF_MELEE, "Grabber %s knocked back, grab on %s removed", t.get_name(), - eff.get_bp()->name ); + if( t.is_monster() ) { + monster *m = t.as_monster(); + if( m->is_grabbing( eff.get_bp().id() ) ) { + m->remove_grab( eff.get_bp().id() ); + remove_effect( eff.get_id(), eff.get_bp() ); + add_msg_debug( debugmode::DF_MELEE, "Grabber %s knocked back, grab on %s removed", t.get_name(), + eff.get_bp()->name ); + } } } } diff --git a/src/monster.cpp b/src/monster.cpp index 737f4402d0c0c..195924644d67b 100644 --- a/src/monster.cpp +++ b/src/monster.cpp @@ -88,6 +88,7 @@ static const efftype_id effect_docile( "docile" ); static const efftype_id effect_downed( "downed" ); static const efftype_id effect_dripping_mechanical_fluid( "dripping_mechanical_fluid" ); static const efftype_id effect_emp( "emp" ); +static const efftype_id effect_grabbing( "grabbing" ); static const efftype_id effect_has_bag( "has_bag" ); static const efftype_id effect_heavysnare( "heavysnare" ); static const efftype_id effect_hit_by_player( "hit_by_player" ); @@ -2332,6 +2333,25 @@ int monster::get_grab_strength() const return type->grab_strength; } +void monster::add_grab( bodypart_str_id bp ) +{ + add_effect( effect_grabbing, 1_days, true, 1 ); + grabbed_limbs.insert( bp ); +} + +void monster::remove_grab( bodypart_str_id bp ) +{ + grabbed_limbs.erase( bp ); + if( grabbed_limbs.empty() ) { + add_effect( effect_grabbing, 1_days, true, 1 ); + } +} + +bool monster::is_grabbing( bodypart_str_id bp ) +{ + return has_effect( effect_grabbing ) && grabbed_limbs.find( bp ) != grabbed_limbs.end(); +} + float monster::fall_damage_mod() const { if( flies() ) { @@ -2608,7 +2628,7 @@ void monster::die( Creature *nkiller ) } // Iterate through all your grabs to figure out which one this critter held for( const effect &grab : you->get_effects_with_flag( json_flag_GRAB ) ) { - if( has_effect( grab.get_bp()->grabbing_effect ) ) { + if( is_grabbing( grab.get_bp().id() ) ) { const effect_type effid = *grab.get_effect_type(); you->remove_effect( effid.id, grab.get_bp() ); } diff --git a/src/monster.h b/src/monster.h index eb6bdbc43a7ec..d469b29cfe069 100644 --- a/src/monster.h +++ b/src/monster.h @@ -401,6 +401,10 @@ class monster : public Creature bool can_attack_high() const override; // Can we attack upper limbs? int get_grab_strength() const; // intensity of grabbed effect + void add_grab( bodypart_str_id bp ); + void remove_grab( bodypart_str_id bp ); + bool is_grabbing( bodypart_str_id bp ); + monster_horde_attraction get_horde_attraction(); void set_horde_attraction( monster_horde_attraction mha ); bool will_join_horde( int size ); @@ -500,6 +504,9 @@ class monster : public Creature void add_msg_debug_player_or_npc( debugmode::debug_filter type, const std::string &player_msg, const std::string &npc_msg ) const override; + // currently grabbed limbs + std::unordered_set grabbed_limbs; + tripoint_abs_ms wander_pos; // Wander destination - Just try to move in that direction bool provocative_sound = false; // Are we wandering toward something we think is alive? int wandf = 0; // Urge to is_wandering - Increased by sound, decrements each move diff --git a/src/savegame_json.cpp b/src/savegame_json.cpp index 030e7a40bbb23..01c5b3619ffa4 100644 --- a/src/savegame_json.cpp +++ b/src/savegame_json.cpp @@ -2707,6 +2707,8 @@ void monster::load( const JsonObject &data ) data.read( "mounted_player_id", mounted_player_id ); data.read( "path", path ); + + data.read( "grabbed_limbs", grabbed_limbs ); } /* @@ -2781,6 +2783,9 @@ void monster::store( JsonOut &json ) const json.member( "dragged_foe_id", dragged_foe_id ); // storing the rider json.member( "mounted_player_id", mounted_player_id ); + + // store grabbed limbs + json.member( "grabbed_limbs", grabbed_limbs ); } void mon_special_attack::serialize( JsonOut &json ) const diff --git a/tests/monster_attack_test.cpp b/tests/monster_attack_test.cpp index 88a15db0a1ebc..e786c6041be1c 100644 --- a/tests/monster_attack_test.cpp +++ b/tests/monster_attack_test.cpp @@ -325,8 +325,8 @@ TEST_CASE( "Targeted_grab_removal_test", "[mattack][grab]" ) //Have two grabs, the monsters have the right filter effects REQUIRE( you.has_effect( effect_grabbed, body_part_arm_r ) ); REQUIRE( you.has_effect( effect_grabbed, body_part_arm_l ) ); - REQUIRE( test_monster_right.has_effect( body_part_arm_r->grabbing_effect ) ); - REQUIRE( test_monster_left.has_effect( body_part_arm_l->grabbing_effect ) ); + REQUIRE( test_monster_right.is_grabbing( body_part_arm_r ) ); + REQUIRE( test_monster_left.is_grabbing( body_part_arm_l ) ); // Kill the left grabber test_monster_left.die( nullptr ); @@ -575,6 +575,7 @@ TEST_CASE( "Unified_grab_break_test", "[mattack][grab]" ) success++; you.clear_effects(); you.set_stamina( you.get_stamina_max() ); + test_monster_1.clear_effects(); REQUIRE( attack_1.call( test_monster_1 ) ); } }