Merge pull request #7625 from ShadowLarkens/tgui_med

TGUI Medical Conversion
This commit is contained in:
Atermonera
2020-09-19 18:52:23 -07:00
committed by GitHub
66 changed files with 4113 additions and 4427 deletions
@@ -9,7 +9,7 @@
if(!H) return
log_and_message_admins("is altering the appearance of [H].")
H.change_appearance(APPEARANCE_ALL, usr, usr, check_species_whitelist = 0, state = admin_state)
H.change_appearance(APPEARANCE_ALL, usr, check_species_whitelist = 0, state = GLOB.tgui_admin_state)
feedback_add_details("admin_verb","CHAA") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/proc/change_human_appearance_self()
@@ -29,10 +29,10 @@
switch(alert("Do you wish for [H] to be allowed to select non-whitelisted races?","Alter Mob Appearance","Yes","No","Cancel"))
if("Yes")
log_and_message_admins("has allowed [H] to change [T.his] appearance, without whitelisting of races.")
H.change_appearance(APPEARANCE_ALL, H.loc, check_species_whitelist = 0)
H.change_appearance(APPEARANCE_ALL, H, check_species_whitelist = 0)
if("No")
log_and_message_admins("has allowed [H] to change [T.his] appearance, with whitelisting of races.")
H.change_appearance(APPEARANCE_ALL, H.loc, check_species_whitelist = 1)
H.change_appearance(APPEARANCE_ALL, H, check_species_whitelist = 1)
feedback_add_details("admin_verb","CMAS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/proc/editappear()
@@ -436,3 +436,37 @@
/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)
+61 -68
View File
@@ -213,17 +213,18 @@
is_off = "-off"
// Fridge contents
switch(contents.len)
if(0)
add_overlay("empty[is_off]")
if(1 to 2)
add_overlay("[icon_contents]-1[is_off]")
if(3 to 5)
add_overlay("[icon_contents]-2[is_off]")
if(6 to 8)
add_overlay("[icon_contents]-3[is_off]")
else
add_overlay("[icon_contents]-4[is_off]")
if(contents)
switch(contents.len)
if(0)
add_overlay("empty[is_off]")
if(1 to 2)
add_overlay("[icon_contents]-1[is_off]")
if(3 to 5)
add_overlay("[icon_contents]-2[is_off]")
if(6 to 8)
add_overlay("[icon_contents]-3[is_off]")
else
add_overlay("[icon_contents]-4[is_off]")
// Fridge top
var/image/top = image(icon, "[icon_base]-top")
@@ -241,7 +242,6 @@
user.visible_message("[user] [panel_open ? "opens" : "closes"] the maintenance panel of \the [src].", "You [panel_open ? "open" : "close"] the maintenance panel of \the [src].")
playsound(src, O.usesound, 50, 1)
update_icon()
SSnanoui.update_uis(src)
return
if(wrenchable && default_unfasten_wrench(user, O, 20))
@@ -261,7 +261,6 @@
stock(O)
user.visible_message("<span class='notice'>[user] has added \the [O] to \the [src].</span>", "<span class='notice'>You add \the [O] to \the [src].</span>")
else if(istype(O, /obj/item/weapon/storage/bag))
var/obj/item/weapon/storage/bag/P = O
var/plants_loaded = 0
@@ -307,11 +306,11 @@
var/datum/stored_item/item = new/datum/stored_item(src, O.type, O.name)
item.add_product(O)
item_records.Add(item)
SSnanoui.update_uis(src)
SStgui.update_uis(src)
/obj/machinery/smartfridge/proc/vend(datum/stored_item/I)
I.get_product(get_turf(src))
SSnanoui.update_uis(src)
SStgui.update_uis(src)
/obj/machinery/smartfridge/attack_ai(mob/user as mob)
attack_hand(user)
@@ -320,66 +319,59 @@
if(stat & (NOPOWER|BROKEN))
return
wires.Interact(user)
ui_interact(user)
tgui_interact(user)
/*******************
* SmartFridge Menu
********************/
/obj/machinery/smartfridge/tgui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, "SmartVend", name)
ui.set_autoupdate(FALSE)
ui.open()
/obj/machinery/smartfridge/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
user.set_machine(src)
/obj/machinery/smartfridge/tgui_data(mob/user)
. = list()
var/data[0]
data["contents"] = null
data["electrified"] = seconds_electrified > 0
data["shoot_inventory"] = shoot_inventory
data["locked"] = locked
data["secure"] = is_secure
var/list/items[0]
for (var/i=1 to length(item_records))
var/list/items = list()
for(var/i=1 to length(item_records))
var/datum/stored_item/I = item_records[i]
var/count = I.get_amount()
if(count > 0)
items.Add(list(list("display_name" = html_encode(capitalize(I.item_name)), "vend" = i, "quantity" = count)))
items.Add(list(list("name" = html_encode(capitalize(I.item_name)), "index" = i, "amount" = count)))
if(items.len > 0)
data["contents"] = items
.["contents"] = items
.["name"] = name
.["locked"] = locked
.["secure"] = is_secure
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open)
if(!ui)
ui = new(user, src, ui_key, "smartfridge.tmpl", src.name, 400, 500)
ui.set_initial_data(data)
ui.open()
/obj/machinery/smartfridge/tgui_act(action, params)
if(..())
return TRUE
/obj/machinery/smartfridge/Topic(href, href_list)
if(..()) return 0
add_fingerprint(usr)
switch(action)
if("Release")
var/amount = 0
if(params["amount"])
amount = params["amount"]
else
amount = input("How many items?", "How many items would you like to take out?", 1) as num|null
if(QDELETED(src) || QDELETED(usr) || !usr.Adjacent(src))
return FALSE
var/index = text2num(params["index"])
var/datum/stored_item/I = item_records[index]
var/count = I.get_amount()
var/mob/user = usr
var/datum/nanoui/ui = SSnanoui.get_open_ui(user, src, "main")
// Sanity check, there are probably ways to press the button when it shouldn't be possible.
if(count > 0)
if((count - amount) < 0)
amount = count
for(var/i = 1 to amount)
vend(I)
src.add_fingerprint(user)
if(href_list["close"])
user.unset_machine()
ui.close()
return 0
if(href_list["vend"])
var/index = text2num(href_list["vend"])
var/amount = text2num(href_list["amount"])
var/datum/stored_item/I = item_records[index]
var/count = I.get_amount()
// Sanity check, there are probably ways to press the button when it shouldn't be possible.
if(count > 0)
if((count - amount) < 0)
amount = count
for(var/i = 1 to amount)
vend(I)
return 1
return 0
return TRUE
return FALSE
/obj/machinery/smartfridge/proc/throw_item()
var/obj/throw_item = null
@@ -398,17 +390,18 @@
spawn(0)
throw_item.throw_at(target,16,3,src)
src.visible_message("<span class='warning'>[src] launches [throw_item.name] at [target.name]!</span>")
SStgui.update_uis(src)
return 1
/************************
* Secure SmartFridges
*************************/
/obj/machinery/smartfridge/secure/Topic(href, href_list)
/obj/machinery/smartfridge/secure/tgui_act(action, params)
if(stat & (NOPOWER|BROKEN))
return 0
return TRUE
if(usr.contents.Find(src) || (in_range(src, usr) && istype(loc, /turf)))
if(!allowed(usr) && !emagged && locked != -1 && href_list["vend"])
if(!allowed(usr) && !emagged && locked != -1 && action == "Release")
to_chat(usr, "<span class='warning'>Access denied.</span>")
return 0
return TRUE
return ..()
@@ -1,7 +1,12 @@
/mob/living/carbon/human/proc/change_appearance(var/flags = APPEARANCE_ALL_HAIR, var/location = src, var/mob/user = src, var/check_species_whitelist = 1, var/list/species_whitelist = list(), var/list/species_blacklist = list(), var/datum/topic_state/state = default_state)
var/datum/nano_module/appearance_changer/AC = new(location, src, check_species_whitelist, species_whitelist, species_blacklist)
/mob/living/carbon/human/proc/change_appearance(var/flags = APPEARANCE_ALL_HAIR,
var/mob/user = src,
var/check_species_whitelist = 1,
var/list/species_whitelist = list(),
var/list/species_blacklist = list(),
var/datum/tgui_state/state = GLOB.tgui_self_state)
var/datum/tgui_module/appearance_changer/AC = new(src, src, check_species_whitelist, species_whitelist, species_blacklist)
AC.flags = flags
AC.ui_interact(user, state = state)
AC.tgui_interact(user, custom_state = state)
/mob/living/carbon/human/proc/change_species(var/new_species)
if(!new_species)
+1 -1
View File
@@ -114,7 +114,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"
@@ -1,191 +0,0 @@
/datum/nano_module/appearance_changer
name = "Appearance Editor"
var/flags = APPEARANCE_ALL_HAIR
var/mob/living/carbon/human/owner = null
var/list/valid_species = list()
var/list/valid_hairstyles = list()
var/list/valid_facial_hairstyles = list()
var/check_whitelist
var/list/whitelist
var/list/blacklist
/datum/nano_module/appearance_changer/New(var/location, var/mob/living/carbon/human/H, var/check_species_whitelist = 1, var/list/species_whitelist = list(), var/list/species_blacklist = list())
..()
owner = H
src.check_whitelist = check_species_whitelist
src.whitelist = species_whitelist
src.blacklist = species_blacklist
/datum/nano_module/appearance_changer/Topic(ref, href_list, var/datum/topic_state/state = default_state)
if(..())
return 1
if(href_list["race"])
if(can_change(APPEARANCE_RACE) && (href_list["race"] in valid_species))
if(owner.change_species(href_list["race"]))
cut_and_generate_data()
return 1
if(href_list["gender"])
if(can_change(APPEARANCE_GENDER) && (href_list["gender"] in get_genders()))
if(owner.change_gender(href_list["gender"]))
cut_and_generate_data()
return 1
if(href_list["gender_id"])
if(can_change(APPEARANCE_GENDER) && (href_list["gender_id"] in all_genders_define_list))
owner.identifying_gender = href_list["gender_id"]
return 1
if(href_list["skin_tone"])
if(can_change_skin_tone())
var/new_s_tone = input(usr, "Choose your character's skin-tone:\n(Light 1 - 220 Dark)", "Skin Tone", -owner.s_tone + 35) as num|null
if(isnum(new_s_tone) && can_still_topic(state))
new_s_tone = 35 - max(min( round(new_s_tone), 220),1)
return owner.change_skin_tone(new_s_tone)
if(href_list["skin_color"])
if(can_change_skin_color())
var/new_skin = input(usr, "Choose your character's skin colour: ", "Skin Color", rgb(owner.r_skin, owner.g_skin, owner.b_skin)) as color|null
if(new_skin && can_still_topic(state))
var/r_skin = hex2num(copytext(new_skin, 2, 4))
var/g_skin = hex2num(copytext(new_skin, 4, 6))
var/b_skin = hex2num(copytext(new_skin, 6, 8))
if(owner.change_skin_color(r_skin, g_skin, b_skin))
update_dna()
return 1
if(href_list["hair"])
if(can_change(APPEARANCE_HAIR) && (href_list["hair"] in valid_hairstyles))
if(owner.change_hair(href_list["hair"]))
update_dna()
return 1
if(href_list["hair_color"])
if(can_change(APPEARANCE_HAIR_COLOR))
var/new_hair = input("Please select hair color.", "Hair Color", rgb(owner.r_hair, owner.g_hair, owner.b_hair)) as color|null
if(new_hair && can_still_topic(state))
var/r_hair = hex2num(copytext(new_hair, 2, 4))
var/g_hair = hex2num(copytext(new_hair, 4, 6))
var/b_hair = hex2num(copytext(new_hair, 6, 8))
if(owner.change_hair_color(r_hair, g_hair, b_hair))
update_dna()
return 1
if(href_list["facial_hair"])
if(can_change(APPEARANCE_FACIAL_HAIR) && (href_list["facial_hair"] in valid_facial_hairstyles))
if(owner.change_facial_hair(href_list["facial_hair"]))
update_dna()
return 1
if(href_list["facial_hair_color"])
if(can_change(APPEARANCE_FACIAL_HAIR_COLOR))
var/new_facial = input("Please select facial hair color.", "Facial Hair Color", rgb(owner.r_facial, owner.g_facial, owner.b_facial)) as color|null
if(new_facial && can_still_topic(state))
var/r_facial = hex2num(copytext(new_facial, 2, 4))
var/g_facial = hex2num(copytext(new_facial, 4, 6))
var/b_facial = hex2num(copytext(new_facial, 6, 8))
if(owner.change_facial_hair_color(r_facial, g_facial, b_facial))
update_dna()
return 1
if(href_list["eye_color"])
if(can_change(APPEARANCE_EYE_COLOR))
var/new_eyes = input("Please select eye color.", "Eye Color", rgb(owner.r_eyes, owner.g_eyes, owner.b_eyes)) as color|null
if(new_eyes && can_still_topic(state))
var/r_eyes = hex2num(copytext(new_eyes, 2, 4))
var/g_eyes = hex2num(copytext(new_eyes, 4, 6))
var/b_eyes = hex2num(copytext(new_eyes, 6, 8))
if(owner.change_eye_color(r_eyes, g_eyes, b_eyes))
update_dna()
return 1
return 0
/datum/nano_module/appearance_changer/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = default_state)
if(!owner || !owner.species)
return
generate_data(check_whitelist, whitelist, blacklist)
var/list/data = host.initial_data()
data["specimen"] = owner.species.name
data["gender"] = owner.gender
data["gender_id"] = owner.identifying_gender
data["change_race"] = can_change(APPEARANCE_RACE)
if(data["change_race"])
var/species[0]
for(var/specimen in valid_species)
species[++species.len] = list("specimen" = specimen)
data["species"] = species
data["change_gender"] = can_change(APPEARANCE_GENDER)
if(data["change_gender"])
var/genders[0]
for(var/gender in get_genders())
genders[++genders.len] = list("gender_name" = gender2text(gender), "gender_key" = gender)
data["genders"] = genders
var/id_genders[0]
for(var/gender in all_genders_define_list)
id_genders[++id_genders.len] = list("gender_name" = gender2text(gender), "gender_key" = gender)
data["id_genders"] = id_genders
data["change_skin_tone"] = can_change_skin_tone()
data["change_skin_color"] = can_change_skin_color()
data["change_eye_color"] = can_change(APPEARANCE_EYE_COLOR)
data["change_hair"] = can_change(APPEARANCE_HAIR)
if(data["change_hair"])
var/hair_styles[0]
for(var/hair_style in valid_hairstyles)
hair_styles[++hair_styles.len] = list("hairstyle" = hair_style)
data["hair_styles"] = hair_styles
data["hair_style"] = owner.h_style
data["change_facial_hair"] = can_change(APPEARANCE_FACIAL_HAIR)
if(data["change_facial_hair"])
var/facial_hair_styles[0]
for(var/facial_hair_style in valid_facial_hairstyles)
facial_hair_styles[++facial_hair_styles.len] = list("facialhairstyle" = facial_hair_style)
data["facial_hair_styles"] = facial_hair_styles
data["facial_hair_style"] = owner.f_style
data["change_hair_color"] = can_change(APPEARANCE_HAIR_COLOR)
data["change_facial_hair_color"] = can_change(APPEARANCE_FACIAL_HAIR_COLOR)
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open)
if (!ui)
ui = new(user, src, ui_key, "appearance_changer.tmpl", "[src]", 800, 450, state = state)
ui.set_initial_data(data)
ui.open()
ui.set_auto_update(1)
/datum/nano_module/appearance_changer/proc/update_dna()
if(owner && (flags & APPEARANCE_UPDATE_DNA))
owner.update_dna()
/datum/nano_module/appearance_changer/proc/can_change(var/flag)
return owner && (flags & flag)
/datum/nano_module/appearance_changer/proc/can_change_skin_tone()
return owner && (flags & APPEARANCE_SKIN) && owner.species.appearance_flags & HAS_SKIN_TONE
/datum/nano_module/appearance_changer/proc/can_change_skin_color()
return owner && (flags & APPEARANCE_SKIN) && owner.species.appearance_flags & HAS_SKIN_COLOR
/datum/nano_module/appearance_changer/proc/cut_and_generate_data()
// Making the assumption that the available species remain constant
valid_facial_hairstyles.Cut()
valid_facial_hairstyles.Cut()
generate_data()
/datum/nano_module/appearance_changer/proc/generate_data()
if(!owner)
return
if(!valid_species.len)
valid_species = owner.generate_valid_species(check_whitelist, whitelist, blacklist)
if(!valid_hairstyles.len || !valid_facial_hairstyles.len)
valid_hairstyles = owner.generate_valid_hairstyles(check_gender = 0)
valid_facial_hairstyles = owner.generate_valid_facial_hairstyles()
/datum/nano_module/appearance_changer/proc/get_genders()
var/datum/species/S = owner.species
var/list/possible_genders = S.genders
if(!owner.internal_organs_by_name["cell"])
return possible_genders
possible_genders = possible_genders.Copy()
possible_genders |= NEUTER
return possible_genders
+44 -31
View File
@@ -36,12 +36,8 @@
to_chat(user, "<span class='notice'>You put [P] in [src].</span>")
user.drop_item()
P.loc = src
icon_state = "[initial(icon_state)]-open"
flick("[initial(icon_state)]-open",src)
playsound(src, 'sound/bureaucracy/filingcabinet.ogg', 50, 1)
sleep(40)
icon_state = initial(icon_state)
updateUsrDialog()
open_animation()
SStgui.update_uis(src)
else if(P.is_wrench())
playsound(src, P.usesound, 50, 1)
anchored = !anchored
@@ -65,20 +61,12 @@
to_chat(user, "<span class='notice'>\The [src] is empty.</span>")
return
user.set_machine(src)
var/dat = "<center><table>"
for(var/obj/item/P in src)
dat += "<tr><td><a href='?src=\ref[src];retrieve=\ref[P]'>[P.name]</a></td></tr>"
dat += "</table></center>"
user << browse("<html><head><title>[name]</title></head><body>[dat]</body></html>", "window=filingcabinet;size=350x300")
return
tgui_interact(user)
/obj/structure/filingcabinet/attack_tk(mob/user)
if(anchored)
attack_self_tk(user)
else
..()
return attack_self_tk(user)
return ..()
/obj/structure/filingcabinet/attack_self_tk(mob/user)
if(contents.len)
@@ -91,20 +79,46 @@
return
to_chat(user, "<span class='notice'>You find nothing in [src].</span>")
/obj/structure/filingcabinet/Topic(href, href_list)
if(href_list["retrieve"])
usr << browse("", "window=filingcabinet") // Close the menu
/obj/structure/filingcabinet/tgui_state(mob/user)
return GLOB.tgui_physical_state
//var/retrieveindex = text2num(href_list["retrieve"])
var/obj/item/P = locate(href_list["retrieve"])//contents[retrieveindex]
if(istype(P) && (P.loc == src) && src.Adjacent(usr))
usr.put_in_hands(P)
updateUsrDialog()
flick("[initial(icon_state)]-open",src)
playsound(src, 'sound/bureaucracy/filingcabinet.ogg', 50, 1)
spawn(0)
sleep(20)
icon_state = initial(icon_state)
/obj/structure/filingcabinet/tgui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, "FileCabinet", name)
ui.set_autoupdate(FALSE)
ui.open()
/obj/structure/filingcabinet/tgui_data(mob/user)
var/list/data = list()
data["contents"] = list()
for(var/obj/item/P in src)
data["contents"].Add(list(list(
"name" = P.name,
"ref" = "\ref[P]",
)))
return data
/obj/structure/filingcabinet/tgui_act(action, params)
if(..())
return TRUE
switch(action)
if("retrieve")
var/obj/item/P = locate(params["ref"])
if(istype(P) && (P.loc == src) && usr.Adjacent(src))
usr.put_in_hands(P)
open_animation()
SStgui.update_uis(src)
/obj/structure/filingcabinet/proc/open_animation()
flick("[initial(icon_state)]-open",src)
playsound(src, 'sound/bureaucracy/filingcabinet.ogg', 50, 1)
spawn(0)
sleep(20)
icon_state = initial(icon_state)
/*
* Security Record Cabinets
@@ -112,7 +126,6 @@
/obj/structure/filingcabinet/security
var/virgin = 1
/obj/structure/filingcabinet/security/proc/populate()
if(virgin)
for(var/datum/data/record/G in data_core.general)
@@ -144,6 +144,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
+482 -277
View File
@@ -2,6 +2,14 @@
#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_UNITS_PER_BOTTLE 60 // Max amount of units in a bottle (it's volume)
#define MAX_CUSTOM_NAME_LEN 64 // Max length of a custom pill/condiment/whatever
@@ -24,11 +32,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 +56,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 +70,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 +96,420 @@
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
var/num = round(text2num(arguments["num"] || 1))
if(!num)
return
arguments["num"] = num
var/amount_per_bottle = CLAMP(reagents.total_volume / num, 0, MAX_UNITS_PER_BOTTLE)
var/default_name = "[reagents.get_master_reagent_name()]"
var/bottles_text = num == 1 ? "new bottle" : "[num] new bottles"
tgui_modal_input(src, id, "Please name your [bottles_text] ([amount_per_bottle]u in bottle):", null, arguments, default_name, MAX_CUSTOM_NAME_LEN)
if("create_bottle_multiple")
if(condi || !reagents.total_volume)
return
tgui_modal_input(src, id, "Please enter the amount of bottles to make (max [MAX_MULTI_AMOUNT] at a time):", null, arguments, pillamount, 5)
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
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_bottle = CLAMP(reagents.total_volume / count, 0, MAX_UNITS_PER_BOTTLE)
while(count--)
if(reagents.total_volume <= 0)
to_chat(usr, "<span class='notice'>Not enough reagents to create these bottles!</span>")
return
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, amount_per_bottle)
P.update_icon()
if("create_bottle_multiple")
if(condi || !reagents.total_volume)
return
tgui_act("modal_open", list("id" = "create_bottle", "arguments" = list("num" = answer)), ui, state)
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 TRUE
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(get_turf(src))
if(Adjacent(usr) && !issilicon(usr))
usr.put_in_hands(loaded_pill_bottle)
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 +549,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
@@ -444,94 +658,65 @@
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
visible_message("<span class='notice'>\The [usr] remove the container from \the [src].</span>")
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()
@@ -549,7 +734,6 @@
// Reset the machine.
spawn(60)
inuse = 0
interact(usr)
// Process.
for (var/obj/item/O in holdingitems)
@@ -576,13 +760,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
///////////////
///////////////
@@ -648,4 +845,12 @@
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_UNITS_PER_BOTTLE
#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 TRUE
/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)
+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 FALSE
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 FALSE
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 FALSE
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 FALSE
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 FALSE
target.op_stage.current_organ = null
var/list/removable_organs = list()
@@ -417,6 +426,9 @@
if (!..())
return 0
if(!istype(tool))
return FALSE
target.op_stage.current_organ = null
var/list/removable_organs = list()
+23 -2
View File
@@ -12,9 +12,10 @@
*
* required user mob The mob who opened/is using the UI.
* optional ui datum/tgui The UI to be updated, if it exists.
* optional parent_ui datum/tgui A parent UI that, when closed, closes this UI as well.
*/
/datum/proc/tgui_interact(mob/user, datum/tgui/ui = null)
/datum/proc/tgui_interact(mob/user, datum/tgui/ui = null, datum/tgui/parent_ui = null)
return FALSE // Not implemented.
/**
@@ -27,7 +28,7 @@
*
* return list Data to be sent to the UI.
*/
/datum/proc/tgui_data(mob/user)
/datum/proc/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state)
return list() // Not implemented.
/**
@@ -140,6 +141,26 @@
*/
/datum/proc/tgui_close(mob/user)
/**
* verb
*
* Used by a client to fix broken TGUI windows caused by opening a UI window before assets load.
* Probably not very performant and forcibly destroys a bunch of windows, so it has some warnings attached.
* Conveniently, also allows devs to force a dev server reattach without relogging, since it yeets windows.
*/
/client/verb/tgui_fix_white()
set desc = "Only use this if you have a broken TGUI window occupying your screen!"
set name = "Fix TGUI"
set category = "OOC"
if(alert(src, "Only use this verb if you have a white TGUI window stuck on your screen.", "Fix TGUI", "Continue", "Nevermind") != "Continue")
return
SStgui.close_user_uis(mob)
if(alert(src, "Did that fix the problem?", "Fix TGUI", "Yes", "No") == "No")
SStgui.force_close_all_windows(mob)
alert(src, "UIs should be fixed now. If not, please cry to your nearest coder.", "Fix TGUI")
/**
* verb
*
+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
+77 -2
View File
@@ -12,17 +12,26 @@ Code is pretty much ripped verbatim from nano modules, but with un-needed stuff
var/list/using_access
var/tgui_id
var/ntos = FALSE
/datum/tgui_module/New(var/host)
src.host = host
if(ntos)
tgui_id = "Ntos" + tgui_id
/datum/tgui_module/tgui_host()
return host ? host : src
return host ? host.tgui_host() : src
/datum/tgui_module/tgui_close(mob/user)
if(host)
host.tgui_close(user)
/datum/tgui_module/proc/check_eye(mob/user)
return -1
/datum/tgui_module/proc/can_still_topic(mob/user, datum/tgui_state/state)
return (tgui_status(user, state) == STATUS_INTERACTIVE)
/datum/tgui_module/proc/check_access(mob/user, access)
if(!access)
return 1
@@ -43,4 +52,70 @@ Code is pretty much ripped verbatim from nano modules, but with un-needed stuff
if(access in I.access)
return 1
return 0
return 0
/datum/tgui_module/tgui_static_data()
. = ..()
var/obj/item/modular_computer/host = tgui_host()
if(istype(host))
. += host.get_header_data()
/datum/tgui_module/tgui_act(action, params)
if(..())
return TRUE
var/obj/item/modular_computer/host = tgui_host()
if(istype(host))
if(action == "PC_exit")
host.kill_program()
return TRUE
if(action == "PC_shutdown")
host.shutdown_computer()
return TRUE
if(action == "PC_minimize")
host.minimize_program(usr)
return TRUE
// Just a nice little default interact in case the subtypes don't need any special behavior here
/datum/tgui_module/tgui_interact(mob/user, datum/tgui/ui = null)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, tgui_id, name)
ui.open()
// This is a helper for anything that wants to render the map.
/datum/tgui_module/proc/get_plane_masters()
. = list()
// 'Utility' planes
. += new /obj/screen/plane_master/fullbright //Lighting system (lighting_overlay objects)
. += new /obj/screen/plane_master/lighting //Lighting system (but different!)
. += new /obj/screen/plane_master/ghosts //Ghosts!
. += new /obj/screen/plane_master{plane = PLANE_AI_EYE} //AI Eye!
. += new /obj/screen/plane_master{plane = PLANE_CH_STATUS} //Status is the synth/human icon left side of medhuds
. += new /obj/screen/plane_master{plane = PLANE_CH_HEALTH} //Health bar
. += new /obj/screen/plane_master{plane = PLANE_CH_LIFE} //Alive-or-not icon
. += new /obj/screen/plane_master{plane = PLANE_CH_ID} //Job ID icon
. += new /obj/screen/plane_master{plane = PLANE_CH_WANTED} //Wanted status
. += new /obj/screen/plane_master{plane = PLANE_CH_IMPLOYAL} //Loyalty implants
. += new /obj/screen/plane_master{plane = PLANE_CH_IMPTRACK} //Tracking implants
. += new /obj/screen/plane_master{plane = PLANE_CH_IMPCHEM} //Chemical implants
. += new /obj/screen/plane_master{plane = PLANE_CH_SPECIAL} //"Special" role stuff
. += new /obj/screen/plane_master{plane = PLANE_CH_STATUS_OOC} //OOC status HUD
. += new /obj/screen/plane_master{plane = PLANE_ADMIN1} //For admin use
. += new /obj/screen/plane_master{plane = PLANE_ADMIN2} //For admin use
. += new /obj/screen/plane_master{plane = PLANE_ADMIN3} //For admin use
. += new /obj/screen/plane_master{plane = PLANE_MESONS} //Meson-specific things like open ceilings.
. += new /obj/screen/plane_master{plane = PLANE_BUILDMODE} //Things that only show up while in build mode
// Real tangible stuff planes
. += new /obj/screen/plane_master/main{plane = TURF_PLANE}
. += new /obj/screen/plane_master/main{plane = OBJ_PLANE}
. += new /obj/screen/plane_master/main{plane = MOB_PLANE}
. += new /obj/screen/plane_master/cloaked //Cloaked atoms!
/datum/tgui_module/proc/relaymove(mob/user, direction)
return FALSE
@@ -0,0 +1,358 @@
/datum/tgui_module/appearance_changer
name = "Appearance Editor"
tgui_id = "AppearanceChanger"
var/flags = APPEARANCE_ALL_HAIR
var/mob/living/carbon/human/owner = null
var/list/valid_species = list()
var/list/valid_hairstyles = list()
var/list/valid_facial_hairstyles = list()
var/check_whitelist
var/list/whitelist
var/list/blacklist
var/customize_usr = FALSE
// Stuff needed to render the map
var/map_name
var/obj/screen/map_view/cam_screen
var/list/cam_plane_masters
var/obj/screen/background/cam_background
var/obj/screen/skybox/local_skybox
// Needed for moving camera support
var/camera_diff_x = -1
var/camera_diff_y = -1
var/camera_diff_z = -1
/datum/tgui_module/appearance_changer/New(
var/host,
mob/living/carbon/human/H,
check_species_whitelist = 1,
list/species_whitelist = list(),
list/species_blacklist = list())
. = ..()
map_name = "appearance_changer_[REF(src)]_map"
// Initialize map objects
cam_screen = new
cam_screen.name = "screen"
cam_screen.assigned_map = map_name
cam_screen.del_on_map_removal = FALSE
cam_screen.screen_loc = "[map_name]:1,1"
cam_plane_masters = get_plane_masters()
for(var/plane in cam_plane_masters)
var/obj/screen/instance = plane
instance.assigned_map = map_name
instance.del_on_map_removal = FALSE
instance.screen_loc = "[map_name]:CENTER"
local_skybox = new()
local_skybox.assigned_map = map_name
local_skybox.del_on_map_removal = FALSE
local_skybox.screen_loc = "[map_name]:CENTER,CENTER"
cam_plane_masters += local_skybox
cam_background = new
cam_background.assigned_map = map_name
cam_background.del_on_map_removal = FALSE
reload_cameraview()
owner = H
check_whitelist = check_species_whitelist
whitelist = species_whitelist
blacklist = species_blacklist
/datum/tgui_module/appearance_changer/Destroy()
qdel(cam_screen)
QDEL_LIST(cam_plane_masters)
qdel(cam_background)
return ..()
/datum/tgui_module/appearance_changer/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state)
if(..())
return TRUE
var/mob/living/carbon/human/target = owner
if(customize_usr)
if(!ishuman(usr))
return TRUE
target = usr
switch(action)
if("race")
if(can_change(APPEARANCE_RACE) && (params["race"] in valid_species))
if(target.change_species(params["race"]))
cut_and_generate_data()
return 1
if("gender")
if(can_change(APPEARANCE_GENDER) && (params["gender"] in get_genders()))
if(target.change_gender(params["gender"]))
cut_and_generate_data()
return 1
if("gender_id")
if(can_change(APPEARANCE_GENDER) && (params["gender_id"] in all_genders_define_list))
target.identifying_gender = params["gender_id"]
return 1
if("skin_tone")
if(can_change_skin_tone())
var/new_s_tone = input(usr, "Choose your character's skin-tone:\n(Light 1 - 220 Dark)", "Skin Tone", -target.s_tone + 35) as num|null
if(isnum(new_s_tone) && can_still_topic(usr, state))
new_s_tone = 35 - max(min( round(new_s_tone), 220),1)
return target.change_skin_tone(new_s_tone)
if("skin_color")
if(can_change_skin_color())
var/new_skin = input(usr, "Choose your character's skin colour: ", "Skin Color", rgb(target.r_skin, target.g_skin, target.b_skin)) as color|null
if(new_skin && can_still_topic(usr, state))
var/r_skin = hex2num(copytext(new_skin, 2, 4))
var/g_skin = hex2num(copytext(new_skin, 4, 6))
var/b_skin = hex2num(copytext(new_skin, 6, 8))
if(target.change_skin_color(r_skin, g_skin, b_skin))
update_dna()
return 1
if("hair")
if(can_change(APPEARANCE_HAIR) && (params["hair"] in valid_hairstyles))
if(target.change_hair(params["hair"]))
update_dna()
return 1
if("hair_color")
if(can_change(APPEARANCE_HAIR_COLOR))
var/new_hair = input("Please select hair color.", "Hair Color", rgb(target.r_hair, target.g_hair, target.b_hair)) as color|null
if(new_hair && can_still_topic(usr, state))
var/r_hair = hex2num(copytext(new_hair, 2, 4))
var/g_hair = hex2num(copytext(new_hair, 4, 6))
var/b_hair = hex2num(copytext(new_hair, 6, 8))
if(target.change_hair_color(r_hair, g_hair, b_hair))
update_dna()
return 1
if("facial_hair")
if(can_change(APPEARANCE_FACIAL_HAIR) && (params["facial_hair"] in valid_facial_hairstyles))
if(target.change_facial_hair(params["facial_hair"]))
update_dna()
return 1
if("facial_hair_color")
if(can_change(APPEARANCE_FACIAL_HAIR_COLOR))
var/new_facial = input("Please select facial hair color.", "Facial Hair Color", rgb(target.r_facial, target.g_facial, target.b_facial)) as color|null
if(new_facial && can_still_topic(usr, state))
var/r_facial = hex2num(copytext(new_facial, 2, 4))
var/g_facial = hex2num(copytext(new_facial, 4, 6))
var/b_facial = hex2num(copytext(new_facial, 6, 8))
if(target.change_facial_hair_color(r_facial, g_facial, b_facial))
update_dna()
return 1
if("eye_color")
if(can_change(APPEARANCE_EYE_COLOR))
var/new_eyes = input("Please select eye color.", "Eye Color", rgb(target.r_eyes, target.g_eyes, target.b_eyes)) as color|null
if(new_eyes && can_still_topic(usr, state))
var/r_eyes = hex2num(copytext(new_eyes, 2, 4))
var/g_eyes = hex2num(copytext(new_eyes, 4, 6))
var/b_eyes = hex2num(copytext(new_eyes, 6, 8))
if(target.change_eye_color(r_eyes, g_eyes, b_eyes))
update_dna()
return 1
return FALSE
/datum/tgui_module/appearance_changer/tgui_interact(mob/user, datum/tgui/ui = null, datum/tgui/parent_ui = null, datum/tgui_state/custom_state = GLOB.tgui_default_state)
var/mob/living/carbon/human/target = owner
if(customize_usr)
if(!ishuman(user))
return TRUE
target = user
if(!target || !target.species)
return
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
reload_cameraview()
// Register map objects
user.client.register_map_obj(cam_screen)
for(var/plane in cam_plane_masters)
user.client.register_map_obj(plane)
user.client.register_map_obj(cam_background)
// Open UI
ui = new(user, src, tgui_id, name)
ui.open()
if(custom_state)
ui.set_state(custom_state)
/datum/tgui_module/appearance_changer/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state)
var/list/data = ..()
generate_data(check_whitelist, whitelist, blacklist)
differential_check()
var/mob/living/carbon/human/target = owner
if(customize_usr)
if(!ishuman(usr))
return TRUE
target = usr
data["name"] = target.name
data["specimen"] = target.species.name
data["gender"] = target.gender
data["gender_id"] = target.identifying_gender
data["change_race"] = can_change(APPEARANCE_RACE)
if(data["change_race"])
var/species[0]
for(var/specimen in valid_species)
species[++species.len] = list("specimen" = specimen)
data["species"] = species
data["change_gender"] = can_change(APPEARANCE_GENDER)
if(data["change_gender"])
var/genders[0]
for(var/gender in get_genders())
genders[++genders.len] = list("gender_name" = gender2text(gender), "gender_key" = gender)
data["genders"] = genders
var/id_genders[0]
for(var/gender in all_genders_define_list)
id_genders[++id_genders.len] = list("gender_name" = gender2text(gender), "gender_key" = gender)
data["id_genders"] = id_genders
data["change_hair"] = can_change(APPEARANCE_HAIR)
if(data["change_hair"])
var/hair_styles[0]
for(var/hair_style in valid_hairstyles)
hair_styles[++hair_styles.len] = list("hairstyle" = hair_style)
data["hair_styles"] = hair_styles
data["hair_style"] = target.h_style
data["change_facial_hair"] = can_change(APPEARANCE_FACIAL_HAIR)
if(data["change_facial_hair"])
var/facial_hair_styles[0]
for(var/facial_hair_style in valid_facial_hairstyles)
facial_hair_styles[++facial_hair_styles.len] = list("facialhairstyle" = facial_hair_style)
data["facial_hair_styles"] = facial_hair_styles
data["facial_hair_style"] = target.f_style
data["change_skin_tone"] = can_change_skin_tone()
data["change_skin_color"] = can_change_skin_color()
if(data["change_skin_color"])
data["skin_color"] = rgb(target.r_skin, target.g_skin, target.b_skin)
data["change_eye_color"] = can_change(APPEARANCE_EYE_COLOR)
if(data["change_eye_color"])
data["eye_color"] = rgb(target.r_eyes, target.g_eyes, target.b_eyes)
data["change_hair_color"] = can_change(APPEARANCE_HAIR_COLOR)
if(data["change_hair_color"])
data["hair_color"] = rgb(target.r_hair, target.g_hair, target.b_hair)
data["change_facial_hair_color"] = can_change(APPEARANCE_FACIAL_HAIR_COLOR)
if(data["change_facial_hair_color"])
data["facial_hair_color"] = rgb(target.r_facial, target.g_facial, target.b_facial)
return data
/datum/tgui_module/appearance_changer/tgui_static_data(mob/user)
var/list/data = ..()
data["mapRef"] = map_name
return data
/datum/tgui_module/appearance_changer/proc/differential_check()
var/turf/T = get_turf(customize_usr ? tgui_host() : owner)
if(T)
var/new_x = T.x
var/new_y = T.y
var/new_z = T.z
if((new_x != camera_diff_x) || (new_y != camera_diff_y) || (new_z != camera_diff_z))
reload_cameraview()
/datum/tgui_module/appearance_changer/proc/reload_cameraview()
var/turf/camTurf = get_turf(customize_usr ? tgui_host() : owner)
if(!camTurf)
return
camera_diff_x = camTurf.x
camera_diff_y = camTurf.y
camera_diff_z = camTurf.z
var/list/visible_turfs = list()
for(var/turf/T in range(1, camTurf))
visible_turfs += T
cam_screen.vis_contents = visible_turfs
cam_background.icon_state = "clear"
cam_background.fill_rect(1, 1, 3, 3)
local_skybox.cut_overlays()
local_skybox.add_overlay(SSskybox.get_skybox(get_z(camTurf)))
local_skybox.scale_to_view(3)
local_skybox.set_position("CENTER", "CENTER", (world.maxx>>1) - camTurf.x, (world.maxy>>1) - camTurf.y)
/datum/tgui_module/appearance_changer/proc/update_dna()
var/mob/living/carbon/human/target = owner
if(customize_usr)
if(!ishuman(usr))
return TRUE
target = usr
if(target && (flags & APPEARANCE_UPDATE_DNA))
target.update_dna()
/datum/tgui_module/appearance_changer/proc/can_change(var/flag)
var/mob/living/carbon/human/target = owner
if(customize_usr)
if(!ishuman(usr))
return TRUE
target = usr
return target && (flags & flag)
/datum/tgui_module/appearance_changer/proc/can_change_skin_tone()
var/mob/living/carbon/human/target = owner
if(customize_usr)
if(!ishuman(usr))
return TRUE
target = usr
return target && (flags & APPEARANCE_SKIN) && target.species.appearance_flags & HAS_SKIN_TONE
/datum/tgui_module/appearance_changer/proc/can_change_skin_color()
var/mob/living/carbon/human/target = owner
if(customize_usr)
if(!ishuman(usr))
return TRUE
target = usr
return target && (flags & APPEARANCE_SKIN) && target.species.appearance_flags & HAS_SKIN_COLOR
/datum/tgui_module/appearance_changer/proc/cut_and_generate_data()
// Making the assumption that the available species remain constant
valid_facial_hairstyles.Cut()
valid_facial_hairstyles.Cut()
generate_data()
/datum/tgui_module/appearance_changer/proc/generate_data()
var/mob/living/carbon/human/target = owner
if(customize_usr)
if(!ishuman(usr))
return TRUE
target = usr
if(!target)
return
if(!valid_species.len)
valid_species = target.generate_valid_species(check_whitelist, whitelist, blacklist)
if(!valid_hairstyles.len || !valid_facial_hairstyles.len)
valid_hairstyles = target.generate_valid_hairstyles(check_gender = 0)
valid_facial_hairstyles = target.generate_valid_facial_hairstyles()
/datum/tgui_module/appearance_changer/proc/get_genders()
var/mob/living/carbon/human/target = owner
if(customize_usr)
if(!ishuman(usr))
return TRUE
target = usr
var/datum/species/S = target.species
var/list/possible_genders = S.genders
if(!target.internal_organs_by_name["cell"])
return possible_genders
possible_genders = possible_genders.Copy()
possible_genders |= NEUTER
return possible_genders
/datum/tgui_module/appearance_changer/mirror
name = "SalonPro Nano-Mirror&trade;"
flags = APPEARANCE_ALL_HAIR
customize_usr = TRUE
/datum/tgui_module/appearance_changer/mirror/coskit
name = "SalonPro Porta-Makeover Deluxe&trade;"
+31 -62
View File
@@ -37,37 +37,7 @@
cam_screen.del_on_map_removal = FALSE
cam_screen.screen_loc = "[map_name]:1,1"
cam_plane_masters = list()
// 'Utility' planes
cam_plane_masters += new /obj/screen/plane_master/fullbright //Lighting system (lighting_overlay objects)
cam_plane_masters += new /obj/screen/plane_master/lighting //Lighting system (but different!)
cam_plane_masters += new /obj/screen/plane_master/ghosts //Ghosts!
cam_plane_masters += new /obj/screen/plane_master{plane = PLANE_AI_EYE} //AI Eye!
cam_plane_masters += new /obj/screen/plane_master{plane = PLANE_CH_STATUS} //Status is the synth/human icon left side of medhuds
cam_plane_masters += new /obj/screen/plane_master{plane = PLANE_CH_HEALTH} //Health bar
cam_plane_masters += new /obj/screen/plane_master{plane = PLANE_CH_LIFE} //Alive-or-not icon
cam_plane_masters += new /obj/screen/plane_master{plane = PLANE_CH_ID} //Job ID icon
cam_plane_masters += new /obj/screen/plane_master{plane = PLANE_CH_WANTED} //Wanted status
cam_plane_masters += new /obj/screen/plane_master{plane = PLANE_CH_IMPLOYAL} //Loyalty implants
cam_plane_masters += new /obj/screen/plane_master{plane = PLANE_CH_IMPTRACK} //Tracking implants
cam_plane_masters += new /obj/screen/plane_master{plane = PLANE_CH_IMPCHEM} //Chemical implants
cam_plane_masters += new /obj/screen/plane_master{plane = PLANE_CH_SPECIAL} //"Special" role stuff
cam_plane_masters += new /obj/screen/plane_master{plane = PLANE_CH_STATUS_OOC} //OOC status HUD
cam_plane_masters += new /obj/screen/plane_master{plane = PLANE_ADMIN1} //For admin use
cam_plane_masters += new /obj/screen/plane_master{plane = PLANE_ADMIN2} //For admin use
cam_plane_masters += new /obj/screen/plane_master{plane = PLANE_ADMIN3} //For admin use
cam_plane_masters += new /obj/screen/plane_master{plane = PLANE_MESONS} //Meson-specific things like open ceilings.
cam_plane_masters += new /obj/screen/plane_master{plane = PLANE_BUILDMODE} //Things that only show up while in build mode
// Real tangible stuff planes
cam_plane_masters += new /obj/screen/plane_master/main{plane = TURF_PLANE}
cam_plane_masters += new /obj/screen/plane_master/main{plane = OBJ_PLANE}
cam_plane_masters += new /obj/screen/plane_master/main{plane = MOB_PLANE}
cam_plane_masters += new /obj/screen/plane_master/cloaked //Cloaked atoms!
cam_plane_masters = get_plane_masters()
for(var/plane in cam_plane_masters)
var/obj/screen/instance = plane
@@ -144,7 +114,7 @@
return data
/datum/tgui_module/camera/tgui_static_data(mob/user)
var/list/data = list()
var/list/data = ..()
data["mapRef"] = map_name
var/list/cameras = get_available_cameras(user)
data["cameras"] = list()
@@ -160,7 +130,10 @@
/datum/tgui_module/camera/tgui_act(action, params)
if(..())
return
return TRUE
if(action && !issilicon(usr))
playsound(tgui_host(), "terminal_type", 50, 1)
if(action == "switch_camera")
var/c_tag = params["name"]
@@ -168,11 +141,33 @@
var/obj/machinery/camera/C = cameras["[ckey(c_tag)]"]
active_camera = C
playsound(tgui_host(), get_sfx("terminal_type"), 25, FALSE)
reload_cameraview()
return TRUE
if(action == "pan")
var/dir = params["dir"]
var/turf/T = get_turf(active_camera)
for(var/i in 1 to 10)
T = get_step(T, dir)
if(T)
var/obj/machinery/camera/target
var/best_dist = INFINITY
var/list/possible_cameras = get_available_cameras(usr)
for(var/obj/machinery/camera/C in get_area(T))
if(!possible_cameras["[ckey(C.c_tag)]"])
continue
var/dist = get_dist(C, T)
if(dist < best_dist)
best_dist = dist
target = C
if(target)
active_camera = target
playsound(tgui_host(), get_sfx("terminal_type"), 25, FALSE)
reload_cameraview()
. = TRUE
/datum/tgui_module/camera/proc/differential_check()
var/turf/T = get_turf(active_camera)
if(T)
@@ -284,33 +279,7 @@
// If/when that is done, just move all the PC_ specific data and stuff to the modular computers themselves
// instead of copying this approach here.
/datum/tgui_module/camera/ntos
tgui_id = "NtosCameraConsole"
/datum/tgui_module/camera/ntos/tgui_state()
return GLOB.tgui_ntos_state
/datum/tgui_module/camera/ntos/tgui_static_data()
. = ..()
var/datum/computer_file/program/host = tgui_host()
if(istype(host) && host.computer)
. += host.computer.get_header_data()
/datum/tgui_module/camera/ntos/tgui_act(action, params)
if(..())
return
var/datum/computer_file/program/host = tgui_host()
if(istype(host) && host.computer)
if(action == "PC_exit")
host.computer.kill_program()
return TRUE
if(action == "PC_shutdown")
host.computer.shutdown_computer()
return TRUE
if(action == "PC_minimize")
host.computer.minimize_program(usr)
return TRUE
ntos = TRUE
// ERT Version provides some additional networks.
/datum/tgui_module/camera/ntos/ert
+6 -29
View File
@@ -5,6 +5,9 @@
/datum/tgui_module/crew_monitor/tgui_act(action, params, datum/tgui/ui)
if(..())
return TRUE
if(action && !issilicon(usr))
playsound(tgui_host(), "terminal_type", 50, 1)
var/turf/T = get_turf(usr)
if(!T || !(T.z in using_map.player_levels))
@@ -21,7 +24,7 @@
return TRUE
if("setZLevel")
ui.set_map_z_level(params["mapZLevel"])
SStgui.update_uis(src)
return TRUE
/datum/tgui_module/crew_monitor/tgui_interact(mob/user, datum/tgui/ui = null)
var/z = get_z(user)
@@ -40,7 +43,7 @@
ui.open()
/datum/tgui_module/crew_monitor/tgui_data(mob/user, ui_key = "main", datum/tgui_state/state = GLOB.tgui_default_state)
/datum/tgui_module/crew_monitor/tgui_data(mob/user)
var/data[0]
data["isAI"] = isAI(user)
@@ -56,33 +59,7 @@
return data
/datum/tgui_module/crew_monitor/ntos
tgui_id = "NtosCrewMonitor"
/datum/tgui_module/crew_monitor/ntos/tgui_state(mob/user)
return GLOB.tgui_ntos_state
/datum/tgui_module/crew_monitor/ntos/tgui_static_data()
. = ..()
var/datum/computer_file/program/host = tgui_host()
if(istype(host) && host.computer)
. += host.computer.get_header_data()
/datum/tgui_module/crew_monitor/ntos/tgui_act(action, params)
if(..())
return
var/datum/computer_file/program/host = tgui_host()
if(istype(host) && host.computer)
if(action == "PC_exit")
host.computer.kill_program()
return TRUE
if(action == "PC_shutdown")
host.computer.shutdown_computer()
return TRUE
if(action == "PC_minimize")
host.computer.minimize_program(usr)
return TRUE
ntos = TRUE
// Subtype for self_state
/datum/tgui_module/crew_monitor/robot
+20 -4
View File
@@ -36,13 +36,29 @@ GLOBAL_DATUM_INIT(tgui_default_state, /datum/tgui_state/default, new)
return STATUS_DISABLED // Otherwise they can keep the UI open.
/mob/living/silicon/ai/default_can_use_tgui_topic(src_object)
. = shared_tgui_interaction(src_object)
if(. < STATUS_INTERACTIVE)
. = shared_tgui_interaction()
if(. != STATUS_INTERACTIVE)
return
// The AI can interact with anything it can see nearby, or with cameras while wireless control is enabled.
if(!control_disabled && can_see(src_object))
// Prevents the AI from using Topic on admin levels (by for example viewing through the court/thunderdome cameras)
// unless it's on the same level as the object it's interacting with.
var/turf/T = get_turf(src_object)
if(!T || !(z == T.z || (T.z in using_map.player_levels)))
return STATUS_CLOSE
// If an object is in view then we can interact with it
if(src_object in view(client.view, src))
return STATUS_INTERACTIVE
// If we're installed in a chassi, rather than transfered to an inteliCard or other container, then check if we have camera view
if(is_in_chassis())
//stop AIs from leaving windows open and using then after they lose vision
if(cameranet && !cameranet.checkTurfVis(get_turf(src_object)))
return STATUS_CLOSE
return STATUS_INTERACTIVE
else if(get_dist(src_object, src) <= client.view) // View does not return what one would expect while installed in an inteliCard
return STATUS_INTERACTIVE
return STATUS_CLOSE
/mob/living/simple_animal/default_can_use_tgui_topic(src_object)
+20 -6
View File
@@ -34,8 +34,12 @@
var/status = STATUS_INTERACTIVE
/// Topic state used to determine status/interactability.
var/datum/tgui_state/state = null
// The map z-level to display.
/// The map z-level to display.
var/map_z_level = 1
/// The Parent UI
var/datum/tgui/parent_ui
/// Children of this UI
var/list/children = list()
/**
* public
@@ -46,12 +50,13 @@
* required src_object datum The object or datum which owns the UI.
* required interface string The interface used to render the UI.
* optional title string The title of the UI.
* optional parent_ui datum/tgui The parent of this UI.
* optional ui_x int Deprecated: Window width.
* optional ui_y int Deprecated: Window height.
*
* return datum/tgui The requested UI.
*/
/datum/tgui/New(mob/user, datum/src_object, interface, title, ui_x, ui_y)
/datum/tgui/New(mob/user, datum/src_object, interface, title, datum/tgui/parent_ui, ui_x, ui_y)
src.user = user
src.src_object = src_object
src.window_key = "[REF(src_object)]-main"
@@ -59,6 +64,9 @@
if(title)
src.title = title
src.state = src_object.tgui_state()
src.parent_ui = parent_ui
if(parent_ui)
parent_ui.children += src
// Deprecated
if(ui_x && ui_y)
src.window_size = list(ui_x, ui_y)
@@ -100,10 +108,13 @@
*
* Close the UI, and all its children.
*/
/datum/tgui/proc/close(can_be_suspended = TRUE)
/datum/tgui/proc/close(can_be_suspended = TRUE, logout = FALSE)
if(closing)
return
closing = TRUE
for(var/datum/tgui/child in children)
child.close(can_be_suspended, logout)
children.Cut()
// If we don't have window_id, open proc did not have the opportunity
// to finish, therefore it's safe to skip this whole block.
if(window)
@@ -111,10 +122,11 @@
// and we want to keep them around, to allow user to read
// the error message properly.
window.release_lock()
window.close(can_be_suspended)
window.close(can_be_suspended, logout)
src_object.tgui_close(user)
SStgui.on_close(src)
state = null
parent_ui = null
qdel(src)
/**
@@ -209,7 +221,7 @@
"observer" = isobserver(user),
),
)
var/data = custom_data || with_data && src_object.tgui_data(user)
var/data = custom_data || with_data && src_object.tgui_data(user, src, state)
if(data)
json_data["data"] = data
var/static_data = with_static_data && src_object.tgui_static_data(user)
@@ -244,7 +256,7 @@
return
// Update through a normal call to ui_interact
if(status != STATUS_DISABLED && (autoupdate || force))
src_object.tgui_interact(user, src)
src_object.tgui_interact(user, src, parent_ui)
return
// Update status only
var/needs_update = process_status()
@@ -262,6 +274,8 @@
/datum/tgui/proc/process_status()
var/prev_status = status
status = src_object.tgui_status(user, state)
if(parent_ui)
status = min(status, parent_ui.status)
return prev_status != status
/datum/tgui/proc/log_message(message)
+12 -5
View File
@@ -133,13 +133,19 @@
*
* optional can_be_suspended bool
*/
/datum/tgui_window/proc/close(can_be_suspended = TRUE)
/datum/tgui_window/proc/close(can_be_suspended = TRUE, logout = FALSE)
if(!client)
return
if(can_be_suspended && can_be_suspended())
log_tgui(client, "[id]/close: suspending")
status = TGUI_WINDOW_READY
send_message("suspend")
// You would think that BYOND would null out client or make it stop passing istypes or, y'know, ANYTHING during
// logout, but nope! It appears to be perfectly valid to call winset by every means we can measure in Logout,
// and yet it causes a bad client runtime. To avoid that happening, we just have to know if we're in Logout or
// not.
if(!logout && client)
winset(client, null, "mapwindow.map.focus=true")
return
log_tgui(client, "[id]/close")
release_lock()
@@ -149,7 +155,8 @@
// to read the error message.
if(!fatally_errored)
client << browse(null, "window=[id]")
if(!logout && client)
winset(client, null, "mapwindow.map.focus=true")
/**
* public
*
@@ -189,9 +196,9 @@
/datum/tgui_window/proc/send_asset(datum/asset/asset)
if(!client || !asset)
return
// if(istype(asset, /datum/asset/spritesheet))
// var/datum/asset/spritesheet/spritesheet = asset
// send_message("asset/stylesheet", spritesheet.css_filename())
if(istype(asset, /datum/asset/spritesheet))
var/datum/asset/spritesheet/spritesheet = asset
send_message("asset/stylesheet", spritesheet.css_filename())
send_message("asset/mappings", asset.get_url_mappings())
sent_assets += list(asset)
asset.send(client)
+57 -66
View File
@@ -26,7 +26,7 @@
O.loc = src
user.visible_message("[user] adds \a [O] to \the [src]!", "You add \a [O] to \the [src]!")
SSnanoui.update_uis(src)
SStgui.update_uis(src)
src.attack_hand(user)
@@ -36,27 +36,32 @@
icon_state = "centrifuge_moving"
/obj/machinery/computer/centrifuge/attack_hand(var/mob/user as mob)
if(..()) return
ui_interact(user)
if(..())
return
tgui_interact(user)
/obj/machinery/computer/centrifuge/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
user.set_machine(src)
/obj/machinery/computer/centrifuge/tgui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, "IsolationCentrifuge", name)
ui.open()
var/data[0]
/obj/machinery/computer/centrifuge/tgui_data(mob/user)
var/list/data = list()
data["antibodies"] = null
data["pathogens"] = null
data["pathogens"] = list()
data["is_antibody_sample"] = null
data["busy"] = null
data["sample_inserted"] = !!sample
if (curing)
if(curing)
data["busy"] = "Isolating antibodies..."
else if (isolating)
else if(isolating)
data["busy"] = "Isolating pathogens..."
else
data["sample_inserted"] = !!sample
if (sample)
if(sample)
var/datum/reagent/blood/B = locate(/datum/reagent/blood) in sample.reagents.reagent_list
if (B)
if(B)
data["antibodies"] = antigens2string(B.data["antibodies"], none=null)
var/list/pathogens[0]
@@ -65,8 +70,7 @@
var/datum/disease2/disease/V = virus[ID]
pathogens.Add(list(list("name" = V.name(), "spread_type" = V.spreadtype, "reference" = "\ref[V]")))
if (pathogens.len > 0)
data["pathogens"] = pathogens
data["pathogens"] = pathogens
else
var/datum/reagent/antibodies/A = locate(/datum/reagent/antibodies) in sample.reagents.reagent_list
@@ -74,103 +78,90 @@
data["antibodies"] = antigens2string(A.data["antibodies"], none=null)
data["is_antibody_sample"] = 1
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open)
if (!ui)
ui = new(user, src, ui_key, "isolation_centrifuge.tmpl", src.name, 400, 500)
ui.set_initial_data(data)
ui.open()
return data
/obj/machinery/computer/centrifuge/process()
..()
if (stat & (NOPOWER|BROKEN)) return
if(stat & (NOPOWER|BROKEN)) return
if (curing)
if(curing)
curing -= 1
if (curing == 0)
if(curing == 0)
cure()
if (isolating)
if(isolating)
isolating -= 1
if(isolating == 0)
isolate()
/obj/machinery/computer/centrifuge/Topic(href, href_list)
if (..()) return 1
/obj/machinery/computer/centrifuge/tgui_act(action, params)
if(..())
return TRUE
var/mob/user = usr
var/datum/nanoui/ui = SSnanoui.get_open_ui(user, src, "main")
add_fingerprint(user)
src.add_fingerprint(user)
if (href_list["close"])
user.unset_machine()
ui.close()
return 0
if (href_list["print"])
print(user)
return 1
if(href_list["isolate"])
var/datum/reagent/blood/B = locate(/datum/reagent/blood) in sample.reagents.reagent_list
if (B)
var/datum/disease2/disease/virus = locate(href_list["isolate"])
virus2 = virus.getcopy()
isolating = 40
update_icon()
return 1
switch(href_list["action"])
if ("antibody")
switch(action)
if("print")
print(user)
. = TRUE
if("isolate")
var/datum/reagent/blood/B = locate(/datum/reagent/blood) in sample.reagents.reagent_list
if(B)
var/datum/disease2/disease/virus = locate(params["isolate"])
virus2 = virus.getcopy()
isolating = 40
update_icon()
. = TRUE
if("antibody")
var/delay = 20
var/datum/reagent/blood/B = locate(/datum/reagent/blood) in sample.reagents.reagent_list
if (!B)
if(!B)
state("\The [src] buzzes, \"No antibody carrier detected.\"", "blue")
return 1
return TRUE
var/has_toxins = locate(/datum/reagent/toxin) in sample.reagents.reagent_list
var/has_radium = sample.reagents.has_reagent("radium")
if (has_toxins || has_radium)
if(has_toxins || has_radium)
state("\The [src] beeps, \"Pathogen purging speed above nominal.\"", "blue")
if (has_toxins)
if(has_toxins)
delay = delay/2
if (has_radium)
if(has_radium)
delay = delay/2
curing = round(delay)
playsound(src, 'sound/machines/juicer.ogg', 50, 1)
update_icon()
return 1
. = TRUE
if("sample")
if(sample)
sample.loc = src.loc
sample = null
return 1
. = TRUE
return 0
/obj/machinery/computer/centrifuge/proc/cure()
if (!sample) return
if(!sample) return
var/datum/reagent/blood/B = locate(/datum/reagent/blood) in sample.reagents.reagent_list
if (!B) return
if(!B) return
var/list/data = list("antibodies" = B.data["antibodies"])
var/amt= sample.reagents.get_reagent_amount("blood")
sample.reagents.remove_reagent("blood", amt)
sample.reagents.add_reagent("antibodies", amt, data)
SSnanoui.update_uis(src)
SStgui.update_uis(src)
update_icon()
ping("\The [src] pings, \"Antibody isolated.\"")
/obj/machinery/computer/centrifuge/proc/isolate()
if (!sample) return
if(!sample) return
var/obj/item/weapon/virusdish/dish = new/obj/item/weapon/virusdish(loc)
dish.virus2 = virus2
virus2 = null
SSnanoui.update_uis(src)
SStgui.update_uis(src)
update_icon()
ping("\The [src] pings, \"Pathogen isolated.\"")
@@ -182,20 +173,20 @@
<large><u>Sample:</u></large> [sample.name]<br>
"}
if (user)
if(user)
P.info += "<u>Generated By:</u> [user.name]<br>"
P.info += "<hr>"
var/datum/reagent/blood/B = locate(/datum/reagent/blood) in sample.reagents.reagent_list
if (B)
if(B)
P.info += "<u>Antibodies:</u> "
P.info += antigens2string(B.data["antibodies"])
P.info += "<br>"
var/list/virus = B.data["virus2"]
P.info += "<u>Pathogens:</u> <br>"
if (virus.len > 0)
if(virus.len > 0)
for (var/ID in virus)
var/datum/disease2/disease/V = virus[ID]
P.info += "[V.name()]<br>"
@@ -204,7 +195,7 @@
else
var/datum/reagent/antibodies/A = locate(/datum/reagent/antibodies) in sample.reagents.reagent_list
if (A)
if(A)
P.info += "The following antibodies have been isolated from the blood sample: "
P.info += antigens2string(A.data["antibodies"])
P.info += "<br>"
+22
View File
@@ -238,6 +238,26 @@ var/global/list/virusDB = list()
return r
/datum/disease2/disease/proc/get_tgui_info()
. = list(
"name" = name(),
"spreadtype" = spreadtype,
"antigen" = antigens2string(antigen),
"rate" = stageprob * 10,
"resistance" = resistance,
"species" = jointext(affected_species, ", "),
"symptoms" = list(),
"ref" = "\ref[src]",
)
for(var/datum/disease2/effectholder/E in effects)
.["symptoms"].Add(list(list(
"stage" = E.stage,
"name" = E.effect.name,
"strength" = "[E.multiplier >= 3 ? "Severe" : E.multiplier > 1 ? "Above Average" : "Average"]",
"aggressiveness" = E.chance * 15,
)))
/datum/disease2/disease/proc/addToDB()
if ("[uniqueID]" in virusDB)
return 0
@@ -245,6 +265,8 @@ var/global/list/virusDB = list()
v.fields["id"] = uniqueID
v.fields["name"] = name()
v.fields["description"] = get_info()
v.fields["tgui_description"] = get_tgui_info()
v.fields["tgui_description"]["record"] = "\ref[v]"
v.fields["antigen"] = antigens2string(antigen)
v.fields["spread type"] = spreadtype
virusDB["[uniqueID]"] = v
+86 -86
View File
@@ -20,7 +20,7 @@
if(istype(I,/obj/item/weapon/virusdish))
var/mob/living/carbon/c = user
if (dish)
if(dish)
to_chat(user, "\The [src] is already loaded.")
return
@@ -40,36 +40,46 @@
return src.attack_hand(user)
/obj/machinery/computer/diseasesplicer/attack_hand(var/mob/user as mob)
if(..()) return
ui_interact(user)
if(..())
return TRUE
tgui_interact(user)
/obj/machinery/computer/diseasesplicer/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
user.set_machine(src)
/obj/machinery/computer/diseasesplicer/tgui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, "DiseaseSplicer", name)
ui.open()
var/data[0]
/obj/machinery/computer/diseasesplicer/tgui_data(mob/user)
var/list/data = list()
data["dish_inserted"] = !!dish
data["growth"] = 0
data["affected_species"] = null
if (memorybank)
data["buffer"] = null
if(memorybank)
data["buffer"] = list("name" = (analysed ? memorybank.effect.name : "Unknown Symptom"), "stage" = memorybank.effect.stage)
if (species_buffer)
data["species_buffer"] = null
if(species_buffer)
data["species_buffer"] = analysed ? jointext(species_buffer, ", ") : "Unknown Species"
if (splicing)
data["effects"] = null
data["info"] = null
data["growth"] = 0
data["affected_species"] = null
data["busy"] = null
if(splicing)
data["busy"] = "Splicing..."
else if (scanning)
else if(scanning)
data["busy"] = "Scanning..."
else if (burning)
else if(burning)
data["busy"] = "Copying data to disk..."
else if (dish)
else if(dish)
data["growth"] = min(dish.growth, 100)
if (dish.virus2)
if (dish.virus2.affected_species)
data["affected_species"] = dish.analysed ? jointext(dish.virus2.affected_species, ", ") : "Unknown"
if(dish.virus2)
if(dish.virus2.affected_species)
data["affected_species"] = dish.analysed ? dish.virus2.affected_species : list()
if (dish.growth >= 50)
if(dish.growth >= 50)
var/list/effects[0]
for (var/datum/disease2/effectholder/e in dish.virus2.effects)
effects.Add(list(list("name" = (dish.analysed ? e.effect.name : "Unknown"), "stage" = (e.stage), "reference" = "\ref[e]")))
@@ -81,11 +91,7 @@
else
data["info"] = "No dish loaded."
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open)
if (!ui)
ui = new(user, src, ui_key, "disease_splicer.tmpl", src.name, 400, 600)
ui.set_initial_data(data)
ui.open()
return data
/obj/machinery/computer/diseasesplicer/process()
if(stat & (NOPOWER|BROKEN))
@@ -95,100 +101,94 @@
scanning -= 1
if(!scanning)
ping("\The [src] pings, \"Analysis complete.\"")
SSnanoui.update_uis(src)
SStgui.update_uis(src)
if(splicing)
splicing -= 1
if(!splicing)
ping("\The [src] pings, \"Splicing operation complete.\"")
SSnanoui.update_uis(src)
SStgui.update_uis(src)
if(burning)
burning -= 1
if(!burning)
var/obj/item/weapon/diseasedisk/d = new /obj/item/weapon/diseasedisk(src.loc)
d.analysed = analysed
if(analysed)
if (memorybank)
if(memorybank)
d.name = "[memorybank.effect.name] GNA disk (Stage: [memorybank.effect.stage])"
d.effect = memorybank
else if (species_buffer)
else if(species_buffer)
d.name = "[jointext(species_buffer, ", ")] GNA disk"
d.species = species_buffer
else
if (memorybank)
if(memorybank)
d.name = "Unknown GNA disk (Stage: [memorybank.effect.stage])"
d.effect = memorybank
else if (species_buffer)
else if(species_buffer)
d.name = "Unknown Species GNA disk"
d.species = species_buffer
ping("\The [src] pings, \"Backup disk saved.\"")
SSnanoui.update_uis(src)
SStgui.update_uis(src)
/obj/machinery/computer/diseasesplicer/Topic(href, href_list)
if(..()) return 1
/obj/machinery/computer/diseasesplicer/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state)
if(..())
return TRUE
var/mob/user = usr
var/datum/nanoui/ui = SSnanoui.get_open_ui(user, src, "main")
add_fingerprint(user)
src.add_fingerprint(user)
switch(action)
if("grab")
if(dish)
memorybank = locate(params["grab"])
species_buffer = null
analysed = dish.analysed
dish = null
scanning = 10
. = TRUE
if (href_list["close"])
user.unset_machine()
ui.close()
return 0
if("affected_species")
if(dish)
memorybank = null
species_buffer = dish.virus2.affected_species
analysed = dish.analysed
dish = null
scanning = 10
. = TRUE
if (href_list["grab"])
if (dish)
memorybank = locate(href_list["grab"])
species_buffer = null
analysed = dish.analysed
dish = null
scanning = 10
return 1
if("eject")
if(dish)
dish.loc = src.loc
dish = null
. = TRUE
if (href_list["affected_species"])
if (dish)
memorybank = null
species_buffer = dish.virus2.affected_species
analysed = dish.analysed
dish = null
scanning = 10
return 1
if("splice")
if(dish)
var/target = text2num(params["splice"]) // target = 1 to 4 for effects, 5 for species
if(memorybank && 0 < target && target <= 4)
if(target < memorybank.effect.stage) return // too powerful, catching this for href exploit prevention
if(href_list["eject"])
if (dish)
dish.loc = src.loc
dish = null
return 1
var/datum/disease2/effectholder/target_holder
var/list/illegal_types = list()
for(var/datum/disease2/effectholder/e in dish.virus2.effects)
if(e.stage == target)
target_holder = e
else
illegal_types += e.effect.type
if(memorybank.effect.type in illegal_types) return
target_holder.effect = memorybank.effect
if(href_list["splice"])
if(dish)
var/target = text2num(href_list["splice"]) // target = 1 to 4 for effects, 5 for species
if(memorybank && 0 < target && target <= 4)
if(target < memorybank.effect.stage) return // too powerful, catching this for href exploit prevention
else if(species_buffer && target == 5)
dish.virus2.affected_species = species_buffer
var/datum/disease2/effectholder/target_holder
var/list/illegal_types = list()
for(var/datum/disease2/effectholder/e in dish.virus2.effects)
if(e.stage == target)
target_holder = e
else
illegal_types += e.effect.type
if(memorybank.effect.type in illegal_types) return
target_holder.effect = memorybank.effect
else
return
else if(species_buffer && target == 5)
dish.virus2.affected_species = species_buffer
splicing = 10
dish.virus2.uniqueID = rand(0,10000)
. = TRUE
else
return
if("disk")
burning = 10
. = TRUE
splicing = 10
dish.virus2.uniqueID = rand(0,10000)
return 1
if(href_list["disk"])
burning = 10
return 1
return 0
+64 -70
View File
@@ -30,7 +30,7 @@
O.loc = src
user.visible_message("[user] adds \a [O] to \the [src]!", "You add \a [O] to \the [src]!")
SSnanoui.update_uis(src)
SStgui.update_uis(src)
src.attack_hand(user)
return
@@ -46,17 +46,23 @@
O.loc = src
user.visible_message("[user] adds \a [O] to \the [src]!", "You add \a [O] to \the [src]!")
SSnanoui.update_uis(src)
SStgui.update_uis(src)
src.attack_hand(user)
/obj/machinery/disease2/incubator/attack_hand(mob/user as mob)
if(stat & (NOPOWER|BROKEN)) return
ui_interact(user)
if(stat & (NOPOWER|BROKEN))
return
tgui_interact(user)
/obj/machinery/disease2/incubator/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
user.set_machine(src)
/obj/machinery/disease2/incubator/tgui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, "DishIncubator", name)
ui.set_autoupdate(FALSE)
ui.open()
/obj/machinery/disease2/incubator/tgui_data(mob/user)
var/data[0]
data["chemicals_inserted"] = !!beaker
data["dish_inserted"] = !!dish
@@ -74,23 +80,19 @@
data["can_breed_virus"] = null
data["blood_already_infected"] = null
if (beaker)
if(beaker)
var/datum/reagent/blood/B = locate(/datum/reagent/blood) in beaker.reagents.reagent_list
data["can_breed_virus"] = dish && dish.virus2 && B
if (B)
if (!B.data["virus2"])
if(B)
if(!B.data["virus2"])
B.data["virus2"] = list()
var/list/virus = B.data["virus2"]
for (var/ID in virus)
data["blood_already_infected"] = virus[ID]
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open)
if (!ui)
ui = new(user, src, ui_key, "dish_incubator.tmpl", src.name, 400, 600)
ui.set_initial_data(data)
ui.open()
return data
/obj/machinery/disease2/incubator/process()
if(dish && on && dish.virus2)
@@ -105,7 +107,7 @@
foodsupply -= 1
dish.growth += 3
SSnanoui.update_uis(src)
SStgui.update_uis(src)
if(radiation)
if(radiation > 50 & prob(5))
@@ -118,90 +120,82 @@
else if(prob(5))
dish.virus2.minormutate()
radiation -= 1
SSnanoui.update_uis(src)
SStgui.update_uis(src)
if(toxins && prob(5))
dish.virus2.infectionchance -= 1
SSnanoui.update_uis(src)
SStgui.update_uis(src)
if(toxins > 50)
dish.growth = 0
dish.virus2 = null
SSnanoui.update_uis(src)
SStgui.update_uis(src)
else if(!dish)
on = 0
icon_state = "incubator"
SSnanoui.update_uis(src)
SStgui.update_uis(src)
if(beaker)
if(foodsupply < 100 && beaker.reagents.remove_reagent("virusfood",5))
if(foodsupply + 10 <= 100)
foodsupply += 10
SSnanoui.update_uis(src)
SStgui.update_uis(src)
if (locate(/datum/reagent/toxin) in beaker.reagents.reagent_list && toxins < 100)
if(locate(/datum/reagent/toxin) in beaker.reagents.reagent_list && toxins < 100)
for(var/datum/reagent/toxin/T in beaker.reagents.reagent_list)
toxins += max(T.strength,1)
beaker.reagents.remove_reagent(T.id,1)
if(toxins > 100)
toxins = 100
break
SSnanoui.update_uis(src)
SStgui.update_uis(src)
/obj/machinery/disease2/incubator/Topic(href, href_list)
if (..()) return 1
/obj/machinery/disease2/incubator/tgui_act(action, params)
if(..())
return TRUE
var/mob/user = usr
var/datum/nanoui/ui = SSnanoui.get_open_ui(user, src, "main")
add_fingerprint(user)
switch(action)
if("ejectchem")
if(beaker)
beaker.loc = src.loc
beaker = null
. = TRUE
src.add_fingerprint(user)
if("power")
if(dish)
on = !on
icon_state = on ? "incubator_on" : "incubator"
. = TRUE
if (href_list["close"])
user.unset_machine()
ui.close()
return 0
if("ejectdish")
if(dish)
dish.loc = src.loc
dish = null
. = TRUE
if (href_list["ejectchem"])
if(beaker)
beaker.loc = src.loc
beaker = null
return 1
if("rad")
radiation = min(100, radiation + 10)
. = TRUE
if (href_list["power"])
if (dish)
on = !on
icon_state = on ? "incubator_on" : "incubator"
return 1
if("flush")
radiation = 0
toxins = 0
foodsupply = 0
. = TRUE
if (href_list["ejectdish"])
if(dish)
dish.loc = src.loc
dish = null
return 1
if("virus")
if(!dish)
return TRUE
if (href_list["rad"])
radiation = min(100, radiation + 10)
return 1
var/datum/reagent/blood/B = locate(/datum/reagent/blood) in beaker.reagents.reagent_list
if(!B)
return TRUE
if (href_list["flush"])
radiation = 0
toxins = 0
foodsupply = 0
return 1
if(!B.data["virus2"])
B.data["virus2"] = list()
if(href_list["virus"])
if (!dish)
return 1
var/list/virus = list("[dish.virus2.uniqueID]" = dish.virus2.getcopy())
B.data["virus2"] += virus
var/datum/reagent/blood/B = locate(/datum/reagent/blood) in beaker.reagents.reagent_list
if (!B)
return 1
if (!B.data["virus2"])
B.data["virus2"] = list()
var/list/virus = list("[dish.virus2.uniqueID]" = dish.virus2.getcopy())
B.data["virus2"] += virus
ping("\The [src] pings, \"Injection complete.\"")
return 1
return 0
ping("\The [src] pings, \"Injection complete.\"")
. = TRUE
+80 -106
View File
@@ -1,8 +1,3 @@
// UI menu navigation
#define HOME "home"
#define LIST "list"
#define ENTRY "entry"
/obj/machinery/disease2/isolator/
name = "pathogenic isolator"
desc = "Used to isolate and identify diseases, allowing for comparison with a remote database."
@@ -11,9 +6,7 @@
icon = 'icons/obj/virology.dmi'
icon_state = "isolator"
var/isolating = 0
var/state = HOME
var/datum/disease2/disease/virus2 = null
var/datum/data/record/entry = null
var/obj/item/weapon/reagent_containers/syringe/sample = null
/obj/machinery/disease2/isolator/update_icon()
@@ -44,71 +37,57 @@
S.loc = src
user.visible_message("[user] adds \a [O] to \the [src]!", "You add \a [O] to \the [src]!")
SSnanoui.update_uis(src)
SStgui.update_uis(src)
update_icon()
src.attack_hand(user)
/obj/machinery/disease2/isolator/attack_hand(mob/user as mob)
if(stat & (NOPOWER|BROKEN)) return
ui_interact(user)
if(stat & (NOPOWER|BROKEN))
return
tgui_interact(user)
/obj/machinery/disease2/isolator/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
user.set_machine(src)
/obj/machinery/disease2/isolator/tgui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, "PathogenicIsolator", name)
ui.open()
var/data[0]
/obj/machinery/disease2/isolator/tgui_data(mob/user)
var/list/data = list()
data["syringe_inserted"] = !!sample
data["isolating"] = isolating
data["pathogen_pool"] = null
data["state"] = state
data["entry"] = entry
data["can_print"] = (state != HOME || sample) && !isolating
data["can_print"] = !isolating
switch (state)
if (HOME)
if (sample)
var/list/pathogen_pool[0]
for(var/datum/reagent/blood/B in sample.reagents.reagent_list)
var/list/virus = B.data["virus2"]
for (var/ID in virus)
var/datum/disease2/disease/V = virus[ID]
var/datum/data/record/R = null
if (ID in virusDB)
R = virusDB[ID]
var/list/pathogen_pool = list()
if(sample)
for(var/datum/reagent/blood/B in sample.reagents.reagent_list)
var/list/virus = B.data["virus2"]
for (var/ID in virus)
var/datum/disease2/disease/V = virus[ID]
var/datum/data/record/R = null
if (ID in virusDB)
R = virusDB[ID]
var/mob/living/carbon/human/D = B.data["donor"]
pathogen_pool.Add(list(list(\
"name" = "[D.get_species()] [B.name]", \
"dna" = B.data["blood_DNA"], \
"unique_id" = V.uniqueID, \
"reference" = "\ref[V]", \
"is_in_database" = !!R, \
"record" = "\ref[R]")))
var/mob/living/carbon/human/D = B.data["donor"]
pathogen_pool.Add(list(list(\
"name" = "[D.get_species()] [B.name]", \
"dna" = B.data["blood_DNA"], \
"unique_id" = V.uniqueID, \
"reference" = "\ref[V]", \
"is_in_database" = !!R, \
"record" = "\ref[R]")))
data["pathogen_pool"] = pathogen_pool
if (pathogen_pool.len > 0)
data["pathogen_pool"] = pathogen_pool
if (LIST)
var/list/db[0]
for (var/ID in virusDB)
var/datum/data/record/r = virusDB[ID]
db.Add(list(list("name" = r.fields["name"], "record" = "\ref[r]")))
if (db.len > 0)
data["database"] = db
if (ENTRY)
if (entry)
var/desc = entry.fields["description"]
data["entry"] = list(\
"name" = entry.fields["name"], \
"description" = replacetext(desc, "\n", ""))
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open)
if (!ui)
ui = new(user, src, ui_key, "pathogenic_isolator.tmpl", src.name, 400, 500)
ui.set_initial_data(data)
ui.open()
var/list/db = list()
for(var/ID in virusDB)
var/datum/data/record/r = virusDB[ID]
db.Add(list(list("name" = r.fields["name"], "record" = "\ref[r]")))
data["database"] = db
data["modal"] = tgui_modal_data(src)
return data
/obj/machinery/disease2/isolator/process()
if (isolating > 0)
@@ -120,62 +99,54 @@
virus2 = null
ping("\The [src] pings, \"Viral strain isolated.\"")
SSnanoui.update_uis(src)
SStgui.update_uis(src)
update_icon()
/obj/machinery/disease2/isolator/Topic(href, href_list)
if (..()) return 1
/obj/machinery/disease2/isolator/tgui_act(action, list/params)
if(..())
return TRUE
var/mob/user = usr
var/datum/nanoui/ui = SSnanoui.get_open_ui(user, src, "main")
add_fingerprint(user)
. = TRUE
switch(tgui_modal_act(src, action, params))
if(TGUI_MODAL_ANSWER)
return
src.add_fingerprint(user)
switch(action)
if("view_entry")
var/datum/data/record/v = locate(params["vir"])
if(!istype(v))
return FALSE
tgui_modal_message(src, "virus", "", null, v.fields["tgui_description"])
return TRUE
if (href_list["close"])
user.unset_machine()
ui.close()
return 0
if("print")
print(user, params)
return TRUE
if (href_list[HOME])
state = HOME
return 1
if("isolate")
var/datum/disease2/disease/V = locate(params["isolate"])
if (V)
virus2 = V
isolating = 20
update_icon()
return TRUE
if (href_list[LIST])
state = LIST
return 1
if (href_list[ENTRY])
if (istype(locate(href_list["view"]), /datum/data/record))
entry = locate(href_list["view"])
state = ENTRY
return 1
if (href_list["print"])
print(user)
return 1
if(!sample) return 1
if (href_list["isolate"])
var/datum/disease2/disease/V = locate(href_list["isolate"])
if (V)
virus2 = V
isolating = 20
if("eject")
if(!sample)
return FALSE
sample.forceMove(loc)
sample = null
update_icon()
return 1
return TRUE
if (href_list["eject"])
sample.loc = src.loc
sample = null
update_icon()
return 1
/obj/machinery/disease2/isolator/proc/print(var/mob/user)
/obj/machinery/disease2/isolator/proc/print(mob/user, list/params)
var/obj/item/weapon/paper/P = new /obj/item/weapon/paper(loc)
switch (state)
if (HOME)
switch(params["type"])
if("patient_diagnosis")
if (!sample) return
P.name = "paper - Patient Diagnostic Report"
P.info = {"
@@ -207,7 +178,7 @@
<u>Additional Notes:</u>&nbsp;
"}
if (LIST)
if("virus_list")
P.name = "paper - Virus List"
P.info = {"
[virology_letterhead("Virus List")]
@@ -225,11 +196,14 @@
<u>Additional Notes:</u>&nbsp;
"}
if (ENTRY)
if("virus_record")
var/datum/data/record/v = locate(params["vir"])
if(!istype(v))
return FALSE
P.name = "paper - Viral Profile"
P.info = {"
[virology_letterhead("Viral Profile")]
[entry.fields["description"]]
[v.fields["description"]]
<hr>
<u>Additional Notes:</u>&nbsp;
"}