Skip to content

Commit

Permalink
Various link up fixes (#67376)
Browse files Browse the repository at this point in the history
* Documentation

* Adjust cable descriptions

* Fix being unable to plug in appliances

* Fix being able to connect appliances more than once

* Fix appliance links having more than 2 tiles of slack

* Reformat `vehicle::connect` to match `link_up_actor::link_to_veh_app`

* Fix cables not drawing power

* Fix now-too-big extended devices not dropping immediately

* Fix error from extending a device with ALLOWS_REMOTE_USE

* Fix error caused by items with ALLOWS_REMOTE_USE getting deleted during their use action

* Allow linked power for some iuse functions

* Skip cable pockets in `get_item_with()`

* Give multicooker ALLOWS_REMOTE_USE

* Remove unused variable
  • Loading branch information
Kamayana authored Aug 2, 2023
1 parent afc82fd commit bdfc14e
Show file tree
Hide file tree
Showing 13 changed files with 79 additions and 54 deletions.
10 changes: 5 additions & 5 deletions data/json/items/tool/cables.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@
"type": "TOOL",
"id": "extension_cable",
"name": { "str": "extension cord" },
"description": "A long 30-foot (or about 10 m) orange extension cord for connecting appliances. Could be used on any appliance or other household electrical system.",
"description": "A long 30-foot (or about 10 m) orange extension cord for extending cables or connecting appliances. Could be used on any appliance or other household electrical system.",
"to_hit": 1,
"color": "light_blue",
"symbol": "&",
Expand All @@ -86,7 +86,7 @@
"type": "TOOL",
"id": "long_extension_cable",
"name": { "str": "outdoor extension cord" },
"description": "An extra-long 100-foot (or about 30 m) orange extension cord for connecting outdoor appliances. Could be used on any appliance or other household electrical system.",
"description": "An extra-long 100-foot (or about 30 m) orange extension cord for extending cables or connecting outdoor appliances. Could be used on any appliance or other household electrical system.",
"copy-from": "extension_cable",
"volume": "3750 ml",
"weight": "2500 g",
Expand All @@ -98,7 +98,7 @@
"type": "TOOL",
"id": "hd_tow_cable",
"name": { "str": "heavy-duty tow cable" },
"description": "An extremely heavy-duty 75-foot (or about 24 m) tow cable made from thick steel wire coated in plastic. If attached to a vehicle, it could be used to pull another vehicle of any weight.",
"description": "An extremely heavy-duty 30-foot (or about 9 m) tow cable made from thick steel wire coated in plastic. If attached to a vehicle, it could be used to pull another vehicle of any weight.",
"to_hit": -1,
"color": "light_blue",
"symbol": "&",
Expand All @@ -108,8 +108,8 @@
"category": "tools",
"price": 80,
"price_postapoc": 500,
"max_charges": 6,
"initial_charges": 6,
"max_charges": 8,
"initial_charges": 8,
"use_action": {
"type": "link_up",
"menu_text": "Attach / Manage connections",
Expand Down
2 changes: 1 addition & 1 deletion data/json/items/tool/cooking.json
Original file line number Diff line number Diff line change
Expand Up @@ -662,7 +662,7 @@
"symbol": ";",
"color": "red",
"ammo": [ "battery" ],
"flags": [ "WATER_BREAK", "ELECTRONIC" ],
"flags": [ "WATER_BREAK", "ELECTRONIC", "ALLOWS_REMOTE_USE" ],
"power_draw": "100 W",
"qualities": [ [ "CONTAIN", 1 ] ],
"use_action": [ "MULTICOOKER", { "type": "link_up", "cable_length": 2, "charge_rate": "1000 W" } ],
Expand Down
2 changes: 1 addition & 1 deletion doc/JSON_FLAGS.md
Original file line number Diff line number Diff line change
Expand Up @@ -1346,7 +1346,7 @@ Melee flags are fully compatible with tool flags, and vice versa.
- ```ALLOWS_REMOTE_USE``` This item can be activated or reloaded from adjacent tile without picking it up.
- ```BELT_CLIP``` The item can be clipped or hooked on to a belt loop of the appropriate size (belt loops are limited by their max_volume and max_weight properties)
- ```BOMB``` It can be a remote controlled bomb.
- ```CABLE_SPOOL``` This item is a cable spool and must be processed as such. It should usually have a "link_up" iuse_action that describes what it can be connected to and how.
- ```CABLE_SPOOL``` This item is a spool of cable and must be processed as such. It should usually have a "link_up" iuse_action, which it has special behavior for.
- ```CANNIBALISM``` The item is a food that contains human flesh, and applies all applicable effects when consumed.
- ```CHARGEDIM``` If illuminated, light intensity fades with charge, starting at 20% charge left.
- ```DIG_TOOL``` If wielded, digs thorough terrain like rock and walls, as player walks into them. If item also has ```POWERED``` flag, then it digs faster, but uses up the item's ammo as if activating it.
Expand Down
1 change: 1 addition & 0 deletions doc/JSON_INFO.md
Original file line number Diff line number Diff line change
Expand Up @@ -4035,6 +4035,7 @@ The contents of use_action fields can either be a string indicating a built-in f
},
"use_action": {
"type": "link_up", // Connect item to a vehicle or appliance, such as plugging a chargeable device into a power source.
// If the item has the CABLE_SPOOL flag, it has special behaviors available, like connecting vehicles together.
"cable_length": 4 // Maximum length of the cable ( Optional, defaults to the item type's maximum charges ).
// If extended by other cables, will use the sum of all cables' lengths.
"charge_rate": "60 W" // The charge rate of the plugged-in device's batteries in watts. ( Optional, defaults to "0 W" )
Expand Down
7 changes: 4 additions & 3 deletions src/avatar_action.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1154,9 +1154,10 @@ void avatar_action::use_item( avatar &you, item_location &loc, std::string const
if( use_in_place ) {
update_lum( loc, false );
you.use( loc, pre_obtain_moves, method );
update_lum( loc, true );

loc.make_active();
if( loc ) {
update_lum( loc, true );
loc.make_active();
}
} else {
you.use( loc, pre_obtain_moves, method );

Expand Down
10 changes: 9 additions & 1 deletion src/item.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12969,7 +12969,7 @@ void item::set_link_traits( const bool assign_t_state )
const link_up_actor *it_actor = static_cast<const link_up_actor *>
( get_use( "link_up" )->get_actor_ptr() );
link->max_length = it_actor->cable_length == -1 ? type->maximum_charges() : it_actor->cable_length;
link->efficiency = it_actor->efficiency < MIN_LINK_EFFICIENCY ? 0.0f : link->efficiency;
link->efficiency = it_actor->efficiency < MIN_LINK_EFFICIENCY ? 0.0f : it_actor->efficiency;
// Reset s_bub_pos to force the item to check the length during process_link.
link->s_bub_pos = tripoint_min;

Expand All @@ -12985,6 +12985,13 @@ void item::set_link_traits( const bool assign_t_state )
link->efficiency * actor->efficiency;
}

link->charge_rate = link->efficiency < MIN_LINK_EFFICIENCY ? 0 : it_actor->charge_rate.value();
// Convert charge_rate to how long it takes to charge 1 kW, the unit batteries use.
// 0 means batteries won't be charged, but it can still provide power to devices unless efficiency is also 0.
link->charge_interval = link->efficiency < MIN_LINK_EFFICIENCY || it_actor->charge_rate == 0_W ? 0 :
std::max( static_cast<int>( std::floor( 1000000.0 / abs( it_actor->charge_rate.value() ) + 0.5 ) ),
1 );

if( assign_t_state && link->t_veh_safe ) {
// Assign t_state based on the parts available at the connected mount point.
if( it_actor->targets.find( link_state::vehicle_port ) != it_actor->targets.end() &&
Expand Down Expand Up @@ -13566,6 +13573,7 @@ bool item::process_internal( map &here, Character *carrier, const tripoint &pos,
}
if( has_flag( flag_CABLE_SPOOL ) && mark_flag() ) {
// DO NOT process this as a tool! It really isn't!
// Cables have been reworked and shouldn't be active items anymore.
active = false;
return false;
}
Expand Down
3 changes: 3 additions & 0 deletions src/item_contents.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1558,6 +1558,9 @@ const item &item_contents::only_item() const
item *item_contents::get_item_with( const std::function<bool( const item &it )> &filter )
{
for( item_pocket &pocket : contents ) {
if( pocket.is_type( item_pocket::pocket_type::CABLE ) ) {
continue;
}
item *it = pocket.get_item_with( filter );
if( it != nullptr ) {
return it;
Expand Down
8 changes: 4 additions & 4 deletions src/iuse.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3861,7 +3861,7 @@ std::optional<int> iuse::tazer( Character *p, item *it, bool, const tripoint &po

std::optional<int> iuse::tazer2( Character *p, item *it, bool b, const tripoint &pos )
{
if( it->ammo_remaining( p ) >= 2 ) {
if( it->ammo_remaining( p, true ) >= 2 ) {
// Instead of having a ctrl+c+v of the function above, spawn a fake tazer and use it
// Ugly, but less so than copied blocks
item fake( "tazer", calendar::turn_zero );
Expand Down Expand Up @@ -7614,7 +7614,7 @@ std::optional<int> iuse::multicooker( Character *p, item *it, bool t, const trip

if( t ) {
//stop action before power runs out and iuse deletes the cooker
if( it->ammo_remaining( p ) < charge_buffer ) {
if( it->ammo_remaining( p, true ) < charge_buffer ) {
it->active = false;
it->erase_var( "RECIPE" );
it->convert( itype_multi_cooker );
Expand Down Expand Up @@ -7709,7 +7709,7 @@ std::optional<int> iuse::multicooker( Character *p, item *it, bool t, const trip
menu.addentry( mc_stop, true, 's', _( "Stop cooking" ) );
} else {
if( dish_it == nullptr ) {
if( it->ammo_remaining( p ) < charges_to_start ) {
if( it->ammo_remaining( p, true ) < charges_to_start ) {
p->add_msg_if_player( _( "Batteries are low." ) );
return 0;
}
Expand Down Expand Up @@ -7845,7 +7845,7 @@ std::optional<int> iuse::multicooker( Character *p, item *it, bool t, const trip
const int all_charges = charges_to_start + mealtime / 1000 * units::to_watt(
it->type->tool->power_draw ) / 1000;

if( it->ammo_remaining( p ) < all_charges ) {
if( it->ammo_remaining( p, true ) < all_charges ) {

p->add_msg_if_player( m_warning,
_( "The multi-cooker needs %d charges to cook this dish." ),
Expand Down
33 changes: 18 additions & 15 deletions src/iuse_actor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4387,7 +4387,7 @@ std::string link_up_actor::get_name() const
return iuse_actor::get_name();
}

std::optional<int> link_up_actor::use( Character *p, item &it, bool t, const tripoint & ) const
std::optional<int> link_up_actor::use( Character *p, item &it, bool t, const tripoint &pnt ) const
{
if( t ) {
return std::nullopt;
Expand Down Expand Up @@ -4595,7 +4595,7 @@ std::optional<int> link_up_actor::use( Character *p, item &it, bool t, const tri

} else if( choice == 30 ) {
// Selection: Attach to another cable, resulting in a longer one.
return link_extend_cable( p, it );
return link_extend_cable( p, it, pnt );

} else if( choice == 31 ) {
// Selection: Remove all cable extensions and give the individual cables to the player.
Expand Down Expand Up @@ -4811,7 +4811,7 @@ std::optional<int> link_up_actor::link_to_veh_app( Character *p, item &it,
( it.link->t_abs_pos + prev_veh->coord_translate( it.link->t_mount ) ).raw(),
it.link->t_abs_pos.raw() );

for( const vpart_reference &vpr : sel_veh->get_any_parts( "POWER_TRANSFER" ) ) {
for( const vpart_reference &vpr : prev_veh->get_any_parts( "POWER_TRANSFER" ) ) {
if( vpr.part().target.first == prev_part_target.first &&
vpr.part().target.second == prev_part_target.second ) {
p->add_msg_if_player( m_warning, _( "The %1$s and %2$s are already connected." ),
Expand Down Expand Up @@ -5041,7 +5041,8 @@ std::optional<int> link_up_actor::link_tow_cable( Character *p, item &it,
}
}

std::optional<int> link_up_actor::link_extend_cable( Character *p, item &it ) const
std::optional<int> link_up_actor::link_extend_cable( Character *p, item &it,
const tripoint &pnt ) const
{
avatar *you = p->as_avatar();
if( !you ) {
Expand Down Expand Up @@ -5082,8 +5083,8 @@ std::optional<int> link_up_actor::link_extend_cable( Character *p, item &it ) co
return std::nullopt;
}

item_location extension = is_cable_item ? form_loc( *p, tripoint_min, it ) : selected;
item_location extended = is_cable_item ? selected : form_loc( *p, tripoint_min, it );
item_location extension = is_cable_item ? form_loc( *p, pnt, it ) : selected;
item_location extended = is_cable_item ? selected : form_loc( *p, pnt, it );
std::optional<item> extended_copy;

// We'll make a copy of the extended item and check pocket weight/volume capacity if:
Expand Down Expand Up @@ -5121,31 +5122,33 @@ std::optional<int> link_up_actor::link_extend_cable( Character *p, item &it ) co

if( extended_copy ) {
// Check if there's another pocket on the same container that can hold the extended item, respecting pocket settings.
if( extended.has_parent() &&
extended.parent_item()->can_contain( *extended_ptr, false, false, false,
item_location(), 10000000_ml, false ).success() ) {
if( !extended.parent_item()->put_in( *extended_ptr,
item_pocket::pocket_type::CONTAINER ).success() ) {
item_location parent = extended.parent_item();
if( parent->can_contain( *extended_ptr, false, false, false,
item_location(), 10000000_ml, false ).success() ) {
if( !parent->put_in( *extended_ptr, item_pocket::pocket_type::CONTAINER ).success() ) {
debugmsg( "Failed to put %s inside %s!", extended_ptr->type_name(),
extended.parent_item()->type_name() );
parent->type_name() );
return std::nullopt;
}
extended.remove_item();
} else {
if( !query_yn( _( "The %1$s can't contain the %2$s with the %3$s attached. Continue?" ),
extended.parent_item()->type_name(), extended_ptr->type_name(), extension->type_name() ) ) {
parent->type_name(), extended_ptr->type_name(), extension->type_name() ) ) {
return std::nullopt;
}
// Attach the cord, even though it won't fit, and let it spill out naturally.
extended.parent_pocket()->add( *extended_ptr );
extended.remove_item();
}
extended.make_active();
extended.remove_item();
}

p->add_msg_if_player( is_cable_item ? _( "You extend the %1$s with the %2$s." ) :
_( "You extend the %1$s's cable with the %2$s." ),
extended_ptr->type_name(), extension->type_name() );
extension.remove_item();
p->invalidate_inventory_validity_cache();
p->invalidate_weight_carried_cache();
p->drop_invalid_inventory();
p->moves -= move_cost;
return 0;
}
Expand Down
4 changes: 2 additions & 2 deletions src/iuse_actor.h
Original file line number Diff line number Diff line change
Expand Up @@ -1072,14 +1072,14 @@ class link_up_actor : public iuse_actor

std::optional<int> link_to_veh_app( Character *p, item &it, bool to_ports ) const;
std::optional<int> link_tow_cable( Character *p, item &it, bool to_towing ) const;
std::optional<int> link_extend_cable( Character *p, item &it ) const;
std::optional<int> link_extend_cable( Character *p, item &it, const tripoint &pnt ) const;
std::optional<int> remove_extensions( Character *p, item &it ) const;

link_up_actor() : iuse_actor( "link_up" ) {}

~link_up_actor() override = default;
void load( const JsonObject &jo ) override;
std::optional<int> use( Character *p, item &it, bool t, const tripoint & ) const override;
std::optional<int> use( Character *p, item &it, bool t, const tripoint &pnt ) const override;
std::unique_ptr<iuse_actor> clone() const override;
void info( const item &, std::vector<iteminfo> & ) const override;
std::string get_name() const override;
Expand Down
2 changes: 1 addition & 1 deletion src/savegame_json.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2870,8 +2870,8 @@ void item::link_data::serialize( JsonOut &jsout ) const
jsout.member( "link_max_length", max_length );
jsout.member( "link_last_processed", last_processed );
jsout.member( "link_charge_rate", charge_rate );
jsout.member( "link_charge_interval", charge_interval );
jsout.member( "link_charge_efficiency", efficiency );
jsout.member( "link_charge_interval", charge_interval );
jsout.end_object();
}

Expand Down
2 changes: 1 addition & 1 deletion src/veh_appliance.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -526,7 +526,7 @@ void veh_app_interact::plug()
{
const int part = veh->part_at( a_point );
const tripoint pos = veh->global_part_pos3( part );
veh->plug_in( get_map().getabs( pos ) );
veh->plug_in( pos );
}

void veh_app_interact::populate_app_actions()
Expand Down
49 changes: 29 additions & 20 deletions src/vehicle_use.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -550,6 +550,7 @@ item vehicle::init_cord( const tripoint &pos )
cord.link->t_veh_safe = get_safe_reference();
cord.link->t_abs_pos = get_map().getglobal( pos );
cord.set_link_traits();
cord.link->max_length = 2;

return cord;
}
Expand All @@ -568,34 +569,42 @@ void vehicle::connect( const tripoint &source_pos, const tripoint &target_pos )
item cord = init_cord( source_pos );
map &here = get_map();

const optional_vpart_position target_vp = here.veh_at( target_pos );
const optional_vpart_position source_vp = here.veh_at( source_pos );
const optional_vpart_position sel_vp = here.veh_at( target_pos );
const optional_vpart_position prev_vp = here.veh_at( source_pos );

if( !target_vp ) {
if( !sel_vp ) {
return;
}
vehicle *const target_veh = &target_vp->vehicle();
vehicle *const source_veh = &source_vp->vehicle();
if( source_veh == target_veh ) {
vehicle *const sel_veh = &sel_vp->vehicle();
vehicle *const prev_veh = &prev_vp->vehicle();
if( prev_veh == sel_veh ) {
return ;
}

tripoint target_global = here.getabs( target_pos );
const vpart_id vpid( cord.typeId().str() );

point vcoords = source_vp->mount();
vehicle_part source_part( vpid, item( cord ) );
source_part.target.first = target_global;
source_part.target.second = target_veh->global_square_location().raw();
source_veh->install_part( vcoords, std::move( source_part ) );
source_veh->precalc_mounts( 1, source_veh->pivot_rotation[1], source_veh->pivot_anchor[1] );

vcoords = target_vp->mount();
vehicle_part target_part( vpid, item( cord ) );
target_part.target.first = cord.link->t_abs_pos.raw();
target_part.target.second = source_veh->global_square_location().raw();
target_veh->install_part( vcoords, std::move( target_part ) );
target_veh->precalc_mounts( 1, target_veh->pivot_rotation[1], target_veh->pivot_anchor[1] );
// Prepare target tripoints for the cable parts that'll be added to the selected/previous vehicles
const std::pair<tripoint, tripoint> prev_part_target = std::make_pair(
here.getabs( target_pos ),
sel_veh->global_square_location().raw() );
const std::pair<tripoint, tripoint> sel_part_target = std::make_pair(
( cord.link->t_abs_pos + prev_veh->coord_translate( cord.link->t_mount ) ).raw(),
cord.link->t_abs_pos.raw() );

const point vcoords1 = cord.link->t_mount;
const point vcoords2 = sel_vp->mount();

vehicle_part prev_veh_part( vpid, item( cord ) );
prev_veh_part.target.first = prev_part_target.first;
prev_veh_part.target.second = prev_part_target.second;
prev_veh->install_part( vcoords1, std::move( prev_veh_part ) );
prev_veh->precalc_mounts( 1, prev_veh->pivot_rotation[1], prev_veh->pivot_anchor[1] );

vehicle_part sel_veh_part( vpid, item( cord ) );
sel_veh_part.target.first = sel_part_target.first;
sel_veh_part.target.second = sel_part_target.second;
sel_veh->install_part( vcoords2, std::move( sel_veh_part ) );
sel_veh->precalc_mounts( 1, sel_veh->pivot_rotation[1], sel_veh->pivot_anchor[1] );
}

double vehicle::engine_cold_factor( const vehicle_part &vp ) const
Expand Down

0 comments on commit bdfc14e

Please sign in to comment.