From 203c2609a7c7134763b74a5793f16746b80b5f89 Mon Sep 17 00:00:00 2001 From: CHOMPStation2StaffMirrorBot <94713762+CHOMPStation2StaffMirrorBot@users.noreply.github.com> Date: Mon, 16 Jun 2025 15:08:30 -0700 Subject: [PATCH] [MIRROR] Completely rewrite xenoarch spectrometer (#11079) Co-authored-by: ShadowLarkens --- .../tools/geosample_scanner.dm | 497 ++++++++---------- .../aerostat/aerostat_science_outpost.dmm | 5 +- maps/tether/tether-07-solars.dmm | 5 +- .../tgui/interfaces/XenoarchSpectrometer.tsx | 420 ++++++++------- 4 files changed, 461 insertions(+), 466 deletions(-) diff --git a/code/modules/xenoarcheaology/tools/geosample_scanner.dm b/code/modules/xenoarcheaology/tools/geosample_scanner.dm index a054d195f2..168618228a 100644 --- a/code/modules/xenoarcheaology/tools/geosample_scanner.dm +++ b/code/modules/xenoarcheaology/tools/geosample_scanner.dm @@ -1,3 +1,21 @@ +#define RPM_FRICTION 15 +#define IDEAL_RPM 600 +#define RPM_OKAY_RANGE 200 +#define RPM_MAX_DELTA RPM_FRICTION * 4 +#define RPM_MIN 0 +#define RPM_MAX 1200 +#define COMPLETION_DELTA_MODIFIER 4 // Make it go faster, 25 ticks at peak efficiency +#define RADIATION_INJECTION_AMT 5 +#define RADIATION_MAX 50 +#define TARGET_RADIATION 20 +#define RADIATION_OK_RANGE 7.5 +#define RADIATION_LOSS 1 +#define HEAT_GAIN 0.5 +#define HEAT_FAILURE_THRESHOLD 10 +#define HEAT_MAX 12 +#define COOLANT_USAGE 2 +#define COOLANT_MAX 100 + /obj/machinery/radiocarbon_spectrometer name = "radiocarbon spectrometer" desc = "A specialised, complex scanner for gleaning information on all manner of small things." @@ -10,114 +28,61 @@ idle_power_usage = 20 active_power_usage = 300 - //var/obj/item/reagent_containers/glass/coolant_container + flags = OPENCONTAINER + var/scanning = 0 var/report_num = 0 - // var/obj/item/scanned_item var/last_scan_data = "No scans on record." - // - var/last_process_worldtime = 0 - // - var/scanner_progress = 0 - var/scanner_rate = 1.25 //80 seconds per scan + var/scan_progress = 0 + + // Mechanics:tm: var/scanner_rpm = 0 - var/scanner_rpm_dir = 1 - var/scanner_temperature = 0 - var/scanner_seal_integrity = 100 - // - var/coolant_usage_rate = 0 //measured in u/microsec - var/fresh_coolant = 0 - var/coolant_purity = 0 - var/datum/reagents/coolant_reagents - var/used_coolant = 0 - var/list/coolant_reagents_purity = list() - // - var/maser_wavelength = 0 - var/optimal_wavelength = 0 - var/optimal_wavelength_target = 0 - var/tleft_retarget_optimal_wavelength = 0 - var/maser_efficiency = 0 - // - var/radiation = 0 //0-100 mSv - var/t_left_radspike = 0 - var/rad_shield = 0 + var/scanner_rpm_delta = 0 + + var/radiation = 0 + + var/heat = 0 /obj/machinery/radiocarbon_spectrometer/Initialize(mapload) . = ..() - create_reagents(500) - coolant_reagents_purity[REAGENT_ID_WATER] = 0.5 - coolant_reagents_purity[REAGENT_ID_ICECOFFEE] = 0.6 - coolant_reagents_purity[REAGENT_ID_ICETEA] = 0.6 - coolant_reagents_purity[REAGENT_ID_MILKSHAKE] = 0.6 - coolant_reagents_purity[REAGENT_ID_LEPORAZINE] = 0.7 - coolant_reagents_purity[REAGENT_ID_KELOTANE] = 0.7 - coolant_reagents_purity[REAGENT_ID_STERILIZINE] = 0.7 - coolant_reagents_purity[REAGENT_ID_DERMALINE] = 0.7 - coolant_reagents_purity[REAGENT_ID_HYPERZINE] = 0.8 - coolant_reagents_purity[REAGENT_ID_CRYOXADONE] = 0.9 - coolant_reagents_purity[REAGENT_ID_COOLANT] = 1 - coolant_reagents_purity[REAGENT_ID_ADMINORDRAZINE] = 2 + create_reagents(COOLANT_MAX) -/obj/machinery/radiocarbon_spectrometer/attackby(var/obj/I as obj, var/mob/user as mob) +/obj/machinery/radiocarbon_spectrometer/attackby(obj/I, mob/user) if(scanning) to_chat(user, span_warning("You can't do that while [src] is scanning!")) - else - if(istype(I, /obj/item/stack/nanopaste)) - var/choice = tgui_alert(user, "What do you want to do with the nanopaste?","Radiometric Scanner",list("Scan nanopaste","Fix seal integrity")) - if(!choice) - return - if(choice == "Fix seal integrity") - var/obj/item/stack/nanopaste/N = I - var/amount_used = min(N.get_amount(), 10 - scanner_seal_integrity / 10) - N.use(amount_used) - scanner_seal_integrity = round(scanner_seal_integrity + amount_used * 10) - return - if(istype(I, /obj/item/reagent_containers/glass)) - var/obj/item/reagent_containers/glass/G = I - if(!G.is_open_container()) - return - var/choice = tgui_alert(user, "What do you want to do with the container?","Radiometric Scanner",list("Add coolant","Empty coolant","Scan container")) - if(!choice) - return - if(choice == "Add coolant") - var/amount_transferred = min(src.reagents.maximum_volume - src.reagents.total_volume, G.reagents.total_volume) - var/trans = G.reagents.trans_to_obj(src, amount_transferred) - to_chat(user, span_info("You empty [trans ? trans : 0]u of coolant into [src].")) - update_coolant() - return - else if(choice == "Empty coolant") - var/amount_transferred = min(G.reagents.maximum_volume - G.reagents.total_volume, src.reagents.total_volume) - var/trans = src.reagents.trans_to(G, amount_transferred) - to_chat(user, span_info("You remove [trans ? trans : 0]u of coolant from [src].")) - update_coolant() - return - if(scanned_item) - to_chat(user, span_warning("\The [src] already has \a [scanned_item] inside!")) - return - user.drop_item() - I.loc = src - scanned_item = I - to_chat(user, span_notice("You put \the [I] into \the [src].")) + return -/obj/machinery/radiocarbon_spectrometer/proc/update_coolant() - var/total_purity = 0 - fresh_coolant = 0 - coolant_purity = 0 - var/num_reagent_types = 0 - for (var/datum/reagent/current_reagent in src.reagents.reagent_list) - if (!current_reagent) - continue - var/cur_purity = coolant_reagents_purity[current_reagent.id] - if(!cur_purity) - cur_purity = 0.1 - else if(cur_purity > 1) - cur_purity = 1 - total_purity += cur_purity * current_reagent.volume - fresh_coolant += current_reagent.volume - num_reagent_types += 1 - if(total_purity && fresh_coolant) - coolant_purity = total_purity / fresh_coolant + if(istype(I, /obj/item/reagent_containers/glass)) + var/obj/item/reagent_containers/glass/G = I + if(!G.is_open_container()) + return + var/choice = tgui_alert(user, "What do you want to do with the container?","Radiometric Scanner",list("Add water","Empty water","Scan container")) + if(!choice) + return + if(choice == "Add water") + if(!G.reagents.has_reagent(REAGENT_ID_WATER)) + to_chat(user, span_danger("No water found in beaker.")) + return + var/trans = G.reagents.trans_id_to(src, REAGENT_ID_WATER, G.amount_per_transfer_from_this) + to_chat(user, span_info("You transfer [trans ? trans : 0]u of water into [src].")) + return + else if(choice == "Empty water") + var/amount_transferred = min(G.reagents.maximum_volume - G.reagents.total_volume, reagents.total_volume) + var/trans = reagents.trans_to(G, amount_transferred) + to_chat(user, span_info("You remove [trans ? trans : 0]u of water from [src].")) + return + // fall through + + if(scanned_item) + to_chat(user, span_warning("[src] already has \a [scanned_item] inside!")) + return + + if(!user.unEquip(I, target = src)) + return + + scanned_item = I + to_chat(user, span_notice("You put [I] into [src].")) /obj/machinery/radiocarbon_spectrometer/attack_hand(mob/user) tgui_interact(user) @@ -135,28 +100,31 @@ data["scanned_item"] = (scanned_item ? scanned_item.name : "") data["scanned_item_desc"] = (scanned_item ? (scanned_item.desc ? scanned_item.desc : "No information on record.") : "") data["last_scan_data"] = last_scan_data - // - data["scan_progress"] = round(scanner_progress) data["scanning"] = scanning - // - data["scanner_seal_integrity"] = round(scanner_seal_integrity) - data["scanner_rpm"] = round(scanner_rpm) - data["scanner_temperature"] = round(scanner_temperature) - // - data["coolant_usage_rate"] = coolant_usage_rate - data["coolant_usage_max"] = 10 - data["unused_coolant_abs"] = round(fresh_coolant) - data["unused_coolant_per"] = round(fresh_coolant / reagents.maximum_volume * 100) - data["coolant_purity"] = coolant_purity * 100 - // - data["optimal_wavelength"] = round(optimal_wavelength) - data["maser_wavelength"] = round(maser_wavelength) - data["maser_wavelength_max"] = 10000 - data["maser_efficiency"] = round(maser_efficiency * 100) - // - data["radiation"] = round(radiation) - data["t_left_radspike"] = round(t_left_radspike) - data["rad_shield_on"] = rad_shield + data["scan_progress"] = scan_progress + + // Mechanics + data["scanner_rpm"] = scanner_rpm + data["scanner_rpm_delta"] = scanner_rpm_delta + data["radiation"] = radiation + data["heat"] = heat + data["coolant"] = reagents.total_volume + + return data + +/obj/machinery/radiocarbon_spectrometer/tgui_static_data(mob/user) + var/list/data = ..() + + data["IDEAL_RPM"] = IDEAL_RPM + data["RPM_MAX_DELTA"] = RPM_MAX_DELTA + data["RPM_MAX"] = RPM_MAX + + data["TARGET_RADIATION"] = TARGET_RADIATION + data["RADIATION_MAX"] = RADIATION_MAX + + data["HEAT_FAILURE_THRESHOLD"] = HEAT_FAILURE_THRESHOLD + data["HEAT_MAX"] = HEAT_MAX + data["COOLANT_MAX"] = COOLANT_MAX return data @@ -169,29 +137,12 @@ if("scanItem") if(scanning) stop_scanning() - else - if(scanned_item) - if(scanner_seal_integrity > 0) - scanner_progress = 0 - scanning = 1 - t_left_radspike = pick(5,10,15) - to_chat(ui.user, span_notice("Scan initiated.")) - else - to_chat(ui.user, span_warning("Could not initiate scan, seal requires replacing.")) - else - to_chat(ui.user, span_warning("Insert an item to scan.")) - return TRUE - - if("maserWavelength") - maser_wavelength = clamp(text2num(params["wavelength"]), 1, 10000) - return TRUE - - if("coolantRate") - coolant_usage_rate = clamp(text2num(params["coolant"]), 0, 10) - return TRUE - - if("toggle_rad_shield") - rad_shield = !rad_shield + return + if(!scanned_item) + to_chat(ui.user, span_warning("Insert an item to scan.")) + return + start_scanning() + to_chat(ui.user, span_notice("Scan initiated.")) return TRUE if("ejectItem") @@ -200,164 +151,160 @@ scanned_item = null return TRUE + if("set_scanner_rpm_delta") + scanner_rpm_delta = CLAMP(params["delta"], -RPM_MAX_DELTA, RPM_MAX_DELTA) + return TRUE + + if("inject_radiation") + if(!scanning) + radiation = 0 + return + radiation = CLAMP(radiation + RADIATION_INJECTION_AMT, 0, RADIATION_MAX) + return TRUE + /obj/machinery/radiocarbon_spectrometer/process() - if(scanning) - if(!scanned_item || scanned_item.loc != src) - scanned_item = null - stop_scanning() - else if(scanner_progress >= 100) - complete_scan() - else - //calculate time difference - var/deltaT = (world.time - last_process_worldtime) * 0.1 + if(!scanning) + return - //modify the RPM over time - //i want 1u to last for 10 sec at 500 RPM, scaling linearly - scanner_rpm += scanner_rpm_dir * 50 * deltaT - if(scanner_rpm > 1000) - scanner_rpm = 1000 - scanner_rpm_dir = -1 * pick(0.5, 2.5, 5.5) - else if(scanner_rpm < 1) - scanner_rpm = 1 - scanner_rpm_dir = 1 * pick(0.5, 2.5, 5.5) + if(!scanned_item || scanned_item.loc != src) + scanned_item = null + stop_scanning() + return - //heat up according to RPM - //each unit of coolant - scanner_temperature += scanner_rpm * deltaT * 0.05 + if(scan_progress >= 100) + complete_scan() + return - //radiation - t_left_radspike -= deltaT - if(t_left_radspike > 0) - //ordinary radiation - radiation = rand() * 15 - else - //radspike - if(t_left_radspike > -5) - radiation = rand() * 15 + 85 - if(!rad_shield) - //irradiate nearby mobs - SSradiation.radiate(src, radiation / 25) - else - t_left_radspike = pick(10,15,25) + // Mechanics time + /************** HEAT *************/ + if(heat > HEAT_FAILURE_THRESHOLD) + visible_message(span_danger("[icon2html(src, viewers(src))] buzzes unhappily. It has failed mid-scan!"), range = 2) + stop_scanning() + return + var/heat_gain = reagents.remove_reagent(REAGENT_ID_WATER, COOLANT_USAGE) ? -1 : HEAT_GAIN + heat += heat_gain + heat = CLAMP(heat, 0, HEAT_MAX) - //use some coolant to cool down - if(coolant_usage_rate > 0) - var/coolant_used = min(fresh_coolant, coolant_usage_rate * deltaT) - if(coolant_used > 0) - fresh_coolant -= coolant_used - used_coolant += coolant_used - scanner_temperature = max(scanner_temperature - coolant_used * coolant_purity * 20, 0) + /************** RPM **************/ + // Scanner slows down over time + scanner_rpm -= RPM_FRICTION + rand(0, RPM_FRICTION) // randomly add more friction + // Motor speeds it up + scanner_rpm += scanner_rpm_delta + // Cap + scanner_rpm = CLAMP(scanner_rpm, RPM_MIN, RPM_MAX) - //modify the optimal wavelength - tleft_retarget_optimal_wavelength -= deltaT - if(tleft_retarget_optimal_wavelength <= 0) - tleft_retarget_optimal_wavelength = pick(4,8,15) - optimal_wavelength_target = rand() * 9900 + 100 - // - if(optimal_wavelength < optimal_wavelength_target) - optimal_wavelength = min(optimal_wavelength + 700 * deltaT, optimal_wavelength_target) - else if(optimal_wavelength > optimal_wavelength_target) - optimal_wavelength = max(optimal_wavelength - 700 * deltaT, optimal_wavelength_target) - // - maser_efficiency = 1 - max(min(10000, abs(optimal_wavelength - maser_wavelength) * 3), 1) / 10000 + // Factor RPM into completion + var/diff_from_ideal_rpm = abs(scanner_rpm - IDEAL_RPM) + var/rpm_factor = CLAMP01(1 - (diff_from_ideal_rpm / RPM_OKAY_RANGE)) - //make some scan progress - if(!rad_shield) - scanner_progress = min(100, scanner_progress + scanner_rate * maser_efficiency * deltaT) + /************** RADIATION *************/ + radiation -= RADIATION_LOSS + radiation = CLAMP(radiation, 0, RADIATION_MAX) + var/diff_from_ideal_radiation = abs(radiation - TARGET_RADIATION) + var/radiation_factor = CLAMP01(1 - (diff_from_ideal_radiation / RADIATION_OK_RANGE)) - //degrade the seal over time according to temperature - //i want temperature of 50K to degrade at 1%/sec - scanner_seal_integrity -= (max(scanner_temperature, 1) / 1000) * deltaT + /************** COMPLETION *************/ + var/completion_delta = rpm_factor + radiation_factor + scan_progress = CLAMP(scan_progress + (completion_delta * COMPLETION_DELTA_MODIFIER), 0, 100) - //emergency stop if seal integrity reaches 0 - if(scanner_seal_integrity <= 0 || (scanner_temperature >= 1273 && !rad_shield)) - stop_scanning() - src.visible_message(span_blue("[icon2html(src,viewers(src))] buzzes unhappily. It has failed mid-scan!"), 2) - - if(prob(5)) - src.visible_message(span_blue("[icon2html(src,viewers(src))] [pick("whirrs","chuffs","clicks")][pick(" excitedly"," energetically"," busily")]."), 2) - else - //gradually cool down over time - if(scanner_temperature > 0) - scanner_temperature = max(scanner_temperature - 5 - 10 * rand(), 0) - if(prob(0.75)) - src.visible_message(span_blue("[icon2html(src,viewers(src))] [pick("plinks","hisses")][pick(" quietly"," softly"," sadly"," plaintively")]."), 2) - playsound(src, 'sound/effects/ding.ogg', 25) - last_process_worldtime = world.time +/obj/machinery/radiocarbon_spectrometer/proc/start_scanning() + icon_state = "analyser_processing" + scanning = TRUE + scan_progress = 0 + scanner_rpm_delta = 0 + scanner_rpm = 0 + radiation = 0 + heat = 0 /obj/machinery/radiocarbon_spectrometer/proc/stop_scanning() - scanning = 0 - scanner_rpm_dir = 1 + icon_state = "analyser" + scanning = FALSE + scan_progress = 0 + scanner_rpm_delta = 0 scanner_rpm = 0 - optimal_wavelength = 0 - maser_efficiency = 0 - maser_wavelength = 0 - coolant_usage_rate = 0 radiation = 0 - t_left_radspike = 0 - if(used_coolant) - src.reagents.remove_any(used_coolant) - used_coolant = 0 + heat = 0 /obj/machinery/radiocarbon_spectrometer/proc/complete_scan() - src.visible_message(span_blue("[icon2html(src,viewers(src))] makes an insistent chime."), 2) + stop_scanning() + visible_message(span_notice("[icon2html(src, viewers(src))] makes an insistent chime."), range = 2) + + if(!scanned_item) + return - if(scanned_item) //create report - var/obj/item/paper/P = new(src) - P.name = "[src] report #[++report_num]: [scanned_item.name]" - P.stamped = list(/obj/item/stamp) - P.add_overlay("paper_stamped") + var/obj/item/paper/P = new(src) + P.name = "[src] report #[++report_num]: [scanned_item.name]" + P.stamped = list(/obj/item/stamp) + P.add_overlay("paper_stamped") - //work out data - var/data = " - Mundane object: [scanned_item.desc ? scanned_item.desc : "No information on record."]
" - var/datum/geosample/G - switch(scanned_item.type) - if(/obj/item/ore) - var/obj/item/ore/O = scanned_item - if(O.geologic_data) - G = O.geologic_data + //work out data + var/data = " - Mundane object: [scanned_item.desc ? scanned_item.desc : "No information on record."]
" + var/datum/geosample/G + switch(scanned_item.type) + if(/obj/item/ore) + var/obj/item/ore/O = scanned_item + if(O.geologic_data) + G = O.geologic_data - if(/obj/item/rocksliver) - var/obj/item/rocksliver/O = scanned_item - if(O.geological_data) - G = O.geological_data + if(/obj/item/rocksliver) + var/obj/item/rocksliver/O = scanned_item + if(O.geological_data) + G = O.geological_data - if(/obj/item/archaeological_find) - data = " - Mundane object (archaic xenos origins)
" + if(/obj/item/archaeological_find) + data = " - Mundane object (archaic xenos origins)
" - var/obj/item/archaeological_find/A = scanned_item - if(A.talking_atom) - data = " - Exhibits properties consistent with sonic reproduction and audio capture technologies.
" + var/obj/item/archaeological_find/A = scanned_item + if(A.talking_atom) + data = " - Exhibits properties consistent with sonic reproduction and audio capture technologies.
" - var/anom_found = 0 - if(G) - data = " - Spectometric analysis on mineral sample has determined type [finds_as_strings[responsive_carriers.Find(G.source_mineral)]]
" - if(G.age_billion > 0) - data += " - Radiometric dating shows age of [G.age_billion].[G.age_million] billion years
" - else if(G.age_million > 0) - data += " - Radiometric dating shows age of [G.age_million].[G.age_thousand] million years
" - else - data += " - Radiometric dating shows age of [G.age_thousand * 1000 + G.age] years
" - data += " - Chromatographic analysis shows the following materials present:
" - for(var/carrier in G.find_presence) - if(G.find_presence[carrier]) - var/index = responsive_carriers.Find(carrier) - if(index > 0 && index <= finds_as_strings.len) - data += " > [100 * G.find_presence[carrier]]% [finds_as_strings[index]]
" + var/anom_found = 0 + if(G) + data = " - Spectometric analysis on mineral sample has determined type [finds_as_strings[responsive_carriers.Find(G.source_mineral)]]
" + if(G.age_billion > 0) + data += " - Radiometric dating shows age of [G.age_billion].[G.age_million] billion years
" + else if(G.age_million > 0) + data += " - Radiometric dating shows age of [G.age_million].[G.age_thousand] million years
" + else + data += " - Radiometric dating shows age of [G.age_thousand * 1000 + G.age] years
" + data += " - Chromatographic analysis shows the following materials present:
" + for(var/carrier in G.find_presence) + if(G.find_presence[carrier]) + var/index = responsive_carriers.Find(carrier) + if(index > 0 && index <= finds_as_strings.len) + data += " > [100 * G.find_presence[carrier]]% [finds_as_strings[index]]
" - if(G.artifact_id && G.artifact_distance >= 0) - anom_found = 1 - data += " - Hyperspectral imaging reveals exotic energy wavelength detected with ID: [G.artifact_id]
" - data += " - Fourier transform analysis on anomalous energy absorption indicates energy source located inside emission radius of [G.artifact_distance]m
" + if(G.artifact_id && G.artifact_distance >= 0) + anom_found = 1 + data += " - Hyperspectral imaging reveals exotic energy wavelength detected with ID: [G.artifact_id]
" + data += " - Fourier transform analysis on anomalous energy absorption indicates energy source located inside emission radius of [G.artifact_distance]m
" - if(!anom_found) - data += " - No anomalous data
" + if(!anom_found) + data += " - No anomalous data
" - P.info = span_bold("[src] analysis report #[report_num]") + "
" - P.info += span_bold("Scanned item:") + " [scanned_item.name]

" + data - last_scan_data = P.info - P.loc = src.loc + P.info = span_bold("[src] analysis report #[report_num]") + "
" + P.info += span_bold("Scanned item:") + " [scanned_item.name]

" + data + last_scan_data = P.info - scanned_item.loc = src.loc - scanned_item = null + P.forceMove(loc) + scanned_item.forceMove(loc) + scanned_item = null + +#undef RPM_FRICTION +#undef IDEAL_RPM +#undef RPM_OKAY_RANGE +#undef RPM_MAX_DELTA +#undef RPM_MIN +#undef RPM_MAX +#undef COMPLETION_DELTA_MODIFIER +#undef RADIATION_INJECTION_AMT +#undef RADIATION_MAX +#undef TARGET_RADIATION +#undef RADIATION_OK_RANGE +#undef RADIATION_LOSS +#undef HEAT_GAIN +#undef HEAT_FAILURE_THRESHOLD +#undef HEAT_MAX +#undef COOLANT_USAGE +#undef COOLANT_MAX diff --git a/maps/expedition_vr/aerostat/aerostat_science_outpost.dmm b/maps/expedition_vr/aerostat/aerostat_science_outpost.dmm index 4fbe20b26e..3f44dff9f9 100644 --- a/maps/expedition_vr/aerostat/aerostat_science_outpost.dmm +++ b/maps/expedition_vr/aerostat/aerostat_science_outpost.dmm @@ -13074,10 +13074,13 @@ /obj/machinery/atmospherics/pipe/manifold/hidden/supply{ dir = 1 }, -/obj/structure/reagent_dispensers/coolanttank, /obj/structure/window/reinforced{ dir = 8 }, +/obj/structure/sink/kitchen{ + pixel_x = 0; + pixel_y = 16 + }, /turf/simulated/floor/tiled/white, /area/offmap/aerostat/inside/xenoarch) "YH" = ( diff --git a/maps/tether/tether-07-solars.dmm b/maps/tether/tether-07-solars.dmm index e25e37d92c..3118bd000e 100644 --- a/maps/tether/tether-07-solars.dmm +++ b/maps/tether/tether-07-solars.dmm @@ -6326,7 +6326,6 @@ /turf/simulated/floor/tiled/monotile, /area/rnd/outpost/anomaly_lab) "mp" = ( -/obj/structure/reagent_dispensers/coolanttank, /obj/effect/floor_decal/steeldecal/steel_decals9, /obj/effect/floor_decal/steeldecal/steel_decals9{ dir = 4 @@ -6337,6 +6336,10 @@ /obj/effect/floor_decal/steeldecal/steel_decals9{ dir = 1 }, +/obj/structure/sink/kitchen{ + pixel_x = 0; + pixel_y = 17 + }, /turf/simulated/floor/tiled/monotile, /area/rnd/outpost/anomaly_lab) "mq" = ( diff --git a/tgui/packages/tgui/interfaces/XenoarchSpectrometer.tsx b/tgui/packages/tgui/interfaces/XenoarchSpectrometer.tsx index b6183df02c..d363078834 100644 --- a/tgui/packages/tgui/interfaces/XenoarchSpectrometer.tsx +++ b/tgui/packages/tgui/interfaces/XenoarchSpectrometer.tsx @@ -1,15 +1,18 @@ +import { useEffect, useState } from 'react'; import { useBackend } from 'tgui/backend'; import { Window } from 'tgui/layouts'; import { Box, Button, + Icon, + Knob, LabeledList, - NoticeBox, ProgressBar, + RoundGauge, Section, - Slider, Stack, } from 'tgui-core/components'; +import { scale } from 'tgui-core/math'; import type { BooleanLike } from 'tgui-core/react'; import { decodeHtmlEntities } from 'tgui-core/string'; @@ -17,23 +20,25 @@ type Data = { scanned_item: string; scanned_item_desc: string; last_scan_data: string; - scan_progress: number; scanning: BooleanLike; - scanner_seal_integrity: number; + + // Mechanics + scan_progress: number; scanner_rpm: number; - scanner_temperature: number; - coolant_usage_rate: number; - coolant_usage_max: number; - unused_coolant_abs: number; - unused_coolant_per: number; - coolant_purity: number; - optimal_wavelength: number; - maser_wavelength: number; - maser_wavelength_max: number; - maser_efficiency: number; + scanner_rpm_delta: number; radiation: number; - t_left_radspike: number; - rad_shield_on: BooleanLike; + heat: number; + coolant: number; + + // Constants + IDEAL_RPM: number; + RPM_MAX_DELTA: number; + RPM_MAX: number; + TARGET_RADIATION: number; + RADIATION_MAX: number; + HEAT_FAILURE_THRESHOLD: number; + HEAT_MAX: number; + COOLANT_MAX: number; }; export const XenoarchSpectrometer = (props) => { @@ -43,27 +48,25 @@ export const XenoarchSpectrometer = (props) => { scanned_item, scanned_item_desc, last_scan_data, - scan_progress, scanning, - scanner_seal_integrity, + scan_progress, scanner_rpm, - scanner_temperature, - coolant_usage_rate, - coolant_usage_max, - unused_coolant_abs, - unused_coolant_per, - coolant_purity, - optimal_wavelength, - maser_wavelength, - maser_wavelength_max, - maser_efficiency, + scanner_rpm_delta, radiation, - t_left_radspike, - rad_shield_on, + heat, + coolant, + IDEAL_RPM, + RPM_MAX_DELTA, + RPM_MAX, + TARGET_RADIATION, + RADIATION_MAX, + HEAT_FAILURE_THRESHOLD, + HEAT_MAX, + COOLANT_MAX, } = data; return ( - +
{ {scanned_item_desc || 'None found.'} - -
-
- - - - - - + + Bring the RPM up to {IDEAL_RPM} and keep it there. + + Bring radiation to {TARGET_RADIATION} and keep it there. +
-
Match wavelengths to progress the scan. - } - > - - - - - - val + ' MHz'} - step={10} - onDrag={(e, val: number) => - act('maserWavelength', { wavelength: val }) - } - /> - - -
-
- - - - {scanner_rpm} RPM - - - - - {scanner_temperature} K - - - -
-
act('toggle_rad_shield')} - > - {rad_shield_on - ? 'Disable Radiation Shielding' - : 'Enable Radiation Shielding'} - - } - > - - - - {radiation} mSv - - - -
-
- - - - {unused_coolant_per * 10} u - - - - val + ' u/s'} - onDrag={(e, val: number) => - act('coolantRate', { coolant: val }) - } - /> - - - - - +
+ {/* Mechanics!~ */} + + + +
{decodeHtmlEntities(last_scan_data) @@ -270,3 +126,189 @@ export const XenoarchSpectrometer = (props) => { ); }; + +export const Spinner = (props: { + value: number; + minValue: number; + maxValue: number; +}) => { + const { value, minValue, maxValue } = props; + const factor = scale(value, minValue, maxValue); + + const [rotation, setRotation] = useState(0); + const SPEED_MULTIPLIER = 0.3; + const STEP_SIZE = 2; + + useEffect(() => { + // Only spin if there's ~some~ flow. + if (!factor) { + return; + } + const id = setInterval(() => { + setRotation((rot) => (rot + STEP_SIZE) % 359); + }, SPEED_MULTIPLIER * factor); + return () => clearInterval(id); + }, [factor]); + + return ( + + + + + + + ); +}; + +export const LeftPanel = (props) => { + const { act, data } = useBackend(); + + const { + scan_progress, + scanner_rpm, + scanner_rpm_delta, + RPM_MAX_DELTA, + RPM_MAX, + } = data; + + return ( + + + + + + + + + + + val.toFixed(2)} + onChange={(e, delta) => act('set_scanner_rpm_delta', { delta })} + /> + } + > + + {scanner_rpm} + + + + + ); +}; +export const RightPanel = (props) => { + const { act, data } = useBackend(); + + const { + radiation, + heat, + coolant, + RADIATION_MAX, + HEAT_FAILURE_THRESHOLD, + HEAT_MAX, + COOLANT_MAX, + } = data; + + return ( + + + + + + + + + + + + + + + + + + + + Temperature + + + + + + + + Coolant + + + + + + + ); +};