Skip to content

Commit

Permalink
feat(port): move vehicles in elevator
Browse files Browse the repository at this point in the history
DDA PR: CleverRaven/Cataclysm-DDA#58840

Co-authored-by: andrei <[email protected]>
  • Loading branch information
scarf005 and andrei8l committed Sep 16, 2023
1 parent 14585ad commit 6e23a8c
Show file tree
Hide file tree
Showing 2 changed files with 222 additions and 96 deletions.
96 changes: 0 additions & 96 deletions src/iexamine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,6 @@ static const std::string flag_SPLINT( "SPLINT" );
static const std::string flag_VARSIZE( "VARSIZE" );
static const std::string flag_WALL( "WALL" );
static const std::string flag_WRITE_MESSAGE( "WRITE_MESSAGE" );
static const std::string flag_ELEVATOR( "ELEVATOR" );

// @TODO maybe make this a property of the item (depend on volume/type)
static const time_duration milling_time = 6_hours;
Expand Down Expand Up @@ -879,101 +878,6 @@ void iexamine::toilet( player &p, const tripoint &examp )
}
}

/**
* If underground, move 2 levels up else move 2 levels down. Stable movement between levels 0 and -2.
*/
void iexamine::elevator( player &p, const tripoint &examp )
{
map &here = get_map();
if( !query_yn( _( "Use the %s?" ), here.tername( examp ) ) ) {
return;
}
int movez = ( examp.z < 0 ? 2 : -2 );

tripoint original_floor_omt = ms_to_omt_copy( here.getabs( examp ) );
tripoint new_floor_omt = original_floor_omt + tripoint( point_zero, movez );


// first find critters in the destination elevator and move them out of the way
for( Creature &critter : g->all_creatures() ) {
if( critter.is_player() ) {
continue;
}

if( !here.has_flag( flag_ELEVATOR, critter.pos() ) ) {
continue;
}

tripoint critter_omt = ms_to_omt_copy( here.getabs( critter.pos() ) );
if( critter_omt != new_floor_omt ) {
continue;
}

for( const tripoint &candidate : closest_points_first( critter.pos(), 10 ) ) {
if( !here.has_flag( flag_ELEVATOR, candidate ) &&
here.passable( candidate ) &&
!g->critter_at( candidate ) ) {
critter.setpos( candidate );
break;
}
}
}

// TODO: do we have struct or pair to indicate from -> to?
const auto move_item = [&]( map_stack & items, const tripoint & src, const tripoint & dest ) {
for( auto it = items.begin(); it != items.end(); ) {
here.add_item_or_charges( dest, *it );
it = here.i_rem( src, it );
}
};

const auto first_elevator_tile = [&]( const tripoint & pos ) -> tripoint {
for( const tripoint &candidate : closest_points_first( pos, 10 ) )
{
if( here.has_flag( flag_ELEVATOR, candidate ) ) {
return candidate;
}
}
return pos;
};

// move along every item in the elevator
for( const tripoint &pos : closest_points_first( p.pos(), 10 ) ) {
if( here.has_flag( flag_ELEVATOR, pos ) ) {
map_stack items = here.i_at( pos );
tripoint dest = first_elevator_tile( pos + tripoint( 0, 0, movez ) );
move_item( items, pos, dest );
}
}

// move the player
g->vertical_move( movez, false );

// finally, bring along everyone who was in the elevator with the player
for( Creature &critter : g->all_creatures() ) {
if( critter.is_player() ) {
continue;
}

if( !here.has_flag( flag_ELEVATOR, critter.pos() ) ) {
continue;
}

tripoint critter_omt = ms_to_omt_copy( here.getabs( critter.pos() ) );
if( critter_omt != original_floor_omt ) {
continue;
}

for( const tripoint &candidate : closest_points_first( p.pos(), 10 ) ) {
if( here.has_flag( flag_ELEVATOR, candidate ) && candidate != p.pos() &&
!g->critter_at( candidate ) ) {
critter.setpos( candidate );
break;
}
}
}
}

/**
* Open or close gate.
*/
Expand Down
222 changes: 222 additions & 0 deletions src/iexamine_elevator.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,222 @@
#include "game.h"
#include "iexamine.h"
#include "mapdata.h"
#include "output.h"
#include "omdata.h"
#include "overmapbuffer.h"
#include "player.h"
#include "coordinates.h"
#include "map.h"
#include "point.h"
#include "ui.h"
#include "vpart_range.h"
#include "point_rotate.h"

namespace
{

using elevator_tiles = std::vector<tripoint>;
auto get_elevator_here( const Character &you ) -> elevator_tiles
{
const auto &here = get_map();
const auto &points = closest_points_first( you.pos(), SEEX - 1 );

elevator_tiles tiles;
std::copy_if( points.begin(), points.end(), std::back_inserter( tiles ),
[&here]( const tripoint & pos ) {
return here.has_flag( TFLAG_ELEVATOR, pos );
} );

return tiles;
}

auto get_elevator_dest( const elevator_tiles &elevator_here,
const tripoint &sm_orig,
int turns,
int movez ) -> elevator_tiles
{
elevator_tiles tiles;
std::transform( elevator_here.begin(), elevator_here.end(), std::back_inserter( tiles ),
[turns, &sm_orig, movez]( tripoint const & p ) {
return rotate_point_sm( { p.xy(), movez }, sm_orig, turns );
} );

return tiles;
}

auto choose_elevator_destz( const tripoint &examp, const tripoint_abs_omt &this_omt,
const tripoint &sm_orig ) -> int
{
const auto this_floor = _( " (this floor)" );

map &here = get_map();
uilist choice;
choice.title = _( "Select destination floor" );
for( int z = OVERMAP_HEIGHT; z >= -OVERMAP_DEPTH; z-- ) {
const tripoint_abs_omt that_omt{ this_omt.xy(), z };
const int delta = get_rot_delta( this_omt, that_omt );
const tripoint zp =
rotate_point_sm( { examp.xy(), z }, sm_orig, delta );

if( here.ter( zp )->examine == &iexamine::elevator ) {
const std::string omt_name = overmap_buffer.ter_existing( that_omt )->get_name();
const auto floor_name = z == examp.z ? this_floor : "";
const std::string name = string_format( "%3iF %s%s", z, omt_name, floor_name );

choice.addentry( z, z != examp.z, MENU_AUTOASSIGN, name );
}
}
choice.query();
return choice.ret;
}

} // namespace

namespace
{

enum class overlap_status { outside, inside, overlap };

auto vehicle_status( const wrapped_vehicle &veh, const elevator_tiles &tiles ) -> overlap_status
{
const auto &ps = veh.v->get_all_parts();
const int all_vparts_count = ps.part_count();
const int vparts_inside = std::count_if( ps.begin(), ps.end(), [&]( const vpart_reference & vp ) {
const tripoint p = veh.pos + vp.part().precalc[0];
const auto eit = std::find( tiles.cbegin(), tiles.cend(), p );
return eit != tiles.cend();
} );

if( vparts_inside == all_vparts_count ) {
return overlap_status::inside;
} else if( vparts_inside == 0 ) {
return overlap_status::outside;
} else {
return overlap_status::overlap;
}
}

struct elevator_vehicles {
bool blocking = false;
std::vector<vehicle *> v;
};

auto get_vehicles_on_elevator( const elevator_tiles &tiles ) -> elevator_vehicles
{
const VehicleList vehs = get_map().get_vehicles();
std::vector<vehicle *> ret;

for( const wrapped_vehicle &veh : vehs ) {
const auto status = vehicle_status( veh, tiles );
if( status == overlap_status::overlap ) {
return { true, { veh.v } };
}
if( status == overlap_status::inside ) {
ret.push_back( veh.v );
}
}
return { false, ret };
}

} // namespace

namespace
{

// still not sure whether there's a utility function for this
// NOLINTNEXTLINE(bugprone-easily-swappable-parameters)
auto move_item( map &here, const tripoint &src, const tripoint &dest ) -> void
{
map_stack items = here.i_at( src );
for( auto it = items.begin(); it != items.end(); ) {
here.add_item_or_charges( dest, *it );
it = here.i_rem( src, it );
}
};

auto move_creatures_away_from( const elevator_tiles &dest ) -> void
{
map &here = get_map();

const auto is_movable = [&]( const tripoint & candidate ) {
return !here.has_flag( TFLAG_ELEVATOR, candidate )
&& here.passable( candidate )
&& !g->critter_at( candidate );
};

for( Creature &critter : g->all_creatures() ) {
const tripoint local_pos = here.getlocal( here.getglobal( critter.pos() ) );

const auto eit = std::find( dest.cbegin(), dest.cend(), local_pos );
if( eit == dest.cend() ) {
continue;
}

const auto xs = closest_points_first( *eit, 10 );
const auto candidate = std::find_if( xs.begin(), xs.end(), is_movable );
if( candidate == xs.end() ) {
continue;
}
critter.setpos( *candidate );
}
}

} //namespace

/**
* If underground, move 2 levels up else move 2 levels down. Stable movement between levels 0 and -2.
*/
void iexamine::elevator( player &p, const tripoint &examp )
{
map &here = get_map();
const tripoint_abs_ms old_abs_pos = here.getglobal( p.pos() );
const tripoint_abs_omt this_omt = project_to<coords::omt>( here.getglobal( examp ) );
const tripoint sm_orig = here.getlocal( project_to<coords::ms>( this_omt ) );

const auto elevator_here = get_elevator_here( p );
const auto vehs = get_vehicles_on_elevator( elevator_here );
if( vehs.blocking ) {
const auto &first_veh_name = vehs.v.front()->name;
popup( string_format( _( "The %s is blocking the elevator." ), first_veh_name ) );
return;
}

const int movez = choose_elevator_destz( examp, this_omt, sm_orig );
if( movez < -OVERMAP_DEPTH ) {
return;
}

const tripoint_abs_omt that_omt{ this_omt.xy(), movez };
const int turns = get_rot_delta( this_omt, that_omt );

const auto elevator_dest = get_elevator_dest( elevator_here, sm_orig, turns, movez );

move_creatures_away_from( elevator_dest );

// move along every item in the elevator
for( decltype( elevator_here )::size_type i = 0; i < elevator_here.size(); i++ ) {
const tripoint &src = elevator_here[i];
move_item( here, src, elevator_dest[i] );
}

// move along all creatures on the elevator
for( Creature &critter : g->all_creatures() ) {
const auto eit = std::find( elevator_here.cbegin(), elevator_here.cend(), critter.pos() );
if( eit != elevator_here.cend() ) {
critter.setpos( elevator_dest[ std::distance( elevator_here.cbegin(), eit ) ] );
}
}

// move vehicles
for( vehicle *v : vehs.v ) {
const tripoint p = rotate_point_sm( { v->global_pos3().xy(), movez }, sm_orig, turns );
here.displace_vehicle( *v, p - v->global_pos3() );
v->turn( turns * 90_degrees );
v->face = tileray( v->turn_dir );
v->precalc_mounts( 0, v->turn_dir, v->pivot_anchor[0] );
}
here.reset_vehicle_cache();

g->vertical_shift( movez );
cata_event_dispatch::avatar_moves( *p.as_avatar(), here, old_abs_pos.raw() );
}

0 comments on commit 6e23a8c

Please sign in to comment.