diff --git a/data/json/effects.json b/data/json/effects.json index 6d2a98fadb3f6..5c1fd0800349a 100644 --- a/data/json/effects.json +++ b/data/json/effects.json @@ -244,6 +244,24 @@ "name": [ "Pushed" ], "desc": [ "AI tag used for monsters pushing each other. This is a bug if you have it." ] }, + { + "type": "effect_type", + "id": "venom_player1", + "name": [ "Weak Player Venom" ], + "desc": [ "Don't worry, you shouldn't get this." ], + "max_duration": 120, + "base_mods": { "hurt_amount": [ 10 ], "hurt_min": [ 1 ], "hurt_chance": [ 2 ] }, + "//": "10+0,5/s dmg, from max duration avg 70 (range 10-130)" + }, + { + "type": "effect_type", + "id": "venom_player2", + "name": [ "Strong Player Venom" ], + "desc": [ "Don't worry, you really shouldn't get this." ], + "max_duration": 120, + "base_mods": { "hurt_amount": [ 20 ], "hurt_min": [ 4 ], "hurt_chance": [ 2 ], "speed_mod": [ -50 ] }, + "//": "20+2/s dmg, from max duration avg 260 (range 20-480)" + }, { "type": "effect_type", "id": "dripping_mechanical_fluid", diff --git a/src/melee.cpp b/src/melee.cpp index 81a4401a7581b..44221184e67ee 100644 --- a/src/melee.cpp +++ b/src/melee.cpp @@ -91,6 +91,10 @@ static const efftype_id effect_lightsnare( "lightsnare" ); static const efftype_id effect_narcosis( "narcosis" ); static const efftype_id effect_poison( "poison" ); static const efftype_id effect_stunned( "stunned" ); +static const efftype_id effect_venom_dmg( "venom_dmg" ); +static const efftype_id effect_venom_weaken( "venom_weaken" ); +static const efftype_id effect_venom_player1( "venom_player1" ); +static const efftype_id effect_venom_player2( "venom_player2" ); static const trait_id trait_ARM_TENTACLES( "ARM_TENTACLES" ); static const trait_id trait_ARM_TENTACLES_4( "ARM_TENTACLES_4" ); @@ -653,15 +657,36 @@ bool Character::melee_attack_abstract( Creature &t, bool allow_special, ( cur_weapon && cur_weapon->is_null() && ( dealt_dam.type_damage( damage_type::CUT ) > 0 || dealt_dam.type_damage( damage_type::STAB ) > 0 ) ) ) { if( has_trait( trait_POISONOUS ) ) { - add_msg_if_player( m_good, _( "You poison %s!" ), t.disp_name() ); - t.add_effect( effect_poison, 6_turns ); + if( t.is_monster() ) { + t.add_effect( effect_venom_player1, 1_minutes ); + } else { + t.add_effect( effect_venom_dmg, 10_minutes ); + } + if( t.is_immune_effect( effect_venom_player1 ) ) { + add_msg_if_player( m_bad, _( "The %s is not affected by your venom" ), t.disp_name() ); + } else { + add_msg_if_player( m_good, _( "You poison %s!" ), t.disp_name() ); + if( x_in_y( 1, 10 ) ) { + t.add_effect( effect_stunned, 1_turns ); + } + } } else if( has_trait( trait_POISONOUS2 ) ) { - add_msg_if_player( m_good, _( "You inject your venom into %s!" ), - t.disp_name() ); - t.add_effect( effect_badpoison, 6_turns ); + if( t.is_monster() ) { + t.add_effect( effect_venom_player2, 1_minutes ); + } else { + t.add_effect( effect_venom_dmg, 15_minutes ); + t.add_effect( effect_venom_weaken, 5_minutes ); + } + if( t.is_immune_effect( effect_venom_player2 ) ) { + add_msg_if_player( m_bad, _( "The %s is not affected by your venom" ), t.disp_name() ); + } else { + add_msg_if_player( m_good, _( "You inject your venom into %s!" ), t.disp_name() ); + if( x_in_y( 1, 4 ) ) { + t.add_effect( effect_stunned, 1_turns ); + } + } } } - // Make a rather quiet sound, to alert any nearby monsters if( !is_quiet() ) { // check martial arts silence //sound generated later diff --git a/src/monster.cpp b/src/monster.cpp index 8cd1df3519a78..4d50c0e698bc7 100644 --- a/src/monster.cpp +++ b/src/monster.cpp @@ -89,6 +89,10 @@ static const efftype_id effect_run( "run" ); static const efftype_id effect_stunned( "stunned" ); static const efftype_id effect_supercharged( "supercharged" ); static const efftype_id effect_tied( "tied" ); +static const efftype_id effect_venom_dmg( "venom_dmg" ); +static const efftype_id effect_venom_player1( "venom_player1" ); +static const efftype_id effect_venom_player2( "venom_player2" ); +static const efftype_id effect_venom_weaken( "venom_weaken" ); static const efftype_id effect_webbed( "webbed" ); static const itype_id itype_corpse( "corpse" ); @@ -97,8 +101,10 @@ static const itype_id itype_milk_raw( "milk_raw" ); static const species_id species_FISH( "FISH" ); static const species_id species_FUNGUS( "FUNGUS" ); +static const species_id species_LEECH_PLANT( "LEECH_PLANT" ); static const species_id species_MAMMAL( "MAMMAL" ); static const species_id species_MOLLUSK( "MOLLUSK" ); +static const species_id species_NETHER( "NETHER" ); static const species_id species_ROBOT( "ROBOT" ); static const species_id species_SPIDER( "SPIDER" ); static const species_id species_ZOMBIE( "ZOMBIE" ); @@ -1318,17 +1324,31 @@ bool monster::is_immune_effect( const efftype_id &effect ) const return type->bloodType() == fd_null; } + if( effect == effect_venom_dmg || + effect == effect_venom_player1 || + effect == effect_venom_player2 ) { + return ( !made_of( material_id( "flesh" ) ) && !made_of( material_id( "iflesh" ) ) ) || + type->in_species( species_NETHER ) || type->in_species( species_LEECH_PLANT ); + } + if( effect == effect_paralyzepoison || effect == effect_badpoison || + effect == effect_venom_weaken || effect == effect_poison ) { - return !has_flag( MF_WARM ) || - ( !made_of( material_id( "flesh" ) ) && !made_of( material_id( "iflesh" ) ) ); + return type->in_species( species_ZOMBIE ) || type->in_species( species_NETHER ) || + !made_of_any( Creature::cmat_flesh ) || type->in_species( species_LEECH_PLANT ); } if( effect == effect_stunned ) { return has_flag( MF_STUN_IMMUNE ); } + if( effect == effect_downed ) { + if( type->bodytype == "insect" || type->bodytype == "spider" || type->bodytype == "crab" ) { + return x_in_y( 3, 4 ); + } else return type->bodytype == "snake" || type->bodytype == "blob" || type->bodytype == "fish" || + has_flag( MF_FLIES ); + } return false; } diff --git a/tests/creature_effect_test.cpp b/tests/creature_effect_test.cpp index f190dc301a90c..ebdabee166658 100644 --- a/tests/creature_effect_test.cpp +++ b/tests/creature_effect_test.cpp @@ -8,6 +8,9 @@ #include "mtype.h" #include "type_id.h" +static const species_id species_NETHER( "NETHER" ); +static const species_id species_ZOMBIE( "ZOMBIE" ); + // Test effect methods from `Creature` class on both `monster` and `player` // Functions covered: @@ -351,39 +354,127 @@ TEST_CASE( "monster is_immune_effect", "[creature][monster][effect][immune]" ) const efftype_id effect_bleed( "bleed" ); const efftype_id effect_poison( "poison" ); const efftype_id effect_badpoison( "badpoison" ); + const efftype_id effect_downed( "downed" ); const efftype_id effect_paralyzepoison( "paralyzepoison" ); + const efftype_id effect_venom_dmg( "venom_dmg" ); + const efftype_id effect_venom_player1( "venom_player1" ); + const efftype_id effect_venom_player2( "venom_player2" ); + const efftype_id effect_venom_weaken( "venom_weaken" ); static const species_id species_WORM( "WORM" ); + static const species_id species_FUNGUS( "FUNGUS" ); // TODO: Monster may be immune to: // - onfire (if is_immune_damage DT_HEAT, made_of LIQUID, has_flag MF_FIREY) // - stunned (if has_flag MF_STUN_IMMUNE) - WHEN( "monster is made of flesh and is warm-blooded" ) { - // Regular zombie - fleshy and warm + WHEN( "monster is made of flesh, is not zombified, has blood and has no legs" ) { + // graboid - fleshy, living snake of a species with bleed data + monster graboid( mtype_id( "mon_graboid" ) ); + graboid.clear_effects(); + REQUIRE( graboid.made_of_any( Creature::cmat_flesh ) ); + REQUIRE( graboid.type->bodytype == "snake" ); + REQUIRE( graboid.type->in_species( species_WORM ) ); + + THEN( "they can bleed" ) { + CHECK_FALSE( graboid.is_immune_effect( effect_bleed ) ); + } + + THEN( "they can be poisoned by all poisons" ) { + CHECK_FALSE( graboid.is_immune_effect( effect_poison ) ); + CHECK_FALSE( graboid.is_immune_effect( effect_badpoison ) ); + CHECK_FALSE( graboid.is_immune_effect( effect_paralyzepoison ) ); + CHECK_FALSE( graboid.is_immune_effect( effect_venom_dmg ) ); + CHECK_FALSE( graboid.is_immune_effect( effect_venom_player1 ) ); + CHECK_FALSE( graboid.is_immune_effect( effect_venom_player2 ) ); + CHECK_FALSE( graboid.is_immune_effect( effect_venom_weaken ) ); + } + + THEN( "they can't be downed" ) { + CHECK( graboid.is_immune_effect( effect_downed ) ); + } + } + + WHEN( "monster is a zombie, made of flesh, has blood and has legs" ) { + // Zombie - fleshy humanoid zombie monster zed( mtype_id( "mon_zombie" ) ); zed.clear_effects(); - REQUIRE( zed.made_of( material_id( "flesh" ) ) ); - REQUIRE( zed.has_flag( MF_WARM ) ); + REQUIRE( zed.made_of_any( Creature::cmat_flesh ) ); + REQUIRE( zed.type->in_species( species_ZOMBIE ) ); + REQUIRE( zed.type->bodytype == "human" ); THEN( "they can bleed" ) { CHECK_FALSE( zed.is_immune_effect( effect_bleed ) ); } - THEN( "they can be poisoned" ) { - CHECK_FALSE( zed.is_immune_effect( effect_poison ) ); - CHECK_FALSE( zed.is_immune_effect( effect_badpoison ) ); - CHECK_FALSE( zed.is_immune_effect( effect_paralyzepoison ) ); + + THEN( "they can be poisoned by stronger poisons" ) { + CHECK_FALSE( zed.is_immune_effect( effect_venom_dmg ) ); + CHECK_FALSE( zed.is_immune_effect( effect_venom_player1 ) ); + CHECK_FALSE( zed.is_immune_effect( effect_venom_player2 ) ); + } + + THEN( "they can't be poisoned by weaker poisons" ) { + CHECK( zed.is_immune_effect( effect_poison ) ); + CHECK( zed.is_immune_effect( effect_badpoison ) ); + CHECK( zed.is_immune_effect( effect_paralyzepoison ) ); + CHECK( zed.is_immune_effect( effect_venom_weaken ) ); + } + + THEN( "they can be downed" ) { + CHECK_FALSE( zed.is_immune_effect( effect_downed ) ); } } - WHEN( "monster species has bleeding type set" ) { - // Graboid is of `WORM` species which has `fd_blood_insect` bleeding type set - monster graboid( mtype_id( "mon_graboid" ) ); - graboid.clear_effects(); - REQUIRE( graboid.type->in_species( species_WORM ) ); + WHEN( "monster is made of nether flesh and is flying" ) { + // Flaming eye, flesh, Nether species and flying + monster feye( mtype_id( "mon_flaming_eye" ) ); + feye.clear_effects(); + REQUIRE( feye.made_of_any( Creature::cmat_flesh ) ); + REQUIRE( feye.has_flag( MF_FLIES ) ); + REQUIRE( feye.type->in_species( species_NETHER ) ); THEN( "they can bleed" ) { - CHECK_FALSE( graboid.is_immune_effect( effect_bleed ) ); + CHECK_FALSE( feye.is_immune_effect( effect_bleed ) ); + } + + THEN( "they can't be poisoned" ) { + CHECK( feye.is_immune_effect( effect_poison ) ); + CHECK( feye.is_immune_effect( effect_badpoison ) ); + CHECK( feye.is_immune_effect( effect_paralyzepoison ) ); + CHECK( feye.is_immune_effect( effect_venom_dmg ) ); + CHECK( feye.is_immune_effect( effect_venom_player1 ) ); + CHECK( feye.is_immune_effect( effect_venom_player2 ) ); + CHECK( feye.is_immune_effect( effect_venom_weaken ) ); + } + + THEN( "they can't be downed" ) { + CHECK( feye.is_immune_effect( effect_downed ) ); + } + } + + WHEN( "monster is not made of flesh or iflesh" ) { + // Fungaloid - veggy, has no blood or bodytype + monster fungaloid( mtype_id( "mon_fungaloid" ) ); + fungaloid.clear_effects(); + REQUIRE_FALSE( fungaloid.made_of_any( Creature::cmat_flesh ) ); + REQUIRE( fungaloid.type->in_species( species_FUNGUS ) ); + + THEN( "they bleed plant sap for now" ) { + CHECK_FALSE( fungaloid.is_immune_effect( effect_bleed ) ); + } + + THEN( "they can't be poisoned" ) { + CHECK( fungaloid.is_immune_effect( effect_poison ) ); + CHECK( fungaloid.is_immune_effect( effect_badpoison ) ); + CHECK( fungaloid.is_immune_effect( effect_paralyzepoison ) ); + CHECK( fungaloid.is_immune_effect( effect_venom_dmg ) ); + CHECK( fungaloid.is_immune_effect( effect_venom_player1 ) ); + CHECK( fungaloid.is_immune_effect( effect_venom_player2 ) ); + CHECK( fungaloid.is_immune_effect( effect_venom_weaken ) ); + } + + THEN( "they can be downed" ) { + CHECK_FALSE( fungaloid.is_immune_effect( effect_downed ) ); } } @@ -397,25 +488,6 @@ TEST_CASE( "monster is_immune_effect", "[creature][monster][effect][immune]" ) CHECK_FALSE( razorclaw.is_immune_effect( effect_bleed ) ); } } - - WHEN( "monster is not made of flesh and not warm-blooded" ) { - // Generator - no flesh, not warm-blooded - monster genny( mtype_id( "mon_generator" ) ); - genny.clear_effects(); - REQUIRE_FALSE( genny.made_of( material_id( "flesh" ) ) ); - REQUIRE_FALSE( genny.made_of( material_id( "iflesh" ) ) ); - REQUIRE_FALSE( genny.has_flag( MF_WARM ) ); - - THEN( "they are immune to the bleed effect" ) { - CHECK( genny.is_immune_effect( effect_bleed ) ); - } - - THEN( "they are immune to all poison effects" ) { - CHECK( genny.is_immune_effect( effect_poison ) ); - CHECK( genny.is_immune_effect( effect_badpoison ) ); - CHECK( genny.is_immune_effect( effect_paralyzepoison ) ); - } - } } // Character::is_immune_effect