Merge pull request #8471 from ShadowLarkens/tgui_med

Medical TGUI
This commit is contained in:
Novacat
2020-07-25 14:54:45 -04:00
committed by GitHub
67 changed files with 8138 additions and 3580 deletions
@@ -443,3 +443,48 @@
/datum/asset/nanoui/send(client)
send_asset_list(client, common)
//Pill sprites for UIs
/datum/asset/chem_master
var/assets = list()
var/verify = FALSE
/datum/asset/chem_master/register()
for(var/i = 1 to 24)
assets["pill[i].png"] = icon('icons/obj/chemical.dmi', "pill[i]")
for(var/i = 1 to 4)
assets["bottle-[i].png"] = icon('icons/obj/chemical.dmi', "bottle-[i]")
for(var/asset_name in assets)
register_asset(asset_name, assets[asset_name])
/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)
// VOREStation Add
/datum/asset/cloning/resleeving
/datum/asset/cloning/resleeving/register()
// This intentionally does not call the parent. Duplicate assets are not allowed.
assets["sleeve_empty.gif"] = icon('icons/obj/machines/implantchair.dmi', "implantchair")
assets["sleeve_occupied.gif"] = icon('icons/obj/machines/implantchair.dmi', "implantchair_on")
assets["synthprinter.gif"] = icon('icons/obj/machines/synthpod.dmi', "pod_0")
assets["synthprinter_working.gif"] = icon('icons/obj/machines/synthpod.dmi', "pod_1")
for(var/asset_name in assets)
register_asset(asset_name, assets[asset_name])
// VOREStation Add End
+1 -1
View File
@@ -118,7 +118,7 @@
return get_turf(src)
/mob/proc/say_test(var/text)
/proc/say_test(var/text)
var/ending = copytext(text, length(text))
if(ending == "?")
return "1"
@@ -140,6 +140,13 @@
crash_with("[my_atom] attempted to add a reagent called '[id]' which doesn't exist. ([usr])")
return 0
/datum/reagents/proc/isolate_reagent(reagent)
for(var/A in reagent_list)
var/datum/reagent/R = A
if(R.id != reagent)
del_reagent(R.id)
update_total()
/datum/reagents/proc/remove_reagent(var/id, var/amount, var/safety = 0)
if(!isnum(amount))
return 0
+456 -276
View File
@@ -2,6 +2,13 @@
#define LIQUID 2
#define GAS 3
#define MAX_PILL_SPRITE 24 //max icon state of the pill sprites
#define MAX_BOTTLE_SPRITE 4 //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 60 // Max amount of units in a pill
#define MAX_UNITS_PER_PATCH 60 // Max amount of units in a patch
#define MAX_CUSTOM_NAME_LEN 64 // Max length of a custom pill/condiment/whatever
@@ -24,11 +31,11 @@
var/condi = 0
var/useramount = 15 // Last used amount
var/pillamount = 10
var/bottlesprite = "1"
var/pillsprite = "1"
var/list/bottle_styles
var/bottlesprite = 1
var/pillsprite = 1
var/max_pill_count = 20
var/tab = "home"
var/analyze_data[0]
var/printing = FALSE
flags = OPENCONTAINER
clicksound = "button"
@@ -48,6 +55,9 @@
qdel(src)
return
/obj/machinery/chem_master/update_icon()
icon_state = "mixer[beaker ? "1" : "0"]"
/obj/machinery/chem_master/attackby(var/obj/item/weapon/B as obj, var/mob/user as mob)
if(istype(B, /obj/item/weapon/reagent_containers/glass) || istype(B, /obj/item/weapon/reagent_containers/food))
@@ -59,7 +69,7 @@
user.drop_item()
B.loc = src
to_chat(user, "You add \the [B] to the machine.")
icon_state = "mixer1"
update_icon()
else if(istype(B, /obj/item/weapon/storage/pill_bottle))
@@ -85,247 +95,395 @@
if(stat & BROKEN)
return
user.set_machine(src)
ui_interact(user)
tgui_interact(user)
/obj/machinery/chem_master/ui_assets(mob/user)
return list(
get_asset_datum(/datum/asset/chem_master),
)
/obj/machinery/chem_master/tgui_interact(mob/user, datum/tgui/ui = null)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, "ChemMaster", name)
ui.open()
/**
* Display the NanoUI window for the chem master.
*
* See NanoUI documentation for details.
*/
/obj/machinery/chem_master/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
user.set_machine(src)
/obj/machinery/chem_master/tgui_data(mob/user)
var/list/data = list()
data["tab"] = tab
data["condi"] = condi
data["loaded_pill_bottle"] = !!loaded_pill_bottle
if(loaded_pill_bottle)
data["pillBottle"] = list("total" = loaded_pill_bottle.contents.len, "max" = loaded_pill_bottle.max_storage_space)
else
data["pillBottle"] = null
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.max_storage_space
data["beaker"] = !!beaker
if(beaker)
var/datum/reagents/R = beaker.reagents
var/ui_reagent_beaker_list[0]
for(var/datum/reagent/G in R.reagent_list)
ui_reagent_beaker_list[++ui_reagent_beaker_list.len] = list("name" = G.name, "volume" = G.volume, "description" = G.description, "id" = G.id)
var/list/beaker_reagents_list = list()
data["beaker_reagents"] = beaker_reagents_list
for(var/datum/reagent/R in beaker.reagents.reagent_list)
beaker_reagents_list[++beaker_reagents_list.len] = list("name" = R.name, "volume" = R.volume, "description" = R.description, "id" = R.id)
data["beaker"] = list("total_volume" = R.total_volume, "reagent_list" = ui_reagent_beaker_list)
else
data["beaker"] = null
if(reagents.total_volume)
var/ui_reagent_list[0]
for(var/datum/reagent/N in reagents.reagent_list)
ui_reagent_list[++ui_reagent_list.len] = list("name" = N.name, "volume" = N.volume, "description" = N.description, "id" = N.id)
data["reagents"] = list("total_volume" = reagents.total_volume, "reagent_list" = ui_reagent_list)
else
data["reagents"] = null
var/list/buffer_reagents_list = list()
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)
data["pillsprite"] = pillsprite
data["bottlesprite"] = bottlesprite
data["mode"] = mode
data["printing"] = printing
if(analyze_data)
data["analyzeData"] = list("name" = analyze_data["name"], "desc" = analyze_data["desc"], "blood_type" = analyze_data["blood_type"], "blood_DNA" = analyze_data["blood_DNA"])
else
data["analyzeData"] = null
// Transfer modal information if there is one
data["modal"] = tgui_modal_data(src)
data["pillSprite"] = pillsprite
data["bottleSprite"] = bottlesprite
return data
var/P[24] //how many pill sprites there are. Sprites are taken from chemical.dmi and can be found in nano/images/pill.png
for(var/i = 1 to P.len)
P[i] = i
data["pillSpritesAmount"] = P
/**
* 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
data["bottleSpritesAmount"] = list(1, 2, 3, 4) //how many bottle sprites there are. Sprites are taken from chemical.dmi and can be found in nano/images/pill.png
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"]
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open)
if (!ui)
ui = new(user, src, ui_key, "chem_master.tmpl", src.name, 575, 400)
ui.set_initial_data(data)
ui.open()
ui.set_auto_update(5)
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")
var/list/choices = list()
for(var/i = 1 to MAX_BOTTLE_SPRITE)
choices += "bottle-[i].png"
tgui_modal_bento(src, id, "Please select the new style for bottles:", null, arguments, bottlesprite, choices)
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/weapon/reagent_containers/pill/P = new(loc)
P.name = "[answer] pack"
P.desc = "A small condiment pack. The label says it contains [answer]."
P.icon_state = "bouilloncube"//Reskinned monkey cube
reagents.trans_to_obj(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
/obj/machinery/chem_master/Topic(href, href_list)
if(stat & (BROKEN|NOPOWER)) return
if(usr.stat || usr.restrained()) return
if(!in_range(src, usr)) return
if(!length(answer))
answer = reagents.get_master_reagent_name()
var/amount_per_pill = CLAMP(reagents.total_volume / count, 0, MAX_UNITS_PER_PILL)
while(count--)
if(reagents.total_volume <= 0)
to_chat(usr, "<span class='notice'>Not enough reagents to create these pills!</span>")
return
src.add_fingerprint(usr)
var/obj/item/weapon/reagent_containers/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]"
if(P.icon_state in list("pill1", "pill2", "pill3", "pill4")) // if using greyscale, take colour from reagent
P.color = reagents.get_color()
reagents.trans_to_obj(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.max_storage_space)
P.forceMove(loaded_pill_bottle)
if("create_pill_multiple")
if(condi || !reagents.total_volume)
return
tgui_act("modal_open", list("id" = "create_pill", "arguments" = list("num" = answer)), ui, state)
if("change_pill_style")
var/new_style = CLAMP(text2num(answer) || 0, 0, MAX_PILL_SPRITE)
if(!new_style)
return
pillsprite = new_style
if("create_patch")
if(condi || !reagents.total_volume)
return
var/count = CLAMP(round(text2num(arguments["num"]) || 0), 0, MAX_MULTI_AMOUNT)
if(!count)
return
if(!length(answer))
answer = reagents.get_master_reagent_name()
var/amount_per_patch = CLAMP(reagents.total_volume / count, 0, MAX_UNITS_PER_PATCH)
// var/is_medical_patch = chemical_safety_check(reagents)
while(count--)
if(reagents.total_volume <= 0)
to_chat(usr, "<span class='notice'>Not enough reagents to create these patches!</span>")
return
var/obj/item/weapon/reagent_containers/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_obj(P, amount_per_patch)
// if(is_medical_patch)
// P.instant_application = TRUE
// P.icon_state = "bandaid_med"
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/weapon/reagent_containers/glass/bottle/P = new(loc)
P.name = "[answer] bottle"
P.pixel_x = rand(-7, 7) // random position
P.pixel_y = rand(-7, 7)
P.icon_state = "bottle-[bottlesprite]" || "bottle-1"
reagents.trans_to_obj(P, 60)
P.update_icon()
if("change_bottle_style")
var/new_style = CLAMP(text2num(answer) || 0, 0, MAX_BOTTLE_SPRITE)
if(!new_style)
return
bottlesprite = new_style
else
return FALSE
else
return FALSE
/obj/machinery/chem_master/tgui_act(action, params, datum/tgui/ui, datum/tgui_state/state)
if(..())
return
if(tgui_act_modal(action, params, ui, state))
return TRUE
add_fingerprint(usr)
usr.set_machine(src)
if(href_list["tab_select"])
tab = href_list["tab_select"]
if (href_list["ejectp"])
if(loaded_pill_bottle)
loaded_pill_bottle.forceMove(get_turf(src))
if(Adjacent(usr))
usr.put_in_hands(loaded_pill_bottle)
loaded_pill_bottle = null
if(beaker)
var/datum/reagents/R = beaker.reagents
if (tab == "analyze")
analyze_data["name"] = href_list["name"]
analyze_data["desc"] = href_list["desc"]
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
analyze_data["name"] = G.name
analyze_data["blood_type"] = G.data["blood_type"]
analyze_data["blood_DNA"] = G.data["blood_DNA"]
else if (href_list["add"])
if(href_list["amount"])
var/id = href_list["add"]
var/amount = CLAMP((text2num(href_list["amount"])), 0, 200)
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 = CLAMP(useramount, 0, 200)
src.Topic(null, list("amount" = "[useramount]", "add" = "[id]"))
else if (href_list["remove"])
if(href_list["amount"])
var/id = href_list["remove"]
var/amount = CLAMP((text2num(href_list["amount"])), 0, 200)
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 = CLAMP(useramount, 0, 200)
src.Topic(null, list("amount" = "[useramount]", "remove" = "[id]"))
else if (href_list["toggle"])
. = TRUE
switch(action)
if("toggle")
mode = !mode
else if (href_list["eject"])
if(beaker)
beaker.forceMove(get_turf(src))
if(Adjacent(usr)) // So the AI doesn't get a beaker somehow.
usr.put_in_hands(beaker)
beaker = null
reagents.clear_reagents()
icon_state = "mixer0"
else if (href_list["createpill"] || href_list["createpill_multiple"])
var/count = 1
if(reagents.total_volume/count < 1) //Sanity checking.
if("ejectp")
if(loaded_pill_bottle)
loaded_pill_bottle.forceMove(loc)
loaded_pill_bottle = null
if("print")
if(printing || condi)
return
if (href_list["createpill_multiple"])
count = input("Select the number of pills to make.", "Max [max_pill_count]", pillamount) as null|num
if(!count) //Covers 0 and cancel
return
count = CLAMP(round(count), 1, max_pill_count) // Fix decimals input and clamp to reasonable amounts
if(reagents.total_volume/count < 1) //Sanity checking.
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/amount_per_pill = reagents.total_volume/count
if (amount_per_pill > 60) amount_per_pill = 60
var/datum/reagent/R = reagent_list[idx]
var/pill_cube = "pill"
if(condi)//For the condimaster
pill_cube = "cube"
printing = TRUE
visible_message("<span class='notice'>[src] rattles and prints out a sheet of paper.</span>")
// playsound(loc, 'sound/goonstation/machines/printer_dotmatrix.ogg', 50, 1)
var/obj/item/weapon/paper/P = new /obj/item/weapon/paper(loc)
P.info = "<center><b>Chemical Analysis</b></center><br>"
P.info += "<b>Time of analysis:</b> [worldtime2stationtime(world.time)]<br><br>"
P.info += "<b>Chemical name:</b> [R.name]<br>"
if(istype(R, /datum/reagent/blood))
var/datum/reagent/blood/B = R
P.info += "<b>Description:</b> N/A<br><b>Blood Type:</b> [B.data["blood_type"]]<br><b>DNA:</b> [B.data["blood_DNA"]]"
else
pill_cube = "pill"
P.info += "<b>Description:</b> [R.description]"
P.info += "<br><br><b>Notes:</b><br>"
P.name = "Chemical Analysis - [R.name]"
spawn(50)
printing = FALSE
else
. = FALSE
var/name = sanitizeSafe(input(usr,"Name:","Name your [pill_cube]!","[reagents.get_master_reagent_name()] ([amount_per_pill]u)") as null|text, MAX_NAME_LEN)
if(. || !beaker)
return
if(!name) //Blank name (sanitized to nothing, or left empty) or cancel
. = TRUE
var/datum/reagents/R = beaker.reagents
switch(action)
if("add")
var/id = params["id"]
var/amount = text2num(params["amount"])
if(!id || !amount)
return
if(reagents.total_volume/count < 1) //Sanity checking.
R.trans_id_to(src, id, amount)
if("remove")
var/id = params["id"]
var/amount = text2num(params["amount"])
if(!id || !amount)
return
while(count-- > 0) // Will definitely eventually stop.
var/obj/item/weapon/reagent_containers/pill/P = new/obj/item/weapon/reagent_containers/pill(src.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)
if(!condi) //If normal
P.icon_state = "pill"+pillsprite
else //If condi is on
P.icon_state = "bouilloncube"//Reskinned monkey cube
P.desc = "A dissolvable cube."
if(P.icon_state in list("pill1", "pill2", "pill3", "pill4")) // if using greyscale, take colour from reagent
P.color = reagents.get_color()
reagents.trans_to_obj(P,amount_per_pill)
if(src.loaded_pill_bottle)
if(loaded_pill_bottle.contents.len < loaded_pill_bottle.max_storage_space)
P.loc = loaded_pill_bottle
else if (href_list["createbottle"])
if(!condi)
var/name = sanitizeSafe(input(usr,"Name:","Name your bottle!",reagents.get_master_reagent_name()), MAX_NAME_LEN)
var/obj/item/weapon/reagent_containers/glass/bottle/P = new/obj/item/weapon/reagent_containers/glass/bottle(src.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 = "bottle-"+bottlesprite
reagents.trans_to_obj(P,60)
P.update_icon()
if(mode)
reagents.trans_id_to(beaker, id, amount)
else
var/obj/item/weapon/reagent_containers/food/condiment/P = new/obj/item/weapon/reagent_containers/food/condiment(src.loc)
reagents.trans_to_obj(P,50)
else if (href_list["createpatch"])
if(reagents.total_volume < 1) //Sanity checking.
reagents.remove_reagent(id, amount)
if("eject")
if(!beaker)
return
var/name = sanitizeSafe(input(usr,"Name:","Name your patch!","[reagents.get_master_reagent_name()] ([round(reagents.total_volume)]u)") as null|text, MAX_NAME_LEN)
if(!name) //Blank name (sanitized to nothing, or left empty) or cancel
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/weapon/reagent_containers/food/condiment/P = new(loc)
reagents.trans_to_obj(P, 50)
else
return FALSE
if(reagents.total_volume < 1) //Sanity checking.
return
var/obj/item/weapon/reagent_containers/pill/patch/P = new/obj/item/weapon/reagent_containers/pill/patch(src.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)
/obj/machinery/chem_master/attack_ai(mob/user)
return attack_hand(user)
reagents.trans_to_obj(P, 60)
if(src.loaded_pill_bottle)
if(loaded_pill_bottle.contents.len < loaded_pill_bottle.max_storage_space)
P.loc = loaded_pill_bottle
/obj/machinery/chem_master/proc/isgoodnumber(num)
if(isnum(num))
if(num > 200)
num = 200
else if(num < 0)
num = 1
return num
else
return FALSE
else if(href_list["pill_sprite"])
pillsprite = href_list["pill_sprite"]
else if(href_list["bottle_sprite"])
bottlesprite = href_list["bottle_sprite"]
SSnanoui.update_uis(src)
/obj/machinery/chem_master/attack_ai(mob/user as mob)
return src.attack_hand(user)
// /obj/machinery/chem_master/proc/chemical_safety_check(datum/reagents/R)
// var/all_safe = TRUE
// for(var/datum/reagent/A in R.reagent_list)
// if(!GLOB.safe_chem_list.Find(A.id))
// all_safe = FALSE
// return all_safe
/obj/machinery/chem_master/condimaster
name = "CondiMaster 3000"
@@ -365,11 +523,41 @@
/obj/item/stack/material/glass/phoronglass = list("platinum", "silicon", "silicon", "silicon"), //5 platinum, 15 silicon,
)
var/static/radial_examine = image(icon = 'icons/mob/radial.dmi', icon_state = "radial_examine")
var/static/radial_eject = image(icon = 'icons/mob/radial.dmi', icon_state = "radial_eject")
var/static/radial_grind = image(icon = 'icons/mob/radial.dmi', icon_state = "radial_grind")
// var/static/radial_juice = image(icon = 'icons/mob/radial.dmi', icon_state = "radial_juice")
// var/static/radial_mix = image(icon = 'icons/mob/radial.dmi', icon_state = "radial_mix")
/obj/machinery/reagentgrinder/Initialize()
. = ..()
beaker = new /obj/item/weapon/reagent_containers/glass/beaker/large(src)
default_apply_parts()
/obj/machinery/reagentgrinder/examine(mob/user)
. = ..()
if(!in_range(user, src) && !issilicon(user) && !isobserver(user))
. += "<span class='warning'>You're too far away to examine [src]'s contents and display!</span>"
return
if(inuse)
. += "<span class='warning'>\The [src] is operating.</span>"
return
if(beaker || length(holdingitems))
. += "<span class='notice'>\The [src] contains:</span>"
if(beaker)
. += "<span class='notice'>- \A [beaker].</span>"
for(var/i in holdingitems)
var/obj/item/O = i
. += "<span class='notice'>- \A [O.name].</span>"
if(!(stat & (NOPOWER|BROKEN)))
. += "<span class='notice'>The status display reads:</span>\n"
if(beaker)
for(var/datum/reagent/R in beaker.reagents.reagent_list)
. += "<span class='notice'>- [R.volume] units of [R.name].</span>"
/obj/machinery/reagentgrinder/update_icon()
icon_state = "juicer"+num2text(!isnull(beaker))
return
@@ -450,93 +638,66 @@
user.remove_from_mob(O)
O.loc = src
holdingitems += O
src.updateUsrDialog()
return 0
/obj/machinery/reagentgrinder/AltClick(mob/user)
. = ..()
if(user.incapacitated() || !Adjacent(user))
return
replace_beaker(user)
/obj/machinery/reagentgrinder/attack_hand(mob/user as mob)
user.set_machine(src)
interact(user)
/obj/machinery/reagentgrinder/interact(mob/user as mob) // The microwave Menu
var/is_chamber_empty = 0
var/is_beaker_ready = 0
var/processing_chamber = ""
var/beaker_contents = ""
var/dat = ""
/obj/machinery/reagentgrinder/interact(mob/user as mob) // The microwave Menu //I am reasonably certain that this is not a microwave
if(inuse || user.incapacitated())
return
if(!inuse)
for (var/obj/item/O in holdingitems)
processing_chamber += "\A [O.name]<BR>"
var/list/options = list()
if (!processing_chamber)
is_chamber_empty = 1
processing_chamber = "Nothing."
if (!beaker)
beaker_contents = "<B>No beaker attached.</B><br>"
else
is_beaker_ready = 1
beaker_contents = "<B>The beaker contains:</B><br>"
var/anything = 0
for(var/datum/reagent/R in beaker.reagents.reagent_list)
anything = 1
beaker_contents += "[R.volume] - [R.name]<br>"
if(!anything)
beaker_contents += "Nothing<br>"
if(beaker || length(holdingitems))
options["eject"] = radial_eject
if(isAI(user))
if(stat & NOPOWER)
return
options["examine"] = radial_examine
dat = {"
<b>Processing chamber contains:</b><br>
[processing_chamber]<br>
[beaker_contents]<hr>
"}
if (is_beaker_ready && !is_chamber_empty && !(stat & (NOPOWER|BROKEN)))
dat += "<A href='?src=\ref[src];action=grind'>Process the reagents</a><BR>"
if(holdingitems && holdingitems.len > 0)
dat += "<A href='?src=\ref[src];action=eject'>Eject the reagents</a><BR>"
if (beaker)
dat += "<A href='?src=\ref[src];action=detach'>Detach the beaker</a><BR>"
// if there is no power or it's broken, the procs will fail but the buttons will still show
if(length(holdingitems))
options["grind"] = radial_grind
var/choice
if(length(options) < 1)
return
if(length(options) == 1)
for(var/key in options)
choice = key
else
dat += "Please wait..."
user << browse("<HEAD><TITLE>All-In-One Grinder</TITLE></HEAD><TT>[dat]</TT>", "window=reagentgrinder")
onclose(user, "reagentgrinder")
return
choice = show_radial_menu(user, src, options, require_near = !issilicon(user))
/obj/machinery/reagentgrinder/Topic(href, href_list)
if(..())
// post choice verification
if(inuse || (isAI(user) && stat & NOPOWER) || user.incapacitated())
return
usr.set_machine(src)
switch(href_list["action"])
if ("grind")
grind()
switch(choice)
if("eject")
eject()
if ("detach")
detach()
src.updateUsrDialog()
return
eject(user)
if("grind")
grind(user)
if("examine")
examine(user)
/obj/machinery/reagentgrinder/proc/detach()
if (usr.stat != 0)
/obj/machinery/reagentgrinder/proc/eject(mob/user)
if(user.incapacitated())
return
if (!beaker)
return
beaker.loc = src.loc
beaker = null
update_icon()
/obj/machinery/reagentgrinder/proc/eject()
if (usr.stat != 0)
return
if (!holdingitems || holdingitems.len == 0)
return
for(var/obj/item/O in holdingitems)
O.loc = src.loc
holdingitems -= O
holdingitems.Cut()
if(beaker)
replace_beaker(user)
/obj/machinery/reagentgrinder/proc/grind()
@@ -554,7 +715,6 @@
// Reset the machine.
spawn(60)
inuse = 0
interact(usr)
// Process.
for (var/obj/item/O in holdingitems)
@@ -581,13 +741,26 @@
continue
if(O.reagents)
O.reagents.trans_to(beaker, min(O.reagents.total_volume, remaining_volume))
O.reagents.trans_to_obj(beaker, min(O.reagents.total_volume, remaining_volume))
if(O.reagents.total_volume == 0)
holdingitems -= O
qdel(O)
if (beaker.reagents.total_volume >= beaker.reagents.maximum_volume)
break
/obj/machinery/reagentgrinder/proc/replace_beaker(mob/living/user, obj/item/weapon/reagent_containers/new_beaker)
if(!user)
return FALSE
if(beaker)
if(!user.incapacitated() && Adjacent(user))
user.put_in_hands(beaker)
else
beaker.forceMove(drop_location())
beaker = null
if(new_beaker)
beaker = new_beaker
update_icon()
return TRUE
///////////////
///////////////
@@ -653,4 +826,11 @@
to_chat(user, span("notice", "Scanning of \the [I] complete."))
analyzing = FALSE
update_icon()
return
return
#undef MAX_PILL_SPRITE
#undef MAX_BOTTLE_SPRITE
#undef MAX_MULTI_AMOUNT
#undef MAX_UNITS_PER_PILL
#undef MAX_UNITS_PER_PATCH
#undef MAX_CUSTOM_NAME_LEN
+56 -46
View File
@@ -68,12 +68,12 @@
C.loc = src
cartridges[C.label] = C
cartridges = sortAssoc(cartridges)
SSnanoui.update_uis(src)
SStgui.update_uis(src)
/obj/machinery/chemical_dispenser/proc/remove_cartridge(label)
. = cartridges[label]
cartridges -= label
SSnanoui.update_uis(src)
SStgui.update_uis(src)
/obj/machinery/chemical_dispenser/attackby(obj/item/weapon/W, mob/user)
if(W.is_wrench())
@@ -119,25 +119,26 @@
user.drop_from_inventory(RC)
RC.loc = src
to_chat(user, "<span class='notice'>You set \the [RC] on \the [src].</span>")
SSnanoui.update_uis(src) // update all UIs attached to src
else
return ..()
/obj/machinery/chemical_dispenser/ui_interact(mob/user, ui_key = "main",var/datum/nanoui/ui = null, var/force_open = 1)
if(stat & (BROKEN|NOPOWER)) return
if(user.stat || user.restrained()) return
/obj/machinery/chemical_dispenser/tgui_interact(mob/user, datum/tgui/ui = null)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, "ChemDispenser", ui_title) // 390, 655
ui.open()
// this is the data which will be sent to the ui
/obj/machinery/chemical_dispenser/tgui_data(mob/user)
var/data[0]
data["amount"] = amount
data["isBeakerLoaded"] = container ? 1 : 0
data["glass"] = accept_drinking
var beakerD[0]
var/beakerContents[0]
if(container && container.reagents && container.reagents.reagent_list.len)
for(var/datum/reagent/R in container.reagents.reagent_list)
beakerD[++beakerD.len] = list("name" = R.name, "volume" = R.volume)
data["beakerContents"] = beakerD
beakerContents.Add(list(list("name" = R.name, "id" = R.id, "volume" = R.volume))) // list in a list because Byond merges the first list...
data["beakerContents"] = beakerContents
if(container)
data["beakerCurrentVolume"] = container.reagents.total_volume
@@ -146,50 +147,59 @@
data["beakerCurrentVolume"] = null
data["beakerMaxVolume"] = null
var chemicals[0]
var/chemicals[0]
for(var/label in cartridges)
var/obj/item/weapon/reagent_containers/chem_disp_cartridge/C = cartridges[label]
chemicals[++chemicals.len] = list("label" = label, "amount" = C.reagents.total_volume)
chemicals.Add(list(list("title" = label, "id" = label, "amount" = C.reagents.total_volume))) // list in a list because Byond merges the first list...
data["chemicals"] = chemicals
return data
// update the ui if it exists, returns null if no ui is passed/found
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open)
if(!ui)
ui = new(user, src, ui_key, "chem_disp.tmpl", ui_title, 390, 680)
ui.set_initial_data(data)
ui.open()
/obj/machinery/chemical_dispenser/tgui_act(action, params)
if(..())
return
/obj/machinery/chemical_dispenser/Topic(href, href_list)
if(stat & (NOPOWER|BROKEN))
return 0 // don't update UIs attached to this object
. = TRUE
switch(action)
if("amount")
amount = clamp(round(text2num(params["amount"]), 1), 0, 120) // round to nearest 1 and clamp 0 - 120
if("dispense")
var/label = params["reagent"]
if(cartridges[label] && container && container.is_open_container())
var/obj/item/weapon/reagent_containers/chem_disp_cartridge/C = cartridges[label]
playsound(src, 'sound/machines/reagent_dispense.ogg', 25, 1)
C.reagents.trans_to(container, amount)
if("remove")
var/amount = text2num(params["amount"])
if(!container || !amount)
return
var/datum/reagents/R = container.reagents
var/id = params["reagent"]
if(amount > 0)
R.remove_reagent(id, amount)
else if(amount == -1) // Isolate
R.isolate_reagent(id)
if("ejectBeaker")
if(container)
container.forceMove(get_turf(src))
if(href_list["amount"])
amount = round(text2num(href_list["amount"]), 1) // round to nearest 1
amount = max(0, min(120, amount)) // Since the user can actually type the commands himself, some sanity checking
if(Adjacent(usr)) // So the AI doesn't get a beaker somehow.
usr.put_in_hands(container)
else if(href_list["dispense"])
var/label = href_list["dispense"]
if(cartridges[label] && container && container.is_open_container())
var/obj/item/weapon/reagent_containers/chem_disp_cartridge/C = cartridges[label]
playsound(src, 'sound/machines/reagent_dispense.ogg', 25, 1)
C.reagents.trans_to(container, amount)
else if(href_list["ejectBeaker"])
if(container)
container.forceMove(get_turf(src))
if(Adjacent(usr)) // So the AI doesn't get a beaker somehow.
usr.put_in_hands(container)
container = null
container = null
else
return FALSE
add_fingerprint(usr)
return 1 // update UIs attached to this object
/obj/machinery/chemical_dispenser/attack_ai(mob/user as mob)
src.attack_hand(user)
/obj/machinery/chemical_dispenser/attack_hand(mob/user as mob)
/obj/machinery/chemical_dispenser/attack_ghost(mob/user)
if(stat & BROKEN)
return
ui_interact(user)
tgui_interact(user)
/obj/machinery/chemical_dispenser/attack_ai(mob/user)
attack_hand(user)
/obj/machinery/chemical_dispenser/attack_hand(mob/user)
if(stat & BROKEN)
return
tgui_interact(user)
+342 -242
View File
@@ -1,3 +1,7 @@
#define MENU_MAIN 1
#define MENU_BODY 2
#define MENU_MIND 3
/obj/machinery/computer/transhuman/resleeving
name = "resleeving control console"
catalogue_data = list(/datum/category_item/catalogue/information/organization/khi,
@@ -7,19 +11,25 @@
light_color = "#315ab4"
circuit = /obj/item/weapon/circuitboard/resleeving_control
req_access = list(access_heads) //Only used for record deletion right now.
var/list/pods = list() //Linked grower pods.
var/list/spods = list()
var/list/sleevers = list() //Linked resleeving booths.
var/temp = ""
var/menu = 1 //Which menu screen to display
var/list/pods = null //Linked grower pods.
var/list/spods = null
var/list/sleevers = null //Linked resleeving booths.
var/list/temp = null
var/menu = MENU_MAIN //Which menu screen to display
var/datum/transhuman/body_record/active_br = null
var/datum/transhuman/mind_record/active_mr = null
var/organic_capable = 1
var/synthetic_capable = 1
var/obj/item/weapon/disk/transcore/disk
var/obj/machinery/clonepod/transhuman/selected_pod
var/obj/machinery/transhuman/synthprinter/selected_printer
var/obj/machinery/transhuman/resleever/selected_sleever
/obj/machinery/computer/transhuman/resleeving/Initialize()
. = ..()
pods = list()
spods = list()
sleevers = list()
updatemodules()
/obj/machinery/computer/transhuman/resleeving/Destroy()
@@ -85,7 +95,6 @@
user.unEquip(W)
W.forceMove(get_turf(src)) // Drop on top of us
active_br = new /datum/transhuman/body_record(brDisk.stored) // Loads a COPY!
menu = 4
to_chat(user, "<span class='notice'>\The [src] loads the body record from \the [W] before ejecting it.</span>")
attack_hand(user)
else
@@ -103,282 +112,357 @@
return
updatemodules()
tgui_interact(user)
ui_interact(user)
/obj/machinery/computer/transhuman/resleeving/ui_assets(mob/user)
return list(
get_asset_datum(/datum/asset/cloning),
get_asset_datum(/datum/asset/cloning/resleeving),
)
/obj/machinery/computer/transhuman/resleeving/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
user.set_machine(src)
/obj/machinery/computer/transhuman/resleeving/tgui_interact(mob/user, datum/tgui/ui = null)
if(stat & (NOPOWER|BROKEN))
return
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, "ResleevingConsole", "Resleeving Console")
ui.open()
/obj/machinery/computer/transhuman/resleeving/tgui_data(mob/user)
var/data[0]
data["menu"] = menu
var/list/temppods[0]
for(var/obj/machinery/clonepod/transhuman/pod in pods)
var/status = "idle"
if(pod.mess)
status = "mess"
else if(pod.occupant && !(pod.stat & NOPOWER))
status = "cloning"
temppods.Add(list(list(
"pod" = "\ref[pod]",
"name" = sanitize(capitalize(pod.name)),
"biomass" = pod.get_biomass(),
"status" = status,
"progress" = (pod.occupant && pod.occupant.stat != DEAD) ? pod.get_completion() : 0
)))
data["pods"] = temppods.Copy()
temppods.Cut()
for(var/obj/machinery/transhuman/synthprinter/spod in spods)
temppods.Add(list(list(
"spod" = "\ref[spod]",
"name" = sanitize(capitalize(spod.name)),
"busy" = spod.busy,
"steel" = spod.stored_material[DEFAULT_WALL_MATERIAL],
"glass" = spod.stored_material["glass"]
)))
data["spods"] = temppods.Copy()
temppods.Cut()
for(var/obj/machinery/transhuman/resleever/resleever in sleevers)
temppods.Add(list(list(
"sleever" = "\ref[resleever]",
"name" = sanitize(capitalize(resleever.name)),
"occupied" = !!resleever.occupant,
"occupant" = resleever.occupant ? resleever.occupant.real_name : "None"
)))
data["sleevers"] = temppods.Copy()
temppods.Cut()
data["coredumped"] = SStranscore.core_dumped
data["emergency"] = disk
data["temp"] = temp
data["selected_pod"] = "\ref[selected_pod]"
data["selected_printer"] = "\ref[selected_printer]"
data["selected_sleever"] = "\ref[selected_sleever]"
var/bodyrecords_list_ui[0]
for(var/N in SStranscore.body_scans)
var/datum/transhuman/body_record/BR = SStranscore.body_scans[N]
bodyrecords_list_ui[++bodyrecords_list_ui.len] = list("name" = N, "recref" = "\ref[BR]")
data["bodyrecords"] = bodyrecords_list_ui
var/mindrecords_list_ui[0]
for(var/N in SStranscore.backed_up)
var/datum/transhuman/mind_record/MR = SStranscore.backed_up[N]
mindrecords_list_ui[++mindrecords_list_ui.len] = list("name" = N, "recref" = "\ref[MR]")
data["mindrecords"] = mindrecords_list_ui
var/pods_list_ui[0]
for(var/obj/machinery/clonepod/transhuman/pod in pods)
pods_list_ui[++pods_list_ui.len] = list("pod" = pod, "biomass" = pod.get_biomass())
data["modal"] = tgui_modal_data(src)
return data
var/spods_list_ui[0]
for(var/obj/machinery/transhuman/synthprinter/spod in spods)
spods_list_ui[++spods_list_ui.len] = list("spod" = spod, "steel" = spod.stored_material[DEFAULT_WALL_MATERIAL], "glass" = spod.stored_material["glass"])
var/sleevers_list_ui[0]
for(var/obj/machinery/transhuman/resleever/resleever in sleevers)
sleevers_list_ui[++sleevers_list_ui.len] = list("sleever" = resleever, "occupant" = resleever.occupant ? resleever.occupant.real_name : "None")
if(pods)
data["pods"] = pods_list_ui
else
data["pods"] = null
if(spods)
data["spods"] = spods_list_ui
else
data["spods"] = null
if(sleevers)
data["sleevers"] = sleevers_list_ui
else
data["pods"] = null
if(bodyrecords_list_ui.len)
data["bodyrecords"] = bodyrecords_list_ui
else
data["bodyrecords"] = null
if(mindrecords_list_ui.len)
data["mindrecords"] = mindrecords_list_ui
else
data["mindrecords"] = null
if(active_br)
var/can_grow_active = 1
if(!synthetic_capable && active_br.synthetic) //Disqualified due to being synthetic in an organic only.
can_grow_active = 0
else if(!organic_capable && !active_br.synthetic) //Disqualified for the opposite.
can_grow_active = 0
else if(!synthetic_capable && !organic_capable) //What have you done??
can_grow_active = 0
else if(active_br.toocomplex)
can_grow_active = 0
data["activeBodyRecord"] = list("real_name" = active_br.mydna.name, \
"speciesname" = active_br.speciesname ? active_br.speciesname : active_br.mydna.dna.species, \
"gender" = active_br.bodygender, \
"synthetic" = active_br.synthetic ? "Yes" : "No", \
"locked" = active_br.locked ? "Low" : "High", \
"cando" = can_grow_active,
"booc" = active_br.body_oocnotes)
else
data["activeRecord"] = null
if(active_mr)
var/can_sleeve_current = 1
if(!sleevers.len)
can_sleeve_current = 0
data["activeMindRecord"] = list("charname" = active_mr.mindname, \
"obviously_dead" = active_mr.dead_state == MR_DEAD ? "Past-due" : "Current", \
"cando" = can_sleeve_current,
"mooc" = active_mr.mind_oocnotes)
else
data["activeMindRecord"] = null
data["menu"] = menu
data["podsLen"] = pods.len
data["spodsLen"] = spods.len
data["sleeversLen"] = sleevers.len
data["temp"] = temp
data["coredumped"] = SStranscore.core_dumped
data["emergency"] = disk ? 1 : 0
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open)
if (!ui)
ui = new(user, src, ui_key, "sleever.tmpl", "Resleeving Control Console", 400, 450)
ui.set_initial_data(data)
ui.open()
ui.set_auto_update(5)
/obj/machinery/computer/transhuman/resleeving/Topic(href, href_list)
/obj/machinery/computer/transhuman/resleeving/tgui_act(action, params)
if(..())
return 1
return
else if (href_list["view_brec"])
active_br = locate(href_list["view_brec"])
if(active_br && istype(active_br.mydna))
menu = 4
else
active_br = null
temp = "ERROR: Record missing."
. = TRUE
switch(tgui_modal_act(src, action, params))
if(TGUI_MODAL_ANSWER)
// if(params["id"] == "del_rec" && active_record)
// var/obj/item/weapon/card/id/C = usr.get_active_hand()
// if(!istype(C) && !istype(C, /obj/item/device/pda))
// set_temp("ID not in hand.", "bad")
// return
// if(check_access(C))
// records.Remove(active_record)
// qdel(active_record)
// set_temp("Record deleted.", "success")
// menu = MENU_RECORDS
// else
// set_temp("Access denied.", "bad")
return
else if (href_list["view_mrec"])
active_mr = locate(href_list["view_mrec"])
if(active_mr && istype(active_mr))
menu = 5
else
active_mr = null
temp = "ERROR: Record missing."
else if (href_list["boocnotes"])
menu = 6
else if (href_list["moocnotes"])
menu = 7
else if (href_list["refresh"])
updateUsrDialog()
else if (href_list["coredump"])
if(disk)
SStranscore.core_dump(disk)
sleep(5)
visible_message("<span class='warning'>\The [src] spits out \the [disk].</span>")
switch(action)
if("view_b_rec")
var/ref = params["ref"]
if(!length(ref))
return
active_br = locate(ref)
if(istype(active_br))
if(isnull(active_br.ckey))
qdel(active_br)
set_temp("Error: Record corrupt.", "bad")
else
var/can_grow_active = 1
if(!synthetic_capable && active_br.synthetic) //Disqualified due to being synthetic in an organic only.
can_grow_active = 0
set_temp("Error: Cannot grow [active_br.mydna.name] due to lack of synthfabs.", "bad")
else if(!organic_capable && !active_br.synthetic) //Disqualified for the opposite.
can_grow_active = 0
set_temp("Error: Cannot grow [active_br.mydna.name] due to lack of cloners.", "bad")
else if(!synthetic_capable && !organic_capable) //What have you done??
can_grow_active = 0
set_temp("Error: Cannot grow [active_br.mydna.name] due to lack of synthfabs and cloners.", "bad")
else if(active_br.toocomplex)
can_grow_active = 0
set_temp("Error: Cannot grow [active_br.mydna.name] due to species complexity.", "bad")
var/list/payload = list(
activerecord = "\ref[active_br]",
realname = sanitize(active_br.mydna.name),
species = active_br.speciesname ? active_br.speciesname : active_br.mydna.dna.species,
sex = active_br.bodygender,
mind_compat = active_br.locked ? "Low" : "High",
synthetic = active_br.synthetic,
oocnotes = active_br.body_oocnotes ? active_br.body_oocnotes : "None",
can_grow_active = can_grow_active,
)
tgui_modal_message(src, action, "", null, payload)
else
active_br = null
set_temp("Error: Record missing.", "bad")
if("view_m_rec")
var/ref = params["ref"]
if(!length(ref))
return
active_mr = locate(ref)
if(istype(active_mr))
if(isnull(active_mr.ckey))
qdel(active_mr)
set_temp("Error: Record corrupt.", "bad")
else
var/can_sleeve_active = 1
if(!LAZYLEN(sleevers))
can_sleeve_active = 0
set_temp("Error: Cannot sleeve due to no sleevers.", "bad")
if(!selected_sleever)
can_sleeve_active = 0
set_temp("Error: Cannot sleeve due to no selected sleever.", "bad")
if(selected_sleever && !selected_sleever.occupant)
can_sleeve_active = 0
set_temp("Error: Cannot sleeve due to lack of sleever occupant.", "bad")
var/list/payload = list(
activerecord = "\ref[active_mr]",
realname = sanitize(active_mr.mindname),
obviously_dead = active_mr.dead_state == MR_DEAD ? "Past-due" : "Current",
oocnotes = active_mr.mind_oocnotes ? active_mr.mind_oocnotes : "None.",
can_sleeve_active = can_sleeve_active,
)
tgui_modal_message(src, action, "", null, payload)
else
active_mr = null
set_temp("Error: Record missing.", "bad")
if("coredump")
if(disk)
SStranscore.core_dump(disk)
sleep(5)
visible_message("<span class='warning'>\The [src] spits out \the [disk].</span>")
disk.forceMove(get_turf(src))
disk = null
if("ejectdisk")
disk.forceMove(get_turf(src))
disk = null
else if (href_list["ejectdisk"])
disk.forceMove(get_turf(src))
disk = null
else if (href_list["create"])
if(istype(active_br))
//Tried to grow a synth but no synth pods.
if(active_br.synthetic && !spods.len)
temp = "Error: No SynthFabs detected."
//Tried to grow an organic but no growpods.
else if(!active_br.synthetic && !pods.len)
temp = "Error: No growpods detected."
//We have the machines. We can rebuild them. Probably.
else
//We're cloning a synth.
if(active_br.synthetic)
var/obj/machinery/transhuman/synthprinter/spod = spods[1]
if (spods.len > 1)
spod = input(usr,"Select a SynthFab to use", "Printer selection") as anything in spods
//Already doing someone.
if(spod.busy)
temp = "Error: SynthFab is currently busy."
//Not enough steel or glass
else if(spod.stored_material[DEFAULT_WALL_MATERIAL] < spod.body_cost)
temp = "Error: Not enough [DEFAULT_WALL_MATERIAL] in SynthFab."
else if(spod.stored_material["glass"] < spod.body_cost)
temp = "Error: Not enough glass in SynthFab."
//Gross pod (broke mid-cloning or something).
else if(spod.broken)
temp = "Error: SynthFab malfunction."
//Do the cloning!
else if(spod.print(active_br))
temp = "Initiating printing cycle..."
menu = 1
else
temp = "Initiating printing cycle...<br>Error: Post-initialisation failed. Printing cycle aborted."
//We're cloning an organic.
if("create")
if(istype(active_br))
//Tried to grow a synth but no synth pods.
if(active_br.synthetic && !spods.len)
set_temp("Error: No SynthFabs detected.", "bad")
//Tried to grow an organic but no growpods.
else if(!active_br.synthetic && !pods.len)
set_temp("Error: No growpods detected.", "Bad")
//We have the machines. We can rebuild them. Probably.
else
var/obj/machinery/clonepod/transhuman/pod = pods[1]
if (pods.len > 1)
pod = input(usr,"Select a growing pod to use", "Pod selection") as anything in pods
//We're cloning a synth.
if(active_br.synthetic)
var/obj/machinery/transhuman/synthprinter/spod = selected_printer
if(!istype(spod))
set_temp("Error: No SynthFab selected.", "bad")
return
//Already doing someone.
if(pod.occupant)
temp = "Error: Growpod is currently occupied."
//Already doing someone.
if(spod.busy)
set_temp("Error: SynthFab is currently busy.", "bad")
return
//Not enough materials.
else if(pod.get_biomass() < CLONE_BIOMASS)
temp = "Error: Not enough biomass."
//Not enough steel or glass
else if(spod.stored_material[DEFAULT_WALL_MATERIAL] < spod.body_cost)
set_temp("Error: Not enough [DEFAULT_WALL_MATERIAL] in SynthFab.", "bad")
return
else if(spod.stored_material["glass"] < spod.body_cost)
set_temp("Error: Not enough glass in SynthFab.", "bad")
return
//Gross pod (broke mid-cloning or something).
else if(pod.mess)
temp = "Error: Growpod malfunction."
//Gross pod (broke mid-cloning or something).
else if(spod.broken)
set_temp("Error: SynthFab malfunction.", "bad")
return
//Disabled in config.
else if(!config.revival_cloning)
temp = "Error: Unable to initiate growing cycle."
//Do the cloning!
else if(spod.print(active_br))
set_temp("Initiating printing cycle...", "good")
menu = 1
else
set_temp("Initiating printing cycle... Error: Post-initialisation failed. Printing cycle aborted.", "bad")
return
//Do the cloning!
else if(pod.growclone(active_br))
temp = "Initiating growing cycle..."
menu = 1
//We're cloning an organic.
else
temp = "Initiating growing cycle...<br>Error: Post-initialisation failed. Growing cycle aborted."
var/obj/machinery/clonepod/transhuman/pod = selected_pod
if(!istype(pod))
set_temp("Error: No clonepod selected.", "bad")
return
//The body record is broken somehow.
else
temp = "Error: Data corruption."
//Already doing someone.
if(pod.occupant)
set_temp("Error: Growpod is currently occupied.", "bad")
return
else if (href_list["sleeve"])
if(istype(active_mr))
if(!sleevers.len)
temp = "Error: No sleevers detected."
//Not enough materials.
else if(pod.get_biomass() < CLONE_BIOMASS)
set_temp("Error: Not enough biomass.", "bad")
return
//Gross pod (broke mid-cloning or something).
else if(pod.mess)
set_temp("Error: Growpod malfunction.", "bad")
return
//Disabled in config.
else if(!config.revival_cloning)
set_temp("Error: Unable to initiate growing cycle.", "bad")
return
//Do the cloning!
else if(pod.growclone(active_br))
set_temp("Initiating growing cycle...", "good")
menu = 1
else
set_temp("Initiating growing cycle... Error: Post-initialisation failed. Growing cycle aborted.", "bad")
return
//The body record is broken somehow.
else
var/mode = text2num(href_list["sleeve"])
var/override
var/obj/machinery/transhuman/resleever/sleever = sleevers[1]
if (sleevers.len > 1)
sleever = input(usr,"Select a resleeving pod to use", "Resleever selection") as anything in sleevers
set_temp("Error: Data corruption.", "bad")
return
switch(mode)
if(1) //Body resleeving
//No body to sleeve into.
if(!sleever.occupant)
temp = "Error: Resleeving pod is not occupied."
if("sleeve")
if(istype(active_mr))
if(!sleevers.len)
set_temp("Error: No sleevers detected.", "bad")
else
var/mode = text2num(params["mode"])
var/override
var/obj/machinery/transhuman/resleever/sleever = selected_sleever
if(!istype(sleever))
set_temp("Error: No resleeving pod selected.", "bad")
return
//OOC body lock thing.
if(sleever.occupant.resleeve_lock && active_mr.ckey != sleever.occupant.resleeve_lock)
temp = "Error: Mind incompatible with body."
switch(mode)
if(1) //Body resleeving
//No body to sleeve into.
if(!sleever.occupant)
set_temp("Error: Resleeving pod is not occupied.", "bad")
return
var/list/subtargets = list()
for(var/mob/living/carbon/human/H in sleever.occupant)
if(H.resleeve_lock && active_mr.ckey != H.resleeve_lock)
continue
subtargets += H
if(subtargets.len)
var/oc_sanity = sleever.occupant
override = input(usr,"Multiple bodies detected. Select target for resleeving of [active_mr.mindname] manually. Sleeving of primary body is unsafe with sub-contents, and is not listed.", "Resleeving Target") as null|anything in subtargets
if(!override || oc_sanity != sleever.occupant || !(override in sleever.occupant))
temp = "Error: Target selection aborted."
//OOC body lock thing.
if(sleever.occupant.resleeve_lock && active_mr.ckey != sleever.occupant.resleeve_lock)
set_temp("Error: Mind incompatible with body.", "bad")
return
if(2) //Card resleeving
if(sleever.sleevecards <= 0)
temp = "Error: No available cards in resleever."
var/list/subtargets = list()
for(var/mob/living/carbon/human/H in sleever.occupant)
if(H.resleeve_lock && active_mr.ckey != H.resleeve_lock)
continue
subtargets += H
if(subtargets.len)
var/oc_sanity = sleever.occupant
override = input(usr,"Multiple bodies detected. Select target for resleeving of [active_mr.mindname] manually. Sleeving of primary body is unsafe with sub-contents, and is not listed.", "Resleeving Target") as null|anything in subtargets
if(!override || oc_sanity != sleever.occupant || !(override in sleever.occupant))
set_temp("Error: Target selection aborted.", "bad")
return
//Body to sleeve into, but mind is in another living body.
if(active_mr.mind_ref.current && active_mr.mind_ref.current.stat < DEAD) //Mind is in a body already that's alive
var/answer = alert(active_mr.mind_ref.current,"Someone is attempting to restore a backup of your mind. Do you want to abandon this body, and move there? You MAY suffer memory loss! (Same rules as CMD apply)","Resleeving","No","Yes")
if(2) //Card resleeving
if(sleever.sleevecards <= 0)
set_temp("Error: No available cards in resleever.", "bad")
return
//They declined to be moved.
if(answer == "No")
temp = "Initiating resleeving...<br>Error: Post-initialisation failed. Resleeving cycle aborted."
//Body to sleeve into, but mind is in another living body.
if(active_mr.mind_ref.current && active_mr.mind_ref.current.stat < DEAD) //Mind is in a body already that's alive
var/answer = alert(active_mr.mind_ref.current,"Someone is attempting to restore a backup of your mind. Do you want to abandon this body, and move there? You MAY suffer memory loss! (Same rules as CMD apply)","Resleeving","No","Yes")
//They declined to be moved.
if(answer == "No")
set_temp("Initiating resleeving... Error: Post-initialisation failed. Resleeving cycle aborted.", "bad")
menu = MENU_MAIN
return TRUE
//They were dead, or otherwise available.
if(!temp)
sleever.putmind(active_mr,mode,override)
set_temp("Initiating resleeving...")
menu = 1
//They were dead, or otherwise available.
if(!temp)
sleever.putmind(active_mr,mode,override)
temp = "Initiating resleeving..."
menu = 1
//IDK but it broke somehow.
return
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("selectprinter")
var/ref = params["ref"]
if(!length(ref))
return
var/obj/machinery/transhuman/synthprinter/selected = locate(ref)
if(istype(selected) && (selected in spods))
selected_printer = selected
if("selectsleever")
var/ref = params["ref"]
if(!length(ref))
return
var/obj/machinery/transhuman/resleever/selected = locate(ref)
if(istype(selected) && (selected in sleevers))
selected_sleever = selected
if("menu")
menu = clamp(text2num(params["num"]), MENU_MAIN, MENU_MIND)
if("cleartemp")
temp = null
else
temp = "Error: Data corruption."
else if (href_list["menu"])
menu = href_list["menu"]
temp = ""
SSnanoui.update_uis(src)
add_fingerprint(usr)
return FALSE
// In here because only relevant to computer
/obj/item/weapon/cmo_disk_holder
@@ -409,3 +493,19 @@
item_state = "card-id"
w_class = ITEMSIZE_SMALL
var/datum/transhuman/mind_record/list/stored = list()
/**
* 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/transhuman/resleeving/proc/set_temp(text = "", style = "info", update_now = FALSE)
temp = list(text = text, style = style)
if(update_now)
SStgui.update_uis(src)
#undef MENU_MAIN
#undef MENU_BODY
#undef MENU_MIND
+37 -20
View File
@@ -162,6 +162,11 @@
return
/obj/machinery/clonepod/transhuman/get_completion()
if(occupant)
return 100 * ((occupant.health + abs(config.health_threshold_dead)) / (occupant.maxHealth + abs(config.health_threshold_dead)))
return 0
//Synthetic version
/obj/machinery/transhuman/synthprinter
name = "SynthFab 3000"
@@ -437,28 +442,40 @@
sickness_duration = (45 - (total_rating-4)*1.875) MINUTES // 45 minutes default, 30 minutes with max non-anomaly upgrades, 15 minutes with max anomaly ones
/obj/machinery/transhuman/resleever/attack_hand(mob/user as mob)
user.set_machine(src)
var/health_text = ""
var/mind_text = ""
if(src.occupant)
if(src.occupant.stat >= DEAD)
health_text = "<FONT color=red>DEAD</FONT>"
else if(src.occupant.health < 0)
health_text = "<FONT color=red>[round(src.occupant.health,0.1)]</FONT>"
else
health_text = "[round(src.occupant.health,0.1)]"
tgui_interact(user)
if(src.occupant.mind)
mind_text = "Mind present: [occupant.mind.name]"
else
mind_text = "Mind absent."
/obj/machinery/transhuman/resleever/tgui_interact(mob/user, datum/tgui/ui = null)
if(stat & (NOPOWER|BROKEN))
return
var/dat ="<B>Resleever Status</B><BR>"
dat +="<B>Current occupant:</B> [src.occupant ? "<BR>Name: [src.occupant]<BR>Health: [health_text]<BR>" : "<FONT color=red>None</FONT>"]<BR>"
dat +="<B>Mind status:</B> [mind_text]<BR>"
user.set_machine(src)
user << browse(dat, "window=resleever")
onclose(user, "resleever")
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, "ResleevingPod", "Resleever")
ui.open()
/obj/machinery/transhuman/resleever/tgui_data(mob/user)
var/list/data = list()
data["occupied"] = !!occupant
if(occupant)
data["name"] = occupant.name
data["health"] = occupant.health
data["maxHealth"] = occupant.maxHealth
data["stat"] = occupant.stat
data["mindStatus"] = !!occupant.mind
data["mindName"] = occupant.mind?.name
if(occupant.has_modifier_of_type(/datum/modifier/resleeving_sickness) || occupant.has_modifier_of_type(/datum/modifier/faux_resleeving_sickness))
data["resleeveSick"] = TRUE
else
data["resleeveSick"] = FALSE
if(occupant.confused || occupant.eye_blurry)
data["initialSick"] = TRUE
else
data["initialSick"] = FALSE
return data
/obj/machinery/transhuman/resleever/attackby(obj/item/W as obj, mob/user as mob)
src.add_fingerprint(user)
+2
View File
@@ -119,6 +119,8 @@
max_duration = 100
/datum/surgery_step/cavity/place_item/can_use(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool)
if(!istype(tool))
return 0
if(..())
var/obj/item/organ/external/affected = target.get_organ(target_zone)
if(istype(user,/mob/living/silicon/robot))
+3 -1
View File
@@ -28,6 +28,8 @@
max_duration = 70
/datum/surgery_step/limb/attach/can_use(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool)
if(!istype(tool))
return 0
var/obj/item/organ/external/E = tool
var/obj/item/organ/external/P = target.organs_by_name[E.parent_organ]
var/obj/item/organ/external/affected = target.get_organ(target_zone)
@@ -117,7 +119,7 @@
max_duration = 100
/datum/surgery_step/limb/mechanize/can_use(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool)
if(..())
if(..() && istype(tool))
var/obj/item/robot_parts/p = tool
if (p.part)
if (!(target_zone in p.part))
+13 -1
View File
@@ -164,6 +164,9 @@
if (!..())
return 0
if(!istype(tool))
return 0
var/obj/item/organ/external/affected = target.get_organ(target_zone)
if(!(affected && !(affected.robotic >= ORGAN_ROBOT)))
@@ -227,6 +230,9 @@
if (!..())
return 0
if(!istype(tool))
return 0
target.op_stage.current_organ = null
var/list/removable_organs = list()
@@ -281,7 +287,7 @@
var/obj/item/organ/internal/O = tool
var/obj/item/organ/external/affected = target.get_organ(target_zone)
if(!affected)
if(!affected || !istype(O))
return
var/organ_compatible
@@ -361,6 +367,9 @@
if (!..())
return 0
if(!istype(tool))
return 0
target.op_stage.current_organ = null
var/list/removable_organs = list()
@@ -417,6 +426,9 @@
if (!..())
return 0
if(!istype(tool))
return 0
target.op_stage.current_organ = null
var/list/removable_organs = list()
+370
View File
@@ -0,0 +1,370 @@
/**
* tgui modals
*
* Allows creation of modals within tgui.
*/
GLOBAL_LIST(tgui_modals)
/**
* Call this from a proc that is called in tgui_act() to process modal actions
*
* Example: /obj/machinery/chem_master/proc/tgui_act_modal
* You can then switch based on the return value and show different
* modals depending on the answer.
* Arguments:
* * source - The source datum
* * action - The called action
* * params - The params to the action
*/
/datum/proc/tgui_modal_act(datum/source = src, action = "", params)
ASSERT(istype(source))
. = null
switch(action)
if("modal_open") // Params: id, arguments
return TGUI_MODAL_OPEN
if("modal_answer") // Params: id, answer, arguments
params["answer"] = tgui_modal_preprocess_answer(source, params["answer"])
if(tgui_modal_answer(source, params["id"], params["answer"])) // If there's a current modal with a delegate that returned TRUE, no need to continue
. = TGUI_MODAL_DELEGATE
else
. = TGUI_MODAL_ANSWER
tgui_modal_clear(source)
if("modal_close") // Params: id
tgui_modal_clear(source)
return TGUI_MODAL_CLOSE
/**
* Call this from tgui_data() to return modal information if needed
* Arguments:
* * source - The source datum
*/
/datum/proc/tgui_modal_data(datum/source = src)
ASSERT(istype(source))
var/datum/tgui_modal/current = LAZYACCESS(GLOB.tgui_modals, REF(source))
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[REF(source)]
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, REF(source))
if(previous && !replace_previous)
return FALSE
modal.owning_source = source
// Previous one should get GC'd
LAZYSET(GLOB.tgui_modals, REF(source), 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, REF(source))
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, REF(source))
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