From daf40026358449eef008064f49867395628ff030 Mon Sep 17 00:00:00 2001 From: Vollch Date: Tue, 12 Dec 2023 01:15:48 +0300 Subject: [PATCH] refactor(port): oxytorch migrated to activity actor and jsonified (#3873) * Jsonize Oxytorch Acitivty (#50133) Co-authored-by: Anton Burmistrov * Make it work * Remove unused const --------- Co-authored-by: Saicchi <47158232+Saicchi@users.noreply.github.com> Co-authored-by: Anton Burmistrov --- .../furniture-storage.json | 5 + .../furniture_and_terrain/terrain-doors.json | 25 ++ .../terrain-fences-gates.json | 16 + .../furniture_and_terrain/terrain-walls.json | 11 + .../terrain-windows.json | 44 +++ data/mods/TEST_DATA/furniture.json | 33 ++ data/mods/TEST_DATA/items.json | 27 ++ data/mods/TEST_DATA/terrain.json | 26 ++ .../docs/en/mod/json/reference/json_info.md | 22 ++ lang/extract_json_strings.py | 6 +- src/activity_actor.cpp | 157 +++++++++- src/activity_actor_definitions.h | 31 ++ src/activity_handlers.cpp | 77 ----- src/activity_handlers.h | 2 - src/iuse.cpp | 75 +---- src/mapdata.cpp | 11 + src/mapdata.h | 2 + tests/player_activities_test.cpp | 282 +++++++++++++++++- 18 files changed, 701 insertions(+), 151 deletions(-) diff --git a/data/json/furniture_and_terrain/furniture-storage.json b/data/json/furniture_and_terrain/furniture-storage.json index e08e630fe26f..f2c203bb346d 100644 --- a/data/json/furniture_and_terrain/furniture-storage.json +++ b/data/json/furniture_and_terrain/furniture-storage.json @@ -360,6 +360,7 @@ "lockpick_result": "f_gunsafe_o", "lockpick_message": "With a satisfying click, the lock on the gun safe door opens.", "examine_action": "locked_object_pickable", + "oxytorch": { "result": "f_safe_o", "duration": "14 seconds" }, "bash": { "str_min": 40, "str_max": 200, @@ -385,6 +386,7 @@ "required_str": 14, "max_volume": "250 L", "flags": [ "TRANSPARENT", "CONTAINER", "SEALED", "PLACE_ITEM", "MOUNTABLE", "MINEABLE" ], + "oxytorch": { "result": "f_safe_o", "duration": "14 seconds" }, "bash": { "str_min": 40, "str_max": 200, @@ -410,6 +412,7 @@ "required_str": 14, "max_volume": "250 L", "flags": [ "TRANSPARENT", "CONTAINER", "SEALED", "PLACE_ITEM", "MOUNTABLE", "MINEABLE" ], + "oxytorch": { "result": "f_safe_o", "duration": "14 seconds" }, "examine_action": "gunsafe_el", "bash": { "str_min": 40, @@ -520,6 +523,7 @@ "coverage": 70, "required_str": 8, "flags": [ "TRANSPARENT", "FLAMMABLE_HARD", "PLACE_ITEM", "BLOCKSDOOR", "MOUNTABLE" ], + "oxytorch": { "duration": "1 seconds", "byproducts": [ { "item": "steel_chunk", "count": [ 2, 6 ] } ] }, "deconstruct": { "items": [ { "item": "pipe", "count": 12 }, { "item": "sheet_metal", "count": 2 } ] }, "max_volume": "1750 L", "bash": { @@ -638,6 +642,7 @@ "coverage": 30, "required_str": 14, "max_volume": "250 L", + "oxytorch": { "result": "f_safe_o", "duration": "14 seconds" }, "flags": [ "TRANSPARENT", "CONTAINER", "SEALED", "PLACE_ITEM", "MOUNTABLE", "MINEABLE" ], "examine_action": "safe", "bash": { diff --git a/data/json/furniture_and_terrain/terrain-doors.json b/data/json/furniture_and_terrain/terrain-doors.json index 4349902862fc..38e42147ea04 100644 --- a/data/json/furniture_and_terrain/terrain-doors.json +++ b/data/json/furniture_and_terrain/terrain-doors.json @@ -2002,6 +2002,11 @@ "roof": "t_flat_roof", "flags": [ "NOITEM", "DOOR", "CONNECT_TO_WALL", "MINEABLE", "BLOCK_WIND" ], "open": "t_door_metal_o", + "oxytorch": { + "result": "t_mdoor_frame", + "duration": "14 seconds", + "byproducts": [ { "item": "steel_plate", "count": [ 0, 1 ] }, { "item": "steel_chunk", "count": [ 3, 8 ] } ] + }, "bash": { "str_min": 80, "str_max": 250, @@ -2192,6 +2197,11 @@ "move_cost": 0, "coverage": 95, "roof": "t_flat_roof", + "oxytorch": { + "result": "t_mdoor_frame", + "duration": "14 seconds", + "byproducts": [ { "item": "steel_plate", "count": [ 0, 1 ] }, { "item": "steel_chunk", "count": [ 3, 8 ] } ] + }, "flags": [ "NOITEM", "REDUCE_SCENT", "CONNECT_TO_WALL", "LOCKED", "MINEABLE", "BLOCK_WIND" ], "bash": { "str_min": 80, @@ -2247,6 +2257,11 @@ "flags": [ "NOITEM", "REDUCE_SCENT", "OPENCLOSE_INSIDE", "CONNECT_TO_WALL", "LOCKED", "MINEABLE", "BLOCK_WIND" ], "open": "t_door_metal_o", "examine_action": "locked_object_pickable", + "oxytorch": { + "result": "t_mdoor_frame", + "duration": "14 seconds", + "byproducts": [ { "item": "steel_plate", "count": [ 0, 1 ] }, { "item": "steel_chunk", "count": [ 3, 8 ] } ] + }, "bash": { "str_min": 80, "str_max": 250, @@ -2283,6 +2298,11 @@ "flags": [ "TRANSPARENT", "NOITEM", "PERMEABLE", "CONNECT_TO_WALL", "THIN_OBSTACLE" ], "open": "t_door_bar_o", "close": "t_door_bar_locked", + "oxytorch": { + "result": "t_mdoor_frame", + "duration": "14 seconds", + "byproducts": [ { "item": "steel_plate", "count": [ 0, 1 ] }, { "item": "steel_chunk", "count": [ 3, 8 ] } ] + }, "bash": { "str_min": 30, "str_max": 210, @@ -2340,6 +2360,11 @@ "lockpick_message": "The door swings open…", "flags": [ "TRANSPARENT", "NOITEM", "PERMEABLE", "CONNECT_TO_WALL", "LOCKED", "THIN_OBSTACLE" ], "examine_action": "locked_object_pickable", + "oxytorch": { + "result": "t_mdoor_frame", + "duration": "14 seconds", + "byproducts": [ { "item": "steel_plate", "count": [ 0, 1 ] }, { "item": "steel_chunk", "count": [ 3, 8 ] } ] + }, "bash": { "str_min": 30, "str_max": 210, diff --git a/data/json/furniture_and_terrain/terrain-fences-gates.json b/data/json/furniture_and_terrain/terrain-fences-gates.json index 96be1a542691..0c1aba515658 100644 --- a/data/json/furniture_and_terrain/terrain-fences-gates.json +++ b/data/json/furniture_and_terrain/terrain-fences-gates.json @@ -75,6 +75,11 @@ "flags": [ "TRANSPARENT", "PERMEABLE", "LOCKED", "THIN_OBSTACLE", "BURROWABLE" ], "connects_to": "CHAINFENCE", "examine_action": "locked_object_pickable", + "oxytorch": { + "result": "t_dirt", + "duration": "9 seconds", + "byproducts": [ { "item": "pipe", "count": [ 1, 4 ] }, { "item": "wire", "count": [ 4, 16 ] } ] + }, "bash": { "str_min": 10, "str_max": 150, @@ -96,6 +101,11 @@ "move_cost": 0, "flags": [ "TRANSPARENT", "DOOR", "PERMEABLE", "THIN_OBSTACLE", "BURROWABLE" ], "connects_to": "CHAINFENCE", + "oxytorch": { + "result": "t_dirt", + "duration": "9 seconds", + "byproducts": [ { "item": "pipe", "count": [ 1, 4 ] }, { "item": "wire", "count": [ 4, 16 ] } ] + }, "open": "t_chaingate_o", "bash": { "str_min": 10, @@ -389,6 +399,11 @@ "flags": [ "TRANSPARENT", "NOITEM", "THIN_OBSTACLE", "PERMEABLE", "UNSTABLE", "CLIMBABLE", "AUTO_WALL_SYMBOL", "BURROWABLE" ], "connects_to": "CHAINFENCE", "examine_action": "chainfence", + "oxytorch": { + "result": "t_dirt", + "duration": "9 seconds", + "byproducts": [ { "item": "pipe", "count": [ 1, 4 ] }, { "item": "wire", "count": [ 4, 16 ] } ] + }, "bash": { "str_min": 10, "str_max": 150, @@ -407,6 +422,7 @@ "color": "cyan", "move_cost": 2, "flags": [ "TRANSPARENT", "THIN_OBSTACLE" ], + "oxytorch": { "result": "t_dirt", "duration": "1 seconds", "byproducts": [ { "item": "pipe", "count": [ 1, 4 ] } ] }, "bash": { "str_min": 8, "str_max": 30, diff --git a/data/json/furniture_and_terrain/terrain-walls.json b/data/json/furniture_and_terrain/terrain-walls.json index 0517315c528f..7bff6740c885 100644 --- a/data/json/furniture_and_terrain/terrain-walls.json +++ b/data/json/furniture_and_terrain/terrain-walls.json @@ -421,6 +421,11 @@ "coverage": 100, "roof": "t_flat_roof", "flags": [ "NOITEM", "SUPPORTS_ROOF", "WALL", "NO_SCENT", "AUTO_WALL_SYMBOL", "MINEABLE", "BLOCK_WIND" ], + "oxytorch": { + "result": "t_scrap_wall_halfway", + "duration": "14 seconds", + "byproducts": [ { "item": "steel_plate", "count": [ 2, 3 ] }, { "item": "steel_chunk", "count": [ 12, 20 ] } ] + }, "bash": { "str_min": 200, "str_max": 600, @@ -551,6 +556,11 @@ "move_cost": 0, "flags": [ "TRANSPARENT", "NOITEM", "PERMEABLE", "THIN_OBSTACLE" ], "connects_to": "WALL", + "oxytorch": { + "result": "t_pit", + "duration": "9 seconds", + "byproducts": [ { "item": "spike", "count": [ 1, 19 ] }, { "item": "scrap", "count": [ 1, 8 ] } ] + }, "bash": { "str_min": 20, "str_max": 100, @@ -1075,6 +1085,7 @@ "color": "light_gray", "move_cost": 0, "flags": [ "TRANSPARENT", "NOITEM", "PERMEABLE", "CONNECT_TO_WALL", "THIN_OBSTACLE" ], + "oxytorch": { "result": "t_floor", "duration": "9 seconds", "byproducts": [ { "item": "pipe", "count": [ 1, 2 ] } ] }, "bash": { "str_min": 60, "str_max": 250, diff --git a/data/json/furniture_and_terrain/terrain-windows.json b/data/json/furniture_and_terrain/terrain-windows.json index 167e23d89b19..e0d9a7ccd949 100644 --- a/data/json/furniture_and_terrain/terrain-windows.json +++ b/data/json/furniture_and_terrain/terrain-windows.json @@ -427,6 +427,11 @@ "coverage": 95, "roof": "t_flat_roof", "flags": [ "NOITEM", "REDUCE_SCENT", "CONNECT_TO_WALL", "BLOCK_WIND" ], + "oxytorch": { + "result": "t_window_empty", + "duration": "4 seconds", + "byproducts": [ { "item": "steel_plate", "count": [ 0, 1 ] }, { "item": "sheet_metal", "count": [ 1, 3 ] } ] + }, "bash": { "str_min": 18, "str_max": 40, @@ -448,6 +453,11 @@ "coverage": 95, "roof": "t_flat_roof", "flags": [ "NOITEM", "REDUCE_SCENT", "CONNECT_TO_WALL", "BLOCK_WIND" ], + "oxytorch": { + "result": "t_window_empty", + "duration": "4 seconds", + "byproducts": [ { "item": "steel_plate", "count": [ 0, 1 ] }, { "item": "sheet_metal", "count": [ 1, 3 ] } ] + }, "bash": { "str_min": 18, "str_max": 40, @@ -468,6 +478,7 @@ "move_cost": 0, "roof": "t_flat_roof", "flags": [ "TRANSPARENT", "NOITEM", "CONNECT_TO_WALL", "THIN_OBSTACLE", "PERMEABLE" ], + "oxytorch": { "result": "t_window_empty", "duration": "9 seconds", "byproducts": [ { "item": "rebar", "count": [ 1, 2 ] } ] }, "bash": { "str_min": 60, "str_max": 250, @@ -493,6 +504,7 @@ "move_cost": 0, "delete": { "flags": [ "FLAMMABLE", "BARRICADABLE_WINDOW" ] }, "extend": { "flags": [ "ALARMED" ] }, + "oxytorch": { "result": "t_window_alarm", "duration": "9 seconds", "byproducts": [ { "item": "rebar", "count": [ 1, 2 ] } ] }, "bash": { "ter_set": "t_window_bars" } }, { @@ -506,6 +518,7 @@ "move_cost": 0, "coverage": 95, "roof": "t_flat_roof", + "oxytorch": { "result": "t_window_domestic", "duration": "9 seconds", "byproducts": [ { "item": "rebar", "count": [ 1, 2 ] } ] }, "flags": [ "NOITEM", "OPENCLOSE_INSIDE", @@ -544,6 +557,7 @@ "color": "light_gray", "move_cost": 0, "roof": "t_flat_roof", + "oxytorch": { "result": "t_window_domestic", "duration": "9 seconds", "byproducts": [ { "item": "rebar", "count": [ 1, 2 ] } ] }, "flags": [ "TRANSPARENT", "NOITEM", @@ -639,6 +653,11 @@ "color": "light_gray", "move_cost": 0, "roof": "t_flat_roof", + "oxytorch": { + "result": "t_window_reinforced", + "duration": "9 seconds", + "byproducts": [ { "item": "pipe", "count": [ 1, 12 ] }, { "item": "sheet_metal", "count": 4 } ] + }, "flags": [ "TRANSPARENT", "NOITEM", @@ -677,6 +696,11 @@ "color": "light_gray", "move_cost": 0, "roof": "t_flat_roof", + "oxytorch": { + "result": "t_window_reinforced", + "duration": "9 seconds", + "byproducts": [ { "item": "pipe", "count": [ 1, 12 ] }, { "item": "sheet_metal", "count": 4 } ] + }, "flags": [ "NOITEM", "CONNECT_TO_WALL", "THIN_OBSTACLE", "BARRICADABLE_WINDOW_CURTAINS", "BLOCK_WIND", "REDUCE_SCENT", "WINDOW" ], "curtain_transform": "t_window_bars", "examine_action": "curtains", @@ -719,6 +743,11 @@ "REDUCE_SCENT", "WINDOW" ], + "oxytorch": { + "result": "t_window_reinforced", + "duration": "9 seconds", + "byproducts": [ { "item": "pipe", "count": [ 1, 12 ] }, { "item": "sheet_metal", "count": 4 } ] + }, "curtain_transform": "t_window_bars", "examine_action": "curtains", "close": "t_metal_grate_window_with_curtain", @@ -751,6 +780,11 @@ "move_cost": 0, "roof": "t_flat_roof", "flags": [ "TRANSPARENT", "NOITEM", "CONNECT_TO_WALL", "BARRICADABLE_WINDOW", "THIN_OBSTACLE", "WINDOW" ], + "oxytorch": { + "result": "t_window_reinforced_noglass", + "duration": "9 seconds", + "byproducts": [ { "item": "pipe", "count": [ 1, 12 ] }, { "item": "sheet_metal", "count": 4 } ] + }, "deconstruct": { "ter_set": "t_window_reinforced", "items": [ { "item": "pipe", "count": 12 }, { "item": "sheet_metal_small", "count": 40 } ] @@ -782,6 +816,11 @@ "flags": [ "NOITEM", "CONNECT_TO_WALL", "THIN_OBSTACLE", "BARRICADABLE_WINDOW_CURTAINS", "BLOCK_WIND", "REDUCE_SCENT", "WINDOW" ], "curtain_transform": "t_metal_grate_window_noglass", "examine_action": "curtains", + "oxytorch": { + "result": "t_window_reinforced_noglass", + "duration": "9 seconds", + "byproducts": [ { "item": "pipe", "count": [ 1, 12 ] }, { "item": "sheet_metal", "count": 4 } ] + }, "open": "t_metal_grate_window_with_curtain_open", "deconstruct": { "ter_set": "t_window_reinforced", @@ -821,6 +860,11 @@ "REDUCE_SCENT", "WINDOW" ], + "oxytorch": { + "result": "t_window_reinforced_noglass", + "duration": "9 seconds", + "byproducts": [ { "item": "pipe", "count": [ 1, 12 ] }, { "item": "sheet_metal", "count": 4 } ] + }, "curtain_transform": "t_metal_grate_window_noglass", "examine_action": "curtains", "close": "t_metal_grate_window_with_curtain", diff --git a/data/mods/TEST_DATA/furniture.json b/data/mods/TEST_DATA/furniture.json index 7bca5fcaeff4..6dd8c950540e 100644 --- a/data/mods/TEST_DATA/furniture.json +++ b/data/mods/TEST_DATA/furniture.json @@ -41,5 +41,38 @@ "move_cost_mod": 1, "required_str": 8, "boltcut": { "duration": "80 seconds" } + }, + { + "type": "furniture", + "id": "test_f_oxytorch1", + "name": "Oxytorch Test Furniture 1", + "description": "Oxytorch furniture with valid data.", + "symbol": "f", + "color": "blue", + "move_cost_mod": 1, + "required_str": 8, + "oxytorch": { "duration": "5 seconds" } + }, + { + "type": "furniture", + "id": "test_f_oxytorch2", + "name": "Oxytorch Test Furniture 2", + "description": "Oxytorch furniture with valid data.", + "symbol": "f", + "color": "blue", + "move_cost_mod": 1, + "required_str": 8, + "oxytorch": { "result": "test_f_oxytorch1", "duration": "5 seconds" } + }, + { + "type": "furniture", + "id": "test_f_oxytorch3", + "name": "Oxytorch Test Furniture 1", + "description": "Oxytorch furniture with valid data.", + "symbol": "f", + "color": "blue", + "move_cost_mod": 1, + "required_str": 8, + "oxytorch": { "duration": "80 seconds" } } ] diff --git a/data/mods/TEST_DATA/items.json b/data/mods/TEST_DATA/items.json index 61eda5540955..783231bafdaf 100644 --- a/data/mods/TEST_DATA/items.json +++ b/data/mods/TEST_DATA/items.json @@ -565,6 +565,33 @@ "price": "75 USD", "price_postapoc": "3 USD" }, + { + "type": "MAGAZINE", + "id": "test_weldtank", + "name": { "str": "welding tank" }, + "description": "A test welding tank full of oxyacetylene.", + "weight": "6 kg", + "volume": "3 L", + "symbol": "#", + "ammo_type": "weldgas", + "capacity": 240, + "count": 240, + "flags": [ "NO_UNLOAD", "NO_RELOAD" ] + }, + { + "type": "TOOL", + "id": "test_oxytorch", + "name": { "str": "test acetylene torch", "str_pl": "test acetylene torches" }, + "description": "An acetylene torch.", + "weight": "1 kg", + "volume": "1 L", + "symbol": ";", + "ammo": "weldgas", + "qualities": [ [ "WELD", 10 ] ], + "charges_per_use": 4, + "use_action": [ "OXYTORCH" ], + "magazines": [ [ "weldgas", [ "weldtank", "tinyweldtank" ] ] ] + }, { "type": "TOOL", "id": "test_boltcutter", diff --git a/data/mods/TEST_DATA/terrain.json b/data/mods/TEST_DATA/terrain.json index 1bb2b146a623..ad12ee1f4af8 100644 --- a/data/mods/TEST_DATA/terrain.json +++ b/data/mods/TEST_DATA/terrain.json @@ -89,5 +89,31 @@ "duration": "5 seconds", "byproducts": [ { "item": "test_rock", "count": 3 }, { "item": "test_2x4", "count": [ 7, 9 ] } ] } + }, + { + "type": "terrain", + "id": "test_t_oxytorch1", + "name": "Oxytorch Test Terrain 1", + "description": "Oxytorch terrain with valid data.", + "symbol": "t", + "color": "blue", + "move_cost": 2, + "bash": { "sound": "thump", "ter_set": "t_null", "str_min": 50, "str_max": 100, "str_min_supported": 100, "bash_below": true }, + "oxytorch": { "result": "t_dirt", "duration": "10 seconds" } + }, + { + "type": "terrain", + "id": "test_t_oxytorch2", + "name": "Oxytorch Test Terrain 2", + "description": "Oxytorch terrain with valid data.", + "symbol": "t", + "color": "blue", + "move_cost": 2, + "bash": { "sound": "thump", "ter_set": "t_null", "str_min": 50, "str_max": 100, "str_min_supported": 100, "bash_below": true }, + "oxytorch": { + "result": "t_dirt", + "duration": "5 seconds", + "byproducts": [ { "item": "test_rock", "count": 3 }, { "item": "test_2x4", "count": [ 7, 9 ] } ] + } } ] diff --git a/doc/src/content/docs/en/mod/json/reference/json_info.md b/doc/src/content/docs/en/mod/json/reference/json_info.md index 7d715198cf3d..7fa510fc9ab1 100644 --- a/doc/src/content/docs/en/mod/json/reference/json_info.md +++ b/doc/src/content/docs/en/mod/json/reference/json_info.md @@ -2663,6 +2663,28 @@ underlying terrain. (Optional) When the furniture is successfully lockpicked, this is the message that will be printed to the player. When it is missing, a generic `"The lock opens…"` message will be printed instead. +#### `oxytorch` + +(Optional) Data for using with an oxytorch. + +```cpp +oxytorch: { + "result": "furniture_id", // (optional) furniture it will become when done, defaults to f_null + "duration": "1 seconds", // ( optional ) time required for oxytorching, default is 1 second + "message": "You quickly cut the metal", // ( optional ) message that will be displayed when finished + "byproducts": [ // ( optional ) list of items that will be spawned when finished + { + "item": "item_id", + "count": 100 // exact amount + }, + { + "item": "item_id", + "count": [ 10, 100 ] // random number in range ( inclusive ) + } + ] +} +``` + #### `light_emitted` How much light the furniture produces. 10 will light the tile it's on brightly, 15 will light that diff --git a/lang/extract_json_strings.py b/lang/extract_json_strings.py index cedeed337119..bf5866c6bcf4 100755 --- a/lang/extract_json_strings.py +++ b/lang/extract_json_strings.py @@ -1095,6 +1095,10 @@ def extract_json(state, item): if "sound_fail" in bash: writestr(state, bash["sound_fail"]) wrote = True + if "oxytorch" in item and "message" in item["oxytorch"]: + c = f"message when oxytorch cutting {name}" + writestr(state, item["oxytorch"]["message"], comment=c) + wrote = True if "boltcut" in item: boltcut = item["boltcut"] if "sound" in boltcut: @@ -1103,7 +1107,7 @@ def extract_json(state, item): wrote = True if "message" in boltcut: c = f"message when finished bolt cutting {name}" - writestr(state, boltcut["sound"], comment=c) + writestr(state, boltcut["message"], comment=c) wrote = True if "pry" in item: pry = item["pry"] diff --git a/src/activity_actor.cpp b/src/activity_actor.cpp index 3d62eba12305..97f5292fd5bb 100644 --- a/src/activity_actor.cpp +++ b/src/activity_actor.cpp @@ -21,6 +21,7 @@ #include "enums.h" #include "event.h" #include "event_bus.h" +#include "field_type.h" #include "flag.h" #include "game.h" #include "gates.h" @@ -1182,7 +1183,6 @@ void boltcutting_activity_actor::finish( player_activity &act, Character &who ) if( byproduct.item->count_by_charges() ) { here.add_item_or_charges( target, item::spawn( byproduct.item, calendar::turn, amount ) ); } else { - item byproduct_item( byproduct.item, calendar::turn ); for( int i = 0; i < amount; ++i ) { here.add_item_or_charges( target, item::spawn( byproduct.item, calendar::turn ) ); } @@ -1410,6 +1410,160 @@ std::unique_ptr lockpick_activity_actor::deserialize( JsonIn &js return actor; } +void oxytorch_activity_actor::start( player_activity &act, Character &/*who*/ ) +{ + const map &here = get_map(); + + if( here.has_furn( target ) ) { + const furn_id furn_type = here.furn( target ); + if( !furn_type->oxytorch->valid() ) { + if( !testing ) { + debugmsg( "%s oxytorch is invalid", furn_type.id().str() ); + } + act.set_to_null(); + return; + } + + act.moves_total = to_moves( furn_type->oxytorch->duration() ); + } else if( !here.ter( target )->is_null() ) { + const ter_id ter_type = here.ter( target ); + if( !ter_type->oxytorch->valid() ) { + if( !testing ) { + debugmsg( "%s oxytorch is invalid", ter_type.id().str() ); + } + act.set_to_null(); + return; + } + act.moves_total = to_moves( ter_type->oxytorch->duration() ); + } else { + if( !testing ) { + debugmsg( "oxytorch activity called on invalid terrain" ); + } + act.set_to_null(); + return; + } + + act.moves_left = act.moves_total; +} + +void oxytorch_activity_actor::do_turn( player_activity &/*act*/, Character &who ) +{ + if( tool->ammo_sufficient() ) { + tool->ammo_consume( tool->ammo_required(), tool->position() ); + sfx::play_activity_sound( "tool", "oxytorch", sfx::get_heard_volume( target ) ); + if( calendar::once_every( 2_turns ) ) { + sounds::sound( target, 10, sounds::sound_t::destructive_activity, _( "hissssssssss!" ) ); + } + } else { + if( who.is_avatar() ) { + who.add_msg_if_player( m_bad, _( "Your %1$s ran out of charges." ), tool->tname() ); + } else { // who.is_npc() + if( get_avatar().sees( who.pos() ) ) { + add_msg( _( "%1$s %2$s ran out of charges." ), who.disp_name( false, + true ), tool->tname() ); + } + } + who.cancel_activity(); + } +} + +void oxytorch_activity_actor::finish( player_activity &act, Character &who ) +{ + map &here = get_map(); + std::string message; + const activity_data_common *data; + + if( here.has_furn( target ) ) { + const furn_id furn_type = here.furn( target ); + if( !furn_type->oxytorch->valid() ) { + if( !testing ) { + debugmsg( "%s oxytorch is invalid", furn_type.id().str() ); + } + act.set_to_null(); + return; + } + + const furn_str_id new_furn = furn_type->oxytorch->result(); + if( !new_furn.is_valid() ) { + if( !testing ) { + debugmsg( "oxytorch furniture: %s invalid furniture", new_furn.str() ); + } + act.set_to_null(); + return; + } + + data = static_cast( &*furn_type->oxytorch ); + here.furn_set( target, new_furn ); + } else if( !here.ter( target )->is_null() ) { + const ter_id ter_type = here.ter( target ); + if( !ter_type->oxytorch->valid() ) { + if( !testing ) { + debugmsg( "%s oxytorch is invalid", ter_type.id().str() ); + } + act.set_to_null(); + return; + } + + const ter_str_id new_ter = ter_type->oxytorch->result(); + if( !new_ter.is_valid() ) { + if( !testing ) { + debugmsg( "oxytorch terrain: %s invalid terrain", new_ter.str() ); + } + act.set_to_null(); + return; + } + + data = static_cast( &*ter_type->oxytorch ); + here.ter_set( target, new_ter ); + } else { + if( !testing ) { + debugmsg( "oxytorch activity finished on invalid terrain" ); + } + act.set_to_null(); + return; + } + + for( const activity_byproduct &byproduct : data->byproducts() ) { + const int amount = byproduct.roll(); + if( byproduct.item->count_by_charges() ) { + here.add_item_or_charges( target, item::spawn( byproduct.item, calendar::turn, amount ) ); + } else { + for( int i = 0; i < amount; ++i ) { + here.add_item_or_charges( target, item::spawn( byproduct.item, calendar::turn ) ); + } + } + } + + // 50% chance of starting a fire. + if( one_in( 2 ) && here.flammable_items_at( target ) ) { + here.add_field( target, fd_fire, 1, 10_minutes ); + } + + if( !data->message().empty() ) { + who.add_msg_if_player( m_info, data->message().translated() ); + } + + act.set_to_null(); +} + +void oxytorch_activity_actor::serialize( JsonOut &jsout ) const +{ + jsout.start_object(); + jsout.member( "target", target ); + jsout.member( "tool", tool ); + jsout.end_object(); +} + +std::unique_ptr oxytorch_activity_actor::deserialize( JsonIn &jsin ) +{ + std::unique_ptr actor( new oxytorch_activity_actor( + tripoint_zero, safe_reference() ) ); + JsonObject data = jsin.get_object(); + data.read( "target", actor->target ); + data.read( "tool", actor->tool ); + return actor; +} + void migration_cancel_activity_actor::do_turn( player_activity &act, Character &who ) { // Stop the activity @@ -1625,6 +1779,7 @@ deserialize_functions = { { activity_id( "ACT_MIGRATION_CANCEL" ), &migration_cancel_activity_actor::deserialize }, { activity_id( "ACT_MOVE_ITEMS" ), &move_items_activity_actor::deserialize }, { activity_id( "ACT_TOGGLE_GATE" ), &toggle_gate_activity_actor::deserialize }, + { activity_id( "ACT_OXYTORCH" ), &oxytorch_activity_actor::deserialize }, { activity_id( "ACT_PICKUP" ), &pickup_activity_actor::deserialize }, { activity_id( "ACT_STASH" ), &stash_activity_actor::deserialize }, { activity_id( "ACT_THROW" ), &throw_activity_actor::deserialize }, diff --git a/src/activity_actor_definitions.h b/src/activity_actor_definitions.h index 5adb8ca8fdb1..11643c0e8a17 100644 --- a/src/activity_actor_definitions.h +++ b/src/activity_actor_definitions.h @@ -564,4 +564,35 @@ class wash_activity_actor : public activity_actor static std::unique_ptr deserialize( JsonIn &jsin ); }; +class oxytorch_activity_actor : public activity_actor +{ + public: + explicit oxytorch_activity_actor( const tripoint &target, + const safe_reference &tool ) : target( target ), tool( tool ) {}; + + activity_id get_type() const override { + return activity_id( "ACT_OXYTORCH" ); + } + + void start( player_activity &act, Character &who ) override; + void do_turn( player_activity &/*act*/, Character &who ) override; + void finish( player_activity &act, Character &who ) override; + + void serialize( JsonOut &jsout ) const override; + static std::unique_ptr deserialize( JsonIn &jsin ); + + // debugmsg causes a backtrace when fired during cata_test + bool testing = false; // NOLINT(cata-serialize) + private: + tripoint target; + safe_reference tool; + + bool can_resume_with_internal( const activity_actor &other, + const Character &/*who*/ ) const override { + const oxytorch_activity_actor &actor = static_cast + ( other ); + return actor.target == target; + } +}; + #endif // CATA_SRC_ACTIVITY_ACTOR_DEFINITIONS_H diff --git a/src/activity_handlers.cpp b/src/activity_handlers.cpp index f84c22b609b2..55080373d4aa 100644 --- a/src/activity_handlers.cpp +++ b/src/activity_handlers.cpp @@ -157,7 +157,6 @@ static const activity_id ACT_MULTIPLE_MINE( "ACT_MULTIPLE_MINE" ); static const activity_id ACT_MULTIPLE_FARM( "ACT_MULTIPLE_FARM" ); static const activity_id ACT_MULTIPLE_FISH( "ACT_MULTIPLE_FISH" ); static const activity_id ACT_OPERATION( "ACT_OPERATION" ); -static const activity_id ACT_OXYTORCH( "ACT_OXYTORCH" ); static const activity_id ACT_PICKAXE( "ACT_PICKAXE" ); static const activity_id ACT_PLANT_SEED( "ACT_PLANT_SEED" ); static const activity_id ACT_PLAY_WITH_PET( "ACT_PLAY_WITH_PET" ); @@ -217,12 +216,10 @@ static const itype_id itype_pipe( "pipe" ); static const itype_id itype_rope_30( "rope_30" ); static const itype_id itype_rope_makeshift_30( "rope_makeshift_30" ); static const itype_id itype_scrap( "scrap" ); -static const itype_id itype_sheet_metal( "sheet_metal" ); static const itype_id itype_spike( "spike" ); static const itype_id itype_splinter( "splinter" ); static const itype_id itype_stick_long( "stick_long" ); static const itype_id itype_steel_chunk( "steel_chunk" ); -static const itype_id itype_steel_plate( "steel_plate" ); static const itype_id itype_vine_30( "vine_30" ); static const itype_id itype_wire( "wire" ); static const itype_id itype_welder( "welder" ); @@ -278,7 +275,6 @@ activity_handlers::do_turn_functions = { { ACT_START_FIRE, start_fire_do_turn }, { ACT_VIBE, vibe_do_turn }, { ACT_HAND_CRANK, hand_crank_do_turn }, - { ACT_OXYTORCH, oxytorch_do_turn }, { ACT_WEAR, wear_do_turn }, { ACT_MULTIPLE_FISH, multiple_fish_do_turn }, { ACT_MULTIPLE_CONSTRUCTION, multiple_construction_do_turn }, @@ -354,7 +350,6 @@ activity_handlers::finish_functions = { { ACT_PLANT_SEED, plant_seed_finish }, { ACT_VEHICLE, vehicle_finish }, { ACT_START_ENGINES, start_engines_finish }, - { ACT_OXYTORCH, oxytorch_finish }, { ACT_PULP, pulp_finish }, { ACT_CRACKING, cracking_finish }, { ACT_REPAIR_ITEM, repair_item_finish }, @@ -2375,78 +2370,6 @@ void activity_handlers::start_engines_finish( player_activity *act, player *p ) } } -void activity_handlers::oxytorch_do_turn( player_activity *act, player *p ) -{ - if( act->values[0] <= 0 ) { - return; - } - - item &it = *act->targets.front(); - // act->values[0] is the number of charges yet to be consumed - const int charges_used = std::min( act->values[0], it.ammo_required() ); - - it.ammo_consume( charges_used, p->pos() ); - act->values[0] -= static_cast( charges_used ); - - sfx::play_activity_sound( "tool", "oxytorch", sfx::get_heard_volume( act->placement ) ); - if( calendar::once_every( 2_turns ) ) { - sounds::sound( act->placement, 10, sounds::sound_t::destructive_activity, _( "hissssssssss!" ) ); - } -} - -void activity_handlers::oxytorch_finish( player_activity *act, player *p ) -{ - act->set_to_null(); - map &here = get_map(); - const tripoint &pos = act->placement; - const ter_id ter = here.ter( pos ); - - // fast players might still have some charges left to be consumed - act->targets.front()->ammo_consume( act->values[0], p->pos() ); - - if( here.furn( pos ) == f_rack ) { - here.furn_set( pos, f_null ); - here.spawn_item( p->pos(), itype_steel_chunk, rng( 2, 6 ) ); - } else if( ter == t_chainfence || ter == t_chaingate_c || ter == t_chaingate_l ) { - here.ter_set( pos, t_dirt ); - here.spawn_item( pos, itype_pipe, rng( 1, 4 ) ); - here.spawn_item( pos, itype_wire, rng( 4, 16 ) ); - } else if( ter == t_chainfence_posts ) { - here.ter_set( pos, t_dirt ); - here.spawn_item( pos, itype_pipe, rng( 1, 4 ) ); - } else if( ter == t_door_metal_locked || ter == t_door_metal_c || ter == t_door_bar_c || - ter == t_door_bar_locked || ter == t_door_metal_pickable ) { - here.ter_set( pos, t_mdoor_frame ); - here.spawn_item( pos, itype_steel_plate, rng( 0, 1 ) ); - here.spawn_item( pos, itype_steel_chunk, rng( 3, 8 ) ); - } else if( ter == t_window_enhanced || ter == t_window_enhanced_noglass ) { - here.ter_set( pos, t_window_empty ); - here.spawn_item( pos, itype_steel_plate, rng( 0, 1 ) ); - here.spawn_item( pos, itype_sheet_metal, rng( 1, 3 ) ); - } else if( ter == t_reb_cage ) { - here.ter_set( pos, t_pit ); - here.spawn_item( pos, itype_spike, rng( 1, 19 ) ); - here.spawn_item( pos, itype_scrap, rng( 1, 8 ) ); - } else if( ter == t_bars ) { - if( here.ter( pos + point_east ) == t_sewage || here.ter( pos + point_south ) == - t_sewage || - here.ter( pos + point_west ) == t_sewage || here.ter( pos + point_north ) == - t_sewage ) { - here.ter_set( pos, t_sewage ); - here.spawn_item( p->pos(), itype_pipe, rng( 1, 2 ) ); - } else { - here.ter_set( pos, t_floor ); - here.spawn_item( p->pos(), itype_pipe, rng( 1, 2 ) ); - } - } else if( ter == t_window_bars_alarm ) { - here.ter_set( pos, t_window_alarm ); - here.spawn_item( p->pos(), itype_pipe, rng( 1, 2 ) ); - } else if( ter == t_window_bars ) { - here.ter_set( pos, t_window_empty ); - here.spawn_item( p->pos(), itype_pipe, rng( 1, 2 ) ); - } -} - void activity_handlers::cracking_finish( player_activity *act, player *p ) { p->add_msg_if_player( m_good, _( "With a satisfying click, the lock on the safe opens!" ) ); diff --git a/src/activity_handlers.h b/src/activity_handlers.h index ae60779a8f59..bc4d2bc2905e 100644 --- a/src/activity_handlers.h +++ b/src/activity_handlers.h @@ -183,7 +183,6 @@ void start_fire_do_turn( player_activity *act, player *p ); void vibe_do_turn( player_activity *act, player *p ); void hand_crank_do_turn( player_activity *act, player *p ); void multiple_chop_planks_do_turn( player_activity *act, player *p ); -void oxytorch_do_turn( player_activity *act, player *p ); void wear_do_turn( player_activity *act, player *p ); void eat_menu_do_turn( player_activity *act, player *p ); void consume_food_menu_do_turn( player_activity *act, player *p ); @@ -250,7 +249,6 @@ void vehicle_finish( player_activity *act, player *p ); void start_engines_finish( player_activity *act, player *p ); void churn_finish( player_activity *act, player *p ); void plant_seed_finish( player_activity *act, player *p ); -void oxytorch_finish( player_activity *act, player *p ); void cracking_finish( player_activity *act, player *p ); void repair_item_finish( player_activity *act, player *p ); void mend_item_finish( player_activity *act, player *p ); diff --git a/src/iuse.cpp b/src/iuse.cpp index 56beddfbbffb..31ae6bfb1338 100644 --- a/src/iuse.cpp +++ b/src/iuse.cpp @@ -139,7 +139,6 @@ static const activity_id ACT_HAND_CRANK( "ACT_HAND_CRANK" ); static const activity_id ACT_JACKHAMMER( "ACT_JACKHAMMER" ); static const activity_id ACT_MEDITATE( "ACT_MEDITATE" ); static const activity_id ACT_MIND_SPLICER( "ACT_MIND_SPLICER" ); -static const activity_id ACT_OXYTORCH( "ACT_OXYTORCH" ); static const activity_id ACT_PICKAXE( "ACT_PICKAXE" ); static const activity_id ACT_PRY_NAILS( "ACT_PRY_NAILS" ); static const activity_id ACT_ROBOT_CONTROL( "ACT_ROBOT_CONTROL" ); @@ -4913,38 +4912,17 @@ int iuse::oxytorch( player *p, item *it, bool, const tripoint & ) return 0; } - const std::set allowed_ter_id { - t_chainfence_posts, - t_window_enhanced, - t_window_enhanced_noglass, - t_chainfence, - t_chaingate_c, - t_chaingate_l, - t_bars, - t_window_bars_alarm, - t_window_bars, - t_reb_cage, - t_door_metal_locked, - t_door_metal_c, - t_door_bar_c, - t_door_bar_locked, - t_door_metal_pickable - }; - const std::set allowed_furn_id { - f_rack - }; - - const std::function f = [&allowed_ter_id, - &allowed_furn_id]( const tripoint & pnt ) { - if( pnt == g->u.pos() ) { + map &here = get_map(); + const std::function f = + [&here, p]( const tripoint & pnt ) { + if( pnt == p->pos() ) { return false; + } else if( here.has_furn( pnt ) ) { + return here.furn( pnt )->oxytorch->valid(); + } else if( !here.ter( pnt )->is_null() ) { + return here.ter( pnt )->oxytorch->valid(); } - const ter_id ter = g->m.ter( pnt ); - const auto furn = g->m.furn( pnt ); - - const bool is_allowed = ( allowed_ter_id.find( ter ) != allowed_ter_id.end() ) || - ( allowed_furn_id.find( furn ) != allowed_furn_id.end() ); - return is_allowed; + return false; }; const std::optional pnt_ = choose_adjacent_highlight( @@ -4953,8 +4931,6 @@ int iuse::oxytorch( player *p, item *it, bool, const tripoint & ) return 0; } const tripoint &pnt = *pnt_; - const ter_id ter = g->m.ter( pnt ); - const furn_id furn = g->m.furn( pnt ); if( !f( pnt ) ) { if( pnt == p->pos() ) { p->add_msg_if_player( m_info, _( "Yuck. Acetylene gas smells weird." ) ); @@ -4964,37 +4940,10 @@ int iuse::oxytorch( player *p, item *it, bool, const tripoint & ) return 0; } - int turns = 0; - if( furn == f_rack || ter == t_chainfence_posts ) { - turns = to_turns( 2_seconds ); - } else if( ter == t_window_enhanced || ter == t_window_enhanced_noglass ) { - turns = to_turns( 5_seconds ); - } else if( ter == t_chainfence || ter == t_chaingate_c || - ter == t_chaingate_l || ter == t_bars || ter == t_window_bars_alarm || - ter == t_window_bars || ter == t_reb_cage ) { - turns = to_turns( 10_seconds ); - } else if( ter == t_door_metal_locked || ter == t_door_metal_c || ter == t_door_bar_c || - ter == t_door_bar_locked || ter == t_door_metal_pickable ) { - turns = to_turns( 15_seconds ); - } else { - return 0; - } - - const int charges = turns * it->ammo_required(); - int moves = to_moves( time_duration::from_turns( turns ) ); - - if( charges > it->ammo_remaining() ) { - p->add_msg_if_player( m_info, _( "Your torch doesn't have enough acetylene to cut that." ) ); - return 0; - } - - // placing ter here makes resuming tasks work better - p->assign_activity( ACT_OXYTORCH, moves, static_cast( ter ) ); - p->activity->targets.emplace_back( it ); - p->activity->placement = pnt; - p->activity->values.push_back( charges ); + p->assign_activity( std::make_unique( std::make_unique( + pnt, safe_reference( *it ) + ) ) ); - // charges will be consumed in oxytorch_do_turn, not here return 0; } diff --git a/src/mapdata.cpp b/src/mapdata.cpp index e42172143ae9..e18787617609 100644 --- a/src/mapdata.cpp +++ b/src/mapdata.cpp @@ -1337,6 +1337,11 @@ void ter_t::load( const JsonObject &jo, const std::string &src ) optional( jo, was_loaded, "lockpick_result", lockpick_result, ter_str_id::NULL_ID() ); optional( jo, was_loaded, "lockpick_message", lockpick_message, translation() ); + oxytorch = cata::make_value(); + if( jo.has_object( "oxytorch" ) ) { + oxytorch->load( jo.get_object( "oxytorch" ) ); + } + boltcut = cata::make_value(); if( jo.has_object( "boltcut" ) ) { boltcut->load( jo.get_object( "boltcut" ) ); @@ -1532,6 +1537,12 @@ void furn_t::load( const JsonObject &jo, const std::string &src ) optional( jo, was_loaded, "lockpick_result", lockpick_result, furn_str_id::NULL_ID() ); optional( jo, was_loaded, "lockpick_message", lockpick_message, translation() ); + + oxytorch = cata::make_value(); + if( jo.has_object( "oxytorch" ) ) { + oxytorch->load( jo.get_object( "oxytorch" ) ); + } + boltcut = cata::make_value(); if( jo.has_object( "boltcut" ) ) { boltcut->load( jo.get_object( "boltcut" ) ); diff --git a/src/mapdata.h b/src/mapdata.h index efd576e6ba26..daeaf836ce49 100644 --- a/src/mapdata.h +++ b/src/mapdata.h @@ -544,6 +544,7 @@ struct ter_t : map_data_common_t { translation lockpick_message; // Lockpick action: message when successfully lockpicked cata::value_ptr boltcut; // Bolt cutting action data + cata::value_ptr oxytorch; // Oxytorch action data std::string trap_id_str; // String storing the id string of the trap. ter_str_id transforms_into; // Transform into what terrain? @@ -599,6 +600,7 @@ struct furn_t : map_data_common_t { int move_str_req = 0; //The amount of strength required to move through this furniture easily. cata::value_ptr boltcut; // Bolt cutting action data + cata::value_ptr oxytorch; // Oxytorch action data cata::value_ptr workbench; diff --git a/tests/player_activities_test.cpp b/tests/player_activities_test.cpp index f2f63bd39a73..04316b1d9e32 100644 --- a/tests/player_activities_test.cpp +++ b/tests/player_activities_test.cpp @@ -6,7 +6,6 @@ #include "avatar.h" #include "calendar.h" #include "character.h" -#include "flag.h" #include "game.h" #include "itype.h" #include "iuse_actor.h" @@ -16,13 +15,24 @@ static const activity_id ACT_NULL( "ACT_NULL" ); static const activity_id ACT_BOLTCUTTING( "ACT_BOLTCUTTING" ); +static const activity_id ACT_OXYTORCH( "ACT_OXYTORCH" ); static const furn_str_id furn_t_test_f_boltcut1( "test_f_boltcut1" ); static const furn_str_id furn_t_test_f_boltcut2( "test_f_boltcut2" ); static const furn_str_id furn_t_test_f_boltcut3( "test_f_boltcut3" ); +static const furn_str_id furn_t_test_f_oxytorch1( "test_f_oxytorch1" ); +static const furn_str_id furn_t_test_f_oxytorch2( "test_f_oxytorch2" ); +static const furn_str_id furn_t_test_f_oxytorch3( "test_f_oxytorch3" ); +static const itype_id itype_oxyacetylene( "oxyacetylene" ); static const itype_id itype_test_boltcutter( "test_boltcutter" ); static const itype_id itype_test_boltcutter_elec( "test_boltcutter_elec" ); +static const itype_id itype_test_oxytorch( "test_oxytorch" ); + +static const quality_id qual_WELD( "WELD" ); + +static const ter_str_id ter_test_t_oxytorch1( "test_t_oxytorch1" ); +static const ter_str_id ter_test_t_oxytorch2( "test_t_oxytorch2" ); static const ter_str_id ter_test_t_boltcut1( "test_t_boltcut1" ); static const ter_str_id ter_test_t_boltcut2( "test_t_boltcut2" ); @@ -157,13 +167,13 @@ TEST_CASE( "boltcut", "[activity][boltcut]" ) mp.furn_set( tripoint_zero, furn_t_test_f_boltcut3 ); REQUIRE( mp.furn( tripoint_zero ) == furn_t_test_f_boltcut3 ); - item &loc = dummy.i_add( item::spawn( itype_test_boltcutter_elec, calendar::start_of_cataclysm, - 2 ) ); - dummy.wield( loc ); + item &boltcutter_elec = dummy.i_add( item::spawn( itype_test_boltcutter_elec, + calendar::start_of_cataclysm, 2 ) ); + dummy.wield( boltcutter_elec ); REQUIRE( dummy.primary_weapon().typeId() == itype_test_boltcutter_elec ); - setup_activity( loc ); + setup_activity( boltcutter_elec ); REQUIRE( dummy.activity->id() == ACT_BOLTCUTTING ); process_activity( dummy ); @@ -171,10 +181,10 @@ TEST_CASE( "boltcut", "[activity][boltcut]" ) REQUIRE( dummy.activity->id() == ACT_NULL ); THEN( "player recharges with fuel" ) { - loc.ammo_set( loc.ammo_default(), -1 ); + boltcutter_elec.ammo_set( boltcutter_elec.ammo_default(), -1 ); AND_THEN( "player can resume the activity" ) { - setup_activity( loc ); + setup_activity( boltcutter_elec ); dummy.moves = dummy.get_speed(); dummy.activity->do_turn( dummy ); CHECK( dummy.activity->id() == ACT_BOLTCUTTING ); @@ -286,3 +296,261 @@ TEST_CASE( "boltcut", "[activity][boltcut]" ) } } } + +TEST_CASE( "oxytorch", "[activity][oxytorch]" ) +{ + map &mp = get_map(); + avatar &dummy = get_avatar(); + + auto setup_dummy = [&dummy]() -> item & { + item &loc = dummy.i_add( item::spawn( itype_test_oxytorch ) ); + loc.ammo_set( itype_oxyacetylene, -1 ); + dummy.wield( loc ); + + REQUIRE( dummy.primary_weapon().typeId() == itype_test_oxytorch ); + REQUIRE( dummy.max_quality( qual_WELD ) == 10 ); + + return loc; + }; + + auto setup_activity = [&dummy]( item & torch ) -> void { + std::unique_ptr act = std::make_unique( + tripoint_zero, safe_reference( torch ) + ); + act->testing = true; + dummy.assign_activity( std::make_unique( std::move( act ) ) ); + }; + + SECTION( "oxytorch start checks" ) { + GIVEN( "a tripoint with nothing" ) { + clear_map(); + clear_avatar(); + + mp.ter_set( tripoint_zero, t_null ); + REQUIRE( mp.ter( tripoint_zero ) == t_null ); + + item &welding_torch = setup_dummy(); + setup_activity( welding_torch ); + + THEN( "oxytorch activity can't start" ) { + CHECK( dummy.activity->id() == ACT_NULL ); + } + } + + GIVEN( "a tripoint with invalid terrain" ) { + clear_map(); + clear_avatar(); + + mp.ter_set( tripoint_zero, t_dirt ); + REQUIRE( mp.ter( tripoint_zero ) == t_dirt ); + + item &welding_torch = setup_dummy(); + setup_activity( welding_torch ); + + THEN( "oxytorch activity can't start" ) { + CHECK( dummy.activity->id() == ACT_NULL ); + } + } + + GIVEN( "a tripoint with valid terrain" ) { + clear_map(); + clear_avatar(); + + mp.ter_set( tripoint_zero, ter_test_t_oxytorch1 ); + REQUIRE( mp.ter( tripoint_zero ) == ter_test_t_oxytorch1 ); + + item &welding_torch = setup_dummy(); + setup_activity( welding_torch ); + + THEN( "oxytorch activity can start" ) { + CHECK( dummy.activity->id() == ACT_OXYTORCH ); + } + } + + GIVEN( "a tripoint with valid furniture" ) { + clear_map(); + clear_avatar(); + + mp.furn_set( tripoint_zero, furn_t_test_f_oxytorch1 ); + REQUIRE( mp.furn( tripoint_zero ) == furn_t_test_f_oxytorch1 ); + + item &welding_torch = setup_dummy(); + setup_activity( welding_torch ); + + THEN( "oxytorch activity can start" ) { + CHECK( dummy.activity->id() == ACT_OXYTORCH ); + } + } + + GIVEN( "a tripoint with valid terrain" ) { + clear_map(); + clear_avatar(); + + mp.ter_set( tripoint_zero, ter_test_t_oxytorch1 ); + REQUIRE( mp.ter( tripoint_zero ) == ter_test_t_oxytorch1 ); + + item &welding_torch = setup_dummy(); + setup_activity( welding_torch ); + REQUIRE( dummy.activity->id() == ACT_OXYTORCH ); + + WHEN( "terrain has a duration of 10 seconds" ) { + REQUIRE( ter_test_t_oxytorch1->oxytorch->duration() == 10_seconds ); + THEN( "moves_left is equal to 10 seconds" ) { + CHECK( dummy.activity->moves_left == to_moves( 10_seconds ) ); + } + } + } + + GIVEN( "a tripoint with valid furniture" ) { + clear_map(); + clear_avatar(); + + mp.furn_set( tripoint_zero, furn_t_test_f_oxytorch1 ); + REQUIRE( mp.furn( tripoint_zero ) == furn_t_test_f_oxytorch1 ); + + item &welding_torch = setup_dummy(); + setup_activity( welding_torch ); + REQUIRE( dummy.activity->id() == ACT_OXYTORCH ); + + WHEN( "furniture has a duration of 5 seconds" ) { + REQUIRE( furn_t_test_f_oxytorch1->oxytorch->duration() == 5_seconds ); + THEN( "moves_left is equal to 5 seconds" ) { + CHECK( dummy.activity->moves_left == to_moves( 5_seconds ) ); + } + } + } + } + + SECTION( "oxytorch turn checks" ) { + GIVEN( "player is in mid activity" ) { + clear_map(); + clear_avatar(); + + mp.furn_set( tripoint_zero, furn_t_test_f_oxytorch3 ); + REQUIRE( mp.furn( tripoint_zero ) == furn_t_test_f_oxytorch3 ); + + item &welding_torch = setup_dummy(); + setup_activity( welding_torch ); + REQUIRE( dummy.activity->id() == ACT_OXYTORCH ); + process_activity( dummy ); + + WHEN( "player runs out of fuel" ) { + REQUIRE( dummy.activity->id() == ACT_NULL ); + + THEN( "player recharges with fuel" ) { + welding_torch.ammo_set( itype_oxyacetylene, -1 ); + + AND_THEN( "player can resume the activity" ) { + setup_activity( welding_torch ); + dummy.moves = dummy.get_speed(); + dummy.activity->do_turn( dummy ); + CHECK( dummy.activity->id() == ACT_OXYTORCH ); + CHECK( dummy.activity->moves_left < to_moves + ( furn_t_test_f_oxytorch3->oxytorch->duration() ) ); + } + } + } + } + } + + SECTION( "oxytorch finish checks" ) { + GIVEN( "a tripoint with valid terrain" ) { + clear_map(); + clear_avatar(); + + mp.ter_set( tripoint_zero, ter_test_t_oxytorch1 ); + REQUIRE( mp.ter( tripoint_zero ) == ter_test_t_oxytorch1 ); + + item &welding_torch = setup_dummy(); + setup_activity( welding_torch ); + + REQUIRE( dummy.activity->id() == ACT_OXYTORCH ); + process_activity( dummy ); + REQUIRE( dummy.activity->id() == ACT_NULL ); + + THEN( "terrain gets converted to new terrain type" ) { + CHECK( mp.ter( tripoint_zero ) == t_dirt ); + } + } + + GIVEN( "a tripoint with valid furniture" ) { + clear_map(); + clear_avatar(); + + mp.furn_set( tripoint_zero, furn_t_test_f_oxytorch1 ); + REQUIRE( mp.furn( tripoint_zero ) == furn_t_test_f_oxytorch1 ); + + item &welding_torch = setup_dummy(); + setup_activity( welding_torch ); + + REQUIRE( dummy.activity->id() == ACT_OXYTORCH ); + process_activity( dummy ); + REQUIRE( dummy.activity->id() == ACT_NULL ); + + THEN( "furniture gets converted to new furniture type" ) { + CHECK( mp.furn( tripoint_zero ) == f_null ); + } + } + + GIVEN( "a tripoint with valid furniture" ) { + clear_map(); + clear_avatar(); + + mp.furn_set( tripoint_zero, furn_t_test_f_oxytorch2 ); + REQUIRE( mp.furn( tripoint_zero ) == furn_t_test_f_oxytorch2 ); + + item &welding_torch = setup_dummy(); + setup_activity( welding_torch ); + + REQUIRE( dummy.activity->id() == ACT_OXYTORCH ); + process_activity( dummy ); + REQUIRE( dummy.activity->id() == ACT_NULL ); + + THEN( "furniture gets converted to new furniture type" ) { + CHECK( mp.furn( tripoint_zero ) == furn_t_test_f_oxytorch1 ); + } + } + + + GIVEN( "a tripoint with a valid furniture with byproducts" ) { + clear_map(); + clear_avatar(); + + mp.ter_set( tripoint_zero, ter_test_t_oxytorch2 ); + REQUIRE( mp.ter( tripoint_zero ) == ter_test_t_oxytorch2 ); + + item &welding_torch = setup_dummy(); + setup_activity( welding_torch ); + + REQUIRE( ter_test_t_oxytorch2->oxytorch->byproducts().size() == 2 ); + + REQUIRE( dummy.activity->id() == ACT_OXYTORCH ); + process_activity( dummy ); + REQUIRE( dummy.activity->id() == ACT_NULL ); + + const itype_id test_amount( "test_rock" ); + const itype_id test_random( "test_2x4" ); + + WHEN( "oxytorch acitivy finishes" ) { + CHECK( dummy.activity->id() == ACT_NULL ); + + THEN( "player receives the items" ) { + int count_amount = 0; + int count_random = 0; + for( const auto &it : get_map().i_at( tripoint_zero ) ) { + // can't use switch here + const itype_id it_id = it->typeId(); + if( it_id == test_amount ) { + count_amount += it->charges; + } else if( it_id == test_random ) { + count_random += 1; + } + } + + CHECK( count_amount == 3 ); + CHECK( ( 7 <= count_random && count_random <= 9 ) ); + } + } + } + } +}