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 && ( + +
+ + + + + + + + + + + +
+
+ {items.map((item, i) => #{i+1}: {item})} +
+
+ ) || ( +
+ No items inserted. +
+ )} +
+
+ ); +}; \ 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 ( + + + + ); + } + + return ( + + + + + + + + {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 => ( + + )) || ( + + 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 => ( +
+ {!!isSilicon && ( +
+ +
+ )} +
+ + + {safetyDisabled ? DISABLED : ENABLED} + + + + + +
+
+
+ ); +}; \ 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 + + )} + + + + + + + + + + + + + + + + round(val * 100, 1) + "%"} + onChange={(e, val) => act("volume", { val: round(val, 2) })} /> + + +
+
+ {tracks.length && sortBy(a => a.title)(tracks).map(track => ( + + )) || ( + + 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 => ( + + ))} +
+
+ + + + + + + + +
+
+
+ ); +}; \ 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 && ( +
+ + Bzzzzttttt!! + +
+ ) || operating && ( +
+ + Microwaving in progress!
+ Please wait...! +
+
+ ) || dirty && ( +
+ + This microwave is dirty!
+ Please clean it before use! +
+
+ ) || items.length && ( +
+ + + + }> + + {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 ( + +