diff --git a/data/mods/Aftershock/EOC/_reusable_conditions.json b/data/mods/Aftershock/EOC/_reusable_conditions.json deleted file mode 100644 index 2b7689a3f77c0..0000000000000 --- a/data/mods/Aftershock/EOC/_reusable_conditions.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "type": "effect_on_condition", - "id": "EOC_CONDITION_can_hack", - "condition": { - "and": [ - { - "or": [ - { "u_has_item": "electrohack" }, - { "u_has_item": "afs_wrist_computer" }, - { "u_has_item": "afs_bio_deck_jack" }, - { "u_has_item": "electrokinetic_electrohack_1" }, - { "u_has_item": "electrokinetic_electrohack_2" }, - { "u_has_item": "electrokinetic_electrohack_3" }, - { "u_has_item": "electrokinetic_electrohack_4" }, - { "u_has_item": "electrokinetic_electrohack_5" }, - { "u_has_item": "electrokinetic_electrohack_6" } - ] - }, - "u_can_see" - ] - }, - "effect": [ - - ] -} diff --git a/data/mods/Aftershock/EOC/hacking_eoc.json b/data/mods/Aftershock/EOC/hacking_eoc.json index a4b38460f1608..0187bf3478336 100644 --- a/data/mods/Aftershock/EOC/hacking_eoc.json +++ b/data/mods/Aftershock/EOC/hacking_eoc.json @@ -25,39 +25,51 @@ "num_args": 3, "return": "_0 - _1 * afs_hack_skill(1) < _2 ? _2 : _0 - _1* afs_hack_skill(1)" }, + { + "type": "jmath_function", + "id": "afs_hack_bonus", + "//": "Intended as: Tool Quality + ( Base Focus Mod * (Focus - Focus Threshold) / Focus Threshold ):rounded to nearest int", + "//1": "Below 100 Focus your focus mod will be negative and you are punished. At 100 focus your mod will be 0. Above 100 focus your mod will be positive and you will be rewarded.", + "//2": "ARGS: (Hack Tool Quality, Current Focus)", + "num_args": 2, + "return": "_0 + trunc( 4 * ( ( _1 - 100 ) / 100 ) )" + }, { "type": "effect_on_condition", - "id": "EOC_afs_hack_unlock", - "condition": { "and": [ { "expects_vars": [ "furn_pos", "difficulty", "t_radius" ] }, { "u_has_effect": "afs_hack_done" } ] }, + "id": "EOC_start_lock_hack", + "condition": { "and": [ { "expects_vars": [ "furn_pos", "t_delay", "difficulty", "t_radius" ] }, "u_can_see" ] }, "effect": [ + { "math": [ "_t_delay = afs_hack_time_adjust(_t_delay, time('20s'), time('2m'))" ] }, + { "math": [ "_hack_cost = (5 * (_t_delay / time('10m') )) * max(1, _power_cost_mult)" ] }, + { "math": [ "_hack_cost_bionic = (energy('2 kJ') * (_t_delay / time('10m') )) * max(1, _power_cost_mult)" ] }, { - "run_eocs": [ + "u_run_inv_eocs": "manual", + "title": "Select a Hacking tool", + "search_data": [ + { "condition": { "math": [ "n_quality('HACK') > 0" ] }, "flags": "HACK_NO_CHARGE" }, { - "id": "_EOC_afs_hack_unlock", - "condition": { "roll_contested": { "math": [ "afs_hack_skill(1)" ] }, "die_size": 4, "difficulty": { "context_val": "difficulty" } }, - "effect": [ - { - "u_transform_radius": { "context_val": "t_radius" }, - "ter_furn_transform": "afs_multi_unlock", - "target_var": { "context_val": "furn_pos" } - } - ], - "false_effect": { "u_message": "You fail to override the device." } + "condition": { "and": [ { "math": [ "n_quality('HACK') > 0" ] }, { "math": [ "_hack_cost_bionic < u_val('power')" ] } ] }, + "flags": "USES_BIONIC_POWER" + }, + { + "condition": { "and": [ { "math": [ "n_quality('HACK') > 0" ] }, { "math": [ "_hack_cost < n_val('power')" ] } ] }, + "excluded_flags": "USES_BIONIC_POWER", + "uses_energy": true } - ] + ], + "true_eocs": [ "EOC_hack_with_tool" ] } ] }, { "type": "effect_on_condition", - "id": "EOC_start_lock_hack", - "condition": { - "and": [ { "expects_vars": [ "furn_pos", "t_delay", "difficulty", "t_radius" ] }, { "test_eoc": "EOC_CONDITION_can_hack" } ] - }, + "id": "EOC_hack_with_tool", "effect": [ - { "math": [ "_t_delay = afs_hack_time_adjust(_t_delay, time('20s'), time('2m'))" ] }, + { "npc_set_flag": "ACTIVE_HACK_TOOL" }, { "u_message": "You connect to the lock controller." }, { "u_assign_activity": "ACT_afs_hack", "duration": { "context_val": "t_delay" } }, + { "math": [ "u_hack_bonus = afs_hack_bonus( n_quality('HACK'), u_val('focus') )" ] }, + { "run_eocs": [ "EOC_hack_item_power_consume", "EOC_hack_bionic_power_consume" ] }, { "run_eocs": "EOC_afs_hack_unlock", "time_in_future": { "context_val": "t_delay" }, @@ -67,7 +79,132 @@ "t_radius": { "context_val": "t_radius" } } } - ], - "false_effect": { "u_message": "You need either an electrohack or working computer to attempt a hack.", "popup": true } + ] + }, + { + "type": "effect_on_condition", + "id": "EOC_hack_item_power_consume", + "effect": [ + { + "u_run_inv_eocs": "all", + "search_data": [ { "flags": "ACTIVE_HACK_TOOL", "excluded_flags": "USES_BIONIC_POWER", "uses_energy": true } ], + "true_eocs": [ + { + "id": "EOC_hack_item_power_consume_found_item", + "effect": [ { "math": [ "n_val('power') -= _hack_cost" ] }, { "npc_unset_flag": "ACTIVE_HACK_TOOL" } ] + } + ] + } + ] + }, + { + "type": "effect_on_condition", + "id": "EOC_hack_bionic_power_consume", + "effect": [ + { + "u_run_inv_eocs": "all", + "search_data": [ { "flags": [ "ACTIVE_HACK_TOOL", "USES_BIONIC_POWER" ] } ], + "true_eocs": [ + { + "id": "EOC_hack_bionic_power_consume_found_item", + "effect": [ { "math": [ "u_val('power') -= _hack_cost_bionic" ] }, { "npc_unset_flag": "ACTIVE_HACK_TOOL" } ] + } + ] + } + ] + }, + { + "type": "effect_on_condition", + "id": "EOC_afs_hack_unlock", + "condition": { "and": [ { "expects_vars": [ "furn_pos", "difficulty", "t_radius" ] }, { "u_has_effect": "afs_hack_done" } ] }, + "effect": [ + { + "run_eocs": [ + { + "id": "EOC_afs_hack_success", + "condition": { "roll_contested": { "math": [ "afs_hack_skill(1)" ] }, "die_size": 4, "difficulty": { "context_val": "difficulty" } }, + "effect": [ + { + "if": { "compare_string": [ { "u_val": "hack_success_eoc" }, "" ] }, + "then": [ { "u_add_var": "hack_success_eoc", "value": "EOC_afs_hack_generic_success" } ] + }, + { "run_eocs": [ { "u_val": "hack_success_eoc" } ] }, + { "u_add_var": "hack_minor_failure_eoc", "value": "" }, + { "u_add_var": "hack_critical_fail_eoc", "value": "" }, + { "u_add_var": "hack_success_eoc", "value": "" } + ], + "false_effect": [ { "run_eocs": [ "EOC_afs_hack_failure" ] } ] + } + ] + } + ] + }, + { + "type": "effect_on_condition", + "id": "EOC_afs_hack_generic_success", + "effect": [ + { + "u_transform_radius": { "context_val": "t_radius" }, + "ter_furn_transform": "afs_multi_unlock", + "target_var": { "context_val": "furn_pos" } + } + ] + }, + { + "type": "effect_on_condition", + "id": "EOC_afs_hack_failure", + "effect": [ + { "u_message": "You fail to override the device.", "type": "bad" }, + { "math": [ "_failure_roll = rand(9)" ] }, + { + "if": { "compare_string": [ { "u_val": "hack_minor_failure_eoc" }, "" ] }, + "then": [ { "u_add_var": "hack_minor_failure_eoc", "value": "EOC_afs_generic_hack_minor_failures" } ] + }, + { + "if": { "compare_string": [ { "u_val": "hack_critical_fail_eoc" }, "" ] }, + "then": [ { "u_add_var": "hack_critical_fail_eoc", "value": "EOC_afs_generic_hack_critical_fail" } ] + }, + { + "if": { "math": [ "_failure_roll <= clamp(_difficulty - afs_hack_skill(1), 1, 10)" ] }, + "then": [ { "run_eocs": [ { "u_val": "hack_critical_fail_eoc" } ] }, { "math": [ "_failure_roll = 10" ] } ] + }, + { + "if": { "math": [ "_failure_roll <= clamp(_difficulty - afs_hack_skill(1) + 3, 1, 10)" ] }, + "then": [ { "run_eocs": [ { "u_val": "hack_minor_failure_eoc" } ] } ] + }, + { "u_add_var": "hack_minor_failure_eoc", "value": "" }, + { "u_add_var": "hack_critical_fail_eoc", "value": "" }, + { "u_add_var": "hack_success_eoc", "value": "" } + ] + }, + { + "type": "effect_on_condition", + "id": "EOC_afs_generic_hack_minor_failures", + "effect": [ + { + "set_string_var": [ "EOC_afs_generic_hack_minor_failure_alarm", "EOC_afs_generic_hack_minor_failure_hurt" ], + "target_var": { "context_val": "failure_eoc" } + }, + { "run_eocs": [ { "context_val": "failure_eoc" } ] } + ] + }, + { + "type": "effect_on_condition", + "id": "EOC_afs_generic_hack_critical_fail", + "effect": [ { "u_transform_radius": 0, "ter_furn_transform": "afs_multi_lockdown", "target_var": { "context_val": "furn_pos" } } ] + }, + { + "type": "effect_on_condition", + "id": "EOC_afs_generic_hack_minor_failure_alarm", + "effect": [ + { "u_message": "You set off the devices alarm!", "type": "bad" }, + { "custom_light_level": 80, "length": [ "1 seconds", "10 seconds" ] }, + { "u_make_sound": "Alarm Bleeping", "volume": 15, "type": "alarm" } + ] + }, + { + "type": "effect_on_condition", + "id": "EOC_afs_generic_hack_minor_failure_hurt", + "effect": [ { "u_message": "The device shocks you!", "type": "bad" }, { "u_deal_damage": "electric", "amount": 10 } ] } ] diff --git a/data/mods/Aftershock/doc/Hacking.md b/data/mods/Aftershock/doc/Hacking.md new file mode 100644 index 0000000000000..2f75a3ba73bb8 --- /dev/null +++ b/data/mods/Aftershock/doc/Hacking.md @@ -0,0 +1,68 @@ +# Hacking +### Hackable Furniture +To define a Hackable Furniture in Aftershock you need to give it an examine action that triggers an EOC. This EOC must call ```EOC_start_lock_hack``` and must set up several essential variables. Refer to the following chart below for what variables are available to you and which ones are required to be defined. When running the EOC be sure to set the Alpha talker to the Avatar so the EOCs can run correctly. + + +| Variable Name | Description | Context | Required | +|-|-|-|-| +|furn_pos |The location of the furniture. Supply pos variable from the examine action.
```"furn_pos": { "context_val": "pos" }``` | EOC Context | ✓ | +|t_delay |The base amount of time it should take to hack this furniture. It is best defined before calling the EOC.
```{ "math": [ "t_delay = time('20min')" ] }``` | EOC Context | ✓ | +|difficulty |The difficulty of hacking this furniture. To hack this furniture the player must beat this number by rolling a d4 and adding their hack skill | EOC Context | ✓ | +|t_radius |The radius around this furniture in which the ter-fur transform is applied. | EOC Context | ✓ | +|power_cost_mult | Multiplier applied to the total power cost of the hack if a tool or bionic is used. This can be used to increase the total power requirement of a hack without making it take longer to complete.| EOC Context | +|hack_minor_failure_eoc |The ID of the EOC to run when the player minorly fails at hacking this furniture. Leaving this blank will trigger the generic hack minor failure EOC to run. | User | +|hack_critical_failure |The ID of the EOC to run when the player critically fails at hacking this furniture. Leaving this blank will trigger the generic critical failure EOC to run. However, you will still need to add this furniture to the multi-lockdown ter-fur transform.| User +|hack_sucess_eoc | The ID of the EOC to run when the player succeeds at hacking the furniture. If this is left blank the generic success EOC will run. However, you will still need to add the furniture to the multi-unlock ter-fur transform.| User + +``` + "examine_action": { + "type": "effect_on_condition", + "effect_on_conditions": [ + { + "id": "EOC_unlock_afs_display_case", + "effect": [ + { "math": [ "_t_delay = time('20 m')" ] }, + { + "run_eocs": "EOC_start_lock_hack", + "variables": { "furn_pos": { "context_val": "pos" }, "t_delay": { "context_val": "t_delay" }, "difficulty": "10", "t_radius": "0" }, + "alpha_talker": "avatar" + } + ] + } + ] + }, +``` + +``` + "examine_action": { + "type": "effect_on_condition", + "effect_on_conditions": [ + { + "id": "EOC_unlock_afs_security_panel", + "effect": [ + { "math": [ "_t_delay = time('20 m')" ] }, + { "u_add_var": "hack_minor_failure_eoc", value: "EOC_Hack_Custom_Minor_Failure" }, + { "u_add_var": "hack_critical_fail_eoc", value: "EOC_Hack_Custom_Critical_Failure" }, + { "u_add_var": "hack_success_eoc", value: "EOC_Hack_Custom_Success" }, + { + "run_eocs": "EOC_start_lock_hack", + "variables": { "furn_pos": { "context_val": "pos" }, "t_delay": { "context_val": "t_delay" }, "difficulty": "10", "t_radius": "6", "power_cost_mult": 2 }, + "alpha_talker": "avatar" + } + ] + } + ] + }, +``` + +### Hacking tools +Hacking tools require relatively little setup to work within our system. Simply make sure your item has the hack tool quality. Electronic tools require no adjustment and work right out of the gate. Bionic hacking tools also work straight away provided they are flagged with ```USE_BIONIC_POWER```. To make an item that doesn't consume charges at all simply flag it with ```HACK_NO_CHARGE```. + +| Quality Level | Description | +|-|-| +| 1 | Electronics that could technically be used for hacking but offer virtually no benefits for it whatsoever. | +| 2 | Improvised Hacking tools that provide some benefit for hacking but fall short when compared to well-manufactured equipment. +| 3 | Small Corporate Infiltrator hacking gear. Providing a suite of hacking utilities. | +| 4 | Military Grade Hacking Gear. The stuff that UICA and the big Corpos use. | +| 5 | Elite Infiltrator Gear. The top-class equipment for that is still technically manufacturable. | +| 6 | Glittertech Hacking Equipment. Tools of this level aren't manufacturable anymore and can only be found in pre-discontinuity ruins. \ No newline at end of file diff --git a/data/mods/Aftershock/flags.json b/data/mods/Aftershock/flags.json index 769fa4a226dc6..ed93647bd85ab 100644 --- a/data/mods/Aftershock/flags.json +++ b/data/mods/Aftershock/flags.json @@ -42,5 +42,15 @@ "id": "HIVE_MIND", "type": "monster_flag", "//": "This monster is part of a hive mind" + }, + { + "id": "ACTIVE_HACK_TOOL", + "type": "json_flag", + "//": "This tool is being used to perform hacking operations." + }, + { + "id": "HACK_NO_CHARGE", + "type": "json_flag", + "//": "This Hacking tool does not consume energy." } ] diff --git a/data/mods/Aftershock/items/armor.json b/data/mods/Aftershock/items/armor.json index 10676499562f7..bb7498f363c35 100644 --- a/data/mods/Aftershock/items/armor.json +++ b/data/mods/Aftershock/items/armor.json @@ -126,7 +126,8 @@ "weight_multiplier": 0.0, "ememory_max": "1 TB" } - ] + ], + "qualities": [ [ "HACK", 1 ] ] }, { "id": "xl_jeans", diff --git a/data/mods/Aftershock/items/esper_summons.json b/data/mods/Aftershock/items/esper_summons.json index b26c7244ac795..d0bc6ab45dccb 100644 --- a/data/mods/Aftershock/items/esper_summons.json +++ b/data/mods/Aftershock/items/esper_summons.json @@ -17,6 +17,7 @@ "PERSONAL", "UNBREAKABLE", "SEMITANGIBLE", + "HACK_NO_CHARGE", "NO_SALVAGE", "ZERO_WEIGHT", "NO_UNLOAD", @@ -24,7 +25,7 @@ "INTEGRATED", "MAGICAL" ], - "qualities": [ [ "HACK", 0 ] ] + "qualities": [ [ "HACK", 1 ] ] }, { "id": "electrokinetic_electrohack_2", @@ -32,7 +33,7 @@ "name": { "str": "[Ψ]hacking interface" }, "description": "You can hack nearby devices.", "copy-from": "electrokinetic_electrohack_1", - "qualities": [ [ "HACK", 1 ] ] + "qualities": [ [ "HACK", 2 ] ] }, { "id": "electrokinetic_electrohack_3", @@ -40,7 +41,7 @@ "name": { "str": "[Ψ]hacking interface" }, "description": "You can hack nearby devices.", "copy-from": "electrokinetic_electrohack_1", - "qualities": [ [ "HACK", 2 ] ] + "qualities": [ [ "HACK", 3 ] ] }, { "id": "electrokinetic_electrohack_4", @@ -48,7 +49,7 @@ "name": { "str": "[Ψ]hacking interface" }, "description": "You can hack nearby devices.", "copy-from": "electrokinetic_electrohack_1", - "qualities": [ [ "HACK", 3 ] ] + "qualities": [ [ "HACK", 4 ] ] }, { "id": "electrokinetic_electrohack_5", @@ -56,6 +57,6 @@ "name": { "str": "[Ψ]hacking interface" }, "description": "You can hack nearby devices.", "copy-from": "electrokinetic_electrohack_1", - "qualities": [ [ "HACK", 4 ] ] + "qualities": [ [ "HACK", 5 ] ] } ] diff --git a/data/mods/Aftershock/items/tools.json b/data/mods/Aftershock/items/tools.json index e35fa3c8a670a..a5387cf03df8f 100644 --- a/data/mods/Aftershock/items/tools.json +++ b/data/mods/Aftershock/items/tools.json @@ -700,7 +700,7 @@ "USES_BIONIC_POWER" ], "charges_per_use": 1, - "qualities": [ [ "HACK", 2 ] ] + "qualities": [ [ "HACK", 4 ] ] }, { "id": "afs_internal_battery_compartment", diff --git a/data/mods/Aftershock/maps/furniture_and_terrain/furniture_habitat.json b/data/mods/Aftershock/maps/furniture_and_terrain/furniture_habitat.json index 05d30c2f6abe8..4e74ac162b2cc 100644 --- a/data/mods/Aftershock/maps/furniture_and_terrain/furniture_habitat.json +++ b/data/mods/Aftershock/maps/furniture_and_terrain/furniture_habitat.json @@ -399,6 +399,31 @@ ] } }, + { + "type": "furniture", + "id": "f_sealed_intermodal_crate_locked", + "name": "powered down intermodal crate", + "description": "A sealed metal crate used to ferry cargo to and from orbit. This one appears to be secured with an electric lock that has shut down.", + "looks_like": "f_sealed_intermodal_crate", + "required_str": 12, + "symbol": "O", + "bgcolor": "brown", + "move_cost_mod": -1, + "coverage": 60, + "oxytorch": { "result": "f_intermodal_crate_open", "duration": "14 seconds" }, + "flags": [ "TRANSPARENT", "MOUNTABLE", "SHORT", "SMALL_HIDE", "PLACE_ITEM", "CONTAINER", "SEALED" ], + "bash": { + "str_min": 40, + "str_max": 200, + "sound": "screeching metal!", + "sound_fail": "whump!", + "items": [ + { "item": "steel_chunk", "count": [ 1, 5 ] }, + { "item": "scrap", "count": [ 1, 5 ] }, + { "item": "afs_circuitry_1", "count": [ 1, 2 ] } + ] + } + }, { "type": "furniture", "id": "f_reinforced_displaycase", @@ -440,6 +465,31 @@ }, "shoot": { "reduce_damage": [ 2, 12 ], "reduce_damage_laser": [ 0, 7 ], "destroy_damage": [ 40, 120 ], "no_laser_destroy": true } }, + { + "type": "furniture", + "id": "f_reinforced_displaycase_locked", + "name": "powered down reinforced display case", + "looks_like": "f_rack", + "description": "A secure metal case at about waist-height, with reinforced glass paneling on top. Useful for storing valuable things while still showing them off. Secured with an electronic lock.", + "symbol": "#", + "color": "light_cyan", + "move_cost_mod": 2, + "coverage": 80, + "required_str": 10, + "flags": [ "TRANSPARENT", "SEALED", "PLACE_ITEM" ], + "rotates_to": "INDOORFLOOR", + "bash": { + "str_min": 80, + "str_max": 180, + "sound": "glass breaking!", + "sound_fail": "whack!", + "sound_vol": 16, + "sound_fail_vol": 12, + "furn_set": "f_displaycase_b", + "items": [ { "item": "glass_shard", "count": [ 25, 50 ] } ] + }, + "shoot": { "reduce_damage": [ 2, 12 ], "reduce_damage_laser": [ 0, 7 ], "destroy_damage": [ 40, 120 ], "no_laser_destroy": true } + }, { "type": "furniture", "id": "f_reinforced_displaycase_o", @@ -576,6 +626,29 @@ ] } }, + { + "type": "furniture", + "id": "f_security_panel_locked", + "name": "burnt out security override panel", + "symbol": "e", + "description": "A slim wall mounted metal panel covering an electronic interface to manually override nearby security systems.", + "color": "blue", + "move_cost_mod": 1, + "required_str": -1, + "flags": [ "TRANSPARENT" ], + "bash": { + "str_min": 6, + "str_max": 30, + "sound": "smash!", + "sound_fail": "whump.", + "items": [ + { "item": "cable", "charges": [ 1, 2 ] }, + { "item": "plastic_chunk", "count": [ 1, 2 ] }, + { "item": "scrap", "count": [ 1, 2 ] }, + { "item": "steel_chunk", "count": [ 0, 1 ] } + ] + } + }, { "type": "furniture", "id": "f_e_safe_c", @@ -645,6 +718,32 @@ ] } }, + { + "type": "furniture", + "id": "f_e_safe_l_down", + "name": "powered down safe", + "looks_like": "f_e_safe_c", + "symbol": "X", + "description": "A small, heavy, and near-unbreachable metal box with an electronic lock. With a hacking tool and the right skills you might be able to crack it.", + "color": "light_gray", + "move_cost_mod": -1, + "coverage": 30, + "required_str": 14, + "max_volume": "250 L", + "flags": [ "TRANSPARENT", "CONTAINER", "SEALED", "PLACE_ITEM", "MOUNTABLE" ], + "oxytorch": { "result": "f_e_safe_o", "duration": "14 seconds" }, + "bash": { + "str_min": 40, + "str_max": 200, + "sound": "screeching metal!", + "sound_fail": "whump!", + "items": [ + { "item": "steel_chunk", "count": [ 1, 5 ] }, + { "item": "scrap", "count": [ 1, 5 ] }, + { "item": "rock", "count": [ 1, 2 ] } + ] + } + }, { "type": "furniture", "id": "f_e_safe_o", diff --git a/data/mods/Aftershock/ter_fur_transform.json b/data/mods/Aftershock/ter_fur_transform.json index 7b97c81cad677..b6ab131b1a7ad 100644 --- a/data/mods/Aftershock/ter_fur_transform.json +++ b/data/mods/Aftershock/ter_fur_transform.json @@ -38,6 +38,40 @@ } ] }, + { + "type": "ter_furn_transform", + "//": "Used for generic critical failures on furniture that doesn't define custom behavior.", + "id": "afs_multi_lockdown", + "furniture": [ + { + "result": "f_sealed_intermodal_crate_locked", + "valid_furniture": [ "f_sealed_intermodal_crate" ], + "message": "The shipping crate beeps unhappily before powering down." + }, + { + "result": "f_e_safe_l_down", + "valid_furniture": [ "f_e_safe_l" ], + "message": "The safe beeps unhappily before powering down." + }, + { + "result": "f_security_panel_locked", + "valid_furniture": [ "f_security_panel" ], + "message": "You trip the security failsafe and the panel fries it's own electronics." + }, + { + "result": "f_reinforced_displaycase_locked", + "valid_furniture": [ "f_reinforced_displaycase" ], + "message": "The display case beeps unhappily before powering down." + } + ], + "terrain": [ + { + "result": "t_door_metal_locked", + "valid_terrain": [ "t_afs_door_metal_elocked" ], + "message": "The door's PIN pad chimes unhappily before powering down." + } + ] + }, { "type": "ter_furn_transform", "id": "transform_gratings",