Skip to content

Commit

Permalink
Sane-ify in-vehicle movement size checks (#74004)
Browse files Browse the repository at this point in the history
* Common Creature::can_move_to_vehicle_tile definition

* Fix most of the movement logic

* Remove duplicate cramped space code in effects processing

* Use midpoints of size classes

* Zombies try to bash through cargo

* Use an overload so we can stop passing dummy values
  • Loading branch information
RenechCDDA authored May 23, 2024
1 parent 0b10f59 commit 61addf8
Show file tree
Hide file tree
Showing 10 changed files with 141 additions and 219 deletions.
105 changes: 5 additions & 100 deletions src/character.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8021,63 +8021,6 @@ std::string Character::weapname_ammo() const
}
}

// Tests to see if a character has room to enter a vehicle tile.
bool Character::move_in_vehicle( Creature *c, const tripoint &dest_loc ) const
{
map &m = get_map();
const optional_vpart_position vp_there = m.veh_at( dest_loc );
if( vp_there ) {
vehicle &veh = vp_there->vehicle();
units::volume capacity = 0_ml;
units::volume free_cargo = 0_ml;
auto cargo_parts = veh.get_parts_at( dest_loc, "CARGO", part_status_flag::any );
for( vehicle_part *&part : cargo_parts ) {
vehicle_stack contents = veh.get_items( *part );
const optional_vpart_position vp = m.veh_at( dest_loc );
// Check for obstacles and appliances to prevent squishing when the part is
// not a vehicle or when the player is not actually entering the tile IE grabbing.
if( !vp.part_with_feature( "CARGO_PASSABLE", false ) &&
!vp.part_with_feature( "APPLIANCE", false ) && !vp.part_with_feature( "OBSTACLE", false ) ) {
capacity += contents.max_volume();
free_cargo += contents.free_volume();
}
}
if( capacity > 0_ml ) {
// First, we'll try to squeeze in. Open-topped vehicle parts have more room for us.
if( !veh.enclosed_at( dest_loc ) ) {
free_cargo *= 1.2;
}
const creature_size size = get_size();
if( ( size == creature_size::tiny && free_cargo < 15625_ml ) ||
( size == creature_size::small && free_cargo < 31250_ml ) ||
( size == creature_size::medium && free_cargo < 62500_ml ) ||
( size == creature_size::large && free_cargo < 125000_ml ) ||
( size == creature_size::huge && free_cargo < 250000_ml ) ) {
if( ( size == creature_size::tiny && free_cargo < 11719_ml ) ||
( size == creature_size::small && free_cargo < 23438_ml ) ||
( size == creature_size::medium && free_cargo < 46875_ml ) ||
( size == creature_size::large && free_cargo < 93750_ml ) ||
( size == creature_size::huge && free_cargo < 187500_ml ) ) {
add_msg_if_player( m_warning, _( "There's not enough room for you to fit there." ) );
return false; // Even if we squeeze, there's no room.
}
c->add_effect( effect_cramped_space, 2_turns, true );
return true;
}
}
const optional_vpart_position vp = m.veh_at( dest_loc );
// Sufficiently gigantic characters aren't comfortable in stock seats, roof or no.
if( in_vehicle && get_size() == creature_size::huge && !vp.part_with_feature( "AISLE", false ) &&
!vp.part_with_feature( "HUGE_OK", false ) && !has_effect( effect_cramped_space ) ) {
add_msg_if_player( m_warning, _( "You barely fit in this tiny human vehicle." ) );
add_msg_if_npc( m_warning, _( "%s has to really cram their huge body to fit." ), c->disp_name() );
c->add_effect( effect_cramped_space, 2_turns, true );
return true;
}
}
return true;
}

void Character::on_hit( Creature *source, bodypart_id bp_hit,
float /*difficulty*/, dealt_projectile_attack const *const proj )
{
Expand Down Expand Up @@ -11127,52 +11070,14 @@ void Character::process_effects()
}

// Being stuck in tight spaces sucks. TODO: could be expanded to apply to non-vehicle conditions.
if( has_effect( effect_cramped_space ) ) {
map &here = get_map();
const tripoint your_pos = pos();
const optional_vpart_position vp_there = here.veh_at( your_pos );
if( !vp_there ) {
remove_effect( effect_cramped_space );
return;
}
if( is_npc() && !has_effect( effect_narcosis ) && has_effect( effect_cramped_space ) ) {
bool cramped = has_effect( effect_cramped_space );
// return is intentionally discarded, sets cramped if appropriate
can_move_to_vehicle_tile( get_map().getglobal( pos() ), cramped );
if( cramped ) {
if( is_npc() && !has_effect( effect_narcosis ) ) {
npc &as_npc = dynamic_cast<npc &>( *this );
as_npc.complain_about( "cramped_vehicle", 30_minutes, "<cramped_vehicle>", false );
}
bool is_cramped_space = false;
vehicle &veh = vp_there->vehicle();
units::volume capacity = 0_ml;
units::volume free_cargo = 0_ml;
auto cargo_parts = veh.get_parts_at( your_pos, "CARGO", part_status_flag::any );
for( vehicle_part *&part : cargo_parts ) {
vehicle_stack contents = veh.get_items( *part );
const optional_vpart_position vp = here.veh_at( your_pos );
if( !vp.part_with_feature( "CARGO_PASSABLE", false ) ) {
capacity += contents.max_volume();
free_cargo += contents.free_volume();
}
const creature_size size = get_size();
if( capacity > 0_ml ) {
// Open-topped vehicle parts have more room.
if( !veh.enclosed_at( your_pos ) ) {
free_cargo *= 1.2;
}
if( ( size == creature_size::tiny && free_cargo < 15625_ml ) ||
( size == creature_size::small && free_cargo < 31250_ml ) ||
( size == creature_size::medium && free_cargo < 62500_ml ) ||
( size == creature_size::large && free_cargo < 125000_ml ) ||
( size == creature_size::huge && free_cargo < 250000_ml ) ) {
is_cramped_space = true;
}
}
if( get_size() == creature_size::huge && !vp.part_with_feature( "AISLE", false ) &&
!vp.part_with_feature( "HUGE_OK", false ) ) {
is_cramped_space = true;
}
}
if( !is_cramped_space ) {
remove_effect( effect_cramped_space );
}
}

Creature::process_effects();
Expand Down
3 changes: 0 additions & 3 deletions src/character.h
Original file line number Diff line number Diff line change
Expand Up @@ -1438,9 +1438,6 @@ class Character : public Creature, public visitable
void dismount();
void forced_dismount();

/** Attempt to enter a tile in a vehicle */
bool move_in_vehicle( Creature *c, const tripoint &dest_loc ) const;

bool is_deaf() const;
bool is_mute() const;
// Get the specified limb score. If bp is defined, only the scores from that body part type are summed.
Expand Down
89 changes: 89 additions & 0 deletions src/creature.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,95 @@ void Creature::setpos( const tripoint &p )
on_move( old_loc );
}

static units::volume size_to_volume( creature_size size_class )
{
// returns midpoint of size from volume_to_size, minus 1_ml
// e.g. max tiny size is 7500, max small size is 46250, we return
// 46250+7500 / 2 - 1_ml = 26875_ml - 1ml
// This is still stupid and both of these functions should be merged into one single source of truth.
if( size_class == creature_size::tiny ) {
return 3749_ml;
} else if( size_class == creature_size::small ) {
return 26874_ml;
} else if( size_class == creature_size::medium ) {
return 77124_ml;
} else if( size_class == creature_size::large ) {
return 295874_ml;
}
return 741874_ml;
}

bool Creature::can_move_to_vehicle_tile( const tripoint_abs_ms &loc, bool &cramped ) const
{
map &here = get_map();
const optional_vpart_position vp_there = here.veh_at( loc );
if( !vp_there ) {
return true;
}

const monster *mon = as_monster();

vehicle &veh = vp_there->vehicle();

std::vector<vehicle_part *> cargo_parts;
cargo_parts = veh.get_parts_at( here.bub_from_abs( loc ), "CARGO", part_status_flag::any );

units::volume capacity = 0_ml;
units::volume free_cargo = 0_ml;
for( vehicle_part *part : cargo_parts ) {
vehicle_stack contents = veh.get_items( *part );
if( !vp_there.part_with_feature( "CARGO_PASSABLE", false ) &&
!vp_there.part_with_feature( "APPLIANCE", false ) &&
!vp_there.part_with_feature( "OBSTACLE", false ) ) {
capacity += contents.max_volume();
free_cargo += contents.free_volume();
}
}
if( capacity > 0_ml ) {
// First, we'll try to squeeze in. Open-topped vehicle parts have more room to step over cargo.
if( !veh.enclosed_at( here.getlocal( loc ) ) ) {
free_cargo *= 1.2;
}
const creature_size size = get_size();
units::volume critter_volume;
if( mon ) {
critter_volume = mon->get_volume();
} else {
critter_volume = size_to_volume( size );
}

if( critter_volume > free_cargo ) {
return false;
}

if( critter_volume > size_to_volume( creature_size::large ) &&
!vp_there.part_with_feature( "HUGE_OK", false ) ) {
return false;
}

if( critter_volume < free_cargo * 1.33 ) {
if( !mon || !( mon->type->bodytype == "snake" || mon->type->bodytype == "blob" ||
mon->type->bodytype == "fish" ||
has_flag( mon_flag_PLASTIC ) || has_flag( mon_flag_SMALL_HIDER ) ) ) {
cramped = true;
}
}

if( size == creature_size::huge && !vp_there.part_with_feature( "AISLE", false ) &&
!vp_there.part_with_feature( "HUGE_OK", false ) ) {
cramped = true;
}
}

return true;
}

bool Creature::can_move_to_vehicle_tile( const tripoint_abs_ms &loc ) const
{
bool dummy = false;
return can_move_to_vehicle_tile( loc, dummy );
}

void Creature::move_to( const tripoint_abs_ms &loc )
{
const tripoint_abs_ms old_loc = get_location();
Expand Down
4 changes: 4 additions & 0 deletions src/creature.h
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,10 @@ class Creature : public viewer
return get_location().z();
}
void setpos( const tripoint &p );
/** Checks if the creature fits into a given tile. Set the boolean argument to true if the creature would barely fit. */
bool can_move_to_vehicle_tile( const tripoint_abs_ms &loc, bool &cramped ) const;
/** Helper overload for when the boolean is discardable */
bool can_move_to_vehicle_tile( const tripoint_abs_ms &loc ) const;
/** Moves the creature to the given location and calls the on_move() handler. */
void move_to( const tripoint_abs_ms &loc );

Expand Down
10 changes: 9 additions & 1 deletion src/game.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,7 @@ static const efftype_id effect_asked_to_train( "asked_to_train" );
static const efftype_id effect_blind( "blind" );
static const efftype_id effect_bouldering( "bouldering" );
static const efftype_id effect_contacts( "contacts" );
static const efftype_id effect_cramped_space( "cramped_space" );
static const efftype_id effect_docile( "docile" );
static const efftype_id effect_downed( "downed" );
static const efftype_id effect_fake_common_cold( "fake_common_cold" );
Expand Down Expand Up @@ -10724,7 +10725,9 @@ bool game::walk_move( const tripoint &dest_loc, const bool via_ramp, const bool
}
u.set_underwater( false );

if( vp_there && !u.move_in_vehicle( static_cast<Creature *>( &u ), dest_loc ) ) {
bool cramped = false;
if( vp_there && !u.can_move_to_vehicle_tile( get_map().getglobal( dest_loc ), cramped ) ) {
add_msg( m_warning, _( "There's not enough room for you to fit there." ) );
return false;
}

Expand Down Expand Up @@ -10941,6 +10944,11 @@ bool game::walk_move( const tripoint &dest_loc, const bool via_ramp, const bool
start_hauling( oldpos );
}

if( cramped ) { // passed by reference, can_move_to_vehicle_tile sets to true if actually cramped
add_msg( m_warning, _( "You barely fit in this tiny human vehicle." ) );
u.add_effect( effect_cramped_space, 2_turns, true );
}

on_move_effects();

return true;
Expand Down
69 changes: 8 additions & 61 deletions src/monmove.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -132,64 +132,6 @@ static bool z_is_valid( int z )
return z >= -OVERMAP_DEPTH && z <= OVERMAP_HEIGHT;
}

bool monster::monster_move_in_vehicle( const tripoint &p ) const
{
map &m = get_map();
monster critter = *this;
const optional_vpart_position vp = m.veh_at( p );
if( vp.has_value() ) {
vehicle &veh = vp->vehicle();
units::volume capacity = 0_ml;
units::volume free_cargo = 0_ml;
auto cargo_parts = veh.get_parts_at( p, "CARGO", part_status_flag::any );
for( vehicle_part *&part : cargo_parts ) {
vehicle_stack contents = veh.get_items( *part );
if( !vp.part_with_feature( "CARGO_PASSABLE", false ) &&
!vp.part_with_feature( "APPLIANCE", false ) && !vp.part_with_feature( "OBSTACLE", false ) ) {
capacity += contents.max_volume();
free_cargo += contents.free_volume();
}
}
if( capacity > 0_ml ) {
// First, we'll try to squeeze in. Open-topped vehicle parts have more room to step over cargo.
if( !veh.enclosed_at( p ) ) {
free_cargo *= 1.2;
}
if( !veh.enclosed_at( p ) && flies() ) {
return true; // No amount of cargo will block a flying monster if there's no roof.
}
const creature_size size = get_size();
if( ( size == creature_size::tiny && free_cargo < 15625_ml ) ||
( size == creature_size::small && free_cargo < 31250_ml ) ||
( size == creature_size::medium && free_cargo < 62500_ml ) ||
( size == creature_size::large && free_cargo < 125000_ml ) ||
( size == creature_size::huge && free_cargo < 250000_ml ) ) {
if( ( size == creature_size::tiny && free_cargo < 11719_ml ) ||
( size == creature_size::small && free_cargo < 23438_ml ) ||
( size == creature_size::medium && free_cargo < 46875_ml ) ||
( size == creature_size::large && free_cargo < 93750_ml ) ||
( size == creature_size::huge && free_cargo < 187500_ml ) ||
( get_volume() > 850000_ml && !vp.part_with_feature( "HUGE_OK", false ) ) ) {
return false; // Return false if there's just no room whatsoever. Anything over 850 liters will simply never fit in a vehicle part that isn't specifically made for it.
// I'm sorry but you can't let a kaiju ride shotgun.
}
if( type->bodytype == "snake" || type->bodytype == "blob" || type->bodytype == "fish" ||
has_flag( mon_flag_PLASTIC ) || has_flag( mon_flag_SMALL_HIDER ) ) {
return true; // Return true if we're wiggly enough to be fine with cramped space.
}
critter.add_effect( effect_cramped_space, 2_turns, true );
return true; // Otherwise we add the effect and return true.
}
if( size == creature_size::huge && !vp.part_with_feature( "AISLE", false ) &&
!vp.part_with_feature( "HUGE_OK", false ) ) {
critter.add_effect( effect_cramped_space, 2_turns, true );
return true; // Sufficiently gigantic creatures have trouble in stock seats, roof or no.
}
}
}
return true;
}

bool monster::will_move_to( const tripoint &p ) const
{
map &here = get_map();
Expand Down Expand Up @@ -1691,7 +1633,8 @@ bool monster::bash_at( const tripoint &p )
return false;
}

bool try_bash = !can_move_to( p ) || one_in( 3 );
const bool too_cramped = !can_move_to_vehicle_tile( get_map().getglobal( p ) );
bool try_bash = !can_move_to( p ) || one_in( 3 ) || too_cramped;
if( !try_bash ) {
return false;
}
Expand All @@ -1701,7 +1644,7 @@ bool monster::bash_at( const tripoint &p )
}

map &here = get_map();
if( !( here.is_bashable_furn( p ) || here.veh_at( p ).obstacle_at_part() ) ) {
if( !( here.is_bashable_furn( p ) || here.veh_at( p ).obstacle_at_part() || too_cramped ) ) {
// if the only thing here is road or flat, rarely bash it
bool flat_ground = here.has_flag( ter_furn_flag::TFLAG_ROAD, p ) ||
here.has_flag( ter_furn_flag::TFLAG_FLAT, p );
Expand Down Expand Up @@ -1872,7 +1815,8 @@ bool monster::move_to( const tripoint &p, bool force, bool step_on_critter,
}
}

if( here.veh_at( p ).part_with_feature( VPFLAG_CARGO, true ) && !monster_move_in_vehicle( p ) ) {
bool cramped = false; // applies an effect if monster does end up moving there
if( !can_move_to_vehicle_tile( here.getglobal( p ), cramped ) ) {
return false;
}

Expand Down Expand Up @@ -1970,6 +1914,9 @@ bool monster::move_to( const tripoint &p, bool force, bool step_on_critter,
optional_vpart_position vp_dest = here.veh_at( destination );
if( vp_dest ) {
vp_dest->vehicle().invalidate_mass();
if( cramped ) {
add_effect( effect_cramped_space, 2_turns, true );
}
}
if( is_hallucination() ) {
//Hallucinations don't do any of the stuff after this point
Expand Down
Loading

0 comments on commit 61addf8

Please sign in to comment.