Skip to content

Commit

Permalink
Fix z-level melee attack difference problems (with testing code) (#48245
Browse files Browse the repository at this point in the history
)

* added sees check and debugmsg

(cherry picked from commit 752f0c4)

* Update src/monster.cpp

Co-authored-by: anothersimulacrum <[email protected]>
(cherry picked from commit da7c760)

* excempt stairs&ramps from vision check

(cherry picked from commit 152980e)

* monster::melee_attack returns false for zlvl violations

(cherry picked from commit cc7b3b7)

* Require daytime for sees in monster_attack test.

Make sure there should be enough light for things to be visible;
weather might also need checking.

(cherry picked from commit 6d253c9)

* Set time to daytime.

It wasn't previously (see results of REQUIRE).

(cherry picked from commit 321404f)

* Move forward by 2 hours.

It gets set to daytime, but isn't?

(cherry picked from commit 592d0c1)

* See if can get info on hour of day.

(cherry picked from commit ea65b7e)

* Try again on checking hour of day.

(cherry picked from commit 874bccf)

* Blasted parenthesis.

In case you're wondering why I don't seem to be compiling locally
before sending things - even with everything else already present
as an object file, the version+ar+compiling the test+putting together
the test executable still takes something like 5-10 minutes...

(cherry picked from commit 23e1394)

* Template problem

I'm still learning my way around C++ where it differs from C.

(cherry picked from commit 1282f44)

* Set weather, update sight limits

If this doesn't work, I'll try the steps in vision_test.cpp.

(cherry picked from commit 2062bbc)

* Wipe visibility, map caches in monster_attack

This is in case something is left over in one of these caches that's
causing problems.

(cherry picked from commit ce394e2)

* Set WEATHER_NULL for weather_override in tests

This sets weather_override to WEATHER_NULL in two places, namely after
(if no exceptions occur!) the monster_attack test and before the
weather realism test. The former should be replaced by
scoped_weather_override (#48227) after that's been merged.

(cherry picked from commit dcacb3d)

* Use scoped_weather_override in monster_attack test

Simply doing get_weather().weather_override = WEATHER_NULL at the end
is not sufficient, since it will not be run in the event of an
exception, such as from Catch2.

(cherry picked from commit 49d5f24)

* Wrong syntax for scoped_weather_override.

As mentioned before, I'm still learning regarding C++ differences
from C.

(cherry picked from commit c90ae42)

* Start on unifying sees requirement with testing

This is a unification of most of #47993 with most of #48202, in an
attempt to remove problems (seen in #42845, which this hopefully
will fix) with players unable to see monsters on different z-levels
(and, as it turns out, the other way around) but they are still able
to attack the player. This is additionally complicated by that,
when fov_3d is enabled, the player can see the monsters and vice-versa,
and they should be able to attack each other (when at an appropriate
distance).

* Use is_adjacent with fov_3d for melee attacks

This changes the melee_attack code in melee.cpp for
Character::melee_attack to consider non-adjacent anything across
z-levels when fov_3d is false. (It is possible that an actual sees
check will be needed, but let's see how this does - no pun intended.)
reach_attack may be a problem still.

Co-authored-by: Jamuro-g <[email protected]>
Co-authored-by: Jamuro-g <[email protected]>
Co-authored-by: actual-nh <[email protected]>
  • Loading branch information
4 people authored Apr 6, 2021
1 parent a81b500 commit fb1d0ad
Show file tree
Hide file tree
Showing 4 changed files with 94 additions and 35 deletions.
3 changes: 2 additions & 1 deletion src/melee.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

#include "avatar.h"
#include "bodypart.h"
#include "cached_options.h"
#include "calendar.h"
#include "cata_utility.h"
#include "character.h"
Expand Down Expand Up @@ -476,7 +477,7 @@ bool Character::melee_attack( Creature &t, bool allow_special, const matec_id &f
add_msg_if_player( m_info, _( "You lack the substance to affect anything." ) );
return false;
}
if( !is_adjacent( &t, true ) ) {
if( !is_adjacent( &t, fov_3d ) ) {
return false;
}
return melee_attack_abstract( t, allow_special, force_technique, allow_unarmed );
Expand Down
5 changes: 1 addition & 4 deletions src/monmove.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1482,10 +1482,7 @@ bool monster::attack_at( const tripoint &p )
}

Character &player_character = get_player_character();
map &here = get_map();
if( p == player_character.pos() && ( sees( player_character ) ||
here.has_flag( TFLAG_RAMP_UP, p ) || here.has_flag( TFLAG_RAMP_DOWN, p ) ||
here.has_flag( TFLAG_GOES_UP, p ) || here.has_flag( TFLAG_GOES_DOWN, p ) ) ) {
if( p == player_character.pos() && sees( player_character ) ) {
return melee_attack( player_character );
}

Expand Down
5 changes: 1 addition & 4 deletions src/monster.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1502,10 +1502,7 @@ bool monster::melee_attack( Creature &target, float accuracy )
if( /*This happens sometimes*/ this == &target || !is_adjacent( &target, true ) ) {
return false;
}
map &here = get_map();
if( !sees( target ) && !here.has_flag( TFLAG_RAMP_UP, target.pos() ) &&
!here.has_flag( TFLAG_RAMP_DOWN, target.pos() ) && !here.has_flag( TFLAG_GOES_UP, target.pos() ) &&
!here.has_flag( TFLAG_GOES_DOWN, target.pos() ) ) {
if( !sees( target ) ) {
debugmsg( "Z-Level view violation: %s tried to attack %s.", disp_name(), target.disp_name() );
return false;
}
Expand Down
116 changes: 90 additions & 26 deletions tests/monster_attack_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,85 +2,149 @@
#include <iosfwd>

#include "cached_options.h"
#include "calendar.h"
#include "catch/catch.hpp"
#include "character.h"
#include "game.h"
#include "line.h"
#include "map.h"
#include "map_helpers.h"
#include "monster.h"
#include "options_helpers.h"
#include "player_helpers.h"
#include "point.h"
#include "type_id.h"
#include "weather.h"
#include "weather_type.h"

static constexpr tripoint attacker_location{ 65, 65, 0 };

static void test_monster_attack( const tripoint &target_offset, bool expected )
static void test_monster_attack( const tripoint &target_offset, bool expect_attack,
bool expect_vision )
{
int day_hour = hour_of_day<int>( calendar::turn );
CAPTURE( day_hour );
REQUIRE( is_day( calendar::turn ) );
clear_creatures();
// Monster adjacent to target.
const std::string monster_type = "mon_zombie";
const tripoint target_location = attacker_location + target_offset;
int distance = rl_dist( attacker_location, target_location );
CAPTURE( distance );
int a_zlev = attacker_location.z;
int t_zlev = target_location.z;
Character &you = get_player_character();
clear_avatar();
you.setpos( target_location );
monster &test_monster = spawn_test_monster( monster_type, attacker_location );
map &here = get_map();
// Why twice? See vision_test.cpp
here.update_visibility_cache( a_zlev );
here.invalidate_map_cache( a_zlev );
here.build_map_cache( a_zlev );
here.update_visibility_cache( a_zlev );
here.invalidate_map_cache( a_zlev );
here.build_map_cache( a_zlev );
if( a_zlev != t_zlev ) {
here.update_visibility_cache( t_zlev );
here.invalidate_map_cache( t_zlev );
here.build_map_cache( t_zlev );
here.update_visibility_cache( t_zlev );
here.invalidate_map_cache( t_zlev );
here.build_map_cache( t_zlev );
}
you.recalc_sight_limits();
// Trigger basic attack.
CAPTURE( attacker_location );
CAPTURE( target_location );
CAPTURE( fov_3d );
CHECK( test_monster.attack_at( target_location ) == expected );
CHECK( test_monster.sees( target_location ) == expect_vision );
CHECK( test_monster.attack_at( target_location ) == expect_attack );
// Then test the reverse.
clear_creatures();
clear_avatar();
you.setpos( attacker_location );
monster &target_monster = spawn_test_monster( monster_type, target_location );
CHECK( you.melee_attack( target_monster, false ) == expected );
here.update_visibility_cache( a_zlev );
here.invalidate_map_cache( a_zlev );
here.build_map_cache( a_zlev );
here.update_visibility_cache( a_zlev );
here.invalidate_map_cache( a_zlev );
here.build_map_cache( a_zlev );
if( a_zlev != t_zlev ) {
here.update_visibility_cache( t_zlev );
here.invalidate_map_cache( t_zlev );
here.build_map_cache( t_zlev );
here.update_visibility_cache( t_zlev );
here.invalidate_map_cache( t_zlev );
here.build_map_cache( t_zlev );
}
you.recalc_sight_limits();
CHECK( you.sees( target_monster ) == expect_vision );
CHECK( you.melee_attack( target_monster, false ) == expect_attack );
}

static void monster_attack_zlevel( const std::string &title, const tripoint &offset,
const std::string &monster_ter, const std::string &target_ter,
bool expected )
{
clear_map();
map &here = get_map();
SECTION( title ) {
restore_on_out_of_scope<bool> restore_fov_3d( fov_3d );
fov_3d = GENERATE( false, true );
override_option opt( "FOV_3D", fov_3d ? "true" : "false" );

std::stringstream section_name;
section_name << title;
section_name << " " << ( fov_3d ? "3d" : "2d" );

SECTION( section_name.str() ) {
here.ter_set( attacker_location, ter_id( monster_ter ) );
here.ter_set( attacker_location + offset, ter_id( target_ter ) );
test_monster_attack( offset, expected );
test_monster_attack( offset, expected && fov_3d, fov_3d );
for( const tripoint &more_offset : eight_horizontal_neighbors ) {
here.ter_set( attacker_location + offset + more_offset, ter_id( "t_floor" ) );
test_monster_attack( offset + more_offset, false );
test_monster_attack( offset + more_offset, false, expected && fov_3d );
}
}
}

TEST_CASE( "monster_attack" )
TEST_CASE( "monster_attack", "[vision][reachability]" )
{
clear_map();
restore_on_out_of_scope<time_point> restore_calendar_turn( calendar::turn );
calendar::turn = daylight_time( calendar::turn ) + 2_hours;
scoped_weather_override weather_clear( WEATHER_CLEAR );
SECTION( "attacking on open ground" ) {
// Adjacent can attack of course.
for( const tripoint &offset : eight_horizontal_neighbors ) {
test_monster_attack( offset, true );
test_monster_attack( offset, true, true );
}
clear_map();
// Too far away cannot.
test_monster_attack( { 2, 2, 0 }, false );
test_monster_attack( { 2, 1, 0 }, false );
test_monster_attack( { 2, 0, 0 }, false );
test_monster_attack( { 2, -1, 0 }, false );
test_monster_attack( { 2, -2, 0 }, false );
test_monster_attack( { 1, 2, 0 }, false );
test_monster_attack( { 1, -2, 0 }, false );
test_monster_attack( { 0, 2, 0 }, false );
test_monster_attack( { 0, -2, 0 }, false );
test_monster_attack( { -1, 2, 0 }, false );
test_monster_attack( { -1, -2, 0 }, false );
test_monster_attack( { -2, 2, 0 }, false );
test_monster_attack( { -2, 1, 0 }, false );
test_monster_attack( { -2, 0, 0 }, false );
test_monster_attack( { -2, -1, 0 }, false );
test_monster_attack( { -2, -2, 0 }, false );
test_monster_attack( { 2, 2, 0 }, false, true );
test_monster_attack( { 2, 1, 0 }, false, true );
test_monster_attack( { 2, 0, 0 }, false, true );
test_monster_attack( { 2, -1, 0 }, false, true );
test_monster_attack( { 2, -2, 0 }, false, true );
test_monster_attack( { 1, 2, 0 }, false, true );
test_monster_attack( { 1, -2, 0 }, false, true );
test_monster_attack( { 0, 2, 0 }, false, true );
test_monster_attack( { 0, -2, 0 }, false, true );
test_monster_attack( { -1, 2, 0 }, false, true );
test_monster_attack( { -1, -2, 0 }, false, true );
test_monster_attack( { -2, 2, 0 }, false, true );
test_monster_attack( { -2, 1, 0 }, false, true );
test_monster_attack( { -2, 0, 0 }, false, true );
test_monster_attack( { -2, -1, 0 }, false, true );
test_monster_attack( { -2, -2, 0 }, false, true );
}

monster_attack_zlevel( "attack_up_stairs", tripoint_above, "t_stairs_up", "t_stairs_down", true );
monster_attack_zlevel( "attack_down_stairs", tripoint_below, "t_stairs_down", "t_stairs_up", true );
monster_attack_zlevel( "attack through ceiling", tripoint_above, "t_floor", "t_floor", false );
monster_attack_zlevel( "attack through floor", tripoint_below, "t_floor", "t_floor", false );

monster_attack_zlevel( "attack up legde", tripoint_above, "t_floor", "t_floor", false );
monster_attack_zlevel( "attack down legde", tripoint_below, "t_floor", "t_floor", false );
monster_attack_zlevel( "attack up ledge", tripoint_above, "t_floor", "t_floor", false );
monster_attack_zlevel( "attack down ledge", tripoint_below, "t_floor", "t_floor", false );
}

0 comments on commit fb1d0ad

Please sign in to comment.