From 0b6eeeefd3af2d32a19c036ef2a6c2a4c54d8635 Mon Sep 17 00:00:00 2001 From: Kamayana Date: Sat, 10 Jun 2023 09:13:19 -0500 Subject: [PATCH 01/68] Make "Plug in / Unplug" the default link_up menu text --- data/json/item_actions.json | 2 +- data/json/items/tool/cables.json | 34 +++++--------- data/json/items/tool/electronics.json | 67 ++++----------------------- data/mods/TEST_DATA/appliance.json | 3 -- 4 files changed, 21 insertions(+), 85 deletions(-) diff --git a/data/json/item_actions.json b/data/json/item_actions.json index 107ccfe3642bf..14bcd625cb1db 100644 --- a/data/json/item_actions.json +++ b/data/json/item_actions.json @@ -983,7 +983,7 @@ { "type": "item_action", "id": "link_up", - "name": { "str": "Attach / Detach" } + "name": { "str": "Plug in / Unplug" } }, { "type": "item_action", diff --git a/data/json/items/tool/cables.json b/data/json/items/tool/cables.json index 258d139e49a60..db2f07a1aef11 100644 --- a/data/json/items/tool/cables.json +++ b/data/json/items/tool/cables.json @@ -16,13 +16,7 @@ "price_postapoc": 100, "max_charges": 3, "initial_charges": 3, - "use_action": { - "type": "link_up", - "menu_text": "Plug in / Unplug", - "is_cable_item": true, - "cable_length": 3, - "targets": [ "vehicle_port" ] - }, + "use_action": { "type": "link_up", "is_cable_item": true, "cable_length": 3, "targets": [ "vehicle_port" ] }, "flags": [ "CABLE_SPOOL", "AUTO_DELETE_CABLE", "SINGLE_USE" ], "melee_damage": { "bash": 2 } }, @@ -83,13 +77,7 @@ "weight": "800 g", "max_charges": 10, "initial_charges": 10, - "use_action": { - "type": "link_up", - "menu_text": "Plug in / Unplug", - "is_cable_item": true, - "cable_length": 10, - "targets": [ "no_link", "vehicle_port" ] - }, + "use_action": { "type": "link_up", "is_cable_item": true, "cable_length": 10, "targets": [ "no_link", "vehicle_port" ] }, "flags": [ "CABLE_SPOOL", "SINGLE_USE" ] }, { @@ -102,13 +90,7 @@ "weight": "2500 g", "max_charges": 30, "initial_charges": 30, - "use_action": { - "type": "link_up", - "menu_text": "Plug in / Unplug", - "is_cable_item": true, - "cable_length": 30, - "targets": [ "no_link", "vehicle_port" ] - }, + "use_action": { "type": "link_up", "is_cable_item": true, "cable_length": 30, "targets": [ "no_link", "vehicle_port" ] }, "flags": [ "CABLE_SPOOL", "SINGLE_USE" ] }, { @@ -127,7 +109,13 @@ "price_postapoc": 500, "max_charges": 6, "initial_charges": 6, - "use_action": { "type": "link_up", "is_cable_item": true, "cable_length": 6, "targets": [ "no_link", "vehicle_tow" ] }, + "use_action": { + "type": "link_up", + "menu_text": "Attach / Detach", + "is_cable_item": true, + "cable_length": 6, + "targets": [ "no_link", "vehicle_tow" ] + }, "qualities": [ [ "ROPE", 2 ] ], "flags": [ "CABLE_SPOOL", "TOW_CABLE", "SINGLE_USE" ], "melee_damage": { "bash": 6 } @@ -161,7 +149,7 @@ "initial_charges": 100, "use_action": { "type": "link_up", - "menu_text": "Plug in / Unplug", + "menu_text": "Attach / Detach", "is_cable_item": true, "cable_length": 100, "efficiency": 1000, diff --git a/data/json/items/tool/electronics.json b/data/json/items/tool/electronics.json index d2a1707a3ed85..5990094769506 100644 --- a/data/json/items/tool/electronics.json +++ b/data/json/items/tool/electronics.json @@ -165,13 +165,7 @@ "need_charges": 1, "need_charges_msg": "The tablet batteries are dead." }, - { - "type": "link_up", - "menu_text": "Plug in / Unplug", - "ammo_scale": 0, - "cable_length": 4, - "charge_rate": "10 W" - } + { "type": "link_up", "ammo_scale": 0, "cable_length": 4, "charge_rate": "10 W" } ], "flags": [ "WATCH", "WATER_BREAK", "ELECTRONIC" ], "pocket_data": [ @@ -209,13 +203,7 @@ "msg": "You power down the screen.", "target": "eink_tablet_pc" }, - { - "type": "link_up", - "menu_text": "Plug in / Unplug", - "ammo_scale": 0, - "cable_length": 4, - "charge_rate": "10 W" - } + { "type": "link_up", "ammo_scale": 0, "cable_length": 4, "charge_rate": "10 W" } ], "//": "LIGHT_10 is the bare minimum for reading without penalties", "flags": [ "LIGHT_10", "TRADER_AVOID", "WATCH", "WATER_BREAK", "ELECTRONIC" ] @@ -376,13 +364,7 @@ "need_charges_msg": "The laptop's batteries need more charge.", "type": "transform" }, - { - "type": "link_up", - "menu_text": "Plug in / Unplug", - "ammo_scale": 0, - "cable_length": 5, - "charge_rate": "140 W" - } + { "type": "link_up", "ammo_scale": 0, "cable_length": 5, "charge_rate": "140 W" } ], "flags": [ "WATCH", "WATER_BREAK", "ELECTRONIC" ], "pocket_data": [ @@ -428,13 +410,7 @@ "menu_text": "Turn off", "type": "transform" }, - { - "type": "link_up", - "menu_text": "Plug in / Unplug", - "ammo_scale": 0, - "cable_length": 5, - "charge_rate": "140 W" - } + { "type": "link_up", "ammo_scale": 0, "cable_length": 5, "charge_rate": "140 W" } ], "flags": [ "WATCH", "LIGHT_10", "TRADER_AVOID", "WATER_BREAK", "ELECTRONIC" ] }, @@ -452,10 +428,7 @@ "symbol": ";", "color": "dark_gray", "ammo": [ "battery" ], - "use_action": [ - "MP3", - { "type": "link_up", "menu_text": "Plug in / Unplug", "ammo_scale": 0, "cable_length": 4, "charge_rate": "5 W" } - ], + "use_action": [ "MP3", { "type": "link_up", "ammo_scale": 0, "cable_length": 4, "charge_rate": "5 W" } ], "charges_per_use": 1, "flags": [ "WATER_BREAK_ACTIVE", "ELECTRONIC" ], "pocket_data": [ @@ -475,10 +448,7 @@ "description": "This mp3 player is turned on and playing some great tunes, raising your morale steadily while on your person. It runs through batteries quickly; you can turn it off by using it. It also obscures your hearing.", "power_draw": "1 W", "revert_to": "mp3", - "use_action": [ - "MP3_ON", - { "type": "link_up", "menu_text": "Plug in / Unplug", "ammo_scale": 0, "cable_length": 4, "charge_rate": "5 W" } - ], + "use_action": [ "MP3_ON", { "type": "link_up", "ammo_scale": 0, "cable_length": 4, "charge_rate": "5 W" } ], "flags": [ "TRADER_AVOID", "WATER_BREAK", "ELECTRONIC" ] }, { @@ -578,7 +548,6 @@ "PORTABLE_GAME", { "type": "link_up", - "menu_text": "Plug in / Unplug", "ammo_scale": 0, "//": "Based on the Nintendo Switch's power adapter", "cable_length": 4, @@ -634,13 +603,7 @@ "need_charges_msg": "The smartphone's charge is too low.", "type": "transform" }, - { - "type": "link_up", - "menu_text": "Plug in / Unplug", - "ammo_scale": 0, - "cable_length": 3, - "charge_rate": "20 W" - } + { "type": "link_up", "ammo_scale": 0, "cable_length": 3, "charge_rate": "20 W" } ], "flags": [ "WATCH", "ALARMCLOCK", "USE_UPS", "NO_UNLOAD", "NO_RELOAD", "WATER_BREAK", "CALORIES_INTAKE", "ELECTRONIC" ], "pocket_data": [ @@ -668,13 +631,7 @@ "EINKTABLETPC", "EBOOKSAVE", "EBOOKREAD", - { - "type": "link_up", - "menu_text": "Plug in / Unplug", - "ammo_scale": 0, - "cable_length": 3, - "charge_rate": "20 W" - } + { "type": "link_up", "ammo_scale": 0, "cable_length": 3, "charge_rate": "20 W" } ], "extend": { "flags": [ "TRADER_AVOID" ] } }, @@ -704,13 +661,7 @@ "menu_text": "Turn off flashlight", "type": "transform" }, - { - "type": "link_up", - "menu_text": "Plug in / Unplug", - "ammo_scale": 0, - "cable_length": 3, - "charge_rate": "20 W" - } + { "type": "link_up", "ammo_scale": 0, "cable_length": 3, "charge_rate": "20 W" } ], "extend": { "flags": [ "LIGHT_20", "CHARGEDIM", "TRADER_AVOID" ] } }, diff --git a/data/mods/TEST_DATA/appliance.json b/data/mods/TEST_DATA/appliance.json index 8e961a10432a0..17d6850b64de2 100644 --- a/data/mods/TEST_DATA/appliance.json +++ b/data/mods/TEST_DATA/appliance.json @@ -57,7 +57,6 @@ "initial_charges": 3, "use_action": { "type": "link_up", - "menu_text": "Plug in / Unplug", "is_cable_item": true, "cable_length": 3, "targets": [ "no_link", "vehicle_port", "vehicle_battery" ] @@ -91,7 +90,6 @@ "initial_charges": 10, "use_action": { "type": "link_up", - "menu_text": "Plug in / Unplug", "is_cable_item": true, "cable_length": 10, "targets": [ "no_link", "vehicle_port", "vehicle_battery" ] @@ -133,7 +131,6 @@ "initial_charges": 3, "use_action": { "type": "link_up", - "menu_text": "Plug in / Unplug", "is_cable_item": true, "cable_length": 3, "targets": [ "no_link", "vehicle_port", "vehicle_battery" ] From abf7a8f915f2337d651e8239cd425ffa08e9bb7c Mon Sep 17 00:00:00 2001 From: Kamayana Date: Sat, 10 Jun 2023 09:14:14 -0500 Subject: [PATCH 02/68] Make link_up use default ammo_scale of 0 --- data/json/items/tool/electronics.json | 19 +++++++++---------- src/item_factory.cpp | 4 ++++ 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/data/json/items/tool/electronics.json b/data/json/items/tool/electronics.json index 5990094769506..03a6d05b2d260 100644 --- a/data/json/items/tool/electronics.json +++ b/data/json/items/tool/electronics.json @@ -165,7 +165,7 @@ "need_charges": 1, "need_charges_msg": "The tablet batteries are dead." }, - { "type": "link_up", "ammo_scale": 0, "cable_length": 4, "charge_rate": "10 W" } + { "type": "link_up", "cable_length": 4, "charge_rate": "10 W" } ], "flags": [ "WATCH", "WATER_BREAK", "ELECTRONIC" ], "pocket_data": [ @@ -203,7 +203,7 @@ "msg": "You power down the screen.", "target": "eink_tablet_pc" }, - { "type": "link_up", "ammo_scale": 0, "cable_length": 4, "charge_rate": "10 W" } + { "type": "link_up", "cable_length": 4, "charge_rate": "10 W" } ], "//": "LIGHT_10 is the bare minimum for reading without penalties", "flags": [ "LIGHT_10", "TRADER_AVOID", "WATCH", "WATER_BREAK", "ELECTRONIC" ] @@ -364,7 +364,7 @@ "need_charges_msg": "The laptop's batteries need more charge.", "type": "transform" }, - { "type": "link_up", "ammo_scale": 0, "cable_length": 5, "charge_rate": "140 W" } + { "type": "link_up", "cable_length": 5, "charge_rate": "140 W" } ], "flags": [ "WATCH", "WATER_BREAK", "ELECTRONIC" ], "pocket_data": [ @@ -410,7 +410,7 @@ "menu_text": "Turn off", "type": "transform" }, - { "type": "link_up", "ammo_scale": 0, "cable_length": 5, "charge_rate": "140 W" } + { "type": "link_up", "cable_length": 5, "charge_rate": "140 W" } ], "flags": [ "WATCH", "LIGHT_10", "TRADER_AVOID", "WATER_BREAK", "ELECTRONIC" ] }, @@ -428,7 +428,7 @@ "symbol": ";", "color": "dark_gray", "ammo": [ "battery" ], - "use_action": [ "MP3", { "type": "link_up", "ammo_scale": 0, "cable_length": 4, "charge_rate": "5 W" } ], + "use_action": [ "MP3", { "type": "link_up", "cable_length": 4, "charge_rate": "5 W" } ], "charges_per_use": 1, "flags": [ "WATER_BREAK_ACTIVE", "ELECTRONIC" ], "pocket_data": [ @@ -448,7 +448,7 @@ "description": "This mp3 player is turned on and playing some great tunes, raising your morale steadily while on your person. It runs through batteries quickly; you can turn it off by using it. It also obscures your hearing.", "power_draw": "1 W", "revert_to": "mp3", - "use_action": [ "MP3_ON", { "type": "link_up", "ammo_scale": 0, "cable_length": 4, "charge_rate": "5 W" } ], + "use_action": [ "MP3_ON", { "type": "link_up", "cable_length": 4, "charge_rate": "5 W" } ], "flags": [ "TRADER_AVOID", "WATER_BREAK", "ELECTRONIC" ] }, { @@ -548,7 +548,6 @@ "PORTABLE_GAME", { "type": "link_up", - "ammo_scale": 0, "//": "Based on the Nintendo Switch's power adapter", "cable_length": 4, "charge_rate": "39 W" @@ -603,7 +602,7 @@ "need_charges_msg": "The smartphone's charge is too low.", "type": "transform" }, - { "type": "link_up", "ammo_scale": 0, "cable_length": 3, "charge_rate": "20 W" } + { "type": "link_up", "cable_length": 3, "charge_rate": "20 W" } ], "flags": [ "WATCH", "ALARMCLOCK", "USE_UPS", "NO_UNLOAD", "NO_RELOAD", "WATER_BREAK", "CALORIES_INTAKE", "ELECTRONIC" ], "pocket_data": [ @@ -631,7 +630,7 @@ "EINKTABLETPC", "EBOOKSAVE", "EBOOKREAD", - { "type": "link_up", "ammo_scale": 0, "cable_length": 3, "charge_rate": "20 W" } + { "type": "link_up", "cable_length": 3, "charge_rate": "20 W" } ], "extend": { "flags": [ "TRADER_AVOID" ] } }, @@ -661,7 +660,7 @@ "menu_text": "Turn off flashlight", "type": "transform" }, - { "type": "link_up", "ammo_scale": 0, "cable_length": 3, "charge_rate": "20 W" } + { "type": "link_up", "cable_length": 3, "charge_rate": "20 W" } ], "extend": { "flags": [ "LIGHT_20", "CHARGEDIM", "TRADER_AVOID" ] } }, diff --git a/src/item_factory.cpp b/src/item_factory.cpp index 16fa96f9b693a..285377f00f31b 100644 --- a/src/item_factory.cpp +++ b/src/item_factory.cpp @@ -667,6 +667,10 @@ void Item_factory::finalize_pre( itype &obj ) obj.book->martial_art = matype_id( "style_" + obj.get_id().str().substr( 7 ) ); } + if( obj.can_use( "link_up" ) ) { + obj.ammo_scale.emplace( "link_up", 0 ); + } + if( obj.longest_side == -1_mm ) { units::volume effective_volume = obj.count_by_charges() && obj.stack_size > 0 ? ( obj.volume / obj.stack_size ) : obj.volume; From 9e23fd42d303d005b5e58c5c9d950060798d0aba Mon Sep 17 00:00:00 2001 From: Kamayana Date: Sat, 10 Jun 2023 15:33:57 -0500 Subject: [PATCH 03/68] cable json cleanup; Remove JUMPSTART from plug cables; change cable materials --- data/json/items/tool/cables.json | 79 ++++++++++++++++---------------- 1 file changed, 39 insertions(+), 40 deletions(-) diff --git a/data/json/items/tool/cables.json b/data/json/items/tool/cables.json index db2f07a1aef11..43249c1c91068 100644 --- a/data/json/items/tool/cables.json +++ b/data/json/items/tool/cables.json @@ -8,7 +8,7 @@ "to_hit": 1, "color": "dark_gray", "symbol": "&", - "material": [ "steel", "plastic" ], + "material": [ "copper", "plastic" ], "volume": "500 ml", "weight": "75 g", "category": "tools", @@ -16,7 +16,7 @@ "price_postapoc": 100, "max_charges": 3, "initial_charges": 3, - "use_action": { "type": "link_up", "is_cable_item": true, "cable_length": 3, "targets": [ "vehicle_port" ] }, + "use_action": { "type": "link_up", "is_cable_item": true, "targets": [ "vehicle_port" ] }, "flags": [ "CABLE_SPOOL", "AUTO_DELETE_CABLE", "SINGLE_USE" ], "melee_damage": { "bash": 2 } }, @@ -29,7 +29,7 @@ "to_hit": 1, "color": "dark_gray", "symbol": "&", - "material": [ "steel", "plastic" ], + "material": [ "copper", "plastic" ], "volume": "500 ml", "weight": "75 g", "category": "tools", @@ -37,7 +37,8 @@ "price_postapoc": 70, "max_charges": 4, "initial_charges": 4, - "flags": [ "CABLE_SPOOL", "AUTO_DELETE_CABLE" ], + "use_action": { "type": "link_up", "is_cable_item": true, "targets": [ "no_link", "vehicle_port" ] }, + "flags": [ "CABLE_SPOOL", "AUTO_DELETE_CABLE", "SINGLE_USE" ], "melee_damage": { "bash": 2 } }, { @@ -48,7 +49,7 @@ "to_hit": 1, "color": "light_blue", "symbol": "&", - "material": [ "steel", "plastic" ], + "material": [ "copper", "plastic" ], "volume": "500 ml", "weight": "75 g", "category": "tools", @@ -60,44 +61,67 @@ "type": "link_up", "menu_text": "Attach / Detach", "is_cable_item": true, - "cable_length": 4, "targets": [ "no_link", "bio_cable", "ups", "solarpack", "vehicle_battery" ] }, "flags": [ "CABLE_SPOOL", "SINGLE_USE" ], "melee_damage": { "bash": 2 }, "qualities": [ [ "JUMPSTART", 1 ] ] }, + { + "type": "TOOL", + "id": "jumper_cable_heavy", + "name": { "str": "heavy-duty cable" }, + "description": "A long, thick, heavy-duty cable with power leads on either end. It looks like you could use it to hook up two vehicles to each other, though you expect the power loss would be noticeable. Can also link other electrical systems.", + "volume": "1500 ml", + "weight": "750 g", + "max_charges": 20, + "initial_charges": 20, + "use_action": { + "type": "link_up", + "menu_text": "Attach / Detach", + "is_cable_item": true, + "targets": [ "no_link", "bio_cable", "ups", "solarpack", "vehicle_battery" ] + }, + "copy-from": "jumper_cable" + }, { "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.", - "copy-from": "jumper_cable", + "to_hit": 1, + "color": "light_blue", + "symbol": "&", + "material": [ "copper", "plastic" ], "volume": "1200 ml", "weight": "800 g", + "category": "tools", + "price": 1, + "price_postapoc": 100, "max_charges": 10, "initial_charges": 10, - "use_action": { "type": "link_up", "is_cable_item": true, "cable_length": 10, "targets": [ "no_link", "vehicle_port" ] }, - "flags": [ "CABLE_SPOOL", "SINGLE_USE" ] + "use_action": { "type": "link_up", "is_cable_item": true, "targets": [ "no_link", "vehicle_port" ] }, + "flags": [ "CABLE_SPOOL", "SINGLE_USE" ], + "melee_damage": { "bash": 2 } }, { "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.", - "copy-from": "jumper_cable", + "copy-from": "extension_cable", "volume": "3750 ml", "weight": "2500 g", "max_charges": 30, "initial_charges": 30, - "use_action": { "type": "link_up", "is_cable_item": true, "cable_length": 30, "targets": [ "no_link", "vehicle_port" ] }, + "use_action": { "type": "link_up", "is_cable_item": true, "targets": [ "no_link", "vehicle_port" ] }, "flags": [ "CABLE_SPOOL", "SINGLE_USE" ] }, { "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. If attached to a vehicle, it could be used to pull another vehicle of any weight.", + "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.", "to_hit": -1, "color": "light_blue", "symbol": "&", @@ -109,40 +133,17 @@ "price_postapoc": 500, "max_charges": 6, "initial_charges": 6, - "use_action": { - "type": "link_up", - "menu_text": "Attach / Detach", - "is_cable_item": true, - "cable_length": 6, - "targets": [ "no_link", "vehicle_tow" ] - }, + "use_action": { "type": "link_up", "menu_text": "Attach / Detach", "is_cable_item": true, "targets": [ "no_link", "vehicle_tow" ] }, "qualities": [ [ "ROPE", 2 ] ], "flags": [ "CABLE_SPOOL", "TOW_CABLE", "SINGLE_USE" ], "melee_damage": { "bash": 6 } }, - { - "type": "TOOL", - "id": "jumper_cable_heavy", - "name": { "str": "heavy-duty cable" }, - "description": "A long, thick, heavy-duty cable with power leads on either end. It looks like you could use it to hook up two vehicles to each other, though you expect the power loss would be noticeable. Can also link other electrical systems.", - "volume": "1500 ml", - "weight": "750 g", - "max_charges": 20, - "initial_charges": 20, - "use_action": { - "type": "link_up", - "menu_text": "Attach / Detach", - "is_cable_item": true, - "cable_length": 20, - "targets": [ "no_link", "bio_cable", "ups", "solarpack", "vehicle_battery" ] - }, - "copy-from": "jumper_cable" - }, { "type": "TOOL", "id": "jumper_cable_debug", "name": { "str": "shiny debug cable" }, "description": "This is the cable of the gods: 100 meters long, no power loss, light as a feather, and fits in a matchbook. You're sure this wasn't supposed to exist, and the way it shimmers makes you uneasy.", + "copy-from": "jumper_cable", "weight": "1 g", "volume": "1 ml", "max_charges": 100, @@ -151,10 +152,8 @@ "type": "link_up", "menu_text": "Attach / Detach", "is_cable_item": true, - "cable_length": 100, "efficiency": 1000, "targets": [ "no_link", "bio_cable", "ups", "solarpack", "vehicle_port", "vehicle_battery", "vehicle_tow" ] - }, - "copy-from": "jumper_cable" + } } ] From a164e9bac2462644371c7fba749c02f60f386d92 Mon Sep 17 00:00:00 2001 From: Kamayana Date: Wed, 21 Jun 2023 00:14:02 -0500 Subject: [PATCH 04/68] Add clear_pockets_if, a flexible function for deleting pocket contents --- src/item_contents.cpp | 9 +++++++++ src/item_contents.h | 1 + 2 files changed, 10 insertions(+) diff --git a/src/item_contents.cpp b/src/item_contents.cpp index c38b76fcacad2..4d0bafd5a5950 100644 --- a/src/item_contents.cpp +++ b/src/item_contents.cpp @@ -1354,6 +1354,15 @@ void item_contents::clear_magazines() } } +void item_contents::clear_pockets_if( const std::function &filter ) +{ + for( item_pocket &pocket : contents ) { + if( filter( pocket ) ) { + pocket.clear_items(); + } + } +} + void item_contents::update_open_pockets() { for( item_pocket &pocket : contents ) { diff --git a/src/item_contents.h b/src/item_contents.h index e185c78d9f2c9..8c9365abc83d3 100644 --- a/src/item_contents.h +++ b/src/item_contents.h @@ -275,6 +275,7 @@ class item_contents void clear_items(); // clears all items from magazine type pockets void clear_magazines(); + void clear_pockets_if( const std::function &filter ); void update_open_pockets(); /** From ea51aca0a90c169bcc565ec8c796dd1ea6fb3fbe Mon Sep 17 00:00:00 2001 From: Kamayana Date: Wed, 12 Jul 2023 06:36:49 -0500 Subject: [PATCH 05/68] Simplify links by making devices handle links themselves; split up link iuse functions; add move_cost parameter; rename charge_rate to wattage --- data/json/items/tool/cables.json | 32 +- data/json/items/tool/electronics.json | 20 +- data/json/items/tool/lighting.json | 16 +- data/json/items/tool/workshop.json | 24 +- data/json/obsoletion/migration_items.json | 6 + data/mods/TEST_DATA/appliance.json | 21 +- doc/JSON_INFO.md | 11 +- src/activity_actor.cpp | 17 +- src/activity_actor_definitions.h | 8 +- src/inventory_ui.cpp | 2 +- src/item.cpp | 237 +++---- src/item.h | 32 +- src/item_factory.cpp | 18 +- src/iuse_actor.cpp | 757 ++++++++++------------ src/iuse_actor.h | 12 +- src/savegame_json.cpp | 5 +- src/vehicle.cpp | 2 +- src/vehicle_part.cpp | 8 +- 18 files changed, 527 insertions(+), 701 deletions(-) diff --git a/data/json/items/tool/cables.json b/data/json/items/tool/cables.json index 43249c1c91068..2e90dd9153bdc 100644 --- a/data/json/items/tool/cables.json +++ b/data/json/items/tool/cables.json @@ -16,28 +16,7 @@ "price_postapoc": 100, "max_charges": 3, "initial_charges": 3, - "use_action": { "type": "link_up", "is_cable_item": true, "targets": [ "vehicle_port" ] }, - "flags": [ "CABLE_SPOOL", "AUTO_DELETE_CABLE", "SINGLE_USE" ], - "melee_damage": { "bash": 2 } - }, - { - "type": "TOOL", - "id": "generic_device_cable", - "name": { "str": "device cable" }, - "description": "A cable for attaching devices to power or to other devices.", - "//": "This cable is automatically handled by devices and is used for connecting them to vehicles/appliances", - "to_hit": 1, - "color": "dark_gray", - "symbol": "&", - "material": [ "copper", "plastic" ], - "volume": "500 ml", - "weight": "75 g", - "category": "tools", - "price": 1, - "price_postapoc": 70, - "max_charges": 4, - "initial_charges": 4, - "use_action": { "type": "link_up", "is_cable_item": true, "targets": [ "no_link", "vehicle_port" ] }, + "use_action": { "type": "link_up", "targets": [ "vehicle_port" ] }, "flags": [ "CABLE_SPOOL", "AUTO_DELETE_CABLE", "SINGLE_USE" ], "melee_damage": { "bash": 2 } }, @@ -60,7 +39,6 @@ "use_action": { "type": "link_up", "menu_text": "Attach / Detach", - "is_cable_item": true, "targets": [ "no_link", "bio_cable", "ups", "solarpack", "vehicle_battery" ] }, "flags": [ "CABLE_SPOOL", "SINGLE_USE" ], @@ -79,7 +57,6 @@ "use_action": { "type": "link_up", "menu_text": "Attach / Detach", - "is_cable_item": true, "targets": [ "no_link", "bio_cable", "ups", "solarpack", "vehicle_battery" ] }, "copy-from": "jumper_cable" @@ -100,7 +77,7 @@ "price_postapoc": 100, "max_charges": 10, "initial_charges": 10, - "use_action": { "type": "link_up", "is_cable_item": true, "targets": [ "no_link", "vehicle_port" ] }, + "use_action": { "type": "link_up", "targets": [ "no_link", "vehicle_port" ] }, "flags": [ "CABLE_SPOOL", "SINGLE_USE" ], "melee_damage": { "bash": 2 } }, @@ -114,7 +91,7 @@ "weight": "2500 g", "max_charges": 30, "initial_charges": 30, - "use_action": { "type": "link_up", "is_cable_item": true, "targets": [ "no_link", "vehicle_port" ] }, + "use_action": { "type": "link_up", "targets": [ "no_link", "vehicle_port" ] }, "flags": [ "CABLE_SPOOL", "SINGLE_USE" ] }, { @@ -133,7 +110,7 @@ "price_postapoc": 500, "max_charges": 6, "initial_charges": 6, - "use_action": { "type": "link_up", "menu_text": "Attach / Detach", "is_cable_item": true, "targets": [ "no_link", "vehicle_tow" ] }, + "use_action": { "type": "link_up", "menu_text": "Attach / Detach", "targets": [ "no_link", "vehicle_tow" ] }, "qualities": [ [ "ROPE", 2 ] ], "flags": [ "CABLE_SPOOL", "TOW_CABLE", "SINGLE_USE" ], "melee_damage": { "bash": 6 } @@ -151,7 +128,6 @@ "use_action": { "type": "link_up", "menu_text": "Attach / Detach", - "is_cable_item": true, "efficiency": 1000, "targets": [ "no_link", "bio_cable", "ups", "solarpack", "vehicle_port", "vehicle_battery", "vehicle_tow" ] } diff --git a/data/json/items/tool/electronics.json b/data/json/items/tool/electronics.json index 03a6d05b2d260..33a09e2bc8c8d 100644 --- a/data/json/items/tool/electronics.json +++ b/data/json/items/tool/electronics.json @@ -165,7 +165,7 @@ "need_charges": 1, "need_charges_msg": "The tablet batteries are dead." }, - { "type": "link_up", "cable_length": 4, "charge_rate": "10 W" } + { "type": "link_up", "cable_length": 4, "wattage": "10 W" } ], "flags": [ "WATCH", "WATER_BREAK", "ELECTRONIC" ], "pocket_data": [ @@ -203,7 +203,7 @@ "msg": "You power down the screen.", "target": "eink_tablet_pc" }, - { "type": "link_up", "cable_length": 4, "charge_rate": "10 W" } + { "type": "link_up", "cable_length": 4, "wattage": "10 W" } ], "//": "LIGHT_10 is the bare minimum for reading without penalties", "flags": [ "LIGHT_10", "TRADER_AVOID", "WATCH", "WATER_BREAK", "ELECTRONIC" ] @@ -364,7 +364,7 @@ "need_charges_msg": "The laptop's batteries need more charge.", "type": "transform" }, - { "type": "link_up", "cable_length": 5, "charge_rate": "140 W" } + { "type": "link_up", "cable_length": 5, "wattage": "140 W" } ], "flags": [ "WATCH", "WATER_BREAK", "ELECTRONIC" ], "pocket_data": [ @@ -410,7 +410,7 @@ "menu_text": "Turn off", "type": "transform" }, - { "type": "link_up", "cable_length": 5, "charge_rate": "140 W" } + { "type": "link_up", "cable_length": 5, "wattage": "140 W" } ], "flags": [ "WATCH", "LIGHT_10", "TRADER_AVOID", "WATER_BREAK", "ELECTRONIC" ] }, @@ -428,7 +428,7 @@ "symbol": ";", "color": "dark_gray", "ammo": [ "battery" ], - "use_action": [ "MP3", { "type": "link_up", "cable_length": 4, "charge_rate": "5 W" } ], + "use_action": [ "MP3", { "type": "link_up", "cable_length": 4, "wattage": "5 W" } ], "charges_per_use": 1, "flags": [ "WATER_BREAK_ACTIVE", "ELECTRONIC" ], "pocket_data": [ @@ -448,7 +448,7 @@ "description": "This mp3 player is turned on and playing some great tunes, raising your morale steadily while on your person. It runs through batteries quickly; you can turn it off by using it. It also obscures your hearing.", "power_draw": "1 W", "revert_to": "mp3", - "use_action": [ "MP3_ON", { "type": "link_up", "cable_length": 4, "charge_rate": "5 W" } ], + "use_action": [ "MP3_ON", { "type": "link_up", "cable_length": 4, "wattage": "5 W" } ], "flags": [ "TRADER_AVOID", "WATER_BREAK", "ELECTRONIC" ] }, { @@ -550,7 +550,7 @@ "type": "link_up", "//": "Based on the Nintendo Switch's power adapter", "cable_length": 4, - "charge_rate": "39 W" + "wattage": "39 W" } ], "pocket_data": [ @@ -602,7 +602,7 @@ "need_charges_msg": "The smartphone's charge is too low.", "type": "transform" }, - { "type": "link_up", "cable_length": 3, "charge_rate": "20 W" } + { "type": "link_up", "cable_length": 3, "wattage": "20 W" } ], "flags": [ "WATCH", "ALARMCLOCK", "USE_UPS", "NO_UNLOAD", "NO_RELOAD", "WATER_BREAK", "CALORIES_INTAKE", "ELECTRONIC" ], "pocket_data": [ @@ -630,7 +630,7 @@ "EINKTABLETPC", "EBOOKSAVE", "EBOOKREAD", - { "type": "link_up", "cable_length": 3, "charge_rate": "20 W" } + { "type": "link_up", "cable_length": 3, "wattage": "20 W" } ], "extend": { "flags": [ "TRADER_AVOID" ] } }, @@ -660,7 +660,7 @@ "menu_text": "Turn off flashlight", "type": "transform" }, - { "type": "link_up", "cable_length": 3, "charge_rate": "20 W" } + { "type": "link_up", "cable_length": 3, "wattage": "20 W" } ], "extend": { "flags": [ "LIGHT_20", "CHARGEDIM", "TRADER_AVOID" ] } }, diff --git a/data/json/items/tool/lighting.json b/data/json/items/tool/lighting.json index a2d6f6e576b82..bda1ecadaa21c 100644 --- a/data/json/items/tool/lighting.json +++ b/data/json/items/tool/lighting.json @@ -156,13 +156,7 @@ "need_charges": 1, "need_charges_msg": "The lantern has no batteries." }, - { - "type": "link_up", - "menu_text": "Plug in / Unplug", - "ammo_scale": 0, - "cable_length": 2, - "charge_rate": "20 W" - } + { "type": "link_up", "cable_length": 2, "wattage": "20 W" } ], "flags": [ "RADIO_MODABLE", "RADIO_INVOKE_PROC", "ALLOWS_REMOTE_USE", "WATER_BREAK" ], "pocket_data": [ @@ -184,13 +178,7 @@ "revert_to": "electric_lantern", "use_action": [ { "menu_text": "Turn off", "type": "transform", "target": "electric_lantern", "msg": "You turn the lamp off." }, - { - "type": "link_up", - "menu_text": "Plug in / Unplug", - "ammo_scale": 0, - "cable_length": 2, - "charge_rate": "20 W" - } + { "type": "link_up", "cable_length": 2, "wattage": "20 W" } ], "flags": [ "RADIO_MODABLE", "RADIO_INVOKE_PROC", "LIGHT_15", "TRADER_AVOID", "ALLOWS_REMOTE_USE" ] }, diff --git a/data/json/items/tool/workshop.json b/data/json/items/tool/workshop.json index 7ddd5f3607231..26a3817cd2491 100644 --- a/data/json/items/tool/workshop.json +++ b/data/json/items/tool/workshop.json @@ -1289,13 +1289,7 @@ "cost_scaling": 0.1, "move_cost": 500 }, - { - "type": "link_up", - "menu_text": "Plug in / Unplug", - "ammo_scale": 0, - "cable_length": 7, - "charge_rate": "3000 W" - } + { "type": "link_up", "cable_length": 7, "wattage": "3000 W" } ], "flags": [ "ALLOWS_REMOTE_USE", "WATER_BREAK" ], "pocket_data": [ @@ -1366,13 +1360,7 @@ "cost_scaling": 0.1, "move_cost": 1000 }, - { - "type": "link_up", - "menu_text": "Plug in / Unplug", - "ammo_scale": 0, - "cable_length": 7, - "charge_rate": "1500 W" - } + { "type": "link_up", "cable_length": 7, "wattage": "1500 W" } ], "flags": [ "ALLOWS_REMOTE_USE", "WATER_BREAK" ], "pocket_data": [ @@ -1445,13 +1433,7 @@ "cost_scaling": 0.1, "move_cost": 500 }, - { - "type": "link_up", - "menu_text": "Plug in / Unplug", - "ammo_scale": 0, - "cable_length": 7, - "charge_rate": "3000 W" - } + { "type": "link_up", "cable_length": 7, "wattage": "3000 W" } ], "flags": [ "ALLOWS_REMOTE_USE", "WATER_BREAK" ], "pocket_data": [ diff --git a/data/json/obsoletion/migration_items.json b/data/json/obsoletion/migration_items.json index bd265b7f9b589..f5aca52a9d204 100644 --- a/data/json/obsoletion/migration_items.json +++ b/data/json/obsoletion/migration_items.json @@ -167,6 +167,12 @@ "replace": "UPS_ON", "flags": [ "IS_UPS" ] }, + { + "id": "generic_device_cable", + "type": "MIGRATION", + "replace": "oxygen", + "reset_item_vars": true + }, { "id": "rebreather_filter", "type": "MIGRATION", diff --git a/data/mods/TEST_DATA/appliance.json b/data/mods/TEST_DATA/appliance.json index 17d6850b64de2..e51665ef08627 100644 --- a/data/mods/TEST_DATA/appliance.json +++ b/data/mods/TEST_DATA/appliance.json @@ -55,12 +55,7 @@ "price_postapoc": 100, "max_charges": 3, "initial_charges": 3, - "use_action": { - "type": "link_up", - "is_cable_item": true, - "cable_length": 3, - "targets": [ "no_link", "vehicle_port", "vehicle_battery" ] - }, + "use_action": { "type": "link_up", "targets": [ "no_link", "vehicle_port", "vehicle_battery" ] }, "flags": [ "CABLE_SPOOL", "AUTO_DELETE_CABLE" ], "melee_damage": { "bash": 2 } }, @@ -88,12 +83,7 @@ "copy-from": "jumper_cable", "max_charges": 10, "initial_charges": 10, - "use_action": { - "type": "link_up", - "is_cable_item": true, - "cable_length": 10, - "targets": [ "no_link", "vehicle_port", "vehicle_battery" ] - } + "use_action": { "type": "link_up", "targets": [ "no_link", "vehicle_port", "vehicle_battery" ] } }, { "type": "vehicle_part", @@ -129,12 +119,7 @@ "price_postapoc": 100, "max_charges": 3, "initial_charges": 3, - "use_action": { - "type": "link_up", - "is_cable_item": true, - "cable_length": 3, - "targets": [ "no_link", "vehicle_port", "vehicle_battery" ] - }, + "use_action": { "type": "link_up", "targets": [ "no_link", "vehicle_port", "vehicle_battery" ] }, "flags": [ "CABLE_SPOOL", "AUTO_DELETE_CABLE", "SINGLE_USE" ], "melee_damage": { "bash": 2 } }, diff --git a/doc/JSON_INFO.md b/doc/JSON_INFO.md index 05fb0bf363f12..a1dbe59835211 100644 --- a/doc/JSON_INFO.md +++ b/doc/JSON_INFO.md @@ -4017,13 +4017,14 @@ 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. - "cable_type": "generic_device_cable" // The item type of the cable created with this action ( Optional, defaults to "generic_device_cable" ). - "cable_length": 5 // Maximum length of the cable ( Optional, defaults to 2 ). - "charge_rate": "60 W" // Charge rate in watts. A positive value will charge the device's chargeable batteries at the expense of the connected power grid. - // A negative value will charge the connected electrical grid's batteries at the expense of the device's. - // A value of 0 won't charge the device's batteries, but will still let the device operate off of the connected power grid ( Optional, defaults to "0 W" ). + "cable_length": 4 // Maximum length of the cable ( Optional, defaults to the item type's maximum charges ). + "wattage": "60 W" // Maximum energy transfer rate of the cable in watts. ( Optional, defaults to "0 W" ) + // A positive value will charge the device's chargeable batteries at the expense of the connected power grid. + // A negative value will charge the connected electrical grid's batteries at the expense of the device's. + // A value of 0 won't charge the device's batteries, but will still let the device operate off of the connected power grid. "efficiency": 7 // one_in(this) chance to fail adding 1 charge every charge interval ( Optional, defaults to 7, which is around 85% efficiency ). "menu_text": // Text displayed in the activation screen ( Optional, defaults to "Connect / Disconnect" ). + "move_cost": // Move cost of attaching the cable ( Optional, defaults to 5 ). "targets": [ // Array of link_states that are valid connection points of the cable ( Optional, defaults to only allowing disconnection ). "no_link", // Must be included to allow letting the player manually disconnect the cable. "vehicle_port", // Can connect to a vehicle's cable ports / electrical controls or an appliance. diff --git a/src/activity_actor.cpp b/src/activity_actor.cpp index 2a62b4994635b..f6e80344caa74 100644 --- a/src/activity_actor.cpp +++ b/src/activity_actor.cpp @@ -5051,17 +5051,10 @@ void reel_cable_activity_actor::start( player_activity &act, Character & ) void reel_cable_activity_actor::finish( player_activity &act, Character &who ) { - cable->active = false; - cable->charges = cable->link->max_length; cable->link.reset(); - if( parent_item ) { - parent_item->contents_linked = false; - who.add_msg_if_player( m_info, string_format( _( "You gather the cable up with the %s." ), - parent_item->label( 1 ) ) ); - } else { - who.add_msg_if_player( m_info, string_format( _( "You reel in the %s and wind it up." ), - cable->label( 1 ) ) ); - } + who.add_msg_if_player( m_info, + string_format( cable->has_flag( flag_CABLE_SPOOL ) ? _( "You reel in the %s and wind it up." ) : + _( "You reel in the %s and wind it up." ), cable->label( 1 ) ) ); if( cable->has_flag( flag_AUTO_DELETE_CABLE ) ) { cable.remove_item(); } @@ -5073,17 +5066,15 @@ void reel_cable_activity_actor::serialize( JsonOut &jsout ) const jsout.start_object(); jsout.member( "moves_total", moves_total ); jsout.member( "cable", cable ); - jsout.member( "parent_item", parent_item ); jsout.end_object(); } std::unique_ptr reel_cable_activity_actor::deserialize( JsonValue &jsin ) { - reel_cable_activity_actor actor( 0, {}, {} ); + reel_cable_activity_actor actor( 0, {} ); JsonObject data = jsin.get_object(); data.read( "moves_total", actor.moves_total ); data.read( "cable", actor.cable ); - data.read( "parent_item", actor.parent_item ); return actor.clone(); } diff --git a/src/activity_actor_definitions.h b/src/activity_actor_definitions.h index d6d1f7204bb3f..d62559b0ad804 100644 --- a/src/activity_actor_definitions.h +++ b/src/activity_actor_definitions.h @@ -1480,11 +1480,9 @@ class reel_cable_activity_actor : public activity_actor private: int moves_total; item_location cable; - item_location parent_item; public: - reel_cable_activity_actor( int moves_total, const item_location &cable, - const item_location &parent_item ) : - moves_total( moves_total ), cable( cable ), parent_item( parent_item ) {} + reel_cable_activity_actor( int moves_total, const item_location &cable ) : + moves_total( moves_total ), cable( cable ) {} activity_id get_type() const override { return activity_id( "ACT_REEL_CABLE" ); } @@ -1493,7 +1491,7 @@ class reel_cable_activity_actor : public activity_actor const Character &/*who*/ ) const override { const reel_cable_activity_actor &actor = static_cast ( other ); - return actor.cable == cable && actor.parent_item == parent_item; + return actor.cable == cable; } void start( player_activity &act, Character & ) override; diff --git a/src/inventory_ui.cpp b/src/inventory_ui.cpp index fb1078c0b285e..01693aab80ad1 100644 --- a/src/inventory_ui.cpp +++ b/src/inventory_ui.cpp @@ -1274,6 +1274,7 @@ inventory_entry *inventory_column::add_entry( const inventory_entry &entry ) entry_item.position() == found_entry_item.position() && entry_item.parent_item() == found_entry_item.parent_item() && entry_item->is_collapsed() == found_entry_item->is_collapsed() && + !!entry_item->link == !!found_entry_item->link && entry_item->display_stacked_with( *found_entry_item, preset.get_checking_components() ); } ); if( entry_with_loc != dest.end() ) { @@ -1390,7 +1391,6 @@ void inventory_column::collate() if( e->is_item() && e->get_category_ptr() == outer->get_category_ptr() && e->any_item()->is_favorite == outer->any_item()->is_favorite && e->any_item()->typeId() == outer->any_item()->typeId() && - e->any_item()->contents_linked == outer->any_item()->contents_linked && !!e->any_item()->link == !!outer->any_item()->link && ( !indent_entries() || e->any_item().parent_item() == outer->any_item().parent_item() ) && diff --git a/src/item.cpp b/src/item.cpp index e50a513156736..36bcefb0e4522 100644 --- a/src/item.cpp +++ b/src/item.cpp @@ -6002,7 +6002,8 @@ nc_color item::color_in_inventory( const Character *const ch ) const } } else if( has_flag( flag_LEAK_DAM ) && has_flag( flag_RADIOACTIVE ) && damage() > 0 ) { ret = c_light_green; - } else if( ( active && !has_temperature() ) || ( is_corpse() && can_revive() ) ) { + } else if( ( active && !has_temperature() ) || ( is_corpse() && can_revive() ) || + ( link && has_flag( flag_CABLE_SPOOL ) ) ) { // Active items show up as yellow (corpses only if reviving) ret = c_yellow; } else if( is_medication() || is_medical_tool() ) { @@ -6659,12 +6660,12 @@ std::string item::tname( unsigned int quantity, bool with_prefix, unsigned int t } if( active && ( has_flag( flag_WATER_EXTINGUISH ) || has_flag( flag_LITCIG ) ) ) { tagtext += _( " (lit)" ); - } else if( contents_linked || ( has_flag( flag_IS_UPS ) && get_var( "cable" ) == "plugged_in" ) ) { + } else if( has_flag( flag_IS_UPS ) && get_var( "cable" ) == "plugged_in" ) { tagtext += _( " (plugged in)" ); } else if( link ) { if( link->s_state == link_state::needs_reeling ) { tagtext += _( " (unspooled)" ); - } else if( active ) { + } else { tagtext += _( " (connected)" ); } } else if( active && !has_temperature() && !string_ends_with( typeId().str(), "_on" ) ) { @@ -10348,20 +10349,13 @@ int item::ammo_remaining( const Character *carrier, bool cable_links ) const } // Cable connections - if( cable_links && contents_linked ) { - for( const item *cable : contents.cables( true ) ) { - if( !cable->link ) { - continue; - } - if( cable->link->t_veh_safe ) { - ret += cable->link->t_veh_safe->connected_battery_power_level().first; - continue; - } else { - const optional_vpart_position vp = get_map().veh_at( cable->link->t_abs_pos ); - if( vp ) { - ret += vp->vehicle().connected_battery_power_level().first; - continue; - } + if( cable_links && link ) { + if( link->t_veh_safe ) { + ret += link->t_veh_safe->connected_battery_power_level().first; + } else { + const optional_vpart_position vp = get_map().veh_at( link->t_abs_pos ); + if( vp ) { + ret += vp->vehicle().connected_battery_power_level().first; } } } @@ -10540,15 +10534,13 @@ int item::ammo_consume( int qty, const tripoint &pos, Character *carrier ) const int wanted_qty = qty; // Consume power from appliances/vehicles connected with cables - if( contents_linked ) { - for( const item *cable : contents.cables( true ) ) { - if( cable->link && cable->link->t_veh_safe ) { - qty = cable->link->t_veh_safe->discharge_battery( qty, true ); - } else { - const optional_vpart_position vp = get_map().veh_at( cable->link->t_abs_pos ); - if( vp ) { - qty = vp->vehicle().discharge_battery( qty, true ); - } + if( link ) { + if( link->t_veh_safe ) { + qty = link->t_veh_safe->discharge_battery( qty, true ); + } else { + const optional_vpart_position vp = get_map().veh_at( link->t_abs_pos ); + if( vp ) { + qty = vp->vehicle().discharge_battery( qty, true ); } } } @@ -12218,7 +12210,7 @@ int item::processing_speed() const return to_turns( 10_minutes ); } - if( active || ethereal || wetness || contents_linked || + if( active || ethereal || wetness || link || has_flag( flag_RADIO_ACTIVATION ) || has_relic_recharge() || has_fault_flag( flag_BLACKPOWDER_FOULING_DAMAGE ) ) { // Unless otherwise indicated, update every turn. @@ -12889,26 +12881,22 @@ bool item::process_extinguish( map &here, Character *carrier, const tripoint &po return false; } -bool item::process_cable( map &here, Character *carrier, const tripoint &pos, item *parent_item ) +bool item::process_link( map &here, Character *carrier, const tripoint &pos ) { - // Active cables need link data to process. - if( !link ) { - return reset_cable( carrier, parent_item ); - } - // Failsafe for loose, unwound cables that are somehow still active. if( link->s_state == link_state::needs_reeling || link->has_no_links() ) { - return reset_cable( carrier, parent_item ); + return reset_link( carrier ); } - // Handle item-side links. + const bool is_cable_item = has_flag( flag_CABLE_SPOOL ); + + // Handle links to items in the inventory. if( link->s_state == link_state::solarpack ) { if( carrier == nullptr || !carrier->worn_with_flag( flag_SOLARPACK_ON ) ) { - add_msg_if_player_sees( pos, m_bad, parent_item == nullptr ? - string_format( _( "The %s has come loose from the solar pack." ), label( 1 ) ) : - string_format( _( "The %s's cable has come loose from the solar pack." ), - parent_item->label( 1 ) ) ); - reset_cable( carrier, parent_item ); + add_msg_if_player_sees( pos, m_bad, + string_format( is_cable_item ? _( "The %s has come loose from the solar pack." ) : + _( "The %s's cable has come loose from the solar pack." ), type_name() ) ); + reset_link( carrier ); return false; } } @@ -12917,18 +12905,18 @@ bool item::process_cable( map &here, Character *carrier, const tripoint &pos, it }; if( link->s_state == link_state::ups ) { if( carrier == nullptr || !carrier->has_item_with( used_ups ) ) { - add_msg_if_player_sees( pos, m_bad, parent_item == nullptr ? - string_format( _( "The %s has come loose from the UPS." ), label( 1 ) ) : - string_format( _( "The %s's cable has come loose from the UPS." ), parent_item->label( 1 ) ) ); - reset_cable( carrier, parent_item ); + add_msg_if_player_sees( pos, m_bad, + string_format( is_cable_item ? _( "The %s has come loose from the UPS." ) : + _( "The %s's cable has come loose from the UPS." ), type_name() ) ); + reset_link( carrier ); return false; } } + // Certain cable states should skip processing and also become inactive if dropped. if( ( link->t_state == link_state::no_link && link->s_state != link_state::vehicle_tow ) || link->t_state == link_state::bio_cable ) { - // Certain cable states should skip processing and also become inactive if dropped. if( carrier == nullptr ) { - return reset_cable( nullptr, parent_item, true, pos ); + return reset_link( nullptr, true, pos ); } return false; } @@ -12937,26 +12925,25 @@ bool item::process_cable( map &here, Character *carrier, const tripoint &pos, it // Lambda function for checking if a cable's been stretched too long, resetting it if so. // @return True if the cable is disconnected. - const auto is_cable_too_long = [this, carrier, pos, parent_item, last_t_abs_pos_is_oob]() { + const auto is_cable_too_long = [this, carrier, pos, last_t_abs_pos_is_oob, is_cable_item]() { if( debug_mode ) { - add_msg_debug( debugmode::DF_IUSE, "%s linked to %s%s, length %d/%d", - parent_item != nullptr ? parent_item->label( 1 ) : label( 1 ), + add_msg_debug( debugmode::DF_IUSE, "%s linked to %s%s, length %d/%d", type_name(), link->t_abs_pos.to_string_writable(), last_t_abs_pos_is_oob ? " (OoB)" : "", - link->max_length - charges, link->max_length ); + link->length, link->max_length ); } - if( charges == 0 && carrier != nullptr ) { - carrier->add_msg_if_player( m_warning, parent_item == nullptr ? - string_format( _( "Your %s is stretched to its limit!" ), label( 1 ) ) : - string_format( _( "Your %s's cable is stretched to its limit!" ), parent_item->label( 1 ) ) ); - } else if( charges < 0 ) { + if( link->length == link->max_length && carrier != nullptr ) { + carrier->add_msg_if_player( m_warning, + string_format( is_cable_item ? _( "Your %s is stretched to its limit!" ) : + _( "Your %s's cable is stretched to its limit!" ), type_name() ) ); + } else if( link->length > link->max_length ) { if( carrier != nullptr ) { - carrier->add_msg_if_player( m_bad, parent_item == nullptr ? - string_format( _( "Your over-extended %s breaks loose!" ), label( 1 ) ) : - string_format( _( "Your %s's over-extended cable breaks loose!" ), parent_item->label( 1 ) ) ); + carrier->add_msg_if_player( m_bad, + string_format( is_cable_item ? _( "Your over-extended %s breaks loose!" ) : + _( "Your %s's over-extended cable breaks loose!" ), type_name() ) ); } else { - add_msg_if_player_sees( pos, m_bad, parent_item == nullptr ? - string_format( _( "The over-extended %s breaks loose!" ), label( 1 ) ) : - string_format( _( "The %s's over-extended cable breaks loose!" ), parent_item->label( 1 ) ) ); + add_msg_if_player_sees( pos, m_bad, + string_format( is_cable_item ? _( "The over-extended %s breaks loose!" ) : + _( "The %s's over-extended cable breaks loose!" ), type_name() ) ); } return true; } @@ -12977,10 +12964,10 @@ bool item::process_cable( map &here, Character *carrier, const tripoint &pos, it if( check_length ) { // We can't get the exact connection point without the vehicle loaded, so fudge some forgiveness by adding the mount dimensions. // Better to err on the side of keeping things connected. - charges = link->max_length - rl_dist( here.getabs( pos ), link->t_abs_pos.raw() ) + - link->t_mount.abs().x + link->t_mount.abs().y; + link->length = rl_dist( here.getabs( pos ), link->t_abs_pos.raw() ) + + link->t_mount.abs().x + link->t_mount.abs().y; if( is_cable_too_long() ) { - return reset_cable( carrier, parent_item ); + return reset_link( carrier ); } } return false; @@ -12988,12 +12975,12 @@ bool item::process_cable( map &here, Character *carrier, const tripoint &pos, it // ... and the last recorded target point is in-bounds, try to recreate the vehicle pointer, disconnecting if it fails. const optional_vpart_position vp = here.veh_at( link->t_abs_pos ); if( !vp ) { - return reset_cable( carrier, parent_item, true, pos ); + return reset_link( carrier, true, pos ); } auto vp_displayed = vp.part_displayed(); if( vp_displayed && vp_displayed->part().has_flag( vp_flag::carried_flag ) ) { // Connected vehicle was racked, so disconnect. - return reset_cable( carrier, parent_item, true, pos ); + return reset_link( carrier, true, pos ); } link->t_veh_safe = vp.value().vehicle().get_safe_reference(); } @@ -13006,10 +12993,10 @@ bool item::process_cable( map &here, Character *carrier, const tripoint &pos, it // we should always process it to avoid erroneously skipping devices riding inside of it. if( last_t_abs_pos_is_oob && t_veh->velocity < HALF_MAPSIZE_X * 400 ) { if( check_length ) { - charges = link->max_length - rl_dist( here.getabs( pos ), link->t_abs_pos.raw() ) + - link->t_mount.abs().x + link->t_mount.abs().y; + link->length = rl_dist( here.getabs( pos ), + link->t_abs_pos.raw() ) + link->t_mount.abs().x + link->t_mount.abs().y; if( is_cable_too_long() ) { - return reset_cable( carrier, parent_item ); + return reset_link( carrier ); } } return false; @@ -13036,7 +13023,7 @@ bool item::process_cable( map &here, Character *carrier, const tripoint &pos, it } if( link_vp_index == -1 ) { // The part with cable ports was lost, so disconnect the cable. - return reset_cable( carrier, parent_item, true, pos ); + return reset_link( carrier, true, pos ); } // Set the new absolute position to the vehicle's origin. @@ -13047,28 +13034,27 @@ bool item::process_cable( map &here, Character *carrier, const tripoint &pos, it check_length = true; } - // If either the link's connected sides moved, check cable's length. + // If either of the link's connected sides moved, check the cable's length. if( check_length ) { - charges = link->max_length - rl_dist( pos, - t_veh_bub_pos + t_veh->part( link_vp_index ).precalc[0] ); + link->length = rl_dist( pos, t_veh_bub_pos + t_veh->part( link_vp_index ).precalc[0] ); if( is_cable_too_long() ) { - return reset_cable( carrier, parent_item ); + return reset_link( carrier ); } } // Extra behaviors for the cabled item. - if( parent_item != nullptr ) { + if( !is_cable_item ) { int turns_elapsed = to_turns( calendar::turn - link->last_processed ); link->last_processed = calendar::turn; int power_draw = 0; // Recharge or charge linked batteries - power_draw -= charge_linked_batteries( *parent_item, *t_veh, turns_elapsed ); + power_draw -= charge_linked_batteries( *t_veh, turns_elapsed ); // Tool power draw display - if( parent_item->active && parent_item->type->tool && parent_item->type->tool->power_draw > 0_W ) { - power_draw -= parent_item->type->tool->power_draw.value(); + if( active && type->tool && type->tool->power_draw > 0_W ) { + power_draw -= type->tool->power_draw.value(); } // Send total power draw to the vehicle so it can be displayed. @@ -13083,22 +13069,22 @@ bool item::process_cable( map &here, Character *carrier, const tripoint &pos, it return false; } -int item::charge_linked_batteries( item &linked_item, vehicle &linked_veh, int turns_elapsed ) +int item::charge_linked_batteries( vehicle &linked_veh, int turns_elapsed ) { - if( !link || link->charge_rate == 0 || turns_elapsed < 1 || link->charge_interval < 1 ) { - return 0; + if( link->wattage == 0 || turns_elapsed < 1 || link->charge_interval < 1 ) { + return link->wattage; } - if( !linked_item.is_battery() ) { - const item *parent_mag = linked_item.magazine_current(); + if( !is_battery() ) { + const item *parent_mag = magazine_current(); if( !parent_mag || !parent_mag->has_flag( flag_RECHARGE ) ) { return 0; } } - const bool power_in = link->charge_rate > 0; - if( power_in ? linked_item.ammo_remaining() >= linked_item.ammo_capacity( ammo_battery ) : - linked_item.ammo_remaining() <= 0 ) { + const bool power_in = link->wattage > 0; + if( power_in ? ammo_remaining() >= ammo_capacity( ammo_battery ) : + ammo_remaining() <= 0 ) { return 0; } @@ -13108,7 +13094,7 @@ int item::charge_linked_batteries( item &linked_item, vehicle &linked_veh, int t if( !calendar::once_every( time_duration::from_turns( link->charge_interval ) ) && short_time_passed ) { - return link->charge_rate; + return link->wattage; } // If a long time passed, multiply the total by the efficiency rather than cancelling a charge. @@ -13119,81 +13105,59 @@ int item::charge_linked_batteries( item &linked_item, vehicle &linked_veh, int t const int battery_deficit = linked_veh.discharge_battery( transfer_total, true ); // Around 85% efficient by default; a few of the discharges don't actually recharge if( battery_deficit == 0 && !( short_time_passed && one_in( link->charge_efficiency ) ) ) { - linked_item.ammo_set( itype_battery, linked_item.ammo_remaining() + transfer_total ); + ammo_set( itype_battery, ammo_remaining() + transfer_total ); } } else { // Around 85% efficient by default; a few of the discharges don't actually charge if( !( short_time_passed && one_in( link->charge_efficiency ) ) ) { const int battery_surplus = linked_veh.charge_battery( transfer_total, true ); if( battery_surplus == 0 ) { - linked_item.ammo_set( itype_battery, linked_item.ammo_remaining() - transfer_total ); + ammo_set( itype_battery, ammo_remaining() - transfer_total ); } } else { const std::pair linked_levels = linked_veh.connected_battery_power_level(); if( linked_levels.first < linked_levels.second ) { - linked_item.ammo_set( itype_battery, linked_item.ammo_remaining() - transfer_total ); + ammo_set( itype_battery, ammo_remaining() - transfer_total ); } } } - return link->charge_rate; + return link->wattage; } -bool item::reset_cable( Character *p, item *parent_item, const bool loose_message, - const tripoint sees_point ) +bool item::reset_link( Character *p, const bool loose_message, const tripoint cable_position ) { - active = false; + const bool is_cable_item = has_flag( flag_CABLE_SPOOL ); + if( is_cable_item ) { + active = false; + } if( !link ) { - if( parent_item != nullptr ) { - debugmsg( "%s's active cable lost its cable data!", parent_item->tname() ); - parent_item->contents_linked = false; - } else { - debugmsg( "Active cable %s lost its cable data!", tname() ); - } - charges = type->maximum_charges(); + debugmsg( "%s lost its link data!", tname() ); return has_flag( flag_AUTO_DELETE_CABLE ); } if( loose_message ) { if( p != nullptr ) { - p->add_msg_if_player( m_warning, parent_item == nullptr ? - string_format( _( "Your %s has come loose." ), label( 1 ) ) : - string_format( _( "Your %s's cable has come loose." ), parent_item->label( 1 ) ) ); + p->add_msg_if_player( m_warning, + string_format( is_cable_item ? _( "Your %s has come loose." ) : + _( "Your %s's cable has come loose." ), type_name() ) ); } else { - add_msg_if_player_sees( sees_point, m_warning, parent_item == nullptr ? - string_format( _( "The %s has come loose." ), label( 1 ) ) : - string_format( _( "The %s's cable has come loose." ), parent_item->label( 1 ) ) ); + add_msg_if_player_sees( cable_position, m_warning, + string_format( is_cable_item ? _( "The %s has come loose." ) : + _( "The %s's cable has come loose." ), type_name() ) ); } } - if( parent_item != nullptr ) { - parent_item->contents_linked = false; - } - const int respool_length = 5; - if( link->max_length - charges > respool_length ) { + + const int respool_threshold = 5; + if( link->length > respool_threshold ) { // Cables that are too long need to be manually rewound before reuse. link->s_state = link_state::needs_reeling; return false; } - charges = link->max_length; link.reset(); return has_flag( flag_AUTO_DELETE_CABLE ); } -void item::reset_cables( Character *p ) -{ - if( !contents_linked ) { - debugmsg( "Tried to reset %s's cables but it wasn't plugged in.", tname() ); - return; - } - std::vector cables = contents.cables( true ); - for( item *cable : cables ) { - if( cable->reset_cable( p, this ) ) { - remove_item( *cable ); - } - } - contents_linked = false; -} - bool item::process_linked_item( Character *carrier, const tripoint & /*pos*/, const link_state required_state ) { @@ -13349,18 +13313,8 @@ bool item::process_internal( map &here, Character *carrier, const tripoint &pos, wetness -= 1; } - if( contents_linked ) { - std::vector cables = contents.cables( true ); - if( !cables.empty() ) { - for( item *cable : cables ) { - if( cable->process_cable( here, carrier, pos, this ) ) { - remove_item( *cable ); - } - } - } else { - debugmsg( "%s was labeled as plugged in but had no active cables inside.", tname() ); - contents_linked = false; - } + if( link ) { + process_link( here, carrier, pos ); } // Remaining stuff is only done for active items. @@ -13424,7 +13378,8 @@ 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! - return process_cable( here, carrier, pos ); + active = false; + return false; } if( has_flag( flag_IS_UPS ) && mark_flag() ) { // DO NOT process this as a tool! It really isn't! diff --git a/src/item.h b/src/item.h index 63609ff16423f..e9289cabb63a7 100644 --- a/src/item.h +++ b/src/item.h @@ -1419,7 +1419,7 @@ class item : public visitable link_state s_state = link_state::no_link; /// State of the link's target, the end represented by t_abs_pos, @ref link_state. link_state t_state = link_state::no_link; - /// The last turn process_cable was called on this cable. Used for time the cable spends outside the bubble. + /// The last turn process_link was called on this cable. Used for time the cable spends outside the bubble. time_point last_processed = calendar::turn; /// Absolute position of the linked target vehicle/appliance. tripoint_abs_ms t_abs_pos = tripoint_abs_ms( tripoint_min ); @@ -1429,10 +1429,12 @@ class item : public visitable safe_reference t_veh_safe; // NOLINT(cata-serialize) /// The linked part's mount offset on the target vehicle. point t_mount = point_zero; + /// The current slack of the cable. + int length = 0; /// The maximum length of the cable. Set during initialization. int max_length = 2; /// The cable's charge rate in watts. Set during initialization. - int charge_rate = 0; + int wattage = 0; /// The turn interval between charges. Set during initialization. int charge_interval = -1; /// one_in(this) chance to fail adding 1 charge. Set during initialization. @@ -1451,29 +1453,26 @@ class item : public visitable void serialize( JsonOut &jsout ) const; void deserialize( const JsonObject &data ); }; - cata::value_ptr link; /** * Brings a cable item back to its initial state. + * @param p Set to character that's holding the linked item, nullptr if none. + * @param loose_message If there should be a notification that the link was disconnected. + * @param cable_position Position of the linked item, used to determine if the player can see the link becoming loose. * @return True if the cable should be deleted. */ - bool reset_cable( Character *p = nullptr, item *parent_item = nullptr, - bool loose_message = false, tripoint sees_point = tripoint_zero ); - /** - * Resets all of an items cables. - */ - void reset_cables( Character *p ); + bool reset_link( Character *p = nullptr, bool loose_message = false, + tripoint cable_position = tripoint_zero ); /** * @brief Exchange power between an item's batteries and the vehicle/appliance it's linked to. - * @brief A positive link.charge_rate will charge the item at the expense of the vehicle, - * while a negative link.charge_rate will charge the vehicle at the expense of the item. + * @brief A positive link.wattage will charge the item at the expense of the vehicle, + * while a negative link.wattage will charge the vehicle at the expense of the item. * - * @param linked_item The item that contains the linking cable. * @param linked_veh The vehicle the item is connected to. * @param turns_elapsed The number of turns the link has spent outside the reality bubble. Default 1. * @return The amount of power given or taken to be displayed; ignores turns_elapsed and inefficiency. */ - int charge_linked_batteries( item &linked_item, vehicle &linked_veh, int turns_elapsed = 1 ); + int charge_linked_batteries( vehicle &linked_veh, int turns_elapsed = 1 ); /** * Whether the item should be processed (by calling @ref process). @@ -2910,8 +2909,7 @@ class item : public visitable // Place conditions that should remove fake smoke item in this sub-function bool process_fake_smoke( map &here, Character *carrier, const tripoint &pos ); bool process_fake_mill( map &here, Character *carrier, const tripoint &pos ); - bool process_cable( map &here, Character *carrier, const tripoint &pos, - item *parent_item = nullptr ); + bool process_link( map &here, Character *carrier, const tripoint &pos ); bool process_linked_item( Character *carrier, const tripoint &pos, link_state required_state ); bool process_blackpowder_fouling( Character *carrier ); bool process_tool( Character *carrier, const tripoint &pos ); @@ -2976,6 +2974,7 @@ class item : public visitable public: // any relic data specific to this item cata::value_ptr relic_data; + cata::value_ptr link; int charges = 0; units::energy energy = 0_mJ; // Amount of energy currently stored in a battery @@ -2998,9 +2997,6 @@ class item : public visitable bool ethereal = false; int wetness = 0; // Turns until this item is completely dry. - // This contains a cable with an active link. Is NOT true for linked cable items themselves. - bool contents_linked = false; - int seed = rng( 0, INT_MAX ); // A random seed for layering and other options harvest_drop_type_id dropped_from = diff --git a/src/item_factory.cpp b/src/item_factory.cpp index 285377f00f31b..deee176a51f9d 100644 --- a/src/item_factory.cpp +++ b/src/item_factory.cpp @@ -3722,17 +3722,13 @@ void Item_factory::add_special_pockets( itype &def ) if( !has_pocket_type( def.pockets, item_pocket::pocket_type::CABLE ) ) { const use_function *iuse = def.get_use( "link_up" ); if( iuse != nullptr ) { - const link_up_actor *actor_ptr = - static_cast( iuse->get_actor_ptr() ); - if( actor_ptr != nullptr && !actor_ptr->is_cable_item ) { - pocket_data cable_pocket( item_pocket::pocket_type::CABLE ); - cable_pocket.rigid = true; - cable_pocket.volume_capacity = units::from_milliliter( 1 ); - cable_pocket.max_contains_weight = units::from_gram( 1 ); - cable_pocket.weight_multiplier = 0.0f; - cable_pocket.volume_multiplier = 0.0f; - def.pockets.emplace_back( cable_pocket ); - } + pocket_data cable_pocket( item_pocket::pocket_type::CABLE ); + cable_pocket.rigid = true; + cable_pocket.volume_capacity = units::from_milliliter( 1 ); + cable_pocket.max_contains_weight = units::from_gram( 1 ); + cable_pocket.weight_multiplier = 0.0f; + cable_pocket.volume_multiplier = 0.0f; + def.pockets.emplace_back( cable_pocket ); } } } diff --git a/src/iuse_actor.cpp b/src/iuse_actor.cpp index a55e3da784c33..e30c7447e81da 100644 --- a/src/iuse_actor.cpp +++ b/src/iuse_actor.cpp @@ -4284,11 +4284,10 @@ std::unique_ptr link_up_actor::clone() const void link_up_actor::load( const JsonObject &jo ) { - jo.read( "is_cable_item", is_cable_item ); - jo.read( "cable_type", type ); jo.read( "cable_length", cable_length ); - jo.read( "charge_rate", charge_rate ); + jo.read( "wattage", wattage ); jo.read( "efficiency", charge_efficiency ); + jo.read( "move_cost", move_cost ); jo.read( "menu_text", menu_text ); jo.read( "targets", targets ); } @@ -4328,12 +4327,13 @@ void link_up_actor::info( const item &, std::vector &dump ) const } dump.emplace_back( "TOOL", _( "Cable length: " ), cable_length ); - if( charge_rate != 0_W ) { - std::string wattage = string_format( _( "%+4.1f W" ), units::to_milliwatt( charge_rate ) / 1000.f ); - if( charge_rate > 0_W ) { - dump.emplace_back( "TOOL", _( "Charge rate: " ), wattage ); + if( wattage != 0_W ) { + std::string wattage_display = string_format( _( "%+4.1f W" ), + units::to_milliwatt( wattage ) / 1000.f ); + if( wattage > 0_W ) { + dump.emplace_back( "TOOL", _( "Charge rate: " ), wattage_display ); } else { - dump.emplace_back( "TOOL", _( "Discharge rate: " ), wattage ); + dump.emplace_back( "TOOL", _( "Discharge rate: " ), wattage_display ); } } } @@ -4352,85 +4352,54 @@ std::optional link_up_actor::use( Character *p, item &it, bool t, const tri return std::nullopt; } - if( !is_cable_item && !it.has_pocket_type( item_pocket::pocket_type::CABLE ) ) { - debugmsg( "Called a link_up action on an item (%s) without a CABLE pocket or \"is_cable_item: true\" set!", - it.tname() ); + if( targets.empty() ) { + debugmsg( "Link up action for %s doesn't have any targets!", it.tname() ); return std::nullopt; } - // If the item is the cable, we can assign some variables now. - // Otherwise, wait until after target selection to create the cable and assign this pointer. - item *cable = nullptr; - const int respool_length = 5; + return link_up( p, it ); +} + +std::optional link_up_actor::link_up( Character *p, item &it ) const +{ + const bool is_cable_item = it.has_flag( flag_CABLE_SPOOL ); + const std::string cable_name = is_cable_item ? it.type_name() : + string_format( _( "%s's cable" ), it.type_name() ); + + const int respool_threshold = 5; const int respool_time_per_square = 200; - bool is_respool_length = false; - if( is_cable_item ) { - cable = ⁢ - } else { - if( !it.get_contents().cables().empty() ) { - cable = it.get_contents().cables().front(); - } - } - if( cable != nullptr ) { - if( !cable->link ) { - cable->link = cata::make_value(); - } - is_respool_length = cable->link->max_length - cable->charges > respool_length; - if( cable->link->s_state == link_state::needs_reeling ) { - if( query_yn( is_cable_item ? string_format( _( "Reel in the %s?" ), it.label( 1 ) ) : - string_format( _( "Reel in the %s's cable?" ), it.label( 1 ) ) ) ) { - p->assign_activity( player_activity( reel_cable_activity_actor( ( cable->link->max_length - - cable->charges - respool_length ) * respool_time_per_square, item_location{*p, cable}, - is_cable_item ? item_location::nowhere : item_location{*p, &it} ) ) ); - } - return 0; + const int respool_time_total = it.link ? ( it.link->length - respool_threshold ) * respool_time_per_square : 0; + const bool past_respool_threshold = it.link && it.link->length > respool_threshold; + + if( it.link && it.link->s_state == link_state::needs_reeling ) { + if( query_yn( _( "Reel in the %s?" ), cable_name ) ) { + p->assign_activity( player_activity( reel_cable_activity_actor( respool_time_total, item_location{*p, &it} ) ) ); } + return 0; } - if( it.contents_linked || ( cable != nullptr && !cable->link->has_state( link_state::no_link ) ) ) { - // Cables without any free ends can only be disconnected. + // If it's a cable item without any free ends OR a device with any connection, only allow disconnecting. + if( it.link && is_cable_item ? !it.link->has_state( link_state::no_link ) : !it.link->has_no_links() ) { if( targets.count( link_state::no_link ) > 0 ) { - if( is_cable_item ) { - if( query_yn( string_format( _( "Detach and re-spool the %s?" ), it.label( 1 ) ) ) ) { - it.reset_cable( p ); - if( cable->link && cable->link->s_state == link_state::needs_reeling ) { - p->assign_activity( player_activity( reel_cable_activity_actor( ( cable->link->max_length - - cable->charges - respool_length ) * respool_time_per_square, item_location{*p, cable}, - is_cable_item ? item_location::nowhere : item_location{*p, &it} ) ) ); - } else { - p->add_msg_if_player( m_info, string_format( _( "You detach the %s." ), it.label( 1 ) ) ); - } - return 0; - } - } else { - if( query_yn( string_format( _( "Detach and re-spool the %s's cable?" ), it.label( 1 ) ) ) ) { - it.reset_cables( p ); - if( cable->link && cable->link->s_state == link_state::needs_reeling ) { - p->assign_activity( player_activity( reel_cable_activity_actor( ( cable->link->max_length - - cable->charges - respool_length ) * respool_time_per_square, item_location{*p, cable}, - is_cable_item ? item_location::nowhere : item_location{*p, &it} ) ) ); - } else { - p->add_msg_if_player( m_info, string_format( _( "You gather the cable up with the %s." ), - it.label( 1 ) ) ); - } - return 0; + if( query_yn( _( "Detach and re-spool the %s?" ), cable_name ) ) { + it.reset_link( p ); + if( it.link && it.link->s_state == link_state::needs_reeling ) { + p->assign_activity( player_activity( reel_cable_activity_actor( respool_time_total, item_location{*p, &it} ) ) ); + } else { + p->add_msg_if_player( m_info, string_format( is_cable_item ? _( "You detach the %s." ) : + _( "You gather the %s's cable up with it." ), it.type_name() ) ); } + return 0; } } return std::nullopt; } - if( targets.empty() ) { - debugmsg( "Link up action for %s doesn't have any targets!", it.tname() ); - return std::nullopt; - } - uilist link_menu; - if( cable == nullptr || cable->link->has_no_links() ) { - // Cable doesn't have any connections, or is a device cable: + if( !it.link || it.link->has_no_links() ) { + // This is either a device's cable or doesn't have any connections yet. - link_menu.text = is_cable_item ? string_format( _( "Attaching the %s:" ), it.label( 1 ) ) : - string_format( _( "Attaching the %s's cable:" ), it.label( 1 ) ); + link_menu.text = string_format( _( "Attaching the %s:" ), cable_name ); if( targets.count( link_state::vehicle_port ) > 0 ) { link_menu.addentry( 0, true, -1, _( "Attach to vehicle controls or appliance" ) ); } @@ -4458,57 +4427,57 @@ std::optional link_up_actor::use( Character *p, item &it, bool t, const tri } } if( targets.count( link_state::no_link ) > 0 ) { - link_menu.addentry( 999, false, -1, - is_respool_length ? _( "Detach and re-spool" ) : _( "Detach" ) ); + link_menu.addentry( 999, false, -1, _( "Detach" ) ); } - } else if( cable != nullptr && cable->link->has_state( link_state::vehicle_tow ) ) { - // Cables that started a tow can finish one or detach, nothing else. + } else if( it.link->has_state( link_state::vehicle_tow ) ) { + // Cables that started a tow can finish one or detach; nothing else. - link_menu.addentry( 10, cable->link->t_state == link_state::vehicle_tow, -1, + link_menu.text = string_format( _( "Attaching the %s:" ), cable_name ); + link_menu.addentry( 10, it.link->t_state == link_state::vehicle_tow, -1, _( "Attach loose end to towing vehicle" ) ); - link_menu.addentry( 11, cable->link->s_state == link_state::vehicle_tow, -1, + link_menu.addentry( 11, it.link->s_state == link_state::vehicle_tow, -1, _( "Attach loose end to towed vehicle" ) ); if( targets.count( link_state::no_link ) > 0 ) { link_menu.addentry( 999, true, -1, - is_respool_length ? _( "Detach and re-spool" ) : _( "Detach" ) ); + past_respool_threshold ? _( "Detach and re-spool" ) : _( "Detach" ) ); } } else if( is_cable_item ) { - // Cable has one connection already: + // This is a cable item and it has one connection already: - link_menu.text = string_format( _( "Attaching the %s's loose end:" ), it.label( 1 ) ); + link_menu.text = string_format( _( "Attaching the %s's loose end:" ), cable_name ); // TODO: Allow plugging UPSes and Solar Packs into more than just bionics. // There is already code to support setting up a link, but none for actual functionality. if( targets.count( link_state::vehicle_port ) > 0 ) { - link_menu.addentry( 0, !cable->link->has_state( link_state::ups ) && - !cable->link->has_state( link_state::solarpack ), + link_menu.addentry( 0, !it.link->has_state( link_state::ups ) && + !it.link->has_state( link_state::solarpack ), -1, _( "Attach loose end to vehicle controls or appliance" ) ); } if( targets.count( link_state::vehicle_battery ) > 0 ) { - link_menu.addentry( 1, !cable->link->has_state( link_state::ups ) && - !cable->link->has_state( link_state::solarpack ), + link_menu.addentry( 1, !it.link->has_state( link_state::ups ) && + !it.link->has_state( link_state::solarpack ), -1, _( "Attach loose end to vehicle battery or appliance" ) ); } if( targets.count( link_state::bio_cable ) > 0 && !p->get_remote_fueled_bionic().is_empty() ) { - link_menu.addentry( 20, !cable->link->has_state( link_state::bio_cable ), + link_menu.addentry( 20, !it.link->has_state( link_state::bio_cable ), -1, _( "Attach loose end to Cable Charger System CBM" ) ); } if( targets.count( link_state::ups ) > 0 && !( p->all_items_with_flag( flag_IS_UPS ) ).empty() ) { - link_menu.addentry( 21, cable->link->has_state( link_state::bio_cable ), + link_menu.addentry( 21, it.link->has_state( link_state::bio_cable ), -1, _( "Attach loose end to UPS" ) ); } if( targets.count( link_state::solarpack ) > 0 ) { const bool has_solar_pack_on = p->worn_with_flag( flag_SOLARPACK_ON ); if( has_solar_pack_on || p->worn_with_flag( flag_SOLARPACK ) ) { - link_menu.addentry( 22, has_solar_pack_on && cable->link->has_state( link_state::bio_cable ), + link_menu.addentry( 22, has_solar_pack_on && it.link->has_state( link_state::bio_cable ), -1, _( "Attach loose end to solar pack" ) ); } } if( targets.count( link_state::no_link ) > 0 ) { link_menu.addentry( 999, true, -1, - is_respool_length ? _( "Detach and re-spool" ) : _( "Detach" ) ); + past_respool_threshold ? _( "Detach and re-spool" ) : _( "Detach" ) ); } } else { debugmsg( "An already connected device (%s) tried to link up again!", it.tname() ); @@ -4522,153 +4491,242 @@ std::optional link_up_actor::use( Character *p, item &it, bool t, const tri choice = link_menu.ret; } - if( choice < 0 ) { // Cancelled selection. - - p->add_msg_if_player( _( "Never mind" ) ); + if( choice < 0 ) { + // Cancelled selection. + p->add_msg_if_player( _( "Never mind." ) ); return std::nullopt; - } else if( choice == 999 ) { // Selection: Unconnect & respool. - - if( is_cable_item ) { - it.reset_cable( p ); - } else { - it.reset_cables( p ); - } - if( cable->link && cable->link->s_state == link_state::needs_reeling ) { + } else if( choice == 999 ) { + // Selection: Unconnect & respool. + it.reset_link( p ); + if( it.link && it.link->s_state == link_state::needs_reeling ) { // Cables that are too long need to be manually rewound before reuse. - // 2 seconds per square - p->assign_activity( player_activity( reel_cable_activity_actor( ( cable->link->max_length - - cable->charges - respool_length ) * respool_time_per_square, item_location{*p, cable}, - is_cable_item ? item_location::nowhere : item_location{*p, &it} ) ) ); + p->assign_activity( player_activity( reel_cable_activity_actor( respool_time_total, item_location{*p, &it} ) ) ); return 0; } else { - p->add_msg_if_player( m_info, is_cable_item ? string_format( _( "You detach the %s." ), - it.label( 1 ) ) : string_format( _( "You gather the cable up with the %s." ), it.label( 1 ) ) ); + p->add_msg_if_player( m_info, string_format( is_cable_item ? _( "You detach the %s." ) : + _( "You gather the %s's cable up with it." ), it.type_name() ) ); } return 0; } - map &here = get_map(); - const int move_cost = 5; - // Lambda that assigns an address to the cable pointer, creating the cable if needed. Returns false if it failed. - const auto set_cable_pointer = [this, &it, &cable]() { - if( cable == nullptr ) { - item new_cable( type ); - if( !it.put_in( new_cable, item_pocket::pocket_type::CABLE ).success() ) { - debugmsg( "Failed to add the %s inside the %s!", new_cable.tname(), it.tname() ); - return false; - } - cable = it.get_contents().cables().front(); - cable->link = cata::make_value(); - } - return true; - }; - if( choice == 0 || choice == 1 ) { // Selection: Attach electrical cable to vehicle ports / appliances, OR vehicle batteries. + return link_to_veh_app( p, it, choice == 0 ); - // You used to be able to plug cables in anywhere on a vehicle, so there's extra effort here - // to inform players that they can only plug them into dashboards or electrical controls now. - const auto can_link = [&here, &choice]( const tripoint & point ) { - const optional_vpart_position ovp = here.veh_at( point ); - if( !ovp ) { - return false; - } - if( choice == 0 ) { - return ovp.avail_part_with_feature( "CABLE_PORTS" ) || ovp.avail_part_with_feature( "APPLIANCE" ); - } else if( choice == 1 ) { - if( ovp.avail_part_with_feature( "APPLIANCE" ) ) { - return true; - } - const vehicle &veh = ovp->vehicle(); - for( const int p : veh.parts_at_relative( ovp->mount(), /* use_cache = */ false ) ) { - const vehicle_part &vp_here = veh.part( p ); - if( vp_here.is_battery() && !vp_here.is_broken() ) { - return true; - } - } - } - return false; + } else if( choice == 10 || choice == 11 ) { + // Selection: Attach tow cable to towing/towed vehicle. + return link_tow_cable( p, it, choice == 10 ); + } + + map &here = get_map(); + + if( choice == 20 ) { + // Selection: Attach electrical cable to Cable Charger System CBM. + if( !it.link ) { + it.link = cata::make_value(); + } + if( it.link->has_no_links() ) { + it.link->t_state = link_state::bio_cable; + p->add_msg_if_player( m_info, _( "You attach the cable to your Cable Charger System." ) ); + } else if( it.link->s_state == link_state::ups ) { + it.link->t_state = link_state::bio_cable; + p->add_msg_if_player( m_good, _( "You are now plugged into the UPS." ) ); + } else if( it.link->s_state == link_state::solarpack ) { + it.link->t_state = link_state::bio_cable; + p->add_msg_if_player( m_good, _( "You are now plugged into the solar backpack." ) ); + } else if( it.link->t_state == link_state::vehicle_port || + it.link->t_state == link_state::vehicle_battery ) { + it.link->s_state = link_state::bio_cable; + p->add_msg_if_player( m_good, _( "You are now plugged into the vehicle." ) ); + } + p->moves -= move_cost; + it.process( here, p, p->pos() ); + return 0; + + } else if( choice == 21 ) { + // Selection: Attach electrical cable to ups. + item_location loc; + avatar *you = p->as_avatar(); + const std::string choose_ups = _( "Choose UPS:" ); + const std::string dont_have_ups = _( "You don't have any UPS." ); + auto ups_filter = [&]( const item & itm ) { + return itm.has_flag( flag_IS_UPS ); }; - const std::optional pnt_ = choose_adjacent_highlight( _( "Attach the cable where?" ), - "", can_link, false, false ); - if( !pnt_ ) { + + if( you != nullptr ) { + loc = game_menus::inv::titled_filter_menu( ups_filter, *you, choose_ups, -1, dont_have_ups ); + } + if( !loc ) { + p->add_msg_if_player( _( "Never mind." ) ); return std::nullopt; } - const tripoint &pnt = *pnt_; - const optional_vpart_position t_vp = here.veh_at( pnt ); - if( !can_link( pnt ) ) { - if( choice == 0 && t_vp && t_vp->vehicle().has_part( "CABLE_PORTS" ) ) { - p->add_msg_if_player( m_info, - _( "You can't attach it there; try the dashboard or electronics controls." ) ); - } else if( choice == 1 && t_vp && t_vp->vehicle().batteries.empty() ) { - p->add_msg_if_player( m_info, - _( "You can't attach it there; try the battery." ) ); - } else { - p->add_msg_if_player( m_info, _( "You can't attach it there." ) ); - } - return std::nullopt; + if( !it.link ) { + it.link = cata::make_value(); + } + if( it.link->has_no_links() ) { + p->add_msg_if_player( m_info, _( "You attach the cable to the UPS." ) ); + } else if( it.link->t_state == link_state::bio_cable ) { + p->add_msg_if_player( m_good, _( "You are now plugged into the UPS." ) ); + } else if( it.link->s_state == link_state::solarpack ) { + p->add_msg_if_player( m_good, _( "You link up the UPS and the solar backpack." ) ); + } else if( it.link->t_state == link_state::vehicle_port || + it.link->t_state == link_state::vehicle_battery ) { + p->add_msg_if_player( m_good, _( "You link up the UPS and the vehicle." ) ); } + it.link->s_state = link_state::ups; + loc->set_var( "cable", "plugged_in" ); + p->moves -= move_cost; + it.process( here, p, p->pos() ); + return 0; + + } else if( choice == 22 ) { + // Selection: Attach electrical cable to solar pack. + item_location loc; + avatar *you = p->as_avatar(); + const std::string choose_solar = _( "Choose solar pack:" ); + const std::string dont_have_solar = _( "You need an unfolded solar pack." ); + auto solar_filter = [&]( const item & itm ) { + return itm.has_flag( flag_SOLARPACK_ON ); + }; - if( !set_cable_pointer() ) { + if( you != nullptr ) { + loc = game_menus::inv::titled_filter_menu( solar_filter, *you, choose_solar, -1, dont_have_solar ); + } + if( !loc ) { + p->add_msg_if_player( _( "Never mind." ) ); return std::nullopt; } - if( !cable->link->has_state( link_state::vehicle_port ) && - !cable->link->has_state( link_state::vehicle_battery ) ) { - // Starting a new connection to a vehicle or connecting a cable CBM to a vehicle. + if( !it.link ) { + it.link = cata::make_value(); + } + if( it.link->has_no_links() ) { + p->add_msg_if_player( m_info, _( "You attach the cable to the solar pack." ) ); + } else if( it.link->t_state == link_state::bio_cable ) { + p->add_msg_if_player( m_good, _( "You are now plugged into the solar pack." ) ); + } else if( it.link->s_state == link_state::ups ) { + p->add_msg_if_player( m_good, _( "You link up the solar pack and the UPS." ) ); + } else if( it.link->t_state == link_state::vehicle_port || + it.link->t_state == link_state::vehicle_battery ) { + p->add_msg_if_player( m_good, _( "You link up the solar pack and the vehicle." ) ); + } + it.link->s_state = link_state::solarpack; + loc->set_var( "cable", "plugged_in" ); + p->moves -= move_cost; + it.process( here, p, p->pos() ); + return 0; + } + return std::nullopt; +} + +std::optional link_up_actor::link_to_veh_app( Character *p, item &it, + const bool to_ports ) const +{ + map &here = get_map(); + // Selection: Attach electrical cable to vehicle ports / appliances, OR vehicle batteries. - if( cable->link->has_no_links() ) { - p->add_msg_if_player( _( "You connect the %1$s to the %2$s." ), it.label( 1 ), - t_vp->vehicle().name ); - } else if( cable->link->has_state( link_state::bio_cable ) ) { - p->add_msg_if_player( m_good, _( "You are now plugged into the %s." ), t_vp->vehicle().name ); - cable->link->s_state = link_state::bio_cable; - } else { - debugmsg( "Failed to connect the %s, it tried to make an invalid connection!", cable->tname() ); - return std::nullopt; + // You used to be able to plug cables in anywhere on a vehicle, so there's extra effort here + // to inform players that they can only plug them into dashboards or electrical controls now. + const auto can_link = [&here, &to_ports]( const tripoint & point ) { + const optional_vpart_position ovp = here.veh_at( point ); + if( !ovp ) { + return false; + } + if( to_ports ) { + return ovp.avail_part_with_feature( "CABLE_PORTS" ) || ovp.avail_part_with_feature( "APPLIANCE" ); + } else { + if( ovp.avail_part_with_feature( "APPLIANCE" ) ) { + return true; } - cable->link->t_state = choice == 0 ? link_state::vehicle_port : link_state::vehicle_battery; - cable->link->t_abs_pos = here.getglobal( pnt ); - cable->link->t_mount = t_vp->mount(); - cable->link->max_length = cable_length != -1 ? cable_length : type->maximum_charges(); - cable->link->charge_efficiency = charge_efficiency; - cable->link->charge_rate = charge_rate.value(); - // Convert wattage to how long it takes to charge 1 kW, the unit batteries use. - // -1 means batteries won't be charged, but it can still provide epower to devices. - cable->link->charge_interval = charge_rate == 0_W ? -1 : - std::max( 1, static_cast( std::floor( 1000000.0 / abs( charge_rate.value() ) + 0.5 ) ) ); - cable->link->last_processed = calendar::turn; - cable->active = true; - it.contents_linked = !is_cable_item; - p->moves -= move_cost; - it.process( here, p, p->pos() ); + const vehicle &veh = ovp->vehicle(); + for( const int p : veh.parts_at_relative( ovp->mount(), /* use_cache = */ false ) ) { + const vehicle_part &vp_here = veh.part( p ); + if( vp_here.is_battery() && !vp_here.is_broken() ) { + return true; + } + } + } + return false; + }; + const std::optional pnt_ = choose_adjacent_highlight( _( "Attach the cable where?" ), + "", can_link, false, false ); + if( !pnt_ ) { + return std::nullopt; + } + const tripoint &pnt = *pnt_; + const optional_vpart_position t_vp = here.veh_at( pnt ); + if( !can_link( pnt ) ) { + if( to_ports && t_vp && t_vp->vehicle().has_part( "CABLE_PORTS" ) ) { + p->add_msg_if_player( m_info, + _( "You can't attach it there; try the dashboard or electronics controls." ) ); + } else if( !to_ports && t_vp && t_vp->vehicle().batteries.empty() ) { + p->add_msg_if_player( m_info, + _( "You can't attach it there; try the battery." ) ); } else { - // Connecting one vehicle/appliance to another. + p->add_msg_if_player( m_info, _( "You can't attach it there." ) ); + } + return std::nullopt; + } - if( !cable->link->t_veh_safe ) { - debugmsg( "Failed to connect the %s, it lost its vehicle pointer!", cable->tname() ); - return std::nullopt; - } - vehicle *const target_veh = &t_vp->vehicle(); - vehicle *const prev_veh = cable->link->t_veh_safe.get(); - if( prev_veh == target_veh ) { - p->add_msg_if_player( m_warning, _( "You cannot connect the %s to itself." ), prev_veh->name ); + if( !it.link ) { + it.link = cata::make_value(); + } + if( !it.link->has_state( link_state::vehicle_port ) && + !it.link->has_state( link_state::vehicle_battery ) ) { + // Starting a new connection to a vehicle or connecting a cable CBM to a vehicle. + + if( it.link->has_no_links() ) { + p->add_msg_if_player( _( "You connect the %1$s to the %2$s." ), it.type_name(), + t_vp->vehicle().name ); + } else if( it.link->has_state( link_state::bio_cable ) ) { + p->add_msg_if_player( m_good, _( "You are now plugged into the %s." ), t_vp->vehicle().name ); + it.link->s_state = link_state::bio_cable; + } else { + debugmsg( "Failed to connect the %s, it tried to make an invalid connection!", it.tname() ); + return std::nullopt; + } + it.link->t_state = to_ports ? link_state::vehicle_port : link_state::vehicle_battery; + it.link->t_abs_pos = here.getglobal( pnt ); + it.link->t_mount = t_vp->mount(); + it.link->max_length = cable_length != -1 ? cable_length : it.type->maximum_charges(); + it.link->charge_efficiency = charge_efficiency; + it.link->wattage = wattage.value(); + // Convert wattage to how long it takes to charge 1 kW, the unit batteries use. + // -1 means batteries won't be charged, but it can still provide epower to devices. + it.link->charge_interval = wattage == 0_W ? -1 : + std::max( 1, static_cast( std::floor( 1000000.0 / abs( wattage.value() ) + 0.5 ) ) ); + it.link->last_processed = calendar::turn; + p->moves -= move_cost; + it.process( here, p, p->pos() ); + return 0; + + } else { + // Connecting one vehicle/appliance to another. + + if( !it.link->t_veh_safe ) { + debugmsg( "Failed to connect the %s, it lost its vehicle pointer!", it.tname() ); + return std::nullopt; + } + vehicle *const target_veh = &t_vp->vehicle(); + vehicle *const prev_veh = it.link->t_veh_safe.get(); + if( prev_veh == target_veh ) { + p->add_msg_if_player( m_warning, _( "You cannot connect the %s to itself." ), prev_veh->name ); + return std::nullopt; + } + const std::pair prev_target = std::make_pair( + here.getabs( prev_veh->mount_to_tripoint( it.link->t_mount ) ), + prev_veh->global_square_location().raw() ); + for( const vpart_reference &vpr : target_veh->get_any_parts( "POWER_TRANSFER" ) ) { + if( vpr.part().target.first == prev_target.first && + vpr.part().target.second == prev_target.second ) { + p->add_msg_if_player( m_warning, _( "The %1$s and %2$s are already connected." ), + target_veh->name, prev_veh->name ); return std::nullopt; } - const std::pair prev_target = std::make_pair( - here.getabs( prev_veh->mount_to_tripoint( cable->link->t_mount ) ), - prev_veh->global_square_location().raw() ); - for( const vpart_reference &vpr : target_veh->get_any_parts( "POWER_TRANSFER" ) ) { - if( vpr.part().target.first == prev_target.first && - vpr.part().target.second == prev_target.second ) { - p->add_msg_if_player( m_warning, _( "The %1$s and %2$s are already connected." ), - target_veh->name, prev_veh->name ); - return std::nullopt; - } - } + } const itype_id item_id = it.typeId(); bool vpid_found = false; @@ -4679,102 +4737,104 @@ std::optional link_up_actor::use( Character *p, item &it, bool t, const tri } } - if( !vpid_found ) { - debugmsg( "item %s is not base item of any vehicle part! Using jumper_cable", item_id.c_str() ); - } - const vpart_id vpid( vpid_found ? item_id.str() : "jumper_cable" ); - - point vcoords = cable->link->t_mount; - vehicle_part source_part( vpid, vpid_found ? item( it ) : item( "jumper_cable" ) ); - source_part.target.first = here.getabs( pnt ); - source_part.target.second = target_veh->global_square_location().raw(); - prev_veh->install_part( vcoords, std::move( source_part ) ); - - vcoords = t_vp->mount(); - vehicle_part target_part( vpid, vpid_found ? item( it ) : item( "jumper_cable" ) ); - target_part.target.first = prev_target.first; - target_part.target.second = prev_target.second; - target_veh->install_part( vcoords, std::move( target_part ) ); + if( !vpid_found ) { + debugmsg( "item %s is not base item of any vehicle part! Using jumper_cable as a fallback", + item_id.c_str() ); + } + const vpart_id vpid( vpid_found ? item_id.str() : "jumper_cable" ); - p->add_msg_if_player( m_good, _( "You link up the %1$s and the %2$s." ), - prev_veh->name, target_veh->name ); + point vcoords = it.link->t_mount; + vehicle_part source_part( vpid, vpid_found ? item( it ) : item( "jumper_cable" ) ); + source_part.target.first = here.getabs( pnt ); + source_part.target.second = target_veh->global_square_location().raw(); + prev_veh->install_part( vcoords, std::move( source_part ) ); - return 1; // Let the cable be destroyed. - } + vcoords = t_vp->mount(); + vehicle_part target_part( vpid, vpid_found ? item( it ) : item( "jumper_cable" ) ); + target_part.target.first = prev_target.first; + target_part.target.second = prev_target.second; + target_veh->install_part( vcoords, std::move( target_part ) ); - } else if( choice == 10 || choice == 11 ) { // Selection: Attach tow cable to towing/towed vehicle. + p->add_msg_if_player( m_good, _( "You link up the %1$s and the %2$s." ), + prev_veh->name, target_veh->name ); - if( !set_cable_pointer() ) { - return std::nullopt; - } + return 1; // Let the cable be destroyed. + } +} - const auto can_link = [&here]( const tripoint & point ) { - const optional_vpart_position ovp = here.veh_at( point ); - return ovp && ovp->vehicle().is_external_part( point ); - }; +std::optional link_up_actor::link_tow_cable( Character *p, item &it, + const bool to_towing ) const +{ + map &here = get_map(); - const std::optional pnt_ = choose_adjacent_highlight( - choice == 10 ? _( "Attach cable to the vehicle that will do the towing." ) : - _( "Attach cable to the vehicle that will be towed." ), "", can_link, false, false ); - if( !pnt_ ) { - return std::nullopt; - } - const tripoint &pnt = *pnt_; - const optional_vpart_position t_vp = here.veh_at( pnt ); - if( !t_vp ) { - p->add_msg_if_player( _( "There's no vehicle there." ) ); - return std::nullopt; - } + const auto can_link = [&here]( const tripoint & point ) { + const optional_vpart_position ovp = here.veh_at( point ); + return ovp && ovp->vehicle().is_external_part( point ); + }; - vehicle *const target_veh = &t_vp->vehicle(); - if( target_veh->has_tow_attached() || target_veh->is_towed() || - target_veh->is_towing() ) { - p->add_msg_if_player( _( "That vehicle already has a tow-line attached." ) ); - return std::nullopt; - } - if( !target_veh->is_external_part( pnt ) ) { - p->add_msg_if_player( _( "You can't attach the tow-line to an internal part." ) ); - return std::nullopt; - } - if( !target_veh->part( t_vp->part_index() ).carried_stack.empty() ) { - p->add_msg_if_player( _( "You can't attach the tow-line to a racked part." ) ); - return std::nullopt; - } + const std::optional pnt_ = choose_adjacent_highlight( + to_towing ? _( "Attach cable to the vehicle that will do the towing." ) : + _( "Attach cable to the vehicle that will be towed." ), "", can_link, false, false ); + if( !pnt_ ) { + return std::nullopt; + } + const tripoint &pnt = *pnt_; + const optional_vpart_position t_vp = here.veh_at( pnt ); + if( !t_vp ) { + p->add_msg_if_player( _( "There's no vehicle there." ) ); + return std::nullopt; + } - if( cable->link->has_no_links() ) { - // Starting a new tow cable connection. + vehicle *const target_veh = &t_vp->vehicle(); + if( target_veh->has_tow_attached() || target_veh->is_towed() || + target_veh->is_towing() ) { + p->add_msg_if_player( _( "That vehicle already has a tow-line attached." ) ); + return std::nullopt; + } + if( !target_veh->is_external_part( pnt ) ) { + p->add_msg_if_player( _( "You can't attach the tow-line to an internal part." ) ); + return std::nullopt; + } + if( !target_veh->part( t_vp->part_index() ).carried_stack.empty() ) { + p->add_msg_if_player( _( "You can't attach the tow-line to a racked part." ) ); + return std::nullopt; + } - p->add_msg_if_player( _( "You connect the %1$s to the %2$s." ), it.label( 1 ), - t_vp->vehicle().name ); - if( choice == 10 ) { - cable->link->s_state = link_state::vehicle_tow; // Assign towing vehicle. - } else { - cable->link->t_state = link_state::vehicle_tow; // Assign towed vehicle. - } - //cable->link->t_state = link_state::vehicle_tow; - cable->link->t_abs_pos = here.getglobal( pnt ); - cable->link->t_mount = t_vp->mount(); - cable->link->max_length = cable_length != -1 ? cable_length : type->maximum_charges(); - cable->link->last_processed = calendar::turn; - cable->active = true; - it.contents_linked = !is_cable_item; - p->moves -= move_cost; - it.process( here, p, p->pos() ); + if( !it.link ) { + it.link = cata::make_value(); + } + if( it.link->has_no_links() ) { + // Starting a new tow cable connection. + p->add_msg_if_player( _( "You connect the %1$s to the %2$s." ), it.type_name(), + t_vp->vehicle().name ); + if( to_towing ) { + it.link->s_state = link_state::vehicle_tow; // Assign towing vehicle. } else { - // Connecting two vehicles with tow cable. + it.link->t_state = link_state::vehicle_tow; // Assign towed vehicle. + } + it.link->t_abs_pos = here.getglobal( pnt ); + it.link->t_mount = t_vp->mount(); + it.link->max_length = cable_length != -1 ? cable_length : it.type->maximum_charges(); + it.link->last_processed = calendar::turn; + p->moves -= move_cost; + it.process( here, p, p->pos() ); + return 0; - if( !cable->link->t_veh_safe ) { - debugmsg( "Failed to connect the %s, it lost its vehicle pointer!", cable->tname() ); - return std::nullopt; + } else { + // Connecting two vehicles with tow cable. + + if( !it.link->t_veh_safe ) { + debugmsg( "Failed to connect the %s, it lost its vehicle pointer!", it.tname() ); + return std::nullopt; + } + vehicle *const prev_veh = it.link->t_veh_safe.get(); + if( prev_veh == target_veh ) { + if( p->has_item( it ) ) { + p->add_msg_if_player( m_warning, _( "The %s cannot tow itself!" ), prev_veh->name ); } - vehicle *const prev_veh = cable->link->t_veh_safe.get(); - if( prev_veh == target_veh ) { - if( p->has_item( it ) ) { - p->add_msg_if_player( m_warning, _( "The %s cannot tow itself!" ), prev_veh->name ); - } - return std::nullopt; - }; + return std::nullopt; + }; const itype_id item_id = it.typeId(); vpart_id vpid = vpart_id::NULL_ID(); @@ -4790,7 +4850,7 @@ std::optional link_up_actor::use( Character *p, item &it, bool t, const tri return std::nullopt; } - const point vcoords1 = cable->link->t_mount; + const point vcoords1 = it.link->t_mount; const point vcoords2 = t_vp->mount(); const ret_val can_mount1 = prev_veh->can_mount( vcoords1, *vpid ); @@ -4815,7 +4875,7 @@ std::optional link_up_actor::use( Character *p, item &it, bool t, const tri prev_veh->install_part( vcoords1, std::move( prev_part ) ); vehicle_part target_part( vpid, item( it ) ); - target_part.target.first = here.getabs( prev_veh->mount_to_tripoint( cable->link->t_mount ) ); + target_part.target.first = here.getabs( prev_veh->mount_to_tripoint( it.link->t_mount ) ); target_part.target.second = prev_veh->global_square_location().raw(); target_veh->install_part( vcoords2, std::move( target_part ) ); @@ -4824,123 +4884,14 @@ std::optional link_up_actor::use( Character *p, item &it, bool t, const tri p->add_msg_if_player( m_good, _( "You attach %1$s to %2$s and %3$s." ), it.type_name(), prev_veh->disp_name(), target_veh->disp_name() ); } - if( choice == 10 ) { + if( to_towing ) { target_veh->tow_data.set_towing( target_veh, prev_veh ); } else { prev_veh->tow_data.set_towing( prev_veh, target_veh ); } - return 1; // Let the cable be destroyed. - } - - } else if( choice == 20 ) { // Selection: Attach electrical cable to Cable Charger System CBM. - - if( !set_cable_pointer() ) { - return std::nullopt; - } - if( cable->link->has_no_links() ) { - cable->link->t_state = link_state::bio_cable; - p->add_msg_if_player( m_info, _( "You attach the cable to your Cable Charger System." ) ); - } else if( cable->link->s_state == link_state::ups ) { - cable->link->t_state = link_state::bio_cable; - p->add_msg_if_player( m_good, _( "You are now plugged into the UPS." ) ); - } else if( cable->link->s_state == link_state::solarpack ) { - cable->link->t_state = link_state::bio_cable; - p->add_msg_if_player( m_good, _( "You are now plugged into the solar backpack." ) ); - } else if( cable->link->t_state == link_state::vehicle_port || - cable->link->t_state == link_state::vehicle_battery ) { - cable->link->s_state = link_state::bio_cable; - p->add_msg_if_player( m_good, _( "You are now plugged into the vehicle." ) ); - } - cable->active = true; - it.contents_linked = !is_cable_item; - p->moves -= move_cost; - it.process( here, p, p->pos() ); - return 0; - - } else if( choice == 21 ) { // Selection: Attach electrical cable to ups. - - item_location loc; - avatar *you = p->as_avatar(); - const std::string choose_ups = _( "Choose UPS:" ); - const std::string dont_have_ups = _( "You don't have any UPS." ); - auto ups_filter = [&]( const item & itm ) { - return itm.has_flag( flag_IS_UPS ); - }; - - if( you != nullptr ) { - loc = game_menus::inv::titled_filter_menu( ups_filter, *you, choose_ups, -1, dont_have_ups ); - } - if( !loc ) { - p->add_msg_if_player( _( "Never mind" ) ); - return std::nullopt; - } - - if( !set_cable_pointer() ) { - return std::nullopt; - } - if( cable->link->has_no_links() ) { - p->add_msg_if_player( m_info, _( "You attach the cable to the UPS." ) ); - } else if( cable->link->t_state == link_state::bio_cable ) { - p->add_msg_if_player( m_good, _( "You are now plugged into the UPS." ) ); - } else if( cable->link->s_state == link_state::solarpack ) { - p->add_msg_if_player( m_good, _( "You link up the UPS and the solar backpack." ) ); - } else if( cable->link->t_state == link_state::vehicle_port || - cable->link->t_state == link_state::vehicle_battery ) { - p->add_msg_if_player( m_good, _( "You link up the UPS and the vehicle." ) ); - } - cable->link->s_state = link_state::ups; - loc->set_var( "cable", "plugged_in" ); - loc->activate(); - cable->active = true; - it.contents_linked = !is_cable_item; - p->moves -= move_cost; - it.process( here, p, p->pos() ); - return 0; - - } else if( choice == 22 ) { // Selection: Attach electrical cable to solar pack. - - item_location loc; - avatar *you = p->as_avatar(); - const std::string choose_solar = _( "Choose solar pack:" ); - const std::string dont_have_solar = _( "You need an unfolded solar pack." ); - auto solar_filter = [&]( const item & itm ) { - return itm.has_flag( flag_SOLARPACK_ON ); - }; - - if( you != nullptr ) { - loc = game_menus::inv::titled_filter_menu( solar_filter, *you, choose_solar, -1, dont_have_solar ); - } - if( !loc ) { - p->add_msg_if_player( _( "Never mind" ) ); - return std::nullopt; - } - - if( !set_cable_pointer() ) { - return std::nullopt; - } - if( cable->link->has_no_links() ) { - p->add_msg_if_player( m_info, _( "You attach the cable to the solar pack." ) ); - } else if( cable->link->t_state == link_state::bio_cable ) { - p->add_msg_if_player( m_good, _( "You are now plugged into the solar pack." ) ); - } else if( cable->link->s_state == link_state::ups ) { - p->add_msg_if_player( m_good, _( "You link up the solar pack and the UPS." ) ); - } else if( cable->link->t_state == link_state::vehicle_port || - cable->link->t_state == link_state::vehicle_battery ) { - p->add_msg_if_player( m_good, _( "You link up the solar pack and the vehicle." ) ); - } - cable->link->s_state = link_state::solarpack; - loc->set_var( "cable", "plugged_in" ); - loc->activate(); - cable->active = true; - cable->process( here, p, p->pos() ); - it.contents_linked = !is_cable_item; - p->moves -= move_cost; - it.process( here, p, p->pos() ); - return 0; + return 1; // Let the cable be destroyed. } - - return 0; } std::unique_ptr deploy_tent_actor::clone() const diff --git a/src/iuse_actor.h b/src/iuse_actor.h index 2dec28f2b75b0..578fdf7328246 100644 --- a/src/iuse_actor.h +++ b/src/iuse_actor.h @@ -1032,21 +1032,23 @@ class modify_gunmods_actor : public iuse_actor class link_up_actor : public iuse_actor { public: - /** True if the link_up action is called by the cable item itself, rather than by a device. */ - bool is_cable_item = false; - /** The type of cable created with this action */ - itype_id type = itype_id( "generic_device_cable" ); /** Maximum length of the cable. At -1, will use the item type's max_charges. */ int cable_length = -1; /** Charge rate in watts */ - units::power charge_rate = 0_W; + units::power wattage = 0_W; /** one_in(this) chance to fail adding 1 charge */ int charge_efficiency = 7; + /** (Optional) The move cost to attach the cable. */ + int move_cost = 5; /** (Optional) Text displayed in the activation screen, defaults to "Plug in / Unplug". */ translation menu_text; std::set targets = { link_state::no_link, link_state::vehicle_port }; + std::optional link_up( Character *p, item &it ) const; + std::optional link_to_veh_app( Character *p, item &it, const bool to_ports ) const; + std::optional link_tow_cable( Character *p, item &it, const bool to_towing ) const; + link_up_actor() : iuse_actor( "link_up" ) {} ~link_up_actor() override = default; diff --git a/src/savegame_json.cpp b/src/savegame_json.cpp index 7f6c78976a3e7..d2b4535b57314 100644 --- a/src/savegame_json.cpp +++ b/src/savegame_json.cpp @@ -2868,7 +2868,7 @@ void item::link_data::serialize( JsonOut &jsout ) const jsout.member( "link_t_mount", t_mount ); 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_rate", wattage ); jsout.member( "link_charge_interval", charge_interval ); jsout.member( "link_charge_efficiency", charge_efficiency ); jsout.end_object(); @@ -2884,7 +2884,7 @@ void item::link_data::deserialize( const JsonObject &data ) data.read( "link_t_mount", t_mount ); max_length = data.get_int( "link_max_length" ); data.read( "link_last_processed", last_processed ); - charge_rate = data.get_int( "link_charge_rate" ); + wattage = data.get_int( "link_charge_rate" ); charge_interval = data.get_int( "link_charge_interval" ); charge_efficiency = data.get_int( "link_charge_efficiency" ); } @@ -2987,7 +2987,6 @@ void item::io( Archive &archive ) archive.io( "item_counter", item_counter, static_cast( 0 ) ); archive.io( "countdown_point", countdown_point, calendar::turn_max ); archive.io( "wetness", wetness, 0 ); - archive.io( "contents_linked", contents_linked, false ); archive.io( "dropped_from", dropped_from, harvest_drop_type_id::NULL_ID() ); archive.io( "rot", rot, 0_turns ); archive.io( "last_temp_check", last_temp_check, calendar::start_of_cataclysm ); diff --git a/src/vehicle.cpp b/src/vehicle.cpp index 32e84452325fb..4437999fe459a 100644 --- a/src/vehicle.cpp +++ b/src/vehicle.cpp @@ -6504,7 +6504,7 @@ void vehicle::invalidate_towing( bool first_vehicle, Character *remover ) drop.link->t_mount = other_veh->part( other_tow_cable_idx ).mount; } } else { - drop.reset_cable(); + drop.reset_link(); } if( remover != nullptr ) { diff --git a/src/vehicle_part.cpp b/src/vehicle_part.cpp index 5d4cda7a2525e..7e14f0ff82f6e 100644 --- a/src/vehicle_part.cpp +++ b/src/vehicle_part.cpp @@ -102,14 +102,14 @@ item vehicle_part::properties_to_item() const iuse_found = true; tmp.link->max_length = actor_ptr->cable_length; tmp.link->charge_efficiency = actor_ptr->charge_efficiency; - tmp.link->charge_rate = actor_ptr->charge_rate.value(); - tmp.link->charge_interval = actor_ptr->charge_rate == 0_W ? -1 : - std::max( 1, static_cast( std::floor( 1000000.0 / abs( actor_ptr->charge_rate.value() ) + + tmp.link->wattage = actor_ptr->wattage.value(); + tmp.link->charge_interval = actor_ptr->wattage == 0_W ? -1 : + std::max( 1, static_cast( std::floor( 1000000.0 / abs( actor_ptr->wattage.value() ) + 0.5 ) ) ); } } if( !iuse_found ) { - debugmsg( "Could not find link_up iuse data for %s! Using default values.", tmp.tname() ); + debugmsg( "Could not find link_up iuse data for %s! Using fallback values.", tmp.tname() ); tmp.link->max_length = tmp.type->maximum_charges(); } From ee69b98d05b7fcf190d27b1fbae66cccc4e67689 Mon Sep 17 00:00:00 2001 From: Kamayana Date: Wed, 12 Jul 2023 06:42:32 -0500 Subject: [PATCH 06/68] Add ability to extend cables with other cables --- data/json/item_actions.json | 2 +- data/json/items/tool/cables.json | 27 ++-- src/activity_actor.cpp | 2 +- src/item.cpp | 90 +++++++++-- src/item.h | 27 +++- src/item_contents.cpp | 12 +- src/item_contents.h | 4 +- src/item_pocket.cpp | 2 +- src/iuse_actor.cpp | 258 ++++++++++++++++++++++--------- src/iuse_actor.h | 5 +- src/savegame_json.cpp | 12 +- src/vehicle_part.cpp | 21 +-- src/vehicle_use.cpp | 1 - 13 files changed, 320 insertions(+), 143 deletions(-) diff --git a/data/json/item_actions.json b/data/json/item_actions.json index 14bcd625cb1db..87214b09881bb 100644 --- a/data/json/item_actions.json +++ b/data/json/item_actions.json @@ -983,7 +983,7 @@ { "type": "item_action", "id": "link_up", - "name": { "str": "Plug in / Unplug" } + "name": { "str": "Plug in / Manage cables" } }, { "type": "item_action", diff --git a/data/json/items/tool/cables.json b/data/json/items/tool/cables.json index 2e90dd9153bdc..c4667381a784e 100644 --- a/data/json/items/tool/cables.json +++ b/data/json/items/tool/cables.json @@ -16,7 +16,7 @@ "price_postapoc": 100, "max_charges": 3, "initial_charges": 3, - "use_action": { "type": "link_up", "targets": [ "vehicle_port" ] }, + "use_action": { "type": "link_up", "menu_text": "Plug in / Manage connections", "targets": [ "vehicle_port" ] }, "flags": [ "CABLE_SPOOL", "AUTO_DELETE_CABLE", "SINGLE_USE" ], "melee_damage": { "bash": 2 } }, @@ -38,8 +38,9 @@ "initial_charges": 4, "use_action": { "type": "link_up", - "menu_text": "Attach / Detach", - "targets": [ "no_link", "bio_cable", "ups", "solarpack", "vehicle_battery" ] + "menu_text": "Attach / Manage connections", + "targets": [ "no_link", "bio_cable", "ups", "solarpack", "vehicle_battery" ], + "can_extend": [ "jumper_cable", "jumper_cable_heavy" ] }, "flags": [ "CABLE_SPOOL", "SINGLE_USE" ], "melee_damage": { "bash": 2 }, @@ -54,11 +55,6 @@ "weight": "750 g", "max_charges": 20, "initial_charges": 20, - "use_action": { - "type": "link_up", - "menu_text": "Attach / Detach", - "targets": [ "no_link", "bio_cable", "ups", "solarpack", "vehicle_battery" ] - }, "copy-from": "jumper_cable" }, { @@ -77,7 +73,12 @@ "price_postapoc": 100, "max_charges": 10, "initial_charges": 10, - "use_action": { "type": "link_up", "targets": [ "no_link", "vehicle_port" ] }, + "use_action": { + "type": "link_up", + "menu_text": "Plug in / Manage connections", + "targets": [ "no_link", "vehicle_port" ], + "can_extend": [ "ELECTRICAL_DEVICES", "extension_cable", "long_extension_cable" ] + }, "flags": [ "CABLE_SPOOL", "SINGLE_USE" ], "melee_damage": { "bash": 2 } }, @@ -91,7 +92,6 @@ "weight": "2500 g", "max_charges": 30, "initial_charges": 30, - "use_action": { "type": "link_up", "targets": [ "no_link", "vehicle_port" ] }, "flags": [ "CABLE_SPOOL", "SINGLE_USE" ] }, { @@ -110,7 +110,7 @@ "price_postapoc": 500, "max_charges": 6, "initial_charges": 6, - "use_action": { "type": "link_up", "menu_text": "Attach / Detach", "targets": [ "no_link", "vehicle_tow" ] }, + "use_action": { "type": "link_up", "menu_text": "Attach / Manage connections", "targets": [ "no_link", "vehicle_tow" ] }, "qualities": [ [ "ROPE", 2 ] ], "flags": [ "CABLE_SPOOL", "TOW_CABLE", "SINGLE_USE" ], "melee_damage": { "bash": 6 } @@ -127,9 +127,10 @@ "initial_charges": 100, "use_action": { "type": "link_up", - "menu_text": "Attach / Detach", + "menu_text": "Attach / Manage connections", "efficiency": 1000, - "targets": [ "no_link", "bio_cable", "ups", "solarpack", "vehicle_port", "vehicle_battery", "vehicle_tow" ] + "targets": [ "no_link", "bio_cable", "ups", "solarpack", "vehicle_port", "vehicle_battery", "vehicle_tow" ], + "can_extend": [ "ELECTRICAL_DEVICES" ] } } ] diff --git a/src/activity_actor.cpp b/src/activity_actor.cpp index f6e80344caa74..681a919741061 100644 --- a/src/activity_actor.cpp +++ b/src/activity_actor.cpp @@ -5054,7 +5054,7 @@ void reel_cable_activity_actor::finish( player_activity &act, Character &who ) cable->link.reset(); who.add_msg_if_player( m_info, string_format( cable->has_flag( flag_CABLE_SPOOL ) ? _( "You reel in the %s and wind it up." ) : - _( "You reel in the %s and wind it up." ), cable->label( 1 ) ) ); + _( "You reel in the %s's cable and wind it up." ), cable->type_name() ) ); if( cable->has_flag( flag_AUTO_DELETE_CABLE ) ) { cable.remove_item(); } diff --git a/src/item.cpp b/src/item.cpp index 36bcefb0e4522..18aa5be3db0d2 100644 --- a/src/item.cpp +++ b/src/item.cpp @@ -10349,7 +10349,7 @@ int item::ammo_remaining( const Character *carrier, bool cable_links ) const } // Cable connections - if( cable_links && link ) { + if( cable_links && link_length() > -1 ) { if( link->t_veh_safe ) { ret += link->t_veh_safe->connected_battery_power_level().first; } else { @@ -10885,6 +10885,11 @@ std::vector item::ebooks() const return contents.ebooks(); } +std::vector item::cables() const +{ + return contents.cables(); +} + item *item::gunmod_find( const itype_id &mod ) { std::vector mods = gunmods(); @@ -12881,11 +12886,63 @@ bool item::process_extinguish( map &here, Character *carrier, const tripoint &po return false; } +void item::set_link_traits() +{ + if( !link || !type->can_use( "link_up" ) ) { + return; + } + + 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; + // Reset s_bub_pos to force the item to check the length during process_link. + link->s_bub_pos = tripoint_min; + + for( const item *cable : cables() ) { + if( !cable->type->can_use( "link_up" ) ) { + continue; + } + const link_up_actor *actor = static_cast + ( cable->get_use( "link_up" )->get_actor_ptr() ); + link->max_length += actor->cable_length == -1 ? cable->type->maximum_charges() : + actor->cable_length; + link->efficiency *= actor->efficiency == 0 ? 0 : 1.0 - 1.0 / actor->efficiency; + } +} + +int item::link_length( bool max ) const +{ + if( !max ) { + return !link || link->has_no_links() ? -2 : + link->has_state( link_state::needs_reeling ) ? -1 : link->length; + } + + if( link ) { + return link->max_length; + } + if( !type->can_use( "link_up" ) ) { + return -2; + } + + const link_up_actor *actor = static_cast + ( get_use( "link_up" )->get_actor_ptr() ); + int total_length = actor->cable_length != -1 ? actor->cable_length : type->maximum_charges(); + + for( const item *cable : cables() ) { + if( !cable->has_flag( flag_CABLE_SPOOL ) ) { + continue; + } + total_length += cable->type->maximum_charges(); + } + + return total_length; +} + bool item::process_link( map &here, Character *carrier, const tripoint &pos ) { - // Failsafe for loose, unwound cables that are somehow still active. - if( link->s_state == link_state::needs_reeling || link->has_no_links() ) { - return reset_link( carrier ); + if( link_length() < 0 ) { + return false; } const bool is_cable_item = has_flag( flag_CABLE_SPOOL ); @@ -12993,8 +13050,8 @@ bool item::process_link( map &here, Character *carrier, const tripoint &pos ) // we should always process it to avoid erroneously skipping devices riding inside of it. if( last_t_abs_pos_is_oob && t_veh->velocity < HALF_MAPSIZE_X * 400 ) { if( check_length ) { - link->length = rl_dist( here.getabs( pos ), - link->t_abs_pos.raw() ) + link->t_mount.abs().x + link->t_mount.abs().y; + link->length = rl_dist( here.getabs( pos ), link->t_abs_pos.raw() ) + + link->t_mount.abs().x + link->t_mount.abs().y; if( is_cable_too_long() ) { return reset_link( carrier ); } @@ -13099,17 +13156,17 @@ int item::charge_linked_batteries( vehicle &linked_veh, int turns_elapsed ) // If a long time passed, multiply the total by the efficiency rather than cancelling a charge. int transfer_total = short_time_passed ? 1 : - ( turns_elapsed * 1.0f / link->charge_interval ) * ( 1.0 - 1.0 / link->charge_efficiency ); + ( turns_elapsed * 1.0f / link->charge_interval ) * ( 1.0 - 1.0 / link->efficiency ); if( power_in ) { const int battery_deficit = linked_veh.discharge_battery( transfer_total, true ); // Around 85% efficient by default; a few of the discharges don't actually recharge - if( battery_deficit == 0 && !( short_time_passed && one_in( link->charge_efficiency ) ) ) { + if( battery_deficit == 0 && !( short_time_passed && one_in( link->efficiency ) ) ) { ammo_set( itype_battery, ammo_remaining() + transfer_total ); } } else { // Around 85% efficient by default; a few of the discharges don't actually charge - if( !( short_time_passed && one_in( link->charge_efficiency ) ) ) { + if( !( short_time_passed && one_in( link->efficiency ) ) ) { const int battery_surplus = linked_veh.charge_battery( transfer_total, true ); if( battery_surplus == 0 ) { ammo_set( itype_battery, ammo_remaining() - transfer_total ); @@ -13126,15 +13183,12 @@ int item::charge_linked_batteries( vehicle &linked_veh, int turns_elapsed ) bool item::reset_link( Character *p, const bool loose_message, const tripoint cable_position ) { - const bool is_cable_item = has_flag( flag_CABLE_SPOOL ); - if( is_cable_item ) { - active = false; - } if( !link ) { - debugmsg( "%s lost its link data!", tname() ); return has_flag( flag_AUTO_DELETE_CABLE ); } + const bool is_cable_item = has_flag( flag_CABLE_SPOOL ); + if( loose_message ) { if( p != nullptr ) { p->add_msg_if_player( m_warning, @@ -13147,13 +13201,19 @@ bool item::reset_link( Character *p, const bool loose_message, const tripoint ca } } - const int respool_threshold = 5; + const int respool_threshold = 6; if( link->length > respool_threshold ) { // Cables that are too long need to be manually rewound before reuse. link->s_state = link_state::needs_reeling; return false; } + if( loose_message && p ) { + p->add_msg_if_player( m_info, string_format( is_cable_item ? + _( "You reel in the %s and wind it up." ) : + _( "You reel in the %s's cable and wind it up." ), tname() ) ); + } + link.reset(); return has_flag( flag_AUTO_DELETE_CABLE ); } diff --git a/src/item.h b/src/item.h index e9289cabb63a7..aa3748a6e2e47 100644 --- a/src/item.h +++ b/src/item.h @@ -1433,12 +1433,12 @@ class item : public visitable int length = 0; /// The maximum length of the cable. Set during initialization. int max_length = 2; - /// The cable's charge rate in watts. Set during initialization. + /// The cable's power capacity in watts, affects battery charge rate. Set during initialization. int wattage = 0; - /// The turn interval between charges. Set during initialization. - int charge_interval = -1; /// one_in(this) chance to fail adding 1 charge. Set during initialization. - int charge_efficiency = 7; + int efficiency = 7; + /// The turn interval between charges. Set during initialization. + int charge_interval = 0; bool has_state( link_state state ) const { return s_state == state || t_state == state; @@ -1453,6 +1453,23 @@ class item : public visitable void serialize( JsonOut &jsout ) const; void deserialize( const JsonObject &data ); }; + /** + * @brief Sets max_length, wattage, and efficiency of a link, taking cable extensions into account. + * @brief max_length is set to the sum of all cable lengths. + * @brief wattage is set to the smallest wattage of any cable. + * @brief efficiency is set to the product of all efficiencies multiplied together. + * @brief charge_interval is set to how many turns it takes for wattage to add up to 1 kW - the unit batteries use. 0 means batteries won't be charged. + */ + void set_link_traits(); + + /** + * @param max If true, return the item's maximum cable length, including extensions, rather than its current length. Will not require active link_data, either. + * @return `-2` If the item has no attachments, or if `max` is true, if the item has no link_up action. + * @return `-1` If the item has link_data but needs reeling. + * @return Otherwise, return the link's current length, or if `max` is true, return the item's maximum possible link length. + */ + int link_length( bool max = false ) const; + /** * Brings a cable item back to its initial state. * @param p Set to character that's holding the linked item, nullptr if none. @@ -2455,6 +2472,8 @@ class item : public visitable std::vector ebooks() const; + std::vector cables() const; + /** Get first attached gunmod matching type or nullptr if no such mod or item is not a gun */ item *gunmod_find( const itype_id &mod ); const item *gunmod_find( const itype_id &mod ) const; diff --git a/src/item_contents.cpp b/src/item_contents.cpp index 4d0bafd5a5950..db09331602f2b 100644 --- a/src/item_contents.cpp +++ b/src/item_contents.cpp @@ -1765,30 +1765,26 @@ std::vector item_contents::ebooks() const return ebooks; } -std::vector item_contents::cables( bool active_only ) +std::vector item_contents::cables() { std::vector cables; for( item_pocket &pocket : contents ) { if( pocket.is_type( item_pocket::pocket_type::CABLE ) ) { for( item *it : pocket.all_items_top() ) { - if( !active_only || it->active ) { - cables.emplace_back( it ); - } + cables.emplace_back( it ); } } } return cables; } -std::vector item_contents::cables( bool active_only ) const +std::vector item_contents::cables() const { std::vector cables; for( const item_pocket &pocket : contents ) { if( pocket.is_type( item_pocket::pocket_type::CABLE ) ) { for( const item *it : pocket.all_items_top() ) { - if( !active_only || it->active ) { - cables.emplace_back( it ); - } + cables.emplace_back( it ); } } } diff --git a/src/item_contents.h b/src/item_contents.h index 8c9365abc83d3..08ff6e7aaf9ea 100644 --- a/src/item_contents.h +++ b/src/item_contents.h @@ -132,8 +132,8 @@ class item_contents std::vector ebooks(); std::vector ebooks() const; - std::vector cables( bool active_only = false ); - std::vector cables( bool active_only = false ) const; + std::vector cables(); + std::vector cables() const; void update_modified_pockets( const std::optional &mag_or_mag_well, std::vector container_pockets ); diff --git a/src/item_pocket.cpp b/src/item_pocket.cpp index 0ec4973152e2d..3f8f130b29793 100644 --- a/src/item_pocket.cpp +++ b/src/item_pocket.cpp @@ -1320,7 +1320,7 @@ ret_val item_pocket::is_compatible( const item &it ) return ret_val::make_success(); } else { return ret_val::make_failure( - contain_code::ERR_MOD, _( "only certain cables can go into cable pocket" ) ); + contain_code::ERR_MOD, _( "only cables can go into cable pocket" ) ); } } diff --git a/src/iuse_actor.cpp b/src/iuse_actor.cpp index e30c7447e81da..3dde699a980f4 100644 --- a/src/iuse_actor.cpp +++ b/src/iuse_actor.cpp @@ -4286,10 +4286,11 @@ void link_up_actor::load( const JsonObject &jo ) { jo.read( "cable_length", cable_length ); jo.read( "wattage", wattage ); - jo.read( "efficiency", charge_efficiency ); + jo.read( "efficiency", efficiency ); jo.read( "move_cost", move_cost ); jo.read( "menu_text", menu_text ); jo.read( "targets", targets ); + jo.read( "can_extend", can_extend ); } void link_up_actor::info( const item &, std::vector &dump ) const @@ -4316,24 +4317,31 @@ void link_up_actor::info( const item &, std::vector &dump ) const if( targets.count( link_state::solarpack ) > 0 ) { targets_strings.emplace_back( _( "solar pack" ) ); } - + if( targets.count( link_state::vehicle_tow ) > 0 ) { + dump.emplace_back( "TOOL", _( "Can tow a vehicle." ) ); + } if( !targets_strings.empty() ) { std::string targets_string = enumerate_as_string( targets_strings, enumeration_conjunction::or_ ); dump.emplace_back( "TOOL", string_format( _( "Can be plugged into: %s." ), targets_string ) ); } - if( targets.count( link_state::vehicle_tow ) > 0 ) { - dump.emplace_back( "TOOL", _( "Can tow a vehicle." ) ); + if( !can_extend.empty() ) { + std::vector cable_types; + for( const std::string &cable_type : can_extend ) { + cable_types.emplace_back( cable_type == "ELECTRICAL_DEVICES" ? "electrical device cables" : + itype_id( cable_type )->nname( 1 ) ); + } + std::string cable_type_list = enumerate_as_string( cable_types, enumeration_conjunction::or_ ); + dump.emplace_back( "TOOL", string_format( _( "Can extend: %s" ), cable_type_list ) ); } - - dump.emplace_back( "TOOL", _( "Cable length: " ), cable_length ); + dump.emplace_back( "TOOL", _( "Cable length: " ), cable_length ); if( wattage != 0_W ) { std::string wattage_display = string_format( _( "%+4.1f W" ), units::to_milliwatt( wattage ) / 1000.f ); if( wattage > 0_W ) { - dump.emplace_back( "TOOL", _( "Charge rate: " ), wattage_display ); + dump.emplace_back( "TOOL", _( "Charge rate: " ), wattage_display ); } else { - dump.emplace_back( "TOOL", _( "Discharge rate: " ), wattage_display ); + dump.emplace_back( "TOOL", _( "Discharge rate: " ), wattage_display ); } } } @@ -4366,123 +4374,112 @@ std::optional link_up_actor::link_up( Character *p, item &it ) const const std::string cable_name = is_cable_item ? it.type_name() : string_format( _( "%s's cable" ), it.type_name() ); - const int respool_threshold = 5; + const int respool_threshold = 6; const int respool_time_per_square = 200; - const int respool_time_total = it.link ? ( it.link->length - respool_threshold ) * respool_time_per_square : 0; - const bool past_respool_threshold = it.link && it.link->length > respool_threshold; - - if( it.link && it.link->s_state == link_state::needs_reeling ) { - if( query_yn( _( "Reel in the %s?" ), cable_name ) ) { - p->assign_activity( player_activity( reel_cable_activity_actor( respool_time_total, item_location{*p, &it} ) ) ); - } - return 0; - } - - // If it's a cable item without any free ends OR a device with any connection, only allow disconnecting. - if( it.link && is_cable_item ? !it.link->has_state( link_state::no_link ) : !it.link->has_no_links() ) { - if( targets.count( link_state::no_link ) > 0 ) { - if( query_yn( _( "Detach and re-spool the %s?" ), cable_name ) ) { - it.reset_link( p ); - if( it.link && it.link->s_state == link_state::needs_reeling ) { - p->assign_activity( player_activity( reel_cable_activity_actor( respool_time_total, item_location{*p, &it} ) ) ); - } else { - p->add_msg_if_player( m_info, string_format( is_cable_item ? _( "You detach the %s." ) : - _( "You gather the %s's cable up with it." ), it.type_name() ) ); - } - return 0; - } - } - return std::nullopt; - } + const int respool_time_total = !it.link || it.link->length < respool_threshold ? 0 : + ( it.link->length - respool_threshold ) * respool_time_per_square; + const bool past_respool_threshold = it.link_length() > respool_threshold; + const bool unspooled = it.link && it.link->has_state( link_state::needs_reeling ); uilist link_menu; - if( !it.link || it.link->has_no_links() ) { - // This is either a device's cable or doesn't have any connections yet. - + const bool has_loose_end = is_cable_item ? !it.link || it.link->has_state( link_state::no_link ) : + !it.link || it.link->has_no_links(); + if( !is_cable_item || !it.link || it.link->has_no_links() ) { + // This cable item or device doesn't have any connections yet. link_menu.text = string_format( _( "Attaching the %s:" ), cable_name ); if( targets.count( link_state::vehicle_port ) > 0 ) { - link_menu.addentry( 0, true, -1, _( "Attach to vehicle controls or appliance" ) ); + link_menu.addentry( 0, has_loose_end, -1, _( "Attach to vehicle controls or appliance" ) ); } if( targets.count( link_state::vehicle_battery ) > 0 ) { - link_menu.addentry( 1, true, -1, _( "Attach to vehicle battery or appliance" ) ); + link_menu.addentry( 1, has_loose_end, -1, _( "Attach to vehicle battery or appliance" ) ); } if( targets.count( link_state::vehicle_tow ) > 0 ) { - link_menu.addentry( 10, true, -1, _( "Attach tow cable to towing vehicle" ) ); - link_menu.addentry( 11, true, -1, _( "Attach tow cable to towed vehicle" ) ); + link_menu.addentry( 10, has_loose_end, -1, _( "Attach tow cable to towing vehicle" ) ); + link_menu.addentry( 11, has_loose_end, -1, _( "Attach tow cable to towed vehicle" ) ); } if( targets.count( link_state::bio_cable ) > 0 ) { if( !p->get_remote_fueled_bionic().is_empty() ) { - link_menu.addentry( 20, true, -1, _( "Attach to Cable Charger System CBM" ) ); + link_menu.addentry( 20, has_loose_end, -1, _( "Attach to Cable Charger System CBM" ) ); } } if( targets.count( link_state::ups ) > 0 ) { if( !( p->all_items_with_flag( flag_IS_UPS ) ).empty() ) { - link_menu.addentry( 21, true, -1, _( "Attach to UPS" ) ); + link_menu.addentry( 21, has_loose_end, -1, _( "Attach to UPS" ) ); } } if( targets.count( link_state::solarpack ) > 0 ) { const bool has_solar_pack_on = p->worn_with_flag( flag_SOLARPACK_ON ); if( has_solar_pack_on || p->worn_with_flag( flag_SOLARPACK ) ) { - link_menu.addentry( 22, has_solar_pack_on, -1, _( "Attach to solar pack" ) ); + link_menu.addentry( 22, has_loose_end && has_solar_pack_on, -1, _( "Attach to solar pack" ) ); } } + if( !is_cable_item || !can_extend.empty() ) { + const bool has_extensions = !it.all_items_top( item_pocket::pocket_type::CABLE ).empty(); + link_menu.addentry( 30, has_loose_end, -1, + is_cable_item ? _( "Extend another cable" ) : _( "Extend with another cable" ) ); + link_menu.addentry( 31, has_extensions, -1, _( "Remove cable extensions" ) ); + } if( targets.count( link_state::no_link ) > 0 ) { - link_menu.addentry( 999, false, -1, _( "Detach" ) ); + link_menu.addentry( 999, !!it.link && !it.link->has_no_links(), -1, unspooled ? _( "Re-spool" ) : + past_respool_threshold ? _( "Detach and re-spool" ) : _( "Detach" ) ); } } else if( it.link->has_state( link_state::vehicle_tow ) ) { // Cables that started a tow can finish one or detach; nothing else. link_menu.text = string_format( _( "Attaching the %s:" ), cable_name ); - link_menu.addentry( 10, it.link->t_state == link_state::vehicle_tow, -1, + link_menu.addentry( 10, has_loose_end && it.link->t_state == link_state::vehicle_tow, -1, _( "Attach loose end to towing vehicle" ) ); - link_menu.addentry( 11, it.link->s_state == link_state::vehicle_tow, -1, + link_menu.addentry( 11, has_loose_end && it.link->s_state == link_state::vehicle_tow, -1, _( "Attach loose end to towed vehicle" ) ); if( targets.count( link_state::no_link ) > 0 ) { - link_menu.addentry( 999, true, -1, + link_menu.addentry( 999, true, -1, unspooled ? _( "Re-spool" ) : past_respool_threshold ? _( "Detach and re-spool" ) : _( "Detach" ) ); } - } else if( is_cable_item ) { - // This is a cable item and it has one connection already: - + } else { + // This is a cable item with one connection already: link_menu.text = string_format( _( "Attaching the %s's loose end:" ), cable_name ); // TODO: Allow plugging UPSes and Solar Packs into more than just bionics. // There is already code to support setting up a link, but none for actual functionality. if( targets.count( link_state::vehicle_port ) > 0 ) { - link_menu.addentry( 0, !it.link->has_state( link_state::ups ) && + link_menu.addentry( 0, has_loose_end && !it.link->has_state( link_state::ups ) && !it.link->has_state( link_state::solarpack ), -1, _( "Attach loose end to vehicle controls or appliance" ) ); } if( targets.count( link_state::vehicle_battery ) > 0 ) { - link_menu.addentry( 1, !it.link->has_state( link_state::ups ) && + link_menu.addentry( 1, has_loose_end && !it.link->has_state( link_state::ups ) && !it.link->has_state( link_state::solarpack ), -1, _( "Attach loose end to vehicle battery or appliance" ) ); } if( targets.count( link_state::bio_cable ) > 0 && !p->get_remote_fueled_bionic().is_empty() ) { - link_menu.addentry( 20, !it.link->has_state( link_state::bio_cable ), + link_menu.addentry( 20, has_loose_end && !it.link->has_state( link_state::bio_cable ), -1, _( "Attach loose end to Cable Charger System CBM" ) ); } if( targets.count( link_state::ups ) > 0 && !( p->all_items_with_flag( flag_IS_UPS ) ).empty() ) { - link_menu.addentry( 21, it.link->has_state( link_state::bio_cable ), + link_menu.addentry( 21, has_loose_end && it.link->has_state( link_state::bio_cable ), -1, _( "Attach loose end to UPS" ) ); } if( targets.count( link_state::solarpack ) > 0 ) { const bool has_solar_pack_on = p->worn_with_flag( flag_SOLARPACK_ON ); if( has_solar_pack_on || p->worn_with_flag( flag_SOLARPACK ) ) { - link_menu.addentry( 22, has_solar_pack_on && it.link->has_state( link_state::bio_cable ), + link_menu.addentry( 22, has_loose_end && has_solar_pack_on && + it.link->has_state( link_state::bio_cable ), -1, _( "Attach loose end to solar pack" ) ); } } + if( !can_extend.empty() ) { + const bool has_extensions = !it.all_items_top( item_pocket::pocket_type::CABLE ).empty(); + link_menu.addentry( 30, has_loose_end, -1, _( "Extend another cable" ) ); + link_menu.addentry( 31, has_extensions, -1, _( "Remove cable extensions" ) ); + } if( targets.count( link_state::no_link ) > 0 ) { - link_menu.addentry( 999, true, -1, + link_menu.addentry( 999, true, -1, unspooled ? _( "Re-spool" ) : past_respool_threshold ? _( "Detach and re-spool" ) : _( "Detach" ) ); } - } else { - debugmsg( "An already connected device (%s) tried to link up again!", it.tname() ); - return std::nullopt; } + int choice = -1; if( targets.size() == 1 ) { choice = link_menu.entries.begin()->retval; @@ -4499,7 +4496,7 @@ std::optional link_up_actor::link_up( Character *p, item &it ) const } else if( choice == 999 ) { // Selection: Unconnect & respool. it.reset_link( p ); - if( it.link && it.link->s_state == link_state::needs_reeling ) { + if( unspooled ) { // Cables that are too long need to be manually rewound before reuse. p->assign_activity( player_activity( reel_cable_activity_actor( respool_time_total, item_location{*p, &it} ) ) ); return 0; @@ -4517,6 +4514,14 @@ std::optional link_up_actor::link_up( Character *p, item &it ) const } else if( choice == 10 || choice == 11 ) { // Selection: Attach tow cable to towing/towed vehicle. return link_tow_cable( p, it, choice == 10 ); + + } else if( choice == 30 ) { + // Selection: Attach to another cable, resulting in a longer one. + return link_extend_cable( p, it ); + + } else if( choice == 31 ) { + // Selection: Remove all cable extensions and give the individual cables to the player. + return remove_extensions( p, it ); } map &here = get_map(); @@ -4691,13 +4696,7 @@ std::optional link_up_actor::link_to_veh_app( Character *p, item &it, it.link->t_state = to_ports ? link_state::vehicle_port : link_state::vehicle_battery; it.link->t_abs_pos = here.getglobal( pnt ); it.link->t_mount = t_vp->mount(); - it.link->max_length = cable_length != -1 ? cable_length : it.type->maximum_charges(); - it.link->charge_efficiency = charge_efficiency; - it.link->wattage = wattage.value(); - // Convert wattage to how long it takes to charge 1 kW, the unit batteries use. - // -1 means batteries won't be charged, but it can still provide epower to devices. - it.link->charge_interval = wattage == 0_W ? -1 : - std::max( 1, static_cast( std::floor( 1000000.0 / abs( wattage.value() ) + 0.5 ) ) ); + it.set_link_traits(); it.link->last_processed = calendar::turn; p->moves -= move_cost; it.process( here, p, p->pos() ); @@ -4757,7 +4756,7 @@ std::optional link_up_actor::link_to_veh_app( Character *p, item &it, p->add_msg_if_player( m_good, _( "You link up the %1$s and the %2$s." ), prev_veh->name, target_veh->name ); - + p->moves -= move_cost; return 1; // Let the cable be destroyed. } } @@ -4889,11 +4888,128 @@ std::optional link_up_actor::link_tow_cable( Character *p, item &it, } else { prev_veh->tow_data.set_towing( prev_veh, target_veh ); } - + p->moves -= move_cost; return 1; // Let the cable be destroyed. } } +std::optional link_up_actor::link_extend_cable( Character *p, item &it ) const +{ + avatar *you = p->as_avatar(); + if( !you ) { + p->add_msg_if_player( m_info, _( "Never mind." ) ); + return std::nullopt; + } + + const bool is_cable_item = it.has_flag( flag_CABLE_SPOOL ); + item_location selected; + if( is_cable_item ) { + const bool can_extend_devices = can_extend.find( "ELECTRICAL_DEVICES" ) != can_extend.end(); + const auto filter = [this, &it, &can_extend_devices]( const item & inv ) { + if( inv.link && ( it.link || inv.link->has_state( link_state::needs_reeling ) ) ) { + return false; + } + if( !inv.has_flag( flag_CABLE_SPOOL ) ) { + return can_extend_devices && inv.type->can_use( "link_up" ); + } + return can_extend.find( inv.typeId().c_str() ) != can_extend.end() && &inv != ⁢ + }; + selected = game_menus::inv::titled_filter_menu( filter, *you, _( "Extend which cable?" ), -1, + _( "You don't have a compatible cable." ) ); + } else { + const auto filter = [&it]( const item & inv ) { + if( !inv.has_flag( flag_CABLE_SPOOL ) || !inv.type->can_use( "link_up" ) || + inv.link && ( it.link || inv.link->has_state( link_state::needs_reeling ) ) ) { + return false; + } + const link_up_actor *actor = static_cast + ( inv.get_use( "link_up" )->get_actor_ptr() ); + return actor->can_extend.find( "ELECTRICAL_DEVICES" ) != actor->can_extend.end(); + }; + selected = game_menus::inv::titled_filter_menu( filter, *you, _( "Extend with which cable?" ), -1, + _( "You don't have a compatible cable." ) ); + } + if( !selected ) { + p->add_msg_if_player( m_info, _( "Never mind." ) ); + return std::nullopt; + } + + item *extension = is_cable_item ? &it : &*selected; + item *extended = is_cable_item ? &*selected : ⁢ + + std::vector all_cables = extension->cables(); + all_cables.emplace_back( extension ); + + // Put the extension cable and all of its attached cables, if any, into the extended item's CABLE pocket. + for( const item *cable : all_cables ) { + item cable_copy( *cable ); + cable_copy.get_contents().clear_items(); + cable_copy.link.reset(); + if( !extended->put_in( cable_copy, item_pocket::pocket_type::CABLE ).success() ) { + debugmsg( "Failed to put %s inside %s!", cable_copy.tname(), extended->tname() ); + } + } + + if( extension->link ) { + extended->link = extension->link; + } + extended->set_link_traits(); + extended->process( get_map(), p, p->pos() ); + + 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->type_name(), extension->type_name() ); + p->i_rem( extension ); + p->moves -= move_cost; + return 0; +} + +std::optional link_up_actor::remove_extensions( Character *p, item &it ) const +{ + std::list all_cables = it.all_items_ptr( item_pocket::pocket_type::CABLE ); + all_cables.remove_if( []( const item * cable ) { + return !cable->has_flag( flag_CABLE_SPOOL ) || !cable->type->can_use( "link_up" ); + } ); + + if( all_cables.size() < 1 ) { + // Delete any non-cables that somehow got into the pocket. + it.get_contents().clear_pockets_if( []( item_pocket const & pocket ) { + return pocket.is_type( item_pocket::pocket_type::CABLE ); + } ); + return 0; + } + item cable_main_copy( *all_cables.back() ); + all_cables.pop_back(); + + if( all_cables.size() > 0 ) { + for( item *cable : all_cables ) { + item cable_copy( *cable ); + cable_copy.get_contents().clear_items(); + cable_copy.link.reset(); + if( !cable_main_copy.put_in( cable_copy, item_pocket::pocket_type::CABLE ).success() ) { + debugmsg( "Failed to put %s inside %s!", cable_copy.tname(), cable_main_copy.tname() ); + } + } + } + p->add_msg_if_player( _( "You disconnect the %1$s from the %2$s." ), + cable_main_copy.type_name(), it.type_name() ); + + it.get_contents().clear_pockets_if( []( item_pocket const & pocket ) { + return pocket.is_type( item_pocket::pocket_type::CABLE ); + } ); + + if( it.link ) { + // If the item was linked, keep the extension cables linked. + cable_main_copy.link = it.link; + cable_main_copy.set_link_traits(); + cable_main_copy.process( get_map(), p, p->pos() ); + it.reset_link( p ); + } + + p->i_add_or_drop( cable_main_copy ); + p->moves -= move_cost; + return 0; +} + std::unique_ptr deploy_tent_actor::clone() const { return std::make_unique( *this ); diff --git a/src/iuse_actor.h b/src/iuse_actor.h index 578fdf7328246..32bc8fd6c5c08 100644 --- a/src/iuse_actor.h +++ b/src/iuse_actor.h @@ -1037,17 +1037,20 @@ class link_up_actor : public iuse_actor /** Charge rate in watts */ units::power wattage = 0_W; /** one_in(this) chance to fail adding 1 charge */ - int charge_efficiency = 7; + int efficiency = 7; /** (Optional) The move cost to attach the cable. */ int move_cost = 5; /** (Optional) Text displayed in the activation screen, defaults to "Plug in / Unplug". */ translation menu_text; std::set targets = { link_state::no_link, link_state::vehicle_port }; + std::set can_extend = {}; std::optional link_up( Character *p, item &it ) const; std::optional link_to_veh_app( Character *p, item &it, const bool to_ports ) const; std::optional link_tow_cable( Character *p, item &it, const bool to_towing ) const; + std::optional link_extend_cable( Character *p, item &it ) const; + std::optional remove_extensions( Character *p, item &it ) const; link_up_actor() : iuse_actor( "link_up" ) {} diff --git a/src/savegame_json.cpp b/src/savegame_json.cpp index d2b4535b57314..46ba011993dfb 100644 --- a/src/savegame_json.cpp +++ b/src/savegame_json.cpp @@ -2866,11 +2866,12 @@ void item::link_data::serialize( JsonOut &jsout ) const jsout.member( "link_t_state", t_state ); jsout.member( "link_t_abs_pos", t_abs_pos ); jsout.member( "link_t_mount", t_mount ); + jsout.member( "link_length", length ); jsout.member( "link_max_length", max_length ); jsout.member( "link_last_processed", last_processed ); jsout.member( "link_charge_rate", wattage ); jsout.member( "link_charge_interval", charge_interval ); - jsout.member( "link_charge_efficiency", charge_efficiency ); + jsout.member( "link_charge_efficiency", efficiency ); jsout.end_object(); } @@ -2882,11 +2883,12 @@ void item::link_data::deserialize( const JsonObject &data ) data.read( "link_t_state", t_state ); data.read( "link_t_abs_pos", t_abs_pos ); data.read( "link_t_mount", t_mount ); - max_length = data.get_int( "link_max_length" ); + data.read( "link_length", length ); + data.read( "link_max_length", max_length ); data.read( "link_last_processed", last_processed ); - wattage = data.get_int( "link_charge_rate" ); - charge_interval = data.get_int( "link_charge_interval" ); - charge_efficiency = data.get_int( "link_charge_efficiency" ); + data.read( "link_charge_rate", wattage ); + data.read( "link_charge_efficiency", efficiency ); + data.read( "link_charge_interval", charge_interval ); } // Template parameter because item::craft_data is private and I don't want to make it public. diff --git a/src/vehicle_part.cpp b/src/vehicle_part.cpp index 7e14f0ff82f6e..3fdd266af821f 100644 --- a/src/vehicle_part.cpp +++ b/src/vehicle_part.cpp @@ -94,27 +94,8 @@ item vehicle_part::properties_to_item() const } bool iuse_found = false; - const use_function *iuse = tmp.type->get_use( "link_up" ); - if( iuse != nullptr ) { - const link_up_actor *actor_ptr = - static_cast( iuse->get_actor_ptr() ); - if( actor_ptr != nullptr ) { - iuse_found = true; - tmp.link->max_length = actor_ptr->cable_length; - tmp.link->charge_efficiency = actor_ptr->charge_efficiency; - tmp.link->wattage = actor_ptr->wattage.value(); - tmp.link->charge_interval = actor_ptr->wattage == 0_W ? -1 : - std::max( 1, static_cast( std::floor( 1000000.0 / abs( actor_ptr->wattage.value() ) + - 0.5 ) ) ); - } - } - if( !iuse_found ) { - debugmsg( "Could not find link_up iuse data for %s! Using fallback values.", tmp.tname() ); - tmp.link->max_length = tmp.type->maximum_charges(); - } - + tmp.set_link_traits(); tmp.link->last_processed = calendar::turn; - tmp.active = true; } // quantize damage and degradation to the middle of each damage_level so that items will stack nicely diff --git a/src/vehicle_use.cpp b/src/vehicle_use.cpp index bdf59530ae075..8e4f31d58bd29 100644 --- a/src/vehicle_use.cpp +++ b/src/vehicle_use.cpp @@ -549,7 +549,6 @@ item vehicle::init_cord( const tripoint &pos ) cord.link->t_state = link_state::vehicle_port; cord.link->t_veh_safe = get_safe_reference(); cord.link->t_abs_pos = get_map().getglobal( pos ); - cord.active = true; return cord; } From d85d3721511064acad7197ea2b901b1b9c02778e Mon Sep 17 00:00:00 2001 From: Kamayana Date: Mon, 3 Jul 2023 04:56:42 -0500 Subject: [PATCH 07/68] Improve link display in inventory, showing current/max length and number of extensions; adjust sorting of linked items --- src/inventory_ui.cpp | 13 +++++++++---- src/item.cpp | 38 ++++++++++++++++++++++++++++---------- 2 files changed, 37 insertions(+), 14 deletions(-) diff --git a/src/inventory_ui.cpp b/src/inventory_ui.cpp index 01693aab80ad1..79924cbbc34c4 100644 --- a/src/inventory_ui.cpp +++ b/src/inventory_ui.cpp @@ -86,9 +86,12 @@ item_name_t &get_cached_name( item const *it ) { auto iter = item_name_cache.find( it ); if( iter == item_name_cache.end() ) { + const std::string name = remove_color_tags( it->tname( 1, false, 0, true, false ) ); + const std::string name_full = string_format( "%s%d%d", + remove_color_tags( it->tname( 1, true, 0, true, false ) ), + it->link_length( true ), it->link_length() ); return item_name_cache - .emplace( it, item_name_t{ remove_color_tags( it->tname( 1, false, 0, true, false ) ), - remove_color_tags( it->tname( 1, true, 0, true, false ) ) } ) + .emplace( it, item_name_t{ name, name_full } ) .first->second; } @@ -1274,7 +1277,8 @@ inventory_entry *inventory_column::add_entry( const inventory_entry &entry ) entry_item.position() == found_entry_item.position() && entry_item.parent_item() == found_entry_item.parent_item() && entry_item->is_collapsed() == found_entry_item->is_collapsed() && - !!entry_item->link == !!found_entry_item->link && + entry_item->link_length() == found_entry_item->link_length() && + entry_item->link_length( true ) == found_entry_item->link_length( true ) && entry_item->display_stacked_with( *found_entry_item, preset.get_checking_components() ); } ); if( entry_with_loc != dest.end() ) { @@ -1391,7 +1395,8 @@ void inventory_column::collate() if( e->is_item() && e->get_category_ptr() == outer->get_category_ptr() && e->any_item()->is_favorite == outer->any_item()->is_favorite && e->any_item()->typeId() == outer->any_item()->typeId() && - !!e->any_item()->link == !!outer->any_item()->link && + std::min( 0, e->any_item()->link_length() ) == std::min( 0, outer->any_item()->link_length() ) && + e->any_item()->link_length( true ) == outer->any_item()->link_length( true ) && ( !indent_entries() || e->any_item().parent_item() == outer->any_item().parent_item() ) && ( e->is_collation_header() || !e->chevron ) && diff --git a/src/item.cpp b/src/item.cpp index 18aa5be3db0d2..103db488f9b3f 100644 --- a/src/item.cpp +++ b/src/item.cpp @@ -6002,8 +6002,7 @@ nc_color item::color_in_inventory( const Character *const ch ) const } } else if( has_flag( flag_LEAK_DAM ) && has_flag( flag_RADIOACTIVE ) && damage() > 0 ) { ret = c_light_green; - } else if( ( active && !has_temperature() ) || ( is_corpse() && can_revive() ) || - ( link && has_flag( flag_CABLE_SPOOL ) ) ) { + } else if( ( active && !has_temperature() ) || ( is_corpse() && can_revive() ) ) { // Active items show up as yellow (corpses only if reviving) ret = c_yellow; } else if( is_medication() || is_medical_tool() ) { @@ -6662,12 +6661,6 @@ std::string item::tname( unsigned int quantity, bool with_prefix, unsigned int t tagtext += _( " (lit)" ); } else if( has_flag( flag_IS_UPS ) && get_var( "cable" ) == "plugged_in" ) { tagtext += _( " (plugged in)" ); - } else if( link ) { - if( link->s_state == link_state::needs_reeling ) { - tagtext += _( " (unspooled)" ); - } else { - tagtext += _( " (connected)" ); - } } else if( active && !has_temperature() && !string_ends_with( typeId().str(), "_on" ) ) { // Usually the items whose ids end in "_on" have the "active" or "on" string already contained // in their name, also food is active while it rots. @@ -6742,6 +6735,7 @@ std::string item::display_name( unsigned int quantity ) const std::string name = tname( quantity ); std::string sidetxt; std::string amt; + std::string cable; switch( get_side() ) { case side::BOTH: @@ -6852,6 +6846,30 @@ std::string item::display_name( unsigned int quantity ) const amt = " (" + ammotext + ")"; } + if( type->can_use( "link_up" ) ) { + std::string extensions = cables().empty() ? "" : string_format( "+%d", cables().size() ); + if( link ) { + if( link->has_state( link_state::needs_reeling ) ) { + cable = string_format( _( " (%1$s cable%2$s)" ), colorize( string_format( "×/%d", + link->max_length ), c_light_red ), extensions ); + } else { + nc_color cable_color; + const double ratio = static_cast( link->length ) / static_cast( link->max_length ); + if( ratio < 1.0 / 3.0 ) { + cable_color = c_light_green; + } else if( ratio < 2.0 / 3.0 ) { + cable_color = c_yellow; + } else { + cable_color = c_red; + } + cable = string_format( " (%s)", colorize( string_format( _( "%d/%d cable%s" ), + link->max_length - link->length, link->max_length, extensions ), cable_color ) ); + } + } else if( !extensions.empty() ) { + cable = string_format( _( " (-/%1$d cable%2$s)" ), link_length( true ), extensions ); + } + } + // HACK: This is a hack to prevent possible crashing when displaying maps as items during character creation if( is_map() && calendar::turn != calendar::turn_zero ) { // TODO: fix point types @@ -6865,7 +6883,7 @@ std::string item::display_name( unsigned int quantity ) const } } - return string_format( "%s%s%s", name, sidetxt, amt ); + return string_format( "%s%s%s%s", name, sidetxt, amt, cable ); } bool item::is_collapsed() const @@ -10349,7 +10367,7 @@ int item::ammo_remaining( const Character *carrier, bool cable_links ) const } // Cable connections - if( cable_links && link_length() > -1 ) { + if( cable_links && link_length() >= 0 ) { if( link->t_veh_safe ) { ret += link->t_veh_safe->connected_battery_power_level().first; } else { From b61ffd6f1b11469ece66550140a09bcba778213e Mon Sep 17 00:00:00 2001 From: Kamayana Date: Wed, 12 Jul 2023 06:44:48 -0500 Subject: [PATCH 08/68] Reopen cable menu for convenience if reeling up an already unspooled cable --- data/json/player_activities.json | 10 +++++++ src/activity_actor.cpp | 36 +++++++++++++++++++++++ src/activity_actor_definitions.h | 26 +++++++++++++++++ src/item.cpp | 4 +++ src/iuse_actor.cpp | 49 +++++++++++++++++++++++--------- src/player_activity.cpp | 2 ++ 6 files changed, 113 insertions(+), 14 deletions(-) diff --git a/data/json/player_activities.json b/data/json/player_activities.json index 21b131e7d41e2..ab35f6192f4f2 100644 --- a/data/json/player_activities.json +++ b/data/json/player_activities.json @@ -902,6 +902,16 @@ "based_on": "neither", "no_resume": true }, + { + "id": "ACT_INVOKE_ITEM", + "type": "activity_type", + "activity_level": "NO_EXERCISE", + "verb": "using item", + "suspendable": false, + "rooted": true, + "based_on": "neither", + "no_resume": true + }, { "id": "ACT_TREE_COMMUNION", "type": "activity_type", diff --git a/src/activity_actor.cpp b/src/activity_actor.cpp index 681a919741061..a88667e0e7fc1 100644 --- a/src/activity_actor.cpp +++ b/src/activity_actor.cpp @@ -117,6 +117,7 @@ static const activity_id ACT_HAIRCUT( "ACT_HAIRCUT" ); static const activity_id ACT_HARVEST( "ACT_HARVEST" ); static const activity_id ACT_HOTWIRE_CAR( "ACT_HOTWIRE_CAR" ); static const activity_id ACT_INSERT_ITEM( "ACT_INSERT_ITEM" ); +static const activity_id ACT_INVOKE_ITEM( "ACT_INVOKE_ITEM" ); static const activity_id ACT_LOCKPICK( "ACT_LOCKPICK" ); static const activity_id ACT_LONGSALVAGE( "ACT_LONGSALVAGE" ); static const activity_id ACT_MEDITATE( "ACT_MEDITATE" ); @@ -5674,6 +5675,40 @@ std::unique_ptr wear_activity_actor::deserialize( JsonValue &jsi return actor.clone(); } +void invoke_item_activity_actor::do_turn( player_activity &, Character &who ) +{ + if( method.empty() ) { + who.cancel_activity(); + who.invoke_item( item.get_item() ); + return; + } + std::string it_method = method; + who.cancel_activity(); + who.invoke_item( item.get_item(), it_method ); +} + +void invoke_item_activity_actor::serialize( JsonOut &jsout ) const +{ + jsout.start_object(); + + jsout.member( "item", item ); + jsout.member( "method", method ); + + jsout.end_object(); +} + +std::unique_ptr invoke_item_activity_actor::deserialize( JsonValue &jsin ) +{ + invoke_item_activity_actor actor( {}, {} ); + + JsonObject data = jsin.get_object(); + + data.read( "item", actor.item ); + data.read( "method", actor.method ); + + return actor.clone(); +} + void pickup_menu_activity_actor::do_turn( player_activity &, Character &who ) { std::optional p( where ); @@ -7191,6 +7226,7 @@ deserialize_functions = { { ACT_HARVEST, &harvest_activity_actor::deserialize}, { ACT_HOTWIRE_CAR, &hotwire_car_activity_actor::deserialize }, { ACT_INSERT_ITEM, &insert_item_activity_actor::deserialize }, + { ACT_INVOKE_ITEM, &invoke_item_activity_actor::deserialize }, { ACT_LOCKPICK, &lockpick_activity_actor::deserialize }, { ACT_LONGSALVAGE, &longsalvage_activity_actor::deserialize }, { ACT_MEDITATE, &meditate_activity_actor::deserialize }, diff --git a/src/activity_actor_definitions.h b/src/activity_actor_definitions.h index d62559b0ad804..975ccc4d0c76d 100644 --- a/src/activity_actor_definitions.h +++ b/src/activity_actor_definitions.h @@ -1697,6 +1697,32 @@ class wield_activity_actor : public activity_actor contents_change_handler handler; }; +class invoke_item_activity_actor : public activity_actor +{ + public: + invoke_item_activity_actor( item_location item, std::string method ) : + item( std::move( item ) ), + method( std::move( method ) ) {}; + activity_id get_type() const override { + return activity_id( "ACT_INVOKE_ITEM" ); + } + + void start( player_activity &, Character & ) override {}; + void do_turn( player_activity &, Character &who ) override; + void finish( player_activity &, Character & ) override {}; + + std::unique_ptr clone() const override { + return std::make_unique( *this ); + } + + void serialize( JsonOut & ) const override; + static std::unique_ptr deserialize( JsonValue & ); + + private: + item_location item; + std::string method; +}; + class pickup_menu_activity_actor : public activity_actor { public: diff --git a/src/item.cpp b/src/item.cpp index 103db488f9b3f..8e16d57a240ee 100644 --- a/src/item.cpp +++ b/src/item.cpp @@ -13204,6 +13204,10 @@ bool item::reset_link( Character *p, const bool loose_message, const tripoint ca if( !link ) { return has_flag( flag_AUTO_DELETE_CABLE ); } + // Cables that need reeling should be reset with a reel_cable_activity_actor instead. + if( link->has_state( link_state::needs_reeling ) ) { + return false; + } const bool is_cable_item = has_flag( flag_CABLE_SPOOL ); diff --git a/src/iuse_actor.cpp b/src/iuse_actor.cpp index 3dde699a980f4..92b0ed311a355 100644 --- a/src/iuse_actor.cpp +++ b/src/iuse_actor.cpp @@ -4380,10 +4380,10 @@ std::optional link_up_actor::link_up( Character *p, item &it ) const ( it.link->length - respool_threshold ) * respool_time_per_square; const bool past_respool_threshold = it.link_length() > respool_threshold; const bool unspooled = it.link && it.link->has_state( link_state::needs_reeling ); + const bool has_loose_end = !unspooled && is_cable_item ? !it.link || it.link->has_state( link_state::no_link ) : + !it.link || it.link->has_no_links(); uilist link_menu; - const bool has_loose_end = is_cable_item ? !it.link || it.link->has_state( link_state::no_link ) : - !it.link || it.link->has_no_links(); if( !is_cable_item || !it.link || it.link->has_no_links() ) { // This cable item or device doesn't have any connections yet. link_menu.text = string_format( _( "Attaching the %s:" ), cable_name ); @@ -4414,14 +4414,18 @@ std::optional link_up_actor::link_up( Character *p, item &it ) const } } if( !is_cable_item || !can_extend.empty() ) { - const bool has_extensions = !it.all_items_top( item_pocket::pocket_type::CABLE ).empty(); + const bool has_extensions = !unspooled && !it.all_items_top( item_pocket::pocket_type::CABLE ).empty(); link_menu.addentry( 30, has_loose_end, -1, is_cable_item ? _( "Extend another cable" ) : _( "Extend with another cable" ) ); link_menu.addentry( 31, has_extensions, -1, _( "Remove cable extensions" ) ); } if( targets.count( link_state::no_link ) > 0 ) { - link_menu.addentry( 999, !!it.link && !it.link->has_no_links(), -1, unspooled ? _( "Re-spool" ) : - past_respool_threshold ? _( "Detach and re-spool" ) : _( "Detach" ) ); + if( unspooled ) { + link_menu.addentry( 998, true, -1, _( "Re-spool" ) ); + } else { + link_menu.addentry( 999, !!it.link && !it.link->has_no_links(), -1, + past_respool_threshold ? _( "Detach and re-spool" ) : _( "Detach" ) ); + } } } else if( it.link->has_state( link_state::vehicle_tow ) ) { @@ -4433,8 +4437,12 @@ std::optional link_up_actor::link_up( Character *p, item &it ) const link_menu.addentry( 11, has_loose_end && it.link->s_state == link_state::vehicle_tow, -1, _( "Attach loose end to towed vehicle" ) ); if( targets.count( link_state::no_link ) > 0 ) { - link_menu.addentry( 999, true, -1, unspooled ? _( "Re-spool" ) : - past_respool_threshold ? _( "Detach and re-spool" ) : _( "Detach" ) ); + if( unspooled ) { + link_menu.addentry( 998, true, -1, _( "Re-spool" ) ); + } else { + link_menu.addentry( 999, true, -1, + past_respool_threshold ? _( "Detach and re-spool" ) : _( "Detach" ) ); + } } } else { @@ -4470,13 +4478,17 @@ std::optional link_up_actor::link_up( Character *p, item &it ) const } } if( !can_extend.empty() ) { - const bool has_extensions = !it.all_items_top( item_pocket::pocket_type::CABLE ).empty(); + const bool has_extensions = !unspooled && !it.all_items_top( item_pocket::pocket_type::CABLE ).empty(); link_menu.addentry( 30, has_loose_end, -1, _( "Extend another cable" ) ); link_menu.addentry( 31, has_extensions, -1, _( "Remove cable extensions" ) ); } if( targets.count( link_state::no_link ) > 0 ) { - link_menu.addentry( 999, true, -1, unspooled ? _( "Re-spool" ) : - past_respool_threshold ? _( "Detach and re-spool" ) : _( "Detach" ) ); + if( unspooled ) { + link_menu.addentry( 998, true, -1, _( "Re-spool" ) ); + } else { + link_menu.addentry( 999, true, -1, + past_respool_threshold ? _( "Detach and re-spool" ) : _( "Detach" ) ); + } } } @@ -4490,14 +4502,23 @@ std::optional link_up_actor::link_up( Character *p, item &it ) const if( choice < 0 ) { // Cancelled selection. - p->add_msg_if_player( _( "Never mind." ) ); return std::nullopt; - } else if( choice == 999 ) { + } else if( choice >= 998 ) { // Selection: Unconnect & respool. - it.reset_link( p ); if( unspooled ) { - // Cables that are too long need to be manually rewound before reuse. + if( choice == 998 ) { + // If the "Re-spool" option was selected, re-open the menu after the cable is reeled. + p->assign_activity( invoke_item_activity_actor( item_location{*p, &it}, "link_up" ) ); + p->activity.auto_resume = true; + } + p->assign_activity( player_activity( reel_cable_activity_actor( respool_time_total, item_location{*p, &it} ) ) ); + return 0; + } + + it.reset_link( p ); + // Cables that are too long need to be manually rewound before reuse. + if( it.link_length() == -1 ) { p->assign_activity( player_activity( reel_cable_activity_actor( respool_time_total, item_location{*p, &it} ) ) ); return 0; } else { diff --git a/src/player_activity.cpp b/src/player_activity.cpp index 50fe13fd9277e..714e6d52b9b22 100644 --- a/src/player_activity.cpp +++ b/src/player_activity.cpp @@ -41,6 +41,7 @@ static const activity_id ACT_CONSUME_MEDS_MENU( "ACT_CONSUME_MEDS_MENU" ); static const activity_id ACT_EAT_MENU( "ACT_EAT_MENU" ); static const activity_id ACT_HACKSAW( "ACT_HACKSAW" ); static const activity_id ACT_HEATING( "ACT_HEATING" ); +static const activity_id ACT_INVOKE_ITEM( "ACT_INVOKE_ITEM" ); static const activity_id ACT_JACKHAMMER( "ACT_JACKHAMMER" ); static const activity_id ACT_MIGRATION_CANCEL( "ACT_MIGRATION_CANCEL" ); static const activity_id ACT_NULL( "ACT_NULL" ); @@ -146,6 +147,7 @@ std::optional player_activity::get_progress_message( const avatar & type == ACT_CONSUME_FOOD_MENU || type == ACT_CONSUME_MEDS_MENU || type == ACT_EAT_MENU || + type == ACT_INVOKE_ITEM || type == ACT_PICKUP_MENU || type == ACT_VIEW_RECIPE ) { return std::nullopt; From 7faf25c7c6f79f5d45d6874905bcbd42919eb8c4 Mon Sep 17 00:00:00 2001 From: Kamayana Date: Wed, 12 Jul 2023 06:49:26 -0500 Subject: [PATCH 09/68] Fix vehicle cables breaking off the turn AFTER they pass the threshold --- src/map.cpp | 2 +- src/vehicle.cpp | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/map.cpp b/src/map.cpp index 22b35bd1bb7ea..96f235f855c62 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -1522,7 +1522,6 @@ bool map::displace_vehicle( vehicle &veh, const tripoint &dp, const bool adjust_ } } - veh.shed_loose_parts( &src, &dst ); smzs = veh.advance_precalc_mounts( dst_offset, src.raw(), dp, ramp_offset, adjust_pos, parts_to_move ); veh.update_active_fakes(); @@ -1535,6 +1534,7 @@ bool map::displace_vehicle( vehicle &veh, const tripoint &dp, const bool adjust_ src_submap->vehicles.erase( src_submap_veh_it ); invalidate_max_populated_zlev( dst.z() ); } + veh.shed_loose_parts( &src, &dst ); if( need_update ) { g->update_map( player_character ); } diff --git a/src/vehicle.cpp b/src/vehicle.cpp index 4437999fe459a..4bdd0a48363bb 100644 --- a/src/vehicle.cpp +++ b/src/vehicle.cpp @@ -6626,8 +6626,7 @@ void vehicle::shed_loose_parts( const tripoint_bub_ms *src, const tripoint_bub_m if( vpi_loose.has_flag( "POWER_TRANSFER" ) ) { int distance = rl_dist( here.getabs( bub_part_pos( vp_loose ) ), vp_loose.target.second ); int max_dist = vp_loose.get_base().type->maximum_charges(); - if( src && ( max_dist - distance ) > 0 ) { - // power line still has some slack to it, so keep it attached for now + if( src ) { vehicle *veh = find_vehicle( vp_loose.target.second ); if( veh != nullptr ) { for( int remote_lp : veh->loose_parts ) { @@ -6640,7 +6639,10 @@ void vehicle::shed_loose_parts( const tripoint_bub_ms *src, const tripoint_bub_m } } } - continue; + if( ( max_dist - distance ) > 0 ) { + // power line still has some slack to it, so keep it attached for now + continue; + } } add_msg_if_player_sees( global_part_pos3( vp_loose ), m_warning, _( "The %s's power connection was detached!" ), name ); From 7c2e5844a76d358b22ee56305fb4260148d52dad Mon Sep 17 00:00:00 2001 From: Kamayana Date: Wed, 12 Jul 2023 06:51:19 -0500 Subject: [PATCH 10/68] Drop snapped off vehicle cables at the previous tile position --- src/map.cpp | 2 +- src/vehicle.cpp | 9 +++++---- src/vehicle.h | 3 ++- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/map.cpp b/src/map.cpp index 96f235f855c62..87d42aa64f039 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -1534,7 +1534,7 @@ bool map::displace_vehicle( vehicle &veh, const tripoint &dp, const bool adjust_ src_submap->vehicles.erase( src_submap_veh_it ); invalidate_max_populated_zlev( dst.z() ); } - veh.shed_loose_parts( &src, &dst ); + veh.shed_loose_parts( &src, &dst, tripoint( -dp.xy(), -ramp_offset ) ); if( need_update ) { g->update_map( player_character ); } diff --git a/src/vehicle.cpp b/src/vehicle.cpp index 4bdd0a48363bb..42633b94704a3 100644 --- a/src/vehicle.cpp +++ b/src/vehicle.cpp @@ -6610,7 +6610,8 @@ void vehicle::remove_remote_part( const vehicle_part &vp_local ) const } } -void vehicle::shed_loose_parts( const tripoint_bub_ms *src, const tripoint_bub_ms *dst ) +void vehicle::shed_loose_parts( const tripoint_bub_ms *src, const tripoint_bub_ms *dst, + const tripoint drop_offset ) { map &here = get_map(); // remove_part rebuilds the loose_parts vector, so iterate over a copy to preserve @@ -6625,7 +6626,7 @@ void vehicle::shed_loose_parts( const tripoint_bub_ms *src, const tripoint_bub_m } if( vpi_loose.has_flag( "POWER_TRANSFER" ) ) { int distance = rl_dist( here.getabs( bub_part_pos( vp_loose ) ), vp_loose.target.second ); - int max_dist = vp_loose.get_base().type->maximum_charges(); + int max_dist = vp_loose.get_base().link_length( true ); if( src ) { vehicle *veh = find_vehicle( vp_loose.target.second ); if( veh != nullptr ) { @@ -6639,7 +6640,7 @@ void vehicle::shed_loose_parts( const tripoint_bub_ms *src, const tripoint_bub_m } } } - if( ( max_dist - distance ) > 0 ) { + if( distance <= max_dist ) { // power line still has some slack to it, so keep it attached for now continue; } @@ -6654,7 +6655,7 @@ void vehicle::shed_loose_parts( const tripoint_bub_ms *src, const tripoint_bub_m } const item drop = vp_loose.properties_to_item(); if( !magic && !drop.has_flag( flag_AUTO_DELETE_CABLE ) ) { - here.add_item_or_charges( global_part_pos3( vp_loose ), drop ); + here.add_item_or_charges( global_part_pos3( vp_loose ) + drop_offset, drop ); } remove_part( vp_loose ); diff --git a/src/vehicle.h b/src/vehicle.h index 8a4cbe132fdb4..59bf0bb74dace 100644 --- a/src/vehicle.h +++ b/src/vehicle.h @@ -1811,7 +1811,8 @@ class vehicle void shift_parts( map &here, const point &delta ); bool shift_if_needed( map &here ); - void shed_loose_parts( const tripoint_bub_ms *src = nullptr, const tripoint_bub_ms *dst = nullptr ); + void shed_loose_parts( const tripoint_bub_ms *src = nullptr, const tripoint_bub_ms *dst = nullptr, + const tripoint drop_offset = tripoint_zero ); /** * @name Vehicle turrets From 0ef57fec7ed20257024fb39f4e07f23f57e8ce23 Mon Sep 17 00:00:00 2001 From: Kamayana Date: Wed, 12 Jul 2023 06:54:45 -0500 Subject: [PATCH 11/68] Change find_vehicle() into a public map function, use for better link vehicle pointer initialization --- src/item.cpp | 40 ++++++++++++---------------------------- src/map.cpp | 26 ++++++++++++++++++++++++++ src/map.h | 6 ++++++ src/vehicle.cpp | 41 ++++++----------------------------------- src/vehicle.h | 8 -------- 5 files changed, 50 insertions(+), 71 deletions(-) diff --git a/src/item.cpp b/src/item.cpp index 8e16d57a240ee..55fbc9239194b 100644 --- a/src/item.cpp +++ b/src/item.cpp @@ -13031,41 +13031,25 @@ bool item::process_link( map &here, Character *carrier, const tripoint &pos ) link->s_bub_pos = pos; check_length = true; } - - // If the vehicle pointer is lost... + // Re-establish vehicle pointer if it got lost or if this item just got loaded. if( !link->t_veh_safe ) { - if( last_t_abs_pos_is_oob ) { - // ... and the last recorded target point is out of bounds, just check the length if needed and exit early. - if( check_length ) { - // We can't get the exact connection point without the vehicle loaded, so fudge some forgiveness by adding the mount dimensions. - // Better to err on the side of keeping things connected. - link->length = rl_dist( here.getabs( pos ), link->t_abs_pos.raw() ) + - link->t_mount.abs().x + link->t_mount.abs().y; - if( is_cable_too_long() ) { - return reset_link( carrier ); - } - } - return false; - } else { - // ... and the last recorded target point is in-bounds, try to recreate the vehicle pointer, disconnecting if it fails. - const optional_vpart_position vp = here.veh_at( link->t_abs_pos ); - if( !vp ) { - return reset_link( carrier, true, pos ); - } - auto vp_displayed = vp.part_displayed(); - if( vp_displayed && vp_displayed->part().has_flag( vp_flag::carried_flag ) ) { - // Connected vehicle was racked, so disconnect. - return reset_link( carrier, true, pos ); - } - link->t_veh_safe = vp.value().vehicle().get_safe_reference(); + vehicle *found_veh = here.find_vehicle( link->t_abs_pos ); + //return false; + if( !found_veh ) { + return reset_link( carrier, true, pos ); + } + if( debug_mode ) { + add_msg_debug( debugmode::DF_IUSE, "Re-established link of %s to %s.", type_name(), + found_veh->disp_name() ); } + link->t_veh_safe = found_veh->get_safe_reference(); } // Regular pointers are faster, so make one now that we know the reference is valid. vehicle *t_veh = link->t_veh_safe.get(); - // We should skip processing if the last saved target point is out of bounds, but if the linked vehicle is moving fast enough, - // we should always process it to avoid erroneously skipping devices riding inside of it. + // We should skip processing if the last saved target point is out of bounds, since vehicles give innacurate absolute coordinates when out of bounds. + // However, if the linked vehicle is moving fast enough, we should always do processing to avoid erroneously skipping linked items riding inside of it. if( last_t_abs_pos_is_oob && t_veh->velocity < HALF_MAPSIZE_X * 400 ) { if( check_length ) { link->length = rl_dist( here.getabs( pos ), link->t_abs_pos.raw() ) + diff --git a/src/map.cpp b/src/map.cpp index 87d42aa64f039..a45355c84ed0d 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -1305,6 +1305,32 @@ vehicle *map::veh_at_internal( const tripoint &p, int &part_num ) return const_cast( const_cast( this )->veh_at_internal( p, part_num ) ); } +vehicle *map::find_vehicle( const tripoint_abs_ms &where ) const +{ + // Is it in the reality bubble? + if( const optional_vpart_position vp = veh_at( where ) ) { + return &vp->vehicle(); + } + // Nope. Load up its submap... + point_sm_ms veh_in_sm; + tripoint_abs_sm veh_sm; + std::tie( veh_sm, veh_in_sm ) = project_remain( where ); + const submap *sm = MAPBUFFER.lookup_submap( veh_sm ); + if( sm == nullptr ) { + return nullptr; + } + + for( const auto &elem : sm->vehicles ) { + vehicle *found_veh = elem.get(); + // TODO: fix point types + if( veh_in_sm.raw() == found_veh->pos ) { + return found_veh; + } + } + + return nullptr; +} + void map::board_vehicle( const tripoint &pos, Character *p ) { if( p == nullptr ) { diff --git a/src/map.h b/src/map.h index e6a557976f7ed..0a70c3ffca58a 100644 --- a/src/map.h +++ b/src/map.h @@ -696,6 +696,12 @@ class map optional_vpart_position veh_at( const tripoint_bub_ms &p ) const; vehicle *veh_at_internal( const tripoint &p, int &part_num ); const vehicle *veh_at_internal( const tripoint &p, int &part_num ) const; + /** + * Find a possibly off-map vehicle. If necessary, loads up its submap through + * the global MAPBUFFER and pulls it from there. + * @param where Absolute coordinates of the target vehicle's origin tile. + */ + vehicle *find_vehicle( const tripoint_abs_ms &where ) const; // Put player on vehicle at x,y void board_vehicle( const tripoint &p, Character *pl ); // Remove given passenger from given vehicle part. diff --git a/src/vehicle.cpp b/src/vehicle.cpp index 42633b94704a3..706cfb5c9f5ed 100644 --- a/src/vehicle.cpp +++ b/src/vehicle.cpp @@ -5029,37 +5029,6 @@ void vehicle::power_parts() } } -vehicle *vehicle::find_vehicle( const tripoint &where ) -{ - map &here = get_map(); - // Is it in the reality bubble? - tripoint veh_local = here.getlocal( where ); - if( const optional_vpart_position vp = here.veh_at( veh_local ) ) { - return &vp->vehicle(); - } - - // Nope. Load up its submap... - point_sm_ms veh_in_sm; - tripoint_abs_sm veh_sm; - // TODO: fix point types - std::tie( veh_sm, veh_in_sm ) = project_remain( tripoint_abs_ms( where ) ); - - const submap *sm = MAPBUFFER.lookup_submap( veh_sm ); - if( sm == nullptr ) { - return nullptr; - } - - for( const auto &elem : sm->vehicles ) { - vehicle *found_veh = elem.get(); - // TODO: fix point types - if( veh_in_sm.raw() == found_veh->pos ) { - return found_veh; - } - } - - return nullptr; -} - template // Templated to support const and non-const vehicle* std::map vehicle::search_connected_vehicles( Vehicle *start ) { @@ -5084,7 +5053,7 @@ std::map vehicle::search_connected_vehicles( Vehicle *start ) continue; } - Vehicle *const v_next = vehicle::find_vehicle( vp.target.second ); + Vehicle *const v_next = get_map().find_vehicle( tripoint_abs_ms( vp.target.second ) ); if( v_next == nullptr ) { // vehicle's rolled away or off-map continue; } @@ -6596,10 +6565,12 @@ bool vehicle::no_towing_slack() const void vehicle::remove_remote_part( const vehicle_part &vp_local ) const { - vehicle *veh = find_vehicle( vp_local.target.second ); + map &here = get_map(); + vehicle *veh = here.find_vehicle( tripoint_abs_ms( vp_local.target.second ) ); + // If the target vehicle is still there, ask it to remove its part if( veh != nullptr ) { - const tripoint local_abs = get_map().getabs( global_part_pos3( vp_local ) ); + const tripoint local_abs = here.getabs( global_part_pos3( vp_local ) ); for( const int remote_partnum : veh->loose_parts ) { vehicle_part &vp_remote = veh->parts[remote_partnum]; if( vp_remote.info().has_flag( "POWER_TRANSFER" ) && vp_remote.target.first == local_abs ) { @@ -6628,7 +6599,7 @@ void vehicle::shed_loose_parts( const tripoint_bub_ms *src, const tripoint_bub_m int distance = rl_dist( here.getabs( bub_part_pos( vp_loose ) ), vp_loose.target.second ); int max_dist = vp_loose.get_base().link_length( true ); if( src ) { - vehicle *veh = find_vehicle( vp_loose.target.second ); + vehicle *veh = here.find_vehicle( tripoint_abs_ms( vp_loose.target.second ) ); if( veh != nullptr ) { for( int remote_lp : veh->loose_parts ) { vehicle_part &vp_remote = veh->part( remote_lp ); diff --git a/src/vehicle.h b/src/vehicle.h index 59bf0bb74dace..83a72b12715b4 100644 --- a/src/vehicle.h +++ b/src/vehicle.h @@ -837,14 +837,6 @@ class vehicle /** empty the contents of a tank, battery or turret spilling liquids randomly on the ground */ void leak_fuel( vehicle_part &pt ) const; - /** - * Find a possibly off-map vehicle. If necessary, loads up its submap through - * the global MAPBUFFER and pulls it from there. For this reason, you should only - * give it the coordinates of the origin tile of a target vehicle. - * @param where Location of the other vehicle's origin tile. - */ - static vehicle *find_vehicle( const tripoint &where ); - /// Returns a map of connected vehicle pointers to power loss factor: /// Keys are vehicles connected by POWER_TRANSFER parts, includes self /// Values are line loss, 0.01 corresponds to 1% charge loss to wire resistance From 1f90e2bc50125ed2e49919a2209e2209a5c7ecc0 Mon Sep 17 00:00:00 2001 From: Kamayana Date: Wed, 5 Jul 2023 18:42:07 -0500 Subject: [PATCH 12/68] Fix link creation connecting to the wrong point --- src/iuse_actor.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/iuse_actor.cpp b/src/iuse_actor.cpp index 92b0ed311a355..36e7542285b60 100644 --- a/src/iuse_actor.cpp +++ b/src/iuse_actor.cpp @@ -4380,7 +4380,8 @@ std::optional link_up_actor::link_up( Character *p, item &it ) const ( it.link->length - respool_threshold ) * respool_time_per_square; const bool past_respool_threshold = it.link_length() > respool_threshold; const bool unspooled = it.link && it.link->has_state( link_state::needs_reeling ); - const bool has_loose_end = !unspooled && is_cable_item ? !it.link || it.link->has_state( link_state::no_link ) : + const bool has_loose_end = !unspooled && is_cable_item ? !it.link || + it.link->has_state( link_state::no_link ) : !it.link || it.link->has_no_links(); uilist link_menu; @@ -4414,7 +4415,8 @@ std::optional link_up_actor::link_up( Character *p, item &it ) const } } if( !is_cable_item || !can_extend.empty() ) { - const bool has_extensions = !unspooled && !it.all_items_top( item_pocket::pocket_type::CABLE ).empty(); + const bool has_extensions = !unspooled && + !it.all_items_top( item_pocket::pocket_type::CABLE ).empty(); link_menu.addentry( 30, has_loose_end, -1, is_cable_item ? _( "Extend another cable" ) : _( "Extend with another cable" ) ); link_menu.addentry( 31, has_extensions, -1, _( "Remove cable extensions" ) ); @@ -4478,7 +4480,8 @@ std::optional link_up_actor::link_up( Character *p, item &it ) const } } if( !can_extend.empty() ) { - const bool has_extensions = !unspooled && !it.all_items_top( item_pocket::pocket_type::CABLE ).empty(); + const bool has_extensions = !unspooled && + !it.all_items_top( item_pocket::pocket_type::CABLE ).empty(); link_menu.addentry( 30, has_loose_end, -1, _( "Extend another cable" ) ); link_menu.addentry( 31, has_extensions, -1, _( "Remove cable extensions" ) ); } @@ -4715,7 +4718,7 @@ std::optional link_up_actor::link_to_veh_app( Character *p, item &it, return std::nullopt; } it.link->t_state = to_ports ? link_state::vehicle_port : link_state::vehicle_battery; - it.link->t_abs_pos = here.getglobal( pnt ); + it.link->t_abs_pos = here.getglobal( t_vp->vehicle().global_pos3() ); it.link->t_mount = t_vp->mount(); it.set_link_traits(); it.link->last_processed = calendar::turn; @@ -4833,7 +4836,7 @@ std::optional link_up_actor::link_tow_cable( Character *p, item &it, } else { it.link->t_state = link_state::vehicle_tow; // Assign towed vehicle. } - it.link->t_abs_pos = here.getglobal( pnt ); + it.link->t_abs_pos = here.getglobal( t_vp->vehicle().global_pos3() ); it.link->t_mount = t_vp->mount(); it.link->max_length = cable_length != -1 ? cable_length : it.type->maximum_charges(); it.link->last_processed = calendar::turn; From 70ed44f390096e2e5259d5fd2358657bc5a20522 Mon Sep 17 00:00:00 2001 From: Kamayana Date: Wed, 5 Jul 2023 20:35:24 -0500 Subject: [PATCH 13/68] Immediately process cables on creation if they're linked --- src/map.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/map.cpp b/src/map.cpp index a45355c84ed0d..22f2d13040154 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -5092,7 +5092,7 @@ item &map::add_item( const tripoint &p, item new_item ) // Process foods and temperature tracked items when they are added to the map, here instead of add_item_at() // to avoid double processing food during active item processing. - if( new_item.has_temperature() && !new_item.is_corpse() ) { + if( new_item.has_temperature() && !new_item.is_corpse() || new_item.link ) { new_item.process( *this, nullptr, p ); } From 4298c4bbc4a35061741f5b5bc073c7d7a9f79775 Mon Sep 17 00:00:00 2001 From: Kamayana Date: Wed, 12 Jul 2023 06:55:32 -0500 Subject: [PATCH 14/68] Remove tow cable section of shed_loose_parts - Tow cables aren't loose parts, so this never gets called --- src/vehicle.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/vehicle.cpp b/src/vehicle.cpp index 706cfb5c9f5ed..6d2dbd84c1b82 100644 --- a/src/vehicle.cpp +++ b/src/vehicle.cpp @@ -6620,10 +6620,6 @@ void vehicle::shed_loose_parts( const tripoint_bub_ms *src, const tripoint_bub_m _( "The %s's power connection was detached!" ), name ); remove_remote_part( vp_loose ); } - if( vpi_loose.has_flag( "TOW_CABLE" ) ) { - invalidate_towing( true ); - continue; - } const item drop = vp_loose.properties_to_item(); if( !magic && !drop.has_flag( flag_AUTO_DELETE_CABLE ) ) { here.add_item_or_charges( global_part_pos3( vp_loose ) + drop_offset, drop ); From 8150f5cd0645189ea2d8e37204c76b1a99b97846 Mon Sep 17 00:00:00 2001 From: Kamayana Date: Thu, 6 Jul 2023 03:22:14 -0500 Subject: [PATCH 15/68] Remove UNMOUNT_ON_MOVE from cables, since it's misleading. Don't change functionality --- data/json/furniture_and_terrain/appliances.json | 2 +- data/json/vehicleparts/vehicle_parts.json | 10 +++++----- data/mods/TEST_DATA/appliance.json | 6 +++--- src/vehicle.cpp | 2 +- src/vehicle.h | 2 +- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/data/json/furniture_and_terrain/appliances.json b/data/json/furniture_and_terrain/appliances.json index 65405266edd67..bed2a80b0e7f7 100644 --- a/data/json/furniture_and_terrain/appliances.json +++ b/data/json/furniture_and_terrain/appliances.json @@ -588,7 +588,7 @@ "durability": 120, "description": "A power cord sticking out of an appliance. You need to plug it into a powered grid for the appliance to work properly.", "item": "power_cord", - "flags": [ "NO_INSTALL_HIDDEN", "NO_UNINSTALL", "UNMOUNT_ON_DAMAGE", "UNMOUNT_ON_MOVE", "POWER_TRANSFER" ], + "flags": [ "NO_INSTALL_HIDDEN", "NO_UNINSTALL", "UNMOUNT_ON_DAMAGE", "POWER_TRANSFER" ], "variants": [ { "symbols": "{", "symbols_broken": "s" } ] }, { diff --git a/data/json/vehicleparts/vehicle_parts.json b/data/json/vehicleparts/vehicle_parts.json index 155036b62fc36..f3fff96da0eaa 100644 --- a/data/json/vehicleparts/vehicle_parts.json +++ b/data/json/vehicleparts/vehicle_parts.json @@ -2381,7 +2381,7 @@ "description": "Thick copper cable with leads on either end. Attach one end to one vehicle and the other to another, and you can transfer electrical power between the two.", "item": "jumper_cable", "requirements": { "removal": { "time": "5 s" } }, - "flags": [ "NO_INSTALL_HIDDEN", "UNMOUNT_ON_DAMAGE", "UNMOUNT_ON_MOVE", "POWER_TRANSFER" ], + "flags": [ "NO_INSTALL_HIDDEN", "UNMOUNT_ON_DAMAGE", "POWER_TRANSFER" ], "breaks_into": [ { "item": "cable", "charges": [ 1, 10 ] }, { "item": "plastic_chunk", "count": [ 1, 2 ] } ], "variants": [ { "symbols": "{", "symbols_broken": "*" } ] }, @@ -2399,7 +2399,7 @@ "description": "A long orange extension cord for connecting appliances. Currently plugged in.", "item": "extension_cable", "requirements": { "removal": { "time": "5 s" } }, - "flags": [ "NO_INSTALL_HIDDEN", "UNMOUNT_ON_DAMAGE", "UNMOUNT_ON_MOVE", "POWER_TRANSFER" ], + "flags": [ "NO_INSTALL_HIDDEN", "UNMOUNT_ON_DAMAGE", "POWER_TRANSFER" ], "breaks_into": [ { "item": "cable", "charges": [ 1, 10 ] }, { "item": "plastic_chunk", "count": [ 1, 2 ] } ], "variants": [ { "symbols": "{", "symbols_broken": "*" } ] }, @@ -2417,7 +2417,7 @@ "description": "An extra long 30m orange extension cord for connecting outdoor appliances. Currently plugged in.", "item": "long_extension_cable", "requirements": { "removal": { "time": "5 s" } }, - "flags": [ "NO_INSTALL_HIDDEN", "UNMOUNT_ON_DAMAGE", "UNMOUNT_ON_MOVE", "POWER_TRANSFER" ], + "flags": [ "NO_INSTALL_HIDDEN", "UNMOUNT_ON_DAMAGE", "POWER_TRANSFER" ], "breaks_into": [ { "item": "cable", "charges": [ 1, 10 ] }, { "item": "plastic_chunk", "count": [ 1, 2 ] } ], "variants": [ { "symbols": "{", "symbols_broken": "*" } ] }, @@ -2435,7 +2435,7 @@ "//": "Epower for POWER_TRANSFER stuff is how much percentage-wise loss there is in transmission", "item": "jumper_cable_heavy", "requirements": { "removal": { "time": "5 s" } }, - "flags": [ "NO_INSTALL_HIDDEN", "UNMOUNT_ON_DAMAGE", "UNMOUNT_ON_MOVE", "POWER_TRANSFER" ], + "flags": [ "NO_INSTALL_HIDDEN", "UNMOUNT_ON_DAMAGE", "POWER_TRANSFER" ], "breaks_into": [ { "item": "wire", "count": [ 4, 8 ] }, { "item": "plastic_chunk", "count": [ 4, 8 ] } ], "variants": [ { "symbols": "{", "symbols_broken": "*" } ] }, @@ -2467,7 +2467,7 @@ "durability": 120, "item": "jumper_cable_debug", "requirements": { "removal": { "time": "5 s" } }, - "flags": [ "NO_INSTALL_HIDDEN", "UNMOUNT_ON_DAMAGE", "UNMOUNT_ON_MOVE", "POWER_TRANSFER" ], + "flags": [ "NO_INSTALL_HIDDEN", "UNMOUNT_ON_DAMAGE", "POWER_TRANSFER" ], "breaks_into": [ { "item": "jumper_cable_debug" } ], "variants": [ { "symbols": "{", "symbols_broken": "*" } ] }, diff --git a/data/mods/TEST_DATA/appliance.json b/data/mods/TEST_DATA/appliance.json index e51665ef08627..2f6b93ef4ae38 100644 --- a/data/mods/TEST_DATA/appliance.json +++ b/data/mods/TEST_DATA/appliance.json @@ -72,7 +72,7 @@ "durability": 120, "description": "A power cord sticking out of an appliance. You need to plug it in a powered grid for the appliance to work properly.", "item": "test_power_cord", - "flags": [ "NO_INSTALL_HIDDEN", "NO_UNINSTALL", "UNMOUNT_ON_DAMAGE", "UNMOUNT_ON_MOVE", "POWER_TRANSFER" ], + "flags": [ "NO_INSTALL_HIDDEN", "NO_UNINSTALL", "UNMOUNT_ON_DAMAGE", "POWER_TRANSFER" ], "variants": [ { "symbols": "{", "symbols_broken": "s" } ] }, { @@ -99,7 +99,7 @@ "description": "A long orange extension cord for connecting appliances. Currently plugged in.", "item": "test_extension_cable", "requirements": { "removal": { "time": "5 s" } }, - "flags": [ "NO_INSTALL_HIDDEN", "UNMOUNT_ON_DAMAGE", "UNMOUNT_ON_MOVE", "POWER_TRANSFER" ], + "flags": [ "NO_INSTALL_HIDDEN", "UNMOUNT_ON_DAMAGE", "POWER_TRANSFER" ], "breaks_into": [ { "item": "cable", "charges": [ 1, 10 ] }, { "item": "plastic_chunk", "count": [ 1, 2 ] } ], "variants": [ { "symbols": "{", "symbols_broken": "*" } ] }, @@ -137,7 +137,7 @@ "description": "A long orange extension cord for connecting appliances. Currently plugged in.", "item": "test_power_cord_25_loss", "requirements": { "removal": { "time": "5 s" } }, - "flags": [ "NO_INSTALL_HIDDEN", "UNMOUNT_ON_DAMAGE", "UNMOUNT_ON_MOVE", "POWER_TRANSFER" ], + "flags": [ "NO_INSTALL_HIDDEN", "UNMOUNT_ON_DAMAGE", "POWER_TRANSFER" ], "breaks_into": [ { "item": "cable", "charges": [ 1, 10 ] }, { "item": "plastic_chunk", "count": [ 1, 2 ] } ], "variants": [ { "symbols": "{", "symbols_broken": "*" } ] } diff --git a/src/vehicle.cpp b/src/vehicle.cpp index 6d2dbd84c1b82..8f8295fef3c7e 100644 --- a/src/vehicle.cpp +++ b/src/vehicle.cpp @@ -5927,7 +5927,7 @@ void vehicle::refresh( const bool remove_fakes ) if( vpi.has_flag( "FUNNEL" ) ) { funnels.push_back( p ); } - if( vpi.has_flag( "UNMOUNT_ON_MOVE" ) ) { + if( vpi.has_flag( "UNMOUNT_ON_MOVE" ) || vpi.has_flag( "POWER_TRANSFER" ) ) { loose_parts.push_back( p ); } if( !vpi.emissions.empty() || !vpi.exhaust.empty() ) { diff --git a/src/vehicle.h b/src/vehicle.h index 83a72b12715b4..c8b3009bed97b 100644 --- a/src/vehicle.h +++ b/src/vehicle.h @@ -2095,7 +2095,7 @@ class vehicle std::vector sails; // NOLINT(cata-serialize) std::vector funnels; // NOLINT(cata-serialize) std::vector emitters; // NOLINT(cata-serialize) - // Parts that will fall off the next time the vehicle moves. + // Parts that will fall off and cables that might disconnect when the vehicle moves. std::vector loose_parts; // NOLINT(cata-serialize) std::vector wheelcache; // NOLINT(cata-serialize) std::vector rotors; // NOLINT(cata-serialize) From 32a22b47a5e09eba20a32a4ea34b6d5f7115eb99 Mon Sep 17 00:00:00 2001 From: Kamayana Date: Wed, 12 Jul 2023 06:57:01 -0500 Subject: [PATCH 16/68] Let shed_loose_parts selectively exclude cables --- src/map.cpp | 2 +- src/vehicle.cpp | 9 ++++++--- src/vehicle.h | 4 ++-- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/map.cpp b/src/map.cpp index 22f2d13040154..af301b171bc40 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -1560,7 +1560,7 @@ bool map::displace_vehicle( vehicle &veh, const tripoint &dp, const bool adjust_ src_submap->vehicles.erase( src_submap_veh_it ); invalidate_max_populated_zlev( dst.z() ); } - veh.shed_loose_parts( &src, &dst, tripoint( -dp.xy(), -ramp_offset ) ); + veh.shed_loose_parts( true, &src, &dst, tripoint( -dp.xy(), -ramp_offset ) ); if( need_update ) { g->update_map( player_character ); } diff --git a/src/vehicle.cpp b/src/vehicle.cpp index 8f8295fef3c7e..03a9f7b05a6fb 100644 --- a/src/vehicle.cpp +++ b/src/vehicle.cpp @@ -5683,7 +5683,7 @@ void vehicle::gain_moves() const bool pl_control = player_in_control( get_player_character() ); if( is_moving() || is_falling ) { if( !loose_parts.empty() ) { - shed_loose_parts(); + shed_loose_parts( false ); } of_turn = 1 + of_turn_carry; const int vslowdown = slowdown( velocity ); @@ -6581,8 +6581,8 @@ void vehicle::remove_remote_part( const vehicle_part &vp_local ) const } } -void vehicle::shed_loose_parts( const tripoint_bub_ms *src, const tripoint_bub_ms *dst, - const tripoint drop_offset ) +void vehicle::shed_loose_parts( const bool can_shed_cables, const tripoint_bub_ms *src, + const tripoint_bub_ms *dst, const tripoint drop_offset ) { map &here = get_map(); // remove_part rebuilds the loose_parts vector, so iterate over a copy to preserve @@ -6596,6 +6596,9 @@ void vehicle::shed_loose_parts( const tripoint_bub_ms *src, const tripoint_bub_m continue; } if( vpi_loose.has_flag( "POWER_TRANSFER" ) ) { + if( !can_shed_cables ) { + continue; + } int distance = rl_dist( here.getabs( bub_part_pos( vp_loose ) ), vp_loose.target.second ); int max_dist = vp_loose.get_base().link_length( true ); if( src ) { diff --git a/src/vehicle.h b/src/vehicle.h index c8b3009bed97b..5bc4b22d91496 100644 --- a/src/vehicle.h +++ b/src/vehicle.h @@ -1803,8 +1803,8 @@ class vehicle void shift_parts( map &here, const point &delta ); bool shift_if_needed( map &here ); - void shed_loose_parts( const tripoint_bub_ms *src = nullptr, const tripoint_bub_ms *dst = nullptr, - const tripoint drop_offset = tripoint_zero ); + void shed_loose_parts( const bool can_shed_cables = true, const tripoint_bub_ms *src = nullptr, + const tripoint_bub_ms *dst = nullptr, const tripoint drop_offset = tripoint_zero ); /** * @name Vehicle turrets From 989afc1055c46f59b98b35a8aa7cf11840413dcb Mon Sep 17 00:00:00 2001 From: Kamayana Date: Wed, 12 Jul 2023 06:58:22 -0500 Subject: [PATCH 17/68] Format vehicle cable linking / tow cable linking to match each other --- src/iuse_actor.cpp | 65 +++++++++++++++++++++++++++++----------------- 1 file changed, 41 insertions(+), 24 deletions(-) diff --git a/src/iuse_actor.cpp b/src/iuse_actor.cpp index 36e7542285b60..49c191e53f7be 100644 --- a/src/iuse_actor.cpp +++ b/src/iuse_actor.cpp @@ -4752,34 +4752,52 @@ std::optional link_up_actor::link_to_veh_app( Character *p, item &it, } const itype_id item_id = it.typeId(); - bool vpid_found = false; - for( const vpart_info &vpi : vehicles::parts::get_all() ) { - if( vpi.base_item == item_id ) { - vpid_found = true; + vpart_id vpid = vpart_id::NULL_ID(); + for( const vpart_info &e : vehicles::parts::get_all() ) { + if( e.base_item == item_id ) { + vpid = e.id; break; } } - if( !vpid_found ) { - debugmsg( "item %s is not base item of any vehicle part! Using jumper_cable as a fallback", - item_id.c_str() ); + if( vpid.is_null() ) { + debugmsg( "item %s is not base item of any vehicle part!", item_id.c_str() ); + return std::nullopt; + } + + const point vcoords1 = it.link->t_mount; + const point vcoords2 = t_vp->mount(); + + const ret_val can_mount1 = prev_veh->can_mount( vcoords1, *vpid ); + if( !can_mount1.success() ) { + //~ %1$s - cable name, %2$s - the reason why it failed + p.add_msg_if_player( m_bad, _( "You can't attach the %1$s: %2$s" ), + it.type_name(), can_mount1.str() ); + return std::nullopt; + } + const ret_val can_mount2 = target_veh->can_mount( vcoords2, *vpid ); + if( !can_mount2.success() ) { + //~ %1$s - cable name, %2$s - the reason why it failed + p.add_msg_if_player( m_bad, _( "You can't attach the %1$s: %2$s" ), + it.type_name(), can_mount2.str() ); + return std::nullopt; } - const vpart_id vpid( vpid_found ? item_id.str() : "jumper_cable" ); - point vcoords = it.link->t_mount; - vehicle_part source_part( vpid, vpid_found ? item( it ) : item( "jumper_cable" ) ); - source_part.target.first = here.getabs( pnt ); - source_part.target.second = target_veh->global_square_location().raw(); - prev_veh->install_part( vcoords, std::move( source_part ) ); + vehicle_part prev_part( vpid, item( it ) ); + prev_part.target.first = here.getabs( pnt ); + prev_part.target.second = target_veh->global_square_location().raw(); + prev_veh->install_part( vcoords1, std::move( prev_part ) ); - vcoords = t_vp->mount(); - vehicle_part target_part( vpid, vpid_found ? item( it ) : item( "jumper_cable" ) ); - target_part.target.first = prev_target.first; - target_part.target.second = prev_target.second; - target_veh->install_part( vcoords, std::move( target_part ) ); + vehicle_part target_part( vpid, item( it ) ); + target_part.target.first = here.getabs( prev_veh->mount_to_tripoint( it.link->t_mount ) ); + target_part.target.second = prev_veh->global_square_location().raw(); + target_veh->install_part( vcoords2, std::move( target_part ) ); - p->add_msg_if_player( m_good, _( "You link up the %1$s and the %2$s." ), - prev_veh->name, target_veh->name ); + if( p->has_item( it ) ) { + //~ %1$s - first vehicle name, %2$s - second vehicle name - %3$s - cable name, + p->add_msg_if_player( m_good, _( "You connect %1$s and %2$s with the %3$s." ), + prev_veh->disp_name(), target_veh->disp_name(), it.type_name() ); + } p->moves -= move_cost; return 1; // Let the cable be destroyed. } @@ -4883,7 +4901,6 @@ std::optional link_up_actor::link_tow_cable( Character *p, item &it, it.type_name(), can_mount1.str() ); return std::nullopt; } - const ret_val can_mount2 = target_veh->can_mount( vcoords2, *vpid ); if( !can_mount2.success() ) { //~ %1$s - tow cable name, %2$s - the reason why it failed @@ -4903,9 +4920,9 @@ std::optional link_up_actor::link_tow_cable( Character *p, item &it, target_veh->install_part( vcoords2, std::move( target_part ) ); if( p->has_item( it ) ) { - //~ %1$s - tow cable name, %2$s - first vehicle name, %3$s - second vehicle name - p->add_msg_if_player( m_good, _( "You attach %1$s to %2$s and %3$s." ), - it.type_name(), prev_veh->disp_name(), target_veh->disp_name() ); + //~ %1$s - first vehicle name, %2$s - second vehicle name - %3$s - tow cable name, + p->add_msg_if_player( m_good, _( "You connect the %1$s and %2$s with the %3$s." ), + prev_veh->disp_name(), target_veh->disp_name(), it.type_name() ); } if( to_towing ) { target_veh->tow_data.set_towing( target_veh, prev_veh ); From 613728eaff251a36b1a9241befd95147604de35e Mon Sep 17 00:00:00 2001 From: Kamayana Date: Wed, 12 Jul 2023 06:59:14 -0500 Subject: [PATCH 18/68] Split link_length funcs between current and max --- src/inventory_ui.cpp | 6 +++--- src/item.cpp | 15 ++++++++------- src/item.h | 15 ++++++++++----- src/iuse_actor.cpp | 5 +++-- src/vehicle.cpp | 2 +- 5 files changed, 25 insertions(+), 18 deletions(-) diff --git a/src/inventory_ui.cpp b/src/inventory_ui.cpp index 79924cbbc34c4..0f5bc937684ea 100644 --- a/src/inventory_ui.cpp +++ b/src/inventory_ui.cpp @@ -89,7 +89,7 @@ item_name_t &get_cached_name( item const *it ) const std::string name = remove_color_tags( it->tname( 1, false, 0, true, false ) ); const std::string name_full = string_format( "%s%d%d", remove_color_tags( it->tname( 1, true, 0, true, false ) ), - it->link_length( true ), it->link_length() ); + it->max_link_length(), it->link_length() ); return item_name_cache .emplace( it, item_name_t{ name, name_full } ) .first->second; @@ -1278,7 +1278,7 @@ inventory_entry *inventory_column::add_entry( const inventory_entry &entry ) entry_item.parent_item() == found_entry_item.parent_item() && entry_item->is_collapsed() == found_entry_item->is_collapsed() && entry_item->link_length() == found_entry_item->link_length() && - entry_item->link_length( true ) == found_entry_item->link_length( true ) && + entry_item->max_link_length() == found_entry_item->max_link_length() && entry_item->display_stacked_with( *found_entry_item, preset.get_checking_components() ); } ); if( entry_with_loc != dest.end() ) { @@ -1396,7 +1396,7 @@ void inventory_column::collate() e->any_item()->is_favorite == outer->any_item()->is_favorite && e->any_item()->typeId() == outer->any_item()->typeId() && std::min( 0, e->any_item()->link_length() ) == std::min( 0, outer->any_item()->link_length() ) && - e->any_item()->link_length( true ) == outer->any_item()->link_length( true ) && + e->any_item()->max_link_length() == outer->any_item()->max_link_length() && ( !indent_entries() || e->any_item().parent_item() == outer->any_item().parent_item() ) && ( e->is_collation_header() || !e->chevron ) && diff --git a/src/item.cpp b/src/item.cpp index 55fbc9239194b..9e33111aa3924 100644 --- a/src/item.cpp +++ b/src/item.cpp @@ -6866,7 +6866,7 @@ std::string item::display_name( unsigned int quantity ) const link->max_length - link->length, link->max_length, extensions ), cable_color ) ); } } else if( !extensions.empty() ) { - cable = string_format( _( " (-/%1$d cable%2$s)" ), link_length( true ), extensions ); + cable = string_format( _( " (-/%1$d cable%2$s)" ), max_link_length(), extensions ); } } @@ -12929,15 +12929,16 @@ void item::set_link_traits() } } -int item::link_length( bool max ) const +int item::link_length() const { - if( !max ) { - return !link || link->has_no_links() ? -2 : - link->has_state( link_state::needs_reeling ) ? -1 : link->length; - } + return !link || + link->has_no_links() ? -2 : link->has_state( link_state::needs_reeling ) ? -1 : link->length; +} +int item::max_link_length() const +{ if( link ) { - return link->max_length; + return link->max_length != -1 ? link->max_length : type->maximum_charges(); } if( !type->can_use( "link_up" ) ) { return -2; diff --git a/src/item.h b/src/item.h index aa3748a6e2e47..3b82d873d1955 100644 --- a/src/item.h +++ b/src/item.h @@ -1463,12 +1463,17 @@ class item : public visitable void set_link_traits(); /** - * @param max If true, return the item's maximum cable length, including extensions, rather than its current length. Will not require active link_data, either. - * @return `-2` If the item has no attachments, or if `max` is true, if the item has no link_up action. - * @return `-1` If the item has link_data but needs reeling. - * @return Otherwise, return the link's current length, or if `max` is true, return the item's maximum possible link length. + * @return The link's current length. + * @return `-1` if the item has link_data but needs reeling. + * @return `-2` if the item has no active link. */ - int link_length( bool max = false ) const; + int link_length() const; + + /** + * @return The item's maximum possible link length, including extensions. Item doesn't need an active link. + * @return `-1` if the item doesn't have a link_up action. + */ + int max_link_length() const; /** * Brings a cable item back to its initial state. diff --git a/src/iuse_actor.cpp b/src/iuse_actor.cpp index 49c191e53f7be..80669f28baad9 100644 --- a/src/iuse_actor.cpp +++ b/src/iuse_actor.cpp @@ -4293,7 +4293,7 @@ void link_up_actor::load( const JsonObject &jo ) jo.read( "can_extend", can_extend ); } -void link_up_actor::info( const item &, std::vector &dump ) const +void link_up_actor::info( const item &it, std::vector &dump ) const { std::vector targets_strings; bool appliance = false; @@ -4334,7 +4334,8 @@ void link_up_actor::info( const item &, std::vector &dump ) const std::string cable_type_list = enumerate_as_string( cable_types, enumeration_conjunction::or_ ); dump.emplace_back( "TOOL", string_format( _( "Can extend: %s" ), cable_type_list ) ); } - dump.emplace_back( "TOOL", _( "Cable length: " ), cable_length ); + dump.emplace_back( "TOOL", _( "Cable length: " ), + cable_length != -1 ? cable_length : it.max_link_length() ); if( wattage != 0_W ) { std::string wattage_display = string_format( _( "%+4.1f W" ), units::to_milliwatt( wattage ) / 1000.f ); diff --git a/src/vehicle.cpp b/src/vehicle.cpp index 03a9f7b05a6fb..31ce8ec386f6f 100644 --- a/src/vehicle.cpp +++ b/src/vehicle.cpp @@ -6600,7 +6600,7 @@ void vehicle::shed_loose_parts( const bool can_shed_cables, const tripoint_bub_m continue; } int distance = rl_dist( here.getabs( bub_part_pos( vp_loose ) ), vp_loose.target.second ); - int max_dist = vp_loose.get_base().link_length( true ); + int max_dist = vp_loose.get_base().max_link_length(); if( src ) { vehicle *veh = here.find_vehicle( tripoint_abs_ms( vp_loose.target.second ) ); if( veh != nullptr ) { From 344b941ec109a16f4d4fa0b5c30132c94141153a Mon Sep 17 00:00:00 2001 From: Kamayana Date: Fri, 7 Jul 2023 03:27:33 -0500 Subject: [PATCH 19/68] Make efficiency a float to more easily handle extensions --- data/json/items/tool/cables.json | 4 ++-- doc/JSON_INFO.md | 6 +++++- src/item.cpp | 26 +++++++++++++++----------- src/item.h | 4 ++-- src/iuse_actor.h | 4 ++-- 5 files changed, 26 insertions(+), 18 deletions(-) diff --git a/data/json/items/tool/cables.json b/data/json/items/tool/cables.json index c4667381a784e..77dc3a101603e 100644 --- a/data/json/items/tool/cables.json +++ b/data/json/items/tool/cables.json @@ -110,7 +110,7 @@ "price_postapoc": 500, "max_charges": 6, "initial_charges": 6, - "use_action": { "type": "link_up", "menu_text": "Attach / Manage connections", "targets": [ "no_link", "vehicle_tow" ] }, + "use_action": { "type": "link_up", "menu_text": "Attach / Manage connections", "efficiency": 0.0, "targets": [ "no_link", "vehicle_tow" ] }, "qualities": [ [ "ROPE", 2 ] ], "flags": [ "CABLE_SPOOL", "TOW_CABLE", "SINGLE_USE" ], "melee_damage": { "bash": 6 } @@ -128,7 +128,7 @@ "use_action": { "type": "link_up", "menu_text": "Attach / Manage connections", - "efficiency": 1000, + "efficiency": 1.0, "targets": [ "no_link", "bio_cable", "ups", "solarpack", "vehicle_port", "vehicle_battery", "vehicle_tow" ], "can_extend": [ "ELECTRICAL_DEVICES" ] } diff --git a/doc/JSON_INFO.md b/doc/JSON_INFO.md index a1dbe59835211..71be1574cf453 100644 --- a/doc/JSON_INFO.md +++ b/doc/JSON_INFO.md @@ -4018,11 +4018,15 @@ 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. "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. "wattage": "60 W" // Maximum energy transfer rate of the cable in watts. ( Optional, defaults to "0 W" ) // A positive value will charge the device's chargeable batteries at the expense of the connected power grid. // A negative value will charge the connected electrical grid's batteries at the expense of the device's. // A value of 0 won't charge the device's batteries, but will still let the device operate off of the connected power grid. - "efficiency": 7 // one_in(this) chance to fail adding 1 charge every charge interval ( Optional, defaults to 7, which is around 85% efficiency ). + // If extended by other cables, will use the smallest wattage of any individual cable. + "efficiency": 0.85f // (this) out of 1.0 chance to successfully add 1 charge every charge interval ( Optional, defaults to 0.85f, AKA 85% efficiency ). + // A value less than 0.001 means the cable won't transfer any electricity at all. + // If extended by other cables, will use the product of all cable's efficiencies multiplied together. "menu_text": // Text displayed in the activation screen ( Optional, defaults to "Connect / Disconnect" ). "move_cost": // Move cost of attaching the cable ( Optional, defaults to 5 ). "targets": [ // Array of link_states that are valid connection points of the cable ( Optional, defaults to only allowing disconnection ). diff --git a/src/item.cpp b/src/item.cpp index 9e33111aa3924..2036e080b2df3 100644 --- a/src/item.cpp +++ b/src/item.cpp @@ -10367,7 +10367,7 @@ int item::ammo_remaining( const Character *carrier, bool cable_links ) const } // Cable connections - if( cable_links && link_length() >= 0 ) { + if( cable_links && link_length() >= 0 && link->efficiency >= 0.001f ) { if( link->t_veh_safe ) { ret += link->t_veh_safe->connected_battery_power_level().first; } else { @@ -10553,7 +10553,7 @@ int item::ammo_consume( int qty, const tripoint &pos, Character *carrier ) // Consume power from appliances/vehicles connected with cables if( link ) { - if( link->t_veh_safe ) { + if( link->t_veh_safe && link->efficiency >= 0.001f ) { qty = link->t_veh_safe->discharge_battery( qty, true ); } else { const optional_vpart_position vp = get_map().veh_at( link->t_abs_pos ); @@ -12925,7 +12925,7 @@ void item::set_link_traits() ( cable->get_use( "link_up" )->get_actor_ptr() ); link->max_length += actor->cable_length == -1 ? cable->type->maximum_charges() : actor->cable_length; - link->efficiency *= actor->efficiency == 0 ? 0 : 1.0 - 1.0 / actor->efficiency; + link->efficiency = link->efficiency < 0.001f ? 0.0f : link->efficiency * actor->efficiency; } } @@ -13109,6 +13109,9 @@ bool item::process_link( map &here, Character *carrier, const tripoint &pos ) int power_draw = 0; + if( link->efficiency < 0.001f ) { + return false; + } // Recharge or charge linked batteries power_draw -= charge_linked_batteries( *t_veh, turns_elapsed ); @@ -13131,7 +13134,8 @@ bool item::process_link( map &here, Character *carrier, const tripoint &pos ) int item::charge_linked_batteries( vehicle &linked_veh, int turns_elapsed ) { - if( link->wattage == 0 || turns_elapsed < 1 || link->charge_interval < 1 ) { + if( link->wattage == 0 || turns_elapsed < 1 || + link->charge_interval < 1 || link->efficiency < 0.001f ) { return link->wattage; } @@ -13148,28 +13152,28 @@ int item::charge_linked_batteries( vehicle &linked_veh, int turns_elapsed ) return 0; } - // Normally efficiency is a random chance to skip a charge, but if we're catching up from time - // spent ouside the reality bubble it should be applied as a percentage of the total instead. + // Normally efficiency is the chance to get a charge every charge_interval, but if we're catching up from + // time spent ouside the reality bubble it should be applied as a percentage of the total instead. bool short_time_passed = turns_elapsed <= link->charge_interval; - if( !calendar::once_every( time_duration::from_turns( link->charge_interval ) ) && - short_time_passed ) { + if( short_time_passed && + !calendar::once_every( time_duration::from_turns( link->charge_interval ) ) ) { return link->wattage; } // If a long time passed, multiply the total by the efficiency rather than cancelling a charge. int transfer_total = short_time_passed ? 1 : - ( turns_elapsed * 1.0f / link->charge_interval ) * ( 1.0 - 1.0 / link->efficiency ); + ( turns_elapsed * 1.0f / link->charge_interval ) * link->charge_interval; if( power_in ) { const int battery_deficit = linked_veh.discharge_battery( transfer_total, true ); // Around 85% efficient by default; a few of the discharges don't actually recharge - if( battery_deficit == 0 && !( short_time_passed && one_in( link->efficiency ) ) ) { + if( battery_deficit == 0 && !( short_time_passed && rng_float( 0.0, 1.0 ) > link->efficiency ) ) { ammo_set( itype_battery, ammo_remaining() + transfer_total ); } } else { // Around 85% efficient by default; a few of the discharges don't actually charge - if( !( short_time_passed && one_in( link->efficiency ) ) ) { + if( !( short_time_passed && rng_float( 0.0, 1.0 ) > link->efficiency ) ) { const int battery_surplus = linked_veh.charge_battery( transfer_total, true ); if( battery_surplus == 0 ) { ammo_set( itype_battery, ammo_remaining() - transfer_total ); diff --git a/src/item.h b/src/item.h index 3b82d873d1955..794e27312b631 100644 --- a/src/item.h +++ b/src/item.h @@ -1435,8 +1435,8 @@ class item : public visitable int max_length = 2; /// The cable's power capacity in watts, affects battery charge rate. Set during initialization. int wattage = 0; - /// one_in(this) chance to fail adding 1 charge. Set during initialization. - int efficiency = 7; + /// (this) out of 1.0 chance to successfully add 1 charge every charge interval. + float efficiency = 0.0f; /// The turn interval between charges. Set during initialization. int charge_interval = 0; diff --git a/src/iuse_actor.h b/src/iuse_actor.h index 32bc8fd6c5c08..69d65894c4c52 100644 --- a/src/iuse_actor.h +++ b/src/iuse_actor.h @@ -1036,8 +1036,8 @@ class link_up_actor : public iuse_actor int cable_length = -1; /** Charge rate in watts */ units::power wattage = 0_W; - /** one_in(this) chance to fail adding 1 charge */ - int efficiency = 7; + /** (this) out of 1.0 chance to successfully add 1 charge every charge interval */ + float efficiency = 0.85f; /** (Optional) The move cost to attach the cable. */ int move_cost = 5; /** (Optional) Text displayed in the activation screen, defaults to "Plug in / Unplug". */ From 23565bf0ec3c16d7b292941d0a11ffe38d95ad07 Mon Sep 17 00:00:00 2001 From: Kamayana Date: Wed, 12 Jul 2023 07:01:04 -0500 Subject: [PATCH 20/68] Adjust link_up item info --- src/iuse_actor.cpp | 129 ++++++++++++++++++++++++--------------------- 1 file changed, 68 insertions(+), 61 deletions(-) diff --git a/src/iuse_actor.cpp b/src/iuse_actor.cpp index 80669f28baad9..f52eb5250bcfe 100644 --- a/src/iuse_actor.cpp +++ b/src/iuse_actor.cpp @@ -4325,6 +4325,24 @@ void link_up_actor::info( const item &it, std::vector &dump ) const dump.emplace_back( "TOOL", string_format( _( "Can be plugged into: %s." ), targets_string ) ); } + + const bool no_extensions = it.cables().empty(); + item dummy( it ); + dummy.link = cata::make_value(); + dummy.set_link_traits(); + + std::string length_all_info = string_format( _( "Cable length: %d" ), + dummy.max_link_length() ); + std::string length_solo_info = no_extensions ? "" : string_format( _( " (%d without extensions)" ), + cable_length != -1 ? cable_length : dummy.type->maximum_charges() ); + dump.emplace_back( "TOOL", length_all_info, length_solo_info ); + + if( wattage != 0_W ) { + //~ Power in Watts. %+4.1f is a 4 digit number with 1 decimal point (ex: 4737.3 W) + dump.emplace_back( "TOOL", _( "Wattage: " ), string_format( _( "%+4.1f W" ), + units::to_milliwatt( wattage ) / 1000.f ) ); + } + if( !can_extend.empty() ) { std::vector cable_types; for( const std::string &cable_type : can_extend ) { @@ -4334,17 +4352,6 @@ void link_up_actor::info( const item &it, std::vector &dump ) const std::string cable_type_list = enumerate_as_string( cable_types, enumeration_conjunction::or_ ); dump.emplace_back( "TOOL", string_format( _( "Can extend: %s" ), cable_type_list ) ); } - dump.emplace_back( "TOOL", _( "Cable length: " ), - cable_length != -1 ? cable_length : it.max_link_length() ); - if( wattage != 0_W ) { - std::string wattage_display = string_format( _( "%+4.1f W" ), - units::to_milliwatt( wattage ) / 1000.f ); - if( wattage > 0_W ) { - dump.emplace_back( "TOOL", _( "Charge rate: " ), wattage_display ); - } else { - dump.emplace_back( "TOOL", _( "Discharge rate: " ), wattage_display ); - } - } } std::string link_up_actor::get_name() const @@ -4752,14 +4759,14 @@ std::optional link_up_actor::link_to_veh_app( Character *p, item &it, } } - const itype_id item_id = it.typeId(); + const itype_id item_id = it.typeId(); vpart_id vpid = vpart_id::NULL_ID(); for( const vpart_info &e : vehicles::parts::get_all() ) { if( e.base_item == item_id ) { vpid = e.id; - break; - } + break; } + } if( vpid.is_null() ) { debugmsg( "item %s is not base item of any vehicle part!", item_id.c_str() ); @@ -4772,14 +4779,14 @@ std::optional link_up_actor::link_to_veh_app( Character *p, item &it, const ret_val can_mount1 = prev_veh->can_mount( vcoords1, *vpid ); if( !can_mount1.success() ) { //~ %1$s - cable name, %2$s - the reason why it failed - p.add_msg_if_player( m_bad, _( "You can't attach the %1$s: %2$s" ), + p->add_msg_if_player( m_bad, _( "You can't attach the %1$s: %2$s" ), it.type_name(), can_mount1.str() ); return std::nullopt; } const ret_val can_mount2 = target_veh->can_mount( vcoords2, *vpid ); if( !can_mount2.success() ) { //~ %1$s - cable name, %2$s - the reason why it failed - p.add_msg_if_player( m_bad, _( "You can't attach the %1$s: %2$s" ), + p->add_msg_if_player( m_bad, _( "You can't attach the %1$s: %2$s" ), it.type_name(), can_mount2.str() ); return std::nullopt; } @@ -4878,58 +4885,58 @@ std::optional link_up_actor::link_tow_cable( Character *p, item &it, return std::nullopt; }; - const itype_id item_id = it.typeId(); - vpart_id vpid = vpart_id::NULL_ID(); - for( const vpart_info &e : vehicles::parts::get_all() ) { - if( e.base_item == item_id ) { - vpid = e.id; - break; - } + const itype_id item_id = it.typeId(); + vpart_id vpid = vpart_id::NULL_ID(); + for( const vpart_info &e : vehicles::parts::get_all() ) { + if( e.base_item == item_id ) { + vpid = e.id; + break; } + } - if( vpid.is_null() ) { - debugmsg( "item %s is not base item of any vehicle part!", item_id.c_str() ); - return std::nullopt; - } + if( vpid.is_null() ) { + debugmsg( "item %s is not base item of any vehicle part!", item_id.c_str() ); + return std::nullopt; + } - const point vcoords1 = it.link->t_mount; - const point vcoords2 = t_vp->mount(); + const point vcoords1 = it.link->t_mount; + const point vcoords2 = t_vp->mount(); - const ret_val can_mount1 = prev_veh->can_mount( vcoords1, *vpid ); - if( !can_mount1.success() ) { - //~ %1$s - tow cable name, %2$s - the reason why it failed - p->add_msg_if_player( m_bad, _( "You can't attach the %1$s: %2$s" ), - it.type_name(), can_mount1.str() ); - return std::nullopt; - } - const ret_val can_mount2 = target_veh->can_mount( vcoords2, *vpid ); - if( !can_mount2.success() ) { - //~ %1$s - tow cable name, %2$s - the reason why it failed - p->add_msg_if_player( m_bad, _( "You can't attach the %1$s: %2$s" ), - it.type_name(), can_mount2.str() ); - return std::nullopt; - } + const ret_val can_mount1 = prev_veh->can_mount( vcoords1, *vpid ); + if( !can_mount1.success() ) { + //~ %1$s - tow cable name, %2$s - the reason why it failed + p->add_msg_if_player( m_bad, _( "You can't attach the %1$s: %2$s" ), + it.type_name(), can_mount1.str() ); + return std::nullopt; + } + const ret_val can_mount2 = target_veh->can_mount( vcoords2, *vpid ); + if( !can_mount2.success() ) { + //~ %1$s - tow cable name, %2$s - the reason why it failed + p->add_msg_if_player( m_bad, _( "You can't attach the %1$s: %2$s" ), + it.type_name(), can_mount2.str() ); + return std::nullopt; + } - vehicle_part prev_part( vpid, item( it ) ); - prev_part.target.first = here.getabs( pnt ); - prev_part.target.second = target_veh->global_square_location().raw(); - prev_veh->install_part( vcoords1, std::move( prev_part ) ); + vehicle_part prev_part( vpid, item( it ) ); + prev_part.target.first = here.getabs( pnt ); + prev_part.target.second = target_veh->global_square_location().raw(); + prev_veh->install_part( vcoords1, std::move( prev_part ) ); - vehicle_part target_part( vpid, item( it ) ); - target_part.target.first = here.getabs( prev_veh->mount_to_tripoint( it.link->t_mount ) ); - target_part.target.second = prev_veh->global_square_location().raw(); - target_veh->install_part( vcoords2, std::move( target_part ) ); + vehicle_part target_part( vpid, item( it ) ); + target_part.target.first = here.getabs( prev_veh->mount_to_tripoint( it.link->t_mount ) ); + target_part.target.second = prev_veh->global_square_location().raw(); + target_veh->install_part( vcoords2, std::move( target_part ) ); - if( p->has_item( it ) ) { - //~ %1$s - first vehicle name, %2$s - second vehicle name - %3$s - tow cable name, - p->add_msg_if_player( m_good, _( "You connect the %1$s and %2$s with the %3$s." ), - prev_veh->disp_name(), target_veh->disp_name(), it.type_name() ); - } - if( to_towing ) { - target_veh->tow_data.set_towing( target_veh, prev_veh ); - } else { - prev_veh->tow_data.set_towing( prev_veh, target_veh ); - } + if( p->has_item( it ) ) { + //~ %1$s - first vehicle name, %2$s - second vehicle name - %3$s - tow cable name, + p->add_msg_if_player( m_good, _( "You connect the %1$s and %2$s with the %3$s." ), + prev_veh->disp_name(), target_veh->disp_name(), it.type_name() ); + } + if( to_towing ) { + target_veh->tow_data.set_towing( target_veh, prev_veh ); + } else { + prev_veh->tow_data.set_towing( prev_veh, target_veh ); + } p->moves -= move_cost; return 1; // Let the cable be destroyed. } From f4c8e82dc2f39ed7c626b05bde7e972e3d2c682d Mon Sep 17 00:00:00 2001 From: Kamayana Date: Wed, 12 Jul 2023 07:01:44 -0500 Subject: [PATCH 21/68] Add BATTERY flag for battery vehicle parts --- data/json/vehicleparts/battery.json | 20 +++++++++++--------- data/json/vehicleparts/vp_flags.json | 4 ++++ src/iuse_actor.cpp | 26 +++++++++++--------------- src/veh_type.cpp | 1 + src/veh_type.h | 1 + src/vehicle_part.cpp | 2 +- 6 files changed, 29 insertions(+), 25 deletions(-) diff --git a/data/json/vehicleparts/battery.json b/data/json/vehicleparts/battery.json index 284b8a8efd17b..01370f3d4de8e 100644 --- a/data/json/vehicleparts/battery.json +++ b/data/json/vehicleparts/battery.json @@ -22,6 +22,7 @@ "repair": { "skills": [ [ "mechanics", 3 ] ], "time": "600 s", "using": [ [ "soldering_standard", 5 ] ] } }, "damage_reduction": { "bash": 10 }, + "flags": [ "BATTERY" ], "variants": [ { "symbols": "o", "symbols_broken": "#" } ] }, { @@ -77,7 +78,7 @@ "removal": { "skills": [ [ "mechanics", 0 ] ], "time": "10 s", "using": [ ] } }, "description": "A battery for storing electrical power, and discharging it to power electrical devices built into the vehicle. This one is mounted on a quick release framework to allow it to be easily swapped, though it still weighs so much that a lifting tool of some kind is necessary for most people.", - "flags": [ "NEEDS_BATTERY_MOUNT" ] + "flags": [ "BATTERY", "NEEDS_BATTERY_MOUNT" ] }, { "id": "medium_storage_battery", @@ -132,6 +133,7 @@ "removal": { "skills": [ [ "mechanics", 2 ] ], "time": "5 m", "using": "vehicle_weld_removal" } }, "damage_reduction": { "bash": 10 }, + "flags": [ "BATTERY" ], "variants": [ { "symbols": "O", "symbols_broken": "#" } ] }, { @@ -145,7 +147,7 @@ "removal": { "skills": [ [ "mechanics", 0 ] ], "time": "10 s", "using": [ ] } }, "description": "A battery for storing electrical power, and discharging it to power electrical devices built into the vehicle. This one is mounted on a quick release framework to allow it to be easily swapped, though it still weighs so much that a lifting tool of some kind is necessary for some people.", - "flags": [ "NEEDS_BATTERY_MOUNT" ] + "flags": [ "BATTERY", "NEEDS_BATTERY_MOUNT" ] }, { "id": "car_light_minus_battery_cell", @@ -161,7 +163,7 @@ "durability": 10, "folded_volume": "10 ml", "breaks_into": [ { "item": "e_scrap", "prob": 10 } ], - "flags": [ "NEEDS_HANDHELD_BATTERY_MOUNT", "NO_REPAIR" ] + "flags": [ "BATTERY", "NEEDS_HANDHELD_BATTERY_MOUNT", "NO_REPAIR" ] }, { "id": "car_light_battery_cell", @@ -177,7 +179,7 @@ "durability": 20, "folded_volume": "25ml", "breaks_into": [ { "item": "e_scrap", "prob": 10 } ], - "flags": [ "NEEDS_HANDHELD_BATTERY_MOUNT", "NO_REPAIR" ] + "flags": [ "BATTERY", "NEEDS_HANDHELD_BATTERY_MOUNT", "NO_REPAIR" ] }, { "id": "car_light_plus_battery_cell", @@ -193,7 +195,7 @@ "durability": 20, "folded_volume": "35ml", "breaks_into": [ { "item": "e_scrap", "prob": 10 } ], - "flags": [ "NEEDS_HANDHELD_BATTERY_MOUNT", "NO_REPAIR" ] + "flags": [ "BATTERY", "NEEDS_HANDHELD_BATTERY_MOUNT", "NO_REPAIR" ] }, { "id": "car_medium_battery_cell", @@ -209,7 +211,7 @@ "durability": 30, "folded_volume": "450 ml", "breaks_into": [ { "item": "light_battery_cell", "count": [ 0, 3 ] }, { "item": "scrap", "count": [ 1, 4 ] } ], - "flags": [ "NEEDS_HANDHELD_BATTERY_MOUNT", "NO_REPAIR" ] + "flags": [ "BATTERY", "NEEDS_HANDHELD_BATTERY_MOUNT", "NO_REPAIR" ] }, { "id": "car_medium_plus_battery_cell", @@ -225,7 +227,7 @@ "durability": 30, "folded_volume": "525 ml", "breaks_into": [ { "item": "light_plus_battery_cell", "count": [ 0, 3 ] }, { "item": "scrap", "count": [ 1, 4 ] } ], - "flags": [ "NEEDS_HANDHELD_BATTERY_MOUNT", "NO_REPAIR" ] + "flags": [ "BATTERY", "NEEDS_HANDHELD_BATTERY_MOUNT", "NO_REPAIR" ] }, { "id": "car_heavy_battery_cell", @@ -241,7 +243,7 @@ "durability": 40, "folded_volume": "1225 ml", "breaks_into": [ { "item": "medium_battery_cell", "count": [ 0, 1 ] }, { "item": "scrap", "count": [ 1, 4 ] } ], - "flags": [ "NEEDS_HANDHELD_BATTERY_MOUNT", "NO_REPAIR" ] + "flags": [ "BATTERY", "NEEDS_HANDHELD_BATTERY_MOUNT", "NO_REPAIR" ] }, { "id": "car_heavy_plus_battery_cell", @@ -257,6 +259,6 @@ "durability": 40, "folded_volume": "1500 ml", "breaks_into": [ { "item": "medium_battery_cell", "count": [ 0, 1 ] }, { "item": "scrap", "count": [ 1, 4 ] } ], - "flags": [ "NEEDS_HANDHELD_BATTERY_MOUNT", "NO_REPAIR" ] + "flags": [ "BATTERY", "NEEDS_HANDHELD_BATTERY_MOUNT", "NO_REPAIR" ] } ] diff --git a/data/json/vehicleparts/vp_flags.json b/data/json/vehicleparts/vp_flags.json index ca0e5492585f6..dbe7201ba8455 100644 --- a/data/json/vehicleparts/vp_flags.json +++ b/data/json/vehicleparts/vp_flags.json @@ -273,6 +273,10 @@ "id": "AUTOPILOT", "type": "json_flag" }, + { + "id": "BATTERY", + "type": "json_flag" + }, { "id": "BATTERY_MOUNT", "type": "json_flag" diff --git a/src/iuse_actor.cpp b/src/iuse_actor.cpp index f52eb5250bcfe..3c0dd61942d99 100644 --- a/src/iuse_actor.cpp +++ b/src/iuse_actor.cpp @@ -4673,19 +4673,8 @@ std::optional link_up_actor::link_to_veh_app( Character *p, item &it, } if( to_ports ) { return ovp.avail_part_with_feature( "CABLE_PORTS" ) || ovp.avail_part_with_feature( "APPLIANCE" ); - } else { - if( ovp.avail_part_with_feature( "APPLIANCE" ) ) { - return true; - } - const vehicle &veh = ovp->vehicle(); - for( const int p : veh.parts_at_relative( ovp->mount(), /* use_cache = */ false ) ) { - const vehicle_part &vp_here = veh.part( p ); - if( vp_here.is_battery() && !vp_here.is_broken() ) { - return true; - } - } } - return false; + return ovp.avail_part_with_feature( "BATTERY" ) || ovp.avail_part_with_feature( "APPLIANCE" ); }; const std::optional pnt_ = choose_adjacent_highlight( _( "Attach the cable where?" ), "", can_link, false, false ); @@ -4715,16 +4704,23 @@ std::optional link_up_actor::link_to_veh_app( Character *p, item &it, !it.link->has_state( link_state::vehicle_battery ) ) { // Starting a new connection to a vehicle or connecting a cable CBM to a vehicle. + std::optional t_vp_ref( t_vp.avail_part_with_feature( "APPLIANCE" ) ); + if( to_ports ) { + t_vp_ref = t_vp.avail_part_with_feature( "CABLE_PORTS" ); + } else { + t_vp_ref = t_vp.avail_part_with_feature( "BATTERY" ); + } + if( it.link->has_no_links() ) { - p->add_msg_if_player( _( "You connect the %1$s to the %2$s." ), it.type_name(), - t_vp->vehicle().name ); + p->add_msg_if_player( _( "You connect the %1$s to the %2$s." ), it.type_name(), t_vp_ref->part().name( false ) ); } else if( it.link->has_state( link_state::bio_cable ) ) { - p->add_msg_if_player( m_good, _( "You are now plugged into the %s." ), t_vp->vehicle().name ); + p->add_msg_if_player( m_good, _( "You are now plugged into the %s." ), t_vp_ref->part().name( false ) ); it.link->s_state = link_state::bio_cable; } else { debugmsg( "Failed to connect the %s, it tried to make an invalid connection!", it.tname() ); return std::nullopt; } + it.link->t_state = to_ports ? link_state::vehicle_port : link_state::vehicle_battery; it.link->t_abs_pos = here.getglobal( t_vp->vehicle().global_pos3() ); it.link->t_mount = t_vp->mount(); diff --git a/src/veh_type.cpp b/src/veh_type.cpp index 06f06e853f9c6..6e7c80cda06f4 100644 --- a/src/veh_type.cpp +++ b/src/veh_type.cpp @@ -132,6 +132,7 @@ static const std::unordered_map vpart_bitflag_map = { "TURRET_CONTROLS", VPFLAG_TURRET_CONTROLS }, { "ROOF", VPFLAG_ROOF }, { "CABLE_PORTS", VPFLAG_CABLE_PORTS }, + { "BATTERY", VPFLAG_BATTERY } }; static std::map vpart_migrations; diff --git a/src/veh_type.h b/src/veh_type.h index 23d5e162965ea..8567137689093 100644 --- a/src/veh_type.h +++ b/src/veh_type.h @@ -107,6 +107,7 @@ enum vpart_bitflags : int { VPFLAG_TURRET_CONTROLS, VPFLAG_ROOF, VPFLAG_CABLE_PORTS, + VPFLAG_BATTERY, NUM_VPFLAGS }; diff --git a/src/vehicle_part.cpp b/src/vehicle_part.cpp index 3fdd266af821f..514bdea05810b 100644 --- a/src/vehicle_part.cpp +++ b/src/vehicle_part.cpp @@ -559,7 +559,7 @@ bool vehicle_part::contains_liquid() const bool vehicle_part::is_battery() const { - return base.is_magazine() && base.ammo_types().count( ammo_battery ); + return info().has_flag( VPFLAG_BATTERY ) || base.is_magazine() && base.ammo_types().count( ammo_battery ); } bool vehicle_part::is_reactor() const From 9ced3534964c4371d29207c65f9ac2ded89b1ec8 Mon Sep 17 00:00:00 2001 From: Kamayana Date: Wed, 12 Jul 2023 07:02:49 -0500 Subject: [PATCH 22/68] Astyle --- src/iuse_actor.cpp | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/src/iuse_actor.cpp b/src/iuse_actor.cpp index 3c0dd61942d99..3f4c19922c152 100644 --- a/src/iuse_actor.cpp +++ b/src/iuse_actor.cpp @@ -4534,7 +4534,7 @@ std::optional link_up_actor::link_up( Character *p, item &it ) const return 0; } else { p->add_msg_if_player( m_info, string_format( is_cable_item ? _( "You detach the %s." ) : - _( "You gather the %s's cable up with it." ), it.type_name() ) ); + _( "You gather the %s's cable up with it." ), it.type_name() ) ); } return 0; } @@ -4712,9 +4712,11 @@ std::optional link_up_actor::link_to_veh_app( Character *p, item &it, } if( it.link->has_no_links() ) { - p->add_msg_if_player( _( "You connect the %1$s to the %2$s." ), it.type_name(), t_vp_ref->part().name( false ) ); + p->add_msg_if_player( _( "You connect the %1$s to the %2$s." ), it.type_name(), + t_vp_ref->part().name( false ) ); } else if( it.link->has_state( link_state::bio_cable ) ) { - p->add_msg_if_player( m_good, _( "You are now plugged into the %s." ), t_vp_ref->part().name( false ) ); + p->add_msg_if_player( m_good, _( "You are now plugged into the %s." ), + t_vp_ref->part().name( false ) ); it.link->s_state = link_state::bio_cable; } else { debugmsg( "Failed to connect the %s, it tried to make an invalid connection!", it.tname() ); @@ -4776,14 +4778,14 @@ std::optional link_up_actor::link_to_veh_app( Character *p, item &it, if( !can_mount1.success() ) { //~ %1$s - cable name, %2$s - the reason why it failed p->add_msg_if_player( m_bad, _( "You can't attach the %1$s: %2$s" ), - it.type_name(), can_mount1.str() ); + it.type_name(), can_mount1.str() ); return std::nullopt; } const ret_val can_mount2 = target_veh->can_mount( vcoords2, *vpid ); if( !can_mount2.success() ) { //~ %1$s - cable name, %2$s - the reason why it failed p->add_msg_if_player( m_bad, _( "You can't attach the %1$s: %2$s" ), - it.type_name(), can_mount2.str() ); + it.type_name(), can_mount2.str() ); return std::nullopt; } @@ -4798,9 +4800,9 @@ std::optional link_up_actor::link_to_veh_app( Character *p, item &it, target_veh->install_part( vcoords2, std::move( target_part ) ); if( p->has_item( it ) ) { - //~ %1$s - first vehicle name, %2$s - second vehicle name - %3$s - cable name, + //~ %1$s - first vehicle name, %2$s - second vehicle name - %3$s - cable name, p->add_msg_if_player( m_good, _( "You connect %1$s and %2$s with the %3$s." ), - prev_veh->disp_name(), target_veh->disp_name(), it.type_name() ); + prev_veh->disp_name(), target_veh->disp_name(), it.type_name() ); } p->moves -= move_cost; return 1; // Let the cable be destroyed. @@ -4902,14 +4904,14 @@ std::optional link_up_actor::link_tow_cable( Character *p, item &it, if( !can_mount1.success() ) { //~ %1$s - tow cable name, %2$s - the reason why it failed p->add_msg_if_player( m_bad, _( "You can't attach the %1$s: %2$s" ), - it.type_name(), can_mount1.str() ); + it.type_name(), can_mount1.str() ); return std::nullopt; } const ret_val can_mount2 = target_veh->can_mount( vcoords2, *vpid ); if( !can_mount2.success() ) { //~ %1$s - tow cable name, %2$s - the reason why it failed p->add_msg_if_player( m_bad, _( "You can't attach the %1$s: %2$s" ), - it.type_name(), can_mount2.str() ); + it.type_name(), can_mount2.str() ); return std::nullopt; } @@ -4924,9 +4926,9 @@ std::optional link_up_actor::link_tow_cable( Character *p, item &it, target_veh->install_part( vcoords2, std::move( target_part ) ); if( p->has_item( it ) ) { - //~ %1$s - first vehicle name, %2$s - second vehicle name - %3$s - tow cable name, + //~ %1$s - first vehicle name, %2$s - second vehicle name - %3$s - tow cable name, p->add_msg_if_player( m_good, _( "You connect the %1$s and %2$s with the %3$s." ), - prev_veh->disp_name(), target_veh->disp_name(), it.type_name() ); + prev_veh->disp_name(), target_veh->disp_name(), it.type_name() ); } if( to_towing ) { target_veh->tow_data.set_towing( target_veh, prev_veh ); @@ -5002,7 +5004,7 @@ std::optional link_up_actor::link_extend_cable( Character *p, item &it ) co extended->process( get_map(), p, p->pos() ); 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->type_name(), extension->type_name() ); + _( "You extend the %1$s's cable with the %2$s." ), extended->type_name(), extension->type_name() ); p->i_rem( extension ); p->moves -= move_cost; return 0; @@ -5036,7 +5038,7 @@ std::optional link_up_actor::remove_extensions( Character *p, item &it ) co } } p->add_msg_if_player( _( "You disconnect the %1$s from the %2$s." ), - cable_main_copy.type_name(), it.type_name() ); + cable_main_copy.type_name(), it.type_name() ); it.get_contents().clear_pockets_if( []( item_pocket const & pocket ) { return pocket.is_type( item_pocket::pocket_type::CABLE ); From bd9d70ac5ff90cb909ef4c802bea803d498e6bc4 Mon Sep 17 00:00:00 2001 From: Kamayana Date: Sun, 9 Jul 2023 19:14:06 -0500 Subject: [PATCH 23/68] Revert "wattage" back to "charge_rate", as I scrapped my plans for that change --- data/json/items/tool/cables.json | 7 ++++++- data/json/items/tool/electronics.json | 20 ++++++++++---------- data/json/items/tool/lighting.json | 4 ++-- data/json/items/tool/workshop.json | 6 +++--- data/json/items/tool_armor.json | 4 ---- doc/JSON_INFO.md | 15 +++++++-------- src/item.cpp | 10 +++++----- src/item.h | 10 ++++------ src/iuse_actor.cpp | 6 +++--- src/iuse_actor.h | 4 ++-- src/savegame_json.cpp | 4 ++-- 11 files changed, 44 insertions(+), 46 deletions(-) diff --git a/data/json/items/tool/cables.json b/data/json/items/tool/cables.json index 77dc3a101603e..250c1bd76c6b6 100644 --- a/data/json/items/tool/cables.json +++ b/data/json/items/tool/cables.json @@ -110,7 +110,12 @@ "price_postapoc": 500, "max_charges": 6, "initial_charges": 6, - "use_action": { "type": "link_up", "menu_text": "Attach / Manage connections", "efficiency": 0.0, "targets": [ "no_link", "vehicle_tow" ] }, + "use_action": { + "type": "link_up", + "menu_text": "Attach / Manage connections", + "efficiency": 0.0, + "targets": [ "no_link", "vehicle_tow" ] + }, "qualities": [ [ "ROPE", 2 ] ], "flags": [ "CABLE_SPOOL", "TOW_CABLE", "SINGLE_USE" ], "melee_damage": { "bash": 6 } diff --git a/data/json/items/tool/electronics.json b/data/json/items/tool/electronics.json index 33a09e2bc8c8d..03a6d05b2d260 100644 --- a/data/json/items/tool/electronics.json +++ b/data/json/items/tool/electronics.json @@ -165,7 +165,7 @@ "need_charges": 1, "need_charges_msg": "The tablet batteries are dead." }, - { "type": "link_up", "cable_length": 4, "wattage": "10 W" } + { "type": "link_up", "cable_length": 4, "charge_rate": "10 W" } ], "flags": [ "WATCH", "WATER_BREAK", "ELECTRONIC" ], "pocket_data": [ @@ -203,7 +203,7 @@ "msg": "You power down the screen.", "target": "eink_tablet_pc" }, - { "type": "link_up", "cable_length": 4, "wattage": "10 W" } + { "type": "link_up", "cable_length": 4, "charge_rate": "10 W" } ], "//": "LIGHT_10 is the bare minimum for reading without penalties", "flags": [ "LIGHT_10", "TRADER_AVOID", "WATCH", "WATER_BREAK", "ELECTRONIC" ] @@ -364,7 +364,7 @@ "need_charges_msg": "The laptop's batteries need more charge.", "type": "transform" }, - { "type": "link_up", "cable_length": 5, "wattage": "140 W" } + { "type": "link_up", "cable_length": 5, "charge_rate": "140 W" } ], "flags": [ "WATCH", "WATER_BREAK", "ELECTRONIC" ], "pocket_data": [ @@ -410,7 +410,7 @@ "menu_text": "Turn off", "type": "transform" }, - { "type": "link_up", "cable_length": 5, "wattage": "140 W" } + { "type": "link_up", "cable_length": 5, "charge_rate": "140 W" } ], "flags": [ "WATCH", "LIGHT_10", "TRADER_AVOID", "WATER_BREAK", "ELECTRONIC" ] }, @@ -428,7 +428,7 @@ "symbol": ";", "color": "dark_gray", "ammo": [ "battery" ], - "use_action": [ "MP3", { "type": "link_up", "cable_length": 4, "wattage": "5 W" } ], + "use_action": [ "MP3", { "type": "link_up", "cable_length": 4, "charge_rate": "5 W" } ], "charges_per_use": 1, "flags": [ "WATER_BREAK_ACTIVE", "ELECTRONIC" ], "pocket_data": [ @@ -448,7 +448,7 @@ "description": "This mp3 player is turned on and playing some great tunes, raising your morale steadily while on your person. It runs through batteries quickly; you can turn it off by using it. It also obscures your hearing.", "power_draw": "1 W", "revert_to": "mp3", - "use_action": [ "MP3_ON", { "type": "link_up", "cable_length": 4, "wattage": "5 W" } ], + "use_action": [ "MP3_ON", { "type": "link_up", "cable_length": 4, "charge_rate": "5 W" } ], "flags": [ "TRADER_AVOID", "WATER_BREAK", "ELECTRONIC" ] }, { @@ -550,7 +550,7 @@ "type": "link_up", "//": "Based on the Nintendo Switch's power adapter", "cable_length": 4, - "wattage": "39 W" + "charge_rate": "39 W" } ], "pocket_data": [ @@ -602,7 +602,7 @@ "need_charges_msg": "The smartphone's charge is too low.", "type": "transform" }, - { "type": "link_up", "cable_length": 3, "wattage": "20 W" } + { "type": "link_up", "cable_length": 3, "charge_rate": "20 W" } ], "flags": [ "WATCH", "ALARMCLOCK", "USE_UPS", "NO_UNLOAD", "NO_RELOAD", "WATER_BREAK", "CALORIES_INTAKE", "ELECTRONIC" ], "pocket_data": [ @@ -630,7 +630,7 @@ "EINKTABLETPC", "EBOOKSAVE", "EBOOKREAD", - { "type": "link_up", "cable_length": 3, "wattage": "20 W" } + { "type": "link_up", "cable_length": 3, "charge_rate": "20 W" } ], "extend": { "flags": [ "TRADER_AVOID" ] } }, @@ -660,7 +660,7 @@ "menu_text": "Turn off flashlight", "type": "transform" }, - { "type": "link_up", "cable_length": 3, "wattage": "20 W" } + { "type": "link_up", "cable_length": 3, "charge_rate": "20 W" } ], "extend": { "flags": [ "LIGHT_20", "CHARGEDIM", "TRADER_AVOID" ] } }, diff --git a/data/json/items/tool/lighting.json b/data/json/items/tool/lighting.json index bda1ecadaa21c..3af4c7ed15a66 100644 --- a/data/json/items/tool/lighting.json +++ b/data/json/items/tool/lighting.json @@ -156,7 +156,7 @@ "need_charges": 1, "need_charges_msg": "The lantern has no batteries." }, - { "type": "link_up", "cable_length": 2, "wattage": "20 W" } + { "type": "link_up", "cable_length": 2, "charge_rate": "20 W" } ], "flags": [ "RADIO_MODABLE", "RADIO_INVOKE_PROC", "ALLOWS_REMOTE_USE", "WATER_BREAK" ], "pocket_data": [ @@ -178,7 +178,7 @@ "revert_to": "electric_lantern", "use_action": [ { "menu_text": "Turn off", "type": "transform", "target": "electric_lantern", "msg": "You turn the lamp off." }, - { "type": "link_up", "cable_length": 2, "wattage": "20 W" } + { "type": "link_up", "cable_length": 2, "charge_rate": "20 W" } ], "flags": [ "RADIO_MODABLE", "RADIO_INVOKE_PROC", "LIGHT_15", "TRADER_AVOID", "ALLOWS_REMOTE_USE" ] }, diff --git a/data/json/items/tool/workshop.json b/data/json/items/tool/workshop.json index 26a3817cd2491..aef01c2b60ce3 100644 --- a/data/json/items/tool/workshop.json +++ b/data/json/items/tool/workshop.json @@ -1289,7 +1289,7 @@ "cost_scaling": 0.1, "move_cost": 500 }, - { "type": "link_up", "cable_length": 7, "wattage": "3000 W" } + { "type": "link_up", "cable_length": 7, "charge_rate": "3000 W" } ], "flags": [ "ALLOWS_REMOTE_USE", "WATER_BREAK" ], "pocket_data": [ @@ -1360,7 +1360,7 @@ "cost_scaling": 0.1, "move_cost": 1000 }, - { "type": "link_up", "cable_length": 7, "wattage": "1500 W" } + { "type": "link_up", "cable_length": 7, "charge_rate": "1500 W" } ], "flags": [ "ALLOWS_REMOTE_USE", "WATER_BREAK" ], "pocket_data": [ @@ -1433,7 +1433,7 @@ "cost_scaling": 0.1, "move_cost": 500 }, - { "type": "link_up", "cable_length": 7, "wattage": "3000 W" } + { "type": "link_up", "cable_length": 7, "charge_rate": "3000 W" } ], "flags": [ "ALLOWS_REMOTE_USE", "WATER_BREAK" ], "pocket_data": [ diff --git a/data/json/items/tool_armor.json b/data/json/items/tool_armor.json index ad0941052ca18..985bf3ae6cdae 100644 --- a/data/json/items/tool_armor.json +++ b/data/json/items/tool_armor.json @@ -3505,8 +3505,6 @@ }, { "type": "link_up", - "menu_text": "Plug in / Unplug", - "ammo_scale": 0, "cable_length": 3, "charge_rate": "110 W" } @@ -3546,8 +3544,6 @@ }, { "type": "link_up", - "menu_text": "Plug in / Unplug", - "ammo_scale": 0, "cable_length": 3, "charge_rate": "110 W" } diff --git a/doc/JSON_INFO.md b/doc/JSON_INFO.md index 71be1574cf453..1efa22b21e574 100644 --- a/doc/JSON_INFO.md +++ b/doc/JSON_INFO.md @@ -4019,15 +4019,14 @@ The contents of use_action fields can either be a string indicating a built-in f "type": "link_up", // Connect item to a vehicle or appliance, such as plugging a chargeable device into a power source. "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. - "wattage": "60 W" // Maximum energy transfer rate of the cable in watts. ( Optional, defaults to "0 W" ) - // A positive value will charge the device's chargeable batteries at the expense of the connected power grid. - // A negative value will charge the connected electrical grid's batteries at the expense of the device's. - // A value of 0 won't charge the device's batteries, but will still let the device operate off of the connected power grid. - // If extended by other cables, will use the smallest wattage of any individual cable. + "charge_rate": "60 W" // The charge rate of the plugged-in device's batteries in watts. ( Optional, defaults to "0 W" ) + // A positive value will charge the device's chargeable batteries at the expense of the connected power grid. + // A negative value will charge the connected electrical grid's batteries at the expense of the device's. + // A value of 0 won't charge the device's batteries, but will still let the device operate off of the connected power grid. "efficiency": 0.85f // (this) out of 1.0 chance to successfully add 1 charge every charge interval ( Optional, defaults to 0.85f, AKA 85% efficiency ). - // A value less than 0.001 means the cable won't transfer any electricity at all. - // If extended by other cables, will use the product of all cable's efficiencies multiplied together. - "menu_text": // Text displayed in the activation screen ( Optional, defaults to "Connect / Disconnect" ). + // A value less than 0.001 means the cable won't transfer any electricity at all. + // If extended by other cables, will use the product of all cable's efficiencies multiplied together. + "menu_text": // Text displayed in the activation screen ( Optional, defaults to Plug in / Manage cables" ). "move_cost": // Move cost of attaching the cable ( Optional, defaults to 5 ). "targets": [ // Array of link_states that are valid connection points of the cable ( Optional, defaults to only allowing disconnection ). "no_link", // Must be included to allow letting the player manually disconnect the cable. diff --git a/src/item.cpp b/src/item.cpp index 2036e080b2df3..1b2b0c4ebbf27 100644 --- a/src/item.cpp +++ b/src/item.cpp @@ -13134,9 +13134,9 @@ bool item::process_link( map &here, Character *carrier, const tripoint &pos ) int item::charge_linked_batteries( vehicle &linked_veh, int turns_elapsed ) { - if( link->wattage == 0 || turns_elapsed < 1 || + if( link->charge_rate == 0 || turns_elapsed < 1 || link->charge_interval < 1 || link->efficiency < 0.001f ) { - return link->wattage; + return link->charge_rate; } if( !is_battery() ) { @@ -13146,7 +13146,7 @@ int item::charge_linked_batteries( vehicle &linked_veh, int turns_elapsed ) } } - const bool power_in = link->wattage > 0; + const bool power_in = link->charge_rate > 0; if( power_in ? ammo_remaining() >= ammo_capacity( ammo_battery ) : ammo_remaining() <= 0 ) { return 0; @@ -13158,7 +13158,7 @@ int item::charge_linked_batteries( vehicle &linked_veh, int turns_elapsed ) if( short_time_passed && !calendar::once_every( time_duration::from_turns( link->charge_interval ) ) ) { - return link->wattage; + return link->charge_rate; } // If a long time passed, multiply the total by the efficiency rather than cancelling a charge. @@ -13185,7 +13185,7 @@ int item::charge_linked_batteries( vehicle &linked_veh, int turns_elapsed ) } } } - return link->wattage; + return link->charge_rate; } bool item::reset_link( Character *p, const bool loose_message, const tripoint cable_position ) diff --git a/src/item.h b/src/item.h index 794e27312b631..6b1181d92c175 100644 --- a/src/item.h +++ b/src/item.h @@ -1434,7 +1434,7 @@ class item : public visitable /// The maximum length of the cable. Set during initialization. int max_length = 2; /// The cable's power capacity in watts, affects battery charge rate. Set during initialization. - int wattage = 0; + int charge_rate = 0; /// (this) out of 1.0 chance to successfully add 1 charge every charge interval. float efficiency = 0.0f; /// The turn interval between charges. Set during initialization. @@ -1454,11 +1454,9 @@ class item : public visitable void deserialize( const JsonObject &data ); }; /** - * @brief Sets max_length, wattage, and efficiency of a link, taking cable extensions into account. + * @brief Sets max_length and efficiency of a link, taking cable extensions into account. * @brief max_length is set to the sum of all cable lengths. - * @brief wattage is set to the smallest wattage of any cable. * @brief efficiency is set to the product of all efficiencies multiplied together. - * @brief charge_interval is set to how many turns it takes for wattage to add up to 1 kW - the unit batteries use. 0 means batteries won't be charged. */ void set_link_traits(); @@ -1487,8 +1485,8 @@ class item : public visitable /** * @brief Exchange power between an item's batteries and the vehicle/appliance it's linked to. - * @brief A positive link.wattage will charge the item at the expense of the vehicle, - * while a negative link.wattage will charge the vehicle at the expense of the item. + * @brief A positive link.charge_rate will charge the item at the expense of the vehicle, + * while a negative link.charge_rate will charge the vehicle at the expense of the item. * * @param linked_veh The vehicle the item is connected to. * @param turns_elapsed The number of turns the link has spent outside the reality bubble. Default 1. diff --git a/src/iuse_actor.cpp b/src/iuse_actor.cpp index 3f4c19922c152..dfadb68389ad4 100644 --- a/src/iuse_actor.cpp +++ b/src/iuse_actor.cpp @@ -4285,7 +4285,7 @@ std::unique_ptr link_up_actor::clone() const void link_up_actor::load( const JsonObject &jo ) { jo.read( "cable_length", cable_length ); - jo.read( "wattage", wattage ); + jo.read( "charge_rate", charge_rate ); jo.read( "efficiency", efficiency ); jo.read( "move_cost", move_cost ); jo.read( "menu_text", menu_text ); @@ -4337,10 +4337,10 @@ void link_up_actor::info( const item &it, std::vector &dump ) const cable_length != -1 ? cable_length : dummy.type->maximum_charges() ); dump.emplace_back( "TOOL", length_all_info, length_solo_info ); - if( wattage != 0_W ) { + if( charge_rate != 0_W ) { //~ Power in Watts. %+4.1f is a 4 digit number with 1 decimal point (ex: 4737.3 W) dump.emplace_back( "TOOL", _( "Wattage: " ), string_format( _( "%+4.1f W" ), - units::to_milliwatt( wattage ) / 1000.f ) ); + units::to_milliwatt( charge_rate ) / 1000.f ) ); } if( !can_extend.empty() ) { diff --git a/src/iuse_actor.h b/src/iuse_actor.h index 69d65894c4c52..2d79335814b18 100644 --- a/src/iuse_actor.h +++ b/src/iuse_actor.h @@ -1035,12 +1035,12 @@ class link_up_actor : public iuse_actor /** Maximum length of the cable. At -1, will use the item type's max_charges. */ int cable_length = -1; /** Charge rate in watts */ - units::power wattage = 0_W; + units::power charge_rate = 0_W; /** (this) out of 1.0 chance to successfully add 1 charge every charge interval */ float efficiency = 0.85f; /** (Optional) The move cost to attach the cable. */ int move_cost = 5; - /** (Optional) Text displayed in the activation screen, defaults to "Plug in / Unplug". */ + /** (Optional) Text displayed in the activation screen, defaults to "Plug in / Manage cables". */ translation menu_text; std::set targets = { link_state::no_link, link_state::vehicle_port }; diff --git a/src/savegame_json.cpp b/src/savegame_json.cpp index 46ba011993dfb..51c27e917c59c 100644 --- a/src/savegame_json.cpp +++ b/src/savegame_json.cpp @@ -2869,7 +2869,7 @@ void item::link_data::serialize( JsonOut &jsout ) const jsout.member( "link_length", length ); jsout.member( "link_max_length", max_length ); jsout.member( "link_last_processed", last_processed ); - jsout.member( "link_charge_rate", wattage ); + jsout.member( "link_charge_rate", charge_rate ); jsout.member( "link_charge_interval", charge_interval ); jsout.member( "link_charge_efficiency", efficiency ); jsout.end_object(); @@ -2886,7 +2886,7 @@ void item::link_data::deserialize( const JsonObject &data ) data.read( "link_length", length ); data.read( "link_max_length", max_length ); data.read( "link_last_processed", last_processed ); - data.read( "link_charge_rate", wattage ); + data.read( "link_charge_rate", charge_rate ); data.read( "link_charge_efficiency", efficiency ); data.read( "link_charge_interval", charge_interval ); } From fdcfe46d397e417ee37cbe24dd38a6bb73f7d262 Mon Sep 17 00:00:00 2001 From: Kamayana Date: Wed, 12 Jul 2023 07:03:48 -0500 Subject: [PATCH 24/68] Remove the separate link_up function - the use for it was scrapped --- src/iuse_actor.cpp | 5 ----- src/iuse_actor.h | 1 - 2 files changed, 6 deletions(-) diff --git a/src/iuse_actor.cpp b/src/iuse_actor.cpp index dfadb68389ad4..586339021dcde 100644 --- a/src/iuse_actor.cpp +++ b/src/iuse_actor.cpp @@ -4373,11 +4373,6 @@ std::optional link_up_actor::use( Character *p, item &it, bool t, const tri return std::nullopt; } - return link_up( p, it ); -} - -std::optional link_up_actor::link_up( Character *p, item &it ) const -{ const bool is_cable_item = it.has_flag( flag_CABLE_SPOOL ); const std::string cable_name = is_cable_item ? it.type_name() : string_format( _( "%s's cable" ), it.type_name() ); diff --git a/src/iuse_actor.h b/src/iuse_actor.h index 2d79335814b18..ee467f7cf7a10 100644 --- a/src/iuse_actor.h +++ b/src/iuse_actor.h @@ -1046,7 +1046,6 @@ class link_up_actor : public iuse_actor std::set targets = { link_state::no_link, link_state::vehicle_port }; std::set can_extend = {}; - std::optional link_up( Character *p, item &it ) const; std::optional link_to_veh_app( Character *p, item &it, const bool to_ports ) const; std::optional link_tow_cable( Character *p, item &it, const bool to_towing ) const; std::optional link_extend_cable( Character *p, item &it ) const; From 17b1dd89b8243089ba3db51a2dd0b272b0375ce4 Mon Sep 17 00:00:00 2001 From: Kamayana Date: Wed, 12 Jul 2023 07:05:51 -0500 Subject: [PATCH 25/68] Remove AUTO_DELETE_CABLE, replace with NO_DROP --- data/json/flags.json | 4 ---- data/json/items/tool/cables.json | 2 +- data/mods/TEST_DATA/appliance.json | 4 ++-- doc/JSON_FLAGS.md | 1 - src/activity_actor.cpp | 2 +- src/flag.cpp | 1 - src/flag.h | 1 - src/item.cpp | 6 +++--- src/vehicle.cpp | 4 ++-- 9 files changed, 9 insertions(+), 16 deletions(-) diff --git a/data/json/flags.json b/data/json/flags.json index ba11e3b2d4520..a4cf2afd83ae2 100644 --- a/data/json/flags.json +++ b/data/json/flags.json @@ -1241,10 +1241,6 @@ "id": "CABLE_SPOOL", "type": "json_flag" }, - { - "id": "AUTO_DELETE_CABLE", - "type": "json_flag" - }, { "id": "CAMERA_PRO", "type": "json_flag" diff --git a/data/json/items/tool/cables.json b/data/json/items/tool/cables.json index 250c1bd76c6b6..ca81f2fde1629 100644 --- a/data/json/items/tool/cables.json +++ b/data/json/items/tool/cables.json @@ -17,7 +17,7 @@ "max_charges": 3, "initial_charges": 3, "use_action": { "type": "link_up", "menu_text": "Plug in / Manage connections", "targets": [ "vehicle_port" ] }, - "flags": [ "CABLE_SPOOL", "AUTO_DELETE_CABLE", "SINGLE_USE" ], + "flags": [ "CABLE_SPOOL", "NO_DROP", "SINGLE_USE" ], "melee_damage": { "bash": 2 } }, { diff --git a/data/mods/TEST_DATA/appliance.json b/data/mods/TEST_DATA/appliance.json index 2f6b93ef4ae38..914c06e52c01f 100644 --- a/data/mods/TEST_DATA/appliance.json +++ b/data/mods/TEST_DATA/appliance.json @@ -56,7 +56,7 @@ "max_charges": 3, "initial_charges": 3, "use_action": { "type": "link_up", "targets": [ "no_link", "vehicle_port", "vehicle_battery" ] }, - "flags": [ "CABLE_SPOOL", "AUTO_DELETE_CABLE" ], + "flags": [ "CABLE_SPOOL", "NO_DROP" ], "melee_damage": { "bash": 2 } }, { @@ -120,7 +120,7 @@ "max_charges": 3, "initial_charges": 3, "use_action": { "type": "link_up", "targets": [ "no_link", "vehicle_port", "vehicle_battery" ] }, - "flags": [ "CABLE_SPOOL", "AUTO_DELETE_CABLE", "SINGLE_USE" ], + "flags": [ "CABLE_SPOOL", "NO_DROP", "SINGLE_USE" ], "melee_damage": { "bash": 2 } }, { diff --git a/doc/JSON_FLAGS.md b/doc/JSON_FLAGS.md index ee4dfedd571f9..96f8fb5ba2c22 100644 --- a/doc/JSON_FLAGS.md +++ b/doc/JSON_FLAGS.md @@ -1340,7 +1340,6 @@ Melee flags are fully compatible with tool flags, and vice versa. - ```ACT_ON_RANGED_HIT``` The item should activate when thrown or fired, then immediately get processed if it spawns on the ground. - ```ALLOWS_REMOTE_USE``` This item can be activated or reloaded from adjacent tile without picking it up. -- ```AUTO_DELETE_CABLE``` This cable is automatically created and deleted by their appliance or device and the player should never be able to directly interact with it. - ```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. diff --git a/src/activity_actor.cpp b/src/activity_actor.cpp index a88667e0e7fc1..59b2a6ed46b72 100644 --- a/src/activity_actor.cpp +++ b/src/activity_actor.cpp @@ -5056,7 +5056,7 @@ void reel_cable_activity_actor::finish( player_activity &act, Character &who ) who.add_msg_if_player( m_info, string_format( cable->has_flag( flag_CABLE_SPOOL ) ? _( "You reel in the %s and wind it up." ) : _( "You reel in the %s's cable and wind it up." ), cable->type_name() ) ); - if( cable->has_flag( flag_AUTO_DELETE_CABLE ) ) { + if( cable->has_flag( flag_NO_DROP ) ) { cable.remove_item(); } act.set_to_null(); diff --git a/src/flag.cpp b/src/flag.cpp index d0802bc199922..ecd6118cfe22d 100644 --- a/src/flag.cpp +++ b/src/flag.cpp @@ -33,7 +33,6 @@ const flag_id flag_ALLOWS_REMOTE_USE( "ALLOWS_REMOTE_USE" ); const flag_id flag_ALWAYS_TWOHAND( "ALWAYS_TWOHAND" ); const flag_id flag_ANIMAL_PRODUCT( "ANIMAL_PRODUCT" ); const flag_id flag_AURA( "AURA" ); -const flag_id flag_AUTO_DELETE_CABLE( "AUTO_DELETE_CABLE" ); const flag_id flag_BAROMETER( "BAROMETER" ); const flag_id flag_BASH_IMMUNE( "BASH_IMMUNE" ); const flag_id flag_BELTED( "BELTED" ); diff --git a/src/flag.h b/src/flag.h index d14c47e25d8f8..66a61bf7de092 100644 --- a/src/flag.h +++ b/src/flag.h @@ -43,7 +43,6 @@ extern const flag_id flag_ALWAYS_TWOHAND; extern const flag_id flag_ANIMAL_PRODUCT; extern const flag_id flag_OLD_CURRENCY; extern const flag_id flag_AURA; -extern const flag_id flag_AUTO_DELETE_CABLE; extern const flag_id flag_BAROMETER; extern const flag_id flag_BASH_IMMUNE; extern const flag_id flag_BELTED; diff --git a/src/item.cpp b/src/item.cpp index 1b2b0c4ebbf27..98ff28a3c0d20 100644 --- a/src/item.cpp +++ b/src/item.cpp @@ -13124,7 +13124,7 @@ bool item::process_link( map &here, Character *carrier, const tripoint &pos ) if( power_draw != 0 ) { t_veh->linked_item_epower_this_turn += units::from_milliwatt( power_draw ); } - } else if( has_flag( flag_AUTO_DELETE_CABLE ) ) { + } else if( has_flag( flag_NO_DROP ) ) { debugmsg( "%s shouldn't exist outside of an item or vehicle part.", tname() ); return true; } @@ -13191,7 +13191,7 @@ int item::charge_linked_batteries( vehicle &linked_veh, int turns_elapsed ) bool item::reset_link( Character *p, const bool loose_message, const tripoint cable_position ) { if( !link ) { - return has_flag( flag_AUTO_DELETE_CABLE ); + return has_flag( flag_NO_DROP ); } // Cables that need reeling should be reset with a reel_cable_activity_actor instead. if( link->has_state( link_state::needs_reeling ) ) { @@ -13226,7 +13226,7 @@ bool item::reset_link( Character *p, const bool loose_message, const tripoint ca } link.reset(); - return has_flag( flag_AUTO_DELETE_CABLE ); + return has_flag( flag_NO_DROP ); } bool item::process_linked_item( Character *carrier, const tripoint & /*pos*/, diff --git a/src/vehicle.cpp b/src/vehicle.cpp index 31ce8ec386f6f..66a58ec74c9b6 100644 --- a/src/vehicle.cpp +++ b/src/vehicle.cpp @@ -6624,7 +6624,7 @@ void vehicle::shed_loose_parts( const bool can_shed_cables, const tripoint_bub_m remove_remote_part( vp_loose ); } const item drop = vp_loose.properties_to_item(); - if( !magic && !drop.has_flag( flag_AUTO_DELETE_CABLE ) ) { + if( !magic ) { here.add_item_or_charges( global_part_pos3( vp_loose ) + drop_offset, drop ); } @@ -7094,7 +7094,7 @@ int vehicle::damage_direct( map &here, vehicle_part &vp, int dmg, const damage_t } else { part_as_item.set_damage( vpi.base_item.obj().damage_max() - 1 ); } - if( !magic && !part_as_item.has_flag( flag_AUTO_DELETE_CABLE ) ) { + if( !magic ) { here.add_item_or_charges( vppos, part_as_item ); } if( !g || &get_map() != &here ) { From 182c1c6f9f8e47b8172f2b0f70230f6ee1f1b038 Mon Sep 17 00:00:00 2001 From: Kamayana Date: Tue, 11 Jul 2023 07:03:09 -0500 Subject: [PATCH 26/68] Show a cable's linked vehicle in the interaction menu --- src/iuse_actor.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/iuse_actor.cpp b/src/iuse_actor.cpp index 586339021dcde..fab2ea7b71208 100644 --- a/src/iuse_actor.cpp +++ b/src/iuse_actor.cpp @@ -4389,8 +4389,10 @@ std::optional link_up_actor::use( Character *p, item &it, bool t, const tri uilist link_menu; if( !is_cable_item || !it.link || it.link->has_no_links() ) { - // This cable item or device doesn't have any connections yet. - link_menu.text = string_format( _( "Attaching the %s:" ), cable_name ); + // This is either a device or a cable item without any connections. + link_menu.text = string_format( _( "What to do with the %s?%s" ), + cable_name, it.link && it.link->t_veh_safe ? + string_format( _( "\nAttached to: %s" ), it.link->t_veh_safe->name ) : "" ); if( targets.count( link_state::vehicle_port ) > 0 ) { link_menu.addentry( 0, has_loose_end, -1, _( "Attach to vehicle controls or appliance" ) ); } @@ -4435,8 +4437,9 @@ std::optional link_up_actor::use( Character *p, item &it, bool t, const tri } else if( it.link->has_state( link_state::vehicle_tow ) ) { // Cables that started a tow can finish one or detach; nothing else. + link_menu.text = string_format( _( "What to do with the %s?%s" ), cable_name, it.link->t_veh_safe ? + string_format( _( "\nAttached to: %s" ), it.link->t_veh_safe->name ) : "" ); - link_menu.text = string_format( _( "Attaching the %s:" ), cable_name ); link_menu.addentry( 10, has_loose_end && it.link->t_state == link_state::vehicle_tow, -1, _( "Attach loose end to towing vehicle" ) ); link_menu.addentry( 11, has_loose_end && it.link->s_state == link_state::vehicle_tow, -1, @@ -4452,7 +4455,8 @@ std::optional link_up_actor::use( Character *p, item &it, bool t, const tri } else { // This is a cable item with one connection already: - link_menu.text = string_format( _( "Attaching the %s's loose end:" ), cable_name ); + link_menu.text = string_format( _( "What to do with the %s?%s" ), cable_name, it.link->t_veh_safe ? + string_format( _( "\nAttached to: %s" ), it.link->t_veh_safe->name ) : "" ); // TODO: Allow plugging UPSes and Solar Packs into more than just bionics. // There is already code to support setting up a link, but none for actual functionality. From 19a3f5cc19bef625149ca8300c8dbb05d02aeeb1 Mon Sep 17 00:00:00 2001 From: Kamayana Date: Thu, 20 Jul 2023 14:56:55 -0500 Subject: [PATCH 27/68] Add a vehicle part inspect action to disconnect cables attached to it. Replace appliance's UNPLUG action with this --- data/raw/keybindings.json | 7 ---- data/raw/keybindings/vehicle.json | 9 ++++- src/item.cpp | 51 +++++++++++++++++++----- src/item.h | 6 ++- src/iuse_actor.cpp | 9 +++++ src/veh_appliance.cpp | 37 ------------------ src/veh_appliance.h | 6 --- src/vehicle.h | 5 ++- src/vehicle_part.cpp | 3 +- src/vehicle_use.cpp | 64 ++++++++++++++++++++++++++++++- 10 files changed, 131 insertions(+), 66 deletions(-) diff --git a/data/raw/keybindings.json b/data/raw/keybindings.json index 34b2d8439ec96..1f4c56c057892 100644 --- a/data/raw/keybindings.json +++ b/data/raw/keybindings.json @@ -1659,13 +1659,6 @@ "name": "Plug in appliance", "bindings": [ { "input_method": "keyboard_any", "key": "g" } ] }, - { - "type": "keybinding", - "id": "UNPLUG", - "category": "APP_INTERACT", - "name": "Unplug power connections", - "bindings": [ { "input_method": "keyboard_any", "key": "u" } ] - }, { "type": "keybinding", "id": "INSTALL", diff --git a/data/raw/keybindings/vehicle.json b/data/raw/keybindings/vehicle.json index 4550bfc1a961a..4c557de293819 100644 --- a/data/raw/keybindings/vehicle.json +++ b/data/raw/keybindings/vehicle.json @@ -137,7 +137,7 @@ "type": "keybinding", "category": "VEHICLE", "name": "Fill container with water from faucet", - "bindings": [ { "input_method": "keyboard_any", "key": "c" } ] + "bindings": [ { "input_method": "keyboard_any", "key": "F" }, { "input_method": "keyboard_code", "key": "f", "mod": [ "shift" ] } ] }, { "id": "FAUCET_DRINK", @@ -405,6 +405,13 @@ "name": "Set turret targeting modes", "bindings": [ { "input_method": "keyboard_any", "key": "t" } ] }, + { + "id": "DISCONNECT_CABLES", + "type": "keybinding", + "category": "VEHICLE", + "name": "Disconnect attached cables", + "bindings": [ { "input_method": "keyboard_any", "key": "c" } ] + }, { "id": "ARCADE", "type": "keybinding", diff --git a/src/item.cpp b/src/item.cpp index 98ff28a3c0d20..b5514498b7b45 100644 --- a/src/item.cpp +++ b/src/item.cpp @@ -12992,7 +12992,7 @@ bool item::process_link( map &here, Character *carrier, const tripoint &pos ) if( ( link->t_state == link_state::no_link && link->s_state != link_state::vehicle_tow ) || link->t_state == link_state::bio_cable ) { if( carrier == nullptr ) { - return reset_link( nullptr, true, pos ); + return reset_link( nullptr, -1, true, pos ); } return false; } @@ -13035,9 +13035,8 @@ bool item::process_link( map &here, Character *carrier, const tripoint &pos ) // Re-establish vehicle pointer if it got lost or if this item just got loaded. if( !link->t_veh_safe ) { vehicle *found_veh = here.find_vehicle( link->t_abs_pos ); - //return false; if( !found_veh ) { - return reset_link( carrier, true, pos ); + return reset_link( carrier, -2, true, pos ); } if( debug_mode ) { add_msg_debug( debugmode::DF_IUSE, "Re-established link of %s to %s.", type_name(), @@ -13083,9 +13082,18 @@ bool item::process_link( map &here, Character *carrier, const tripoint &pos ) } if( link_vp_index == -1 ) { // The part with cable ports was lost, so disconnect the cable. - return reset_link( carrier, true, pos ); + return reset_link( carrier, -2, true, pos ); } + if( link->last_processed <= t_veh->part( link_vp_index ).last_disconnected ) { + add_msg_if_player_sees( pos, m_warning, string_format( _( "You detached the %s." ), type_name() ) ); + return reset_link( carrier, -2 ); + } + t_veh->part( link_vp_index ).set_flag( vp_flag::linked_flag ); + + int turns_elapsed = to_turns( calendar::turn - link->last_processed ); + link->last_processed = calendar::turn; + // Set the new absolute position to the vehicle's origin. tripoint t_veh_bub_pos = t_veh->global_pos3(); tripoint_abs_ms new_t_abs_pos = here.getglobal( t_veh_bub_pos ); @@ -13098,15 +13106,12 @@ bool item::process_link( map &here, Character *carrier, const tripoint &pos ) if( check_length ) { link->length = rl_dist( pos, t_veh_bub_pos + t_veh->part( link_vp_index ).precalc[0] ); if( is_cable_too_long() ) { - return reset_link( carrier ); + return reset_link( carrier, link_vp_index ); } } // Extra behaviors for the cabled item. if( !is_cable_item ) { - int turns_elapsed = to_turns( calendar::turn - link->last_processed ); - link->last_processed = calendar::turn; - int power_draw = 0; if( link->efficiency < 0.001f ) { @@ -13188,7 +13193,8 @@ int item::charge_linked_batteries( vehicle &linked_veh, int turns_elapsed ) return link->charge_rate; } -bool item::reset_link( Character *p, const bool loose_message, const tripoint cable_position ) +bool item::reset_link( Character *p, int vpart_index, + const bool loose_message, const tripoint cable_position ) { if( !link ) { return has_flag( flag_NO_DROP ); @@ -13198,6 +13204,33 @@ bool item::reset_link( Character *p, const bool loose_message, const tripoint ca return false; } + if( vpart_index != -2 && link->t_veh_safe ) { + if( vpart_index == -1 ) { + vehicle *t_veh = link->t_veh_safe.get(); + // Find the vp_part index the cable is linked to. + if( link->t_state == link_state::vehicle_port ) { + for( int idx : t_veh->cable_ports ) { + if( t_veh->part( idx ).mount == link->t_mount ) { + vpart_index = idx; + break; + } + } + } else if( link->t_state == link_state::vehicle_battery ) { + for( int idx : t_veh->batteries ) { + if( t_veh->part( idx ).mount == link->t_mount ) { + vpart_index = idx; + break; + } + } + } else if( link->t_state == link_state::vehicle_tow || link->s_state == link_state::vehicle_tow ) { + vpart_index = t_veh->part_at( t_veh->coord_translate( link->t_mount ) ); + } + } + if( vpart_index != -1 ) { + link->t_veh_safe->part( vpart_index ).remove_flag( vp_flag::linked_flag ); + } + } + const bool is_cable_item = has_flag( flag_CABLE_SPOOL ); if( loose_message ) { diff --git a/src/item.h b/src/item.h index 6b1181d92c175..0265b910fc5f7 100644 --- a/src/item.h +++ b/src/item.h @@ -1476,12 +1476,14 @@ class item : public visitable /** * Brings a cable item back to its initial state. * @param p Set to character that's holding the linked item, nullptr if none. + * @param vpart_index The index of the vehicle part the cable is attached to, so it can have `linked_flag` removed. + * @param * At -1, the default, this function will look up the index itself. At -2, skip modifying the part's flags entirely. * @param loose_message If there should be a notification that the link was disconnected. * @param cable_position Position of the linked item, used to determine if the player can see the link becoming loose. * @return True if the cable should be deleted. */ - bool reset_link( Character *p = nullptr, bool loose_message = false, - tripoint cable_position = tripoint_zero ); + bool reset_link( Character *p = nullptr, int vpart_index = -1, + bool loose_message = false, tripoint cable_position = tripoint_zero ); /** * @brief Exchange power between an item's batteries and the vehicle/appliance it's linked to. diff --git a/src/iuse_actor.cpp b/src/iuse_actor.cpp index fab2ea7b71208..d81591b7ca366 100644 --- a/src/iuse_actor.cpp +++ b/src/iuse_actor.cpp @@ -131,6 +131,7 @@ static const itype_id itype_char_smoker( "char_smoker" ); static const itype_id itype_fire( "fire" ); static const itype_id itype_stock_none( "stock_none" ); static const itype_id itype_syringe( "syringe" ); +static const itype_id itype_power_cord( "power_cord" ); static const mon_flag_str_id mon_flag_INTERIOR_AMMO( "INTERIOR_AMMO" ); @@ -4803,6 +4804,10 @@ std::optional link_up_actor::link_to_veh_app( Character *p, item &it, p->add_msg_if_player( m_good, _( "You connect %1$s and %2$s with the %3$s." ), prev_veh->disp_name(), target_veh->disp_name(), it.type_name() ); } + if( it.typeId() != itype_power_cord ) { + // Remove linked_flag from attached parts - the just-added cable vehicle parts do the same thing. + it.reset_link( p ); + } p->moves -= move_cost; return 1; // Let the cable be destroyed. } @@ -4934,6 +4939,10 @@ std::optional link_up_actor::link_tow_cable( Character *p, item &it, } else { prev_veh->tow_data.set_towing( prev_veh, target_veh ); } + if( it.typeId() != itype_power_cord ) { + // Remove linked_flag from attached parts - the just-added cable vehicle parts do the same thing. + it.reset_link( p ); + } p->moves -= move_cost; return 1; // Let the cable be destroyed. } diff --git a/src/veh_appliance.cpp b/src/veh_appliance.cpp index 1dfafca2830a3..fd462952f489c 100644 --- a/src/veh_appliance.cpp +++ b/src/veh_appliance.cpp @@ -172,7 +172,6 @@ veh_app_interact::veh_app_interact( vehicle &veh, const point &p ) ctxt.register_action( "SIPHON" ); ctxt.register_action( "RENAME" ); ctxt.register_action( "REMOVE" ); - ctxt.register_action( "UNPLUG" ); } // @returns true if a battery part exists on any vehicle connected to veh @@ -350,14 +349,6 @@ bool veh_app_interact::can_siphon() return false; } -bool veh_app_interact::can_unplug() -{ - vehicle_part_range vpr = veh->get_all_parts(); - return std::any_of( vpr.begin(), vpr.end(), []( const vpart_reference & ref ) { - return ref.info().has_flag( "POWER_TRANSFER" ); - } ); -} - // 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 &parts, @@ -538,27 +529,6 @@ void veh_app_interact::plug() veh->plug_in( get_map().getabs( pos ) ); } -void veh_app_interact::unplug() -{ - veh->shed_loose_parts(); - int const part = veh->part_at( a_point ); - vehicle_part &vp = veh->part( part >= 0 ? part : 0 ); - act = player_activity( ACT_VEHICLE, 1, static_cast( 'u' ) ); - act.str_values.push_back( vp.info().id.str() ); - const point q = veh->coord_translate( vp.mount ); - map &here = get_map(); - for( const tripoint &p : veh->get_points( true ) ) { - act.coord_set.insert( here.getabs( p ) ); - } - act.values.push_back( here.getabs( veh->global_pos3() ).x + q.x ); - act.values.push_back( here.getabs( veh->global_pos3() ).y + q.y ); - act.values.push_back( a_point.x ); - act.values.push_back( a_point.y ); - act.values.push_back( -a_point.x ); - act.values.push_back( -a_point.y ); - act.values.push_back( veh->index_of_part( &vp ) ); -} - void veh_app_interact::populate_app_actions() { const std::string ctxt_letters = ctxt.get_available_single_char_hotkeys(); @@ -597,13 +567,6 @@ void veh_app_interact::populate_app_actions() imenu.addentry( -1, true, ctxt.keys_bound_to( "PLUG" ).front(), ctxt.get_action_name( "PLUG" ) ); - // Unplug - app_actions.emplace_back( [this]() { - unplug(); - } ); - imenu.addentry( -1, can_unplug(), ctxt.keys_bound_to( "UNPLUG" ).front(), - ctxt.get_action_name( "UNPLUG" ) ); - /*************** Get part-specific actions ***************/ veh_menu menu( veh, "IF YOU SEE THIS IT IS A BUG" ); veh->build_interact_menu( menu, veh->mount_to_tripoint( a_point ), false ); diff --git a/src/veh_appliance.h b/src/veh_appliance.h index 756f8861b0c10..ca29923a68f9d 100644 --- a/src/veh_appliance.h +++ b/src/veh_appliance.h @@ -134,12 +134,6 @@ class veh_app_interact * Connects the power cable to selected tile. */ void plug(); - /** - * Function associated with the "UNPLUG" action. - * Removes all power connections to other appliances and vehicles and drops - * any used cable items on the ground. - */ - void unplug(); /** * The main loop of the appliance UI. Redraws windows, checks for input, and * performs selected actions. The loop exits once an activity is assigned diff --git a/src/vehicle.h b/src/vehicle.h index 5bc4b22d91496..47eb5c86aa843 100644 --- a/src/vehicle.h +++ b/src/vehicle.h @@ -227,7 +227,8 @@ enum class vp_flag : uint32_t { animal_flag = 2, carried_flag = 4, carrying_flag = 8, - tracked_flag = 16 //carried vehicle part with tracking enabled + tracked_flag = 16, //carried vehicle part with tracking enabled + linked_flag = 32 //a cable is attached to this }; class turret_cpu @@ -511,6 +512,8 @@ struct vehicle_part { /** If it's a part with variants, which variant it is */ std::string variant; + time_point last_disconnected = calendar::before_time_starts; + private: // part type definition // note: this could be a const& but doing so would require hassle with implementing diff --git a/src/vehicle_part.cpp b/src/vehicle_part.cpp index 514bdea05810b..4126926647b3b 100644 --- a/src/vehicle_part.cpp +++ b/src/vehicle_part.cpp @@ -559,7 +559,8 @@ bool vehicle_part::contains_liquid() const bool vehicle_part::is_battery() const { - return info().has_flag( VPFLAG_BATTERY ) || base.is_magazine() && base.ammo_types().count( ammo_battery ); + return info().has_flag( VPFLAG_BATTERY ) || base.is_magazine() && + base.ammo_types().count( ammo_battery ); } bool vehicle_part::is_reactor() const diff --git a/src/vehicle_use.cpp b/src/vehicle_use.cpp index 8e4f31d58bd29..f62416e3d6d5b 100644 --- a/src/vehicle_use.cpp +++ b/src/vehicle_use.cpp @@ -1831,9 +1831,13 @@ void vehicle::build_interact_menu( veh_menu &menu, const tripoint &p, bool with_ const vpart_position vp = *ovp; const tripoint vppos = vp.pos(); + std::vector vp_parts = get_parts_at( vppos, "", part_status_flag::working ); + // @returns true if pos contains available part with a flag - const auto has_part_here = [this, vppos]( const std::string & flag ) { - return !get_parts_at( vppos, flag, part_status_flag::working ).empty(); + const auto has_part_here = [vp_parts]( const std::string & flag ) { + return std::any_of( vp_parts.begin(), vp_parts.end(), [flag]( const vehicle_part * vp_part ) { + return vp_part->info().has_flag( flag ); + } ); }; const bool remote = g->remoteveh() == this; @@ -1845,6 +1849,13 @@ void vehicle::build_interact_menu( veh_menu &menu, const tripoint &p, bool with_ const bool player_inside = get_map().veh_at( get_player_character().pos() ) ? &get_map().veh_at( get_player_character().pos() )->vehicle() == this : false; + bool power_linked = false; + bool cable_linked = false; + for( vehicle_part *vp_part : vp_parts ) { + power_linked = power_linked ? true : vp_part->info().has_flag( "POWER_TRANSFER" ); + cable_linked = cable_linked ? true : vp_part->has_flag( vp_flag::linked_flag ) || + vp_part->info().has_flag( "TOW_CABLE" ); + } if( !is_appliance() ) { menu.add( _( "Examine vehicle" ) ) @@ -2056,6 +2067,55 @@ void vehicle::build_interact_menu( veh_menu &menu, const tripoint &p, bool with_ } } + if( power_linked ) { + menu.add( _( "Disconnect power connections" ) ) + .enable( !cable_linked ) + .desc( string_format( !cable_linked ? "" : _( "Remove other cables first" ) ) ) + .skip_locked_check() + .hotkey( "DISCONNECT_CABLES" ) + .on_submit( [this, vp_parts] { + for( vehicle_part *vp_part : vp_parts ) + { + if( vp_part->info().has_flag( "POWER_TRANSFER" ) ) { + item drop = vp_part->properties_to_item(); + if( !magic && !drop.has_flag( flag_id( "NO_DROP" ) ) ) { + get_player_character().i_add_or_drop( drop ); + } + add_msg( _( "You detached the %s." ), drop.type_name() ); + remove_remote_part( *vp_part ); + remove_part( *vp_part ); + } + } + get_player_character().pause(); + } ); + } + if( cable_linked ) { + menu.add( _( "Disconnect cables" ) ) + .skip_locked_check() + .hotkey( "DISCONNECT_CABLES" ) + .on_submit( [this, vp_parts] { + for( vehicle_part *vp_part : vp_parts ) + { + if( vp_part->has_flag( vp_flag::linked_flag ) ) { + vp_part->last_disconnected = calendar::turn; + vp_part->remove_flag( vp_flag::linked_flag ); + add_msg( _( "You detached the %s's cables." ), vp_part->name( false ) ); + } + if( vp_part->info().has_flag( "TOW_CABLE" ) ) { + item drop = vp_part->properties_to_item(); + if( !magic && !drop.has_flag( flag_id( "NO_DROP" ) ) ) { + get_player_character().i_add_or_drop( drop ); + } + const int index = index_of_part( vp_part ); + add_msg( _( "You detached the %s." ), drop.type_name() ); + remove_remote_part( *vp_part ); + remove_part( *vp_part ); + } + } + get_player_character().pause(); + } ); + } + for( const auto&[tool_item, hk] : vp.get_tools() ) { const itype_id &tool_type = tool_item.typeId(); if( !tool_type->has_use() ) { From 088c19e1addf747d69da12177fbc0b40310ddd44 Mon Sep 17 00:00:00 2001 From: Kamayana Date: Wed, 12 Jul 2023 18:03:43 -0500 Subject: [PATCH 28/68] Improve cable stretch message for circular distances --- src/item.cpp | 36 ++++++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/src/item.cpp b/src/item.cpp index b5514498b7b45..92b062e7941d0 100644 --- a/src/item.cpp +++ b/src/item.cpp @@ -13001,17 +13001,15 @@ bool item::process_link( map &here, Character *carrier, const tripoint &pos ) // Lambda function for checking if a cable's been stretched too long, resetting it if so. // @return True if the cable is disconnected. - const auto is_cable_too_long = [this, carrier, pos, last_t_abs_pos_is_oob, is_cable_item]() { + const auto is_cable_too_long = [this, carrier, pos, last_t_abs_pos_is_oob, + is_cable_item]( float new_length ) { + link->length = new_length; if( debug_mode ) { add_msg_debug( debugmode::DF_IUSE, "%s linked to %s%s, length %d/%d", type_name(), link->t_abs_pos.to_string_writable(), last_t_abs_pos_is_oob ? " (OoB)" : "", link->length, link->max_length ); } - if( link->length == link->max_length && carrier != nullptr ) { - carrier->add_msg_if_player( m_warning, - string_format( is_cable_item ? _( "Your %s is stretched to its limit!" ) : - _( "Your %s's cable is stretched to its limit!" ), type_name() ) ); - } else if( link->length > link->max_length ) { + if( link->length > link->max_length ) { if( carrier != nullptr ) { carrier->add_msg_if_player( m_bad, string_format( is_cable_item ? _( "Your over-extended %s breaks loose!" ) : @@ -13022,6 +13020,10 @@ bool item::process_link( map &here, Character *carrier, const tripoint &pos ) _( "The %s's over-extended cable breaks loose!" ), type_name() ) ); } return true; + } else if( new_length + M_SQRT2 >= link->max_length + 1 && carrier != nullptr ) { + carrier->add_msg_if_player( m_warning, + string_format( is_cable_item ? _( "Your %s is stretched to its limit!" ) : + _( "Your %s's cable is stretched to its limit!" ), type_name() ) ); } return false; }; @@ -13051,12 +13053,17 @@ bool item::process_link( map &here, Character *carrier, const tripoint &pos ) // We should skip processing if the last saved target point is out of bounds, since vehicles give innacurate absolute coordinates when out of bounds. // However, if the linked vehicle is moving fast enough, we should always do processing to avoid erroneously skipping linked items riding inside of it. if( last_t_abs_pos_is_oob && t_veh->velocity < HALF_MAPSIZE_X * 400 ) { - if( check_length ) { - link->length = rl_dist( here.getabs( pos ), link->t_abs_pos.raw() ) + - link->t_mount.abs().x + link->t_mount.abs().y; - if( is_cable_too_long() ) { + if( !check_length ) { + return false; + } + if( trigdist ) { + if( is_cable_too_long( trig_dist( here.getabs( pos ), link->t_abs_pos.raw() ) + + link->t_mount.abs().x + link->t_mount.abs().y ) ) { return reset_link( carrier ); } + } else if( is_cable_too_long( square_dist( here.getabs( pos ), link->t_abs_pos.raw() ) + + link->t_mount.abs().x + link->t_mount.abs().y ) ) { + return reset_link( carrier ); } return false; } @@ -13104,8 +13111,13 @@ bool item::process_link( map &here, Character *carrier, const tripoint &pos ) // If either of the link's connected sides moved, check the cable's length. if( check_length ) { - link->length = rl_dist( pos, t_veh_bub_pos + t_veh->part( link_vp_index ).precalc[0] ); - if( is_cable_too_long() ) { + if( trigdist ) { + if( is_cable_too_long( trig_dist( pos, t_veh_bub_pos + + t_veh->part( link_vp_index ).precalc[0] ) ) ) { + return reset_link( carrier, link_vp_index ); + } + } else if( is_cable_too_long( square_dist( pos, t_veh_bub_pos + + t_veh->part( link_vp_index ).precalc[0] ) ) ) { return reset_link( carrier, link_vp_index ); } } From ad58cb2ac3a3be26784a3497fb0e914a1639ef0d Mon Sep 17 00:00:00 2001 From: Kamayana Date: Wed, 12 Jul 2023 19:06:32 -0500 Subject: [PATCH 29/68] Only return items with the CABLE_SPOOL flag with cables(), temporary measure for 0.G --- src/item_contents.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/item_contents.cpp b/src/item_contents.cpp index db09331602f2b..f5281a907d2e5 100644 --- a/src/item_contents.cpp +++ b/src/item_contents.cpp @@ -1784,7 +1784,10 @@ std::vector item_contents::cables() const for( const item_pocket &pocket : contents ) { if( pocket.is_type( item_pocket::pocket_type::CABLE ) ) { for( const item *it : pocket.all_items_top() ) { - cables.emplace_back( it ); + // TODO: remove flag check after 0.H + if( it->has_flag( flag_id( "CABLE_SPOOL" ) ) ) { + cables.emplace_back( it ); + } } } } From dfbe8b69253248308f9cc882f0231416740ea062 Mon Sep 17 00:00:00 2001 From: Kamayana Date: Wed, 12 Jul 2023 19:15:08 -0500 Subject: [PATCH 30/68] Documentation --- doc/JSON_INFO.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/doc/JSON_INFO.md b/doc/JSON_INFO.md index 1efa22b21e574..e02a4e633c893 100644 --- a/doc/JSON_INFO.md +++ b/doc/JSON_INFO.md @@ -4035,7 +4035,13 @@ The contents of use_action fields can either be a string indicating a built-in f "vehicle_tow", // Can be used as a tow cable between two vehicles. "bio_cable", // Can connect to a cable system bionic. "ups", // Can link to a UPS. - "solarpack", // Can link to a worn solar pack. + "solarpack" // Can link to a worn solar pack. + ], + "can_extend": [ // Array of cable items that can be extended by this one ( Optional, defaults to none ). + "extension_cable", + "long_extension_cable", + "ELECTRICAL_DEVICES" // "ELECTRICAL_DEVICES" is a special keyword that lets this cable extend all electrical devices that have link_up actions. + ] }, "use_action" : { "type" : "delayed_transform", // Like transform, but it will only transform when the item has a certain age From 813b720283e3c30025917c37e262be31216263a1 Mon Sep 17 00:00:00 2001 From: Kamayana Date: Thu, 13 Jul 2023 00:56:28 -0500 Subject: [PATCH 31/68] Reopen menu on manually detaching cable, as well --- src/activity_actor.cpp | 3 +-- src/iuse_actor.cpp | 15 +++++---------- 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/src/activity_actor.cpp b/src/activity_actor.cpp index 59b2a6ed46b72..f647ad955595f 100644 --- a/src/activity_actor.cpp +++ b/src/activity_actor.cpp @@ -5682,9 +5682,8 @@ void invoke_item_activity_actor::do_turn( player_activity &, Character &who ) who.invoke_item( item.get_item() ); return; } - std::string it_method = method; who.cancel_activity(); - who.invoke_item( item.get_item(), it_method ); + who.invoke_item( item.get_item(), method ); } void invoke_item_activity_actor::serialize( JsonOut &jsout ) const diff --git a/src/iuse_actor.cpp b/src/iuse_actor.cpp index d81591b7ca366..02967aadedcd3 100644 --- a/src/iuse_actor.cpp +++ b/src/iuse_actor.cpp @@ -4383,7 +4383,7 @@ std::optional link_up_actor::use( Character *p, item &it, bool t, const tri const int respool_time_total = !it.link || it.link->length < respool_threshold ? 0 : ( it.link->length - respool_threshold ) * respool_time_per_square; const bool past_respool_threshold = it.link_length() > respool_threshold; - const bool unspooled = it.link && it.link->has_state( link_state::needs_reeling ); + const bool unspooled = it.link_length() == -1; const bool has_loose_end = !unspooled && is_cable_item ? !it.link || it.link->has_state( link_state::no_link ) : !it.link || it.link->has_no_links(); @@ -4517,15 +4517,10 @@ std::optional link_up_actor::use( Character *p, item &it, bool t, const tri } else if( choice >= 998 ) { // Selection: Unconnect & respool. - if( unspooled ) { - if( choice == 998 ) { - // If the "Re-spool" option was selected, re-open the menu after the cable is reeled. - p->assign_activity( invoke_item_activity_actor( item_location{*p, &it}, "link_up" ) ); - p->activity.auto_resume = true; - } - p->assign_activity( player_activity( reel_cable_activity_actor( respool_time_total, item_location{*p, &it} ) ) ); - return 0; - } + + // Reopen the menu after respooling. + p->assign_activity( invoke_item_activity_actor( item_location{*p, &it}, "link_up" ) ); + p->activity.auto_resume = true; it.reset_link( p ); // Cables that are too long need to be manually rewound before reuse. From 46414637e7a25cd79a95e354b6994271c25b4d58 Mon Sep 17 00:00:00 2001 From: Kamayana Date: Thu, 13 Jul 2023 01:50:54 -0500 Subject: [PATCH 32/68] Minor fixes --- src/item.h | 4 ++-- src/iuse_actor.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/item.h b/src/item.h index 0265b910fc5f7..c9c52365abd34 100644 --- a/src/item.h +++ b/src/item.h @@ -1443,8 +1443,8 @@ class item : public visitable bool has_state( link_state state ) const { return s_state == state || t_state == state; } - bool has_states( link_state i_state_, link_state t_state_ ) const { - return s_state == i_state_ && t_state == t_state_; + bool has_states( link_state s_state_, link_state t_state_ ) const { + return s_state == s_state_ && t_state == t_state_; } bool has_no_links() const { return s_state == link_state::no_link && t_state == link_state::no_link; diff --git a/src/iuse_actor.cpp b/src/iuse_actor.cpp index 02967aadedcd3..7641583d9ec90 100644 --- a/src/iuse_actor.cpp +++ b/src/iuse_actor.cpp @@ -4455,7 +4455,7 @@ std::optional link_up_actor::use( Character *p, item &it, bool t, const tri } } else { - // This is a cable item with one connection already: + // This is a cable item with at least one connection already: link_menu.text = string_format( _( "What to do with the %s?%s" ), cable_name, it.link->t_veh_safe ? string_format( _( "\nAttached to: %s" ), it.link->t_veh_safe->name ) : "" ); @@ -4581,7 +4581,7 @@ std::optional link_up_actor::use( Character *p, item &it, bool t, const tri item_location loc; avatar *you = p->as_avatar(); const std::string choose_ups = _( "Choose UPS:" ); - const std::string dont_have_ups = _( "You don't have any UPS." ); + const std::string dont_have_ups = _( "You need an active UPS." ); auto ups_filter = [&]( const item & itm ) { return itm.has_flag( flag_IS_UPS ); }; From a8c776dcfcb77692c49a690c543f4627fd6205c7 Mon Sep 17 00:00:00 2001 From: Kamayana Date: Thu, 13 Jul 2023 03:45:15 -0500 Subject: [PATCH 33/68] Improve messaging of CBM/UPS/solar backpack links --- src/iuse_actor.cpp | 38 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/src/iuse_actor.cpp b/src/iuse_actor.cpp index 7641583d9ec90..6bb003fcb8734 100644 --- a/src/iuse_actor.cpp +++ b/src/iuse_actor.cpp @@ -4456,8 +4456,35 @@ std::optional link_up_actor::use( Character *p, item &it, bool t, const tri } else { // This is a cable item with at least one connection already: - link_menu.text = string_format( _( "What to do with the %s?%s" ), cable_name, it.link->t_veh_safe ? - string_format( _( "\nAttached to: %s" ), it.link->t_veh_safe->name ) : "" ); + std::string state_desc_lhs; + std::string state_desc_rhs; + if( it.link->has_state( link_state::no_link ) ) { + state_desc_lhs = _( "\nAttached to " ); + if( it.link->t_veh_safe ) { + state_desc_rhs = it.link->t_veh_safe->name; + } else if( it.link->has_state( link_state::bio_cable ) ) { + state_desc_rhs = _( "Cable Charger System" ); + } else if( it.link->has_state( link_state::ups ) ) { + state_desc_rhs = _( "Unified Power Supply" ); + } else if( it.link->has_state( link_state::solarpack ) ) { + state_desc_rhs = _( "solar backpack" ); + } + } else { + if( it.link->s_state == link_state::bio_cable ) { + state_desc_lhs = _( "\nConnecting Cable Charger System to " ); + } else if( it.link->s_state == link_state::ups ) { + state_desc_lhs = _( "\nConnecting UPS to " ); + } else if( it.link->s_state == link_state::solarpack ) { + state_desc_lhs = _( "\nConnecting solar backpack to " ); + } + if( it.link->t_veh_safe ) { + state_desc_rhs = it.link->t_veh_safe->name; + } else if( it.link->t_state == link_state::bio_cable ) { + state_desc_rhs = _( "Cable Charger System" ); + } + } + link_menu.text = string_format( _( "What to do with the %s?%s%s" ), cable_name, + state_desc_lhs, state_desc_rhs ); // TODO: Allow plugging UPSes and Solar Packs into more than just bionics. // There is already code to support setting up a link, but none for actual functionality. @@ -4572,6 +4599,8 @@ std::optional link_up_actor::use( Character *p, item &it, bool t, const tri it.link->s_state = link_state::bio_cable; p->add_msg_if_player( m_good, _( "You are now plugged into the vehicle." ) ); } + it.set_link_traits(); + it.link->last_processed = calendar::turn; p->moves -= move_cost; it.process( here, p, p->pos() ); return 0; @@ -4609,6 +4638,8 @@ std::optional link_up_actor::use( Character *p, item &it, bool t, const tri } it.link->s_state = link_state::ups; loc->set_var( "cable", "plugged_in" ); + it.set_link_traits(); + it.link->last_processed = calendar::turn; p->moves -= move_cost; it.process( here, p, p->pos() ); return 0; @@ -4646,6 +4677,8 @@ std::optional link_up_actor::use( Character *p, item &it, bool t, const tri } it.link->s_state = link_state::solarpack; loc->set_var( "cable", "plugged_in" ); + it.set_link_traits(); + it.link->last_processed = calendar::turn; p->moves -= move_cost; it.process( here, p, p->pos() ); return 0; @@ -4862,6 +4895,7 @@ std::optional link_up_actor::link_tow_cable( Character *p, item &it, it.link->t_abs_pos = here.getglobal( t_vp->vehicle().global_pos3() ); it.link->t_mount = t_vp->mount(); it.link->max_length = cable_length != -1 ? cable_length : it.type->maximum_charges(); + it.set_link_traits(); it.link->last_processed = calendar::turn; p->moves -= move_cost; it.process( here, p, p->pos() ); From e77add080ef3c312d62bc2eec9c2c75e24f142d9 Mon Sep 17 00:00:00 2001 From: Kamayana Date: Thu, 13 Jul 2023 04:50:16 -0500 Subject: [PATCH 34/68] Lint JSON --- data/json/items/tool_armor.json | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/data/json/items/tool_armor.json b/data/json/items/tool_armor.json index 985bf3ae6cdae..7f7b5d04c5568 100644 --- a/data/json/items/tool_armor.json +++ b/data/json/items/tool_armor.json @@ -3503,11 +3503,7 @@ "need_charges": 1, "need_charges_msg": "The blanket's batteries are dead." }, - { - "type": "link_up", - "cable_length": 3, - "charge_rate": "110 W" - } + { "type": "link_up", "cable_length": 3, "charge_rate": "110 W" } ], "flags": [ "OVERSIZE", "OUTER", "ALLOWS_NATURAL_ATTACKS", "WATER_BREAK" ], "pocket_data": [ @@ -3542,11 +3538,7 @@ "msg": "You turn the blanket's heating elements off.", "target": "electric_blanket" }, - { - "type": "link_up", - "cable_length": 3, - "charge_rate": "110 W" - } + { "type": "link_up", "cable_length": 3, "charge_rate": "110 W" } ] }, { From c08b1c0f7bf42ad403ad1f46dc0761fb0c92e09a Mon Sep 17 00:00:00 2001 From: Kamayana Date: Fri, 14 Jul 2023 01:25:24 -0500 Subject: [PATCH 35/68] Update newly added cables --- data/json/items/tool/cooking.json | 77 ++++----------------------- data/json/items/tool/misc.json | 32 ++--------- data/json/items/tool/radio_tools.json | 12 ++--- data/json/items/tool/science.json | 35 +++++------- data/json/items/tool/workshop.json | 60 +++++---------------- 5 files changed, 43 insertions(+), 173 deletions(-) diff --git a/data/json/items/tool/cooking.json b/data/json/items/tool/cooking.json index 0a2c86c70b018..56562cace5263 100644 --- a/data/json/items/tool/cooking.json +++ b/data/json/items/tool/cooking.json @@ -295,16 +295,7 @@ "flags": [ "WATER_BREAK" ], "charges_per_use": 25, "qualities": [ [ "BOIL", 1 ] ], - "use_action": [ - "HOTPLATE", - { - "type": "link_up", - "menu_text": "Plug in / Unplug", - "ammo_scale": 0, - "cable_length": 2, - "charge_rate": "1 kW" - } - ], + "use_action": [ "HOTPLATE", { "type": "link_up", "cable_length": 2, "charge_rate": "1 kW" } ], "pocket_data": [ { "pocket_type": "MAGAZINE_WELL", @@ -330,7 +321,7 @@ "color": "blue", "ammo": [ "battery" ], "flags": [ "ALLOWS_REMOTE_USE", "WATER_BREAK", "ELECTRONIC" ], - "use_action": { "type": "link_up", "menu_text": "Plug in / Unplug", "ammo_scale": 0, "cable_length": 2, "charge_rate": "600 W" }, + "use_action": { "type": "link_up", "cable_length": 2, "charge_rate": "600 W" }, "pocket_data": [ { "pocket_type": "MAGAZINE_WELL", @@ -431,7 +422,7 @@ "color": "white", "ammo": [ "battery" ], "flags": [ "ALLOWS_REMOTE_USE", "WATER_BREAK", "ELECTRONIC" ], - "use_action": { "type": "link_up", "menu_text": "Plug in / Unplug", "ammo_scale": 0, "cable_length": 2, "charge_rate": "450 W" }, + "use_action": { "type": "link_up", "cable_length": 2, "charge_rate": "450 W" }, "pocket_data": [ { "pocket_type": "MAGAZINE_WELL", @@ -510,16 +501,7 @@ "flags": [ "WATER_BREAK" ], "ammo": [ "battery" ], "charges_per_use": 35, - "use_action": [ - "HOTPLATE", - { - "type": "link_up", - "menu_text": "Plug in / Unplug", - "ammo_scale": 0, - "cable_length": 3, - "charge_rate": "1 kW" - } - ], + "use_action": [ "HOTPLATE", { "type": "link_up", "cable_length": 3, "charge_rate": "1 kW" } ], "pocket_data": [ { "pocket_type": "MAGAZINE_WELL", @@ -548,16 +530,7 @@ "flags": [ "WATER_BREAK", "ELECTRONIC" ], "ammo": [ "battery" ], "charges_per_use": 25, - "use_action": [ - "HOTPLATE", - { - "type": "link_up", - "menu_text": "Plug in / Unplug", - "ammo_scale": 0, - "cable_length": 3, - "charge_rate": "1500 W" - } - ], + "use_action": [ "HOTPLATE", { "type": "link_up", "cable_length": 3, "charge_rate": "1500 W" } ], "pocket_data": [ { "pocket_type": "MAGAZINE_WELL", @@ -584,7 +557,7 @@ "color": "white", "ammo": [ "battery" ], "flags": [ "ALLOWS_REMOTE_USE", "WATER_BREAK" ], - "use_action": { "type": "link_up", "menu_text": "Plug in / Unplug", "ammo_scale": 0, "cable_length": 4, "charge_rate": "55 W" }, + "use_action": { "type": "link_up", "cable_length": 4, "charge_rate": "55 W" }, "pocket_data": [ { "pocket_type": "MAGAZINE_WELL", @@ -691,16 +664,7 @@ "flags": [ "WATER_BREAK", "ELECTRONIC" ], "power_draw": "100 W", "qualities": [ [ "CONTAIN", 1 ] ], - "use_action": [ - "MULTICOOKER", - { - "type": "link_up", - "menu_text": "Plug in / Unplug", - "ammo_scale": 0, - "cable_length": 2, - "charge_rate": "1000 W" - } - ], + "use_action": [ "MULTICOOKER", { "type": "link_up", "cable_length": 2, "charge_rate": "1000 W" } ], "pocket_data": [ { "pocket_type": "MAGAZINE_WELL", @@ -761,7 +725,7 @@ "symbol": ";", "color": "green", "ammo": [ "battery" ], - "use_action": { "type": "link_up", "menu_text": "Plug in / Unplug", "ammo_scale": 0, "cable_length": 2, "charge_rate": "1 kW" }, + "use_action": { "type": "link_up", "cable_length": 2, "charge_rate": "1 kW" }, "pocket_data": [ { "pocket_type": "MAGAZINE_WELL", @@ -1019,7 +983,7 @@ "color": "white", "ammo": [ "battery" ], "flags": [ "ALLOWS_REMOTE_USE", "WATER_BREAK" ], - "use_action": { "type": "link_up", "menu_text": "Plug in / Unplug", "ammo_scale": 0, "cable_length": 2, "charge_rate": "110 W" }, + "use_action": { "type": "link_up", "cable_length": 2, "charge_rate": "110 W" }, "pocket_data": [ { "pocket_type": "MAGAZINE_WELL", @@ -1065,16 +1029,7 @@ "color": "light_blue", "ammo": [ "battery" ], "charges_per_use": 14, - "use_action": [ - "WATER_PURIFIER", - { - "type": "link_up", - "menu_text": "Plug in / Unplug", - "ammo_scale": 0, - "cable_length": 2, - "charge_rate": "30 W" - } - ], + "use_action": [ "WATER_PURIFIER", { "type": "link_up", "cable_length": 2, "charge_rate": "30 W" } ], "flags": [ "ALLOWS_REMOTE_USE" ], "pocket_data": [ { @@ -1142,17 +1097,7 @@ "material": [ "steel", "plastic" ], "weight": "11339 g", "volume": "9 L", - "use_action": [ - "HOTPLATE", - "HEAT_FOOD", - { - "type": "link_up", - "menu_text": "Plug in / Unplug", - "ammo_scale": 0, - "cable_length": 2, - "charge_rate": "1800 W" - } - ], + "use_action": [ "HOTPLATE", "HEAT_FOOD", { "type": "link_up", "cable_length": 2, "charge_rate": "1800 W" } ], "qualities": [ [ "COOK", 1 ], [ "OVEN", 1 ] ], "flags": [ "WATER_BREAK" ], "charges_per_use": 25, diff --git a/data/json/items/tool/misc.json b/data/json/items/tool/misc.json index 7915f42690a87..3fa5b843d5f00 100644 --- a/data/json/items/tool/misc.json +++ b/data/json/items/tool/misc.json @@ -373,13 +373,7 @@ "menu_text": "Turn on", "type": "transform" }, - { - "type": "link_up", - "menu_text": "Plug in / Unplug", - "ammo_scale": 0, - "cable_length": 2, - "charge_rate": "1 kW" - } + { "type": "link_up", "cable_length": 2, "charge_rate": "1 kW" } ], "pocket_data": [ { @@ -409,13 +403,7 @@ "menu_text": "Turn off", "type": "transform" }, - { - "type": "link_up", - "menu_text": "Plug in / Unplug", - "ammo_scale": 0, - "cable_length": 2, - "charge_rate": "1 kW" - } + { "type": "link_up", "cable_length": 2, "charge_rate": "1 kW" } ] }, { @@ -591,13 +579,7 @@ "menu_text": "Turn on", "type": "transform" }, - { - "type": "link_up", - "menu_text": "Plug in / Unplug", - "ammo_scale": 0, - "cable_length": 2, - "charge_rate": "500 W" - } + { "type": "link_up", "cable_length": 2, "charge_rate": "500 W" } ], "pocket_data": [ { @@ -627,13 +609,7 @@ "menu_text": "Turn off", "type": "transform" }, - { - "type": "link_up", - "menu_text": "Plug in / Unplug", - "ammo_scale": 0, - "cable_length": 2, - "charge_rate": "500 W" - } + { "type": "link_up", "cable_length": 2, "charge_rate": "500 W" } ] }, { diff --git a/data/json/items/tool/radio_tools.json b/data/json/items/tool/radio_tools.json index 8ca65f2c9316a..86c48b8a0e072 100644 --- a/data/json/items/tool/radio_tools.json +++ b/data/json/items/tool/radio_tools.json @@ -100,10 +100,7 @@ "ammo": [ "battery" ], "flags": [ "WATER_BREAK", "ELECTRONIC" ], "charges_per_use": 1, - "use_action": [ - "RADIO_OFF", - { "type": "link_up", "menu_text": "Plug in / Unplug", "ammo_scale": 0, "cable_length": 3, "charge_rate": "5 W" } - ], + "use_action": [ "RADIO_OFF", { "type": "link_up", "cable_length": 3, "charge_rate": "5 W" } ], "pocket_data": [ { "pocket_type": "MAGAZINE_WELL", @@ -122,10 +119,7 @@ "description": "A portable radio that is turned on and continually draining its batteries. It is playing the broadcast being sent from any nearby radio towers.", "power_draw": "500 mW", "revert_to": "radio", - "use_action": [ - "RADIO_ON", - { "type": "link_up", "menu_text": "Plug in / Unplug", "ammo_scale": 0, "cable_length": 3, "charge_rate": "5 W" } - ], + "use_action": [ "RADIO_ON", { "type": "link_up", "cable_length": 3, "charge_rate": "5 W" } ], "flags": [ "TRADER_AVOID", "WATER_BREAK", "ELECTRONIC" ] }, { @@ -143,7 +137,7 @@ "ammo": [ "battery" ], "charges_per_use": 1, "flags": [ "TWO_WAY_RADIO", "WATER_BREAK", "ELECTRONIC" ], - "use_action": [ { "type": "link_up", "menu_text": "Plug in / Unplug", "ammo_scale": 0, "cable_length": 3, "charge_rate": "5 W" } ], + "use_action": [ { "type": "link_up", "cable_length": 3, "charge_rate": "5 W" } ], "pocket_data": [ { "pocket_type": "MAGAZINE_WELL", diff --git a/data/json/items/tool/science.json b/data/json/items/tool/science.json index d8fe27baf33c5..032859d5a1b79 100644 --- a/data/json/items/tool/science.json +++ b/data/json/items/tool/science.json @@ -55,16 +55,7 @@ "sub": "hotplate", "charges_per_use": 1, "qualities": [ [ "DISTILL", 1 ], [ "CHEM", 3 ], [ "BOIL", 1 ] ], - "use_action": [ - "HOTPLATE", - { - "type": "link_up", - "menu_text": "Plug in / Unplug", - "ammo_scale": 0, - "cable_length": 7, - "charge_rate": "1 kW" - } - ], + "use_action": [ "HOTPLATE", { "type": "link_up", "cable_length": 7, "charge_rate": "1 kW" } ], "pocket_data": [ { "pocket_type": "MAGAZINE_WELL", @@ -106,7 +97,7 @@ "symbol": ";", "ammo": [ "battery" ], "flags": [ "WATER_BREAK" ], - "use_action": [ { "type": "link_up", "menu_text": "Plug in / Unplug", "ammo_scale": 0, "cable_length": 2, "charge_rate": "300 W" } ], + "use_action": [ { "type": "link_up", "cable_length": 2, "charge_rate": "300 W" } ], "pocket_data": [ { "pocket_type": "MAGAZINE_WELL", @@ -129,7 +120,7 @@ "color": "light_gray", "ammo": [ "battery" ], "flags": [ "WATER_BREAK" ], - "use_action": [ { "type": "link_up", "menu_text": "Plug in / Unplug", "ammo_scale": 0, "cable_length": 2, "charge_rate": "150 W" } ], + "use_action": [ { "type": "link_up", "cable_length": 2, "charge_rate": "150 W" } ], "pocket_data": [ { "pocket_type": "MAGAZINE_WELL", @@ -264,7 +255,7 @@ "symbol": "E", "flags": [ "ALLOWS_REMOTE_USE", "WATER_BREAK", "ELECTRONIC" ], "color": "dark_gray", - "use_action": [ { "type": "link_up", "menu_text": "Plug in / Unplug", "ammo_scale": 0, "cable_length": 2, "charge_rate": "1200 W" } ], + "use_action": [ { "type": "link_up", "cable_length": 2, "charge_rate": "1200 W" } ], "pocket_data": [ { "pocket_type": "MAGAZINE_WELL", @@ -291,7 +282,7 @@ "flags": [ "ALLOWS_REMOTE_USE", "WATER_BREAK" ], "ammo": [ "battery" ], "qualities": [ [ "EXTRACT", 2 ] ], - "use_action": [ { "type": "link_up", "menu_text": "Plug in / Unplug", "ammo_scale": 0, "cable_length": 2, "charge_rate": "600 W" } ], + "use_action": [ { "type": "link_up", "cable_length": 2, "charge_rate": "600 W" } ], "pocket_data": [ { "pocket_type": "MAGAZINE_WELL", @@ -331,7 +322,7 @@ "flags": [ "ALLOWS_REMOTE_USE", "TRADER_AVOID", "WATER_BREAK", "ELECTRONIC" ], "ammo": [ "battery" ], "qualities": [ [ "EXTRACT", 1 ] ], - "use_action": [ { "type": "link_up", "menu_text": "Plug in / Unplug", "ammo_scale": 0, "cable_length": 2, "charge_rate": "1800 W" } ], + "use_action": [ { "type": "link_up", "cable_length": 2, "charge_rate": "1800 W" } ], "pocket_data": [ { "pocket_type": "MAGAZINE_WELL", @@ -444,7 +435,7 @@ "material": [ "iron" ], "flags": [ "ALLOWS_REMOTE_USE", "WATER_BREAK" ], "ammo": [ "battery" ], - "use_action": [ { "type": "link_up", "menu_text": "Plug in / Unplug", "ammo_scale": 0, "cable_length": 2, "charge_rate": "250 W" } ], + "use_action": [ { "type": "link_up", "cable_length": 2, "charge_rate": "250 W" } ], "pocket_data": [ { "pocket_type": "MAGAZINE_WELL", @@ -500,7 +491,7 @@ "color": "light_gray", "ammo": [ "battery" ], "flags": [ "ALLOWS_REMOTE_USE", "WATER_BREAK" ], - "use_action": [ { "type": "link_up", "menu_text": "Plug in / Unplug", "ammo_scale": 0, "cable_length": 2, "charge_rate": "150 W" } ], + "use_action": [ { "type": "link_up", "cable_length": 2, "charge_rate": "150 W" } ], "pocket_data": [ { "pocket_type": "MAGAZINE_WELL", "item_restriction": [ "storage_battery", "large_storage_battery" ] } ], "melee_damage": { "bash": 20 } }, @@ -520,7 +511,7 @@ "symbol": ";", "color": "light_gray", "ammo": [ "battery" ], - "use_action": [ { "type": "link_up", "menu_text": "Plug in / Unplug", "ammo_scale": 0, "cable_length": 2, "charge_rate": "800 W" } ], + "use_action": [ { "type": "link_up", "cable_length": 2, "charge_rate": "800 W" } ], "flags": [ "ALLOWS_REMOTE_USE", "WATER_BREAK", "ELECTRONIC" ], "pocket_data": [ { "pocket_type": "MAGAZINE_WELL", "item_restriction": [ "large_storage_battery" ] } ], "melee_damage": { "bash": 20 } @@ -541,7 +532,7 @@ "symbol": ";", "color": "light_gray", "ammo": [ "battery" ], - "use_action": [ { "type": "link_up", "menu_text": "Plug in / Unplug", "ammo_scale": 0, "cable_length": 2, "charge_rate": "350 W" } ], + "use_action": [ { "type": "link_up", "cable_length": 2, "charge_rate": "350 W" } ], "flags": [ "ALLOWS_REMOTE_USE", "WATER_BREAK", "ELECTRONIC" ], "pocket_data": [ { "pocket_type": "MAGAZINE_WELL", "item_restriction": [ "large_storage_battery" ] } ], "melee_damage": { "bash": 20 } @@ -562,7 +553,7 @@ "symbol": ";", "color": "light_gray", "ammo": [ "battery" ], - "use_action": [ { "type": "link_up", "menu_text": "Plug in / Unplug", "ammo_scale": 0, "cable_length": 2, "charge_rate": "1200 W" } ], + "use_action": [ { "type": "link_up", "cable_length": 2, "charge_rate": "1200 W" } ], "flags": [ "ALLOWS_REMOTE_USE", "WATER_BREAK", "ELECTRONIC" ], "pocket_data": [ { @@ -1164,7 +1155,7 @@ "color": "white", "charges_per_use": 5, "charged_qualities": [ [ "CONCENTRATE", 1 ] ], - "use_action": [ { "type": "link_up", "menu_text": "Plug in / Unplug", "ammo_scale": 0, "cable_length": 2, "charge_rate": "400 W" } ], + "use_action": [ { "type": "link_up", "cable_length": 2, "charge_rate": "400 W" } ], "pocket_data": [ { "pocket_type": "MAGAZINE_WELL", @@ -1229,7 +1220,7 @@ "flags": [ "WATER_BREAK", "ELECTRONIC" ], "charges_per_use": 5, "charged_qualities": [ [ "CONCENTRATE", 1 ] ], - "use_action": [ { "type": "link_up", "menu_text": "Plug in / Unplug", "ammo_scale": 0, "cable_length": 2, "charge_rate": "400 W" } ], + "use_action": [ { "type": "link_up", "cable_length": 2, "charge_rate": "400 W" } ], "pocket_data": [ { "pocket_type": "MAGAZINE_WELL", diff --git a/data/json/items/tool/workshop.json b/data/json/items/tool/workshop.json index aef01c2b60ce3..3b02c3ceb1134 100644 --- a/data/json/items/tool/workshop.json +++ b/data/json/items/tool/workshop.json @@ -76,7 +76,7 @@ "charges_per_use": 1, "power_draw": "800 W", "flags": [ "NONCONDUCTIVE", "WATER_BREAK" ], - "use_action": { "type": "link_up", "menu_text": "Plug in / Unplug", "ammo_scale": 0, "cable_length": 30, "charge_rate": "800 W" }, + "use_action": { "type": "link_up", "cable_length": 30, "charge_rate": "800 W" }, "pocket_data": [ { "pocket_type": "MAGAZINE_WELL", @@ -168,7 +168,7 @@ "color": "white", "ammo": [ "battery" ], "flags": [ "ALLOWS_REMOTE_USE" ], - "use_action": { "type": "link_up", "menu_text": "Plug in / Unplug", "ammo_scale": 0, "cable_length": 2, "charge_rate": "300 W" }, + "use_action": { "type": "link_up", "cable_length": 2, "charge_rate": "300 W" }, "pocket_data": [ { "pocket_type": "MAGAZINE_WELL", @@ -195,7 +195,7 @@ "color": "white", "ammo": [ "battery" ], "flags": [ "ALLOWS_REMOTE_USE" ], - "use_action": { "type": "link_up", "menu_text": "Plug in / Unplug", "ammo_scale": 0, "cable_length": 2, "charge_rate": "600 W" }, + "use_action": { "type": "link_up", "cable_length": 2, "charge_rate": "600 W" }, "pocket_data": [ { "pocket_type": "MAGAZINE_WELL", @@ -274,16 +274,7 @@ "ammo": [ "battery" ], "charges_per_use": 20, "qualities": [ [ "CONTAIN", 1 ] ], - "use_action": [ - "HOTPLATE", - { - "type": "link_up", - "menu_text": "Plug in / Unplug", - "ammo_scale": 0, - "cable_length": 15, - "charge_rate": "1500 W" - } - ], + "use_action": [ "HOTPLATE", { "type": "link_up", "cable_length": 15, "charge_rate": "1500 W" } ], "pocket_data": [ { "pocket_type": "MAGAZINE_WELL", @@ -389,16 +380,7 @@ "ammo": [ "battery" ], "pocket_data": [ { "pocket_type": "MAGAZINE", "rigid": true, "ammo_restriction": { "battery": 7920 } } ], "charges_per_use": 3960, - "use_action": [ - "JACKHAMMER", - { - "type": "link_up", - "menu_text": "Plug in / Unplug", - "ammo_scale": 0, - "cable_length": 33, - "charge_rate": "2200 W" - } - ], + "use_action": [ "JACKHAMMER", { "type": "link_up", "cable_length": 33, "charge_rate": "2200 W" } ], "flags": [ "DIG_TOOL", "POWERED", "USE_UPS", "NO_UNLOAD", "NO_RELOAD", "WATER_BREAK", "ELECTRONIC" ], "melee_damage": { "bash": 14, "stab": 6 } }, @@ -583,7 +565,7 @@ "color": "dark_gray", "ammo": [ "battery" ], "flags": [ "ALLOWS_REMOTE_USE", "WATER_BREAK", "ELECTRONIC" ], - "use_action": [ { "type": "link_up", "menu_text": "Plug in / Unplug", "ammo_scale": 0, "cable_length": 2, "charge_rate": "1500 W" } ], + "use_action": [ { "type": "link_up", "cable_length": 2, "charge_rate": "1500 W" } ], "pocket_data": [ { "pocket_type": "MAGAZINE_WELL", @@ -637,13 +619,7 @@ "cost_scaling": 0.1, "move_cost": 1500 }, - { - "type": "link_up", - "menu_text": "Plug in / Unplug", - "ammo_scale": 0, - "cable_length": 3, - "charge_rate": "70 W" - } + { "type": "link_up", "cable_length": 3, "charge_rate": "70 W" } ], "flags": [ "ALLOWS_REMOTE_USE" ], "pocket_data": [ @@ -923,7 +899,7 @@ "charged_qualities": [ [ "GRIND", 2 ] ], "ammo": [ "battery" ], "flags": [ "TRADER_AVOID", "WATER_BREAK" ], - "use_action": [ { "type": "link_up", "menu_text": "Plug in / Unplug", "ammo_scale": 0, "cable_length": 5, "charge_rate": "1600 W" } ], + "use_action": [ { "type": "link_up", "cable_length": 5, "charge_rate": "1600 W" } ], "pocket_data": [ { "pocket_type": "MAGAZINE_WELL", @@ -970,7 +946,7 @@ "charges_per_use": 1, "power_draw": "800 W", "flags": [ "NONCONDUCTIVE", "WATER_BREAK" ], - "use_action": [ { "type": "link_up", "menu_text": "Plug in / Unplug", "ammo_scale": 0, "cable_length": 2, "charge_rate": "800 W" } ], + "use_action": [ { "type": "link_up", "cable_length": 2, "charge_rate": "800 W" } ], "pocket_data": [ { "pocket_type": "MAGAZINE_WELL", @@ -1128,13 +1104,7 @@ "cost_scaling": 0.1, "move_cost": 1500 }, - { - "type": "link_up", - "menu_text": "Plug in / Unplug", - "ammo_scale": 0, - "cable_length": 3, - "charge_rate": "70 W" - } + { "type": "link_up", "cable_length": 3, "charge_rate": "70 W" } ], "flags": [ "ALLOWS_REMOTE_USE" ], "pocket_data": [ @@ -1171,13 +1141,7 @@ "cost_scaling": 0.1, "move_cost": 1500 }, - { - "type": "link_up", - "menu_text": "Plug in / Unplug", - "ammo_scale": 0, - "cable_length": 3, - "charge_rate": "70 W" - } + { "type": "link_up", "cable_length": 3, "charge_rate": "70 W" } ], "flags": [ "SPEAR", "BELT_CLIP", "ALLOWS_REMOTE_USE", "WATER_BREAK" ], "pocket_data": [ @@ -1615,7 +1579,7 @@ "symbol": ";", "color": "light_gray", "ammo": [ "battery" ], - "use_action": [ { "type": "link_up", "menu_text": "Plug in / Unplug", "ammo_scale": 0, "cable_length": 2, "charge_rate": "1500 W" } ], + "use_action": [ { "type": "link_up", "cable_length": 2, "charge_rate": "1500 W" } ], "pocket_data": [ { "pocket_type": "MAGAZINE_WELL", From 68359961cb51589736745e38bc4cdecf49effc06 Mon Sep 17 00:00:00 2001 From: Kamayana Date: Fri, 14 Jul 2023 01:58:20 -0500 Subject: [PATCH 36/68] Clang tidying --- src/item_contents.cpp | 2 +- src/iuse_actor.cpp | 11 ++++++----- src/iuse_actor.h | 4 ++-- src/map.cpp | 2 +- src/savegame_json.cpp | 2 ++ src/vehicle.h | 4 ++-- src/vehicle_part.cpp | 5 ++--- src/vehicle_use.cpp | 5 ++--- 8 files changed, 18 insertions(+), 17 deletions(-) diff --git a/src/item_contents.cpp b/src/item_contents.cpp index f5281a907d2e5..52ca61e065b4a 100644 --- a/src/item_contents.cpp +++ b/src/item_contents.cpp @@ -1785,7 +1785,7 @@ std::vector item_contents::cables() const if( pocket.is_type( item_pocket::pocket_type::CABLE ) ) { for( const item *it : pocket.all_items_top() ) { // TODO: remove flag check after 0.H - if( it->has_flag( flag_id( "CABLE_SPOOL" ) ) ) { + if( it->has_flag( STATIC( flag_id( "CABLE_SPOOL" ) ) ) ) { cables.emplace_back( it ); } } diff --git a/src/iuse_actor.cpp b/src/iuse_actor.cpp index 6bb003fcb8734..25ab3f6e6b692 100644 --- a/src/iuse_actor.cpp +++ b/src/iuse_actor.cpp @@ -129,9 +129,9 @@ static const itype_id itype_barrel_small( "barrel_small" ); static const itype_id itype_brazier( "brazier" ); static const itype_id itype_char_smoker( "char_smoker" ); static const itype_id itype_fire( "fire" ); +static const itype_id itype_power_cord( "power_cord" ); static const itype_id itype_stock_none( "stock_none" ); static const itype_id itype_syringe( "syringe" ); -static const itype_id itype_power_cord( "power_cord" ); static const mon_flag_str_id mon_flag_INTERIOR_AMMO( "INTERIOR_AMMO" ); @@ -4346,6 +4346,7 @@ void link_up_actor::info( const item &it, std::vector &dump ) const if( !can_extend.empty() ) { std::vector cable_types; + cable_types.reserve( can_extend.size() ); for( const std::string &cable_type : can_extend ) { cable_types.emplace_back( cable_type == "ELECTRICAL_DEVICES" ? "electrical device cables" : itype_id( cable_type )->nname( 1 ) ); @@ -5003,7 +5004,7 @@ std::optional link_up_actor::link_extend_cable( Character *p, item &it ) co } else { const auto filter = [&it]( const item & inv ) { if( !inv.has_flag( flag_CABLE_SPOOL ) || !inv.type->can_use( "link_up" ) || - inv.link && ( it.link || inv.link->has_state( link_state::needs_reeling ) ) ) { + ( inv.link && ( it.link || inv.link->has_state( link_state::needs_reeling ) ) ) ) { return false; } const link_up_actor *actor = static_cast @@ -5054,17 +5055,17 @@ std::optional link_up_actor::remove_extensions( Character *p, item &it ) co return !cable->has_flag( flag_CABLE_SPOOL ) || !cable->type->can_use( "link_up" ); } ); - if( all_cables.size() < 1 ) { + if( all_cables.empty() ) { // Delete any non-cables that somehow got into the pocket. it.get_contents().clear_pockets_if( []( item_pocket const & pocket ) { return pocket.is_type( item_pocket::pocket_type::CABLE ); } ); return 0; } + item cable_main_copy( *all_cables.back() ); all_cables.pop_back(); - - if( all_cables.size() > 0 ) { + if( !all_cables.empty() ) { for( item *cable : all_cables ) { item cable_copy( *cable ); cable_copy.get_contents().clear_items(); diff --git a/src/iuse_actor.h b/src/iuse_actor.h index ee467f7cf7a10..de9f8be2ad010 100644 --- a/src/iuse_actor.h +++ b/src/iuse_actor.h @@ -1046,8 +1046,8 @@ class link_up_actor : public iuse_actor std::set targets = { link_state::no_link, link_state::vehicle_port }; std::set can_extend = {}; - std::optional link_to_veh_app( Character *p, item &it, const bool to_ports ) const; - std::optional link_tow_cable( Character *p, item &it, const bool to_towing ) const; + 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 remove_extensions( Character *p, item &it ) const; diff --git a/src/map.cpp b/src/map.cpp index af301b171bc40..38cb8885416a8 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -5092,7 +5092,7 @@ item &map::add_item( const tripoint &p, item new_item ) // Process foods and temperature tracked items when they are added to the map, here instead of add_item_at() // to avoid double processing food during active item processing. - if( new_item.has_temperature() && !new_item.is_corpse() || new_item.link ) { + if( new_item.link || ( new_item.has_temperature() && !new_item.is_corpse() ) ) { new_item.process( *this, nullptr, p ); } diff --git a/src/savegame_json.cpp b/src/savegame_json.cpp index 51c27e917c59c..a8d0b0d9577bb 100644 --- a/src/savegame_json.cpp +++ b/src/savegame_json.cpp @@ -3362,6 +3362,7 @@ void vehicle_part::deserialize( const JsonObject &data ) data.read( "target_second_z", target.second.z ); data.read( "ammo_pref", ammo_pref ); data.read( "locked", locked ); + data.read( "last_disconnected", last_disconnected ); if( migration != nullptr ) { for( const itype_id &it : migration->add_veh_tools ) { @@ -3414,6 +3415,7 @@ void vehicle_part::serialize( JsonOut &json ) const } json.member( "ammo_pref", ammo_pref ); json.member( "locked", locked ); + json.member( "last_disconnected", last_disconnected ); json.end_object(); } diff --git a/src/vehicle.h b/src/vehicle.h index 47eb5c86aa843..c89ccb2daf1d0 100644 --- a/src/vehicle.h +++ b/src/vehicle.h @@ -1806,8 +1806,8 @@ class vehicle void shift_parts( map &here, const point &delta ); bool shift_if_needed( map &here ); - void shed_loose_parts( const bool can_shed_cables = true, const tripoint_bub_ms *src = nullptr, - const tripoint_bub_ms *dst = nullptr, const tripoint drop_offset = tripoint_zero ); + void shed_loose_parts( bool can_shed_cables = true, const tripoint_bub_ms *src = nullptr, + const tripoint_bub_ms *dst = nullptr, tripoint drop_offset = tripoint_zero ); /** * @name Vehicle turrets diff --git a/src/vehicle_part.cpp b/src/vehicle_part.cpp index 4126926647b3b..b8111efa0b669 100644 --- a/src/vehicle_part.cpp +++ b/src/vehicle_part.cpp @@ -93,7 +93,6 @@ item vehicle_part::properties_to_item() const tmp.link->t_state = link_state::vehicle_port; } - bool iuse_found = false; tmp.set_link_traits(); tmp.link->last_processed = calendar::turn; } @@ -559,8 +558,8 @@ bool vehicle_part::contains_liquid() const bool vehicle_part::is_battery() const { - return info().has_flag( VPFLAG_BATTERY ) || base.is_magazine() && - base.ammo_types().count( ammo_battery ); + return info().has_flag( VPFLAG_BATTERY ) || + ( base.is_magazine() && base.ammo_types().count( ammo_battery ) ); } bool vehicle_part::is_reactor() const diff --git a/src/vehicle_use.cpp b/src/vehicle_use.cpp index f62416e3d6d5b..80eba43e345fc 100644 --- a/src/vehicle_use.cpp +++ b/src/vehicle_use.cpp @@ -2078,7 +2078,7 @@ void vehicle::build_interact_menu( veh_menu &menu, const tripoint &p, bool with_ { if( vp_part->info().has_flag( "POWER_TRANSFER" ) ) { item drop = vp_part->properties_to_item(); - if( !magic && !drop.has_flag( flag_id( "NO_DROP" ) ) ) { + if( !magic && !drop.has_flag( STATIC( flag_id( "NO_DROP" ) ) ) ) { get_player_character().i_add_or_drop( drop ); } add_msg( _( "You detached the %s." ), drop.type_name() ); @@ -2103,10 +2103,9 @@ void vehicle::build_interact_menu( veh_menu &menu, const tripoint &p, bool with_ } if( vp_part->info().has_flag( "TOW_CABLE" ) ) { item drop = vp_part->properties_to_item(); - if( !magic && !drop.has_flag( flag_id( "NO_DROP" ) ) ) { + if( !magic && !drop.has_flag( STATIC( flag_id( "NO_DROP" ) ) ) ) { get_player_character().i_add_or_drop( drop ); } - const int index = index_of_part( vp_part ); add_msg( _( "You detached the %s." ), drop.type_name() ); remove_remote_part( *vp_part ); remove_part( *vp_part ); From 2bf5eed7e52190e3413fd777246937f6fb5474fc Mon Sep 17 00:00:00 2001 From: Kamayana Date: Fri, 14 Jul 2023 07:58:02 -0500 Subject: [PATCH 37/68] Set cable traits and cable part mounts immediately to avoid inaccuracies --- data/mods/TEST_DATA/appliance.json | 2 +- src/iuse_actor.cpp | 4 ++++ src/vehicle_use.cpp | 3 +++ tests/vehicle_test.cpp | 6 +++++- 4 files changed, 13 insertions(+), 2 deletions(-) diff --git a/data/mods/TEST_DATA/appliance.json b/data/mods/TEST_DATA/appliance.json index 914c06e52c01f..3e38cc7d4fd70 100644 --- a/data/mods/TEST_DATA/appliance.json +++ b/data/mods/TEST_DATA/appliance.json @@ -70,7 +70,7 @@ "epower": "0 W", "//": "Epower for POWER_TRANSFER stuff is how much percentage-wise loss there is in transmission", "durability": 120, - "description": "A power cord sticking out of an appliance. You need to plug it in a powered grid for the appliance to work properly.", + "description": "A power cord sticking out of an appliance. You need to plug it into a powered grid for the appliance to work properly.", "item": "test_power_cord", "flags": [ "NO_INSTALL_HIDDEN", "NO_UNINSTALL", "UNMOUNT_ON_DAMAGE", "POWER_TRANSFER" ], "variants": [ { "symbols": "{", "symbols_broken": "s" } ] diff --git a/src/iuse_actor.cpp b/src/iuse_actor.cpp index 25ab3f6e6b692..51b02716a512f 100644 --- a/src/iuse_actor.cpp +++ b/src/iuse_actor.cpp @@ -4822,11 +4822,13 @@ std::optional link_up_actor::link_to_veh_app( Character *p, item &it, prev_part.target.first = here.getabs( pnt ); prev_part.target.second = target_veh->global_square_location().raw(); prev_veh->install_part( vcoords1, std::move( prev_part ) ); + prev_veh->precalc_mounts( 1, prev_veh->pivot_rotation[1], prev_veh->pivot_anchor[1] ); vehicle_part target_part( vpid, item( it ) ); target_part.target.first = here.getabs( prev_veh->mount_to_tripoint( it.link->t_mount ) ); target_part.target.second = prev_veh->global_square_location().raw(); target_veh->install_part( vcoords2, std::move( target_part ) ); + target_veh->precalc_mounts( 1, target_veh->pivot_rotation[1], target_veh->pivot_anchor[1] ); if( p->has_item( it ) ) { //~ %1$s - first vehicle name, %2$s - second vehicle name - %3$s - cable name, @@ -4953,11 +4955,13 @@ std::optional link_up_actor::link_tow_cable( Character *p, item &it, prev_part.target.first = here.getabs( pnt ); prev_part.target.second = target_veh->global_square_location().raw(); prev_veh->install_part( vcoords1, std::move( prev_part ) ); + prev_veh->precalc_mounts( 1, prev_veh->pivot_rotation[1], prev_veh->pivot_anchor[1] ); vehicle_part target_part( vpid, item( it ) ); target_part.target.first = here.getabs( prev_veh->mount_to_tripoint( it.link->t_mount ) ); target_part.target.second = prev_veh->global_square_location().raw(); target_veh->install_part( vcoords2, std::move( target_part ) ); + target_veh->precalc_mounts( 1, target_veh->pivot_rotation[1], target_veh->pivot_anchor[1] ); if( p->has_item( it ) ) { //~ %1$s - first vehicle name, %2$s - second vehicle name - %3$s - tow cable name, diff --git a/src/vehicle_use.cpp b/src/vehicle_use.cpp index 80eba43e345fc..9d513083dd12d 100644 --- a/src/vehicle_use.cpp +++ b/src/vehicle_use.cpp @@ -549,6 +549,7 @@ item vehicle::init_cord( const tripoint &pos ) cord.link->t_state = link_state::vehicle_port; cord.link->t_veh_safe = get_safe_reference(); cord.link->t_abs_pos = get_map().getglobal( pos ); + cord.set_link_traits(); return cord; } @@ -587,12 +588,14 @@ void vehicle::connect( const tripoint &source_pos, const tripoint &target_pos ) 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] ); } double vehicle::engine_cold_factor( const vehicle_part &vp ) const diff --git a/tests/vehicle_test.cpp b/tests/vehicle_test.cpp index a3c4dff2d33a5..13e42a6f0173b 100644 --- a/tests/vehicle_test.cpp +++ b/tests/vehicle_test.cpp @@ -430,7 +430,7 @@ static void connect_power_line( const tripoint &src_pos, const tripoint &dst_pos cord.link = cata::make_value(); cord.link->t_state = link_state::vehicle_port; cord.link->t_abs_pos = here.getglobal( src_pos ); - cord.active = true; + cord.set_link_traits(); const optional_vpart_position target_vp = here.veh_at( dst_pos ); const optional_vpart_position source_vp = here.veh_at( src_pos ); @@ -452,12 +452,14 @@ static void connect_power_line( const tripoint &src_pos, const tripoint &dst_pos 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] ); } TEST_CASE( "power_cable_stretch_disconnect" ) @@ -490,6 +492,7 @@ TEST_CASE( "power_cable_stretch_disconnect" ) REQUIRE( app2.part_count() == 2 ); REQUIRE( app1.part( 1 ).get_base().type->maximum_charges() == 3 ); + REQUIRE( app1.part( 1 ).get_base().max_link_length() == 3 ); const int max_dist = app1.part( 1 ).get_base().type->maximum_charges(); @@ -530,6 +533,7 @@ TEST_CASE( "power_cable_stretch_disconnect" ) REQUIRE( app2.part_count() == 2 ); REQUIRE( app1.part( 1 ).get_base().type->maximum_charges() == 10 ); + REQUIRE( app1.part( 1 ).get_base().max_link_length() == 10 ); const int max_dist = app1.part( 1 ).get_base().type->maximum_charges(); From 973f2689b2fe094f60736d6237362790b63a9ed7 Mon Sep 17 00:00:00 2001 From: Kamayana Date: Mon, 17 Jul 2023 16:32:04 -0500 Subject: [PATCH 38/68] Fix linked repair tools --- src/activity_handlers.cpp | 6 +++--- src/item.cpp | 14 ++++++++------ src/item.h | 10 ++++++---- src/item_factory.cpp | 4 ++-- 4 files changed, 19 insertions(+), 15 deletions(-) diff --git a/src/activity_handlers.cpp b/src/activity_handlers.cpp index bc53593d90233..0006bc48d3d9e 100644 --- a/src/activity_handlers.cpp +++ b/src/activity_handlers.cpp @@ -2416,7 +2416,7 @@ void repair_item_finish( player_activity *act, Character *you, bool no_menu ) fix.tname() ); ammotype current_ammo; std::string ammo_name; - if( used_tool->has_flag( flag_USE_UPS ) ) { + if( used_tool->link || used_tool->has_flag( flag_USE_UPS ) ) { ammo_name = _( "battery" ); current_ammo = ammo_battery; } else if( used_tool->has_flag( flag_USES_BIONIC_POWER ) ) { @@ -2431,7 +2431,7 @@ void repair_item_finish( player_activity *act, Character *you, bool no_menu ) ammo_name = item::nname( used_tool->ammo_current() ); } - int ammo_remaining = used_tool->ammo_remaining( you ); + int ammo_remaining = used_tool->ammo_remaining( you, true ); std::set valid_entries = actor->get_valid_repair_materials( fix ); const inventory &crafting_inv = you->crafting_inventory(); @@ -2457,7 +2457,7 @@ void repair_item_finish( player_activity *act, Character *you, bool no_menu ) } title += string_format( _( "Charges: %s/%s %s (%s per use)\n" ), - ammo_remaining, used_tool->ammo_capacity( current_ammo ), + ammo_remaining, used_tool->ammo_capacity( current_ammo, true ), ammo_name, used_tool->ammo_required() ); title += string_format( _( "Materials available: %s\n" ), string_join( material_list, ", " ) ); diff --git a/src/item.cpp b/src/item.cpp index 92b062e7941d0..2c6be3c640eaf 100644 --- a/src/item.cpp +++ b/src/item.cpp @@ -10356,7 +10356,7 @@ int item::shots_remaining( const Character *carrier ) const return ret; } -int item::ammo_remaining( const Character *carrier, bool cable_links ) const +int item::ammo_remaining( const Character *carrier, const bool include_linked ) const { int ret = 0; @@ -10367,7 +10367,7 @@ int item::ammo_remaining( const Character *carrier, bool cable_links ) const } // Cable connections - if( cable_links && link_length() >= 0 && link->efficiency >= 0.001f ) { + if( include_linked && link_length() >= 0 && link->efficiency >= 0.001f ) { if( link->t_veh_safe ) { ret += link->t_veh_safe->connected_battery_power_level().first; } else { @@ -10416,9 +10416,9 @@ int item::ammo_remaining( const Character *carrier, bool cable_links ) const return ret; } -int item::ammo_remaining( bool cable_links ) const +int item::ammo_remaining( const bool include_linked ) const { - return ammo_remaining( nullptr, cable_links ); + return ammo_remaining( nullptr, include_linked ); } units::energy item::energy_remaining( const Character *carrier ) const @@ -10472,10 +10472,12 @@ int item::remaining_ammo_capacity() const } } -int item::ammo_capacity( const ammotype &ammo ) const +int item::ammo_capacity( const ammotype &ammo, bool include_linked ) const { const item *mag = magazine_current(); - if( mag ) { + if( include_linked && link ) { + return link->t_veh_safe ? link->t_veh_safe->connected_battery_power_level().second : 0; + } else if( mag ) { return mag->ammo_capacity( ammo ); } else if( has_flag( flag_USES_BIONIC_POWER ) ) { return units::to_kilojoule( get_player_character().get_max_power_level() ); diff --git a/src/item.h b/src/item.h index c9c52365abd34..38a94e07bb1e4 100644 --- a/src/item.h +++ b/src/item.h @@ -2325,15 +2325,17 @@ class item : public visitable /** * Quantity of ammunition currently loaded in tool, gun or auxiliary gunmod. * @param carrier is used for UPS and bionic power for tools - * @param cable_links Add cable-linked vehicles' ammo to the ammo count + * @param include_linked Add cable-linked vehicles' ammo to the ammo count */ - int ammo_remaining( const Character *carrier = nullptr, bool cable_links = false ) const; - int ammo_remaining( bool cable_links ) const; + int ammo_remaining( const Character *carrier = nullptr, bool include_linked = false ) const; + int ammo_remaining( bool include_linked ) const; /** * ammo capacity for a specific ammo + * @param ammo The ammo type to get the capacity for + * @param include_linked If linked up, return linked electricity grid's capacity */ - int ammo_capacity( const ammotype &ammo ) const; + int ammo_capacity( const ammotype &ammo, bool include_linked = false ) const; /** * how much more ammo can fit into this item diff --git a/src/item_factory.cpp b/src/item_factory.cpp index deee176a51f9d..9c25a3c282799 100644 --- a/src/item_factory.cpp +++ b/src/item_factory.cpp @@ -2402,7 +2402,7 @@ void Item_factory::check_definitions() const inp_mngr.pump_events(); } } - +#pragma optimize("", off) //Returns the template with the given identification tag const itype *Item_factory::find_template( const itype_id &id ) const { @@ -2440,7 +2440,7 @@ const itype *Item_factory::find_template( const itype_id &id ) const m_runtimes[ id ].reset( def ); return def; } - +#pragma optimize("", on) Item_spawn_data *Item_factory::get_group( const item_group_id &group_tag ) { GroupMap::iterator group_iter = m_template_groups.find( group_tag ); From 40a1d84d275881075f90a513ff837ce99d0f13fd Mon Sep 17 00:00:00 2001 From: Kamayana Date: Thu, 20 Jul 2023 15:31:05 -0500 Subject: [PATCH 39/68] Fix invoke_item_activity losing the method after cancel_activity --- src/activity_actor.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/activity_actor.cpp b/src/activity_actor.cpp index f647ad955595f..5c400ff9f8f75 100644 --- a/src/activity_actor.cpp +++ b/src/activity_actor.cpp @@ -5682,8 +5682,9 @@ void invoke_item_activity_actor::do_turn( player_activity &, Character &who ) who.invoke_item( item.get_item() ); return; } + std::string _method = method; who.cancel_activity(); - who.invoke_item( item.get_item(), method ); + who.invoke_item( item.get_item(), _method ); } void invoke_item_activity_actor::serialize( JsonOut &jsout ) const From 4c0bf5d430225db55cdbcb76a82f4d644a597351 Mon Sep 17 00:00:00 2001 From: Kamayana Date: Thu, 20 Jul 2023 16:12:42 -0500 Subject: [PATCH 40/68] Remove debug code --- src/item_factory.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/item_factory.cpp b/src/item_factory.cpp index 9c25a3c282799..deee176a51f9d 100644 --- a/src/item_factory.cpp +++ b/src/item_factory.cpp @@ -2402,7 +2402,7 @@ void Item_factory::check_definitions() const inp_mngr.pump_events(); } } -#pragma optimize("", off) + //Returns the template with the given identification tag const itype *Item_factory::find_template( const itype_id &id ) const { @@ -2440,7 +2440,7 @@ const itype *Item_factory::find_template( const itype_id &id ) const m_runtimes[ id ].reset( def ); return def; } -#pragma optimize("", on) + Item_spawn_data *Item_factory::get_group( const item_group_id &group_tag ) { GroupMap::iterator group_iter = m_template_groups.find( group_tag ); From 823fa0be255841a2d24dd89ca42f83276c40ce0f Mon Sep 17 00:00:00 2001 From: Kamayana Date: Thu, 20 Jul 2023 16:40:35 -0500 Subject: [PATCH 41/68] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Jianxiang Wang (王健翔) --- data/raw/keybindings/vehicle.json | 2 +- src/iuse_actor.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/data/raw/keybindings/vehicle.json b/data/raw/keybindings/vehicle.json index 4c557de293819..816a91596acef 100644 --- a/data/raw/keybindings/vehicle.json +++ b/data/raw/keybindings/vehicle.json @@ -137,7 +137,7 @@ "type": "keybinding", "category": "VEHICLE", "name": "Fill container with water from faucet", - "bindings": [ { "input_method": "keyboard_any", "key": "F" }, { "input_method": "keyboard_code", "key": "f", "mod": [ "shift" ] } ] + "bindings": [ { "input_method": "keyboard_char", "key": "F" }, { "input_method": "keyboard_code", "key": "f", "mod": [ "shift" ] } ] }, { "id": "FAUCET_DRINK", diff --git a/src/iuse_actor.cpp b/src/iuse_actor.cpp index 51b02716a512f..29a5e520fa711 100644 --- a/src/iuse_actor.cpp +++ b/src/iuse_actor.cpp @@ -4348,7 +4348,7 @@ void link_up_actor::info( const item &it, std::vector &dump ) const std::vector cable_types; cable_types.reserve( can_extend.size() ); for( const std::string &cable_type : can_extend ) { - cable_types.emplace_back( cable_type == "ELECTRICAL_DEVICES" ? "electrical device cables" : + cable_types.emplace_back( cable_type == "ELECTRICAL_DEVICES" ? _( "electrical device cables" ) : itype_id( cable_type )->nname( 1 ) ); } std::string cable_type_list = enumerate_as_string( cable_types, enumeration_conjunction::or_ ); From ddd2e10c71d0af6ab62e647bf329b0c08d8bb6fa Mon Sep 17 00:00:00 2001 From: Kamayana Date: Wed, 26 Jul 2023 00:21:32 -0700 Subject: [PATCH 42/68] More readable booleans Co-authored-by: andrei <68240139+andrei8l@users.noreply.github.com> --- src/item.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/item.cpp b/src/item.cpp index 2c6be3c640eaf..1c127f805ece2 100644 --- a/src/item.cpp +++ b/src/item.cpp @@ -13187,12 +13187,12 @@ int item::charge_linked_batteries( vehicle &linked_veh, int turns_elapsed ) if( power_in ) { const int battery_deficit = linked_veh.discharge_battery( transfer_total, true ); // Around 85% efficient by default; a few of the discharges don't actually recharge - if( battery_deficit == 0 && !( short_time_passed && rng_float( 0.0, 1.0 ) > link->efficiency ) ) { + if( battery_deficit == 0 && ( !short_time_passed || rng_float( 0.0, 1.0 ) <= link->efficiency ) ) { ammo_set( itype_battery, ammo_remaining() + transfer_total ); } } else { // Around 85% efficient by default; a few of the discharges don't actually charge - if( !( short_time_passed && rng_float( 0.0, 1.0 ) > link->efficiency ) ) { + if( !short_time_passed || rng_float( 0.0, 1.0 ) <= link->efficiency ) { const int battery_surplus = linked_veh.charge_battery( transfer_total, true ); if( battery_surplus == 0 ) { ammo_set( itype_battery, ammo_remaining() - transfer_total ); From 5223b4ccfcd3b3f88492d535c2d28bfe32ce2c07 Mon Sep 17 00:00:00 2001 From: Maleclypse <54345792+Maleclypse@users.noreply.github.com> Date: Thu, 27 Jul 2023 19:41:36 -0500 Subject: [PATCH 43/68] Update src/item.cpp Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- src/item.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/item.cpp b/src/item.cpp index 1c127f805ece2..b0f5418e9f0ae 100644 --- a/src/item.cpp +++ b/src/item.cpp @@ -13192,7 +13192,7 @@ int item::charge_linked_batteries( vehicle &linked_veh, int turns_elapsed ) } } else { // Around 85% efficient by default; a few of the discharges don't actually charge - if( !short_time_passed || rng_float( 0.0, 1.0 ) <= link->efficiency ) { + if( !short_time_passed || rng_float( 0.0, 1.0 ) <= link->efficiency ) { const int battery_surplus = linked_veh.charge_battery( transfer_total, true ); if( battery_surplus == 0 ) { ammo_set( itype_battery, ammo_remaining() - transfer_total ); From cf91cd765290765cfd01769caaa724f36abc26ba Mon Sep 17 00:00:00 2001 From: Kamayana Date: Fri, 28 Jul 2023 05:00:02 -0500 Subject: [PATCH 44/68] Make cable extensions contribute volume and weight --- src/item_factory.cpp | 9 ++------- src/iuse_actor.cpp | 43 ++++++++++++++++++++++++++++++++++--------- 2 files changed, 36 insertions(+), 16 deletions(-) diff --git a/src/item_factory.cpp b/src/item_factory.cpp index 80fbaa41e9029..4c09b4bd6b776 100644 --- a/src/item_factory.cpp +++ b/src/item_factory.cpp @@ -3721,14 +3721,9 @@ void Item_factory::add_special_pockets( itype &def ) def.pockets.emplace_back( item_pocket::pocket_type::MIGRATION ); } if( !has_pocket_type( def.pockets, item_pocket::pocket_type::CABLE ) ) { - const use_function *iuse = def.get_use( "link_up" ); - if( iuse != nullptr ) { + if( def.get_use( "link_up" ) != nullptr ) { pocket_data cable_pocket( item_pocket::pocket_type::CABLE ); - cable_pocket.rigid = true; - cable_pocket.volume_capacity = units::from_milliliter( 1 ); - cable_pocket.max_contains_weight = units::from_gram( 1 ); - cable_pocket.weight_multiplier = 0.0f; - cable_pocket.volume_multiplier = 0.0f; + cable_pocket.rigid = false; def.pockets.emplace_back( cable_pocket ); } } diff --git a/src/iuse_actor.cpp b/src/iuse_actor.cpp index fbc274b839de0..323226520d7fd 100644 --- a/src/iuse_actor.cpp +++ b/src/iuse_actor.cpp @@ -5041,31 +5041,56 @@ std::optional link_up_actor::link_extend_cable( Character *p, item &it ) co return std::nullopt; } - item *extension = is_cable_item ? &it : &*selected; - item *extended = is_cable_item ? &*selected : ⁢ + 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 ); + std::string extended_name = extended->type_name(); - std::vector all_cables = extension->cables(); - all_cables.emplace_back( extension ); + const bool can_stay_put = extended.parent_pocket() == extension.parent_pocket() || + ( extended.volume_capacity() - extension->volume() >= 0_ml && + extended.weight_capacity() - extension->weight() >= 0_gram ); // Put the extension cable and all of its attached cables, if any, into the extended item's CABLE pocket. + std::vector all_cables = extension->cables(); + all_cables.emplace_back( &*extension ); for( const item *cable : all_cables ) { item cable_copy( *cable ); cable_copy.get_contents().clear_items(); cable_copy.link.reset(); if( !extended->put_in( cable_copy, item_pocket::pocket_type::CABLE ).success() ) { - debugmsg( "Failed to put %s inside %s!", cable_copy.tname(), extended->tname() ); + debugmsg( "Failed to put %s inside %s!", cable_copy.type_name(), extended_name ); } } - if( extension->link ) { extended->link = extension->link; } extended->set_link_traits(); - extended->process( get_map(), p, p->pos() ); + + // If the device's containing pocket can't also hold the extension cord, move to another pocket. If no other pocket works, ask the player. + if( !can_stay_put ) { + if( extended.has_parent() && extended.parent_item()->insert_cost( *extended ) > -1 ) { + if( !extended.parent_item()->put_in( *extended, item_pocket::pocket_type::CONTAINER ).success() ) { + debugmsg( "Failed to put %s inside %s!", extended_name, extended.parent_item()->type_name() ); + it.get_contents().clear_pockets_if( []( item_pocket const & pocket ) { + return pocket.is_type( item_pocket::pocket_type::CABLE ); + } ); + extended->link.reset(); + 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_name, extension->type_name() ) ) { + it.get_contents().clear_pockets_if( []( item_pocket const & pocket ) { + return pocket.is_type( item_pocket::pocket_type::CABLE ); + } ); + extended->link.reset(); + return std::nullopt; + } + } 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->type_name(), extension->type_name() ); - p->i_rem( extension ); + _( "You extend the %1$s's cable with the %2$s." ), extended_name, extension->type_name() ); + extension.remove_item(); + p->invalidate_inventory_validity_cache(); p->moves -= move_cost; return 0; } From bfbe8890a4eababc7e6dd80f1fe04f65243b5409 Mon Sep 17 00:00:00 2001 From: Kamayana Date: Fri, 28 Jul 2023 05:01:48 -0500 Subject: [PATCH 45/68] Minor fixes --- data/json/items/tool/radio_tools.json | 1 - src/item.cpp | 3 --- src/item.h | 2 +- 3 files changed, 1 insertion(+), 5 deletions(-) diff --git a/data/json/items/tool/radio_tools.json b/data/json/items/tool/radio_tools.json index 3e203fd06c095..ef19b85574e84 100644 --- a/data/json/items/tool/radio_tools.json +++ b/data/json/items/tool/radio_tools.json @@ -119,7 +119,6 @@ "description": "A portable radio that is turned on and continually draining its batteries. It is playing the broadcast being sent from any nearby radio towers.", "power_draw": "500 mW", "revert_to": "radio", - "use_action": [ "RADIO_ON", { "type": "link_up", "menu_text": "Plug in / Unplug", "ammo_scale": 0, "cable_length": 3, "charge_rate": "5 W" }, diff --git a/src/item.cpp b/src/item.cpp index f2577cf0985f7..b926356cbca72 100644 --- a/src/item.cpp +++ b/src/item.cpp @@ -12987,9 +12987,6 @@ int item::max_link_length() const int total_length = actor->cable_length != -1 ? actor->cable_length : type->maximum_charges(); for( const item *cable : cables() ) { - if( !cable->has_flag( flag_CABLE_SPOOL ) ) { - continue; - } total_length += cable->type->maximum_charges(); } diff --git a/src/item.h b/src/item.h index 17a47fdf30d01..4ed1a95023ff5 100644 --- a/src/item.h +++ b/src/item.h @@ -1470,7 +1470,7 @@ class item : public visitable /** * @return The item's maximum possible link length, including extensions. Item doesn't need an active link. - * @return `-1` if the item doesn't have a link_up action. + * @return `-2` if the item doesn't have a link_up action. */ int max_link_length() const; From 10bc8468238340a30180e186ca411c6af4664d0e Mon Sep 17 00:00:00 2001 From: Kamayana Date: Sat, 29 Jul 2023 00:06:08 -0500 Subject: [PATCH 46/68] Use const for minimum link efficiency --- src/item.cpp | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/item.cpp b/src/item.cpp index b926356cbca72..649cc72cb0dea 100644 --- a/src/item.cpp +++ b/src/item.cpp @@ -223,6 +223,8 @@ static const std::string flag_BLACKPOWDER_FOULING_DAMAGE( "BLACKPOWDER_FOULING_D // item pricing static const int PRICE_FILTHY_MALUS = 100; // cents +static constexpr float MIN_LINK_EFFICIENCY = 0.001f; + class npc_class; using npc_class_id = string_id; @@ -10402,7 +10404,7 @@ int item::ammo_remaining( const Character *carrier, const bool include_linked ) } // Cable connections - if( include_linked && link_length() >= 0 && link->efficiency >= 0.001f ) { + if( include_linked && link_length() >= 0 && link->efficiency >= MIN_LINK_EFFICIENCY ) { if( link->t_veh_safe ) { ret += link->t_veh_safe->connected_battery_power_level().first; } else { @@ -10590,7 +10592,7 @@ int item::ammo_consume( int qty, const tripoint &pos, Character *carrier ) // Consume power from appliances/vehicles connected with cables if( link ) { - if( link->t_veh_safe && link->efficiency >= 0.001f ) { + if( link->t_veh_safe && link->efficiency >= MIN_LINK_EFFICIENCY ) { qty = link->t_veh_safe->discharge_battery( qty, true ); } else { const optional_vpart_position vp = get_map().veh_at( link->t_abs_pos ); @@ -12951,7 +12953,7 @@ void item::set_link_traits() 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; + link->efficiency = it_actor->efficiency < MIN_LINK_EFFICIENCY ? 0.0f : link->efficiency; // Reset s_bub_pos to force the item to check the length during process_link. link->s_bub_pos = tripoint_min; @@ -12963,14 +12965,15 @@ void item::set_link_traits() ( cable->get_use( "link_up" )->get_actor_ptr() ); link->max_length += actor->cable_length == -1 ? cable->type->maximum_charges() : actor->cable_length; - link->efficiency = link->efficiency < 0.001f ? 0.0f : link->efficiency * actor->efficiency; + link->efficiency = link->efficiency < MIN_LINK_EFFICIENCY ? 0.0f : + link->efficiency * actor->efficiency; } } int item::link_length() const { - return !link || - link->has_no_links() ? -2 : link->has_state( link_state::needs_reeling ) ? -1 : link->length; + return !link || link->has_no_links() ? -2 : + link->has_state( link_state::needs_reeling ) ? -1 : link->length; } int item::max_link_length() const @@ -13161,7 +13164,7 @@ bool item::process_link( map &here, Character *carrier, const tripoint &pos ) if( !is_cable_item ) { int power_draw = 0; - if( link->efficiency < 0.001f ) { + if( link->efficiency < MIN_LINK_EFFICIENCY ) { return false; } // Recharge or charge linked batteries @@ -13187,7 +13190,7 @@ bool item::process_link( map &here, Character *carrier, const tripoint &pos ) int item::charge_linked_batteries( vehicle &linked_veh, int turns_elapsed ) { if( link->charge_rate == 0 || turns_elapsed < 1 || - link->charge_interval < 1 || link->efficiency < 0.001f ) { + link->charge_interval < 1 || link->efficiency < MIN_LINK_EFFICIENCY ) { return link->charge_rate; } From 93554fa6496ff3a18ed8334a804b3927a263e1ce Mon Sep 17 00:00:00 2001 From: Kamayana Date: Sat, 29 Jul 2023 03:26:37 -0500 Subject: [PATCH 47/68] Revamp how max_length is handled so its recalculated less often --- src/activity_actor.cpp | 5 +++- src/item.cpp | 58 +++++++++++++++++------------------------- src/item.h | 2 +- src/iuse_actor.cpp | 7 +++-- 4 files changed, 34 insertions(+), 38 deletions(-) diff --git a/src/activity_actor.cpp b/src/activity_actor.cpp index 86d70c5b0d655..b3f245e922fd7 100644 --- a/src/activity_actor.cpp +++ b/src/activity_actor.cpp @@ -5049,7 +5049,10 @@ void reel_cable_activity_actor::start( player_activity &act, Character & ) void reel_cable_activity_actor::finish( player_activity &act, Character &who ) { - cable->link.reset(); + cable->link->length = 0; + cable->link->s_state = link_state::no_link; + cable->link->t_state = link_state::no_link; + cable->reset_link( &who, -2 ); who.add_msg_if_player( m_info, string_format( cable->has_flag( flag_CABLE_SPOOL ) ? _( "You reel in the %s and wind it up." ) : _( "You reel in the %s's cable and wind it up." ), cable->type_name() ) ); diff --git a/src/item.cpp b/src/item.cpp index 649cc72cb0dea..8c1dfaabb184d 100644 --- a/src/item.cpp +++ b/src/item.cpp @@ -6854,27 +6854,27 @@ std::string item::display_name( unsigned int quantity ) const amt = " (" + ammotext + ")"; } - if( type->can_use( "link_up" ) ) { + if( link ) { std::string extensions = cables().empty() ? "" : string_format( "+%d", cables().size() ); - if( link ) { - if( link->has_state( link_state::needs_reeling ) ) { - cable = string_format( _( " (%1$s cable%2$s)" ), colorize( string_format( "×/%d", - link->max_length ), c_light_red ), extensions ); + const int link_len = link_length(); + const int link_max_len = max_link_length(); + if( link_len <= -2 ) { + cable = string_format( _( " (-/%1$d cable%2$s)" ), link_max_len, extensions ); + } else if( link_len == -1 ) { + cable = string_format( _( " (%1$s cable%2$s)" ), + colorize( string_format( "×/%d", link_max_len ), c_light_red ), extensions ); + } else { + nc_color cable_color; + const double ratio = static_cast( link_len ) / static_cast( link_max_len ); + if( ratio < 1.0 / 3.0 ) { + cable_color = c_light_green; + } else if( ratio < 2.0 / 3.0 ) { + cable_color = c_yellow; } else { - nc_color cable_color; - const double ratio = static_cast( link->length ) / static_cast( link->max_length ); - if( ratio < 1.0 / 3.0 ) { - cable_color = c_light_green; - } else if( ratio < 2.0 / 3.0 ) { - cable_color = c_yellow; - } else { - cable_color = c_red; - } - cable = string_format( " (%s)", colorize( string_format( _( "%d/%d cable%s" ), - link->max_length - link->length, link->max_length, extensions ), cable_color ) ); + cable_color = c_red; } - } else if( !extensions.empty() ) { - cable = string_format( _( " (-/%1$d cable%2$s)" ), max_link_length(), extensions ); + cable = string_format( " (%s)", colorize( string_format( _( "%d/%d cable%s" ), + link_max_len - link_len, link_max_len, extensions ), cable_color ) ); } } @@ -12978,22 +12978,7 @@ int item::link_length() const int item::max_link_length() const { - if( link ) { - return link->max_length != -1 ? link->max_length : type->maximum_charges(); - } - if( !type->can_use( "link_up" ) ) { - return -2; - } - - const link_up_actor *actor = static_cast - ( get_use( "link_up" )->get_actor_ptr() ); - int total_length = actor->cable_length != -1 ? actor->cable_length : type->maximum_charges(); - - for( const item *cable : cables() ) { - total_length += cable->type->maximum_charges(); - } - - return total_length; + return !link ? -2 : link->max_length != -1 ? link->max_length : type->maximum_charges(); } bool item::process_link( map &here, Character *carrier, const tripoint &pos ) @@ -13309,6 +13294,11 @@ bool item::reset_link( Character *p, int vpart_index, } link.reset(); + if( !cables().empty() ) { + // If there are extensions, keep link active to maintain max_length. + link = cata::make_value(); + set_link_traits(); + } return has_flag( flag_NO_DROP ); } diff --git a/src/item.h b/src/item.h index 4ed1a95023ff5..8dbaa31d58821 100644 --- a/src/item.h +++ b/src/item.h @@ -1470,7 +1470,7 @@ class item : public visitable /** * @return The item's maximum possible link length, including extensions. Item doesn't need an active link. - * @return `-2` if the item doesn't have a link_up action. + * @return `-2` if the item has no active link or extensions. */ int max_link_length() const; diff --git a/src/iuse_actor.cpp b/src/iuse_actor.cpp index 323226520d7fd..e45d88be8479e 100644 --- a/src/iuse_actor.cpp +++ b/src/iuse_actor.cpp @@ -5013,7 +5013,7 @@ std::optional link_up_actor::link_extend_cable( Character *p, item &it ) co if( is_cable_item ) { const bool can_extend_devices = can_extend.find( "ELECTRICAL_DEVICES" ) != can_extend.end(); const auto filter = [this, &it, &can_extend_devices]( const item & inv ) { - if( inv.link && ( it.link || inv.link->has_state( link_state::needs_reeling ) ) ) { + if( inv.link && ( it.link_length() >= 0 || inv.link->has_state( link_state::needs_reeling ) ) ) { return false; } if( !inv.has_flag( flag_CABLE_SPOOL ) ) { @@ -5026,7 +5026,7 @@ std::optional link_up_actor::link_extend_cable( Character *p, item &it ) co } else { const auto filter = [&it]( const item & inv ) { if( !inv.has_flag( flag_CABLE_SPOOL ) || !inv.type->can_use( "link_up" ) || - ( inv.link && ( it.link || inv.link->has_state( link_state::needs_reeling ) ) ) ) { + ( inv.link && ( it.link_length() >= 0 || inv.link->has_state( link_state::needs_reeling ) ) ) ) { return false; } const link_up_actor *actor = static_cast @@ -5063,6 +5063,9 @@ std::optional link_up_actor::link_extend_cable( Character *p, item &it ) co if( extension->link ) { extended->link = extension->link; } + if( !extended->link ) { + extended->link = cata::make_value(); + } extended->set_link_traits(); // If the device's containing pocket can't also hold the extension cord, move to another pocket. If no other pocket works, ask the player. From fa5f4896b41a2c637092db066537e0bec71a828d Mon Sep 17 00:00:00 2001 From: Kamayana Date: Sat, 29 Jul 2023 06:41:22 -0500 Subject: [PATCH 48/68] Rework link_extend_cable's weight/volume check to be more readable and functional --- src/iuse_actor.cpp | 69 ++++++++++++++++++++++++++++------------------ 1 file changed, 42 insertions(+), 27 deletions(-) diff --git a/src/iuse_actor.cpp b/src/iuse_actor.cpp index e45d88be8479e..16629537699f9 100644 --- a/src/iuse_actor.cpp +++ b/src/iuse_actor.cpp @@ -5043,11 +5043,22 @@ std::optional link_up_actor::link_extend_cable( Character *p, item &it ) co 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 ); - std::string extended_name = extended->type_name(); + std::optional extended_copy; - const bool can_stay_put = extended.parent_pocket() == extension.parent_pocket() || - ( extended.volume_capacity() - extension->volume() >= 0_ml && - extended.weight_capacity() - extension->weight() >= 0_gram ); + // We'll make a copy the extended item and check pocket weight/volume capacity if: + // 1. The extended item is in a container, + // 2. The extended item and extension cord(s) aren't in the same pocket, and + // 3. The extended item is in a pocket without enough remaining room for the extension cord(s). + if( extended.where() == item_location::type::container && + extended.parent_pocket() != extension.parent_pocket() && + ( extended.volume_capacity() - extension->volume() < 0_ml || + extended.weight_capacity() - extension->weight() < 0_gram ) ) { + + extended_copy = *extended; + } + + //item *extended_ptr = has_to_move ? &extended_copy.value() : &*extended; + item *extended_ptr = extended_copy ? &extended_copy.value() : &*extended; // Put the extension cable and all of its attached cables, if any, into the extended item's CABLE pocket. std::vector all_cables = extension->cables(); @@ -5056,42 +5067,46 @@ std::optional link_up_actor::link_extend_cable( Character *p, item &it ) co item cable_copy( *cable ); cable_copy.get_contents().clear_items(); cable_copy.link.reset(); - if( !extended->put_in( cable_copy, item_pocket::pocket_type::CABLE ).success() ) { - debugmsg( "Failed to put %s inside %s!", cable_copy.type_name(), extended_name ); + if( !extended_ptr->put_in( cable_copy, item_pocket::pocket_type::CABLE ).success() ) { + debugmsg( "Failed to put %s inside %s!", cable_copy.type_name(), extended_ptr->type_name() ); } } - if( extension->link ) { - extended->link = extension->link; - } - if( !extended->link ) { - extended->link = cata::make_value(); + if( !extended_ptr->link ) { + extended_ptr->link = cata::make_value(); } - extended->set_link_traits(); - - // If the device's containing pocket can't also hold the extension cord, move to another pocket. If no other pocket works, ask the player. - if( !can_stay_put ) { - if( extended.has_parent() && extended.parent_item()->insert_cost( *extended ) > -1 ) { - if( !extended.parent_item()->put_in( *extended, item_pocket::pocket_type::CONTAINER ).success() ) { - debugmsg( "Failed to put %s inside %s!", extended_name, extended.parent_item()->type_name() ); + if( extension->link ) { + extended_ptr->link = extension->link; + } + extended_ptr->set_link_traits(); + + 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() ) { + debugmsg( "Failed to put %s inside %s!", extended_ptr->type_name(), + extended.parent_item()->type_name() ); it.get_contents().clear_pockets_if( []( item_pocket const & pocket ) { return pocket.is_type( item_pocket::pocket_type::CABLE ); } ); - extended->link.reset(); 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_name, extension->type_name() ) ) { - it.get_contents().clear_pockets_if( []( item_pocket const & pocket ) { - return pocket.is_type( item_pocket::pocket_type::CABLE ); - } ); - extended->link.reset(); - return std::nullopt; + } 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() ) ) { + return std::nullopt; + } + extended.parent_pocket()->add( *extended_ptr ); + 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_name, extension->type_name() ); + _( "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->moves -= move_cost; From 9e1edc2283b4e3ae1309f6b9b0e1bc16955741f2 Mon Sep 17 00:00:00 2001 From: Kamayana Date: Sat, 29 Jul 2023 06:43:45 -0500 Subject: [PATCH 49/68] Rework link length sorting, moving it to sort_compare --- src/inventory_ui.cpp | 10 ++++------ src/item.cpp | 14 ++++++++++++++ src/item.h | 5 +++++ 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/src/inventory_ui.cpp b/src/inventory_ui.cpp index 0f5bc937684ea..c1fa34631eb40 100644 --- a/src/inventory_ui.cpp +++ b/src/inventory_ui.cpp @@ -86,12 +86,9 @@ item_name_t &get_cached_name( item const *it ) { auto iter = item_name_cache.find( it ); if( iter == item_name_cache.end() ) { - const std::string name = remove_color_tags( it->tname( 1, false, 0, true, false ) ); - const std::string name_full = string_format( "%s%d%d", - remove_color_tags( it->tname( 1, true, 0, true, false ) ), - it->max_link_length(), it->link_length() ); return item_name_cache - .emplace( it, item_name_t{ name, name_full } ) + .emplace( it, item_name_t{ remove_color_tags( it->tname( 1, false, 0, true, false ) ), + remove_color_tags( it->tname( 1, true, 0, true, false ) ) } ) .first->second; } @@ -662,7 +659,8 @@ bool inventory_selector_preset::sort_compare( const inventory_entry &lhs, const inventory_entry &rhs ) const { auto const sort_key = []( inventory_entry const & e ) { - return std::make_tuple( *e.cached_name, *e.cached_name_full, e.generation ); + return std::make_tuple( *e.cached_name, *e.cached_name_full, + e.any_item()->link_sort_key(), e.generation ); }; return localized_compare( sort_key( lhs ), sort_key( rhs ) ); } diff --git a/src/item.cpp b/src/item.cpp index 8c1dfaabb184d..bc8f3113b887b 100644 --- a/src/item.cpp +++ b/src/item.cpp @@ -1517,6 +1517,12 @@ bool item::stacks_with( const item &rhs, bool check_components, bool combine_liq } } } + if( link_length() != rhs.link_length() ) { + return false; + } + if( max_link_length() != rhs.max_link_length() ) { + return false; + } const std::vector this_mods = mods(); const std::vector that_mods = rhs.mods(); if( this_mods.size() != that_mods.size() ) { @@ -12981,6 +12987,14 @@ int item::max_link_length() const return !link ? -2 : link->max_length != -1 ? link->max_length : type->maximum_charges(); } +int item::link_sort_key() const +{ + const int length = link_length(); + int key = length >= 0 ? -1000000000 : length == -1 ? 0 : 1000000000; + key += max_link_length() * 100000; + return key - length; +} + bool item::process_link( map &here, Character *carrier, const tripoint &pos ) { if( link_length() < 0 ) { diff --git a/src/item.h b/src/item.h index 8dbaa31d58821..b57428fac08c5 100644 --- a/src/item.h +++ b/src/item.h @@ -1474,6 +1474,11 @@ class item : public visitable */ int max_link_length() const; + /** + * Value used for sorting linked items in inventory lists. + */ + int link_sort_key() const; + /** * Brings a cable item back to its initial state. * @param p Set to character that's holding the linked item, nullptr if none. From dbafd423b31574eb209e958eca0f90165c39b432 Mon Sep 17 00:00:00 2001 From: Kamayana Date: Sat, 29 Jul 2023 09:24:12 -0500 Subject: [PATCH 50/68] Make AIM sort like inventory_ui does, including using link_sort_key --- src/advanced_inv.cpp | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/src/advanced_inv.cpp b/src/advanced_inv.cpp index 5df693038559e..df7ecea6dc8a8 100644 --- a/src/advanced_inv.cpp +++ b/src/advanced_inv.cpp @@ -643,19 +643,11 @@ struct advanced_inv_sorter { } break; } - // secondary sort by name - const std::string *n1; - const std::string *n2; - if( d1.name_without_prefix == d2.name_without_prefix ) { - //if names without prefix equal, compare full name - n1 = &d1.name; - n2 = &d2.name; - } else { - //else compare name without prefix - n1 = &d1.name_without_prefix; - n2 = &d2.name_without_prefix; - } - return localized_compare( *n1, *n2 ); + // secondary sort by name and link length + auto const sort_key = []( advanced_inv_listitem const & d ) { + return std::make_tuple( d.name_without_prefix, d.name, d.items.front()->link_sort_key() ); + }; + return localized_compare( sort_key( d1 ), sort_key( d2 ) ); } }; From cd02310100b34c023c90c9c745dbee6ad9c8b068 Mon Sep 17 00:00:00 2001 From: Kamayana Date: Sat, 29 Jul 2023 18:41:19 -0500 Subject: [PATCH 51/68] Let debug cables extend debug cables --- data/json/items/tool/cables.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/json/items/tool/cables.json b/data/json/items/tool/cables.json index ca81f2fde1629..f89eb3cfb4219 100644 --- a/data/json/items/tool/cables.json +++ b/data/json/items/tool/cables.json @@ -135,7 +135,7 @@ "menu_text": "Attach / Manage connections", "efficiency": 1.0, "targets": [ "no_link", "bio_cable", "ups", "solarpack", "vehicle_port", "vehicle_battery", "vehicle_tow" ], - "can_extend": [ "ELECTRICAL_DEVICES" ] + "can_extend": [ "ELECTRICAL_DEVICES", "jumper_cable_debug" ] } } ] From 8a9827457e29b0351ae2cefa2f0bfac15ea60810 Mon Sep 17 00:00:00 2001 From: Kamayana Date: Sat, 29 Jul 2023 19:46:20 -0500 Subject: [PATCH 52/68] Make getting/removing remote parts two separate functions --- src/vehicle.cpp | 19 ++++++++++++++----- src/vehicle.h | 8 ++++++-- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/src/vehicle.cpp b/src/vehicle.cpp index 66a58ec74c9b6..86aae79aa1faa 100644 --- a/src/vehicle.cpp +++ b/src/vehicle.cpp @@ -6563,22 +6563,31 @@ bool vehicle::no_towing_slack() const } -void vehicle::remove_remote_part( const vehicle_part &vp_local ) const +std::optional> vehicle::get_remote_part( const vehicle_part &vp_local ) const { map &here = get_map(); - vehicle *veh = here.find_vehicle( tripoint_abs_ms( vp_local.target.second ) ); - + vehicle *veh = find_vehicle( tripoint_abs_ms( vp_local.target.second ), &here ); // If the target vehicle is still there, ask it to remove its part if( veh != nullptr ) { const tripoint local_abs = here.getabs( global_part_pos3( vp_local ) ); for( const int remote_partnum : veh->loose_parts ) { vehicle_part &vp_remote = veh->parts[remote_partnum]; if( vp_remote.info().has_flag( "POWER_TRANSFER" ) && vp_remote.target.first == local_abs ) { - veh->remove_part( vp_remote ); - return; + return std::make_pair( veh, &vp_remote ); } } } + return std::nullopt; +} + +void vehicle::remove_remote_part( const vehicle_part &vp_local ) const +{ + std::optional> part = get_remote_part( vp_local ); + if( part ) { + part->first->remove_part( *part->second ); + // Rebuild vehicle cache to avoid creating an illusion of the remote car. + get_map().rebuild_vehicle_level_caches(); + } } void vehicle::shed_loose_parts( const bool can_shed_cables, const tripoint_bub_ms *src, diff --git a/src/vehicle.h b/src/vehicle.h index c89ccb2daf1d0..84456be24531a 100644 --- a/src/vehicle.h +++ b/src/vehicle.h @@ -1085,8 +1085,12 @@ class vehicle item_location part_base( int p ); /** - * Remove a part from a targeted remote vehicle. Useful for, e.g. power cables that have - * a vehicle part on both sides. + * Get the remote vehicle and part that a part is targeting. + * Useful for, e.g. power cables that have a vehicle part on both sides. + */ + std::optional> get_remote_part( const vehicle_part &vp_local ) const; + /** + * Remove the part on a targeted remote vehicle that a part is targeting. */ void remove_remote_part( const vehicle_part &vp_local ) const; /** From a7f5c4ff99f7d18404e0e08307a9f6c075f0b4d8 Mon Sep 17 00:00:00 2001 From: Kamayana Date: Sat, 29 Jul 2023 21:09:25 -0500 Subject: [PATCH 53/68] Change find_vehicle back into a static vehicle function --- src/item.cpp | 3 ++- src/map.cpp | 26 -------------------------- src/map.h | 6 ------ src/vehicle.cpp | 38 ++++++++++++++++++++++++++++++++------ src/vehicle.h | 10 +++++++++- 5 files changed, 43 insertions(+), 40 deletions(-) diff --git a/src/item.cpp b/src/item.cpp index bc8f3113b887b..cb7894dce1cec 100644 --- a/src/item.cpp +++ b/src/item.cpp @@ -13071,9 +13071,10 @@ bool item::process_link( map &here, Character *carrier, const tripoint &pos ) link->s_bub_pos = pos; check_length = true; } + // Re-establish vehicle pointer if it got lost or if this item just got loaded. if( !link->t_veh_safe ) { - vehicle *found_veh = here.find_vehicle( link->t_abs_pos ); + vehicle *found_veh = vehicle::find_vehicle( link->t_abs_pos ); if( !found_veh ) { return reset_link( carrier, -2, true, pos ); } diff --git a/src/map.cpp b/src/map.cpp index 08d1195463504..94518d39fa848 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -1306,32 +1306,6 @@ vehicle *map::veh_at_internal( const tripoint &p, int &part_num ) return const_cast( const_cast( this )->veh_at_internal( p, part_num ) ); } -vehicle *map::find_vehicle( const tripoint_abs_ms &where ) const -{ - // Is it in the reality bubble? - if( const optional_vpart_position vp = veh_at( where ) ) { - return &vp->vehicle(); - } - // Nope. Load up its submap... - point_sm_ms veh_in_sm; - tripoint_abs_sm veh_sm; - std::tie( veh_sm, veh_in_sm ) = project_remain( where ); - const submap *sm = MAPBUFFER.lookup_submap( veh_sm ); - if( sm == nullptr ) { - return nullptr; - } - - for( const auto &elem : sm->vehicles ) { - vehicle *found_veh = elem.get(); - // TODO: fix point types - if( veh_in_sm.raw() == found_veh->pos ) { - return found_veh; - } - } - - return nullptr; -} - void map::board_vehicle( const tripoint &pos, Character *p ) { if( p == nullptr ) { diff --git a/src/map.h b/src/map.h index 0a70c3ffca58a..e6a557976f7ed 100644 --- a/src/map.h +++ b/src/map.h @@ -696,12 +696,6 @@ class map optional_vpart_position veh_at( const tripoint_bub_ms &p ) const; vehicle *veh_at_internal( const tripoint &p, int &part_num ); const vehicle *veh_at_internal( const tripoint &p, int &part_num ) const; - /** - * Find a possibly off-map vehicle. If necessary, loads up its submap through - * the global MAPBUFFER and pulls it from there. - * @param where Absolute coordinates of the target vehicle's origin tile. - */ - vehicle *find_vehicle( const tripoint_abs_ms &where ) const; // Put player on vehicle at x,y void board_vehicle( const tripoint &p, Character *pl ); // Remove given passenger from given vehicle part. diff --git a/src/vehicle.cpp b/src/vehicle.cpp index 86aae79aa1faa..5bdd81150157b 100644 --- a/src/vehicle.cpp +++ b/src/vehicle.cpp @@ -5028,6 +5028,32 @@ void vehicle::power_parts() noise_and_smoke( 0, 1_turns ); // refreshes this->vehicle_noise } } +vehicle *vehicle::find_vehicle( const tripoint_abs_ms &where ) +{ + map &here = get_map(); + // Is it in the reality bubble? + if( const optional_vpart_position vp = here.veh_at( where ) ) { + return &vp->vehicle(); + } + // Nope. Load up its submap... + point_sm_ms veh_in_sm; + tripoint_abs_sm veh_sm; + std::tie( veh_sm, veh_in_sm ) = project_remain( where ); + const submap *sm = MAPBUFFER.lookup_submap( veh_sm ); + if( sm == nullptr ) { + return nullptr; + } + + for( const auto &elem : sm->vehicles ) { + vehicle *found_veh = elem.get(); + // TODO: fix point types + if( veh_in_sm.raw() == found_veh->pos ) { + return found_veh; + } + } + + return nullptr; +} template // Templated to support const and non-const vehicle* std::map vehicle::search_connected_vehicles( Vehicle *start ) @@ -5053,7 +5079,7 @@ std::map vehicle::search_connected_vehicles( Vehicle *start ) continue; } - Vehicle *const v_next = get_map().find_vehicle( tripoint_abs_ms( vp.target.second ) ); + Vehicle *const v_next = find_vehicle( tripoint_abs_ms( vp.target.second ) ); if( v_next == nullptr ) { // vehicle's rolled away or off-map continue; } @@ -6563,13 +6589,13 @@ bool vehicle::no_towing_slack() const } -std::optional> vehicle::get_remote_part( const vehicle_part &vp_local ) const +std::optional> vehicle::get_remote_part( + const vehicle_part &vp_local ) const { - map &here = get_map(); - vehicle *veh = find_vehicle( tripoint_abs_ms( vp_local.target.second ), &here ); + vehicle *veh = find_vehicle( tripoint_abs_ms( vp_local.target.second ) ); // If the target vehicle is still there, ask it to remove its part if( veh != nullptr ) { - const tripoint local_abs = here.getabs( global_part_pos3( vp_local ) ); + const tripoint local_abs = get_map().getabs( global_part_pos3( vp_local ) ); for( const int remote_partnum : veh->loose_parts ) { vehicle_part &vp_remote = veh->parts[remote_partnum]; if( vp_remote.info().has_flag( "POWER_TRANSFER" ) && vp_remote.target.first == local_abs ) { @@ -6611,7 +6637,7 @@ void vehicle::shed_loose_parts( const bool can_shed_cables, const tripoint_bub_m int distance = rl_dist( here.getabs( bub_part_pos( vp_loose ) ), vp_loose.target.second ); int max_dist = vp_loose.get_base().max_link_length(); if( src ) { - vehicle *veh = here.find_vehicle( tripoint_abs_ms( vp_loose.target.second ) ); + vehicle *veh = find_vehicle( tripoint_abs_ms( vp_loose.target.second ) ); if( veh != nullptr ) { for( int remote_lp : veh->loose_parts ) { vehicle_part &vp_remote = veh->part( remote_lp ); diff --git a/src/vehicle.h b/src/vehicle.h index 84456be24531a..c8f10b00934c0 100644 --- a/src/vehicle.h +++ b/src/vehicle.h @@ -848,6 +848,13 @@ class vehicle template static std::map search_connected_vehicles( Vehicle *start ); public: + /** + * Find a possibly off-map vehicle. If necessary, loads up its submap through + * the global MAPBUFFER and pulls it from there. For this reason, you should only + * give it the coordinates of the origin tile of a target vehicle. + * @param where Location of the other vehicle's origin tile. + */ + static vehicle *find_vehicle( const tripoint_abs_ms &where ); //! @copydoc vehicle::search_connected_vehicles( Vehicle *start ) std::map search_connected_vehicles(); //! @copydoc vehicle::search_connected_vehicles( Vehicle *start ) @@ -1088,7 +1095,8 @@ class vehicle * Get the remote vehicle and part that a part is targeting. * Useful for, e.g. power cables that have a vehicle part on both sides. */ - std::optional> get_remote_part( const vehicle_part &vp_local ) const; + std::optional> get_remote_part( const vehicle_part &vp_local ) + const; /** * Remove the part on a targeted remote vehicle that a part is targeting. */ From aee0013a3920ae91f47e63760b5aca7c9fff1364 Mon Sep 17 00:00:00 2001 From: Kamayana Date: Sat, 29 Jul 2023 23:33:00 -0500 Subject: [PATCH 54/68] Change properties_to_item into a vehicle function, part_to_item, as it now requires functions only accessible from vehicle --- src/veh_interact.cpp | 6 +++--- src/vehicle.cpp | 51 +++++++++++++++++++++++++++++++++++++------- src/vehicle.h | 11 +++++----- src/vehicle_part.cpp | 36 ------------------------------- src/vehicle_use.cpp | 4 ++-- 5 files changed, 54 insertions(+), 54 deletions(-) diff --git a/src/veh_interact.cpp b/src/veh_interact.cpp index 254193272187a..876d0a48898b8 100644 --- a/src/veh_interact.cpp +++ b/src/veh_interact.cpp @@ -1757,7 +1757,7 @@ bool veh_interact::can_remove_part( int idx, const Character &you ) nmsg += string_format( _( "Removing the %1$s may yield:\n> %2$s\n" ), sel_vehicle_part->name(), enumerate_as_string( removed_names ) ); } else { - item result_of_removal = sel_vehicle_part->properties_to_item(); + item result_of_removal = veh->part_to_item( *sel_vehicle_part ); nmsg += string_format( _( "Removing the %1$s will yield:\n> %2$s\n" ), sel_vehicle_part->name(), result_of_removal.display_name() ); @@ -3308,7 +3308,7 @@ void veh_interact::complete_vehicle( Character &you ) } if( wall_wire_removal ) { - vp.properties_to_item(); // what's going on here? this line isn't doing anything... + veh.part_to_item( vp ); // what's going on here? this line isn't doing anything... } else if( vpi.has_flag( "TOW_CABLE" ) ) { veh.invalidate_towing( true, &you ); } else if( broken ) { @@ -3319,7 +3319,7 @@ void veh_interact::complete_vehicle( Character &you ) item_group::ItemList pieces = vp.pieces_for_broken_part(); resulting_items.insert( resulting_items.end(), pieces.begin(), pieces.end() ); } else { - resulting_items.push_back( vp.properties_to_item() ); + resulting_items.push_back( veh.part_to_item( vp ) ); } for( const std::pair &sk : vpi.install_skills ) { // removal is half as educational as installation diff --git a/src/vehicle.cpp b/src/vehicle.cpp index 5bdd81150157b..e464555dfde84 100644 --- a/src/vehicle.cpp +++ b/src/vehicle.cpp @@ -1836,7 +1836,7 @@ bool vehicle::remove_part( vehicle_part &vp, RemovePartHandler &handler ) return false; } vehicle_part &vp_dep = parts[dep]; - handler.add_item_or_charges( part_loc, vp_dep.properties_to_item(), false ); + handler.add_item_or_charges( part_loc, part_to_item( vp_dep ), false ); remove_part( vp_dep, handler ); return true; }; @@ -6483,7 +6483,7 @@ void vehicle::invalidate_towing( bool first_vehicle, Character *remover ) const int tow_cable_idx = get_tow_part(); if( tow_cable_idx > -1 ) { vehicle_part &vp = parts[tow_cable_idx]; - item drop = vp.properties_to_item(); + item drop = part_to_item( vp ); drop.set_damage( 0 ); if( other_veh != nullptr ) { if( is_towing() ) { @@ -6658,7 +6658,7 @@ void vehicle::shed_loose_parts( const bool can_shed_cables, const tripoint_bub_m _( "The %s's power connection was detached!" ), name ); remove_remote_part( vp_loose ); } - const item drop = vp_loose.properties_to_item(); + const item drop = part_to_item( vp_loose ); if( !magic ) { here.add_item_or_charges( global_part_pos3( vp_loose ) + drop_offset, drop ); } @@ -6955,7 +6955,7 @@ int vehicle::break_off( map &here, vehicle_part &vp, int dmg ) } else if( vpi_here.has_flag( "POWER_TRANSFER" ) ) { // Electrical cables - remove it in one piece and remove remote part add_msg_if_player_sees( pos, m_bad, _( "The %1$s's %2$s is disconnected!" ), name, vp_here.name() ); - here.add_item_or_charges( pos, vp_here.properties_to_item() ); + here.add_item_or_charges( pos, part_to_item( vp_here ) ); remove_remote_part( vp_here ); } else if( vp_here.is_broken() ) { // Tearing off a broken part - break it up @@ -6965,7 +6965,7 @@ int vehicle::break_off( map &here, vehicle_part &vp, int dmg ) // Intact (but possibly damaged) part - remove it in one piece add_msg_if_player_sees( pos, m_bad, _( "The %1$s's %2$s is torn off!" ), name, vp_here.name() ); if( !magic ) { - here.add_item_or_charges( pos, vp_here.properties_to_item() ); + here.add_item_or_charges( pos, part_to_item( vp_here ) ); } } remove_part( vp_here, *handler_ptr ); @@ -6983,7 +6983,7 @@ int vehicle::break_off( map &here, vehicle_part &vp, int dmg ) } else if( vpi.has_flag( "POWER_TRANSFER" ) ) { // Electrical cables - remove it in one piece and remove remote part add_msg_if_player_sees( pos, m_bad, _( "The %1$s's %2$s is disconnected!" ), name, vp.name() ); - here.add_item_or_charges( pos, vp.properties_to_item() ); + here.add_item_or_charges( pos, part_to_item( vp ) ); remove_remote_part( vp ); } else { //Just break it off @@ -7019,7 +7019,7 @@ int vehicle::break_off( map &here, vehicle_part &vp, int dmg ) if( vpi_here.has_flag( "POWER_TRANSFER" ) ) { remove_remote_part( vp_here ); } - here.add_item_or_charges( pos, vp_here.properties_to_item() ); + here.add_item_or_charges( pos, part_to_item( vp_here ) ); remove_part( vp_here, *handler_ptr ); } } @@ -7121,7 +7121,7 @@ int vehicle::damage_direct( map &here, vehicle_part &vp, int dmg, const damage_t if( vpi.has_flag( "TOW_CABLE" ) ) { invalidate_towing( true ); } else { - item part_as_item = vp.properties_to_item(); + item part_as_item = part_to_item( vp ); add_msg_if_player_sees( vppos, m_bad, _( "The %1$s's %2$s is disconnected!" ), name, vp.name() ); if( vpi.has_flag( "POWER_TRANSFER" ) ) { remove_remote_part( vp ); @@ -7815,6 +7815,41 @@ vehicle_part_with_fakes_range vehicle::get_all_parts_with_fakes( bool with_inact return vehicle_part_with_fakes_range( const_cast( *this ), with_inactive ); } +item vehicle::part_to_item( const vehicle_part &vp ) const +{ + item tmp = vp.base; + tmp.unset_flag( flag_VEHICLE ); + + // Cables get special handling: their target coordinates need to remain + // stored, and if a cable actually drops, it should be half-connected. + if( tmp.has_flag( flag_CABLE_SPOOL ) ) { + map &here = get_map(); + tmp.link = cata::make_value(); + + // Tow cables have these variables assigned in invalidate_towing, which calls part_to_item. + if( !tmp.has_flag( flag_TOW_CABLE ) ) { + std::optional> remote_part = get_remote_part( vp ); + if( remote_part ) { + tmp.link->t_mount = remote_part->second->mount; + } else { + // The linked vehicle can't be found, so this part shouldn't exist. + tmp.set_flag( flag_NO_DROP ); + } + tmp.link->t_abs_pos = tripoint_abs_ms( vp.target.second ); + tmp.link->s_state = link_state::no_link; + tmp.link->t_state = link_state::vehicle_port; + } + + tmp.set_link_traits(); + tmp.link->last_processed = calendar::turn; + } + + // quantize damage and degradation to the middle of each damage_level so that items will stack nicely + tmp.set_damage( ( tmp.damage_level() - 0.5 ) * itype::damage_scale ); + tmp.set_degradation( ( tmp.damage_level() - 0.5 ) * itype::damage_scale ); + return tmp; +} + bool vehicle::refresh_zones() { if( zones_dirty ) { diff --git a/src/vehicle.h b/src/vehicle.h index c8f10b00934c0..8880a8c3b9b38 100644 --- a/src/vehicle.h +++ b/src/vehicle.h @@ -543,11 +543,6 @@ struct vehicle_part { void serialize( JsonOut &json ) const; void deserialize( const JsonObject &data ); - /** - * Generate the corresponding item from this vehicle part. It includes - * the hp (item damage), fuel charges (battery or liquids), aspect, ... - */ - item properties_to_item() const; /** * Returns an ItemList of the pieces that should arise from breaking * this part. @@ -2091,6 +2086,12 @@ class vehicle // map.cpp calls this in displace_vehicle void update_active_fakes(); + /** + * Generate the corresponding item from this vehicle part. It includes + * the hp (item damage), fuel charges (battery or liquids), aspect, ... + */ + item part_to_item( const vehicle_part &vp ) const; + // Updates the internal precalculated mount offsets after the vehicle has been displaced // used in map::displace_vehicle() std::set advance_precalc_mounts( const point &new_pos, const tripoint &src, diff --git a/src/vehicle_part.cpp b/src/vehicle_part.cpp index b8111efa0b669..d26fd61c9a6b8 100644 --- a/src/vehicle_part.cpp +++ b/src/vehicle_part.cpp @@ -67,42 +67,6 @@ void vehicle_part::set_base( item &&new_base ) base.set_flag( flag_VEHICLE ); } -item vehicle_part::properties_to_item() const -{ - item tmp = base; - tmp.unset_flag( flag_VEHICLE ); - - // Cables get special handling: their target coordinates need to remain - // stored, and if a cable actually drops, it should be half-connected. - if( tmp.has_flag( flag_CABLE_SPOOL ) ) { - map &here = get_map(); - tmp.link = cata::make_value(); - - // Tow cables have these variables assigned in invalidate_towing, which calls properties_to_item. - if( !tmp.has_flag( flag_TOW_CABLE ) ) { - const tripoint local_pos = here.getlocal( target.first ); - const optional_vpart_position target_vp = here.veh_at( local_pos ); - if( !target_vp ) { - // That vehicle ain't there no more. - tmp.set_flag( flag_NO_DROP ); - } else { - tmp.link->t_mount = target_vp->mount(); - } - tmp.link->t_abs_pos = tripoint_abs_ms( target.second ); - tmp.link->s_state = link_state::no_link; - tmp.link->t_state = link_state::vehicle_port; - } - - tmp.set_link_traits(); - tmp.link->last_processed = calendar::turn; - } - - // quantize damage and degradation to the middle of each damage_level so that items will stack nicely - tmp.set_damage( ( tmp.damage_level() - 0.5 ) * itype::damage_scale ); - tmp.set_degradation( ( tmp.damage_level() - 0.5 ) * itype::damage_scale ); - return tmp; -} - std::string vehicle_part::name( bool with_prefix ) const { std::string res; diff --git a/src/vehicle_use.cpp b/src/vehicle_use.cpp index 9d513083dd12d..b13ac3df5f0ab 100644 --- a/src/vehicle_use.cpp +++ b/src/vehicle_use.cpp @@ -2080,7 +2080,7 @@ void vehicle::build_interact_menu( veh_menu &menu, const tripoint &p, bool with_ for( vehicle_part *vp_part : vp_parts ) { if( vp_part->info().has_flag( "POWER_TRANSFER" ) ) { - item drop = vp_part->properties_to_item(); + item drop = part_to_item( *vp_part ); if( !magic && !drop.has_flag( STATIC( flag_id( "NO_DROP" ) ) ) ) { get_player_character().i_add_or_drop( drop ); } @@ -2105,7 +2105,7 @@ void vehicle::build_interact_menu( veh_menu &menu, const tripoint &p, bool with_ add_msg( _( "You detached the %s's cables." ), vp_part->name( false ) ); } if( vp_part->info().has_flag( "TOW_CABLE" ) ) { - item drop = vp_part->properties_to_item(); + item drop = part_to_item( *vp_part ); if( !magic && !drop.has_flag( STATIC( flag_id( "NO_DROP" ) ) ) ) { get_player_character().i_add_or_drop( drop ); } From 5f289efcc7c9edce600cd4d74b126a70a6366dc7 Mon Sep 17 00:00:00 2001 From: Kamayana Date: Sun, 30 Jul 2023 16:17:11 -0500 Subject: [PATCH 55/68] Make it more clear you pick up cables you disconnect --- src/vehicle_use.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/vehicle_use.cpp b/src/vehicle_use.cpp index b13ac3df5f0ab..6285297160d33 100644 --- a/src/vehicle_use.cpp +++ b/src/vehicle_use.cpp @@ -2083,8 +2083,10 @@ void vehicle::build_interact_menu( veh_menu &menu, const tripoint &p, bool with_ item drop = part_to_item( *vp_part ); if( !magic && !drop.has_flag( STATIC( flag_id( "NO_DROP" ) ) ) ) { get_player_character().i_add_or_drop( drop ); + add_msg( _( "You detach the %s and take it." ), drop.type_name() ); + } else { + add_msg( _( "You detached the %s." ), drop.type_name() ); } - add_msg( _( "You detached the %s." ), drop.type_name() ); remove_remote_part( *vp_part ); remove_part( *vp_part ); } @@ -2108,8 +2110,10 @@ void vehicle::build_interact_menu( veh_menu &menu, const tripoint &p, bool with_ item drop = part_to_item( *vp_part ); if( !magic && !drop.has_flag( STATIC( flag_id( "NO_DROP" ) ) ) ) { get_player_character().i_add_or_drop( drop ); + add_msg( _( "You detach the %s and take it." ), drop.type_name() ); + } else { + add_msg( _( "You detached the %s." ), drop.type_name() ); } - add_msg( _( "You detached the %s." ), drop.type_name() ); remove_remote_part( *vp_part ); remove_part( *vp_part ); } From 9e3107e5d162506ae1ccf67d5232a445db3a2c07 Mon Sep 17 00:00:00 2001 From: Kamayana Date: Mon, 31 Jul 2023 04:54:39 -0500 Subject: [PATCH 56/68] Rework shed_loose_parts to fix vehicle connections, particularly out-of-bubble ones --- src/iuse_actor.cpp | 15 +++++++----- src/map.cpp | 2 +- src/veh_interact.cpp | 2 +- src/vehicle.cpp | 54 +++++++++++++++++++++----------------------- src/vehicle.h | 16 +++++++++---- 5 files changed, 49 insertions(+), 40 deletions(-) diff --git a/src/iuse_actor.cpp b/src/iuse_actor.cpp index 16629537699f9..a65a52ea77b38 100644 --- a/src/iuse_actor.cpp +++ b/src/iuse_actor.cpp @@ -4793,8 +4793,8 @@ std::optional link_up_actor::link_to_veh_app( Character *p, item &it, return std::nullopt; } const std::pair prev_target = std::make_pair( - here.getabs( prev_veh->mount_to_tripoint( it.link->t_mount ) ), - prev_veh->global_square_location().raw() ); + ( 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 : target_veh->get_any_parts( "POWER_TRANSFER" ) ) { if( vpr.part().target.first == prev_target.first && vpr.part().target.second == prev_target.second ) { @@ -4843,8 +4843,8 @@ std::optional link_up_actor::link_to_veh_app( Character *p, item &it, prev_veh->precalc_mounts( 1, prev_veh->pivot_rotation[1], prev_veh->pivot_anchor[1] ); vehicle_part target_part( vpid, item( it ) ); - target_part.target.first = here.getabs( prev_veh->mount_to_tripoint( it.link->t_mount ) ); - target_part.target.second = prev_veh->global_square_location().raw(); + target_part.target.first = prev_target.first; + target_part.target.second = prev_target.second; target_veh->install_part( vcoords2, std::move( target_part ) ); target_veh->precalc_mounts( 1, target_veh->pivot_rotation[1], target_veh->pivot_anchor[1] ); @@ -4976,8 +4976,11 @@ std::optional link_up_actor::link_tow_cable( Character *p, item &it, prev_veh->precalc_mounts( 1, prev_veh->pivot_rotation[1], prev_veh->pivot_anchor[1] ); vehicle_part target_part( vpid, item( it ) ); - target_part.target.first = here.getabs( prev_veh->mount_to_tripoint( it.link->t_mount ) ); - target_part.target.second = prev_veh->global_square_location().raw(); + const std::pair prev_target = std::make_pair( + ( it.link->t_abs_pos + prev_veh->coord_translate( it.link->t_mount ) ).raw(), + it.link->t_abs_pos.raw() ); + target_part.target.first = prev_target.first; + target_part.target.second = prev_target.second; target_veh->install_part( vcoords2, std::move( target_part ) ); target_veh->precalc_mounts( 1, target_veh->pivot_rotation[1], target_veh->pivot_anchor[1] ); diff --git a/src/map.cpp b/src/map.cpp index 94518d39fa848..17bb7bd211e45 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -1523,6 +1523,7 @@ bool map::displace_vehicle( vehicle &veh, const tripoint &dp, const bool adjust_ } } + veh.shed_loose_parts( trinary::SOME, &dst ); smzs = veh.advance_precalc_mounts( dst_offset, src.raw(), dp, ramp_offset, adjust_pos, parts_to_move ); veh.update_active_fakes(); @@ -1535,7 +1536,6 @@ bool map::displace_vehicle( vehicle &veh, const tripoint &dp, const bool adjust_ src_submap->vehicles.erase( src_submap_veh_it ); invalidate_max_populated_zlev( dst.z() ); } - veh.shed_loose_parts( true, &src, &dst, tripoint( -dp.xy(), -ramp_offset ) ); if( need_update ) { g->update_map( player_character ); } diff --git a/src/veh_interact.cpp b/src/veh_interact.cpp index 876d0a48898b8..16acd78e25b77 100644 --- a/src/veh_interact.cpp +++ b/src/veh_interact.cpp @@ -3329,7 +3329,7 @@ void veh_interact::complete_vehicle( Character &you ) // Remove any leftover power cords from the appliance if( appliance_removal && veh.part_count() >= 2 ) { - veh.shed_loose_parts(); + veh.shed_loose_parts( trinary::ALL ); veh.part_removal_cleanup(); //always stop after removing an appliance you.activity.set_to_null(); diff --git a/src/vehicle.cpp b/src/vehicle.cpp index e464555dfde84..876558c883cfa 100644 --- a/src/vehicle.cpp +++ b/src/vehicle.cpp @@ -5709,7 +5709,7 @@ void vehicle::gain_moves() const bool pl_control = player_in_control( get_player_character() ); if( is_moving() || is_falling ) { if( !loose_parts.empty() ) { - shed_loose_parts( false ); + shed_loose_parts(); } of_turn = 1 + of_turn_carry; const int vslowdown = slowdown( velocity ); @@ -6608,7 +6608,7 @@ std::optional> vehicle::get_remote_part( void vehicle::remove_remote_part( const vehicle_part &vp_local ) const { - std::optional> part = get_remote_part( vp_local ); + const auto part = get_remote_part( vp_local ); if( part ) { part->first->remove_part( *part->second ); // Rebuild vehicle cache to avoid creating an illusion of the remote car. @@ -6616,8 +6616,7 @@ void vehicle::remove_remote_part( const vehicle_part &vp_local ) const } } -void vehicle::shed_loose_parts( const bool can_shed_cables, const tripoint_bub_ms *src, - const tripoint_bub_ms *dst, const tripoint drop_offset ) +void vehicle::shed_loose_parts( const trinary shed_cables, const tripoint_bub_ms *dst ) { map &here = get_map(); // remove_part rebuilds the loose_parts vector, so iterate over a copy to preserve @@ -6626,43 +6625,42 @@ void vehicle::shed_loose_parts( const bool can_shed_cables, const tripoint_bub_m for( const int elem : lp ) { vehicle_part &vp_loose = part( elem ); const vpart_info &vpi_loose = vp_loose.info(); + bool remove_remote = false; if( std::find( loose_parts.begin(), loose_parts.end(), elem ) == loose_parts.end() ) { // part was removed elsewhere continue; } if( vpi_loose.has_flag( "POWER_TRANSFER" ) ) { - if( !can_shed_cables ) { + if( shed_cables == trinary::NONE ) { + // Skip cables if we're only calling shed_loose_parts to remove parts with UNMOUNT_ON_MOVE. continue; } - int distance = rl_dist( here.getabs( bub_part_pos( vp_loose ) ), vp_loose.target.second ); - int max_dist = vp_loose.get_base().max_link_length(); - if( src ) { - vehicle *veh = find_vehicle( tripoint_abs_ms( vp_loose.target.second ) ); - if( veh != nullptr ) { - for( int remote_lp : veh->loose_parts ) { - vehicle_part &vp_remote = veh->part( remote_lp ); - if( vp_remote.info().has_flag( "POWER_TRANSFER" ) && - vp_remote.target.first == here.getabs( *src ) ) { - // update remote part's target to new position - vp_remote.target.first = here.getabs( dst ? *dst : bub_part_pos( vp_loose ) ); - vp_remote.target.second = vp_remote.target.first; - } - } - } - if( distance <= max_dist ) { - // power line still has some slack to it, so keep it attached for now - continue; + tripoint vp_loose_dst = here.getabs( dst ? *dst + vp_loose.precalc[1] : bub_part_pos( vp_loose ) ); + const int distance = rl_dist( vp_loose_dst, vp_loose.target.first ); + const int max_dist = vp_loose.get_base().max_link_length(); + + if( distance > max_dist || shed_cables == trinary::ALL ) { + add_msg_if_player_sees( global_part_pos3( vp_loose ), m_warning, + _( "The %s's %s was detached!" ), name, vp_loose.name( false ) ); + remove_remote = true; + } else { + // cable still has some slack to it, so update the remote part's target and continue. + const auto remote = get_remote_part( vp_loose ); + if( remote ) { + remote->second->target.first = vp_loose_dst; + remote->second->target.second = here.getabs( dst ? *dst : pos_bub() ); } + continue; } - add_msg_if_player_sees( global_part_pos3( vp_loose ), m_warning, - _( "The %s's power connection was detached!" ), name ); - remove_remote_part( vp_loose ); } const item drop = part_to_item( vp_loose ); if( !magic ) { - here.add_item_or_charges( global_part_pos3( vp_loose ) + drop_offset, drop ); + here.add_item_or_charges( global_part_pos3( vp_loose ), drop ); } + if( remove_remote ) { + remove_remote_part( vp_loose ); + } remove_part( vp_loose ); } } @@ -7828,7 +7826,7 @@ item vehicle::part_to_item( const vehicle_part &vp ) const // Tow cables have these variables assigned in invalidate_towing, which calls part_to_item. if( !tmp.has_flag( flag_TOW_CABLE ) ) { - std::optional> remote_part = get_remote_part( vp ); + const auto remote_part = get_remote_part( vp ); if( remote_part ) { tmp.link->t_mount = remote_part->second->mount; } else { diff --git a/src/vehicle.h b/src/vehicle.h index 8880a8c3b9b38..44da02e5dfdfd 100644 --- a/src/vehicle.h +++ b/src/vehicle.h @@ -1089,9 +1089,10 @@ class vehicle /** * Get the remote vehicle and part that a part is targeting. * Useful for, e.g. power cables that have a vehicle part on both sides. + * @param vp_local Vehicle part that is connected to the remote part. */ - std::optional> get_remote_part( const vehicle_part &vp_local ) - const; + std::optional> get_remote_part( + const vehicle_part &vp_local ) const; /** * Remove the part on a targeted remote vehicle that a part is targeting. */ @@ -1813,8 +1814,15 @@ class vehicle void shift_parts( map &here, const point &delta ); bool shift_if_needed( map &here ); - void shed_loose_parts( bool can_shed_cables = true, const tripoint_bub_ms *src = nullptr, - const tripoint_bub_ms *dst = nullptr, tripoint drop_offset = tripoint_zero ); + /** + * Drop parts with UNMOUNT_ON_MOVE onto the ground. + * @param shed_cables If cable parts should also be dropped. + * @param - If set to trinary::NONE, the default, don't drop any cables. + * @param - If set to trinary::SOME, calculate cable length, updating remote parts, and drop if it's too long. + * @param - If set to trinary::ALL, drop all cables. + * @param dst Future vehicle position, used for calculating cable length when shed_cables == trinary::SOME. + */ + void shed_loose_parts( trinary shed_cables = trinary::NONE, const tripoint_bub_ms *dst = nullptr ); /** * @name Vehicle turrets From 27796859ce6d5286252720e52499bfea16f4da44 Mon Sep 17 00:00:00 2001 From: Kamayana Date: Mon, 31 Jul 2023 04:54:53 -0500 Subject: [PATCH 57/68] Cleanup --- src/item_factory.cpp | 11 +++++------ src/iuse_actor.cpp | 6 +----- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/src/item_factory.cpp b/src/item_factory.cpp index 4c09b4bd6b776..070ce922df82a 100644 --- a/src/item_factory.cpp +++ b/src/item_factory.cpp @@ -3720,12 +3720,11 @@ void Item_factory::add_special_pockets( itype &def ) if( !has_pocket_type( def.pockets, item_pocket::pocket_type::MIGRATION ) ) { def.pockets.emplace_back( item_pocket::pocket_type::MIGRATION ); } - if( !has_pocket_type( def.pockets, item_pocket::pocket_type::CABLE ) ) { - if( def.get_use( "link_up" ) != nullptr ) { - pocket_data cable_pocket( item_pocket::pocket_type::CABLE ); - cable_pocket.rigid = false; - def.pockets.emplace_back( cable_pocket ); - } + if( !has_pocket_type( def.pockets, item_pocket::pocket_type::CABLE ) && + def.get_use( "link_up" ) != nullptr ) { + pocket_data cable_pocket( item_pocket::pocket_type::CABLE ); + cable_pocket.rigid = false; + def.pockets.emplace_back( cable_pocket ); } } diff --git a/src/iuse_actor.cpp b/src/iuse_actor.cpp index a65a52ea77b38..6a34baa4e216a 100644 --- a/src/iuse_actor.cpp +++ b/src/iuse_actor.cpp @@ -5048,7 +5048,7 @@ std::optional link_up_actor::link_extend_cable( Character *p, item &it ) co item_location extended = is_cable_item ? selected : form_loc( *p, tripoint_min, it ); std::optional extended_copy; - // We'll make a copy the extended item and check pocket weight/volume capacity if: + // We'll make a copy of the extended item and check pocket weight/volume capacity if: // 1. The extended item is in a container, // 2. The extended item and extension cord(s) aren't in the same pocket, and // 3. The extended item is in a pocket without enough remaining room for the extension cord(s). @@ -5060,7 +5060,6 @@ std::optional link_up_actor::link_extend_cable( Character *p, item &it ) co extended_copy = *extended; } - //item *extended_ptr = has_to_move ? &extended_copy.value() : &*extended; item *extended_ptr = extended_copy ? &extended_copy.value() : &*extended; // Put the extension cable and all of its attached cables, if any, into the extended item's CABLE pocket. @@ -5091,9 +5090,6 @@ std::optional link_up_actor::link_extend_cable( Character *p, item &it ) co item_pocket::pocket_type::CONTAINER ).success() ) { debugmsg( "Failed to put %s inside %s!", extended_ptr->type_name(), extended.parent_item()->type_name() ); - it.get_contents().clear_pockets_if( []( item_pocket const & pocket ) { - return pocket.is_type( item_pocket::pocket_type::CABLE ); - } ); return std::nullopt; } extended.remove_item(); From 6badf5fa78ebeae5ec5b2026af4a78845bd24678 Mon Sep 17 00:00:00 2001 From: Kamayana Date: Mon, 31 Jul 2023 08:52:18 -0500 Subject: [PATCH 58/68] Make invalidate_towing give the cable directly to the player if they can stash it --- src/vehicle.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/vehicle.cpp b/src/vehicle.cpp index 876558c883cfa..3e153a81c34d9 100644 --- a/src/vehicle.cpp +++ b/src/vehicle.cpp @@ -6503,8 +6503,12 @@ void vehicle::invalidate_towing( bool first_vehicle, Character *remover ) } if( remover != nullptr ) { - std::list drops{ drop }; - put_into_vehicle_or_drop( *remover, item_drop_reason::deliberate, drops ); + if( !drop.has_flag( flag_NO_DROP ) && remover->can_stash( drop ) ) { + remover->i_add_or_drop( drop ); + } else { + std::list drops{ drop }; + put_into_vehicle_or_drop( *remover, item_drop_reason::deliberate, drops ); + } } else { get_map().add_item_or_charges( global_part_pos3( vp ), drop ); } From 88e2f5f2b435443e3d374586a437847e0e6be853 Mon Sep 17 00:00:00 2001 From: Kamayana Date: Mon, 31 Jul 2023 09:00:56 -0500 Subject: [PATCH 59/68] Fix disconnecting tow cables and jumper cables --- src/item.cpp | 13 ++++++++++++- src/item.h | 3 ++- src/veh_interact.cpp | 10 +++++----- src/vehicle.cpp | 11 +++++------ src/vehicle_use.cpp | 11 ++++------- 5 files changed, 28 insertions(+), 20 deletions(-) diff --git a/src/item.cpp b/src/item.cpp index cb7894dce1cec..1517ebb76d85a 100644 --- a/src/item.cpp +++ b/src/item.cpp @@ -12950,7 +12950,7 @@ bool item::process_extinguish( map &here, Character *carrier, const tripoint &po return false; } -void item::set_link_traits() +void item::set_link_traits( const bool assign_t_state ) { if( !link || !type->can_use( "link_up" ) ) { return; @@ -12974,6 +12974,17 @@ void item::set_link_traits() link->efficiency = link->efficiency < MIN_LINK_EFFICIENCY ? 0.0f : link->efficiency * actor->efficiency; } + + 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() && + link->t_veh_safe->avail_part_with_feature( link->t_mount, "CABLE_PORTS" ) != -1 ) { + link->t_state = link_state::vehicle_port; + } else if( it_actor->targets.find( link_state::vehicle_battery ) != it_actor->targets.end() && + link->t_veh_safe->avail_part_with_feature( link->t_mount, "BATTERY" ) != -1 ) { + link->t_state = link_state::vehicle_battery; + } + } } int item::link_length() const diff --git a/src/item.h b/src/item.h index b57428fac08c5..7e63ef0b64156 100644 --- a/src/item.h +++ b/src/item.h @@ -1458,8 +1458,9 @@ class item : public visitable * @brief Sets max_length and efficiency of a link, taking cable extensions into account. * @brief max_length is set to the sum of all cable lengths. * @brief efficiency is set to the product of all efficiencies multiplied together. + * @param assign_t_state If true, set the t_state based on the parts at the connection point. Defaults to false. */ - void set_link_traits(); + void set_link_traits( bool assign_t_state = false ); /** * @return The link's current length. diff --git a/src/veh_interact.cpp b/src/veh_interact.cpp index 16acd78e25b77..e198812d3d8f6 100644 --- a/src/veh_interact.cpp +++ b/src/veh_interact.cpp @@ -3293,11 +3293,6 @@ void veh_interact::complete_vehicle( Character &you ) resulting_items.insert( resulting_items.end(), contents.begin(), contents.end() ); contents.clear(); - // Power cables must remove parts from the target vehicle, too. - if( vpi.has_flag( "POWER_TRANSFER" ) ) { - veh.remove_remote_part( vp ); - } - if( broken ) { you.add_msg_if_player( _( "You remove the broken %1$s from the %2$s." ), vp.name(), veh.name ); } else if( smash_remove ) { @@ -3327,6 +3322,11 @@ void veh_interact::complete_vehicle( Character &you ) } } + // Power cables must remove parts from the target vehicle, too. + if( vpi.has_flag( "POWER_TRANSFER" ) ) { + veh.remove_remote_part( vp ); + } + // Remove any leftover power cords from the appliance if( appliance_removal && veh.part_count() >= 2 ) { veh.shed_loose_parts( trinary::ALL ); diff --git a/src/vehicle.cpp b/src/vehicle.cpp index 3e153a81c34d9..1312932dc7ab4 100644 --- a/src/vehicle.cpp +++ b/src/vehicle.cpp @@ -7830,19 +7830,18 @@ item vehicle::part_to_item( const vehicle_part &vp ) const // Tow cables have these variables assigned in invalidate_towing, which calls part_to_item. if( !tmp.has_flag( flag_TOW_CABLE ) ) { - const auto remote_part = get_remote_part( vp ); - if( remote_part ) { - tmp.link->t_mount = remote_part->second->mount; + const auto remote = get_remote_part( vp ); + if( remote ) { + tmp.link->t_veh_safe = remote->first->get_safe_reference(); + tmp.link->t_mount = remote->second->mount; } else { // The linked vehicle can't be found, so this part shouldn't exist. tmp.set_flag( flag_NO_DROP ); } tmp.link->t_abs_pos = tripoint_abs_ms( vp.target.second ); - tmp.link->s_state = link_state::no_link; - tmp.link->t_state = link_state::vehicle_port; } - tmp.set_link_traits(); + tmp.set_link_traits( true ); tmp.link->last_processed = calendar::turn; } diff --git a/src/vehicle_use.cpp b/src/vehicle_use.cpp index 6285297160d33..dfe43546610f3 100644 --- a/src/vehicle_use.cpp +++ b/src/vehicle_use.cpp @@ -2107,15 +2107,12 @@ void vehicle::build_interact_menu( veh_menu &menu, const tripoint &p, bool with_ add_msg( _( "You detached the %s's cables." ), vp_part->name( false ) ); } if( vp_part->info().has_flag( "TOW_CABLE" ) ) { - item drop = part_to_item( *vp_part ); - if( !magic && !drop.has_flag( STATIC( flag_id( "NO_DROP" ) ) ) ) { - get_player_character().i_add_or_drop( drop ); - add_msg( _( "You detach the %s and take it." ), drop.type_name() ); + invalidate_towing( true, &get_player_character() ); + if( get_player_character().can_stash( vp_part->get_base() ) ) { + add_msg( _( "You detach the %s and take it." ), vp_part->name( false ) ); } else { - add_msg( _( "You detached the %s." ), drop.type_name() ); + add_msg( _( "You detached the %s." ), vp_part->name( false ) ); } - remove_remote_part( *vp_part ); - remove_part( *vp_part ); } } get_player_character().pause(); From 209b4f1df19e869d1fc980d75bb2470eec9fd41c Mon Sep 17 00:00:00 2001 From: Kamayana Date: Mon, 31 Jul 2023 17:52:54 -0500 Subject: [PATCH 60/68] Unused variable --- src/vehicle.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/vehicle.cpp b/src/vehicle.cpp index 1312932dc7ab4..cfc47d191932f 100644 --- a/src/vehicle.cpp +++ b/src/vehicle.cpp @@ -7825,7 +7825,6 @@ item vehicle::part_to_item( const vehicle_part &vp ) const // Cables get special handling: their target coordinates need to remain // stored, and if a cable actually drops, it should be half-connected. if( tmp.has_flag( flag_CABLE_SPOOL ) ) { - map &here = get_map(); tmp.link = cata::make_value(); // Tow cables have these variables assigned in invalidate_towing, which calls part_to_item. From e0fffb3894ec6944489b5579efcaa7968ff357f5 Mon Sep 17 00:00:00 2001 From: Kamayana Date: Mon, 31 Jul 2023 18:52:38 -0500 Subject: [PATCH 61/68] Make assign_t_state work with appliances --- src/item.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/item.cpp b/src/item.cpp index 1517ebb76d85a..109ae3347cfc2 100644 --- a/src/item.cpp +++ b/src/item.cpp @@ -12978,10 +12978,12 @@ void item::set_link_traits( const bool assign_t_state ) 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() && - link->t_veh_safe->avail_part_with_feature( link->t_mount, "CABLE_PORTS" ) != -1 ) { + ( link->t_veh_safe->avail_part_with_feature( link->t_mount, "CABLE_PORTS" ) != -1 || + link->t_veh_safe->avail_part_with_feature( link->t_mount, "APPLIANCE" ) != -1 ) ) { link->t_state = link_state::vehicle_port; } else if( it_actor->targets.find( link_state::vehicle_battery ) != it_actor->targets.end() && - link->t_veh_safe->avail_part_with_feature( link->t_mount, "BATTERY" ) != -1 ) { + ( link->t_veh_safe->avail_part_with_feature( link->t_mount, "BATTERY" ) != -1 || + link->t_veh_safe->avail_part_with_feature( link->t_mount, "APPLIANCE" ) != -1 ) ) { link->t_state = link_state::vehicle_battery; } } From 76c713835e482db5df0b6e89e67b91c3af4d9cc2 Mon Sep 17 00:00:00 2001 From: Kamayana Date: Mon, 31 Jul 2023 19:40:21 -0500 Subject: [PATCH 62/68] Fix jumper cable -> appliance links --- src/item.cpp | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/item.cpp b/src/item.cpp index 109ae3347cfc2..4e82a1fbbedc8 100644 --- a/src/item.cpp +++ b/src/item.cpp @@ -12979,11 +12979,11 @@ void item::set_link_traits( const bool assign_t_state ) // 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() && ( link->t_veh_safe->avail_part_with_feature( link->t_mount, "CABLE_PORTS" ) != -1 || - link->t_veh_safe->avail_part_with_feature( link->t_mount, "APPLIANCE" ) != -1 ) ) { + link->t_veh_safe->avail_part_with_feature( link->t_mount, "APPLIANCE" ) != -1 ) ) { link->t_state = link_state::vehicle_port; } else if( it_actor->targets.find( link_state::vehicle_battery ) != it_actor->targets.end() && ( link->t_veh_safe->avail_part_with_feature( link->t_mount, "BATTERY" ) != -1 || - link->t_veh_safe->avail_part_with_feature( link->t_mount, "APPLIANCE" ) != -1 ) ) { + link->t_veh_safe->avail_part_with_feature( link->t_mount, "APPLIANCE" ) != -1 ) ) { link->t_state = link_state::vehicle_battery; } } @@ -13135,6 +13135,15 @@ bool item::process_link( map &here, Character *carrier, const tripoint &pos ) break; } } + if( link_vp_index == -1 ) { + // Check cable_ports, since that includes appliances + for( int idx : t_veh->cable_ports ) { + if( t_veh->part( idx ).mount == link->t_mount ) { + link_vp_index = idx; + break; + } + } + } } else if( link->t_state == link_state::vehicle_tow || link->s_state == link_state::vehicle_tow ) { link_vp_index = t_veh->part_at( t_veh->coord_translate( link->t_mount ) ); } From 9f1d88fbbecc91c90b47fcf125472202daea20bd Mon Sep 17 00:00:00 2001 From: Kamayana Date: Mon, 31 Jul 2023 19:50:00 -0500 Subject: [PATCH 63/68] Reword link_to_veh_app & link_tow_cable variables for clarity, as "target" is already used for part targets --- src/iuse_actor.cpp | 131 ++++++++++++++++++++++++--------------------- 1 file changed, 71 insertions(+), 60 deletions(-) diff --git a/src/iuse_actor.cpp b/src/iuse_actor.cpp index 6a34baa4e216a..ee94716d4d774 100644 --- a/src/iuse_actor.cpp +++ b/src/iuse_actor.cpp @@ -4728,14 +4728,14 @@ std::optional link_up_actor::link_to_veh_app( Character *p, item &it, if( !pnt_ ) { return std::nullopt; } - const tripoint &pnt = *pnt_; + const tripoint &selection = *pnt_; - const optional_vpart_position t_vp = here.veh_at( pnt ); - if( !can_link( pnt ) ) { - if( to_ports && t_vp && t_vp->vehicle().has_part( "CABLE_PORTS" ) ) { + const optional_vpart_position s_vp = here.veh_at( selection ); + if( !can_link( selection ) ) { + if( to_ports && s_vp && s_vp->vehicle().has_part( "CABLE_PORTS" ) ) { p->add_msg_if_player( m_info, _( "You can't attach it there; try the dashboard or electronics controls." ) ); - } else if( !to_ports && t_vp && t_vp->vehicle().batteries.empty() ) { + } else if( !to_ports && s_vp && s_vp->vehicle().batteries.empty() ) { p->add_msg_if_player( m_info, _( "You can't attach it there; try the battery." ) ); } else { @@ -4751,19 +4751,19 @@ std::optional link_up_actor::link_to_veh_app( Character *p, item &it, !it.link->has_state( link_state::vehicle_battery ) ) { // Starting a new connection to a vehicle or connecting a cable CBM to a vehicle. - std::optional t_vp_ref( t_vp.avail_part_with_feature( "APPLIANCE" ) ); + std::optional s_vp_ref( s_vp.avail_part_with_feature( "APPLIANCE" ) ); if( to_ports ) { - t_vp_ref = t_vp.avail_part_with_feature( "CABLE_PORTS" ); + s_vp_ref = s_vp.avail_part_with_feature( "CABLE_PORTS" ); } else { - t_vp_ref = t_vp.avail_part_with_feature( "BATTERY" ); + s_vp_ref = s_vp.avail_part_with_feature( "BATTERY" ); } if( it.link->has_no_links() ) { p->add_msg_if_player( _( "You connect the %1$s to the %2$s." ), it.type_name(), - t_vp_ref->part().name( false ) ); + s_vp_ref->part().name( false ) ); } else if( it.link->has_state( link_state::bio_cable ) ) { p->add_msg_if_player( m_good, _( "You are now plugged into the %s." ), - t_vp_ref->part().name( false ) ); + s_vp_ref->part().name( false ) ); it.link->s_state = link_state::bio_cable; } else { debugmsg( "Failed to connect the %s, it tried to make an invalid connection!", it.tname() ); @@ -4771,8 +4771,8 @@ std::optional link_up_actor::link_to_veh_app( Character *p, item &it, } it.link->t_state = to_ports ? link_state::vehicle_port : link_state::vehicle_battery; - it.link->t_abs_pos = here.getglobal( t_vp->vehicle().global_pos3() ); - it.link->t_mount = t_vp->mount(); + it.link->t_abs_pos = here.getglobal( s_vp->vehicle().global_pos3() ); + it.link->t_mount = s_vp->mount(); it.set_link_traits(); it.link->last_processed = calendar::turn; p->moves -= move_cost; @@ -4786,20 +4786,26 @@ std::optional link_up_actor::link_to_veh_app( Character *p, item &it, debugmsg( "Failed to connect the %s, it lost its vehicle pointer!", it.tname() ); return std::nullopt; } - vehicle *const target_veh = &t_vp->vehicle(); + vehicle *const sel_veh = &s_vp->vehicle(); vehicle *const prev_veh = it.link->t_veh_safe.get(); - if( prev_veh == target_veh ) { + if( prev_veh == sel_veh ) { p->add_msg_if_player( m_warning, _( "You cannot connect the %s to itself." ), prev_veh->name ); return std::nullopt; } - const std::pair prev_target = std::make_pair( + + // 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( selection ), + sel_veh->global_square_location().raw() ); + const std::pair sel_part_target = std::make_pair( ( 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 : target_veh->get_any_parts( "POWER_TRANSFER" ) ) { - if( vpr.part().target.first == prev_target.first && - vpr.part().target.second == prev_target.second ) { + + for( const vpart_reference &vpr : sel_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." ), - target_veh->name, prev_veh->name ); + sel_veh->name, prev_veh->name ); return std::nullopt; } } @@ -4819,7 +4825,7 @@ std::optional link_up_actor::link_to_veh_app( Character *p, item &it, } const point vcoords1 = it.link->t_mount; - const point vcoords2 = t_vp->mount(); + const point vcoords2 = s_vp->mount(); const ret_val can_mount1 = prev_veh->can_mount( vcoords1, *vpid ); if( !can_mount1.success() ) { @@ -4828,7 +4834,7 @@ std::optional link_up_actor::link_to_veh_app( Character *p, item &it, it.type_name(), can_mount1.str() ); return std::nullopt; } - const ret_val can_mount2 = target_veh->can_mount( vcoords2, *vpid ); + const ret_val can_mount2 = sel_veh->can_mount( vcoords2, *vpid ); if( !can_mount2.success() ) { //~ %1$s - cable name, %2$s - the reason why it failed p->add_msg_if_player( m_bad, _( "You can't attach the %1$s: %2$s" ), @@ -4836,22 +4842,22 @@ std::optional link_up_actor::link_to_veh_app( Character *p, item &it, return std::nullopt; } - vehicle_part prev_part( vpid, item( it ) ); - prev_part.target.first = here.getabs( pnt ); - prev_part.target.second = target_veh->global_square_location().raw(); - prev_veh->install_part( vcoords1, std::move( prev_part ) ); + vehicle_part prev_veh_part( vpid, item( it ) ); + 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 target_part( vpid, item( it ) ); - target_part.target.first = prev_target.first; - target_part.target.second = prev_target.second; - target_veh->install_part( vcoords2, std::move( target_part ) ); - target_veh->precalc_mounts( 1, target_veh->pivot_rotation[1], target_veh->pivot_anchor[1] ); + vehicle_part sel_veh_part( vpid, item( it ) ); + 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] ); if( p->has_item( it ) ) { //~ %1$s - first vehicle name, %2$s - second vehicle name - %3$s - cable name, p->add_msg_if_player( m_good, _( "You connect %1$s and %2$s with the %3$s." ), - prev_veh->disp_name(), target_veh->disp_name(), it.type_name() ); + prev_veh->disp_name(), sel_veh->disp_name(), it.type_name() ); } if( it.typeId() != itype_power_cord ) { // Remove linked_flag from attached parts - the just-added cable vehicle parts do the same thing. @@ -4878,24 +4884,24 @@ std::optional link_up_actor::link_tow_cable( Character *p, item &it, if( !pnt_ ) { return std::nullopt; } - const tripoint &pnt = *pnt_; - const optional_vpart_position t_vp = here.veh_at( pnt ); - if( !t_vp ) { + const tripoint &selection = *pnt_; + const optional_vpart_position s_vp = here.veh_at( selection ); + if( !s_vp ) { p->add_msg_if_player( _( "There's no vehicle there." ) ); return std::nullopt; } - vehicle *const target_veh = &t_vp->vehicle(); - if( target_veh->has_tow_attached() || target_veh->is_towed() || - target_veh->is_towing() ) { + vehicle *const sel_veh = &s_vp->vehicle(); + if( sel_veh->has_tow_attached() || sel_veh->is_towed() || + sel_veh->is_towing() ) { p->add_msg_if_player( _( "That vehicle already has a tow-line attached." ) ); return std::nullopt; } - if( !target_veh->is_external_part( pnt ) ) { + if( !sel_veh->is_external_part( selection ) ) { p->add_msg_if_player( _( "You can't attach the tow-line to an internal part." ) ); return std::nullopt; } - if( !target_veh->part( t_vp->part_index() ).carried_stack.empty() ) { + if( !sel_veh->part( s_vp->part_index() ).carried_stack.empty() ) { p->add_msg_if_player( _( "You can't attach the tow-line to a racked part." ) ); return std::nullopt; } @@ -4907,14 +4913,14 @@ std::optional link_up_actor::link_tow_cable( Character *p, item &it, // Starting a new tow cable connection. p->add_msg_if_player( _( "You connect the %1$s to the %2$s." ), it.type_name(), - t_vp->vehicle().name ); + s_vp->vehicle().name ); if( to_towing ) { it.link->s_state = link_state::vehicle_tow; // Assign towing vehicle. } else { it.link->t_state = link_state::vehicle_tow; // Assign towed vehicle. } - it.link->t_abs_pos = here.getglobal( t_vp->vehicle().global_pos3() ); - it.link->t_mount = t_vp->mount(); + it.link->t_abs_pos = here.getglobal( s_vp->vehicle().global_pos3() ); + it.link->t_mount = s_vp->mount(); it.link->max_length = cable_length != -1 ? cable_length : it.type->maximum_charges(); it.set_link_traits(); it.link->last_processed = calendar::turn; @@ -4930,7 +4936,7 @@ std::optional link_up_actor::link_tow_cable( Character *p, item &it, return std::nullopt; } vehicle *const prev_veh = it.link->t_veh_safe.get(); - if( prev_veh == target_veh ) { + if( prev_veh == sel_veh ) { if( p->has_item( it ) ) { p->add_msg_if_player( m_warning, _( "The %s cannot tow itself!" ), prev_veh->name ); } @@ -4952,7 +4958,7 @@ std::optional link_up_actor::link_tow_cable( Character *p, item &it, } const point vcoords1 = it.link->t_mount; - const point vcoords2 = t_vp->mount(); + const point vcoords2 = s_vp->mount(); const ret_val can_mount1 = prev_veh->can_mount( vcoords1, *vpid ); if( !can_mount1.success() ) { @@ -4961,7 +4967,7 @@ std::optional link_up_actor::link_tow_cable( Character *p, item &it, it.type_name(), can_mount1.str() ); return std::nullopt; } - const ret_val can_mount2 = target_veh->can_mount( vcoords2, *vpid ); + const ret_val can_mount2 = sel_veh->can_mount( vcoords2, *vpid ); if( !can_mount2.success() ) { //~ %1$s - tow cable name, %2$s - the reason why it failed p->add_msg_if_player( m_bad, _( "You can't attach the %1$s: %2$s" ), @@ -4969,30 +4975,35 @@ std::optional link_up_actor::link_tow_cable( Character *p, item &it, return std::nullopt; } - vehicle_part prev_part( vpid, item( it ) ); - prev_part.target.first = here.getabs( pnt ); - prev_part.target.second = target_veh->global_square_location().raw(); - prev_veh->install_part( vcoords1, std::move( prev_part ) ); - prev_veh->precalc_mounts( 1, prev_veh->pivot_rotation[1], prev_veh->pivot_anchor[1] ); - - vehicle_part target_part( vpid, item( it ) ); - const std::pair prev_target = std::make_pair( + // 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( selection ), + sel_veh->global_square_location().raw() ); + const std::pair sel_part_target = std::make_pair( ( it.link->t_abs_pos + prev_veh->coord_translate( it.link->t_mount ) ).raw(), it.link->t_abs_pos.raw() ); - target_part.target.first = prev_target.first; - target_part.target.second = prev_target.second; - target_veh->install_part( vcoords2, std::move( target_part ) ); - target_veh->precalc_mounts( 1, target_veh->pivot_rotation[1], target_veh->pivot_anchor[1] ); + + vehicle_part prev_veh_part( vpid, item( it ) ); + 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( it ) ); + 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] ); if( p->has_item( it ) ) { //~ %1$s - first vehicle name, %2$s - second vehicle name - %3$s - tow cable name, p->add_msg_if_player( m_good, _( "You connect the %1$s and %2$s with the %3$s." ), - prev_veh->disp_name(), target_veh->disp_name(), it.type_name() ); + prev_veh->disp_name(), sel_veh->disp_name(), it.type_name() ); } if( to_towing ) { - target_veh->tow_data.set_towing( target_veh, prev_veh ); + sel_veh->tow_data.set_towing( sel_veh, prev_veh ); } else { - prev_veh->tow_data.set_towing( prev_veh, target_veh ); + prev_veh->tow_data.set_towing( prev_veh, sel_veh ); } if( it.typeId() != itype_power_cord ) { // Remove linked_flag from attached parts - the just-added cable vehicle parts do the same thing. From a3a29c6d2c59e9f3c2ab4697d58d370b87cd1483 Mon Sep 17 00:00:00 2001 From: Kamayana Date: Mon, 31 Jul 2023 19:58:42 -0500 Subject: [PATCH 64/68] Fix connection messages --- src/iuse_actor.cpp | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/iuse_actor.cpp b/src/iuse_actor.cpp index ee94716d4d774..1ed5d00902782 100644 --- a/src/iuse_actor.cpp +++ b/src/iuse_actor.cpp @@ -4734,10 +4734,10 @@ std::optional link_up_actor::link_to_veh_app( Character *p, item &it, if( !can_link( selection ) ) { if( to_ports && s_vp && s_vp->vehicle().has_part( "CABLE_PORTS" ) ) { p->add_msg_if_player( m_info, - _( "You can't attach it there; try the dashboard or electronics controls." ) ); - } else if( !to_ports && s_vp && s_vp->vehicle().batteries.empty() ) { + _( "You can't attach it there - try the dashboard or electronics controls." ) ); + } else if( !to_ports && s_vp && !s_vp->vehicle().batteries.empty() ) { p->add_msg_if_player( m_info, - _( "You can't attach it there; try the battery." ) ); + _( "You can't attach it there - try the battery." ) ); } else { p->add_msg_if_player( m_info, _( "You can't attach it there." ) ); } @@ -4751,19 +4751,19 @@ std::optional link_up_actor::link_to_veh_app( Character *p, item &it, !it.link->has_state( link_state::vehicle_battery ) ) { // Starting a new connection to a vehicle or connecting a cable CBM to a vehicle. - std::optional s_vp_ref( s_vp.avail_part_with_feature( "APPLIANCE" ) ); - if( to_ports ) { - s_vp_ref = s_vp.avail_part_with_feature( "CABLE_PORTS" ); - } else { - s_vp_ref = s_vp.avail_part_with_feature( "BATTERY" ); + // Get the part name for the connection message, using the vehicle name as a fallback. + std::string s_vp_name = s_vp->vehicle().name; + std::optional s_vp_ref; + if( ( s_vp_ref = s_vp.avail_part_with_feature( "APPLIANCE" ) ) || + ( s_vp_ref = s_vp.avail_part_with_feature( "CABLE_PORTS" ) ) || + ( s_vp_ref = s_vp.avail_part_with_feature( "BATTERY" ) ) ) { + s_vp_name = s_vp_ref->part().name( false ); } if( it.link->has_no_links() ) { - p->add_msg_if_player( _( "You connect the %1$s to the %2$s." ), it.type_name(), - s_vp_ref->part().name( false ) ); + p->add_msg_if_player( _( "You connect the %1$s to the %2$s." ), it.type_name(), s_vp_name ); } else if( it.link->has_state( link_state::bio_cable ) ) { - p->add_msg_if_player( m_good, _( "You are now plugged into the %s." ), - s_vp_ref->part().name( false ) ); + p->add_msg_if_player( m_good, _( "You are now plugged into the %s." ), s_vp_name ); it.link->s_state = link_state::bio_cable; } else { debugmsg( "Failed to connect the %s, it tried to make an invalid connection!", it.tname() ); From b40a7af0c52800df109f151a47ae3c11bba05041 Mon Sep 17 00:00:00 2001 From: Kamayana Date: Mon, 31 Jul 2023 20:10:50 -0500 Subject: [PATCH 65/68] Reword lambda for cable setting & checking --- src/item.cpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/item.cpp b/src/item.cpp index 4e82a1fbbedc8..85853600580eb 100644 --- a/src/item.cpp +++ b/src/item.cpp @@ -13049,9 +13049,9 @@ bool item::process_link( map &here, Character *carrier, const tripoint &pos ) const bool last_t_abs_pos_is_oob = !here.inbounds( link->t_abs_pos ); - // Lambda function for checking if a cable's been stretched too long, resetting it if so. + // Lambda function for setting a cable's length, then checking if it's now stretched too long, resetting it if so. // @return True if the cable is disconnected. - const auto is_cable_too_long = [this, carrier, pos, last_t_abs_pos_is_oob, + const auto set_length_and_check = [this, carrier, pos, last_t_abs_pos_is_oob, is_cable_item]( float new_length ) { link->length = new_length; if( debug_mode ) { @@ -13108,12 +13108,12 @@ bool item::process_link( map &here, Character *carrier, const tripoint &pos ) return false; } if( trigdist ) { - if( is_cable_too_long( trig_dist( here.getabs( pos ), link->t_abs_pos.raw() ) + - link->t_mount.abs().x + link->t_mount.abs().y ) ) { + if( set_length_and_check( trig_dist( here.getabs( pos ), link->t_abs_pos.raw() ) + + link->t_mount.abs().x + link->t_mount.abs().y ) ) { return reset_link( carrier ); } - } else if( is_cable_too_long( square_dist( here.getabs( pos ), link->t_abs_pos.raw() ) + - link->t_mount.abs().x + link->t_mount.abs().y ) ) { + } else if( set_length_and_check( square_dist( here.getabs( pos ), link->t_abs_pos.raw() ) + + link->t_mount.abs().x + link->t_mount.abs().y ) ) { return reset_link( carrier ); } return false; @@ -13172,12 +13172,12 @@ bool item::process_link( map &here, Character *carrier, const tripoint &pos ) // If either of the link's connected sides moved, check the cable's length. if( check_length ) { if( trigdist ) { - if( is_cable_too_long( trig_dist( pos, t_veh_bub_pos + - t_veh->part( link_vp_index ).precalc[0] ) ) ) { + if( set_length_and_check( trig_dist( pos, t_veh_bub_pos + + t_veh->part( link_vp_index ).precalc[0] ) ) ) { return reset_link( carrier, link_vp_index ); } - } else if( is_cable_too_long( square_dist( pos, t_veh_bub_pos + - t_veh->part( link_vp_index ).precalc[0] ) ) ) { + } else if( set_length_and_check( square_dist( pos, t_veh_bub_pos + + t_veh->part( link_vp_index ).precalc[0] ) ) ) { return reset_link( carrier, link_vp_index ); } } From 813597f74f41e2c97d8d936d1fa42d9a0acf7801 Mon Sep 17 00:00:00 2001 From: Kamayana Date: Mon, 31 Jul 2023 20:19:15 -0500 Subject: [PATCH 66/68] Cancel link_up if the target point is too far for the cable --- src/iuse_actor.cpp | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/src/iuse_actor.cpp b/src/iuse_actor.cpp index 1ed5d00902782..e7ff9737e9c40 100644 --- a/src/iuse_actor.cpp +++ b/src/iuse_actor.cpp @@ -4810,6 +4810,12 @@ std::optional link_up_actor::link_to_veh_app( Character *p, item &it, } } + if( trigdist ? trig_dist( prev_part_target.first, sel_part_target.first ) > it.link->max_length : + square_dist( prev_part_target.first, sel_part_target.first ) > it.link->max_length ) { + p->add_msg_if_player( m_warning, _( "The %1$s can't stretch that far!" ), it.type_name() ); + return std::nullopt; + } + const itype_id item_id = it.typeId(); vpart_id vpid = vpart_id::NULL_ID(); for( const vpart_info &e : vehicles::parts::get_all() ) { @@ -4943,6 +4949,20 @@ std::optional link_up_actor::link_tow_cable( Character *p, item &it, return std::nullopt; }; + // 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( selection ), + sel_veh->global_square_location().raw() ); + const std::pair sel_part_target = std::make_pair( + ( it.link->t_abs_pos + prev_veh->coord_translate( it.link->t_mount ) ).raw(), + it.link->t_abs_pos.raw() ); + + if( trigdist ? trig_dist( prev_part_target.first, sel_part_target.first ) > it.link->max_length : + square_dist( prev_part_target.first, sel_part_target.first ) > it.link->max_length ) { + p->add_msg_if_player( m_warning, _( "The %1$s can't stretch that far!" ), it.type_name() ); + return std::nullopt; + } + const itype_id item_id = it.typeId(); vpart_id vpid = vpart_id::NULL_ID(); for( const vpart_info &e : vehicles::parts::get_all() ) { @@ -4975,14 +4995,6 @@ std::optional link_up_actor::link_tow_cable( Character *p, item &it, return std::nullopt; } - // 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( selection ), - sel_veh->global_square_location().raw() ); - const std::pair sel_part_target = std::make_pair( - ( it.link->t_abs_pos + prev_veh->coord_translate( it.link->t_mount ) ).raw(), - it.link->t_abs_pos.raw() ); - vehicle_part prev_veh_part( vpid, item( it ) ); prev_veh_part.target.first = prev_part_target.first; prev_veh_part.target.second = prev_part_target.second; From c75cda2af5a282d8bffe71c13dbd5a33948bff88 Mon Sep 17 00:00:00 2001 From: Kamayana Date: Mon, 31 Jul 2023 20:30:10 -0500 Subject: [PATCH 67/68] Fix error when trying to link a cable immediately after loading or saving --- src/iuse_actor.cpp | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/iuse_actor.cpp b/src/iuse_actor.cpp index e7ff9737e9c40..51bc56e54acb7 100644 --- a/src/iuse_actor.cpp +++ b/src/iuse_actor.cpp @@ -4783,8 +4783,13 @@ std::optional link_up_actor::link_to_veh_app( Character *p, item &it, // Connecting one vehicle/appliance to another. if( !it.link->t_veh_safe ) { - debugmsg( "Failed to connect the %s, it lost its vehicle pointer!", it.tname() ); - return std::nullopt; + vehicle *found_veh = vehicle::find_vehicle( it.link->t_abs_pos ); + if( found_veh ) { + it.link->t_veh_safe = found_veh->get_safe_reference(); + } else { + debugmsg( "Failed to connect the %s, it lost its vehicle pointer!", it.tname() ); + return std::nullopt; + } } vehicle *const sel_veh = &s_vp->vehicle(); vehicle *const prev_veh = it.link->t_veh_safe.get(); @@ -4938,8 +4943,13 @@ std::optional link_up_actor::link_tow_cable( Character *p, item &it, // Connecting two vehicles with tow cable. if( !it.link->t_veh_safe ) { - debugmsg( "Failed to connect the %s, it lost its vehicle pointer!", it.tname() ); - return std::nullopt; + vehicle *found_veh = vehicle::find_vehicle( it.link->t_abs_pos ); + if( found_veh ) { + it.link->t_veh_safe = found_veh->get_safe_reference(); + } else { + debugmsg( "Failed to connect the %s, it lost its vehicle pointer!", it.tname() ); + return std::nullopt; + } } vehicle *const prev_veh = it.link->t_veh_safe.get(); if( prev_veh == sel_veh ) { From 45463cc6196f614a105928da2f5282324b6c398e Mon Sep 17 00:00:00 2001 From: Kamayana Date: Mon, 31 Jul 2023 20:32:00 -0500 Subject: [PATCH 68/68] Rename `heavy-duty cable` to `heavy-duty jumper cable` --- data/json/items/tool/cables.json | 4 ++-- data/json/vehicleparts/vehicle_parts.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/data/json/items/tool/cables.json b/data/json/items/tool/cables.json index f89eb3cfb4219..bf5e6abc56da1 100644 --- a/data/json/items/tool/cables.json +++ b/data/json/items/tool/cables.json @@ -49,8 +49,8 @@ { "type": "TOOL", "id": "jumper_cable_heavy", - "name": { "str": "heavy-duty cable" }, - "description": "A long, thick, heavy-duty cable with power leads on either end. It looks like you could use it to hook up two vehicles to each other, though you expect the power loss would be noticeable. Can also link other electrical systems.", + "name": { "str": "heavy-duty jumper cable" }, + "description": "A long, thick, heavy-duty jumper cable with power leads on either end. It looks like you could use it to hook up two vehicles to each other, though you expect the power loss would be noticeable. Can also link other electrical systems.", "volume": "1500 ml", "weight": "750 g", "max_charges": 20, diff --git a/data/json/vehicleparts/vehicle_parts.json b/data/json/vehicleparts/vehicle_parts.json index f3fff96da0eaa..a1baf3b98cb83 100644 --- a/data/json/vehicleparts/vehicle_parts.json +++ b/data/json/vehicleparts/vehicle_parts.json @@ -2424,7 +2424,7 @@ { "type": "vehicle_part", "id": "jumper_cable_heavy", - "name": { "str": "heavy-duty cable" }, + "name": { "str": "heavy-duty jumper cable" }, "categories": [ "other" ], "color": "yellow", "broken_color": "dark_gray",