diff --git a/code/_global_vars/religion.dm b/code/_global_vars/religion.dm
new file mode 100644
index 00000000000..7dab008b193
--- /dev/null
+++ b/code/_global_vars/religion.dm
@@ -0,0 +1,8 @@
+// All religion stuff
+GLOBAL_VAR(religion)
+GLOBAL_VAR(deity)
+
+//bible
+GLOBAL_VAR(bible_name)
+GLOBAL_VAR(bible_icon_state)
+GLOBAL_VAR(bible_item_state)
diff --git a/code/controllers/subsystems/ticker.dm b/code/controllers/subsystems/ticker.dm
index 35ab98aba95..cdb2adefde3 100644
--- a/code/controllers/subsystems/ticker.dm
+++ b/code/controllers/subsystems/ticker.dm
@@ -32,12 +32,6 @@ SUBSYSTEM_DEF(ticker)
var/list/datum/mind/minds = list() // The people in the game. Used for objective tracking.
- // TODO - I am sure there is a better place these can go.
- var/Bible_icon_state // icon_state the chaplain has chosen for his bible
- var/Bible_item_state // item_state the chaplain has chosen for his bible
- var/Bible_name // name of the bible
- var/Bible_deity_name
-
var/random_players = FALSE // If set to nonzero, ALL players who latejoin or declare-ready join will have random appearances/genders
// TODO - Should this go here or in the job subsystem?
@@ -567,8 +561,4 @@ var/global/datum/controller/subsystem/ticker/ticker
minds = SSticker.minds
- Bible_icon_state = SSticker.Bible_icon_state
- Bible_item_state = SSticker.Bible_item_state
- Bible_name = SSticker.Bible_name
- Bible_deity_name = SSticker.Bible_deity_name
random_players = SSticker.random_players
diff --git a/code/datums/vending/stored_item.dm b/code/datums/vending/stored_item.dm
index 7c27776aa93..23d06c909a9 100644
--- a/code/datums/vending/stored_item.dm
+++ b/code/datums/vending/stored_item.dm
@@ -2,11 +2,11 @@
* Datum that holds the instances and information about the items stored. Currently used in SmartFridges and Vending Machines.
*/
/datum/stored_item
- var/item_name = "name" //Name of the item(s) displayed
- var/item_path = null
- var/amount = 0
- var/list/instances //What items are actually stored
- var/stored //The thing holding it is
+ var/item_name = "name" //Name of the item(s) displayed
+ var/item_path = null
+ var/amount = 0
+ var/list/instances //What items are actually stored
+ var/stored //The thing holding it is
/datum/stored_item/New(var/stored, var/path, var/name = null, var/amount = 0)
src.item_path = path
diff --git a/code/game/jobs/job/civilian_chaplain.dm b/code/game/jobs/job/civilian_chaplain.dm
index 93989d2c44f..5fba7b155dc 100644
--- a/code/game/jobs/job/civilian_chaplain.dm
+++ b/code/game/jobs/job/civilian_chaplain.dm
@@ -32,136 +32,69 @@
if(!B)
return
- spawn(0)
- var/religion_name = "Unitarianism"
- var/new_religion = sanitize(input(H, "You are the crew services officer. Would you like to change your religion? Default is Unitarianism", "Name change", religion_name), MAX_NAME_LEN)
+ if(GLOB.religion)
+ B.deity_name = GLOB.deity
+ B.name = GLOB.bible_name
+ B.icon_state = GLOB.bible_icon_state
+ B.item_state = GLOB.bible_item_state
+ to_chat(H, "There is already an established religion onboard the station. You are an acolyte of [GLOB.deity]. Defer to the [title]. ")
+ return
+
+ INVOKE_ASYNC(src, .proc/religion_prompts, H, B)
- if (!new_religion)
- new_religion = religion_name
- switch(lowertext(new_religion))
- if("unitarianism")
- B.name = "The Great Canon"
- if("christianity")
- B.name = "The Holy Bible"
- if("judaism")
- B.name = "The Torah"
- if("islam")
- B.name = "Quran"
- if("buddhism")
- B.name = "Tripitakas"
- if("hinduism")
- B.name = pick("The Srimad Bhagvatam", "The Four Vedas", "The Shiv Mahapuran", "Devi Mahatmya")
- if("neopaganism")
- B.name = "Neopagan Hymnbook"
- if("phact shintoism")
- B.name = "The Kojiki"
- if("kishari national faith")
- B.name = "The Scriptures of Kishar"
- if("pleromanism")
- B.name = "The Revised Great Canon"
- if("spectralism")
- B.name = "The Book of the Spark"
- if("hauler")
- B.name = "Histories of Captaincy"
- if("nock")
- B.name = "The Book of the First"
- if("singulitarian worship")
- B.name = "The Book of the Precursors"
- if("starlit path of angessa martei")
- B.name = "Quotations of Exalted Martei"
- if("sikhism")
- B.name = "Guru Granth Sahib"
- else
- B.name = "The Holy Book of [new_religion]"
- feedback_set_details("religion_name","[new_religion]")
+/datum/job/chaplain/proc/religion_prompts(mob/living/carbon/human/H, obj/item/weapon/storage/bible/B)
+ var/religion_name = "Unitarianism"
+ var/new_religion = sanitize(input(H, "You are the crew services officer. Would you like to change your religion? Default is Unitarianism", "Name change", religion_name), MAX_NAME_LEN)
+ if(!new_religion)
+ new_religion = religion_name
- spawn(1)
- var/deity_name = "Hashem"
- var/new_deity = sanitize(input(H, "Would you like to change your deity? Default is Hashem", "Name change", deity_name), MAX_NAME_LEN)
+ switch(lowertext(new_religion))
+ if("unitarianism")
+ B.name = "The Great Canon"
+ if("christianity")
+ B.name = "The Holy Bible"
+ if("judaism")
+ B.name = "The Torah"
+ if("islam")
+ B.name = "Quran"
+ if("buddhism")
+ B.name = "Tripitakas"
+ if("hinduism")
+ B.name = pick("The Srimad Bhagvatam", "The Four Vedas", "The Shiv Mahapuran", "Devi Mahatmya")
+ if("neopaganism")
+ B.name = "Neopagan Hymnbook"
+ if("phact shintoism")
+ B.name = "The Kojiki"
+ if("kishari national faith")
+ B.name = "The Scriptures of Kishar"
+ if("pleromanism")
+ B.name = "The Revised Great Canon"
+ if("spectralism")
+ B.name = "The Book of the Spark"
+ if("hauler")
+ B.name = "Histories of Captaincy"
+ if("nock")
+ B.name = "The Book of the First"
+ if("singulitarian worship")
+ B.name = "The Book of the Precursors"
+ if("starlit path of angessa martei")
+ B.name = "Quotations of Exalted Martei"
+ if("sikhism")
+ B.name = "Guru Granth Sahib"
+ else
+ B.name = "The Holy Book of [new_religion]"
+ feedback_set_details("religion_name","[new_religion]")
- if ((length(new_deity) == 0) || (new_deity == "Hashem") )
- new_deity = deity_name
- B.deity_name = new_deity
+ var/deity_name = "Hashem"
+ var/new_deity = sanitize(input(H, "Would you like to change your deity? Default is Hashem", "Name change", deity_name), MAX_NAME_LEN)
- var/accepted = 0
- var/outoftime = 0
- spawn(200) // 20 seconds to choose
- outoftime = 1
- var/new_book_style = "Bible"
+ if((length(new_deity) == 0) || (new_deity == "Hashem"))
+ new_deity = deity_name
+ B.deity_name = new_deity
- while(!accepted)
- if(!B) break // prevents possible runtime errors
- new_book_style = input(H,"Which bible style would you like?") in list("Bible", "Koran", "Scrapbook", "Pagan", "White Bible", "Holy Light", "Athiest", "Tome", "The King in Yellow", "Ithaqua", "Scientology", "the bible melts", "Necronomicon","Orthodox","Torah")
- switch(new_book_style)
- if("Koran")
- B.icon_state = "koran"
- B.item_state = "koran"
- if("Scrapbook")
- B.icon_state = "scrapbook"
- B.item_state = "scrapbook"
- if("White Bible")
- B.icon_state = "white"
- B.item_state = "syringe_kit"
- if("Holy Light")
- B.icon_state = "holylight"
- B.item_state = "syringe_kit"
- if("Athiest")
- B.icon_state = "athiest"
- B.item_state = "syringe_kit"
- if("Tome")
- B.icon_state = "tome"
- B.item_state = "syringe_kit"
- if("The King in Yellow")
- B.icon_state = "kingyellow"
- B.item_state = "kingyellow"
- if("Ithaqua")
- B.icon_state = "ithaqua"
- B.item_state = "ithaqua"
- if("Scientology")
- B.icon_state = "scientology"
- B.item_state = "scientology"
- if("the bible melts")
- B.icon_state = "melted"
- B.item_state = "melted"
- if("Necronomicon")
- B.icon_state = "necronomicon"
- B.item_state = "necronomicon"
- if("Pagan")
- B.icon_state = "shadows"
- B.item_state = "syringe_kit"
- if("Orthodox")
- B.icon_state = "orthodoxy"
- B.item_state = "bible"
- if("Torah")
- B.icon_state = "torah"
- B.item_state = "clipboard"
- if("Guru")
- B.icon_state = "guru"
- B.item_state = "clipboard"
- else
- B.icon_state = "bible"
- B.item_state = "bible"
+ GLOB.religion = new_religion
+ GLOB.bible_name = B.name
+ GLOB.deity = B.deity_name
+ feedback_set_details("religion_deity","[new_deity]")
+
- H.update_inv_l_hand() // so that it updates the bible's item_state in his hand
-
- switch(input(H,"Look at your bible - is this what you want?") in list("Yes","No"))
- if("Yes")
- accepted = 1
- if("No")
- if(outoftime)
- to_chat(H, "Welp, out of time, buddy. You're stuck. Next time choose faster.")
- accepted = 1
-
- if(ticker)
- ticker.Bible_icon_state = B.icon_state
- ticker.Bible_item_state = B.item_state
- ticker.Bible_name = B.name
- ticker.Bible_deity_name = B.deity_name
- feedback_set_details("religion_deity","[new_deity]")
- feedback_set_details("religion_book","[new_book_style]")
- return 1
-
-/* If you uncomment this, every time the mob preview updates it makes a new PDA. It seems to work just fine and display without it, so why this exists, haven't a clue. -Hawk
-/datum/job/chaplain/equip_preview(var/mob/living/carbon/human/H, var/alt_title)
- return equip(H, alt_title, FALSE)
-*/
diff --git a/code/game/machinery/computer/timeclock_vr.dm b/code/game/machinery/computer/timeclock_vr.dm
index 85924d79f6a..a94b3383208 100644
--- a/code/game/machinery/computer/timeclock_vr.dm
+++ b/code/game/machinery/computer/timeclock_vr.dm
@@ -66,23 +66,33 @@
if(..())
return
user.set_machine(src)
- ui_interact(user)
+ tgui_interact(user)
-/obj/machinery/computer/timeclock/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
- user.set_machine(src)
+/obj/machinery/computer/timeclock/tgui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "TimeClock", name)
+ ui.open()
+
+/obj/machinery/computer/timeclock/tgui_data(mob/user)
+ var/list/data = ..()
- var/list/data = list()
// Okay, data for showing the user's OWN PTO stuff
if(user.client)
data["department_hours"] = SANITIZE_LIST(user.client.department_hours)
data["user_name"] = "[user]"
// Data about the card that we put into it.
+ data["card"] = null
+ data["assignment"] = null
+ data["job_datum"] = null
+ data["allow_change_job"] = null
+ data["job_choices"] = null
if(card)
data["card"] = "[card]"
data["assignment"] = card.assignment
var/datum/job/job = job_master.GetJob(card.rank)
- if (job)
+ if(job)
data["job_datum"] = list(
"title" = job.title,
"departments" = english_list(job.departments),
@@ -96,46 +106,42 @@
if(job && job.timeoff_factor < 0) // Currently are Off Duty, so gotta lookup what on-duty jobs are open
data["job_choices"] = getOpenOnDutyJobs(user, job.pto_type)
- ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open)
- if (!ui)
- ui = new(user, src, ui_key, "timeclock_vr.tmpl", capitalize(src.name), 500, 520)
- ui.set_initial_data(data)
- ui.open()
+ return data
-/obj/machinery/computer/timeclock/Topic(href, href_list)
+/obj/machinery/computer/timeclock/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state)
if(..())
- return 1
- usr.set_machine(src)
- src.add_fingerprint(usr)
+ return TRUE
- if (href_list["id"])
- if(card)
- usr.put_in_hands(card)
- card = null
- else
- var/obj/item/I = usr.get_active_hand()
- if (istype(I, /obj/item/weapon/card/id) && usr.unEquip(I))
- I.forceMove(src)
- card = I
- update_icon()
- return 1
- if(href_list["switch-to-onduty-rank"])
- if(checkFace())
- if(checkCardCooldown())
- makeOnDuty(href_list["switch-to-onduty-rank"], href_list["switch-to-onduty-assignment"])
+ add_fingerprint(usr)
+
+ switch(action)
+ if("id")
+ if(card)
usr.put_in_hands(card)
card = null
- update_icon()
- return 1
- if(href_list["switch-to-offduty"])
- if(checkFace())
- if(checkCardCooldown())
- makeOffDuty()
- usr.put_in_hands(card)
- card = null
- update_icon()
- return 1
- return 1 // Return 1 to update UI
+ else
+ var/obj/item/I = usr.get_active_hand()
+ if (istype(I, /obj/item/weapon/card/id) && usr.unEquip(I))
+ I.forceMove(src)
+ card = I
+ update_icon()
+ return TRUE
+ if("switch-to-onduty-rank")
+ if(checkFace())
+ if(checkCardCooldown())
+ makeOnDuty(params["switch-to-onduty-rank"], params["switch-to-onduty-assignment"])
+ usr.put_in_hands(card)
+ card = null
+ update_icon()
+ return TRUE
+ if("switch-to-offduty")
+ if(checkFace())
+ if(checkCardCooldown())
+ makeOffDuty()
+ usr.put_in_hands(card)
+ card = null
+ update_icon()
+ return TRUE
/obj/machinery/computer/timeclock/proc/getOpenOnDutyJobs(var/mob/user, var/department)
var/list/available_jobs = list()
diff --git a/code/game/machinery/cryopod.dm b/code/game/machinery/cryopod.dm
index a133aab9b1a..2a36b5db6cf 100644
--- a/code/game/machinery/cryopod.dm
+++ b/code/game/machinery/cryopod.dm
@@ -74,97 +74,81 @@
storage_name = "Travel Oversight Control"
allow_items = 1
-/obj/machinery/computer/cryopod/attack_ai()
- attack_hand()
+/obj/machinery/computer/cryopod/attack_ai(mob/user)
+ attack_hand(user)
-/obj/machinery/computer/cryopod/attack_hand(mob/user = usr)
+/obj/machinery/computer/cryopod/attack_hand(mob/user)
if(stat & (NOPOWER|BROKEN))
return
- user.set_machine(src)
- add_fingerprint(usr)
+ tgui_interact(user)
- var/dat
+/obj/machinery/computer/cryopod/tgui_interact(mob/user, datum/tgui/ui, datum/tgui/parent_ui)
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "CryoStorageVr", storage_name) // VOREStation Edit - Use our own template for our custom data
+ ui.open()
- if(!(ticker))
- return
+/obj/machinery/computer/cryopod/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state)
+ var/list/data = ..()
- dat += "
[storage_name] "
- dat += "Welcome, [user.real_name]. "
- dat += "View storage log . "
+ data["real_name"] = user.real_name
+ data["allow_items"] = allow_items
+ data["crew"] = frozen_crew
+
+ data["items"] = list()
if(allow_items)
- dat += "View objects . "
- //dat += "Recover object . " //VOREStation Removal - Just log them.
- //dat += "Recover all objects . " //VOREStation Removal
+ for(var/F in frozen_items)
+ data["items"].Add(F) // VOREStation Edit
+ /* VOREStation Removal
+ data["items"].Add(list(list(
+ "name" = "[F]",
+ "ref" = REF(F),
+ )))
+ VOREStation Removal End */
- user << browse(dat, "window=cryopod_console")
- onclose(user, "cryopod_console")
-
-/obj/machinery/computer/cryopod/Topic(href, href_list)
+ return data
+/obj/machinery/computer/cryopod/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state)
if(..())
return
- var/mob/user = usr
+ add_fingerprint(usr)
- add_fingerprint(user)
+ return FALSE // VOREStation Edit - prevent topic exploits
- if(href_list["log"])
+ switch(action)
+ if("item")
+ if(!allow_items)
+ return
- var/dat = "Recently stored [storage_type] "
- for(var/person in frozen_crew)
- dat += "[person] "
- dat += " "
+ if(!LAZYLEN(frozen_items))
+ to_chat(usr, "There is nothing to recover from storage. ")
+ return
- user << browse(dat, "window=cryolog")
+ var/obj/item/I = locate(params["ref"]) in frozen_items
+ if(!I)
+ to_chat(usr, "\The [I] is no longer in storage. ")
+ return
- if(href_list["view"])
- if(!allow_items) return
+ visible_message("The console beeps happily as it disgorges [I]. ")
- var/dat = "Recently stored objects "
- //VOREStation Edit Start
- for(var/I in frozen_items)
- dat += "[I] "
- //VOREStation Edit End
- dat += " "
-
- user << browse(dat, "window=cryoitems")
-
- else if(href_list["item"])
- if(!allow_items) return
-
- if(frozen_items.len == 0)
- to_chat(user, "There is nothing to recover from storage. ")
- return
-
- var/obj/item/I = input(usr, "Please choose which object to retrieve.","Object recovery",null) as null|anything in frozen_items
- if(!I)
- return
-
- if(!(I in frozen_items))
- to_chat(user, "\The [I] is no longer in storage. ")
- return
-
- visible_message("The console beeps happily as it disgorges \the [I]. ", 3)
-
- I.forceMove(get_turf(src))
- frozen_items -= I
-
- else if(href_list["allitems"])
- if(!allow_items) return
-
- if(frozen_items.len == 0)
- to_chat(user, "There is nothing to recover from storage. ")
- return
-
- visible_message("The console beeps happily as it disgorges the desired objects. ", 3)
-
- for(var/obj/item/I in frozen_items)
I.forceMove(get_turf(src))
frozen_items -= I
+ if("allitems")
+ if(!allow_items)
+ return
+
+ if(!LAZYLEN(frozen_items))
+ to_chat(usr, "There is nothing to recover from storage. ")
+ return
+
+ visible_message("The console beeps happily as it disgorges the desired objects. ")
+
+ for(var/obj/item/I in frozen_items)
+ I.forceMove(get_turf(src))
+ frozen_items -= I
- updateUsrDialog()
- return
/obj/item/weapon/circuitboard/cryopodcontrol
name = "Circuit board (Cryogenic Oversight Console)"
diff --git a/code/game/machinery/jukebox.dm b/code/game/machinery/jukebox.dm
index f08178f03db..00a8e687601 100644
--- a/code/game/machinery/jukebox.dm
+++ b/code/game/machinery/jukebox.dm
@@ -188,91 +188,92 @@
if (panel_open)
overlays += "panel_open"
-/obj/machinery/media/jukebox/Topic(href, href_list)
- if(..() || !(Adjacent(usr) || istype(usr, /mob/living/silicon)))
- return
-
- if(!anchored)
- to_chat(usr, "You must secure \the [src] first. ")
- return
-
- if(inoperable())
- to_chat(usr, "\The [src] doesn't appear to function.")
- return
-
- if(href_list["change_track"])
- var/datum/track/T = locate(href_list["change_track"]) in tracks
- if(istype(T))
- current_track = T
- StartPlaying()
- else if(href_list["loopmode"])
- var/newval = text2num(href_list["loopmode"])
- loop_mode = sanitize_inlist(newval, list(JUKEMODE_NEXT, JUKEMODE_RANDOM, JUKEMODE_REPEAT_SONG, JUKEMODE_PLAY_ONCE), loop_mode)
- else if(href_list["volume"])
- var/newval = input("Choose Jukebox volume (0-100%)", "Jukebox volume", round(volume * 100.0))
- newval = sanitize_integer(text2num(newval), min = 0, max = 100, default = volume * 100.0)
- volume = newval / 100.0
- update_music() // To broadcast volume change without restarting song
- else if(href_list["stop"])
- StopPlaying()
- else if(href_list["play"])
- if(emagged)
- playsound(src, 'sound/items/AirHorn.ogg', 100, 1)
- for(var/mob/living/carbon/M in ohearers(6, src))
- if(M.get_ear_protection() >= 2)
- continue
- M.SetSleeping(0)
- M.stuttering += 20
- M.ear_deaf += 30
- M.Weaken(3)
- if(prob(30))
- M.Stun(10)
- M.Paralyse(4)
- else
- M.make_jittery(500)
- spawn(15)
- explode()
- else if(current_track == null)
- to_chat(usr, "No track selected.")
- else
- StartPlaying()
-
- return 1
-
/obj/machinery/media/jukebox/interact(mob/user)
if(inoperable())
to_chat(usr, "\The [src] doesn't appear to function.")
return
ui_interact(user)
+ tgui_interact(user)
-/obj/machinery/media/jukebox/ui_interact(mob/user, ui_key = "jukebox", var/datum/nanoui/ui = null, var/force_open = 1)
- var/title = "RetroBox - Space Style"
- var/data[0]
+/obj/machinery/media/jukebox/tgui_status(mob/user)
+ if(inoperable())
+ to_chat(user, "[src] doesn't appear to function. ")
+ return STATUS_CLOSE
+ if(!anchored)
+ to_chat(user, "You must secure [src] first. ")
+ return STATUS_CLOSE
+ . = ..()
- if(operable())
- data["playing"] = playing
- data["hacked"] = hacked
- data["max_queue_len"] = max_queue_len
- data["media_start_time"] = media_start_time
- data["loop_mode"] = loop_mode
- data["volume"] = volume
- if(current_track)
- data["current_track_ref"] = "\ref[current_track]" // Convenient shortcut
- data["current_track"] = current_track.toNanoList()
- data["percent"] = playing ? min(100, round(world.time - media_start_time) / current_track.duration) : 0;
-
- var/list/nano_tracks = new
- for(var/datum/track/T in tracks)
- nano_tracks[++nano_tracks.len] = T.toNanoList()
- data["tracks"] = nano_tracks
-
- // 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, "jukebox.tmpl", title, 450, 600)
- ui.set_initial_data(data)
+/obj/machinery/media/jukebox/tgui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "Jukebox", "RetroBox - Space Style")
ui.open()
- ui.set_auto_update(playing)
+
+/obj/machinery/media/jukebox/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state)
+ var/list/data = ..()
+
+ data["playing"] = playing
+ data["loop_mode"] = loop_mode
+ data["volume"] = volume
+ data["current_track_ref"] = null
+ data["current_track"] = null
+ if(current_track)
+ data["current_track_ref"] = "\ref[current_track]" // Convenient shortcut
+ data["current_track"] = current_track.toTguiList()
+ data["percent"] = playing ? min(100, round(world.time - media_start_time) / current_track.duration) : 0;
+
+ var/list/tgui_tracks = list()
+ for(var/datum/track/T in tracks)
+ tgui_tracks.Add(list(T.toTguiList()))
+ data["tracks"] = tgui_tracks
+
+ return data
+
+/obj/machinery/media/jukebox/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state)
+ if(..())
+ return TRUE
+
+ switch(action)
+ if("change_track")
+ var/datum/track/T = locate(params["change_track"]) in tracks
+ if(istype(T))
+ current_track = T
+ StartPlaying()
+ return TRUE
+ if("loopmode")
+ var/newval = text2num(params["loopmode"])
+ loop_mode = sanitize_inlist(newval, list(JUKEMODE_NEXT, JUKEMODE_RANDOM, JUKEMODE_REPEAT_SONG, JUKEMODE_PLAY_ONCE), loop_mode)
+ return TRUE
+ if("volume")
+ volume = clamp(params["val"], 0, 1)
+ update_music() // To broadcast volume change without restarting song
+ return TRUE
+ if("stop")
+ StopPlaying()
+ return TRUE
+ if("play")
+ if(emagged)
+ playsound(src, 'sound/items/AirHorn.ogg', 100, 1)
+ for(var/mob/living/carbon/M in ohearers(6, src))
+ if(M.get_ear_protection() >= 2)
+ continue
+ M.SetSleeping(0)
+ M.stuttering += 20
+ M.ear_deaf += 30
+ M.Weaken(3)
+ if(prob(30))
+ M.Stun(10)
+ M.Paralyse(4)
+ else
+ M.make_jittery(500)
+ spawn(15)
+ explode()
+ else if(current_track == null)
+ to_chat(usr, "No track selected.")
+ else
+ StartPlaying()
+ return TRUE
/obj/machinery/media/jukebox/attack_ai(mob/user as mob)
return src.attack_hand(user)
diff --git a/code/game/machinery/newscaster.dm b/code/game/machinery/newscaster.dm
index e0e4d141e2e..7c0951d9436 100644
--- a/code/game/machinery/newscaster.dm
+++ b/code/game/machinery/newscaster.dm
@@ -132,20 +132,6 @@ GLOBAL_LIST_BOILERPLATE(allCasters, /obj/machinery/newscaster)
var/ispowered = 1 //starts powered, changes with power_change()
//var/list/datum/feed_channel/channel_list = list() //This list will contain the names of the feed channels. Each name will refer to a data region where the messages of the feed channels are stored.
//OBSOLETE: We're now using a global news network
- var/screen = 0 //Or maybe I'll make it into a list within a list afterwards... whichever I prefer, go fuck yourselves :3
- // 0 = welcome screen - main menu
- // 1 = view feed channels
- // 2 = create feed channel
- // 3 = create feed story
- // 4 = feed story submited sucessfully
- // 5 = feed channel created successfully
- // 6 = ERROR: Cannot create feed story
- // 7 = ERROR: Cannot create feed channel
- // 8 = print newspaper
- // 9 = viewing channel feeds
- // 10 = censor feed story
- // 11 = censor feed channel
- //Holy shit this is outdated, made this when I was still starting newscasters :3
var/paper_remaining = 0
var/securityCaster = 0
// 0 = Caster cannot be used to issue wanted posters
@@ -171,6 +157,8 @@ GLOBAL_LIST_BOILERPLATE(allCasters, /obj/machinery/newscaster)
anchored = 1
var/obj/machinery/exonet_node/node = null
circuit = /obj/item/weapon/circuitboard/newscaster
+ // TGUI
+ var/list/temp = null
/obj/machinery/newscaster/security_unit //Security unit
name = "Security Newscaster"
@@ -246,10 +234,16 @@ GLOBAL_LIST_BOILERPLATE(allCasters, /obj/machinery/newscaster)
return
return
-/obj/machinery/newscaster/attack_ai(mob/user as mob)
+/obj/machinery/newscaster/attack_ai(mob/user)
return attack_hand(user)
-/obj/machinery/newscaster/attack_hand(mob/user as mob) //########### THE MAIN BEEF IS HERE! And in the proc below this...############
+/obj/machinery/newscaster/tgui_status(mob/user)
+ if(!ispowered || isbroken)
+ return STATUS_CLOSE
+ . = ..()
+
+
+/obj/machinery/newscaster/attack_hand(mob/user)
if(!ispowered || isbroken)
return
@@ -263,263 +257,136 @@ GLOBAL_LIST_BOILERPLATE(allCasters, /obj/machinery/newscaster)
if(!user.IsAdvancedToolUser())
return 0
+
+ tgui_interact(user)
- if(istype(user, /mob/living/carbon/human) || istype(user,/mob/living/silicon))
- var/mob/living/human_or_robot_user = user
- var/dat
- dat = text("Newscaster Newscaster Unit #[unit_no] ")
+/**
+ * Sets a temporary message to display to the user
+ *
+ * Arguments:
+ * * text - Text to display, null/empty to clear the message from the UI
+ * * style - The style of the message: (color name), info, success, warning, danger, virus
+ */
+/obj/machinery/newscaster/proc/set_temp(text = "", style = "info", update_now = FALSE)
+ temp = list(text = text, style = style)
+ if(update_now)
+ SStgui.update_uis(src)
+
+/obj/machinery/newscaster/tgui_interact(mob/user, datum/tgui/ui, datum/tgui/parent_ui)
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "Newscaster", "Newscaster Unit #[unit_no]")
+ ui.open()
- scan_user(human_or_robot_user) //Newscaster scans you
+/obj/machinery/newscaster/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state)
+ var/list/data = ..()
- switch(screen)
- if(0)
- dat += "Welcome to Newscasting Unit #[unit_no]. Interface & News networks Operational."
- dat += "Property of NanoTrasen Inc "
- if(news_network.wanted_issue)
- dat+= "Read Wanted Issue "
- dat+= "Create Feed Channel "
- dat+= "View Feed Channels "
- dat+= "Submit new Feed story "
- dat+= "Print newspaper "
- dat+= "Re-scan User "
- dat+= "Exit "
- if(securityCaster)
- var/wanted_already = 0
- if(news_network.wanted_issue)
- wanted_already = 1
+ // Main menu
+ data["temp"] = temp
- dat+="Feed Security functions: "
- dat+="[(wanted_already) ? ("Manage") : ("Publish")] \"Wanted\" Issue "
- dat+="Censor Feed Stories "
- dat+="Mark Feed Channel with [using_map.company_name] D-Notice "
- dat+=" The newscaster recognises you as: [scanned_user] "
- if(1)
- dat+= "Station Feed Channels "
- if(isemptylist(news_network.network_channels))
- dat+="No active channels found... "
- else
- for(var/datum/feed_channel/CHANNEL in news_network.network_channels)
- if(CHANNEL.is_admin_channel)
- dat+="[CHANNEL.channel_name] "
- else
- dat+="[CHANNEL.channel_name] [(CHANNEL.censored) ? ("*** ") : null] "
- dat+="Refresh "
- dat+="Back "
- if(2)
- dat+="Creating new Feed Channel..."
- dat+="Channel Name : [channel_name] "
- dat+="Channel Author: [scanned_user] "
- dat+="Will Accept Public Feeds : [(c_locked) ? ("NO") : ("YES")] "
- dat+="Submit Cancel "
- if(3)
- dat+="Creating new Feed Message..."
- dat+="Receiving Channel : [channel_name] " //MARK
- dat+="Message Author: [scanned_user] "
- dat+="Message Body : [msg] "
- dat+="Attach Photo : [(photo_data ? "Photo Attached" : "No Photo")]"
- dat+="Submit Cancel "
- if(4)
- dat+="Feed story successfully submitted to [channel_name]. "
- dat+="Return "
- if(5)
- dat+="Feed Channel [channel_name] created successfully. "
- dat+="Return "
- if(6)
- dat+="ERROR: Could not submit Feed story to Network. "
- if(channel_name=="")
- dat+="Invalid receiving channel name. "
- if(scanned_user=="Unknown")
- dat+="Channel author unverified. "
- if(msg == "" || msg == "\[REDACTED\]")
- dat+="Invalid message body. "
+ data["user"] = tgui_user_name(user)
+ data["unit_no"] = unit_no
- dat+="Return "
- if(7)
- dat+="ERROR: Could not submit Feed Channel to Network. "
- var/list/existing_authors = list()
- for(var/datum/feed_channel/FC in news_network.network_channels)
- if(FC.author == "\[REDACTED\]")
- existing_authors += FC.backup_author
- else
- existing_authors += FC.author
- if(scanned_user in existing_authors)
- dat+="There already exists a Feed channel under your name. "
- if(channel_name=="" || channel_name == "\[REDACTED\]")
- dat+="Invalid channel name. "
- var/check = 0
- for(var/datum/feed_channel/FC in news_network.network_channels)
- if(FC.channel_name == channel_name)
- check = 1
- break
- if(check)
- dat+="Channel name already in use. "
- if(scanned_user=="Unknown")
- dat+="Channel author unverified. "
- dat+="Return "
- if(8)
- var/total_num=length(news_network.network_channels)
- var/active_num=total_num
- var/message_num=0
- for(var/datum/feed_channel/FC in news_network.network_channels)
- if(!FC.censored)
- message_num += length(FC.messages) //Dont forget, datum/feed_channel's var messages is a list of datum/feed_message
- else
- active_num--
- dat+="Network currently serves a total of [total_num] Feed channels, [active_num] of which are active, and a total of [message_num] Feed Stories." //TODO: CONTINUE
- dat+="Liquid Paper remaining: [(paper_remaining) *100 ] cm^3"
- dat+="Print Paper "
- dat+="Cancel "
- if(9)
- dat+="[viewing_channel.channel_name]: \[created by: [viewing_channel.author] \] "
- if(viewing_channel.censored)
- dat+="ATTENTION: This channel has been deemed as threatening to the welfare of the station, and marked with a [using_map.company_name] D-Notice. "
- dat+="No further feed story additions are allowed while the D-Notice is in effect. "
- else
- if(isemptylist(viewing_channel.messages))
- dat+="No feed messages found in channel... "
- else
- var/i = 0
- for(var/datum/feed_message/MESSAGE in viewing_channel.messages)
- i++
- dat+="-[MESSAGE.body] "
- if(MESSAGE.img)
- usr << browse_rsc(MESSAGE.img, "tmp_photo[i].png")
- dat+=" "
- if(MESSAGE.caption)
- dat+="[MESSAGE.caption] "
- dat+=" "
- dat+="\[Story by [MESSAGE.author] - [MESSAGE.time_stamp] \] "
- dat+="Refresh "
- dat+="Back "
- if(10)
- dat+="[using_map.company_name] Feed Censorship Tool "
- dat+="NOTE: Due to the nature of news Feeds, total deletion of a Feed Story is not possible. "
- dat+="Keep in mind that users attempting to view a censored feed will instead see the \[REDACTED\] tag above it. "
- dat+=" Select Feed channel to get Stories from: "
- if(isemptylist(news_network.network_channels))
- dat+="No feed channels found active... "
- else
- for(var/datum/feed_channel/CHANNEL in news_network.network_channels)
- dat+="[CHANNEL.channel_name] [(CHANNEL.censored) ? ("*** ") : null] "
- dat+="Cancel "
- if(11)
- dat+="[using_map.company_name] D-Notice Handler "
- dat+="A D-Notice is to be bestowed upon the channel if the handling Authority deems it as harmful for the station's"
- dat+="morale, integrity or disciplinary behaviour. A D-Notice will render a channel unable to be updated by anyone, without deleting any feed"
- dat+="stories it might contain at the time. You can lift a D-Notice if you have the required access at any time. "
- if(isemptylist(news_network.network_channels))
- dat+="No feed channels found active... "
- else
- for(var/datum/feed_channel/CHANNEL in news_network.network_channels)
- dat+="[CHANNEL.channel_name] [(CHANNEL.censored) ? ("*** ") : null] "
+ var/list/wanted_issue = null
+ if(news_network.wanted_issue)
+ wanted_issue = list(
+ "author" = news_network.wanted_issue.backup_author,
+ "criminal" = news_network.wanted_issue.author,
+ "desc" = news_network.wanted_issue.body,
+ "img" = null
+ )
+ if(news_network.wanted_issue.img)
+ wanted_issue["img"] = icon2base64(news_network.wanted_issue.img)
- dat+="Back "
- if(12)
- dat+="[viewing_channel.channel_name]: \[ created by: [viewing_channel.author] \] "
- dat+="[(viewing_channel.author=="\[REDACTED\]") ? ("Undo Author censorship") : ("Censor channel Author")] "
+ data["wanted_issue"] = wanted_issue
+ data["securityCaster"] = !!securityCaster
+
+ var/list/network_channels = list()
+ for(var/datum/feed_channel/FC in news_network.network_channels)
+ network_channels.Add(list(list(
+ "admin" = FC.is_admin_channel,
+ "ref" = REF(FC),
+ "name" = FC.channel_name,
+ "censored" = FC.censored,
+ )))
+ data["channels"] = network_channels
- if(isemptylist(viewing_channel.messages))
- dat+="No feed messages found in channel... "
- else
- for(var/datum/feed_message/MESSAGE in viewing_channel.messages)
- dat+="-[MESSAGE.body] \[[MESSAGE.message_type] by [MESSAGE.author] \] "
- dat+="[(MESSAGE.body == "\[REDACTED\]") ? ("Undo story censorship") : ("Censor story")] - [(MESSAGE.author == "\[REDACTED\]") ? ("Undo Author Censorship") : ("Censor message Author")] "
- dat+="Back "
- if(13)
- dat+="[viewing_channel.channel_name]: \[ created by: [viewing_channel.author] \] "
- dat+="Channel messages listed below. If you deem them dangerous to the station, you can Bestow a D-Notice upon the channel . "
- if(viewing_channel.censored)
- dat+="ATTENTION: This channel has been deemed as threatening to the welfare of the station, and marked with a [using_map.company_name] D-Notice. "
- dat+="No further feed story additions are allowed while the D-Notice is in effect. "
- else
- if(isemptylist(viewing_channel.messages))
- dat+="No feed messages found in channel... "
- else
- for(var/datum/feed_message/MESSAGE in viewing_channel.messages)
- dat+="-[MESSAGE.body] \[[MESSAGE.message_type] by [MESSAGE.author] \] "
+ // Creating Channels
+ data["channel_name"] = channel_name
+ data["c_locked"] = c_locked
- dat+="Back "
- if(14)
- dat+="Wanted Issue Handler: "
- var/wanted_already = 0
- var/end_param = 1
- if(news_network.wanted_issue)
- wanted_already = 1
- end_param = 2
+ // Creating Messages
+ // data["channel_name"] = channel_name
+ data["msg"] = msg
+ data["photo_data"] = !!photo_data
- if(wanted_already)
- dat+="A wanted issue is already in Feed Circulation. You can edit or cancel it below. "
- dat+=" "
- dat+="Criminal Name : [channel_name] "
- dat+="Description : [msg] "
- dat+="Attach Photo : [(photo_data ? "Photo Attached" : "No Photo")]"
- if(wanted_already)
- dat+="Wanted Issue created by: [news_network.wanted_issue.backup_author] "
- else
- dat+="Wanted Issue will be created under prosecutor: [scanned_user] "
- dat+="[(wanted_already) ? ("Edit Issue") : ("Submit")] "
- if(wanted_already)
- dat+="Take down Issue "
- dat+="Cancel "
- if(15)
- dat+="Wanted issue for [channel_name] is now in Network Circulation. "
- dat+="Return "
- if(16)
- dat+="ERROR: Wanted Issue rejected by Network. "
- if(channel_name=="" || channel_name == "\[REDACTED\]")
- dat+="Invalid name for person wanted. "
- if(scanned_user=="Unknown")
- dat+="Issue author unverified. "
- if(msg == "" || msg == "\[REDACTED\]")
- dat+="Invalid description. "
- dat+="Return "
- if(17)
- dat+="Wanted Issue successfully deleted from Circulation "
- dat+="Return "
- if(18)
- dat+="-- STATIONWIDE WANTED ISSUE -- \[Submitted by: [news_network.wanted_issue.backup_author] \] "
- dat+="Criminal : [news_network.wanted_issue.author] "
- dat+="Description : [news_network.wanted_issue.body] "
- dat+="Photo: : "
- if(news_network.wanted_issue.img)
- usr << browse_rsc(news_network.wanted_issue.img, "tmp_photow.png")
- dat+=" "
- else
- dat+="None"
- dat+="Back "
- if(19)
- dat+="Wanted issue for [channel_name] successfully edited. "
- dat+="Return "
- if(20)
- dat+="Printing successful. Please receive your newspaper from the bottom of the machine. "
- dat+="Return "
- if(21)
- dat+="Unable to print newspaper. Insufficient paper. Please notify maintenance personnel to refill machine storage. "
- dat+="Return "
- else
- dat+="I'm sorry to break your immersion. This shit's bugged. Report this bug to Agouri, polyxenitopalidou@gmail.com"
+ // Printing menu
+ var/total_num = LAZYLEN(news_network.network_channels)
+ var/active_num = total_num
+ var/message_num = 0
+ for(var/datum/feed_channel/FC in news_network.network_channels)
+ if(!FC.censored)
+ message_num += length(FC.messages) //Dont forget, datum/feed_channel's var messages is a list of datum/feed_message
+ else
+ active_num--
+ data["total_num"] = total_num
+ data["active_num"] = active_num
+ data["message_num"] = message_num
+ data["paper_remaining"] = paper_remaining
+ // Viewing a specific channel
+ var/list/viewing = null
+ if(viewing_channel)
+ var/list/messages = list()
+ viewing = list(
+ "name" = viewing_channel.channel_name,
+ "author" = viewing_channel.author,
+ "censored" = viewing_channel.censored,
+ "messages" = messages,
+ "ref" = REF(viewing_channel),
+ )
+ if(!viewing_channel.censored)
+ for(var/datum/feed_message/M in viewing_channel.messages)
+ var/list/msgdata = list(
+ "body" = M.body,
+ "img" = null,
+ "type" = M.message_type,
+ "caption" = null,
+ "author" = M.author,
+ "timestamp" = M.time_stamp,
+ "ref" = REF(M),
+ )
+ if(M.img)
+ msgdata["img"] = icon2base64(M.img)
+ msgdata["caption"] = M.caption
- human_or_robot_user << browse(dat, "window=newscaster_main;size=400x600")
- onclose(human_or_robot_user, "newscaster_main")
+ messages.Add(list(msgdata))
+ data["viewing_channel"] = viewing
-/obj/machinery/newscaster/Topic(href, href_list)
+ // Censorship
+ data["company"] = using_map.company_name
+
+ return data
+
+/obj/machinery/newscaster/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state)
if(..())
- return
- if((usr.contents.Find(src) || ((get_dist(src, usr) <= 1) && istype(src.loc, /turf))) || (istype(usr, /mob/living/silicon)))
- usr.set_machine(src)
- if(href_list["set_channel_name"])
- channel_name = sanitizeSafe(input(usr, "Provide a Feed Channel Name", "Network Channel Handler", ""), MAX_LNAME_LEN)
- updateUsrDialog()
- //update_icon()
+ return TRUE
- else if(href_list["set_channel_lock"])
+ switch(action)
+ if("cleartemp")
+ temp = null
+ return TRUE
+
+ if("set_channel_name")
+ channel_name = sanitizeSafe(params["val"], MAX_LNAME_LEN)
+ return TRUE
+
+ if("set_channel_lock")
c_locked = !c_locked
- updateUsrDialog()
- //update_icon()
+ return TRUE
- else if(href_list["submit_new_channel"])
+ if("submit_new_channel")
//var/list/existing_channels = list() //OBSOLETE
var/list/existing_authors = list()
for(var/datum/feed_channel/FC in news_network.network_channels)
@@ -533,121 +400,118 @@ GLOBAL_LIST_BOILERPLATE(allCasters, /obj/machinery/newscaster)
if(FC.channel_name == channel_name)
check = 1
break
- if(channel_name == "" || channel_name == "\[REDACTED\]" || scanned_user == "Unknown" || check || (scanned_user in existing_authors))
- screen=7
- else
- var/choice = alert("Please confirm Feed channel creation","Network Channel Handler","Confirm","Cancel")
- if(choice=="Confirm")
- news_network.CreateFeedChannel(channel_name, scanned_user, c_locked)
- screen=5
- updateUsrDialog()
- //update_icon()
+ var/our_user = tgui_user_name(usr)
+ if(channel_name == "" || channel_name == "\[REDACTED\]")
+ set_temp("Error: Could not submit feed channel to network: Invalid Channel Name.", "danger", FALSE)
+ return TRUE
+ if(our_user == "Unknown")
+ set_temp("Error: Could not submit feed channel to network: Channel author unverified.", "danger", FALSE)
+ return TRUE
+ if(check)
+ set_temp("Error: Could not submit feed channel to network: Channel name already in use.", "danger", FALSE)
+ return TRUE
+ if(our_user in existing_authors)
+ set_temp("Error: Could not submit feed channel to network: A feed channel already exists under your name.", "danger", FALSE)
+ return TRUE
- else if(href_list["set_channel_receiving"])
+ var/choice = alert("Please confirm Feed channel creation","Network Channel Handler","Confirm","Cancel")
+ if(choice == "Confirm")
+ news_network.CreateFeedChannel(channel_name, our_user, c_locked)
+ set_temp("Feed channel [channel_name] created successfully.", "success", FALSE)
+ return TRUE
+
+ if("set_channel_receiving")
//var/list/datum/feed_channel/available_channels = list()
var/list/available_channels = list()
for(var/datum/feed_channel/F in news_network.network_channels)
if((!F.locked || F.author == scanned_user) && !F.censored)
available_channels += F.channel_name
- channel_name = input(usr, "Choose receiving Feed Channel", "Network Channel Handler") in available_channels
- updateUsrDialog()
+ var/new_channel_name = input(usr, "Choose receiving Feed Channel", "Network Channel Handler") as null|anything in available_channels
+ if(new_channel_name)
+ channel_name = new_channel_name
+ return TRUE
- else if(href_list["set_new_message"])
- msg = sanitize(input(usr, "Write your Feed story", "Network Channel Handler", ""))
- updateUsrDialog()
+ if("set_new_message")
+ msg = sanitize(input(usr, "Write your Feed story", "Network Channel Handler", "") as message|null)
+ return TRUE
- else if(href_list["set_attachment"])
+ if("set_attachment")
AttachPhoto(usr)
- updateUsrDialog()
+ return TRUE
- else if(href_list["submit_new_message"])
- if(msg =="" || msg=="\[REDACTED\]" || scanned_user == "Unknown" || channel_name == "")
- screen=6
- else
- var/image = photo_data ? photo_data.photo : null
- feedback_inc("newscaster_stories",1)
- news_network.SubmitArticle(msg, scanned_user, channel_name, image, 0)
- screen=4
+ if("submit_new_message")
+ var/our_user = tgui_user_name(usr)
+ if(msg == "" || msg == "\[REDACTED\]")
+ set_temp("Error: Could not submit feed message to network: Invalid Message.", "danger", FALSE)
+ return TRUE
+ if(our_user == "Unknown")
+ set_temp("Error: Could not submit feed message to network: Channel author unverified.", "danger", FALSE)
+ return TRUE
+ if(channel_name == "")
+ set_temp("Error: Could not submit feed message to network: No feed channel selected.", "danger", FALSE)
+ return TRUE
- updateUsrDialog()
+ var/image = photo_data ? photo_data.photo : null
+ feedback_inc("newscaster_stories",1)
+ news_network.SubmitArticle(msg, our_user, channel_name, image, 0)
+ set_temp("Feed message created successfully.", "success", FALSE)
+ return TRUE
- else if(href_list["create_channel"])
- screen=2
- updateUsrDialog()
-
- else if(href_list["create_feed_story"])
- screen=3
- updateUsrDialog()
-
- else if(href_list["menu_paper"])
- screen=8
- updateUsrDialog()
- else if(href_list["print_paper"])
+ if("print_paper")
if(!paper_remaining)
- screen=21
- else
- print_paper()
- screen = 20
- updateUsrDialog()
+ set_temp("Unable to print newspaper. Insufficient paper. Please notify maintenance personnel to refill machine storage.", "danger", FALSE)
+ return TRUE
+
+ print_paper()
+ set_temp("Printing successful. Please receive your newspaper from the bottom of the machine.", "success", FALSE)
+ return TRUE
- else if(href_list["menu_censor_story"])
- screen=10
- updateUsrDialog()
+ if("set_wanted_desc")
+ msg = sanitize(params["val"])
+ return TRUE
- else if(href_list["menu_censor_channel"])
- screen=11
- updateUsrDialog()
+ if("submit_wanted")
+ if(!securityCaster)
+ return FALSE
+ var/our_user = tgui_user_name(usr)
+ if(channel_name == "")
+ set_temp("Error: Could not submit wanted issue to network: Invalid Criminal Name.", "danger", FALSE)
+ return TRUE
+ if(msg == "")
+ set_temp("Error: Could not submit wanted issue to network: Invalid Description.", "danger", FALSE)
+ return TRUE
+ if(our_user == "Unknown")
+ set_temp("Error: Could not submit wanted issue to network: Author unverified.", "danger", FALSE)
+ return TRUE
- else if(href_list["menu_wanted"])
- var/already_wanted = 0
- if(news_network.wanted_issue)
- already_wanted = 1
+ var/choice = alert("Please confirm Wanted Issue change.", "Network Security Handler", "Confirm", "Cancel")
+ if(choice == "Confirm")
+ if(news_network.wanted_issue)
+ if(news_network.wanted_issue.is_admin_message)
+ alert("The wanted issue has been distributed by a [using_map.company_name] higherup. You cannot edit it.", "Ok")
+ return
+ news_network.wanted_issue.author = channel_name
+ news_network.wanted_issue.body = msg
+ news_network.wanted_issue.backup_author = scanned_user
+ if(photo_data)
+ news_network.wanted_issue.img = photo_data.photo.img
+ set_temp("Wanted issue for [channel_name] successfully edited.", "success", FALSE)
+ return TRUE
- if(already_wanted)
- channel_name = news_network.wanted_issue.author
- msg = news_network.wanted_issue.body
- screen = 14
- updateUsrDialog()
+ var/datum/feed_message/WANTED = new /datum/feed_message
+ WANTED.author = channel_name
+ WANTED.body = msg
+ WANTED.backup_author = scanned_user //I know, a bit wacky
+ if(photo_data)
+ WANTED.img = photo_data.photo.img
+ news_network.wanted_issue = WANTED
+ news_network.alert_readers()
+ set_temp("Wanted issue for [channel_name] is now in Network Circulation.", "success", FALSE)
+ return TRUE
- else if(href_list["set_wanted_name"])
- channel_name = sanitizeSafe(input(usr, "Provide the name of the Wanted person", "Network Security Handler", ""), MAX_LNAME_LEN)
- updateUsrDialog()
-
- else if(href_list["set_wanted_desc"])
- msg = sanitize(input(usr, "Provide the a description of the Wanted person and any other details you deem important", "Network Security Handler", ""))
- updateUsrDialog()
-
- else if(href_list["submit_wanted"])
- var/input_param = text2num(href_list["submit_wanted"])
- if(msg == "" || channel_name == "" || scanned_user == "Unknown")
- screen = 16
- else
- var/choice = alert("Please confirm Wanted Issue [(input_param==1) ? ("creation.") : ("edit.")]","Network Security Handler","Confirm","Cancel")
- if(choice=="Confirm")
- if(input_param==1) //If input_param == 1 we're submitting a new wanted issue. At 2 we're just editing an existing one. See the else below
- var/datum/feed_message/WANTED = new /datum/feed_message
- WANTED.author = channel_name
- WANTED.body = msg
- WANTED.backup_author = scanned_user //I know, a bit wacky
- if(photo_data)
- WANTED.img = photo_data.photo.img
- news_network.wanted_issue = WANTED
- news_network.alert_readers()
- screen = 15
- else
- if(news_network.wanted_issue.is_admin_message)
- alert("The wanted issue has been distributed by a [using_map.company_name] higherup. You cannot edit it.","Ok")
- return
- news_network.wanted_issue.author = channel_name
- news_network.wanted_issue.body = msg
- news_network.wanted_issue.backup_author = scanned_user
- if(photo_data)
- news_network.wanted_issue.img = photo_data.photo.img
- screen = 19
-
- updateUsrDialog()
-
- else if(href_list["cancel_wanted"])
+ if("cancel_wanted")
+ if(!securityCaster)
+ return FALSE
if(news_network.wanted_issue.is_admin_message)
alert("The wanted issue has been distributed by a [using_map.company_name] higherup. You cannot take it down.","Ok")
return
@@ -656,49 +520,52 @@ GLOBAL_LIST_BOILERPLATE(allCasters, /obj/machinery/newscaster)
news_network.wanted_issue = null
for(var/obj/machinery/newscaster/NEWSCASTER in allCasters)
NEWSCASTER.update_icon()
- screen=17
- updateUsrDialog()
+ set_temp("Wanted issue taken down.", "success", FALSE)
+ return TRUE
- else if(href_list["view_wanted"])
- screen=18
- updateUsrDialog()
- else if(href_list["censor_channel_author"])
- var/datum/feed_channel/FC = locate(href_list["censor_channel_author"])
+ if("censor_channel_author")
+ if(!securityCaster)
+ return FALSE
+ var/datum/feed_channel/FC = locate(params["ref"])
if(FC.is_admin_channel)
alert("This channel was created by a [using_map.company_name] Officer. You cannot censor it.","Ok")
return
- if(FC.author != "\[REDACTED\] ")
+ if(FC.author != "\[REDACTED\]")
FC.backup_author = FC.author
- FC.author = "\[REDACTED\] "
+ FC.author = "\[REDACTED\]"
else
FC.author = FC.backup_author
FC.update()
- updateUsrDialog()
+ return TRUE
- else if(href_list["censor_channel_story_author"])
- var/datum/feed_message/MSG = locate(href_list["censor_channel_story_author"])
+ if("censor_channel_story_author")
+ if(!securityCaster)
+ return FALSE
+ var/datum/feed_message/MSG = locate(params["ref"])
if(MSG.is_admin_message)
alert("This message was created by a [using_map.company_name] Officer. You cannot censor its author.","Ok")
return
- if(MSG.author != "\[REDACTED\] ")
+ if(MSG.author != "\[REDACTED\]")
MSG.backup_author = MSG.author
- MSG.author = "\[REDACTED\] "
+ MSG.author = "\[REDACTED\]"
else
MSG.author = MSG.backup_author
MSG.parent_channel.update()
- updateUsrDialog()
+ return TRUE
- else if(href_list["censor_channel_story_body"])
- var/datum/feed_message/MSG = locate(href_list["censor_channel_story_body"])
+ if("censor_channel_story_body")
+ if(!securityCaster)
+ return FALSE
+ var/datum/feed_message/MSG = locate(params["ref"])
if(MSG.is_admin_message)
alert("This channel was created by a [using_map.company_name] Officer. You cannot censor it.","Ok")
return
- if(MSG.body != "\[REDACTED\] ")
+ if(MSG.body != "\[REDACTED\]")
MSG.backup_body = MSG.body
MSG.backup_caption = MSG.caption
MSG.backup_img = MSG.img
- MSG.body = "\[REDACTED\] "
- MSG.caption = "\[REDACTED\] "
+ MSG.body = "\[REDACTED\]"
+ MSG.caption = "\[REDACTED\]"
MSG.img = null
else
MSG.body = MSG.backup_body
@@ -706,60 +573,32 @@ GLOBAL_LIST_BOILERPLATE(allCasters, /obj/machinery/newscaster)
MSG.img = MSG.backup_img
MSG.parent_channel.update()
- updateUsrDialog()
+ return TRUE
- else if(href_list["pick_d_notice"])
- var/datum/feed_channel/FC = locate(href_list["pick_d_notice"])
- viewing_channel = FC
- screen=13
- updateUsrDialog()
-
- else if(href_list["toggle_d_notice"])
- var/datum/feed_channel/FC = locate(href_list["toggle_d_notice"])
+ if("toggle_d_notice")
+ if(!securityCaster)
+ return FALSE
+ var/datum/feed_channel/FC = locate(params["ref"])
if(FC.is_admin_channel)
alert("This channel was created by a [using_map.company_name] Officer. You cannot place a D-Notice upon it.","Ok")
return
FC.censored = !FC.censored
FC.update()
- updateUsrDialog()
+ return TRUE
- else if(href_list["view"])
- screen=1
- updateUsrDialog()
-
- else if(href_list["setScreen"]) //Brings us to the main menu and resets all fields~
- screen = text2num(href_list["setScreen"])
- if(screen == 0)
- scanned_user = "Unknown";
- msg = "";
- c_locked=0;
- channel_name="";
- viewing_channel = null
- updateUsrDialog()
-
- else if(href_list["show_channel"])
- var/datum/feed_channel/FC = locate(href_list["show_channel"])
+ if("show_channel")
+ var/datum/feed_channel/FC = locate(params["show_channel"])
viewing_channel = FC
- screen = 9
- updateUsrDialog()
+ return TRUE
- else if(href_list["pick_censor_channel"])
- var/datum/feed_channel/FC = locate(href_list["pick_censor_channel"])
- viewing_channel = FC
- screen = 12
- updateUsrDialog()
-
- else if(href_list["refresh"])
- updateUsrDialog()
-
-/obj/machinery/newscaster/attackby(I as obj, user as mob)
+/obj/machinery/newscaster/attackby(I as obj, user)
if(computer_deconstruction_screwdriver(user, I))
return
else
attack_hand(user)
return
-/obj/machinery/newscaster/attack_ai(mob/user as mob)
+/obj/machinery/newscaster/attack_ai(mob/user)
return attack_hand(user) //or maybe it'll have some special functions? No idea.
/datum/news_photo
@@ -770,7 +609,7 @@ GLOBAL_LIST_BOILERPLATE(allCasters, /obj/machinery/newscaster)
is_synth = synth
photo = p
-/obj/machinery/newscaster/proc/AttachPhoto(mob/user as mob)
+/obj/machinery/newscaster/proc/AttachPhoto(mob/user)
if(photo_data)
if(!photo_data.is_synth)
photo_data.photo.loc = src.loc
@@ -812,7 +651,7 @@ GLOBAL_LIST_BOILERPLATE(allCasters, /obj/machinery/newscaster)
drop_sound = 'sound/items/drop/wrapper.ogg'
pickup_sound = 'sound/items/pickup/wrapper.ogg'
-obj/item/weapon/newspaper/attack_self(mob/user as mob)
+obj/item/weapon/newspaper/attack_self(mob/user)
if(ishuman(user))
var/mob/living/carbon/human/human_user = user
var/dat
@@ -922,7 +761,7 @@ obj/item/weapon/newspaper/Topic(href, href_list)
if(istype(src.loc, /mob))
attack_self(src.loc)
-obj/item/weapon/newspaper/attackby(obj/item/weapon/W as obj, mob/user as mob)
+obj/item/weapon/newspaper/attackby(obj/item/weapon/W as obj, mob/user)
if(istype(W, /obj/item/weapon/pen))
if(scribble_page == curr_page)
to_chat(user, "There's already a scribble in this page... You wouldn't want to make things too cluttered, would you? ")
@@ -939,8 +778,20 @@ obj/item/weapon/newspaper/attackby(obj/item/weapon/W as obj, mob/user as mob)
return
////////////////////////////////////helper procs
+/obj/machinery/newscaster/proc/tgui_user_name(mob/user)
+ if(ishuman(user))
+ var/mob/living/carbon/human/H = user
+ var/obj/item/weapon/card/id/I = H.GetIdCard()
+ if(I)
+ return GetNameAndAssignmentFromId(I)
-/obj/machinery/newscaster/proc/scan_user(mob/living/user as mob)
+ if(issilicon(user))
+ var/mob/living/silicon/S = user
+ return "[S.name] ([S.job])"
+
+ return "Unknown"
+
+/obj/machinery/newscaster/proc/scan_user(mob/living/user)
if(istype(user,/mob/living/carbon/human)) //User is a human
var/mob/living/carbon/human/human_user = user
var/obj/item/weapon/card/id/I = human_user.GetIdCard()
diff --git a/code/game/machinery/painter_vr.dm b/code/game/machinery/painter_vr.dm
index cc961a737fe..64490a19b25 100644
--- a/code/game/machinery/painter_vr.dm
+++ b/code/game/machinery/painter_vr.dm
@@ -1,3 +1,10 @@
+// I'm honestly pretty sure that short of stuffing five million things into this
+// there's absolutely no way it could ever have any performance impact
+// Given that all it does is set the color var
+// But just in case it's cursed in some arcane horrible way
+// I'm going to leave this limit here
+#define MAX_PROCESSING 10 // Arbitrary performance insurance
+
/obj/machinery/gear_painter
name = "Color Mate"
desc = "A machine to give your apparel a fresh new color! Recommended to use with white items for best results."
@@ -29,9 +36,9 @@
processing.Cut()
return ..()
-/obj/machinery/gear_painter/attackby(obj/item/W as obj, mob/user as mob)
- if(processing.len)
- to_chat(user, "The machine is already loaded. ")
+/obj/machinery/gear_painter/attackby(obj/item/W, mob/user)
+ if(LAZYLEN(processing) >= MAX_PROCESSING)
+ to_chat(user, "The machine is full. ")
return
if(default_deconstruction_screwdriver(user, W))
return
@@ -45,65 +52,65 @@
user.drop_from_inventory(W)
W.forceMove(src)
processing |= W
+ SStgui.update_uis(src)
else
..()
update_icon()
-/obj/machinery/gear_painter/attack_hand(mob/user as mob)
+/obj/machinery/gear_painter/attack_hand(mob/user)
if(..())
return
- interact(user)
+ tgui_interact(user)
-/obj/machinery/gear_painter/interact(mob/user as mob)
- if(inoperable())
- return
- user.set_machine(src)
- var/dat = "Color Mate Control Panel "
- if(!processing.len)
- dat += "No item inserted."
- else
- for(var/atom/movable/O in processing)
- dat += "Item inserted: [O] "
- dat += "Select new color. "
- dat += "Color: ⚫ "
- dat += "Apply new color. "
- dat += "Remove paintjob. "
- dat += "Eject item. "
+/obj/machinery/gear_painter/tgui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "ColorMate", name)
+ ui.open()
- var/datum/browser/menu = new(user, "colormate","Color Mate Control Panel", 400, 600, src)
- menu.set_content(dat)
- menu.open()
- return
+/obj/machinery/gear_painter/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state)
+ var/list/data = ..()
-/obj/machinery/gear_painter/Topic(href, href_list)
+ var/list/items = list()
+ for(var/atom/movable/O in processing)
+ items.Add("[O]")
+ data["items"] = items
+
+ data["activecolor"] = activecolor
+ return data
+
+/obj/machinery/gear_painter/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state)
if(..())
- return
+ return TRUE
- usr.set_machine(src)
add_fingerprint(usr)
- if(href_list["close"])
- return
+ switch(action)
+ if("select")
+ var/newcolor = input(usr, "Choose a color.", "", activecolor) as color|null
+ if(newcolor)
+ activecolor = newcolor
+ . = TRUE
- if(href_list["select"])
- var/newcolor = input(usr, "Choose a color.", "", activecolor) as color|null
- if(newcolor)
- activecolor = newcolor
+ if("paint")
+ for(var/atom/movable/O in processing)
+ O.color = activecolor
+ CHECK_TICK
+ playsound(src, 'sound/effects/spray3.ogg', 50, 1)
+ . = TRUE
- if(href_list["paint"])
- for(var/atom/movable/O in processing)
- O.color = activecolor
- playsound(src, 'sound/effects/spray3.ogg', 50, 1)
+ if("clear")
+ for(var/atom/movable/O in processing)
+ O.color = initial(O.color)
+ CHECK_TICK
+ playsound(src, 'sound/effects/spray3.ogg', 50, 1)
+ . = TRUE
- if(href_list["clear"])
- for(var/atom/movable/O in processing)
- O.color = initial(O.color)
- playsound(src, 'sound/effects/spray3.ogg', 50, 1)
-
- if(href_list["eject"])
- for(var/atom/movable/O in processing)
- O.forceMove(drop_location())
- processing.Cut()
+ if("eject")
+ for(var/atom/movable/O in processing)
+ O.forceMove(drop_location())
+ CHECK_TICK
+ processing.Cut()
+ . = TRUE
update_icon()
- updateUsrDialog()
diff --git a/code/game/machinery/vending.dm b/code/game/machinery/vending.dm
index dcdd8d88fe4..003ddcbd8e1 100644
--- a/code/game/machinery/vending.dm
+++ b/code/game/machinery/vending.dm
@@ -26,9 +26,6 @@
var/vend_delay = 10 //How long does it take to vend?
var/categories = CAT_NORMAL // Bitmask of cats we're currently showing
var/datum/stored_item/vending_product/currently_vending = null // What we're requesting payment for right now
- var/tmp/actively_vending = null // Used to allow TGUI to display normal items in-progress being vended
- var/status_message = "" // Status screen messages like "insufficient funds", displayed in NanoUI
- var/status_error = 0 // Set to 1 if status_message is an error
var/vending_sound = "machines/vending/vending_drop.ogg"
/*
@@ -91,6 +88,7 @@
build_inventory()
power_change()
+GLOBAL_LIST_EMPTY(vending_products)
/**
* Build produdct_records from the products lists
*
@@ -115,6 +113,7 @@
product.category = category
product_records.Add(product)
+ GLOB.vending_products[entry] = 1
/obj/machinery/vending/Destroy()
qdel(wires)
@@ -151,32 +150,8 @@
return 1
/obj/machinery/vending/attackby(obj/item/weapon/W as obj, mob/user as mob)
-
var/obj/item/weapon/card/id/I = W.GetID()
- if(currently_vending && vendor_account && !vendor_account.suspended)
- var/paid = 0
- var/handled = 0
-
- if(I) //for IDs and PDAs and wallets with IDs
- paid = pay_with_card(I,W)
- handled = 1
- else if(istype(W, /obj/item/weapon/spacecash/ewallet))
- var/obj/item/weapon/spacecash/ewallet/C = W
- paid = pay_with_ewallet(C)
- handled = 1
- else if(istype(W, /obj/item/weapon/spacecash))
- var/obj/item/weapon/spacecash/C = W
- paid = pay_with_cash(C, user)
- handled = 1
-
- if(paid)
- vend(currently_vending, usr)
- return
- else if(handled)
- SStgui.update_uis(src)
- return // don't smack that machine with your 2 thalers
-
if(I || istype(W, /obj/item/weapon/spacecash))
attack_hand(user)
return
@@ -262,8 +237,7 @@
visible_message("\The [usr] swipes \the [wallet] through \the [src]. ")
playsound(src, 'sound/machines/id_swipe.ogg', 50, 1)
if(currently_vending.price > wallet.worth)
- status_message = "Insufficient funds on chargecard."
- status_error = 1
+ to_chat(usr, "Insufficient funds on chargecard. ")
return 0
else
wallet.worth -= currently_vending.price
@@ -276,22 +250,18 @@
* Takes payment for whatever is the currently_vending item. Returns 1 if
* successful, 0 if failed
*/
-/obj/machinery/vending/proc/pay_with_card(var/obj/item/weapon/card/id/I, var/obj/item/ID_container)
- if(I==ID_container || ID_container == null)
- visible_message("\The [usr] swipes \the [I] through \the [src]. ")
- else
- visible_message("\The [usr] swipes \the [ID_container] through \the [src]. ")
+/obj/machinery/vending/proc/pay_with_card(obj/item/weapon/card/id/I, mob/M)
+ visible_message("[M] swipes a card through [src]. ")
playsound(src, 'sound/machines/id_swipe.ogg', 50, 1)
+
var/datum/money_account/customer_account = get_account(I.associated_account_number)
if(!customer_account)
- status_message = "Error: Unable to access account. Please contact technical support if problem persists."
- status_error = 1
- return 0
+ to_chat(M, "Error: Unable to access account. Please contact technical support if problem persists. ")
+ return FALSE
if(customer_account.suspended)
- status_message = "Unable to access account: account suspended."
- status_error = 1
- return 0
+ to_chat(M, "Unable to access account: account suspended. ")
+ return FALSE
// Have the customer punch in the PIN before checking if there's enough money. Prevents people from figuring out acct is
// empty at high security levels
@@ -300,38 +270,36 @@
customer_account = attempt_account_access(I.associated_account_number, attempt_pin, 2)
if(!customer_account)
- status_message = "Unable to access account: incorrect credentials."
- status_error = 1
- return 0
+ to_chat(M, "Unable to access account: incorrect credentials. ")
+ return FALSE
if(currently_vending.price > customer_account.money)
- status_message = "Insufficient funds in account."
- status_error = 1
- return 0
+ to_chat(M, "Insufficient funds in account. ")
+ return FALSE
+
+ // Okay to move the money at this point
+
+ // debit money from the purchaser's account
+ customer_account.money -= currently_vending.price
+
+ // create entry in the purchaser's account log
+ var/datum/transaction/T = new()
+ T.target_name = "[vendor_account.owner_name] (via [name])"
+ T.purpose = "Purchase of [currently_vending.item_name]"
+ if(currently_vending.price > 0)
+ T.amount = "([currently_vending.price])"
else
- // Okay to move the money at this point
+ T.amount = "[currently_vending.price]"
+ T.source_terminal = name
+ T.date = current_date_string
+ T.time = stationtime2text()
+ customer_account.transaction_log.Add(T)
- // debit money from the purchaser's account
- customer_account.money -= currently_vending.price
-
- // create entry in the purchaser's account log
- var/datum/transaction/T = new()
- T.target_name = "[vendor_account.owner_name] (via [name])"
- T.purpose = "Purchase of [currently_vending.item_name]"
- if(currently_vending.price > 0)
- T.amount = "([currently_vending.price])"
- else
- T.amount = "[currently_vending.price]"
- T.source_terminal = name
- T.date = current_date_string
- T.time = stationtime2text()
- customer_account.transaction_log.Add(T)
-
- // Give the vendor the money. We use the account owner name, which means
- // that purchases made with stolen/borrowed card will look like the card
- // owner made them
- credit_purchase(customer_account.owner_name)
- return 1
+ // Give the vendor the money. We use the account owner name, which means
+ // that purchases made with stolen/borrowed card will look like the card
+ // owner made them
+ credit_purchase(customer_account.owner_name)
+ return 1
/**
* Add money for current purchase to the vendor account.
@@ -367,6 +335,11 @@
wires.Interact(user)
tgui_interact(user)
+/obj/machinery/vending/ui_assets(mob/user)
+ return list(
+ get_asset_datum(/datum/asset/spritesheet/vending),
+ )
+
/obj/machinery/vending/tgui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
@@ -375,39 +348,34 @@
/obj/machinery/vending/tgui_data(mob/user)
var/list/data = list()
- if(currently_vending)
- data["mode"] = 1
- data["product"] = currently_vending.item_name
- data["price"] = currently_vending.price
- data["message"] = status_message
- data["message_err"] = status_error
- data["products"] = null
- else
- data["mode"] = 0
- var/list/listed_products = list()
+ var/list/listed_products = list()
- for(var/key = 1 to product_records.len)
- var/datum/stored_item/vending_product/I = product_records[key]
+ data["chargesMoney"] = length(prices) > 0 ? TRUE : FALSE
+ for(var/key = 1 to product_records.len)
+ var/datum/stored_item/vending_product/I = product_records[key]
- if(!(I.category & categories))
- continue
+ if(!(I.category & categories))
+ continue
- listed_products.Add(list(list(
- "key" = key,
- "name" = I.item_name,
- "price" = I.price,
- "color" = I.display_color,
- "amount" = I.get_amount())))
+ listed_products.Add(list(list(
+ "key" = key,
+ "name" = I.item_name,
+ "price" = I.price,
+ "color" = I.display_color,
+ "isatom" = ispath(I.item_path, /atom),
+ "path" = replacetext(replacetext("[I.item_path]", "/obj/item/", ""), "/", "-"),
+ "amount" = I.get_amount()
+ )))
- data["products"] = listed_products
+ data["products"] = listed_products
if(coin)
data["coin"] = coin.name
else
data["coin"] = FALSE
- if(actively_vending)
- data["actively_vending"] = actively_vending
+ if(currently_vending)
+ data["actively_vending"] = currently_vending.item_name
else
data["actively_vending"] = null
@@ -417,6 +385,29 @@
else
data["panel"] = 0
+ var/mob/living/carbon/human/H
+ var/obj/item/weapon/card/id/C
+
+ data["guestNotice"] = "No valid ID card detected. Wear your ID, or present cash.";
+ data["userMoney"] = 0
+ data["user"] = null
+ if(ishuman(user))
+ H = user
+ C = H.GetIdCard()
+ var/obj/item/weapon/spacecash/S = H.get_active_hand()
+ if(istype(S))
+ data["userMoney"] = S.worth
+ data["guestNotice"] = "Accepting [S.initial_name]. You have: [S.worth]₮."
+ else if(istype(C))
+ var/datum/money_account/A = get_account(C.associated_account_number)
+ if(istype(A))
+ data["user"] = list()
+ data["user"]["name"] = A.owner_name
+ data["userMoney"] = A.money
+ data["user"]["job"] = (istype(C) && C.rank) ? C.rank : "No Job"
+ else
+ data["guestNotice"] = "Unlinked ID detected. Present cash to pay.";
+
return data
/obj/machinery/vending/tgui_act(action, params)
@@ -427,36 +418,36 @@
if(..())
return TRUE
- if(action == "remove_coin")
- if(issilicon(usr))
- return FALSE
-
- if(!coin)
- to_chat(usr, "There is no coin in this machine.")
- return
-
- coin.forceMove(src.loc)
- if(!usr.get_active_hand())
- usr.put_in_hands(coin)
-
- to_chat(usr, "You remove \the [coin] from \the [src]. ")
- coin = null
- categories &= ~CAT_COIN
- return TRUE
-
- if(!usr.contents.Find(src) && (!in_range(src, usr) && isturf(loc)))
- return FALSE
-
. = TRUE
switch(action)
+ if("remove_coin")
+ if(issilicon(usr))
+ return FALSE
+
+ if(!coin)
+ to_chat(usr, "There is no coin in this machine.")
+ return
+
+ coin.forceMove(src.loc)
+ if(!usr.get_active_hand())
+ usr.put_in_hands(coin)
+
+ to_chat(usr, "You remove \the [coin] from \the [src]. ")
+ coin = null
+ categories &= ~CAT_COIN
+ return TRUE
if("vend")
- if(!vend_ready || currently_vending)
+ if(!vend_ready)
+ to_chat(usr, "[src] is busy! ")
return
if(!allowed(usr) && !emagged && scan_id)
to_chat(usr, "Access denied. ") //Unless emagged of course
flick("[icon_state]-deny",src)
playsound(src, 'sound/machines/deniedbeep.ogg', 50, 0)
return
+ if(panel_open)
+ to_chat(usr, "[src] cannot dispense products while its service panel is open! ")
+ return
var/key = text2num(params["vend"])
var/datum/stored_item/vending_product/R = product_records[key]
@@ -464,39 +455,83 @@
// This should not happen unless the request from NanoUI was bad
if(!(R.category & categories))
return
+
+ if(!can_buy(R, usr))
+ return
+
+ vend_ready = FALSE // From this point onwards, vendor is locked to performing this transaction only, until it is resolved.
if(R.price <= 0)
vend(R, usr)
- else if(issilicon(usr)) //If the item is not free, provide feedback if a synth is trying to buy something.
+ add_fingerprint(usr)
+ vend_ready = TRUE
+ return TRUE
+
+ if(issilicon(usr)) //If the item is not free, provide feedback if a synth is trying to buy something.
to_chat(usr, "Lawed unit recognized. Lawed units cannot complete this transaction. Purchase canceled. ")
return
- else
- currently_vending = R
- if(!vendor_account || vendor_account.suspended)
- status_message = "This machine is currently unable to process payments due to issues with the associated account."
- status_error = 1
- else
- status_message = "Please swipe a card or insert cash to pay for the item."
- status_error = 0
+ if(!ishuman(usr))
+ return
- if("cancelpurchase")
- currently_vending = null
+ var/mob/living/carbon/human/H = usr
+ var/obj/item/weapon/card/id/C = H.GetIdCard()
+
+ if(!vendor_account || vendor_account.suspended)
+ to_chat(usr, "Vendor account offline. Unable to process transaction.")
+ flick("[icon_state]-deny",src)
+ vend_ready = TRUE
+ return
+
+ currently_vending = R
+
+ var/paid = FALSE
+
+ if(istype(usr.get_active_hand(), /obj/item/weapon/spacecash))
+ var/obj/item/weapon/spacecash/cash = usr.get_active_hand()
+ paid = pay_with_cash(cash, usr)
+ else if(istype(usr.get_active_hand(), /obj/item/weapon/spacecash/ewallet))
+ var/obj/item/weapon/spacecash/ewallet/wallet = usr.get_active_hand()
+ paid = pay_with_ewallet(wallet)
+ else if(istype(C, /obj/item/weapon/card))
+ paid = pay_with_card(C, usr)
+ /*else if(usr.can_advanced_admin_interact())
+ to_chat(usr, "Vending object due to admin interaction. ")
+ paid = TRUE*/
+ else
+ to_chat(usr, "Payment failure: you have no ID or other method of payment.")
+ vend_ready = TRUE
+ flick("[icon_state]-deny",src)
+ return TRUE // we set this because they shouldn't even be able to get this far, and we want the UI to update.
+ if(paid)
+ vend(currently_vending, usr) // vend will handle vend_ready
+ . = TRUE
+ else
+ to_chat(usr, "Payment failure: unable to process payment.")
+ vend_ready = TRUE
if("togglevoice")
if(!panel_open)
return FALSE
shut_up = !shut_up
-/obj/machinery/vending/proc/vend(datum/stored_item/vending_product/R, mob/user)
- if((!allowed(usr)) && !emagged && scan_id) //For SECURE VENDING MACHINES YEAH
- to_chat(usr, "Access denied. ") //Unless emagged of course
+/obj/machinery/vending/proc/can_buy(datum/stored_item/vending_product/R, mob/user)
+ if(!allowed(user) && !emagged && scan_id)
+ to_chat(user, "Access denied. ") //Unless emagged of course
flick("[icon_state]-deny",src)
playsound(src, 'sound/machines/deniedbeep.ogg', 50, 0)
+ return FALSE
+ return TRUE
+
+/obj/machinery/vending/proc/vend(datum/stored_item/vending_product/R, mob/user)
+ if(!can_buy(R, user))
return
- vend_ready = 0 //One thing at a time!!
- actively_vending = R.item_name
- status_message = "Vending..."
- status_error = 0
+
+ if(!R.amount)
+ to_chat(user, "[src] has ran out of that product. ")
+ vend_ready = TRUE
+ return
+
+ vend_ready = FALSE //One thing at a time!!
SStgui.update_uis(src)
if(R.category & CAT_COIN)
@@ -523,24 +558,22 @@
use_power(vend_power_usage) //actuators and stuff
flick("[icon_state]-vend",src)
- spawn(vend_delay)
- R.get_product(get_turf(src))
- if(has_logs)
- do_logging(R, user, 1)
- if(prob(1))
- sleep(3)
- if(R.get_product(get_turf(src)))
- visible_message("\The [src] clunks as it vends an additional item. ")
- playsound(src, "sound/[vending_sound]", 100, 1, 1)
+ addtimer(CALLBACK(src, .proc/delayed_vend, R, user), vend_delay)
- status_message = ""
- status_error = 0
- vend_ready = 1
- actively_vending = null
- currently_vending = null
- SStgui.update_uis(src)
+/obj/machinery/vending/proc/delayed_vend(datum/stored_item/vending_product/R, mob/user)
+ R.get_product(get_turf(src))
+ if(has_logs)
+ do_logging(R, user, 1)
+ if(prob(1))
+ sleep(3)
+ if(R.get_product(get_turf(src)))
+ visible_message("\The [src] clunks as it vends an additional item. ")
+ playsound(src, "sound/[vending_sound]", 100, 1, 1)
+
+ vend_ready = 1
+ currently_vending = null
+ SStgui.update_uis(src)
- return 1
/obj/machinery/vending/proc/do_logging(datum/stored_item/vending_product/R, mob/user, var/vending = 0)
if(user.GetIdCard())
diff --git a/code/game/machinery/vending_machines.dm b/code/game/machinery/vending_machines.dm
index 5cd055c5a54..8f09407df72 100644
--- a/code/game/machinery/vending_machines.dm
+++ b/code/game/machinery/vending_machines.dm
@@ -535,6 +535,7 @@
product.category = category
product_records.Add(product)
+ GLOB.vending_products[entry] = 1
/obj/machinery/vending/magivend
name = "MagiVend"
diff --git a/code/game/objects/items/weapons/storage/bible.dm b/code/game/objects/items/weapons/storage/bible.dm
index 62c70457c0b..aaa7fbe144e 100644
--- a/code/game/objects/items/weapons/storage/bible.dm
+++ b/code/game/objects/items/weapons/storage/bible.dm
@@ -1,3 +1,24 @@
+GLOBAL_LIST_INIT(biblenames, list(
+ "Bible", "Koran", "Scrapbook",
+ "Pagan", "White Bible", "Holy Light",
+ "Athiest", "Tome", "The King in Yellow",
+ "Ithaqua", "Scientology", "the bible melts",
+ "Necronomicon", "Orthodox", "Torah"))
+//If you get these two lists not matching in size, there will be runtimes and I will hurt you in ways you couldn't even begin to imagine
+// if your bible has no custom itemstate, use one of the existing ones
+GLOBAL_LIST_INIT(biblestates, list(
+ "bible", "koran", "scrapbook",
+ "shadows", "white", "holylight",
+ "athiest", "tome", "kingyellow",
+ "ithaqua", "scientology", "melted",
+ "necronomicon", "orthodoxy", "torah"))
+GLOBAL_LIST_INIT(bibleitemstates, list(
+ "bible", "koran", "scrapbook",
+ "syringe_kit", "syringe_kit", "syringe_kit",
+ "syringe_kit", "syringe_kit", "kingyellow",
+ "ithaqua", "scientology", "melted",
+ "necronomicon", "bible", "clipboard"))
+
/obj/item/weapon/storage/bible
name = "bible"
desc = "Apply to head repeatedly."
@@ -15,6 +36,51 @@
use_sound = 'sound/bureaucracy/bookopen.ogg'
drop_sound = 'sound/bureaucracy/bookclose.ogg'
+/obj/item/weapon/storage/bible/attack_self(mob/living/carbon/human/user)
+ if(GLOB.bible_icon_state)
+ icon_state = GLOB.bible_icon_state
+ item_state = GLOB.bible_item_state
+ return FALSE
+ if(user?.mind?.assigned_role != "Chaplain")
+ return FALSE
+
+ var/list/skins = list()
+ for(var/i in 1 to GLOB.biblestates.len)
+ var/image/bible_image = image(icon = 'icons/obj/storage.dmi', icon_state = GLOB.biblestates[i])
+ skins += list("[GLOB.biblenames[i]]" = bible_image)
+
+ var/choice = show_radial_menu(user, src, skins, custom_check = CALLBACK(src, .proc/check_menu, user), radius = 40, require_near = TRUE)
+ if(!choice)
+ return FALSE
+ var/bible_index = GLOB.biblenames.Find(choice)
+ if(!bible_index)
+ return FALSE
+ icon_state = GLOB.biblestates[bible_index]
+ item_state = GLOB.bibleitemstates[bible_index]
+
+ GLOB.bible_icon_state = icon_state
+ GLOB.bible_item_state = item_state
+ feedback_set_details("religion_book", "[choice]")
+
+/**
+ * Checks if we are allowed to interact with a radial menu
+ *
+ * Arguments:
+ * * user The mob interacting with the menu
+ */
+/obj/item/weapon/storage/bible/proc/check_menu(mob/living/carbon/human/user)
+ if(GLOB.bible_icon_state)
+ return FALSE
+ if(!istype(user))
+ return FALSE
+ if(user.get_active_hand() != src)
+ return FALSE
+ if(user.incapacitated())
+ return FALSE
+ if(user.mind.assigned_role != "Chaplain")
+ return FALSE
+ return TRUE
+
/obj/item/weapon/storage/bible/booze
name = "bible"
desc = "To be applied to the head repeatedly."
diff --git a/code/game/objects/items/weapons/storage/briefcase.dm b/code/game/objects/items/weapons/storage/briefcase.dm
index 86e2b06298a..ed4d8fdbbe7 100644
--- a/code/game/objects/items/weapons/storage/briefcase.dm
+++ b/code/game/objects/items/weapons/storage/briefcase.dm
@@ -15,7 +15,6 @@
/obj/item/weapon/storage/briefcase/clutch
name = "clutch purse"
desc = "A fashionable handheld bag typically used by women."
- icon = 'icons/obj/clothing/backpack.dmi' //VOREStation Edit - Wrong sprite location
icon_state = "clutch"
item_state_slots = list(slot_r_hand_str = "smpurse", slot_l_hand_str = "smpurse")
force = 0
diff --git a/code/modules/asset_cache/asset_list_items.dm b/code/modules/asset_cache/asset_list_items.dm
index ce718155262..384dcbcd9b9 100644
--- a/code/modules/asset_cache/asset_list_items.dm
+++ b/code/modules/asset_cache/asset_list_items.dm
@@ -355,39 +355,56 @@
// Insert(initial(D.id), I)
// return ..()
-// /datum/asset/spritesheet/vending
-// name = "vending"
+/datum/asset/spritesheet/vending
+ name = "vending"
-// /datum/asset/spritesheet/vending/register()
-// for (var/k in GLOB.vending_products)
-// var/atom/item = k
-// if (!ispath(item, /atom))
-// continue
+/datum/asset/spritesheet/vending/register()
+ for(var/k in GLOB.vending_products)
+ var/atom/item = k
+ if(!ispath(item, /atom))
+ continue
-// var/icon_file = initial(item.icon)
-// var/icon_state = initial(item.icon_state)
-// var/icon/I
+ var/icon_file = initial(item.icon)
+ var/icon_state = initial(item.icon_state)
-// var/icon_states_list = icon_states(icon_file)
-// if(icon_state in icon_states_list)
-// I = icon(icon_file, icon_state, SOUTH)
-// var/c = initial(item.color)
-// if (!isnull(c) && c != "#FFFFFF")
-// I.Blend(c, ICON_MULTIPLY)
-// else
-// var/icon_states_string
-// for (var/an_icon_state in icon_states_list)
-// if (!icon_states_string)
-// icon_states_string = "[json_encode(an_icon_state)](\ref[an_icon_state])"
-// else
-// icon_states_string += ", [json_encode(an_icon_state)](\ref[an_icon_state])"
-// stack_trace("[item] does not have a valid icon state, icon=[icon_file], icon_state=[json_encode(icon_state)](\ref[icon_state]), icon_states=[icon_states_string]")
-// I = icon('icons/turf/floors.dmi', "", SOUTH)
+ // I really don't like the fact that I have to do this, but what the hell else *can* I do to make all of these
+ // random special items work?
+ if(ispath(item, /obj/item/weapon/reagent_containers/food/drinks/glass2) && !ispath(item, /obj/item/weapon/reagent_containers/food/drinks/glass2/fitnessflask))
+ var/obj/item/weapon/reagent_containers/food/drinks/glass2/G = item
+ icon_state = initial(G.base_icon)
+ if(ispath(item, /obj/item/clothing/suit))
+ var/obj/item/clothing/suit/U = item
+ if(initial(U.index))
+ icon_file = "icons/obj/clothing/suits_[initial(U.index)].dmi"
+ if(ispath(item, /obj/item/clothing/under))
+ var/obj/item/clothing/under/U = item
+ if(initial(U.index))
+ icon_file = "icons/obj/clothing/uniforms_[initial(U.index)].dmi"
+ if(ispath(item, /obj/item/weapon/reagent_containers/hypospray/autoinjector))
+ icon_state += "0"
-// var/imgid = replacetext(replacetext("[item]", "/obj/item/", ""), "/", "-")
+ var/icon/I
-// Insert(imgid, I)
-// return ..()
+ var/icon_states_list = icon_states(icon_file)
+ if(icon_state in icon_states_list)
+ I = icon(icon_file, icon_state, SOUTH)
+ var/c = initial(item.color)
+ if(!isnull(c) && c != "#FFFFFF")
+ I.Blend(c, ICON_MULTIPLY)
+ else
+ var/icon_states_string
+ for(var/an_icon_state in icon_states_list)
+ if(!icon_states_string)
+ icon_states_string = "[json_encode(an_icon_state)](\ref[an_icon_state])"
+ else
+ icon_states_string += ", [json_encode(an_icon_state)](\ref[an_icon_state])"
+ stack_trace("[item] does not have a valid icon state, icon=[icon_file], icon_state=[json_encode(icon_state)](\ref[icon_state]), icon_states=[icon_states_string]")
+ I = icon('icons/turf/floors.dmi', "", SOUTH)
+
+ var/imgid = replacetext(replacetext("[item]", "/obj/item/", ""), "/", "-")
+
+ Insert(imgid, I)
+ return ..()
// /datum/asset/simple/genetics
// assets = list(
diff --git a/code/modules/clothing/under/accessories/accessory_vr.dm b/code/modules/clothing/under/accessories/accessory_vr.dm
index 2d8086b2b33..c6715ebf891 100644
--- a/code/modules/clothing/under/accessories/accessory_vr.dm
+++ b/code/modules/clothing/under/accessories/accessory_vr.dm
@@ -29,6 +29,7 @@
slot_flags = SLOT_TIE | SLOT_OCLOTHING
icon = 'icons/obj/clothing/ties_vr.dmi'
icon_override = 'icons/mob/ties_vr.dmi'
+ icon_state = "collar_blk"
var/writtenon = 0
/obj/item/clothing/accessory/collar/silver
diff --git a/code/modules/clothing/under/miscellaneous.dm b/code/modules/clothing/under/miscellaneous.dm
index 32816339d18..1d4ba551d9b 100644
--- a/code/modules/clothing/under/miscellaneous.dm
+++ b/code/modules/clothing/under/miscellaneous.dm
@@ -1027,7 +1027,7 @@ Uniforms and such
desc = "Made from a space-proof fibre and tight fitting, this uniform usually gives the agile Rangers all kinds of protection while not inhibiting their movement. \
This costume is instead made from genuine cotton fibre and is based on the season three uniform."
icon = 'icons/obj/clothing/ranger.dmi'
- icon_state = "ranger_uniform"
+ icon_state = "white_ranger_uniform"
rolled_down = 0
rolled_sleeves = 0
diff --git a/code/modules/clothing/under/shorts.dm b/code/modules/clothing/under/shorts.dm
index 0e345779d2b..0d6516cc8e6 100644
--- a/code/modules/clothing/under/shorts.dm
+++ b/code/modules/clothing/under/shorts.dm
@@ -78,7 +78,7 @@
/obj/item/clothing/under/shorts/jeans/grey
name = "grey jeans shorts"
- icon_state = "greypants_shorts"
+ icon_state = "greyshorts"
/obj/item/clothing/under/shorts/jeans/grey/female
name = "grey jeans short shorts"
diff --git a/code/modules/economy/cash.dm b/code/modules/economy/cash.dm
index 3d4d44d918a..f13684642c6 100644
--- a/code/modules/economy/cash.dm
+++ b/code/modules/economy/cash.dm
@@ -1,5 +1,6 @@
/obj/item/weapon/spacecash
name = "0 Thaler"
+ var/initial_name = "Thaler"
desc = "It's worth 0 Thalers."
gender = PLURAL
icon = 'icons/obj/items.dmi'
@@ -31,15 +32,15 @@
h_user.drop_from_inventory(src)
h_user.drop_from_inventory(SC)
h_user.put_in_hands(SC)
- to_chat(user, "You combine the Thalers to a bundle of [SC.worth] Thalers. ")
+ to_chat(user, "You combine the [initial_name]s to a bundle of [SC.worth] [initial_name]s. ")
qdel(src)
/obj/item/weapon/spacecash/update_icon()
overlays.Cut()
- name = "[worth] Thaler\s"
+ name = "[worth] [initial_name]\s"
if(worth in list(1000,500,200,100,50,20,10,1))
icon_state = "spacecash[worth]"
- desc = "It's worth [worth] Thalers."
+ desc = "It's worth [worth] [initial_name]s."
return
var/sum = src.worth
var/num = 0
@@ -60,7 +61,7 @@
M.Turn(pick(-45, -27.5, 0, 0, 0, 0, 0, 0, 0, 27.5, 45))
banknote.transform = M
src.overlays += banknote
- src.desc = "They are worth [worth] Thalers."
+ src.desc = "They are worth [worth] [initial_name]s."
/obj/item/weapon/spacecash/proc/adjust_worth(var/adjust_worth = 0, var/update = 1)
worth += adjust_worth
@@ -79,7 +80,7 @@
return worth
/obj/item/weapon/spacecash/attack_self()
- var/amount = input(usr, "How many Thalers do you want to take? (0 to [src.worth])", "Take Money", 20) as num
+ var/amount = input(usr, "How many [initial_name]s do you want to take? (0 to [src.worth])", "Take Money", 20) as num
if(!src || QDELETED(src))
return
amount = round(CLAMP(amount, 0, src.worth))
@@ -150,6 +151,7 @@ proc/spawn_money(var/sum, spawnloc, mob/living/carbon/human/human_user as mob)
/obj/item/weapon/spacecash/ewallet
name = "charge card"
+ initial_name = "charge card"
icon_state = "efundcard"
desc = "A card that holds an amount of money."
drop_sound = 'sound/items/drop/card.ogg'
diff --git a/code/modules/food/kitchen/cooking_machines/_appliance.dm b/code/modules/food/kitchen/cooking_machines/_appliance.dm
index 780a1502ffc..a0be64fff98 100644
--- a/code/modules/food/kitchen/cooking_machines/_appliance.dm
+++ b/code/modules/food/kitchen/cooking_machines/_appliance.dm
@@ -81,6 +81,30 @@
else
to_chat(user, "")
+/obj/machinery/appliance/proc/report_progress_tgui(datum/cooking_item/CI)
+ if(!CI || !CI.max_cookwork)
+ return list("average", "Not Cooking.")
+
+ if(!CI.cookwork)
+ return list("blue", "Cold.")
+
+ var/progress = CI.cookwork / CI.max_cookwork
+
+ if (progress < 0.25)
+ return list("blue", "It's barely started cooking.")
+ if (progress < 0.75)
+ return list("average", "It's cooking away nicely.")
+ if (progress < 1)
+ return list("good", "It's almost ready!")
+
+ var/half_overcook = (CI.overcook_mult - 1)*0.5
+ if (progress < 1+half_overcook)
+ return list("good", "It's done!")
+ if (progress < CI.overcook_mult)
+ return list("bad", "It looks overcooked, get it out!")
+ else
+ return list("bad", "It is burning!")
+
/obj/machinery/appliance/proc/report_progress(var/datum/cooking_item/CI)
if (!CI || !CI.max_cookwork)
return null
@@ -240,28 +264,28 @@
/obj/machinery/appliance/attackby(var/obj/item/I, var/mob/user)
if(!cook_type || (stat & (BROKEN)))
to_chat(user, "\The [src] is not working. ")
- return
+ return FALSE
var/result = can_insert(I, user)
if(!result)
if(!(default_deconstruction_screwdriver(user, I)))
default_part_replacement(user, I)
- return
+ return FALSE
if(result == 2)
var/obj/item/weapon/grab/G = I
if (G && istype(G) && G.affecting)
cook_mob(G.affecting, user)
- return
+ return FALSE
//From here we can start cooking food
- add_content(I, user)
+ . = add_content(I, user)
update_icon()
//Override for container mechanics
/obj/machinery/appliance/proc/add_content(var/obj/item/I, var/mob/user)
if(!user.unEquip(I))
- return
+ return FALSE
var/datum/cooking_item/CI = has_space(I)
if (istype(I, /obj/item/weapon/reagent_containers/cooking_container) && CI == 1)
@@ -271,13 +295,13 @@
cooking_objs.Add(CI)
user.visible_message("\The [user] puts \the [I] into \the [src]. ")
if (CC.check_contents() == 0)//If we're just putting an empty container in, then dont start any processing.
- return
+ return TRUE
else
if (CI && istype(CI))
I.forceMove(CI.container)
else //Something went wrong
- return
+ return FALSE
if (selected_option)
CI.combine_target = selected_option
@@ -549,11 +573,11 @@
FA.alarm()
/obj/machinery/appliance/attack_hand(var/mob/user)
- if (cooking_objs.len)
- if (removal_menu(user))
- return
- else
- ..()
+ if(..())
+ return
+
+ if(cooking_objs.len)
+ removal_menu(user)
/obj/machinery/appliance/proc/removal_menu(var/mob/user)
if (can_remove_items(user))
@@ -571,7 +595,7 @@
return TRUE
return FALSE
-/obj/machinery/appliance/proc/can_remove_items(var/mob/user)
+/obj/machinery/appliance/proc/can_remove_items(var/mob/user, show_warning = TRUE)
if (!Adjacent(user))
return FALSE
diff --git a/code/modules/food/kitchen/cooking_machines/_cooker.dm b/code/modules/food/kitchen/cooking_machines/_cooker.dm
index 94b40b44b2f..ef097485b93 100644
--- a/code/modules/food/kitchen/cooking_machines/_cooker.dm
+++ b/code/modules/food/kitchen/cooking_machines/_cooker.dm
@@ -14,6 +14,63 @@
mobdamagetype = BURN
can_burn_food = TRUE
+/obj/machinery/appliance/cooker/attack_hand(mob/user)
+ tgui_interact(user)
+
+/obj/machinery/appliance/cooker/tgui_interact(mob/user, datum/tgui/ui, datum/tgui/parent_ui)
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "CookingAppliance", name)
+ ui.open()
+
+/obj/machinery/appliance/cooker/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state)
+ var/list/data = ..()
+
+ data["temperature"] = round(temperature - T0C, 0.1)
+ data["optimalTemp"] = round(optimal_temp - T0C, 0.1)
+ data["temperatureEnough"] = temperature >= min_temp
+ data["efficiency"] = round(get_efficiency(), 0.1)
+ data["containersRemovable"] = can_remove_items(user, show_warning = FALSE)
+
+ var/list/our_contents = list()
+ for(var/i in 1 to max_contents)
+ our_contents += list(list("empty" = TRUE))
+ if(i <= LAZYLEN(cooking_objs))
+ var/datum/cooking_item/CI = cooking_objs[i]
+ if(istype(CI))
+ our_contents[i] = list()
+ our_contents[i]["progress"] = 0
+ our_contents[i]["progressText"] = report_progress_tgui(CI)
+ if(CI.max_cookwork)
+ our_contents[i]["progress"] = CI.cookwork / CI.max_cookwork
+ if(CI.container)
+ our_contents[i]["container"] = CI.container.label(i)
+ else
+ our_contents[i]["container"] = null
+ data["our_contents"] = our_contents
+
+ return data
+
+/obj/machinery/appliance/cooker/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state)
+ if(..())
+ return TRUE
+
+ switch(action)
+ if("slot")
+ var/slot = params["slot"]
+ var/obj/item/I = usr.get_active_hand()
+ if(slot <= LAZYLEN(cooking_objs)) // Inserting
+ var/datum/cooking_item/CI = cooking_objs[slot]
+
+ if(istype(I) && can_insert(I)) // Why do hard work when we can just make them smack us?
+ attackby(I, usr)
+ else if(istype(CI))
+ eject(CI, usr)
+ return TRUE
+ if(istype(I)) // Why do hard work when we can just make them smack us?
+ attackby(I, usr)
+ return TRUE
+
/obj/machinery/appliance/cooker/examine(var/mob/user)
. = ..()
if(.) //no need to duplicate adjacency check
@@ -141,6 +198,6 @@
/obj/machinery/appliance/cooker/add_content(var/obj/item/I, var/mob/user)
var/datum/cooking_item/CI = ..()
- if (CI && CI.combine_target)
+ if(istype(CI) && CI.combine_target)
to_chat(user, "\The [I] will be used to make a [selected_option]. Output selection is returned to default for future items.")
selected_option = null
\ No newline at end of file
diff --git a/code/modules/food/kitchen/cooking_machines/_mixer.dm b/code/modules/food/kitchen/cooking_machines/_mixer.dm
index e1c3b0d838c..079b0c686c2 100644
--- a/code/modules/food/kitchen/cooking_machines/_mixer.dm
+++ b/code/modules/food/kitchen/cooking_machines/_mixer.dm
@@ -68,11 +68,13 @@ fundamental differences
return 0
-/obj/machinery/appliance/mixer/can_remove_items(var/mob/user)
- if (stat)
+/obj/machinery/appliance/mixer/can_remove_items(var/mob/user, show_warning = TRUE)
+ if(stat)
return 1
else
- to_chat(user, "You can't remove ingredients while it's turned on! Turn it off first or wait for it to finish. ")
+ if(show_warning)
+ to_chat(user, "You can't remove ingredients while it's turned on! Turn it off first or wait for it to finish. ")
+ return 0
//Container is not removable
/obj/machinery/appliance/mixer/removal_menu(var/mob/user)
diff --git a/code/modules/food/kitchen/cooking_machines/oven.dm b/code/modules/food/kitchen/cooking_machines/oven.dm
index d6169736311..b7a07d599c7 100644
--- a/code/modules/food/kitchen/cooking_machines/oven.dm
+++ b/code/modules/food/kitchen/cooking_machines/oven.dm
@@ -104,7 +104,7 @@
cooking = FALSE
playsound(src, 'sound/machines/hatch_open.ogg', 20, 1)
- to_chat(user, "You [open? "close":"open"] the oven door ")
+ to_chat(user, "You [open ? "open" : "close"] the oven door. ")
update_icon()
/obj/machinery/appliance/cooker/oven/proc/manip(var/obj/item/I)
@@ -133,9 +133,10 @@
if(temperature > T.temperature)
equalize_temperature()
-/obj/machinery/appliance/cooker/oven/can_remove_items(var/mob/user)
+/obj/machinery/appliance/cooker/oven/can_remove_items(var/mob/user, show_warning = TRUE)
if(!open)
- to_chat(user, "You can't take anything out while the door is closed! ")
+ if(show_warning)
+ to_chat(user, "You can't take anything out while the door is closed! ")
return 0
else
diff --git a/code/modules/food/kitchen/microwave.dm b/code/modules/food/kitchen/microwave.dm
index 065d036920c..5a499ae846c 100644
--- a/code/modules/food/kitchen/microwave.dm
+++ b/code/modules/food/kitchen/microwave.dm
@@ -120,6 +120,7 @@
src.broken = 0 // just to be sure
src.icon_state = "mw"
src.flags = OPENCONTAINER | NOREACT
+ SStgui.update_uis(src)
else //Otherwise bad luck!!
to_chat(user, "It's dirty! ")
return 1
@@ -141,10 +142,11 @@
user.visible_message( \
"\The [user] has added \the [O] to \the [src]. ", \
"You add \the [O] to \the [src]. ")
+ SStgui.update_uis(src)
return
else if(istype(O,/obj/item/weapon/reagent_containers/glass) || \
- istype(O,/obj/item/weapon/reagent_containers/food/drinks) || \
- istype(O,/obj/item/weapon/reagent_containers/food/condiment) \
+ istype(O,/obj/item/weapon/reagent_containers/food/drinks) || \
+ istype(O,/obj/item/weapon/reagent_containers/food/condiment) \
)
if (!O.reagents)
return 1
@@ -181,83 +183,168 @@
else
to_chat(user, "You have no idea what you can cook with this [O]. ")
..()
- src.updateUsrDialog()
+ SStgui.update_uis(src)
+
+/obj/machinery/microwave/tgui_state(mob/user)
+ return GLOB.tgui_physical_state
/obj/machinery/microwave/attack_ai(mob/user as mob)
- if(istype(user, /mob/living/silicon/robot) && Adjacent(user))
- attack_hand(user)
+ attack_hand(user)
/obj/machinery/microwave/attack_hand(mob/user as mob)
user.set_machine(src)
- interact(user)
+ tgui_interact(user)
/*******************
* Microwave Menu
********************/
+/obj/machinery/microwave/tgui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "Microwave", name)
+ ui.open()
-/obj/machinery/microwave/interact(mob/user as mob) // The microwave Menu
- var/dat = ""
- if(src.broken > 0)
- dat = {"Bzzzzttttt "}
- else if(src.operating)
- dat = {"Microwaving in progress! Please wait...! "}
- else if(src.dirty==100)
- dat = {"This microwave is dirty! Please clean it before use! "}
- else
- var/list/items_counts = new
- var/list/items_measures = new
- var/list/items_measures_p = new
- for (var/obj/O in ((contents - component_parts) - circuit))
- var/display_name = O.name
- if (istype(O,/obj/item/weapon/reagent_containers/food/snacks/egg))
- items_measures[display_name] = "egg"
- items_measures_p[display_name] = "eggs"
- if (istype(O,/obj/item/weapon/reagent_containers/food/snacks/tofu))
- items_measures[display_name] = "tofu chunk"
- items_measures_p[display_name] = "tofu chunks"
- if (istype(O,/obj/item/weapon/reagent_containers/food/snacks/meat)) //any meat
- items_measures[display_name] = "slab of meat"
- items_measures_p[display_name] = "slabs of meat"
- if (istype(O,/obj/item/weapon/reagent_containers/food/snacks/donkpocket))
- display_name = "Turnovers"
- items_measures[display_name] = "turnover"
- items_measures_p[display_name] = "turnovers"
- if (istype(O,/obj/item/weapon/reagent_containers/food/snacks/carpmeat))
- items_measures[display_name] = "fillet of meat"
- items_measures_p[display_name] = "fillets of meat"
- items_counts[display_name]++
- for (var/O in items_counts)
- var/N = items_counts[O]
- if (!(O in items_measures))
- dat += {"[capitalize(O)]: [N] [lowertext(O)]\s "}
- else
- if (N==1)
- dat += {"[capitalize(O)]: [N] [items_measures[O]] "}
- else
- dat += {"[capitalize(O)]: [N] [items_measures_p[O]] "}
+/obj/machinery/microwave/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state)
+ var/list/data = ..()
- for (var/datum/reagent/R in reagents.reagent_list)
- var/display_name = R.name
- if (R.id == "capsaicin")
- display_name = "Hotsauce"
- if (R.id == "frostoil")
- display_name = "Coldsauce"
- dat += {"[display_name]: [R.volume] unit\s "}
+ data["broken"] = broken
+ data["operating"] = operating
+ data["dirty"] = dirty == 100
+ data["items"] = get_items_list()
+
+ return data
- if (items_counts.len==0 && reagents.reagent_list.len==0)
- dat = {"The microwave is empty "}
+/obj/machinery/microwave/proc/get_items_list()
+ var/list/data = list()
+
+ var/list/items_counts = list()
+ var/list/items_measures = list()
+ var/list/items_measures_p = list()
+ for(var/obj/O in ((contents - component_parts) - circuit))
+ var/display_name = O.name
+ if(istype(O,/obj/item/weapon/reagent_containers/food/snacks/egg))
+ items_measures[display_name] = "egg"
+ items_measures_p[display_name] = "eggs"
+ if(istype(O,/obj/item/weapon/reagent_containers/food/snacks/tofu))
+ items_measures[display_name] = "tofu chunk"
+ items_measures_p[display_name] = "tofu chunks"
+ if(istype(O,/obj/item/weapon/reagent_containers/food/snacks/meat)) //any meat
+ items_measures[display_name] = "slab of meat"
+ items_measures_p[display_name] = "slabs of meat"
+ if(istype(O,/obj/item/weapon/reagent_containers/food/snacks/donkpocket))
+ display_name = "Turnovers"
+ items_measures[display_name] = "turnover"
+ items_measures_p[display_name] = "turnovers"
+ if(istype(O,/obj/item/weapon/reagent_containers/food/snacks/carpmeat))
+ items_measures[display_name] = "fillet of meat"
+ items_measures_p[display_name] = "fillets of meat"
+ items_counts[display_name]++
+ for(var/O in items_counts)
+ var/N = items_counts[O]
+ if(!(O in items_measures))
+ data.Add(list(list(
+ "name" = capitalize(O),
+ "amt" = N,
+ "extra" = "[lowertext(O)][N > 1 ? "s" : ""]",
+ )))
else
- dat = {"Ingredients: [dat]"}
- dat += {" \
-Turn on! \
- Eject ingredients! \
-"}
+ data.Add(list(list(
+ "name" = capitalize(O),
+ "amt" = N,
+ "extra" = N == 1 ? items_measures[O] : items_measures_p[O],
+ )))
- user << browse("Microwave Controls [dat] ", "window=microwave")
- onclose(user, "microwave")
- return
+ for(var/datum/reagent/R in reagents.reagent_list)
+ var/display_name = R.name
+ if(R.id == "capsaicin")
+ display_name = "Hotsauce"
+ if(R.id == "frostoil")
+ display_name = "Coldsauce"
+ data.Add(list(list(
+ "name" = display_name,
+ "amt" = R.volume,
+ "extra" = "unit[R.volume > 1 ? "s" : ""]"
+ )))
+ return data
+/obj/machinery/microwave/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state)
+ if(..())
+ return TRUE
+
+ if(operating)
+ return TRUE
+
+ switch(action)
+ if("cook")
+ cook()
+ return TRUE
+
+ if("dispose")
+ dispose()
+ return TRUE
+
+// /obj/machinery/microwave/interact(mob/user as mob) // The microwave Menu
+// var/dat = ""
+// if(src.broken > 0)
+// dat = {"Bzzzzttttt "}
+// else if(src.operating)
+// dat = {"Microwaving in progress! Please wait...! "}
+// else if(src.dirty==100)
+// dat = {"This microwave is dirty! Please clean it before use! "}
+// else
+// var/list/items_counts = new
+// var/list/items_measures = new
+// var/list/items_measures_p = new
+// for (var/obj/O in ((contents - component_parts) - circuit))
+// var/display_name = O.name
+// if (istype(O,/obj/item/weapon/reagent_containers/food/snacks/egg))
+// items_measures[display_name] = "egg"
+// items_measures_p[display_name] = "eggs"
+// if (istype(O,/obj/item/weapon/reagent_containers/food/snacks/tofu))
+// items_measures[display_name] = "tofu chunk"
+// items_measures_p[display_name] = "tofu chunks"
+// if (istype(O,/obj/item/weapon/reagent_containers/food/snacks/meat)) //any meat
+// items_measures[display_name] = "slab of meat"
+// items_measures_p[display_name] = "slabs of meat"
+// if (istype(O,/obj/item/weapon/reagent_containers/food/snacks/donkpocket))
+// display_name = "Turnovers"
+// items_measures[display_name] = "turnover"
+// items_measures_p[display_name] = "turnovers"
+// if (istype(O,/obj/item/weapon/reagent_containers/food/snacks/carpmeat))
+// items_measures[display_name] = "fillet of meat"
+// items_measures_p[display_name] = "fillets of meat"
+// items_counts[display_name]++
+// for (var/O in items_counts)
+// var/N = items_counts[O]
+// if (!(O in items_measures))
+// dat += {"[capitalize(O)]: [N] [lowertext(O)]\s "}
+// else
+// if (N==1)
+// dat += {"[capitalize(O)]: [N] [items_measures[O]] "}
+// else
+// dat += {"[capitalize(O)]: [N] [items_measures_p[O]] "}
+
+// for (var/datum/reagent/R in reagents.reagent_list)
+// var/display_name = R.name
+// if (R.id == "capsaicin")
+// display_name = "Hotsauce"
+// if (R.id == "frostoil")
+// display_name = "Coldsauce"
+// dat += {"[display_name]: [R.volume] unit\s "}
+
+// if (items_counts.len==0 && reagents.reagent_list.len==0)
+// dat = {"The microwave is empty "}
+// else
+// dat = {"Ingredients: [dat]"}
+// dat += {" \
+// Turn on! \
+// Eject ingredients! \
+// "}
+
+// user << browse("Microwave Controls [dat] ", "window=microwave")
+// onclose(user, "microwave")
+// return
/***********************************
* Microwave Menu Handling/Cooking
@@ -388,13 +475,13 @@
soundloop.start()
src.operating = TRUE
src.icon_state = "mw1"
- src.updateUsrDialog()
+ SStgui.update_uis(src)
/obj/machinery/microwave/proc/abort()
operating = FALSE // Turn it off again aferwards
if(icon_state == "mw1")
icon_state = "mw"
- updateUsrDialog()
+ SStgui.update_uis(src)
soundloop.stop()
/obj/machinery/microwave/proc/stop()
@@ -402,7 +489,7 @@
operating = FALSE // Turn it off again aferwards
if(icon_state == "mw1")
icon_state = "mw"
- updateUsrDialog()
+ SStgui.update_uis(src)
soundloop.stop()
/obj/machinery/microwave/proc/dispose(var/message = 1)
@@ -413,7 +500,7 @@
src.reagents.clear_reagents()
if(message)
to_chat(usr, "You dispose of the microwave contents. ")
- src.updateUsrDialog()
+ SStgui.update_uis(src)
/obj/machinery/microwave/proc/muck_start()
playsound(src, 'sound/effects/splat.ogg', 50, 1) // Play a splat sound
@@ -425,7 +512,7 @@
src.flags = null //So you can't add condiments
src.icon_state = "mwbloody" // Make it look dirty too
src.operating = 0 // Turn it off again aferwards
- src.updateUsrDialog()
+ SStgui.update_uis(src)
soundloop.stop()
@@ -438,7 +525,7 @@
src.broken = 2 // Make it broken so it can't be used util fixed
src.flags = null //So you can't add condiments
src.operating = 0 // Turn it off again aferwards
- src.updateUsrDialog()
+ SStgui.update_uis(src)
soundloop.stop()
/obj/machinery/microwave/proc/fail()
@@ -460,23 +547,6 @@
ffuu.reagents.add_reagent("toxin", amount/10)
return ffuu
-/obj/machinery/microwave/Topic(href, href_list)
- if(..())
- return
-
- usr.set_machine(src)
- if(src.operating)
- src.updateUsrDialog()
- return
-
- switch(href_list["action"])
- if ("cook")
- cook()
-
- if ("dispose")
- dispose()
- return
-
/obj/machinery/microwave/verb/Eject()
set src in oview(1)
set category = "Object"
diff --git a/code/modules/holodeck/HolodeckControl.dm b/code/modules/holodeck/HolodeckControl.dm
index 5ba939fd8d2..bfa684f4e61 100644
--- a/code/modules/holodeck/HolodeckControl.dm
+++ b/code/modules/holodeck/HolodeckControl.dm
@@ -79,60 +79,59 @@
return
user.set_machine(src)
- ui_interact(user)
+ tgui_interact(user)
/**
- * Display the NanoUI window for the Holodeck Computer.
- *
- * See NanoUI documentation for details.
+ * Open the UI!
*/
-/obj/machinery/computer/HolodeckControl/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
- user.set_machine(src)
+/obj/machinery/computer/HolodeckControl/tgui_interact(mob/user, datum/tgui/ui, datum/tgui/parent_ui)
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "Holodeck", name)
+ ui.open()
- var/list/data = list()
- var/program_list[0]
- var/restricted_program_list[0]
+/**
+ * Data for the TGUI UI
+ */
+/obj/machinery/computer/HolodeckControl/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state)
+ var/list/data = ..()
+ var/list/program_list = list()
+ var/list/restricted_program_list = list()
for(var/P in supported_programs)
- program_list[++program_list.len] = P
+ program_list.Add(P)
for(var/P in restricted_programs)
- restricted_program_list[++restricted_program_list.len] = P
+ restricted_program_list.Add(P)
data["supportedPrograms"] = program_list
data["restrictedPrograms"] = restricted_program_list
data["currentProgram"] = current_program
+ data["isSilicon"] = FALSE
if(issilicon(user))
- data["isSilicon"] = 1
- else
- data["isSilicon"] = null
+ data["isSilicon"] = TRUE
+
data["safetyDisabled"] = safety_disabled
data["emagged"] = emagged
+ data["gravity"] = FALSE
if(linkedholodeck.has_gravity)
- data["gravity"] = 1
- else
- data["gravity"] = null
+ data["gravity"] = TRUE
- ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open)
- if (!ui)
- ui = new(user, src, ui_key, "holodeck.tmpl", src.name, 400, 550)
- ui.set_initial_data(data)
- ui.open()
- ui.set_auto_update(20)
+ return data
-/obj/machinery/computer/HolodeckControl/Topic(href, href_list)
+/obj/machinery/computer/HolodeckControl/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state)
if(..())
- return 1
- if((usr.contents.Find(src) || (in_range(src, usr) && istype(src.loc, /turf))) || (istype(usr, /mob/living/silicon)))
- usr.set_machine(src)
+ return TRUE
- if(href_list["program"])
- var/prog = href_list["program"]
+ switch(action)
+ if("program")
+ var/prog = params["program"]
if(prog in (supported_programs + restricted_programs))
if(loadProgram(prog))
current_program = prog
+ return TRUE
- else if(href_list["AIoverride"])
+ if("AIoverride")
if(!issilicon(usr))
return
@@ -147,13 +146,13 @@
else
message_admins("[key_name_admin(usr)] restored the holodeck's safeties")
log_game("[key_name(usr)] restored the holodeck's safeties")
+ return TRUE
- else if(href_list["gravity"])
+ if("gravity")
toggleGravity(linkedholodeck)
+ return TRUE
- src.add_fingerprint(usr)
-
- SSnanoui.update_uis(src)
+ add_fingerprint(usr)
/obj/machinery/computer/HolodeckControl/emag_act(var/remaining_charges, var/mob/user as mob)
playsound(src, 'sound/effects/sparks4.ogg', 75, 1)
diff --git a/code/modules/library/lib_machines.dm b/code/modules/library/lib_machines.dm
index 1caf83608b0..17eee1149cd 100644
--- a/code/modules/library/lib_machines.dm
+++ b/code/modules/library/lib_machines.dm
@@ -338,11 +338,11 @@ datum/borrowbook // Datum used to keep track of who has borrowed what when and f
if(!bibledelay)
var/obj/item/weapon/storage/bible/B = new /obj/item/weapon/storage/bible(src.loc)
- if(ticker && ( ticker.Bible_icon_state && ticker.Bible_item_state) )
- B.icon_state = ticker.Bible_icon_state
- B.item_state = ticker.Bible_item_state
- B.name = ticker.Bible_name
- B.deity_name = ticker.Bible_deity_name
+ if(GLOB.religion)
+ B.icon_state = GLOB.bible_icon_state
+ B.item_state = GLOB.bible_item_state
+ B.name = GLOB.bible_name
+ B.deity_name = GLOB.deity
bibledelay = 1
spawn(60)
diff --git a/code/modules/looking_glass/lg_console.dm b/code/modules/looking_glass/lg_console.dm
index 23a4e1b12e4..3a151fa94a4 100644
--- a/code/modules/looking_glass/lg_console.dm
+++ b/code/modules/looking_glass/lg_console.dm
@@ -50,27 +50,30 @@
return ..()
/obj/machinery/computer/looking_glass/attack_ai(var/mob/user as mob)
- return src.attack_hand(user)
+ return attack_hand(user)
/obj/machinery/computer/looking_glass/attack_hand(var/mob/user as mob)
if(..())
return
- user.set_machine(src)
- ui_interact(user)
+ tgui_interact(user)
-/obj/machinery/computer/looking_glass/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
- user.set_machine(src)
-
- var/list/data = list()
- var/program_list[0]
+/obj/machinery/computer/looking_glass/tgui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "LookingGlass", name)
+ ui.open()
+/obj/machinery/computer/looking_glass/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state)
+ var/list/data = ..()
+
+ var/list/program_list = list()
for(var/P in supported_programs)
- program_list[++program_list.len] = P
+ program_list.Add(P)
if(emagged)
for(var/P in secret_programs)
- program_list[++program_list.len] = P
+ program_list.Add(P)
data["supportedPrograms"] = program_list
data["currentProgram"] = current_program
@@ -78,24 +81,18 @@
if(my_area?.has_gravity)
data["gravity"] = 1
else
- data["gravity"] = null
+ data["gravity"] = 0
- ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open)
- if (!ui)
- ui = new(user, src, ui_key, "lookingglass.tmpl", src.name, 400, 550)
- ui.set_initial_data(data)
- ui.open()
- ui.set_auto_update(20)
+ return data
-/obj/machinery/computer/looking_glass/Topic(href, href_list)
+/obj/machinery/computer/looking_glass/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state)
if(..())
- return 1
- if((usr.contents.Find(src) || (in_range(src, usr) && istype(src.loc, /turf))) || (istype(usr, /mob/living/silicon)))
- usr.set_machine(src)
-
- if(href_list["program"])
+ return TRUE
+
+ switch(action)
+ if("program")
if(ready)
- var/prog = href_list["program"]
+ var/prog = params["program"]
if(prog == "Off")
current_program = "Off"
unload_program()
@@ -104,17 +101,18 @@
load_program(prog)
else
visible_message("ERROR. Recalibrating displays. ")
+ return TRUE
- else if(href_list["gravity"])
+ if("gravity")
toggle_gravity(my_area)
+ return TRUE
- else if(href_list["immersion"])
+ if("immersion")
immersion = !immersion
my_area.toggle_optional(immersion)
+ return TRUE
- src.add_fingerprint(usr)
-
- SSnanoui.update_uis(src)
+ add_fingerprint(usr)
/obj/machinery/computer/looking_glass/emag_act(var/remaining_charges, var/mob/user as mob)
if (!emagged)
diff --git a/code/modules/media/media_tracks.dm b/code/modules/media/media_tracks.dm
index 42ad9809fb0..840f857f960 100644
--- a/code/modules/media/media_tracks.dm
+++ b/code/modules/media/media_tracks.dm
@@ -25,7 +25,7 @@
str += " by [artist]"
return str
-/datum/track/proc/toNanoList()
+/datum/track/proc/toTguiList()
return list("ref" = "\ref[src]", "title" = title, "artist" = artist, "duration" = duration)
diff --git a/code/modules/nifsoft/nif.dm b/code/modules/nifsoft/nif.dm
index d2400c6bff6..d03a2adf07d 100644
--- a/code/modules/nifsoft/nif.dm
+++ b/code/modules/nifsoft/nif.dm
@@ -379,6 +379,29 @@ You can also set the stat of a NIF to NIF_TEMPFAIL without any issues to disable
human.adjust_nutrition(-use_charge)
return TRUE
+// This operates on a nifsoft *path*, not an instantiation.
+// It tells the nifsoft shop if it's installation will succeed, to prevent it
+// from charging the user for incompatible software.
+/obj/item/device/nif/proc/can_install(var/datum/nifsoft/path)
+ if(stat == NIF_TEMPFAIL)
+ return FALSE
+
+ if(nifsofts[initial(path.list_pos)])
+ notify("The software \"[initial(path.name)]\" is already installed.", TRUE)
+ return FALSE
+
+ if(human)
+ var/applies_to = initial(path.applies_to)
+ var/synth = human.isSynthetic()
+ if(synth && !(applies_to & NIF_SYNTHETIC))
+ notify("The software \"[initial(path.name)]\" is not supported on your chassis type.",TRUE)
+ return FALSE
+ if(!synth && !(applies_to & NIF_ORGANIC))
+ notify("The software \"[initial(path.name)]\" is not supported in organic life.",TRUE)
+ return FALSE
+
+ return TRUE
+
//Install a piece of software
/obj/item/device/nif/proc/install(var/datum/nifsoft/new_soft)
if(stat == NIF_TEMPFAIL) return FALSE
diff --git a/code/modules/nifsoft/nif_softshop.dm b/code/modules/nifsoft/nif_softshop.dm
index d802abde4c6..5cfae594c4c 100644
--- a/code/modules/nifsoft/nif_softshop.dm
+++ b/code/modules/nifsoft/nif_softshop.dm
@@ -25,6 +25,10 @@
wires = new /datum/wires/vending/no_contraband(src) //These wires can't be hacked for contraband.
entopic = new(aholder = src, aicon = icon, aicon_state = "beacon")
+/obj/machinery/vending/nifsoft_shop/tgui_data(mob/user)
+ . = ..()
+ .["chargesMoney"] = TRUE
+
/obj/machinery/vending/nifsoft_shop/Destroy()
QDEL_NULL(entopic)
return ..()
@@ -93,95 +97,39 @@
product_records.Add(product)
-/obj/machinery/vending/nifsoft_shop/allowed(mob/user)
- if(!ishuman(user))
- return FALSE
+/obj/machinery/vending/nifsoft_shop/can_buy(datum/stored_item/vending_product/R, mob/user)
+ . = ..()
+ if(.)
+ var/datum/nifsoft/path = R.item_path
+ if(!ishuman(user))
+ return FALSE
+
+ var/mob/living/carbon/human/H = user
+ if(!H.nif || !H.nif.stat == NIF_WORKING)
+ to_chat(H, "[src] seems unable to connect to your NIF... ")
+ return FALSE
- var/mob/living/carbon/human/H = user
- if(!H.nif || !H.nif.stat == NIF_WORKING)
- to_chat(H, "[src] seems unable to connect to your NIF... ")
- flick("[icon_state]-deny",entopic.my_image)
- return FALSE
-
- return ..()
-
-//Had to override this too
-/obj/machinery/vending/nifsoft_shop/Topic(href, href_list)
- if(stat & (BROKEN|NOPOWER))
- return
- if(usr.stat || usr.restrained())
- return
-
- if(href_list["remove_coin"] && !istype(usr,/mob/living/silicon))
- if(!coin)
- to_chat(usr, "There is no coin in this machine.")
- return
-
- coin.forceMove(src.loc)
- if(!usr.get_active_hand())
- usr.put_in_hands(coin)
- to_chat(usr, "You remove \the [coin] from \the [src] ")
- coin = null
- categories &= ~CAT_COIN
-
- if((usr.contents.Find(src) || (in_range(src, usr) && istype(src.loc, /turf))))
- if((href_list["vend"]) && (vend_ready) && (!currently_vending))
- if((!allowed(usr)) && !emagged && scan_id) //For SECURE VENDING MACHINES YEAH
- to_chat(usr, "Access denied. ") //Unless emagged of course
- flick("[icon_state]-deny",entopic.my_image)
- return
-
- var/key = text2num(href_list["vend"])
- var/datum/stored_item/vending_product/R = product_records[key]
-
- // This should not happen unless the request from NanoUI was bad
- if(!(R.category & categories))
- return
-
- //Specific soft access checking
- var/datum/nifsoft/path = R.item_path
- if(initial(path.access))
- var/list/soft_access = list(initial(path.access))
- var/list/usr_access = usr.GetAccess()
- if(scan_id && !has_access(soft_access, list(), usr_access) && !emagged)
- to_chat(usr, "You aren't authorized to buy [initial(path.name)]. ")
- flick("[icon_state]-deny",entopic.my_image)
- return
-
- if(R.price <= 0)
- vend(R, usr)
- else if(istype(usr,/mob/living/silicon)) //If the item is not free, provide feedback if a synth is trying to buy something.
- to_chat(usr, "Artificial unit recognized. Artificial units cannot complete this transaction. Purchase canceled. ")
- return
- else
- currently_vending = R
- if(!vendor_account || vendor_account.suspended)
- status_message = "This machine is currently unable to process payments due to problems with the associated account."
- status_error = 1
- else
- status_message = "[initial(path.desc)]Please swipe a card or insert cash to pay for the item. "
- status_error = 0
-
- else if(href_list["cancelpurchase"])
- currently_vending = null
-
- else if((href_list["togglevoice"]) && (panel_open))
- shut_up = !shut_up
-
- add_fingerprint(usr)
- SSnanoui.update_uis(src)
+ if(!H.nif.can_install(path))
+ flick("[icon_state]-deny", entopic.my_image)
+ return FALSE
+
+ if(initial(path.access))
+ var/list/soft_access = list(initial(path.access))
+ var/list/usr_access = user.GetAccess()
+ if(scan_id && !has_access(soft_access, list(), usr_access) && !emagged)
+ to_chat(user, "You aren't authorized to buy [initial(path.name)]. ")
+ flick("[icon_state]-deny", entopic.my_image)
+ return FALSE
// Also special treatment!
/obj/machinery/vending/nifsoft_shop/vend(datum/stored_item/vending_product/R, mob/user)
var/mob/living/carbon/human/H = user
- if((!allowed(usr)) && !emagged && scan_id && istype(H)) //For SECURE VENDING MACHINES YEAH
- to_chat(usr, "Purchase not allowed. ") //Unless emagged of course
+ if(!can_buy(R, user)) //For SECURE VENDING MACHINES YEAH
+ to_chat(user, "Purchase not allowed. ") //Unless emagged of course
flick("[icon_state]-deny",entopic.my_image)
return
vend_ready = 0 //One thing at a time!!
- status_message = "Installing..."
- status_error = 0
- SSnanoui.update_uis(src)
+ SStgui.update_uis(src)
if(R.category & CAT_COIN)
if(!coin)
@@ -209,15 +157,14 @@
spawn(vend_delay)
R.amount--
new R.item_path(H.nif)
+ H.nif.notify("New software installed: [R.item_name]")
flick("[icon_state]-vend",entopic.my_image)
if(has_logs)
do_logging(R, user, 1)
- status_message = ""
- status_error = 0
vend_ready = 1
currently_vending = null
- SSnanoui.update_uis(src)
+ SStgui.update_uis(src)
return 1
//Can't throw intangible software at people.
diff --git a/code/modules/paperwork/photography.dm b/code/modules/paperwork/photography.dm
index b63048fdc0c..c4460e5dcf5 100644
--- a/code/modules/paperwork/photography.dm
+++ b/code/modules/paperwork/photography.dm
@@ -195,7 +195,7 @@ var/global/photo_count = 0
for(var/i; i <= sorted.len; i++)
var/atom/A = sorted[i]
if(A)
- var/icon/img = getFlatIcon(A)//, picture_planes = picture_planes)//build_composite_icon(A) //VOREStation Edit
+ var/icon/img = getFlatIcon(A, no_anim = TRUE)//, picture_planes = picture_planes)//build_composite_icon(A) //VOREStation Edit
// If what we got back is actually a picture, draw it.
if(istype(img, /icon))
@@ -216,7 +216,7 @@ var/global/photo_count = 0
// Calculate where we are relative to the center of the photo
var/xoff = (the_turf.x - center.x) * 32 + center_offset
var/yoff = (the_turf.y - center.y) * 32 + center_offset
- res.Blend(getFlatIcon(the_turf.loc), blendMode2iconMode(the_turf.blend_mode),xoff,yoff)
+ res.Blend(getFlatIcon(the_turf.loc, no_anim = TRUE), blendMode2iconMode(the_turf.blend_mode),xoff,yoff)
return res
diff --git a/icons/obj/clothing/ranger.dmi b/icons/obj/clothing/ranger.dmi
index 5f810d2ea46..a61db90889a 100644
Binary files a/icons/obj/clothing/ranger.dmi and b/icons/obj/clothing/ranger.dmi differ
diff --git a/nano/templates/holodeck.tmpl b/nano/templates/holodeck.tmpl
deleted file mode 100644
index a91d5a60e51..00000000000
--- a/nano/templates/holodeck.tmpl
+++ /dev/null
@@ -1,36 +0,0 @@
-
-
-Current Loaded Programs:
-{{for data.supportedPrograms}}
- {{:helper.link(value, data.currentProgram == value ? 'check' : 'close', {'program' : value}, null, data.currentProgram == value ? 'linkOn' : null)}}
-{{/for}}
-Please ensure that only holographic weapons are used in the holodeck if a combat simulation has been loaded.
-{{if data.isSilicon}}
- {{if data.safetyDisabled}}
- {{if data.emagged}}
- ERROR : Cannot re-enable Safety Protocols.
- {{else}}
- {{:helper.link('Re-Enable Safety Protocols?', 'help', {'AIoverride' : 1}, null, 'linkOn')}}
- {{/if}}
- {{else}}
- {{:helper.link('Re-Enable Safety Protocols?', 'help', {'AIoverride' : 1}, null, 'redButton')}}
- {{/if}}
-{{/if}}
-
-{{if data.safetyDisabled}}
- {{for data.restrictedPrograms}}
- {{:helper.link('Begin ' + value, data.currentProgram == value ? 'check' : 'close', {'program' : value.program}, null, data.currentProgram == value ? 'linkOn' : null)}}
- {{/for}}
- Ensure the holodeck is empty before testing.
- Safety Protocols are DISABLED
-{{else}}
- Safety Protocols are ENABLED
-{{/if}}
-
-
-
Gravity:
- {{:helper.link(data.gravity ? 'On ' : 'Off', data.gravity ? 'check' : 'close', {'gravity' : 1}, null, data.gravity ? 'linkOn' : 'redButton')}}
-
\ No newline at end of file
diff --git a/nano/templates/jukebox.tmpl b/nano/templates/jukebox.tmpl
deleted file mode 100644
index 77edb9ed419..00000000000
--- a/nano/templates/jukebox.tmpl
+++ /dev/null
@@ -1,49 +0,0 @@
-
-
-
-
-
Currently Playing:
- {{if data.playing && data.current_track }}
-
{{:data.current_track.title}} by {{:data.current_track.artist ? data.current_track.artist : "Unknown"}}
- {{else}}
-
Stopped
- {{/if}}
-
-
-
- {{:helper.displayBar(data.percent, 0, 1, 'good')}}
-
-
- {{:helper.link('Play' , 'play', {'play' : 1}, data.playing == 1 ? 'disabled' : null, null)}}
- {{:helper.link('Stop' , 'stop', {'stop' : 1}, data.playing == 0 ? 'disabled' : null, null)}}
-
-
-
-
{{:helper.link('Volume', 'volume-on', {'volume' : 1})}}
-
- {{:helper.displayBar(data.volume, 0, 1, (data.volume < .25) ? 'bad' : (data.volume < .75) ? 'average' : 'good')}}
-
-
-
-
-
-
Loop Mode:
-
- {{:helper.link('Next', 'triangle-1-e', {'loopmode' : 1}, data.loop_mode == 1 ? 'disabled' : null, null)}}
- {{:helper.link('Random', 'shuffle', {'loopmode' : 2}, data.loop_mode == 2 ? 'disabled' : null, null)}}
- {{:helper.link('Repeat', 'arrowrefresh-1-w', {'loopmode' : 3}, data.loop_mode == 3 ? 'disabled' : null, null)}}
- {{:helper.link('Once', 'arrowstop-1-e', {'loopmode' : 4}, data.loop_mode == 4 ? 'disabled' : null, null)}}
-
-
-
-Available tracks:
-
- {{for data.tracks}}
-
- {{:helper.link(value.title, 'gear', {'change_track' : value.ref}, data.current_track_ref == value.ref ? 'disabled' : null, null)}}
-
- {{/for}}
-
diff --git a/nano/templates/lookingglass.tmpl b/nano/templates/lookingglass.tmpl
deleted file mode 100644
index 8f117e6f66f..00000000000
--- a/nano/templates/lookingglass.tmpl
+++ /dev/null
@@ -1,14 +0,0 @@
-Current Loaded Programs:
-{{for data.supportedPrograms}}
- {{:helper.link(value, data.currentProgram == value ? 'check' : 'close', {'program' : value}, null, data.currentProgram == value ? 'linkOn' : null)}}
-{{/for}}
-
-
-
Area Gravity:
- {{:helper.link(data.gravity ? 'On ' : 'Off', data.gravity ? 'check' : 'close', {'gravity' : 1}, null, data.gravity ? 'linkOn' : 'redButton')}}
-
-
-
-
Full Immersion:
- {{:helper.link(data.immersion ? 'On ' : 'Off', data.immersion ? 'check' : 'close', {'immersion' : 1}, null, data.immersion ? 'linkOn' : 'redButton')}}
-
\ No newline at end of file
diff --git a/nano/templates/timeclock_vr.tmpl b/nano/templates/timeclock_vr.tmpl
deleted file mode 100644
index cc070c771d6..00000000000
--- a/nano/templates/timeclock_vr.tmpl
+++ /dev/null
@@ -1,83 +0,0 @@
-
-
-
-
-
OOC Note: PTO acquired is account-wide and shared across all characters. Info listed below is not IC information.
-
Time Off Balance for {{:config.user.name}}
- {{props data.department_hours}}
-
-
{{:key}}
-
{{:helper.fixed(value)}} {{:helper.fixed(value) == 1.0 ? 'hour' : 'hours'}}
-
- {{empty}}
-
No Hours Accrued
- {{/props}}
-
-
-Employment Information
-
-
-
Employee ID:
- {{:helper.link(data.card ? data.card : 'Insert ID', 'person', {'id' : 1})}}
-
- {{if data.job_datum }}
-
-
Rank:
-
- {{:data.job_datum.title}}
-
-
-
-
Departments:
-
{{:data.job_datum.departments}}
-
-
-
Pay Scale:
-
{{:data.job_datum.economic_modifier}}
-
-
-
PTO Eligibility:
- {{if data.job_datum.timeoff_factor > 0 }}
-
- Earns PTO - {{:data.job_datum.pto_department}}
-
- {{else data.job_datum.timeoff_factor < 0}}
-
- Requires PTO - {{:data.job_datum.pto_department}}
-
- {{else}}
-
- Neutral
-
- {{/if}}
-
- {{/if}}
-
-
-{{if data.allow_change_job && data.job_datum && data.job_datum.timeoff_factor != 0 && !(data.assignment == "Dismissed")}}
-Employment Actions
-
-
- {{if (data.job_datum.timeoff_factor > 0) }}
- {{if helper.round(data.department_hours[data.job_datum.pto_department]) > 0}}
- {{:helper.link('Go Off-Duty', 'alert', {'switch-to-offduty': 1})}}
- {{else}}
-
-
Insufficent Time Off Accrued
- {{/if}}
- {{else (data.job_datum.timeoff_factor < 0) }}
- {{props data.job_choices :alt_titles:job }}
- {{props alt_titles :alt_title:alt_title_index }}
-
{{:alt_title}}
-
{{:helper.link("Go On-Duty", 'suitcase', {'switch-to-onduty-rank' : job,'switch-to-onduty-assignment' : alt_title})}}
- {{/props}}
- {{empty}}
-
No Open Positions - See Head of Personnel
- {{/props}}
- {{/if}}
-
-
-{{/if}}
diff --git a/tgui/packages/tgui/interfaces/ColorMate.js b/tgui/packages/tgui/interfaces/ColorMate.js
new file mode 100644
index 00000000000..546438278fb
--- /dev/null
+++ b/tgui/packages/tgui/interfaces/ColorMate.js
@@ -0,0 +1,66 @@
+import { Fragment } from 'inferno';
+import { useBackend } from "../backend";
+import { Box, Button, Flex, Icon, LabeledList, ProgressBar, Section } from "../components";
+import { Window } from "../layouts";
+
+export const ColorMate = (props, context) => {
+ const { act, data } = useBackend(context);
+
+ const {
+ items,
+ activecolor,
+ } = data;
+
+ let height = Math.min(270 + (items.length * 15), 600);
+
+ return (
+
+
+ {items.length && (
+
+
+
+
+
+
+
+ act("select")}>
+ Select Color
+
+ act("paint")}>
+ Paint Items
+
+ act("clear")}>
+ Remove Paintjob
+
+ act("eject")}>
+ Eject Items
+
+
+
+
+
+ {items.map((item, i) => #{i+1}: {item} )}
+
+
+ ) || (
+
+ )}
+
+
+ );
+};
\ No newline at end of file
diff --git a/tgui/packages/tgui/interfaces/CookingAppliance.js b/tgui/packages/tgui/interfaces/CookingAppliance.js
new file mode 100644
index 00000000000..44e68414857
--- /dev/null
+++ b/tgui/packages/tgui/interfaces/CookingAppliance.js
@@ -0,0 +1,78 @@
+import { round } from 'common/math';
+import { Fragment } from 'inferno';
+import { useBackend } from "../backend";
+import { Box, Button, Flex, Icon, LabeledList, ProgressBar, Section, AnimatedNumber } from "../components";
+import { Window } from "../layouts";
+
+export const CookingAppliance = (props, context) => {
+ const { act, data } = useBackend(context);
+
+ const {
+ temperature,
+ optimalTemp,
+ temperatureEnough,
+ efficiency,
+ containersRemovable,
+ our_contents,
+ } = data;
+
+ return (
+
+
+
+
+
+
+ °C / {optimalTemp}°C
+
+
+
+ %
+
+
+
+
+
+ {our_contents.map((content, i) => {
+ if (content.empty) {
+ return (
+
+ act("slot", { slot: i + 1 })}>
+ Empty
+
+
+ );
+ }
+
+ return (
+
+
+
+ act("slot", { slot: i + 1 })}>
+ {content.container || "No Container"}
+
+
+
+
+ {content.progressText[1]}
+
+
+
+
+ );
+ })}
+
+
+
+
+ );
+};
\ No newline at end of file
diff --git a/tgui/packages/tgui/interfaces/CryoStorage.js b/tgui/packages/tgui/interfaces/CryoStorage.js
new file mode 100644
index 00000000000..ee20e63323b
--- /dev/null
+++ b/tgui/packages/tgui/interfaces/CryoStorage.js
@@ -0,0 +1,87 @@
+import { useBackend, useLocalState } from "../backend";
+import { Box, Button, Section, Tabs, NoticeBox } from "../components";
+import { Window } from "../layouts";
+
+export const CryoStorage = (props, context) => {
+ const { act, data } = useBackend(context);
+
+ const {
+ real_name,
+ allow_items,
+ } = data;
+
+ const [tab, setTab] = useLocalState(context, "tab", 0);
+
+ return (
+
+
+
+ setTab(0)}>
+ Crew
+
+ {!!allow_items && (
+ setTab(1)}>
+ Items
+
+ )}
+
+ Welcome, {real_name}.
+ {tab === 0 && }
+ {!!allow_items && tab === 1 && }
+
+
+ );
+};
+
+export const CryoStorageCrew = (props, context) => {
+ const { act, data } = useBackend(context);
+
+ const {
+ crew,
+ } = data;
+
+ return (
+
+ {crew.length && crew.map(c => {c} ) || (
+
+ No crew currently stored.
+
+ )}
+
+ );
+};
+
+export const CryoStorageItems = (props, context) => {
+ const { act, data } = useBackend(context);
+
+ const {
+ items,
+ } = data;
+
+ return (
+ act("allitems")}>
+ Claim All
+
+ }>
+ {items.length && items.map(item => (
+ act("item", { ref: item.ref })}>
+ {item.name}
+
+ )) || (
+
+ No items stored.
+
+ )}
+
+ );
+};
\ No newline at end of file
diff --git a/tgui/packages/tgui/interfaces/CryoStorageVr.js b/tgui/packages/tgui/interfaces/CryoStorageVr.js
new file mode 100644
index 00000000000..0fa51aca5f8
--- /dev/null
+++ b/tgui/packages/tgui/interfaces/CryoStorageVr.js
@@ -0,0 +1,57 @@
+import { useBackend, useLocalState } from "../backend";
+import { Box, Button, Section, Tabs, NoticeBox } from "../components";
+import { Window } from "../layouts";
+import { CryoStorageCrew } from "./CryoStorage";
+
+export const CryoStorageVr = (props, context) => {
+ const { act, data } = useBackend(context);
+
+ const {
+ real_name,
+ allow_items,
+ } = data;
+
+ const [tab, setTab] = useLocalState(context, "tab", 0);
+
+ return (
+
+
+
+ setTab(0)}>
+ Crew
+
+ {!!allow_items && (
+ setTab(1)}>
+ Items
+
+ )}
+
+ Welcome, {real_name}.
+ {tab === 0 && }
+ {!!allow_items && tab === 1 && }
+
+
+ );
+};
+
+export const CryoStorageItemsVr = (props, context) => {
+ const { act, data } = useBackend(context);
+
+ const {
+ items,
+ } = data;
+
+ return (
+
+ {items.length && items.map(item => {item} ) || (
+
+ No items stored.
+
+ )}
+
+ );
+};
\ No newline at end of file
diff --git a/tgui/packages/tgui/interfaces/Holodeck.js b/tgui/packages/tgui/interfaces/Holodeck.js
new file mode 100644
index 00000000000..2e54d6e9a9a
--- /dev/null
+++ b/tgui/packages/tgui/interfaces/Holodeck.js
@@ -0,0 +1,72 @@
+import { round } from 'common/math';
+import { Fragment } from 'inferno';
+import { useBackend } from "../backend";
+import { Box, Button, Flex, Icon, LabeledList, ProgressBar, Section } from "../components";
+import { Window } from "../layouts";
+
+export const Holodeck = (props, context) => {
+ const { act, data } = useBackend(context);
+
+ const {
+ supportedPrograms,
+ restrictedPrograms,
+ currentProgram,
+ isSilicon,
+ safetyDisabled,
+ emagged,
+ gravity,
+ } = data;
+
+ let programsToShow = supportedPrograms;
+
+ if (safetyDisabled) {
+ programsToShow = programsToShow.concat(restrictedPrograms);
+ }
+
+ return (
+
+
+
+ {programsToShow.map(prog => (
+ act("program", { program: prog })} />
+ ))}
+
+ {!!isSilicon && (
+
+ act("AIoverride")}>
+ {!!emagged && "Error, unable to control. "}
+ {safetyDisabled ? "Enable Safeties" : "Disable Safeties"}
+
+
+ )}
+
+
+
+ {safetyDisabled ? DISABLED : ENABLED }
+
+
+ act("gravity")}>
+ {gravity ? "Enabled" : "Disabled"}
+
+
+
+
+
+
+ );
+};
\ No newline at end of file
diff --git a/tgui/packages/tgui/interfaces/IDCard.js b/tgui/packages/tgui/interfaces/IDCard.js
index 9883fbc88dc..fc28fb08a4a 100644
--- a/tgui/packages/tgui/interfaces/IDCard.js
+++ b/tgui/packages/tgui/interfaces/IDCard.js
@@ -2,122 +2,7 @@ import { Box, Flex, LabeledList, Section, Icon } from "../components";
import { useBackend } from "../backend";
import { Window } from "../layouts";
import { Fragment } from "inferno";
-
-const rank2icon = {
- // Command
- 'Colony Director': 'user-tie',
- 'Site Manager': 'user-tie',
- 'Overseer': 'user-tie',
- 'Head of Personnel': 'briefcase',
- 'Crew Resources Officer': 'briefcase',
- 'Deputy Director': 'briefcase',
- 'Command Secretary': 'user-tie',
- // Security
- 'Head of Security': 'user-shield',
- 'Security Commander': 'user-shield',
- 'Chief of Security': 'user-shield',
- 'Warden': ['city', 'shield-alt'],
- 'Detective': 'search',
- 'Forensic Technician': 'search',
- 'Security Officer': 'user-shield',
- 'Junior Officer': 'user-shield',
- // Engineering
- 'Chief Engineer': 'toolbox',
- 'Atmospheric Technician': 'wind',
- 'Station Engineer': 'toolbox',
- 'Maintenance Technician': 'wrench',
- 'Engine Technician': 'toolbox',
- 'Electrician': 'toolbox',
- // Medical
- 'Chief Medical Officer': 'user-md',
- 'Chemist': 'mortar-pestle',
- 'Pharmacist': 'mortar-pestle',
- 'Medical Doctor': 'user-md',
- 'Surgeon': 'user-md',
- 'Emergency Physician': 'user-md',
- 'Nurse': 'user-md',
- 'Virologist': 'disease',
- 'Paramedic': 'ambulance',
- 'Emergency Medical Technician': 'ambulance',
- 'Psychiatrist': 'couch',
- 'Psychologist': 'couch',
- // Science
- 'Research Director': 'user-graduate',
- 'Research Supervisor': 'user-graduate',
- 'Roboticist': 'robot',
- 'Biomechanical Engineer': ['wrench', 'heartbeat'],
- 'Mechatronic Engineer': 'wrench',
- 'Scientist': 'flask',
- 'Xenoarchaeologist': 'flask',
- 'Anomalist': 'flask',
- 'Phoron Researcher': 'flask',
- 'Circuit Designer': 'car-battery',
- 'Xenobiologist': 'meteor',
- 'Xenobotanist': ['biohazard', 'seedling'],
- // Cargo
- 'Quartermaster': 'box-open',
- 'Supply Chief': 'warehouse',
- 'Cargo Technician': 'box-open',
- 'Shaft Miner': 'hard-hat',
- 'Drill Technician': 'hard-hat',
- // Exploration
- 'Pathfinder': 'binoculars',
- 'Explorer': 'user-astronaut',
- 'Field Medic': ['user-md', 'user-astronaut'],
- 'Pilot': 'space-shuttle',
- // Civvies
- 'Bartender': 'glass-martini',
- 'Barista': 'coffee',
- 'Botanist': 'leaf',
- 'Gardener': 'leaf',
- 'Chaplain': 'place-of-worship',
- 'Counselor': 'couch',
- 'Chef': 'utensils',
- 'Cook': 'utensils',
- 'Entertainer': 'smile-beam',
- 'Performer': 'smile-beam',
- 'Musician': 'guitar',
- 'Stagehand': 'smile-beam',
- // All of the interns
- 'Intern': 'school',
- 'Apprentice Engineer': ['school', 'wrench'],
- 'Medical Intern': ['school', 'user-md'],
- 'Lab Assistant': ['school', 'flask'],
- 'Security Cadet': ['school', 'shield-alt'],
- 'Jr. Cargo Tech': ['school', 'box'],
- 'Jr. Explorer': ['school', 'user-astronaut'],
- 'Server': ['school', 'utensils'],
- // Back to civvies
- 'Internal Affairs Agent': 'balance-scale',
- 'Janitor': 'broom',
- 'Custodian': 'broom',
- 'Sanitation Technician': 'hand-sparkles',
- 'Maid': 'broom',
- 'Librarian': 'book',
- 'Journalist': 'newspaper',
- 'Writer': 'book',
- 'Historian': 'chalkboard-teacher',
- 'Professor': 'chalkboard-teacher',
- 'Visitor': 'user',
- // Special roles
- 'Emergency Responder': 'fighter-jet',
-};
-const RankIcon = (props, context) => {
- const {
- rank,
- } = props;
-
- let rankObj = rank2icon[rank];
- if (typeof rankObj === "string") {
- return ;
- } else if (Array.isArray(rankObj)) {
- return rankObj.map(icon => (
-
- ));
- } else {
- return ;
- }
-};
+import { RankIcon } from "./common/RankIcon";
export const IDCard = (props, context) => {
const { data } = useBackend(context);
diff --git a/tgui/packages/tgui/interfaces/Jukebox.js b/tgui/packages/tgui/interfaces/Jukebox.js
new file mode 100644
index 00000000000..b9eac0520b1
--- /dev/null
+++ b/tgui/packages/tgui/interfaces/Jukebox.js
@@ -0,0 +1,117 @@
+import { round } from 'common/math';
+import { sortBy } from 'common/collections';
+import { useBackend } from "../backend";
+import { Box, Button, LabeledList, ProgressBar, Section, Slider } from "../components";
+import { Window } from "../layouts";
+
+export const Jukebox = (props, context) => {
+ const { act, data } = useBackend(context);
+
+ const {
+ playing,
+ loop_mode,
+ volume,
+ current_track_ref,
+ current_track,
+ percent,
+ tracks,
+ } = data;
+
+ return (
+
+
+
+
+
+ {playing && current_track && (
+
+ {current_track.title} by {current_track.artist || "Unkown"}
+
+ ) || (
+
+ Stopped
+
+ )}
+
+
+ act("play")}>
+ Play
+
+ act("stop")}>
+ Stop
+
+
+
+ act("loopmode", { loopmode: 1 })}
+ selected={loop_mode === 1}>
+ Next
+
+ act("loopmode", { loopmode: 2 })}
+ selected={loop_mode === 2}>
+ Shuffle
+
+ act("loopmode", { loopmode: 3 })}
+ selected={loop_mode === 3}>
+ Repeat
+
+ act("loopmode", { loopmode: 4 })}
+ selected={loop_mode === 4}>
+ Once
+
+
+
+
+
+
+ round(val * 100, 1) + "%"}
+ onChange={(e, val) => act("volume", { val: round(val, 2) })} />
+
+
+
+
+ {tracks.length && sortBy(a => a.title)(tracks).map(track => (
+ act("change_track", { change_track: track.ref })}>
+ {track.title}
+
+ )) || (
+
+ Error: No songs loaded.
+
+ )}
+
+
+
+ );
+};
\ No newline at end of file
diff --git a/tgui/packages/tgui/interfaces/LookingGlass.js b/tgui/packages/tgui/interfaces/LookingGlass.js
new file mode 100644
index 00000000000..f7a15789500
--- /dev/null
+++ b/tgui/packages/tgui/interfaces/LookingGlass.js
@@ -0,0 +1,60 @@
+import { round } from 'common/math';
+import { Fragment } from 'inferno';
+import { useBackend } from "../backend";
+import { Box, Button, Flex, Icon, LabeledList, ProgressBar, Section } from "../components";
+import { Window } from "../layouts";
+
+export const LookingGlass = (props, context) => {
+ const { act, data } = useBackend(context);
+
+ const {
+ supportedPrograms,
+ currentProgram,
+ immersion,
+ gravity,
+ } = data;
+
+ let height = Math.min(180 + (supportedPrograms.length * 23), 600);
+
+ return (
+
+
+
+ {supportedPrograms.map(program => (
+ act("program", { program: program })}>
+ {program}
+
+ ))}
+
+
+
+
+ act("gravity")}>
+ {gravity ? "Enabled" : "Disabled"}
+
+
+
+ act("immersion")}>
+ {immersion ? "Enabled" : "Disabled"}
+
+
+
+
+
+
+ );
+};
\ No newline at end of file
diff --git a/tgui/packages/tgui/interfaces/Microwave.js b/tgui/packages/tgui/interfaces/Microwave.js
new file mode 100644
index 00000000000..5f789ccf142
--- /dev/null
+++ b/tgui/packages/tgui/interfaces/Microwave.js
@@ -0,0 +1,73 @@
+import { round } from 'common/math';
+import { Fragment } from 'inferno';
+import { useBackend } from "../backend";
+import { Box, Button, Flex, Icon, LabeledList, ProgressBar, Section } from "../components";
+import { Window } from "../layouts";
+
+export const Microwave = (props, context) => {
+ const { act, config, data } = useBackend(context);
+
+ const {
+ broken,
+ operating,
+ dirty,
+ items,
+ } = data;
+
+ return (
+
+
+ {broken && (
+
+ ) || operating && (
+
+
+ Microwaving in progress!
+ Please wait...!
+
+
+ ) || dirty && (
+
+
+ This microwave is dirty!
+ Please clean it before use!
+
+
+ ) || items.length && (
+
+ act("cook")}>
+ Microwave
+
+ act("dispose")}>
+ Eject
+
+
+ }>
+
+ {items.map(item => (
+
+ {item.amt} {item.extra}
+
+ ))}
+
+
+ ) || (
+
+
+ {config.title} is empty.
+
+
+ )}
+
+
+ );
+};
\ No newline at end of file
diff --git a/tgui/packages/tgui/interfaces/Newscaster.js b/tgui/packages/tgui/interfaces/Newscaster.js
new file mode 100644
index 00000000000..3caf6a80d13
--- /dev/null
+++ b/tgui/packages/tgui/interfaces/Newscaster.js
@@ -0,0 +1,570 @@
+import { decodeHtmlEntities } from 'common/string';
+import { Fragment } from 'inferno';
+import { useBackend, useSharedState } from "../backend";
+import { Box, Button, Flex, Icon, LabeledList, Input, ProgressBar, Section, NoticeBox } from "../components";
+import { Window } from "../layouts";
+import { TemporaryNotice } from './common/TemporaryNotice';
+
+const NEWSCASTER_SCREEN_MAIN = "Main Menu";
+const NEWSCASTER_SCREEN_NEWCHANNEL = "New Channel";
+const NEWSCASTER_SCREEN_VIEWLIST = "View List";
+const NEWSCASTER_SCREEN_NEWSTORY = "New Story";
+const NEWSCASTER_SCREEN_PRINT = "Print";
+const NEWSCASTER_SCREEN_NEWWANTED = "New Wanted";
+const NEWSCASTER_SCREEN_VIEWWANTED = "View Wanted";
+const NEWSCASTER_SCREEN_SELECTEDCHANNEL = "View Selected Channel";
+
+export const Newscaster = (props, context) => {
+ const { act, data } = useBackend(context);
+
+ const {
+ screen,
+ user,
+ } = data;
+
+ return (
+
+
+
+
+
+
+ );
+};
+
+const NewscasterContent = (props, context) => {
+ const { act, data } = useBackend(context);
+
+ const {
+ user,
+ } = data;
+
+ const [screen, setScreen] = useSharedState(context, "screen", NEWSCASTER_SCREEN_MAIN);
+ let Template = screenToTemplate[screen];
+
+ return (
+
+
+
+ );
+};
+
+const NewscasterMainMenu = (props, context) => {
+ const { act, data } = useBackend(context);
+
+ const {
+ securityCaster,
+ wanted_issue,
+ } = data;
+
+ const {
+ setScreen,
+ } = props;
+
+ return (
+
+
+ {wanted_issue && (
+ setScreen(NEWSCASTER_SCREEN_VIEWWANTED)} color="bad">
+ Read WANTED Issue
+
+ )}
+ setScreen(NEWSCASTER_SCREEN_VIEWLIST)}>
+ View Feed Channels
+
+ setScreen(NEWSCASTER_SCREEN_NEWCHANNEL)}>
+ Create Feed Channel
+
+ setScreen(NEWSCASTER_SCREEN_NEWSTORY)}>
+ Create Feed Message
+
+ setScreen(NEWSCASTER_SCREEN_PRINT)}>
+ Print Newspaper
+
+
+ {!!securityCaster && (
+
+ setScreen(NEWSCASTER_SCREEN_NEWWANTED)}>
+ Manage "Wanted" Issue
+
+
+ )}
+
+ );
+};
+
+const NewscasterNewChannel = (props, context) => {
+ const { act, data } = useBackend(context);
+
+ const {
+ channel_name,
+ c_locked,
+ user,
+ } = data;
+
+ const {
+ setScreen,
+ } = props;
+
+ return (
+
+ );
+};
+
+const NewscasterViewList = (props, context) => {
+ const { act, data } = useBackend(context);
+
+ const {
+ channels,
+ } = data;
+
+ const {
+ setScreen,
+ } = props;
+
+ return (
+ setScreen(NEWSCASTER_SCREEN_MAIN)}>
+ Back
+
+ }>
+ {channels.map(channel => (
+ {
+ act("show_channel", { show_channel: channel.ref });
+ setScreen(NEWSCASTER_SCREEN_SELECTEDCHANNEL);
+ }}>
+ {decodeHtmlEntities(channel.name)}
+
+ ))}
+
+ );
+};
+
+const NewscasterNewStory = (props, context) => {
+ const { act, data } = useBackend(context);
+
+ const {
+ channel_name,
+ user,
+ msg,
+ photo_data,
+ } = data;
+
+ const {
+ setScreen,
+ } = props;
+
+ return (
+ setScreen(NEWSCASTER_SCREEN_MAIN)}>
+ Back
+
+ }>
+
+
+ act("set_channel_receiving")}>
+ {channel_name || "Unset"}
+
+
+
+ {user}
+
+
+
+
+
+ {msg || "(no message yet)"}
+
+
+
+ act("set_new_message")}
+ icon="pen"
+ tooltip="Edit Message"
+ tooltipPosition="left" />
+
+
+
+
+ act("set_attachment")}>
+ {photo_data ? "Photo Attached" : "No Photo"}
+
+
+
+ act("submit_new_message")}>
+ Submit Message
+
+ setScreen(NEWSCASTER_SCREEN_MAIN)}>
+ Cancel
+
+
+ );
+};
+
+const NewscasterPrint = (props, context) => {
+ const { act, data } = useBackend(context);
+
+ const {
+ total_num,
+ active_num,
+ message_num,
+ paper_remaining,
+ } = data;
+
+ const {
+ setScreen,
+ } = props;
+
+ return (
+ setScreen(NEWSCASTER_SCREEN_MAIN)}>
+ Back
+
+ }>
+
+ Newscaster currently serves a total of {total_num} Feed channels, {active_num} of which are active,
+ and a total of {message_num} Feed stories.
+
+
+
+ {paper_remaining * 100} cm³
+
+
+ act("print_paper")}>
+ Print Paper
+
+ setScreen(NEWSCASTER_SCREEN_MAIN)}>
+ Cancel
+
+
+ );
+};
+
+const NewscasterNewWanted = (props, context) => {
+ const { act, data } = useBackend(context);
+
+ const {
+ channel_name,
+ msg,
+ photo_data,
+ user,
+ wanted_issue,
+ } = data;
+
+ const {
+ setScreen,
+ } = props;
+
+ return (
+
+ );
+};
+
+const NewscasterViewWanted = (props, context) => {
+ const { act, data } = useBackend(context);
+
+ const {
+ wanted_issue,
+ } = data;
+
+ const {
+ setScreen,
+ } = props;
+
+ if (!wanted_issue) {
+ return (
+ setScreen(NEWSCASTER_SCREEN_MAIN)}>
+ Back
+
+ }>
+ There are no wanted issues currently outstanding.
+
+ );
+ }
+
+ return (
+ setScreen(NEWSCASTER_SCREEN_MAIN)}>
+ Back
+
+ }>
+
+
+
+ {decodeHtmlEntities(wanted_issue.author)}
+
+
+
+ {decodeHtmlEntities(wanted_issue.criminal)}
+
+
+ {decodeHtmlEntities(wanted_issue.desc)}
+
+
+ {wanted_issue.img && || "None"}
+
+
+
+
+ );
+};
+
+const NewscasterViewSelected = (props, context) => {
+ const { act, data } = useBackend(context);
+
+ const {
+ viewing_channel,
+ securityCaster,
+ company,
+ } = data;
+
+ const {
+ setScreen,
+ } = props;
+
+ if (!viewing_channel) {
+ return (
+ setScreen(NEWSCASTER_SCREEN_VIEWLIST)}>
+ Back
+
+ }>
+ The channel you were looking for no longer exists.
+
+ );
+ }
+
+ return (
+
+ {!!securityCaster && (
+ act("toggle_d_notice", { ref: viewing_channel.ref })} />
+ )}
+ setScreen(NEWSCASTER_SCREEN_VIEWLIST)}>
+ Back
+
+
+ }>
+
+
+ {securityCaster && (
+ act("censor_channel_author", { ref: viewing_channel.ref })} />
+ ) || (
+
+ {decodeHtmlEntities(viewing_channel.author)}
+
+ )}
+
+
+ {!!viewing_channel.censored && (
+
+ ATTENTION: This channel has been deemed as threatening to the welfare of the station,
+ and marked with a {company} D-Notice. No further feed story additions are allowed
+ while the D-Notice is in effect.
+
+ )}
+ {!!viewing_channel.messages.length && viewing_channel.messages.map(message => (
+
+ - {decodeHtmlEntities(message.body)}
+ {!!message.img && (
+
+
+ {decodeHtmlEntities(message.caption) || null}
+
+ )}
+
+ [Story by {decodeHtmlEntities(message.author)} - {message.timestamp}]
+
+ {!!securityCaster && (
+
+ act("censor_channel_story_body", { ref: message.ref })} />
+ act("censor_channel_story_author", { ref: message.ref })} />
+
+ )}
+
+ )) || !viewing_channel.censored && (
+
+ No feed messages found in channel.
+
+ )}
+
+ );
+};
+
+/* Must be at the bottom because of how the const lifting rules work */
+let screenToTemplate = {};
+screenToTemplate[NEWSCASTER_SCREEN_MAIN] = NewscasterMainMenu;
+screenToTemplate[NEWSCASTER_SCREEN_NEWCHANNEL] = NewscasterNewChannel;
+screenToTemplate[NEWSCASTER_SCREEN_VIEWLIST] = NewscasterViewList;
+screenToTemplate[NEWSCASTER_SCREEN_NEWSTORY] = NewscasterNewStory;
+screenToTemplate[NEWSCASTER_SCREEN_PRINT] = NewscasterPrint;
+screenToTemplate[NEWSCASTER_SCREEN_NEWWANTED] = NewscasterNewWanted;
+screenToTemplate[NEWSCASTER_SCREEN_VIEWWANTED] = NewscasterViewWanted;
+screenToTemplate[NEWSCASTER_SCREEN_SELECTEDCHANNEL] = NewscasterViewSelected;
\ No newline at end of file
diff --git a/tgui/packages/tgui/interfaces/TimeClock.js b/tgui/packages/tgui/interfaces/TimeClock.js
new file mode 100644
index 00000000000..d97f162d55e
--- /dev/null
+++ b/tgui/packages/tgui/interfaces/TimeClock.js
@@ -0,0 +1,144 @@
+import { toFixed } from 'common/math';
+import { Fragment } from 'inferno';
+import { useBackend } from "../backend";
+import { Box, Button, Flex, Icon, LabeledList, ProgressBar, Section, NoticeBox } from "../components";
+import { Window } from "../layouts";
+import { RankIcon } from "./common/RankIcon";
+
+export const TimeClock = (props, context) => {
+ const { act, data } = useBackend(context);
+
+ const {
+ department_hours,
+ user_name,
+ card,
+ assignment,
+ job_datum,
+ allow_change_job,
+ job_choices,
+ } = data;
+
+ return (
+
+
+
+
+ OOC Note: PTO acquired is account-wide and shared across all characters.
+ Info listed below is not IC information.
+
+
+
+ {Object.keys(department_hours).map(key => (
+ 6
+ ? "good"
+ : department_hours[key] > 1
+ ? "average"
+ : "bad"
+ }>
+ {toFixed(department_hours[key], 1)} {department_hours[key] === 1 ? "hour" : "hours"}
+
+ ))}
+
+
+
+
+
+
+ act("id")}>
+ {card || "Insert ID"}
+
+
+ {!!job_datum && (
+
+
+
+
+
+
+
+
+
+
+
+ {job_datum.title}
+
+
+
+
+
+
+ {job_datum.departments}
+
+
+ {job_datum.economic_modifier}
+
+
+ {job_datum.timeoff_factor > 0 && (
+
+ Earns PTO - {job_datum.pto_department}
+
+ ) || job_datum.timeoff_factor < 0 && (
+
+ Requires PTO - {job_datum.pto_department}
+
+ ) || (
+
+ Neutral
+
+ )}
+
+
+ )}
+
+
+ {!!(allow_change_job && job_datum && job_datum.timeoff_factor !== 0 && assignment !== "Dismissed") && (
+
+ {job_datum.timeoff_factor > 0 && (
+ department_hours[job_datum.pto_department] > 0 && (
+ act("switch-to-offduty")}>
+ Go Off-Duty
+
+ ) || (
+
+ Warning: You do not have enough accrued time off to go off-duty.
+
+ )
+ ) || (
+ Object.keys(job_choices).length && Object.keys(job_choices).map(job => {
+ let alt_titles = job_choices[job];
+
+ return alt_titles.map(title => (
+ act("switch-to-onduty-rank", {
+ "switch-to-onduty-rank": job,
+ "switch-to-onduty-assignment": title,
+ })}>
+ {title}
+
+ ));
+ }) || (
+
+ No Open Positions - See Head Of Personnel
+
+ )
+ )}
+
+ )}
+
+
+ );
+};
\ No newline at end of file
diff --git a/tgui/packages/tgui/interfaces/Vending.js b/tgui/packages/tgui/interfaces/Vending.js
index 3dc09d64072..6e60dd8c1e0 100644
--- a/tgui/packages/tgui/interfaces/Vending.js
+++ b/tgui/packages/tgui/interfaces/Vending.js
@@ -4,9 +4,6 @@ import { useBackend } from '../backend';
import { Box, Button, Section, Table, LabeledList } from '../components';
import { Window } from '../layouts';
-const VENDING_MODE_PRODUCTS = 0;
-const VENDING_MODE_PURCHASESCREEN = 1;
-
const VendingRow = (props, context) => {
const { act, data } = useBackend(context);
const {
@@ -15,11 +12,21 @@ const VendingRow = (props, context) => {
const {
product,
} = props;
- const free = (
- product.price === 0
- );
return (
+
+ {product.isatom && (
+
+ ) || null}
+
{product.name}
@@ -51,25 +58,16 @@ const VendingRow = (props, context) => {
export const Vending = (props, context) => {
const { act, data } = useBackend(context);
const {
- mode,
panel,
} = data;
- let body = null;
-
- if (mode === VENDING_MODE_PRODUCTS) {
- body = ;
- } else if (mode === VENDING_MODE_PURCHASESCREEN) {
- body = ;
- }
-
return (
- {body}
+
{panel ? (
) : null}
@@ -82,6 +80,10 @@ export const VendingProducts = (props, context) => {
const { act, data } = useBackend(context);
const {
coin,
+ chargesMoney,
+ user,
+ userMoney,
+ guestNotice,
products,
} = data;
@@ -89,6 +91,23 @@ export const VendingProducts = (props, context) => {
let myproducts = products.filter(item => !!item);
return (
+ {!!chargesMoney && (
+
+ {user && (
+
+ Welcome, {user.name} ,
+ {' '}
+ {user.job || 'Unemployed'} !
+
+ Your balance is {userMoney}₮ Thalers .
+
+ ) || (
+
+ {guestNotice}
+
+ )}
+
+ )}
{myproducts.map(product => (
@@ -112,37 +131,6 @@ export const VendingProducts = (props, context) => {
);
};
-export const VendingPurchaseScreen = (props, context) => {
- const { act, data } = useBackend(context);
- const {
- product,
- price,
- message,
- message_err,
- } = data;
-
- return (
- act('cancelpurchase')} />
- )}>
-
-
- {product}
-
-
- {price}₮
-
-
-
-
- );
-};
-
export const VendingMaintenance = (props, context) => {
const { act, data } = useBackend(context);
const {
diff --git a/tgui/packages/tgui/interfaces/common/RankIcon.js b/tgui/packages/tgui/interfaces/common/RankIcon.js
new file mode 100644
index 00000000000..bdb88742f9e
--- /dev/null
+++ b/tgui/packages/tgui/interfaces/common/RankIcon.js
@@ -0,0 +1,119 @@
+import { Icon } from "../../components";
+
+const rank2icon = {
+ // Command
+ 'Colony Director': 'user-tie',
+ 'Site Manager': 'user-tie',
+ 'Overseer': 'user-tie',
+ 'Head of Personnel': 'briefcase',
+ 'Crew Resources Officer': 'briefcase',
+ 'Deputy Director': 'briefcase',
+ 'Command Secretary': 'user-tie',
+ // Security
+ 'Head of Security': 'user-shield',
+ 'Security Commander': 'user-shield',
+ 'Chief of Security': 'user-shield',
+ 'Warden': ['city', 'shield-alt'],
+ 'Detective': 'search',
+ 'Forensic Technician': 'search',
+ 'Security Officer': 'user-shield',
+ 'Junior Officer': 'user-shield',
+ // Engineering
+ 'Chief Engineer': 'toolbox',
+ 'Atmospheric Technician': 'wind',
+ 'Station Engineer': 'toolbox',
+ 'Maintenance Technician': 'wrench',
+ 'Engine Technician': 'toolbox',
+ 'Electrician': 'toolbox',
+ // Medical
+ 'Chief Medical Officer': 'user-md',
+ 'Chemist': 'mortar-pestle',
+ 'Pharmacist': 'mortar-pestle',
+ 'Medical Doctor': 'user-md',
+ 'Surgeon': 'user-md',
+ 'Emergency Physician': 'user-md',
+ 'Nurse': 'user-md',
+ 'Virologist': 'disease',
+ 'Paramedic': 'ambulance',
+ 'Emergency Medical Technician': 'ambulance',
+ 'Psychiatrist': 'couch',
+ 'Psychologist': 'couch',
+ // Science
+ 'Research Director': 'user-graduate',
+ 'Research Supervisor': 'user-graduate',
+ 'Roboticist': 'robot',
+ 'Biomechanical Engineer': ['wrench', 'heartbeat'],
+ 'Mechatronic Engineer': 'wrench',
+ 'Scientist': 'flask',
+ 'Xenoarchaeologist': 'flask',
+ 'Anomalist': 'flask',
+ 'Phoron Researcher': 'flask',
+ 'Circuit Designer': 'car-battery',
+ 'Xenobiologist': 'meteor',
+ 'Xenobotanist': ['biohazard', 'seedling'],
+ // Cargo
+ 'Quartermaster': 'box-open',
+ 'Supply Chief': 'warehouse',
+ 'Cargo Technician': 'box-open',
+ 'Shaft Miner': 'hard-hat',
+ 'Drill Technician': 'hard-hat',
+ // Exploration
+ 'Pathfinder': 'binoculars',
+ 'Explorer': 'user-astronaut',
+ 'Field Medic': ['user-md', 'user-astronaut'],
+ 'Pilot': 'space-shuttle',
+ // Civvies
+ 'Bartender': 'glass-martini',
+ 'Barista': 'coffee',
+ 'Botanist': 'leaf',
+ 'Gardener': 'leaf',
+ 'Chaplain': 'place-of-worship',
+ 'Counselor': 'couch',
+ 'Chef': 'utensils',
+ 'Cook': 'utensils',
+ 'Entertainer': 'smile-beam',
+ 'Performer': 'smile-beam',
+ 'Musician': 'guitar',
+ 'Stagehand': 'smile-beam',
+ // All of the interns
+ 'Intern': 'school',
+ 'Apprentice Engineer': ['school', 'wrench'],
+ 'Medical Intern': ['school', 'user-md'],
+ 'Lab Assistant': ['school', 'flask'],
+ 'Security Cadet': ['school', 'shield-alt'],
+ 'Jr. Cargo Tech': ['school', 'box'],
+ 'Jr. Explorer': ['school', 'user-astronaut'],
+ 'Server': ['school', 'utensils'],
+ // Back to civvies
+ 'Internal Affairs Agent': 'balance-scale',
+ 'Janitor': 'broom',
+ 'Custodian': 'broom',
+ 'Sanitation Technician': 'hand-sparkles',
+ 'Maid': 'broom',
+ 'Librarian': 'book',
+ 'Journalist': 'newspaper',
+ 'Writer': 'book',
+ 'Historian': 'chalkboard-teacher',
+ 'Professor': 'chalkboard-teacher',
+ 'Visitor': 'user',
+ // Special roles
+ 'Emergency Responder': 'fighter-jet',
+};
+
+export const RankIcon = (props, context) => {
+ const {
+ rank,
+ color = 'label',
+ } = props;
+
+ let rankObj = rank2icon[rank];
+ if (typeof rankObj === "string") {
+ return ;
+ } else if (Array.isArray(rankObj)) {
+ return rankObj.map(icon => (
+
+ ));
+ } else {
+ return ;
+ }
+};
\ No newline at end of file
diff --git a/tgui/packages/tgui/interfaces/common/TemporaryNotice.js b/tgui/packages/tgui/interfaces/common/TemporaryNotice.js
index 498bd0a0397..e28d867a206 100644
--- a/tgui/packages/tgui/interfaces/common/TemporaryNotice.js
+++ b/tgui/packages/tgui/interfaces/common/TemporaryNotice.js
@@ -1,3 +1,4 @@
+import { decodeHtmlEntities } from 'common/string';
import { useBackend } from "../../backend";
import { Box, Button, NoticeBox } from "../../components";
@@ -15,6 +16,9 @@ import { Box, Button, NoticeBox } from "../../components";
* @param {object} context
*/
export const TemporaryNotice = (_properties, context) => {
+ const {
+ decode,
+ } = _properties;
const { act, data } = useBackend(context);
const { temp } = data;
if (!temp) {
@@ -24,7 +28,7 @@ export const TemporaryNotice = (_properties, context) => {
return (
- {temp.text}
+ {decode ? decodeHtmlEntities(temp.text) : temp.text}