Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adjust monster immunities to venoms, being downed #46156

Merged
merged 28 commits into from
Feb 18, 2021
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions data/json/effects.json
Original file line number Diff line number Diff line change
Expand Up @@ -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)"
},
{
"//": "ACTUAL PLAYER EFFECTS START HERE",
"type": "effect_type",
Expand Down
23 changes: 21 additions & 2 deletions src/melee.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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" );
Expand Down Expand Up @@ -643,11 +647,26 @@ void Character::melee_attack( Creature &t, bool allow_special, const matec_id &f
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( x_in_y( 1, 5 ) ) {
t.add_effect( effect_stunned, 1_turns );
}
if( t.is_monster() ) {
t.add_effect( effect_venom_player1, 1_minutes );
} else {
t.add_effect( effect_venom_dmg, 10_minutes );
}
} 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( x_in_y( 1, 5 ) ) {
t.add_effect( effect_downed, 1_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 );
}
}
}

Expand Down
40 changes: 29 additions & 11 deletions src/monster.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,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" );
Expand All @@ -96,8 +100,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" );
Expand All @@ -111,7 +117,7 @@ static const trait_id trait_FLOWERS( "FLOWERS" );
static const trait_id trait_KILLER( "KILLER" );
static const trait_id trait_MYCUS_FRIEND( "MYCUS_FRIEND" );
static const trait_id trait_PACIFIST( "PACIFIST" );
static const trait_id trait_PHEROMONE_INSECT( "PHEROMONE_INSECT" );
static const trait_id trait_PHEROMONE_( "PHEROMONE_INSECT" );
Venera3 marked this conversation as resolved.
Show resolved Hide resolved
static const trait_id trait_PHEROMONE_MAMMAL( "PHEROMONE_MAMMAL" );
static const trait_id trait_TERRIFYING( "TERRIFYING" );
static const trait_id trait_THRESH_MYCUS( "THRESH_MYCUS" );
Expand Down Expand Up @@ -1288,21 +1294,34 @@ bool monster::is_immune_effect( const efftype_id &effect ) const
}

if( effect == effect_bleed ) {
return !has_flag( MF_WARM ) ||
!made_of( material_id( "flesh" ) );
return !made_of( material_id( "flesh" ) ) && !made_of( material_id( "iflesh" ) );
}

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;
}

Expand Down Expand Up @@ -1367,16 +1386,15 @@ void monster::melee_attack( Creature &target )

void monster::melee_attack( Creature &target, float accuracy )
{
// Note: currently this method must consume move even if attack hasn't actually happen
// otherwise infinite loop will happen
int hitspread = target.deal_melee_attack( this, melee::melee_hit_range( accuracy ) );
mod_moves( -type->attack_cost );
if( /*This happens sometimes*/ this == &target || !is_adjacent( &target, true ) ) {
if( type->melee_dice == 0 ) {
// We don't attack, so just return
return;
}

int hitspread = target.deal_melee_attack( this, melee::melee_hit_range( accuracy ) );
if( type->melee_dice == 0 ) {
// We don't attack, so just return
if( this == &target ) {
// This happens sometimes
return;
}

Expand Down
141 changes: 98 additions & 43 deletions tests/creature_effect_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -81,8 +84,8 @@ TEST_CASE( "character add_effect", "[creature][character][effect][add]" )
TEST_CASE( "monster add_effect", "[creature][monster][effect][add]" )
{
monster mummy( mtype_id( "debug_mon" ) );
const efftype_id effect_bleed( "bleed" );
const efftype_id effect_grabbed( "grabbed" );
const efftype_id effect_poison( "poison" );

mummy.clear_effects();

Expand All @@ -96,21 +99,21 @@ TEST_CASE( "monster add_effect", "[creature][monster][effect][add]" )
}
}

// Debug monster is "flesh", but doesn't have the "WARM" flag, so is immune to bleeding.
// Debug monster is "flesh", but is zombified so can't be easily poisoned
GIVEN( "monster is immune to effect" ) {
REQUIRE( mummy.is_immune_effect( effect_bleed ) );
REQUIRE( mummy.is_immune_effect( effect_poison ) );

THEN( "monster add_effect is called with force = false" ) {
mummy.add_effect( effect_bleed, 1_minutes, false, 1, false );
mummy.add_effect( effect_poison, 1_minutes, false, 1, false );
THEN( "they do not have the effect" ) {
CHECK_FALSE( mummy.has_effect( effect_bleed ) );
CHECK_FALSE( mummy.has_effect( effect_poison ) );
}
}

WHEN( "monster add_effect is called with force = true" ) {
mummy.add_effect( effect_bleed, 1_minutes, false, 1, true );
mummy.add_effect( effect_poison, 1_minutes, false, 1, true );
THEN( "they have the effect" ) {
CHECK( mummy.has_effect( effect_bleed ) );
CHECK( mummy.has_effect( effect_poison ) );
}
}
}
Expand Down Expand Up @@ -351,66 +354,118 @@ 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" );

// 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 and is not zombified" ) {
// snek - fleshy, living snake
monster snek( mtype_id( "mon_rattlesnake" ) );
snek.clear_effects();
REQUIRE( snek.made_of_any( Creature::cmat_flesh ) );
REQUIRE_FALSE( snek.type->in_species( species_ZOMBIE ) );
REQUIRE( snek.type->bodytype == "snake" );

THEN( "they can bleed" ) {
CHECK_FALSE( snek.is_immune_effect( effect_bleed ) );
}
THEN( "they can be poisoned by all poisons" ) {
CHECK_FALSE( snek.is_immune_effect( effect_poison ) );
CHECK_FALSE( snek.is_immune_effect( effect_badpoison ) );
CHECK_FALSE( snek.is_immune_effect( effect_paralyzepoison ) );
CHECK_FALSE( snek.is_immune_effect( effect_venom_dmg ) );
CHECK_FALSE( snek.is_immune_effect( effect_venom_player1 ) );
CHECK_FALSE( snek.is_immune_effect( effect_venom_player2 ) );
CHECK_FALSE( snek.is_immune_effect( effect_venom_weaken ) );
}

THEN( "they can't be downed" ) {
CHECK( snek.is_immune_effect( effect_downed ) );
}

Venera3 marked this conversation as resolved.
Show resolved Hide resolved
}
WHEN( "monster is made of flesh but is zombified" ) {
// 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_bleed ) );
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 is not not made of flesh and not warm-blooded" ) {
// Skeleton - no flesh, not warm-blooded
monster skelly( mtype_id( "mon_skeleton" ) );
skelly.clear_effects();
REQUIRE_FALSE( skelly.made_of( material_id( "flesh" ) ) );
REQUIRE_FALSE( skelly.made_of( material_id( "iflesh" ) ) );
REQUIRE_FALSE( skelly.has_flag( MF_WARM ) );
WHEN( "monster is not made of flesh, but it's weird nether stuff" ) {
// Flaming eye, flesh but Nether species and flying
monster feye( mtype_id( "mon_flaming_eye" ) );
feye.clear_effects();
REQUIRE( feye.made_of( material_id( "flesh" ) ) );
REQUIRE( feye.has_flag( MF_FLIES ) );
REQUIRE( feye.type->in_species( species_NETHER ) );

THEN( "they are immune to the bleed effect" ) {
CHECK( skelly.is_immune_effect( effect_bleed ) );
THEN( "they can still bleed" ) {
CHECK_FALSE( feye.is_immune_effect( effect_bleed ) );
}

THEN( "they are immune to all poison effects" ) {
CHECK( skelly.is_immune_effect( effect_bleed ) );
CHECK( skelly.is_immune_effect( effect_poison ) );
CHECK( skelly.is_immune_effect( effect_badpoison ) );
CHECK( skelly.is_immune_effect( effect_paralyzepoison ) );
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 ) );
}
}

WHEN( "monster is made of flesh, but not warm-blooded" ) {
// Razorclaw - fleshy, with arthropod blood
monster razorclaw( mtype_id( "mon_razorclaw" ) );
razorclaw.clear_effects();
REQUIRE( razorclaw.made_of( material_id( "flesh" ) ) );
REQUIRE_FALSE( razorclaw.has_flag( MF_WARM ) );
THEN( "they can't be downed" ) {
CHECK( feye.is_immune_effect( effect_downed ) );
}
}
WHEN( "monster is not made of flesh or iflesh" ) {
// Fungaloid - veggy
monster fungaloid( mtype_id( "mon_fungaloid" ) );
fungaloid.clear_effects();
REQUIRE_FALSE( fungaloid.made_of( material_id( "flesh" ) ) );
REQUIRE_FALSE( fungaloid.made_of( material_id( "iflesh" ) ) );

THEN( "they are immune to the bleed effect" ) {
CHECK( razorclaw.is_immune_effect( effect_bleed ) );
CHECK( fungaloid.is_immune_effect( effect_bleed ) );
}

THEN( "they are immune to all poison effects" ) {
CHECK( razorclaw.is_immune_effect( effect_bleed ) );
CHECK( razorclaw.is_immune_effect( effect_poison ) );
CHECK( razorclaw.is_immune_effect( effect_badpoison ) );
CHECK( razorclaw.is_immune_effect( effect_paralyzepoison ) );
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 ) );
}
}
}
Expand Down