diff --git a/code/_onclick/observer.dm b/code/_onclick/observer.dm index b1f8e627375..4ac8c1739ea 100644 --- a/code/_onclick/observer.dm +++ b/code/_onclick/observer.dm @@ -54,7 +54,7 @@ if(SEND_SIGNAL(src, COMSIG_ATOM_ATTACK_GHOST, user) & COMPONENT_CANCEL_ATTACK_CHAIN) return TRUE if(user.client) - if(user.gas_scan && atmos_scan(user=user, target=src, tool=null, silent=TRUE)) + if(user.gas_scan && atmos_scan(user=user, target=src, silent=TRUE)) return TRUE else if(isAdminGhostAI(user)) attack_ai(user) diff --git a/code/game/objects/items/devices/scanners/gas_analyzer.dm b/code/game/objects/items/devices/scanners/gas_analyzer.dm index 2b1a22190f9..4297245040d 100644 --- a/code/game/objects/items/devices/scanners/gas_analyzer.dm +++ b/code/game/objects/items/devices/scanners/gas_analyzer.dm @@ -116,7 +116,7 @@ /obj/item/analyzer/attack_self(mob/user, modifiers) if(user.stat != CONSCIOUS || !user.can_read(src) || user.is_blind()) return - atmos_scan(user=user, target=get_turf(src), tool=src, silent=FALSE) + atmos_scan(user=user, target=get_turf(src), silent=FALSE) on_analyze(source=src, target=get_turf(src)) /obj/item/analyzer/attack_self_secondary(mob/user, modifiers) @@ -127,6 +127,7 @@ /// Called when our analyzer is used on something /obj/item/analyzer/proc/on_analyze(datum/source, atom/target) + SIGNAL_HANDLER var/mixture = target.return_analyzable_air() if(!mixture) return FALSE @@ -145,7 +146,7 @@ * Gets called by analyzer_act, which in turn is called by tool_act. * Also used in other chat-based gas scans. */ -/proc/atmos_scan(mob/user, atom/target, obj/tool, silent=FALSE) +/proc/atmos_scan(mob/user, atom/target, silent=FALSE) var/mixture = target.return_analyzable_air() if(!mixture) return FALSE diff --git a/code/game/objects/objs.dm b/code/game/objects/objs.dm index 288ec91c7c3..4a1d436adc7 100644 --- a/code/game/objects/objs.dm +++ b/code/game/objects/objs.dm @@ -341,7 +341,7 @@ return TRUE /obj/analyzer_act(mob/living/user, obj/item/analyzer/tool) - if(atmos_scan(user=user, target=src, tool=tool, silent=FALSE)) + if(atmos_scan(user=user, target=src, silent=FALSE)) return TRUE return ..() diff --git a/code/modules/admin/verbs/debug.dm b/code/modules/admin/verbs/debug.dm index 7f1da58359a..f5421213f55 100644 --- a/code/modules/admin/verbs/debug.dm +++ b/code/modules/admin/verbs/debug.dm @@ -23,7 +23,7 @@ var/turf/T = get_turf(mob) if(!isturf(T)) return - atmos_scan(user=usr, target=T, tool=null, silent=TRUE) + atmos_scan(user=usr, target=T, silent=TRUE) SSblackbox.record_feedback("tally", "admin_verb", 1, "Air Status In Location") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! /client/proc/cmd_admin_robotize(mob/M in GLOB.mob_list) diff --git a/code/modules/admin/verbs/diagnostics.dm b/code/modules/admin/verbs/diagnostics.dm index a98f40f82b0..69a98ad42f7 100644 --- a/code/modules/admin/verbs/diagnostics.dm +++ b/code/modules/admin/verbs/diagnostics.dm @@ -4,7 +4,7 @@ if(!isturf(target)) return - atmos_scan(user=usr, target=target, tool=null, silent=TRUE) + atmos_scan(user=usr, target=target, silent=TRUE) SSblackbox.record_feedback("tally", "admin_verb", 1, "Show Air Status") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! /client/proc/fix_next_move() diff --git a/code/modules/modular_computers/file_system/program.dm b/code/modules/modular_computers/file_system/program.dm index 23a5216f6df..02dccac4789 100644 --- a/code/modules/modular_computers/file_system/program.dm +++ b/code/modules/modular_computers/file_system/program.dm @@ -77,7 +77,7 @@ return 0 /** - *Runs when the device is used to attack an atom in non-combat mode. + *Runs when the device is used to attack an atom in non-combat mode using right click (secondary). * *Simulates using the device to read or scan something. Tap is called by the computer during pre_attack *and sends us all of the related info. If we return TRUE, the computer will stop the attack process diff --git a/code/modules/modular_computers/file_system/programs/atmosscan.dm b/code/modules/modular_computers/file_system/programs/atmosscan.dm index 9fec1e41032..c4867955ee1 100644 --- a/code/modules/modular_computers/file_system/programs/atmosscan.dm +++ b/code/modules/modular_computers/file_system/programs/atmosscan.dm @@ -1,3 +1,8 @@ +/// Scan the turf where the computer is on. +#define ATMOZPHERE_SCAN_ENV "env" +/// Scan the objects that the tablet clicks. +#define ATMOZPHERE_SCAN_CLICK "click" + /datum/computer_file/program/atmosscan filename = "atmosscan" filedesc = "AtmoZphere" @@ -8,18 +13,75 @@ tgui_id = "NtosGasAnalyzer" program_icon = "thermometer-half" + /// Whether we scan the current turf automatically (env) or scan tapped objects manually (click). + var/atmozphere_mode = ATMOZPHERE_SCAN_ENV + /// Saved [GasmixParser][/proc/gas_mixture_parser] data of the last thing we scanned. + var/list/last_gasmix_data + +/// Secondary attack self. +/datum/computer_file/program/atmosscan/proc/turf_analyze(datum/source, mob/user) + SIGNAL_HANDLER + if(atmozphere_mode != ATMOZPHERE_SCAN_CLICK) + return + atmos_scan(user=user, target=get_turf(computer), silent=FALSE) + on_analyze(source=source, target=get_turf(computer)) + return COMPONENT_CANCEL_ATTACK_CHAIN + +/// Keep this in sync with it's tool based counterpart [/obj/proc/analyzer_act] and [/atom/proc/tool_act] +/datum/computer_file/program/atmosscan/tap(atom/A, mob/living/user, params) + if(atmozphere_mode != ATMOZPHERE_SCAN_CLICK) + return FALSE + if(!atmos_scan(user=user, target=A, silent=FALSE)) + return FALSE + on_analyze(source=computer, target=A) + return TRUE + +/// Updates our gasmix data if on click mode. +/datum/computer_file/program/atmosscan/proc/on_analyze(datum/source, atom/target) + var/mixture = target.return_analyzable_air() + if(!mixture) + return FALSE + var/list/airs = islist(mixture) ? mixture : list(mixture) + var/list/new_gasmix_data = list() + for(var/datum/gas_mixture/air as anything in airs) + var/mix_name = capitalize(lowertext(target.name)) + if(airs.len != 1) //not a unary gas mixture + mix_name += " - Node [airs.Find(air)]" + new_gasmix_data += list(gas_mixture_parser(air, mix_name)) + last_gasmix_data = new_gasmix_data + /datum/computer_file/program/atmosscan/ui_static_data(mob/user) return return_atmos_handbooks() /datum/computer_file/program/atmosscan/ui_data(mob/user) var/list/data = get_header_data() var/turf/turf = get_turf(computer) - var/datum/gas_mixture/air = turf?.return_air() - - data["gasmixes"] = list(gas_mixture_parser(air, "Sensor Reading")) //Null air wont cause errors, don't worry. + data["atmozphereMode"] = atmozphere_mode + data["clickAtmozphereCompatible"] = computer.hardware_flag == PROGRAM_TABLET + switch (atmozphere_mode) //Null air wont cause errors, don't worry. + if(ATMOZPHERE_SCAN_ENV) + var/datum/gas_mixture/air = turf?.return_air() + data["gasmixes"] = list(gas_mixture_parser(air, "Location Reading")) + if(ATMOZPHERE_SCAN_CLICK) + LAZYINITLIST(last_gasmix_data) + data["gasmixes"] = last_gasmix_data return data /datum/computer_file/program/atmosscan/ui_act(action, list/params) . = ..() if(.) return + switch(action) + if("scantoggle") + if(atmozphere_mode == ATMOZPHERE_SCAN_CLICK) + atmozphere_mode = ATMOZPHERE_SCAN_ENV + UnregisterSignal(computer, COMSIG_ITEM_ATTACK_SELF_SECONDARY) + return TRUE + if(computer.hardware_flag != PROGRAM_TABLET) + computer.say("Device incompatible for scanning objects!") + return FALSE + atmozphere_mode = ATMOZPHERE_SCAN_CLICK + RegisterSignal(computer, COMSIG_ITEM_ATTACK_SELF_SECONDARY, .proc/turf_analyze) + var/turf/turf = get_turf(computer) + last_gasmix_data = list(gas_mixture_parser(turf?.return_air(), "Location Reading")) + return TRUE diff --git a/tgui/packages/tgui/interfaces/GasAnalyzer.tsx b/tgui/packages/tgui/interfaces/GasAnalyzer.tsx index 96695edc2c5..9d9152a932e 100644 --- a/tgui/packages/tgui/interfaces/GasAnalyzer.tsx +++ b/tgui/packages/tgui/interfaces/GasAnalyzer.tsx @@ -8,8 +8,12 @@ import { import { Window } from '../layouts'; import { Section } from '../components'; +export type GasAnalyzerData = { + gasmixes: Gasmix[] +} + export const GasAnalyzerContent = (props, context) => { - const { act, data } = useBackend<{ gasmixes: Gasmix[] }>(context); + const { act, data } = useBackend(context); const { gasmixes } = data; const [setActiveGasId, setActiveReactionId] = atmosHandbookHooks(context); return ( diff --git a/tgui/packages/tgui/interfaces/NtosGasAnalyzer.tsx b/tgui/packages/tgui/interfaces/NtosGasAnalyzer.tsx index 2ac0a1fd7db..e70e544fae9 100644 --- a/tgui/packages/tgui/interfaces/NtosGasAnalyzer.tsx +++ b/tgui/packages/tgui/interfaces/NtosGasAnalyzer.tsx @@ -1,10 +1,37 @@ +import { BooleanLike } from 'common/react'; +import { useBackend } from '../backend'; +import { Button } from '../components'; import { NtosWindow } from '../layouts'; -import { GasAnalyzerContent } from './GasAnalyzer'; +import { GasAnalyzerContent, GasAnalyzerData } from './GasAnalyzer'; + +type NtosGasAnalyzerData = GasAnalyzerData & { + atmozphereMode: 'click' | 'env'; + clickAtmozphereCompatible: BooleanLike; +}; export const NtosGasAnalyzer = (props, context) => { + const { act, data } = useBackend(context); + const { atmozphereMode, clickAtmozphereCompatible } = data; return ( + {!!clickAtmozphereCompatible && ( + + )}