Merge pull request #13738 from dearmochi/tgui-medical

[TGUI] Medical Machines
This commit is contained in:
AffectedArc07
2020-09-15 15:19:39 +01:00
committed by GitHub
55 changed files with 5978 additions and 3230 deletions
+5
View File
@@ -92,3 +92,8 @@
// Firelock states
#define FD_OPEN 1
#define FD_CLOSED 2
// Computer login types
#define LOGIN_TYPE_NORMAL 1
#define LOGIN_TYPE_AI 2
#define LOGIN_TYPE_ROBOT 3
+8
View File
@@ -0,0 +1,8 @@
// TGUI defines
#define TGUI_MODAL_INPUT_MAX_LENGTH 1024
#define TGUI_MODAL_INPUT_MAX_LENGTH_NAME 64 // Names for generally anything don't go past 32, let alone 64.
#define TGUI_MODAL_OPEN 1
#define TGUI_MODAL_DELEGATE 2
#define TGUI_MODAL_ANSWER 3
#define TGUI_MODAL_CLOSE 4
+343 -403
View File
@@ -7,6 +7,11 @@
#define NEGATE_MUTATION_THRESHOLD 30 // Occupants with over ## percent radiation threshold will not gain mutations
#define PAGE_UI "ui"
#define PAGE_SE "se"
#define PAGE_BUFFER "buffer"
#define PAGE_REJUVENATORS "rejuvenators"
//list("data" = null, "owner" = null, "label" = null, "type" = null, "ue" = 0),
/datum/dna2/record
var/datum/dna/dna = null
@@ -161,6 +166,7 @@
occupant = usr
icon_state = "scanner_occupied"
add_fingerprint(usr)
SStgui.update_uis(src)
/obj/machinery/dna_scannernew/MouseDrop_T(atom/movable/O, mob/user)
if(!istype(O))
@@ -214,6 +220,7 @@
return
beaker = I
SStgui.update_uis(src)
I.forceMove(src)
user.visible_message("[user] adds \a [I] to \the [src]!", "You add \a [I] to \the [src]!")
return
@@ -260,6 +267,7 @@
M.forceMove(src)
occupant = M
icon_state = "scanner_occupied"
SStgui.update_uis(src)
// search for ghosts, if the corpse is empty and the scanner is connected to a cloner
if(locate(/obj/machinery/computer/cloning, get_step(src, NORTH)) \
@@ -281,6 +289,7 @@
occupant.forceMove(loc)
occupant = null
icon_state = "scanner_open"
SStgui.update_uis(src)
/obj/machinery/dna_scannernew/force_eject_occupant()
go_out(null, TRUE)
@@ -296,6 +305,7 @@
occupant = null
updateUsrDialog()
update_icon()
SStgui.update_uis(src)
// Checks if occupants can be irradiated/mutated - prevents exploits where wearing full rad protection would still let you gain mutations
/obj/machinery/dna_scannernew/proc/radiation_check()
@@ -333,12 +343,11 @@
var/injector_ready = FALSE //Quick fix for issue 286 (screwdriver the screen twice to restore injector) -Pete
var/obj/machinery/dna_scannernew/connected = null
var/obj/item/disk/data/disk = null
var/selected_menu_key = null
var/selected_menu_key = PAGE_UI
anchored = TRUE
use_power = IDLE_POWER_USE
idle_power_usage = 10
active_power_usage = 400
var/waiting_for_user_input = 0 // Fix for #274 (Mash create block injector without answering dialog to make unlimited injectors) - N3X
/obj/machinery/computer/scan_consolenew/attackby(obj/item/I, mob/user, params)
if(istype(I, /obj/item/disk/data)) //INSERT SOME diskS
@@ -347,7 +356,7 @@
I.forceMove(src)
disk = I
to_chat(user, "You insert [I].")
SSnanoui.update_uis(src) // update all UIs attached to src()
SStgui.update_uis(src)
return
else
return ..()
@@ -399,35 +408,18 @@
if(stat & (NOPOWER|BROKEN))
return
ui_interact(user)
tgui_interact(user)
/**
* The ui_interact proc is used to open and update Nano UIs
* If ui_interact is not used then the UI will not update correctly
* ui_interact is currently defined for /atom/movable
*
* @param user /mob The mob who is interacting with this ui
* @param ui_key string A string key to use for this ui. Allows for multiple unique uis on one obj/mob (defaut value "main")
* @param ui /datum/nanoui This parameter is passed by the nanoui process() proc when updating an open ui
*
* @return nothing
*/
/obj/machinery/computer/scan_consolenew/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1)
/obj/machinery/computer/scan_consolenew/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state)
if(user == connected.occupant)
return
// update the ui if it exists, returns null if no ui is passed/found
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
// the ui does not exist, so we'll create a new() one
// for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm
ui = new(user, src, ui_key, "dna_modifier.tmpl", "DNA Modifier Console", 660, 700)
// open the new ui window
ui = new(user, src, ui_key, "DNAModifier", name, 660, 700, master_ui, state)
ui.open()
// auto update every Master Controller tick
ui.set_auto_update(1)
/obj/machinery/computer/scan_consolenew/ui_data(mob/user, datum/topic_state/state)
/obj/machinery/computer/scan_consolenew/tgui_data(mob/user)
var/data[0]
data["selectedMenuKey"] = selected_menu_key
data["locked"] = connected.locked
@@ -501,9 +493,12 @@
for(var/datum/reagent/R in connected.beaker.reagents.reagent_list)
data["beakerVolume"] += R.volume
// Transfer modal information if there is one
data["modal"] = tgui_modal_data(src)
return data
/obj/machinery/computer/scan_consolenew/Topic(href, href_list)
/obj/machinery/computer/scan_consolenew/tgui_act(action, params)
if(..())
return FALSE // don't update uis
if(!istype(usr.loc, /turf))
@@ -512,405 +507,350 @@
return FALSE // don't update uis
if(irradiating) // Make sure that it isn't already irradiating someone...
return FALSE // don't update uis
if(stat & (NOPOWER|BROKEN))
return
add_fingerprint(usr)
if(href_list["selectMenuKey"])
selected_menu_key = href_list["selectMenuKey"]
return TRUE // return 1 forces an update to all Nano uis attached to src
if(href_list["toggleLock"])
if((connected && connected.occupant))
connected.locked = !(connected.locked)
return TRUE // return 1 forces an update to all Nano uis attached to src
if(href_list["pulseRadiation"])
irradiating = radiation_duration
var/lock_state = connected.locked
connected.locked = TRUE //lock it
SSnanoui.update_uis(src) // update all UIs attached to src
sleep(10 * radiation_duration) // sleep for radiation_duration seconds
irradiating = 0
connected.locked = lock_state
if(!connected.occupant)
return TRUE // return 1 forces an update to all Nano uis attached to src
var/radiation = (((radiation_intensity * 3) + radiation_duration * 3) / connected.damage_coeff)
connected.occupant.apply_effect(radiation, IRRADIATE, 0)
if(connected.radiation_check())
return TRUE
if(prob(95))
if(prob(75))
randmutb(connected.occupant)
else
randmuti(connected.occupant)
else
if(prob(95))
randmutg(connected.occupant)
else
randmuti(connected.occupant)
return TRUE // return 1 forces an update to all Nano uis attached to src
if(href_list["radiationDuration"])
if(text2num(href_list["radiationDuration"]) > 0)
if(radiation_duration < 20)
radiation_duration += 2
else
if(radiation_duration > 2)
radiation_duration -= 2
return TRUE // return 1 forces an update to all Nano uis attached to src
if(href_list["radiationIntensity"])
if(text2num(href_list["radiationIntensity"]) > 0)
if(radiation_intensity < 10)
radiation_intensity++
else
if(radiation_intensity > 1)
radiation_intensity--
return TRUE // return 1 forces an update to all Nano uis attached to src
////////////////////////////////////////////////////////
if(href_list["changeUITarget"] && text2num(href_list["changeUITarget"]) > 0)
if(selected_ui_target < 15)
selected_ui_target++
selected_ui_target_hex = selected_ui_target
switch(selected_ui_target)
if(10)
selected_ui_target_hex = "A"
if(11)
selected_ui_target_hex = "B"
if(12)
selected_ui_target_hex = "C"
if(13)
selected_ui_target_hex = "D"
if(14)
selected_ui_target_hex = "E"
if(15)
selected_ui_target_hex = "F"
else
selected_ui_target = 0
selected_ui_target_hex = 0
return TRUE // return 1 forces an update to all Nano uis attached to src
if(href_list["changeUITarget"] && text2num(href_list["changeUITarget"]) < 1)
if(selected_ui_target > 0)
selected_ui_target--
selected_ui_target_hex = selected_ui_target
switch(selected_ui_target)
if(10)
selected_ui_target_hex = "A"
if(11)
selected_ui_target_hex = "B"
if(12)
selected_ui_target_hex = "C"
if(13)
selected_ui_target_hex = "D"
if(14)
selected_ui_target_hex = "E"
else
selected_ui_target = 15
selected_ui_target_hex = "F"
return TRUE // return 1 forces an update to all Nano uis attached to src
if(href_list["selectUIBlock"] && href_list["selectUISubblock"]) // This chunk of code updates selected block / sub-block based on click
var/select_block = text2num(href_list["selectUIBlock"])
var/select_subblock = text2num(href_list["selectUISubblock"])
if((select_block <= DNA_UI_LENGTH) && (select_block >= 1))
selected_ui_block = select_block
if((select_subblock <= DNA_BLOCK_SIZE) && (select_subblock >= 1))
selected_ui_subblock = select_subblock
return TRUE // return 1 forces an update to all Nano uis attached to src
if(href_list["pulseUIRadiation"])
var/block = connected.occupant.dna.GetUISubBlock(selected_ui_block, selected_ui_subblock)
irradiating = radiation_duration
var/lock_state = connected.locked
connected.locked = TRUE //lock it
SSnanoui.update_uis(src) // update all UIs attached to src
sleep(10 * radiation_duration) // sleep for radiation_duration seconds
irradiating = 0
connected.locked = lock_state
if(!connected.occupant)
return TRUE
if(prob((80 + (radiation_duration / 2))))
var/radiation = (radiation_intensity + radiation_duration)
connected.occupant.apply_effect(radiation,IRRADIATE,0)
if(connected.radiation_check())
return TRUE
block = miniscrambletarget(num2text(selected_ui_target), radiation_intensity, radiation_duration)
connected.occupant.dna.SetUISubBlock(selected_ui_block, selected_ui_subblock, block)
connected.occupant.UpdateAppearance()
else
var/radiation = ((radiation_intensity * 2) + radiation_duration)
connected.occupant.apply_effect(radiation, IRRADIATE, 0)
if(connected.radiation_check())
return TRUE
if(prob(20 + radiation_intensity))
randmutb(connected.occupant)
domutcheck(connected.occupant, connected)
else
randmuti(connected.occupant)
connected.occupant.UpdateAppearance()
return TRUE // return 1 forces an update to all Nano uis attached to src
////////////////////////////////////////////////////////
if(href_list["injectRejuvenators"])
if(!connected.occupant)
return FALSE
var/inject_amount = round(text2num(href_list["injectRejuvenators"]), 5) // round to nearest 5
if(inject_amount < 0) // Since the user can actually type the commands himself, some sanity checking
inject_amount = 0
if(inject_amount > 50)
inject_amount = 50
connected.beaker.reagents.trans_to(connected.occupant, inject_amount)
connected.beaker.reagents.reaction(connected.occupant)
return TRUE // return 1 forces an update to all Nano uis attached to src
////////////////////////////////////////////////////////
if(href_list["selectSEBlock"] && href_list["selectSESubblock"]) // This chunk of code updates selected block / sub-block based on click (se stands for strutural enzymes)
var/select_block = text2num(href_list["selectSEBlock"])
var/select_subblock = text2num(href_list["selectSESubblock"])
if((select_block <= DNA_SE_LENGTH) && (select_block >= 1))
selected_se_block = select_block
if((select_subblock <= DNA_BLOCK_SIZE) && (select_subblock >= 1))
selected_se_subblock = select_subblock
//testing("User selected block [selected_se_block] (sent [select_block]), subblock [selected_se_subblock] (sent [select_block]).")
return TRUE // return 1 forces an update to all Nano uis attached to src
if(href_list["pulseSERadiation"])
var/block = connected.occupant.dna.GetSESubBlock(selected_se_block, selected_se_subblock)
//var/original_block=block
//testing("Irradiating SE block [selected_se_block]:[selected_se_subblock] ([block])...")
irradiating = radiation_duration
var/lock_state = connected.locked
connected.locked = TRUE //lock it
SSnanoui.update_uis(src) // update all UIs attached to src
sleep(10 * radiation_duration) // sleep for radiation_duration seconds
irradiating = 0
connected.locked = lock_state
if(connected.occupant)
if(prob((80 + ((radiation_duration / 2) + (connected.precision_coeff ** 3)))))
var/radiation = ((radiation_intensity + radiation_duration) / connected.damage_coeff)
connected.occupant.apply_effect(radiation, IRRADIATE, 0)
if(connected.radiation_check())
return 1
var/real_SE_block=selected_se_block
block = miniscramble(block, radiation_intensity, radiation_duration)
if(prob(20))
if(selected_se_block > 1 && selected_se_block < DNA_SE_LENGTH/2)
real_SE_block++
else if(selected_se_block > DNA_SE_LENGTH/2 && selected_se_block < DNA_SE_LENGTH)
real_SE_block--
//testing("Irradiated SE block [real_SE_block]:[selected_se_subblock] ([original_block] now [block]) [(real_SE_block!=selected_se_block) ? "(SHIFTED)":""]!")
connected.occupant.dna.SetSESubBlock(real_SE_block, selected_se_subblock, block)
domutcheck(connected.occupant, connected)
else
var/radiation = (((radiation_intensity * 2) + radiation_duration) / connected.damage_coeff)
connected.occupant.apply_effect(radiation, IRRADIATE, 0)
if(connected.radiation_check())
return 1
if(prob(80 - radiation_duration))
//testing("Random bad mut!")
randmutb(connected.occupant)
domutcheck(connected.occupant, connected)
else
randmuti(connected.occupant)
//testing("Random identity mut!")
connected.occupant.UpdateAppearance()
return TRUE // return 1 forces an update to all Nano uis attached to src
if(href_list["ejectBeaker"])
if(connected.beaker)
var/obj/item/reagent_containers/glass/B = connected.beaker
B.forceMove(connected.loc)
connected.beaker = null
if(tgui_act_modal(action, params))
return TRUE
if(href_list["ejectOccupant"])
connected.eject_occupant(usr)
return TRUE
// Transfer Buffer Management
if(href_list["bufferOption"])
var/bufferOption = href_list["bufferOption"]
// These bufferOptions do not require a bufferId
if(bufferOption == "wipeDisk")
if((isnull(disk)) || (disk.read_only))
//temphtml = "Invalid disk. Please try again."
return FALSE
disk.buf = null
//temphtml = "Data saved."
return TRUE
if(bufferOption == "ejectDisk")
if(!disk)
. = TRUE
switch(action)
if("selectMenuKey")
var/key = params["key"]
if(!(key in list(PAGE_UI, PAGE_SE, PAGE_BUFFER, PAGE_REJUVENATORS)))
return
disk.forceMove(get_turf(src))
disk = null
return TRUE
// All bufferOptions from here on require a bufferId
if(!href_list["bufferId"])
return FALSE
var/bufferId = text2num(href_list["bufferId"])
if(bufferId < 1 || bufferId > 3)
return FALSE // Not a valid buffer id
if(bufferOption == "saveUI")
if(connected.occupant && connected.occupant.dna)
var/datum/dna2/record/databuf = new
databuf.types = DNA2_BUF_UI // DNA2_BUF_UE
databuf.dna = connected.occupant.dna.Clone()
if(ishuman(connected.occupant))
databuf.dna.real_name=connected.occupant.name
databuf.name = "Unique Identifier"
buffers[bufferId] = databuf
return TRUE
if(bufferOption == "saveUIAndUE")
if(connected.occupant && connected.occupant.dna)
var/datum/dna2/record/databuf = new
databuf.types = DNA2_BUF_UI|DNA2_BUF_UE
databuf.dna = connected.occupant.dna.Clone()
if(ishuman(connected.occupant))
databuf.dna.real_name=connected.occupant.dna.real_name
databuf.name = "Unique Identifier + Unique Enzymes"
buffers[bufferId] = databuf
return TRUE
if(bufferOption == "saveSE")
if(connected.occupant && connected.occupant.dna)
var/datum/dna2/record/databuf = new
databuf.types = DNA2_BUF_SE
databuf.dna = connected.occupant.dna.Clone()
if(ishuman(connected.occupant))
databuf.dna.real_name = connected.occupant.dna.real_name
databuf.name = "Structural Enzymes"
buffers[bufferId] = databuf
return TRUE
if(bufferOption == "clear")
buffers[bufferId] = new /datum/dna2/record()
return TRUE
if(bufferOption == "changeLabel")
var/datum/dna2/record/buf = buffers[bufferId]
var/text = sanitize(input(usr, "New Label:", "Edit Label", buf.name) as text|null)
buf.name = text
buffers[bufferId] = buf
return TRUE
if(bufferOption == "transfer")
if(!connected.occupant || (NOCLONE in connected.occupant.mutations && connected.scan_level < 3) || !connected.occupant.dna)
return TRUE
irradiating = 2
selected_menu_key = key
if("toggleLock")
if(connected && connected.occupant)
connected.locked = !(connected.locked)
if("pulseRadiation")
irradiating = radiation_duration
var/lock_state = connected.locked
connected.locked = TRUE //lock it
SSnanoui.update_uis(src) // update all UIs attached to src
sleep(2 SECONDS)
SStgui.update_uis(src)
sleep(10 * radiation_duration) // sleep for radiation_duration seconds
irradiating = 0
connected.locked = lock_state
var/radiation = (rand(20,50) / connected.damage_coeff)
if(!connected.occupant)
return
var/radiation = (((radiation_intensity * 3) + radiation_duration * 3) / connected.damage_coeff)
connected.occupant.apply_effect(radiation, IRRADIATE, 0)
if(connected.radiation_check())
return TRUE
return
var/datum/dna2/record/buf = buffers[bufferId]
if((buf.types & DNA2_BUF_UI))
if((buf.types & DNA2_BUF_UE))
connected.occupant.real_name = buf.dna.real_name
connected.occupant.name = buf.dna.real_name
connected.occupant.UpdateAppearance(buf.dna.UI.Copy())
else if(buf.types & DNA2_BUF_SE)
connected.occupant.dna.SE = buf.dna.SE.Copy()
connected.occupant.dna.UpdateSE()
domutcheck(connected.occupant, connected)
return TRUE
if(bufferOption == "createInjector")
if(injector_ready && !waiting_for_user_input)
var/success = 1
var/obj/item/dnainjector/I = new /obj/item/dnainjector
var/datum/dna2/record/buf = buffers[bufferId]
buf = buf.copy()
if(href_list["createBlockInjector"])
waiting_for_user_input=1
var/list/selectedbuf
if(buf.types & DNA2_BUF_SE)
selectedbuf=buf.dna.SE
else
selectedbuf=buf.dna.UI
var/blk = input(usr,"Select Block","Block") as null|anything in all_dna_blocks(selectedbuf)
success = setInjectorBlock(I,blk,buf)
if(prob(95))
if(prob(75))
randmutb(connected.occupant)
else
I.buf = buf
waiting_for_user_input = 0
if(success)
I.forceMove(loc)
I.name += " ([buf.name])"
if(connected)
I.damage_coeff = connected.damage_coeff
injector_ready = FALSE
spawn(300)
injector_ready = TRUE
return TRUE
randmuti(connected.occupant)
else
if(prob(95))
randmutg(connected.occupant)
else
randmuti(connected.occupant)
if("radiationDuration")
radiation_duration = clamp(text2num(params["value"]), 1, 20)
if("radiationIntensity")
radiation_intensity = clamp(text2num(params["value"]), 1, 10)
////////////////////////////////////////////////////////
if("changeUITarget")
selected_ui_target = clamp(text2num(params["value"]), 1, 15)
selected_ui_target_hex = num2text(selected_ui_target, 1, 16)
if("selectUIBlock") // This chunk of code updates selected block / sub-block based on click
var/select_block = text2num(params["block"])
var/select_subblock = text2num(params["subblock"])
if(!select_block || !select_subblock)
return
if(bufferOption == "loadDisk")
if((isnull(disk)) || (!disk.buf))
//temphtml = "Invalid disk. Please try again."
return FALSE
selected_ui_block = clamp(select_block, 1, DNA_UI_LENGTH)
selected_ui_subblock = clamp(select_subblock, 1, DNA_BLOCK_SIZE)
if("pulseUIRadiation")
var/block = connected.occupant.dna.GetUISubBlock(selected_ui_block, selected_ui_subblock)
buffers[bufferId] = disk.buf.copy()
//temphtml = "Data loaded."
return TRUE
irradiating = radiation_duration
var/lock_state = connected.locked
connected.locked = TRUE //lock it
if(bufferOption == "saveDisk")
if((isnull(disk)) || (disk.read_only))
//temphtml = "Invalid disk. Please try again."
return FALSE
SStgui.update_uis(src)
sleep(10 * radiation_duration) // sleep for radiation_duration seconds
var/datum/dna2/record/buf = buffers[bufferId]
irradiating = 0
connected.locked = lock_state
disk.buf = buf.copy()
disk.name = "data disk - '[buf.dna.real_name]'"
//temphtml = "Data saved."
return TRUE
if(!connected.occupant)
return
if(prob((80 + (radiation_duration / 2))))
var/radiation = (radiation_intensity + radiation_duration)
connected.occupant.apply_effect(radiation,IRRADIATE,0)
if(connected.radiation_check())
return
block = miniscrambletarget(num2text(selected_ui_target), radiation_intensity, radiation_duration)
connected.occupant.dna.SetUISubBlock(selected_ui_block, selected_ui_subblock, block)
connected.occupant.UpdateAppearance()
else
var/radiation = ((radiation_intensity * 2) + radiation_duration)
connected.occupant.apply_effect(radiation, IRRADIATE, 0)
if(connected.radiation_check())
return
if(prob(20 + radiation_intensity))
randmutb(connected.occupant)
domutcheck(connected.occupant, connected)
else
randmuti(connected.occupant)
connected.occupant.UpdateAppearance()
////////////////////////////////////////////////////////
if("injectRejuvenators")
if(!connected.occupant || !connected.beaker)
return
var/inject_amount = clamp(round(text2num(params["amount"]), 5), 0, 50) // round to nearest 5 and clamp to 0-50
if(!inject_amount)
return
connected.beaker.reagents.trans_to(connected.occupant, inject_amount)
connected.beaker.reagents.reaction(connected.occupant)
////////////////////////////////////////////////////////
if("selectSEBlock") // This chunk of code updates selected block / sub-block based on click (se stands for strutural enzymes)
var/select_block = text2num(params["block"])
var/select_subblock = text2num(params["subblock"])
if(!select_block || !select_subblock)
return
selected_se_block = clamp(select_block, 1, DNA_SE_LENGTH)
selected_se_subblock = clamp(select_subblock, 1, DNA_BLOCK_SIZE)
if("pulseSERadiation")
var/block = connected.occupant.dna.GetSESubBlock(selected_se_block, selected_se_subblock)
//var/original_block=block
//testing("Irradiating SE block [selected_se_block]:[selected_se_subblock] ([block])...")
irradiating = radiation_duration
var/lock_state = connected.locked
connected.locked = TRUE //lock it
SStgui.update_uis(src)
sleep(10 * radiation_duration) // sleep for radiation_duration seconds
irradiating = 0
connected.locked = lock_state
if(connected.occupant)
if(prob((80 + ((radiation_duration / 2) + (connected.precision_coeff ** 3)))))
var/radiation = ((radiation_intensity + radiation_duration) / connected.damage_coeff)
connected.occupant.apply_effect(radiation, IRRADIATE, 0)
if(connected.radiation_check())
return 1
var/real_SE_block=selected_se_block
block = miniscramble(block, radiation_intensity, radiation_duration)
if(prob(20))
if(selected_se_block > 1 && selected_se_block < DNA_SE_LENGTH/2)
real_SE_block++
else if(selected_se_block > DNA_SE_LENGTH/2 && selected_se_block < DNA_SE_LENGTH)
real_SE_block--
//testing("Irradiated SE block [real_SE_block]:[selected_se_subblock] ([original_block] now [block]) [(real_SE_block!=selected_se_block) ? "(SHIFTED)":""]!")
connected.occupant.dna.SetSESubBlock(real_SE_block, selected_se_subblock, block)
domutcheck(connected.occupant, connected)
else
var/radiation = (((radiation_intensity * 2) + radiation_duration) / connected.damage_coeff)
connected.occupant.apply_effect(radiation, IRRADIATE, 0)
if(connected.radiation_check())
return
if(prob(80 - radiation_duration))
//testing("Random bad mut!")
randmutb(connected.occupant)
domutcheck(connected.occupant, connected)
else
randmuti(connected.occupant)
//testing("Random identity mut!")
connected.occupant.UpdateAppearance()
if("ejectBeaker")
if(connected.beaker)
var/obj/item/reagent_containers/glass/B = connected.beaker
B.forceMove(connected.loc)
connected.beaker = null
if("ejectOccupant")
connected.eject_occupant()
// Transfer Buffer Management
if("bufferOption")
var/bufferOption = params["option"]
var/bufferId = text2num(params["id"])
if(bufferId < 1 || bufferId > 3) // Not a valid buffer id
return
var/datum/dna2/record/buffer = buffers[bufferId]
switch(bufferOption)
if("saveUI")
if(connected.occupant && connected.occupant.dna)
var/datum/dna2/record/databuf = new
databuf.types = DNA2_BUF_UI // DNA2_BUF_UE
databuf.dna = connected.occupant.dna.Clone()
if(ishuman(connected.occupant))
databuf.dna.real_name=connected.occupant.name
databuf.name = "Unique Identifier"
buffers[bufferId] = databuf
if("saveUIAndUE")
if(connected.occupant && connected.occupant.dna)
var/datum/dna2/record/databuf = new
databuf.types = DNA2_BUF_UI|DNA2_BUF_UE
databuf.dna = connected.occupant.dna.Clone()
if(ishuman(connected.occupant))
databuf.dna.real_name=connected.occupant.dna.real_name
databuf.name = "Unique Identifier + Unique Enzymes"
buffers[bufferId] = databuf
if("saveSE")
if(connected.occupant && connected.occupant.dna)
var/datum/dna2/record/databuf = new
databuf.types = DNA2_BUF_SE
databuf.dna = connected.occupant.dna.Clone()
if(ishuman(connected.occupant))
databuf.dna.real_name = connected.occupant.dna.real_name
databuf.name = "Structural Enzymes"
buffers[bufferId] = databuf
if("clear")
buffers[bufferId] = new /datum/dna2/record()
if("changeLabel")
tgui_modal_input(src, "changeBufferLabel", "Please enter the new buffer label:", null, list("id" = bufferId), buffer.name, TGUI_MODAL_INPUT_MAX_LENGTH_NAME)
if("transfer")
if(!connected.occupant || (NOCLONE in connected.occupant.mutations && connected.scan_level < 3) || !connected.occupant.dna)
return
irradiating = 2
var/lock_state = connected.locked
connected.locked = TRUE //lock it
SStgui.update_uis(src)
sleep(2 SECONDS)
irradiating = 0
connected.locked = lock_state
var/radiation = (rand(20,50) / connected.damage_coeff)
connected.occupant.apply_effect(radiation, IRRADIATE, 0)
if(connected.radiation_check())
return
var/datum/dna2/record/buf = buffers[bufferId]
if((buf.types & DNA2_BUF_UI))
if((buf.types & DNA2_BUF_UE))
connected.occupant.real_name = buf.dna.real_name
connected.occupant.name = buf.dna.real_name
connected.occupant.UpdateAppearance(buf.dna.UI.Copy())
else if(buf.types & DNA2_BUF_SE)
connected.occupant.dna.SE = buf.dna.SE.Copy()
connected.occupant.dna.UpdateSE()
domutcheck(connected.occupant, connected)
if("createInjector")
if(!injector_ready)
return
if(text2num(params["block"]) > 0)
var/list/choices = all_dna_blocks((buffer.types & DNA2_BUF_SE) ? buffer.dna.SE : buffer.dna.UI)
tgui_modal_choice(src, "createInjectorBlock", "Please select the block to create an injector from:", null, list("id" = bufferId), null, choices)
else
create_injector(bufferId, TRUE)
if("loadDisk")
if(isnull(disk) || disk.read_only)
return
buffers[bufferId] = disk.buf.copy()
if("saveDisk")
if(isnull(disk) || disk.read_only)
return
var/datum/dna2/record/buf = buffers[bufferId]
disk.buf = buf.copy()
disk.name = "data disk - '[buf.dna.real_name]'"
if("wipeDisk")
if(isnull(disk) || disk.read_only)
return
disk.buf = null
if("ejectDisk")
if(!disk)
return
disk.forceMove(get_turf(src))
disk = null
/**
* Creates a blank injector with the name of the buffer at the given buffer_id
*
* Arguments:
* * buffer_id - The ID of the buffer
* * copy_buffer - Whether the injector should copy the buffer contents
*/
/obj/machinery/computer/scan_consolenew/proc/create_injector(buffer_id, copy_buffer = FALSE)
if(buffer_id < 1 || buffer_id > length(buffers))
return
// Cooldown
injector_ready = FALSE
addtimer(CALLBACK(src, .proc/injector_cooldown_finish), 30 SECONDS)
// Create it
var/datum/dna2/record/buf = buffers[buffer_id]
var/obj/item/dnainjector/I = new()
I.forceMove(loc)
I.name += " ([buf.name])"
if(copy_buffer)
I.buf = buf.copy()
if(connected)
I.damage_coeff = connected.damage_coeff
return I
/**
* Called when the injector creation cooldown finishes
*/
/obj/machinery/computer/scan_consolenew/proc/injector_cooldown_finish()
injector_ready = TRUE
/**
* Called in tgui_act() to process modal actions
*
* Arguments:
* * action - The action passed by tgui
* * params - The params passed by tgui
*/
/obj/machinery/computer/scan_consolenew/proc/tgui_act_modal(action, params)
. = TRUE
var/id = params["id"] // The modal's ID
var/list/arguments = istext(params["arguments"]) ? json_decode(params["arguments"]) : params["arguments"]
switch(tgui_modal_act(src, action, params))
if(TGUI_MODAL_ANSWER)
var/answer = params["answer"]
switch(id)
if("createInjectorBlock")
var/buffer_id = text2num(arguments["id"])
if(buffer_id < 1 || buffer_id > length(buffers))
return
var/datum/dna2/record/buf = buffers[buffer_id]
var/obj/item/dnainjector/I = create_injector(buffer_id)
setInjectorBlock(I, answer, buf.copy())
if("changeBufferLabel")
var/buffer_id = text2num(arguments["id"])
if(buffer_id < 1 || buffer_id > length(buffers))
return
var/datum/dna2/record/buf = buffers[buffer_id]
buf.name = answer
buffers[buffer_id] = buf
else
return FALSE
else
return FALSE
#undef PAGE_UI
#undef PAGE_SE
#undef PAGE_BUFFER
#undef PAGE_REJUVENATORS
/////////////////////////// DNA MACHINES
+54 -43
View File
@@ -125,7 +125,7 @@
return attack_hand(user)
/obj/machinery/sleeper/attack_ghost(mob/user)
return attack_hand(user)
tgui_interact(user)
/obj/machinery/sleeper/attack_hand(mob/user)
if(stat & (NOPOWER|BROKEN))
@@ -135,17 +135,17 @@
to_chat(user, "<span class='notice'>Close the maintenance panel first.</span>")
return
ui_interact(user)
tgui_interact(user)
/obj/machinery/sleeper/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1)
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
/obj/machinery/sleeper/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "sleeper.tmpl", "Sleeper", 550, 770)
ui = new(user, src, ui_key, "Sleeper", "Sleeper", 550, 775)
ui.open()
ui.set_auto_update(1)
/obj/machinery/sleeper/ui_data(mob/user, datum/topic_state/state)
/obj/machinery/sleeper/tgui_data(mob/user)
var/data[0]
data["amounts"] = amounts
data["hasOccupant"] = occupant ? 1 : 0
var/occupantData[0]
var/crisis = 0
@@ -210,9 +210,13 @@
if(beaker)
data["isBeakerLoaded"] = 1
if(beaker.reagents)
data["beakerMaxSpace"] = beaker.reagents.maximum_volume
data["beakerFreeSpace"] = round(beaker.reagents.maximum_volume - beaker.reagents.total_volume)
else
data["beakerMaxSpace"] = 0
data["beakerFreeSpace"] = 0
else
data["isBeakerLoaded"] = FALSE
var/chemicals[0]
for(var/re in possible_chems)
@@ -234,51 +238,52 @@
if(temp.id in occupant.reagents.overdose_list())
overdosing = 1
// Because I don't know how to do this on the nano side
pretty_amount = round(reagent_amount, 0.05)
chemicals.Add(list(list("title" = temp.name, "id" = temp.id, "commands" = list("chemical" = temp.id), "occ_amount" = reagent_amount, "pretty_amount" = pretty_amount, "injectable" = injectable, "overdosing" = overdosing, "od_warning" = caution)))
data["chemicals"] = chemicals
return data
/obj/machinery/sleeper/Topic(href, href_list)
if(!controls_inside && usr == occupant)
return 0
/obj/machinery/sleeper/tgui_act(action, params)
if(..())
return 1
return
if(!controls_inside && usr == occupant)
return
if(panel_open)
to_chat(usr, "<span class='notice'>Close the maintenance panel first.</span>")
return 0
return
if(stat & (NOPOWER|BROKEN))
return
if((usr.contents.Find(src) || ((get_dist(src, usr) <= 1) && istype(loc, /turf))) || (istype(usr, /mob/living/silicon/ai)))
if(href_list["chemical"])
if(occupant)
if(occupant.stat == DEAD)
to_chat(usr, "<span class='danger'>This person has no life for to preserve anymore. Take [occupant.p_them()] to a department capable of reanimating them.</span>")
else if(occupant.health > min_health || (href_list["chemical"] in emergency_chems))
inject_chemical(usr,href_list["chemical"],text2num(href_list["amount"]))
else
to_chat(usr, "<span class='danger'>This person is not in good enough condition for sleepers to be effective! Use another means of treatment, such as cryogenics!</span>")
if(href_list["removebeaker"])
. = TRUE
switch(action)
if("chemical")
if(!occupant)
return
if(occupant.stat == DEAD)
to_chat(usr, "<span class='danger'>This person has no life to preserve anymore. Take [occupant.p_them()] to a department capable of reanimating them.</span>")
return
var/chemical = params["chemid"]
var/amount = text2num(params["amount"])
if(!length(chemical) || amount <= 0)
return
if(occupant.health > min_health || (chemical in emergency_chems))
inject_chemical(usr, chemical, amount)
else
to_chat(usr, "<span class='danger'>This person is not in good enough condition for sleepers to be effective! Use another means of treatment, such as cryogenics!</span>")
if("removebeaker")
remove_beaker()
if(href_list["togglefilter"])
if("togglefilter")
toggle_filter()
if(href_list["ejectify"])
if("ejectify")
eject()
if(href_list["auto_eject_dead_on"])
if("auto_eject_dead_on")
auto_eject_dead = TRUE
if(href_list["auto_eject_dead_off"])
if("auto_eject_dead_off")
auto_eject_dead = FALSE
add_fingerprint(usr)
return 1
else
return FALSE
add_fingerprint(usr)
/obj/machinery/sleeper/attackby(obj/item/I, mob/user, params)
if(istype(I, /obj/item/reagent_containers/glass))
@@ -290,6 +295,7 @@
beaker = I
I.forceMove(src)
user.visible_message("[user] adds \a [I] to [src]!", "You add \a [I] to [src]!")
SStgui.update_uis(src)
return
else
@@ -328,6 +334,7 @@
to_chat(M, "<span class='boldnotice'>You feel cool air surround you. You go numb as your senses turn inward.</span>")
add_fingerprint(user)
qdel(G)
SStgui.update_uis(src)
return
return ..()
@@ -374,9 +381,11 @@
occupant = null
updateUsrDialog()
update_icon()
SStgui.update_uis(src)
if(A == beaker)
beaker = null
updateUsrDialog()
SStgui.update_uis(src)
/obj/machinery/sleeper/emp_act(severity)
if(filtering)
@@ -394,7 +403,7 @@
qdel(src)
/obj/machinery/sleeper/proc/toggle_filter()
if(filtering)
if(filtering || !beaker)
filtering = 0
else
filtering = 1
@@ -410,29 +419,28 @@
// eject trash the occupant dropped
for(var/atom/movable/A in contents - component_parts - list(beaker))
A.forceMove(loc)
SStgui.update_uis(src)
/obj/machinery/sleeper/force_eject_occupant()
go_out()
/obj/machinery/sleeper/proc/inject_chemical(mob/living/user as mob, chemical, amount)
/obj/machinery/sleeper/proc/inject_chemical(mob/living/user, chemical, amount)
if(!(chemical in possible_chems))
to_chat(user, "<span class='notice'>The sleeper does not offer that chemical!</span>")
return
if(!(amount in amounts))
return
if(occupant)
if(occupant.reagents)
if(occupant.reagents.get_reagent_amount(chemical) + amount <= max_chem)
occupant.reagents.add_reagent(chemical, amount)
return
else
to_chat(user, "You can not inject any more of this chemical.")
return
else
to_chat(user, "The patient rejects the chemicals!")
return
else
to_chat(user, "There's no occupant in the sleeper!")
return
/obj/machinery/sleeper/verb/eject()
set name = "Eject Sleeper"
@@ -459,6 +467,7 @@
filtering = 0
beaker.forceMove(usr.loc)
beaker = null
SStgui.update_uis(src)
add_fingerprint(usr)
return
@@ -511,6 +520,7 @@
add_fingerprint(user)
if(user.pulling == L)
user.stop_pulling()
SStgui.update_uis(src)
return
return
@@ -547,6 +557,7 @@
for(var/obj/O in src)
qdel(O)
add_fingerprint(usr)
SStgui.update_uis(src)
return
return
+38 -30
View File
@@ -65,6 +65,7 @@
icon_state = "body_scanner_1"
add_fingerprint(user)
qdel(TYPECAST_YOUR_SHIT)
SStgui.update_uis(src)
return
return ..()
@@ -127,12 +128,13 @@
occupant = H
icon_state = "bodyscanner"
add_fingerprint(user)
SStgui.update_uis(src)
/obj/machinery/bodyscanner/attack_ai(user)
return attack_hand(user)
/obj/machinery/bodyscanner/attack_ghost(user)
return attack_hand(user)
tgui_interact(user)
/obj/machinery/bodyscanner/attack_hand(user)
if(stat & (NOPOWER|BROKEN))
@@ -145,7 +147,7 @@
to_chat(user, "<span class='notice'>Close the maintenance panel first.</span>")
return
ui_interact(user)
tgui_interact(user)
/obj/machinery/bodyscanner/relaymove(mob/user)
if(user.incapacitated())
@@ -171,6 +173,7 @@
// eject trash the occupant dropped
for(var/atom/movable/A in contents - component_parts)
A.forceMove(loc)
SStgui.update_uis(src)
/obj/machinery/bodyscanner/force_eject_occupant()
go_out()
@@ -192,17 +195,16 @@
new /obj/effect/gibspawner/generic(get_turf(loc)) //I REPLACE YOUR TECHNOLOGY WITH FLESH!
qdel(src)
/obj/machinery/bodyscanner/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1)
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
/obj/machinery/bodyscanner/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "adv_med.tmpl", "Body Scanner", 690, 600)
ui = new(user, src, ui_key, "BodyScanner", "Body Scanner", 690, 600)
ui.open()
ui.set_auto_update(1)
/obj/machinery/bodyscanner/ui_data(mob/user, datum/topic_state/state)
/obj/machinery/bodyscanner/tgui_data(mob/user)
var/data[0]
data["occupied"] = occupant ? 1 : 0
data["occupied"] = occupant ? TRUE : FALSE
var/occupantData[0]
if(occupant)
@@ -236,9 +238,9 @@
occupantData["hasBorer"] = occupant.has_brain_worms()
var/bloodData[0]
bloodData["hasBlood"] = 0
bloodData["hasBlood"] = FALSE
if(!(NO_BLOOD in occupant.dna.species.species_traits))
bloodData["hasBlood"] = 1
bloodData["hasBlood"] = TRUE
bloodData["volume"] = occupant.blood_volume
bloodData["percent"] = round(((occupant.blood_volume / BLOOD_VOLUME_NORMAL)*100))
bloodData["pulse"] = occupant.get_pulse(GETPULSE_TOOL)
@@ -282,19 +284,19 @@
if(E.status & ORGAN_BROKEN)
organStatus["broken"] = E.broken_description
if(E.is_robotic())
organStatus["robotic"] = 1
organStatus["robotic"] = TRUE
if(E.status & ORGAN_SPLINTED)
organStatus["splinted"] = 1
organStatus["splinted"] = TRUE
if(E.status & ORGAN_DEAD)
organStatus["dead"] = 1
organStatus["dead"] = TRUE
organData["status"] = organStatus
if(istype(E, /obj/item/organ/external/chest) && occupant.is_lung_ruptured())
organData["lungRuptured"] = 1
organData["lungRuptured"] = TRUE
if(E.internal_bleeding)
organData["internalBleeding"] = 1
organData["internalBleeding"] = TRUE
extOrganData.Add(list(organData))
@@ -319,27 +321,33 @@
occupantData["blind"] = (BLINDNESS in occupant.mutations)
occupantData["colourblind"] = (COLOURBLIND in occupant.mutations)
occupantData["nearsighted"] = (NEARSIGHTED in occupant.mutations)
occupantData["nearsighted"] = (NEARSIGHTED in occupant.mutations)
data["occupant"] = occupantData
return data
/obj/machinery/bodyscanner/Topic(href, href_list)
/obj/machinery/bodyscanner/tgui_act(action, params)
if(..())
return 1
return
if(stat & (NOPOWER|BROKEN))
return
if(href_list["ejectify"])
eject()
if(href_list["print_p"])
visible_message("<span class='notice'>[src] rattles and prints out a sheet of paper.</span>")
var/obj/item/paper/P = new /obj/item/paper(loc)
playsound(loc, 'sound/goonstation/machines/printer_dotmatrix.ogg', 50, 1)
P.info = "<CENTER><B>Body Scan - [href_list["name"]]</B></CENTER><BR>"
P.info += "<b>Time of scan:</b> [station_time_timestamp()]<br><br>"
P.info += "[generate_printing_text()]"
P.info += "<br><br><b>Notes:</b><br>"
P.name = "Body Scan - [href_list["name"]]"
. = TRUE
switch(action)
if("ejectify")
eject()
if("print_p")
visible_message("<span class='notice'>[src] rattles and prints out a sheet of paper.</span>")
var/obj/item/paper/P = new /obj/item/paper(loc)
playsound(loc, 'sound/goonstation/machines/printer_dotmatrix.ogg', 50, TRUE)
var/name = occupant ? occupant.name : "Unknown"
P.info = "<CENTER><B>Body Scan - [name]</B></CENTER><BR>"
P.info += "<b>Time of scan:</b> [station_time_timestamp()]<br><br>"
P.info += "[generate_printing_text()]"
P.info += "<br><br><b>Notes:</b><br>"
P.name = "Body Scan - [name]"
else
return FALSE
/obj/machinery/bodyscanner/proc/generate_printing_text()
var/dat = ""
+40 -36
View File
@@ -40,8 +40,7 @@
add_fingerprint(user)
if(stat & (BROKEN|NOPOWER))
return
ui_interact(user)
tgui_interact(user)
/obj/machinery/computer/operating/attack_hand(mob/user)
if(..(user))
@@ -52,16 +51,15 @@
add_fingerprint(user)
ui_interact(user)
tgui_interact(user)
/obj/machinery/computer/operating/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)//ui is mostly copy pasta from the sleeper ui
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
/obj/machinery/computer/operating/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "op_computer.tmpl", "Patient Monitor", 650, 455)
ui = new(user, src, ui_key, "OperatingComputer", "Patient Monitor", 650, 455, master_ui, state)
ui.open()
ui.set_auto_update(1)
/obj/machinery/computer/operating/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state)
/obj/machinery/computer/operating/tgui_data(mob/user)
var/data[0]
var/mob/living/carbon/human/occupant
if(table)
@@ -136,38 +134,43 @@
return data
/obj/machinery/computer/operating/Topic(href, href_list)
/obj/machinery/computer/operating/tgui_act(action, params)
if(..())
return 1
return
if(stat & (NOPOWER|BROKEN))
return
if((usr.contents.Find(src) || (in_range(src, usr) && istype(src.loc, /turf))) || (istype(usr, /mob/living/silicon)))
usr.set_machine(src)
if(href_list["verboseOn"])
verbose=1
if(href_list["verboseOff"])
verbose=0
if(href_list["healthOn"])
healthAnnounce=1
if(href_list["healthOff"])
healthAnnounce=0
if(href_list["critOn"])
crit=1
if(href_list["critOff"])
crit=0
if(href_list["oxyOn"])
oxy=1
if(href_list["oxyOff"])
oxy=0
if(href_list["oxy_adj"]!=0)
oxyAlarm=oxyAlarm+text2num(href_list["oxy_adj"])
if(href_list["choiceOn"])
choice=1
if(href_list["choiceOff"])
choice=0
if(href_list["health_adj"]!=0)
healthAlarm=healthAlarm+text2num(href_list["health_adj"])
return
. = TRUE
switch(action)
if("verboseOn")
verbose = TRUE
if("verboseOff")
verbose = FALSE
if("healthOn")
healthAnnounce = TRUE
if("healthOff")
healthAnnounce = FALSE
if("critOn")
crit = TRUE
if("critOff")
crit = FALSE
if("oxyOn")
oxy = TRUE
if("oxyOff")
oxy = FALSE
if("oxy_adj")
oxyAlarm = clamp(text2num(params["new"]), -100, 100)
if("choiceOn")
choice = TRUE
if("choiceOff")
choice = FALSE
if("health_adj")
healthAlarm = clamp(text2num(params["new"]), -100, 100)
else
return FALSE
/obj/machinery/computer/operating/process()
@@ -178,6 +181,7 @@
atom_say("New patient detected, loading stats")
victim = table.victim
atom_say("[victim.real_name], [victim.dna.blood_type] blood, [victim.stat ? "Non-Responsive" : "Awake"]")
SStgui.update_uis(src)
if(nextTick < world.time)
nextTick=world.time + OP_COMPUTER_COOLDOWN
if(crit && victim.health <= -50 )
+247 -199
View File
@@ -1,3 +1,6 @@
#define MENU_MAIN 1
#define MENU_RECORDS 2
/obj/machinery/computer/cloning
name = "cloning console"
icon = 'icons/obj/computer.dmi'
@@ -6,11 +9,11 @@
circuit = /obj/item/circuitboard/cloning
req_access = list(ACCESS_HEADS) //Only used for record deletion right now.
var/obj/machinery/dna_scannernew/scanner = null //Linked scanner. For scanning.
var/list/pods = list() //Linked cloning pods.
var/temp = ""
var/scantemp = "Scanner ready."
var/menu = 1 //Which menu screen to display
var/list/records = list()
var/list/pods = null //Linked cloning pods.
var/list/temp = null
var/list/scantemp = null
var/menu = MENU_MAIN //Which menu screen to display
var/list/records = null
var/datum/dna2/record/active_record = null
var/obj/item/disk/data/diskette = null //Mostly so the geneticist can steal everything.
var/loading = 0 // Nice loading text
@@ -24,6 +27,9 @@
/obj/machinery/computer/cloning/Initialize()
..()
pods = list()
records = list()
set_scan_temp("Scanner ready.", "good")
updatemodules()
/obj/machinery/computer/cloning/Destroy()
@@ -91,7 +97,7 @@
W.loc = src
src.diskette = W
to_chat(user, "You insert [W].")
SSnanoui.update_uis(src)
SStgui.update_uis(src)
return
else if(istype(W, /obj/item/multitool))
var/obj/item/multitool/M = W
@@ -117,19 +123,21 @@
return
updatemodules()
ui_interact(user)
tgui_interact(user)
/obj/machinery/computer/cloning/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
/obj/machinery/computer/cloning/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state)
if(stat & (NOPOWER|BROKEN))
return
// Set up the Nano UI
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
var/datum/asset/cloning/assets = get_asset_datum(/datum/asset/cloning)
assets.send(user)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "cloning_console.tmpl", "Cloning Console UI", 640, 520)
ui = new(user, src, ui_key, "CloningConsole", "Cloning Console", 640, 520)
ui.open()
/obj/machinery/computer/cloning/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state)
/obj/machinery/computer/cloning/tgui_data(mob/user)
var/data[0]
data["menu"] = src.menu
data["scanner"] = sanitize("[src.scanner]")
@@ -143,7 +151,18 @@
if(pod.efficiency > 5)
canpodautoprocess = 1
tempods.Add(list(list("pod" = "\ref[pod]", "name" = sanitize(capitalize(pod.name)), "biomass" = pod.biomass)))
var/status = "idle"
if(pod.mess)
status = "mess"
else if(pod.occupant && !(pod.stat & NOPOWER))
status = "cloning"
tempods.Add(list(list(
"pod" = "\ref[pod]",
"name" = sanitize(capitalize(pod.name)),
"biomass" = pod.biomass,
"status" = status,
"progress" = (pod.occupant && pod.occupant.stat != DEAD) ? pod.get_completion() : 0
)))
data["pods"] = tempods
data["loading"] = loading
@@ -168,188 +187,190 @@
temprecords.Add(list(list("record" = "\ref[R]", "realname" = sanitize(tempRealName))))
data["records"] = temprecords
if(src.menu == 3)
if(src.active_record)
data["activerecord"] = "\ref[src.active_record]"
var/obj/item/implant/health/H = null
if(src.active_record.implant)
H = locate(src.active_record.implant)
if(selected_pod && (selected_pod in pods) && selected_pod.biomass >= CLONE_BIOMASS)
data["podready"] = 1
else
data["podready"] = 0
if((H) && (istype(H)))
data["health"] = H.sensehealth()
data["realname"] = sanitize(src.active_record.dna.real_name)
data["unidentity"] = src.active_record.dna.uni_identity
data["strucenzymes"] = src.active_record.dna.struc_enzymes
if(selected_pod && (selected_pod in pods) && selected_pod.biomass >= CLONE_BIOMASS)
data["podready"] = 1
else
data["podready"] = 0
data["modal"] = tgui_modal_data(src)
return data
/obj/machinery/computer/cloning/Topic(href, href_list)
/obj/machinery/computer/cloning/tgui_act(action, params)
if(..())
return 1
if(loading)
return
if(stat & (NOPOWER|BROKEN))
return
if(href_list["scan"] && scanner && scanner.occupant)
scantemp = "Scanner ready."
loading = 1
spawn(20)
if(can_brainscan() && scan_mode)
scan_mob(scanner.occupant, scan_brain = 1)
else
scan_mob(scanner.occupant)
loading = 0
SSnanoui.update_uis(src)
if(href_list["task"])
switch(href_list["task"])
if("autoprocess")
autoprocess = 1
SSnanoui.update_uis(src)
if("stopautoprocess")
autoprocess = 0
SSnanoui.update_uis(src)
//No locking an open scanner.
else if((href_list["lock"]) && (!isnull(src.scanner)))
if((!src.scanner.locked) && (src.scanner.occupant))
src.scanner.locked = 1
else
src.scanner.locked = 0
else if(href_list["view_rec"])
src.active_record = locate(href_list["view_rec"])
if(istype(src.active_record,/datum/dna2/record))
if((isnull(src.active_record.ckey)))
qdel(src.active_record)
src.temp = "<span class=\"bad\">Error: Record corrupt.</span>"
else
src.menu = 3
else
src.active_record = null
src.temp = "<span class=\"bad\">Error: Record missing.</span>"
else if(href_list["del_rec"])
if((!src.active_record) || (src.menu < 3))
return
if(src.menu == 3) //If we are viewing a record, confirm deletion
src.temp = "Please confirm that you want to delete the record?"
src.menu = 4
else if(src.menu == 4)
var/obj/item/card/id/C = usr.get_active_hand()
if(istype(C)||istype(C, /obj/item/pda))
if(src.check_access(C))
src.records.Remove(src.active_record)
qdel(src.active_record)
src.temp = "Record deleted."
src.menu = 2
. = TRUE
switch(tgui_modal_act(src, action, params))
if(TGUI_MODAL_ANSWER)
if(params["id"] == "del_rec" && active_record)
var/obj/item/card/id/C = usr.get_active_hand()
if(!istype(C) && !istype(C, /obj/item/pda))
set_temp("ID not in hand.", "danger")
return
if(check_access(C))
records.Remove(active_record)
qdel(active_record)
set_temp("Record deleted.", "success")
menu = MENU_RECORDS
else
src.temp = "<span class=\"bad\">Error: Access denied.</span>"
else if(href_list["disk"]) //Load or eject.
switch(href_list["disk"])
if("load")
if((isnull(src.diskette)) || isnull(src.diskette.buf))
src.temp = "<span class=\"bad\">Error: The disk's data could not be read.</span>"
SSnanoui.update_uis(src)
return
if(isnull(src.active_record))
src.temp = "<span class=\"bad\">Error: No active record was found.</span>"
src.menu = 1
SSnanoui.update_uis(src)
return
src.active_record = src.diskette.buf.copy()
src.temp = "Load successful."
if("eject")
if(!isnull(src.diskette))
src.diskette.loc = src.loc
src.diskette = null
else if(href_list["save_disk"]) //Save to disk!
if((isnull(src.diskette)) || (src.diskette.read_only) || (isnull(src.active_record)))
src.temp = "<span class=\"bad\">Error: The data could not be saved.</span>"
SSnanoui.update_uis(src)
set_temp("Access denied.", "danger")
return
// DNA2 makes things a little simpler.
src.diskette.buf=src.active_record.copy()
src.diskette.buf.types=0
switch(href_list["save_disk"]) //Save as Ui/Ui+Ue/Se
if("ui")
src.diskette.buf.types=DNA2_BUF_UI
if("ue")
src.diskette.buf.types=DNA2_BUF_UI|DNA2_BUF_UE
if("se")
src.diskette.buf.types=DNA2_BUF_SE
src.diskette.name = "data disk - '[src.active_record.dna.real_name]'"
src.temp = "Save \[[href_list["save_disk"]]\] successful."
switch(action)
if("scan")
if(!scanner || !scanner.occupant || loading)
return
set_scan_temp("Scanner ready.", "good")
loading = TRUE
else if(href_list["refresh"])
SSnanoui.update_uis(src)
else if(href_list["selectpod"])
var/obj/machinery/clonepod/selected = locate(href_list["selectpod"])
if(istype(selected) && (selected in pods))
selected_pod = selected
else if(href_list["clone"])
var/datum/dna2/record/C = locate(href_list["clone"])
//Look for that player! They better be dead!
if(istype(C))
//Can't clone without someone to clone. Or a pod. Or if the pod is busy. Or full of gibs.
if(!pods.len)
temp = "<span class=\"bad\">Error: No cloning pod detected.</span>"
else
var/obj/machinery/clonepod/pod = selected_pod
var/cloneresult
if(!selected_pod)
temp = "<span class=\"bad\">Error: No cloning pod selected.</span>"
else if(pod.occupant)
temp = "<span class=\"bad\">Error: The cloning pod is currently occupied.</span>"
else if(pod.biomass < CLONE_BIOMASS)
temp = "<span class=\"bad\">Error: Not enough biomass.</span>"
else if(pod.mess)
temp = "<span class=\"bad\">Error: The cloning pod is malfunctioning.</span>"
else if(!config.revival_cloning)
temp = "<span class=\"bad\">Error: Unable to initiate cloning cycle.</span>"
spawn(20)
if(can_brainscan() && scan_mode)
scan_mob(scanner.occupant, scan_brain = TRUE)
else
cloneresult = pod.growclone(C)
if(cloneresult)
if(cloneresult > 0)
temp = "Initiating cloning cycle..."
records.Remove(C)
qdel(C)
menu = 1
scan_mob(scanner.occupant)
loading = FALSE
SStgui.update_uis(src)
if("autoprocess")
autoprocess = text2num(params["on"]) > 0
if("lock")
if(isnull(scanner) || !scanner.occupant) //No locking an open scanner.
return
scanner.locked = !scanner.locked
if("view_rec")
var/ref = params["ref"]
if(!length(ref))
return
active_record = locate(ref)
if(istype(active_record))
if(isnull(active_record.ckey))
qdel(active_record)
set_temp("Error: Record corrupt.", "danger")
else
var/obj/item/implant/health/H = null
if(active_record.implant)
H = locate(active_record.implant)
var/list/payload = list(
activerecord = "\ref[active_record]",
health = (H && istype(H)) ? H.sensehealth() : "",
realname = sanitize(active_record.dna.real_name),
unidentity = active_record.dna.uni_identity,
strucenzymes = active_record.dna.struc_enzymes,
)
tgui_modal_message(src, action, "", null, payload)
else
active_record = null
set_temp("Error: Record missing.", "danger")
if("del_rec")
if(!active_record)
return
tgui_modal_boolean(src, action, "Please confirm that you want to delete the record by holding your ID and pressing Delete:", yes_text = "Delete", no_text = "Cancel")
if("disk") // Disk management.
if(!length(params["option"]))
return
switch(params["option"])
if("load")
if(isnull(diskette) || isnull(diskette.buf))
set_temp("Error: The disk's data could not be read.", "danger")
return
else if(isnull(active_record))
set_temp("Error: No active record was found.", "danger")
menu = MENU_MAIN
return
active_record = diskette.buf.copy()
set_temp("Successfully loaded from disk.", "success")
if("save")
if(isnull(diskette) || diskette.read_only || isnull(active_record))
set_temp("Error: The data could not be saved.", "danger")
return
// DNA2 makes things a little simpler.
var/types
switch(params["savetype"]) // Save as Ui/Ui+Ue/Se
if("ui")
types = DNA2_BUF_UI
if("ue")
types = DNA2_BUF_UI|DNA2_BUF_UE
if("se")
types = DNA2_BUF_SE
else
set_temp("Error: Invalid save format.", "danger")
return
diskette.buf = active_record.copy()
diskette.buf.types = types
diskette.name = "data disk - '[active_record.dna.real_name]'"
set_temp("Successfully saved to disk.", "success")
if("eject")
if(!isnull(diskette))
diskette.loc = loc
diskette = null
if("refresh")
SStgui.update_uis(src)
if("selectpod")
var/ref = params["ref"]
if(!length(ref))
return
var/obj/machinery/clonepod/selected = locate(ref)
if(istype(selected) && (selected in pods))
selected_pod = selected
if("clone")
var/ref = params["ref"]
if(!length(ref))
return
var/datum/dna2/record/C = locate(ref)
//Look for that player! They better be dead!
if(istype(C))
tgui_modal_clear(src)
//Can't clone without someone to clone. Or a pod. Or if the pod is busy. Or full of gibs.
if(!length(pods))
set_temp("Error: No cloning pod detected.", "danger")
else
var/obj/machinery/clonepod/pod = selected_pod
var/cloneresult
if(!selected_pod)
set_temp("Error: No cloning pod selected.", "danger")
else if(pod.occupant)
set_temp("Error: The cloning pod is currently occupied.", "danger")
else if(pod.biomass < CLONE_BIOMASS)
set_temp("Error: Not enough biomass.", "danger")
else if(pod.mess)
set_temp("Error: The cloning pod is malfunctioning.", "danger")
else if(!config.revival_cloning)
set_temp("Error: Unable to initiate cloning cycle.", "danger")
else
temp = "[C.name] => <font class='bad'>Initialisation failure.</font>"
cloneresult = pod.growclone(C)
if(cloneresult)
set_temp("Initiating cloning cycle...", "success")
records.Remove(C)
qdel(C)
menu = MENU_MAIN
else
set_temp("Error: Initialisation failure.", "danger")
else
set_temp("Error: Data corruption.", "danger")
if("menu")
menu = clamp(text2num(params["num"]), MENU_MAIN, MENU_RECORDS)
if("toggle_mode")
if(loading)
return
if(can_brainscan())
scan_mode = !scan_mode
else
scan_mode = FALSE
if("eject")
if(usr.incapacitated() || !scanner || loading)
return
scanner.eject_occupant(usr)
scanner.add_fingerprint(usr)
if("cleartemp")
temp = null
else
temp = "<span class=\"bad\">Error: Data corruption.</span>"
else if(href_list["menu"])
src.menu = text2num(href_list["menu"])
temp = ""
scantemp = "Scanner ready."
else if(href_list["toggle_mode"])
if(can_brainscan())
scan_mode = !scan_mode
else
scan_mode = 0
return FALSE
src.add_fingerprint(usr)
SSnanoui.update_uis(src)
return
/obj/machinery/computer/cloning/proc/scan_mob(mob/living/carbon/human/subject as mob, var/scan_brain = 0)
if(stat & NOPOWER)
@@ -360,46 +381,46 @@
return
if(isnull(subject) || (!(ishuman(subject))) || (!subject.dna))
if(isalien(subject))
scantemp = "<span class=\"bad\">Error: Xenomorphs are not scannable.</span>"
SSnanoui.update_uis(src)
set_scan_temp("Xenomorphs are not scannable.", "bad")
SStgui.update_uis(src)
return
// can add more conditions for specific non-human messages here
else
scantemp = "<span class=\"bad\">Error: Subject species is not scannable.</span>"
SSnanoui.update_uis(src)
set_scan_temp("Subject species is not scannable.", "bad")
SStgui.update_uis(src)
return
if(subject.get_int_organ(/obj/item/organ/internal/brain))
var/obj/item/organ/internal/brain/Brn = subject.get_int_organ(/obj/item/organ/internal/brain)
if(istype(Brn))
if(NO_SCAN in Brn.dna.species.species_traits)
scantemp = "<span class=\"bad\">Error: [Brn.dna.species.name_plural] are not scannable.</span>"
SSnanoui.update_uis(src)
set_scan_temp("[Brn.dna.species.name_plural] are not scannable.", "bad")
SStgui.update_uis(src)
return
if(!subject.get_int_organ(/obj/item/organ/internal/brain))
scantemp = "<span class=\"bad\">Error: No brain detected in subject.</span>"
SSnanoui.update_uis(src)
set_scan_temp("No brain detected in subject.", "bad")
SStgui.update_uis(src)
return
if(subject.suiciding)
scantemp = "<span class=\"bad\">Error: Subject has committed suicide and is not scannable.</span>"
SSnanoui.update_uis(src)
set_scan_temp("Subject has committed suicide and is not scannable.", "bad")
SStgui.update_uis(src)
return
if((!subject.ckey) || (!subject.client))
scantemp = "<span class=\"bad\">Error: Subject's brain is not responding. Further attempts after a short delay may succeed.</span>"
SSnanoui.update_uis(src)
set_scan_temp("Subject's brain is not responding. Further attempts after a short delay may succeed.", "bad")
SStgui.update_uis(src)
return
if((NOCLONE in subject.mutations) && src.scanner.scan_level < 2)
scantemp = "<span class=\"bad\">Error: Subject has incompatible genetic mutations.</span>"
SSnanoui.update_uis(src)
set_scan_temp("Subject has incompatible genetic mutations.", "bad")
SStgui.update_uis(src)
return
if(!isnull(find_record(subject.ckey)))
scantemp = "Subject already in database."
SSnanoui.update_uis(src)
set_scan_temp("Subject already in database.")
SStgui.update_uis(src)
return
for(var/obj/machinery/clonepod/pod in pods)
if(pod.occupant && pod.clonemind == subject.mind)
scantemp = "Subject already getting cloned."
SSnanoui.update_uis(src)
set_scan_temp("Subject already getting cloned.")
SStgui.update_uis(src)
return
subject.dna.check_integrity()
@@ -434,8 +455,8 @@
R.mind = "\ref[subject.mind]"
src.records += R
scantemp = "Subject successfully scanned. " + extra_info
SSnanoui.update_uis(src)
set_scan_temp("Subject successfully scanned. [extra_info]", "good")
SStgui.update_uis(src)
//Find a specific record by key.
/obj/machinery/computer/cloning/proc/find_record(var/find_key)
@@ -451,3 +472,30 @@
/obj/machinery/computer/cloning/proc/can_brainscan()
return (scanner && scanner.scan_level > 3)
/**
* Sets a temporary message to display to the user
*
* Arguments:
* * text - Text to display, null/empty to clear the message from the UI
* * style - The style of the message: (color name), info, success, warning, danger
*/
/obj/machinery/computer/cloning/proc/set_temp(text = "", style = "info", update_now = FALSE)
temp = list(text = text, style = style)
if(update_now)
SStgui.update_uis(src)
/**
* Sets a temporary scan message to display to the user
*
* Arguments:
* * text - Text to display, null/empty to clear the message from the UI
* * color - The color of the message: (color name)
*/
/obj/machinery/computer/cloning/proc/set_scan_temp(text = "", color = "", update_now = FALSE)
scantemp = list(text = text, color = color)
if(update_now)
SStgui.update_uis(src)
#undef MENU_MAIN
#undef MENU_RECORDS
+331 -392
View File
@@ -1,10 +1,12 @@
#define MED_DATA_MAIN 1 // Main menu
#define MED_DATA_R_LIST 2 // Record list
#define MED_DATA_MAINT 3 // Records maintenance
#define MED_DATA_RECORD 4 // Record
#define MED_DATA_V_DATA 5 // Virus database
#define MED_DATA_MEDBOT 6 // Medbot monitor
#define FIELD(N, V, E) list(field = N, value = V, edit = E)
#define MED_FIELD(N, V, E, LB) list(field = N, value = V, edit = E, line_break = LB)
/obj/machinery/computer/med_data //TODO:SANITY
name = "medical records console"
desc = "This can be used to check medical records."
@@ -18,11 +20,45 @@
var/screen = null
var/datum/data/record/active1 = null
var/datum/data/record/active2 = null
var/temp = null
var/list/temp = null
var/printing = null
// The below are used to make modal generation more convenient
var/static/list/field_edit_questions
var/static/list/field_edit_choices
light_color = LIGHT_COLOR_DARKBLUE
/obj/machinery/computer/med_data/Initialize()
..()
field_edit_questions = list(
// General
"sex" = "Please select new sex:",
"age" = "Please input new age:",
"fingerprint" = "Please input new fingerprint hash:",
"p_stat" = "Please select new physical status:",
"m_stat" = "Please select new mental status:",
// Medical
"blood_type" = "Please select new blood type:",
"b_dna" = "Please input new DNA:",
"mi_dis" = "Please input new minor disabilities:",
"mi_dis_d" = "Please summarize minor disabilities:",
"ma_dis" = "Please input new major disabilities:",
"ma_dis_d" = "Please summarize major disabilities:",
"alg" = "Please input new allergies:",
"alg_d" = "Please summarize allergies:",
"cdi" = "Please input new current diseases:",
"cdi_d" = "Please summarize current diseases:",
"notes" = "Please input new important notes:",
)
field_edit_choices = list(
// General
"sex" = list("Male", "Female"),
"p_stat" = list("*Deceased*", "*SSD*", "Active", "Physically Unfit", "Disabled"),
"m_stat" = list("*Insane*", "*Unstable*", "*Watch*", "Stable"),
// Medical
"blood_type" = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O+", "O-"),
)
/obj/machinery/computer/med_data/Destroy()
active1 = null
active2 = null
@@ -33,7 +69,7 @@
usr.drop_item()
O.forceMove(src)
scan = O
ui_interact(user)
tgui_interact(user)
return
return ..()
@@ -44,20 +80,25 @@
to_chat(user, "<span class='danger'>Unable to establish a connection</span>: You're too far away from the station!")
return
add_fingerprint(user)
ui_interact(user)
tgui_interact(user)
/obj/machinery/computer/med_data/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
/obj/machinery/computer/med_data/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "med_data.tmpl", name, 800, 380)
ui = new(user, src, ui_key, "MedicalRecords", "Medical Records", 800, 380, master_ui, state)
ui.open()
ui.set_autoupdate(FALSE)
/obj/machinery/computer/med_data/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state)
/obj/machinery/computer/med_data/tgui_data(mob/user)
var/data[0]
data["temp"] = temp
data["scan"] = scan ? scan.name : null
data["authenticated"] = authenticated
data["rank"] = rank
data["screen"] = screen
data["printing"] = printing
data["isAI"] = isAI(user)
data["isRobot"] = isrobot(user)
if(authenticated)
switch(screen)
if(MED_DATA_R_LIST)
@@ -72,17 +113,17 @@
if(istype(active1, /datum/data/record) && GLOB.data_core.general.Find(active1))
var/list/fields = list()
general["fields"] = fields
fields[++fields.len] = list("field" = "Name:", "value" = active1.fields["name"], "edit" = null)
fields[++fields.len] = list("field" = "ID:", "value" = active1.fields["id"], "edit" = null)
fields[++fields.len] = list("field" = "Sex:", "value" = active1.fields["sex"], "edit" = "sex")
fields[++fields.len] = list("field" = "Age:", "value" = active1.fields["age"], "edit" = "age")
fields[++fields.len] = list("field" = "Fingerprint:", "value" = active1.fields["fingerprint"], "edit" = "fingerprint")
fields[++fields.len] = list("field" = "Physical Status:", "value" = active1.fields["p_stat"], "edit" = "p_stat")
fields[++fields.len] = list("field" = "Mental Status:", "value" = active1.fields["m_stat"], "edit" = "m_stat")
fields[++fields.len] = FIELD("Name", active1.fields["name"], null)
fields[++fields.len] = FIELD("ID", active1.fields["id"], null)
fields[++fields.len] = FIELD("Sex", active1.fields["sex"], "sex")
fields[++fields.len] = FIELD("Age", active1.fields["age"], "age")
fields[++fields.len] = FIELD("Fingerprint", active1.fields["fingerprint"], "fingerprint")
fields[++fields.len] = FIELD("Physical Status", active1.fields["p_stat"], "p_stat")
fields[++fields.len] = FIELD("Mental Status", active1.fields["m_stat"], "m_stat")
var/list/photos = list()
general["photos"] = photos
photos[++photos.len] = list("photo" = active1.fields["photo-south"])
photos[++photos.len] = list("photo" = active1.fields["photo-west"])
photos[++photos.len] = active1.fields["photo-south"]
photos[++photos.len] = active1.fields["photo-west"]
general["has_photos"] = (active1.fields["photo-south"] || active1.fields["photo-west"] ? 1 : 0)
general["empty"] = 0
else
@@ -93,17 +134,17 @@
if(istype(active2, /datum/data/record) && GLOB.data_core.medical.Find(active2))
var/list/fields = list()
medical["fields"] = fields
fields[++fields.len] = list("field" = "Blood Type:", "value" = active2.fields["blood_type"], "edit" = "blood_type", "line_break" = 0)
fields[++fields.len] = list("field" = "DNA:", "value" = active2.fields["b_dna"], "edit" = "b_dna", "line_break" = 1)
fields[++fields.len] = list("field" = "Minor Disabilities:", "value" = active2.fields["mi_dis"], "edit" = "mi_dis", "line_break" = 0)
fields[++fields.len] = list("field" = "Details:", "value" = active2.fields["mi_dis_d"], "edit" = "mi_dis_d", "line_break" = 1)
fields[++fields.len] = list("field" = "Major Disabilities:", "value" = active2.fields["ma_dis"], "edit" = "ma_dis", "line_break" = 0)
fields[++fields.len] = list("field" = "Details:", "value" = active2.fields["ma_dis_d"], "edit" = "ma_dis_d", "line_break" = 1)
fields[++fields.len] = list("field" = "Allergies:", "value" = active2.fields["alg"], "edit" = "alg", "line_break" = 0)
fields[++fields.len] = list("field" = "Details:", "value" = active2.fields["alg_d"], "edit" = "alg_d", "line_break" = 1)
fields[++fields.len] = list("field" = "Current Diseases:", "value" = active2.fields["cdi"], "edit" = "cdi", "line_break" = 0)
fields[++fields.len] = list("field" = "Details:", "value" = active2.fields["cdi_d"], "edit" = "cdi_d", "line_break" = 1)
fields[++fields.len] = list("field" = "Important Notes:", "value" = active2.fields["notes"], "edit" = "notes", "line_break" = 0)
fields[++fields.len] = MED_FIELD("Blood Type", active2.fields["blood_type"], "blood_type", FALSE)
fields[++fields.len] = MED_FIELD("DNA", active2.fields["b_dna"], "b_dna", TRUE)
fields[++fields.len] = MED_FIELD("Minor Disabilities", active2.fields["mi_dis"], "mi_dis", FALSE)
fields[++fields.len] = MED_FIELD("Details", active2.fields["mi_dis_d"], "mi_dis_d", TRUE)
fields[++fields.len] = MED_FIELD("Major Disabilities", active2.fields["ma_dis"], "ma_dis", FALSE)
fields[++fields.len] = MED_FIELD("Details", active2.fields["ma_dis_d"], "ma_dis_d", TRUE)
fields[++fields.len] = MED_FIELD("Allergies", active2.fields["alg"], "alg", FALSE)
fields[++fields.len] = MED_FIELD("Details", active2.fields["alg_d"], "alg_d", TRUE)
fields[++fields.len] = MED_FIELD("Current Diseases", active2.fields["cdi"], "cdi", FALSE)
fields[++fields.len] = MED_FIELD("Details", active2.fields["cdi_d"], "cdi_d", TRUE)
fields[++fields.len] = MED_FIELD("Important Notes", active2.fields["notes"], "notes", TRUE)
if(!active2.fields["comments"] || !islist(active2.fields["comments"]))
active2.fields["comments"] = list()
medical["comments"] = active2.fields["comments"]
@@ -127,7 +168,9 @@
var/turf/T = get_turf(M)
if(T)
var/medbot = list()
var/area/A = get_area(T)
medbot["name"] = M.name
medbot["area"] = A.name
medbot["x"] = T.x
medbot["y"] = T.y
medbot["on"] = M.on
@@ -138,395 +181,290 @@
else
medbot["use_beaker"] = 0
data["medbots"] += list(medbot)
data["modal"] = tgui_modal_data(src)
return data
/obj/machinery/computer/med_data/Topic(href, href_list)
/obj/machinery/computer/med_data/tgui_act(action, params)
if(..())
return 1
return
if(stat & (NOPOWER|BROKEN))
return
if(!GLOB.data_core.general.Find(active1))
active1 = null
if(!GLOB.data_core.medical.Find(active2))
active2 = null
if(href_list["temp"])
temp = null
. = TRUE
if(tgui_act_modal(action, params))
return
if(href_list["temp_action"])
if(href_list["temp_action"])
var/temp_href = splittext(href_list["temp_action"], "=")
switch(temp_href[1])
if("del_all2")
for(var/datum/data/record/R in GLOB.data_core.medical)
qdel(R)
setTemp("<h3>All records deleted.</h3>")
if("p_stat")
if(active1)
switch(temp_href[2])
if("deceased")
active1.fields["p_stat"] = "*Deceased*"
if("ssd")
active1.fields["p_stat"] = "*SSD*"
if("active")
active1.fields["p_stat"] = "Active"
if("unfit")
active1.fields["p_stat"] = "Physically Unfit"
if("disabled")
active1.fields["p_stat"] = "Disabled"
if("m_stat")
if(active1)
switch(temp_href[2])
if("insane")
active1.fields["m_stat"] = "*Insane*"
if("unstable")
active1.fields["m_stat"] = "*Unstable*"
if("watch")
active1.fields["m_stat"] = "*Watch*"
if("stable")
active1.fields["m_stat"] = "Stable"
if("blood_type")
if(active2)
switch(temp_href[2])
if("an")
active2.fields["blood_type"] = "A-"
if("bn")
active2.fields["blood_type"] = "B-"
if("abn")
active2.fields["blood_type"] = "AB-"
if("on")
active2.fields["blood_type"] = "O-"
if("ap")
active2.fields["blood_type"] = "A+"
if("bp")
active2.fields["blood_type"] = "B+"
if("abp")
active2.fields["blood_type"] = "AB+"
if("op")
active2.fields["blood_type"] = "O+"
if("del_r2")
QDEL_NULL(active2)
if(href_list["scan"])
if(scan)
scan.forceMove(loc)
if(ishuman(usr) && !usr.get_active_hand())
usr.put_in_hands(scan)
scan = null
switch(action)
if("cleartemp")
temp = null
if("scan")
if(scan)
scan.forceMove(loc)
if(ishuman(usr) && !usr.get_active_hand())
usr.put_in_hands(scan)
scan = null
else
var/obj/item/I = usr.get_active_hand()
if(istype(I, /obj/item/card/id))
usr.drop_item()
I.forceMove(src)
scan = I
if("login")
var/login_type = text2num(params["login_type"])
if(login_type == LOGIN_TYPE_NORMAL && istype(scan))
if(check_access(scan))
authenticated = scan.registered_name
rank = scan.assignment
else if(login_type == LOGIN_TYPE_AI && isAI(usr))
authenticated = usr.name
rank = "AI"
else if(login_type == LOGIN_TYPE_ROBOT && isrobot(usr))
authenticated = usr.name
var/mob/living/silicon/robot/R = usr
rank = "[R.modtype] [R.braintype]"
if(authenticated)
active1 = null
active2 = null
screen = MED_DATA_R_LIST
else
var/obj/item/I = usr.get_active_hand()
if(istype(I, /obj/item/card/id))
usr.drop_item()
I.forceMove(src)
scan = I
. = FALSE
if(href_list["login"])
if(isAI(usr))
authenticated = usr.name
rank = "AI"
else if(isrobot(usr))
authenticated = usr.name
var/mob/living/silicon/robot/R = usr
rank = "[R.modtype] [R.braintype]"
else if(istype(scan, /obj/item/card/id))
if(check_access(scan))
authenticated = scan.registered_name
rank = scan.assignment
if(authenticated)
active1 = null
active2 = null
screen = MED_DATA_MAIN
if(.)
return
if(authenticated)
if(href_list["logout"])
authenticated = null
screen = null
active1 = null
active2 = null
. = TRUE
switch(action)
if("logout")
if(scan)
scan.forceMove(loc)
if(ishuman(usr) && !usr.get_active_hand())
usr.put_in_hands(scan)
scan = null
authenticated = null
screen = null
active1 = null
active2 = null
if("screen")
screen = clamp(text2num(params["screen"]) || 0, MED_DATA_R_LIST, MED_DATA_MEDBOT)
active1 = null
active2 = null
if("vir")
var/type = text2path(params["vir"] || "")
if(!ispath(type, /datum/disease))
return
if(href_list["screen"])
screen = text2num(href_list["screen"])
if(screen < 1)
screen = MED_DATA_MAIN
var/datum/disease/D = new type(0)
var/list/payload = list(
name = D.name,
max_stages = D.max_stages,
spread_text = D.spread_text,
cure = D.cure_text || "None",
desc = D.desc,
severity = D.severity
);
tgui_modal_message(src, "virus", "", null, payload)
qdel(D)
if("del_all")
for(var/datum/data/record/R in GLOB.data_core.medical)
qdel(R)
set_temp("All medical records deleted.")
if("del_r")
if(active2)
set_temp("Medical record deleted.")
qdel(active2)
if("d_rec")
var/datum/data/record/general_record = locate(params["d_rec"] || "")
if(!GLOB.data_core.general.Find(general_record))
set_temp("Record not found.", "danger")
return
active1 = null
active2 = null
var/datum/data/record/medical_record
for(var/datum/data/record/M in GLOB.data_core.medical)
if(M.fields["name"] == general_record.fields["name"] && M.fields["id"] == general_record.fields["id"])
medical_record = M
break
if(href_list["vir"])
var/type = href_list["vir"]
var/datum/disease/D = new type(0)
var/afs = ""
for(var/mob/M in D.viable_mobtypes)
afs += "[initial(M.name)];"
var/severity = D.severity
switch(severity)
if("Harmful", "Minor")
severity = "<span class='good'>[severity]</span>"
if("Medium")
severity = "<span class='average'>[severity]</span>"
if("Dangerous!")
severity = "<span class='bad'>[severity]</span>"
if("BIOHAZARD THREAT!")
severity = "<h4><span class='bad'>[severity]</span></h4>"
setTemp({"<b>Name:</b> [D.name]
<BR><b>Number of stages:</b> [D.max_stages]
<BR><b>Spread:</b> [D.spread_text] Transmission
<BR><b>Possible Cure:</b> [(D.cure_text||"none")]
<BR><b>Affected Lifeforms:</b>[afs]<BR>
<BR><b>Notes:</b> [D.desc]<BR>
<BR><b>Severity:</b> [severity]"})
qdel(D)
if(href_list["del_all"])
var/list/buttons = list()
buttons[++buttons.len] = list("name" = "Yes", "icon" = "check", "href" = "del_all2=1")
buttons[++buttons.len] = list("name" = "No", "icon" = "times", "href" = null)
setTemp("<h3>Are you sure you wish to delete all records?</h3>", buttons)
if(href_list["field"])
if(..())
return 1
var/a1 = active1
var/a2 = active2
switch(href_list["field"])
if("fingerprint")
if(istype(active1, /datum/data/record))
var/t1 = copytext(trim(sanitize(input("Please input fingerprint hash:", "Med. records", active1.fields["fingerprint"], null) as text)), 1, MAX_MESSAGE_LEN)
if(!t1 || ..() || active1 != a1)
return 1
active1.fields["fingerprint"] = t1
if("sex")
if(istype(active1, /datum/data/record))
if(active1.fields["sex"] == "Male")
active1.fields["sex"] = "Female"
else
active1.fields["sex"] = "Male"
if("age")
if(istype(active1, /datum/data/record))
var/t1 = input("Please input age:", "Med. records", active1.fields["age"], null) as num
if(!t1 || ..() || active1 != a1)
return 1
active1.fields["age"] = t1
if("mi_dis")
if(istype(active2, /datum/data/record))
var/t1 = copytext(trim(sanitize(input("Please input minor disabilities list:", "Med. records", active2.fields["mi_dis"], null) as text)), 1, MAX_MESSAGE_LEN)
if(!t1 || ..() || active2 != a2)
return 1
active2.fields["mi_dis"] = t1
if("mi_dis_d")
if(istype(active2, /datum/data/record))
var/t1 = copytext(trim(sanitize(input("Please summarize minor dis.:", "Med. records", active2.fields["mi_dis_d"], null) as message)), 1, MAX_MESSAGE_LEN)
if(!t1 || ..() || active2 != a2)
return 1
active2.fields["mi_dis_d"] = t1
if("ma_dis")
if(istype(active2, /datum/data/record))
var/t1 = copytext(trim(sanitize(input("Please input major diabilities list:", "Med. records", active2.fields["ma_dis"], null) as text)), 1, MAX_MESSAGE_LEN)
if(!t1 || ..() || active2 != a2)
return 1
active2.fields["ma_dis"] = t1
if("ma_dis_d")
if(istype(active2, /datum/data/record))
var/t1 = copytext(trim(sanitize(input("Please summarize major dis.:", "Med. records", active2.fields["ma_dis_d"], null) as message)), 1, MAX_MESSAGE_LEN)
if(!t1 || ..() || active2 != a2)
return 1
active2.fields["ma_dis_d"] = t1
if("alg")
if(istype(active2, /datum/data/record))
var/t1 = copytext(trim(sanitize(input("Please state allergies:", "Med. records", active2.fields["alg"], null) as text)), 1, MAX_MESSAGE_LEN)
if(!t1 || ..() || active2 != a2)
return 1
active2.fields["alg"] = t1
if("alg_d")
if(istype(active2, /datum/data/record))
var/t1 = copytext(trim(sanitize(input("Please summarize allergies:", "Med. records", active2.fields["alg_d"], null) as message)), 1, MAX_MESSAGE_LEN)
if(!t1 || ..() || active2 != a2)
return 1
active2.fields["alg_d"] = t1
if("cdi")
if(istype(active2, /datum/data/record))
var/t1 = copytext(trim(sanitize(input("Please state diseases:", "Med. records", active2.fields["cdi"], null) as text)), 1, MAX_MESSAGE_LEN)
if(!t1 || ..() || active2 != a2)
return 1
active2.fields["cdi"] = t1
if("cdi_d")
if(istype(active2, /datum/data/record))
var/t1 = copytext(trim(sanitize(input("Please summarize diseases:", "Med. records", active2.fields["cdi_d"], null) as message)), 1, MAX_MESSAGE_LEN)
if(!t1 || ..() || active2 != a2)
return 1
active2.fields["cdi_d"] = t1
if("notes")
if(istype(active2, /datum/data/record))
var/t1 = copytext(html_encode(trim(input("Please summarize notes:", "Med. records", html_decode(active2.fields["notes"]), null) as message)), 1, MAX_MESSAGE_LEN)
if(!t1 || ..() || active2 != a2)
return 1
active2.fields["notes"] = t1
if("p_stat")
if(istype(active1, /datum/data/record))
var/list/buttons = list()
buttons[++buttons.len] = list("name" = "*Deceased*", "icon" = "stethoscope", "href" = "p_stat=deceased", "status" = (active1.fields["p_stat"] == "*Deceased*" ? "selected" : null))
buttons[++buttons.len] = list("name" = "*SSD*", "icon" = "stethoscope", "href" = "p_stat=ssd", "status" = (active1.fields["p_stat"] == "*SSD*" ? "selected" : null))
buttons[++buttons.len] = list("name" = "Active", "icon" = "stethoscope", "href" = "p_stat=active", "status" = (active1.fields["p_stat"] == "Active" ? "selected" : null))
buttons[++buttons.len] = list("name" = "Physically Unfit", "icon" = "stethoscope", "href" = "p_stat=unfit", "status" = (active1.fields["p_stat"] == "Physically Unfit" ? "selected" : null))
buttons[++buttons.len] = list("name" = "Disabled", "icon" = "stethoscope", "href" = "p_stat=disabled", "status" = (active1.fields["p_stat"] == "Disabled" ? "selected" : null))
setTemp("<h3>Physical Condition</h3>", buttons)
if("m_stat")
if(istype(active1, /datum/data/record))
var/list/buttons = list()
buttons[++buttons.len] = list("name" = "*Insane*", "icon" = "stethoscope", "href" = "m_stat=insane", "status" = (active1.fields["m_stat"] == "*Insane*" ? "selected" : null))
buttons[++buttons.len] = list("name" = "*Unstable*", "icon" = "stethoscope", "href" = "m_stat=unstable", "status" = (active1.fields["m_stat"] == "*Unstable*" ? "selected" : null))
buttons[++buttons.len] = list("name" = "*Watch*", "icon" = "stethoscope", "href" = "m_stat=watch", "status" = (active1.fields["m_stat"] == "*Watch*" ? "selected" : null))
buttons[++buttons.len] = list("name" = "Stable", "icon" = "stethoscope", "href" = "m_stat=stable", "status" = (active1.fields["m_stat"] == "Stable" ? "selected" : null))
setTemp("<h3>Mental Condition</h3>", buttons)
if("blood_type")
if(istype(active2, /datum/data/record))
var/list/buttons = list()
buttons[++buttons.len] = list("name" = "A-", "icon" = "tint", "href" = "blood_type=an", "status" = (active2.fields["blood_type"] == "A-" ? "selected" : null))
buttons[++buttons.len] = list("name" = "A+", "icon" = "tint", "href" = "blood_type=ap", "status" = (active2.fields["blood_type"] == "A+" ? "selected" : null))
buttons[++buttons.len] = list("name" = "B-", "icon" = "tint", "href" = "blood_type=bn", "status" = (active2.fields["blood_type"] == "B-" ? "selected" : null))
buttons[++buttons.len] = list("name" = "B+", "icon" = "tint", "href" = "blood_type=bp", "status" = (active2.fields["blood_type"] == "B+" ? "selected" : null))
buttons[++buttons.len] = list("name" = "AB-", "icon" = "tint", "href" = "blood_type=abn", "status" = (active2.fields["blood_type"] == "AB-" ? "selected" : null))
buttons[++buttons.len] = list("name" = "AB+", "icon" = "tint", "href" = "blood_type=abp", "status" = (active2.fields["blood_type"] == "AB+" ? "selected" : null))
buttons[++buttons.len] = list("name" = "O-", "icon" = "tint", "href" = "blood_type=on", "status" = (active2.fields["blood_type"] == "O-" ? "selected" : null))
buttons[++buttons.len] = list("name" = "O+", "icon" = "tint", "href" = "blood_type=op", "status" = (active2.fields["blood_type"] == "O+" ? "selected" : null))
setTemp("<h3>Blood Type</h3>", buttons)
if("b_dna")
if(istype(active2, /datum/data/record))
var/t1 = copytext(trim(sanitize(input("Please input DNA hash:", "Med. records", active2.fields["b_dna"], null) as text)), 1, MAX_MESSAGE_LEN)
if(!t1 || ..() || active2 != a2)
return 1
active2.fields["b_dna"] = t1
if("vir_name")
var/datum/data/record/v = locate(href_list["edit_vir"])
if(v)
var/t1 = copytext(trim(sanitize(input("Please input pathogen name:", "VirusDB", v.fields["name"], null) as text)), 1, MAX_MESSAGE_LEN)
if(!t1 || ..() || active1 != a1)
return 1
v.fields["name"] = t1
if("vir_desc")
var/datum/data/record/v = locate(href_list["edit_vir"])
if(v)
var/t1 = copytext(trim(sanitize(input("Please input information about pathogen:", "VirusDB", v.fields["description"], null) as message)), 1, MAX_MESSAGE_LEN)
if(!t1 || ..() || active1 != a1)
return 1
v.fields["description"] = t1
if(href_list["del_r"])
if(active2)
var/list/buttons = list()
buttons[++buttons.len] = list("name" = "Yes", "icon" = "check", "href" = "del_r2=1", "status" = null)
buttons[++buttons.len] = list("name" = "No", "icon" = "times", "href" = null, "status" = null)
setTemp("<h3>Are you sure you wish to delete the record (Medical Portion Only)?</h3>", buttons)
if(href_list["d_rec"])
var/datum/data/record/R = locate(href_list["d_rec"])
var/datum/data/record/M = locate(href_list["d_rec"])
if(!GLOB.data_core.general.Find(R))
setTemp("<h3 class='bad'>Record not found!</h3>")
return 1
for(var/datum/data/record/E in GLOB.data_core.medical)
if(E.fields["name"] == R.fields["name"] && E.fields["id"] == R.fields["id"])
M = E
active1 = R
active2 = M
screen = MED_DATA_RECORD
if(href_list["new"])
if(istype(active1, /datum/data/record) && !istype(active2, /datum/data/record))
var/datum/data/record/R = new /datum/data/record()
R.fields["name"] = active1.fields["name"]
R.fields["id"] = active1.fields["id"]
R.name = "Medical Record #[R.fields["id"]]"
R.fields["blood_type"] = "Unknown"
R.fields["b_dna"] = "Unknown"
R.fields["mi_dis"] = "None"
R.fields["mi_dis_d"] = "No minor disabilities have been declared."
R.fields["ma_dis"] = "None"
R.fields["ma_dis_d"] = "No major disabilities have been diagnosed."
R.fields["alg"] = "None"
R.fields["alg_d"] = "No allergies have been detected in this patient."
R.fields["cdi"] = "None"
R.fields["cdi_d"] = "No diseases have been diagnosed at the moment."
R.fields["notes"] = "No notes."
GLOB.data_core.medical += R
active2 = R
active1 = general_record
active2 = medical_record
screen = MED_DATA_RECORD
if(href_list["add_c"])
if(!istype(active2, /datum/data/record))
return 1
var/a2 = active2
var/t1 = copytext(trim(sanitize(input("Add Comment:", "Med. records", null, null) as message)), 1, MAX_MESSAGE_LEN)
if(!t1 || ..() || active2 != a2)
return 1
active2.fields["comments"] += "Made by [authenticated] ([rank]) on [GLOB.current_date_string] [station_time_timestamp()]<BR>[t1]"
if(href_list["del_c"])
var/index = min(max(text2num(href_list["del_c"]) + 1, 1), length(active2.fields["comments"]))
if(istype(active2, /datum/data/record) && active2.fields["comments"][index])
active2.fields["comments"] -= active2.fields["comments"][index]
if(href_list["search"])
var/t1 = clean_input("Search String: (Name, DNA, or ID)", "Med. records", null, null)
if(!t1 || ..())
return 1
active1 = null
active2 = null
t1 = lowertext(t1)
for(var/datum/data/record/R in GLOB.data_core.medical)
if(t1 == lowertext(R.fields["name"]) || t1 == lowertext(R.fields["id"]) || t1 == lowertext(R.fields["b_dna"]))
if("new")
if(istype(active1, /datum/data/record) && !istype(active2, /datum/data/record))
var/datum/data/record/R = new /datum/data/record()
R.fields["name"] = active1.fields["name"]
R.fields["id"] = active1.fields["id"]
R.name = "Medical Record #[R.fields["id"]]"
R.fields["blood_type"] = "Unknown"
R.fields["b_dna"] = "Unknown"
R.fields["mi_dis"] = "None"
R.fields["mi_dis_d"] = "No minor disabilities have been declared."
R.fields["ma_dis"] = "None"
R.fields["ma_dis_d"] = "No major disabilities have been diagnosed."
R.fields["alg"] = "None"
R.fields["alg_d"] = "No allergies have been detected in this patient."
R.fields["cdi"] = "None"
R.fields["cdi_d"] = "No diseases have been diagnosed at the moment."
R.fields["notes"] = "No notes."
GLOB.data_core.medical += R
active2 = R
if(!active2)
setTemp("<h3 class='bad'>Could not locate record [t1].</h3>")
else
screen = MED_DATA_RECORD
set_temp("Medical record created.", "success")
if("del_c")
var/index = text2num(params["del_c"] || "")
if(!index || !istype(active2, /datum/data/record))
return
var/list/comments = active2.fields["comments"]
index = clamp(index, 1, length(comments))
if(comments[index])
comments.Cut(index, index + 1)
if("search")
active1 = null
active2 = null
var/t1 = lowertext(params["t1"] || "")
if(!length(t1))
return
for(var/datum/data/record/R in GLOB.data_core.medical)
if(t1 == lowertext(R.fields["name"]) || t1 == lowertext(R.fields["id"]) || t1 == lowertext(R.fields["b_dna"]))
active2 = R
break
if(!active2)
set_temp("Medical record not found. You must enter the person's exact name, ID or DNA.", "danger")
return
for(var/datum/data/record/E in GLOB.data_core.general)
if(E.fields["name"] == active2.fields["name"] && E.fields["id"] == active2.fields["id"])
active1 = E
break
screen = MED_DATA_RECORD
if("print_p")
if(!printing)
printing = TRUE
playsound(loc, 'sound/goonstation/machines/printer_dotmatrix.ogg', 50, TRUE)
SStgui.update_uis(src)
addtimer(CALLBACK(src, .proc/print_finish), 5 SECONDS)
else
return FALSE
if(href_list["print_p"])
if(!printing)
printing = 1
playsound(loc, 'sound/goonstation/machines/printer_dotmatrix.ogg', 50, 1)
sleep(50)
var/obj/item/paper/P = new /obj/item/paper(loc)
P.info = "<CENTER><B>Medical Record</B></CENTER><BR>"
if(istype(active1, /datum/data/record) && GLOB.data_core.general.Find(active1))
P.info += {"Name: [active1.fields["name"]] ID: [active1.fields["id"]]
<BR>\nSex: [active1.fields["sex"]]
<BR>\nAge: [active1.fields["age"]]
<BR>\nFingerprint: [active1.fields["fingerprint"]]
<BR>\nPhysical Status: [active1.fields["p_stat"]]
<BR>\nMental Status: [active1.fields["m_stat"]]<BR>"}
/**
* Called in tgui_act() to process modal actions
*
* Arguments:
* * action - The action passed by tgui
* * params - The params passed by tgui
*/
/obj/machinery/computer/med_data/proc/tgui_act_modal(action, params)
. = TRUE
var/id = params["id"] // The modal's ID
var/list/arguments = istext(params["arguments"]) ? json_decode(params["arguments"]) : params["arguments"]
switch(tgui_modal_act(src, action, params))
if(TGUI_MODAL_OPEN)
switch(id)
if("edit")
var/field = arguments["field"]
if(!length(field) || !field_edit_questions[field])
return
var/question = field_edit_questions[field]
var/choices = field_edit_choices[field]
if(length(choices))
tgui_modal_choice(src, id, question, arguments = arguments, value = arguments["value"], choices = choices)
else
tgui_modal_input(src, id, question, arguments = arguments, value = arguments["value"])
if("add_c")
tgui_modal_input(src, id, "Please enter your message:")
else
P.info += "<B>General Record Lost!</B><BR>"
if(istype(active2, /datum/data/record) && GLOB.data_core.medical.Find(active2))
P.info += {"<BR>\n<CENTER><B>Medical Data</B></CENTER>
<BR>\nBlood Type: [active2.fields["blood_type"]]
<BR>\nDNA: [active2.fields["b_dna"]]<BR>\n
<BR>\nMinor Disabilities: [active2.fields["mi_dis"]]
<BR>\nDetails: [active2.fields["mi_dis_d"]]<BR>\n
<BR>\nMajor Disabilities: [active2.fields["ma_dis"]]
<BR>\nDetails: [active2.fields["ma_dis_d"]]<BR>\n
<BR>\nAllergies: [active2.fields["alg"]]
<BR>\nDetails: [active2.fields["alg_d"]]<BR>\n
<BR>\nCurrent Diseases: [active2.fields["cdi"]] (per disease info placed in log/comment section)
<BR>\nDetails: [active2.fields["cdi_d"]]<BR>\n
<BR>\nImportant Notes:
<BR>\n\t[active2.fields["notes"]]<BR>\n
<BR>\n
<CENTER><B>Comments/Log</B></CENTER><BR>"}
for(var/c in active2.fields["comments"])
P.info += "[c]<BR>"
else
P.info += "<B>Medical Record Lost!</B><BR>"
P.info += "</TT>"
P.name = "paper- 'Medical Record: [active1.fields["name"]]'"
printing = 0
return 1
return FALSE
if(TGUI_MODAL_ANSWER)
var/answer = params["answer"]
switch(id)
if("edit")
var/field = arguments["field"]
if(!length(field) || !field_edit_questions[field])
return
var/list/choices = field_edit_choices[field]
if(length(choices) && !(answer in choices))
return
/obj/machinery/computer/med_data/proc/setTemp(text, list/buttons = list())
temp = list("text" = text, "buttons" = buttons, "has_buttons" = buttons.len > 0)
if(field == "age")
var/new_age = text2num(answer)
if(new_age < AGE_MIN || new_age > AGE_MAX)
set_temp("Invalid age. It must be between [AGE_MIN] and [AGE_MAX].", "danger")
return
answer = new_age
if(istype(active2) && (field in active2.fields))
active2.fields[field] = answer
else if(istype(active1) && (field in active1.fields))
active1.fields[field] = answer
if("add_c")
if(!length(answer) || !istype(active2) || !length(authenticated))
return
active2.fields["comments"] += list(list(
header = "Made by [authenticated] ([rank]) on [GLOB.current_date_string] [station_time_timestamp()]",
text = answer
))
else
return FALSE
else
return FALSE
/**
* Called when the print timer finishes
*/
/obj/machinery/computer/med_data/proc/print_finish()
var/obj/item/paper/P = new /obj/item/paper(loc)
P.info = "<center></b>Medical Record</b></center><br>"
if(istype(active1, /datum/data/record) && GLOB.data_core.general.Find(active1))
P.info += {"Name: [active1.fields["name"]] ID: [active1.fields["id"]]
<br>\nSex: [active1.fields["sex"]]
<br>\nAge: [active1.fields["age"]]
<br>\nFingerprint: [active1.fields["fingerprint"]]
<br>\nPhysical Status: [active1.fields["p_stat"]]
<br>\nMental Status: [active1.fields["m_stat"]]<br>"}
else
P.info += "</b>General Record Lost!</b><br>"
if(istype(active2, /datum/data/record) && GLOB.data_core.medical.Find(active2))
P.info += {"<br>\n<center></b>Medical Data</b></center>
<br>\nBlood Type: [active2.fields["blood_type"]]
<br>\nDNA: [active2.fields["b_dna"]]<br>\n
<br>\nMinor Disabilities: [active2.fields["mi_dis"]]
<br>\nDetails: [active2.fields["mi_dis_d"]]<br>\n
<br>\nMajor Disabilities: [active2.fields["ma_dis"]]
<br>\nDetails: [active2.fields["ma_dis_d"]]<br>\n
<br>\nAllergies: [active2.fields["alg"]]
<br>\nDetails: [active2.fields["alg_d"]]<br>\n
<br>\nCurrent Diseases: [active2.fields["cdi"]] (per disease info placed in log/comment section)
<br>\nDetails: [active2.fields["cdi_d"]]<br>\n
<br>\nImportant Notes:
<br>\n\t[active2.fields["notes"]]<br>\n
<br>\n
<center></b>Comments/Log</b></center><br>"}
for(var/c in active2.fields["comments"])
P.info += "[c]<br>"
else
P.info += "</b>Medical Record Lost!</b><br>"
P.info += "</tt>"
P.name = "paper - 'Medical Record: [active1.fields["name"]]'"
printing = FALSE
SStgui.update_uis(src)
/**
* Sets a temporary message to display to the user
*
* Arguments:
* * text - Text to display, null/empty to clear the message from the UI
* * style - The style of the message: (color name), info, success, warning, danger, virus
*/
/obj/machinery/computer/med_data/proc/set_temp(text = "", style = "info", update_now = FALSE)
temp = list(text = text, style = style)
if(update_now)
SStgui.update_uis(src)
/obj/machinery/computer/med_data/emp_act(severity)
if(stat & (BROKEN|NOPOWER))
@@ -564,9 +502,10 @@
icon_screen = "medlaptop"
density = 0
#undef MED_DATA_MAIN
#undef MED_DATA_R_LIST
#undef MED_DATA_MAINT
#undef MED_DATA_RECORD
#undef MED_DATA_V_DATA
#undef MED_DATA_MEDBOT
#undef FIELD
#undef MED_FIELD
+48 -67
View File
@@ -103,7 +103,7 @@
beaker.forceMove(drop_location())
beaker = null
/obj/machinery/atmospherics/unary/cryo_cell/MouseDrop_T(atom/movable/O as mob|obj, mob/living/user as mob)
/obj/machinery/atmospherics/unary/cryo_cell/MouseDrop_T(atom/movable/O, mob/living/user)
if(O.loc == user) //no you can't pull things out of your ass
return
if(user.incapacitated()) //are you cuffed, dying, lying, stunned or other
@@ -140,6 +140,7 @@
add_attack_logs(user, L, "put into a cryo cell at [COORD(src)].", ATKLOG_ALL)
if(user.pulling == L)
user.stop_pulling()
SStgui.update_uis(src)
/obj/machinery/atmospherics/unary/cryo_cell/process()
..()
@@ -177,14 +178,14 @@
return FALSE
/obj/machinery/atmospherics/unary/cryo_cell/relaymove(mob/user as mob)
/obj/machinery/atmospherics/unary/cryo_cell/relaymove(mob/user)
if(user.stat)
return
go_out()
return
/obj/machinery/atmospherics/unary/cryo_cell/attack_ghost(mob/user)
return attack_hand(user)
tgui_interact(user)
/obj/machinery/atmospherics/unary/cryo_cell/attack_hand(mob/user)
if(user == occupant)
@@ -194,36 +195,18 @@
to_chat(usr, "<span class='boldnotice'>Close the maintenance panel first.</span>")
return
ui_interact(user)
tgui_interact(user)
/**
* The ui_interact proc is used to open and update Nano UIs
* If ui_interact is not used then the UI will not update correctly
* ui_interact is currently defined for /atom/movable (which is inherited by /obj and /mob)
*
* @param user /mob The mob who is interacting with this ui
* @param ui_key string A string key to use for this ui. Allows for multiple unique uis on one obj/mob (defaut value "main")
* @param ui /datum/nanoui This parameter is passed by the nanoui process() proc when updating an open ui
*
* @return nothing
*/
/obj/machinery/atmospherics/unary/cryo_cell/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
// update the ui if it exists, returns null if no ui is passed/found
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
/obj/machinery/atmospherics/unary/cryo_cell/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
// the ui does not exist, so we'll create a new() one
// for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm
ui = new(user, src, ui_key, "cryo.tmpl", "Cryo Cell Control System", 520, 480)
// open the new ui window
ui = new(user, src, ui_key, "Cryo", "Cryo Cell", 520, 490)
ui.open()
// auto update every Master Controller tick
ui.set_auto_update(1)
/obj/machinery/atmospherics/unary/cryo_cell/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state)
/obj/machinery/atmospherics/unary/cryo_cell/tgui_data(mob/user)
var/data[0]
data["isOperating"] = on
data["hasOccupant"] = occupant ? 1 : 0
data["hasOccupant"] = occupant ? TRUE : FALSE
var/occupantData[0]
if(occupant)
@@ -246,7 +229,7 @@
else if(air_contents.temperature > TCRYO)
data["cellTemperatureStatus"] = "average"
data["isBeakerLoaded"] = beaker ? 1 : 0
data["isBeakerLoaded"] = beaker ? TRUE : FALSE
data["beakerLabel"] = null
data["beakerVolume"] = 0
if(beaker)
@@ -259,48 +242,44 @@
data["auto_eject_dead"] = (auto_eject_prefs & AUTO_EJECT_DEAD) ? TRUE : FALSE
return data
/obj/machinery/atmospherics/unary/cryo_cell/Topic(href, href_list)
if(usr == occupant)
return 0 // don't update UIs attached to this object
/obj/machinery/atmospherics/unary/cryo_cell/tgui_act(action, params)
if(..() || usr == occupant)
return
if(stat & (NOPOWER|BROKEN))
return
if(..())
return 0 // don't update UIs attached to this object
if(href_list["switchOn"])
on = TRUE
update_icon()
if(href_list["switchOff"])
on = FALSE
update_icon()
if(href_list["auto_eject_healthy_on"])
auto_eject_prefs |= AUTO_EJECT_HEALTHY
if(href_list["auto_eject_healthy_off"])
auto_eject_prefs &= ~AUTO_EJECT_HEALTHY
if(href_list["auto_eject_dead_on"])
auto_eject_prefs |= AUTO_EJECT_DEAD
if(href_list["auto_eject_dead_off"])
auto_eject_prefs &= ~AUTO_EJECT_DEAD
if(href_list["ejectBeaker"])
if(beaker)
. = TRUE
switch(action)
if("switchOn")
on = TRUE
update_icon()
if("switchOff")
on = FALSE
update_icon()
if("auto_eject_healthy_on")
auto_eject_prefs |= AUTO_EJECT_HEALTHY
if("auto_eject_healthy_off")
auto_eject_prefs &= ~AUTO_EJECT_HEALTHY
if("auto_eject_dead_on")
auto_eject_prefs |= AUTO_EJECT_DEAD
if("auto_eject_dead_off")
auto_eject_prefs &= ~AUTO_EJECT_DEAD
if("ejectBeaker")
if(!beaker)
return
beaker.forceMove(get_step(loc, SOUTH))
beaker = null
if(href_list["ejectOccupant"])
if(!occupant || isslime(usr) || ispAI(usr))
return 0 // don't update UIs attached to this object
add_attack_logs(usr, occupant, "ejected from cryo cell at [COORD(src)]", ATKLOG_ALL)
go_out()
if("ejectOccupant")
if(!occupant || isslime(usr) || ispAI(usr))
return
add_attack_logs(usr, occupant, "ejected from cryo cell at [COORD(src)]", ATKLOG_ALL)
go_out()
else
return FALSE
add_fingerprint(usr)
return 1 // update UIs attached to this object
/obj/machinery/atmospherics/unary/cryo_cell/attackby(var/obj/item/G as obj, var/mob/user as mob, params)
/obj/machinery/atmospherics/unary/cryo_cell/attackby(var/obj/item/G, var/mob/user, params)
if(istype(G, /obj/item/reagent_containers/glass))
var/obj/item/reagent_containers/B = G
if(beaker)
@@ -313,6 +292,7 @@
beaker = B
add_attack_logs(user, null, "Added [B] containing [B.reagents.log_list()] to a cryo cell at [COORD(src)]")
user.visible_message("[user] adds \a [B] to [src]!", "You add \a [B] to [src]!")
SStgui.update_uis(src)
return
if(exchange_parts(user, G))
@@ -357,7 +337,7 @@
return
if(occupant)
var/image/pickle = image(occupant.icon, occupant.icon_state)
var/mutable_appearance/pickle = mutable_appearance(occupant.icon, occupant.icon_state)
pickle.overlays = occupant.overlays
pickle.pixel_y = 22
@@ -455,8 +435,9 @@
playsound(loc, 'sound/machines/ding.ogg', 50, 1)
if(AUTO_EJECT_DEAD)
playsound(loc, 'sound/machines/buzz-sigh.ogg', 40)
SStgui.update_uis(src)
/obj/machinery/atmospherics/unary/cryo_cell/proc/put_mob(mob/living/carbon/M as mob)
/obj/machinery/atmospherics/unary/cryo_cell/proc/put_mob(mob/living/carbon/M)
if(!istype(M))
to_chat(usr, "<span class='danger'>The cryo cell cannot handle such a lifeform!</span>")
return
@@ -530,7 +511,7 @@
/datum/data/function/proc/reset()
return
/datum/data/function/proc/r_input(href, href_list, mob/user as mob)
/datum/data/function/proc/r_input(href, href_list, mob/user)
return
/datum/data/function/proc/display()
+14
View File
@@ -309,6 +309,20 @@ GLOBAL_LIST_EMPTY(asset_datums)
/datum/asset/chem_master/send(client)
send_asset_list(client, assets, verify)
//Cloning pod sprites for UIs
/datum/asset/cloning
var/assets = list()
var/verify = FALSE
/datum/asset/cloning/register()
assets["pod_idle.gif"] = icon('icons/obj/cloning.dmi', "pod_idle")
assets["pod_cloning.gif"] = icon('icons/obj/cloning.dmi', "pod_cloning")
assets["pod_mess.gif"] = icon('icons/obj/cloning.dmi', "pod_mess")
for(var/asset_name in assets)
register_asset(asset_name, assets[asset_name])
/datum/asset/cloning/send(client)
send_asset_list(client, assets, verify)
//Pipe sprites for UIs
/datum/asset/rpd
@@ -24,6 +24,7 @@
var/list/hacked_reagents = list("toxin")
var/hack_message = "You disable the safety safeguards, enabling the \"Mad Scientist\" mode."
var/unhack_message = "You re-enable the safety safeguards, enabling the \"NT Standard\" mode."
var/is_drink = FALSE
/obj/machinery/chem_dispenser/get_cell()
return cell
@@ -120,7 +121,6 @@
var/usedpower = cell.give(recharge_amount)
if(usedpower)
use_power(15 * recharge_amount)
SSnanoui.update_uis(src) // update all UIs attached to src
recharge_counter = 0
return
recharge_counter++
@@ -131,7 +131,6 @@
else
spawn(rand(0, 15))
stat |= NOPOWER
SSnanoui.update_uis(src) // update all UIs attached to src
/obj/machinery/chem_dispenser/ex_act(severity)
if(severity < 3)
@@ -145,19 +144,17 @@
beaker = null
overlays.Cut()
/obj/machinery/chem_dispenser/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1)
/obj/machinery/chem_dispenser/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state)
// update the ui if it exists, returns null if no ui is passed/found
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
// the ui does not exist, so we'll create a new() one
// for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm
ui = new(user, src, ui_key, "chem_dispenser.tmpl", ui_title, 390, 655)
// open the new ui window
ui = new(user, src, ui_key, "ChemDispenser", ui_title, 390, 655)
ui.open()
/obj/machinery/chem_dispenser/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state)
/obj/machinery/chem_dispenser/tgui_data(mob/user)
var/data[0]
data["glass"] = is_drink
data["amount"] = amount
data["energy"] = cell.charge ? cell.charge * powerefficiency : "0" //To prevent NaN in the UI.
data["maxEnergy"] = cell.maxcharge * powerefficiency
@@ -187,63 +184,59 @@
return data
/obj/machinery/chem_dispenser/Topic(href, href_list)
/obj/machinery/chem_dispenser/tgui_act(actions, params)
if(..())
return TRUE
return
if(stat & (NOPOWER|BROKEN))
return
if(href_list["amount"])
amount = round(text2num(href_list["amount"]), 1) // round to nearest 1
if(amount < 0) // Since the user can actually type the commands himself, some sanity checking
amount = 0
if(amount > 100)
amount = 100
if(href_list["dispense"])
if(!is_operational() || QDELETED(cell))
return
if(beaker && dispensable_reagents.Find(href_list["dispense"]))
. = TRUE
switch(actions)
if("amount")
amount = clamp(round(text2num(params["amount"]), 1), 0, 50) // round to nearest 1 and clamp to 0 - 50
if("dispense")
if(!is_operational() || QDELETED(cell))
return
if(!beaker || !dispensable_reagents.Find(params["reagent"]))
return
var/datum/reagents/R = beaker.reagents
var/free = R.maximum_volume - R.total_volume
var/actual = min(amount, (cell.charge * powerefficiency) * 10, free)
if(!cell.use(actual / powerefficiency))
atom_say("Not enough energy to complete operation!")
return
R.add_reagent(href_list["dispense"], actual)
R.add_reagent(params["reagent"], actual)
overlays.Cut()
if(!icon_beaker)
icon_beaker = image('icons/obj/chemical.dmi', src, "disp_beaker") //randomize beaker overlay position.
icon_beaker = mutable_appearance('icons/obj/chemical.dmi', "disp_beaker") //randomize beaker overlay position.
icon_beaker.pixel_x = rand(-10, 5)
overlays += icon_beaker
if(href_list["remove"])
if(beaker)
if(href_list["removeamount"])
var/amount = text2num(href_list["removeamount"])
if(isnum(amount) && (amount > 0))
var/datum/reagents/R = beaker.reagents
var/id = href_list["remove"]
R.remove_reagent(id, amount)
else if(isnum(amount) && (amount == -1)) //Isolate instead
var/datum/reagents/R = beaker.reagents
var/id = href_list["remove"]
R.isolate_reagent(id)
if(href_list["ejectBeaker"])
if(beaker)
if("remove")
var/amount = text2num(params["amount"])
if(!beaker || !amount)
return
var/datum/reagents/R = beaker.reagents
var/id = params["reagent"]
if(amount > 0)
R.remove_reagent(id, amount)
else if(amount == -1) //Isolate instead
R.isolate_reagent(id)
if("ejectBeaker")
if(!beaker)
return
beaker.forceMove(loc)
if(Adjacent(usr) && !issilicon(usr))
usr.put_in_hands(beaker)
beaker = null
overlays.Cut()
else
return FALSE
add_fingerprint(usr)
return TRUE // update UIs attached to this object
/obj/machinery/chem_dispenser/attackby(obj/item/I, mob/user, params)
if(exchange_parts(user, I))
SSnanoui.update_uis(src)
SStgui.update_uis(src)
return
if(isrobot(user))
@@ -263,9 +256,9 @@
beaker = I
I.forceMove(src)
to_chat(user, "<span class='notice'>You set [I] on the machine.</span>")
SSnanoui.update_uis(src) // update all UIs attached to src
SStgui.update_uis(src) // update all UIs attached to src
if(!icon_beaker)
icon_beaker = image('icons/obj/chemical.dmi', src, "disp_beaker") //randomize beaker overlay position.
icon_beaker = mutable_appearance('icons/obj/chemical.dmi', "disp_beaker") //randomize beaker overlay position.
icon_beaker.pixel_x = rand(-10, 5)
overlays += icon_beaker
return
@@ -299,8 +292,7 @@
to_chat(user, unhack_message)
dispensable_reagents -= hacked_reagents
hackedcheck = FALSE
SSnanoui.update_uis(src)
SStgui.update_uis(src)
/obj/machinery/chem_dispenser/screwdriver_act(mob/user, obj/item/I)
if(default_deconstruction_screwdriver(user, "[initial(icon_state)]-o", "[initial(icon_state)]", I))
@@ -321,14 +313,14 @@
return attack_hand(user)
/obj/machinery/chem_dispenser/attack_ghost(mob/user)
if(user.can_admin_interact())
return attack_hand(user)
if(stat & BROKEN)
return
tgui_interact(user)
/obj/machinery/chem_dispenser/attack_hand(mob/user)
if(stat & BROKEN)
return
ui_interact(user)
tgui_interact(user)
/obj/machinery/chem_dispenser/soda
icon_state = "soda_dispenser"
@@ -342,6 +334,7 @@
hacked_reagents = list("thirteenloko")
hack_message = "You change the mode from 'McNano' to 'Pizza King'."
unhack_message = "You change the mode from 'Pizza King' to 'McNano'."
is_drink = TRUE
/obj/machinery/chem_dispenser/soda/New()
..()
@@ -377,6 +370,7 @@
hacked_reagents = list("goldschlager", "patron", "absinthe", "ethanol", "nothing", "sake")
hack_message = "You disable the 'nanotrasen-are-cheap-bastards' lock, enabling hidden and very expensive boozes."
unhack_message = "You re-enable the 'nanotrasen-are-cheap-bastards' lock, disabling hidden and very expensive boozes."
is_drink = TRUE
/obj/machinery/chem_dispenser/beer/New()
..()
@@ -1,15 +1,19 @@
/obj/machinery/chem_heater
name = "chemical heater"
density = 1
anchored = 1
density = TRUE
anchored = TRUE
icon = 'icons/obj/chemical.dmi'
icon_state = "mixer0b"
use_power = IDLE_POWER_USE
idle_power_usage = 40
resistance_flags = FIRE_PROOF | ACID_PROOF
resistance_flags = FIRE_PROOF|ACID_PROOF
var/obj/item/reagent_containers/beaker = null
var/desired_temp = T0C
var/on = FALSE
/// Whether this should auto-eject the beaker once done heating/cooling.
var/auto_eject = FALSE
/// The higher this number, the faster reagents will heat/cool.
var/speed_increase = 0
/obj/machinery/chem_heater/New()
..()
@@ -19,35 +23,36 @@
component_parts += new /obj/item/stack/sheet/glass(null)
RefreshParts()
/obj/machinery/chem_heater/RefreshParts()
speed_increase = initial(speed_increase)
for(var/obj/item/stock_parts/micro_laser/M in component_parts)
speed_increase += 5 * (M.rating - 1)
/obj/machinery/chem_heater/process()
..()
if(stat & NOPOWER)
if(stat & (NOPOWER|BROKEN))
return
var/state_change = FALSE
if(on)
if(beaker)
if(!beaker.reagents.total_volume)
on = FALSE
SSnanoui.update_uis(src)
return
beaker.reagents.temperature_reagents(desired_temp)
beaker.reagents.temperature_reagents(desired_temp)
if(abs(beaker.reagents.chem_temp - desired_temp) <= 3)
beaker.reagents.temperature_reagents(desired_temp, max(1, 35 - speed_increase))
if(round(beaker.reagents.chem_temp) == round(desired_temp))
playsound(loc, 'sound/machines/ding.ogg', 50, 1)
on = FALSE
state_change = TRUE
if(state_change)
SSnanoui.update_uis(src)
if(auto_eject)
eject_beaker()
/obj/machinery/chem_heater/proc/eject_beaker(mob/user)
if(beaker)
beaker.forceMove(get_turf(src))
if(Adjacent(user) && !issilicon(user))
if(user && Adjacent(user) && !issilicon(user))
user.put_in_hands(beaker)
beaker = null
icon_state = "mixer0b"
on = FALSE
SSnanoui.update_uis(src)
SStgui.update_uis(src)
/obj/machinery/chem_heater/power_change()
if(powered())
@@ -55,7 +60,6 @@
else
spawn(rand(0, 15))
stat |= NOPOWER
SSnanoui.update_uis(src)
/obj/machinery/chem_heater/attackby(obj/item/I, mob/user)
if(isrobot(user))
@@ -71,7 +75,7 @@
I.forceMove(src)
to_chat(user, "<span class='notice'>You add the beaker to the machine!</span>")
icon_state = "mixer1b"
SSnanoui.update_uis(src)
SStgui.update_uis(src)
return
if(exchange_parts(user, I))
@@ -95,62 +99,62 @@
default_deconstruction_crowbar(user, I)
/obj/machinery/chem_heater/attack_hand(mob/user)
ui_interact(user)
tgui_interact(user)
/obj/machinery/chem_heater/attack_ghost(mob/user)
if(user.can_admin_interact())
return attack_hand(user)
tgui_interact(user)
/obj/machinery/chem_heater/attack_ai(mob/user)
add_hiddenprint(user)
return attack_hand(user)
/obj/machinery/chem_heater/Topic(href, href_list)
/obj/machinery/chem_heater/tgui_act(action, params)
if(..())
return FALSE
return
if(stat & (NOPOWER|BROKEN))
return
if(href_list["toggle_on"])
if(!beaker.reagents.total_volume)
return FALSE
on = !on
. = 1
if(href_list["adjust_temperature"])
var/val = href_list["adjust_temperature"]
if(isnum(val))
desired_temp = clamp(desired_temp+val, 0, 1000)
else if(val == "input")
var/target = input("Please input the target temperature", name) as num
desired_temp = clamp(target, 0, 1000)
. = TRUE
switch(action)
if("toggle_on")
on = !on
if("adjust_temperature")
desired_temp = clamp(text2num(params["target"]), 0, 1000)
if("eject_beaker")
eject_beaker(usr)
. = FALSE
if("toggle_autoeject")
auto_eject = !auto_eject
else
return FALSE
. = 1
add_fingerprint(usr)
if(href_list["eject_beaker"])
eject_beaker(usr)
. = 0 //updated in eject_beaker() already
/obj/machinery/chem_heater/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null)
/obj/machinery/chem_heater/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state)
if(user.stat || user.restrained())
return
// update the ui if it exists, returns null if no ui is passed/found
ui = SSnanoui.try_update_ui(user, src, ui_key, ui)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "chem_heater.tmpl", "ChemHeater", 350, 270)
ui = new(user, src, ui_key, "ChemHeater", "Chemical Heater", 350, 270, master_ui, state)
ui.open()
/obj/machinery/chem_heater/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state)
/obj/machinery/chem_heater/tgui_data(mob/user)
var/data[0]
var/cur_temp = beaker ? beaker.reagents.chem_temp : null
data["targetTemp"] = desired_temp
data["targetTempReached"] = FALSE
data["autoEject"] = auto_eject
data["isActive"] = on
data["isBeakerLoaded"] = beaker ? 1 : 0
data["isBeakerLoaded"] = beaker ? TRUE : FALSE
data["currentTemp"] = beaker ? beaker.reagents.chem_temp : null
data["currentTemp"] = cur_temp
data["beakerCurrentVolume"] = beaker ? beaker.reagents.total_volume : null
data["beakerMaxVolume"] = beaker ? beaker.volume : null
if(cur_temp)
data["targetTempReached"] = round(cur_temp) == round(desired_temp)
//copy-pasted from chem dispenser
var/beakerContents[0]
if(beaker)
@@ -1,3 +1,9 @@
#define MAX_PILL_SPRITE 20 //max icon state of the pill sprites
#define MAX_MULTI_AMOUNT 20 // Max number of pills/patches that can be made at once
#define MAX_UNITS_PER_PILL 100 // Max amount of units in a pill
#define MAX_UNITS_PER_PATCH 30 // Max amount of units in a patch
#define MAX_CUSTOM_NAME_LEN 64 // Max length of a custom pill/condiment/whatever
/obj/machinery/chem_master
name = "\improper ChemMaster 3000"
density = TRUE
@@ -14,10 +20,12 @@
var/useramount = 30 // Last used amount
var/pillamount = 10
var/patchamount = 10
var/bottlesprite = "bottle"
var/pillsprite = "1"
var/bottlesprite = 1
var/pillsprite = 1
var/client/has_sprites = list()
var/printing = FALSE
var/static/list/pill_bottle_wrappers
var/static/list/bottle_styles
/obj/machinery/chem_master/New()
..()
@@ -94,7 +102,7 @@
beaker = I
I.forceMove(src)
to_chat(user, "<span class='notice'>You add the beaker to the machine!</span>")
SSnanoui.update_uis(src)
SStgui.update_uis(src)
update_icon()
else if(istype(I, /obj/item/storage/pill_bottle))
@@ -109,14 +117,10 @@
loaded_pill_bottle = I
I.forceMove(src)
to_chat(user, "<span class='notice'>You add [I] into the dispenser slot!</span>")
SSnanoui.update_uis(src)
SStgui.update_uis(src)
else
return ..()
/obj/machinery/chem_master/crowbar_act(mob/user, obj/item/I)
if(!panel_open)
return
@@ -142,319 +146,127 @@
power_change()
return TRUE
/obj/machinery/chem_master/Topic(href, href_list)
/obj/machinery/chem_master/tgui_act(action, params, datum/tgui/ui, datum/tgui_state/state)
if(..())
return
if(stat & (NOPOWER|BROKEN))
return
if(tgui_act_modal(action, params, ui, state))
return TRUE
add_fingerprint(usr)
usr.set_machine(src)
if(href_list["ejectp"])
if(loaded_pill_bottle)
loaded_pill_bottle.forceMove(loc)
loaded_pill_bottle = null
else if(href_list["change_pillbottle"])
if(loaded_pill_bottle)
var/list/wrappers = list("Default wrapper", "Red wrapper", "Green wrapper", "Pale green wrapper", "Blue wrapper", "Light blue wrapper", "Teal wrapper", "Yellow wrapper", "Orange wrapper", "Pink wrapper", "Brown wrapper")
var/chosen = input(usr, "Select a pillbottle wrapper", "Pillbottle wrapper", wrappers[1]) as null|anything in wrappers
if(!chosen)
. = TRUE
switch(action)
if("toggle")
mode = !mode
if("ejectp")
if(loaded_pill_bottle)
loaded_pill_bottle.forceMove(loc)
loaded_pill_bottle = null
if("print")
if(printing || condi)
return
var/color
switch(chosen)
if("Default wrapper")
loaded_pill_bottle.cut_overlays()
return
if("Red wrapper")
color = COLOR_RED
if("Green wrapper")
color = COLOR_GREEN
if("Pink wrapper")
color = COLOR_PINK
if("Teal wrapper")
color = COLOR_TEAL
if("Blue wrapper")
color = COLOR_BLUE
if("Brown wrapper")
color = COLOR_MAROON
if("Light blue wrapper")
color = COLOR_CYAN_BLUE
if("Yellow wrapper")
color = COLOR_YELLOW
if("Pale green wrapper")
color = COLOR_PALE_BTL_GREEN
if("Orange wrapper")
color = COLOR_ORANGE
loaded_pill_bottle.wrapper_color = color
loaded_pill_bottle.apply_wrap()
else if(href_list["close"])
usr << browse(null, "window=chem_master")
onclose(usr, "chem_master")
usr.unset_machine()
return
if(href_list["print_p"])
if(!printing)
var/idx = text2num(params["idx"]) || 0
var/from_beaker = text2num(params["beaker"]) || FALSE
var/reagent_list = from_beaker ? beaker.reagents.reagent_list : reagents.reagent_list
if(idx < 1 || idx > length(reagent_list))
return
var/datum/reagent/R = reagent_list[idx]
printing = TRUE
visible_message("<span class='notice'>[src] rattles and prints out a sheet of paper.</span>")
playsound(loc, 'sound/goonstation/machines/printer_dotmatrix.ogg', 50, 1)
var/obj/item/paper/P = new /obj/item/paper(loc)
P.info = "<CENTER><B>Chemical Analysis</B></CENTER><BR>"
P.info = "<center><b>Chemical Analysis</b></center><br>"
P.info += "<b>Time of analysis:</b> [station_time_timestamp()]<br><br>"
P.info += "<b>Chemical name:</b> [href_list["name"]]<br>"
if(href_list["name"] == "Blood")
var/datum/reagents/R = beaker.reagents
var/datum/reagent/blood/G
for(var/datum/reagent/F in R.reagent_list)
if(F.name == href_list["name"])
G = F
break
var/B = G.data["blood_type"]
var/C = G.data["blood_DNA"]
P.info += "<b>Description:</b><br>Blood Type: [B]<br>DNA: [C]"
P.info += "<b>Chemical name:</b> [R.name]<br>"
if(istype(R, /datum/reagent/blood))
var/datum/reagent/blood/B = R
P.info += "<b>Description:</b> N/A<br><b>Blood Type:</b> [B.data["blood_type"]]<br><b>DNA:</b> [B.data["blood_DNA"]]"
else
P.info += "<b>Description:</b> [href_list["desc"]]"
P.info += "<b>Description:</b> [R.description]"
P.info += "<br><br><b>Notes:</b><br>"
P.name = "Chemical Analysis - [href_list["name"]]"
printing = FALSE
P.name = "Chemical Analysis - [R.name]"
spawn(50)
printing = FALSE
else
. = FALSE
if(beaker)
var/datum/reagents/R = beaker.reagents
if(href_list["analyze"])
var/dat = ""
if(!condi)
if(href_list["name"] == "Blood")
var/datum/reagent/blood/G
for(var/datum/reagent/F in R.reagent_list)
if(F.name == href_list["name"])
G = F
break
var/A = G.name
var/B = G.data["blood_type"]
var/C = G.data["blood_DNA"]
dat += "<TITLE>Chemmaster 3000</TITLE>Chemical infos:<BR><BR>Name:<BR>[A]<BR><BR>Description:<BR>Blood Type: [B]<br>DNA: [C]"
else
dat += "<TITLE>Chemmaster 3000</TITLE>Chemical infos:<BR><BR>Name:<BR>[href_list["name"]]<BR><BR>Description:<BR>[href_list["desc"]]"
dat += "<BR><BR><A href='?src=[UID()];print_p=1;desc=[href_list["desc"]];name=[href_list["name"]]'>(Print Analysis)</A><BR>"
dat += "<A href='?src=[UID()];main=1'>(Back)</A>"
if(. || !beaker)
return
. = TRUE
var/datum/reagents/R = beaker.reagents
switch(action)
if("add")
var/id = params["id"]
var/amount = text2num(params["amount"])
if(!id || !amount)
return
R.trans_id_to(src, id, amount)
if("remove")
var/id = params["id"]
var/amount = text2num(params["amount"])
if(!id || !amount)
return
if(mode)
reagents.trans_id_to(beaker, id, amount)
else
dat += "<TITLE>Condimaster 3000</TITLE>Condiment infos:<BR><BR>Name:<BR>[href_list["name"]]<BR><BR>Description:<BR>[href_list["desc"]]<BR><BR><BR><A href='?src=[UID()];main=1'>(Back)</A>"
usr << browse(dat, "window=chem_master;size=575x500")
return
else if(href_list["add"])
if(href_list["amount"])
var/id = href_list["add"]
var/amount = text2num(href_list["amount"])
R.trans_id_to(src, id, amount)
else if(href_list["addcustom"])
var/id = href_list["addcustom"]
useramount = input("Select the amount to transfer.", 30, useramount) as num
useramount = isgoodnumber(useramount)
Topic(null, list("amount" = "[useramount]", "add" = "[id]"))
else if(href_list["remove"])
if(href_list["amount"])
var/id = href_list["remove"]
var/amount = text2num(href_list["amount"])
if(mode)
reagents.trans_id_to(beaker, id, amount)
else
reagents.remove_reagent(id, amount)
else if(href_list["removecustom"])
var/id = href_list["removecustom"]
useramount = input("Select the amount to transfer.", 30, useramount) as num
useramount = isgoodnumber(useramount)
Topic(null, list("amount" = "[useramount]", "remove" = "[id]"))
else if(href_list["toggle"])
mode = !mode
else if(href_list["main"])
attack_hand(usr)
return
else if(href_list["eject"])
if(beaker)
beaker.forceMove(get_turf(src))
if(Adjacent(usr) && !issilicon(usr))
usr.put_in_hands(beaker)
beaker = null
reagents.clear_reagents()
update_icon()
else if(href_list["createpill"] || href_list["createpill_multiple"])
if(!condi)
var/count = 1
if(href_list["createpill_multiple"])
count = input("Select the number of pills to make.", 10, pillamount) as num|null
if(count == null)
return
count = isgoodnumber(count)
if(count > 20)
count = 20 //Pevent people from creating huge stacks of pills easily. Maybe move the number to defines?
if(count <= 0)
return
var/amount_per_pill = reagents.total_volume / count
if(amount_per_pill > 100)
amount_per_pill = 100
var/name = clean_input("Name:","Name your pill!","[reagents.get_master_reagent_name()] ([amount_per_pill]u)")
if(!name)
return
name = reject_bad_text(name)
while(count--)
if(reagents.total_volume <= 0)
to_chat(usr, "<span class='notice'>Not enough reagents to create these pills!</span>")
return
var/obj/item/reagent_containers/food/pill/P = new/obj/item/reagent_containers/food/pill(loc)
if(!name)
name = reagents.get_master_reagent_name()
P.name = "[name] pill"
P.pixel_x = rand(-7, 7) //random position
P.pixel_y = rand(-7, 7)
P.icon_state = "pill"+pillsprite
reagents.trans_to(P, amount_per_pill)
if(loaded_pill_bottle && loaded_pill_bottle.type == /obj/item/storage/pill_bottle)
if(loaded_pill_bottle.contents.len < loaded_pill_bottle.storage_slots)
P.forceMove(loaded_pill_bottle)
updateUsrDialog()
else
var/name = clean_input("Name:", "Name your bag!", reagents.get_master_reagent_name())
if(!name)
return
name = reject_bad_text(name)
var/obj/item/reagent_containers/food/condiment/pack/P = new/obj/item/reagent_containers/food/condiment/pack(loc)
if(!name) name = reagents.get_master_reagent_name()
P.originalname = name
P.name = "[name] pack"
P.desc = "A small condiment pack. The label says it contains [name]."
reagents.trans_to(P, 10)
else if(href_list["createpatch"] || href_list["createpatch_multiple"])
if(!condi)
var/count = 1
if(href_list["createpatch_multiple"])
count = input("Select the number of patches to make.", 10, patchamount) as num|null
if(count == null)
return
count = isgoodnumber(count)
if(!count || count <= 0)
return
if(count > 20)
count = 20 //Pevent people from creating huge stacks of patches easily. Maybe move the number to defines?
var/amount_per_patch = reagents.total_volume/count
if(amount_per_patch > 30)
amount_per_patch = 30
var/name = clean_input("Name:", "Name your patch!", "[reagents.get_master_reagent_name()] ([amount_per_patch]u)")
if(!name)
return
name = reject_bad_text(name)
var/is_medical_patch = chemical_safety_check(reagents)
while(count--)
var/obj/item/reagent_containers/food/pill/patch/P = new/obj/item/reagent_containers/food/pill/patch(loc)
if(!name) name = reagents.get_master_reagent_name()
P.name = "[name] patch"
P.pixel_x = rand(-7, 7) //random position
P.pixel_y = rand(-7, 7)
reagents.trans_to(P,amount_per_patch)
if(is_medical_patch)
P.instant_application = TRUE
P.icon_state = "bandaid_med"
if(loaded_pill_bottle && loaded_pill_bottle.type == /obj/item/storage/pill_bottle/patch_pack)
if(loaded_pill_bottle.contents.len < loaded_pill_bottle.storage_slots)
P.forceMove(loaded_pill_bottle)
updateUsrDialog()
else if(href_list["createbottle"])
if(!condi)
var/name = clean_input("Name:", "Name your bottle!", reagents.get_master_reagent_name())
if(!name)
return
name = reject_bad_text(name)
var/obj/item/reagent_containers/glass/bottle/reagent/P = new/obj/item/reagent_containers/glass/bottle/reagent(loc)
if(!name)
name = reagents.get_master_reagent_name()
P.name = "[name] bottle"
P.pixel_x = rand(-7, 7) //random position
P.pixel_y = rand(-7, 7)
P.icon_state = bottlesprite
reagents.trans_to(P, 50)
else
var/obj/item/reagent_containers/food/condiment/P = new/obj/item/reagent_containers/food/condiment(loc)
reagents.trans_to(P, 50)
else if(href_list["change_pill"])
#define MAX_PILL_SPRITE 20 //max icon state of the pill sprites
var/dat = "<table>"
var/j = 0
for(var/i = 1 to MAX_PILL_SPRITE)
j++
if(j == 1)
dat += "<tr>"
dat += "<td><a href=\"?src=[UID()]&pill_sprite=[i]\"><img src=\"pill[i].png\" /></a></td>"
if(j == 5)
dat += "</tr>"
j = 0
dat += "</table>"
usr << browse(dat, "window=chem_master_iconsel;size=225x193")
return
else if(href_list["change_bottle"])
var/dat = "<table>"
var/j = 0
for(var/i in list("bottle", "small_bottle", "wide_bottle", "round_bottle", "reagent_bottle"))
j++
if(j == 1)
dat += "<tr>"
dat += "<td><a href=\"?src=[UID()]&bottle_sprite=[i]\"><img src=\"[i].png\" /></a></td>"
if(j == 5)
dat += "</tr>"
j = 0
dat += "</table>"
usr << browse(dat, "window=chem_master_iconsel;size=225x193")
return
else if(href_list["pill_sprite"])
pillsprite = href_list["pill_sprite"]
usr << browse(null, "window=chem_master_iconsel")
else if(href_list["bottle_sprite"])
bottlesprite = href_list["bottle_sprite"]
usr << browse(null, "window=chem_master_iconsel")
SSnanoui.update_uis(src)
reagents.remove_reagent(id, amount)
if("eject")
if(!beaker)
return
beaker.forceMove(get_turf(src))
if(Adjacent(usr) && !issilicon(usr))
usr.put_in_hands(beaker)
beaker = null
reagents.clear_reagents()
update_icon()
if("create_condi_bottle")
if(!condi || !reagents.total_volume)
return
var/obj/item/reagent_containers/food/condiment/P = new(loc)
reagents.trans_to(P, 50)
else
return FALSE
/obj/machinery/chem_master/attack_ai(mob/user)
return attack_hand(user)
/obj/machinery/chem_master/attack_ghost(mob/user)
if(user.can_admin_interact())
return attack_hand(user)
tgui_interact(user)
/obj/machinery/chem_master/attack_hand(mob/user)
if(..())
return TRUE
ui_interact(user)
tgui_interact(user)
/obj/machinery/chem_master/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = TRUE)
/obj/machinery/chem_master/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state)
var/datum/asset/chem_master/assets = get_asset_datum(/datum/asset/chem_master)
assets.send(user)
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "chem_master.tmpl", name, 575, 500)
ui = new(user, src, ui_key, "ChemMaster", name, 575, 500)
ui.open()
/obj/machinery/chem_master/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state)
/obj/machinery/chem_master/tgui_data(mob/user)
var/data[0]
data["condi"] = condi
data["loaded_pill_bottle"] = (loaded_pill_bottle ? 1 : 0)
data["loaded_pill_bottle"] = loaded_pill_bottle ? TRUE : FALSE
if(loaded_pill_bottle)
data["loaded_pill_bottle_name"] = loaded_pill_bottle.name
data["loaded_pill_bottle_contents_len"] = loaded_pill_bottle.contents.len
data["loaded_pill_bottle_storage_slots"] = loaded_pill_bottle.storage_slots
data["beaker"] = (beaker ? 1 : 0)
data["beaker"] = beaker ? TRUE : FALSE
if(beaker)
var/list/beaker_reagents_list = list()
data["beaker_reagents"] = beaker_reagents_list
@@ -464,13 +276,255 @@
data["buffer_reagents"] = buffer_reagents_list
for(var/datum/reagent/R in reagents.reagent_list)
buffer_reagents_list[++buffer_reagents_list.len] = list("name" = R.name, "volume" = R.volume, "id" = R.id, "description" = R.description)
else
data["beaker_reagents"] = list()
data["buffer_reagents"] = list()
data["pillsprite"] = pillsprite
data["bottlesprite"] = bottlesprite
data["mode"] = mode
data["printing"] = printing
// Transfer modal information if there is one
data["modal"] = tgui_modal_data(src)
return data
/**
* Called in tgui_act() to process modal actions
*
* Arguments:
* * action - The action passed by tgui
* * params - The params passed by tgui
*/
/obj/machinery/chem_master/proc/tgui_act_modal(action, params, datum/tgui/ui, datum/tgui_state/state)
. = TRUE
var/id = params["id"] // The modal's ID
var/list/arguments = istext(params["arguments"]) ? json_decode(params["arguments"]) : params["arguments"]
switch(tgui_modal_act(src, action, params))
if(TGUI_MODAL_OPEN)
switch(id)
if("analyze")
var/idx = text2num(arguments["idx"]) || 0
var/from_beaker = text2num(arguments["beaker"]) || FALSE
var/reagent_list = from_beaker ? beaker.reagents.reagent_list : reagents.reagent_list
if(idx < 1 || idx > length(reagent_list))
return
var/datum/reagent/R = reagent_list[idx]
var/list/result = list("idx" = idx, "name" = R.name, "desc" = R.description)
if(!condi && istype(R, /datum/reagent/blood))
var/datum/reagent/blood/B = R
result["blood_type"] = B.data["blood_type"]
result["blood_dna"] = B.data["blood_DNA"]
arguments["analysis"] = result
tgui_modal_message(src, id, "", null, arguments)
if("change_pill_bottle_style")
if(!loaded_pill_bottle)
return
if(!pill_bottle_wrappers)
pill_bottle_wrappers = list(
"CLEAR" = "Default",
COLOR_RED = "Red",
COLOR_GREEN = "Green",
COLOR_PALE_BTL_GREEN = "Pale green",
COLOR_BLUE = "Blue",
COLOR_CYAN_BLUE = "Light blue",
COLOR_TEAL = "Teal",
COLOR_YELLOW = "Yellow",
COLOR_ORANGE = "Orange",
COLOR_PINK = "Pink",
COLOR_MAROON = "Brown"
)
var/current = pill_bottle_wrappers[loaded_pill_bottle.wrapper_color] || "Default"
tgui_modal_choice(src, id, "Please select a pill bottle wrapper:", null, arguments, current, pill_bottle_wrappers)
if("addcustom")
if(!beaker || !beaker.reagents.total_volume)
return
tgui_modal_input(src, id, "Please enter the amount to transfer to buffer:", null, arguments, useramount)
if("removecustom")
if(!reagents.total_volume)
return
tgui_modal_input(src, id, "Please enter the amount to transfer to [mode ? "beaker" : "disposal"]:", null, arguments, useramount)
if("create_condi_pack")
if(!condi || !reagents.total_volume)
return
tgui_modal_input(src, id, "Please name your new condiment pack:", null, arguments, reagents.get_master_reagent_name(), MAX_CUSTOM_NAME_LEN)
if("create_pill")
if(condi || !reagents.total_volume)
return
var/num = round(text2num(arguments["num"] || 1))
if(!num)
return
arguments["num"] = num
var/amount_per_pill = clamp(reagents.total_volume / num, 0, MAX_UNITS_PER_PILL)
var/default_name = "[reagents.get_master_reagent_name()] ([amount_per_pill]u)"
var/pills_text = num == 1 ? "new pill" : "[num] new pills"
tgui_modal_input(src, id, "Please name your [pills_text]:", null, arguments, default_name, MAX_CUSTOM_NAME_LEN)
if("create_pill_multiple")
if(condi || !reagents.total_volume)
return
tgui_modal_input(src, id, "Please enter the amount of pills to make (max [MAX_MULTI_AMOUNT] at a time):", null, arguments, pillamount, 5)
if("change_pill_style")
var/list/choices = list()
for(var/i = 1 to MAX_PILL_SPRITE)
choices += "pill[i].png"
tgui_modal_bento(src, id, "Please select the new style for pills:", null, arguments, pillsprite, choices)
if("create_patch")
if(condi || !reagents.total_volume)
return
var/num = round(text2num(arguments["num"] || 1))
if(!num)
return
arguments["num"] = num
var/amount_per_patch = clamp(reagents.total_volume / num, 0, MAX_UNITS_PER_PATCH)
var/default_name = "[reagents.get_master_reagent_name()] ([amount_per_patch]u)"
var/patches_text = num == 1 ? "new patch" : "[num] new patches"
tgui_modal_input(src, id, "Please name your [patches_text]:", null, arguments, default_name, MAX_CUSTOM_NAME_LEN)
if("create_patch_multiple")
if(condi || !reagents.total_volume)
return
tgui_modal_input(src, id, "Please enter the amount of patches to make (max [MAX_MULTI_AMOUNT] at a time):", null, arguments, pillamount, 5)
if("create_bottle")
if(condi || !reagents.total_volume)
return
tgui_modal_input(src, id, "Please name your bottle:", null, arguments, reagents.get_master_reagent_name(), MAX_CUSTOM_NAME_LEN)
if("change_bottle_style")
if(!bottle_styles)
bottle_styles = list("bottle", "small_bottle", "wide_bottle", "round_bottle", "reagent_bottle")
var/list/bottle_styles_png = list()
for(var/style in bottle_styles)
bottle_styles_png += "[style].png"
tgui_modal_bento(src, id, "Please select the new style for bottles:", null, arguments, bottlesprite, bottle_styles_png)
else
return FALSE
if(TGUI_MODAL_ANSWER)
var/answer = params["answer"]
switch(id)
if("change_pill_bottle_style")
if(!pill_bottle_wrappers || !loaded_pill_bottle) // wat?
return
var/color = "CLEAR"
for(var/col in pill_bottle_wrappers)
var/col_name = pill_bottle_wrappers[col]
if(col_name == answer)
color = col
break
if(length(color) && color != "CLEAR")
loaded_pill_bottle.wrapper_color = color
loaded_pill_bottle.apply_wrap()
else
loaded_pill_bottle.wrapper_color = null
loaded_pill_bottle.cut_overlays()
if("addcustom")
var/amount = isgoodnumber(text2num(answer))
if(!amount || !arguments["id"])
return
tgui_act("add", list("id" = arguments["id"], "amount" = amount), ui, state)
if("removecustom")
var/amount = isgoodnumber(text2num(answer))
if(!amount || !arguments["id"])
return
tgui_act("remove", list("id" = arguments["id"], "amount" = amount), ui, state)
if("create_condi_pack")
if(!condi || !reagents.total_volume)
return
if(!length(answer))
answer = reagents.get_master_reagent_name()
var/obj/item/reagent_containers/food/condiment/pack/P = new(loc)
P.originalname = answer
P.name = "[answer] pack"
P.desc = "A small condiment pack. The label says it contains [answer]."
reagents.trans_to(P, 10)
if("create_pill")
if(condi || !reagents.total_volume)
return
var/count = clamp(round(text2num(arguments["num"]) || 0), 0, MAX_MULTI_AMOUNT)
if(!count)
return
if(!length(answer))
answer = reagents.get_master_reagent_name()
var/amount_per_pill = clamp(reagents.total_volume / count, 0, MAX_UNITS_PER_PILL)
while(count--)
if(reagents.total_volume <= 0)
to_chat(usr, "<span class='notice'>Not enough reagents to create these pills!</span>")
return
var/obj/item/reagent_containers/food/pill/P = new(loc)
P.name = "[answer] pill"
P.pixel_x = rand(-7, 7) // Random position
P.pixel_y = rand(-7, 7)
P.icon_state = "pill[pillsprite]"
reagents.trans_to(P, amount_per_pill)
// Load the pills in the bottle if there's one loaded
if(istype(loaded_pill_bottle) && length(loaded_pill_bottle.contents) < loaded_pill_bottle.storage_slots)
P.forceMove(loaded_pill_bottle)
if("create_pill_multiple")
if(condi || !reagents.total_volume)
return
tgui_act("modal_open", list("id" = "create_pill", "arguments" = list("num" = answer)), ui, state)
if("change_pill_style")
var/new_style = clamp(text2num(answer) || 0, 0, MAX_PILL_SPRITE)
if(!new_style)
return
pillsprite = new_style
if("create_patch")
if(condi || !reagents.total_volume)
return
var/count = clamp(round(text2num(arguments["num"]) || 0), 0, MAX_MULTI_AMOUNT)
if(!count)
return
if(!length(answer))
answer = reagents.get_master_reagent_name()
var/amount_per_patch = clamp(reagents.total_volume / count, 0, MAX_UNITS_PER_PATCH)
var/is_medical_patch = chemical_safety_check(reagents)
while(count--)
if(reagents.total_volume <= 0)
to_chat(usr, "<span class='notice'>Not enough reagents to create these patches!</span>")
return
var/obj/item/reagent_containers/food/pill/patch/P = new(loc)
P.name = "[answer] patch"
P.pixel_x = rand(-7, 7) // random position
P.pixel_y = rand(-7, 7)
reagents.trans_to(P, amount_per_patch)
if(is_medical_patch)
P.instant_application = TRUE
P.icon_state = "bandaid_med"
// Load the patches in the bottle if there's one loaded
if(istype(loaded_pill_bottle, /obj/item/storage/pill_bottle/patch_pack) && length(loaded_pill_bottle.contents) < loaded_pill_bottle.storage_slots)
P.forceMove(loaded_pill_bottle)
if("create_patch_multiple")
if(condi || !reagents.total_volume)
return
tgui_act("modal_open", list("id" = "create_patch", "arguments" = list("num" = answer)), ui, state)
if("create_bottle")
if(condi || !reagents.total_volume)
return
if(!length(answer))
answer = reagents.get_master_reagent_name()
var/obj/item/reagent_containers/glass/bottle/reagent/P = new(loc)
P.name = "[answer] bottle"
P.pixel_x = rand(-7, 7) // random position
P.pixel_y = rand(-7, 7)
P.icon_state = length(bottle_styles) && bottle_styles[bottlesprite] || "bottle"
reagents.trans_to(P, 50)
if("change_bottle_style")
if(!bottle_styles)
return
var/new_sprite = text2num(answer) || 1
if(new_sprite < 1 || new_sprite > length(bottle_styles))
return
bottlesprite = new_sprite
else
return FALSE
else
return FALSE
/obj/machinery/chem_master/proc/isgoodnumber(num)
if(isnum(num))
if(num > 200)
@@ -481,7 +535,7 @@
num = round(num)
return num
else
return 0
return FALSE
/obj/machinery/chem_master/proc/chemical_safety_check(datum/reagents/R)
var/all_safe = TRUE
@@ -503,3 +557,9 @@
component_parts += new /obj/item/reagent_containers/glass/beaker(null)
component_parts += new /obj/item/reagent_containers/glass/beaker(null)
RefreshParts()
#undef MAX_PILL_SPRITE
#undef MAX_MULTI_AMOUNT
#undef MAX_UNITS_PER_PILL
#undef MAX_UNITS_PER_PATCH
#undef MAX_CUSTOM_NAME_LEN
+370
View File
@@ -0,0 +1,370 @@
/**
* tgui modals
*
* Allows creation of modals within tgui.
*/
GLOBAL_LIST(tgui_modals)
/**
* Call this from a proc that is called in tgui_act() to process modal actions
*
* Example: /obj/machinery/chem_master/proc/tgui_act_modal
* You can then switch based on the return value and show different
* modals depending on the answer.
* Arguments:
* * source - The source datum
* * action - The called action
* * params - The params to the action
*/
/datum/proc/tgui_modal_act(datum/source = src, action = "", params)
ASSERT(istype(source))
. = null
switch(action)
if("modal_open") // Params: id, arguments
return TGUI_MODAL_OPEN
if("modal_answer") // Params: id, answer, arguments
params["answer"] = tgui_modal_preprocess_answer(source, params["answer"])
if(tgui_modal_answer(source, params["id"], params["answer"])) // If there's a current modal with a delegate that returned TRUE, no need to continue
. = TGUI_MODAL_DELEGATE
else
. = TGUI_MODAL_ANSWER
tgui_modal_clear(source)
if("modal_close") // Params: id
tgui_modal_clear(source)
return TGUI_MODAL_CLOSE
/**
* Call this from tgui_data() to return modal information if needed
* Arguments:
* * source - The source datum
*/
/datum/proc/tgui_modal_data(datum/source = src)
ASSERT(istype(source))
var/datum/tgui_modal/current = LAZYACCESS(GLOB.tgui_modals, source.UID())
if(!current)
return null
return current.to_data()
/**
* Clears the current modal for a given datum
*
* Arguments:
* * source - The source datum
*/
/datum/proc/tgui_modal_clear(datum/source = src)
ASSERT(istype(source))
LAZYINITLIST(GLOB.tgui_modals)
var/datum/tgui_modal/previous = GLOB.tgui_modals[source.UID()]
if(!previous)
return FALSE
for(var/i in 1 to length(GLOB.tgui_modals))
var/key = GLOB.tgui_modals[i]
if(previous == GLOB.tgui_modals[key])
GLOB.tgui_modals.Cut(i, i + 1)
break
SStgui.update_uis(source)
return TRUE
/**
* Opens a message TGUI modal
*
* Arguments:
* * source - The source datum
* * id - The ID of the modal
* * text - The text to display above the answers
* * delegate - The proc to call when closed
* * arguments - List of arguments passed to and from JS (mostly useful for chaining modals)
*/
/datum/proc/tgui_modal_message(datum/source = src, id, text = "Default modal message", delegate, arguments)
ASSERT(length(id))
var/datum/tgui_modal/modal = new(id, text, delegate, arguments)
return tgui_modal_new(source, modal)
/**
* Opens a text input TGUI modal
*
* Arguments:
* * source - The source datum
* * id - The ID of the modal
* * text - The text to display above the answers
* * delegate - The proc to call when submitted
* * arguments - List of arguments passed to and from JS (mostly useful for chaining modals)
* * value - The default value of the input
* * max_length - The maximum char length of the input
*/
/datum/proc/tgui_modal_input(datum/source = src, id, text = "Default modal message", delegate, arguments, value = "", max_length = TGUI_MODAL_INPUT_MAX_LENGTH)
ASSERT(length(id))
ASSERT(max_length > 0)
var/datum/tgui_modal/input/modal = new(id, text, delegate, arguments, value, max_length)
return tgui_modal_new(source, modal)
/**
* Opens a dropdown input TGUI modal
*
* Internally checks if the answer is in the list of choices.
* Arguments:
* * source - The source datum
* * id - The ID of the modal
* * text - The text to display above the answers
* * delegate - The proc to call when submitted
* * arguments - List of arguments passed to and from JS (mostly useful for chaining modals)
* * value - The default value of the dropdown
* * choices - The list of available choices in the dropdown
*/
/datum/proc/tgui_modal_choice(datum/source = src, id, text = "Default modal message", delegate, arguments, value = "", choices)
ASSERT(length(id))
var/datum/tgui_modal/input/choice/modal = new(id, text, delegate, arguments, value, choices)
return tgui_modal_new(source, modal)
/**
* Opens a bento input TGUI modal
*
* Internally checks if the answer is in the list of choices.
* Arguments:
* * source - The source datum
* * id - The ID of the modal
* * text - The text to display above the answers
* * delegate - The proc to call when submitted
* * arguments - List of arguments passed to and from JS (mostly useful for chaining modals)
* * value - The default value of the bento
* * choices - The list of available choices in the bento
*/
/datum/proc/tgui_modal_bento(datum/source = src, id, text = "Default modal message", delegate, arguments, value, choices)
ASSERT(length(id))
var/datum/tgui_modal/input/bento/modal = new(id, text, delegate, arguments, value, choices)
return tgui_modal_new(source, modal)
/**
* Opens a yes/no TGUI modal
*
* Arguments:
* * source - The source datum
* * id - The ID of the modal
* * text - The text to display above the answers
* * delegate - The proc to call when "Yes" is pressed
* * delegate_no - The proc to call when "No" is pressed
* * arguments - List of arguments passed to and from JS (mostly useful for chaining modals)
* * yes_text - The text to show in the "Yes" button
* * no_text - The text to show in the "No" button
*/
/datum/proc/tgui_modal_boolean(datum/source = src, id, text = "Default modal message", delegate, delegate_no, arguments, yes_text = "Yes", no_text = "No")
ASSERT(length(id))
var/datum/tgui_modal/boolean/modal = new(id, text, delegate, delegate_no, arguments, yes_text, no_text)
return tgui_modal_new(source, modal)
/**
* Registers a given modal to a source. Private.
*
* Arguments:
* * source - The source datum
* * modal - The datum/tgui_modal to register
* * replace_previous - Whether any modal currently assigned to source should be replaced
* * instant_update - Whether the changes should reflect immediately
*/
/datum/proc/tgui_modal_new(datum/source = src, datum/tgui_modal/modal = null, replace_previous = TRUE, instant_update = TRUE)
ASSERT(istype(source))
ASSERT(istype(modal))
var/datum/tgui_modal/previous = LAZYACCESS(GLOB.tgui_modals, source.UID())
if(previous && !replace_previous)
return FALSE
modal.owning_source = source
// Previous one should get GC'd
LAZYSET(GLOB.tgui_modals, source.UID(), modal)
if(instant_update)
SStgui.update_uis(source)
return TRUE
/**
* Calls the source's currently assigned modal's (if there is one) on_answer() proc. Private.
*
* Arguments:
* * source - The source datum
* * id - The ID of the modal
* * answer - The provided answer
*/
/datum/proc/tgui_modal_answer(datum/source = src, id, answer = "")
ASSERT(istype(source))
var/datum/tgui_modal/current = LAZYACCESS(GLOB.tgui_modals, source.UID())
if(!current)
return FALSE
return current.on_answer(answer)
/**
* Passes an answer from JS through the modal's proc.
*
* Used namely for cutting the text short if it's longer
* than an input modal's max_length.
* Arguments:
* * source - The source datum
* * answer - The provided answer
*/
/datum/proc/tgui_modal_preprocess_answer(datum/source = src, answer = "")
ASSERT(istype(source))
var/datum/tgui_modal/current = LAZYACCESS(GLOB.tgui_modals, source.UID())
if(!current)
return answer
return current.preprocess_answer(answer)
/**
* Modal datum (contains base information for a modal)
*/
/datum/tgui_modal
var/datum/owning_source
var/id
var/text
var/delegate
var/list/arguments
var/modal_type = "message"
/datum/tgui_modal/New(id, text, delegate, list/arguments)
src.id = id
src.text = text
src.delegate = delegate
src.arguments = arguments
/**
* Called when it's time to pre-process the answer before using it
*
* Arguments:
* * answer - The answer, a nullable text
*/
/datum/tgui_modal/proc/preprocess_answer(answer)
return reject_bad_text(answer, TGUI_MODAL_INPUT_MAX_LENGTH) // bleh
/**
* Called when a modal receives an answer
*
* Arguments:
* * answer - The answer, a nullable text
*/
/datum/tgui_modal/proc/on_answer(answer)
if(delegate)
return call(owning_source, delegate)(answer, arguments)
return FALSE
/**
* Creates a list that describes a modal visually to be passed to JS
*/
/datum/tgui_modal/proc/to_data()
. = list()
.["id"] = id
.["text"] = text
.["args"] = arguments || list()
.["type"] = modal_type
/**
* Input modal - has a text entry that can be used to enter an answer
*/
/datum/tgui_modal/input
modal_type = "input"
var/value
var/max_length
/datum/tgui_modal/input/New(id, text, delegate, list/arguments, value, max_length)
..(id, text, delegate, arguments)
src.value = value
src.max_length = max_length
/datum/tgui_modal/input/preprocess_answer(answer)
. = ..(answer)
if(length(answer) > max_length)
. = copytext(., 1, max_length + 1)
/datum/tgui_modal/input/to_data()
. = ..()
.["value"] = value
/**
* Choice modal - has a dropdown menu that can be used to select an answer
*/
/datum/tgui_modal/input/choice
modal_type = "choice"
var/choices
/datum/tgui_modal/input/choice/New(id, text, delegate, list/arguments, value, choices)
..(id, text, delegate, arguments, value, TGUI_MODAL_INPUT_MAX_LENGTH) // Max length doesn't really matter in dropdowns, but whatever
src.choices = choices
/datum/tgui_modal/input/choice/on_answer(answer)
if(answer in choices) // Make sure the answer is actually in our choices!
return ..(answer, arguments)
return FALSE
/datum/tgui_modal/input/choice/to_data()
. = ..()
.["choices"] = choices
/**
* Bento modal - Similar to choice, it displays the choices in a grid of images
*
* The returned answer is the index of the choice.
*/
/datum/tgui_modal/input/bento
modal_type = "bento"
var/choices
/datum/tgui_modal/input/bento/New(id, text, delegate, list/arguments, value, choices)
..(id, text, delegate, arguments, text2num(value), TGUI_MODAL_INPUT_MAX_LENGTH) // Max length doesn't really matter in here, but whatever
src.choices = choices
/datum/tgui_modal/input/bento/preprocess_answer(answer)
return text2num(answer) || 0
/datum/tgui_modal/input/bento/on_answer(answer)
if(answer >= 1 && answer <= length(choices)) // Make sure the answer index is actually in our indexes!
return ..(answer, arguments)
return FALSE
/datum/tgui_modal/input/bento/to_data()
. = ..()
.["choices"] = choices
/**
* Boolean modal - has yes/no buttons that do different actions depending on which is pressed
*/
/datum/tgui_modal/boolean
modal_type = "boolean"
var/delegate_no
var/yes_text
var/no_text
/datum/tgui_modal/boolean/New(id, text, delegate, delegate_no, list/arguments, yes_text, no_text)
..(id, text, delegate, arguments)
src.delegate_no = delegate_no
src.yes_text = yes_text
src.no_text = no_text
/datum/tgui_modal/boolean/preprocess_answer(answer)
return text2num(answer) || FALSE
/datum/tgui_modal/boolean/on_answer(answer)
if(answer)
return ..(answer, arguments)
else if(delegate_no)
return call(owning_source, delegate_no)(arguments)
return FALSE
/datum/tgui_modal/boolean/to_data()
. = ..()
.["yes_text"] = yes_text
.["no_text"] = no_text
-231
View File
@@ -1,231 +0,0 @@
<!--
Title: Body Scanner UI
Used In File(s): \code\game\machinery\adv_med.dm
-->
{{if !data.occupied}}
<h3>No occupant detected.</h3>
{{else}}
<h4><b>Occupant Data:</b></h4>
<div class="item">
<div class="itemLabelNarrow">
Name:
</div>
<div class="itemContent">
{{:data.occupant.name}}
</div>
</div>
<div class="item">
<div class="itemLabelNarrow">
Health:
</div>
{{:helper.displayBar(data.occupant.health, -100, 100, (data.occupant.health >= 50) ? 'good' : (data.occupant.health >= 0) ? 'average' : 'bad')}}
<div class="itemContent" style="width: 60px">
{{:helper.smoothRound(data.occupant.health)}}%
</div>
</div>
<div class="item">
<div class="itemLabelNarrow">
Status:
</div>
<div class="itemContent">
{{if data.occupant.stat==0}}
<span class="good"> Alive </span>
{{else data.occupant.stat==1}}
<span class="average"> Critical </span>
{{else}}
<span class="bad"> Dead </span>
{{/if}}
</div>
</div>
<div class="item">
<div class="itemLabelNarrow">
Temperature:
</div>
<div class="itemContent">
{{:helper.smoothRound(data.occupant.bodyTempC)}}&deg;C, {{:helper.smoothRound(data.occupant.bodyTempF)}}&deg;F
</div>
</div>
{{if data.occupant.hasBorer}}
<span class="bad">
Large growth detected in frontal lobe, possibly cancerous. Surgical removal is recommended. </span> <br>
{{/if}}
{{if data.occupant.blind}}
<span class="average">Cataracts detected.</span><br>
{{/if}}
{{if data.occupant.colourblind}}
<span class="average">Photoreceptor abnormalities detected.</span><br>
{{/if}}
{{if data.occupant.nearsighted}}
<span class="average">Retinal misalignment detected.</span> <br>
{{/if}}
<br>
{{:helper.link('Print', 'print', {'print_p' : 1, 'name' : data.occupant.name})}}
{{:helper.link('Eject Occupant', 'arrow-circle-o-down', {'ejectify' : 1}, data.occupied ? null : 'disabled')}}
<br> <br>
<b>Damage: </b>
<table> <tbody>
<tr>
<td> Brute Damage: </td>
<td> Brain Damage: </td>
</tr>
<tr>
<td> {{:helper.displayBar(data.occupant.bruteLoss, 0, data.occupant.maxHealth+100, (data.occupant.bruteLoss < 15) ? 'notgood' : (data.occupant.bruteLoss < 40) ? 'average' : 'bad', data.occupant.bruteLoss) }} </td>
<td> {{:helper.displayBar(data.occupant.brainLoss, 0, data.occupant.maxHealth+100, (data.occupant.brainLoss < 15) ? 'notgood' : (data.occupant.brainLoss < 40) ? 'average' : 'bad', data.occupant.brainLoss)}} </td>
</tr>
<tr>
<td> Resp. Damage: </td>
<td> Radiation Level: </td>
</tr>
<tr>
<td> {{:helper.displayBar(data.occupant.oxyLoss, 0, data.occupant.maxHealth+100, (data.occupant.oxyLoss < 15) ? 'notgood' : (data.occupant.oxyLoss < 40) ? 'average' : 'bad', data.occupant.oxyLoss)}} </td>
<td> {{:helper.displayBar(data.occupant.radLoss, 0, data.occupant.maxHealth+100, (data.occupant.radLoss < 15) ? 'notgood' : (data.occupant.radLoss < 40) ? 'average' : 'bad', data.occupant.radLoss)}} </td>
</tr>
<tr>
<td> Toxin Damage: </td>
<td> Genetic Damage: </td>
</tr>
<tr>
<td> {{:helper.displayBar(data.occupant.toxLoss, 0, data.occupant.maxHealth+100, (data.occupant.toxLoss < 15) ? 'notgood' : (data.occupant.toxLoss < 40) ? 'average' : 'bad', data.occupant.toxLoss)}} </td>
<td> {{:helper.displayBar(data.occupant.cloneLoss, 0, data.occupant.maxHealth+100, (data.occupant.cloneLoss < 15) ? 'notgood' : (data.occupant.cloneLoss < 40) ? 'average' : 'bad', data.occupant.cloneLoss)}} </td>
</tr>
<tr>
<td> Burn Severity: </td>
<td> Paralysis %: ({{:helper.smoothRound(data.occupant.paralysisSeconds)}} seconds left) </td>
</tr>
<tr>
<td> {{:helper.displayBar(data.occupant.fireLoss, 0, data.occupant.maxHealth+100, (data.occupant.fireLoss < 15) ? 'notgood' : (data.occupant.fireLoss < 40) ? 'average' : 'bad', data.occupant.fireLoss)}} </td>
<td> {{:helper.displayBar(data.occupant.paralysis, 0, 100, (data.occupant.paralysis < 25) ? 'notgood' : (data.occupant.paralysis < 50) ? 'average' : 'bad', data.occupant.paralysis)}} </td>
</tr>
</tbody>
</table>
{{if data.occupant.blood.hasBlood}}
<br>
<b>Blood:</b>
<div class="line">
<div class="statusLabel">Pulse:</div> <div class="statusValue">{{:data.occupant.blood.pulse}} bpm</div>
</div>
<div class="line">
<div class="statusLabel">Blood Level:</div>
{{:helper.displayBar(data.occupant.blood.bloodLevel, 0, data.occupant.blood.bloodMax, (data.occupant.blood.percent <= 60) ? 'bad' : (data.occupant.blood.percent <= 90) ? 'average' : 'good' )}}
<div class="statusValue"> {{:helper.smoothRound(data.occupant.blood.percent)}}%, {{:helper.smoothRound(data.occupant.blood.bloodLevel)}}cl </div>
</div>
{{/if}}
<br>
{{if data.occupant.hasVirus}}
<span class="bad">
Viral pathogen detected in blood stream.
</span> <br>
{{/if}}
{{if data.occupant.implant_len}}
<br>
<b>Implants</b>
<table style="width: 30%">
<tbody class="striped">
{{for data.occupant.implant}}
<tr><td>{{:value.name}}</td></tr>
{{/for}}
</tbody>
</table>
<br>
{{/if}}
<b>External Organs</b>
<table> <thead>
<tr>
<th style="width: 25%"> Name </th>
<th style='width: 15%'> Total damage </th>
<th style="width: 15%"> Brute damage </th>
<th style="width: 15%"> Burn damage </th>
<th style="width: 30%"> Injuries </th>
</tr>
</thead>
<tbody class="striped">
{{for data.occupant.extOrgan}}
<tr>
<td> {{:value.name}} </td>
<td> {{:helper.displayBar(value.totalLoss, 0, value.maxHealth, (value.totalLoss < value.bruised) ? 'notgood' : (value.totalLoss < value.broken) ? 'average' : 'bad', value.totalLoss) }} </td>
<td> {{if value.bruteLoss> 0}} <span class='average'> {{else}} <span> {{/if}} {{:helper.smoothRound(value.bruteLoss)}} </span> </td>
<td> {{if value.fireLoss> 0}} <span class='average'> {{else}} <span> {{/if}} {{:helper.smoothRound(value.fireLoss)}} </span> </td>
<td>
<span class='average'>
{{if value.internalBleeding}} internal bleeding <br> {{/if}}
{{if value.lungRuptured}} lung ruptured <br> {{/if}}
{{if value.status.broken}} {{:value.status.broken}} <br> {{/if}}
{{if value.germ_level > 100}}
{{if value.germ_level < 300}}
mild infection <br>
{{else value.germ_level < 400}}
mild infection+ <br>
{{else value.germ_level < 500}}
mild infection++ <br>
{{else value.germ_level < 700}}
acute infection <br>
{{else value.germ_level < 800}}
acute infection+ <br>
{{else value.germ_level < 900}}
acute infection++ <br>
{{else value.germ_level >= 900}}
septic <br>
{{/if}}
{{/if}}
{{if value.open}} open incision <br> {{/if}}
</span>
{{if value.status.splinted}} splinted <br> {{/if}}
{{if value.status.robotic}} robotic <br> {{/if}}
{{if value.status.dead}} <span class='bad'>DEAD</span> <br {{/if}}
{{if value.shrapnel_len}}
{{for value.shrapnel : shrapValue:shrapindex}}
{{:shrapValue.known ? shrapValue.name : "unknown object"}} <br>
{{/for}}
{{/if}}
</td>
</tr>
{{/for}}
</tbody>
</table>
<br>
<b>Internal Organs</b>
<table> <thead>
<tr>
<th width=25%> Name </th>
<th width=35%> Brute damage </th>
<th widht=40%> Injuries </th>
</tr>
</thead>
<tbody class="striped">
{{for data.occupant.intOrgan}}
<tr>
<td> {{:value.name}} <br> </td>
<td> {{:helper.displayBar(value.damage, 0, value.maxHealth, (value.damage < value.bruised) ? 'notgood' : (value.damage < value.broken) ? 'average' : 'bad', value.damage) }} </td>
<td>
<span class="average">
{{if value.germ_level > 100}}
{{if value.germ_level < 300}}
mild infection <br>
{{else value.germ_level < 400}}
mild infection+ <br>
{{else value.germ_level < 500}}
mild infection++ <br>
{{else value.germ_level < 700}}
acute Infection <br>
{{else value.germ_level < 800}}
acute Infection+ <br>
{{else value.germ_level < 900}}
acute Infection++ <br>
{{else value.germ_level >= 900}}
septic <br>
{{/if}}
{{/if}}
</span>
{{if value.robotic == 1}} robotic <br> {{/if}}
{{if value.robotic == 2}} assisted <br> {{/if}}
{{if value.dead}} <span class='bad'>DEAD</span> <br> {{/if}}
</td>
</tr>
{{/for}}
</tbody>
</table>
{{/if}}
-93
View File
@@ -1,93 +0,0 @@
<!--
Title: Chem Dispenser 5000 UI
Used In File(s): \code\modules\reagents\Chemistry-Machinery.dm
-->
<div class="item">
<div class="itemLabel">
Energy:
</div>
<div class="itemContent">
{{:helper.displayBar(data.energy, 0, data.maxEnergy, 'good', data.energy + ' Units')}}
</div>
</div>
<div class="item">
<div class="itemLabel">
Dispense:
</div>
<div class="itemContent">
{{:helper.link('1', 'gear', {'amount' : 1}, (data.amount == 1) ? 'selected' : null)}}
{{:helper.link('5', 'gear', {'amount' : 5}, (data.amount == 5) ? 'selected' : null)}}
{{:helper.link('10', 'gear', {'amount' : 10}, (data.amount == 10) ? 'selected' : null)}}
{{:helper.link('20', 'gear', {'amount' : 20}, (data.amount == 20) ? 'selected' : null)}}
{{:helper.link('30', 'gear', {'amount' : 30}, (data.amount == 30) ? 'selected' : null)}}
{{:helper.link('50', 'gear', {'amount' : 50}, (data.amount == 50) ? 'selected' : null)}}
</div>
</div>
<div class="item">&nbsp;</div>
<div class="item">
<div class="itemLabel" style="width: 100%;">
{{if data.glass}}
Drink Dispenser
{{else}}
Chemical Dispenser
{{/if}}
</div>
</div>
<div class="item">
<div class="itemContentWide" style="width: 100%;">
{{for data.chemicals}}
{{:helper.link(value.title, 'arrow-circle-down', value.commands, null, data.glass ? 'fixedLeftWide' : 'fixedLeft')}}
{{/for}}
</div>
</div>
<div class="item">&nbsp;</div>
<div class="item">
<div class="itemLabel">
{{if data.glass}}
Glass
{{else}}
Beaker
{{/if}} Contents
</div>
<div class="itemContent">
{{:helper.link(data.glass ? 'Eject Glass' : 'Eject Beaker', 'eject', {'ejectBeaker' : 1}, data.isBeakerLoaded ? null : 'disabled', 'floatRight')}}
</div>
</div>
<div class="statusDisplay" style="min-height: 180px;">
<div class="item">
<div class="itemContent" style="width: 100%;">
{{if data.isBeakerLoaded}}
{{if data.beakerCurrentVolume}}
<span class="floatRight" style="width: 115px;"><b>Dispose:</b></span>
{{/if}}
<b>Volume:&nbsp;{{:data.beakerCurrentVolume}}&nbsp;/&nbsp;{{:data.beakerMaxVolume}}</b><br>
{{for data.beakerContents}}
<div style="min-height: 22px;">
<div class="floatLeft">
<span class="highlight">
{{:value.volume}} units of {{:value.name}}
</span>
</div>
<div class="floatRight">
{{:helper.link('Isolate', null, {'remove' : value.id, 'removeamount' : -1})}}
{{:helper.link('1', null, {'remove' : value.id, 'removeamount' : 1})}}
{{:helper.link('5', null, {'remove' : value.id, 'removeamount' : 5})}}
{{:helper.link('10', null, {'remove' : value.id, 'removeamount' : 10})}}
{{:helper.link('ALL', null, {'remove' : value.id, 'removeamount' : value.volume})}}
</div>
</div>
<div style="height:2px"></div> <!-- This is necessary to prevent the link margins from hitting each other and causing weird things to happen. It's essentialy a smaller <br> tag. -->
{{empty}}
<span class="bad">{{if data.glass}}Glass{{else}}Beaker{{/if}} is empty
</span>
{{/for}}
{{else}}
<span class="average"><i>
No {{if data.glass}}Glass{{else}}Beaker{{/if}} loaded
</i></span>
{{/if}}
</div>
</div>
</div>
-47
View File
@@ -1,47 +0,0 @@
<!--
Title: ChemHeater UI
Used In File(s): \code\modules\reagents\newchem\chem_heater.dm
-->
<div class="item">
<div class="itemLabel">
Status:
</div>
<div class="itemContent">
{{:helper.link(data.isActive ? 'On' : 'Off', 'power-off', {'toggle_on' : 1}, data.isBeakerLoaded ? null : 'disabled')}}
</div>
</div>
<div class="item">
<div class="itemLabel">
Target:
</div>
<div class="itemContent">
{{:helper.link(data.targetTemp + 'K', 'gear', {'adjust_temperature' : 'input'}, null)}}
</div>
</div>
<div class="item">
<div class="itemLabel">
Beaker
</div>
<div class="itemContent">
{{:helper.link('Eject', 'eject', {'eject_beaker' : 1}, data.isBeakerLoaded ? null : 'disabled', 'floatRight')}}
</div>
</div>
<div class="statusDisplay">
<div style="height: 110px; overflow: auto;">
<div class="item">
<div class="itemContent">
<div style="width: 100%;">
{{if data.isBeakerLoaded}}
<b>Volume:&nbsp;{{:helper.smoothRound(data.beakerCurrentVolume)}}&nbsp;/&nbsp;{{:data.beakerMaxVolume}}</b><br>
<b>Temperature:&nbsp;{{:helper.smoothRound(data.currentTemp)}}&nbsp;Kelvin</b><br>
{{for data.beakerContents}}
<span class="highlight">{{:value.volume}} units of {{:value.name}}</span><br>
{{/for}}
{{else}}
<span class="average"><i>No beaker loaded</i></span>
{{/if}}
</div>
</div>
</div>
</div>
</div>
-106
View File
@@ -1,106 +0,0 @@
{{if data.beaker}}
<div class='item'>{{:helper.link('Eject Beaker and Clear Buffer', 'eject', {'eject': 1})}}</div>
<div class='statusDisplay'>
<h3>Add to buffer:</h3>
{{for data.beaker_reagents}}
<div class='line'>
<div class='itemLabel'>{{:value.name}}, {{:value.volume}} units</div>
{{:helper.link('Analyze', 'search', {'analyze': 1, 'desc': value.description, 'name': value.name})}}
{{:helper.link('1', 'plus', {'add': value.id, 'amount': 1})}}
{{:helper.link('5', 'plus', {'add': value.id, 'amount': 5})}}
{{:helper.link('10', 'plus', {'add': value.id, 'amount': 10})}}
{{:helper.link('All', 'plus', {'add': value.id, 'amount': value.volume})}}
{{:helper.link('Custom', 'plus', {'addcustom': value.id})}}
</div>
{{/for}}
</div>
<div class='statusDisplay'>
<h3>
<div class='item'>
Transfer to {{:helper.link(data.mode ? 'beaker' : 'disposal', 'arrows-h', {'toggle': 1})}}
</div>
</h3>
{{for data.buffer_reagents}}
<div class='line'>
<div class='itemLabel'>{{:value.name}}, {{:value.volume}} units</div>
{{:helper.link('Analyze', 'search', {'analyze': 1, 'desc': value.description, 'name': value.name})}}
{{:helper.link('1', 'plus', {'remove': value.id, 'amount': 1})}}
{{:helper.link('5', 'plus', {'remove': value.id, 'amount': 5})}}
{{:helper.link('10', 'plus', {'remove': value.id, 'amount': 10})}}
{{:helper.link('All', 'plus', {'remove': value.id, 'amount': value.volume})}}
{{:helper.link('Custom', 'plus', {'removecustom': value.id})}}
</div>
{{/for}}
</div>
<div class='statusDisplay'>
<h3>Containers:</h3>
<div class='item'>
{{if data.condi}}
{{:helper.link('Create pack (10u max)', 'arrow-right', {'createpill': 1})}}
{{:helper.link('Create bottle (50u max)', 'arrow-right', {'createbottle': 1})}}
{{else}}
<table>
<tr>
<td>
{{:helper.link('Create pill (100u max)', 'arrow-right', {'createpill': 1})}}
</td>
<td>
{{:helper.link('Multiple', 'arrow-right', {'createpill_multiple': 1})}}
</td>
<td>
{{:helper.link('Style', 'pencil', {'change_pill': 1})}}
</td>
<td>
<img src='pill{{:data.pillsprite}}.png'>
</td>
</tr>
<tr>
<td>
{{:helper.link('Create patch (30u max)', 'arrow-right', {'createpatch': 1})}}
</td>
<td>
{{:helper.link('Multiple', 'arrow-right', {'createpatch_multiple': 1})}}
</td>
</tr>
<tr>
<td>
{{:helper.link('Create bottle (50u max)', 'arrow-right', {'createbottle': 1})}}
</td>
<td>
{{:helper.link('Style', 'pencil', {'change_bottle': 1})}}
</td>
<td>
<img src='{{:data.bottlesprite}}.png'>
</td>
</tr>
</table>
{{/if}}
</div>
</div>
{{else}}
<span class='item'>No beaker loaded</span><br>
{{/if}}
<div class='statusDisplay'>
<span class='item'>
{{if data.loaded_pill_bottle}}
<table>
<tr>
<td>
{{:helper.link('Pill Bottle (' + data.loaded_pill_bottle_contents_len + '/' + data.loaded_pill_bottle_storage_slots + ')', 'eject', {'ejectp': 1})}}
</td>
<td>
{{:helper.link('Change Wrapper', 'pencil', {'change_pillbottle': 1})}}
</td>
</tr>
</table>
{{else}}
<table>
<tr>
<td>
{{:helper.link('No Pill Bottle', 'eject', {}, 'disabled')}}
</td>
</tr>
</table>
{{/if}}
</span>
</div>
-148
View File
@@ -1,148 +0,0 @@
<!--
Title: Cloning Console UI
Used In File(s): \code\game\machinery\computer\cloning.dm
-->
<div class="block">
<div class="item">
<div class="itemLabelNarrow">Menu</div>
<div class="itemContentWide">
{{:helper.link('Main', 'home', {'menu' : 1}, data.menu == 1 ? 'selected' : '')}}{{:helper.link('Records', 'folder-open', {'menu' : 2}, data.menu == 2 ? 'selected' : '')}}
</div>
</div>
<div class="item">
<div class="itemLabelNarrow">Refresh</div>
<div class="itemContentWide">
{{:helper.link('Refresh', 'refresh', {'refresh' : 1})}}
</div>
</div>
<div class="item">
<div class="itemLabelNarrow">Autoprocessing</div>
<div class="itemContentWide">
{{if data.autoallowed}}
{{:helper.link('Enabled', 'check', {'task' : 'autoprocess'}, data.autoprocess ? 'selected' : '')}}{{:helper.link('Disabled', 'close', {'task' : 'stopautoprocess'}, !data.autoprocess ? 'selected' : '')}}
{{else}}
{{:helper.link('Enabled', 'check', null, 'disabled')}}{{:helper.link('Disabled', 'close', null, 'selected')}}
{{/if}}
</div>
</div>
{{if data.disk}}
<div class="item">
<div class="itemLabelNarrow">Disk</div>
<div class="itemContentWide">{{:helper.link('Eject', 'eject', {'disk' : 'eject'})}}</div>
</div>
{{/if}}
</div>
{{if data.menu == 1}}
<h2>Modules</h2>
<div class="item">
<div class="itemLabelNarrow">Scanner</div>
<div class="itemContentWide{{if data.scanner}} good{{else}} bad{{/if}}">
{{if data.scanner}} Found!{{else}} Error: Not detected!{{/if}}
</div>
</div>
<div class="item">
<div class="itemLabelNarrow">Pods</div>
<div class="itemContentWide{{if data.numberofpods}} good{{else}} bad{{/if}}">
{{if data.numberofpods}} Found {{:data.numberofpods}}!{{else}} Error: None detected!{{/if}}
</div>
</div>
{{if data.scanner}}
<h2>Scanner Functions</h2>
{{if data.loading}}
<b>Scanning...</b>
{{else}}
<b>{{:data.scantemp}}</b>
{{/if}}
<div class="item">
<div class="itemLabelNarrow">Scan Occupant</div>
<div class="itemContentWide">{{:helper.link('Scan', 'search', {'scan' : 1}, !data.occupant ? 'disabled' : '')}}</div>
</div>
<div class="item">
<div class="itemLabelNarrow">Scanner Lock</div>
<div class="itemContentWide">{{if data.occupant}}{{:helper.link('Engaged', 'lock', {'lock' : 1}, data.locked ? 'selected' : '')}}{{else}} {{:helper.link('Engaged', 'lock', null, 'disabled')}}{{/if}}{{:helper.link('Disengaged', 'unlock', {'lock' : 1}, !data.locked ? 'selected' : '')}}</div>
</div>
{{if data.can_brainscan}}
<div class="item">
<div class="itemLabelNarrow">Scan Mode</div>
<div class="itemContentWide">
{{:helper.link('Brain', null, {'toggle_mode' : 1}, !data.scan_mode ? '' : 'selected')}}{{:helper.link('Body', null, {'toggle_mode' : 1}, data.scan_mode ? '' : 'selected')}}
</div>
</div>
{{/if}}
{{/if}}
{{if data.numberofpods}}
<h2>Pods</h2>
{{for data.pods}}
<b>{{:value.name}}</b></br>
<div class="item">
<div class="itemLabelNarrow">Biomass</div>
<div class="itemContentWide{{if value.biomass > 0}} good{{else}} bad{{/if}}">
{{:value.biomass}}
</div>
</div>
<div class="item">
<div class="itemLabelNarrow">Select</div>
<div class="itemContentWide">
{{:helper.link('Select', 'check', {'selectpod' : value.pod}, value.pod == data.selected_pod ? 'selected' : '')}}
</div>
</div>
{{/for}}
{{/if}}
{{/if}}
{{if data.menu == 2}}
<h2>Current Records</h2>
{{if data.temp}}
<div class="item">
{{:data.temp}}
</div>
{{/if}}
{{for data.records}}
<div>{{:helper.link(value.realname, 'arrow-circle-down', {'view_rec' : value.record})}}</div>
{{empty}}
No records found!
{{/for}}
{{/if}}
{{if data.menu == 3}}
<h2>Selected Record</h2>
{{if data.temp}}
<div class="item">
{{:data.temp}}
</div>
{{/if}}
{{if !data.activerecord}}
<div class="bad">Error: Record not found!</div>
{{else}}
<div class="item">
<b>Real Name:</b> {{:data.realname}}<br />
<b>Health:</b> {{:data.health}} (<span class="oxyloss_light">OXY</span> - <span class="burn">BURN</span> - <span class="toxin_light">TOX</span> - <span class="brute">BRUTE</span>)
</div>
<div class="item">
<b>UI:</b> {{:data.unidentity}}<br />
<b>SE:</b> <div style="word-wrap: break-word;">{{:data.strucenzymes}}</div>
</div>
{{if data.disk}}
<div class="item">
<div class="itemLabelNarrow">Disk</div>
<div class="itemContentWide">{{:helper.link('Load from Disk', 'floppy-o', {'disk' : 'load'})}}{{:helper.link('Save UI + UE', 'arrow-circle-up', {'save_disk' : 'ue'})}}{{:helper.link('Save UI', 'arrow-circle-up', {'save_disk' : 'ui'})}}{{:helper.link('Save SE', 'arrow-circle-up', {'save_disk' : 'se'})}}</div>
</div>
{{/if}}
<div class="item">
<div class="itemLabelNarrow">Actions</div>
<div class="itemContentWide">{{:helper.link('Clone', 'arrow-right', {'clone' : data.activerecord}, !data.podready ? 'disabled' : '')}}{{:helper.link('Delete', 'trash', {'del_rec' : 1})}}</div>
</div>
{{/if}}
{{/if}}
{{if data.menu == 4}}
<h2>Confirm Record Deletion</h2>
{{if data.temp}}
<div class="item">
{{:data.temp}}
</div>
{{/if}}
<div class="item">
{{:helper.link('Scan Card and Confirm', 'trash', {'del_rec' : 1})}}{{:helper.link('Cancel', 'close', {'menu' : 3})}}
</div>
{{/if}}
-117
View File
@@ -1,117 +0,0 @@
<!--
Title: Cryo Cell Status UI
Used In File(s): \code\game\machinery\cryo.dm
-->
<h3>Cryo Cell Status</h3>
<div class="statusDisplay">
{{if !data.hasOccupant}}
<div class="line">Cell Unoccupied</div>
{{else}}
<div class="line">
{{:data.occupant.name}}&nbsp;<i class="fa fa-arrow-right"></i>&nbsp;
{{if data.occupant.stat == 0}}
<span class="good">Conscious</span>
{{else data.occupant.stat == 1}}
<span class="average">Unconscious</span>
{{else}}
<span class="bad">DEAD</span>
{{/if}}
</div>
{{if data.occupant.stat < 2}}
<div class="line">
<div class="statusLabel">Health:</div>
{{if data.occupant.health >= 0}}
{{:helper.displayBar(data.occupant.health, 0, data.occupant.maxHealth, 'good')}}
{{else}}
{{:helper.displayBar(data.occupant.health, 0, data.occupant.minHealth, 'average alignRight')}}
{{/if}}
<div class="statusValue">{{:helper.smoothRound(data.occupant.health)}}</div>
</div>
<div class="line">
<div class="statusLabel"><i class="fa fa-arrow-right"></i> Brute Damage:</div>
{{:helper.displayBar(data.occupant.bruteLoss, 0, data.occupant.maxHealth, 'bad')}}
<div class="statusValue">{{:helper.smoothRound(data.occupant.bruteLoss)}}</div>
</div>
<div class="line">
<div class="statusLabel"><i class="fa fa-arrow-right"></i> Resp. Damage:</div>
{{:helper.displayBar(data.occupant.oxyLoss, 0, data.occupant.maxHealth, 'bad')}}
<div class="statusValue">{{:helper.smoothRound(data.occupant.oxyLoss)}}</div>
</div>
<div class="line">
<div class="statusLabel"><i class="fa fa-arrow-right"></i> Toxin Damage:</div>
{{:helper.displayBar(data.occupant.toxLoss, 0, data.occupant.maxHealth, 'bad')}}
<div class="statusValue">{{:helper.smoothRound(data.occupant.toxLoss)}}</div>
</div>
<div class="line">
<div class="statusLabel"><i class="fa fa-arrow-right"></i> Burn Severity:</div>
{{:helper.displayBar(data.occupant.fireLoss, 0, data.occupant.maxHealth, 'bad')}}
<div class="statusValue">{{:helper.smoothRound(data.occupant.fireLoss)}}</div>
</div>
{{/if}}
{{/if}}
<br>
<hr>
<div class="line">
<div class="statusLabel">Cell Temperature:</div><div class="statusValue">
<span class="{{:data.cellTemperatureStatus}}">{{:helper.smoothRound(data.cellTemperature)}} K</span>
</div>
</div>
</div>
<h3>Cryo Cell Operation</h3>
<div class="item">
<div class="itemLabel">
Cryo Cell Status:
</div>
<div class="itemContent" style="width: 40%;">
{{:helper.link('On', 'power-off', {'switchOn' : 1}, data.isOperating ? 'selected' : null)}}{{:helper.link('Off', 'close', {'switchOff' : 1}, data.isOperating ? null : 'selected')}}
</div>
<div class="itemContent" style="width: 26%;">
{{:helper.link('Eject Occupant', 'arrow-circle-o-down', {'ejectOccupant' : 1}, data.hasOccupant ? null : 'disabled')}}
</div>
</div>
<div class="item">&nbsp;</div>
<div class="item">
<div class="itemLabel">
Beaker:
</div>
<div class="itemContent" style="width: 40%;">
{{if data.isBeakerLoaded}}
{{:data.beakerLabel ? data.beakerLabel : '<span class="average">No label</span>'}}<br>
{{if data.beakerVolume}}
<span class="highlight">{{:helper.smoothRound(data.beakerVolume)}} units remaining</span><br>
{{else}}
<span class="bad">Beaker is empty</span>
{{/if}}
{{else}}
<span class="average"><i>No beaker loaded</i></span>
{{/if}}
</div>
<div class="itemContent" style="width: 26%;">
{{:helper.link('Eject Beaker', 'eject', {'ejectBeaker' : 1}, data.isBeakerLoaded ? null : 'disabled')}}
</div>
</div>
<div class="item">&nbsp;</div>
<div class="item">
<div class="itemLabel" style="width: 70%">
Auto-eject healthy patients:
</div>
<div class="itemContent" style="width: 30%;">
{{:helper.link('On', 'power-off', {'auto_eject_healthy_on' : 1}, data.auto_eject_healthy ? 'selected' : null)}}{{:helper.link('Off', 'close', {'auto_eject_healthy_off' : 0}, data.auto_eject_healthy ? null : 'selected')}}
</div>
</div>
<div class="item">
<div class="itemLabel" style="width: 70%">
Auto-eject dead patients:
</div>
<div class="itemContent" style="width: 30%;">
{{:helper.link('On', 'power-off', {'auto_eject_dead_on' : 1}, data.auto_eject_dead ? 'selected' : null)}}{{:helper.link('Off', 'close', {'auto_eject_dead_off' : 0}, data.auto_eject_dead ? null : 'selected')}}
</div>
</div>
-313
View File
@@ -1,313 +0,0 @@
<!--
Title: DNA Modifier UI
Used In File(s): D:\Development\SS13-BS12\code\game\dna\dna_modifier.dm
-->
<h3>Status</h3>
<div class="statusDisplay">
{{if !data.hasOccupant}}
<div class="line">Cell Unoccupied</div>
{{else}}
<div class="line">
{{:data.occupant.name}}&nbsp;=>&nbsp;
{{if data.occupant.stat == 0}}
<span class="good">Conscious</span>
{{else data.occupant.stat == 1}}
<span class="average">Unconscious</span>
{{else}}
<span class="bad">DEAD</span>
{{/if}}
</div>
{{if !data.occupant.isViableSubject || !data.occupant.uniqueIdentity || !data.occupant.structuralEnzymes}}
<div class="notice">
The occupant's DNA structure is ruined beyond recognition, please insert a subject with an intact DNA structure.
</div>
{{else data.occupant.stat < 2}}
<div class="line">
<div class="statusLabel">Health:</div>
{{if data.occupant.health >= 0}}
{{:helper.displayBar(data.occupant.health, 0, data.occupant.maxHealth, 'good')}}
{{else}}
{{:helper.displayBar(data.occupant.health, 0, data.occupant.minHealth, 'average alignRight')}}
{{/if}}
<div class="statusValue">{{:helper.smoothRound(data.occupant.health)}}</div>
</div>
<div class="line">
<div class="statusLabel">Radiation:</div>
{{:helper.displayBar(data.occupant.radiationLevel, 0, 100, 'average')}}
<div class="statusValue">{{:helper.smoothRound(data.occupant.radiationLevel)}}</div>
</div>
<div class="line">
<div class="statusLabel">Unique Enzymes:</div>
<div class="statusValue">{{:data.occupant.uniqueEnzymes ? data.occupant.uniqueEnzymes : '<span class="bad">Unknown</span>'}}</div>
</div>
{{/if}}
{{/if}}
<div class="clearBoth"></div>
</div>
<h3>Operations</h3>
{{if !data.occupant.isViableSubject || !data.occupant.uniqueIdentity || !data.occupant.structuralEnzymes}}
<div class="notice">
No operation possible on this subject
</div>
{{else}}
<div class="item">
{{:helper.link('Modify U.I.', 'link', {'selectMenuKey' : 'ui'}, data.selectedMenuKey == 'ui' ? 'selected' : null)}}
{{:helper.link('Modify S.E.', 'link', {'selectMenuKey' : 'se'}, data.selectedMenuKey == 'se' ? 'selected' : null)}}
{{:helper.link('Transfer Buffers', 'floppy-o', {'selectMenuKey' : 'buffer'}, data.selectedMenuKey == 'buffer' ? 'selected' : null)}}
{{:helper.link('Rejuvenators', 'plus', {'selectMenuKey' : 'rejuvenators'}, data.selectedMenuKey == 'rejuvenators' ? 'selected' : null)}}
</div>
<div class="item">&nbsp;</div>
{{if !data.selectedMenuKey || data.selectedMenuKey == 'ui'}}
<h3>Modify Unique Identifier</h3>
{{:helper.displayDNABlocks(data.occupant.uniqueIdentity, data.selectedUIBlock, data.selectedUISubBlock, data.dnaBlockSize, 'UI')}}
<div class="clearBoth"></div>
<div class="item">
<div class="itemLabelNarrow">
Target:
</div>
<div class="itemContentWide">
{{:helper.link('-', null, {'changeUITarget' : 0}, (data.selectedUITarget > 0) ? null : 'disabled')}}
<div class="statusValue">&nbsp;{{:data.selectedUITargetHex}}&nbsp;</div>
{{:helper.link('+', null, {'changeUITarget' : 1}, (data.selectedUITarget < 15) ? null : 'disabled')}}
</div>
</div>
<div class="item">
<div class="itemContentWide">
{{:helper.link('Irradiate Block', 'exclamation-circle', {'pulseUIRadiation' : 1}, !data.occupant.isViableSubject ? 'disabled' : null)}}
</div>
</div>
{{else data.selectedMenuKey == 'se'}}
<h3>Modify Structural Enzymes</h3>
{{:helper.displayDNABlocks(data.occupant.structuralEnzymes, data.selectedSEBlock, data.selectedSESubBlock, data.dnaBlockSize, 'SE')}}
<div class="clearBoth"></div>
<div class="item">
<div class="itemContentWide">
{{:helper.link('Irradiate Block', 'exclamation-circle', {'pulseSERadiation' : 1}, !data.occupant.isViableSubject ? 'disabled' : null)}}
</div>
</div>
{{else data.selectedMenuKey == 'buffer'}}
<h3>Transfer Buffers</h3>
{{for data.buffers}}
<h4>Buffer {{:(index + 1)}}</h4>
<div class="itemGroup">
<div class="item">
<div class="itemLabelNarrow">
Load Data:
</div>
<div class="itemContentWide">
{{:helper.link('Subject U.I.', 'link', {'bufferOption' : 'saveUI', 'bufferId' : (index + 1)}, !data.hasOccupant ? 'disabled' : null)}}
{{:helper.link('Subject U.I. + U.E.', 'link', {'bufferOption' : 'saveUIAndUE', 'bufferId' : (index + 1)}, !data.hasOccupant ? 'disabled' : null)}}
{{:helper.link('Subject S.E.', 'link', {'bufferOption' : 'saveSE', 'bufferId' : (index + 1)}, !data.hasOccupant ? 'disabled' : null)}}
{{:helper.link('From Disk', 'floppy-o', {'bufferOption' : 'loadDisk', 'bufferId' : (index + 1)}, (!data.hasDisk || !data.disk.data) ? 'disabled' : null)}}
</div>
</div>
{{if value.data}}
<div class="item">
<div class="itemLabelNarrow">
Label:
</div>
<div class="itemContentWide">
{{:helper.link(value.label, 'file', {'bufferOption' : 'changeLabel', 'bufferId' : (index + 1)})}}
</div>
</div>
<div class="item">
<div class="itemLabelNarrow">
Subject:
</div>
<div class="itemContentWide">
{{:value.owner ? value.owner : '<span class="average">Unknown</span>'}}
</div>
</div>
<div class="item">
<div class="itemLabelNarrow">
Stored Data:
</div>
<div class="itemContentWide">
{{:value.data == 'ui' ? 'Unique Identifiers' : 'Structural Enzymes'}}
{{:value.ue ? ' + Unique Enzymes' : ''}}
</div>
</div>
{{else}}
<div class="item">
<div class="itemContentWide">
<span class="highlight">This buffer is empty.</span>
</div>
</div>
{{/if}}
<div class="item">
<div class="itemLabelNarrow">
Options:
</div>
<div class="itemContentWide">
{{:helper.link('Clear', 'trash', {'bufferOption' : 'clear', 'bufferId' : (index + 1)}, !value.data ? 'disabled' : null)}}
{{:helper.link('Injector', data.isInjectorReady ? 'pencil' : 'clock-o', {'bufferOption' : 'createInjector', 'bufferId' : (index + 1)}, (!data.isInjectorReady || !value.data) ? 'disabled' : null)}}
{{:helper.link('Block Injector', data.isInjectorReady ? 'pencil' : 'clock-o', {'bufferOption' : 'createInjector', 'bufferId' : (index + 1), 'createBlockInjector' : 1}, (!data.isInjectorReady || !value.data) ? 'disabled' : null)}}
{{:helper.link('Transfer', 'exclamation-circle', {'bufferOption' : 'transfer', 'bufferId' : (index + 1)}, (!data.hasOccupant || !value.data) ? 'disabled' : null)}}
{{:helper.link('Save To Disk', 'floppy-o', {'bufferOption' : 'saveDisk', 'bufferId' : (index + 1)}, (!value.data || !data.hasDisk) ? 'disabled' : null)}}
</div>
</div>
</div>
{{/for}}
<h4>Data Disk</h4>
<div class="itemGroup">
{{if data.hasDisk}}
{{if data.disk.data}}
<div class="item">
<div class="itemLabelNarrow">
Label:
</div>
<div class="itemContentWide">
{{:data.disk.label ? data.disk.label : 'No Label'}}
</div>
</div>
<div class="item">
<div class="itemLabelNarrow">
Subject:
</div>
<div class="itemContentWide">
{{:data.disk.owner ? data.disk.owner : '<span class="average">Unknown</span>'}}
</div>
</div>
<div class="item">
<div class="itemLabelNarrow">
Stored Data:
</div>
<div class="itemContentWide">
{{:data.disk.data == 'ui' ? 'Unique Identifiers' : 'Structural Enzymes'}}
{{:data.disk.ue ? ' + Unique Enzymes' : ''}}
</div>
</div>
{{else}}
<div class="item">
<div class="itemContentWide">
<span class="average">Disk is blank.</span>
</div>
</div>
{{/if}}
{{else}}
<div class="item">
<div class="itemContentWide">
<span class="highlight">No disk inserted.</span>
</div>
</div>
{{/if}}
<div class="item">
<div class="itemLabelNarrow">
Options:
</div>
<div class="itemContentWide">
{{:helper.link('Wipe Disk', 'trash', {'bufferOption' : 'wipeDisk'}, (!data.hasDisk || !data.disk.data) ? 'disabled' : null)}}
{{:helper.link('Eject Disk', 'eject', {'bufferOption' : 'ejectDisk'}, !data.hasDisk ? 'disabled' : null)}}
</div>
</div>
</div>
{{else data.selectedMenuKey == 'rejuvenators'}}
<h3>Rejuvenators</h3>
<div class="item">
<div class="itemLabelNarrow">
Inject:
</div>
<div class="itemContentWide">
{{:helper.link('5', 'pencil', {'injectRejuvenators' : 5}, (!data.hasOccupant || !data.beakerVolume) ? 'disabled' : null)}}
{{:helper.link('10', 'pencil', {'injectRejuvenators' : 10}, (!data.hasOccupant || !data.beakerVolume) ? 'disabled' : null)}}
{{:helper.link('20', 'pencil', {'injectRejuvenators' : 20}, (!data.hasOccupant || !data.beakerVolume) ? 'disabled' : null)}}
{{:helper.link('30', 'pencil', {'injectRejuvenators' : 30}, (!data.hasOccupant || !data.beakerVolume) ? 'disabled' : null)}}
{{:helper.link('50', 'pencil', {'injectRejuvenators' : 50}, (!data.hasOccupant || !data.beakerVolume) ? 'disabled' : null)}}
</div>
</div>
<div class="item">&nbsp;</div>
<div class="item">
<div class="itemLabelNarrow">
Beaker:
</div>
<div class="itemContentWide" style="width: 40%;">
{{if data.isBeakerLoaded}}
{{:data.beakerLabel ? data.beakerLabel : '<span class="average">No label</span>'}}<br>
{{if data.beakerVolume}}
<span class="highlight">{{:data.beakerVolume}} units remaining</span><br>
{{else}}
<span class="bad">Beaker is empty</span>
{{/if}}
{{else}}
<span class="average"><i>No beaker loaded</i></span>
{{/if}}
</div>
<div class="itemContentWide" style="width: 26%;">
{{:helper.link('Eject Beaker', 'eject', {'ejectBeaker' : 1}, data.isBeakerLoaded ? null : 'disabled')}}
</div>
</div>
{{/if}}
<div class="item">&nbsp;</div>
{{if !data.selectedMenuKey || data.selectedMenuKey == 'ui' || data.selectedMenuKey == 'se'}}
<h3>Radiation Emitter Settings</h3>
<div class="item">
<div class="itemLabelNarrow">
Intensity:
</div>
<div class="itemContentWide">
{{:helper.link('-', null, {'radiationIntensity' : 0}, (data.radiationIntensity > 1) ? null : 'disabled')}}
<div class="statusValue">&nbsp;{{:data.radiationIntensity}}&nbsp;</div>
{{:helper.link('+', null, {'radiationIntensity' : 1}, (data.radiationIntensity < 10) ? null : 'disabled')}}
</div>
</div>
<div class="item">
<div class="itemLabelNarrow">
Duration:
</div>
<div class="itemContentWide">
{{:helper.link('-', null, {'radiationDuration' : 0}, (data.radiationDuration > 2) ? null : 'disabled')}}
<div class="statusValue">&nbsp;{{:data.radiationDuration}}&nbsp;</div>
{{:helper.link('+', null, {'radiationDuration' : 1}, (data.radiationDuration < 20) ? null : 'disabled')}}
</div>
</div>
<div class="item">
<div class="itemLabelNarrow">
&nbsp;
</div>
<div class="itemContentWide">
{{:helper.link('Pulse Radiation', 'exclamation-circle', {'pulseRadiation' : 1}, !data.hasOccupant ? 'disabled' : null)}}
</div>
</div>
{{/if}}
{{/if}}
<div class="item">&nbsp;</div>
<hr>
<div class="item">
<div class="itemLabelNarrow">
Occupant:
</div>
<div class="itemContentWide">
{{:helper.link('Eject Occupant', 'eject', {'ejectOccupant' : 1}, data.locked || !data.hasOccupant || data.irradiating ? 'disabled' : null)}}
</div>
</div>
<div class="item">
<div class="itemLabelNarrow">
Door Lock:
</div>
<div class="itemContentWide">
{{:helper.link('Engaged', 'lock', {'toggleLock' : 1}, data.locked ? 'selected' : !data.hasOccupant ? 'disabled' : null)}}
{{:helper.link('Disengaged', 'unlock', {'toggleLock' : 1}, !data.locked ? 'selected' : data.irradiating ? 'disabled' : null)}}
</div>
</div>
{{if data.irradiating}}
<div class="mask">
<div class="maskContent">
<h1>Irradiating Subject</h1>
<h3>For {{:data.irradiating}} seconds.</h3>
</div>
</div>
{{/if}}
-171
View File
@@ -1,171 +0,0 @@
<!--
Title: Medical Records UI
Used In File(s): \code\game\machinery\computer\medical.dm
-->
<!--
#define MED_DATA_MAIN 1 // Main menu
#define MED_DATA_R_LIST 2 // Record list
#define MED_DATA_MAINT 3 // Records maintenance
#define MED_DATA_RECORD 4 // Record
#define MED_DATA_V_DATA 5 // Virus database
#define MED_DATA_MEDBOT 6 // Medbot monitor
-->
<div class="item">
<div class="itemLabelNarrow">
<b>Confirm Identity:</b>
</div>
<div class="itemContent">
{{:helper.link(data.scan ? data.scan : "----------", 'eject', {'scan' : 1}, null, data.scan ? 'itemContentWide' : 'fixedLeft')}}
</div>
</div>
<hr>
{{if data.authenticated}}
{{if data.screen == 1}} <!-- MED_DATA_MAIN -->
<h3>Menu</h3>
<div class="item">
<div class="line">{{:helper.link('Search Records', 'search', {'search' : 1})}}</div>
<div class="line">{{:helper.link('List Records', 'list', {'screen' : 2})}}</div>
<div class="line">{{:helper.link('Virus Database', 'database', {'screen' : 5})}}</div>
<div class="line">{{:helper.link('Medbot Tracking', 'plus-square', {'screen' : 6})}}</div>
<div class="line">{{:helper.link('Record Maintenance', 'wrench', {'screen' : 3})}}</div>
<div class="line">{{:helper.link('Logout', 'lock', {'logout' : 1})}}</div>
</div>
{{else data.screen == 2}} <!-- MED_DATA_R_LIST -->
<h3><center>Record List</center></h3>
{{for data.records}}
<div class="line">{{:helper.link(value.id + ': ' + value.name, 'user', {'d_rec' : value.ref})}}</div>
{{/for}}
<br><hr>
<div class="line">{{:helper.link('Back', 'arrow-left', {'screen' : 1})}}</div>
{{else data.screen == 3}} <!-- MED_DATA_MAINT -->
<h3>Records Maintenance</h3>
<div class="line">{{:helper.link('Backup To Disk', 'download', {'back' : 1}, 'disabled')}}</div>
<div class="line">{{:helper.link('Upload From disk', 'upload', {'u_load' : 1}, 'disabled')}}</div>
<div class="line">{{:helper.link('Delete All Records', 'trash', {'del_all' : 1})}}</div>
<div class="line">{{:helper.link('Back', 'arrow-left', {'screen' : 1})}}</div>
{{else data.screen == 4}} <!-- MED_DATA_RECORD -->
<h3><center>Medical Record</center></h3>
<h4><center>General Data</center></h4>
{{if data.general.empty}}
<span class="bad"><center>General Record Lost!</center></span>
{{else}}
<div class="statusDisplayRecords">
<table style='width:100%'><tr>
<td valign='top' style='margin-right: 300px;'>
{{for data.general.fields}}
<div class="item">
<div class="itemLabel">{{:value.field}}</div>
<div class="itemContent">{{:helper.link(value.value, value.edit ? 'pencil' : 'user-times', {'field' : value.edit}, value.edit ? null : 'disabled')}}</div>
</div>
{{/for}}
</td>
<td valign='middle' align='center' style='width: 300px;'>
{{if data.general.has_photos}}
{{for data.general.photos}}
{{if value.photo}}
<img src={{:value.photo}} height=96 width=96 border=5>
{{/if}}
{{/for}}
{{/if}}
</td>
</tr></table>
</div>
{{/if}}
<h4><center>Medical Data</center></h4>
{{if data.medical.empty}}
<span class="bad"><center>Medical Record Lost!</center></span>
<div style="text-align: center;">
<div style="display: inline-block;">{{:helper.link('New Record', 'plus', {'new' : 1})}}</div>
</div>
<h3><center>Menu</center></h3>
{{else}}
{{for data.medical.fields}}
<div class="item">
<div class="itemLabel">{{:value.field}}</div>
<div class="itemContent">{{:helper.link(value.value, 'pencil', {'field' : value.edit})}}</div>
</div>
{{if value.line_break}}
<br>
{{/if}}
{{/for}}
<h4><center>Comments/Log</center></h4>
{{for data.medical.comments}}
<div class="line">{{:value}}</div>
<div class="line">{{:helper.link('Remove entry', 'trash', {'del_c' : index})}}</div>
<br><hr>
{{/for}}
<div style="text-align: center;">
<div style="display: inline-block;">{{:helper.link('Add Entry', 'plus', {'add_c' : 1})}}</div>
</div>
<h3><center>Menu</center></h3>
<div class="line">{{:helper.link('Delete Record (Medical Only)', 'trash', {'del_r' : 1})}}</div>
{{/if}}
<div class="line">{{:helper.link('Print Record', 'print', {'print_p' : 1})}}</div>
<div class="line">{{:helper.link('Back', 'arrow-left', {'screen' : 2})}}</div>
{{else data.screen == 5}} <!-- MED_DATA_V_DATA -->
<h3><center>Virus Database</center></h3>
{{for data.virus}}
<div class="line">{{:helper.link(value.name, 'arrow-right', {'vir' : value.D})}}</div>
{{/for}}
<br><hr>
{{:helper.link('Back', 'arrow-left', {'screen' : 1})}}
{{else data.screen == 6}} <!-- MED_DATA_MEDBOT -->
<h3><center>Medical Robot Monitor</center></h3>
{{for data.medbots}}
<h4>{{:value.name}}</h4>
<div class="item">
<div class="itemLabel">Location:</div>
<div class="itemContent">{{:value.x}}, {{:value.y}}</div>
</div>
<div class="item">
<div class="itemLabel">Status:</div>
<div class="itemContent">
{{:value.on ? "<span class='good'>Online</span>" : "<span class='average'>Offline</span>"}}. {{if value.use_beaker}}Reservoir: [{{:value.total_volume}}/{{:value.maximum_volume}}]{{else}}Using internal synthesizer.{{/if}}
</div>
</div>
<hr>
{{empty}}
<h4 class="bad"><center>None detected!</center></h4>
{{/for}}
<div class="line">{{:helper.link('Back', 'arrow-left', {'screen' : 1})}}</div>
{{/if}}
{{else}}
<h3>Menu</h3>
{{:helper.link('Login', 'unlock', {'login' : 1})}}
{{/if}}
{{if data.temp}}
<div class="mask" style="display: table;">
<div class="maskContent" style="margin: 0px; vertical-align: middle; align:center; display: table-cell;">
{{if data.temp.notice}}
<div class="notice"><center>{{:data.temp.text}}</center></div>
{{else}}
<center>{{:data.temp.text}}</center>
{{/if}}
{{if data.temp.has_buttons}}
<div class="line">
<div style="display: inline-block;">
<div style="width: 500px; display: flex; display: -webkit-flex; flex-wrap: wrap; -webkit-flex-wrap: wrap; justify-content: center; -webkit-justify-content: center;">
{{for data.temp.buttons}}
{{:helper.link(value.name, value.icon, {'temp' : 1, 'temp_action' : value.href}, value.status)}}
{{/for}}
</div>
</div>
</div>
{{/if}}
<div style="display: inline-block;">
<div class="item">{{:helper.link('Clear screen', 'home', {'temp' : 1})}}</div>
</div>
</div>
</div>
{{/if}}
-215
View File
@@ -1,215 +0,0 @@
<!--
Title: Operating Computer UI
Used In File(s): \code\game\machinery\computer\Operating.dm
-->
<h3>Patient Monitor</h3>
<div class="statusDisplay">
{{if data.choice==0}}
{{if !data.hasOccupant}}
<div class="line">No Patient Detected</div>
{{else}}
<div class="line">
{{:data.occupant.name}}&nbsp;=>&nbsp;
{{if data.occupant.stat == 0}}
<span class="good">Conscious</span>
{{else data.occupant.stat == 1}}
<span class="average">Unconscious</span>
{{else}}
<span class="bad">DEAD</span>
{{/if}}
</div>
<div class="line">
<div class="statusLabel">Health:</div>
{{if data.occupant.health >= data.occupant.maxHealth}}
{{:helper.displayBar(data.occupant.health, 0, data.occupant.maxHealth, 'good')}}
{{else data.occupant.health > 0}}
{{:helper.displayBar(data.occupant.health, 0, data.occupant.maxHealth, 'average')}}
{{else data.occupant.health >= data.minhealth}}
{{:helper.displayBar(data.occupant.health, 0, data.occupant.minHealth, 'average alignRight')}}
{{else}}
{{:helper.displayBar(data.occupant.health, 0, data.occupant.minHealth, 'bad alignRight')}}
{{/if}}
<div class="statusValue">{{:helper.smoothRound(data.occupant.health)}}</div>
</div>
<div class="line">
<div class="statusLabel">Brute Damage:</div>
{{:helper.displayBar(data.occupant.bruteLoss, 0, data.occupant.maxHealth, 'bad')}}
<div class="statusValue">{{:helper.smoothRound(data.occupant.bruteLoss)}}</div>
</div>
<div class="line">
<div class="statusLabel">Resp. Damage:</div>
{{:helper.displayBar(data.occupant.oxyLoss, 0, data.occupant.maxHealth, 'bad')}}
<div class="statusValue">{{:helper.smoothRound(data.occupant.oxyLoss)}}</div>
</div>
<div class="line">
<div class="statusLabel">Toxin Damage:</div>
{{:helper.displayBar(data.occupant.toxLoss, 0, data.occupant.maxHealth, 'bad')}}
<div class="statusValue">{{:helper.smoothRound(data.occupant.toxLoss)}}</div>
</div>
<div class="line">
<div class="statusLabel">Burn Severity:</div>
{{:helper.displayBar(data.occupant.fireLoss, 0, data.occupant.maxHealth, 'bad')}}
<div class="statusValue">{{:helper.smoothRound(data.occupant.fireLoss)}}</div>
</div>
<div class="line">
<div class="statusLabel">Patient Temperature:</div>
{{if data.occupant.temperatureSuitability == -3}}
{{:helper.displayBar(data.occupant.bodyTemperature, 0, data.occupant.maxTemp, 'bad')}}
<div class="statusValue bad">{{:helper.smoothRound(data.occupant.btCelsius)}}&deg;C, {{:helper.smoothRound(data.occupant.btFaren)}}&deg;F</div>
{{else data.occupant.temperatureSuitability == -2}}
{{:helper.displayBar(data.occupant.bodyTemperature, 0, data.occupant.maxTemp, 'average')}}
<div class="statusValue bad">{{:helper.smoothRound(data.occupant.btCelsius)}}&deg;C, {{:helper.smoothRound(data.occupant.btFaren)}}&deg;F</div>
{{else data.occupant.temperatureSuitability == -1}}
{{:helper.displayBar(data.occupant.bodyTemperature, 0, data.occupant.maxTemp, 'average')}}
<div class="statusValue">{{:helper.smoothRound(data.occupant.btCelsius)}}&deg;C, {{:helper.helper.smoothRound(data.occupant.btFaren)}}&deg;F</div>
{{else data.occupant.temperatureSuitability == 0}}
{{:helper.displayBar(data.occupant.bodyTemperature, 0, data.occupant.maxTemp, 'good')}}
<div class="statusValue">{{:helper.smoothRound(data.occupant.btCelsius)}}&deg;C, {{:helper.smoothRound(data.occupant.btFaren)}}&deg;F</div>
{{else data.occupant.temperatureSuitability == 1}}
{{:helper.displayBar(data.occupant.bodyTemperature, 0, data.occupant.maxTemp, 'average')}}
<div class="statusValue">{{:helper.smoothRound(data.occupant.btCelsius)}}&deg;C, {{:helper.smoothRound(data.occupant.btFaren)}}&deg;F</div>
{{else data.occupant.temperatureSuitability == 2}}
{{:helper.displayBar(data.occupant.bodyTemperature, 0, data.occupant.maxTemp, 'average')}}
<div class="statusValue bad">{{:helper.smoothRound(data.occupant.btCelsius)}}&deg;C, {{:helper.smoothRound(data.occupant.btFaren)}}&deg;F</div>
{{else data.occupant.temperatureSuitability == 3}}
{{:helper.displayBar(data.occupant.bodyTemperature, 0, data.occupant.maxTemp, 'bad')}}
<div class="statusValue bad">{{:helper.smoothRound(data.occupant.btCelsius)}}&deg;C, {{:helper.smoothRound(data.occupant.btFaren)}}&deg;F</div>
{{/if}}
</div>
{{if data.occupant.hasBlood}}
<br>
<div class="line">
<div class="statusLabel">Blood Level:</div>
{{if data.occupant.bloodPercent <= 60}}
{{:helper.displayBar(data.occupant.bloodLevel, 0, data.occupant.bloodMax, 'bad')}}
<div class="statusValue">
{{:helper.smoothRound(data.occupant.bloodPercent)}}%, {{:helper.smoothRound(data.occupant.bloodLevel)}}cl
</div>
{{else data.occupant.bloodPercent <= 90}}
{{:helper.displayBar(data.occupant.bloodLevel, 0, data.occupant.bloodMax, 'average')}}
<div class="statusValue">
{{:helper.smoothRound(data.occupant.bloodPercent)}}%, {{:helper.smoothRound(data.occupant.bloodLevel)}}cl
</div>
{{else}}
{{:helper.displayBar(data.occupant.bloodLevel, 0, data.occupant.bloodMax, 'good')}}
<div class="statusValue">
{{:helper.smoothRound(data.occupant.bloodPercent)}}%, {{:helper.smoothRound(data.occupant.bloodLevel)}}cl
</div>
{{/if}}
<div class="line">
<div class="statusLabel">Blood Type:</div> <div class="statusValue">{{:data.occupant.bloodType}}</div>
</div>
</div>
<div class="line">
<div class="statusLabel">Pulse:</div> <div class="statusValue">{{:data.occupant.pulse}} bpm</div>
</div>
{{/if}}
{{if data.occupant.inSurgery}}
<div class="line">
<div class="statusLabel">Initiated Surgery Procedure:</div> <div class="statusValue">{{:data.occupant.surgeryName}}</div>
<br>
<div class="statusLabel">Next Step:</div> <div class="statusValue">{{:data.occupant.stepName}}</div>
</div>
{{/if}}
<br>
<br>
{{/if}}
<div class="item">
<div class="itemLabelWide">
{{:helper.link('Options', 'gear', {'choiceOn' : 1})}}
</div>
</div>
{{else}}
<div class="item">
<div class="statusLabel">
Loudspeaker:
</div>
<div class="itemContentNarrow" style="float: left">
{{:helper.link('On', 'power-off', {'verboseOn' : 1}, data.verbose ? 'selected' : null)}}
{{:helper.link('Off', 'close', {'verboseOff' : 1}, data.verbose ? null : 'selected')}}
</div>
</div>
<br>
<div class="item">
<div class="statusLabel">
Health Announcer:
</div>
<div class="itemContentNarrow" style="float: left">
{{:helper.link('On', 'power-off', {'healthOn' : 1}, data.health ? 'selected' : null)}}
{{:helper.link('Off', 'close', {'healthOff' : 1}, data.health ? null : 'selected')}}
</div>
</div>
<div class="item">
<div class="line">
<div class="statusLabel">Health Announcer Threshold:</div>
{{:helper.link('-', null, {'health_adj' : -100}, (data.healthAlarm >= 0) ? null : 'disabled')}}
{{:helper.link('-', null, {'health_adj' : -10}, (data.healthAlarm >= -90) ? null : 'disabled')}}
{{:helper.link('-', null, {'health_adj' : -1}, (data.healthAlarm > -100) ? null : 'disabled')}}
{{if data.healthAlarm >= 100}}
{{:helper.displayBar(data.healthAlarm, 0, 100, 'good')}}
{{else data.healthAlarm > 0}}
{{:helper.displayBar(data.healthAlarm, 0, 100, 'average')}}
{{else data.healthAlarm >= -100}}
{{:helper.displayBar(data.healthAlarm, 0, -100, 'average alignRight')}}
{{else}}
{{:helper.displayBar(data.healthAlarm, 0, -100, 'bad alignRight')}}
{{/if}}
{{:helper.link('+', null, {'health_adj' : 1}, (data.healthAlarm < 100) ? null : 'disabled')}}
{{:helper.link('+', null, {'health_adj' : 10}, (data.healthAlarm <= 90) ? null : 'disabled')}}
{{:helper.link('+', null, {'health_adj' : 100}, (data.healthAlarm <= 0) ? null : 'disabled')}}
<div style="float: left; width: 80px; text-align: center;">&nbsp;{{: data.healthAlarm }} &nbsp;</div>
</div>
</div>
<br>
<div class="item">
<div class="statusLabel">
Oxygen Alarm:
</div>
<div class="itemContentNarrow" style="float: left">
{{:helper.link('On', 'power-off', {'oxyOn' : 1}, data.oxy ? 'selected' : null)}}
{{:helper.link('Off', 'close', {'oxyOff' : 1}, data.oxy ? null : 'selected')}}
</div>
</div>
<div class="item">
<div class="line">
<div class="statusLabel">Oxygen Alert Threshold:</div>
{{:helper.link('-', null, {'oxy_adj' : -100}, (data.oxyAlarm >= 100) ? null : 'disabled')}}
{{:helper.link('-', null, {'oxy_adj' : -10}, (data.oxyAlarm >= 10) ? null : 'disabled')}}
{{:helper.link('-', null, {'oxy_adj' : -1}, (data.oxyAlarm >= 0) ? null : 'disabled')}}
{{:helper.displayBar( data.oxyAlarm, 0, 200, 'good')}}
{{:helper.link('+', null, {'oxy_adj' : 1}, (data.oxyAlarm < 200) ? null : 'disabled')}}
{{:helper.link('+', null, {'oxy_adj' : 10}, (data.oxyAlarm <= 190) ? null : 'disabled')}}
{{:helper.link('+', null, {'oxy_adj' : 100}, (data.oxyAlarm <= 100) ? null : 'disabled')}}
<div style="float: left; width: 80px; text-align: center;">&nbsp;{{: data.oxyAlarm }} &nbsp;</div>
</div>
</div>
<br>
<div class="item">
<div class="statusLabel">
Critical Alert:
</div>
<div class="itemContentNarrow" style="float: left">
{{:helper.link('On', 'power-off', {'critOn' : 1}, data.crit ? 'selected' : null)}}
{{:helper.link('Off', 'close', {'critOff' : 1}, data.crit ? null : 'selected')}}
</div>
</div>
<br>
<div class="item">
<div class="itemLabelWide">
{{:helper.link('Return', 'arrow-left', {'choiceOff' : 1})}}
</div>
</div>
{{/if}}
</div>
-211
View File
@@ -1,211 +0,0 @@
<!--
Title: Sleeper Status UI
Used In File(s): \code\game\machinery\Sleeper.dm
-->
<h3>Sleeper Status</h3>
<div class="statusDisplay">
{{if !data.hasOccupant}}
<div class="line">Sleeper Unoccupied</div>
{{else}}
<div class="line">
{{:data.occupant.name}}&nbsp;=>&nbsp;
{{if data.occupant.stat == 0}}
<span class="good">Conscious</span>
{{else data.occupant.stat == 1}}
<span class="average">Unconscious</span>
{{else}}
<span class="bad">DEAD</span>
{{/if}}
</div>
<div class="line">
<div class="statusLabel">Health:</div>
{{if data.occupant.health >= data.occupant.maxHealth}}
{{:helper.displayBar(data.occupant.health, 0, data.occupant.maxHealth, 'good')}}
{{else data.occupant.health > 0}}
{{:helper.displayBar(data.occupant.health, 0, data.occupant.maxHealth, 'average')}}
{{else data.occupant.health >= data.minhealth}}
{{:helper.displayBar(data.occupant.health, 0, data.occupant.minHealth, 'average alignRight')}}
{{else}}
{{:helper.displayBar(data.occupant.health, 0, data.occupant.minHealth, 'bad alignRight')}}
{{/if}}
<div class="statusValue">{{:helper.smoothRound(data.occupant.health)}}</div>
</div>
<div class="line">
<div class="statusLabel">=&gt; Brute Damage:</div>
{{:helper.displayBar(data.occupant.bruteLoss, 0, data.occupant.maxHealth, 'bad')}}
<div class="statusValue">{{:helper.smoothRound(data.occupant.bruteLoss)}}</div>
</div>
<div class="line">
<div class="statusLabel">=&gt; Resp. Damage:</div>
{{:helper.displayBar(data.occupant.oxyLoss, 0, data.occupant.maxHealth, 'bad')}}
<div class="statusValue">{{:helper.smoothRound(data.occupant.oxyLoss)}}</div>
</div>
<div class="line">
<div class="statusLabel">=&gt; Toxin Damage:</div>
{{:helper.displayBar(data.occupant.toxLoss, 0, data.occupant.maxHealth, 'bad')}}
<div class="statusValue">{{:helper.smoothRound(data.occupant.toxLoss)}}</div>
</div>
<div class="line">
<div class="statusLabel">=&gt; Burn Severity:</div>
{{:helper.displayBar(data.occupant.fireLoss, 0, data.occupant.maxHealth, 'bad')}}
<div class="statusValue">{{:helper.smoothRound(data.occupant.fireLoss)}}</div>
</div>
<br>
<div class="line">
<!--
<div class="statusLabel">Patient Temperature:</div>
{{if data.occupant.temperatureSuitability == -3}}
{{:helper.displayBar(data.occupant.bodyTemperature, 0, data.occupant.maxTemp, 'cold3')}}
<div class="statusValue bad">{{:helper.smoothRound(data.occupant.btCelsius)}}&deg;C, {{:helper.smoothRound(data.occupant.btFaren)}}&deg;F</div>
{{else data.occupant.temperatureSuitability == -2}}
{{:helper.displayBar(data.occupant.bodyTemperature, 0, data.occupant.maxTemp, 'cold2')}}
<div class="statusValue bad">{{:helper.smoothRound(data.occupant.btCelsius)}}&deg;C, {{:helper.smoothRound(data.occupant.btFaren)}}&deg;F</div>
{{else data.occupant.temperatureSuitability == -1}}
{{:helper.displayBar(data.occupant.bodyTemperature, 0, data.occupant.maxTemp, 'cold1')}}
<div class="statusValue">{{:helper.smoothRound(data.occupant.btCelsius)}}&deg;C, {{:helper.smoothRound(data.occupant.btFaren)}}&deg;F</div>
{{else data.occupant.temperatureSuitability == 0}}
{{:helper.displayBar(data.occupant.bodyTemperature, 0, data.occupant.maxTemp, 'good')}}
<div class="statusValue">{{:helper.smoothRound(data.occupant.btCelsius)}}&deg;C, {{:helper.smoothRound(data.occupant.btFaren)}}&deg;F</div>
{{else data.occupant.temperatureSuitability == 1}}
{{:helper.displayBar(data.occupant.bodyTemperature, 0, data.occupant.maxTemp, 'hot1')}}
<div class="statusValue">{{:helper.smoothRound(data.occupant.btCelsius)}}&deg;C, {{:helper.smoothRound(data.occupant.btFaren)}}&deg;F</div>
{{else data.occupant.temperatureSuitability == 2}}
{{:helper.displayBar(data.occupant.bodyTemperature, 0, data.occupant.maxTemp, 'hot2')}}
<div class="statusValue bad">{{:helper.smoothRound(data.occupant.btCelsius)}}&deg;C, {{:helper.smoothRound(data.occupant.btFaren)}}&deg;F</div>
{{else data.occupant.temperatureSuitability == 3}}
{{:helper.displayBar(data.occupant.bodyTemperature, 0, data.occupant.maxTemp, 'hot3')}}
<div class="statusValue bad">{{:helper.smoothRound(data.occupant.btCelsius)}}&deg;C, {{:helper.smoothRound(data.occupant.btFaren)}}&deg;F</div>
{{/if}}
If you can get the changing temperature bars to work right, congratulations
-->
<div class="statusLabel">Patient Temperature:</div>
{{if data.occupant.temperatureSuitability == -3}}
{{:helper.displayBar(data.occupant.bodyTemperature, 0, data.occupant.maxTemp, 'bad')}}
<div class="statusValue bad">{{:helper.smoothRound(data.occupant.btCelsius)}}&deg;C, {{:helper.smoothRound(data.occupant.btFaren)}}&deg;F</div>
{{else data.occupant.temperatureSuitability == -2}}
{{:helper.displayBar(data.occupant.bodyTemperature, 0, data.occupant.maxTemp, 'average')}}
<div class="statusValue bad">{{:helper.smoothRound(data.occupant.btCelsius)}}&deg;C, {{:helper.smoothRound(data.occupant.btFaren)}}&deg;F</div>
{{else data.occupant.temperatureSuitability == -1}}
{{:helper.displayBar(data.occupant.bodyTemperature, 0, data.occupant.maxTemp, 'average')}}
<div class="statusValue">{{:helper.smoothRound(data.occupant.btCelsius)}}&deg;C, {{:helper.smoothRound(data.occupant.btFaren)}}&deg;F</div>
{{else data.occupant.temperatureSuitability == 0}}
{{:helper.displayBar(data.occupant.bodyTemperature, 0, data.occupant.maxTemp, 'good')}}
<div class="statusValue">{{:helper.smoothRound(data.occupant.btCelsius)}}&deg;C, {{:helper.smoothRound(data.occupant.btFaren)}}&deg;F</div>
{{else data.occupant.temperatureSuitability == 1}}
{{:helper.displayBar(data.occupant.bodyTemperature, 0, data.occupant.maxTemp, 'average')}}
<div class="statusValue">{{:helper.smoothRound(data.occupant.btCelsius)}}&deg;C, {{:helper.smoothRound(data.occupant.btFaren)}}&deg;F</div>
{{else data.occupant.temperatureSuitability == 2}}
{{:helper.displayBar(data.occupant.bodyTemperature, 0, data.occupant.maxTemp, 'average')}}
<div class="statusValue bad">{{:helper.smoothRound(data.occupant.btCelsius)}}&deg;C, {{:helper.smoothRound(data.occupant.btFaren)}}&deg;F</div>
{{else data.occupant.temperatureSuitability == 3}}
{{:helper.displayBar(data.occupant.bodyTemperature, 0, data.occupant.maxTemp, 'bad')}}
<div class="statusValue bad">{{:helper.smoothRound(data.occupant.btCelsius)}}&deg;C, {{:helper.smoothRound(data.occupant.btFaren)}}&deg;F</div>
{{/if}}
</div>
{{if data.occupant.hasBlood}}
<br>
<div class="line">
<div class="statusLabel">Pulse:</div> <div class="statusValue">{{:data.occupant.pulse}} bpm</div>
</div>
<div class="line">
<div class="statusLabel">Blood Level:</div>
{{if data.occupant.bloodPercent <= 60}}
{{:helper.displayBar(data.occupant.bloodLevel, 0, data.occupant.bloodMax, 'bad')}}
<div class="statusValue">
{{:helper.smoothRound(data.occupant.bloodPercent)}}%, {{:helper.smoothRound(data.occupant.bloodLevel)}}cl
</div>
{{else data.occupant.bloodPercent <= 90}}
{{:helper.displayBar(data.occupant.bloodLevel, 0, data.occupant.bloodMax, 'average')}}
<div class="statusValue">
{{:helper.smoothRound(data.occupant.bloodPercent)}}%, {{:helper.smoothRound(data.occupant.bloodLevel)}}cl
</div>
{{else}}
{{:helper.displayBar(data.occupant.bloodLevel, 0, data.occupant.bloodMax, 'good')}}
<div class="statusValue">
{{:helper.smoothRound(data.occupant.bloodPercent)}}%, {{:helper.smoothRound(data.occupant.bloodLevel)}}cl
</div>
{{/if}}
</div>
{{/if}}
{{/if}}
</div>
<h3>Sleeper Operation</h3>
<div class="item">
<div class="itemLabel">
Sleeper Status:
</div>
<div class="itemContent" style="width: 26%;">
{{:helper.link('Eject Occupant', 'arrow-circle-o-down', {'ejectify' : 1}, data.hasOccupant ? null : 'disabled')}}
</div>
</div>
<div class="item">&nbsp;</div>
<div class="item">
<div class="itemLabel">
Dialysis Beaker:
</div>
<div class="itemContent" style="width: 40%;">
{{if data.isBeakerLoaded}}
<span class="highlight">{{:helper.smoothRound(data.beakerFreeSpace)}} units of space remaining</span><br>
{{else}}
<span class="average"><i>No Dialysis Output Beaker Loaded</i></span>
{{/if}}
</div>
<div class="itemContent" style="width: 26%;">
{{:helper.link('Eject Beaker', 'eject', {'removebeaker' : 1}, data.isBeakerLoaded ? null : 'disabled')}}
{{if data.isBeakerLoaded}}
<div class="line">
<div class="itemContent">
{{:helper.link('On', 'power-off', {'togglefilter' : 1}, data.occupant.hasBlood ? (data.dialysis ? 'selected' : null) : 'disabled')}}{{:helper.link('Off', 'close', {'togglefilter' : 1}, data.dialysis ? null : 'selected')}}
</div>
</div>
{{/if}}
</div>
</div>
<div class="item">
<div class="itemLabel">
Chemicals:
</div>
{{for data.chemicals}}
<div class="line"><b>{{:value.title}}</b></div>
<div class="line">
{{if value.overdosing}}
{{:helper.displayBar(value.occ_amount, 0, data.maxchem, 'bad')}}
{{else value.od_warning}}
{{:helper.displayBar(value.occ_amount, 0, data.maxchem, 'average')}}
{{else}}
{{:helper.displayBar(value.occ_amount, 0, data.maxchem, 'good')}}
{{/if}}
<div class="statusValue">{{:helper.smoothRound(value.pretty_amount)}}/{{:data.maxchem}}</div>
<br>
</div>
<div class="line">
<div class="itemContent">
{{:helper.link('5', 'gear', {'chemical' : value.id, 'amount' : 5}, (!value.injectable || ((value.occ_amount + 5) > data.maxchem)) ? 'disabled' : null)}}
{{:helper.link('10', 'gear', {'chemical' : value.id, 'amount' : 10}, (!value.injectable || ((value.occ_amount + 10) > data.maxchem)) ? 'disabled' : null)}}
</div>
</div>
{{/for}}
<div class="item">&nbsp;</div>
<div class="item">
<div class="itemLabel" style="width: 35%">
Auto-eject dead patients:
</div>
<div class="itemContent" style="width: 30%">
{{:helper.link('On', 'power-off', {'auto_eject_dead_on' : 1}, data.auto_eject_dead ? 'selected' : null)}}{{:helper.link('Off', 'close', {'auto_eject_dead_off' : 0}, data.auto_eject_dead ? null : 'selected')}}
</div>
</div>
</div>
+2
View File
@@ -72,6 +72,7 @@
#include "code\__DEFINES\station_goals.dm"
#include "code\__DEFINES\status_effects.dm"
#include "code\__DEFINES\subsystems.dm"
#include "code\__DEFINES\tgui.dm"
#include "code\__DEFINES\tools.dm"
#include "code\__DEFINES\typeids.dm"
#include "code\__DEFINES\vv.dm"
@@ -2455,6 +2456,7 @@
#include "code\modules\telesci\telepad.dm"
#include "code\modules\telesci\telesci_computer.dm"
#include "code\modules\tgui\external.dm"
#include "code\modules\tgui\modal.dm"
#include "code\modules\tgui\states.dm"
#include "code\modules\tgui\tgui.dm"
#include "code\modules\tgui\modules\_base.dm"
+21
View File
@@ -68,7 +68,10 @@ const mapColorPropTo = attrName => (style, value) => {
const styleMapperByPropName = {
// Direct mapping
display: mapRawPropTo('display'),
position: mapRawPropTo('position'),
float: mapRawPropTo('float'),
clear: mapRawPropTo('clear'),
overflow: mapRawPropTo('overflow'),
overflowX: mapRawPropTo('overflow-x'),
overflowY: mapRawPropTo('overflow-y'),
@@ -88,6 +91,9 @@ const styleMapperByPropName = {
opacity: mapRawPropTo('opacity'),
textAlign: mapRawPropTo('text-align'),
verticalAlign: mapRawPropTo('vertical-align'),
textTransform: mapRawPropTo('text-transform'),
wordWrap: mapRawPropTo('word-wrap'),
textOverflow: mapRawPropTo('text-overflow'),
// Boolean props
inline: mapBooleanPropTo('display', 'inline-block'),
bold: mapBooleanPropTo('font-weight', 'bold'),
@@ -125,6 +131,18 @@ const styleMapperByPropName = {
color: mapColorPropTo('color'),
textColor: mapColorPropTo('color'),
backgroundColor: mapColorPropTo('background-color'),
// Flex props
order: mapRawPropTo('order'),
flexDirection: mapRawPropTo('flex-direction'),
flexGrow: mapRawPropTo('flex-grow'),
flexShrink: mapRawPropTo('flex-shrink'),
flexWrap: mapRawPropTo('flex-wrap'),
flexFlow: mapRawPropTo('flex-flow'),
flexBasis: mapRawPropTo('flex-basis'),
flex: mapRawPropTo('flex'),
alignItems: mapRawPropTo('align-items'),
justifyContent: mapRawPropTo('justify-content'),
alignSelf: mapRawPropTo('align-self'),
// Utility props
fillPositionedParent: (style, value) => {
if (value) {
@@ -140,6 +158,9 @@ const styleMapperByPropName = {
export const computeBoxProps = props => {
const computedProps = {};
const computedStyles = {};
if (props.double) {
computedStyles["transform"] = "scale(2);";
}
// Compute props
for (let propName of Object.keys(props)) {
if (propName === 'style') {
+4
View File
@@ -8,9 +8,11 @@ export const computeFlexProps = props => {
direction,
wrap,
align,
alignContent,
justify,
inline,
spacing = 0,
spacingPrecise = 0,
...rest
} = props;
return {
@@ -23,6 +25,7 @@ export const computeFlexProps = props => {
),
inline && 'Flex--inline',
spacing > 0 && 'Flex--spacing--' + spacing,
spacingPrecise > 0 && 'Flex--spacingPrecise--' + spacingPrecise,
className,
]),
style: {
@@ -30,6 +33,7 @@ export const computeFlexProps = props => {
'flex-direction': direction,
'flex-wrap': wrap,
'align-items': align,
'align-content': alignContent,
'justify-content': justify,
},
...rest,
+6
View File
@@ -76,6 +76,11 @@ export class Input extends Component {
const input = this.inputRef.current;
if (input) {
input.value = toInputValue(nextValue);
if (this.props.autofocus) {
input.focus();
input.selectionStart = 0;
input.selectionEnd = input.value.length;
}
}
}
@@ -104,6 +109,7 @@ export class Input extends Component {
value,
maxLength,
placeholder,
autofocus,
...boxProps
} = props;
// Box props
+5 -1
View File
@@ -35,6 +35,7 @@ export const Knob = props => {
size,
bipolar,
children,
popUpPosition,
...rest
} = props;
return (
@@ -102,7 +103,10 @@ export const Knob = props => {
</div>
</div>
{dragging && (
<div className="Knob__popupValue">
<div className={classes([
'Knob__popupValue',
popUpPosition && 'Knob__popupValue--' + popUpPosition,
])}>
{displayElement}
</div>
)}
@@ -20,6 +20,7 @@ export const LabeledListItem = props => {
labelColor = 'label',
color,
textAlign,
verticalAlign,
buttons,
content,
children,
@@ -33,6 +34,7 @@ export const LabeledListItem = props => {
<Box
as="td"
color={labelColor}
verticalAlign={verticalAlign}
className={classes([
'LabeledList__cell',
'LabeledList__label',
@@ -43,6 +45,7 @@ export const LabeledListItem = props => {
as="td"
color={color}
textAlign={textAlign}
verticalAlign={verticalAlign}
className={classes([
'LabeledList__cell',
'LabeledList__content',
+11 -1
View File
@@ -6,10 +6,20 @@ export const Modal = props => {
const {
className,
children,
onEnter,
...rest
} = props;
let handleKeyDown;
if (onEnter) {
handleKeyDown = e => {
if (e.keyCode === 13) {
onEnter(e);
}
};
}
return (
<Dimmer>
<Dimmer
onKeyDown={handleKeyDown}>
<div
className={classes([
'Modal',
+10 -2
View File
@@ -8,7 +8,10 @@ export const Section = props => {
level = 1,
buttons,
content,
stretchContents,
noTopPadding,
children,
scrollable,
...rest
} = props;
const hasTitle = !isFalsy(title) || !isFalsy(buttons);
@@ -18,6 +21,7 @@ export const Section = props => {
className={classes([
'Section',
'Section--level--' + level,
props.flexGrow && 'Section--flex',
className,
])}
{...rest}>
@@ -32,10 +36,14 @@ export const Section = props => {
</div>
)}
{hasContent && (
<div className="Section__content">
<Box className={classes([
"Section__content",
!!stretchContents && "Section__content--stretchContents",
!!noTopPadding && "Section__content--noTopPadding",
])}>
{content}
{children}
</div>
</Box>
)}
</Box>
);
+16 -16
View File
@@ -3,30 +3,30 @@ import 'core-js/es';
import 'core-js/web/immediate';
import 'core-js/web/queue-microtask';
import 'core-js/web/timers';
import 'regenerator-runtime/runtime';
import './polyfills/html5shiv';
import './polyfills/ie8';
import './polyfills/dom4';
import './polyfills/css-om';
import './polyfills/inferno';
// Themes
import './styles/main.scss';
import './styles/themes/cardtable.scss';
import './styles/themes/malfunction.scss';
import './styles/themes/ntos.scss';
import './styles/themes/hackerman.scss';
import './styles/themes/retro.scss';
import './styles/themes/syndicate.scss';
import { loadCSS } from 'fg-loadcss';
import { render } from 'inferno';
import 'regenerator-runtime/runtime';
import { setupHotReloading } from 'tgui-dev-server/link/client';
import { backendUpdate } from './backend';
import { IS_IE8 } from './byond';
import { setupDrag } from './drag';
import { logger } from './logging';
import './polyfills/css-om';
import './polyfills/dom4';
import './polyfills/html5shiv';
import './polyfills/ie8';
import './polyfills/inferno';
import { createStore, StoreProvider } from './store';
// Themes
import './styles/main.scss';
import './styles/themes/cardtable.scss';
import './styles/themes/hackerman.scss';
import './styles/themes/malfunction.scss';
import './styles/themes/ntos.scss';
import './styles/themes/retro.scss';
import './styles/themes/syndicate.scss';
const enteredBundleAt = Date.now();
const store = createStore();
@@ -0,0 +1,433 @@
import { round } from 'common/math';
import { Fragment } from 'inferno';
import { useBackend } from "../backend";
import { AnimatedNumber, Box, Button, Flex, Icon, LabeledList, ProgressBar, Section, Table, Tooltip } from "../components";
import { Window } from "../layouts";
const stats = [
['good', 'Alive'],
['average', 'Critical'],
['bad', 'DEAD'],
];
const abnormalities = [
['hasBorer', 'bad', 'Large growth detected in frontal lobe,'
+ ' possibly cancerous. Surgical removal is recommended.'],
['hasVirus', 'bad', 'Viral pathogen detected in blood stream.'],
['blind', 'average', 'Cataracts detected.'],
['colourblind', 'average', 'Photoreceptor abnormalities detected.'],
['nearsighted', 'average', 'Retinal misalignment detected.'],
];
const damages = [
['Respiratory', 'oxyLoss'],
['Brain', 'brainLoss'],
['Toxin', 'toxLoss'],
['Radioactive', 'radLoss'],
['Brute', 'bruteLoss'],
['Genetic', 'cloneLoss'],
['Burn', 'fireLoss'],
['Paralysis', 'paralysis'],
];
const damageRange = {
average: [0.25, 0.5],
bad: [0.5, Infinity],
};
const mapTwoByTwo = (a, c) => {
let result = [];
for (let i = 0; i < a.length; i += 2) {
result.push(c(a[i], a[i + 1], i));
}
return result;
};
const reduceOrganStatus = A => {
return A.length > 0
? A
.filter(s => !!s && s.length > 0)
.reduce((a, s) => a === null
? [s]
: <Fragment key={s}>{a}<br />{s}</Fragment>, null
)
: null;
};
const germStatus = i => {
if (i > 100) {
if (i < 300) { return "mild infection"; }
if (i < 400) { return "mild infection+"; }
if (i < 500) { return "mild infection++"; }
if (i < 700) { return "acute infection"; }
if (i < 800) { return "acute infection+"; }
if (i < 900) { return "acute infection++"; }
if (i >= 900) { return "septic"; }
}
return "";
};
export const BodyScanner = (props, context) => {
const { data } = useBackend(context);
const {
occupied,
occupant = {},
} = data;
const body = occupied ? (
<BodyScannerMain occupant={occupant} />
) : (
<BodyScannerEmpty />
);
return (
<Window resizable>
<Window.Content scrollable className="Layout__content--flexColumn">
{body}
</Window.Content>
</Window>
);
};
const BodyScannerMain = props => {
const {
occupant,
} = props;
return (
<Box>
<BodyScannerMainOccupant occupant={occupant} />
<BodyScannerMainAbnormalities occupant={occupant} />
<BodyScannerMainDamage occupant={occupant} />
<BodyScannerMainOrgansExternal organs={occupant.extOrgan} />
<BodyScannerMainOrgansInternal organs={occupant.intOrgan} />
</Box>
);
};
const BodyScannerMainOccupant = (props, context) => {
const { act, data } = useBackend(context);
const {
occupant,
} = data;
return (
<Section
title="Occupant"
buttons={(
<Fragment>
<Button
icon="user-slash"
onClick={() => act('ejectify')}>
Eject
</Button>
<Button
icon="print"
onClick={() => act('print_p')}>
Print Report
</Button>
</Fragment>
)}>
<LabeledList>
<LabeledList.Item label="Name">
{occupant.name}
</LabeledList.Item>
<LabeledList.Item label="Health">
<ProgressBar
min="0"
max={occupant.maxHealth}
value={occupant.health / occupant.maxHealth}
ranges={{
good: [0.5, Infinity],
average: [0, 0.5],
bad: [-Infinity, 0],
}}
/>
</LabeledList.Item>
<LabeledList.Item label="Status" color={stats[occupant.stat][0]}>
{stats[occupant.stat][1]}
</LabeledList.Item>
<LabeledList.Item label="Temperature">
<AnimatedNumber
value={round(occupant.bodyTempC, 0)}
/>&deg;C,&nbsp;
<AnimatedNumber
value={round(occupant.bodyTempF, 0)}
/>&deg;F
</LabeledList.Item>
<LabeledList.Item label="Implants">
{occupant.implant_len ? (
<Box>
{occupant.implant.map(im => im.name).join(', ')}
</Box>
) : (
<Box color="label">
None
</Box>
)}
</LabeledList.Item>
</LabeledList>
</Section>
);
};
const BodyScannerMainAbnormalities = props => {
const {
occupant,
} = props;
if (!(occupant.hasBorer || occupant.blind
|| occupant.colourblind || occupant.nearsighted
|| occupant.hasVirus)) {
return (
<Section title="Abnormalities">
<Box color="label">
No abnormalities found.
</Box>
</Section>
);
}
return (
<Section title="Abnormalities">
{abnormalities.map((a, i) => {
if (occupant[a[0]]) {
return (
<Box color={a[1]} bold={a[1] === "bad"}>
{a[2]}
</Box>
);
}
})}
</Section>
);
};
const BodyScannerMainDamage = props => {
const {
occupant,
} = props;
return (
<Section title="Damage">
<Table>
{mapTwoByTwo(damages, (d1, d2, i) => (
<Fragment>
<Table.Row color="label">
<Table.Cell>
{d1[0]}:
</Table.Cell>
<Table.Cell>
{!!d2 && d2[0] + ":"}
</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
<BodyScannerMainDamageBar
value={occupant[d1[1]]}
marginBottom={i < (damages.length - 2)}
/>
</Table.Cell>
<Table.Cell>
{!!d2 && (
<BodyScannerMainDamageBar value={occupant[d2[1]]} />
)}
</Table.Cell>
</Table.Row>
</Fragment>
))}
</Table>
</Section>
);
};
const BodyScannerMainDamageBar = props => {
return (
<ProgressBar
min="0"
max="100"
value={props.value / 100}
mt="0.5rem"
mb={!!props.marginBottom && "0.5rem"}
ranges={damageRange}>
{round(props.value, 0)}
</ProgressBar>
);
};
const BodyScannerMainOrgansExternal = props => {
if (props.organs.length === 0) {
return (
<Section title="External Organs">
<Box color="label">
N/A
</Box>
</Section>
);
}
return (
<Section title="External Organs">
<Table>
<Table.Row header>
<Table.Cell>
Name
</Table.Cell>
<Table.Cell textAlign="center">
Damage
</Table.Cell>
<Table.Cell textAlign="right">
Injuries
</Table.Cell>
</Table.Row>
{props.organs.map((o, i) => (
<Table.Row key={i} textTransform="capitalize">
<Table.Cell width="33%">
{o.name}
</Table.Cell>
<Table.Cell textAlign="center" q>
<ProgressBar
min="0"
max={o.maxHealth}
mt={i > 0 && "0.5rem"}
value={o.totalLoss / 100}
ranges={damageRange}>
<Box float="left" display="inline">
{!!o.bruteLoss && (
<Box display="inline" position="relative">
<Icon name="bone" />
{round(o.bruteLoss, 0)}&nbsp;
<Tooltip
position="top"
content="Brute damage"
/>
</Box>)}
{!!o.fireLoss && (
<Box display="inline" position="relative">
<Icon name="fire" />
{round(o.fireLoss, 0)}
<Tooltip
position="top"
content="Burn damage"
/>
</Box>)}
</Box>
<Box display="inline">
{round(o.totalLoss, 0)}
</Box>
</ProgressBar>
</Table.Cell>
<Table.Cell
textAlign="right"
width="33%">
<Box color="average" display="inline">
{reduceOrganStatus([
o.internalBleeding && "Internal bleeding",
o.lungRuptured && "Ruptured lung",
!!o.status.broken && o.status.broken,
germStatus(o.germ_level),
!!o.open && "Open incision",
])}
</Box>
<Box display="inline">
{reduceOrganStatus([
!!o.status.splinted && "Splinted",
!!o.status.robotic && "Robotic",
!!o.status.dead && (
<Box color="bad">
DEAD
</Box>
),
])}
{reduceOrganStatus(o.shrapnel.map(
s => s.known
? s.name
: "Unknown object"
))}
</Box>
</Table.Cell>
</Table.Row>
))}
</Table>
</Section>
);
};
const BodyScannerMainOrgansInternal = props => {
if (props.organs.length === 0) {
return (
<Section title="Internal Organs">
<Box color="label">
N/A
</Box>
</Section>
);
}
return (
<Section title="Internal Organs">
<Table>
<Table.Row header>
<Table.Cell>
Name
</Table.Cell>
<Table.Cell textAlign="center">
Damage
</Table.Cell>
<Table.Cell textAlign="right">
Injuries
</Table.Cell>
</Table.Row>
{props.organs.map((o, i) => (
<Table.Row key={i} textTransform="capitalize">
<Table.Cell width="33%">
{o.name}
</Table.Cell>
<Table.Cell textAlign="center">
<ProgressBar
min="0"
max={o.maxHealth}
value={o.damage / 100}
mt={i > 0 && "0.5rem"}
ranges={damageRange}>
{round(o.damage, 0)}
</ProgressBar>
</Table.Cell>
<Table.Cell
textAlign="right"
width="33%">
<Box color="average" display="inline">
{reduceOrganStatus([
germStatus(o.germ_level),
])}
</Box>
<Box display="inline">
{reduceOrganStatus([
(o.robotic === 1) && "Robotic",
(o.robotic === 2) && "Assisted",
!!o.dead && (
<Box color="bad">
DEAD
</Box>
),
])}
</Box>
</Table.Cell>
</Table.Row>
))}
</Table>
</Section>
);
};
const BodyScannerEmpty = () => {
return (
<Section textAlign="center" flexGrow="1">
<Flex height="100%">
<Flex.Item grow="1" align="center" color="label">
<Icon
name="user-slash"
mb="0.5rem"
size="5"
/><br />
No occupant detected.
</Flex.Item>
</Flex>
</Section>
);
};
@@ -0,0 +1,181 @@
import { Fragment } from 'inferno';
import { useBackend } from "../backend";
import { Box, Button, Flex, LabeledList, ProgressBar, Section } from "../components";
import { BeakerContents } from "../interfaces/common/BeakerContents";
import { Window } from "../layouts";
const dispenseAmounts = [1, 5, 10, 20, 30, 50];
const removeAmounts = [1, 5, 10];
export const ChemDispenser = (props, context) => {
return (
<Window resizable>
<Window.Content className="Layout__content--flexColumn">
<ChemDispenserSettings />
<ChemDispenserChemicals />
<ChemDispenserBeaker />
</Window.Content>
</Window>
);
};
const ChemDispenserSettings = (properties, context) => {
const { act, data } = useBackend(context);
const {
amount,
energy,
maxEnergy,
} = data;
return (
<Section title="Settings" flex="content">
<LabeledList>
<LabeledList.Item label="Energy">
<ProgressBar
value={energy}
minValue={0}
maxValue={maxEnergy}
ranges={{
good: [maxEnergy * 0.5, Infinity],
average: [maxEnergy * 0.25, maxEnergy * 0.5],
bad: [-Infinity, maxEnergy * 0.25],
}}>
{energy} / {maxEnergy} Units
</ProgressBar>
</LabeledList.Item>
<LabeledList.Item label="Dispense" verticalAlign="middle">
<Flex direction="row" spacing="1">
{dispenseAmounts.map((a, i) => (
<Flex.Item key={i} grow="1" width="14%" display="inline-block">
<Button
icon="cog"
selected={amount === a}
content={a}
m="0"
width="100%"
onClick={() => act('amount', {
amount: a,
})}
/>
</Flex.Item>
))}
</Flex>
</LabeledList.Item>
</LabeledList>
</Section>
);
};
const ChemDispenserChemicals = (properties, context) => {
const { act, data } = useBackend(context);
const {
chemicals = [],
} = data;
const flexFillers = [];
for (let i = 0; i < (chemicals.length + 1) % 3; i++) {
flexFillers.push(true);
}
return (
<Section
title={data.glass ? 'Drink Dispenser' : 'Chemical Dispenser'}
flexGrow="1">
<Flex
direction="row"
wrap="wrap"
height="100%"
spacingPrecise="2"
align="flex-start"
alignContent="flex-start">
{chemicals.map((c, i) => (
<Flex.Item
key={i}
grow="1"
basis="25%"
height="20px"
width="30%"
display="inline-block">
<Button
icon="arrow-circle-down"
overflow="hidden"
textOverflow="ellipsis"
width="100%"
height="100%"
align="flex-start"
content={c.title}
onClick={() => act('dispense', {
reagent: c.id,
})}
/>
</Flex.Item>
))}
{flexFillers.map((_, i) => (
<Flex.Item key={i} grow="1" basis="25%" height="20px" />
))}
</Flex>
</Section>
);
};
const ChemDispenserBeaker = (properties, context) => {
const { act, data } = useBackend(context);
const {
isBeakerLoaded,
beakerCurrentVolume,
beakerMaxVolume,
beakerContents = [],
} = data;
return (
<Section
title={data.glass ? 'Glass' : 'Beaker'}
flex="content"
minHeight="25%"
buttons={(
<Box>
{!!isBeakerLoaded && (
<Box inline color="label" mr={2}>
{beakerCurrentVolume} / {beakerMaxVolume} units
</Box>
)}
<Button
icon="eject"
content="Eject"
disabled={!isBeakerLoaded}
onClick={() => act('ejectBeaker')}
/>
</Box>
)}>
<BeakerContents
beakerLoaded={isBeakerLoaded}
beakerContents={beakerContents}
buttons={chemical => (
<Fragment>
<Button
content="Isolate"
icon="compress-arrows-alt"
onClick={() => act('remove', {
reagent: chemical.id,
amount: -1,
})}
/>
{removeAmounts.map((a, i) => (
<Button
key={i}
content={a}
onClick={() => act('remove', {
reagent: chemical.id,
amount: a,
})}
/>
))}
<Button
content="ALL"
onClick={() => act('remove', {
reagent: chemical.id,
amount: chemical.volume,
})}
/>
</Fragment>
)}
/>
</Section>
);
};
+106
View File
@@ -0,0 +1,106 @@
import { round, toFixed } from 'common/math';
import { Fragment } from 'inferno';
import { useBackend } from "../backend";
import { AnimatedNumber, Box, Button, LabeledList, NumberInput, Section } from "../components";
import { BeakerContents } from '../interfaces/common/BeakerContents';
import { Window } from "../layouts";
export const ChemHeater = (_props, _context) => {
return (
<Window resizable>
<Window.Content className="Layout__content--flexColumn">
<ChemHeaterSettings />
<ChemHeaterBeaker />
</Window.Content>
</Window>
);
};
const ChemHeaterSettings = (_properties, context) => {
const { act, data } = useBackend(context);
const {
targetTemp,
targetTempReached,
autoEject,
isActive,
currentTemp,
isBeakerLoaded,
} = data;
return (
<Section
title="Settings"
flexBasis="content"
buttons={(
<Fragment>
<Button
content="Auto-eject"
icon={autoEject ? "toggle-on" : "toggle-off"}
selected={autoEject}
onClick={() => act("toggle_autoeject")} />
<Button
content={isActive ? "On" : "Off"}
icon="power-off"
selected={isActive}
disabled={!isBeakerLoaded}
onClick={() => act("toggle_on")} />
</Fragment>
)}>
<LabeledList>
<LabeledList.Item label="Target">
<NumberInput
width="65px"
unit="K"
step={10}
stepPixelSize={3}
value={round(targetTemp, 0)}
minValue={0}
maxValue={1000}
onDrag={(e, value) => act('adjust_temperature', {
target: value,
})}
/>
</LabeledList.Item>
<LabeledList.Item
label="Reading"
color={targetTempReached ? "good" : "average"}>
{isBeakerLoaded && (
<AnimatedNumber
value={currentTemp}
format={value => toFixed(value) + " K"} />
) || '—'}
</LabeledList.Item>
</LabeledList>
</Section>
);
};
const ChemHeaterBeaker = (_properties, context) => {
const { act, data } = useBackend(context);
const {
isBeakerLoaded,
beakerCurrentVolume,
beakerMaxVolume,
beakerContents,
} = data;
return (
<Section
title="Beaker"
flexGrow="1"
buttons={!!isBeakerLoaded && (
<Box>
<Box inline color="label" mr={2}>
{beakerCurrentVolume} / {beakerMaxVolume} units
</Box>
<Button
icon="eject"
content="Eject"
onClick={() => act('eject_beaker')} />
</Box>
)}>
<BeakerContents
beakerLoaded={isBeakerLoaded}
beakerContents={beakerContents}
/>
</Section>
);
};
+408
View File
@@ -0,0 +1,408 @@
import { Fragment } from 'inferno';
import { useBackend } from "../backend";
import { Box, Button, Flex, Icon, LabeledList, Section } from "../components";
import { Window } from "../layouts";
import { BeakerContents } from './common/BeakerContents';
import { ComplexModal, modalOpen, modalRegisterBodyOverride } from './common/ComplexModal';
const transferAmounts = [1, 5, 10];
const bottleStyles = [
"bottle.png",
"small_bottle.png",
"wide_bottle.png",
"round_bottle.png",
"reagent_bottle.png",
];
const analyzeModalBodyOverride = (modal, context) => {
const { act, data } = useBackend(context);
const result = modal.args.analysis;
return (
<Section
level={2}
m="-1rem"
pb="1rem"
title={data.condi ? "Condiment Analysis" : "Reagent Analysis"}>
<Box mx="0.5rem">
<LabeledList>
<LabeledList.Item label="Name">
{result.name}
</LabeledList.Item>
<LabeledList.Item label="Description">
{(result.desc || "").length > 0 ? result.desc : "N/A"}
</LabeledList.Item>
{result.blood_type && (
<Fragment>
<LabeledList.Item label="Blood type">
{result.blood_type}
</LabeledList.Item>
<LabeledList.Item
label="Blood DNA"
className="LabeledList__breakContents">
{result.blood_dna}
</LabeledList.Item>
</Fragment>
)}
{!data.condi && (
<Button
icon={data.printing ? 'spinner' : 'print'}
disabled={data.printing}
iconSpin={!!data.printing}
ml="0.5rem"
content="Print"
onClick={() => act('print', {
idx: result.idx,
beaker: modal.args.beaker,
})}
/>
)}
</LabeledList>
</Box>
</Section>
);
};
export const ChemMaster = (props, context) => {
const { data } = useBackend(context);
const {
condi,
beaker,
beaker_reagents = [],
buffer_reagents = [],
mode,
} = data;
return (
<Window resizable>
<ComplexModal />
<Window.Content scrollable className="Layout__content--flexColumn">
<ChemMasterBeaker
beaker={beaker}
beakerReagents={beaker_reagents}
bufferNonEmpty={buffer_reagents.length > 0}
/>
<ChemMasterBuffer
mode={mode}
bufferReagents={buffer_reagents}
/>
<ChemMasterProduction
isCondiment={condi}
bufferNonEmpty={buffer_reagents.length > 0}
/>
<ChemMasterCustomization />
</Window.Content>
</Window>
);
};
const ChemMasterBeaker = (props, context) => {
const { act } = useBackend(context);
const {
beaker,
beakerReagents,
bufferNonEmpty,
} = props;
return (
<Section
title="Beaker"
flexGrow="0"
flexBasis="300px"
buttons={bufferNonEmpty
? <Button.Confirm
icon="eject"
disabled={!beaker}
content="Eject and Clear Buffer"
onClick={() => act('eject')}
/>
: <Button
icon="eject"
disabled={!beaker}
content="Eject and Clear Buffer"
onClick={() => act('eject')}
/>}>
{beaker
? <BeakerContents
beakerLoaded
beakerContents={beakerReagents}
buttons={(chemical, i) => (
<Box mb={(i < beakerReagents.length - 1) && "2px"}>
<Button
content="Analyze"
mb="0"
onClick={() => modalOpen(context, 'analyze', {
idx: i + 1,
beaker: 1,
})}
/>
{transferAmounts.map((am, j) =>
(<Button
key={j}
content={am}
mb="0"
onClick={() => act('add', {
id: chemical.id,
amount: am,
})}
/>)
)}
<Button
content="All"
mb="0"
onClick={() => act('add', {
id: chemical.id,
amount: chemical.volume,
})}
/>
<Button
content="Custom.."
mb="0"
onClick={() => modalOpen(context, 'addcustom', {
id: chemical.id,
})}
/>
</Box>
)}
/>
: (
<Box color="label">
No beaker loaded.
</Box>
)}
</Section>
);
};
const ChemMasterBuffer = (props, context) => {
const { act } = useBackend(context);
const {
mode,
bufferReagents = [],
} = props;
return (
<Section
title="Buffer"
flexGrow="0"
flexBasis="300px"
buttons={
<Box color="label">
Transferring to&nbsp;
<Button
icon={mode ? "flask" : "trash"}
color={!mode && "bad"}
content={mode ? "Beaker" : "Disposal"}
onClick={() => act('toggle')}
/>
</Box>
}>
{(bufferReagents.length > 0)
? <BeakerContents
beakerLoaded
beakerContents={bufferReagents}
buttons={(chemical, i) => (
<Box mb={(i < bufferReagents.length - 1) && "2px"}>
<Button
content="Analyze"
mb="0"
onClick={() => modalOpen(context, 'analyze', {
idx: i + 1,
beaker: 0,
})}
/>
{transferAmounts.map((am, i) =>
(<Button
key={i}
content={am}
mb="0"
onClick={() => act('remove', {
id: chemical.id,
amount: am,
})}
/>)
)}
<Button
content="All"
mb="0"
onClick={() => act('remove', {
id: chemical.id,
amount: chemical.volume,
})}
/>
<Button
content="Custom.."
mb="0"
onClick={() => modalOpen(context, 'removecustom', {
id: chemical.id,
})}
/>
</Box>
)}
/>
: (
<Box color="label">
Buffer is empty.
</Box>
)}
</Section>
);
};
const ChemMasterProduction = (props, context) => {
const { act } = useBackend(context);
if (!props.bufferNonEmpty) {
return (
<Section
title="Production"
height="100%"
flexGrow="1">
<Flex height="100%">
<Flex.Item
grow="1"
align="center"
textAlign="center"
color="label">
<Icon
name="tint-slash"
mb="0.5rem"
size="5"
/><br />
Buffer is empty.
</Flex.Item>
</Flex>
</Section>
);
}
return (
<Section title="Production" height="100%" flexGrow="1">
{!props.isCondiment ? (
<ChemMasterProductionChemical />
) : (
<ChemMasterProductionCondiment />
)}
</Section>
);
};
const ChemMasterProductionChemical = (props, context) => {
const { act, data } = useBackend(context);
return (
<LabeledList>
<LabeledList.Item label="Pills">
<Button
icon="circle"
content="One (100u max)"
onClick={() => modalOpen(context, 'create_pill')}
/>
<Button
icon="plus-circle"
content="Multiple"
mx="0.5rem"
onClick={() => modalOpen(context, 'create_pill_multiple')}
/>
<Button
onClick={() => modalOpen(context, 'change_pill_style')}>
<div style={
"display: inline-block;"
+ "width: 16px;"
+ "height: 16px;"
+ "vertical-align: middle;"
+ "background: url(pill" + data.pillsprite + ".png);"
+ "background-size: 200%;"
+ "background-position: left -10px bottom -6px;"
} />
Style
</Button>
</LabeledList.Item>
<LabeledList.Item label="Patches">
<Button
icon="square"
content="One (30u max)"
mr="0.5rem"
onClick={() => modalOpen(context, 'create_patch')}
/>
<Button
icon="plus-square"
content="Multiple"
onClick={() => modalOpen(context, 'create_patch_multiple')}
/>
</LabeledList.Item>
<LabeledList.Item label="Bottle">
<Button
icon="wine-bottle"
content="Create bottle (50u max)"
mr="0.5rem"
mb="0.5rem"
onClick={() => modalOpen(context, 'create_bottle')}
/>
<Button
mb="0.5rem"
onClick={() => modalOpen(context, 'change_bottle_style')}>
<div style={
"display: inline-block;"
+ "width: 16px;"
+ "height: 16px;"
+ "vertical-align: middle;"
+ "background: url(" + bottleStyles[data.bottlesprite - 1] + ");"
+ "background-size: 200%;"
+ "background-position: left -10px bottom -6px;"
} />
Style
</Button>
</LabeledList.Item>
</LabeledList>
);
};
const ChemMasterProductionCondiment = (props, context) => {
const { act } = useBackend(context);
return (
<Fragment>
<Button
icon="box"
content="Create condiment pack (10u max)"
mb="0.5rem"
onClick={() => modalOpen(context, 'create_condi_pack')}
/><br />
<Button
icon="wine-bottle"
content="Create bottle (50u max)"
mb="0"
onClick={() => act('create_condi_bottle')}
/>
</Fragment>
);
};
const ChemMasterCustomization = (props, context) => {
const { act, data } = useBackend(context);
if (!data.loaded_pill_bottle) {
return (
<Section title="Pill Bottle Customization">
<Box color="label">
None loaded.
</Box>
</Section>
);
}
return (
<Section title="Pill Bottle Customization">
<Button
disabled={!data.loaded_pill_bottle}
icon="eject"
content={data.loaded_pill_bottle
? data.loaded_pill_bottle_name
: "None loaded"}
onClick={() => act('ejectp')}
/>
<Button
disabled={!data.loaded_pill_bottle}
icon="palette"
content="Change wrapper"
mb="0"
onClick={() => modalOpen(context, 'change_pill_bottle_style')}
/>
</Section>
);
};
modalRegisterBodyOverride('analyze', analyzeModalBodyOverride);
@@ -0,0 +1,461 @@
import { round } from 'common/math';
import { Fragment } from 'inferno';
import { useBackend } from "../backend";
import { Box, Button, Flex, Icon, LabeledList, NoticeBox, ProgressBar, Section, Tabs } from "../components";
import { COLORS } from '../constants';
import { ComplexModal, modalRegisterBodyOverride } from '../interfaces/common/ComplexModal';
import { Window } from "../layouts";
const viewRecordModalBodyOverride = (modal, context) => {
const { act, data } = useBackend(context);
const {
activerecord,
realname,
health,
unidentity,
strucenzymes,
} = modal.args;
const damages = health.split(' - ');
return (
<Section
level={2}
m="-1rem"
pb="1rem"
title={"Records of " + realname}>
<LabeledList>
<LabeledList.Item label="Name">
{realname}
</LabeledList.Item>
<LabeledList.Item label="Damage">
{damages.length > 1 ? (
<Fragment>
<Box color={COLORS.damageType.oxy} display="inline">
{damages[0]}
</Box>
&nbsp;|&nbsp;
<Box color={COLORS.damageType.toxin} display="inline">
{damages[2]}
</Box>
&nbsp;|&nbsp;
<Box color={COLORS.damageType.brute} display="inline">
{damages[3]}
</Box>
&nbsp;|&nbsp;
<Box color={COLORS.damageType.burn} display="inline">
{damages[1]}
</Box>
</Fragment>
) : (
<Box color="bad">
Unknown
</Box>
)}
</LabeledList.Item>
<LabeledList.Item label="UI" className="LabeledList__breakContents">
{unidentity}
</LabeledList.Item>
<LabeledList.Item label="SE" className="LabeledList__breakContents">
{strucenzymes}
</LabeledList.Item>
<LabeledList.Item label="Disk">
<Button.Confirm
disabled={!data.disk}
icon="arrow-circle-down"
content="Import"
onClick={() => act('disk', {
option: 'load',
})}
/>
<Button
disabled={!data.disk}
icon="arrow-circle-up"
content="Export UI"
onClick={() => act('disk', {
option: 'save',
savetype: 'ui',
})}
/>
<Button
disabled={!data.disk}
icon="arrow-circle-up"
content="Export UI and UE"
onClick={() => act('disk', {
option: 'save',
savetype: 'ue',
})}
/>
<Button
disabled={!data.disk}
icon="arrow-circle-up"
content="Export SE"
onClick={() => act('disk', {
option: 'save',
savetype: 'se',
})}
/>
</LabeledList.Item>
<LabeledList.Item label="Actions">
<Button
disabled={!data.podready}
icon="user-plus"
content="Clone"
onClick={() => act('clone', {
ref: activerecord,
})}
/>
<Button
icon="trash"
content="Delete"
onClick={() => act('del_rec')}
/>
</LabeledList.Item>
</LabeledList>
</Section>
);
};
export const CloningConsole = (props, context) => {
const { act, data } = useBackend(context);
const {
menu,
} = data;
modalRegisterBodyOverride('view_rec', viewRecordModalBodyOverride);
return (
<Window resizable>
<ComplexModal maxWidth="75%" maxHeight="75%" />
<Window.Content className="Layout__content--flexColumn">
<CloningConsoleTemp />
<CloningConsoleStatus />
<CloningConsoleNavigation />
<Section noTopPadding flexGrow="1">
<CloningConsoleBody />
</Section>
</Window.Content>
</Window>
);
};
const CloningConsoleNavigation = (props, context) => {
const { act, data } = useBackend(context);
const {
menu,
} = data;
return (
<Tabs>
<Tabs.Tab
selected={menu === 1}
icon="home"
onClick={() => act('menu', {
num: 1,
})}>
Main
</Tabs.Tab>
<Tabs.Tab
selected={menu === 2}
icon="folder"
onClick={() => act('menu', {
num: 2,
})}>
Records
</Tabs.Tab>
</Tabs>
);
};
const CloningConsoleBody = (props, context) => {
const { data } = useBackend(context);
const {
menu,
} = data;
let body;
if (menu === 1) {
body = <CloningConsoleMain />;
} else if (menu === 2) {
body = <CloningConsoleRecords />;
}
return body;
};
const CloningConsoleMain = (props, context) => {
const { act, data } = useBackend(context);
const {
loading,
scantemp,
occupant,
locked,
can_brainscan,
scan_mode,
numberofpods,
pods,
selected_pod,
} = data;
const isLocked = locked && !!occupant;
return (
<Fragment>
<Section
title="Scanner"
level="2"
buttons={
<Fragment>
<Box display="inline" color="label">
Scanner Lock:&nbsp;
</Box>
<Button
disabled={!occupant}
selected={isLocked}
icon={isLocked ? "toggle-on" : "toggle-off"}
content={isLocked ? "Engaged" : "Disengaged"}
onClick={() => act('lock')}
/>
<Button
disabled={isLocked || !occupant}
icon="user-slash"
content="Eject Occupant"
onClick={() => act('eject')}
/>
</Fragment>
}>
<LabeledList>
<LabeledList.Item label="Status">
{loading ? (
<Box color="average">
<Icon name="spinner" spin />&nbsp;
Scanning...
</Box>
) : (
<Box color={scantemp.color}>
{scantemp.text}
</Box>
)}
</LabeledList.Item>
{!!can_brainscan && (
<LabeledList.Item label="Scan Mode">
<Button
icon={scan_mode ? "brain" : "male"}
content={scan_mode ? "Brain" : "Body"}
onClick={() => act('toggle_mode')}
/>
</LabeledList.Item>
)}
</LabeledList>
<Button
disabled={!occupant || loading}
icon="user"
content="Scan Occupant"
mt="0.5rem"
mb="0"
onClick={() => act('scan')}
/>
</Section>
<Section title="Pods" level="2">
{numberofpods
? pods.map((pod, i) => {
let podAction;
if (pod.status === "cloning") {
podAction = (
<ProgressBar
min="0"
max="100"
value={pod.progress / 100}
ranges={{
good: [0.75, Infinity],
average: [0.25, 0.75],
bad: [-Infinity, 0.25],
}}
mt="0.5rem">
<Box textAlign="center">
{round(pod.progress, 0) + "%"}
</Box>
</ProgressBar>
);
} else if (pod.status === "mess") {
podAction = (
<Box bold color="bad" mt="0.5rem">
ERROR
</Box>
);
} else {
podAction = (
<Button
selected={selected_pod === pod.pod}
icon={selected_pod === pod.pod && "check"}
content="Select"
mt="0.5rem"
onClick={() => act('selectpod', {
ref: pod.pod,
})}
/>
);
}
return (
<Box
key={i}
width="64px"
textAlign="center"
display="inline-block"
mr="0.5rem">
<img
src={"pod_" + pod.status + ".gif"}
style={{
width: "100%",
"-ms-interpolation-mode": "nearest-neighbor",
}}
/>
<Box color="label">
Pod #{i + 1}
</Box>
<Box
bold
color={pod.biomass >= 150 ? "good" : "bad"}
display="inline">
<Icon
name={pod.biomass >= 150 ? "circle" : "circle-o"}
/>&nbsp;
{pod.biomass}
</Box>
{podAction}
</Box>
);
})
: (
<Box color="bad">
No pods detected. Unable to clone.
</Box>
)}
</Section>
</Fragment>
);
};
const CloningConsoleRecords = (props, context) => {
const { act, data } = useBackend(context);
const {
records,
} = data;
if (!records.length) {
return (
<Flex height="100%">
<Flex.Item
grow="1"
align="center"
textAlign="center"
color="label">
<Icon
name="user-slash"
mb="0.5rem"
size="5"
/><br />
No records found.
</Flex.Item>
</Flex>
);
}
return (
<Box mt="0.5rem">
{records.map((record, i) => (
<Button
key={i}
icon="user"
mb="0.5rem"
content={record.realname}
onClick={() => act('view_rec', {
ref: record.record,
})}
/>
))}
</Box>
);
};
const CloningConsoleTemp = (props, context) => {
const { act, data } = useBackend(context);
const {
temp,
} = data;
if (!temp || !temp.text || temp.text.length <= 0) {
return;
}
const tempProp = { [temp.style]: true };
return (
<NoticeBox {...tempProp}>
<Box display="inline-block" verticalAlign="middle">
{temp.text}
</Box>
<Button
icon="times-circle"
float="right"
onClick={() => act('cleartemp')}
/>
<Box clear="both" />
</NoticeBox>
);
};
const CloningConsoleStatus = (props, context) => {
const { act, data } = useBackend(context);
const {
scanner,
numberofpods,
autoallowed,
autoprocess,
disk,
} = data;
return (
<Section
title="Status"
buttons={
<Fragment>
{!!autoallowed && (
<Fragment>
<Box display="inline" color="label">
Auto-processing:&nbsp;
</Box>
<Button
selected={autoprocess}
icon={autoprocess ? "toggle-on" : "toggle-off"}
content={autoprocess ? "Enabled" : "Disabled"}
onClick={() => act('autoprocess', {
on: autoprocess ? 0 : 1,
})}
/>
</Fragment>
)}
<Button
disabled={!disk}
icon="eject"
content="Eject Disk"
onClick={() => act('disk', {
option: 'eject',
})}
/>
</Fragment>
}>
<LabeledList>
<LabeledList.Item label="Scanner">
{scanner ? (
<Box color="good">
Connected
</Box>
) : (
<Box color="bad">
Not connected!
</Box>
)}
</LabeledList.Item>
<LabeledList.Item label="Pods">
{numberofpods ? (
<Box color="good">
{numberofpods} connected
</Box>
) : (
<Box color="bad">
None connected!
</Box>
)}
</LabeledList.Item>
</LabeledList>
</Section>
);
};
+206
View File
@@ -0,0 +1,206 @@
import { Fragment } from 'inferno';
import { useBackend } from "../backend";
import { AnimatedNumber, Box, Button, Flex, Icon, LabeledList, ProgressBar, Section } from "../components";
import { Window } from "../layouts";
const damageTypes = [
{
label: "Resp.",
type: "oxyLoss",
},
{
label: "Toxin",
type: "toxLoss",
},
{
label: "Brute",
type: "bruteLoss",
},
{
label: "Burn",
type: "fireLoss",
},
];
const statNames = [
["good", "Conscious"],
["average", "Unconscious"],
["bad", "DEAD"],
];
export const Cryo = (props, context) => {
return (
<Window>
<Window.Content className="Layout__content--flexColumn">
<CryoContent />
</Window.Content>
</Window>
);
};
const CryoContent = (props, context) => {
const { act, data } = useBackend(context);
const {
isOperating,
hasOccupant,
occupant = [],
cellTemperature,
cellTemperatureStatus,
isBeakerLoaded,
auto_eject_healthy,
auto_eject_dead,
} = data;
return (
<Fragment>
<Section
title="Occupant"
flexGrow="1"
buttons={(
<Button
icon="user-slash"
onClick={() => act('ejectOccupant')}
disabled={!hasOccupant}>
Eject
</Button>
)}>
{hasOccupant ? (
<LabeledList>
<LabeledList.Item label="Occupant">
{occupant.name || "Unknown"}
</LabeledList.Item>
<LabeledList.Item label="Health">
<ProgressBar
min={occupant.health}
max={occupant.maxHealth}
value={occupant.health / occupant.maxHealth}
color={occupant.health > 0 ? 'good' : 'average'}>
<AnimatedNumber
value={Math.round(occupant.health)} />
</ProgressBar>
</LabeledList.Item>
<LabeledList.Item
label="Status"
color={statNames[occupant.stat][0]}>
{statNames[occupant.stat][1]}
</LabeledList.Item>
<LabeledList.Item label="Temperature">
<AnimatedNumber
value={Math.round(occupant.bodyTemperature)} />
{' K'}
</LabeledList.Item>
<LabeledList.Divider />
{(damageTypes.map(damageType => (
<LabeledList.Item
key={damageType.id}
label={damageType.label}>
<ProgressBar
value={occupant[damageType.type]/100}
ranges={{ bad: [0.01, Infinity] }}>
<AnimatedNumber
value={Math.round(occupant[damageType.type])} />
</ProgressBar>
</LabeledList.Item>
)))}
</LabeledList>
) : (
<Flex height="100%" textAlign="center">
<Flex.Item grow="1" align="center" color="label">
<Icon
name="user-slash"
mb="0.5rem"
size="5"
/><br />
No occupant detected.
</Flex.Item>
</Flex>
)}
</Section>
<Section
title="Cell"
buttons={(
<Button
icon="eject"
onClick={() => act('ejectBeaker')}
disabled={!isBeakerLoaded}>
Eject Beaker
</Button>
)}>
<LabeledList>
<LabeledList.Item label="Power">
<Button
icon="power-off"
onClick={() => act(isOperating ? 'switchOff' : 'switchOn')}
selected={isOperating}>
{isOperating ? "On" : "Off"}
</Button>
</LabeledList.Item>
<LabeledList.Item label="Temperature" color={cellTemperatureStatus}>
<AnimatedNumber value={cellTemperature} /> K
</LabeledList.Item>
<LabeledList.Item label="Beaker">
<CryoBeaker />
</LabeledList.Item>
<LabeledList.Divider />
<LabeledList.Item label="Auto-eject healthy occupants">
<Button
icon={auto_eject_healthy ? "toggle-on" : "toggle-off"}
selected={auto_eject_healthy}
onClick={() => act(auto_eject_healthy
? 'auto_eject_healthy_off'
: 'auto_eject_healthy_on'
)}>
{auto_eject_healthy ? "On" : "Off"}
</Button>
</LabeledList.Item>
<LabeledList.Item label="Auto-eject dead occupants">
<Button
icon={auto_eject_dead ? "toggle-on" : "toggle-off"}
selected={auto_eject_dead}
onClick={() => act(auto_eject_dead
? 'auto_eject_dead_off'
: 'auto_eject_dead_on'
)}>
{auto_eject_dead ? "On" : "Off"}
</Button>
</LabeledList.Item>
</LabeledList>
</Section>
</Fragment>
);
};
const CryoBeaker = (props, context) => {
const { act, data } = useBackend(context);
const {
isBeakerLoaded,
beakerLabel,
beakerVolume,
} = data;
if (isBeakerLoaded) {
return (
<Fragment>
{beakerLabel
? beakerLabel
: (
<Box color="average">
No label
</Box>
)}
<Box color={!beakerVolume && "bad"}>
{beakerVolume ? (
<AnimatedNumber
value={beakerVolume}
format={v => Math.round(v) + " units remaining"}
/>
) : "Beaker is empty"}
</Box>
</Fragment>
);
} else {
return (
<Box color="average">
No beaker loaded
</Box>
);
}
};
@@ -0,0 +1,711 @@
import { Fragment } from 'inferno';
import { useBackend } from "../backend";
import { Box, Button, Dimmer, Flex, Icon, Knob, LabeledList, ProgressBar, Section, Tabs } from "../components";
import { Window } from "../layouts";
import { ComplexModal } from './common/ComplexModal';
const stats = [
['good', 'Alive'],
['average', 'Critical'],
['bad', 'DEAD'],
];
const operations = [
['ui', 'Modify U.I.', 'dna'],
['se', 'Modify S.E.', 'dna'],
['buffer', 'Transfer Buffers', 'syringe'],
['rejuvenators', 'Rejuvenators', 'flask'],
];
const rejuvenatorsDoses = [5, 10, 20, 30, 50];
export const DNAModifier = (props, context) => {
const { act, data } = useBackend(context);
const {
irradiating,
dnaBlockSize,
occupant,
} = data;
context.dnaBlockSize = dnaBlockSize;
context.isDNAInvalid = !occupant.isViableSubject
|| !occupant.uniqueIdentity
|| !occupant.structuralEnzymes;
let radiatingModal;
if (irradiating) {
radiatingModal = <DNAModifierIrradiating duration={irradiating} />;
}
return (
<Window resizable>
<ComplexModal />
{radiatingModal}
<Window.Content className="Layout__content--flexColumn">
<DNAModifierOccupant />
<DNAModifierMain />
</Window.Content>
</Window>
);
};
const DNAModifierOccupant = (props, context) => {
const { act, data } = useBackend(context);
const {
locked,
hasOccupant,
occupant,
} = data;
return (
<Section
title="Occupant"
buttons={
<Fragment>
<Box color="label" display="inline" mr="0.5rem">
Door Lock:
</Box>
<Button
disabled={!hasOccupant}
selected={locked}
icon={locked ? "toggle-on" : "toggle-off"}
content={locked ? "Engaged" : "Disengaged"}
onClick={() => act('toggleLock')}
/>
<Button
disabled={!hasOccupant || locked}
icon="user-slash"
content="Eject"
onClick={() => act('ejectOccupant')}
/>
</Fragment>
}>
{hasOccupant ? (
<Fragment>
<Box>
<LabeledList>
<LabeledList.Item label="Name">
{occupant.name}
</LabeledList.Item>
<LabeledList.Item label="Health">
<ProgressBar
min={occupant.minHealth}
max={occupant.maxHealth}
value={occupant.health / occupant.maxHealth}
ranges={{
good: [0.5, Infinity],
average: [0, 0.5],
bad: [-Infinity, 0],
}}
/>
</LabeledList.Item>
<LabeledList.Item label="Status" color={stats[occupant.stat][0]}>
{stats[occupant.stat][1]}
</LabeledList.Item>
<LabeledList.Divider />
</LabeledList>
</Box>
{context.isDNAInvalid ? (
<Box color="bad">
<Icon name="exclamation-circle" />&nbsp;
The occupant&apos;s DNA structure is ruined beyond recognition,
please insert a subject with an intact DNA structure.
</Box>
) : (
<LabeledList>
<LabeledList.Item label="Radiation">
<ProgressBar
min="0"
max="100"
value={occupant.radiationLevel / 100}
color="average"
/>
</LabeledList.Item>
<LabeledList.Item label="Unique Enzymes">
{data.occupant.uniqueEnzymes
? data.occupant.uniqueEnzymes : (
<Box color="bad">
<Icon name="exclamation-circle" />&nbsp;
Unknown
</Box>
)}
</LabeledList.Item>
</LabeledList>
)}
</Fragment>
) : (
<Box color="label">
Cell unoccupied.
</Box>
)}
</Section>
);
};
const DNAModifierMain = (props, context) => {
const { act, data } = useBackend(context);
const {
selectedMenuKey,
hasOccupant,
occupant,
} = data;
if (!hasOccupant) {
return (
<Section flexGrow="1">
<Flex height="100%">
<Flex.Item
grow="1"
align="center"
textAlign="center"
color="label">
<Icon
name="user-slash"
mb="0.5rem"
size="5"
/><br />
No occupant in DNA modifier.
</Flex.Item>
</Flex>
</Section>
);
} else if (context.isDNAInvalid) {
return (
<Section flexGrow="1">
<Flex height="100%">
<Flex.Item
grow="1"
align="center"
textAlign="center"
color="label">
<Icon
name="user-slash"
mb="0.5rem"
size="5"
/><br />
No operation possible on this subject.
</Flex.Item>
</Flex>
</Section>
);
}
let body;
if (selectedMenuKey === "ui") {
body = (
<Fragment>
<DNAModifierMainUI />
<DNAModifierMainRadiationEmitter />
</Fragment>
);
} else if (selectedMenuKey === "se") {
body = (
<Fragment>
<DNAModifierMainSE />
<DNAModifierMainRadiationEmitter />
</Fragment>
);
} else if (selectedMenuKey === "buffer") {
body = <DNAModifierMainBuffers />;
} else if (selectedMenuKey === "rejuvenators") {
body = <DNAModifierMainRejuvenators />;
}
return (
<Section flexGrow="1">
<Tabs>
{operations.map((op, i) => (
<Tabs.Tab
key={i}
selected={selectedMenuKey === op[0]}
onClick={() => act('selectMenuKey', { key: op[0] })}>
<Icon name={op[2]} />
{op[1]}
</Tabs.Tab>
))}
</Tabs>
{body}
</Section>
);
};
const DNAModifierMainUI = (props, context) => {
const { act, data } = useBackend(context);
const {
selectedUIBlock,
selectedUISubBlock,
selectedUITarget,
occupant,
} = data;
return (
<Section title="Modify Unique Identifier" level="2">
<DNAModifierBlocks
dnaString={occupant.uniqueIdentity}
selectedBlock={selectedUIBlock}
selectedSubblock={selectedUISubBlock}
blockSize={context.dnaBlockSize}
action="selectUIBlock"
/>
<LabeledList>
<LabeledList.Item label="Target">
<Knob
minValue="1"
maxValue="15"
stepPixelSize="20"
value={selectedUITarget}
format={value => value.toString(16).toUpperCase()}
ml="0"
onChange={(e, val) => act('changeUITarget', { value: val })}
/>
</LabeledList.Item>
</LabeledList>
<Button
icon="radiation"
content="Irradiate Block"
mt="0.5rem"
onClick={() => act('pulseUIRadiation')}
/>
</Section>
);
};
const DNAModifierMainSE = (props, context) => {
const { act, data } = useBackend(context);
const {
selectedSEBlock,
selectedSESubBlock,
occupant,
} = data;
return (
<Section title="Modify Structural Enzymes" level="2">
<DNAModifierBlocks
dnaString={occupant.structuralEnzymes}
selectedBlock={selectedSEBlock}
selectedSubblock={selectedSESubBlock}
blockSize={context.dnaBlockSize}
action="selectSEBlock"
/>
<Button
icon="radiation"
content="Irradiate Block"
onClick={() => act('pulseSERadiation')}
/>
</Section>
);
};
const DNAModifierMainRadiationEmitter = (props, context) => {
const { act, data } = useBackend(context);
const {
radiationIntensity,
radiationDuration,
} = data;
return (
<Section title="Radiation Emitter" level="2">
<LabeledList>
<LabeledList.Item label="Intensity">
<Knob
minValue="1"
maxValue="10"
stepPixelSize="20"
value={radiationIntensity}
popUpPosition="right"
ml="0"
onChange={(e, val) => act('radiationIntensity', { value: val })}
/>
</LabeledList.Item>
<LabeledList.Item label="Duration">
<Knob
minValue="1"
maxValue="20"
stepPixelSize="10"
unit="s"
value={radiationDuration}
popUpPosition="right"
ml="0"
onChange={(e, val) => act('radiationDuration', { value: val })}
/>
</LabeledList.Item>
</LabeledList>
<Button
icon="radiation"
content="Pulse Radiation"
tooltip="Mutates a random block of either the occupant's UI or SE."
tooltipPosition="top-right"
mt="0.5rem"
onClick={() => act('pulseRadiation')}
/>
</Section>
);
};
const DNAModifierMainBuffers = (props, context) => {
const { act, data } = useBackend(context);
const {
buffers,
} = data;
let bufferElements = buffers.map((buffer, i) => (
<DNAModifierMainBuffersElement
key={i}
id={i + 1}
name={"Buffer " + (i + 1)}
buffer={buffer}
/>
));
return (
<Fragment>
<Section title="Buffers" level="2">
{bufferElements}
</Section>
<DNAModifierMainBuffersDisk />
</Fragment>
);
};
const DNAModifierMainBuffersElement = (props, context) => {
const { act, data } = useBackend(context);
const {
id,
name,
buffer,
} = props;
const isInjectorReady = data.isInjectorReady;
const realName = name + (buffer.data ? ' - ' + buffer.label : '');
return (
<Box backgroundColor="rgba(0, 0, 0, 0.33)" mb="0.5rem">
<Section
title={realName}
level="3"
mx="0"
lineHeight="18px"
buttons={
<Fragment>
<Button.Confirm
disabled={!buffer.data}
icon="trash"
content="Clear"
onClick={() => act('bufferOption', {
option: 'clear',
id: id,
})}
/>
<Button
disabled={!buffer.data}
icon="pen"
content="Rename"
onClick={() => act('bufferOption', {
option: 'changeLabel',
id: id,
})}
/>
<Button
disabled={!buffer.data || !data.hasDisk}
icon="save"
content="Export"
tooltip="Exports this buffer to the currently loaded data disk."
tooltipPosition="bottom-left"
onClick={() => act('bufferOption', {
option: 'saveDisk',
id: id,
})}
/>
</Fragment>
}>
<LabeledList>
<LabeledList.Item label="Write">
<Button
icon="arrow-circle-down"
content="Subject U.I"
mb="0"
onClick={() => act('bufferOption', {
option: 'saveUI',
id: id,
})}
/>
<Button
icon="arrow-circle-down"
content="Subject U.I and U.E."
mb="0"
onClick={() => act('bufferOption', {
option: 'saveUIAndUE',
id: id,
})}
/>
<Button
icon="arrow-circle-down"
content="Subject S.E."
mb="0"
onClick={() => act('bufferOption', {
option: 'saveSE',
id: id,
})}
/>
<Button
disabled={!data.hasDisk || !data.disk.data}
icon="arrow-circle-down"
content="From Disk"
mb="0"
onClick={() => act('bufferOption', {
option: 'loadDisk',
id: id,
})}
/>
</LabeledList.Item>
{!!buffer.data && (
<Fragment>
<LabeledList.Item label="Subject">
{buffer.owner || (
<Box color="average">
Unknown
</Box>
)}
</LabeledList.Item>
<LabeledList.Item label="Data Type">
{buffer.type === "ui"
? "Unique Identifiers"
: "Structural Enzymes"}
{!!buffer.ue && " and Unique Enzymes"}
</LabeledList.Item>
<LabeledList.Item label="Transfer to">
<Button
disabled={!isInjectorReady}
icon={isInjectorReady ? "syringe" : "spinner"}
iconSpin={!isInjectorReady}
content="Injector"
mb="0"
onClick={() => act('bufferOption', {
option: 'createInjector',
id: id,
})}
/>
<Button
disabled={!isInjectorReady}
icon={isInjectorReady ? "syringe" : "spinner"}
iconSpin={!isInjectorReady}
content="Block Injector"
mb="0"
onClick={() => act('bufferOption', {
option: 'createInjector',
id: id,
block: 1,
})}
/>
<Button
icon="user"
content="Subject"
mb="0"
onClick={() => act('bufferOption', {
option: 'transfer',
id: id,
})}
/>
</LabeledList.Item>
</Fragment>
)}
</LabeledList>
{!buffer.data && (
<Box color="label" mt="0.5rem">
This buffer is empty.
</Box>
)}
</Section>
</Box>
);
};
const DNAModifierMainBuffersDisk = (props, context) => {
const { act, data } = useBackend(context);
const {
hasDisk,
disk,
} = data;
return (
<Section
title="Data Disk"
level="2"
buttons={
<Fragment>
<Button.Confirm
disabled={!hasDisk || !disk.data}
icon="trash"
content="Wipe"
onClick={() => act('wipeDisk')}
/>
<Button
disabled={!hasDisk}
icon="eject"
content="Eject"
onClick={() => act('ejectDisk')}
/>
</Fragment>
}>
{hasDisk ? (
disk.data ? (
<LabeledList>
<LabeledList.Item label="Label">
{disk.label ? disk.label : "No label"}
</LabeledList.Item>
<LabeledList.Item label="Subject">
{disk.owner
? disk.owner : (
<Box color="average">
Unknown
</Box>
)}
</LabeledList.Item>
<LabeledList.Item label="Data Type">
{disk.type === "ui"
? "Unique Identifiers"
: "Structural Enzymes"}
{!!disk.ue && " and Unique Enzymes"}
</LabeledList.Item>
</LabeledList>
) : (
<Box color="label">
Disk is blank.
</Box>
)
) : (
<Box color="label" textAlign="center" my="1rem">
<Icon name="save-o" size="4" /><br />
No disk inserted.
</Box>
)}
</Section>
);
};
const DNAModifierMainRejuvenators = (props, context) => {
const { act, data } = useBackend(context);
const {
isBeakerLoaded,
beakerVolume,
beakerLabel,
} = data;
return (
<Section
title="Rejuvenators and Beaker"
level="2"
buttons={
<Button
disabled={!isBeakerLoaded}
icon="eject"
content="Eject"
onClick={() => act('ejectBeaker')}
/>
}>
{isBeakerLoaded ? (
<LabeledList>
<LabeledList.Item label="Inject">
{rejuvenatorsDoses.map((a, i) => (
<Button
key={i}
disabled={a > beakerVolume}
icon="syringe"
content={a}
onClick={() => act('injectRejuvenators', {
amount: a,
})}
/>
))}
<Button
disabled={beakerVolume <= 0}
icon="syringe"
content="All"
onClick={() => act('injectRejuvenators', {
amount: beakerVolume,
})}
/>
</LabeledList.Item>
<LabeledList.Item label="Beaker">
<Box mb="0.5rem">
{beakerLabel ? beakerLabel : "No label"}
</Box>
{beakerVolume ? (
<Box color="good">
{beakerVolume} unit{beakerVolume === 1 ? "" : "s"} remaining
</Box>
) : (
<Box color="bad">
Empty
</Box>
)}
</LabeledList.Item>
</LabeledList>
) : (
<Box color="label" textAlign="center" my="25%">
<Icon name="exclamation-triangle" size="4" /><br />
No beaker loaded.
</Box>
)}
</Section>
);
};
const DNAModifierIrradiating = (props, context) => {
return (
<Dimmer textAlign="center">
<Icon name="spinner" size="5" spin /><br />
<Box color="average">
<h1>
<Icon name="radiation" />
&nbsp;Irradiating occupant&nbsp;
<Icon name="radiation" />
</h1>
</Box>
<Box color="label">
<h3>
For {props.duration} second{props.duration === 1 ? "" : "s"}
</h3>
</Box>
</Dimmer>
);
};
const DNAModifierBlocks = (props, context) => {
const { act, data } = useBackend(context);
const {
dnaString,
selectedBlock,
selectedSubblock,
blockSize,
action,
} = props;
const characters = dnaString.split('');
let curBlock = 0;
let dnaBlocks = [];
for (let block = 0; block < characters.length; block += blockSize) {
const realBlock = block / blockSize + 1;
let subBlocks = [];
for (let subblock = 0; subblock < blockSize; subblock++) {
const realSubblock = subblock + 1;
subBlocks.push(
<Button
selected={selectedBlock === realBlock
&& selectedSubblock === realSubblock}
content={characters[block + subblock]}
mb="0"
onClick={() => act(action, {
block: realBlock,
subblock: realSubblock,
})}
/>
);
}
dnaBlocks.push(
<Flex.Item flex="0 0 16%" mb="1rem">
<Box
display="inline-block"
width="20px"
height="20px"
mr="0.5rem"
lineHeight="20px"
backgroundColor="rgba(0, 0, 0, 0.33)"
fontFamily="monospace"
textAlign="center">
{realBlock}
</Box>
{subBlocks}
</Flex.Item>
);
}
return (
<Flex wrap="wrap">
{dnaBlocks}
</Flex>
);
};
@@ -0,0 +1,422 @@
import { Fragment } from 'inferno';
import { useBackend } from "../backend";
import { Box, Button, Collapsible, Icon, Input, LabeledList, Section, Tabs } from "../components";
import { ComplexModal, modalOpen, modalRegisterBodyOverride } from "../interfaces/common/ComplexModal";
import { Window } from "../layouts";
import { LoginInfo } from './common/LoginInfo';
import { LoginScreen } from './common/LoginScreen';
import { TemporaryNotice } from './common/TemporaryNotice';
const severities = {
"Minor": "good",
"Medium": "average",
"Dangerous!": "bad",
"Harmful": "bad",
"BIOHAZARD THREAT!": "bad",
};
const doEdit = (context, field) => {
modalOpen(context, 'edit', {
field: field.edit,
value: field.value,
});
};
const virusModalBodyOverride = (modal, context) => {
const virus = modal.args;
return (
<Section
level={2}
m="-1rem"
pb="1rem"
title={virus.name || "Virus"}>
<Box mx="0.5rem">
<LabeledList>
<LabeledList.Item label="Number of stages">
{virus.max_stages}
</LabeledList.Item>
<LabeledList.Item label="Spread">
{virus.spread_text} Transmission
</LabeledList.Item>
<LabeledList.Item label="Possible cure">
{virus.cure}
</LabeledList.Item>
<LabeledList.Item label="Notes">
{virus.desc}
</LabeledList.Item>
<LabeledList.Item label="Severity"
color={severities[virus.severity]}>
{virus.severity}
</LabeledList.Item>
</LabeledList>
</Box>
</Section>
);
};
export const MedicalRecords = (_properties, context) => {
const { data } = useBackend(context);
const {
authenticated,
screen,
} = data;
if (!authenticated) {
return (
<Window resizable>
<Window.Content>
<LoginScreen />
</Window.Content>
</Window>
);
}
let body;
if (screen === 2) { // List Records
body = <MedicalRecordsList />;
} else if (screen === 3) { // Record Maintenance
body = <MedicalRecordsMaintenance />;
} else if (screen === 4) { // View Records
body = <MedicalRecordsView />;
} else if (screen === 5) { // Virus Database
body = <MedicalRecordsViruses />;
} else if (screen === 6) { // Medbot Tracking
body = <MedicalRecordsMedbots />;
}
return (
<Window resizable>
<ComplexModal />
<Window.Content className="Layout__content--flexColumn">
<LoginInfo />
<TemporaryNotice />
<MedicalRecordsNavigation />
<Section height="100%" flexGrow="1">
{body}
</Section>
</Window.Content>
</Window>
);
};
const MedicalRecordsList = (_properties, context) => {
const { act, data } = useBackend(context);
const {
records,
} = data;
return (
<Fragment>
<Input
fluid
placeholder="Search by Name, DNA, or ID"
onChange={(_event, value) => act('search', { t1: value })}
/>
<Box mt="0.5rem">
{records.map((record, i) => (
<Fragment key={i}>
<Button
icon="user"
mb="0.5rem"
content={record.id + ": " + record.name}
onClick={() => act('d_rec', { d_rec: record.ref })}
/>
<br />
</Fragment>
))}
</Box>
</Fragment>
);
};
const MedicalRecordsMaintenance = (_properties, context) => {
const { act } = useBackend(context);
return (
<Fragment>
<Button
icon="download"
content="Backup to Disk"
disabled
/><br />
<Button
icon="upload"
content="Upload from Disk"
my="0.5rem"
disabled
/> <br />
<Button.Confirm
icon="trash"
content="Delete All Medical Records"
onClick={() => act('del_all')}
/>
</Fragment>
);
};
const MedicalRecordsView = (_properties, context) => {
const { act, data } = useBackend(context);
const {
medical,
printing,
} = data;
return (
<Fragment>
<Section title="General Data" level={2} mt="-6px">
<MedicalRecordsViewGeneral />
</Section>
<Section title="Medical Data" level={2}>
<MedicalRecordsViewMedical />
</Section>
<Section title="Actions" level={2}>
<Button.Confirm
icon="trash"
disabled={!!medical.empty}
content="Delete Medical Record"
color="bad"
onClick={() => act('del_r')}
/>
<Button
icon={printing ? 'spinner' : 'print'}
disabled={printing}
iconSpin={!!printing}
content="Print Entry"
ml="0.5rem"
onClick={() => act('print_p')}
/><br />
<Button
icon="arrow-left"
content="Back"
mt="0.5rem"
onClick={() => act('screen', { screen: 2 })}
/>
</Section>
</Fragment>
);
};
const MedicalRecordsViewGeneral = (_properties, context) => {
const { data } = useBackend(context);
const {
general,
} = data;
if (!general || !general.fields) {
return (
<Box color="bad">
General records lost!
</Box>
);
}
return (
<Fragment>
<Box width="50%" float="left">
<LabeledList>
{general.fields.map((field, i) => (
<LabeledList.Item key={i} label={field.field}>
<Box height="20px" display="inline-block">
{field.value}
</Box>
{!!field.edit && (
<Button
icon="pen"
ml="0.5rem"
onClick={() => doEdit(context, field)}
/>
)}
</LabeledList.Item>
))}
</LabeledList>
</Box>
<Box width="50%" float="right" textAlign="right">
{!!general.has_photos && (
general.photos.map((p, i) => (
<Box
key={i}
display="inline-block"
textAlign="center"
color="label">
<img
src={p.substr(1, p.length - 1)}
style={{
width: '96px',
'margin-bottom': '0.5rem',
'-ms-interpolation-mode': 'nearest-neighbor',
}}
/><br />
Photo #{i + 1}
</Box>
))
)}
</Box>
</Fragment>
);
};
const MedicalRecordsViewMedical = (_properties, context) => {
const { act, data } = useBackend(context);
const {
medical,
} = data;
if (!medical || !medical.fields) {
return (
<Box color="bad">
Medical records lost!
<Button
icon="pen"
content="New Record"
ml="0.5rem"
onClick={() => act('new')}
/>
</Box>
);
}
return (
<Fragment>
<LabeledList>
{medical.fields.map((field, i) => (
<LabeledList.Item
key={i}
label={field.field}>
{field.value}
<Button
icon="pen"
ml="0.5rem"
mb={field.line_break ? '1rem' : 'initial'}
onClick={() => doEdit(context, field)}
/>
</LabeledList.Item>
))}
</LabeledList>
<Section title="Comments/Log" level={2}>
{medical.comments.length === 0 ? (
<Box color="label">
No comments found.
</Box>
)
: medical.comments.map((comment, i) => (
<Box key={i}>
<Box color="label" display="inline">
{comment.header}
</Box><br />
{comment.text}
<Button
icon="comment-slash"
color="bad"
ml="0.5rem"
onClick={() => act('del_c', { del_c: i + 1 })}
/>
</Box>
))}
<Button
icon="comment-medical"
content="Add Entry"
color="good"
mt="0.5rem"
mb="0"
onClick={() => modalOpen(context, 'add_c')}
/>
</Section>
</Fragment>
);
};
const MedicalRecordsViruses = (_properties, context) => {
const { act, data } = useBackend(context);
const {
virus,
} = data;
virus.sort((a, b) => a.name > b.name ? 1 : -1);
return virus.map((vir, i) => (
<Fragment key={i}>
<Button
icon="flask"
content={vir.name}
mb="0.5rem"
onClick={() => act('vir', { vir: vir.D })}
/>
<br />
</Fragment>
));
};
const MedicalRecordsMedbots = (_properties, context) => {
const { data } = useBackend(context);
const {
medbots,
} = data;
if (medbots.length === 0) {
return (
<Box color="label">
There are no Medbots.
</Box>
);
}
return medbots.map((medbot, i) => (
<Collapsible
key={i}
open
title={medbot.name}>
<Box px="0.5rem">
<LabeledList>
<LabeledList.Item label="Location">
{medbot.area || 'Unknown'} ({medbot.x}, {medbot.y})
</LabeledList.Item>
<LabeledList.Item label="Status">
{medbot.on ? (
<Fragment>
<Box color="good">
Online
</Box>
<Box mt="0.5rem">
{medbot.use_beaker
? ("Reservoir: "
+ medbot.total_volume + "/" + medbot.maximum_volume)
: "Using internal synthesizer."}
</Box>
</Fragment>
) : (
<Box color="average">
Offline
</Box>
)}
</LabeledList.Item>
</LabeledList>
</Box>
</Collapsible>
));
};
const MedicalRecordsNavigation = (_properties, context) => {
const { act, data } = useBackend(context);
const {
screen,
} = data;
return (
<Tabs>
<Tabs.Tab
selected={screen === 2}
onClick={() => act('screen', { screen: 2 })}>
<Icon name="list" />
List Records
</Tabs.Tab>
<Tabs.Tab
selected={screen === 5}
onClick={() => act('screen', { screen: 5 })}>
<Icon name="database" />
Virus Database
</Tabs.Tab>
<Tabs.Tab
selected={screen === 6}
onClick={() => act('screen', { screen: 6 })}>
<Icon name="plus-square" />
Medbot Tracking
</Tabs.Tab>
<Tabs.Tab
selected={screen === 3}
onClick={() => act('screen', { screen: 3 })}>
<Icon name="wrench" />
Record Maintenance
</Tabs.Tab>
</Tabs>
);
};
modalRegisterBodyOverride('virus', virusModalBodyOverride);
@@ -0,0 +1,251 @@
import { round } from 'common/math';
import { Fragment } from 'inferno';
import { useBackend } from "../backend";
import { Window } from "../layouts";
import { Box, Button, Flex, Icon, Knob, LabeledList, Section, Tabs, ProgressBar } from "../components";
const stats = [
['good', 'Conscious'],
['average', 'Unconscious'],
['bad', 'DEAD'],
];
const damages = [
['Resp.', 'oxyLoss'],
['Toxin', 'toxLoss'],
['Brute', 'bruteLoss'],
['Burn', 'fireLoss'],
];
const damageRange = {
average: [0.25, 0.5],
bad: [0.5, Infinity],
};
const tempColors = [
'bad',
'average',
'average',
'good',
'average',
'average',
'bad',
];
export const OperatingComputer = (props, context) => {
const { act, data } = useBackend(context);
const {
hasOccupant,
choice,
} = data;
let body;
if (!choice) {
body = hasOccupant
? <OperatingComputerPatient />
: <OperatingComputerUnoccupied />;
} else {
body = <OperatingComputerOptions />;
}
return (
<Window resizable>
<Window.Content className="Layout__content--flexColumn">
<Tabs>
<Tabs.Tab
selected={!choice}
icon="user"
onClick={() => act('choiceOff')}>
Patient
</Tabs.Tab>
<Tabs.Tab
selected={!!choice}
icon="cog"
onClick={() => act('choiceOn')}>
Options
</Tabs.Tab>
</Tabs>
<Section flexGrow="1">
{body}
</Section>
</Window.Content>
</Window>
);
};
const OperatingComputerPatient = (props, context) => {
const { data } = useBackend(context);
const {
occupant,
} = data;
return (
<Fragment>
<Section title="Patient" level="2">
<LabeledList>
<LabeledList.Item label="Name">
{occupant.name}
</LabeledList.Item>
<LabeledList.Item label="Status" color={stats[occupant.stat][0]}>
{stats[occupant.stat][1]}
</LabeledList.Item>
<LabeledList.Item label="Health">
<ProgressBar
min="0"
max={occupant.maxHealth}
value={occupant.health / occupant.maxHealth}
ranges={{
good: [0.5, Infinity],
average: [0, 0.5],
bad: [-Infinity, 0],
}}
/>
</LabeledList.Item>
{damages.map((d, i) => (
<LabeledList.Item key={i} label={d[0] + " Damage"}>
<ProgressBar
key={i}
min="0"
max="100"
value={occupant[d[1]] / 100}
ranges={damageRange}>
{round(occupant[d[1]])}
</ProgressBar>
</LabeledList.Item>
))}
<LabeledList.Item label="Temperature">
<ProgressBar
min="0"
max={occupant.maxTemp}
value={occupant.bodyTemperature / occupant.maxTemp}
color={tempColors[occupant.temperatureSuitability + 3]}>
{round(occupant.btCelsius)}&deg;C, {round(occupant.btFaren)}&deg;F
</ProgressBar>
</LabeledList.Item>
{!!occupant.hasBlood && (
<Fragment>
<LabeledList.Item label="Blood Level">
<ProgressBar
min="0"
max={occupant.bloodMax}
value={occupant.bloodLevel / occupant.bloodMax}
ranges={{
bad: [-Infinity, 0.6],
average: [0.6, 0.9],
good: [0.6, Infinity],
}}>
{occupant.bloodPercent}%, {occupant.bloodLevel}cl
</ProgressBar>
</LabeledList.Item>
<LabeledList.Item label="Pulse">
{occupant.pulse} BPM
</LabeledList.Item>
</Fragment>
)}
</LabeledList>
</Section>
<Section title="Current Procedure" level="2">
{occupant.inSurgery ? (
<LabeledList>
<LabeledList.Item label="Procedure">
{occupant.surgeryName}
</LabeledList.Item>
<LabeledList.Item label="Next Step">
{occupant.stepName}
</LabeledList.Item>
</LabeledList>
) : (
<Box color="label">
No procedure ongoing.
</Box>
)}
</Section>
</Fragment>
);
};
const OperatingComputerUnoccupied = () => {
return (
<Flex textAlign="center" height="100%">
<Flex.Item grow="1" align="center" color="label">
<Icon
name="user-slash"
mb="0.5rem"
size="5"
/><br />
No patient detected.
</Flex.Item>
</Flex>
);
};
const OperatingComputerOptions = (props, context) => {
const { act, data } = useBackend(context);
const {
verbose,
health,
healthAlarm,
oxy,
oxyAlarm,
crit,
} = data;
return (
<LabeledList>
<LabeledList.Item label="Loudspeaker">
<Button
selected={verbose}
icon={verbose ? "toggle-on" : "toggle-off"}
content={verbose ? "On" : "Off"}
onClick={() => act(verbose ? 'verboseOff' : 'verboseOn')}
/>
</LabeledList.Item>
<LabeledList.Item label="Health Announcer">
<Button
selected={health}
icon={health ? "toggle-on" : "toggle-off"}
content={health ? "On" : "Off"}
onClick={() => act(health ? 'healthOff' : 'healthOn')}
/>
</LabeledList.Item>
<LabeledList.Item label="Health Announcer Threshold">
<Knob
bipolar
minValue="-100"
maxValue="100"
value={healthAlarm}
stepPixelSize="5"
ml="0"
onChange={(e, val) => act('health_adj', {
new: val,
})}
/>
</LabeledList.Item>
<LabeledList.Item label="Oxygen Alarm">
<Button
selected={oxy}
icon={oxy ? "toggle-on" : "toggle-off"}
content={oxy ? "On" : "Off"}
onClick={() => act(oxy ? 'oxyOff' : 'oxyOn')}
/>
</LabeledList.Item>
<LabeledList.Item label="Oxygen Alarm Threshold">
<Knob
bipolar
minValue="-100"
maxValue="100"
value={oxyAlarm}
stepPixelSize="5"
ml="0"
onChange={(e, val) => act('oxy_adj', {
new: val,
})}
/>
</LabeledList.Item>
<LabeledList.Item label="Critical Alert">
<Button
selected={crit}
icon={crit ? "toggle-on" : "toggle-off"}
content={crit ? "On" : "Off"}
onClick={() => act(crit ? 'critOff' : 'critOn')}
/>
</LabeledList.Item>
</LabeledList>
);
};
+315
View File
@@ -0,0 +1,315 @@
import { round } from 'common/math';
import { Fragment } from 'inferno';
import { useBackend } from "../backend";
import { Box, Button, Flex, Icon, LabeledList, ProgressBar, Section } from "../components";
import { Window } from "../layouts";
const stats = [
['good', 'Alive'],
['average', 'Critical'],
['bad', 'DEAD'],
];
const damages = [
['Resp.', 'oxyLoss'],
['Toxin', 'toxLoss'],
['Brute', 'bruteLoss'],
['Burn', 'fireLoss'],
];
const damageRange = {
average: [0.25, 0.5],
bad: [0.5, Infinity],
};
const tempColors = [
'bad',
'average',
'average',
'good',
'average',
'average',
'bad',
];
export const Sleeper = (props, context) => {
const { act, data } = useBackend(context);
const {
hasOccupant,
} = data;
const body = hasOccupant ? <SleeperMain /> : <SleeperEmpty />;
return (
<Window resizable>
<Window.Content className="Layout__content--flexColumn">
{body}
</Window.Content>
</Window>
);
};
const SleeperMain = (props, context) => {
const { act, data } = useBackend(context);
const {
occupant,
} = data;
return (
<Fragment>
<SleeperOccupant />
<SleeperDamage />
<SleeperChemicals />
<SleeperDialysis />
</Fragment>
);
};
const SleeperOccupant = (props, context) => {
const { act, data } = useBackend(context);
const {
occupant,
auto_eject_dead,
} = data;
return (
<Section
title="Occupant"
buttons={(
<Fragment>
<Box color="label" display="inline">
Auto-eject if dead:&nbsp;
</Box>
<Button
icon={auto_eject_dead ? "toggle-on" : "toggle-off"}
selected={auto_eject_dead}
content={auto_eject_dead ? 'On' : 'Off'}
onClick={() =>
act('auto_eject_dead_' + (auto_eject_dead ? 'off' : 'on'))}
/>
<Button
icon="user-slash"
content="Eject"
onClick={() => act('ejectify')}
/>
</Fragment>
)}>
<LabeledList>
<LabeledList.Item label="Name">
{occupant.name}
</LabeledList.Item>
<LabeledList.Item label="Health">
<ProgressBar
min="0"
max={occupant.maxHealth}
value={occupant.health / occupant.maxHealth}
ranges={{
good: [0.5, Infinity],
average: [0, 0.5],
bad: [-Infinity, 0],
}}>
{round(occupant.health, 0)}
</ProgressBar>
</LabeledList.Item>
<LabeledList.Item label="Status" color={stats[occupant.stat][0]}>
{stats[occupant.stat][1]}
</LabeledList.Item>
<LabeledList.Item label="Temperature">
<ProgressBar
min="0"
max={occupant.maxTemp}
value={occupant.bodyTemperature / occupant.maxTemp}
color={tempColors[occupant.temperatureSuitability + 3]}>
{round(occupant.btCelsius, 0)}&deg;C,
{round(occupant.btFaren, 0)}&deg;F
</ProgressBar>
</LabeledList.Item>
{!!occupant.hasBlood && (
<Fragment>
<LabeledList.Item label="Blood Level">
<ProgressBar
min="0"
max={occupant.bloodMax}
value={occupant.bloodLevel / occupant.bloodMax}
ranges={{
bad: [-Infinity, 0.6],
average: [0.6, 0.9],
good: [0.6, Infinity],
}}>
{occupant.bloodPercent}%, {occupant.bloodLevel}cl
</ProgressBar>
</LabeledList.Item>
<LabeledList.Item label="Pulse" verticalAlign="middle">
{occupant.pulse} BPM
</LabeledList.Item>
</Fragment>
)}
</LabeledList>
</Section>
);
};
const SleeperDamage = (props, context) => {
const { data } = useBackend(context);
const {
occupant,
} = data;
return (
<Section
title="Damage">
<LabeledList>
{damages.map((d, i) => (
<LabeledList.Item key={i} label={d[0]}>
<ProgressBar
key={i}
min="0"
max="100"
value={occupant[d[1]] / 100}
ranges={damageRange}>
{round(occupant[d[1]], 0)}
</ProgressBar>
</LabeledList.Item>
))}
</LabeledList>
</Section>
);
};
const SleeperDialysis = (props, context) => {
const { act, data } = useBackend(context);
const {
isBeakerLoaded,
beakerMaxSpace,
beakerFreeSpace,
dialysis,
} = data;
const canDialysis = dialysis && beakerFreeSpace > 0;
return (
<Section
title="Dialysis"
buttons={
<Fragment>
<Button
disabled={!isBeakerLoaded || beakerFreeSpace <= 0}
selected={canDialysis}
icon={canDialysis ? "toggle-on" : "toggle-off"}
content={canDialysis ? "Active" : "Inactive"}
onClick={() => act('togglefilter')}
/>
<Button
disabled={!isBeakerLoaded}
icon="eject"
content="Eject"
onClick={() => act('removebeaker')}
/>
</Fragment>
}>
{isBeakerLoaded ? (
<LabeledList>
<LabeledList.Item label="Remaining Space">
<ProgressBar
min="0"
max={beakerMaxSpace}
value={beakerFreeSpace / beakerMaxSpace}
ranges={{
good: [0.5, Infinity],
average: [0.25, 0.5],
bad: [-Infinity, 0.25],
}}>
{beakerFreeSpace}u
</ProgressBar>
</LabeledList.Item>
</LabeledList>
) : (
<Box color="label">
No beaker loaded.
</Box>
)}
</Section>
);
};
const SleeperChemicals = (props, context) => {
const { act, data } = useBackend(context);
const {
occupant,
chemicals,
maxchem,
amounts,
} = data;
return (
<Section title="Chemicals" flexGrow="1">
{chemicals.map((chem, i) => {
let barColor = '';
let odWarning;
if (chem.overdosing) {
barColor = 'bad';
odWarning = (
<Box color="bad">
<Icon name="exclamation-circle" />&nbsp;
Overdosing!
</Box>
);
} else if (chem.od_warning) {
barColor = 'average';
odWarning = (
<Box color="average">
<Icon name="exclamation-triangle" />&nbsp;
Close to overdosing
</Box>
);
}
return (
<Box key={i} backgroundColor="rgba(0, 0, 0, 0.33)" mb="0.5rem">
<Section
title={chem.title}
level="3"
mx="0"
lineHeight="18px"
buttons={odWarning}>
<Flex align="flex-start">
<ProgressBar
min="0"
max={maxchem}
value={chem.occ_amount / maxchem}
color={barColor}
mr="0.5rem">
{chem.pretty_amount}/{maxchem}u
</ProgressBar>
{amounts.map((a, i) => (
<Button
key={i}
disabled={!chem.injectable
|| ((chem.occ_amount + a) > maxchem)
|| occupant.stat === 2}
icon="syringe"
content={a}
mb="0"
height="19px"
onClick={() => act('chemical', {
chemid: chem.id,
amount: a,
})}
/>
))}
</Flex>
</Section>
</Box>
);
})}
</Section>
);
};
const SleeperEmpty = (props, context) => {
return (
<Section textAlign="center" flexGrow="1">
<Flex height="100%">
<Flex.Item grow="1" align="center" color="label">
<Icon
name="user-slash"
mb="0.5rem"
size="5"
/><br />
No occupant detected.
</Flex.Item>
</Flex>
</Section>
);
};
@@ -0,0 +1,53 @@
import { Box } from '../../components';
const PropTypes = require('prop-types');
const formatUnits = a => a + ' unit' + (a === 1 ? '' : 's');
/**
* Displays a beaker's contents
* @property {object} props
*/
export const BeakerContents = props => {
const { beakerLoaded, beakerContents = [], buttons } = props;
return (
<Box>
{!beakerLoaded && (
<Box color="label">
No beaker loaded.
</Box>
) || beakerContents.length === 0 && (
<Box color="label">
Beaker is empty.
</Box>
)}
{beakerContents.map((chemical, i) => (
<Box key={chemical.name} width="100%">
<Box color="label" display="inline" verticalAlign="middle">
{formatUnits(chemical.volume)} of {chemical.name}
</Box>
{!!buttons && (
<Box float="right" display="inline">
{buttons(chemical, i)}
</Box>
)}
<Box clear="both" />
</Box>
))}
</Box>
);
};
BeakerContents.propTypes = {
/**
* Whether there is a loaded beaker or not
*/
beakerLoaded: PropTypes.bool,
/**
* The reagents in the beaker
*/
beakerContents: PropTypes.array,
/**
* The buttons to display next to each reagent line
*/
buttons: PropTypes.arrayOf(PropTypes.element),
};
@@ -0,0 +1,198 @@
import { useBackend } from "../../backend";
import { Box, Button, Dropdown, Flex, Input, Modal } from '../../components';
let bodyOverrides = {};
/**
* Sends a call to BYOND to open a modal
* @param {any} context The React context
* @param {string} id The identifier of the modal
* @param {object=} args The arguments to pass to the modal
*/
export const modalOpen = (context, id, args) => {
const { act, data } = useBackend(context);
const newArgs = Object.assign(data.modal ? data.modal.args : {}, args || {});
act('modal_open', {
id: id,
arguments: JSON.stringify(newArgs),
});
};
/**
* Registers an override for any modal with the given id
* @param {string} id The identifier of the modal
* @param {function} bodyOverride The override function that returns the
* modal contents
*/
export const modalRegisterBodyOverride = (id, bodyOverride) => {
bodyOverrides[id] = bodyOverride;
};
const modalAnswer = (context, id, answer, args) => {
const { act, data } = useBackend(context);
if (!data.modal) {
return;
}
const newArgs = Object.assign(data.modal.args || {}, args || {});
act('modal_answer', {
id: id,
answer: answer,
arguments: JSON.stringify(newArgs),
});
};
const modalClose = (context, id) => {
const { act } = useBackend(context);
act('modal_close', {
id: id,
});
};
/**
* Displays a modal and its actions. Passed data must have a valid modal field
*
* **A valid modal field contains:**
*
* `id` — The identifier of the modal.
* Used for server-client communication and overriding
*
* `text` — The text of the modal
*
* `type` — The type of the modal:
* `message`, `input`, `choice`, `bento` and `boolean`.
* Overriden by a body override registered to the identifier if applicable.
* Defaults to `message` if not found
* @param {object} props
* @param {object} context
*/
export const ComplexModal = (props, context) => {
const { data } = useBackend(context);
if (!data.modal) {
return;
}
const {
id,
text,
type,
} = data.modal;
let modalOnEnter;
let modalBody;
let modalFooter = (
<Button
icon="arrow-left"
content="Cancel"
color="grey"
onClick={() => modalClose(context)}
/>
);
// Different contents depending on the type
if (bodyOverrides[id]) {
modalBody = bodyOverrides[id](data.modal, context);
} else if (type === "input") {
let curValue = data.modal.value;
modalOnEnter = e => modalAnswer(context, id, curValue);
modalBody = (
<Input
value={data.modal.value}
placeholder="ENTER to submit"
width="100%"
my="0.5rem"
autofocus
onChange={(_e, val) => {
curValue = val;
}}
/>
);
modalFooter = (
<Box mt="0.5rem">
<Button
icon="arrow-left"
content="Cancel"
color="grey"
onClick={() => modalClose(context)}
/>
<Button
icon="check"
content={"Confirm"}
color="good"
float="right"
m="0"
onClick={() => modalAnswer(context, id, curValue)}
/>
<Box clear="both" />
</Box>
);
} else if (type === "choice") {
const realChoices = (typeof data.modal.choices === "object")
? Object.values(data.modal.choices)
: data.modal.choices;
modalBody = (
<Dropdown
options={realChoices}
selected={data.modal.value}
width="100%"
my="0.5rem"
onSelected={val => modalAnswer(context, id, val)}
/>
);
} else if (type === "bento") {
modalBody = (
<Flex
spacingPrecise="1"
wrap="wrap"
my="0.5rem"
maxHeight="1%">
{data.modal.choices.map((c, i) => (
<Flex.Item key={i} flex="1 1 auto">
<Button
selected={(i + 1) === parseInt(data.modal.value, 10)}
onClick={() => modalAnswer(context, id, i + 1)}>
<img src={c} />
</Button>
</Flex.Item>
))}
</Flex>
);
} else if (type === "boolean") {
modalFooter = (
<Box mt="0.5rem">
<Button
icon="times"
content={data.modal.no_text}
color="bad"
float="left"
mb="0"
onClick={() => modalAnswer(context, id, 0)}
/>
<Button
icon="check"
content={data.modal.yes_text}
color="good"
float="right"
m="0"
onClick={() => modalAnswer(context, id, 1)}
/>
<Box clear="both" />
</Box>
);
}
return (
<Modal
maxWidth={props.maxWidth || (window.innerWidth / 2 + "px")}
maxHeight={props.maxHeight || (window.innerHeight / 2 + "px")}
onEnter={modalOnEnter}
mx="auto">
<Box display="inline">
{text}
</Box>
{modalBody}
{modalFooter}
</Modal>
);
};
@@ -0,0 +1,36 @@
import { useBackend } from '../../backend';
import { Box, Button, NoticeBox } from '../../components';
/**
* Displays a notice box showing the
* `authenticated` and `rank` data fields if they exist.
*
* Also gives an option to log off (calls `logout` TGUI action)
* @param {object} _properties
* @param {object} context
*/
export const LoginInfo = (_properties, context) => {
const { act, data } = useBackend(context);
const {
authenticated,
rank,
} = data;
if (!data) {
return;
}
return (
<NoticeBox info>
<Box display="inline-block" verticalAlign="middle">
Logged in as: {authenticated} ({rank})
</Box>
<Button
icon="sign-out-alt"
content="Logout and Eject ID"
color="good"
float="right"
onClick={() => act('logout')}
/>
<Box clear="both" />
</NoticeBox>
);
};
@@ -0,0 +1,84 @@
import { useBackend } from '../../backend';
import { Box, Button, Flex, Icon, Section } from '../../components';
/**
* Displays a login screen that users can interact with
* using an ID card in their hand.
* Required data fields:
* * `scan` The name of the currently inserted ID
* * `isAI` Whether the user is an AI. If true, shows "Login as AI"
* * `isRobot` Whether the user is a robot. If true, shows "Login as Cyborg"
*
* Clicking the main button calls the `scan` TGUI act.
* Clicking either the AI or normal login button calls
* the `login` TGUI act with the a `login_type` parameter with the value:
* * 1 (LOGIN_TYPE_NORMAL) if it's an ID login
* * 2 (LOGIN_TYPE_AI) if it's an AI login
* * 3 (LOGIN_TYPE_ROBOT) if it's a robot login
*
* You will have to handle the AI login case in the same action.
* The normal login button is only available when `scan` is not null.
* The AI and robot login buttons are only visible if the user is one
* @param {object} _properties
* @param {object} context
*/
export const LoginScreen = (_properties, context) => {
const { act, data } = useBackend(context);
const {
scan,
isAI,
isRobot,
} = data;
return (
<Section title="Welcome" height="100%" stretchContents>
<Flex height="100%" align="center" justify="center">
<Flex.Item textAlign="center" mt="-2rem">
<Box fontSize="1.5rem" bold>
<Icon
name="user-circle"
verticalAlign="middle"
size={3}
mr="1rem"
/>
Guest
</Box>
<Box color="label" my="1rem">
ID:
<Button
icon="id-card"
content={scan ? scan : "----------"}
ml="0.5rem"
onClick={() => act('scan')}
/>
</Box>
<Button
icon="sign-in-alt"
disabled={!scan}
content="Login"
onClick={() => act('login', {
login_type: 1,
})}
/>
{!!isAI && (
<Button
icon="sign-in-alt"
content="Login as AI"
onClick={() => act('login', {
login_type: 2,
})}
/>
)}
{!!isRobot && (
<Button
icon="sign-in-alt"
content="Login as Cyborg"
onClick={() => act('login', {
login_type: 3,
})}
/>
)}
</Flex.Item>
</Flex>
</Section>
);
};
@@ -0,0 +1,36 @@
import { useBackend } from "../../backend";
import { Box, Button, NoticeBox } from "../../components";
/**
* Displays a notice box with text and style dictated by the
* `temp` data field if it exists.
*
* A valid `temp` object contains:
*
* - `style` The style of the NoticeBox
* - `text` The text to display
*
* Allows clearing the notice through the `cleartemp` TGUI act
* @param {object} _properties
* @param {object} context
*/
export const TemporaryNotice = (_properties, context) => {
const { act, data } = useBackend(context);
const { temp } = data;
if (!temp) {
return;
}
const temporaryProperty = { [temp.style]: true };
return (
<NoticeBox {...temporaryProperty}>
<Box display="inline-block" verticalAlign="middle">
{temp.text}
</Box>
<Button
icon="times-circle"
float="right"
onClick={() => act('cleartemp')} />
<Box clear="both" />
</NoticeBox>
);
};
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -33,4 +33,12 @@
margin: 0 3px * $i;
}
}
.Flex--spacingPrecise--#{$i} {
margin: -1px * $i -1px * $i;
& > .Flex__item {
margin: 1px * $i 1px * $i;
}
}
}
@@ -59,7 +59,7 @@ $inner-padding: 0.1em;
background-color: rgba(255, 255, 255, 0.9);
}
.Knob__popupValue {
.Knob__popupValue, .Knob__popupValue--right {
position: absolute;
top: -2rem;
right: 50%;
@@ -71,6 +71,11 @@ $inner-padding: 0.1em;
white-space: nowrap;
}
.Knob__popupValue--right {
top: 0.25rem;
right: -50%;
}
.Knob__ring {
position: absolute;
top: 0;
@@ -41,3 +41,8 @@
padding-top: 1px;
padding-bottom: 0;
}
.LabeledList__breakContents {
word-break: break-all;
word-wrap: break-word;
}
@@ -47,8 +47,17 @@ $color-separator: colors.$primary !default;
}
}
.Section--flex {
display: flex;
flex-flow: column;
}
.Section--flex .Section__content {
overflow: auto;
flex: 1;
}
.Section__title {
position: relative;
padding: 6px;
border-bottom: 2px solid $color-separator;
}
@@ -69,6 +78,14 @@ $color-separator: colors.$primary !default;
padding: 8px 6px;
}
.Section__content--noTopPadding {
padding-top: 0;
}
.Section__content--stretchContents {
height: 100%;
}
.Section--level--1 .Section__titleText {
font-size: 14px;
}
@@ -56,6 +56,26 @@ $border-radius: base.$border-radius !default;
transform: translateX(-50%) translateY(-8px);
}
.Tooltip--top-left::after {
bottom: 100%;
right: 50%;
transform: translateX(12px) translateY(-8px);
}
.Tooltip--top-left:hover::after {
transform: translateX(12px) translateY(8px);
}
.Tooltip--top-right::after {
bottom: 100%;
left: 50%;
transform: translateX(-12px) translateY(-8px);
}
.Tooltip--top-right:hover::after {
transform: translateX(-12px) translateY(8px);
}
.Tooltip--bottom::after {
top: 100%;
left: 50%;