diff --git a/data/json/items/tool/cables.json b/data/json/items/tool/cables.json index bf5e6abc56da1..e0f2c06f852da 100644 --- a/data/json/items/tool/cables.json +++ b/data/json/items/tool/cables.json @@ -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": "&", @@ -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", @@ -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": "&", @@ -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", diff --git a/data/json/items/tool/cooking.json b/data/json/items/tool/cooking.json index c5497334c8db5..960cd70b4e2bd 100644 --- a/data/json/items/tool/cooking.json +++ b/data/json/items/tool/cooking.json @@ -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" } ], diff --git a/doc/JSON_FLAGS.md b/doc/JSON_FLAGS.md index 46f6753d37738..4725b18d6c2f6 100644 --- a/doc/JSON_FLAGS.md +++ b/doc/JSON_FLAGS.md @@ -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. diff --git a/doc/JSON_INFO.md b/doc/JSON_INFO.md index d59333efe1f7b..a15c056f13146 100644 --- a/doc/JSON_INFO.md +++ b/doc/JSON_INFO.md @@ -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" ) diff --git a/src/avatar_action.cpp b/src/avatar_action.cpp index 7a74abdadabd7..23dfb93ee4807 100644 --- a/src/avatar_action.cpp +++ b/src/avatar_action.cpp @@ -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 ); diff --git a/src/item.cpp b/src/item.cpp index 95e3a3228e8d6..cecf11c7c732d 100644 --- a/src/item.cpp +++ b/src/item.cpp @@ -12969,7 +12969,7 @@ void item::set_link_traits( const bool assign_t_state ) const link_up_actor *it_actor = static_cast ( 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; @@ -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( 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() && @@ -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; } diff --git a/src/item_contents.cpp b/src/item_contents.cpp index 52ca61e065b4a..8761b490c7474 100644 --- a/src/item_contents.cpp +++ b/src/item_contents.cpp @@ -1558,6 +1558,9 @@ const item &item_contents::only_item() const item *item_contents::get_item_with( const std::function &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; diff --git a/src/iuse.cpp b/src/iuse.cpp index 276d89694c626..f136a10702d7f 100644 --- a/src/iuse.cpp +++ b/src/iuse.cpp @@ -3861,7 +3861,7 @@ std::optional iuse::tazer( Character *p, item *it, bool, const tripoint &po std::optional 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 ); @@ -7614,7 +7614,7 @@ std::optional 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 ); @@ -7709,7 +7709,7 @@ std::optional 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; } @@ -7845,7 +7845,7 @@ std::optional 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." ), diff --git a/src/iuse_actor.cpp b/src/iuse_actor.cpp index e04cfc17b1635..3a778666cdb8f 100644 --- a/src/iuse_actor.cpp +++ b/src/iuse_actor.cpp @@ -4387,7 +4387,7 @@ std::string link_up_actor::get_name() const return iuse_actor::get_name(); } -std::optional link_up_actor::use( Character *p, item &it, bool t, const tripoint & ) const +std::optional link_up_actor::use( Character *p, item &it, bool t, const tripoint &pnt ) const { if( t ) { return std::nullopt; @@ -4595,7 +4595,7 @@ std::optional 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. @@ -4811,7 +4811,7 @@ std::optional 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." ), @@ -5041,7 +5041,8 @@ std::optional link_up_actor::link_tow_cable( Character *p, item &it, } } -std::optional link_up_actor::link_extend_cable( Character *p, item &it ) const +std::optional link_up_actor::link_extend_cable( Character *p, item &it, + const tripoint &pnt ) const { avatar *you = p->as_avatar(); if( !you ) { @@ -5082,8 +5083,8 @@ std::optional 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 extended_copy; // We'll make a copy of the extended item and check pocket weight/volume capacity if: @@ -5121,24 +5122,24 @@ std::optional 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." ) : @@ -5146,6 +5147,8 @@ std::optional link_up_actor::link_extend_cable( Character *p, item &it ) co 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; } diff --git a/src/iuse_actor.h b/src/iuse_actor.h index ff4b9b8992354..e674c6ddc7943 100644 --- a/src/iuse_actor.h +++ b/src/iuse_actor.h @@ -1072,14 +1072,14 @@ class link_up_actor : public iuse_actor std::optional link_to_veh_app( Character *p, item &it, bool to_ports ) const; std::optional link_tow_cable( Character *p, item &it, bool to_towing ) const; - std::optional link_extend_cable( Character *p, item &it ) const; + std::optional link_extend_cable( Character *p, item &it, const tripoint &pnt ) const; std::optional 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 use( Character *p, item &it, bool t, const tripoint & ) const override; + std::optional use( Character *p, item &it, bool t, const tripoint &pnt ) const override; std::unique_ptr clone() const override; void info( const item &, std::vector & ) const override; std::string get_name() const override; diff --git a/src/savegame_json.cpp b/src/savegame_json.cpp index 5914508845df7..e94b9b4a96a72 100644 --- a/src/savegame_json.cpp +++ b/src/savegame_json.cpp @@ -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(); } diff --git a/src/veh_appliance.cpp b/src/veh_appliance.cpp index fd462952f489c..c3410c454177a 100644 --- a/src/veh_appliance.cpp +++ b/src/veh_appliance.cpp @@ -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() diff --git a/src/vehicle_use.cpp b/src/vehicle_use.cpp index dfe43546610f3..397a112a7c84a 100644 --- a/src/vehicle_use.cpp +++ b/src/vehicle_use.cpp @@ -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; } @@ -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 prev_part_target = std::make_pair( + here.getabs( target_pos ), + sel_veh->global_square_location().raw() ); + const std::pair 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