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

feat(port): move vehicles in elevator #3151

Merged
merged 8 commits into from
Sep 17, 2023
Merged
Show file tree
Hide file tree
Changes from all 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
86 changes: 0 additions & 86 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,91 +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;
} else if( here.has_flag( flag_ELEVATOR, critter.pos() ) ) {
tripoint critter_omt = ms_to_omt_copy( here.getabs( critter.pos() ) );
if( critter_omt == new_floor_omt ) {
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;
} else if( here.has_flag( flag_ELEVATOR, critter.pos() ) ) {
tripoint critter_omt = ms_to_omt_copy( here.getabs( critter.pos() ) );

if( critter_omt == original_floor_omt ) {
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
241 changes: 241 additions & 0 deletions src/iexamine_elevator.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,241 @@
#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
{

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

namespace elevator
{

using tiles = std::vector<tripoint>;

auto 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 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_floor( const tripoint &examp, const tripoint_abs_omt &this_omt,
const tripoint &sm_orig ) -> int
{
constexpr int retval_offset = 10000; // workaround for uilist retval autoassign when retval == -1
const auto this_floor = _( " (this floor)" );

map &here = get_map();
uilist choice;
choice.title = _( "Please 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 ) {
continue;
}
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 + retval_offset, z != examp.z, MENU_AUTOASSIGN, name );
}
choice.query();
return choice.ret - retval_offset;
}

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

auto warn_blocking( const elevator_vehicles &vehs, std::string_view location ) -> void
{
const auto &first_veh_name = vehs.v.front()->name;
popup( string_format( _( "%1$s %2$s is blocking the elevator." ), first_veh_name, location ) );
}

auto move_creatures_away( 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 );
}
}

auto move_items( const elevator::tiles from, const elevator::tiles dest ) -> void
{
map &here = get_map();

for( decltype( from )::size_type i = 0; i < from.size(); i++ ) {
const tripoint &src = from[i];
move_item( here, src, dest[i] );
}
}

auto move_creatures( const elevator::tiles from, const elevator::tiles dest ) -> void
{
for( Creature &critter : g->all_creatures() ) {
const auto eit = std::find( from.cbegin(), from.cend(), critter.pos() );
if( eit != from.cend() ) {
critter.setpos( dest[ std::distance( from.cbegin(), eit ) ] );
}
}
}

auto move_vehicles( const elevator_vehicles &vehs, const tripoint &sm_orig, int movez,
int turns ) -> void
{
map &here = get_map();

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

} //namespace elevator

} // 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 = elevator::here( p );
const auto vehs = elevator::vehicles_on( elevator_here );
if( vehs.blocking ) {
return warn_blocking( vehs, _( "here" ) );
}

const int movez = elevator::choose_floor( 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 = elevator::dest( elevator_here, sm_orig, turns, movez );
const auto vehicles_dest = elevator::vehicles_on( elevator_dest );
if( !vehicles_dest.v.empty() ) {
return warn_blocking( vehicles_dest, _( "at the destination floor" ) );
}

elevator::move_creatures_away( elevator_dest );
elevator::move_items( elevator_here, elevator_dest );
elevator::move_creatures( elevator_here, elevator_dest );
elevator::move_vehicles( vehs, sm_orig, movez, turns );

g->vertical_shift( movez );
cata_event_dispatch::avatar_moves( *p.as_avatar(), here, old_abs_pos.raw() );
}
1 change: 1 addition & 0 deletions src/mapdata.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,7 @@ static const std::unordered_map<std::string, ter_bitflags> ter_bitflags_map = {
{ "SUSPENDED", TFLAG_SUSPENDED }, // This furniture is suspended between other terrain, and will cause a cascading failure on break.
{ "FRIDGE", TFLAG_FRIDGE }, // This is an active fridge.
{ "FREEZER", TFLAG_FREEZER }, // This is an active freezer.
{ "ELEVATOR", TFLAG_ELEVATOR }, // This is an elevator.
}
};

Expand Down
1 change: 1 addition & 0 deletions src/mapdata.h
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,7 @@ enum ter_bitflags : int {
TFLAG_SUSPENDED,
TFLAG_FRIDGE,
TFLAG_FREEZER,
TFLAG_ELEVATOR,

NUM_TERFLAGS
};
Expand Down
40 changes: 40 additions & 0 deletions src/point_rotate.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#include "point_rotate.h"
#include "overmapbuffer.h"
#include "omdata.h"
#include "point.h"

auto rotate( point p, point dim, int turns ) -> point
{
switch( turns ) {
case 1:
return { dim.y - p.y - 1, p.x };
case 2:
return { dim.x - p.x - 1, dim.y - p.y - 1 };

Check warning on line 12 in src/point_rotate.cpp

View workflow job for this annotation

GitHub Actions / build

Construction of 'point' can be simplified using overloaded arithmetic operators. [cata-use-point-arithmetic]
case 3:
return { p.y, dim.x - p.x - 1 };
default:
return p;
}
}

auto rotate( const tripoint &p, point dim, int turns ) -> tripoint
{
return { rotate( p.xy(), dim, turns ), p.z };
}

auto rotate_point_sm( const tripoint &p, const tripoint &orig, int turns ) -> tripoint
{
const tripoint p_sm{ p - orig.xy() };
const tripoint rd{ rotate( p_sm, { SEEX * 2, SEEY * 2 }, turns ) };

return tripoint{ rd + orig.xy() };
}

auto get_rot_delta( const tripoint_abs_omt &here, const tripoint_abs_omt &there ) -> int
{
const auto this_dir = overmap_buffer.ter( there )->get_dir();
const auto that_dir = overmap_buffer.ter( here )->get_dir();

int const diff = static_cast<int>( this_dir ) - static_cast<int>( that_dir );
return diff >= 0 ? diff : 4 + diff;
}
Loading
Loading