Skip to content

Commit

Permalink
Make power grids easier to split up, reconnect, and merge parts to (#…
Browse files Browse the repository at this point in the history
…73056)

* Write message if grids are successfully merged

* Stop dragging appliance if merging it to a grid

This was a bug that let you drag power grids and break the game

* Remove power cords linked between merging grids

Extension cords are dropped to the ground

* Add functions for removing all but one power grid part

The appliance menu can now exit `app_loop` manually by setting `veh` to nullptr. I needed to add this because it'd be ambiguous which of the split-apart vehicles the menu is targeting otherwise.

* Move app menu's Merge functionality into Plug

If the connected appliances are adjacent & mergable powergrids, then merge them. Otherwise, connect with a power cord.

* Rename "power grid" to part name if split down to one part

* Ask to split part off from power grid when trying to drag it

(unless the part is wall mounted)

* Recalc powergrid pos & pivot when it gets split up

* Improve clarity of power grid removal section

* Remove CANT_DRAG tag. Instead, determine draggability when trying to grab

* Do cleanup after merging, as power cords between merged vehicles now get disconnected

* Adjust cable disconnection messages in menu

Disconnect cables -> Disconnect items / tow cables / items and tow cables

* Clang fixes
  • Loading branch information
Kamayana authored May 2, 2024
1 parent 58e3513 commit 9bb7453
Show file tree
Hide file tree
Showing 9 changed files with 190 additions and 101 deletions.
6 changes: 3 additions & 3 deletions data/raw/keybindings.json
Original file line number Diff line number Diff line change
Expand Up @@ -1760,10 +1760,10 @@
},
{
"type": "keybinding",
"id": "MERGE",
"id": "DISCONNECT_GRID",
"category": "APP_INTERACT",
"name": "Merge power grids",
"bindings": [ { "input_method": "keyboard_any", "key": "m" } ]
"name": "Separate from power grid",
"bindings": [ { "input_method": "keyboard_any", "key": "c" } ]
},
{
"type": "keybinding",
Expand Down
25 changes: 20 additions & 5 deletions src/handle_action.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -173,8 +173,6 @@ static const zone_type_id zone_type_UNLOAD_ALL( "UNLOAD_ALL" );
static const zone_type_id zone_type_VEHICLE_DECONSTRUCT( "VEHICLE_DECONSTRUCT" );
static const zone_type_id zone_type_VEHICLE_REPAIR( "VEHICLE_REPAIR" );

static const std::string flag_CANT_DRAG( "CANT_DRAG" );

#define dbg(x) DebugLog((x),D_GAME) << __FILE__ << ":" << __LINE__ << ": "

#if defined(__ANDROID__)
Expand Down Expand Up @@ -710,15 +708,32 @@ static void grab()
}

if( const optional_vpart_position vp = here.veh_at( grabp ) ) {
std::string veh_name = vp->vehicle().name;
if( !vp->vehicle().handle_potential_theft( you ) ) {
return;
}
if( vp->vehicle().has_tag( flag_CANT_DRAG ) ) {
add_msg( m_info, _( "There's nothing to grab there!" ) );
if( vp.part_with_feature( VPFLAG_WALL_MOUNTED, false ) ) {
add_msg( m_info, _( "You can't move that, it's attached to the wall." ) );
return;
}
// Powergrids with more than one part are undraggable.
// Offer to split the targeted part off onto its own, making it draggable.
if( vp->vehicle().is_powergrid() && vp->vehicle().part_count() > 1 ) {
if( !query_yn(
_( "That's part of a power grid. Separate it from the grid so you can move it?" ) ) ) {
return;
}
get_player_character().pause();
vp->vehicle().separate_from_grid( vp.value().mount() );
if( const optional_vpart_position split_vp = here.veh_at( grabp ) ) {
veh_name = split_vp->vehicle().name;
} else {
debugmsg( "Lost the part to drag after splitting power grid!" );
return;
}
}
you.grab( object_type::VEHICLE, grabp - you.pos() );
add_msg( _( "You grab the %s." ), vp->vehicle().name );
add_msg( _( "You grab the %s." ), veh_name );
} else if( here.has_furn( grabp ) ) { // If not, grab furniture if present
if( !here.furn( grabp ).obj().is_movable() ) {
add_msg( _( "You can not grab the %s." ), here.furnname( grabp ) );
Expand Down
17 changes: 15 additions & 2 deletions src/iuse_actor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4864,17 +4864,30 @@ std::optional<int> link_up_actor::link_to_veh_app( Character *p, item &it,
} else {

// Connecting two vehicles together.
const bool using_power_cord = it.typeId() == itype_power_cord;
if( using_power_cord && it.link().t_veh->is_powergrid() && sel_vp->vehicle().is_powergrid() ) {
// If both vehicles are adjacent power grids, try to merge them together first.
const point prev_pos = here.bub_from_abs( it.link().t_veh->coord_translate( it.link().t_mount ) +
it.link().t_abs_pos ).xy().raw();
if( selection.xy().distance( prev_pos ) <= 1.5f &&
it.link().t_veh->merge_appliance_into_grid( sel_vp->vehicle() ) ) {
it.link().t_veh->part_removal_cleanup();
p->add_msg_if_player( _( "You merge the two power grids." ) );
return 1;
}
// Unable to merge, so connect them with a power cord instead.
}
ret_val<void> result = it.link_to( sel_vp, to_ports ? link_state::vehicle_port :
link_state::vehicle_battery );
if( !result.success() ) {
p->add_msg_if_player( m_bad, result.str() );
return 0;
}
if( p->has_item( it ) ) {
if( using_power_cord || p->has_item( it ) ) {
p->add_msg_if_player( m_good, result.str() );
}

if( it.typeId() != itype_power_cord ) {
if( using_power_cord ) {
// Remove linked_flag from attached parts - the just-added cable vehicle parts do the same thing.
it.reset_link( true, p );
}
Expand Down
95 changes: 38 additions & 57 deletions src/veh_appliance.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include "itype.h"
#include "map_iterator.h"
#include "action.h"
#include "messages.h"
#include "output.h"
#include "overmapbuffer.h"
#include "player_activity.h"
Expand Down Expand Up @@ -33,8 +34,6 @@ static const vpart_id vpart_ap_standing_lamp( "ap_standing_lamp" );
static const vproto_id vehicle_prototype_none( "none" );

static const std::string flag_APPLIANCE( "APPLIANCE" );
static const std::string flag_WALL_MOUNTED( "WALL_MOUNTED" );
static const std::string flag_CANT_DRAG( "CANT_DRAG" );
static const std::string flag_WIRING( "WIRING" );
static const std::string flag_HALF_CIRCLE_LIGHT( "HALF_CIRCLE_LIGHT" );

Expand Down Expand Up @@ -83,10 +82,6 @@ void place_appliance( const tripoint &p, const vpart_id &vpart, const std::optio
veh->add_tag( flag_WIRING );
}

if( veh->is_powergrid() || vpinfo.has_flag( flag_WALL_MOUNTED ) ) {
veh->add_tag( flag_CANT_DRAG );
}

// Update the vehicle cache immediately,
// or the appliance will be invisible for the first couple of turns.
here.add_vehicle_to_cache( veh );
Expand All @@ -100,8 +95,9 @@ void place_appliance( const tripoint &p, const vpart_id &vpart, const std::optio
}
vehicle &veh_target = vp->vehicle();
if( veh_target.has_tag( flag_APPLIANCE ) ) {
if( veh->is_powergrid() && veh_target.is_powergrid() ) {
veh->merge_appliance_into_grid( veh_target );
if( veh->is_powergrid() && veh_target.is_powergrid() &&
veh->merge_appliance_into_grid( veh_target ) ) {
add_msg( _( "You merge it into the adjacent power grid." ) );
continue;
}
if( connected_vehicles.find( &veh_target ) == connected_vehicles.end() ) {
Expand All @@ -110,6 +106,7 @@ void place_appliance( const tripoint &p, const vpart_id &vpart, const std::optio
}
}
}
veh->part_removal_cleanup();

// Make some lighting appliances directed
if( vpinfo.has_flag( flag_HALF_CIRCLE_LIGHT ) && partnum != -1 ) {
Expand Down Expand Up @@ -148,6 +145,7 @@ veh_app_interact::veh_app_interact( vehicle &veh, const point &p )
ctxt.register_action( "RENAME" );
ctxt.register_action( "REMOVE" );
ctxt.register_action( "MERGE" );
ctxt.register_action( "DISCONNECT_GRID" );
}

// @returns true if a battery part exists on any vehicle connected to veh
Expand Down Expand Up @@ -325,11 +323,6 @@ bool veh_app_interact::can_siphon()
return false;
}

bool veh_app_interact::can_merge()
{
return veh->is_powergrid();
}

// Helper function for selecting a part in the parts list.
// If only one part is available, don't prompt the player.
static vehicle_part *pick_part( const std::vector<vehicle_part *> &parts,
Expand Down Expand Up @@ -510,6 +503,23 @@ void veh_app_interact::remove()
}
}

bool veh_app_interact::can_disconnect()
{
for( int &i : veh->parts_at_relative( a_point, true ) ) {
if( veh->part( i ).has_flag( vp_flag::linked_flag ) ||
veh->part( i ).info().has_flag( "TOW_CABLE" ) ) {
return false;
}
}
return true;
}

void veh_app_interact::disconnect()
{
veh->separate_from_grid( a_point );
get_player_character().pause();
}

void veh_app_interact::plug()
{
const int part = veh->part_at( veh->coord_translate( a_point ) );
Expand All @@ -521,41 +531,6 @@ void veh_app_interact::plug()
}
}

void veh_app_interact::merge()
{
map &here = get_map();

const int part = veh->part_at( a_point );
const tripoint app_pos = veh->global_part_pos3( part );

const std::function<bool( const tripoint & )> f = [&here, app_pos]( const tripoint & pnt ) {
if( pnt == app_pos ) {
return false;
}
const optional_vpart_position target_vp = here.veh_at( pnt );
if( !target_vp ) {
return false;
}
vehicle &target_veh = target_vp->vehicle();
if( !target_veh.is_powergrid() ) {
return false;
}
return true;
};

const std::optional<tripoint> target_pos = choose_adjacent_highlight( app_pos,
_( "Merge the appliance into which grid?" ), _( "Target must be adjacent." ), f, false, false );
if( !target_pos ) {
return;
}
const optional_vpart_position target_vp = here.veh_at( *target_pos );
if( !target_vp ) {
return;
}
vehicle &target_veh = target_vp->vehicle();
veh->merge_appliance_into_grid( target_veh );
}

void veh_app_interact::populate_app_actions()
{
map &here = get_map();
Expand Down Expand Up @@ -602,14 +577,20 @@ void veh_app_interact::populate_app_actions()
plug();
} );
imenu.addentry( -1, true, ctxt.keys_bound_to( "PLUG" ).front(),
ctxt.get_action_name( "PLUG" ) );

// Merge
app_actions.emplace_back( [this]() {
merge();
} );
imenu.addentry( -1, can_merge(), ctxt.keys_bound_to( "MERGE" ).front(),
ctxt.get_action_name( "MERGE" ) );
string_format( "%s%s", ctxt.get_action_name( "PLUG" ),
//~ An addendum to Plug In's description, as in: Plug in appliance / merge power grid".
veh->is_powergrid() ? _( " / merge power grid" ) : "" ) );

if( veh->is_powergrid() && veh->part_count() > 1 && !vp->info().has_flag( VPFLAG_WALL_MOUNTED ) ) {
// Disconnect from power grid
app_actions.emplace_back( [this]() {
disconnect();
veh = nullptr;
} );
const bool can_disc = can_disconnect();
imenu.addentry_desc( -1, can_disc, ctxt.keys_bound_to( "DISCONNECT_GRID" ).front(),
ctxt.get_action_name( "DISCONNECT_GRID" ), can_disc ? "" : _( "Remove other cables first" ) );
}

/*************** Get part-specific actions ***************/
veh_menu menu( veh, "IF YOU SEE THIS IT IS A BUG" );
Expand Down Expand Up @@ -663,7 +644,7 @@ void veh_app_interact::app_loop()
app_actions[ret]();
}
// Player activity queued up, close interaction menu
if( !act.is_null() || !get_player_character().activity.is_null() ) {
if( veh == nullptr || !act.is_null() || !get_player_character().activity.is_null() ) {
done = true;
}
}
Expand Down
22 changes: 11 additions & 11 deletions src/veh_appliance.h
Original file line number Diff line number Diff line change
Expand Up @@ -97,17 +97,6 @@ class veh_app_interact
* @returns True if a liquid can be siphoned from the appliance.
*/
bool can_siphon();
/**
* Checks whether the current appliance is power storage
* or powergen or a cable and can thus be merged into a powergrid.
* @returns True if the appliance can be merged.
*/
bool can_merge();
/**
* Function associated with the "MERGE" action.
* Merge power grid elements together into a single appliance
*/
void merge();
/**
* Function associated with the "REFILL" action.
* Checks all appliance parts for a watertight container to refill. If multiple
Expand All @@ -133,6 +122,17 @@ class veh_app_interact
* Turns the installed appliance into its base item.
*/
void remove();
/**
* Checks whether the part has any items linked to it so it can tell the player
* to disconnect those first. This prevents players from doing this by accident.
* @returns True if there aren't any tow cable parts or items linked to the mount point.
*/
bool can_disconnect();
/**
* Function associated with the "DISCONNECT_GRID" action.
* Removes appliance from a power grid, allowing it to be moved individually.
*/
void disconnect();
/**
* Function associated with the "PLUG" action.
* Connects the power cable to selected tile.
Expand Down
7 changes: 5 additions & 2 deletions src/veh_interact.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3362,10 +3362,13 @@ void veh_interact::complete_vehicle( Character &you )
veh.remove_remote_part( *vp );
}

// Remove any leftover power cords from the appliance
if( appliance_removal && veh.part_count() >= 2 ) {
if( appliance_removal && veh.part_count() > 1 ) {
// Split up power grids
veh.find_and_split_vehicles( here, { vp_index } );
veh.part_removal_cleanup();
// Ensure the position, pivot, and precalc points are up-to-date
veh.pos -= veh.pivot_anchor[0];
veh.precalc_mounts( 0, veh.turn_dir, point() );
here.rebuild_vehicle_level_caches();

if( auto newpart = here.veh_at( act_pos ).part_with_feature( VPFLAG_APPLIANCE, false ) ) {
Expand Down
Loading

0 comments on commit 9bb7453

Please sign in to comment.