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
+
+
+
+
+
+
+ );
+};