Skip to content

Commit

Permalink
Merge pull request #34179 from BevapDin/avr
Browse files Browse the repository at this point in the history
Fix vehicle accessing the main map during mapgen:
  • Loading branch information
ZhilkinSerg authored Sep 22, 2019
2 parents 0c8a30a + b768c7e commit 867b9c2
Show file tree
Hide file tree
Showing 5 changed files with 170 additions and 64 deletions.
4 changes: 2 additions & 2 deletions src/map_extras.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -412,9 +412,9 @@ static void mx_helicopter( map &m, const tripoint &abs_sub )
break;
}
if( !one_in( 4 ) ) {
wreckage->smash( 0.8f, 1.2f, 1.0f, point( dice( 1, 8 ) - 5, dice( 1, 8 ) - 5 ), 6 + dice( 1, 10 ) );
wreckage->smash( m, 0.8f, 1.2f, 1.0f, point( dice( 1, 8 ) - 5, dice( 1, 8 ) - 5 ), 6 + dice( 1, 10 ) );
} else {
wreckage->smash( 0.1f, 0.9f, 1.0f, point( dice( 1, 8 ) - 5, dice( 1, 8 ) - 5 ), 6 + dice( 1, 10 ) );
wreckage->smash( m, 0.1f, 0.9f, 1.0f, point( dice( 1, 8 ) - 5, dice( 1, 8 ) - 5 ), 6 + dice( 1, 10 ) );
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/mapgen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6887,7 +6887,7 @@ std::unique_ptr<vehicle> map::add_vehicle_to_map(
// Try again with the wreckage
std::unique_ptr<vehicle> new_veh = add_vehicle_to_map( std::move( wreckage ), true );
if( new_veh != nullptr ) {
new_veh->smash();
new_veh->smash( *this );
return new_veh;
}

Expand All @@ -6913,7 +6913,7 @@ std::unique_ptr<vehicle> map::add_vehicle_to_map(
}

if( needs_smashing ) {
veh->smash();
veh->smash( *this );
}

return veh;
Expand Down
2 changes: 1 addition & 1 deletion src/monmove.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1902,7 +1902,7 @@ void monster::shove_vehicle( const tripoint &remote_destination,
g->m.move_vehicle( veh, shove_destination, veh.face );
}
veh.move = tileray( point( destination_delta_x, destination_delta_y ) );
veh.smash( shove_damage_min, shove_damage_max, 0.10F );
veh.smash( g->m, shove_damage_min, shove_damage_max, 0.10F );
}
}
}
Expand Down
209 changes: 153 additions & 56 deletions src/vehicle.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,121 @@ const int bat_energy_j = 1000;
// Point dxs for the adjacent cardinal tiles.
point vehicles::cardinal_d[5] = { point_west, point_east, point_north, point_south, point_zero };

// For reference what each function is supposed to do, see their implementation in
// @ref DefaultRemovePartHandler. Add compatible code for it into @ref MapgenRemovePartHandler,
// if needed.
class RemovePartHandler
{
public:
virtual ~RemovePartHandler() = default;

virtual void unboard( const tripoint &loc ) = 0;
virtual void add_item_or_charges( const tripoint &loc, item it ) = 0;
virtual void set_transparency_cache_dirty( int z ) = 0;
virtual void removed( vehicle &veh, int part ) = 0;
virtual void spawn_animal_from_part( item &base, const tripoint &loc ) = 0;
};

class DefaultRemovePartHandler : public RemovePartHandler
{
public:
~DefaultRemovePartHandler() override = default;

void unboard( const tripoint &loc ) override {
g->m.unboard_vehicle( loc );
}
void add_item_or_charges( const tripoint &loc, item it ) override {
g->m.add_item_or_charges( loc, std::move( it ) );
}
void set_transparency_cache_dirty( const int z ) override {
g->m.set_transparency_cache_dirty( z );
}
void removed( vehicle &veh, const int part ) override {
// If the player is currently working on the removed part, stop them as it's futile now.
const player_activity &act = g->u.activity;
if( act.id() == activity_id( "ACT_VEHICLE" ) && act.moves_left > 0 && act.values.size() > 6 ) {
if( veh_pointer_or_null( g->m.veh_at( tripoint( act.values[0], act.values[1],
g->u.posz() ) ) ) == &veh ) {
if( act.values[6] >= part ) {
g->u.cancel_activity();
add_msg( m_info, _( "The vehicle part you were working on has gone!" ) );
}
}
}
// @todo maybe do this for all the nearby NPCs as well?

if( g->u.get_grab_type() == OBJECT_VEHICLE && g->u.grab_point == veh.global_part_pos3( part ) ) {
if( veh.parts_at_relative( veh.parts[part].mount, false ).empty() ) {
add_msg( m_info, _( "The vehicle part you were holding has been destroyed!" ) );
g->u.grab( OBJECT_NONE );
}
}

g->m.dirty_vehicle_list.insert( &veh );
}
void spawn_animal_from_part( item &base, const tripoint &loc ) override {
tripoint target = loc;
bool spawn = true;
if( !g->is_empty( target ) ) {
std::vector<tripoint> valid;
for( const tripoint &dest : g->m.points_in_radius( target, 1 ) ) {
if( g->is_empty( dest ) ) {
valid.push_back( dest );
}
}
if( valid.empty() ) {
spawn = false;
} else {
target = random_entry( valid );
}
}
base.release_monster( target, spawn );
}
};

class MapgenRemovePartHandler : public RemovePartHandler
{
private:
map &m;

public:
MapgenRemovePartHandler( map &m ) : m( m ) { }

~MapgenRemovePartHandler() override = default;

void unboard( const tripoint &/*loc*/ ) override {
debugmsg( "Tried to unboard during mapgen!" );
// Ignored. Will almost certainly not be called anyway, because
// there are no creatures that could have been mounted during mapgen.
}
void add_item_or_charges( const tripoint &loc, item it ) override {
if( !m.inbounds( loc ) ) {
debugmsg( "Tried to put item %s on invalid tile %d,%d,%d during mapgen!", it.tname(), loc.x, loc.y,
loc.z );
tripoint copy = loc;
m.clip_to_bounds( copy );
assert( m.inbounds( copy ) ); // prevent infinite recursion
add_item_or_charges( copy, std::move( it ) );
return;
}
m.add_item_or_charges( loc, std::move( it ) );
}
void set_transparency_cache_dirty( const int /*z*/ ) override {
// Ignored for now. We don't initialize the transparency cache in mapgen anyway.
}
void removed( vehicle &veh, const int /*part*/ ) override {
// @todo check if this is necessary, it probably isn't during mapgen
m.dirty_vehicle_list.insert( &veh );
}
void spawn_animal_from_part( item &/*base*/, const tripoint &/*loc*/ ) override {
debugmsg( "Tried to spawn animal from vehicle part during mapgen!" );
// Ignored. The base item will not be changed and will spawn as is:
// still containing the animal.
// This should not happend during mapgen anyway.
// @todo *if* this actually happens: create a spawn point for the animal instead.
}
};

// Vehicle stack methods.
vehicle_stack::iterator vehicle_stack::erase( vehicle_stack::const_iterator it )
{
Expand Down Expand Up @@ -693,7 +808,7 @@ void vehicle::do_autodrive()
* (ie, any spot with multiple frames) will be completely destroyed, as that
* was the collision point.
*/
void vehicle::smash( float hp_percent_loss_min, float hp_percent_loss_max,
void vehicle::smash( map &m, float hp_percent_loss_min, float hp_percent_loss_max,
float percent_of_parts_to_affect, point damage_origin, float damage_size )
{
for( auto &part : parts ) {
Expand Down Expand Up @@ -734,6 +849,8 @@ void vehicle::smash( float hp_percent_loss_min, float hp_percent_loss_max,
}
}
}

std::unique_ptr<RemovePartHandler> handler_ptr;
// clear out any duplicated locations
for( int p = static_cast<int>( parts.size() ) - 1; p >= 0; p-- ) {
vehicle_part &part = parts[ p ];
Expand All @@ -749,7 +866,18 @@ void vehicle::smash( float hp_percent_loss_min, float hp_percent_loss_max,
if( ( part_info( p ).location.empty() &&
part_info( p ).get_id() == part_info( other_p ).get_id() ) ||
( part_info( p ).location == part_info( other_p ).location ) ) {
remove_part( other_p );
// Deferred creation of the handler to here so it is only created when actually needed.
if( !handler_ptr ) {
// This is a heuristic: we just assume the default handler is good enough when called
// on the main game map. And assume that we run from some mapgen code if called on
// another instance.
if( g && &g->m == &m ) {
handler_ptr = std::make_unique<DefaultRemovePartHandler>();
} else {
handler_ptr = std::make_unique<MapgenRemovePartHandler>( m );
}
}
remove_part( other_p, *handler_ptr );
}
}
}
Expand Down Expand Up @@ -1700,8 +1828,19 @@ bool vehicle::merge_rackable_vehicle( vehicle *carry_veh, const std::vector<int>
* Mark a part as removed from the vehicle.
* @return bool true if the vehicle's 0,0 point shifted.
*/
bool vehicle::remove_part( int p )
bool vehicle::remove_part( const int p )
{
DefaultRemovePartHandler handler;
return remove_part( p, handler );
}

bool vehicle::remove_part( const int p, RemovePartHandler &handler )
{
// NOTE: Don't access g or g->m or anything from it directly here.
// Forward all access to the handler.
// There are currently two implementations of it:
// - one for normal game play (vehicle is on the main map g->m),
// - one for mapgen (vehicle is on a temporary map used only during mapgen).
if( p >= static_cast<int>( parts.size() ) ) {
debugmsg( "Tried to remove part %d but only %d parts!", p, parts.size() );
return false;
Expand All @@ -1717,13 +1856,8 @@ bool vehicle::remove_part( int p )
const tripoint part_loc = global_part_pos3( p );

// Unboard any entities standing on removed boardable parts
if( part_flag( p, "BOARDABLE" ) ) {
std::vector<int> bp = boarded_parts();
for( auto &elem : bp ) {
if( elem == p ) {
g->m.unboard_vehicle( part_loc );
}
}
if( part_flag( p, "BOARDABLE" ) && parts[p].has_flag( vehicle_part::passenger_flag ) ) {
handler.unboard( part_loc );
}

// If `p` has flag `parent_flag`, remove child with flag `child_flag`
Expand All @@ -1733,9 +1867,8 @@ bool vehicle::remove_part( int p )
if( part_flag( p, parent_flag ) ) {
int dep = part_with_feature( p, child_flag, false );
if( dep >= 0 ) {
item it = parts[dep].properties_to_item();
g->m.add_item_or_charges( part_loc, it );
remove_part( dep );
handler.add_item_or_charges( part_loc, parts[dep].properties_to_item() );
remove_part( dep, handler );
return true;
}
}
Expand All @@ -1745,31 +1878,16 @@ bool vehicle::remove_part( int p )
// if a windshield is removed (usually destroyed) also remove curtains
// attached to it.
if( remove_dependent_part( "WINDOW", "CURTAIN" ) || part_flag( p, VPFLAG_OPAQUE ) ) {
g->m.set_transparency_cache_dirty( sm_pos.z );
handler.set_transparency_cache_dirty( sm_pos.z );
}

remove_dependent_part( "SEAT", "SEATBELT" );
remove_dependent_part( "BATTERY_MOUNT", "NEEDS_BATTERY_MOUNT" );

// Release any animal held by the part
if( parts[p].has_flag( vehicle_part::animal_flag ) ) {
tripoint target = part_loc;
bool spawn = true;
if( !g->is_empty( target ) ) {
std::vector<tripoint> valid;
for( const tripoint &dest : g->m.points_in_radius( target, 1 ) ) {
if( g->is_empty( dest ) ) {
valid.push_back( dest );
}
}
if( valid.empty() ) {
spawn = false;
} else {
target = random_entry( valid );
}
}
item base = item( parts[p].get_base() );
base.release_monster( target, spawn );
handler.spawn_animal_from_part( base, part_loc );
parts[p].set_base( base );
parts[p].remove_flag( vehicle_part::animal_flag );
}
Expand Down Expand Up @@ -1807,41 +1925,20 @@ bool vehicle::remove_part( int p )
parts[p].removed = true;
removed_part_count++;

// If the player is currently working on the removed part, stop them as it's futile now.
const player_activity &act = g->u.activity;
if( act.id() == activity_id( "ACT_VEHICLE" ) && act.moves_left > 0 && act.values.size() > 6 ) {
if( veh_pointer_or_null( g->m.veh_at( tripoint( act.values[0], act.values[1],
g->u.posz() ) ) ) == this ) {
if( act.values[6] >= p ) {
g->u.cancel_activity();
add_msg( m_info, _( "The vehicle part you were working on has gone!" ) );
}
}
}
handler.removed( *this, p );

const point &vp_mount = parts[p].mount;
const auto iter = labels.find( label( vp_mount ) );
const bool no_label = iter != labels.end();
const bool grab_found = g->u.get_grab_type() == OBJECT_VEHICLE && g->u.grab_point == part_loc;
// Checking these twice to avoid calling the relatively expensive parts_at_relative() unnecessarily.
if( no_label || grab_found ) {
if( parts_at_relative( vp_mount, false ).empty() ) {
if( no_label ) {
labels.erase( iter );
}
if( grab_found ) {
add_msg( m_info, _( "The vehicle part you were holding has been destroyed!" ) );
g->u.grab( OBJECT_NONE );
}
}
if( iter != labels.end() && parts_at_relative( vp_mount, false ).empty() ) {
labels.erase( iter );
}

for( auto &i : get_items( p ) ) {
// Note: this can spawn items on the other side of the wall!
// @todo fix this ^^
tripoint dest( part_loc + point( rng( -3, 3 ), rng( -3, 3 ) ) );
g->m.add_item_or_charges( dest, i );
handler.add_item_or_charges( dest, i );
}
g->m.dirty_vehicle_list.insert( this );
refresh();
coeff_air_changed = true;
return shift_if_needed();
Expand Down
15 changes: 12 additions & 3 deletions src/vehicle.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ class Creature;
class nc_color;
class player;
class npc;
class map;
class vehicle;
class vehicle_part_range;
class JsonIn;
Expand Down Expand Up @@ -524,6 +525,8 @@ struct label : public point {
std::string text;
};

class RemovePartHandler;

/**
* A vehicle as a whole with all its components.
*
Expand Down Expand Up @@ -720,9 +723,8 @@ class vehicle
void init_state( int init_veh_fuel, int init_veh_status );

// damages all parts of a vehicle by a random amount
void smash( float hp_percent_loss_min = 0.1f, float hp_percent_loss_max = 1.2f,
float percent_of_parts_to_affect = 1.0f, point damage_origin = point_zero,
float damage_size = 0 );
void smash( map &m, float hp_percent_loss_min = 0.1f, float hp_percent_loss_max = 1.2f,
float percent_of_parts_to_affect = 1.0f, point damage_origin = point_zero, float damage_size = 0 );

void serialize( JsonOut &json ) const;
void deserialize( JsonIn &jsin );
Expand Down Expand Up @@ -804,6 +806,13 @@ class vehicle
// merge a previously found single tile vehicle into this vehicle
bool merge_rackable_vehicle( vehicle *carry_veh, const std::vector<int> &rack_parts );

/**
* @param handler A class that receives various callbacks, e.g. for placing items.
* This handler is different when called during mapgen (when items need to be placed
* on the temporary mapgen map), and when called during normal game play (when items
* go on the main map g->m).
*/
bool remove_part( int p, RemovePartHandler &handler );
bool remove_part( int p );
void part_removal_cleanup();

Expand Down

0 comments on commit 867b9c2

Please sign in to comment.