diff --git a/code/__DEFINES/machines.dm b/code/__DEFINES/machines.dm index b56024bfab8..070aaaa2249 100644 --- a/code/__DEFINES/machines.dm +++ b/code/__DEFINES/machines.dm @@ -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 diff --git a/code/__DEFINES/tgui.dm b/code/__DEFINES/tgui.dm new file mode 100644 index 00000000000..0ef159f11c2 --- /dev/null +++ b/code/__DEFINES/tgui.dm @@ -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 diff --git a/code/game/dna/dna_modifier.dm b/code/game/dna/dna_modifier.dm index d887153c6d5..3158cf90303 100644 --- a/code/game/dna/dna_modifier.dm +++ b/code/game/dna/dna_modifier.dm @@ -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 diff --git a/code/game/machinery/Sleeper.dm b/code/game/machinery/Sleeper.dm index 7c5a03e29e6..d33518be857 100644 --- a/code/game/machinery/Sleeper.dm +++ b/code/game/machinery/Sleeper.dm @@ -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, "Close the maintenance panel first.") 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, "Close the maintenance panel first.") - 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, "This person has no life for to preserve anymore. Take [occupant.p_them()] to a department capable of reanimating them.") - 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, "This person is not in good enough condition for sleepers to be effective! Use another means of treatment, such as cryogenics!") - - if(href_list["removebeaker"]) + . = TRUE + switch(action) + if("chemical") + if(!occupant) + return + if(occupant.stat == DEAD) + to_chat(usr, "This person has no life to preserve anymore. Take [occupant.p_them()] to a department capable of reanimating them.") + 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, "This person is not in good enough condition for sleepers to be effective! Use another means of treatment, such as cryogenics!") + 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, "You feel cool air surround you. You go numb as your senses turn inward.") 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, "The sleeper does not offer that chemical!") 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 diff --git a/code/game/machinery/adv_med.dm b/code/game/machinery/adv_med.dm index 3fa78dd02e6..97ab80fc1d7 100644 --- a/code/game/machinery/adv_med.dm +++ b/code/game/machinery/adv_med.dm @@ -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, "Close the maintenance panel first.") 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("[src] rattles and prints out a sheet of paper.") - var/obj/item/paper/P = new /obj/item/paper(loc) - playsound(loc, 'sound/goonstation/machines/printer_dotmatrix.ogg', 50, 1) - P.info = "
Body Scan - [href_list["name"]]

" - P.info += "Time of scan: [station_time_timestamp()]

" - P.info += "[generate_printing_text()]" - P.info += "

Notes:
" - P.name = "Body Scan - [href_list["name"]]" + . = TRUE + switch(action) + if("ejectify") + eject() + if("print_p") + visible_message("[src] rattles and prints out a sheet of paper.") + 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 = "
Body Scan - [name]

" + P.info += "Time of scan: [station_time_timestamp()]

" + P.info += "[generate_printing_text()]" + P.info += "

Notes:
" + P.name = "Body Scan - [name]" + else + return FALSE /obj/machinery/bodyscanner/proc/generate_printing_text() var/dat = "" diff --git a/code/game/machinery/computer/Operating.dm b/code/game/machinery/computer/Operating.dm index 21fddce64b7..1b083bd9c87 100644 --- a/code/game/machinery/computer/Operating.dm +++ b/code/game/machinery/computer/Operating.dm @@ -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 ) diff --git a/code/game/machinery/computer/cloning.dm b/code/game/machinery/computer/cloning.dm index 5198a97a88c..04a4f7e0db4 100644 --- a/code/game/machinery/computer/cloning.dm +++ b/code/game/machinery/computer/cloning.dm @@ -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 = "Error: Record corrupt." - else - src.menu = 3 - else - src.active_record = null - src.temp = "Error: Record missing." - - 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 = "Error: Access denied." - - else if(href_list["disk"]) //Load or eject. - switch(href_list["disk"]) - if("load") - if((isnull(src.diskette)) || isnull(src.diskette.buf)) - src.temp = "Error: The disk's data could not be read." - SSnanoui.update_uis(src) - return - if(isnull(src.active_record)) - src.temp = "Error: No active record was found." - 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 = "Error: The data could not be saved." - 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 = "Error: No cloning pod detected." - else - var/obj/machinery/clonepod/pod = selected_pod - var/cloneresult - if(!selected_pod) - temp = "Error: No cloning pod selected." - else if(pod.occupant) - temp = "Error: The cloning pod is currently occupied." - else if(pod.biomass < CLONE_BIOMASS) - temp = "Error: Not enough biomass." - else if(pod.mess) - temp = "Error: The cloning pod is malfunctioning." - else if(!config.revival_cloning) - temp = "Error: Unable to initiate cloning cycle." + 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] => Initialisation failure." - + 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 = "Error: Data corruption." - - 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 = "Error: Xenomorphs are not scannable." - 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 = "Error: Subject species is not scannable." - 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 = "Error: [Brn.dna.species.name_plural] are not scannable." - 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 = "Error: No brain detected in subject." - SSnanoui.update_uis(src) + set_scan_temp("No brain detected in subject.", "bad") + SStgui.update_uis(src) return if(subject.suiciding) - scantemp = "Error: Subject has committed suicide and is not scannable." - 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 = "Error: Subject's brain is not responding. Further attempts after a short delay may succeed." - 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 = "Error: Subject has incompatible genetic mutations." - 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 diff --git a/code/game/machinery/computer/medical.dm b/code/game/machinery/computer/medical.dm index 3674358f07f..c733a7c92c1 100644 --- a/code/game/machinery/computer/medical.dm +++ b/code/game/machinery/computer/medical.dm @@ -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, "Unable to establish a connection: 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("

All records deleted.

") - 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 = "[severity]" - if("Medium") - severity = "[severity]" - if("Dangerous!") - severity = "[severity]" - if("BIOHAZARD THREAT!") - severity = "

[severity]

" - setTemp({"Name: [D.name] -
Number of stages: [D.max_stages] -
Spread: [D.spread_text] Transmission -
Possible Cure: [(D.cure_text||"none")] -
Affected Lifeforms:[afs]
-
Notes: [D.desc]
-
Severity: [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("

Are you sure you wish to delete all records?

", 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("

Physical Condition

", 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("

Mental Condition

", 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("

Blood Type

", 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("

Are you sure you wish to delete the record (Medical Portion Only)?

", 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("

Record not found!

") - 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()]
[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("

Could not locate record [t1].

") - 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 = "
Medical Record

" - if(istype(active1, /datum/data/record) && GLOB.data_core.general.Find(active1)) - P.info += {"Name: [active1.fields["name"]] ID: [active1.fields["id"]] -
\nSex: [active1.fields["sex"]] -
\nAge: [active1.fields["age"]] -
\nFingerprint: [active1.fields["fingerprint"]] -
\nPhysical Status: [active1.fields["p_stat"]] -
\nMental Status: [active1.fields["m_stat"]]
"} +/** + * 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 += "General Record Lost!
" - if(istype(active2, /datum/data/record) && GLOB.data_core.medical.Find(active2)) - P.info += {"
\n
Medical Data
-
\nBlood Type: [active2.fields["blood_type"]] -
\nDNA: [active2.fields["b_dna"]]
\n -
\nMinor Disabilities: [active2.fields["mi_dis"]] -
\nDetails: [active2.fields["mi_dis_d"]]
\n -
\nMajor Disabilities: [active2.fields["ma_dis"]] -
\nDetails: [active2.fields["ma_dis_d"]]
\n -
\nAllergies: [active2.fields["alg"]] -
\nDetails: [active2.fields["alg_d"]]
\n -
\nCurrent Diseases: [active2.fields["cdi"]] (per disease info placed in log/comment section) -
\nDetails: [active2.fields["cdi_d"]]
\n -
\nImportant Notes: -
\n\t[active2.fields["notes"]]
\n -
\n -
Comments/Log

"} - for(var/c in active2.fields["comments"]) - P.info += "[c]
" - else - P.info += "Medical Record Lost!
" - P.info += "" - 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 = "
Medical Record

" + if(istype(active1, /datum/data/record) && GLOB.data_core.general.Find(active1)) + P.info += {"Name: [active1.fields["name"]] ID: [active1.fields["id"]] +
\nSex: [active1.fields["sex"]] +
\nAge: [active1.fields["age"]] +
\nFingerprint: [active1.fields["fingerprint"]] +
\nPhysical Status: [active1.fields["p_stat"]] +
\nMental Status: [active1.fields["m_stat"]]
"} + else + P.info += "General Record Lost!
" + if(istype(active2, /datum/data/record) && GLOB.data_core.medical.Find(active2)) + P.info += {"
\n
Medical Data
+
\nBlood Type: [active2.fields["blood_type"]] +
\nDNA: [active2.fields["b_dna"]]
\n +
\nMinor Disabilities: [active2.fields["mi_dis"]] +
\nDetails: [active2.fields["mi_dis_d"]]
\n +
\nMajor Disabilities: [active2.fields["ma_dis"]] +
\nDetails: [active2.fields["ma_dis_d"]]
\n +
\nAllergies: [active2.fields["alg"]] +
\nDetails: [active2.fields["alg_d"]]
\n +
\nCurrent Diseases: [active2.fields["cdi"]] (per disease info placed in log/comment section) +
\nDetails: [active2.fields["cdi_d"]]
\n +
\nImportant Notes: +
\n\t[active2.fields["notes"]]
\n +
\n +
Comments/Log

"} + for(var/c in active2.fields["comments"]) + P.info += "[c]
" + else + P.info += "Medical Record Lost!
" + P.info += "" + 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 diff --git a/code/game/machinery/cryo.dm b/code/game/machinery/cryo.dm index 8be09ef30b7..ba8f430937c 100644 --- a/code/game/machinery/cryo.dm +++ b/code/game/machinery/cryo.dm @@ -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, "Close the maintenance panel first.") 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, "The cryo cell cannot handle such a lifeform!") 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() diff --git a/code/modules/client/asset_cache.dm b/code/modules/client/asset_cache.dm index 8ca8bf0fbb1..8d8d83b4ae2 100644 --- a/code/modules/client/asset_cache.dm +++ b/code/modules/client/asset_cache.dm @@ -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 diff --git a/code/modules/reagents/chemistry/machinery/chem_dispenser.dm b/code/modules/reagents/chemistry/machinery/chem_dispenser.dm index 91d5900610d..bacf2285c83 100644 --- a/code/modules/reagents/chemistry/machinery/chem_dispenser.dm +++ b/code/modules/reagents/chemistry/machinery/chem_dispenser.dm @@ -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, "You set [I] on the machine.") - 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() ..() diff --git a/code/modules/reagents/chemistry/machinery/chem_heater.dm b/code/modules/reagents/chemistry/machinery/chem_heater.dm index 39aa91099c0..8fddc97537f 100644 --- a/code/modules/reagents/chemistry/machinery/chem_heater.dm +++ b/code/modules/reagents/chemistry/machinery/chem_heater.dm @@ -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, "You add the beaker to the machine!") 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) diff --git a/code/modules/reagents/chemistry/machinery/chem_master.dm b/code/modules/reagents/chemistry/machinery/chem_master.dm index 4d1f577969c..f2b3247fe4b 100644 --- a/code/modules/reagents/chemistry/machinery/chem_master.dm +++ b/code/modules/reagents/chemistry/machinery/chem_master.dm @@ -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, "You add the beaker to the machine!") - 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, "You add [I] into the dispenser slot!") - 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("[src] rattles and prints out a sheet of paper.") playsound(loc, 'sound/goonstation/machines/printer_dotmatrix.ogg', 50, 1) + var/obj/item/paper/P = new /obj/item/paper(loc) - P.info = "
Chemical Analysis

" + P.info = "
Chemical Analysis

" P.info += "Time of analysis: [station_time_timestamp()]

" - P.info += "Chemical name: [href_list["name"]]
" - 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 += "Description:
Blood Type: [B]
DNA: [C]" + P.info += "Chemical name: [R.name]
" + if(istype(R, /datum/reagent/blood)) + var/datum/reagent/blood/B = R + P.info += "Description: N/A
Blood Type: [B.data["blood_type"]]
DNA: [B.data["blood_DNA"]]" else - P.info += "Description: [href_list["desc"]]" + P.info += "Description: [R.description]" P.info += "

Notes:
" - 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 += "Chemmaster 3000Chemical infos:

Name:
[A]

Description:
Blood Type: [B]
DNA: [C]" - else - dat += "Chemmaster 3000Chemical infos:

Name:
[href_list["name"]]

Description:
[href_list["desc"]]" - dat += "

(Print Analysis)
" - dat += "(Back)" + 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 += "Condimaster 3000Condiment infos:

Name:
[href_list["name"]]

Description:
[href_list["desc"]]


(Back)" - 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, "Not enough reagents to create these pills!") - 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 = "" - var/j = 0 - for(var/i = 1 to MAX_PILL_SPRITE) - j++ - if(j == 1) - dat += "" - dat += "" - if(j == 5) - dat += "" - j = 0 - dat += "
" - usr << browse(dat, "window=chem_master_iconsel;size=225x193") - return - else if(href_list["change_bottle"]) - var/dat = "" - var/j = 0 - for(var/i in list("bottle", "small_bottle", "wide_bottle", "round_bottle", "reagent_bottle")) - j++ - if(j == 1) - dat += "" - dat += "" - if(j == 5) - dat += "" - j = 0 - dat += "
" - 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, "Not enough reagents to create these pills!") + 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, "Not enough reagents to create these patches!") + 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 diff --git a/code/modules/tgui/modal.dm b/code/modules/tgui/modal.dm new file mode 100644 index 00000000000..f09f54f627c --- /dev/null +++ b/code/modules/tgui/modal.dm @@ -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 diff --git a/nano/templates/adv_med.tmpl b/nano/templates/adv_med.tmpl deleted file mode 100644 index a1ca2815679..00000000000 --- a/nano/templates/adv_med.tmpl +++ /dev/null @@ -1,231 +0,0 @@ - - -{{if !data.occupied}} -

No occupant detected.

-{{else}} -

Occupant Data:

-
-
- Name: -
-
- {{:data.occupant.name}} -
-
-
-
- Health: -
- {{:helper.displayBar(data.occupant.health, -100, 100, (data.occupant.health >= 50) ? 'good' : (data.occupant.health >= 0) ? 'average' : 'bad')}} -
- {{:helper.smoothRound(data.occupant.health)}}% -
-
-
-
- Status: -
-
- {{if data.occupant.stat==0}} - Alive - {{else data.occupant.stat==1}} - Critical - {{else}} - Dead - {{/if}} -
-
- -
-
- Temperature: -
-
- {{:helper.smoothRound(data.occupant.bodyTempC)}}°C, {{:helper.smoothRound(data.occupant.bodyTempF)}}°F -
-
- {{if data.occupant.hasBorer}} - - Large growth detected in frontal lobe, possibly cancerous. Surgical removal is recommended.
- {{/if}} - {{if data.occupant.blind}} - Cataracts detected.
- {{/if}} - {{if data.occupant.colourblind}} - Photoreceptor abnormalities detected.
- {{/if}} - {{if data.occupant.nearsighted}} - Retinal misalignment detected.
- {{/if}} - -
- {{: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')}} -

- - Damage: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Brute Damage: Brain Damage:
{{:helper.displayBar(data.occupant.bruteLoss, 0, data.occupant.maxHealth+100, (data.occupant.bruteLoss < 15) ? 'notgood' : (data.occupant.bruteLoss < 40) ? 'average' : 'bad', data.occupant.bruteLoss) }} {{:helper.displayBar(data.occupant.brainLoss, 0, data.occupant.maxHealth+100, (data.occupant.brainLoss < 15) ? 'notgood' : (data.occupant.brainLoss < 40) ? 'average' : 'bad', data.occupant.brainLoss)}}
Resp. Damage: Radiation Level:
{{:helper.displayBar(data.occupant.oxyLoss, 0, data.occupant.maxHealth+100, (data.occupant.oxyLoss < 15) ? 'notgood' : (data.occupant.oxyLoss < 40) ? 'average' : 'bad', data.occupant.oxyLoss)}} {{:helper.displayBar(data.occupant.radLoss, 0, data.occupant.maxHealth+100, (data.occupant.radLoss < 15) ? 'notgood' : (data.occupant.radLoss < 40) ? 'average' : 'bad', data.occupant.radLoss)}}
Toxin Damage: Genetic Damage:
{{:helper.displayBar(data.occupant.toxLoss, 0, data.occupant.maxHealth+100, (data.occupant.toxLoss < 15) ? 'notgood' : (data.occupant.toxLoss < 40) ? 'average' : 'bad', data.occupant.toxLoss)}} {{:helper.displayBar(data.occupant.cloneLoss, 0, data.occupant.maxHealth+100, (data.occupant.cloneLoss < 15) ? 'notgood' : (data.occupant.cloneLoss < 40) ? 'average' : 'bad', data.occupant.cloneLoss)}}
Burn Severity: Paralysis %: ({{:helper.smoothRound(data.occupant.paralysisSeconds)}} seconds left)
{{:helper.displayBar(data.occupant.fireLoss, 0, data.occupant.maxHealth+100, (data.occupant.fireLoss < 15) ? 'notgood' : (data.occupant.fireLoss < 40) ? 'average' : 'bad', data.occupant.fireLoss)}} {{:helper.displayBar(data.occupant.paralysis, 0, 100, (data.occupant.paralysis < 25) ? 'notgood' : (data.occupant.paralysis < 50) ? 'average' : 'bad', data.occupant.paralysis)}}
- {{if data.occupant.blood.hasBlood}} -
- Blood: -
-
Pulse:
{{:data.occupant.blood.pulse}} bpm
-
-
-
Blood Level:
- {{:helper.displayBar(data.occupant.blood.bloodLevel, 0, data.occupant.blood.bloodMax, (data.occupant.blood.percent <= 60) ? 'bad' : (data.occupant.blood.percent <= 90) ? 'average' : 'good' )}} -
{{:helper.smoothRound(data.occupant.blood.percent)}}%, {{:helper.smoothRound(data.occupant.blood.bloodLevel)}}cl
-
- {{/if}} -
- {{if data.occupant.hasVirus}} - - Viral pathogen detected in blood stream. -
- {{/if}} - {{if data.occupant.implant_len}} -
- Implants - - - {{for data.occupant.implant}} - - {{/for}} - -
{{:value.name}}
-
- {{/if}} - External Organs - - - - - - - - - - - {{for data.occupant.extOrgan}} - - - - - - - - {{/for}} - -
Name Total damage Brute damage Burn damage Injuries
{{:value.name}} {{:helper.displayBar(value.totalLoss, 0, value.maxHealth, (value.totalLoss < value.bruised) ? 'notgood' : (value.totalLoss < value.broken) ? 'average' : 'bad', value.totalLoss) }} {{if value.bruteLoss> 0}} {{else}} {{/if}} {{:helper.smoothRound(value.bruteLoss)}} {{if value.fireLoss> 0}} {{else}} {{/if}} {{:helper.smoothRound(value.fireLoss)}} - - {{if value.internalBleeding}} internal bleeding
{{/if}} - {{if value.lungRuptured}} lung ruptured
{{/if}} - {{if value.status.broken}} {{:value.status.broken}}
{{/if}} - {{if value.germ_level > 100}} - {{if value.germ_level < 300}} - mild infection
- {{else value.germ_level < 400}} - mild infection+
- {{else value.germ_level < 500}} - mild infection++
- {{else value.germ_level < 700}} - acute infection
- {{else value.germ_level < 800}} - acute infection+
- {{else value.germ_level < 900}} - acute infection++
- {{else value.germ_level >= 900}} - septic
- {{/if}} - {{/if}} - {{if value.open}} open incision
{{/if}} -
- {{if value.status.splinted}} splinted
{{/if}} - {{if value.status.robotic}} robotic
{{/if}} - {{if value.status.dead}} DEAD
- {{/for}} - {{/if}} -
-
- Internal Organs - - - - - - - - - {{for data.occupant.intOrgan}} - - - - - - {{/for}} - -
Name Brute damage Injuries
{{:value.name}}
{{:helper.displayBar(value.damage, 0, value.maxHealth, (value.damage < value.bruised) ? 'notgood' : (value.damage < value.broken) ? 'average' : 'bad', value.damage) }} - - {{if value.germ_level > 100}} - {{if value.germ_level < 300}} - mild infection
- {{else value.germ_level < 400}} - mild infection+
- {{else value.germ_level < 500}} - mild infection++
- {{else value.germ_level < 700}} - acute Infection
- {{else value.germ_level < 800}} - acute Infection+
- {{else value.germ_level < 900}} - acute Infection++
- {{else value.germ_level >= 900}} - septic
- {{/if}} - {{/if}} -
- {{if value.robotic == 1}} robotic
{{/if}} - {{if value.robotic == 2}} assisted
{{/if}} - {{if value.dead}} DEAD
{{/if}} -
-{{/if}} diff --git a/nano/templates/chem_dispenser.tmpl b/nano/templates/chem_dispenser.tmpl deleted file mode 100644 index 74d694530c9..00000000000 --- a/nano/templates/chem_dispenser.tmpl +++ /dev/null @@ -1,93 +0,0 @@ - -
-
- Energy: -
-
- {{:helper.displayBar(data.energy, 0, data.maxEnergy, 'good', data.energy + ' Units')}} -
-
- -
-
- Dispense: -
-
- {{: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)}} -
-
-
 
-
-
- {{if data.glass}} - Drink Dispenser - {{else}} - Chemical Dispenser - {{/if}} -
-
-
-
- {{for data.chemicals}} - {{:helper.link(value.title, 'arrow-circle-down', value.commands, null, data.glass ? 'fixedLeftWide' : 'fixedLeft')}} - {{/for}} - -
-
-
 
-
-
- {{if data.glass}} - Glass - {{else}} - Beaker - {{/if}} Contents -
-
- {{:helper.link(data.glass ? 'Eject Glass' : 'Eject Beaker', 'eject', {'ejectBeaker' : 1}, data.isBeakerLoaded ? null : 'disabled', 'floatRight')}} -
-
-
-
-
- {{if data.isBeakerLoaded}} - {{if data.beakerCurrentVolume}} - Dispose: - {{/if}} - Volume: {{:data.beakerCurrentVolume}} / {{:data.beakerMaxVolume}}
- {{for data.beakerContents}} -
-
- - {{:value.volume}} units of {{:value.name}} - -
-
- {{: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})}} -
-
-
- {{empty}} - {{if data.glass}}Glass{{else}}Beaker{{/if}} is empty - - {{/for}} - {{else}} - - No {{if data.glass}}Glass{{else}}Beaker{{/if}} loaded - - {{/if}} -
-
-
diff --git a/nano/templates/chem_heater.tmpl b/nano/templates/chem_heater.tmpl deleted file mode 100644 index 9d9e16fa7e1..00000000000 --- a/nano/templates/chem_heater.tmpl +++ /dev/null @@ -1,47 +0,0 @@ - -
-
- Status: -
-
- {{:helper.link(data.isActive ? 'On' : 'Off', 'power-off', {'toggle_on' : 1}, data.isBeakerLoaded ? null : 'disabled')}} -
-
-
-
- Target: -
-
- {{:helper.link(data.targetTemp + 'K', 'gear', {'adjust_temperature' : 'input'}, null)}} -
-
-
-
- Beaker -
-
- {{:helper.link('Eject', 'eject', {'eject_beaker' : 1}, data.isBeakerLoaded ? null : 'disabled', 'floatRight')}} -
-
-
-
-
-
-
- {{if data.isBeakerLoaded}} - Volume: {{:helper.smoothRound(data.beakerCurrentVolume)}} / {{:data.beakerMaxVolume}}
- Temperature: {{:helper.smoothRound(data.currentTemp)}} Kelvin
- {{for data.beakerContents}} - {{:value.volume}} units of {{:value.name}}
- {{/for}} - {{else}} - No beaker loaded - {{/if}} -
-
-
-
-
\ No newline at end of file diff --git a/nano/templates/chem_master.tmpl b/nano/templates/chem_master.tmpl deleted file mode 100644 index e91a43d357b..00000000000 --- a/nano/templates/chem_master.tmpl +++ /dev/null @@ -1,106 +0,0 @@ -{{if data.beaker}} -
{{:helper.link('Eject Beaker and Clear Buffer', 'eject', {'eject': 1})}}
-
-

Add to buffer:

- {{for data.beaker_reagents}} -
-
{{:value.name}}, {{:value.volume}} units
- {{: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})}} -
- {{/for}} -
-
-

-
- Transfer to {{:helper.link(data.mode ? 'beaker' : 'disposal', 'arrows-h', {'toggle': 1})}} -
-

- {{for data.buffer_reagents}} -
-
{{:value.name}}, {{:value.volume}} units
- {{: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})}} -
- {{/for}} -
-
-

Containers:

-
- {{if data.condi}} - {{:helper.link('Create pack (10u max)', 'arrow-right', {'createpill': 1})}} - {{:helper.link('Create bottle (50u max)', 'arrow-right', {'createbottle': 1})}} - {{else}} - - - - - - - - - - - - - - - - -
- {{:helper.link('Create pill (100u max)', 'arrow-right', {'createpill': 1})}} - - {{:helper.link('Multiple', 'arrow-right', {'createpill_multiple': 1})}} - - {{:helper.link('Style', 'pencil', {'change_pill': 1})}} - - -
- {{:helper.link('Create patch (30u max)', 'arrow-right', {'createpatch': 1})}} - - {{:helper.link('Multiple', 'arrow-right', {'createpatch_multiple': 1})}} -
- {{:helper.link('Create bottle (50u max)', 'arrow-right', {'createbottle': 1})}} - - {{:helper.link('Style', 'pencil', {'change_bottle': 1})}} - - -
- {{/if}} -
-
-{{else}} - No beaker loaded
-{{/if}} -
- - {{if data.loaded_pill_bottle}} - - - - - -
- {{:helper.link('Pill Bottle (' + data.loaded_pill_bottle_contents_len + '/' + data.loaded_pill_bottle_storage_slots + ')', 'eject', {'ejectp': 1})}} - - {{:helper.link('Change Wrapper', 'pencil', {'change_pillbottle': 1})}} -
- {{else}} - - - - -
- {{:helper.link('No Pill Bottle', 'eject', {}, 'disabled')}} -
- {{/if}} -
-
\ No newline at end of file diff --git a/nano/templates/cloning_console.tmpl b/nano/templates/cloning_console.tmpl deleted file mode 100644 index c541ca6d6b4..00000000000 --- a/nano/templates/cloning_console.tmpl +++ /dev/null @@ -1,148 +0,0 @@ - -
-
-
Menu
-
- {{:helper.link('Main', 'home', {'menu' : 1}, data.menu == 1 ? 'selected' : '')}}{{:helper.link('Records', 'folder-open', {'menu' : 2}, data.menu == 2 ? 'selected' : '')}} -
-
-
-
Refresh
-
- {{:helper.link('Refresh', 'refresh', {'refresh' : 1})}} -
-
-
-
Autoprocessing
-
- {{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}} -
-
- {{if data.disk}} -
-
Disk
-
{{:helper.link('Eject', 'eject', {'disk' : 'eject'})}}
-
- {{/if}} -
-{{if data.menu == 1}} -

Modules

-
-
Scanner
-
- {{if data.scanner}} Found!{{else}} Error: Not detected!{{/if}} -
-
-
-
Pods
-
- {{if data.numberofpods}} Found {{:data.numberofpods}}!{{else}} Error: None detected!{{/if}} -
-
- {{if data.scanner}} -

Scanner Functions

- {{if data.loading}} - Scanning... - {{else}} - {{:data.scantemp}} - {{/if}} -
-
Scan Occupant
-
{{:helper.link('Scan', 'search', {'scan' : 1}, !data.occupant ? 'disabled' : '')}}
-
-
-
Scanner Lock
-
{{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' : '')}}
-
- {{if data.can_brainscan}} -
-
Scan Mode
-
- {{:helper.link('Brain', null, {'toggle_mode' : 1}, !data.scan_mode ? '' : 'selected')}}{{:helper.link('Body', null, {'toggle_mode' : 1}, data.scan_mode ? '' : 'selected')}} -
-
- {{/if}} - {{/if}} - {{if data.numberofpods}} -

Pods

- {{for data.pods}} - {{:value.name}}
-
-
Biomass
-
- {{:value.biomass}} -
-
- -
-
Select
-
- {{:helper.link('Select', 'check', {'selectpod' : value.pod}, value.pod == data.selected_pod ? 'selected' : '')}} -
-
- - {{/for}} - {{/if}} -{{/if}} -{{if data.menu == 2}} -

Current Records

- {{if data.temp}} -
- {{:data.temp}} -
- {{/if}} - {{for data.records}} -
{{:helper.link(value.realname, 'arrow-circle-down', {'view_rec' : value.record})}}
- {{empty}} - No records found! - {{/for}} -{{/if}} -{{if data.menu == 3}} -

Selected Record

- {{if data.temp}} -
- {{:data.temp}} -
- {{/if}} - {{if !data.activerecord}} -
Error: Record not found!
- {{else}} -
- Real Name: {{:data.realname}}
- Health: {{:data.health}} (OXY - BURN - TOX - BRUTE) -
-
- UI: {{:data.unidentity}}
- SE:
{{:data.strucenzymes}}
-
- {{if data.disk}} -
-
Disk
-
{{: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'})}}
-
- {{/if}} -
-
Actions
-
{{:helper.link('Clone', 'arrow-right', {'clone' : data.activerecord}, !data.podready ? 'disabled' : '')}}{{:helper.link('Delete', 'trash', {'del_rec' : 1})}}
-
- {{/if}} -{{/if}} -{{if data.menu == 4}} -

Confirm Record Deletion

- {{if data.temp}} -
- {{:data.temp}} -
- {{/if}} -
- {{:helper.link('Scan Card and Confirm', 'trash', {'del_rec' : 1})}}{{:helper.link('Cancel', 'close', {'menu' : 3})}} -
-{{/if}} - diff --git a/nano/templates/cryo.tmpl b/nano/templates/cryo.tmpl deleted file mode 100644 index de949c855f2..00000000000 --- a/nano/templates/cryo.tmpl +++ /dev/null @@ -1,117 +0,0 @@ - -

Cryo Cell Status

- -
- {{if !data.hasOccupant}} -
Cell Unoccupied
- {{else}} -
- {{:data.occupant.name}}   - {{if data.occupant.stat == 0}} - Conscious - {{else data.occupant.stat == 1}} - Unconscious - {{else}} - DEAD - {{/if}} -
- - {{if data.occupant.stat < 2}} -
-
Health:
- {{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}} -
{{:helper.smoothRound(data.occupant.health)}}
-
- -
-
Brute Damage:
- {{:helper.displayBar(data.occupant.bruteLoss, 0, data.occupant.maxHealth, 'bad')}} -
{{:helper.smoothRound(data.occupant.bruteLoss)}}
-
- -
-
Resp. Damage:
- {{:helper.displayBar(data.occupant.oxyLoss, 0, data.occupant.maxHealth, 'bad')}} -
{{:helper.smoothRound(data.occupant.oxyLoss)}}
-
- -
-
Toxin Damage:
- {{:helper.displayBar(data.occupant.toxLoss, 0, data.occupant.maxHealth, 'bad')}} -
{{:helper.smoothRound(data.occupant.toxLoss)}}
-
- -
-
Burn Severity:
- {{:helper.displayBar(data.occupant.fireLoss, 0, data.occupant.maxHealth, 'bad')}} -
{{:helper.smoothRound(data.occupant.fireLoss)}}
-
- {{/if}} - {{/if}} -
-
-
-
Cell Temperature:
- {{:helper.smoothRound(data.cellTemperature)}} K -
-
-
- -

Cryo Cell Operation

-
-
- Cryo Cell Status: -
-
- {{:helper.link('On', 'power-off', {'switchOn' : 1}, data.isOperating ? 'selected' : null)}}{{:helper.link('Off', 'close', {'switchOff' : 1}, data.isOperating ? null : 'selected')}} -
-
- {{:helper.link('Eject Occupant', 'arrow-circle-o-down', {'ejectOccupant' : 1}, data.hasOccupant ? null : 'disabled')}} -
-
-
 
-
-
- Beaker: -
-
- {{if data.isBeakerLoaded}} - {{:data.beakerLabel ? data.beakerLabel : 'No label'}}
- {{if data.beakerVolume}} - {{:helper.smoothRound(data.beakerVolume)}} units remaining
- {{else}} - Beaker is empty - {{/if}} - {{else}} - No beaker loaded - {{/if}} -
-
- {{:helper.link('Eject Beaker', 'eject', {'ejectBeaker' : 1}, data.isBeakerLoaded ? null : 'disabled')}} -
-
- -
 
-
-
- Auto-eject healthy patients: -
-
- {{: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')}} -
-
-
-
- Auto-eject dead patients: -
-
- {{: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')}} -
-
diff --git a/nano/templates/dna_modifier.tmpl b/nano/templates/dna_modifier.tmpl deleted file mode 100644 index 2789a4a7881..00000000000 --- a/nano/templates/dna_modifier.tmpl +++ /dev/null @@ -1,313 +0,0 @@ - -

Status

- -
- {{if !data.hasOccupant}} -
Cell Unoccupied
- {{else}} -
- {{:data.occupant.name}} =>  - {{if data.occupant.stat == 0}} - Conscious - {{else data.occupant.stat == 1}} - Unconscious - {{else}} - DEAD - {{/if}} -
- - {{if !data.occupant.isViableSubject || !data.occupant.uniqueIdentity || !data.occupant.structuralEnzymes}} -
- The occupant's DNA structure is ruined beyond recognition, please insert a subject with an intact DNA structure. -
- {{else data.occupant.stat < 2}} -
-
Health:
- {{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}} -
{{:helper.smoothRound(data.occupant.health)}}
-
- -
-
Radiation:
- {{:helper.displayBar(data.occupant.radiationLevel, 0, 100, 'average')}} -
{{:helper.smoothRound(data.occupant.radiationLevel)}}
-
- -
-
Unique Enzymes:
-
{{:data.occupant.uniqueEnzymes ? data.occupant.uniqueEnzymes : 'Unknown'}}
-
- - {{/if}} - {{/if}} -
-
- -

Operations

- {{if !data.occupant.isViableSubject || !data.occupant.uniqueIdentity || !data.occupant.structuralEnzymes}} -
- No operation possible on this subject -
- {{else}} -
- {{: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)}} -
- -
 
- - {{if !data.selectedMenuKey || data.selectedMenuKey == 'ui'}} -

Modify Unique Identifier

- {{:helper.displayDNABlocks(data.occupant.uniqueIdentity, data.selectedUIBlock, data.selectedUISubBlock, data.dnaBlockSize, 'UI')}} -
-
-
- Target: -
-
- {{:helper.link('-', null, {'changeUITarget' : 0}, (data.selectedUITarget > 0) ? null : 'disabled')}} -
 {{:data.selectedUITargetHex}} 
- {{:helper.link('+', null, {'changeUITarget' : 1}, (data.selectedUITarget < 15) ? null : 'disabled')}} -
-
-
-
- {{:helper.link('Irradiate Block', 'exclamation-circle', {'pulseUIRadiation' : 1}, !data.occupant.isViableSubject ? 'disabled' : null)}} -
-
- {{else data.selectedMenuKey == 'se'}} -

Modify Structural Enzymes

- {{:helper.displayDNABlocks(data.occupant.structuralEnzymes, data.selectedSEBlock, data.selectedSESubBlock, data.dnaBlockSize, 'SE')}} -
-
-
- {{:helper.link('Irradiate Block', 'exclamation-circle', {'pulseSERadiation' : 1}, !data.occupant.isViableSubject ? 'disabled' : null)}} -
-
- {{else data.selectedMenuKey == 'buffer'}} -

Transfer Buffers

- {{for data.buffers}} -

Buffer {{:(index + 1)}}

-
-
-
- Load Data: -
-
- {{: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)}} -
-
- {{if value.data}} -
-
- Label: -
-
- {{:helper.link(value.label, 'file', {'bufferOption' : 'changeLabel', 'bufferId' : (index + 1)})}} -
-
-
-
- Subject: -
-
- {{:value.owner ? value.owner : 'Unknown'}} -
-
-
-
- Stored Data: -
-
- {{:value.data == 'ui' ? 'Unique Identifiers' : 'Structural Enzymes'}} - {{:value.ue ? ' + Unique Enzymes' : ''}} -
-
- {{else}} -
-
- This buffer is empty. -
-
- {{/if}} -
-
- Options: -
-
- {{: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)}} -
-
-
- {{/for}} - -

Data Disk

-
- {{if data.hasDisk}} - {{if data.disk.data}} -
-
- Label: -
-
- {{:data.disk.label ? data.disk.label : 'No Label'}} -
-
-
-
- Subject: -
-
- {{:data.disk.owner ? data.disk.owner : 'Unknown'}} -
-
-
-
- Stored Data: -
-
- {{:data.disk.data == 'ui' ? 'Unique Identifiers' : 'Structural Enzymes'}} - {{:data.disk.ue ? ' + Unique Enzymes' : ''}} -
-
- {{else}} -
-
- Disk is blank. -
-
- {{/if}} - {{else}} -
-
- No disk inserted. -
-
- {{/if}} -
-
- Options: -
-
- {{: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)}} -
-
-
- {{else data.selectedMenuKey == 'rejuvenators'}} -

Rejuvenators

-
-
- Inject: -
-
- {{: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)}} -
-
-
 
-
-
- Beaker: -
-
- {{if data.isBeakerLoaded}} - {{:data.beakerLabel ? data.beakerLabel : 'No label'}}
- {{if data.beakerVolume}} - {{:data.beakerVolume}} units remaining
- {{else}} - Beaker is empty - {{/if}} - {{else}} - No beaker loaded - {{/if}} -
-
- {{:helper.link('Eject Beaker', 'eject', {'ejectBeaker' : 1}, data.isBeakerLoaded ? null : 'disabled')}} -
-
- {{/if}} - -
 
- - {{if !data.selectedMenuKey || data.selectedMenuKey == 'ui' || data.selectedMenuKey == 'se'}} -

Radiation Emitter Settings

-
-
- Intensity: -
-
- {{:helper.link('-', null, {'radiationIntensity' : 0}, (data.radiationIntensity > 1) ? null : 'disabled')}} -
 {{:data.radiationIntensity}} 
- {{:helper.link('+', null, {'radiationIntensity' : 1}, (data.radiationIntensity < 10) ? null : 'disabled')}} -
-
-
-
- Duration: -
-
- {{:helper.link('-', null, {'radiationDuration' : 0}, (data.radiationDuration > 2) ? null : 'disabled')}} -
 {{:data.radiationDuration}} 
- {{:helper.link('+', null, {'radiationDuration' : 1}, (data.radiationDuration < 20) ? null : 'disabled')}} -
-
-
-
-   -
-
- {{:helper.link('Pulse Radiation', 'exclamation-circle', {'pulseRadiation' : 1}, !data.hasOccupant ? 'disabled' : null)}} -
-
- {{/if}} -{{/if}} -
 
- -
- -
-
- Occupant: -
-
- {{:helper.link('Eject Occupant', 'eject', {'ejectOccupant' : 1}, data.locked || !data.hasOccupant || data.irradiating ? 'disabled' : null)}} -
-
-
-
- Door Lock: -
-
- {{: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)}} -
-
- -{{if data.irradiating}} -
-
-

Irradiating Subject

-

For {{:data.irradiating}} seconds.

-
-
-{{/if}} diff --git a/nano/templates/med_data.tmpl b/nano/templates/med_data.tmpl deleted file mode 100644 index f47e13c6885..00000000000 --- a/nano/templates/med_data.tmpl +++ /dev/null @@ -1,171 +0,0 @@ - - - - -
-
- Confirm Identity: -
-
- {{:helper.link(data.scan ? data.scan : "----------", 'eject', {'scan' : 1}, null, data.scan ? 'itemContentWide' : 'fixedLeft')}} -
-
-
-{{if data.authenticated}} - {{if data.screen == 1}} -

Menu

-
-
{{:helper.link('Search Records', 'search', {'search' : 1})}}
-
{{:helper.link('List Records', 'list', {'screen' : 2})}}
-
{{:helper.link('Virus Database', 'database', {'screen' : 5})}}
-
{{:helper.link('Medbot Tracking', 'plus-square', {'screen' : 6})}}
-
{{:helper.link('Record Maintenance', 'wrench', {'screen' : 3})}}
-
{{:helper.link('Logout', 'lock', {'logout' : 1})}}
-
- {{else data.screen == 2}} -

Record List

- {{for data.records}} -
{{:helper.link(value.id + ': ' + value.name, 'user', {'d_rec' : value.ref})}}
- {{/for}} -

-
{{:helper.link('Back', 'arrow-left', {'screen' : 1})}}
- {{else data.screen == 3}} -

Records Maintenance

-
{{:helper.link('Backup To Disk', 'download', {'back' : 1}, 'disabled')}}
-
{{:helper.link('Upload From disk', 'upload', {'u_load' : 1}, 'disabled')}}
-
{{:helper.link('Delete All Records', 'trash', {'del_all' : 1})}}
-
{{:helper.link('Back', 'arrow-left', {'screen' : 1})}}
- {{else data.screen == 4}} -

Medical Record

-

General Data

- {{if data.general.empty}} -
General Record Lost!
- {{else}} -
- - - -
- {{for data.general.fields}} -
-
{{:value.field}}
-
{{:helper.link(value.value, value.edit ? 'pencil' : 'user-times', {'field' : value.edit}, value.edit ? null : 'disabled')}}
-
- {{/for}} -
- {{if data.general.has_photos}} - {{for data.general.photos}} - {{if value.photo}} - - {{/if}} - {{/for}} - {{/if}} -
-
- {{/if}} - -

Medical Data

- {{if data.medical.empty}} -
Medical Record Lost!
-
-
{{:helper.link('New Record', 'plus', {'new' : 1})}}
-
-

Menu

- {{else}} - {{for data.medical.fields}} -
-
{{:value.field}}
-
{{:helper.link(value.value, 'pencil', {'field' : value.edit})}}
-
- {{if value.line_break}} -
- {{/if}} - {{/for}} - -

Comments/Log

- {{for data.medical.comments}} -
{{:value}}
-
{{:helper.link('Remove entry', 'trash', {'del_c' : index})}}
-

- {{/for}} - -
-
{{:helper.link('Add Entry', 'plus', {'add_c' : 1})}}
-
- -

Menu

-
{{:helper.link('Delete Record (Medical Only)', 'trash', {'del_r' : 1})}}
- {{/if}} - -
{{:helper.link('Print Record', 'print', {'print_p' : 1})}}
-
{{:helper.link('Back', 'arrow-left', {'screen' : 2})}}
- {{else data.screen == 5}} -

Virus Database

- {{for data.virus}} -
{{:helper.link(value.name, 'arrow-right', {'vir' : value.D})}}
- {{/for}} -

- {{:helper.link('Back', 'arrow-left', {'screen' : 1})}} - {{else data.screen == 6}} -

Medical Robot Monitor

- {{for data.medbots}} -

{{:value.name}}

-
-
Location:
-
{{:value.x}}, {{:value.y}}
-
-
-
Status:
-
- {{:value.on ? "Online" : "Offline"}}. {{if value.use_beaker}}Reservoir: [{{:value.total_volume}}/{{:value.maximum_volume}}]{{else}}Using internal synthesizer.{{/if}} -
-
-
- {{empty}} -

None detected!

- {{/for}} -
{{:helper.link('Back', 'arrow-left', {'screen' : 1})}}
- {{/if}} -{{else}} -

Menu

- {{:helper.link('Login', 'unlock', {'login' : 1})}} -{{/if}} - -{{if data.temp}} -
-
- {{if data.temp.notice}} -
{{:data.temp.text}}
- {{else}} -
{{:data.temp.text}}
- {{/if}} - - - {{if data.temp.has_buttons}} -
-
-
- {{for data.temp.buttons}} - {{:helper.link(value.name, value.icon, {'temp' : 1, 'temp_action' : value.href}, value.status)}} - {{/for}} -
-
-
- {{/if}} - -
-
{{:helper.link('Clear screen', 'home', {'temp' : 1})}}
-
-
-
-{{/if}} \ No newline at end of file diff --git a/nano/templates/op_computer.tmpl b/nano/templates/op_computer.tmpl deleted file mode 100644 index 77894bcba16..00000000000 --- a/nano/templates/op_computer.tmpl +++ /dev/null @@ -1,215 +0,0 @@ - -

Patient Monitor

- -
- {{if data.choice==0}} - {{if !data.hasOccupant}} -
No Patient Detected
- {{else}} -
- {{:data.occupant.name}} =>  - {{if data.occupant.stat == 0}} - Conscious - {{else data.occupant.stat == 1}} - Unconscious - {{else}} - DEAD - {{/if}} -
- -
-
Health:
- {{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}} -
{{:helper.smoothRound(data.occupant.health)}}
-
- -
-
Brute Damage:
- {{:helper.displayBar(data.occupant.bruteLoss, 0, data.occupant.maxHealth, 'bad')}} -
{{:helper.smoothRound(data.occupant.bruteLoss)}}
-
- -
-
Resp. Damage:
- {{:helper.displayBar(data.occupant.oxyLoss, 0, data.occupant.maxHealth, 'bad')}} -
{{:helper.smoothRound(data.occupant.oxyLoss)}}
-
- -
-
Toxin Damage:
- {{:helper.displayBar(data.occupant.toxLoss, 0, data.occupant.maxHealth, 'bad')}} -
{{:helper.smoothRound(data.occupant.toxLoss)}}
-
- -
-
Burn Severity:
- {{:helper.displayBar(data.occupant.fireLoss, 0, data.occupant.maxHealth, 'bad')}} -
{{:helper.smoothRound(data.occupant.fireLoss)}}
-
- -
-
Patient Temperature:
- {{if data.occupant.temperatureSuitability == -3}} - {{:helper.displayBar(data.occupant.bodyTemperature, 0, data.occupant.maxTemp, 'bad')}} -
{{:helper.smoothRound(data.occupant.btCelsius)}}°C, {{:helper.smoothRound(data.occupant.btFaren)}}°F
- {{else data.occupant.temperatureSuitability == -2}} - {{:helper.displayBar(data.occupant.bodyTemperature, 0, data.occupant.maxTemp, 'average')}} -
{{:helper.smoothRound(data.occupant.btCelsius)}}°C, {{:helper.smoothRound(data.occupant.btFaren)}}°F
- {{else data.occupant.temperatureSuitability == -1}} - {{:helper.displayBar(data.occupant.bodyTemperature, 0, data.occupant.maxTemp, 'average')}} -
{{:helper.smoothRound(data.occupant.btCelsius)}}°C, {{:helper.helper.smoothRound(data.occupant.btFaren)}}°F
- {{else data.occupant.temperatureSuitability == 0}} - {{:helper.displayBar(data.occupant.bodyTemperature, 0, data.occupant.maxTemp, 'good')}} -
{{:helper.smoothRound(data.occupant.btCelsius)}}°C, {{:helper.smoothRound(data.occupant.btFaren)}}°F
- {{else data.occupant.temperatureSuitability == 1}} - {{:helper.displayBar(data.occupant.bodyTemperature, 0, data.occupant.maxTemp, 'average')}} -
{{:helper.smoothRound(data.occupant.btCelsius)}}°C, {{:helper.smoothRound(data.occupant.btFaren)}}°F
- {{else data.occupant.temperatureSuitability == 2}} - {{:helper.displayBar(data.occupant.bodyTemperature, 0, data.occupant.maxTemp, 'average')}} -
{{:helper.smoothRound(data.occupant.btCelsius)}}°C, {{:helper.smoothRound(data.occupant.btFaren)}}°F
- {{else data.occupant.temperatureSuitability == 3}} - {{:helper.displayBar(data.occupant.bodyTemperature, 0, data.occupant.maxTemp, 'bad')}} -
{{:helper.smoothRound(data.occupant.btCelsius)}}°C, {{:helper.smoothRound(data.occupant.btFaren)}}°F
- {{/if}} -
- {{if data.occupant.hasBlood}} -
-
-
Blood Level:
- {{if data.occupant.bloodPercent <= 60}} - {{:helper.displayBar(data.occupant.bloodLevel, 0, data.occupant.bloodMax, 'bad')}} -
- {{:helper.smoothRound(data.occupant.bloodPercent)}}%, {{:helper.smoothRound(data.occupant.bloodLevel)}}cl -
- {{else data.occupant.bloodPercent <= 90}} - {{:helper.displayBar(data.occupant.bloodLevel, 0, data.occupant.bloodMax, 'average')}} -
- {{:helper.smoothRound(data.occupant.bloodPercent)}}%, {{:helper.smoothRound(data.occupant.bloodLevel)}}cl -
- {{else}} - {{:helper.displayBar(data.occupant.bloodLevel, 0, data.occupant.bloodMax, 'good')}} -
- {{:helper.smoothRound(data.occupant.bloodPercent)}}%, {{:helper.smoothRound(data.occupant.bloodLevel)}}cl -
- {{/if}} -
-
Blood Type:
{{:data.occupant.bloodType}}
-
-
-
-
Pulse:
{{:data.occupant.pulse}} bpm
-
- {{/if}} - {{if data.occupant.inSurgery}} -
-
Initiated Surgery Procedure:
{{:data.occupant.surgeryName}}
-
-
Next Step:
{{:data.occupant.stepName}}
- -
- {{/if}} -
-
- {{/if}} -
-
- {{:helper.link('Options', 'gear', {'choiceOn' : 1})}} -
-
- {{else}} -
-
- Loudspeaker: -
-
- {{:helper.link('On', 'power-off', {'verboseOn' : 1}, data.verbose ? 'selected' : null)}} - {{:helper.link('Off', 'close', {'verboseOff' : 1}, data.verbose ? null : 'selected')}} -
-
-
-
-
- Health Announcer: -
-
- {{:helper.link('On', 'power-off', {'healthOn' : 1}, data.health ? 'selected' : null)}} - {{:helper.link('Off', 'close', {'healthOff' : 1}, data.health ? null : 'selected')}} -
-
- -
-
-
Health Announcer Threshold:
- {{: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')}} -
 {{: data.healthAlarm }}  
-
-
-
-
-
- Oxygen Alarm: -
-
- {{:helper.link('On', 'power-off', {'oxyOn' : 1}, data.oxy ? 'selected' : null)}} - {{:helper.link('Off', 'close', {'oxyOff' : 1}, data.oxy ? null : 'selected')}} -
-
- -
-
-
Oxygen Alert Threshold:
- {{: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')}} -
 {{: data.oxyAlarm }}  
-
-
-
-
-
- Critical Alert: -
-
- {{:helper.link('On', 'power-off', {'critOn' : 1}, data.crit ? 'selected' : null)}} - {{:helper.link('Off', 'close', {'critOff' : 1}, data.crit ? null : 'selected')}} -
-
-
-
-
- {{:helper.link('Return', 'arrow-left', {'choiceOff' : 1})}} -
-
- {{/if}} -
diff --git a/nano/templates/sleeper.tmpl b/nano/templates/sleeper.tmpl deleted file mode 100644 index cacf6c83ff5..00000000000 --- a/nano/templates/sleeper.tmpl +++ /dev/null @@ -1,211 +0,0 @@ - -

Sleeper Status

- -
- {{if !data.hasOccupant}} -
Sleeper Unoccupied
- {{else}} -
- {{:data.occupant.name}} =>  - {{if data.occupant.stat == 0}} - Conscious - {{else data.occupant.stat == 1}} - Unconscious - {{else}} - DEAD - {{/if}} -
- -
-
Health:
- {{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}} -
{{:helper.smoothRound(data.occupant.health)}}
-
- -
-
=> Brute Damage:
- {{:helper.displayBar(data.occupant.bruteLoss, 0, data.occupant.maxHealth, 'bad')}} -
{{:helper.smoothRound(data.occupant.bruteLoss)}}
-
- -
-
=> Resp. Damage:
- {{:helper.displayBar(data.occupant.oxyLoss, 0, data.occupant.maxHealth, 'bad')}} -
{{:helper.smoothRound(data.occupant.oxyLoss)}}
-
- -
-
=> Toxin Damage:
- {{:helper.displayBar(data.occupant.toxLoss, 0, data.occupant.maxHealth, 'bad')}} -
{{:helper.smoothRound(data.occupant.toxLoss)}}
-
- -
-
=> Burn Severity:
- {{:helper.displayBar(data.occupant.fireLoss, 0, data.occupant.maxHealth, 'bad')}} -
{{:helper.smoothRound(data.occupant.fireLoss)}}
-
- -
-
- -
Patient Temperature:
- {{if data.occupant.temperatureSuitability == -3}} - {{:helper.displayBar(data.occupant.bodyTemperature, 0, data.occupant.maxTemp, 'bad')}} -
{{:helper.smoothRound(data.occupant.btCelsius)}}°C, {{:helper.smoothRound(data.occupant.btFaren)}}°F
- {{else data.occupant.temperatureSuitability == -2}} - {{:helper.displayBar(data.occupant.bodyTemperature, 0, data.occupant.maxTemp, 'average')}} -
{{:helper.smoothRound(data.occupant.btCelsius)}}°C, {{:helper.smoothRound(data.occupant.btFaren)}}°F
- {{else data.occupant.temperatureSuitability == -1}} - {{:helper.displayBar(data.occupant.bodyTemperature, 0, data.occupant.maxTemp, 'average')}} -
{{:helper.smoothRound(data.occupant.btCelsius)}}°C, {{:helper.smoothRound(data.occupant.btFaren)}}°F
- {{else data.occupant.temperatureSuitability == 0}} - {{:helper.displayBar(data.occupant.bodyTemperature, 0, data.occupant.maxTemp, 'good')}} -
{{:helper.smoothRound(data.occupant.btCelsius)}}°C, {{:helper.smoothRound(data.occupant.btFaren)}}°F
- {{else data.occupant.temperatureSuitability == 1}} - {{:helper.displayBar(data.occupant.bodyTemperature, 0, data.occupant.maxTemp, 'average')}} -
{{:helper.smoothRound(data.occupant.btCelsius)}}°C, {{:helper.smoothRound(data.occupant.btFaren)}}°F
- {{else data.occupant.temperatureSuitability == 2}} - {{:helper.displayBar(data.occupant.bodyTemperature, 0, data.occupant.maxTemp, 'average')}} -
{{:helper.smoothRound(data.occupant.btCelsius)}}°C, {{:helper.smoothRound(data.occupant.btFaren)}}°F
- {{else data.occupant.temperatureSuitability == 3}} - {{:helper.displayBar(data.occupant.bodyTemperature, 0, data.occupant.maxTemp, 'bad')}} -
{{:helper.smoothRound(data.occupant.btCelsius)}}°C, {{:helper.smoothRound(data.occupant.btFaren)}}°F
- {{/if}} -
- - - {{if data.occupant.hasBlood}} - -
-
-
Pulse:
{{:data.occupant.pulse}} bpm
-
-
-
Blood Level:
- {{if data.occupant.bloodPercent <= 60}} - {{:helper.displayBar(data.occupant.bloodLevel, 0, data.occupant.bloodMax, 'bad')}} -
- {{:helper.smoothRound(data.occupant.bloodPercent)}}%, {{:helper.smoothRound(data.occupant.bloodLevel)}}cl -
- {{else data.occupant.bloodPercent <= 90}} - {{:helper.displayBar(data.occupant.bloodLevel, 0, data.occupant.bloodMax, 'average')}} -
- {{:helper.smoothRound(data.occupant.bloodPercent)}}%, {{:helper.smoothRound(data.occupant.bloodLevel)}}cl -
- {{else}} - {{:helper.displayBar(data.occupant.bloodLevel, 0, data.occupant.bloodMax, 'good')}} -
- {{:helper.smoothRound(data.occupant.bloodPercent)}}%, {{:helper.smoothRound(data.occupant.bloodLevel)}}cl -
- {{/if}} -
- {{/if}} - {{/if}} -
- -

Sleeper Operation

-
-
- Sleeper Status: -
-
- {{:helper.link('Eject Occupant', 'arrow-circle-o-down', {'ejectify' : 1}, data.hasOccupant ? null : 'disabled')}} -
-
-
 
-
-
- Dialysis Beaker: -
-
- {{if data.isBeakerLoaded}} - {{:helper.smoothRound(data.beakerFreeSpace)}} units of space remaining
- {{else}} - No Dialysis Output Beaker Loaded - {{/if}} -
-
- {{:helper.link('Eject Beaker', 'eject', {'removebeaker' : 1}, data.isBeakerLoaded ? null : 'disabled')}} - {{if data.isBeakerLoaded}} -
-
- {{: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')}} -
-
- {{/if}} -
-
-
-
- Chemicals: -
- - {{for data.chemicals}} -
{{:value.title}}
-
- {{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}} -
{{:helper.smoothRound(value.pretty_amount)}}/{{:data.maxchem}}
-
-
-
-
- {{: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)}} -
-
- {{/for}} - -
 
-
-
- Auto-eject dead patients: -
-
- {{: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')}} -
-
-
- diff --git a/paradise.dme b/paradise.dme index cfeeb1d55a4..6e1235bfc86 100644 --- a/paradise.dme +++ b/paradise.dme @@ -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" diff --git a/tgui/packages/tgui/components/Box.js b/tgui/packages/tgui/components/Box.js index 9d5e71c78a7..ada17f05d55 100644 --- a/tgui/packages/tgui/components/Box.js +++ b/tgui/packages/tgui/components/Box.js @@ -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') { diff --git a/tgui/packages/tgui/components/Flex.js b/tgui/packages/tgui/components/Flex.js index e63051438f3..26de30ef93c 100644 --- a/tgui/packages/tgui/components/Flex.js +++ b/tgui/packages/tgui/components/Flex.js @@ -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, diff --git a/tgui/packages/tgui/components/Input.js b/tgui/packages/tgui/components/Input.js index 3c73d88a8fd..b7f2dcb1f15 100644 --- a/tgui/packages/tgui/components/Input.js +++ b/tgui/packages/tgui/components/Input.js @@ -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 diff --git a/tgui/packages/tgui/components/Knob.js b/tgui/packages/tgui/components/Knob.js index 501308aea8d..0e817111653 100644 --- a/tgui/packages/tgui/components/Knob.js +++ b/tgui/packages/tgui/components/Knob.js @@ -35,6 +35,7 @@ export const Knob = props => { size, bipolar, children, + popUpPosition, ...rest } = props; return ( @@ -102,7 +103,10 @@ export const Knob = props => { {dragging && ( -
+
{displayElement}
)} diff --git a/tgui/packages/tgui/components/LabeledList.js b/tgui/packages/tgui/components/LabeledList.js index e2b515014a2..43dbd0d5a32 100644 --- a/tgui/packages/tgui/components/LabeledList.js +++ b/tgui/packages/tgui/components/LabeledList.js @@ -20,6 +20,7 @@ export const LabeledListItem = props => { labelColor = 'label', color, textAlign, + verticalAlign, buttons, content, children, @@ -33,6 +34,7 @@ export const LabeledListItem = props => { { as="td" color={color} textAlign={textAlign} + verticalAlign={verticalAlign} className={classes([ 'LabeledList__cell', 'LabeledList__content', diff --git a/tgui/packages/tgui/components/Modal.js b/tgui/packages/tgui/components/Modal.js index 916150de402..125593902b6 100644 --- a/tgui/packages/tgui/components/Modal.js +++ b/tgui/packages/tgui/components/Modal.js @@ -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 ( - +
{ 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 => {
)} {hasContent && ( -
+ {content} {children} -
+
)} ); diff --git a/tgui/packages/tgui/index.js b/tgui/packages/tgui/index.js index 59199f9edbe..45012a7e956 100644 --- a/tgui/packages/tgui/index.js +++ b/tgui/packages/tgui/index.js @@ -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(); diff --git a/tgui/packages/tgui/interfaces/BodyScanner.js b/tgui/packages/tgui/interfaces/BodyScanner.js new file mode 100644 index 00000000000..48a4a08e20f --- /dev/null +++ b/tgui/packages/tgui/interfaces/BodyScanner.js @@ -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] + : {a}
{s}
, 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 ? ( + + ) : ( + + ); + return ( + + + {body} + + + ); +}; + +const BodyScannerMain = props => { + const { + occupant, + } = props; + return ( + + + + + + + + ); +}; + +const BodyScannerMainOccupant = (props, context) => { + const { act, data } = useBackend(context); + const { + occupant, + } = data; + return ( +
+ + + + )}> + + + {occupant.name} + + + + + + {stats[occupant.stat][1]} + + + °C,  + °F + + + {occupant.implant_len ? ( + + {occupant.implant.map(im => im.name).join(', ')} + + ) : ( + + None + + )} + + +
+ ); +}; + +const BodyScannerMainAbnormalities = props => { + const { + occupant, + } = props; + if (!(occupant.hasBorer || occupant.blind + || occupant.colourblind || occupant.nearsighted + || occupant.hasVirus)) { + return ( +
+ + No abnormalities found. + +
+ ); + } + + return ( +
+ {abnormalities.map((a, i) => { + if (occupant[a[0]]) { + return ( + + {a[2]} + + ); + } + })} +
+ ); +}; + +const BodyScannerMainDamage = props => { + const { + occupant, + } = props; + return ( +
+ + {mapTwoByTwo(damages, (d1, d2, i) => ( + + + + {d1[0]}: + + + {!!d2 && d2[0] + ":"} + + + + + + + + {!!d2 && ( + + )} + + + + ))} +
+
+ ); +}; + +const BodyScannerMainDamageBar = props => { + return ( + + {round(props.value, 0)} + + ); +}; + +const BodyScannerMainOrgansExternal = props => { + if (props.organs.length === 0) { + return ( +
+ + N/A + +
+ ); + } + + return ( +
+ + + + Name + + + Damage + + + Injuries + + + {props.organs.map((o, i) => ( + + + {o.name} + + + 0 && "0.5rem"} + value={o.totalLoss / 100} + ranges={damageRange}> + + {!!o.bruteLoss && ( + + + {round(o.bruteLoss, 0)}  + + )} + {!!o.fireLoss && ( + + + {round(o.fireLoss, 0)} + + )} + + + {round(o.totalLoss, 0)} + + + + + + {reduceOrganStatus([ + o.internalBleeding && "Internal bleeding", + o.lungRuptured && "Ruptured lung", + !!o.status.broken && o.status.broken, + germStatus(o.germ_level), + !!o.open && "Open incision", + ])} + + + {reduceOrganStatus([ + !!o.status.splinted && "Splinted", + !!o.status.robotic && "Robotic", + !!o.status.dead && ( + + DEAD + + ), + ])} + {reduceOrganStatus(o.shrapnel.map( + s => s.known + ? s.name + : "Unknown object" + ))} + + + + ))} +
+
+ ); +}; + +const BodyScannerMainOrgansInternal = props => { + if (props.organs.length === 0) { + return ( +
+ + N/A + +
+ ); + } + + return ( +
+ + + + Name + + + Damage + + + Injuries + + + {props.organs.map((o, i) => ( + + + {o.name} + + + 0 && "0.5rem"} + ranges={damageRange}> + {round(o.damage, 0)} + + + + + {reduceOrganStatus([ + germStatus(o.germ_level), + ])} + + + {reduceOrganStatus([ + (o.robotic === 1) && "Robotic", + (o.robotic === 2) && "Assisted", + !!o.dead && ( + + DEAD + + ), + ])} + + + + ))} +
+
+ ); +}; + +const BodyScannerEmpty = () => { + return ( +
+ + +
+ No occupant detected. +
+
+
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/ChemDispenser.js b/tgui/packages/tgui/interfaces/ChemDispenser.js new file mode 100644 index 00000000000..7a7300fec23 --- /dev/null +++ b/tgui/packages/tgui/interfaces/ChemDispenser.js @@ -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 ( + + + + + + + + ); +}; + +const ChemDispenserSettings = (properties, context) => { + const { act, data } = useBackend(context); + const { + amount, + energy, + maxEnergy, + } = data; + return ( +
+ + + + {energy} / {maxEnergy} Units + + + + + {dispenseAmounts.map((a, i) => ( + +
+ ); +}; + +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 ( +
+ + {chemicals.map((c, i) => ( + +
+ ); +}; + +const ChemDispenserBeaker = (properties, context) => { + const { act, data } = useBackend(context); + const { + isBeakerLoaded, + beakerCurrentVolume, + beakerMaxVolume, + beakerContents = [], + } = data; + return ( +
+ {!!isBeakerLoaded && ( + + {beakerCurrentVolume} / {beakerMaxVolume} units + + )} +
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/ChemHeater.js b/tgui/packages/tgui/interfaces/ChemHeater.js new file mode 100644 index 00000000000..e7ea7a6ace9 --- /dev/null +++ b/tgui/packages/tgui/interfaces/ChemHeater.js @@ -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 ( + + + + + + + ); +}; + +const ChemHeaterSettings = (_properties, context) => { + const { act, data } = useBackend(context); + const { + targetTemp, + targetTempReached, + autoEject, + isActive, + currentTemp, + isBeakerLoaded, + } = data; + return ( +
+
+ ); +}; + +const ChemHeaterBeaker = (_properties, context) => { + const { act, data } = useBackend(context); + const { + isBeakerLoaded, + beakerCurrentVolume, + beakerMaxVolume, + beakerContents, + } = data; + return ( +
+ + {beakerCurrentVolume} / {beakerMaxVolume} units + +
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/ChemMaster.js b/tgui/packages/tgui/interfaces/ChemMaster.js new file mode 100644 index 00000000000..d0a70d2b4ce --- /dev/null +++ b/tgui/packages/tgui/interfaces/ChemMaster.js @@ -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 ( +
+ + + + {result.name} + + + {(result.desc || "").length > 0 ? result.desc : "N/A"} + + {result.blood_type && ( + + + {result.blood_type} + + + {result.blood_dna} + + + )} + {!data.condi && ( +
+ ); +}; + +export const ChemMaster = (props, context) => { + const { data } = useBackend(context); + const { + condi, + beaker, + beaker_reagents = [], + buffer_reagents = [], + mode, + } = data; + return ( + + + + 0} + /> + + 0} + /> + + + + ); +}; + +const ChemMasterBeaker = (props, context) => { + const { act } = useBackend(context); + const { + beaker, + beakerReagents, + bufferNonEmpty, + } = props; + return ( +
act('eject')} + /> + :
+ ); +}; + +const ChemMasterBuffer = (props, context) => { + const { act } = useBackend(context); + const { + mode, + bufferReagents = [], + } = props; + return ( +
+ Transferring to  +
+ ); +}; + +const ChemMasterProduction = (props, context) => { + const { act } = useBackend(context); + if (!props.bufferNonEmpty) { + return ( +
+ + +
+ Buffer is empty. +
+
+
+ ); + } + + return ( +
+ {!props.isCondiment ? ( + + ) : ( + + )} +
+ ); +}; + +const ChemMasterProductionChemical = (props, context) => { + const { act, data } = useBackend(context); + return ( + + + + + + + + + ); +}; + +const ChemMasterProductionCondiment = (props, context) => { + const { act } = useBackend(context); + return ( + + + )}> + {hasOccupant ? ( + + + {occupant.name || "Unknown"} + + + 0 ? 'good' : 'average'}> + + + + + {statNames[occupant.stat][1]} + + + + {' K'} + + + {(damageTypes.map(damageType => ( + + + + + + )))} + + ) : ( + + +
+ No occupant detected. +
+
+ )} + +
act('ejectBeaker')} + disabled={!isBeakerLoaded}> + Eject Beaker + + )}> + + + + + + K + + + + + + + + + + + + +
+
+ ); +}; + +const CryoBeaker = (props, context) => { + const { act, data } = useBackend(context); + const { + isBeakerLoaded, + beakerLabel, + beakerVolume, + } = data; + if (isBeakerLoaded) { + return ( + + {beakerLabel + ? beakerLabel + : ( + + No label + + )} + + {beakerVolume ? ( + Math.round(v) + " units remaining"} + /> + ) : "Beaker is empty"} + + + ); + } else { + return ( + + No beaker loaded + + ); + } +}; diff --git a/tgui/packages/tgui/interfaces/DNAModifier.js b/tgui/packages/tgui/interfaces/DNAModifier.js new file mode 100644 index 00000000000..111aa59c8a6 --- /dev/null +++ b/tgui/packages/tgui/interfaces/DNAModifier.js @@ -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 = ; + } + return ( + + + {radiatingModal} + + + + + + ); +}; + +const DNAModifierOccupant = (props, context) => { + const { act, data } = useBackend(context); + const { + locked, + hasOccupant, + occupant, + } = data; + return ( +
+ + Door Lock: + +
+ ); +}; + +const DNAModifierMain = (props, context) => { + const { act, data } = useBackend(context); + const { + selectedMenuKey, + hasOccupant, + occupant, + } = data; + if (!hasOccupant) { + return ( +
+ + +
+ No occupant in DNA modifier. +
+
+
+ ); + } else if (context.isDNAInvalid) { + return ( +
+ + +
+ No operation possible on this subject. +
+
+
+ ); + } + let body; + if (selectedMenuKey === "ui") { + body = ( + + + + + ); + } else if (selectedMenuKey === "se") { + body = ( + + + + + ); + } else if (selectedMenuKey === "buffer") { + body = ; + } else if (selectedMenuKey === "rejuvenators") { + body = ; + } + return ( +
+ + {operations.map((op, i) => ( + act('selectMenuKey', { key: op[0] })}> + + {op[1]} + + ))} + + {body} +
+ ); +}; + +const DNAModifierMainUI = (props, context) => { + const { act, data } = useBackend(context); + const { + selectedUIBlock, + selectedUISubBlock, + selectedUITarget, + occupant, + } = data; + return ( +
+ + + + value.toString(16).toUpperCase()} + ml="0" + onChange={(e, val) => act('changeUITarget', { value: val })} + /> + + +
+ ); +}; + +const DNAModifierMainSE = (props, context) => { + const { act, data } = useBackend(context); + const { + selectedSEBlock, + selectedSESubBlock, + occupant, + } = data; + return ( +
+ +
+ ); +}; + +const DNAModifierMainRadiationEmitter = (props, context) => { + const { act, data } = useBackend(context); + const { + radiationIntensity, + radiationDuration, + } = data; + return ( +
+ + + act('radiationIntensity', { value: val })} + /> + + + act('radiationDuration', { value: val })} + /> + + +
+ ); +}; + +const DNAModifierMainBuffers = (props, context) => { + const { act, data } = useBackend(context); + const { + buffers, + } = data; + let bufferElements = buffers.map((buffer, i) => ( + + )); + return ( + +
+ {bufferElements} +
+ +
+ ); +}; + +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 ( + +
+ act('bufferOption', { + option: 'clear', + id: id, + })} + /> +
+
+ ); +}; + +const DNAModifierMainBuffersDisk = (props, context) => { + const { act, data } = useBackend(context); + const { + hasDisk, + disk, + } = data; + return ( +
+ act('wipeDisk')} + /> +
+ ); +}; + +const DNAModifierMainRejuvenators = (props, context) => { + const { act, data } = useBackend(context); + const { + isBeakerLoaded, + beakerVolume, + beakerLabel, + } = data; + return ( +
act('ejectBeaker')} + /> + }> + {isBeakerLoaded ? ( + + + {rejuvenatorsDoses.map((a, i) => ( +
+ ); +}; + +const DNAModifierIrradiating = (props, context) => { + return ( + +
+ +

+ +  Irradiating occupant  + +

+
+ +

+ For {props.duration} second{props.duration === 1 ? "" : "s"} +

+
+
+ ); +}; + +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( + + + ))} + + ); + } else if (type === "boolean") { + modalFooter = ( + +