mirror of
https://github.com/CHOMPStation2/CHOMPStation2.git
synced 2025-12-10 02:09:41 +00:00
[MIRROR] Completely rewrite xenoarch spectrometer (#11079)
Co-authored-by: ShadowLarkens <shadowlarkens@gmail.com>
This commit is contained in:
committed by
GitHub
parent
2f1ecc1565
commit
203c2609a7
@@ -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"))
|
||||
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 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()
|
||||
if(choice == "Add water")
|
||||
if(!G.reagents.has_reagent(REAGENT_ID_WATER))
|
||||
to_chat(user, span_danger("No water found in beaker."))
|
||||
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()
|
||||
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
|
||||
if(scanned_item)
|
||||
to_chat(user, span_warning("\The [src] already has \a [scanned_item] inside!"))
|
||||
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
|
||||
user.drop_item()
|
||||
I.loc = src
|
||||
scanned_item = I
|
||||
to_chat(user, span_notice("You put \the [I] into \the [src]."))
|
||||
// fall through
|
||||
|
||||
/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(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
|
||||
return
|
||||
if(!scanned_item)
|
||||
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
|
||||
start_scanning()
|
||||
to_chat(ui.user, span_notice("Scan initiated."))
|
||||
return TRUE
|
||||
|
||||
if("ejectItem")
|
||||
@@ -200,109 +151,87 @@
|
||||
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(!scanning)
|
||||
return
|
||||
|
||||
if(!scanned_item || scanned_item.loc != src)
|
||||
scanned_item = null
|
||||
stop_scanning()
|
||||
else if(scanner_progress >= 100)
|
||||
return
|
||||
|
||||
if(scan_progress >= 100)
|
||||
complete_scan()
|
||||
else
|
||||
//calculate time difference
|
||||
var/deltaT = (world.time - last_process_worldtime) * 0.1
|
||||
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)
|
||||
|
||||
//heat up according to RPM
|
||||
//each unit of coolant
|
||||
scanner_temperature += scanner_rpm * deltaT * 0.05
|
||||
|
||||
//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)
|
||||
|
||||
//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)
|
||||
|
||||
//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
|
||||
|
||||
//make some scan progress
|
||||
if(!rad_shield)
|
||||
scanner_progress = min(100, scanner_progress + scanner_rate * maser_efficiency * deltaT)
|
||||
|
||||
//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
|
||||
|
||||
//emergency stop if seal integrity reaches 0
|
||||
if(scanner_seal_integrity <= 0 || (scanner_temperature >= 1273 && !rad_shield))
|
||||
// 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()
|
||||
src.visible_message(span_blue("[icon2html(src,viewers(src))] buzzes unhappily. It has failed mid-scan!"), 2)
|
||||
return
|
||||
var/heat_gain = reagents.remove_reagent(REAGENT_ID_WATER, COOLANT_USAGE) ? -1 : HEAT_GAIN
|
||||
heat += heat_gain
|
||||
heat = CLAMP(heat, 0, HEAT_MAX)
|
||||
|
||||
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
|
||||
/************** 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)
|
||||
|
||||
// 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))
|
||||
|
||||
/************** 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))
|
||||
|
||||
/************** COMPLETION *************/
|
||||
var/completion_delta = rpm_factor + radiation_factor
|
||||
scan_progress = CLAMP(scan_progress + (completion_delta * COMPLETION_DELTA_MODIFIER), 0, 100)
|
||||
|
||||
/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]"
|
||||
@@ -357,7 +286,25 @@
|
||||
P.info = span_bold("[src] analysis report #[report_num]") + "<br>"
|
||||
P.info += span_bold("Scanned item:") + " [scanned_item.name]<br><br>" + data
|
||||
last_scan_data = P.info
|
||||
P.loc = src.loc
|
||||
|
||||
scanned_item.loc = src.loc
|
||||
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
|
||||
|
||||
@@ -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" = (
|
||||
|
||||
@@ -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" = (
|
||||
|
||||
@@ -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 (
|
||||
<Window width={900} height={760}>
|
||||
<Window width={700} height={760}>
|
||||
<Window.Content scrollable>
|
||||
<Section
|
||||
title="Status"
|
||||
@@ -97,167 +100,20 @@ export const XenoarchSpectrometer = (props) => {
|
||||
<LabeledList.Item label="Heuristic Analysis">
|
||||
{scanned_item_desc || 'None found.'}
|
||||
</LabeledList.Item>
|
||||
</LabeledList>
|
||||
</Section>
|
||||
<Section title="Scanner">
|
||||
<LabeledList>
|
||||
<LabeledList.Item label="Scan Progress">
|
||||
<ProgressBar
|
||||
value={scan_progress}
|
||||
minValue={0}
|
||||
maxValue={100}
|
||||
color="good"
|
||||
/>
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Vacuum Seal Integrity">
|
||||
<ProgressBar
|
||||
value={scanner_seal_integrity}
|
||||
minValue={0}
|
||||
maxValue={100}
|
||||
ranges={{
|
||||
good: [66, 100],
|
||||
average: [33, 66],
|
||||
bad: [0, 33],
|
||||
}}
|
||||
/>
|
||||
<LabeledList.Item label="Instructions">
|
||||
<Box>Bring the RPM up to {IDEAL_RPM} and keep it there.</Box>
|
||||
<Box>
|
||||
Bring radiation to {TARGET_RADIATION} and keep it there.
|
||||
</Box>
|
||||
</LabeledList.Item>
|
||||
</LabeledList>
|
||||
</Section>
|
||||
<Section
|
||||
title="MASER"
|
||||
buttons={
|
||||
<NoticeBox info>Match wavelengths to progress the scan.</NoticeBox>
|
||||
}
|
||||
>
|
||||
<LabeledList>
|
||||
<LabeledList.Item label="MASER Efficiency">
|
||||
<ProgressBar
|
||||
value={maser_efficiency}
|
||||
minValue={0}
|
||||
maxValue={100}
|
||||
ranges={{
|
||||
good: [66, 100],
|
||||
average: [33, 66],
|
||||
bad: [0, 33],
|
||||
}}
|
||||
/>
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Wavelength">
|
||||
<Slider
|
||||
animated
|
||||
value={maser_wavelength}
|
||||
fillValue={optimal_wavelength}
|
||||
minValue={1}
|
||||
maxValue={maser_wavelength_max}
|
||||
format={(val: number) => val + ' MHz'}
|
||||
step={10}
|
||||
onDrag={(e, val: number) =>
|
||||
act('maserWavelength', { wavelength: val })
|
||||
}
|
||||
/>
|
||||
</LabeledList.Item>
|
||||
</LabeledList>
|
||||
</Section>
|
||||
<Section title="Environment / Internal">
|
||||
<LabeledList>
|
||||
<LabeledList.Item label="Centrifuge Speed">
|
||||
<ProgressBar
|
||||
value={scanner_rpm}
|
||||
minValue={0}
|
||||
maxValue={1000}
|
||||
color="good"
|
||||
>
|
||||
{scanner_rpm} RPM
|
||||
</ProgressBar>
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Internal Temperature">
|
||||
<ProgressBar
|
||||
minValue={0}
|
||||
value={scanner_temperature}
|
||||
maxValue={1273}
|
||||
ranges={{
|
||||
bad: [1000, Infinity],
|
||||
average: [250, 1000],
|
||||
good: [0, 250],
|
||||
}}
|
||||
>
|
||||
{scanner_temperature} K
|
||||
</ProgressBar>
|
||||
</LabeledList.Item>
|
||||
</LabeledList>
|
||||
</Section>
|
||||
<Section
|
||||
title="Radiation"
|
||||
buttons={
|
||||
<Button
|
||||
selected={rad_shield_on}
|
||||
icon="radiation"
|
||||
onClick={() => act('toggle_rad_shield')}
|
||||
>
|
||||
{rad_shield_on
|
||||
? 'Disable Radiation Shielding'
|
||||
: 'Enable Radiation Shielding'}
|
||||
</Button>
|
||||
}
|
||||
>
|
||||
<LabeledList>
|
||||
<LabeledList.Item label="Ambient Radiation">
|
||||
<ProgressBar
|
||||
minValue={0}
|
||||
value={radiation}
|
||||
maxValue={100}
|
||||
ranges={{
|
||||
bad: [65, Infinity],
|
||||
average: [15, 65],
|
||||
good: [0, 15],
|
||||
}}
|
||||
>
|
||||
{radiation} mSv
|
||||
</ProgressBar>
|
||||
</LabeledList.Item>
|
||||
</LabeledList>
|
||||
</Section>
|
||||
<Section title="Cooling">
|
||||
<LabeledList>
|
||||
<LabeledList.Item label="Coolant Remaining">
|
||||
<ProgressBar
|
||||
minValue={0}
|
||||
value={unused_coolant_per * 10}
|
||||
maxValue={1000}
|
||||
ranges={{
|
||||
good: [65, Infinity],
|
||||
average: [15, 65],
|
||||
bad: [0, 15],
|
||||
}}
|
||||
>
|
||||
{unused_coolant_per * 10} u
|
||||
</ProgressBar>
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Coolant Flow Rate">
|
||||
<Slider
|
||||
minValue={0}
|
||||
value={coolant_usage_rate}
|
||||
maxValue={coolant_usage_max}
|
||||
stepPixelSize={50}
|
||||
format={(val: number) => val + ' u/s'}
|
||||
onDrag={(e, val: number) =>
|
||||
act('coolantRate', { coolant: val })
|
||||
}
|
||||
/>
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Coolant Purity">
|
||||
<ProgressBar
|
||||
minValue={0}
|
||||
value={coolant_purity}
|
||||
maxValue={100}
|
||||
ranges={{
|
||||
good: [66, Infinity],
|
||||
average: [33, 66],
|
||||
bad: [0, 33],
|
||||
}}
|
||||
/>
|
||||
</LabeledList.Item>
|
||||
</LabeledList>
|
||||
<Section>
|
||||
{/* Mechanics!~ */}
|
||||
<Stack fill>
|
||||
<LeftPanel />
|
||||
<RightPanel />
|
||||
</Stack>
|
||||
</Section>
|
||||
<Section title="Latest Results">
|
||||
{decodeHtmlEntities(last_scan_data)
|
||||
@@ -270,3 +126,189 @@ export const XenoarchSpectrometer = (props) => {
|
||||
</Window>
|
||||
);
|
||||
};
|
||||
|
||||
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 (
|
||||
<Box
|
||||
width={4}
|
||||
height={4}
|
||||
mt={16}
|
||||
mb={16}
|
||||
style={{
|
||||
fontSize: '4rem',
|
||||
transform: `rotate(${rotation}deg)`,
|
||||
transformOrigin: 'center',
|
||||
}}
|
||||
position="relative"
|
||||
>
|
||||
<Icon
|
||||
name="vial"
|
||||
position="absolute"
|
||||
rotation={-45 - 180}
|
||||
top={-4}
|
||||
left={0}
|
||||
/>
|
||||
<Icon name="vial" position="absolute" rotation={45} top={0} left={-4} />
|
||||
<Icon
|
||||
name="vial"
|
||||
position="absolute"
|
||||
rotation={45 - 90}
|
||||
top={4}
|
||||
left={0}
|
||||
/>
|
||||
<Icon
|
||||
name="vial"
|
||||
position="absolute"
|
||||
rotation={45 + 180}
|
||||
top={0}
|
||||
left={4}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export const LeftPanel = (props) => {
|
||||
const { act, data } = useBackend<Data>();
|
||||
|
||||
const {
|
||||
scan_progress,
|
||||
scanner_rpm,
|
||||
scanner_rpm_delta,
|
||||
RPM_MAX_DELTA,
|
||||
RPM_MAX,
|
||||
} = data;
|
||||
|
||||
return (
|
||||
<Stack.Item grow>
|
||||
<Stack align="center" justify="center">
|
||||
<Stack.Item>
|
||||
<Spinner value={scanner_rpm} minValue={0} maxValue={RPM_MAX} />
|
||||
</Stack.Item>
|
||||
</Stack>
|
||||
<LabeledList>
|
||||
<LabeledList.Item label="Completion">
|
||||
<ProgressBar value={scan_progress} minValue={0} maxValue={100} />
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item
|
||||
label="RPM"
|
||||
buttons={
|
||||
<Knob
|
||||
value={scanner_rpm_delta}
|
||||
minValue={-RPM_MAX_DELTA}
|
||||
maxValue={RPM_MAX_DELTA}
|
||||
step={1}
|
||||
format={(val) => val.toFixed(2)}
|
||||
onChange={(e, delta) => act('set_scanner_rpm_delta', { delta })}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<ProgressBar value={scanner_rpm} minValue={0} maxValue={RPM_MAX}>
|
||||
{scanner_rpm}
|
||||
</ProgressBar>
|
||||
</LabeledList.Item>
|
||||
</LabeledList>
|
||||
</Stack.Item>
|
||||
);
|
||||
};
|
||||
export const RightPanel = (props) => {
|
||||
const { act, data } = useBackend<Data>();
|
||||
|
||||
const {
|
||||
radiation,
|
||||
heat,
|
||||
coolant,
|
||||
RADIATION_MAX,
|
||||
HEAT_FAILURE_THRESHOLD,
|
||||
HEAT_MAX,
|
||||
COOLANT_MAX,
|
||||
} = data;
|
||||
|
||||
return (
|
||||
<Stack.Item basis={20}>
|
||||
<Stack fill vertical align="center" justify="center">
|
||||
<Stack.Item grow>
|
||||
<Stack fill vertical align="center" justify="center">
|
||||
<Stack.Item>
|
||||
<RoundGauge
|
||||
value={radiation}
|
||||
minValue={0}
|
||||
maxValue={RADIATION_MAX}
|
||||
size={4}
|
||||
/>
|
||||
</Stack.Item>
|
||||
<Stack.Item>
|
||||
<Button icon="radiation" onClick={() => act('inject_radiation')}>
|
||||
Inject Radiation
|
||||
</Button>
|
||||
</Stack.Item>
|
||||
</Stack>
|
||||
</Stack.Item>
|
||||
<Stack.Item>
|
||||
<Stack align="center" justify="center">
|
||||
<Stack.Item>
|
||||
<Stack vertical align="center" justify="center">
|
||||
<Stack.Item>
|
||||
<RoundGauge
|
||||
size={2}
|
||||
value={heat}
|
||||
minValue={0}
|
||||
maxValue={HEAT_MAX}
|
||||
ranges={{
|
||||
good: [0, HEAT_FAILURE_THRESHOLD / 2],
|
||||
average: [
|
||||
HEAT_FAILURE_THRESHOLD / 2,
|
||||
HEAT_FAILURE_THRESHOLD,
|
||||
],
|
||||
bad: [HEAT_FAILURE_THRESHOLD, HEAT_MAX],
|
||||
}}
|
||||
/>
|
||||
</Stack.Item>
|
||||
<Stack.Item>Temperature</Stack.Item>
|
||||
</Stack>
|
||||
</Stack.Item>
|
||||
<Stack.Item>
|
||||
<Stack vertical align="center" justify="center">
|
||||
<Stack.Item>
|
||||
<RoundGauge
|
||||
size={2}
|
||||
value={coolant}
|
||||
minValue={0}
|
||||
maxValue={COOLANT_MAX}
|
||||
ranges={{
|
||||
bad: [0, COOLANT_MAX / 4],
|
||||
average: [COOLANT_MAX / 4, COOLANT_MAX / 2],
|
||||
good: [COOLANT_MAX / 2, COOLANT_MAX],
|
||||
}}
|
||||
/>
|
||||
</Stack.Item>
|
||||
<Stack.Item>Coolant</Stack.Item>
|
||||
</Stack>
|
||||
</Stack.Item>
|
||||
</Stack>
|
||||
</Stack.Item>
|
||||
</Stack>
|
||||
</Stack.Item>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user