TGUI Civilian

UI's Converted:
 - Aurora Cooking
 - Chaplain Book Selection
 - ColorMate
 - Cryo Storage
 - Holodeck
 - Jukebox
 - Looking Glass
 - Microwave
 - Newscasters
 - Timeclock
 - Vending Machine Improvements
This commit is contained in:
ShadowLarkens
2020-08-28 00:20:10 -07:00
parent 48db324343
commit c32f05e1f7
50 changed files with 2739 additions and 1580 deletions

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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, "<span class='boldnotice'>There is already an established religion onboard the station. You are an acolyte of [GLOB.deity]. Defer to the [title].</span>")
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)
*/

View File

@@ -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()

View File

@@ -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 += "<hr/><br/><b>[storage_name]</b><br/>"
dat += "<i>Welcome, [user.real_name].</i><br/><br/><hr/>"
dat += "<a href='?src=\ref[src];log=1'>View storage log</a>.<br>"
data["real_name"] = user.real_name
data["allow_items"] = allow_items
data["crew"] = frozen_crew
data["items"] = list()
if(allow_items)
dat += "<a href='?src=\ref[src];view=1'>View objects</a>.<br>"
//dat += "<a href='?src=\ref[src];item=1'>Recover object</a>.<br>" //VOREStation Removal - Just log them.
//dat += "<a href='?src=\ref[src];allitems=1'>Recover all objects</a>.<br>" //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 = "<b>Recently stored [storage_type]</b><br/><hr/><br/>"
for(var/person in frozen_crew)
dat += "[person]<br/>"
dat += "<hr/>"
if(!LAZYLEN(frozen_items))
to_chat(usr, "<span class='notice'>There is nothing to recover from storage.</span>")
return
user << browse(dat, "window=cryolog")
var/obj/item/I = locate(params["ref"]) in frozen_items
if(!I)
to_chat(usr, "<span class='notice'>\The [I] is no longer in storage.</span>")
return
if(href_list["view"])
if(!allow_items) return
visible_message("<span class='notice'>The console beeps happily as it disgorges [I].</span>")
var/dat = "<b>Recently stored objects</b><br/><hr/><br/>"
//VOREStation Edit Start
for(var/I in frozen_items)
dat += "[I]<br/>"
//VOREStation Edit End
dat += "<hr/>"
user << browse(dat, "window=cryoitems")
else if(href_list["item"])
if(!allow_items) return
if(frozen_items.len == 0)
to_chat(user, "<span class='notice'>There is nothing to recover from storage.</span>")
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, "<span class='notice'>\The [I] is no longer in storage.</span>")
return
visible_message("<span class='notice'>The console beeps happily as it disgorges \the [I].</span>", 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, "<span class='notice'>There is nothing to recover from storage.</span>")
return
visible_message("<span class='notice'>The console beeps happily as it disgorges the desired objects.</span>", 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, "<span class='notice'>There is nothing to recover from storage.</span>")
return
visible_message("<span class='notice'>The console beeps happily as it disgorges the desired objects.</span>")
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)"

View File

@@ -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, "<span class='warning'>You must secure \the [src] first.</span>")
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, "<span class='warning'>[src] doesn't appear to function.</span>")
return STATUS_CLOSE
if(!anchored)
to_chat(user, "<span class='warning'>You must secure [src] first.</span>")
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)

View File

@@ -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("<HEAD><TITLE>Newscaster</TITLE></HEAD><H3>Newscaster Unit #[unit_no]</H3>")
/**
* 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].<BR> Interface & News networks Operational."
dat += "<BR><FONT SIZE=1>Property of NanoTrasen Inc</FONT>"
if(news_network.wanted_issue)
dat+= "<HR><A href='?src=\ref[src];view_wanted=1'>Read Wanted Issue</A>"
dat+= "<HR><BR><A href='?src=\ref[src];create_channel=1'>Create Feed Channel</A>"
dat+= "<BR><A href='?src=\ref[src];view=1'>View Feed Channels</A>"
dat+= "<BR><A href='?src=\ref[src];create_feed_story=1'>Submit new Feed story</A>"
dat+= "<BR><A href='?src=\ref[src];menu_paper=1'>Print newspaper</A>"
dat+= "<BR><A href='?src=\ref[src];refresh=1'>Re-scan User</A>"
dat+= "<BR><BR><A href='?src=\ref[human_or_robot_user];mach_close=newscaster_main'>Exit</A>"
if(securityCaster)
var/wanted_already = 0
if(news_network.wanted_issue)
wanted_already = 1
// Main menu
data["temp"] = temp
dat+="<HR><B>Feed Security functions:</B><BR>"
dat+="<BR><A href='?src=\ref[src];menu_wanted=1'>[(wanted_already) ? ("Manage") : ("Publish")] \"Wanted\" Issue</A>"
dat+="<BR><A href='?src=\ref[src];menu_censor_story=1'>Censor Feed Stories</A>"
dat+="<BR><A href='?src=\ref[src];menu_censor_channel=1'>Mark Feed Channel with [using_map.company_name] D-Notice</A>"
dat+="<BR><HR>The newscaster recognises you as: <FONT COLOR='green'>[scanned_user]</FONT>"
if(1)
dat+= "Station Feed Channels<HR>"
if(isemptylist(news_network.network_channels))
dat+="<I>No active channels found...</I>"
else
for(var/datum/feed_channel/CHANNEL in news_network.network_channels)
if(CHANNEL.is_admin_channel)
dat+="<B><FONT style='BACKGROUND-COLOR: LightGreen '><A href='?src=\ref[src];show_channel=\ref[CHANNEL]'>[CHANNEL.channel_name]</A></FONT></B><BR>"
else
dat+="<B><A href='?src=\ref[src];show_channel=\ref[CHANNEL]'>[CHANNEL.channel_name]</A> [(CHANNEL.censored) ? ("<FONT COLOR='red'>***</FONT>") : null]<BR></B>"
dat+="<BR><HR><A href='?src=\ref[src];refresh=1'>Refresh</A>"
dat+="<BR><A href='?src=\ref[src];setScreen=[0]'>Back</A>"
if(2)
dat+="Creating new Feed Channel..."
dat+="<HR><B><A href='?src=\ref[src];set_channel_name=1'>Channel Name</A>:</B> [channel_name]<BR>"
dat+="<B>Channel Author:</B> <FONT COLOR='green'>[scanned_user]</FONT><BR>"
dat+="<B><A href='?src=\ref[src];set_channel_lock=1'>Will Accept Public Feeds</A>:</B> [(c_locked) ? ("NO") : ("YES")]<BR><BR>"
dat+="<BR><A href='?src=\ref[src];submit_new_channel=1'>Submit</A><BR><BR><A href='?src=\ref[src];setScreen=[0]'>Cancel</A><BR>"
if(3)
dat+="Creating new Feed Message..."
dat+="<HR><B><A href='?src=\ref[src];set_channel_receiving=1'>Receiving Channel</A>:</B> [channel_name]<BR>" //MARK
dat+="<B>Message Author:</B> <FONT COLOR='green'>[scanned_user]</FONT><BR>"
dat+="<B><A href='?src=\ref[src];set_new_message=1'>Message Body</A>:</B> [msg] <BR>"
dat+="<B><A href='?src=\ref[src];set_attachment=1'>Attach Photo</A>:</B> [(photo_data ? "Photo Attached" : "No Photo")]</BR>"
dat+="<BR><A href='?src=\ref[src];submit_new_message=1'>Submit</A><BR><BR><A href='?src=\ref[src];setScreen=[0]'>Cancel</A><BR>"
if(4)
dat+="Feed story successfully submitted to [channel_name].<BR><BR>"
dat+="<BR><A href='?src=\ref[src];setScreen=[0]'>Return</A><BR>"
if(5)
dat+="Feed Channel [channel_name] created successfully.<BR><BR>"
dat+="<BR><A href='?src=\ref[src];setScreen=[0]'>Return</A><BR>"
if(6)
dat+="<B><FONT COLOR='maroon'>ERROR: Could not submit Feed story to Network.</B></FONT><HR><BR>"
if(channel_name=="")
dat+="<FONT COLOR='maroon'>•Invalid receiving channel name.</FONT><BR>"
if(scanned_user=="Unknown")
dat+="<FONT COLOR='maroon'>•Channel author unverified.</FONT><BR>"
if(msg == "" || msg == "\[REDACTED\]")
dat+="<FONT COLOR='maroon'>•Invalid message body.</FONT><BR>"
data["user"] = tgui_user_name(user)
data["unit_no"] = unit_no
dat+="<BR><A href='?src=\ref[src];setScreen=[3]'>Return</A><BR>"
if(7)
dat+="<B><FONT COLOR='maroon'>ERROR: Could not submit Feed Channel to Network.</B></FONT><HR><BR>"
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+="<FONT COLOR='maroon'>•There already exists a Feed channel under your name.</FONT><BR>"
if(channel_name=="" || channel_name == "\[REDACTED\]")
dat+="<FONT COLOR='maroon'>•Invalid channel name.</FONT><BR>"
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+="<FONT COLOR='maroon'>•Channel name already in use.</FONT><BR>"
if(scanned_user=="Unknown")
dat+="<FONT COLOR='maroon'>•Channel author unverified.</FONT><BR>"
dat+="<BR><A href='?src=\ref[src];setScreen=[2]'>Return</A><BR>"
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+="<BR><BR><B>Liquid Paper remaining:</B> [(paper_remaining) *100 ] cm^3"
dat+="<BR><BR><A href='?src=\ref[src];print_paper=[0]'>Print Paper</A>"
dat+="<BR><A href='?src=\ref[src];setScreen=[0]'>Cancel</A>"
if(9)
dat+="<B>[viewing_channel.channel_name]: </B><FONT SIZE=1>\[created by: <FONT COLOR='maroon'>[viewing_channel.author]</FONT>\]</FONT><HR>"
if(viewing_channel.censored)
dat+="<FONT COLOR='red'><B>ATTENTION: </B></FONT>This channel has been deemed as threatening to the welfare of the station, and marked with a [using_map.company_name] D-Notice.<BR>"
dat+="No further feed story additions are allowed while the D-Notice is in effect.</FONT><BR><BR>"
else
if(isemptylist(viewing_channel.messages))
dat+="<I>No feed messages found in channel...</I><BR>"
else
var/i = 0
for(var/datum/feed_message/MESSAGE in viewing_channel.messages)
i++
dat+="-[MESSAGE.body] <BR>"
if(MESSAGE.img)
usr << browse_rsc(MESSAGE.img, "tmp_photo[i].png")
dat+="<img src='tmp_photo[i].png' width = '180'><BR>"
if(MESSAGE.caption)
dat+="<FONT SIZE=1><B>[MESSAGE.caption]</B></FONT><BR>"
dat+="<BR>"
dat+="<FONT SIZE=1>\[Story by <FONT COLOR='maroon'>[MESSAGE.author] - [MESSAGE.time_stamp]</FONT>\]</FONT><BR>"
dat+="<BR><HR><A href='?src=\ref[src];refresh=1'>Refresh</A>"
dat+="<BR><A href='?src=\ref[src];setScreen=[1]'>Back</A>"
if(10)
dat+="<B>[using_map.company_name] Feed Censorship Tool</B><BR>"
dat+="<FONT SIZE=1>NOTE: Due to the nature of news Feeds, total deletion of a Feed Story is not possible.<BR>"
dat+="Keep in mind that users attempting to view a censored feed will instead see the \[REDACTED\] tag above it.</FONT>"
dat+="<HR>Select Feed channel to get Stories from:<BR>"
if(isemptylist(news_network.network_channels))
dat+="<I>No feed channels found active...</I><BR>"
else
for(var/datum/feed_channel/CHANNEL in news_network.network_channels)
dat+="<A href='?src=\ref[src];pick_censor_channel=\ref[CHANNEL]'>[CHANNEL.channel_name]</A> [(CHANNEL.censored) ? ("<FONT COLOR='red'>***</FONT>") : null]<BR>"
dat+="<BR><A href='?src=\ref[src];setScreen=[0]'>Cancel</A>"
if(11)
dat+="<B>[using_map.company_name] D-Notice Handler</B><HR>"
dat+="<FONT SIZE=1>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.</FONT><HR>"
if(isemptylist(news_network.network_channels))
dat+="<I>No feed channels found active...</I><BR>"
else
for(var/datum/feed_channel/CHANNEL in news_network.network_channels)
dat+="<A href='?src=\ref[src];pick_d_notice=\ref[CHANNEL]'>[CHANNEL.channel_name]</A> [(CHANNEL.censored) ? ("<FONT COLOR='red'>***</FONT>") : null]<BR>"
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+="<BR><A href='?src=\ref[src];setScreen=[0]'>Back</A>"
if(12)
dat+="<B>[viewing_channel.channel_name]: </B><FONT SIZE=1>\[ created by: <FONT COLOR='maroon'>[viewing_channel.author]</FONT> \]</FONT><BR>"
dat+="<FONT SIZE=2><A href='?src=\ref[src];censor_channel_author=\ref[viewing_channel]'>[(viewing_channel.author=="\[REDACTED\]") ? ("Undo Author censorship") : ("Censor channel Author")]</A></FONT><HR>"
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+="<I>No feed messages found in channel...</I><BR>"
else
for(var/datum/feed_message/MESSAGE in viewing_channel.messages)
dat+="-[MESSAGE.body] <BR><FONT SIZE=1>\[[MESSAGE.message_type] by <FONT COLOR='maroon'>[MESSAGE.author]</FONT>\]</FONT><BR>"
dat+="<FONT SIZE=2><A href='?src=\ref[src];censor_channel_story_body=\ref[MESSAGE]'>[(MESSAGE.body == "\[REDACTED\]") ? ("Undo story censorship") : ("Censor story")]</A> - <A href='?src=\ref[src];censor_channel_story_author=\ref[MESSAGE]'>[(MESSAGE.author == "\[REDACTED\]") ? ("Undo Author Censorship") : ("Censor message Author")]</A></FONT><BR>"
dat+="<BR><A href='?src=\ref[src];setScreen=[10]'>Back</A>"
if(13)
dat+="<B>[viewing_channel.channel_name]: </B><FONT SIZE=1>\[ created by: <FONT COLOR='maroon'>[viewing_channel.author]</FONT> \]</FONT><BR>"
dat+="Channel messages listed below. If you deem them dangerous to the station, you can <A href='?src=\ref[src];toggle_d_notice=\ref[viewing_channel]'>Bestow a D-Notice upon the channel</A>.<HR>"
if(viewing_channel.censored)
dat+="<FONT COLOR='red'><B>ATTENTION: </B></FONT>This channel has been deemed as threatening to the welfare of the station, and marked with a [using_map.company_name] D-Notice.<BR>"
dat+="No further feed story additions are allowed while the D-Notice is in effect.<BR><BR>"
else
if(isemptylist(viewing_channel.messages))
dat+="<I>No feed messages found in channel...</I><BR>"
else
for(var/datum/feed_message/MESSAGE in viewing_channel.messages)
dat+="-[MESSAGE.body] <BR><FONT SIZE=1>\[[MESSAGE.message_type] by <FONT COLOR='maroon'>[MESSAGE.author]</FONT>\]</FONT><BR>"
// Creating Channels
data["channel_name"] = channel_name
data["c_locked"] = c_locked
dat+="<BR><A href='?src=\ref[src];setScreen=[11]'>Back</A>"
if(14)
dat+="<B>Wanted Issue Handler:</B>"
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+="<FONT SIZE=2><BR><I>A wanted issue is already in Feed Circulation. You can edit or cancel it below.</FONT></I>"
dat+="<HR>"
dat+="<A href='?src=\ref[src];set_wanted_name=1'>Criminal Name</A>: [channel_name] <BR>"
dat+="<A href='?src=\ref[src];set_wanted_desc=1'>Description</A>: [msg] <BR>"
dat+="<A href='?src=\ref[src];set_attachment=1'>Attach Photo</A>: [(photo_data ? "Photo Attached" : "No Photo")]</BR>"
if(wanted_already)
dat+="<B>Wanted Issue created by:</B><FONT COLOR='green'> [news_network.wanted_issue.backup_author]</FONT><BR>"
else
dat+="<B>Wanted Issue will be created under prosecutor:</B><FONT COLOR='green'> [scanned_user]</FONT><BR>"
dat+="<BR><A href='?src=\ref[src];submit_wanted=[end_param]'>[(wanted_already) ? ("Edit Issue") : ("Submit")]</A>"
if(wanted_already)
dat+="<BR><A href='?src=\ref[src];cancel_wanted=1'>Take down Issue</A>"
dat+="<BR><A href='?src=\ref[src];setScreen=[0]'>Cancel</A>"
if(15)
dat+="<FONT COLOR='green'>Wanted issue for [channel_name] is now in Network Circulation.</FONT><BR><BR>"
dat+="<BR><A href='?src=\ref[src];setScreen=[0]'>Return</A><BR>"
if(16)
dat+="<B><FONT COLOR='maroon'>ERROR: Wanted Issue rejected by Network.</B></FONT><HR><BR>"
if(channel_name=="" || channel_name == "\[REDACTED\]")
dat+="<FONT COLOR='maroon'>•Invalid name for person wanted.</FONT><BR>"
if(scanned_user=="Unknown")
dat+="<FONT COLOR='maroon'>•Issue author unverified.</FONT><BR>"
if(msg == "" || msg == "\[REDACTED\]")
dat+="<FONT COLOR='maroon'>•Invalid description.</FONT><BR>"
dat+="<BR><A href='?src=\ref[src];setScreen=[0]'>Return</A><BR>"
if(17)
dat+="<B>Wanted Issue successfully deleted from Circulation</B><BR>"
dat+="<BR><A href='?src=\ref[src];setScreen=[0]'>Return</A><BR>"
if(18)
dat+="<B><FONT COLOR ='maroon'>-- STATIONWIDE WANTED ISSUE --</B></FONT><BR><FONT SIZE=2>\[Submitted by: <FONT COLOR='green'>[news_network.wanted_issue.backup_author]</FONT>\]</FONT><HR>"
dat+="<B>Criminal</B>: [news_network.wanted_issue.author]<BR>"
dat+="<B>Description</B>: [news_network.wanted_issue.body]<BR>"
dat+="<B>Photo:</B>: "
if(news_network.wanted_issue.img)
usr << browse_rsc(news_network.wanted_issue.img, "tmp_photow.png")
dat+="<BR><img src='tmp_photow.png' width = '180'>"
else
dat+="None"
dat+="<BR><BR><A href='?src=\ref[src];setScreen=[0]'>Back</A><BR>"
if(19)
dat+="<FONT COLOR='green'>Wanted issue for [channel_name] successfully edited.</FONT><BR><BR>"
dat+="<BR><A href='?src=\ref[src];setScreen=[0]'>Return</A><BR>"
if(20)
dat+="<FONT COLOR='green'>Printing successful. Please receive your newspaper from the bottom of the machine.</FONT><BR><BR>"
dat+="<A href='?src=\ref[src];setScreen=[0]'>Return</A>"
if(21)
dat+="<FONT COLOR='maroon'>Unable to print newspaper. Insufficient paper. Please notify maintenance personnel to refill machine storage.</FONT><BR><BR>"
dat+="<A href='?src=\ref[src];setScreen=[0]'>Return</A>"
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 != "<B>\[REDACTED\]</B>")
if(FC.author != "\[REDACTED\]")
FC.backup_author = FC.author
FC.author = "<B>\[REDACTED\]</B>"
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 != "<B>\[REDACTED\]</B>")
if(MSG.author != "\[REDACTED\]")
MSG.backup_author = MSG.author
MSG.author = "<B>\[REDACTED\]</B>"
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 != "<B>\[REDACTED\]</B>")
if(MSG.body != "\[REDACTED\]")
MSG.backup_body = MSG.body
MSG.backup_caption = MSG.caption
MSG.backup_img = MSG.img
MSG.body = "<B>\[REDACTED\]</B>"
MSG.caption = "<B>\[REDACTED\]</B>"
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, "<FONT COLOR='blue'>There's already a scribble in this page... You wouldn't want to make things too cluttered, would you?</FONT>")
@@ -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()

View File

@@ -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, "<span class='warning'>The machine is already loaded.</span>")
/obj/machinery/gear_painter/attackby(obj/item/W, mob/user)
if(LAZYLEN(processing) >= MAX_PROCESSING)
to_chat(user, "<span class='warning'>The machine is full.</span>")
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 = "<TITLE>Color Mate Control Panel</TITLE><BR>"
if(!processing.len)
dat += "No item inserted."
else
for(var/atom/movable/O in processing)
dat += "Item inserted: [O]<HR>"
dat += "<A href='?src=\ref[src];select=1'>Select new color.</A><BR>"
dat += "Color: <font color='[activecolor]'>&#9899;</font>"
dat += "<A href='?src=\ref[src];paint=1'>Apply new color.</A><BR><BR>"
dat += "<A href='?src=\ref[src];clear=1'>Remove paintjob.</A><BR><BR>"
dat += "<A href='?src=\ref[src];eject=1'>Eject item.</A><BR><BR>"
/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()

View File

@@ -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("<span class='info'>\The [usr] swipes \the [wallet] through \the [src].</span>")
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, "<span class='warning'>Insufficient funds on chargecard.</span>")
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("<span class='info'>\The [usr] swipes \the [I] through \the [src].</span>")
else
visible_message("<span class='info'>\The [usr] swipes \the [ID_container] through \the [src].</span>")
/obj/machinery/vending/proc/pay_with_card(obj/item/weapon/card/id/I, mob/M)
visible_message("<span class='info'>[M] swipes a card through [src].</span>")
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, "<span class='warning'>Error: Unable to access account. Please contact technical support if problem persists.</span>")
return FALSE
if(customer_account.suspended)
status_message = "Unable to access account: account suspended."
status_error = 1
return 0
to_chat(M, "<span class='warning'>Unable to access account: account suspended.</span>")
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, "<span class='warning'>Unable to access account: incorrect credentials.</span>")
return FALSE
if(currently_vending.price > customer_account.money)
status_message = "Insufficient funds in account."
status_error = 1
return 0
to_chat(M, "<span class='warning'>Insufficient funds in account.</span>")
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, "<span class='notice'>You remove \the [coin] from \the [src].</span>")
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, "<span class='notice'>You remove \the [coin] from \the [src].</span>")
coin = null
categories &= ~CAT_COIN
return TRUE
if("vend")
if(!vend_ready || currently_vending)
if(!vend_ready)
to_chat(usr, "<span class='warning'>[src] is busy!</span>")
return
if(!allowed(usr) && !emagged && scan_id)
to_chat(usr, "<span class='warning'>Access denied.</span>") //Unless emagged of course
flick("[icon_state]-deny",src)
playsound(src, 'sound/machines/deniedbeep.ogg', 50, 0)
return
if(panel_open)
to_chat(usr, "<span class='warning'>[src] cannot dispense products while its service panel is open!</span>")
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, "<span class='danger'>Lawed unit recognized. Lawed units cannot complete this transaction. Purchase canceled.</span>")
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, "<span class='notice'>Vending object due to admin interaction.</span>")
paid = TRUE*/
else
to_chat(usr, "<span class='warning'>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, "<span class='warning'>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, "<span class='warning'>Access denied.</span>") //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, "<span class='warning'>Access denied.</span>") //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, "<span class='warning'>[src] has ran out of that product.</span>")
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("<span class='notice'>\The [src] clunks as it vends an additional item.</span>")
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("<span class='notice'>\The [src] clunks as it vends an additional item.</span>")
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())

View File

@@ -535,6 +535,7 @@
product.category = category
product_records.Add(product)
GLOB.vending_products[entry] = 1
/obj/machinery/vending/magivend
name = "MagiVend"

View File

@@ -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."

View File

@@ -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

View File

@@ -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(

View File

@@ -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

View File

@@ -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

View File

@@ -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"

View File

@@ -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, "<span class='notice'>You combine the Thalers to a bundle of [SC.worth] Thalers.</span>")
to_chat(user, "<span class='notice'>You combine the [initial_name]s to a bundle of [SC.worth] [initial_name]s.</span>")
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'

View File

@@ -81,6 +81,30 @@
else
to_chat(user, "<span class='notice>'It is empty.</span>")
/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, "<span class='warning'>\The [src] is not working.</span>")
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("<span class='notice'>\The [user] puts \the [I] into \the [src].</span>")
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

View File

@@ -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

View File

@@ -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, "<span class='warning'>You can't remove ingredients while it's turned on! Turn it off first or wait for it to finish.</span>")
if(show_warning)
to_chat(user, "<span class='warning'>You can't remove ingredients while it's turned on! Turn it off first or wait for it to finish.</span>")
return 0
//Container is not removable
/obj/machinery/appliance/mixer/removal_menu(var/mob/user)

View File

@@ -104,7 +104,7 @@
cooking = FALSE
playsound(src, 'sound/machines/hatch_open.ogg', 20, 1)
to_chat(user, "<span class='notice'>You [open? "close":"open"] the oven door</span>")
to_chat(user, "<span class='notice'>You [open ? "open" : "close"] the oven door.</span>")
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, "<span class='warning'>You can't take anything out while the door is closed!</span>")
if(show_warning)
to_chat(user, "<span class='warning'>You can't take anything out while the door is closed!</span>")
return 0
else

View File

@@ -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, "<span class='warning'>It's dirty!</span>")
return 1
@@ -141,10 +142,11 @@
user.visible_message( \
"<span class='notice'>\The [user] has added \the [O] to \the [src].</span>", \
"<span class='notice'>You add \the [O] to \the [src].</span>")
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, "<span class='warning'>You have no idea what you can cook with this [O].</span>")
..()
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 = {"<TT>Bzzzzttttt</TT>"}
else if(src.operating)
dat = {"<TT>Microwaving in progress!<BR>Please wait...!</TT>"}
else if(src.dirty==100)
dat = {"<TT>This microwave is dirty!<BR>Please clean it before use!</TT>"}
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 += {"<B>[capitalize(O)]:</B> [N] [lowertext(O)]\s<BR>"}
else
if (N==1)
dat += {"<B>[capitalize(O)]:</B> [N] [items_measures[O]]<BR>"}
else
dat += {"<B>[capitalize(O)]:</B> [N] [items_measures_p[O]]<BR>"}
/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 += {"<B>[display_name]:</B> [R.volume] unit\s<BR>"}
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 = {"<B>The microwave is empty</B><BR>"}
/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 = {"<b>Ingredients:</b><br>[dat]"}
dat += {"<HR><BR>\
<A href='?src=\ref[src];action=cook'>Turn on!<BR>\
<A href='?src=\ref[src];action=dispose'>Eject ingredients!<BR>\
"}
data.Add(list(list(
"name" = capitalize(O),
"amt" = N,
"extra" = N == 1 ? items_measures[O] : items_measures_p[O],
)))
user << browse("<HEAD><TITLE>Microwave Controls</TITLE></HEAD><TT>[dat]</TT>", "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 = {"<TT>Bzzzzttttt</TT>"}
// else if(src.operating)
// dat = {"<TT>Microwaving in progress!<BR>Please wait...!</TT>"}
// else if(src.dirty==100)
// dat = {"<TT>This microwave is dirty!<BR>Please clean it before use!</TT>"}
// 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 += {"<B>[capitalize(O)]:</B> [N] [lowertext(O)]\s<BR>"}
// else
// if (N==1)
// dat += {"<B>[capitalize(O)]:</B> [N] [items_measures[O]]<BR>"}
// else
// dat += {"<B>[capitalize(O)]:</B> [N] [items_measures_p[O]]<BR>"}
// 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 += {"<B>[display_name]:</B> [R.volume] unit\s<BR>"}
// if (items_counts.len==0 && reagents.reagent_list.len==0)
// dat = {"<B>The microwave is empty</B><BR>"}
// else
// dat = {"<b>Ingredients:</b><br>[dat]"}
// dat += {"<HR><BR>\
// <A href='?src=\ref[src];action=cook'>Turn on!<BR>\
// <A href='?src=\ref[src];action=dispose'>Eject ingredients!<BR>\
// "}
// user << browse("<HEAD><TITLE>Microwave Controls</TITLE></HEAD><TT>[dat]</TT>", "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, "<span class='notice'>You dispose of the microwave contents.</span>")
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"

View File

@@ -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)

View File

@@ -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)

View File

@@ -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("<span class='warning'>ERROR. Recalibrating displays.</span>")
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)

View File

@@ -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)

View File

@@ -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

View File

@@ -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, "<span class='warning'>[src] seems unable to connect to your NIF...</span>")
return FALSE
var/mob/living/carbon/human/H = user
if(!H.nif || !H.nif.stat == NIF_WORKING)
to_chat(H, "<span class='warning'>[src] seems unable to connect to your NIF...</span>")
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, "<span class='notice'>You remove \the [coin] from \the [src]</span>")
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, "<span class='warning'>Access denied.</span>") //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, "<span class='warning'>You aren't authorized to buy [initial(path.name)].</span>")
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, "<span class='danger'>Artificial unit recognized. Artificial units cannot complete this transaction. Purchase canceled.</span>")
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)]<br><br><b>Please swipe a card or insert cash to pay for the item.</b>"
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, "<span class='warning'>You aren't authorized to buy [initial(path.name)].</span>")
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, "<span class='warning'>Purchase not allowed.</span>") //Unless emagged of course
if(!can_buy(R, user)) //For SECURE VENDING MACHINES YEAH
to_chat(user, "<span class='warning'>Purchase not allowed.</span>") //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.

View File

@@ -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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

@@ -1,36 +0,0 @@
<!--
Interface for holodeck computers
See: code/modules/holodeck/HolodeckControl.dm
-->
<h3>Current Loaded Programs:</h3>
{{for data.supportedPrograms}}
<div class='item'>{{:helper.link(value, data.currentProgram == value ? 'check' : 'close', {'program' : value}, null, data.currentProgram == value ? 'linkOn' : null)}}</div>
{{/for}}
<div class='item'>Please ensure that only holographic weapons are used in the holodeck if a combat simulation has been loaded.</div>
{{if data.isSilicon}}
{{if data.safetyDisabled}}
{{if data.emagged}}
<div class='item'><span class='bad'><b>ERROR</b></span>: Cannot re-enable Safety Protocols.</div>
{{else}}
<div class='item'>{{:helper.link('Re-Enable Safety Protocols?', 'help', {'AIoverride' : 1}, null, 'linkOn')}}</div>
{{/if}}
{{else}}
<div class='item'>{{:helper.link('Re-Enable Safety Protocols?', 'help', {'AIoverride' : 1}, null, 'redButton')}}</div>
{{/if}}
{{/if}}
{{if data.safetyDisabled}}
{{for data.restrictedPrograms}}
<div class='item'>{{:helper.link('Begin ' + value, data.currentProgram == value ? 'check' : 'close', {'program' : value.program}, null, data.currentProgram == value ? 'linkOn' : null)}}</div>
{{/for}}
<div class='item average'>Ensure the holodeck is empty before testing.</div>
<div class='item'>Safety Protocols are <span class='bad'>DISABLED</span></div>
{{else}}
<div class='item'>Safety Protocols are <span class='good'>ENABLED</span></div>
{{/if}}
<div class='item'>
<div class='itemLabelNarrow'>Gravity:</div>
{{:helper.link(data.gravity ? 'On ' : 'Off', data.gravity ? 'check' : 'close', {'gravity' : 1}, null, data.gravity ? 'linkOn' : 'redButton')}}
</div>

View File

@@ -1,49 +0,0 @@
<!--
Title: Jukebox UI
Used In File(s): \code\game\machinery\jukebox.dm
-->
<div class='statusDisplay'>
<div class="item">
<div class="itemLabel">Currently Playing:</div>
{{if data.playing && data.current_track }}
<div class="itemContent"><b>{{:data.current_track.title}}</b> by <i>{{:data.current_track.artist ? data.current_track.artist : "Unknown"}}</i></div>
{{else}}
<div class="itemContent">Stopped</div>
{{/if}}
</div>
<div class="item">
<div class="itemContent">
{{:helper.displayBar(data.percent, 0, 1, 'good')}}
</div>
<div class="itemLabel">
{{:helper.link('Play' , 'play', {'play' : 1}, data.playing == 1 ? 'disabled' : null, null)}}
{{:helper.link('Stop' , 'stop', {'stop' : 1}, data.playing == 0 ? 'disabled' : null, null)}}
</div>
</div>
<div class="item">
<div class="itemLabel">{{:helper.link('Volume', 'volume-on', {'volume' : 1})}}</div>
<div class="itemContent">
{{:helper.displayBar(data.volume, 0, 1, (data.volume < .25) ? 'bad' : (data.volume < .75) ? 'average' : 'good')}}
</div>
</div>
</div>
<div class="item">
<div class="itemLabel">Loop Mode:</div>
<div class="itemContent">
{{: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)}}
</div>
</div>
<H3><span class="white">Available tracks:</span></H3>
<div class="itemContent">
{{for data.tracks}}
<div class="item">
{{:helper.link(value.title, 'gear', {'change_track' : value.ref}, data.current_track_ref == value.ref ? 'disabled' : null, null)}}
</div>
{{/for}}
</div>

View File

@@ -1,14 +0,0 @@
<h3>Current Loaded Programs:</h3>
{{for data.supportedPrograms}}
<div class='item'>{{:helper.link(value, data.currentProgram == value ? 'check' : 'close', {'program' : value}, null, data.currentProgram == value ? 'linkOn' : null)}}</div>
{{/for}}
<div class='item'>
<div class='itemLabel'>Area&nbspGravity:</div>
{{:helper.link(data.gravity ? 'On ' : 'Off', data.gravity ? 'check' : 'close', {'gravity' : 1}, null, data.gravity ? 'linkOn' : 'redButton')}}
</div>
<div class='item'>
<div class='itemLabel'>Full&nbspImmersion:</div>
{{:helper.link(data.immersion ? 'On ' : 'Off', data.immersion ? 'check' : 'close', {'immersion' : 1}, null, data.immersion ? 'linkOn' : 'redButton')}}
</div>

View File

@@ -1,83 +0,0 @@
<!--
Timeclock UI
See: code/game/machinery/computer/timeclock_vr.dm
-->
<!-- Always show person's status display -->
<div class="statusDisplay">
<h3><div class='notice'>OOC Note: PTO acquired is account-wide and shared across all characters. Info listed below is not IC information.</div></h3>
<h3>Time Off Balance for {{:config.user.name}}</h3>
{{props data.department_hours}}
<div class="line">
<div class="statusLabel">{{:key}}</div>
<div class="statusValue {{:value > 6 ? 'good' : value < 1 ? 'bad' : 'average'}}">{{:helper.fixed(value)}} {{:helper.fixed(value) == 1.0 ? 'hour' : 'hours'}}</div>
</div>
{{empty}}
<div class='notice'>No Hours Accrued</div>
{{/props}}
</div>
<h3>Employment Information</h3>
<div class='itemGroup'>
<div class='item'>
<div class='itemLabel'>Employee ID:</div>
{{:helper.link(data.card ? data.card : 'Insert ID', 'person', {'id' : 1})}}
</div>
{{if data.job_datum }}
<div class='item'>
<div class='itemLabel'>Rank:</div>
<div class='itemContent'>
<span style='padding: 2px 8px; border-radius: 4px; background-color: {{:data.job_datum.selection_color}};'>{{:data.job_datum.title}}</span>
</div>
</div>
<div class='item'>
<div class='itemLabel'>Departments:</div>
<div class='itemContent'>{{:data.job_datum.departments}}</div>
</div>
<div class='item'>
<div class='itemLabel'>Pay Scale:</div>
<div class='itemContent'>{{:data.job_datum.economic_modifier}}</div>
</div>
<div class='item'>
<div class='itemLabel'>PTO Eligibility:</div>
{{if data.job_datum.timeoff_factor > 0 }}
<div class='itemContent' title='Hours working this position will earn time off for its department.' style='cursor: help;'>
Earns PTO - {{:data.job_datum.pto_department}}
</div>
{{else data.job_datum.timeoff_factor < 0}}
<div class='itemContent' title='Hours spent in this position are deducted from your time off for its department.' style='cursor: help;'>
Requires PTO - {{:data.job_datum.pto_department}}
</div>
{{else}}
<div class='itemContent' title='This job neither requires nor earns time off hours.' style='cursor: help;'>
Neutral
</div>
{{/if}}
</div>
{{/if}}
</div>
{{if data.allow_change_job && data.job_datum && data.job_datum.timeoff_factor != 0 && !(data.assignment == "Dismissed")}}
<h3>Employment Actions</h3>
<div class='itemGroup'>
<div class='item'>
{{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}}
<i class='uiIcon16 icon-alert-red'></i>
<span class='bad'>Insufficent Time Off Accrued</span>
{{/if}}
{{else (data.job_datum.timeoff_factor < 0) }}
{{props data.job_choices :alt_titles:job }}
{{props alt_titles :alt_title:alt_title_index }}
<div class='itemLabelWide'>{{:alt_title}}</div>
<div class='itemContentMedium'>{{:helper.link("Go On-Duty", 'suitcase', {'switch-to-onduty-rank' : job,'switch-to-onduty-assignment' : alt_title})}}</div>
{{/props}}
{{empty}}
<div class='notice'>No Open Positions - See Head of Personnel</div>
{{/props}}
{{/if}}
</div>
</div>
{{/if}}

View File

@@ -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 (
<Window width={300} height={height} resizable>
<Window.Content>
{items.length && (
<Fragment>
<Section title="Paint">
<Flex justify="center" align="center">
<Flex.Item basis="50%">
<Box backgroundColor={activecolor} width="120px" height="120px" />
</Flex.Item>
<Flex.Item basis="50% ">
<Button
fluid
icon="eye-dropper"
onClick={() => act("select")}>
Select Color
</Button>
<Button
fluid
icon="fill-drip"
onClick={() => act("paint")}>
Paint Items
</Button>
<Button
fluid
icon="tint-slash"
onClick={() => act("clear")}>
Remove Paintjob
</Button>
<Button
fluid
icon="eject"
onClick={() => act("eject")}>
Eject Items
</Button>
</Flex.Item>
</Flex>
</Section>
<Section title="Items">
{items.map((item, i) => <Box key={i}>#{i+1}: {item}</Box>)}
</Section>
</Fragment>
) || (
<Section>
<Box color="bad">No items inserted.</Box>
</Section>
)}
</Window.Content>
</Window>
);
};

View File

@@ -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 (
<Window width={600} height={600} resizable>
<Window.Content scrollable>
<Section title="Status">
<LabeledList>
<LabeledList.Item label="Temperature">
<ProgressBar
color={temperatureEnough ? "good" : "blue"}
value={temperature}
maxValue={optimalTemp + 100}>
<AnimatedNumber value={temperature} />&deg;C / {optimalTemp}&deg;C
</ProgressBar>
</LabeledList.Item>
<LabeledList.Item label="Efficiency">
<AnimatedNumber value={efficiency} />%
</LabeledList.Item>
</LabeledList>
</Section>
<Section title="Containers">
<LabeledList>
{our_contents.map((content, i) => {
if (content.empty) {
return (
<LabeledList.Item label={"Slot #" + (i + 1)} >
<Button
onClick={() => act("slot", { slot: i + 1 })}>
Empty
</Button>
</LabeledList.Item>
);
}
return (
<LabeledList.Item label={"Slot #" + (i + 1)} verticalAlign="middle" key={i}>
<Flex spacing={1}>
<Flex.Item>
<Button
disabled={!containersRemovable}
onClick={() => act("slot", { slot: i + 1 })}>
{content.container || "No Container"}
</Button>
</Flex.Item>
<Flex.Item grow={1}>
<ProgressBar
color={content.progressText[0]}
value={content.progress}
maxValue={1}>
{content.progressText[1]}
</ProgressBar>
</Flex.Item>
</Flex>
</LabeledList.Item>
);
})}
</LabeledList>
</Section>
</Window.Content>
</Window>
);
};

View File

@@ -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 (
<Window width={400} height={600} resizable>
<Window.Content scrollable>
<Tabs>
<Tabs.Tab
selected={tab === 0}
onClick={() => setTab(0)}>
Crew
</Tabs.Tab>
{!!allow_items && (
<Tabs.Tab
selected={tab === 1}
onClick={() => setTab(1)}>
Items
</Tabs.Tab>
)}
</Tabs>
<NoticeBox info>Welcome, {real_name}.</NoticeBox>
{tab === 0 && <CryoStorageCrew />}
{!!allow_items && tab === 1 && <CryoStorageItems />}
</Window.Content>
</Window>
);
};
export const CryoStorageCrew = (props, context) => {
const { act, data } = useBackend(context);
const {
crew,
} = data;
return (
<Section title="Stored Crew">
{crew.length && crew.map(c => <Box key={c} color="label">{c}</Box>) || (
<Box color="good">
No crew currently stored.
</Box>
)}
</Section>
);
};
export const CryoStorageItems = (props, context) => {
const { act, data } = useBackend(context);
const {
items,
} = data;
return (
<Section title="Stored Items" buttons={
<Button
icon="hand-rock"
onClick={() => act("allitems")}>
Claim All
</Button>
}>
{items.length && items.map(item => (
<Button
key={item.ref}
icon="hand-rock"
onClick={() => act("item", { ref: item.ref })}>
{item.name}
</Button>
)) || (
<Box color="average">
No items stored.
</Box>
)}
</Section>
);
};

View File

@@ -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 (
<Window width={400} height={600} resizable>
<Window.Content scrollable>
<Tabs>
<Tabs.Tab
selected={tab === 0}
onClick={() => setTab(0)}>
Crew
</Tabs.Tab>
{!!allow_items && (
<Tabs.Tab
selected={tab === 1}
onClick={() => setTab(1)}>
Items
</Tabs.Tab>
)}
</Tabs>
<NoticeBox info>Welcome, {real_name}.</NoticeBox>
{tab === 0 && <CryoStorageCrew />}
{!!allow_items && tab === 1 && <CryoStorageItemsVr />}
</Window.Content>
</Window>
);
};
export const CryoStorageItemsVr = (props, context) => {
const { act, data } = useBackend(context);
const {
items,
} = data;
return (
<Section title="Stored Items">
{items.length && items.map(item => <Box color="label" key={item}>{item}</Box>) || (
<Box color="average">
No items stored.
</Box>
)}
</Section>
);
};

View File

@@ -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 (
<Window width={400} height={610} resizable>
<Window.Content scrollable>
<Section title="Programs">
{programsToShow.map(prog => (
<Button
key={prog}
color={restrictedPrograms.indexOf(prog) !== -1 ? "bad" : null}
icon="eye"
content={prog}
selected={currentProgram === prog}
fluid
onClick={() => act("program", { program: prog })} />
))}
</Section>
{!!isSilicon && (
<Section title="Override">
<Button
icon="exclamation-triangle"
fluid
disabled={emagged}
color={safetyDisabled ? "good" : "bad"}
onClick={() => act("AIoverride")}>
{!!emagged && "Error, unable to control. "}
{safetyDisabled ? "Enable Safeties" : "Disable Safeties"}
</Button>
</Section>
)}
<Section title="Controls">
<LabeledList>
<LabeledList.Item label="Safeties">
{safetyDisabled ? <Box color="bad">DISABLED</Box> : <Box color="good">ENABLED</Box>}
</LabeledList.Item>
<LabeledList.Item label="Gravity">
<Button
icon="user-astronaut"
selected={gravity}
onClick={() => act("gravity")}>
{gravity ? "Enabled" : "Disabled"}
</Button>
</LabeledList.Item>
</LabeledList>
</Section>
</Window.Content>
</Window>
);
};

View File

@@ -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 <Icon inline color="label" name={rankObj} size={2} />;
} else if (Array.isArray(rankObj)) {
return rankObj.map(icon => (
<Icon inline key={icon} color="label" name={icon} size={2} />
));
} else {
return <Icon inline color="label" name="user" size={2} />;
}
};
import { RankIcon } from "./common/RankIcon";
export const IDCard = (props, context) => {
const { data } = useBackend(context);

View File

@@ -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 (
<Window width={450} height={600} resizable>
<Window.Content scrollable>
<Section title="Currently Playing">
<LabeledList>
<LabeledList.Item label="Title">
{playing && current_track && (
<Box>
{current_track.title} by {current_track.artist || "Unkown"}
</Box>
) || (
<Box>
Stopped
</Box>
)}
</LabeledList.Item>
<LabeledList.Item label="Controls">
<Button
icon="play"
disabled={playing}
onClick={() => act("play")}>
Play
</Button>
<Button
icon="stop"
disabled={!playing}
onClick={() => act("stop")}>
Stop
</Button>
</LabeledList.Item>
<LabeledList.Item label="Loop Mode">
<Button
icon="play"
onClick={() => act("loopmode", { loopmode: 1 })}
selected={loop_mode === 1}>
Next
</Button>
<Button
icon="random"
onClick={() => act("loopmode", { loopmode: 2 })}
selected={loop_mode === 2}>
Shuffle
</Button>
<Button
icon="redo"
onClick={() => act("loopmode", { loopmode: 3 })}
selected={loop_mode === 3}>
Repeat
</Button>
<Button
icon="step-forward"
onClick={() => act("loopmode", { loopmode: 4 })}
selected={loop_mode === 4}>
Once
</Button>
</LabeledList.Item>
<LabeledList.Item label="Progress">
<ProgressBar
value={percent}
maxValue={1}
color="good" />
</LabeledList.Item>
<LabeledList.Item label="Volume">
<Slider
minValue={0}
step={0.01}
value={volume}
maxValue={1}
ranges={{
good: [.75, Infinity],
average: [.25, .75],
bad: [0, .25],
}}
format={val => round(val * 100, 1) + "%"}
onChange={(e, val) => act("volume", { val: round(val, 2) })} />
</LabeledList.Item>
</LabeledList>
</Section>
<Section title="Available Tracks">
{tracks.length && sortBy(a => a.title)(tracks).map(track => (
<Button
fluid
icon="play"
key={track.ref}
selected={current_track_ref === track.ref}
onClick={() => act("change_track", { change_track: track.ref })}>
{track.title}
</Button>
)) || (
<Box color="bad">
Error: No songs loaded.
</Box>
)}
</Section>
</Window.Content>
</Window>
);
};

View File

@@ -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 (
<Window width={300} height={height} resizable>
<Window.Content scrollable>
<Section title="Programs">
{supportedPrograms.map(program => (
<Button
key={program}
fluid
icon="eye"
selected={program === currentProgram}
onClick={() => act("program", { program: program })}>
{program}
</Button>
))}
</Section>
<Section title="Controls">
<LabeledList>
<LabeledList.Item label="Gravity">
<Button
fluid
icon="user-astronaut"
selected={gravity}
onClick={() => act("gravity")}>
{gravity ? "Enabled" : "Disabled"}
</Button>
</LabeledList.Item>
<LabeledList.Item label="Full Immersion">
<Button
mt={-1}
fluid
icon="eye"
selected={immersion}
onClick={() => act("immersion")}>
{immersion ? "Enabled" : "Disabled"}
</Button>
</LabeledList.Item>
</LabeledList>
</Section>
</Window.Content>
</Window>
);
};

View File

@@ -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 (
<Window width={400} height={500} resizable>
<Window.Content scrollable>
{broken && (
<Section>
<Box color="bad">
Bzzzzttttt!!
</Box>
</Section>
) || operating && (
<Section>
<Box color="good">
Microwaving in progress!<br />
Please wait...!
</Box>
</Section>
) || dirty && (
<Section>
<Box color="bad">
This microwave is dirty!<br />
Please clean it before use!
</Box>
</Section>
) || items.length && (
<Section level={1} title="Ingredients" buttons={
<Fragment>
<Button
icon="radiation"
onClick={() => act("cook")}>
Microwave
</Button>
<Button
icon="eject"
onClick={() => act("dispose")}>
Eject
</Button>
</Fragment>
}>
<LabeledList>
{items.map(item => (
<LabeledList.Item key={item.name} label={item.name}>
{item.amt} {item.extra}
</LabeledList.Item>
))}
</LabeledList>
</Section>
) || (
<Section>
<Box color="bad">
{config.title} is empty.
</Box>
</Section>
)}
</Window.Content>
</Window>
);
};

View File

@@ -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 (
<Window width={600} height={600} resizable>
<Window.Content scrollable>
<TemporaryNotice decode />
<NewscasterContent />
</Window.Content>
</Window>
);
};
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 (
<Box>
<Template setScreen={setScreen} />
</Box>
);
};
const NewscasterMainMenu = (props, context) => {
const { act, data } = useBackend(context);
const {
securityCaster,
wanted_issue,
} = data;
const {
setScreen,
} = props;
return (
<Fragment>
<Section title="Main Menu">
{wanted_issue && (
<Button fluid icon="eye" onClick={() => setScreen(NEWSCASTER_SCREEN_VIEWWANTED)} color="bad">
Read WANTED Issue
</Button>
)}
<Button fluid icon="eye" onClick={() => setScreen(NEWSCASTER_SCREEN_VIEWLIST)}>
View Feed Channels
</Button>
<Button fluid icon="plus" onClick={() => setScreen(NEWSCASTER_SCREEN_NEWCHANNEL)}>
Create Feed Channel
</Button>
<Button fluid icon="plus" onClick={() => setScreen(NEWSCASTER_SCREEN_NEWSTORY)}>
Create Feed Message
</Button>
<Button fluid icon="print" onClick={() => setScreen(NEWSCASTER_SCREEN_PRINT)}>
Print Newspaper
</Button>
</Section>
{!!securityCaster && (
<Section title="Feed Security Functions">
<Button fluid icon="plus" onClick={() => setScreen(NEWSCASTER_SCREEN_NEWWANTED)}>
Manage &quot;Wanted&quot; Issue
</Button>
</Section>
)}
</Fragment>
);
};
const NewscasterNewChannel = (props, context) => {
const { act, data } = useBackend(context);
const {
channel_name,
c_locked,
user,
} = data;
const {
setScreen,
} = props;
return (
<Section title="Creating new Feed Channel" buttons={
<Button
icon="undo"
onClick={() => setScreen(NEWSCASTER_SCREEN_MAIN)}>
Back
</Button>
}>
<LabeledList>
<LabeledList.Item label="Channel Name">
<Input
fluid
value={decodeHtmlEntities(channel_name)}
onInput={(e, val) => act("set_channel_name", { val: val })} />
</LabeledList.Item>
<LabeledList.Item label="Channel Author" color="good">
{user}
</LabeledList.Item>
<LabeledList.Item label="Accept Public Feeds">
<Button
icon={c_locked ? "lock" : "lock-open"}
selected={!c_locked}
onClick={() => act("set_channel_lock")}>
{c_locked ? "No" : "Yes"}
</Button>
</LabeledList.Item>
</LabeledList>
<Button
fluid
color="good"
icon="plus"
onClick={() => act("submit_new_channel")}>
Submit Channel
</Button>
<Button
fluid
color="bad"
icon="undo"
onClick={() => setScreen(NEWSCASTER_SCREEN_MAIN)}>
Cancel
</Button>
</Section>
);
};
const NewscasterViewList = (props, context) => {
const { act, data } = useBackend(context);
const {
channels,
} = data;
const {
setScreen,
} = props;
return (
<Section title="Station Feed Channels" buttons={
<Button
icon="undo"
onClick={() => setScreen(NEWSCASTER_SCREEN_MAIN)}>
Back
</Button>
}>
{channels.map(channel => (
<Button
fluid
key={channel.name}
icon="eye"
color={channel.admin ? "good" : channel.censored ? "bad" : ""}
onClick={() => {
act("show_channel", { show_channel: channel.ref });
setScreen(NEWSCASTER_SCREEN_SELECTEDCHANNEL);
}}>
{decodeHtmlEntities(channel.name)}
</Button>
))}
</Section>
);
};
const NewscasterNewStory = (props, context) => {
const { act, data } = useBackend(context);
const {
channel_name,
user,
msg,
photo_data,
} = data;
const {
setScreen,
} = props;
return (
<Section title="Creating new Feed Message..." buttons={
<Button
icon="undo"
onClick={() => setScreen(NEWSCASTER_SCREEN_MAIN)}>
Back
</Button>
}>
<LabeledList>
<LabeledList.Item label="Receiving Channel">
<Button
fluid
onClick={() => act("set_channel_receiving")}>
{channel_name || "Unset"}
</Button>
</LabeledList.Item>
<LabeledList.Item label="Message Author" color="good">
{user}
</LabeledList.Item>
<LabeledList.Item label="Message Body" verticalAlign="top">
<Flex>
<Flex.Item grow={1}>
<Section width="99%" inline>
{msg || "(no message yet)"}
</Section>
</Flex.Item>
<Flex.Item>
<Button
verticalAlign="top"
onClick={() => act("set_new_message")}
icon="pen"
tooltip="Edit Message"
tooltipPosition="left" />
</Flex.Item>
</Flex>
</LabeledList.Item>
<LabeledList.Item label="Attach Photo">
<Button
fluid
icon="image"
onClick={() => act("set_attachment")}>
{photo_data ? "Photo Attached" : "No Photo"}
</Button>
</LabeledList.Item>
</LabeledList>
<Button
fluid
color="good"
icon="plus"
onClick={() => act("submit_new_message")}>
Submit Message
</Button>
<Button
fluid
color="bad"
icon="undo"
onClick={() => setScreen(NEWSCASTER_SCREEN_MAIN)}>
Cancel
</Button>
</Section>
);
};
const NewscasterPrint = (props, context) => {
const { act, data } = useBackend(context);
const {
total_num,
active_num,
message_num,
paper_remaining,
} = data;
const {
setScreen,
} = props;
return (
<Section title="Printing" buttons={
<Button
icon="undo"
onClick={() => setScreen(NEWSCASTER_SCREEN_MAIN)}>
Back
</Button>
}>
<Box color="label" mb={1}>
Newscaster currently serves a total of {total_num} Feed channels, {active_num} of which are active,
and a total of {message_num} Feed stories.
</Box>
<LabeledList>
<LabeledList.Item label="Liquid Paper remaining">
{paper_remaining * 100} cm&sup3;
</LabeledList.Item>
</LabeledList>
<Button
mt={1}
fluid
color="good"
icon="plus"
onClick={() => act("print_paper")}>
Print Paper
</Button>
<Button
fluid
color="bad"
icon="undo"
onClick={() => setScreen(NEWSCASTER_SCREEN_MAIN)}>
Cancel
</Button>
</Section>
);
};
const NewscasterNewWanted = (props, context) => {
const { act, data } = useBackend(context);
const {
channel_name,
msg,
photo_data,
user,
wanted_issue,
} = data;
const {
setScreen,
} = props;
return (
<Section title="Wanted Issue Handler" buttons={
<Button
icon="undo"
onClick={() => setScreen(NEWSCASTER_SCREEN_MAIN)}>
Back
</Button>
}>
<LabeledList>
{!!wanted_issue && (
<LabeledList.Item label="Already In Circulation">
A wanted issue is already in circulation. You can edit or cancel it below.
</LabeledList.Item>
)}
<LabeledList.Item label="Criminal Name">
<Input
fluid
value={decodeHtmlEntities(channel_name)}
onInput={(e, val) => act("set_channel_name", { val: val })} />
</LabeledList.Item>
<LabeledList.Item label="Description">
<Input
fluid
value={decodeHtmlEntities(msg)}
onInput={(e, val) => act("set_wanted_desc", { val: val })} />
</LabeledList.Item>
<LabeledList.Item label="Attach Photo">
<Button
fluid
icon="image"
onClick={() => act("set_attachment")}>
{photo_data ? "Photo Attached" : "No Photo"}
</Button>
</LabeledList.Item>
<LabeledList.Item label="Prosecutor" color="good">
{user}
</LabeledList.Item>
</LabeledList>
<Button
mt={1}
fluid
color="good"
icon="plus"
onClick={() => act("submit_wanted")}>
Submit Wanted Issue
</Button>
{!!wanted_issue && (
<Button
fluid
color="average"
icon="minus"
onClick={() => act("cancel_wanted")}>
Take Down Issue
</Button>
)}
<Button
fluid
color="bad"
icon="undo"
onClick={() => setScreen(NEWSCASTER_SCREEN_MAIN)}>
Cancel
</Button>
</Section>
);
};
const NewscasterViewWanted = (props, context) => {
const { act, data } = useBackend(context);
const {
wanted_issue,
} = data;
const {
setScreen,
} = props;
if (!wanted_issue) {
return (
<Section title="No Outstanding Wanted Issues" buttons={
<Button
icon="undo"
onClick={() => setScreen(NEWSCASTER_SCREEN_MAIN)}>
Back
</Button>
}>
There are no wanted issues currently outstanding.
</Section>
);
}
return (
<Section title="--STATIONWIDE WANTED ISSUE--" color="bad" buttons={
<Button
icon="undo"
onClick={() => setScreen(NEWSCASTER_SCREEN_MAIN)}>
Back
</Button>
}>
<Box color="white">
<LabeledList>
<LabeledList.Item label="Submitted by" color="good">
{decodeHtmlEntities(wanted_issue.author)}
</LabeledList.Item>
<LabeledList.Divider />
<LabeledList.Item label="Criminal">
{decodeHtmlEntities(wanted_issue.criminal)}
</LabeledList.Item>
<LabeledList.Item label="Description">
{decodeHtmlEntities(wanted_issue.desc)}
</LabeledList.Item>
<LabeledList.Item label="Photo">
{wanted_issue.img && <img src={wanted_issue.img} /> || "None"}
</LabeledList.Item>
</LabeledList>
</Box>
</Section>
);
};
const NewscasterViewSelected = (props, context) => {
const { act, data } = useBackend(context);
const {
viewing_channel,
securityCaster,
company,
} = data;
const {
setScreen,
} = props;
if (!viewing_channel) {
return (
<Section title="Channel Not Found" buttons={
<Button
icon="undo"
onClick={() => setScreen(NEWSCASTER_SCREEN_VIEWLIST)}>
Back
</Button>
}>
The channel you were looking for no longer exists.
</Section>
);
}
return (
<Section title={decodeHtmlEntities(viewing_channel.name)} buttons={
<Fragment>
{!!securityCaster && (
<Button.Confirm
color="bad"
icon="ban"
confirmIcon="ban"
content="Issue D-Notice"
onClick={() => act("toggle_d_notice", { ref: viewing_channel.ref })} />
)}
<Button
icon="undo"
onClick={() => setScreen(NEWSCASTER_SCREEN_VIEWLIST)}>
Back
</Button>
</Fragment>
}>
<LabeledList>
<LabeledList.Item label="Channel Created By">
{securityCaster && (
<Button.Confirm
color="bad"
icon="strikethrough"
confirmIcon="strikethrough"
content={decodeHtmlEntities(viewing_channel.author)}
tooltip="Censor?"
confirmContent="Censor Author"
onClick={() => act("censor_channel_author", { ref: viewing_channel.ref })} />
) || (
<Box>
{decodeHtmlEntities(viewing_channel.author)}
</Box>
)}
</LabeledList.Item>
</LabeledList>
{!!viewing_channel.censored && (
<Box color="bad">
ATTENTION: This channel has been deemed as threatening to the welfare of the station,
and marked with a {company} D-Notice. No further feed story additions are allowed
while the D-Notice is in effect.
</Box>
)}
{!!viewing_channel.messages.length && viewing_channel.messages.map(message => (
<Section key={message.ref}>
- {decodeHtmlEntities(message.body)}
{!!message.img && (
<Box>
<img src={"data:image/png;base64," + message.img} />
{decodeHtmlEntities(message.caption) || null}
</Box>
)}
<Box color="grey">
[Story by {decodeHtmlEntities(message.author)} - {message.timestamp}]
</Box>
{!!securityCaster && (
<Fragment>
<Button.Confirm
mt={1}
color="bad"
icon="strikethrough"
confirmIcon="strikethrough"
content="Censor Story"
onClick={() => act("censor_channel_story_body", { ref: message.ref })} />
<Button.Confirm
color="bad"
icon="strikethrough"
confirmIcon="strikethrough"
content="Censor Author"
onClick={() => act("censor_channel_story_author", { ref: message.ref })} />
</Fragment>
)}
</Section>
)) || !viewing_channel.censored && (
<Box color="average">
No feed messages found in channel.
</Box>
)}
</Section>
);
};
/* Must be at the bottom because of how the const lifting rules work */
let screenToTemplate = {};
screenToTemplate[NEWSCASTER_SCREEN_MAIN] = NewscasterMainMenu;
screenToTemplate[NEWSCASTER_SCREEN_NEWCHANNEL] = NewscasterNewChannel;
screenToTemplate[NEWSCASTER_SCREEN_VIEWLIST] = NewscasterViewList;
screenToTemplate[NEWSCASTER_SCREEN_NEWSTORY] = NewscasterNewStory;
screenToTemplate[NEWSCASTER_SCREEN_PRINT] = NewscasterPrint;
screenToTemplate[NEWSCASTER_SCREEN_NEWWANTED] = NewscasterNewWanted;
screenToTemplate[NEWSCASTER_SCREEN_VIEWWANTED] = NewscasterViewWanted;
screenToTemplate[NEWSCASTER_SCREEN_SELECTEDCHANNEL] = NewscasterViewSelected;

View File

@@ -0,0 +1,144 @@
import { toFixed } from 'common/math';
import { Fragment } from 'inferno';
import { useBackend } from "../backend";
import { Box, Button, Flex, Icon, LabeledList, ProgressBar, Section, NoticeBox } from "../components";
import { Window } from "../layouts";
import { RankIcon } from "./common/RankIcon";
export const TimeClock = (props, context) => {
const { act, data } = useBackend(context);
const {
department_hours,
user_name,
card,
assignment,
job_datum,
allow_change_job,
job_choices,
} = data;
return (
<Window width={500} height={520} resizable>
<Window.Content scrollable>
<Section title="OOC">
<NoticeBox>
OOC Note: PTO acquired is account-wide and shared across all characters.
Info listed below is not IC information.
</NoticeBox>
<Section level={2} title={"Time Off Balance for " + user_name}>
<LabeledList>
{Object.keys(department_hours).map(key => (
<LabeledList.Item
key={key}
label={key}
color={
department_hours[key] > 6
? "good"
: department_hours[key] > 1
? "average"
: "bad"
}>
{toFixed(department_hours[key], 1)} {department_hours[key] === 1 ? "hour" : "hours"}
</LabeledList.Item>
))}
</LabeledList>
</Section>
</Section>
<Section title="Employee Info">
<LabeledList>
<LabeledList.Item label="Employee ID">
<Button
fluid
icon="user"
onClick={() => act("id")}>
{card || "Insert ID"}
</Button>
</LabeledList.Item>
{!!job_datum && (
<Fragment>
<LabeledList.Item label="Rank">
<Box
backgroundColor={job_datum.selection_color}
p={0.8}>
<Flex justify="space-between" align="center">
<Flex.Item>
<Box ml={1}>
<RankIcon color="white" rank={job_datum.title} />
</Box>
</Flex.Item>
<Flex.Item>
<Box fontSize={1.5} inline mr={1}>
{job_datum.title}
</Box>
</Flex.Item>
</Flex>
</Box>
</LabeledList.Item>
<LabeledList.Item label="Departments">
{job_datum.departments}
</LabeledList.Item>
<LabeledList.Item label="Pay Scale">
{job_datum.economic_modifier}
</LabeledList.Item>
<LabeledList.Item label="PTO Elegibility">
{job_datum.timeoff_factor > 0 && (
<Box>
Earns PTO - {job_datum.pto_department}
</Box>
) || job_datum.timeoff_factor < 0 && (
<Box>
Requires PTO - {job_datum.pto_department}
</Box>
) || (
<Box>
Neutral
</Box>
)}
</LabeledList.Item>
</Fragment>
)}
</LabeledList>
</Section>
{!!(allow_change_job && job_datum && job_datum.timeoff_factor !== 0 && assignment !== "Dismissed") && (
<Section title="Employment Actions">
{job_datum.timeoff_factor > 0 && (
department_hours[job_datum.pto_department] > 0 && (
<Button
fluid
icon="exclamation-triangle"
onClick={() => act("switch-to-offduty")}>
Go Off-Duty
</Button>
) || (
<Box color="bad">
Warning: You do not have enough accrued time off to go off-duty.
</Box>
)
) || (
Object.keys(job_choices).length && Object.keys(job_choices).map(job => {
let alt_titles = job_choices[job];
return alt_titles.map(title => (
<Button
key={title}
icon="suitcase"
onClick={() => act("switch-to-onduty-rank", {
"switch-to-onduty-rank": job,
"switch-to-onduty-assignment": title,
})}>
{title}
</Button>
));
}) || (
<Box color="bad">
No Open Positions - See Head Of Personnel
</Box>
)
)}
</Section>
)}
</Window.Content>
</Window>
);
};

View File

@@ -4,9 +4,6 @@ import { useBackend } from '../backend';
import { Box, Button, Section, Table, LabeledList } from '../components';
import { Window } from '../layouts';
const VENDING_MODE_PRODUCTS = 0;
const VENDING_MODE_PURCHASESCREEN = 1;
const VendingRow = (props, context) => {
const { act, data } = useBackend(context);
const {
@@ -15,11 +12,21 @@ const VendingRow = (props, context) => {
const {
product,
} = props;
const free = (
product.price === 0
);
return (
<Table.Row>
<Table.Cell collapsing>
{product.isatom && (
<span
className={classes([
'vending32x32',
product.path,
])}
style={{
'vertical-align': 'middle',
'horizontal-align': 'middle',
}} />
) || null}
</Table.Cell>
<Table.Cell bold color={product.color}>
{product.name}
</Table.Cell>
@@ -51,25 +58,16 @@ const VendingRow = (props, context) => {
export const Vending = (props, context) => {
const { act, data } = useBackend(context);
const {
mode,
panel,
} = data;
let body = null;
if (mode === VENDING_MODE_PRODUCTS) {
body = <VendingProducts />;
} else if (mode === VENDING_MODE_PURCHASESCREEN) {
body = <VendingPurchaseScreen />;
}
return (
<Window
width={450}
height={600}
resizable>
<Window.Content scrollable>
{body}
<VendingProducts />
{panel ? (
<VendingMaintenance />
) : null}
@@ -82,6 +80,10 @@ export const VendingProducts = (props, context) => {
const { act, data } = useBackend(context);
const {
coin,
chargesMoney,
user,
userMoney,
guestNotice,
products,
} = data;
@@ -89,6 +91,23 @@ export const VendingProducts = (props, context) => {
let myproducts = products.filter(item => !!item);
return (
<Fragment>
{!!chargesMoney && (
<Section title="User">
{user && (
<Box>
Welcome, <b>{user.name}</b>,
{' '}
<b>{user.job || 'Unemployed'}</b>!
<br />
Your balance is <b>{userMoney} Thalers</b>.
</Box>
) || (
<Box color="light-grey">
{guestNotice}
</Box>
)}
</Section>
)}
<Section title="Products">
<Table>
{myproducts.map(product => (
@@ -112,37 +131,6 @@ export const VendingProducts = (props, context) => {
);
};
export const VendingPurchaseScreen = (props, context) => {
const { act, data } = useBackend(context);
const {
product,
price,
message,
message_err,
} = data;
return (
<Section title="Payment Confirmation" buttons={(
<Button
icon="undo"
content="Cancel"
onClick={() => act('cancelpurchase')} />
)}>
<LabeledList>
<LabeledList.Item label="Item selected">
{product}
</LabeledList.Item>
<LabeledList.Item label="Charge">
{price}
</LabeledList.Item>
</LabeledList>
<Section mt={1} label="Message" color={message_err ? 'bad' : ''}>
{message}
</Section>
</Section>
);
};
export const VendingMaintenance = (props, context) => {
const { act, data } = useBackend(context);
const {

View File

@@ -0,0 +1,119 @@
import { Icon } from "../../components";
const rank2icon = {
// Command
'Colony Director': 'user-tie',
'Site Manager': 'user-tie',
'Overseer': 'user-tie',
'Head of Personnel': 'briefcase',
'Crew Resources Officer': 'briefcase',
'Deputy Director': 'briefcase',
'Command Secretary': 'user-tie',
// Security
'Head of Security': 'user-shield',
'Security Commander': 'user-shield',
'Chief of Security': 'user-shield',
'Warden': ['city', 'shield-alt'],
'Detective': 'search',
'Forensic Technician': 'search',
'Security Officer': 'user-shield',
'Junior Officer': 'user-shield',
// Engineering
'Chief Engineer': 'toolbox',
'Atmospheric Technician': 'wind',
'Station Engineer': 'toolbox',
'Maintenance Technician': 'wrench',
'Engine Technician': 'toolbox',
'Electrician': 'toolbox',
// Medical
'Chief Medical Officer': 'user-md',
'Chemist': 'mortar-pestle',
'Pharmacist': 'mortar-pestle',
'Medical Doctor': 'user-md',
'Surgeon': 'user-md',
'Emergency Physician': 'user-md',
'Nurse': 'user-md',
'Virologist': 'disease',
'Paramedic': 'ambulance',
'Emergency Medical Technician': 'ambulance',
'Psychiatrist': 'couch',
'Psychologist': 'couch',
// Science
'Research Director': 'user-graduate',
'Research Supervisor': 'user-graduate',
'Roboticist': 'robot',
'Biomechanical Engineer': ['wrench', 'heartbeat'],
'Mechatronic Engineer': 'wrench',
'Scientist': 'flask',
'Xenoarchaeologist': 'flask',
'Anomalist': 'flask',
'Phoron Researcher': 'flask',
'Circuit Designer': 'car-battery',
'Xenobiologist': 'meteor',
'Xenobotanist': ['biohazard', 'seedling'],
// Cargo
'Quartermaster': 'box-open',
'Supply Chief': 'warehouse',
'Cargo Technician': 'box-open',
'Shaft Miner': 'hard-hat',
'Drill Technician': 'hard-hat',
// Exploration
'Pathfinder': 'binoculars',
'Explorer': 'user-astronaut',
'Field Medic': ['user-md', 'user-astronaut'],
'Pilot': 'space-shuttle',
// Civvies
'Bartender': 'glass-martini',
'Barista': 'coffee',
'Botanist': 'leaf',
'Gardener': 'leaf',
'Chaplain': 'place-of-worship',
'Counselor': 'couch',
'Chef': 'utensils',
'Cook': 'utensils',
'Entertainer': 'smile-beam',
'Performer': 'smile-beam',
'Musician': 'guitar',
'Stagehand': 'smile-beam',
// All of the interns
'Intern': 'school',
'Apprentice Engineer': ['school', 'wrench'],
'Medical Intern': ['school', 'user-md'],
'Lab Assistant': ['school', 'flask'],
'Security Cadet': ['school', 'shield-alt'],
'Jr. Cargo Tech': ['school', 'box'],
'Jr. Explorer': ['school', 'user-astronaut'],
'Server': ['school', 'utensils'],
// Back to civvies
'Internal Affairs Agent': 'balance-scale',
'Janitor': 'broom',
'Custodian': 'broom',
'Sanitation Technician': 'hand-sparkles',
'Maid': 'broom',
'Librarian': 'book',
'Journalist': 'newspaper',
'Writer': 'book',
'Historian': 'chalkboard-teacher',
'Professor': 'chalkboard-teacher',
'Visitor': 'user',
// Special roles
'Emergency Responder': 'fighter-jet',
};
export const RankIcon = (props, context) => {
const {
rank,
color = 'label',
} = props;
let rankObj = rank2icon[rank];
if (typeof rankObj === "string") {
return <Icon inline color={color} name={rankObj} size={2} />;
} else if (Array.isArray(rankObj)) {
return rankObj.map(icon => (
<Icon inline key={icon} color={color} name={icon} size={2} />
));
} else {
return <Icon inline color={color} name="user" size={2} />;
}
};

View File

@@ -1,3 +1,4 @@
import { decodeHtmlEntities } from 'common/string';
import { useBackend } from "../../backend";
import { Box, Button, NoticeBox } from "../../components";
@@ -15,6 +16,9 @@ import { Box, Button, NoticeBox } from "../../components";
* @param {object} context
*/
export const TemporaryNotice = (_properties, context) => {
const {
decode,
} = _properties;
const { act, data } = useBackend(context);
const { temp } = data;
if (!temp) {
@@ -24,7 +28,7 @@ export const TemporaryNotice = (_properties, context) => {
return (
<NoticeBox {...temporaryProperty}>
<Box display="inline-block" verticalAlign="middle">
{temp.text}
{decode ? decodeHtmlEntities(temp.text) : temp.text}
</Box>
<Button
icon="times-circle"

View File

@@ -102,6 +102,7 @@
#include "code\_global_vars\bitfields.dm"
#include "code\_global_vars\misc.dm"
#include "code\_global_vars\mobs.dm"
#include "code\_global_vars\religion.dm"
#include "code\_global_vars\sensitive.dm"
#include "code\_global_vars\lists\mapping.dm"
#include "code\_global_vars\lists\misc.dm"