mirror of
https://github.com/ParadiseSS13/Paradise.git
synced 2026-07-14 08:34:16 +01:00
[TGUI] Newscasters
This commit is contained in:
@@ -120,7 +120,7 @@ GLOBAL_LIST_EMPTY(announced_news_types)
|
||||
/proc/announce_newscaster_news(datum/news_announcement/news)
|
||||
|
||||
var/datum/feed_channel/sendto
|
||||
for(var/datum/feed_channel/FC in GLOB.news_network.network_channels)
|
||||
for(var/datum/feed_channel/FC in GLOB.news_network.channels)
|
||||
if(FC.channel_name == news.channel_name)
|
||||
sendto = FC
|
||||
break
|
||||
@@ -129,17 +129,16 @@ GLOBAL_LIST_EMPTY(announced_news_types)
|
||||
sendto = new /datum/feed_channel
|
||||
sendto.channel_name = news.channel_name
|
||||
sendto.author = news.author
|
||||
sendto.locked = 1
|
||||
sendto.is_admin_channel = 1
|
||||
GLOB.news_network.network_channels += sendto
|
||||
sendto.frozen = TRUE
|
||||
sendto.admin_locked = TRUE
|
||||
GLOB.news_network.channels += sendto
|
||||
|
||||
var/datum/feed_message/newMsg = new /datum/feed_message
|
||||
newMsg.author = news.author ? news.author : sendto.author
|
||||
newMsg.is_admin_message = !news.can_be_redacted
|
||||
newMsg.admin_locked = !news.can_be_redacted
|
||||
newMsg.body = news.message
|
||||
newMsg.message_type = news.message_type
|
||||
|
||||
sendto.messages += newMsg
|
||||
sendto.add_message(newMsg)
|
||||
|
||||
for(var/obj/machinery/newscaster/NEWSCASTER in GLOB.allNewscasters)
|
||||
NEWSCASTER.newsAlert(news.channel_name)
|
||||
NEWSCASTER.alert_news(news.channel_name)
|
||||
|
||||
@@ -1,855 +0,0 @@
|
||||
//##############################################
|
||||
//################### NEWSCASTERS BE HERE! ####
|
||||
//###-Agouri###################################
|
||||
|
||||
/datum/feed_message
|
||||
var/author = ""
|
||||
var/title = ""
|
||||
var/body = ""
|
||||
var/message_type = "Story"
|
||||
var/backup_body = ""
|
||||
var/backup_author = ""
|
||||
var/is_admin_message = 0
|
||||
var/icon/img = null
|
||||
var/icon/backup_img
|
||||
var/view_count = 0
|
||||
|
||||
/datum/feed_channel
|
||||
var/channel_name = ""
|
||||
var/list/datum/feed_message/messages = list()
|
||||
var/locked = 0
|
||||
var/author = ""
|
||||
var/backup_author = ""
|
||||
var/censored = 0
|
||||
var/is_admin_channel = 0
|
||||
var/total_view_count = 0
|
||||
|
||||
/datum/feed_message/proc/clear()
|
||||
author = ""
|
||||
body = ""
|
||||
backup_body = ""
|
||||
backup_author = ""
|
||||
img = null
|
||||
backup_img = null
|
||||
view_count = 0
|
||||
|
||||
/datum/feed_channel/proc/clear()
|
||||
channel_name = ""
|
||||
messages = list()
|
||||
locked = 0
|
||||
author = ""
|
||||
backup_author = ""
|
||||
censored = 0
|
||||
is_admin_channel = 0
|
||||
total_view_count = 0
|
||||
|
||||
/datum/feed_channel/proc/announce_news(title="")
|
||||
if(title)
|
||||
return "Breaking news from [channel_name]: [title]"
|
||||
return "Breaking news from [channel_name]"
|
||||
|
||||
/datum/feed_channel/station/announce_news()
|
||||
return "New Station Announcement Available"
|
||||
|
||||
/datum/feed_network
|
||||
var/list/datum/feed_channel/network_channels = list()
|
||||
var/datum/feed_message/wanted_issue
|
||||
|
||||
GLOBAL_DATUM_INIT(news_network, /datum/feed_network, new()) //The global news-network, which is coincidentally a global list.
|
||||
|
||||
GLOBAL_LIST_EMPTY(allNewscasters) //Global list that will contain reference to all newscasters in existence.
|
||||
|
||||
#define NEWSCASTER_MAIN 0 // Main menu
|
||||
#define NEWSCASTER_FC_LIST 1 // Feed channel list
|
||||
#define NEWSCASTER_CREATE_FC 2 // Create feed channel
|
||||
#define NEWSCASTER_CREATE_FM 3 // Create feed message
|
||||
#define NEWSCASTER_PRINT 4 // Print newspaper
|
||||
#define NEWSCASTER_VIEW_FC 5 // Read feed channel
|
||||
#define NEWSCASTER_NT_CENSOR 6 // Nanotrasen Feed Censorship Tool
|
||||
#define NEWSCASTER_D_NOTICE 7 // Nanotrasen D-Notice Handler
|
||||
#define NEWSCASTER_CENSOR_FC 8 // Censor feed channel
|
||||
#define NEWSCASTER_D_NOTICE_FC 9 // D-Notice feed channel
|
||||
#define NEWSCASTER_W_ISSUE_H 10 // Wanted Issue handler
|
||||
#define NEWSCASTER_W_ISSUE 11 // STATIONWIDE WANTED ISSUE
|
||||
#define NEWSCASTER_JOBS 12 // Available jobs
|
||||
|
||||
/obj/machinery/newscaster
|
||||
name = "newscaster"
|
||||
desc = "A standard Nanotrasen-licensed newsfeed handler for use in commercial space stations. All the news you absolutely have no use for, in one place!"
|
||||
icon = 'icons/obj/terminals.dmi'
|
||||
icon_state = "newscaster_normal"
|
||||
armor = list(melee = 50, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 0, rad = 0, fire = 50, acid = 30)
|
||||
max_integrity = 200
|
||||
integrity_failure = 50
|
||||
var/screen = NEWSCASTER_MAIN
|
||||
var/paper_remaining = 15
|
||||
var/securityCaster = 0
|
||||
// 0 = Caster cannot be used to issue wanted posters
|
||||
// 1 = the opposite
|
||||
var/unit_no = 0 //Each newscaster has a unit number
|
||||
var/alert_delay = 500
|
||||
var/alert = 0
|
||||
// 0 = there hasn't been a news/wanted update in the last alert_delay
|
||||
// 1 = there has
|
||||
var/scanned_user = "Unknown" //Will contain the name of the person who currently uses the newscaster
|
||||
var/msg = "" //Feed message
|
||||
var/msg_title = "" // Feed message title
|
||||
var/obj/item/photo/photo = null
|
||||
var/channel_name = "" //the feed channel which will be receiving the feed, or being created
|
||||
var/c_locked = 0 //Will our new channel be locked to public submissions?
|
||||
var/datum/feed_channel/viewing_channel = null
|
||||
var/silence = 0
|
||||
var/temp = null
|
||||
var/temp_back_screen = NEWSCASTER_MAIN
|
||||
var/list/jobblacklist = list(
|
||||
/datum/job/ai,
|
||||
/datum/job/cyborg,
|
||||
/datum/job/captain,
|
||||
/datum/job/judge,
|
||||
/datum/job/blueshield,
|
||||
/datum/job/nanotrasenrep,
|
||||
/datum/job/pilot,
|
||||
/datum/job/brigdoc,
|
||||
/datum/job/mechanic,
|
||||
/datum/job/barber,
|
||||
/datum/job/chaplain,
|
||||
/datum/job/ntnavyofficer,
|
||||
/datum/job/ntspecops,
|
||||
/datum/job/civilian,
|
||||
/datum/job/syndicateofficer)
|
||||
|
||||
var/static/REDACTED = "<b class='bad'>\[REDACTED\]</b>"
|
||||
light_range = 0
|
||||
anchored = 1
|
||||
|
||||
|
||||
/obj/machinery/newscaster/security_unit
|
||||
name = "Security Newscaster"
|
||||
securityCaster = 1
|
||||
|
||||
/obj/machinery/newscaster/New()
|
||||
GLOB.allNewscasters += src
|
||||
unit_no = GLOB.allNewscasters.len
|
||||
update_icon() //for any custom ones on the map...
|
||||
..()
|
||||
|
||||
/obj/machinery/newscaster/Destroy()
|
||||
GLOB.allNewscasters -= src
|
||||
viewing_channel = null
|
||||
QDEL_NULL(photo)
|
||||
return ..()
|
||||
|
||||
/obj/machinery/newscaster/update_icon()
|
||||
cut_overlays()
|
||||
if(inoperable())
|
||||
icon_state = "newscaster_off"
|
||||
else
|
||||
if(!GLOB.news_network.wanted_issue) //wanted icon state, there can be no overlays on it as it's a priority message
|
||||
icon_state = "newscaster_normal"
|
||||
if(alert) //new message alert overlay
|
||||
add_overlay("newscaster_alert")
|
||||
var/hp_percent = obj_integrity * 100 /max_integrity
|
||||
switch(hp_percent)
|
||||
if(75 to 100)
|
||||
return
|
||||
if(50 to 75)
|
||||
add_overlay("crack1")
|
||||
if(25 to 50)
|
||||
add_overlay("crack2")
|
||||
else
|
||||
add_overlay("crack3")
|
||||
|
||||
/obj/machinery/newscaster/power_change()
|
||||
..()
|
||||
update_icon()
|
||||
|
||||
/obj/machinery/newscaster/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1, attack_dir)
|
||||
. = ..()
|
||||
update_icon()
|
||||
|
||||
/obj/machinery/newscaster/attack_ghost(mob/user)
|
||||
ui_interact(user)
|
||||
|
||||
/obj/machinery/newscaster/attack_hand(mob/user)
|
||||
if(..())
|
||||
return
|
||||
ui_interact(user)
|
||||
|
||||
/obj/machinery/newscaster/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
|
||||
user.set_machine(src)
|
||||
if(can_scan(user))
|
||||
scan_user(user)
|
||||
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
|
||||
if(!ui)
|
||||
ui = new(user, src, ui_key, "newscaster.tmpl", name, 400, 600)
|
||||
ui.open()
|
||||
|
||||
/obj/machinery/newscaster/ui_data(user)
|
||||
var/list/data = list()
|
||||
|
||||
data["unit_no"] = unit_no
|
||||
data["temp"] = temp
|
||||
data["temp_back_screen"] = temp_back_screen
|
||||
data["screen"] = screen
|
||||
|
||||
switch(screen)
|
||||
if(0)
|
||||
data["wanted_issue"] = GLOB.news_network.wanted_issue ? 1 : 0
|
||||
data["silence"] = silence
|
||||
data["securityCaster"] = securityCaster
|
||||
if(securityCaster)
|
||||
data["scanned_user"] = scanned_user
|
||||
if(1, 6, 7)
|
||||
var/list/channels = list()
|
||||
data["channels"] = channels
|
||||
for(var/datum/feed_channel/C in GLOB.news_network.network_channels)
|
||||
channels[++channels.len] = list("name" = C.channel_name, "ref" = "\ref[C]", "censored" = C.censored, "admin" = C.is_admin_channel)
|
||||
if(2)
|
||||
data["scanned_user"] = scanned_user
|
||||
data["channel_name"] = channel_name
|
||||
data["c_locked"] = c_locked
|
||||
if(3)
|
||||
data["scanned_user"] = scanned_user
|
||||
data["channel_name"] = channel_name
|
||||
data["title"] = msg_title
|
||||
data["msg"] = msg
|
||||
data["photo"] = photo ? 1 : 0
|
||||
if(4)
|
||||
var/total_num = length(GLOB.news_network.network_channels)
|
||||
var/active_num = total_num
|
||||
var/message_num=0
|
||||
for(var/datum/feed_channel/FC in GLOB.news_network.network_channels)
|
||||
if(!FC.censored)
|
||||
message_num += length(FC.messages)
|
||||
else
|
||||
active_num--
|
||||
data["total_num"] = total_num
|
||||
data["active_num"] = active_num
|
||||
data["message_num"] = message_num
|
||||
data["paper_remaining"] = paper_remaining * 100
|
||||
if(5)
|
||||
data["channel_name"] = viewing_channel.channel_name
|
||||
data["author"] = viewing_channel.author
|
||||
data["total_view_count"] = viewing_channel.total_view_count
|
||||
data["censored"] = viewing_channel.censored
|
||||
var/list/messages = list()
|
||||
data["messages"] = messages
|
||||
var/message_number = 0
|
||||
for(var/datum/feed_message/M in viewing_channel.messages)
|
||||
if(M.img)
|
||||
user << browse_rsc(M.img, "tmp_photo[message_number].png")
|
||||
messages[++messages.len] = list("title" = M.title, "body" = M.body, "img" = M.img ? M.img : null, "message_type" = M.message_type, "author" = M.author, "view_count" = M.view_count, "message_number" = message_number)
|
||||
message_number += 1
|
||||
if(8, 9)
|
||||
data["channel_name"] = viewing_channel.channel_name
|
||||
data["ref"] = "\ref[viewing_channel]"
|
||||
data["author"] = viewing_channel.author
|
||||
data["author_redacted"] = viewing_channel.author == REDACTED ? 1 : 0
|
||||
data["total_view_count"] = viewing_channel.total_view_count
|
||||
data["censored"] = viewing_channel.censored
|
||||
var/list/messages = list()
|
||||
data["messages"] = messages
|
||||
for(var/datum/feed_message/M in viewing_channel.messages)
|
||||
messages[++messages.len] = list("title" = M.title, "body" = M.body, "body_redacted" = (M.body == REDACTED ? 1 : 0) , "message_type" = M.message_type, "author" = M.author, "author_redacted" = (M.author == REDACTED ? 1 : 0), "ref" = "\ref[M]", "view_count" = M.view_count)
|
||||
if(10)
|
||||
var/wanted_already = 0
|
||||
var/end_param = 1
|
||||
if(GLOB.news_network.wanted_issue)
|
||||
wanted_already = 1
|
||||
end_param = 2
|
||||
data["wanted_already"] = wanted_already
|
||||
data["end_param"] = end_param
|
||||
data["channel_name"] = channel_name
|
||||
data["msg"] = msg
|
||||
data["photo"] = photo ? 1 : 0
|
||||
if(wanted_already)
|
||||
data["author"] = GLOB.news_network.wanted_issue.backup_author
|
||||
else
|
||||
data["scanned_user"] = scanned_user
|
||||
if(11)
|
||||
data["author"] = GLOB.news_network.wanted_issue.backup_author
|
||||
data["criminal"] = GLOB.news_network.wanted_issue.author
|
||||
data["description"] = GLOB.news_network.wanted_issue.body
|
||||
if(GLOB.news_network.wanted_issue.img)
|
||||
user << browse_rsc(GLOB.news_network.wanted_issue.img, "tmp_photow.png")
|
||||
data["photo"] = GLOB.news_network.wanted_issue.img ? GLOB.news_network.wanted_issue.img : 0
|
||||
if(12)
|
||||
var/list/jobs = list()
|
||||
data["jobs"] = jobs
|
||||
for(var/datum/job/job in SSjobs.occupations)
|
||||
if(job_blacklisted(job))
|
||||
continue
|
||||
if(job.is_position_available())
|
||||
jobs[++jobs.len] = list("title" = job.title)
|
||||
return data
|
||||
|
||||
/obj/machinery/newscaster/Topic(href, href_list)
|
||||
if(..())
|
||||
return 1
|
||||
|
||||
if(href_list["set_channel_name"])
|
||||
channel_name = trim(sanitize(strip_html_simple(input(usr, "Provide a Feed Channel Name", "Network Channel Handler", ""))))
|
||||
|
||||
else if(href_list["set_channel_lock"])
|
||||
c_locked = !c_locked
|
||||
|
||||
else if(href_list["submit_new_channel"])
|
||||
var/list/existing_authors = list()
|
||||
for(var/datum/feed_channel/FC in GLOB.news_network.network_channels)
|
||||
if(FC.author == REDACTED)
|
||||
existing_authors += FC.backup_author
|
||||
else
|
||||
existing_authors += FC.author
|
||||
var/check = 0
|
||||
for(var/datum/feed_channel/FC in GLOB.news_network.network_channels)
|
||||
if(FC.channel_name == channel_name)
|
||||
check = 1
|
||||
break
|
||||
var/choice = alert("Please confirm feed channel creation", "Network Channel Handler", "Confirm", "Cancel")
|
||||
if(choice == "Confirm")
|
||||
if(channel_name == "" || channel_name == REDACTED || scanned_user == "Unknown" || check || (scanned_user in existing_authors))
|
||||
temp = "<b class='bad'>ERROR: Could not submit feed channel to Network.</b><ul class='bad'>"
|
||||
if(scanned_user in existing_authors)
|
||||
temp += "<li>There already exists a feed channel under your name.</li>"
|
||||
if(channel_name == "" || channel_name == REDACTED)
|
||||
temp += "<li>Invalid channel name.</li>"
|
||||
if(check)
|
||||
temp += "<li>Channel name already in use.</li>"
|
||||
if(scanned_user == "Unknown")
|
||||
temp += "<li>Channel author unverified.</li>"
|
||||
temp += "</ul>"
|
||||
temp_back_screen = NEWSCASTER_CREATE_FC
|
||||
else
|
||||
var/datum/feed_channel/newChannel = new /datum/feed_channel
|
||||
newChannel.channel_name = channel_name
|
||||
newChannel.author = scanned_user
|
||||
newChannel.locked = c_locked
|
||||
feedback_inc("newscaster_channels", 1)
|
||||
GLOB.news_network.network_channels += newChannel //Adding channel to the global network
|
||||
temp = "<span class='good'>Feed channel '[channel_name]' created successfully.</span>"
|
||||
temp_back_screen = NEWSCASTER_MAIN
|
||||
|
||||
else if(href_list["set_channel_receiving"])
|
||||
var/list/available_channels = list()
|
||||
for(var/datum/feed_channel/F in GLOB.news_network.network_channels)
|
||||
if((!F.locked || F.author == scanned_user) && !F.censored)
|
||||
available_channels += F.channel_name
|
||||
channel_name = strip_html_simple(input(usr, "Choose receiving Feed Channel", "Network Channel Handler") in available_channels)
|
||||
|
||||
else if(href_list["set_message_title"])
|
||||
msg_title = trim(strip_html(input(usr, "Write a title for your feed story", "Network Channel Handler", "")))
|
||||
msg_title = dd_limittext(msg_title, 256)
|
||||
|
||||
else if(href_list["set_new_message"])
|
||||
msg = trim(strip_html(input(usr, "Write your feed story", "Network Channel Handler", "")))
|
||||
|
||||
else if(href_list["set_attachment"])
|
||||
AttachPhoto(usr)
|
||||
|
||||
else if(href_list["submit_new_message"])
|
||||
if(msg == "" || msg == REDACTED || scanned_user == "Unknown" || channel_name == "" )
|
||||
temp = "<b class='bad'>ERROR: Could not submit feed story to Network.</b><ul class='bad'>"
|
||||
if(channel_name == "")
|
||||
temp += "<li>Invalid receiving channel name.</li>"
|
||||
if(scanned_user == "Unknown")
|
||||
temp += "<li>Channel author unverified.</li>"
|
||||
if(msg == "" || msg == REDACTED)
|
||||
temp += "<li>Invalid message body.</li>"
|
||||
temp += "</ul>"
|
||||
temp_back_screen = NEWSCASTER_CREATE_FM
|
||||
else
|
||||
var/datum/feed_message/newMsg = new /datum/feed_message
|
||||
newMsg.author = scanned_user
|
||||
newMsg.title = msg_title
|
||||
newMsg.body = msg
|
||||
if(photo)
|
||||
newMsg.img = photo.img
|
||||
feedback_inc("newscaster_stories",1)
|
||||
var/announcement = ""
|
||||
for(var/datum/feed_channel/FC in GLOB.news_network.network_channels)
|
||||
if(FC.channel_name == channel_name)
|
||||
FC.messages += newMsg //Adding message to the network's appropriate feed_channel
|
||||
announcement = FC.announce_news(msg_title)
|
||||
break
|
||||
temp = "<span class='good'>Feed story successfully submitted to [channel_name].</span>"
|
||||
temp_back_screen = NEWSCASTER_MAIN
|
||||
for(var/obj/machinery/newscaster/NC in GLOB.allNewscasters)
|
||||
NC.newsAlert(announcement)
|
||||
|
||||
else if(href_list["create_channel"])
|
||||
screen = NEWSCASTER_CREATE_FC
|
||||
|
||||
else if(href_list["create_feed_story"])
|
||||
screen = NEWSCASTER_CREATE_FM
|
||||
|
||||
else if(href_list["menu_paper"])
|
||||
screen = NEWSCASTER_PRINT
|
||||
|
||||
else if(href_list["print_paper"])
|
||||
if(!paper_remaining)
|
||||
temp = "<span class='bad'>Unable to print newspaper. Insufficient paper. Please notify maintenance personnell to refill machine storage.</span>"
|
||||
temp_back_screen = NEWSCASTER_MAIN
|
||||
else
|
||||
print_paper()
|
||||
temp = "<span class='good'>Printing successful. Please receive your newspaper from the bottom of the machine.</span>"
|
||||
temp_back_screen = NEWSCASTER_MAIN
|
||||
|
||||
else if(href_list["silence_unit"])
|
||||
silence = !silence
|
||||
|
||||
else if(href_list["menu_censor_story"])
|
||||
screen = NEWSCASTER_NT_CENSOR
|
||||
|
||||
else if(href_list["menu_censor_channel"])
|
||||
screen = NEWSCASTER_D_NOTICE
|
||||
|
||||
else if(href_list["menu_wanted"])
|
||||
var/already_wanted = 0
|
||||
if(GLOB.news_network.wanted_issue)
|
||||
already_wanted = 1
|
||||
|
||||
if(already_wanted)
|
||||
channel_name = GLOB.news_network.wanted_issue.author
|
||||
msg = GLOB.news_network.wanted_issue.body
|
||||
screen = NEWSCASTER_W_ISSUE_H
|
||||
|
||||
else if(href_list["set_wanted_name"])
|
||||
channel_name = trim(strip_html(input(usr, "Provide the name of the wanted person", "Network Security Handler", "")))
|
||||
|
||||
else if(href_list["set_wanted_desc"])
|
||||
msg = trim(strip_html(input(usr, "Provide the a description of the wanted person and any other details you deem important", "Network Security Handler", "")))
|
||||
|
||||
else if(href_list["submit_wanted"])
|
||||
var/input_param = text2num(href_list["submit_wanted"])
|
||||
var/choice = alert("Please confirm wanted issue [input_param == 1 ? "creation." : "edit."]", "Network Security Handler", "Confirm", "Cancel")
|
||||
if(choice == "Confirm")
|
||||
if(msg == "" || channel_name == "" || scanned_user == "Unknown")
|
||||
temp = "<b class='bad'>ERROR: Wanted issue rejected by Network.</b><ul class='bad'>"
|
||||
if(channel_name == "" || channel_name == REDACTED)
|
||||
temp += "<li>Invalid name for person wanted.</li>"
|
||||
if(scanned_user == "Unknown")
|
||||
temp += "<li>Channel author unverified.</li>"
|
||||
if(msg == "" || msg == REDACTED)
|
||||
temp += "<li>Invalid description.</li>"
|
||||
temp += "</ul>"
|
||||
temp_back_screen = NEWSCASTER_MAIN
|
||||
else
|
||||
if(input_param == 1) //input_param == 1: new wanted issue, input_param == 2: editing an existing one
|
||||
var/datum/feed_message/W = new /datum/feed_message
|
||||
W.author = channel_name
|
||||
W.body = msg
|
||||
W.backup_author = scanned_user //I know, a bit wacky
|
||||
if(photo)
|
||||
W.img = photo.img
|
||||
GLOB.news_network.wanted_issue = W
|
||||
for(var/obj/machinery/newscaster/NS in GLOB.allNewscasters)
|
||||
NS.newsAlert()
|
||||
NS.update_icon()
|
||||
temp = "<span class='good'>Wanted issue for [channel_name] is now in Network Circulation.</span>"
|
||||
temp_back_screen = NEWSCASTER_MAIN
|
||||
else
|
||||
if(GLOB.news_network.wanted_issue.is_admin_message)
|
||||
alert("The wanted issue has been distributed by a Nanotrasen higherup. You cannot edit it.","Ok")
|
||||
return
|
||||
GLOB.news_network.wanted_issue.author = channel_name
|
||||
GLOB.news_network.wanted_issue.body = msg
|
||||
GLOB.news_network.wanted_issue.backup_author = scanned_user
|
||||
if(photo)
|
||||
GLOB.news_network.wanted_issue.img = photo.img
|
||||
temp = "<span class='good'>Wanted issue for [channel_name] successfully edited.</span>"
|
||||
temp_back_screen = NEWSCASTER_MAIN
|
||||
|
||||
else if(href_list["cancel_wanted"])
|
||||
if(GLOB.news_network.wanted_issue.is_admin_message)
|
||||
alert("The wanted issue has been distributed by a Nanotrasen higherup. You cannot take it down.", "Ok")
|
||||
return
|
||||
var/choice = alert("Please confirm wanted issue removal", "Network Security Handler", "Confirm", "Cancel")
|
||||
if(choice == "Confirm")
|
||||
GLOB.news_network.wanted_issue = null
|
||||
for(var/obj/machinery/newscaster/NC in GLOB.allNewscasters)
|
||||
NC.update_icon()
|
||||
temp = "<b class='good'>Wanted Issue successfully deleted from Circulation</b>"
|
||||
temp_back_screen = NEWSCASTER_MAIN
|
||||
|
||||
else if(href_list["view_wanted"])
|
||||
screen = NEWSCASTER_W_ISSUE
|
||||
else if(href_list["censor_channel_author"])
|
||||
var/datum/feed_channel/FC = locate(href_list["censor_channel_author"])
|
||||
if(FC)
|
||||
if(FC.is_admin_channel)
|
||||
alert("This channel was created by a Nanotrasen Officer. You cannot censor it.","Ok")
|
||||
return
|
||||
if(FC.author != REDACTED)
|
||||
FC.backup_author = FC.author
|
||||
FC.author = REDACTED
|
||||
else
|
||||
FC.author = FC.backup_author
|
||||
|
||||
else if(href_list["censor_channel_story_author"])
|
||||
var/datum/feed_message/M = locate(href_list["censor_channel_story_author"])
|
||||
if(M)
|
||||
if(M.is_admin_message)
|
||||
alert("This message was created by a Nanotrasen Officer. You cannot censor its author.","Ok")
|
||||
return
|
||||
if(M.author != REDACTED)
|
||||
M.backup_author = M.author
|
||||
M.author = REDACTED
|
||||
else
|
||||
M.author = M.backup_author
|
||||
|
||||
else if(href_list["censor_channel_story_body"])
|
||||
var/datum/feed_message/M = locate(href_list["censor_channel_story_body"])
|
||||
if(M)
|
||||
if(M.is_admin_message)
|
||||
alert("This channel was created by a Nanotrasen Officer. You cannot censor it.","Ok")
|
||||
return
|
||||
if(M.img != null)
|
||||
M.backup_img = M.img
|
||||
M.img = null
|
||||
else
|
||||
M.img = M.backup_img
|
||||
if(M.body != REDACTED)
|
||||
M.backup_body = M.body
|
||||
M.body = REDACTED
|
||||
else
|
||||
M.body = M.backup_body
|
||||
|
||||
else if(href_list["pick_d_notice"])
|
||||
var/datum/feed_channel/FC = locate(href_list["pick_d_notice"])
|
||||
if(FC)
|
||||
viewing_channel = FC
|
||||
screen = NEWSCASTER_D_NOTICE_FC
|
||||
|
||||
else if(href_list["toggle_d_notice"])
|
||||
var/datum/feed_channel/FC = locate(href_list["toggle_d_notice"])
|
||||
if(FC)
|
||||
if(FC.is_admin_channel)
|
||||
alert("This channel was created by a Nanotrasen Officer. You cannot place a D-Notice upon it.", "Ok")
|
||||
return
|
||||
FC.censored = !FC.censored
|
||||
|
||||
else if(href_list["view"])
|
||||
screen = NEWSCASTER_FC_LIST
|
||||
|
||||
else if(href_list["setScreen"]) //Brings us to the main menu and resets all fields~
|
||||
screen = text2num(href_list["setScreen"])
|
||||
if(screen == NEWSCASTER_MAIN)
|
||||
scanned_user = "Unknown"
|
||||
msg = ""
|
||||
msg_title = ""
|
||||
c_locked = 0
|
||||
channel_name = ""
|
||||
viewing_channel = null
|
||||
temp = null
|
||||
temp_back_screen = NEWSCASTER_MAIN
|
||||
|
||||
else if(href_list["show_channel"])
|
||||
var/datum/feed_channel/FC = locate(href_list["show_channel"])
|
||||
if(FC)
|
||||
viewing_channel = FC
|
||||
viewing_channel.total_view_count++
|
||||
for(var/datum/feed_message/M in viewing_channel.messages)
|
||||
M.view_count++
|
||||
screen = NEWSCASTER_VIEW_FC
|
||||
|
||||
else if(href_list["pick_censor_channel"])
|
||||
var/datum/feed_channel/FC = locate(href_list["pick_censor_channel"])
|
||||
if(FC)
|
||||
viewing_channel = FC
|
||||
screen = NEWSCASTER_CENSOR_FC
|
||||
|
||||
else if(href_list["refresh"])
|
||||
if(can_scan(usr))
|
||||
scan_user(usr) //Newscaster scans you
|
||||
|
||||
else if(href_list["jobs"])
|
||||
screen = NEWSCASTER_JOBS
|
||||
|
||||
SSnanoui.update_uis(src)
|
||||
return 1
|
||||
|
||||
/obj/machinery/newscaster/wrench_act(mob/user, obj/item/I)
|
||||
. = TRUE
|
||||
if(!I.tool_use_check(user, 0))
|
||||
return
|
||||
to_chat(user, "<span class='notice'>Now [anchored ? "un" : ""]securing [name]</span>")
|
||||
if(!I.use_tool(src, user, 60, volume = I.tool_volume))
|
||||
return
|
||||
playsound(loc, 'sound/items/deconstruct.ogg', 50, TRUE)
|
||||
if(stat & BROKEN)
|
||||
to_chat(user, "<span class='warning'>The broken remains of [src] fall on the ground.</span>")
|
||||
new /obj/item/stack/sheet/metal(loc, 5)
|
||||
new /obj/item/shard(loc)
|
||||
new /obj/item/shard(loc)
|
||||
else
|
||||
to_chat(user, "<span class='notice'>You [anchored ? "un" : ""]secure [name].</span>")
|
||||
new /obj/item/mounted/frame/newscaster_frame(loc)
|
||||
qdel(src)
|
||||
|
||||
/obj/machinery/newscaster/welder_act(mob/user, obj/item/I)
|
||||
. = TRUE
|
||||
if(!I.tool_use_check(user, 0))
|
||||
return
|
||||
default_welder_repair(user, I)
|
||||
|
||||
/obj/machinery/newscaster/play_attack_sound(damage, damage_type = BRUTE, damage_flag = 0)
|
||||
switch(damage_type)
|
||||
if(BRUTE)
|
||||
if(stat & BROKEN)
|
||||
playsound(loc, 'sound/effects/hit_on_shattered_glass.ogg', 100, TRUE)
|
||||
else
|
||||
playsound(loc, 'sound/effects/glasshit.ogg', 90, TRUE)
|
||||
if(BURN)
|
||||
playsound(src.loc, 'sound/items/welder.ogg', 100, TRUE)
|
||||
|
||||
/obj/machinery/newscaster/deconstruct(disassembled = TRUE)
|
||||
if(!(flags & NODECONSTRUCT))
|
||||
new /obj/item/stack/sheet/metal(loc, 2)
|
||||
new /obj/item/shard(loc)
|
||||
new /obj/item/shard(loc)
|
||||
qdel(src)
|
||||
|
||||
/obj/machinery/newscaster/obj_break()
|
||||
if(!(stat & BROKEN) && !(flags & NODECONSTRUCT))
|
||||
stat |= BROKEN
|
||||
playsound(loc, 'sound/effects/glassbr3.ogg', 100, TRUE)
|
||||
update_icon()
|
||||
|
||||
/obj/machinery/newscaster/proc/AttachPhoto(mob/user)
|
||||
if(photo)
|
||||
if(!issilicon(user))
|
||||
photo.forceMove(get_turf(src))
|
||||
user.put_in_inactive_hand(photo)
|
||||
photo = null
|
||||
if(istype(user.get_active_hand(), /obj/item/photo))
|
||||
photo = user.get_active_hand()
|
||||
user.drop_item()
|
||||
photo.forceMove(src)
|
||||
else if(issilicon(user))
|
||||
var/mob/living/silicon/tempAI = user
|
||||
var/obj/item/camera/siliconcam/camera = tempAI.aiCamera
|
||||
|
||||
if(!camera)
|
||||
return
|
||||
var/datum/picture/selection = camera.selectpicture()
|
||||
if(!selection)
|
||||
return
|
||||
|
||||
var/obj/item/photo/P = new/obj/item/photo()
|
||||
P.construct(selection)
|
||||
photo = P
|
||||
|
||||
|
||||
//########################################################################################################################
|
||||
//###################################### NEWSPAPER! ######################################################################
|
||||
//########################################################################################################################
|
||||
|
||||
/obj/item/newspaper
|
||||
name = "newspaper"
|
||||
desc = "An issue of The Griffon, the newspaper circulating aboard Nanotrasen Space Stations."
|
||||
icon = 'icons/obj/bureaucracy.dmi'
|
||||
icon_state = "newspaper"
|
||||
w_class = WEIGHT_CLASS_SMALL //Let's make it fit in trashbags!
|
||||
attack_verb = list("bapped")
|
||||
var/screen = 0
|
||||
var/pages = 0
|
||||
var/curr_page = 0
|
||||
var/list/datum/feed_channel/news_content = list()
|
||||
var/datum/feed_message/important_message = null
|
||||
var/scribble=""
|
||||
var/scribble_page = null
|
||||
|
||||
/obj/item/newspaper/attack_self(mob/user as mob)
|
||||
if(ishuman(user))
|
||||
var/mob/living/carbon/human/human_user = user
|
||||
var/dat
|
||||
pages = 0
|
||||
switch(screen)
|
||||
if(0) //Cover
|
||||
dat+="<DIV ALIGN='center'><B><FONT SIZE=6>The Griffon</FONT></B></div>"
|
||||
dat+="<DIV ALIGN='center'><FONT SIZE=2>Nanotrasen-standard newspaper, for use on Nanotrasen Space Facilities</FONT></div><HR>"
|
||||
if(isemptylist(news_content))
|
||||
if(important_message)
|
||||
dat+="Contents:<BR><ul><B><FONT COLOR='red'>**</FONT>Important Security Announcement<FONT COLOR='red'>**</FONT></B> <FONT SIZE=2>\[page [pages+2]\]</FONT><BR></ul>"
|
||||
else
|
||||
dat+="<I>Other than the title, the rest of the newspaper is unprinted...</I>"
|
||||
else
|
||||
dat+="Contents:<BR><ul>"
|
||||
for(var/datum/feed_channel/NP in news_content)
|
||||
pages++
|
||||
if(important_message)
|
||||
dat+="<B><FONT COLOR='red'>**</FONT>Important Security Announcement<FONT COLOR='red'>**</FONT></B> <FONT SIZE=2>\[page [pages+2]\]</FONT><BR>"
|
||||
var/temp_page=0
|
||||
for(var/datum/feed_channel/NP in news_content)
|
||||
temp_page++
|
||||
dat+="<B>[NP.channel_name]</B> <FONT SIZE=2>\[page [temp_page+1]\]</FONT><BR>"
|
||||
dat+="</ul>"
|
||||
if(scribble_page==curr_page)
|
||||
dat+="<BR><I>There is a small scribble near the end of this page... It reads: \"[scribble]\"</I>"
|
||||
dat+= "<HR><DIV STYLE='float:right;'><A href='?src=[UID()];next_page=1'>Next Page</A></DIV> <div style='float:left;'><A href='?src=[human_user.UID()];mach_close=newspaper_main'>Done reading</A></DIV>"
|
||||
if(1) // X channel pages inbetween.
|
||||
for(var/datum/feed_channel/NP in news_content)
|
||||
pages++ //Let's get it right again.
|
||||
var/datum/feed_channel/C = news_content[curr_page]
|
||||
dat+="<FONT SIZE=4><B>[C.channel_name]</B></FONT><FONT SIZE=1> \[created by: <FONT COLOR='maroon'>[C.author]</FONT>\]</FONT><BR><BR>"
|
||||
if(C.censored)
|
||||
dat+="This channel was deemed dangerous to the general welfare of the station and therefore marked with a <B><FONT COLOR='red'>D-Notice</B></FONT>. Its contents were not transferred to the newspaper at the time of printing."
|
||||
else
|
||||
if(isemptylist(C.messages))
|
||||
dat+="No Feed stories stem from this channel..."
|
||||
else
|
||||
dat+="<ul>"
|
||||
var/i = 0
|
||||
for(var/datum/feed_message/MESSAGE in C.messages)
|
||||
i++
|
||||
dat+="<b>[MESSAGE.title]</b> <br>"
|
||||
dat+="[MESSAGE.body] <BR>"
|
||||
if(MESSAGE.img)
|
||||
user << browse_rsc(MESSAGE.img, "tmp_photo[i].png")
|
||||
dat+="<img src='tmp_photo[i].png' width = '180'><BR>"
|
||||
dat+="<FONT SIZE=1>\[Story by <FONT COLOR='maroon'>[MESSAGE.author]</FONT>\]</FONT><BR><BR>"
|
||||
dat+="</ul>"
|
||||
if(scribble_page==curr_page)
|
||||
dat+="<BR><I>There is a small scribble near the end of this page... It reads: \"[scribble]\"</I>"
|
||||
dat+= "<BR><HR><DIV STYLE='float:left;'><A href='?src=[UID()];prev_page=1'>Previous Page</A></DIV> <DIV STYLE='float:right;'><A href='?src=[UID()];next_page=1'>Next Page</A></DIV>"
|
||||
if(2) //Last page
|
||||
for(var/datum/feed_channel/NP in news_content)
|
||||
pages++
|
||||
if(important_message!=null)
|
||||
dat+="<DIV STYLE='float:center;'><FONT SIZE=4><B>Wanted Issue:</B></FONT SIZE></DIV><BR><BR>"
|
||||
dat+="<B>Criminal name</B>: <FONT COLOR='maroon'>[important_message.author]</FONT><BR>"
|
||||
dat+="<B>Description</B>: [important_message.body]<BR>"
|
||||
dat+="<B>Photo:</B>: "
|
||||
if(important_message.img)
|
||||
user << browse_rsc(important_message.img, "tmp_photow.png")
|
||||
dat+="<BR><img src='tmp_photow.png' width = '180'>"
|
||||
else
|
||||
dat+="None"
|
||||
else
|
||||
dat+="<I>Apart from some uninteresting Classified ads, there's nothing on this page...</I>"
|
||||
if(scribble_page==curr_page)
|
||||
dat+="<BR><I>There is a small scribble near the end of this page... It reads: \"[scribble]\"</I>"
|
||||
dat+= "<HR><DIV STYLE='float:left;'><A href='?src=[UID()];prev_page=1'>Previous Page</A></DIV>"
|
||||
else
|
||||
dat+="I'm sorry to break your immersion. This shit's bugged. Report this bug to Agouri, polyxenitopalidou@gmail.com"
|
||||
|
||||
dat+="<BR><HR><div align='center'>[curr_page+1]</div>"
|
||||
human_user << browse(dat, "window=newspaper_main;size=300x400")
|
||||
onclose(human_user, "newspaper_main")
|
||||
else
|
||||
to_chat(user, "The paper is full of intelligible symbols!")
|
||||
|
||||
|
||||
/obj/item/newspaper/Topic(href, href_list)
|
||||
var/mob/living/U = usr
|
||||
..()
|
||||
if((src in U.contents) || ( istype(loc, /turf) && in_range(src, U) ))
|
||||
U.set_machine(src)
|
||||
if(href_list["next_page"])
|
||||
if(curr_page==pages+1)
|
||||
return //Don't need that at all, but anyway.
|
||||
if(curr_page == pages) //We're at the middle, get to the end
|
||||
screen = 2
|
||||
else
|
||||
if(curr_page == 0) //We're at the start, get to the middle
|
||||
screen=1
|
||||
curr_page++
|
||||
playsound(loc, "pageturn", 50, 1)
|
||||
|
||||
else if(href_list["prev_page"])
|
||||
if(curr_page == 0)
|
||||
return
|
||||
if(curr_page == 1)
|
||||
screen = 0
|
||||
|
||||
else
|
||||
if(curr_page == pages+1) //we're at the end, let's go back to the middle.
|
||||
screen = 1
|
||||
curr_page--
|
||||
playsound(loc, "pageturn", 50, 1)
|
||||
|
||||
if(istype(loc, /mob))
|
||||
attack_self(loc)
|
||||
|
||||
|
||||
/obj/item/newspaper/attackby(obj/item/W as obj, mob/user as mob, params)
|
||||
if(istype(W, /obj/item/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>")
|
||||
else
|
||||
var/s = strip_html( input(user, "Write something", "Newspaper", "") )
|
||||
s = sanitize(copytext(s, 1, MAX_MESSAGE_LEN))
|
||||
if(!s)
|
||||
return
|
||||
if(!in_range(src, usr) && loc != usr)
|
||||
return
|
||||
scribble_page = curr_page
|
||||
scribble = s
|
||||
attack_self(user)
|
||||
return
|
||||
return ..()
|
||||
|
||||
|
||||
////////////////////////////////////helper procs
|
||||
|
||||
/obj/machinery/newscaster/proc/job_blacklisted(datum/job/job)
|
||||
return (job.type in jobblacklist)
|
||||
|
||||
/obj/machinery/newscaster/proc/scan_user(mob/user)
|
||||
if(ishuman(user)) //User is a human
|
||||
var/mob/living/carbon/human/human_user = user
|
||||
if(human_user.wear_id) //Newscaster scans you
|
||||
if(istype(human_user.wear_id, /obj/item/pda)) //autorecognition, woo!
|
||||
var/obj/item/pda/P = human_user.wear_id
|
||||
if(P.id)
|
||||
scanned_user = "[P.id.registered_name] ([P.id.assignment])"
|
||||
else
|
||||
scanned_user = "Unknown"
|
||||
else if(istype(human_user.wear_id, /obj/item/card/id))
|
||||
var/obj/item/card/id/ID = human_user.wear_id
|
||||
scanned_user = "[ID.registered_name] ([ID.assignment])"
|
||||
else
|
||||
scanned_user = "Unknown"
|
||||
else
|
||||
scanned_user = "Unknown"
|
||||
else if(issilicon(user))
|
||||
var/mob/living/silicon/ai_user = user
|
||||
scanned_user = "[ai_user.name] ([ai_user.job])"
|
||||
else
|
||||
scanned_user = "Unknown"
|
||||
|
||||
/obj/machinery/newscaster/proc/can_scan(mob/user)
|
||||
if(ishuman(user) || issilicon(user))
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/obj/machinery/newscaster/proc/print_paper()
|
||||
feedback_inc("newscaster_newspapers_printed",1)
|
||||
var/obj/item/newspaper/NEWSPAPER = new /obj/item/newspaper
|
||||
for(var/datum/feed_channel/FC in GLOB.news_network.network_channels)
|
||||
NEWSPAPER.news_content += FC
|
||||
if(GLOB.news_network.wanted_issue)
|
||||
NEWSPAPER.important_message = GLOB.news_network.wanted_issue
|
||||
NEWSPAPER.loc = get_turf(src)
|
||||
paper_remaining--
|
||||
return
|
||||
|
||||
//Removed for now so these aren't even checked every tick. Left this here in-case Agouri needs it later.
|
||||
///obj/machinery/newscaster/process() //Was thinking of doing the icon update through process, but multiple iterations per second does not
|
||||
// return //bode well with a newscaster network of 10+ machines. Let's just return it, as it's added in the machines list.
|
||||
|
||||
/obj/machinery/newscaster/proc/newsAlert(var/news_call) //This isn't Agouri's work, for it is ugly and vile.
|
||||
if(news_call)
|
||||
|
||||
atom_say("[news_call]!")
|
||||
alert = 1
|
||||
update_icon()
|
||||
spawn(300)
|
||||
alert = 0
|
||||
update_icon()
|
||||
if(!silence)
|
||||
playsound(loc, 'sound/machines/twobeep.ogg', 75, 1)
|
||||
else
|
||||
atom_say("Attention! Wanted issue distributed!")
|
||||
playsound(loc, 'sound/machines/warning-buzzer.ogg', 75, 1)
|
||||
return
|
||||
@@ -291,251 +291,6 @@ GLOBAL_VAR_INIT(nologevent, 0)
|
||||
if(key)
|
||||
vpn_whitelist_panel(key)
|
||||
|
||||
/datum/admins/proc/access_news_network() //MARKER
|
||||
set category = "Event"
|
||||
set name = "Access Newscaster Network"
|
||||
set desc = "Allows you to view, add and edit news feeds."
|
||||
|
||||
if(!check_rights(R_EVENT))
|
||||
return
|
||||
|
||||
if(!istype(src,/datum/admins))
|
||||
src = usr.client.holder
|
||||
|
||||
var/dat
|
||||
dat = text("<HEAD><TITLE>Admin Newscaster</TITLE></HEAD><H3>Admin Newscaster Unit</H3>")
|
||||
|
||||
switch(admincaster_screen)
|
||||
if(0)
|
||||
dat += {"Welcome to the admin newscaster.<BR> Here you can add, edit and censor every newspiece on the network.
|
||||
<BR>Feed channels and stories entered through here will be uneditable and handled as official news by the rest of the units.
|
||||
<BR>Note that this panel allows full freedom over the news network, there are no constrictions except the few basic ones. Don't break things!</FONT>
|
||||
"}
|
||||
if(GLOB.news_network.wanted_issue)
|
||||
dat+= "<HR><A href='?src=[UID()];ac_view_wanted=1'>Read Wanted Issue</A>"
|
||||
|
||||
dat+= {"<HR><BR><A href='?src=[UID()];ac_create_channel=1'>Create Feed Channel</A>
|
||||
<BR><A href='?src=[UID()];ac_view=1'>View Feed Channels</A>
|
||||
<BR><A href='?src=[UID()];ac_create_feed_story=1'>Submit new Feed story</A>
|
||||
<BR><BR><A href='?src=[usr.UID()];mach_close=newscaster_main'>Exit</A>
|
||||
"}
|
||||
|
||||
var/wanted_already = 0
|
||||
if(GLOB.news_network.wanted_issue)
|
||||
wanted_already = 1
|
||||
|
||||
dat+={"<HR><B>Feed Security functions:</B><BR>
|
||||
<BR><A href='?src=[UID()];ac_menu_wanted=1'>[(wanted_already) ? ("Manage") : ("Publish")] \"Wanted\" Issue</A>
|
||||
<BR><A href='?src=[UID()];ac_menu_censor_story=1'>Censor Feed Stories</A>
|
||||
<BR><A href='?src=[UID()];ac_menu_censor_channel=1'>Mark Feed Channel with Nanotrasen D-Notice (disables and locks the channel.</A>
|
||||
<BR><HR><A href='?src=[UID()];ac_set_signature=1'>The newscaster recognises you as:<BR> <FONT COLOR='green'>[src.admincaster_signature]</FONT></A>
|
||||
"}
|
||||
if(1)
|
||||
dat+= "Station Feed Channels<HR>"
|
||||
if( isemptylist(GLOB.news_network.network_channels) )
|
||||
dat+="<I>No active channels found...</I>"
|
||||
else
|
||||
for(var/datum/feed_channel/CHANNEL in GLOB.news_network.network_channels)
|
||||
if(CHANNEL.is_admin_channel)
|
||||
dat+="<B><FONT style='BACKGROUND-COLOR: LightGreen'><A href='?src=[UID()];ac_show_channel=\ref[CHANNEL]'>[CHANNEL.channel_name]</A></FONT></B><BR>"
|
||||
else
|
||||
dat+="<B><A href='?src=[UID()];ac_show_channel=\ref[CHANNEL]'>[CHANNEL.channel_name]</A> [(CHANNEL.censored) ? ("<FONT COLOR='red'>***</FONT>") : ""]<BR></B>"
|
||||
dat+={"<BR><HR><A href='?src=[UID()];ac_refresh=1'>Refresh</A>
|
||||
<BR><A href='?src=[UID()];ac_setScreen=[0]'>Back</A>
|
||||
"}
|
||||
|
||||
if(2)
|
||||
dat+={"
|
||||
Creating new Feed Channel...
|
||||
<HR><B><A href='?src=[UID()];ac_set_channel_name=1'>Channel Name</A>:</B> [src.admincaster_feed_channel.channel_name]<BR>
|
||||
<B><A href='?src=[UID()];ac_set_signature=1'>Channel Author</A>:</B> <FONT COLOR='green'>[src.admincaster_signature]</FONT><BR>
|
||||
<B><A href='?src=[UID()];ac_set_channel_lock=1'>Will Accept Public Feeds</A>:</B> [(src.admincaster_feed_channel.locked) ? ("NO") : ("YES")]<BR><BR>
|
||||
<BR><A href='?src=[UID()];ac_submit_new_channel=1'>Submit</A><BR><BR><A href='?src=[UID()];ac_setScreen=[0]'>Cancel</A><BR>
|
||||
"}
|
||||
if(3)
|
||||
dat+={"
|
||||
Creating new Feed Message...
|
||||
<HR><B><A href='?src=[UID()];ac_set_channel_receiving=1'>Receiving Channel</A>:</B> [src.admincaster_feed_channel.channel_name]<BR>
|
||||
<B>Message Author:</B> <FONT COLOR='green'>[src.admincaster_signature]</FONT><BR>
|
||||
<B><A href='?src=[UID()];ac_set_new_message=1'>Message Body</A>:</B> [src.admincaster_feed_message.body] <BR>
|
||||
<BR><A href='?src=[UID()];ac_submit_new_message=1'>Submit</A><BR><BR><A href='?src=[UID()];ac_setScreen=[0]'>Cancel</A><BR>
|
||||
"}
|
||||
if(4)
|
||||
dat+={"
|
||||
Feed story successfully submitted to [src.admincaster_feed_channel.channel_name].<BR><BR>
|
||||
<BR><A href='?src=[UID()];ac_setScreen=[0]'>Return</A><BR>
|
||||
"}
|
||||
if(5)
|
||||
dat+={"
|
||||
Feed Channel [src.admincaster_feed_channel.channel_name] created successfully.<BR><BR>
|
||||
<BR><A href='?src=[UID()];ac_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(src.admincaster_feed_channel.channel_name=="")
|
||||
dat+="<FONT COLOR='maroon'>Invalid receiving channel name.</FONT><BR>"
|
||||
if(src.admincaster_feed_message.body == "" || src.admincaster_feed_message.body == "\[REDACTED\]")
|
||||
dat+="<FONT COLOR='maroon'>Invalid message body.</FONT><BR>"
|
||||
dat+="<BR><A href='?src=[UID()];ac_setScreen=[3]'>Return</A><BR>"
|
||||
if(7)
|
||||
dat+="<B><FONT COLOR='maroon'>ERROR: Could not submit Feed Channel to Network.</B></FONT><HR><BR>"
|
||||
if(src.admincaster_feed_channel.channel_name =="" || src.admincaster_feed_channel.channel_name == "\[REDACTED\]")
|
||||
dat+="<FONT COLOR='maroon'>Invalid channel name.</FONT><BR>"
|
||||
var/check = 0
|
||||
for(var/datum/feed_channel/FC in GLOB.news_network.network_channels)
|
||||
if(FC.channel_name == src.admincaster_feed_channel.channel_name)
|
||||
check = 1
|
||||
break
|
||||
if(check)
|
||||
dat+="<FONT COLOR='maroon'>Channel name already in use.</FONT><BR>"
|
||||
dat+="<BR><A href='?src=[UID()];ac_setScreen=[2]'>Return</A><BR>"
|
||||
if(9)
|
||||
dat+="<B>[src.admincaster_feed_channel.channel_name]: </B><FONT SIZE=1>\[created by: <FONT COLOR='maroon'>[src.admincaster_feed_channel.author]</FONT>\]</FONT><HR>"
|
||||
if(src.admincaster_feed_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 Nanotrasen D-Notice.<BR>
|
||||
No further feed story additions are allowed while the D-Notice is in effect.</FONT><BR><BR>
|
||||
"}
|
||||
else
|
||||
if( isemptylist(src.admincaster_feed_channel.messages) )
|
||||
dat+="<I>No feed messages found in channel...</I><BR>"
|
||||
else
|
||||
var/i = 0
|
||||
for(var/datum/feed_message/MESSAGE in src.admincaster_feed_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><BR>"
|
||||
dat+="<FONT SIZE=1>\[Story by <FONT COLOR='maroon'>[MESSAGE.author]</FONT>\]</FONT><BR>"
|
||||
dat+={"
|
||||
<BR><HR><A href='?src=[UID()];ac_refresh=1'>Refresh</A>
|
||||
<BR><A href='?src=[UID()];ac_setScreen=[1]'>Back</A>
|
||||
"}
|
||||
if(10)
|
||||
dat+={"
|
||||
<B>Nanotrasen Feed Censorship Tool</B><BR>
|
||||
<FONT SIZE=1>NOTE: Due to the nature of news Feeds, total deletion of a Feed Story is not possible.<BR>
|
||||
Keep in mind that users attempting to view a censored feed will instead see the \[REDACTED\] tag above it.</FONT>
|
||||
<HR>Select Feed channel to get Stories from:<BR>
|
||||
"}
|
||||
if(isemptylist(GLOB.news_network.network_channels))
|
||||
dat+="<I>No feed channels found active...</I><BR>"
|
||||
else
|
||||
for(var/datum/feed_channel/CHANNEL in GLOB.news_network.network_channels)
|
||||
dat+="<A href='?src=[UID()];ac_pick_censor_channel=\ref[CHANNEL]'>[CHANNEL.channel_name]</A> [(CHANNEL.censored) ? ("<FONT COLOR='red'>***</FONT>") : ""]<BR>"
|
||||
dat+="<BR><A href='?src=[UID()];ac_setScreen=[0]'>Cancel</A>"
|
||||
if(11)
|
||||
dat+={"
|
||||
<B>Nanotrasen D-Notice Handler</B><HR>
|
||||
<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
|
||||
morale, integrity or disciplinary behaviour. A D-Notice will render a channel unable to be updated by anyone, without deleting any feed
|
||||
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(GLOB.news_network.network_channels))
|
||||
dat+="<I>No feed channels found active...</I><BR>"
|
||||
else
|
||||
for(var/datum/feed_channel/CHANNEL in GLOB.news_network.network_channels)
|
||||
dat+="<A href='?src=[UID()];ac_pick_d_notice=\ref[CHANNEL]'>[CHANNEL.channel_name]</A> [(CHANNEL.censored) ? ("<FONT COLOR='red'>***</FONT>") : ""]<BR>"
|
||||
|
||||
dat+="<BR><A href='?src=[UID()];ac_setScreen=[0]'>Back</A>"
|
||||
if(12)
|
||||
dat+={"
|
||||
<B>[src.admincaster_feed_channel.channel_name]: </B><FONT SIZE=1>\[ created by: <FONT COLOR='maroon'>[src.admincaster_feed_channel.author]</FONT> \]</FONT><BR>
|
||||
<FONT SIZE=2><A href='?src=[UID()];ac_censor_channel_author=\ref[src.admincaster_feed_channel]'>[(src.admincaster_feed_channel.author=="\[REDACTED\]") ? ("Undo Author censorship") : ("Censor channel Author")]</A></FONT><HR>
|
||||
"}
|
||||
if( isemptylist(src.admincaster_feed_channel.messages) )
|
||||
dat+="<I>No feed messages found in channel...</I><BR>"
|
||||
else
|
||||
for(var/datum/feed_message/MESSAGE in src.admincaster_feed_channel.messages)
|
||||
dat+={"
|
||||
-[MESSAGE.body] <BR><FONT SIZE=1>\[Story by <FONT COLOR='maroon'>[MESSAGE.author]</FONT>\]</FONT><BR>
|
||||
<FONT SIZE=2><A href='?src=[UID()];ac_censor_channel_story_body=\ref[MESSAGE]'>[(MESSAGE.body == "\[REDACTED\]") ? ("Undo story censorship") : ("Censor story")]</A> - <A href='?src=[UID()];ac_censor_channel_story_author=\ref[MESSAGE]'>[(MESSAGE.author == "\[REDACTED\]") ? ("Undo Author Censorship") : ("Censor message Author")]</A></FONT><BR>
|
||||
"}
|
||||
dat+="<BR><A href='?src=[UID()];ac_setScreen=[10]'>Back</A>"
|
||||
if(13)
|
||||
dat+={"
|
||||
<B>[src.admincaster_feed_channel.channel_name]: </B><FONT SIZE=1>\[ created by: <FONT COLOR='maroon'>[src.admincaster_feed_channel.author]</FONT> \]</FONT><BR>
|
||||
Channel messages listed below. If you deem them dangerous to the station, you can <A href='?src=[UID()];ac_toggle_d_notice=\ref[src.admincaster_feed_channel]'>Bestow a D-Notice upon the channel</A>.<HR>
|
||||
"}
|
||||
if(src.admincaster_feed_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 Nanotrasen D-Notice.<BR>
|
||||
No further feed story additions are allowed while the D-Notice is in effect.</FONT><BR><BR>
|
||||
"}
|
||||
else
|
||||
if( isemptylist(src.admincaster_feed_channel.messages) )
|
||||
dat+="<I>No feed messages found in channel...</I><BR>"
|
||||
else
|
||||
for(var/datum/feed_message/MESSAGE in src.admincaster_feed_channel.messages)
|
||||
dat+="-[MESSAGE.body] <BR><FONT SIZE=1>\[Story by <FONT COLOR='maroon'>[MESSAGE.author]</FONT>\]</FONT><BR>"
|
||||
|
||||
dat+="<BR><A href='?src=[UID()];ac_setScreen=[11]'>Back</A>"
|
||||
if(14)
|
||||
dat+="<B>Wanted Issue Handler:</B>"
|
||||
var/wanted_already = 0
|
||||
var/end_param = 1
|
||||
if(GLOB.news_network.wanted_issue)
|
||||
wanted_already = 1
|
||||
end_param = 2
|
||||
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>
|
||||
<A href='?src=[UID()];ac_set_wanted_name=1'>Criminal Name</A>: [src.admincaster_feed_message.author] <BR>
|
||||
<A href='?src=[UID()];ac_set_wanted_desc=1'>Description</A>: [src.admincaster_feed_message.body] <BR>
|
||||
"}
|
||||
if(wanted_already)
|
||||
dat+="<B>Wanted Issue created by:</B><FONT COLOR='green'> [GLOB.news_network.wanted_issue.backup_author]</FONT><BR>"
|
||||
else
|
||||
dat+="<B>Wanted Issue will be created under prosecutor:</B><FONT COLOR='green'> [src.admincaster_signature]</FONT><BR>"
|
||||
dat+="<BR><A href='?src=[UID()];ac_submit_wanted=[end_param]'>[(wanted_already) ? ("Edit Issue") : ("Submit")]</A>"
|
||||
if(wanted_already)
|
||||
dat+="<BR><A href='?src=[UID()];ac_cancel_wanted=1'>Take down Issue</A>"
|
||||
dat+="<BR><A href='?src=[UID()];ac_setScreen=[0]'>Cancel</A>"
|
||||
if(15)
|
||||
dat+={"
|
||||
<FONT COLOR='green'>Wanted issue for [src.admincaster_feed_message.author] is now in Network Circulation.</FONT><BR><BR>
|
||||
<BR><A href='?src=[UID()];ac_setScreen=[0]'>Return</A><BR>
|
||||
"}
|
||||
if(16)
|
||||
dat+="<B><FONT COLOR='maroon'>ERROR: Wanted Issue rejected by Network.</B></FONT><HR><BR>"
|
||||
if(src.admincaster_feed_message.author =="" || src.admincaster_feed_message.author == "\[REDACTED\]")
|
||||
dat+="<FONT COLOR='maroon'>Invalid name for person wanted.</FONT><BR>"
|
||||
if(src.admincaster_feed_message.body == "" || src.admincaster_feed_message.body == "\[REDACTED\]")
|
||||
dat+="<FONT COLOR='maroon'>Invalid description.</FONT><BR>"
|
||||
dat+="<BR><A href='?src=[UID()];ac_setScreen=[0]'>Return</A><BR>"
|
||||
if(17)
|
||||
dat+={"
|
||||
<B>Wanted Issue successfully deleted from Circulation</B><BR>
|
||||
<BR><A href='?src=[UID()];ac_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'>[GLOB.news_network.wanted_issue.backup_author]</FONT>\]</FONT><HR>
|
||||
<B>Criminal</B>: [GLOB.news_network.wanted_issue.author]<BR>
|
||||
<B>Description</B>: [GLOB.news_network.wanted_issue.body]<BR>
|
||||
<B>Photo:</B>:
|
||||
"}
|
||||
if(GLOB.news_network.wanted_issue.img)
|
||||
usr << browse_rsc(GLOB.news_network.wanted_issue.img, "tmp_photow.png")
|
||||
dat+="<BR><img src='tmp_photow.png' width = '180'>"
|
||||
else
|
||||
dat+="None"
|
||||
dat+="<BR><A href='?src=[UID()];ac_setScreen=[0]'>Back</A><BR>"
|
||||
if(19)
|
||||
dat+={"
|
||||
<FONT COLOR='green'>Wanted issue for [src.admincaster_feed_message.author] successfully edited.</FONT><BR><BR>
|
||||
<BR><A href='?src=[UID()];ac_setScreen=[0]'>Return</A><BR>
|
||||
"}
|
||||
else
|
||||
dat+="I'm sorry to break your immersion. This shit's bugged. Report this bug to Agouri, polyxenitopalidou@gmail.com"
|
||||
|
||||
// to_chat(world, "Channelname: [src.admincaster_feed_channel.channel_name] [src.admincaster_feed_channel.author]")
|
||||
// to_chat(world, "Msg: [src.admincaster_feed_message.author] [src.admincaster_feed_message.body]")
|
||||
usr << browse(dat, "window=admincaster_main;size=400x600")
|
||||
onclose(usr, "admincaster_main")
|
||||
|
||||
/datum/admins/proc/Jobbans()
|
||||
if(!check_rights(R_BAN))
|
||||
return
|
||||
|
||||
@@ -98,7 +98,6 @@ GLOBAL_LIST_INIT(admin_verbs_event, list(
|
||||
/client/proc/toggle_ert_calling,
|
||||
/client/proc/show_tip,
|
||||
/client/proc/cmd_admin_change_custom_event,
|
||||
/datum/admins/proc/access_news_network, /*allows access of newscasters*/
|
||||
/client/proc/cmd_admin_subtle_message, /*send an message to somebody as a 'voice in their head'*/
|
||||
/client/proc/cmd_admin_direct_narrate, /*send text directly to a player with no padding. Useful for narratives and fluff-text*/
|
||||
/client/proc/cmd_admin_world_narrate, /*sends text to all players with no padding*/
|
||||
|
||||
@@ -3214,220 +3214,6 @@
|
||||
J.spawn_positions = -1
|
||||
message_admins("[key_name_admin(usr)] has removed the cap on security officers.")
|
||||
|
||||
else if(href_list["ac_view_wanted"]) //Admin newscaster Topic() stuff be here
|
||||
src.admincaster_screen = 18 //The ac_ prefix before the hrefs stands for AdminCaster.
|
||||
src.access_news_network()
|
||||
|
||||
else if(href_list["ac_set_channel_name"])
|
||||
src.admincaster_feed_channel.channel_name = strip_html_simple(input(usr, "Provide a Feed Channel Name", "Network Channel Handler", ""))
|
||||
while(findtext(src.admincaster_feed_channel.channel_name," ") == 1)
|
||||
src.admincaster_feed_channel.channel_name = copytext(src.admincaster_feed_channel.channel_name,2,length(src.admincaster_feed_channel.channel_name)+1)
|
||||
src.access_news_network()
|
||||
|
||||
else if(href_list["ac_set_channel_lock"])
|
||||
src.admincaster_feed_channel.locked = !src.admincaster_feed_channel.locked
|
||||
src.access_news_network()
|
||||
|
||||
else if(href_list["ac_submit_new_channel"])
|
||||
var/check = 0
|
||||
for(var/datum/feed_channel/FC in GLOB.news_network.network_channels)
|
||||
if(FC.channel_name == src.admincaster_feed_channel.channel_name)
|
||||
check = 1
|
||||
break
|
||||
if(src.admincaster_feed_channel.channel_name == "" || src.admincaster_feed_channel.channel_name == "\[REDACTED\]" || check )
|
||||
src.admincaster_screen=7
|
||||
else
|
||||
var/choice = alert("Please confirm Feed channel creation","Network Channel Handler","Confirm","Cancel")
|
||||
if(choice=="Confirm")
|
||||
var/datum/feed_channel/newChannel = new /datum/feed_channel
|
||||
newChannel.channel_name = src.admincaster_feed_channel.channel_name
|
||||
newChannel.author = src.admincaster_signature
|
||||
newChannel.locked = src.admincaster_feed_channel.locked
|
||||
newChannel.is_admin_channel = 1
|
||||
feedback_inc("newscaster_channels",1)
|
||||
GLOB.news_network.network_channels += newChannel //Adding channel to the global network
|
||||
log_admin("[key_name_admin(usr)] created command feed channel: [src.admincaster_feed_channel.channel_name]!")
|
||||
src.admincaster_screen=5
|
||||
src.access_news_network()
|
||||
|
||||
else if(href_list["ac_set_channel_receiving"])
|
||||
var/list/available_channels = list()
|
||||
for(var/datum/feed_channel/F in GLOB.news_network.network_channels)
|
||||
available_channels += F.channel_name
|
||||
src.admincaster_feed_channel.channel_name = adminscrub(input(usr, "Choose receiving Feed Channel", "Network Channel Handler") in available_channels )
|
||||
src.access_news_network()
|
||||
|
||||
else if(href_list["ac_set_new_message"])
|
||||
src.admincaster_feed_message.body = adminscrub(input(usr, "Write your Feed story", "Network Channel Handler", ""))
|
||||
while(findtext(src.admincaster_feed_message.body," ") == 1)
|
||||
src.admincaster_feed_message.body = copytext(src.admincaster_feed_message.body,2,length(src.admincaster_feed_message.body)+1)
|
||||
src.access_news_network()
|
||||
|
||||
else if(href_list["ac_submit_new_message"])
|
||||
if(src.admincaster_feed_message.body =="" || src.admincaster_feed_message.body =="\[REDACTED\]" || src.admincaster_feed_channel.channel_name == "" )
|
||||
src.admincaster_screen = 6
|
||||
else
|
||||
var/datum/feed_message/newMsg = new /datum/feed_message
|
||||
newMsg.author = src.admincaster_signature
|
||||
newMsg.body = src.admincaster_feed_message.body
|
||||
newMsg.is_admin_message = 1
|
||||
feedback_inc("newscaster_stories",1)
|
||||
for(var/datum/feed_channel/FC in GLOB.news_network.network_channels)
|
||||
if(FC.channel_name == src.admincaster_feed_channel.channel_name)
|
||||
FC.messages += newMsg //Adding message to the network's appropriate feed_channel
|
||||
break
|
||||
src.admincaster_screen=4
|
||||
|
||||
for(var/obj/machinery/newscaster/NEWSCASTER in GLOB.allNewscasters)
|
||||
NEWSCASTER.newsAlert(src.admincaster_feed_channel.channel_name)
|
||||
|
||||
log_admin("[key_name_admin(usr)] submitted a feed story to channel: [src.admincaster_feed_channel.channel_name]!")
|
||||
src.access_news_network()
|
||||
|
||||
else if(href_list["ac_create_channel"])
|
||||
src.admincaster_screen=2
|
||||
src.access_news_network()
|
||||
|
||||
else if(href_list["ac_create_feed_story"])
|
||||
src.admincaster_screen=3
|
||||
src.access_news_network()
|
||||
|
||||
else if(href_list["ac_menu_censor_story"])
|
||||
src.admincaster_screen=10
|
||||
src.access_news_network()
|
||||
|
||||
else if(href_list["ac_menu_censor_channel"])
|
||||
src.admincaster_screen=11
|
||||
src.access_news_network()
|
||||
|
||||
else if(href_list["ac_menu_wanted"])
|
||||
var/already_wanted = 0
|
||||
if(GLOB.news_network.wanted_issue)
|
||||
already_wanted = 1
|
||||
|
||||
if(already_wanted)
|
||||
src.admincaster_feed_message.author = GLOB.news_network.wanted_issue.author
|
||||
src.admincaster_feed_message.body = GLOB.news_network.wanted_issue.body
|
||||
src.admincaster_screen = 14
|
||||
src.access_news_network()
|
||||
|
||||
else if(href_list["ac_set_wanted_name"])
|
||||
src.admincaster_feed_message.author = adminscrub(input(usr, "Provide the name of the Wanted person", "Network Security Handler", ""))
|
||||
while(findtext(src.admincaster_feed_message.author," ") == 1)
|
||||
src.admincaster_feed_message.author = copytext(admincaster_feed_message.author,2,length(admincaster_feed_message.author)+1)
|
||||
src.access_news_network()
|
||||
|
||||
else if(href_list["ac_set_wanted_desc"])
|
||||
src.admincaster_feed_message.body = adminscrub(input(usr, "Provide the a description of the Wanted person and any other details you deem important", "Network Security Handler", ""))
|
||||
while(findtext(src.admincaster_feed_message.body," ") == 1)
|
||||
src.admincaster_feed_message.body = copytext(src.admincaster_feed_message.body,2,length(src.admincaster_feed_message.body)+1)
|
||||
src.access_news_network()
|
||||
|
||||
else if(href_list["ac_submit_wanted"])
|
||||
var/input_param = text2num(href_list["ac_submit_wanted"])
|
||||
if(src.admincaster_feed_message.author == "" || src.admincaster_feed_message.body == "")
|
||||
src.admincaster_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 = src.admincaster_feed_message.author //Wanted name
|
||||
WANTED.body = src.admincaster_feed_message.body //Wanted desc
|
||||
WANTED.backup_author = src.admincaster_signature //Submitted by
|
||||
WANTED.is_admin_message = 1
|
||||
GLOB.news_network.wanted_issue = WANTED
|
||||
for(var/obj/machinery/newscaster/NEWSCASTER in GLOB.allNewscasters)
|
||||
NEWSCASTER.newsAlert()
|
||||
NEWSCASTER.update_icon()
|
||||
src.admincaster_screen = 15
|
||||
else
|
||||
GLOB.news_network.wanted_issue.author = src.admincaster_feed_message.author
|
||||
GLOB.news_network.wanted_issue.body = src.admincaster_feed_message.body
|
||||
GLOB.news_network.wanted_issue.backup_author = src.admincaster_feed_message.backup_author
|
||||
src.admincaster_screen = 19
|
||||
log_admin("[key_name_admin(usr)] issued a Station-wide Wanted Notification for [src.admincaster_feed_message.author]!")
|
||||
src.access_news_network()
|
||||
|
||||
else if(href_list["ac_cancel_wanted"])
|
||||
var/choice = alert("Please confirm Wanted Issue removal","Network Security Handler","Confirm","Cancel")
|
||||
if(choice=="Confirm")
|
||||
GLOB.news_network.wanted_issue = null
|
||||
for(var/obj/machinery/newscaster/NEWSCASTER in GLOB.allNewscasters)
|
||||
NEWSCASTER.update_icon()
|
||||
src.admincaster_screen=17
|
||||
src.access_news_network()
|
||||
|
||||
else if(href_list["ac_censor_channel_author"])
|
||||
var/datum/feed_channel/FC = locate(href_list["ac_censor_channel_author"])
|
||||
if(FC.author != "<b>\[REDACTED\]</b>")
|
||||
FC.backup_author = FC.author
|
||||
FC.author = "<b>\[REDACTED\]</b>"
|
||||
else
|
||||
FC.author = FC.backup_author
|
||||
src.access_news_network()
|
||||
|
||||
else if(href_list["ac_censor_channel_story_author"])
|
||||
var/datum/feed_message/MSG = locate(href_list["ac_censor_channel_story_author"])
|
||||
if(MSG.author != "<b>\[REDACTED\]</b>")
|
||||
MSG.backup_author = MSG.author
|
||||
MSG.author = "<b>\[REDACTED\]</b>"
|
||||
else
|
||||
MSG.author = MSG.backup_author
|
||||
src.access_news_network()
|
||||
|
||||
else if(href_list["ac_censor_channel_story_body"])
|
||||
var/datum/feed_message/MSG = locate(href_list["ac_censor_channel_story_body"])
|
||||
if(MSG.body != "<b>\[REDACTED\]</b>")
|
||||
MSG.backup_body = MSG.body
|
||||
MSG.body = "<b>\[REDACTED\]</b>"
|
||||
else
|
||||
MSG.body = MSG.backup_body
|
||||
src.access_news_network()
|
||||
|
||||
else if(href_list["ac_pick_d_notice"])
|
||||
var/datum/feed_channel/FC = locate(href_list["ac_pick_d_notice"])
|
||||
src.admincaster_feed_channel = FC
|
||||
src.admincaster_screen=13
|
||||
src.access_news_network()
|
||||
|
||||
else if(href_list["ac_toggle_d_notice"])
|
||||
var/datum/feed_channel/FC = locate(href_list["ac_toggle_d_notice"])
|
||||
FC.censored = !FC.censored
|
||||
src.access_news_network()
|
||||
|
||||
else if(href_list["ac_view"])
|
||||
src.admincaster_screen=1
|
||||
src.access_news_network()
|
||||
|
||||
else if(href_list["ac_setScreen"]) //Brings us to the main menu and resets all fields~
|
||||
src.admincaster_screen = text2num(href_list["ac_setScreen"])
|
||||
if(src.admincaster_screen == 0)
|
||||
if(src.admincaster_feed_channel)
|
||||
src.admincaster_feed_channel = new /datum/feed_channel
|
||||
if(src.admincaster_feed_message)
|
||||
src.admincaster_feed_message = new /datum/feed_message
|
||||
src.access_news_network()
|
||||
|
||||
else if(href_list["ac_show_channel"])
|
||||
var/datum/feed_channel/FC = locate(href_list["ac_show_channel"])
|
||||
src.admincaster_feed_channel = FC
|
||||
src.admincaster_screen = 9
|
||||
src.access_news_network()
|
||||
|
||||
else if(href_list["ac_pick_censor_channel"])
|
||||
var/datum/feed_channel/FC = locate(href_list["ac_pick_censor_channel"])
|
||||
src.admincaster_feed_channel = FC
|
||||
src.admincaster_screen = 12
|
||||
src.access_news_network()
|
||||
|
||||
else if(href_list["ac_refresh"])
|
||||
src.access_news_network()
|
||||
|
||||
else if(href_list["ac_set_signature"])
|
||||
src.admincaster_signature = adminscrub(input(usr, "Provide your desired signature", "Network Identity Handler", ""))
|
||||
src.access_news_network()
|
||||
|
||||
if(href_list["secretsmenu"])
|
||||
switch(href_list["secretsmenu"])
|
||||
if("tab")
|
||||
|
||||
@@ -71,23 +71,26 @@ GLOBAL_VAR_INIT(setup_economy, 0)
|
||||
var/datum/feed_channel/newChannel = new /datum/feed_channel
|
||||
newChannel.channel_name = "Public Station Announcements"
|
||||
newChannel.author = "Automated Announcement Listing"
|
||||
newChannel.locked = 1
|
||||
newChannel.is_admin_channel = 1
|
||||
GLOB.news_network.network_channels += newChannel
|
||||
newChannel.icon = "bullhorn"
|
||||
newChannel.frozen = TRUE
|
||||
newChannel.admin_locked = TRUE
|
||||
GLOB.news_network.channels += newChannel
|
||||
|
||||
newChannel = new /datum/feed_channel
|
||||
newChannel.channel_name = "Nyx Daily"
|
||||
newChannel.author = "CentComm Minister of Information"
|
||||
newChannel.locked = 1
|
||||
newChannel.is_admin_channel = 1
|
||||
GLOB.news_network.network_channels += newChannel
|
||||
newChannel.icon = "meteor"
|
||||
newChannel.frozen = TRUE
|
||||
newChannel.admin_locked = TRUE
|
||||
GLOB.news_network.channels += newChannel
|
||||
|
||||
newChannel = new /datum/feed_channel
|
||||
newChannel.channel_name = "The Gibson Gazette"
|
||||
newChannel.author = "Editor Mike Hammers"
|
||||
newChannel.locked = 1
|
||||
newChannel.is_admin_channel = 1
|
||||
GLOB.news_network.network_channels += newChannel
|
||||
newChannel.icon = "star"
|
||||
newChannel.frozen = TRUE
|
||||
newChannel.admin_locked = TRUE
|
||||
GLOB.news_network.channels += newChannel
|
||||
|
||||
for(var/loc_type in subtypesof(/datum/trade_destination))
|
||||
var/datum/trade_destination/D = new loc_type
|
||||
|
||||
@@ -58,7 +58,7 @@
|
||||
//copy-pasted from the admin verbs to submit new newscaster messages
|
||||
var/datum/feed_message/newMsg = new /datum/feed_message
|
||||
newMsg.author = "Nyx Daily"
|
||||
newMsg.is_admin_message = 1
|
||||
newMsg.admin_locked = TRUE
|
||||
|
||||
//see if our location has custom event info for this event
|
||||
newMsg.body = affected_dest.get_custom_eventstring()
|
||||
@@ -94,12 +94,12 @@
|
||||
if(FESTIVAL)
|
||||
newMsg.body = "A [pick("festival","week long celebration","day of revelry","planet-wide holiday")] has been declared on [affected_dest.name] by [pick("Governor","Commissioner","General","Commandant","Administrator")] [random_name(pick(MALE,FEMALE))] to celebrate [pick("the birth of their [pick("son","daughter")]","coming of age of their [pick("son","daughter")]","the pacification of rogue military cell","the apprehension of a violent criminal who had been terrorising the planet")]. Massive stocks of food and meat have been bought driving up prices across the planet."
|
||||
|
||||
for(var/datum/feed_channel/FC in GLOB.news_network.network_channels)
|
||||
for(var/datum/feed_channel/FC in GLOB.news_network.channels)
|
||||
if(FC.channel_name == "Nyx Daily")
|
||||
FC.messages += newMsg
|
||||
FC.add_message(newMsg)
|
||||
break
|
||||
for(var/obj/machinery/newscaster/NEWSCASTER in GLOB.allNewscasters)
|
||||
NEWSCASTER.newsAlert("Nyx Daily")
|
||||
NEWSCASTER.alert_news("Nyx Daily")
|
||||
|
||||
/datum/event/economic_event/end()
|
||||
for(var/good_type in dearer_goods)
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
//copy-pasted from the admin verbs to submit new newscaster messages
|
||||
var/datum/feed_message/newMsg = new /datum/feed_message
|
||||
newMsg.author = "Nyx Daily"
|
||||
newMsg.is_admin_message = 1
|
||||
newMsg.admin_locked = TRUE
|
||||
|
||||
//see if our location has custom event info for this event
|
||||
newMsg.body = affected_dest.get_custom_eventstring()
|
||||
@@ -126,12 +126,12 @@
|
||||
Nyx Daily is offering discount tickets for two to see [random_name(pick(MALE,FEMALE))] live in return for eyewitness reports and up to the minute coverage."
|
||||
|
||||
|
||||
for(var/datum/feed_channel/FC in GLOB.news_network.network_channels)
|
||||
for(var/datum/feed_channel/FC in GLOB.news_network.channels)
|
||||
if(FC.channel_name == "Nyx Daily")
|
||||
FC.messages += newMsg
|
||||
FC.add_message(newMsg)
|
||||
break
|
||||
for(var/obj/machinery/newscaster/NEWSCASTER in GLOB.allNewscasters)
|
||||
NEWSCASTER.newsAlert("Nyx Daily")
|
||||
NEWSCASTER.alert_news("Nyx Daily")
|
||||
|
||||
/datum/event/trivial_news
|
||||
endWhen = 10
|
||||
@@ -145,9 +145,9 @@
|
||||
newMsg.body = pick(file2list("config/news/trivial.txt"))
|
||||
newMsg.body = replacetext(newMsg.body,"{{AFFECTED}}",affected_dest.name)
|
||||
|
||||
for(var/datum/feed_channel/FC in GLOB.news_network.network_channels)
|
||||
for(var/datum/feed_channel/FC in GLOB.news_network.channels)
|
||||
if(FC.channel_name == "The Gibson Gazette")
|
||||
FC.messages += newMsg
|
||||
FC.add_message(newMsg)
|
||||
break
|
||||
for(var/obj/machinery/newscaster/NEWSCASTER in GLOB.allNewscasters)
|
||||
NEWSCASTER.newsAlert("The Gibson Gazette")
|
||||
NEWSCASTER.alert_news("The Gibson Gazette")
|
||||
|
||||
@@ -15,16 +15,16 @@
|
||||
/datum/event/money_lotto/announce()
|
||||
var/datum/feed_message/newMsg = new /datum/feed_message
|
||||
newMsg.author = "Nanotrasen Editor"
|
||||
newMsg.is_admin_message = 1
|
||||
newMsg.admin_locked = TRUE
|
||||
|
||||
newMsg.body = "Nyx Daily wishes to congratulate <b>[winner_name]</b> for recieving the Nyx Stellar Slam Lottery, and receiving the out of this world sum of [winner_sum] credits!"
|
||||
if(!deposit_success)
|
||||
newMsg.body += "<br>Unfortunately, we were unable to verify the account details provided, so we were unable to transfer the money. Send a cheque containing the sum of $500 to ND 'Stellar Slam' office on the Nyx gateway containing updated details, and your winnings'll be re-sent within the month."
|
||||
|
||||
for(var/datum/feed_channel/FC in GLOB.news_network.network_channels)
|
||||
for(var/datum/feed_channel/FC in GLOB.news_network.channels)
|
||||
if(FC.channel_name == "Nyx Daily")
|
||||
FC.messages += newMsg
|
||||
FC.add_message(newMsg)
|
||||
break
|
||||
|
||||
for(var/obj/machinery/newscaster/NEWSCASTER in GLOB.allNewscasters)
|
||||
NEWSCASTER.newsAlert("Nyx Daily")
|
||||
NEWSCASTER.alert_news("Nyx Daily")
|
||||
|
||||
@@ -0,0 +1,161 @@
|
||||
/**
|
||||
* # Feed Network
|
||||
*
|
||||
* Singleton that contains all informations related to newscasters (channels, stories).
|
||||
*/
|
||||
/datum/feed_network
|
||||
var/list/datum/feed_channel/channels = list()
|
||||
var/list/datum/feed_message/stories = list()
|
||||
var/datum/feed_message/wanted_issue
|
||||
|
||||
/**
|
||||
* Returns the [/datum/get_channel_by_name] with the given name, or null if not found.
|
||||
*
|
||||
* Arguments:
|
||||
* * name - The name
|
||||
*/
|
||||
/datum/feed_network/proc/get_channel_by_name(name)
|
||||
for(var/fc in channels)
|
||||
var/datum/feed_channel/FC = fc
|
||||
if(FC.channel_name == name)
|
||||
return FC
|
||||
|
||||
/**
|
||||
* Returns the [/datum/feed_channel] at the given index, or null if not found.
|
||||
*
|
||||
* Arguments:
|
||||
* * idx - The index
|
||||
*/
|
||||
/datum/feed_network/proc/get_channel_by_idx(idx)
|
||||
if(!ISINDEXSAFE(channels, idx))
|
||||
return
|
||||
return channels[idx]
|
||||
|
||||
/**
|
||||
* # Feed Message
|
||||
*
|
||||
* Describes a single feed story. Always owned by a [/datum/feed_channel].
|
||||
*/
|
||||
/datum/feed_message
|
||||
/// The author of the story.
|
||||
var/author = ""
|
||||
/// The title of the story.
|
||||
var/title = ""
|
||||
/// The textual contents of the story.
|
||||
var/body = ""
|
||||
/// The story's icon.
|
||||
var/icon/img = null
|
||||
/// Flags that dictate the story should be censored.
|
||||
var/censor_flags = 0
|
||||
/// Whether the story is admin-locked.
|
||||
var/admin_locked = FALSE
|
||||
/// The number of views the story has.
|
||||
var/view_count = 0
|
||||
/// The world.time at which the story was published.
|
||||
var/publish_time = 0
|
||||
|
||||
/datum/feed_message/New()
|
||||
publish_time = world.time
|
||||
|
||||
/**
|
||||
* Clears the story's information.
|
||||
*
|
||||
* Does not delete it from the owning channel.
|
||||
*/
|
||||
/datum/feed_message/proc/clear()
|
||||
author = ""
|
||||
title = ""
|
||||
body = ""
|
||||
img = null
|
||||
censor_flags = 0
|
||||
admin_locked = 0
|
||||
view_count = 0
|
||||
publish_time = 0
|
||||
|
||||
/**
|
||||
* # Feed Channel
|
||||
*
|
||||
* Describes a single feed channel. Owns a list of [/datum/feed_message].
|
||||
*/
|
||||
/datum/feed_channel
|
||||
/// The name of the channel.
|
||||
var/channel_name = ""
|
||||
/// The author of the channel.
|
||||
var/author = ""
|
||||
/// The description of the channel.
|
||||
var/description = ""
|
||||
/// The channel's icon.
|
||||
var/icon = "newspaper"
|
||||
/// The fallback author name to display if the channel is censored.
|
||||
var/backup_author = ""
|
||||
/// Lazy list. Contains all [/datum/feed_message] pertaining to the channel.
|
||||
var/list/datum/feed_message/messages
|
||||
/// Whether the channel is public or not.
|
||||
var/is_public = FALSE
|
||||
/// Whether the channel is frozen or not.
|
||||
var/frozen = FALSE
|
||||
/// Whether the channel is admin-locked.
|
||||
var/admin_locked = FALSE
|
||||
|
||||
/datum/feed_channel/Destroy()
|
||||
for(var/m in messages)
|
||||
GLOB.news_network.stories -= m
|
||||
|
||||
/**
|
||||
* Returns whether the given user can publish new stories to this channel.
|
||||
*
|
||||
* Arguments:
|
||||
* * user - The user
|
||||
* * scanned_user - The user's identifying information on the newscaster
|
||||
*/
|
||||
/datum/feed_channel/proc/can_publish(mob/user, scanned_user = "Unknown")
|
||||
return (!frozen && (is_public || (author == scanned_user))) || user.can_admin_interact()
|
||||
|
||||
/**
|
||||
* Returns whether the given user can edit or delete this channel.
|
||||
*
|
||||
* Arguments:
|
||||
* * user - The user
|
||||
* * scanned_user - The user's identifying information on the newscaster
|
||||
*/
|
||||
/datum/feed_channel/proc/can_modify(mob/user, scanned_user = "Unknown")
|
||||
return (!frozen && author == scanned_user) || user.can_admin_interact()
|
||||
|
||||
/**
|
||||
* Clears the channel's information.
|
||||
*
|
||||
* Discards all owned stories.
|
||||
*/
|
||||
/datum/feed_channel/proc/clear()
|
||||
channel_name = ""
|
||||
author = ""
|
||||
backup_author = ""
|
||||
messages = list()
|
||||
is_public = FALSE
|
||||
frozen = FALSE
|
||||
admin_locked = FALSE
|
||||
|
||||
/**
|
||||
* Adds a new [story][/datum/feed_message] to the channel and network singleton.
|
||||
*
|
||||
* Arguments:
|
||||
* * M - The story to add.
|
||||
*/
|
||||
/datum/feed_channel/proc/add_message(datum/feed_message/M)
|
||||
ASSERT(istype(M))
|
||||
|
||||
if(!length(M.title))
|
||||
M.title = "[channel_name] Story #[length(messages) + 1]"
|
||||
LAZYADD(messages, M)
|
||||
GLOB.news_network.stories += M
|
||||
|
||||
/**
|
||||
* Returns the text to be said by newscasters when announcing new news from a channel.
|
||||
*
|
||||
* Arguments:
|
||||
* * title - Optional. The headline to announce along with the channel's name. Typically the newest story's title.
|
||||
*/
|
||||
/datum/feed_channel/proc/get_announce_text(title)
|
||||
if(length(title))
|
||||
return "Breaking news from [channel_name]: [title]"
|
||||
return "Breaking news from [channel_name]"
|
||||
@@ -0,0 +1,43 @@
|
||||
// Globals
|
||||
/// The feed network singleton. Contains all channels (which contain all stories).
|
||||
GLOBAL_DATUM_INIT(news_network, /datum/feed_network, new)
|
||||
/// Global list that contains all existing newscasters in the world.
|
||||
GLOBAL_LIST_EMPTY(allNewscasters)
|
||||
|
||||
// Screen indexes
|
||||
/// Main Menu screen index.
|
||||
#define NEWSCASTER_MAIN 0
|
||||
/// Feed Channel List screen index.
|
||||
#define NEWSCASTER_FC_LIST 1
|
||||
/// Create Feed Channel screen index.
|
||||
#define NEWSCASTER_CREATE_FC 2
|
||||
/// Create Feed Message screen index.
|
||||
#define NEWSCASTER_CREATE_FM 3
|
||||
/// Print Newspaper screen index.
|
||||
#define NEWSCASTER_PRINT 4
|
||||
/// Read Feed Channel screen index.
|
||||
#define NEWSCASTER_VIEW_FC 5
|
||||
/// Nanotrasen Feed Censorship Tool screen index.
|
||||
#define NEWSCASTER_NT_CENSOR 6
|
||||
/// Nanotrasen D-Notice Handler screen index.
|
||||
#define NEWSCASTER_D_NOTICE 7
|
||||
/// Censor Feed Channel screen index.
|
||||
#define NEWSCASTER_CENSOR_FC 8
|
||||
/// D-Notice Feed Channel screen index.
|
||||
#define NEWSCASTER_D_NOTICE_FC 9
|
||||
/// Wanted Issue Handler screen index.
|
||||
#define NEWSCASTER_W_ISSUE_H 10
|
||||
/// Stationwide Wanted Issue screen index.
|
||||
#define NEWSCASTER_W_ISSUE 11
|
||||
/// Available Jobs screen index.
|
||||
#define NEWSCASTER_JOBS 12
|
||||
/// Headlines screen index.
|
||||
#define NEWSCASTER_HEADLINES 13
|
||||
/// View Channel screen index.
|
||||
#define NEWSCASTER_CHANNEL 14
|
||||
|
||||
// Censor flags
|
||||
/// Censor author name.
|
||||
#define CENSOR_AUTHOR (1 << 0)
|
||||
/// Censor story title, body and image.
|
||||
#define CENSOR_STORY (1 << 1)
|
||||
@@ -0,0 +1,872 @@
|
||||
#define CHANNEL_NAME_MAX_LENGTH 50
|
||||
#define CHANNEL_DESC_MAX_LENGTH 128
|
||||
#define STORY_NAME_MAX_LENGTH 128
|
||||
#define STORY_BODY_MAX_LENGTH 1024
|
||||
#define WANTED_NOTICE_NAME_MAX_LENGTH 128
|
||||
#define WANTED_NOTICE_DESC_MAX_LENGTH 512
|
||||
#define STORIES_PER_LOAD 9999 // TODO during QP...
|
||||
|
||||
/obj/machinery/newscaster
|
||||
name = "newscaster"
|
||||
desc = "A standard Nanotrasen-licensed newsfeed handler for use in commercial space stations. All the news you absolutely have no use for, in one place!"
|
||||
icon = 'icons/obj/terminals.dmi'
|
||||
icon_state = "newscaster_normal"
|
||||
max_integrity = 200
|
||||
integrity_failure = 50
|
||||
light_range = 0
|
||||
anchored = TRUE
|
||||
/// The current screen index in the UI.
|
||||
var/screen = NEWSCASTER_HEADLINES
|
||||
/// The amount of newspapers the newscaster can print.
|
||||
var/paper_remaining = 15
|
||||
/// Whether the newscaster can be used to make wanted issues or not.
|
||||
var/is_security = FALSE
|
||||
/// Whether the newscaster has new stories or not.
|
||||
var/alert = FALSE
|
||||
/// The newcaster's index among all newscasters (GLOB.allNewscasters).
|
||||
var/unit_number = 0
|
||||
/// The name of the mob currently using the newscaster.
|
||||
var/scanned_user = "Unknown"
|
||||
/// The currently attached photo.
|
||||
var/obj/item/photo/photo = null
|
||||
/// The currently viewed channel.
|
||||
var/datum/feed_channel/viewing_channel = null
|
||||
/// Whether the unit is silent or not.
|
||||
var/is_silent = FALSE
|
||||
/// The current temporary notice.
|
||||
var/temp_notice
|
||||
/// Jobs that shouldn't be advertised if a position is available.
|
||||
var/list/jobblacklist = list(
|
||||
/datum/job/ai,
|
||||
/datum/job/cyborg,
|
||||
/datum/job/captain,
|
||||
/datum/job/judge,
|
||||
/datum/job/blueshield,
|
||||
/datum/job/nanotrasenrep,
|
||||
/datum/job/pilot,
|
||||
/datum/job/brigdoc,
|
||||
/datum/job/mechanic,
|
||||
/datum/job/barber,
|
||||
/datum/job/chaplain,
|
||||
/datum/job/ntnavyofficer,
|
||||
/datum/job/ntspecops,
|
||||
/datum/job/civilian,
|
||||
/datum/job/syndicateofficer)
|
||||
/// Redacted text
|
||||
var/static/REDACTED = "<b class='bad'>\[REDACTED\]</b>"
|
||||
/// Static, lazy list containing a user's last view time per channel.
|
||||
var/static/last_views
|
||||
|
||||
/obj/machinery/newscaster/security_unit
|
||||
name = "security newscaster"
|
||||
is_security = TRUE
|
||||
|
||||
/obj/machinery/newscaster/New()
|
||||
GLOB.allNewscasters += src
|
||||
unit_number = GLOB.allNewscasters.len
|
||||
update_icon() //for any custom ones on the map...
|
||||
if(!last_views)
|
||||
last_views = list()
|
||||
armor = list(melee = 50, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 0, rad = 0, fire = 50, acid = 30)
|
||||
..()
|
||||
|
||||
/obj/machinery/newscaster/Initialize(mapload)
|
||||
. = ..()
|
||||
|
||||
/obj/machinery/newscaster/Destroy()
|
||||
GLOB.allNewscasters -= src
|
||||
viewing_channel = null
|
||||
QDEL_NULL(photo)
|
||||
return ..()
|
||||
|
||||
/obj/machinery/newscaster/update_icon()
|
||||
cut_overlays()
|
||||
if(inoperable())
|
||||
icon_state = "newscaster_off"
|
||||
else
|
||||
if(!GLOB.news_network.wanted_issue) //wanted icon state, there can be no overlays on it as it's a priority message
|
||||
icon_state = "newscaster_normal"
|
||||
if(alert) //new message alert overlay
|
||||
add_overlay("newscaster_alert")
|
||||
var/hp_percent = obj_integrity * 100 / max_integrity
|
||||
switch(hp_percent)
|
||||
if(75 to INFINITY)
|
||||
return
|
||||
if(50 to 75)
|
||||
add_overlay("crack1")
|
||||
if(25 to 50)
|
||||
add_overlay("crack2")
|
||||
else
|
||||
add_overlay("crack3")
|
||||
|
||||
/obj/machinery/newscaster/power_change()
|
||||
..()
|
||||
update_icon()
|
||||
|
||||
/obj/machinery/newscaster/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = TRUE, attack_dir)
|
||||
. = ..()
|
||||
update_icon()
|
||||
|
||||
/obj/machinery/newscaster/attack_ghost(mob/user)
|
||||
tgui_interact(user)
|
||||
|
||||
/obj/machinery/newscaster/attack_hand(mob/user)
|
||||
if(..())
|
||||
return
|
||||
tgui_interact(user)
|
||||
|
||||
/obj/machinery/newscaster/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = TRUE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state)
|
||||
if(can_scan(user))
|
||||
scanned_user = get_scanned_user(user)
|
||||
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
|
||||
if(!ui)
|
||||
ui = new(user, src, ui_key, "Newscaster", name, 800, 600)
|
||||
ui.open()
|
||||
ui.set_autoupdate(FALSE)
|
||||
|
||||
/obj/machinery/newscaster/tgui_data(mob/user)
|
||||
var/list/data = list()
|
||||
data["unit_number"] = unit_number
|
||||
data["is_security"] = is_security
|
||||
data["is_admin"] = user.can_admin_interact()
|
||||
data["is_silent"] = is_silent
|
||||
data["screen"] = screen
|
||||
data["modal"] = tgui_modal_data(src)
|
||||
if(data["modal"] && !isnull(data["modal"]["args"]["is_admin"]))
|
||||
data["modal"]["args"]["is_admin"] = user.can_admin_interact()
|
||||
data["temp"] = temp_notice
|
||||
|
||||
// Wanted notice
|
||||
if(GLOB.news_network.wanted_issue)
|
||||
data["wanted"] = get_message_data(GLOB.news_network.wanted_issue, user)[1]
|
||||
data["world_time"] = world.time
|
||||
|
||||
var/user_name = get_scanned_user(usr)
|
||||
switch(screen)
|
||||
if(NEWSCASTER_HEADLINES, NEWSCASTER_CHANNEL)
|
||||
// Get the list of stories to pick from - either all or from a specific channel depending on the screen
|
||||
var/list/message_list = GLOB.news_network.stories
|
||||
if(screen == NEWSCASTER_CHANNEL)
|
||||
if(!viewing_channel) // Uh oh, channel doesn't exist! Redirect to Headlines
|
||||
screen = NEWSCASTER_HEADLINES
|
||||
return tgui_data(user)
|
||||
message_list = viewing_channel.messages
|
||||
data["channel_idx"] = GLOB.news_network.channels.Find(viewing_channel)
|
||||
data["channel_can_manage"] = viewing_channel.can_modify(usr, user_name)
|
||||
// Append the data
|
||||
var/list/stories = list()
|
||||
data["stories"] = stories
|
||||
for(var/i in 1 to min(STORIES_PER_LOAD, length(message_list)))
|
||||
stories += get_message_data(message_list[i], user)
|
||||
// View and unread data
|
||||
var/now = world.time
|
||||
data["world_time"] = now
|
||||
if(user_name)
|
||||
// Increase views
|
||||
for(var/m in stories)
|
||||
if(now >= m["publish_time"])
|
||||
var/datum/feed_message/FM = locateUID(m["uid"])
|
||||
if(FM && !(FM.censor_flags & CENSOR_STORY))
|
||||
FM.view_count++
|
||||
m["view_count"] = FM.view_count
|
||||
// Update the last viewed times for the user
|
||||
LAZYINITLIST(last_views[user_name])
|
||||
for(var/c in GLOB.news_network.channels)
|
||||
var/datum/feed_channel/C = c
|
||||
if(screen == NEWSCASTER_CHANNEL && C != viewing_channel)
|
||||
continue
|
||||
last_views[user_name][C.UID()] = now
|
||||
if(NEWSCASTER_PRINT)
|
||||
// TODO
|
||||
// var/total_num = length(GLOB.news_network.channels)
|
||||
// var/active_num = total_num
|
||||
// var/message_num=0
|
||||
// for(var/datum/feed_channel/FC in GLOB.news_network.channels)
|
||||
// if(!FC.censored)
|
||||
// message_num += length(FC.messages)
|
||||
// else
|
||||
// active_num--
|
||||
// data["total_num"] = total_num
|
||||
// data["active_num"] = active_num
|
||||
// data["message_num"] = message_num
|
||||
// data["paper_remaining"] = paper_remaining * 100
|
||||
if(NEWSCASTER_JOBS)
|
||||
var/list/jobs = list()
|
||||
data["jobs"] = jobs
|
||||
for(var/cat in list("security", "engineering", "medical", "science", "service", "supply"))
|
||||
jobs[cat] = list()
|
||||
|
||||
for(var/datum/job/job in SSjobs.occupations)
|
||||
if(job_blacklisted(job))
|
||||
continue
|
||||
if(job.is_position_available())
|
||||
var/list/opening_data = list("title" = job.title)
|
||||
// Is the job a command job?
|
||||
if(job.title in GLOB.command_positions)
|
||||
opening_data["is_command"] = TRUE
|
||||
// Add the job opening to the corresponding categories
|
||||
// Ugly!
|
||||
opening_data = list(opening_data)
|
||||
if(job.is_security)
|
||||
jobs["security"] += opening_data
|
||||
if(job.is_engineering)
|
||||
jobs["engineering"] += opening_data
|
||||
if(job.is_medical)
|
||||
jobs["medical"] += opening_data
|
||||
if(job.is_science)
|
||||
jobs["science"] += opening_data
|
||||
if(job.is_service)
|
||||
jobs["service"] += opening_data
|
||||
if(job.is_supply)
|
||||
jobs["supply"] += opening_data
|
||||
|
||||
// Append temp photo
|
||||
if(photo && data["modal"] && data["modal"]["id"] in list("create_story", "wanted_notice"))
|
||||
data["photo"] = list(
|
||||
name = photo.name,
|
||||
uid = photo.UID(),
|
||||
)
|
||||
user << browse_rsc(photo.img, "inserted_photo_[photo.UID()].png")
|
||||
else
|
||||
data["photo"] = null
|
||||
|
||||
// Append channels
|
||||
var/list/channels = list()
|
||||
data["channels"] = channels
|
||||
for(var/c in GLOB.news_network.channels)
|
||||
var/datum/feed_channel/C = c
|
||||
var/list/channel = list(
|
||||
uid = C.UID(),
|
||||
name = C.channel_name,
|
||||
author = C.author,
|
||||
description = C.description,
|
||||
icon = C.icon,
|
||||
public = C.is_public,
|
||||
frozen = C.frozen,
|
||||
admin = C.admin_locked,
|
||||
unread = 0,
|
||||
)
|
||||
// Add the number of unseen stories if authed
|
||||
if(user_name)
|
||||
var/last_view_time = (last_views[user_name] && last_views[user_name][C.UID()]) || 0
|
||||
for(var/m in C.messages)
|
||||
var/datum/feed_message/M = m
|
||||
if(last_view_time < M.publish_time)
|
||||
channel["unread"]++
|
||||
channels += list(channel)
|
||||
|
||||
return data
|
||||
|
||||
/**
|
||||
* Returns a [/datum/feed_message] in a format that can be used as TGUI data.
|
||||
*
|
||||
* Arguments:
|
||||
* * FM - The story to send
|
||||
* * M - Optional. The user to send the story's photo to if it exists
|
||||
*/
|
||||
/obj/machinery/newscaster/proc/get_message_data(datum/feed_message/FM, mob/M)
|
||||
if(!(FM.censor_flags & CENSOR_STORY) && M && FM.img)
|
||||
M << browse_rsc(FM.img, "story_photo_[FM.UID()].png")
|
||||
return list(list(
|
||||
uid = FM.UID(),
|
||||
author = (FM.censor_flags & CENSOR_AUTHOR) ? "" : FM.author,
|
||||
title = (FM.censor_flags & CENSOR_STORY) ? "" : FM.title,
|
||||
body = (FM.censor_flags & CENSOR_STORY) ? "" : FM.body,
|
||||
admin_locked = FM.admin_locked,
|
||||
censor_flags = FM.censor_flags,
|
||||
view_count = FM.view_count,
|
||||
publish_time = FM.publish_time,
|
||||
publish_time_proper = station_time_timestamp(time = FM.publish_time),
|
||||
has_photo = !isnull(FM.img),
|
||||
))
|
||||
|
||||
/obj/machinery/newscaster/tgui_act(action, list/params)
|
||||
if(..())
|
||||
return
|
||||
|
||||
. = TRUE
|
||||
if(tgui_act_modal(action, params))
|
||||
return
|
||||
|
||||
switch(action)
|
||||
if("cleartemp")
|
||||
temp_notice = null
|
||||
if("jobs")
|
||||
screen = NEWSCASTER_JOBS
|
||||
if("headlines")
|
||||
if(screen == NEWSCASTER_HEADLINES)
|
||||
return FALSE
|
||||
screen = NEWSCASTER_HEADLINES
|
||||
if("channel")
|
||||
var/datum/feed_channel/FC = locateUID(params["uid"])
|
||||
if(!istype(FC))
|
||||
return
|
||||
if(screen == NEWSCASTER_CHANNEL && viewing_channel == FC)
|
||||
return FALSE
|
||||
screen = NEWSCASTER_CHANNEL
|
||||
viewing_channel = FC
|
||||
if("attach_photo")
|
||||
var/list/open_modal = tgui_modal_data(src)
|
||||
if(photo || !open_modal || !(open_modal["id"] in list("create_story", "wanted_notice")))
|
||||
return
|
||||
if(ishuman(usr))
|
||||
var/obj/item/photo/P = usr.get_active_hand()
|
||||
if(istype(P) && usr.unEquip(P))
|
||||
photo = P
|
||||
P.forceMove(src)
|
||||
usr.visible_message("<span class='notice'>[usr] inserts [P] into [src]'s photo slot.</span>",\
|
||||
"<span class='notice'>You insert [P] into [src]'s photo slot.</span>")
|
||||
playsound(loc, 'sound/machines/terminal_insert_disc.ogg', 30, TRUE)
|
||||
else if(issilicon(usr))
|
||||
var/mob/living/silicon/M = usr
|
||||
var/datum/picture/selection = M.aiCamera?.selectpicture()
|
||||
if(!selection)
|
||||
return
|
||||
var/obj/item/photo/P = new
|
||||
P.construct(selection)
|
||||
P.forceMove(src)
|
||||
photo = P
|
||||
visible_message("<span class='notice'>[src]'s photo slot quietly whirs as it prints [P] inside it.</span>")
|
||||
playsound(loc, 'sound/goonstation/machines/printer_thermal.ogg', 15, TRUE)
|
||||
if("eject_photo")
|
||||
eject_photo(usr)
|
||||
return FALSE // Updating handled in that proc
|
||||
if("freeze_channel")
|
||||
if(is_security && !has_security_access(usr))
|
||||
set_temp("You do not have permission to perform this action. Please ensure your ID has appropiate access.", "danger")
|
||||
return
|
||||
var/datum/feed_channel/FC = locateUID(params["uid"])
|
||||
if(!istype(FC))
|
||||
return
|
||||
if(FC.admin_locked && !usr.can_admin_interact())
|
||||
set_temp("This channel has been locked by CentComm and thus cannot be (un)frozen.", "danger")
|
||||
return
|
||||
FC.frozen = !FC.frozen
|
||||
if("censor_author", "censor_story")
|
||||
if(is_security && !has_security_access(usr))
|
||||
set_temp("You do not have permission to perform this action. Please ensure your ID has appropiate access.", "danger")
|
||||
return
|
||||
var/datum/feed_message/FM = locateUID(params["uid"])
|
||||
if(!istype(FM))
|
||||
return
|
||||
if(FM.admin_locked && !usr.can_admin_interact())
|
||||
set_temp("This story has been locked by CentComm and thus cannot be censored in any way.", "danger")
|
||||
return
|
||||
if(action == "censor_author")
|
||||
FM.censor_flags = (FM.censor_flags & CENSOR_AUTHOR) ? (FM.censor_flags & ~CENSOR_AUTHOR) : (FM.censor_flags|CENSOR_AUTHOR)
|
||||
else if(action == "censor_story")
|
||||
FM.censor_flags = (FM.censor_flags & CENSOR_STORY) ? (FM.censor_flags & ~CENSOR_STORY) : (FM.censor_flags|CENSOR_STORY)
|
||||
else
|
||||
return FALSE
|
||||
if("clear_wanted_notice")
|
||||
if(is_security && !has_security_access(usr))
|
||||
set_temp("You do not have permission to perform this action. Please ensure your ID has appropiate access.", "danger")
|
||||
return
|
||||
var/datum/feed_message/WN = GLOB.news_network.wanted_issue
|
||||
if(!WN)
|
||||
return
|
||||
if(WN.admin_locked && !usr.can_admin_interact())
|
||||
set_temp("This wanted notice has been locked by CentComm and thus cannot be altered.", "danger")
|
||||
return
|
||||
GLOB.news_network.wanted_issue = null
|
||||
set_temp("Wanted notice cleared.", update_now = TRUE)
|
||||
return FALSE
|
||||
if("toggle_mute")
|
||||
is_silent = !is_silent
|
||||
else
|
||||
return FALSE
|
||||
|
||||
add_fingerprint(usr)
|
||||
|
||||
/**
|
||||
* Called in tgui_act() to process modal actions
|
||||
*
|
||||
* Arguments:
|
||||
* * action - The action passed by tgui
|
||||
* * params - The params passed by tgui
|
||||
*/
|
||||
/obj/machinery/newscaster/proc/tgui_act_modal(action, list/params)
|
||||
. = TRUE
|
||||
var/id = params["id"]
|
||||
var/list/arguments = istext(params["arguments"]) ? json_decode(params["arguments"]) : params["arguments"]
|
||||
switch(tgui_modal_act(src, action, params))
|
||||
if(TGUI_MODAL_OPEN)
|
||||
switch(id)
|
||||
if("create_channel", "manage_channel")
|
||||
// If trying to manage the channel, make sure the user is allowed to!
|
||||
if(id == "manage_channel")
|
||||
var/datum/feed_channel/FC = locateUID(arguments["uid"])
|
||||
if(!istype(FC) || !FC.can_modify(usr, get_scanned_user(usr)))
|
||||
return
|
||||
tgui_modal_message(src, id, "", arguments = list(
|
||||
uid = arguments["uid"], // Only when managing a channel
|
||||
scanned_user = scanned_user,
|
||||
is_admin = usr.can_admin_interact(),
|
||||
))
|
||||
if("create_story", "wanted_notice") // Other modals
|
||||
if(id == "wanted_notice" && !(is_security || usr.can_admin_interact()))
|
||||
return
|
||||
tgui_modal_message(src, id, "", arguments = list(
|
||||
scanned_user = scanned_user,
|
||||
is_admin = usr.can_admin_interact(),
|
||||
))
|
||||
else
|
||||
return FALSE
|
||||
if(TGUI_MODAL_ANSWER)
|
||||
switch(id)
|
||||
if("create_channel", "manage_channel")
|
||||
var/author = trim(arguments["author"])
|
||||
var/name = trim(arguments["name"])
|
||||
if(!length(author) || !length(name))
|
||||
return
|
||||
var/description = trim(arguments["description"])
|
||||
var/icon = arguments["icon"]
|
||||
var/public = text2num(arguments["public"])
|
||||
var/admin_locked = text2num(arguments["admin_locked"])
|
||||
//
|
||||
var/datum/feed_channel/FC = null
|
||||
if(id == "create_channel") // Channel creation
|
||||
if(GLOB.news_network.get_channel_by_name(name))
|
||||
set_temp("A channel with this name already exists.", "danger")
|
||||
return
|
||||
// TODO: check if author already has made a channel or not
|
||||
FC = new
|
||||
GLOB.news_network.channels += FC
|
||||
feedback_inc("newscaster_channels", 1)
|
||||
// Redirect
|
||||
screen = NEWSCASTER_CHANNEL
|
||||
viewing_channel = FC
|
||||
else if (id == "manage_channel") // Channel management
|
||||
FC = locateUID(arguments["uid"])
|
||||
if(!FC || !FC.can_modify(usr, get_scanned_user(usr)))
|
||||
return
|
||||
// Add/update the information
|
||||
FC.channel_name = copytext(name, 1, CHANNEL_NAME_MAX_LENGTH)
|
||||
FC.description = copytext(description, 1, CHANNEL_DESC_MAX_LENGTH)
|
||||
FC.icon = usr.can_admin_interact() ? icon : "newspaper"
|
||||
FC.author = usr.can_admin_interact() ? author : scanned_user
|
||||
FC.is_public = public
|
||||
FC.admin_locked = usr.can_admin_interact() && admin_locked
|
||||
set_temp("Channel [FC.channel_name] created.", "good")
|
||||
if("create_story")
|
||||
var/author = trim(arguments["author"])
|
||||
var/channel = trim(arguments["channel"])
|
||||
var/title = trim(arguments["title"])
|
||||
var/body = trim(arguments["body"])
|
||||
var/admin_locked = text2num(arguments["admin_locked"])
|
||||
if(!length(author) || !length(title) || !length(body))
|
||||
return
|
||||
// Find the named channel the user is trying to publish a story to
|
||||
var/user_name = get_scanned_user(usr)
|
||||
var/datum/feed_channel/FC
|
||||
for(var/fc in GLOB.news_network.channels)
|
||||
var/datum/feed_channel/_FC = fc
|
||||
if(_FC.channel_name == channel)
|
||||
if(_FC.can_publish(usr, user_name))
|
||||
FC = _FC
|
||||
break
|
||||
if(!FC)
|
||||
return
|
||||
var/datum/feed_message/FM = new
|
||||
FM.author = usr.can_admin_interact() ? author : scanned_user
|
||||
FM.title = copytext(title, 1, STORY_NAME_MAX_LENGTH)
|
||||
FM.body = copytext(body, 1, STORY_BODY_MAX_LENGTH)
|
||||
FM.img = photo?.img
|
||||
FM.admin_locked = usr.can_admin_interact() && admin_locked
|
||||
// Register it
|
||||
FC.add_message(FM)
|
||||
feedback_inc("newscaster_stories", 1)
|
||||
var/announcement = FC.get_announce_text(title)
|
||||
// Announce it
|
||||
for(var/nc in GLOB.allNewscasters)
|
||||
var/obj/machinery/newscaster/NC = nc
|
||||
NC.alert_news(announcement)
|
||||
// Redirect and eject photo
|
||||
LAZYINITLIST(last_views[user_name])
|
||||
last_views[user_name][FC.UID()] = world.time
|
||||
screen = NEWSCASTER_CHANNEL
|
||||
viewing_channel = FC
|
||||
eject_photo(usr)
|
||||
set_temp("Story published to channel [FC.channel_name].", "good")
|
||||
if("wanted_notice")
|
||||
if(id == "wanted_notice" && !(is_security || usr.can_admin_interact()))
|
||||
return
|
||||
var/author = trim(arguments["author"])
|
||||
var/name = trim(arguments["name"])
|
||||
var/description = trim(arguments["description"])
|
||||
var/admin_locked = text2num(arguments["admin_locked"])
|
||||
if(!length(author) || !length(name) || !length(description))
|
||||
return
|
||||
var/datum/feed_message/WN = GLOB.news_network.wanted_issue
|
||||
if(WN)
|
||||
if(WN.admin_locked && !usr.can_admin_interact())
|
||||
set_temp("This wanted notice has been locked by CentComm and thus cannot be altered.", "danger")
|
||||
return
|
||||
else
|
||||
WN = new
|
||||
GLOB.news_network.wanted_issue = WN
|
||||
WN.author = usr.can_admin_interact() ? author : scanned_user
|
||||
WN.title = "WANTED: [copytext(name, 1, WANTED_NOTICE_NAME_MAX_LENGTH)]"
|
||||
WN.body = copytext(description, 1, WANTED_NOTICE_DESC_MAX_LENGTH)
|
||||
WN.img = photo?.img
|
||||
WN.admin_locked = usr.can_admin_interact() && admin_locked
|
||||
WN.publish_time = world.time
|
||||
// Announce it and eject photo
|
||||
for(var/nc in GLOB.allNewscasters)
|
||||
var/obj/machinery/newscaster/NC = nc
|
||||
NC.alert_news(wanted_notice = TRUE)
|
||||
eject_photo(usr)
|
||||
set_temp("Wanted notice distributed.", "good")
|
||||
else
|
||||
return FALSE
|
||||
else
|
||||
return FALSE
|
||||
|
||||
/**
|
||||
* Ejects the photo currently held by the machine if there is one.
|
||||
*
|
||||
* Arguments:
|
||||
* * user - The user to try to give the photo to.
|
||||
*/
|
||||
/obj/machinery/newscaster/proc/eject_photo(mob/user)
|
||||
if(!photo)
|
||||
return
|
||||
var/obj/item/photo/P = photo
|
||||
photo = null
|
||||
P.forceMove(loc)
|
||||
if(ishuman(user) && user.put_in_active_hand(P))
|
||||
visible_message("<span class='notice'>[src] ejects [P] from its photo slot into [user]'s hand.")
|
||||
else
|
||||
visible_message("<span class='notice'>[src] ejects [P] from its photo slot.")
|
||||
playsound(loc, 'sound/machines/terminal_insert_disc.ogg', 30, TRUE)
|
||||
SStgui.update_uis(src)
|
||||
|
||||
/**
|
||||
* Sets a temporary message to display to the user
|
||||
*
|
||||
* Arguments:
|
||||
* * text - Text to display, null/empty to clear the message from the UI
|
||||
* * style - The style of the message: (color name), info, success, warning, danger
|
||||
*/
|
||||
/obj/machinery/newscaster/proc/set_temp(text = "", style = "info", update_now = FALSE)
|
||||
temp_notice = list(text = text, style = style)
|
||||
if(update_now)
|
||||
SStgui.update_uis(src)
|
||||
|
||||
/obj/machinery/newscaster/wrench_act(mob/user, obj/item/I)
|
||||
. = TRUE
|
||||
if(!I.tool_use_check(user, 0))
|
||||
return
|
||||
to_chat(user, "<span class='notice'>Now [anchored ? "un" : ""]securing [name]</span>")
|
||||
if(!I.use_tool(src, user, 60, volume = I.tool_volume))
|
||||
return
|
||||
playsound(loc, 'sound/items/deconstruct.ogg', 50, TRUE)
|
||||
if(stat & BROKEN)
|
||||
to_chat(user, "<span class='warning'>The broken remains of [src] fall on the ground.</span>")
|
||||
new /obj/item/stack/sheet/metal(loc, 5)
|
||||
new /obj/item/shard(loc)
|
||||
new /obj/item/shard(loc)
|
||||
else
|
||||
to_chat(user, "<span class='notice'>You [anchored ? "un" : ""]secure [name].</span>")
|
||||
new /obj/item/mounted/frame/newscaster_frame(loc)
|
||||
qdel(src)
|
||||
|
||||
/obj/machinery/newscaster/welder_act(mob/user, obj/item/I)
|
||||
. = TRUE
|
||||
if(!I.tool_use_check(user, 0))
|
||||
return
|
||||
default_welder_repair(user, I)
|
||||
|
||||
/obj/machinery/newscaster/play_attack_sound(damage, damage_type = BRUTE, damage_flag = 0)
|
||||
switch(damage_type)
|
||||
if(BRUTE)
|
||||
if(stat & BROKEN)
|
||||
playsound(loc, 'sound/effects/hit_on_shattered_glass.ogg', 100, TRUE)
|
||||
else
|
||||
playsound(loc, 'sound/effects/glasshit.ogg', 90, TRUE)
|
||||
if(BURN)
|
||||
playsound(src.loc, 'sound/items/welder.ogg', 100, TRUE)
|
||||
|
||||
/obj/machinery/newscaster/deconstruct(disassembled = TRUE)
|
||||
if(!(flags & NODECONSTRUCT))
|
||||
new /obj/item/stack/sheet/metal(loc, 2)
|
||||
new /obj/item/shard(loc)
|
||||
new /obj/item/shard(loc)
|
||||
qdel(src)
|
||||
|
||||
/obj/machinery/newscaster/obj_break()
|
||||
if(!(stat & BROKEN) && !(flags & NODECONSTRUCT))
|
||||
stat |= BROKEN
|
||||
playsound(loc, 'sound/effects/glassbr3.ogg', 100, TRUE)
|
||||
update_icon()
|
||||
|
||||
/obj/machinery/newscaster/proc/AttachPhoto(mob/user)
|
||||
if(photo)
|
||||
if(!issilicon(user))
|
||||
photo.forceMove(get_turf(src))
|
||||
user.put_in_inactive_hand(photo)
|
||||
photo = null
|
||||
if(istype(user.get_active_hand(), /obj/item/photo))
|
||||
photo = user.get_active_hand()
|
||||
user.drop_item()
|
||||
photo.forceMove(src)
|
||||
else if(issilicon(user))
|
||||
var/mob/living/silicon/tempAI = user
|
||||
var/obj/item/camera/siliconcam/camera = tempAI.aiCamera
|
||||
|
||||
if(!camera)
|
||||
return
|
||||
var/datum/picture/selection = camera.selectpicture()
|
||||
if(!selection)
|
||||
return
|
||||
|
||||
var/obj/item/photo/P = new/obj/item/photo()
|
||||
P.construct(selection)
|
||||
photo = P
|
||||
|
||||
|
||||
//########################################################################################################################
|
||||
//###################################### NEWSPAPER! ######################################################################
|
||||
//########################################################################################################################
|
||||
|
||||
/obj/item/newspaper
|
||||
name = "newspaper"
|
||||
desc = "An issue of The Griffon, the newspaper circulating aboard Nanotrasen Space Stations."
|
||||
icon = 'icons/obj/bureaucracy.dmi'
|
||||
icon_state = "newspaper"
|
||||
w_class = WEIGHT_CLASS_SMALL //Let's make it fit in trashbags!
|
||||
attack_verb = list("bapped")
|
||||
var/screen = 0
|
||||
var/pages = 0
|
||||
var/curr_page = 0
|
||||
var/list/datum/feed_channel/news_content = list()
|
||||
var/datum/feed_message/important_message = null
|
||||
var/scribble=""
|
||||
var/scribble_page = null
|
||||
|
||||
/obj/item/newspaper/attack_self(mob/user as mob)
|
||||
if(ishuman(user))
|
||||
var/mob/living/carbon/human/human_user = user
|
||||
var/dat
|
||||
pages = 0
|
||||
switch(screen)
|
||||
if(0) //Cover
|
||||
dat+="<DIV ALIGN='center'><B><FONT SIZE=6>The Griffon</FONT></B></div>"
|
||||
dat+="<DIV ALIGN='center'><FONT SIZE=2>Nanotrasen-standard newspaper, for use on Nanotrasen Space Facilities</FONT></div><HR>"
|
||||
if(isemptylist(news_content))
|
||||
if(important_message)
|
||||
dat+="Contents:<BR><ul><B><FONT COLOR='red'>**</FONT>Important Security Announcement<FONT COLOR='red'>**</FONT></B> <FONT SIZE=2>\[page [pages+2]\]</FONT><BR></ul>"
|
||||
else
|
||||
dat+="<I>Other than the title, the rest of the newspaper is unprinted...</I>"
|
||||
else
|
||||
dat+="Contents:<BR><ul>"
|
||||
for(var/datum/feed_channel/NP in news_content)
|
||||
pages++
|
||||
if(important_message)
|
||||
dat+="<B><FONT COLOR='red'>**</FONT>Important Security Announcement<FONT COLOR='red'>**</FONT></B> <FONT SIZE=2>\[page [pages+2]\]</FONT><BR>"
|
||||
var/temp_page=0
|
||||
for(var/datum/feed_channel/NP in news_content)
|
||||
temp_page++
|
||||
dat+="<B>[NP.channel_name]</B> <FONT SIZE=2>\[page [temp_page+1]\]</FONT><BR>"
|
||||
dat+="</ul>"
|
||||
if(scribble_page==curr_page)
|
||||
dat+="<BR><I>There is a small scribble near the end of this page... It reads: \"[scribble]\"</I>"
|
||||
dat+= "<HR><DIV STYLE='float:right;'><A href='?src=[UID()];next_page=1'>Next Page</A></DIV> <div style='float:left;'><A href='?src=[human_user.UID()];mach_close=newspaper_main'>Done reading</A></DIV>"
|
||||
if(1) // X channel pages inbetween.
|
||||
for(var/datum/feed_channel/NP in news_content)
|
||||
pages++ //Let's get it right again.
|
||||
var/datum/feed_channel/C = news_content[curr_page]
|
||||
dat+="<FONT SIZE=4><B>[C.channel_name]</B></FONT><FONT SIZE=1> \[created by: <FONT COLOR='maroon'>[C.author]</FONT>\]</FONT><BR><BR>"
|
||||
if(C.frozen)
|
||||
dat+="This channel was deemed dangerous to the general welfare of the station and therefore marked with a <B><FONT COLOR='red'>D-Notice</B></FONT>. Its contents were not transferred to the newspaper at the time of printing."
|
||||
else
|
||||
if(isemptylist(C.messages))
|
||||
dat+="No Feed stories stem from this channel..."
|
||||
else
|
||||
dat+="<ul>"
|
||||
var/i = 0
|
||||
for(var/datum/feed_message/MESSAGE in C.messages)
|
||||
i++
|
||||
dat+="<b>[MESSAGE.title]</b> <br>"
|
||||
dat+="[MESSAGE.body] <BR>"
|
||||
if(MESSAGE.img)
|
||||
user << browse_rsc(MESSAGE.img, "tmp_photo[i].png")
|
||||
dat+="<img src='tmp_photo[i].png' width = '180'><BR>"
|
||||
dat+="<FONT SIZE=1>\[Story by <FONT COLOR='maroon'>[MESSAGE.author]</FONT>\]</FONT><BR><BR>"
|
||||
dat+="</ul>"
|
||||
if(scribble_page==curr_page)
|
||||
dat+="<BR><I>There is a small scribble near the end of this page... It reads: \"[scribble]\"</I>"
|
||||
dat+= "<BR><HR><DIV STYLE='float:left;'><A href='?src=[UID()];prev_page=1'>Previous Page</A></DIV> <DIV STYLE='float:right;'><A href='?src=[UID()];next_page=1'>Next Page</A></DIV>"
|
||||
if(2) //Last page
|
||||
for(var/datum/feed_channel/NP in news_content)
|
||||
pages++
|
||||
if(important_message!=null)
|
||||
dat+="<DIV STYLE='float:center;'><FONT SIZE=4><B>Wanted Issue:</B></FONT SIZE></DIV><BR><BR>"
|
||||
dat+="<B>Criminal name</B>: <FONT COLOR='maroon'>[important_message.author]</FONT><BR>"
|
||||
dat+="<B>Description</B>: [important_message.body]<BR>"
|
||||
dat+="<B>Photo:</B>: "
|
||||
if(important_message.img)
|
||||
user << browse_rsc(important_message.img, "tmp_photow.png")
|
||||
dat+="<BR><img src='tmp_photow.png' width = '180'>"
|
||||
else
|
||||
dat+="None"
|
||||
else
|
||||
dat+="<I>Apart from some uninteresting Classified ads, there's nothing on this page...</I>"
|
||||
if(scribble_page==curr_page)
|
||||
dat+="<BR><I>There is a small scribble near the end of this page... It reads: \"[scribble]\"</I>"
|
||||
dat+= "<HR><DIV STYLE='float:left;'><A href='?src=[UID()];prev_page=1'>Previous Page</A></DIV>"
|
||||
else
|
||||
dat+="I'm sorry to break your immersion. This shit's bugged. Report this bug to Agouri, polyxenitopalidou@gmail.com"
|
||||
|
||||
dat+="<BR><HR><div align='center'>[curr_page+1]</div>"
|
||||
human_user << browse(dat, "window=newspaper_main;size=300x400")
|
||||
onclose(human_user, "newspaper_main")
|
||||
else
|
||||
to_chat(user, "The paper is full of intelligible symbols!")
|
||||
|
||||
|
||||
/obj/item/newspaper/Topic(href, href_list)
|
||||
var/mob/living/U = usr
|
||||
..()
|
||||
if((src in U.contents) || ( istype(loc, /turf) && in_range(src, U) ))
|
||||
U.set_machine(src)
|
||||
if(href_list["next_page"])
|
||||
if(curr_page==pages+1)
|
||||
return //Don't need that at all, but anyway.
|
||||
if(curr_page == pages) //We're at the middle, get to the end
|
||||
screen = 2
|
||||
else
|
||||
if(curr_page == 0) //We're at the start, get to the middle
|
||||
screen=1
|
||||
curr_page++
|
||||
playsound(loc, "pageturn", 50, 1)
|
||||
|
||||
else if(href_list["prev_page"])
|
||||
if(curr_page == 0)
|
||||
return
|
||||
if(curr_page == 1)
|
||||
screen = 0
|
||||
|
||||
else
|
||||
if(curr_page == pages+1) //we're at the end, let's go back to the middle.
|
||||
screen = 1
|
||||
curr_page--
|
||||
playsound(loc, "pageturn", 50, 1)
|
||||
|
||||
if(istype(loc, /mob))
|
||||
attack_self(loc)
|
||||
|
||||
|
||||
/obj/item/newspaper/attackby(obj/item/W as obj, mob/user as mob, params)
|
||||
if(istype(W, /obj/item/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>")
|
||||
else
|
||||
var/s = strip_html( input(user, "Write something", "Newspaper", "") )
|
||||
s = sanitize(copytext(s, 1, MAX_MESSAGE_LEN))
|
||||
if(!s)
|
||||
return
|
||||
if(!in_range(src, usr) && loc != usr)
|
||||
return
|
||||
scribble_page = curr_page
|
||||
scribble = s
|
||||
attack_self(user)
|
||||
return
|
||||
return ..()
|
||||
|
||||
/obj/machinery/newscaster/proc/job_blacklisted(datum/job/job)
|
||||
return (job.type in jobblacklist)
|
||||
|
||||
/obj/machinery/newscaster/proc/get_scanned_user(mob/user)
|
||||
if(ishuman(user)) //User is a human
|
||||
var/mob/living/carbon/human/human_user = user
|
||||
if(human_user.wear_id) //Newscaster scans you
|
||||
if(istype(human_user.wear_id, /obj/item/pda)) //autorecognition, woo!
|
||||
var/obj/item/pda/P = human_user.wear_id
|
||||
if(P.id)
|
||||
return "[P.id.registered_name] ([P.id.assignment])"
|
||||
else if(istype(human_user.wear_id, /obj/item/card/id))
|
||||
var/obj/item/card/id/ID = human_user.wear_id
|
||||
return "[ID.registered_name] ([ID.assignment])"
|
||||
else if(issilicon(user))
|
||||
var/mob/living/silicon/ai_user = user
|
||||
return "[ai_user.name] ([ai_user.job])"
|
||||
return "Unknown"
|
||||
|
||||
/obj/machinery/newscaster/proc/has_security_access(mob/user)
|
||||
if(ishuman(user))
|
||||
var/mob/living/carbon/human/M = user
|
||||
if(!M.wear_id)
|
||||
return FALSE
|
||||
var/obj/item/card/id/ID
|
||||
if(istype(M.wear_id, /obj/item/pda))
|
||||
var/obj/item/pda/P = M.wear_id
|
||||
ID = P.id
|
||||
else if(istype(M.wear_id, /obj/item/card/id))
|
||||
ID = M.wear_id
|
||||
if(!istype(ID))
|
||||
return FALSE
|
||||
return has_access(list(), list(ACCESS_SECURITY), ID.access)
|
||||
return issilicon(user) || user.can_admin_interact()
|
||||
|
||||
/obj/machinery/newscaster/proc/can_scan(mob/user)
|
||||
if(ishuman(user) || issilicon(user))
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/obj/machinery/newscaster/proc/print_paper()
|
||||
feedback_inc("newscaster_newspapers_printed",1)
|
||||
var/obj/item/newspaper/NEWSPAPER = new /obj/item/newspaper
|
||||
for(var/datum/feed_channel/FC in GLOB.news_network.channels)
|
||||
NEWSPAPER.news_content += FC
|
||||
if(GLOB.news_network.wanted_issue)
|
||||
NEWSPAPER.important_message = GLOB.news_network.wanted_issue
|
||||
NEWSPAPER.loc = get_turf(src)
|
||||
paper_remaining--
|
||||
return
|
||||
|
||||
/**
|
||||
* Makes the newscaster say a message and change its icon state for a while.
|
||||
*
|
||||
* Arguments:
|
||||
* * announcement - The message to say
|
||||
* * wanted_notice - Whether the alert is a wanted notice notification (overrides announcement)
|
||||
*/
|
||||
/obj/machinery/newscaster/proc/alert_news(announcement, wanted_notice = FALSE)
|
||||
if(wanted_notice)
|
||||
atom_say("Attention! Wanted issue distributed!")
|
||||
playsound(loc, 'sound/machines/warning-buzzer.ogg', 75, TRUE)
|
||||
else if(length(announcement))
|
||||
atom_say("[announcement]!")
|
||||
if(!is_silent)
|
||||
playsound(loc, 'sound/machines/twobeep.ogg', 75, TRUE)
|
||||
else
|
||||
return
|
||||
alert = TRUE
|
||||
addtimer(CALLBACK(src, .proc/alert_timer_finish), 30 SECONDS)
|
||||
update_icon()
|
||||
|
||||
/**
|
||||
* Called when the timer following a news alert finishes.
|
||||
*/
|
||||
/obj/machinery/newscaster/proc/alert_timer_finish()
|
||||
alert = FALSE
|
||||
update_icon()
|
||||
|
||||
/obj/machinery/newscaster/verb/eject_photo_verb()
|
||||
set name = "Eject Photo"
|
||||
set category = "Object"
|
||||
set src in oview(1)
|
||||
|
||||
if(usr.incapacitated())
|
||||
return
|
||||
|
||||
eject_photo(usr)
|
||||
|
||||
#undef CHANNEL_NAME_MAX_LENGTH
|
||||
#undef CHANNEL_DESC_MAX_LENGTH
|
||||
#undef STORY_NAME_MAX_LENGTH
|
||||
#undef STORY_BODY_MAX_LENGTH
|
||||
#undef WANTED_NOTICE_NAME_MAX_LENGTH
|
||||
#undef WANTED_NOTICE_DESC_MAX_LENGTH
|
||||
#undef STORIES_PER_LOAD
|
||||
@@ -1,333 +0,0 @@
|
||||
<!--
|
||||
Title: Newscaster
|
||||
Used In File(s): /code/game/machinery/newscaster.dm
|
||||
-->
|
||||
|
||||
<!--
|
||||
#define NEWSCASTER_MAIN 0
|
||||
#define NEWSCASTER_FC_LIST 1
|
||||
#define NEWSCASTER_CREATE_FC 2
|
||||
#define NEWSCASTER_CREATE_FM 3
|
||||
#define NEWSCASTER_PRINT 4
|
||||
#define NEWSCASTER_VIEW_FC 5
|
||||
#define NEWSCASTER_NT_CENSOR 6
|
||||
#define NEWSCASTER_D_NOTICE 7
|
||||
#define NEWSCASTER_CENSOR_FC 8
|
||||
#define NEWSCASTER_D_NOTICE_FC 9
|
||||
#define NEWSCASTER_W_ISSUE_H 10
|
||||
#define NEWSCASTER_W_ISSUE 11
|
||||
-->
|
||||
|
||||
<h3>Welcome to Newscasting Unit #{{:data.unit_no}}.</h3>
|
||||
<font size=1>Interface & News networks Operational.<br>
|
||||
Property of Nanotrasen</font><br>
|
||||
<hr>
|
||||
{{if data.temp}}
|
||||
<div class='line'>{{:data.temp}}</div>
|
||||
<div class='line'>{{:helper.link('Return', 'arrow-left', {'setScreen' : data.temp_back_screen})}}</div>
|
||||
{{else}}
|
||||
{{if data.screen == 0}} <!-- NEWSCASTER_MAIN -->
|
||||
<div class='item'>
|
||||
{{if data.wanted_issue}}
|
||||
<div class='line'>{{:helper.link('Read Wanted issue', 'exclamation-triangle', {'view_wanted' : 1})}}</div>
|
||||
{{/if}}
|
||||
<div class='line'>{{:helper.link('View available NT jobs', 'share', {'jobs' : 1})}}</div>
|
||||
<div class='line'>{{:helper.link('Create feed channel', 'plus', {'create_channel' : 1})}}</div>
|
||||
<div class='line'>{{:helper.link('View feed channels', 'arrow-right', {'view' : 1})}}</div>
|
||||
<div class='line'>{{:helper.link('Submit new feed story', 'share', {'create_feed_story' : 1})}}</div>
|
||||
<div class='line'>{{:helper.link('Print newspaper', 'print', {'menu_paper' : 1})}}</div>
|
||||
<div class='line'>{{:helper.link(data.silence ? 'Unsilence unit' : 'Silence unit', data.silence ? 'volume-up' : 'volume-off', {'silence_unit' : 1})}}</div>
|
||||
<div class='line'>{{:helper.link('Re-scan user', 'refresh', {'refresh' : 1})}}</div>
|
||||
{{if data.securityCaster}}
|
||||
<div class='line'><b>Feed Security functions:</b></div>
|
||||
<div class='line'>{{:helper.link(data.wanted_issue ? 'Manage wanted issue' : 'Publish wanted issue', data.wanted_issue ? 'pencil' : 'share', {'menu_wanted' : 1})}}</div>
|
||||
<div class='line'>{{:helper.link('Censor feed stories', 'ban', {'menu_censor_story' : 1})}}</div>
|
||||
<div class='line'>{{:helper.link('Mark feed channel with Nanotrasen D-Notice', 'thumb-tack', {'menu_censor_channel' : 1})}}</div>
|
||||
<div class='line'>The newscaster recognises you as: <span class='good'>{{:data.scanned_user}}</span></div>
|
||||
{{/if}}
|
||||
</div>
|
||||
{{else data.screen == 1}} <!-- NEWSCASTER_FC_LIST -->
|
||||
<h4>Station Feed Channels</h4>
|
||||
<div class='item'>
|
||||
{{for data.channels}}
|
||||
<div class='line'>
|
||||
{{if value.admin}}
|
||||
{{:helper.link('<b>' + value.name + '</b>', 'arrow-right', {'show_channel' : value.ref})}}
|
||||
{{else}}
|
||||
{{:helper.link(value.name, 'arrow-right', {'show_channel' : value.ref}, null, value.censored ? 'link linkDanger' : null)}}
|
||||
{{/if}}
|
||||
</div>
|
||||
{{empty}}
|
||||
<i class='average'>No active channels found...</i>
|
||||
{{/for}}
|
||||
</div>
|
||||
<hr>
|
||||
<div class='item'>
|
||||
<div class='line'>{{:helper.link('Refresh', 'refresh', {'refresh' : 1})}}</div>
|
||||
<div class='line'>{{:helper.link('Back', 'arrow-left', {'setScreen' : 0})}}</div>
|
||||
</div>
|
||||
{{else data.screen == 2}} <!-- NEWSCASTER_CREATE_FC -->
|
||||
<h4>Creating new feed channel...</h4>
|
||||
<div class='item'>
|
||||
<div class='itemLabel'>Channel name:</div>
|
||||
<div class='itemContent'>
|
||||
<a style='float: left; padding-right: 6px;'>{{:data.channel_name}}</a>
|
||||
{{:helper.link('Edit', 'pencil', {'set_channel_name' : 1})}}
|
||||
</div>
|
||||
</div>
|
||||
<div class='item'>
|
||||
<div class='itemLabel'>Channel author:</div>
|
||||
<div class='itemContent good'>{{:data.scanned_user}}</div>
|
||||
</div>
|
||||
<div class='item'>
|
||||
<div class='itemLabel'>Accept public feeds:</div>
|
||||
<div class='itemContent'>{{:helper.link(data.c_locked ? 'NO' : 'YES', data.c_locked ? 'times' : 'check', {'set_channel_lock' : 1})}}</div>
|
||||
</div>
|
||||
<div class='item'>
|
||||
{{:helper.link('Submit', 'share', {'submit_new_channel' : 1})}}
|
||||
{{:helper.link('Cancel', 'arrow-left', {'setScreen' : 0})}}
|
||||
</div>
|
||||
{{else data.screen == 3}} <!-- NEWSCASTER_CREATE_FM -->
|
||||
<b>Creating new feed message...</b>
|
||||
<div class='item'>
|
||||
<div class='itemLabel'>Receiving channel:</div>
|
||||
<div class='itemContent'>
|
||||
<a style='float: left; padding-right: 6px;'>{{:data.channel_name}}</a>
|
||||
{{:helper.link('Edit', 'pencil', {'set_channel_receiving' : 1})}}
|
||||
</div>
|
||||
</div>
|
||||
<div class='item'>
|
||||
<div class='itemLabel'>Title:</div>
|
||||
<div class='itemContent'>
|
||||
<a style='float: left; padding-right: 6px;'>{{:data.title}}</a>
|
||||
{{:helper.link('Edit', 'pencil', {'set_message_title' : 1})}}
|
||||
</div>
|
||||
</div>
|
||||
<div class='item'>
|
||||
<div class='itemLabel'>Message body:</div>
|
||||
<div class='itemContent'>
|
||||
<a style='float: left; padding-right: 6px;'>{{:data.msg}}</a>
|
||||
{{:helper.link('Edit', 'pencil', {'set_new_message' : 1})}}
|
||||
</div>
|
||||
</div>
|
||||
<div class='item'>
|
||||
<div class='itemLabel'>Attached photo:</div>
|
||||
<div class='itemContent'>{{:helper.link(data.photo ? 'Dettach photo' : 'Attach photo', data.photo ? 'times' : 'paperclip', {'set_attachment' : 1})}}</div>
|
||||
</div>
|
||||
<div class='item'>
|
||||
{{:helper.link('Submit', 'share', {'submit_new_message' : 1})}}
|
||||
{{:helper.link('Cancel', 'arrow-left', {'setScreen' : 0})}}
|
||||
</div>
|
||||
{{else data.screen == 4}} <!-- NEWSCASTER_PRINT -->
|
||||
Network currently serves a total of {{:data.total_num}} feed channels, {{:data.active_num}} of which are active, and a total of {{:data.message_num}} feed stories.
|
||||
<div class='item'>
|
||||
<div class='itemLabel'>Liquid paper remaining:</div>
|
||||
<div class='itemContent'>{{:data.paper_remaining}} cm<sup>3</sup></div>
|
||||
</div>
|
||||
<div class='item'>
|
||||
{{:helper.link('Print paper', 'print', {'print_paper' : 0})}}
|
||||
{{:helper.link('Cancel', 'arrow-left', {'setScreen' : 0})}}
|
||||
</div>
|
||||
{{else data.screen == 5}} <!-- NEWSCASTER_VIEW_FC -->
|
||||
<b>{{:data.channel_name}}</b> <i><font size=1>[created by: <span class='good'>{{:data.author}}</span>]</font></i><br>
|
||||
<i><font size=1>Feed view count: {{:data.total_view_count}}</font></i><br>
|
||||
{{if data.censored}}
|
||||
<div class='statusDisplay'>
|
||||
<b class='bad'>ATTENTION: </b>This channel has been deemed as threatening to the welfare of the station, and marked with a Nanotrasen D-Notice.<BR>
|
||||
No further feed story additions are allowed while the D-Notice is in effect.
|
||||
</div>
|
||||
{{else}}
|
||||
{{for data.messages}}
|
||||
<div class='statusDisplay'>
|
||||
<p style='text-align: center;'>
|
||||
<font size="5"><b>{{:value.title}}</b></font><br>
|
||||
</p>
|
||||
{{if value.img}}
|
||||
<img src='tmp_photo{{:value.message_number}}.png' style='display: block; margin-left: auto; margin-right: auto;' width=350><br>
|
||||
{{/if}}
|
||||
<p style='text-align: justify;'>
|
||||
{{:value.body}}<br>
|
||||
</p>
|
||||
<i><font size=1>[{{:value.message_type}} by <span class='good'>{{:value.author}}</span>]</font></i><br>
|
||||
<i><font size=1>Message view count: {{:value.view_count}}</font></i>
|
||||
</div>
|
||||
{{empty}}
|
||||
<i>No feed messages found in channel...</i>
|
||||
{{/for}}
|
||||
{{/if}}
|
||||
<hr>
|
||||
<div class='item'>
|
||||
<div class='line'>{{:helper.link('Refresh', 'refresh', {'refresh' : 0})}}</div>
|
||||
<div class='line'>{{:helper.link('Back', 'arrow-left', {'setScreen' : 1})}}</div>
|
||||
</div>
|
||||
{{else data.screen == 6}} <!-- NEWSCASTER_NT_CENSOR -->
|
||||
<B>Nanotrasen Feed Censorship Tool</B><BR>
|
||||
<i><font size=1>NOTE: Due to the nature of news Feeds, total deletion of a Feed Story is not possible.
|
||||
Keep in mind that users attempting to view a censored feed will instead see the <b class='bad'>[REDACTED]</b> tag above it.</font></i>
|
||||
<hr>
|
||||
Select feed channel to get stories from:
|
||||
<div class='item'>
|
||||
{{for data.channels}}
|
||||
<div class='line'>
|
||||
{{if value.admin}}
|
||||
{{:helper.link('<b>' + value.name + '</b>', 'arrow-right', {'pick_censor_channel' : value.ref})}}
|
||||
{{else}}
|
||||
{{:helper.link(value.name, 'arrow-right', {'pick_censor_channel' : value.ref}, null, value.censored ? 'link linkDanger' : null)}}
|
||||
{{/if}}
|
||||
</div>
|
||||
{{empty}}
|
||||
<i>No feed channels found active...</i>
|
||||
{{/for}}
|
||||
</div>
|
||||
<div class='item'>
|
||||
<div class='line'>{{:helper.link('Back', 'arrow-left', {'setScreen' : 0})}}</div>
|
||||
</div>
|
||||
{{else data.screen == 7}} <!-- NEWSCASTER_D_NOTICE -->
|
||||
<B>Nanotrasen D-Notice Handler</B><BR>
|
||||
<i><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 morale, integrity or disciplinary behaviour.
|
||||
A D-Notice will render a channel unable to be updated by anyone, without deleting any feedstories it might contain at the time.
|
||||
You can lift a D-Notice if you have the required access at any time.</font></i>
|
||||
<hr>
|
||||
<div class='item'>
|
||||
{{for data.channels}}
|
||||
<div class='line'>
|
||||
{{if value.admin}}
|
||||
{{:helper.link('<b>' + value.name + '</b>', 'arrow-right', {'pick_d_notice' : value.ref})}}
|
||||
{{else}}
|
||||
{{:helper.link(value.name, 'arrow-right', {'pick_d_notice' : value.ref}, null, value.censored ? 'link linkDanger' : null)}}
|
||||
{{/if}}
|
||||
</div>
|
||||
{{empty}}
|
||||
<i>No feed channels found active...</i>
|
||||
{{/for}}
|
||||
</div>
|
||||
<hr>
|
||||
<div class='item'>
|
||||
<div class='line'>{{:helper.link('Back', 'arrow-left', {'setScreen' : 0})}}</div>
|
||||
</div>
|
||||
{{else data.screen == 8}} <!-- NEWSCASTER_CENSOR_FC -->
|
||||
<b>{{:data.channel_name}}</b> <i><font size=1>[created by: <span class='good'>{{:data.author}}</span>]</font></i><br>
|
||||
<i>{{:helper.link(data.author_redacted ? 'Undo author censorship' : 'Censor channel author', null, {'censor_channel_author' : data.ref})}}</i><br><hr>
|
||||
{{for data.messages}}
|
||||
<div class='statusDisplay'>
|
||||
<b>{{:value.title}}</b><br>
|
||||
{{:value.body}}<br>
|
||||
{{if value.img}}
|
||||
<img src='{{:value.img}}' width=180><br>
|
||||
{{/if}}
|
||||
<i><font size=1>[{{:value.message_type}} by <span class='good'>{{:value.author}}</span>]</font></i><br>
|
||||
<i>
|
||||
{{:helper.link(value.body_redacted ? 'Undo story censorship' : 'Censor story', null, {'censor_channel_story_body' : value.ref})}}
|
||||
{{:helper.link(value.author_redacted ? 'Undo author censorship' : 'Censor message author', null, {'censor_channel_story_author' : value.ref})}}
|
||||
</i>
|
||||
</div>
|
||||
{{empty}}
|
||||
<i>No feed messages found in channel...</i>
|
||||
{{/for}}
|
||||
<hr>
|
||||
<div class='item'>
|
||||
<div class='line'>{{:helper.link('Back', 'arrow-left', {'setScreen' : 6})}}</div>
|
||||
</div>
|
||||
{{else data.screen == 9}} <!-- NEWSCASTER_D_NOTICE_FC -->
|
||||
<b>{{:data.channel_name}}</b> <i><font size=1>[created by: <span class='good'>{{:data.author}}</span>]</font></i><br>
|
||||
<i>Channel messages listed below. If you deem them dangerous to the station, you can <div style='clear: both;'>{{:helper.link(data.censored ? 'Remove the D-Notice on the channel' : 'Bestow a D-Notice upon the channel', null, {'toggle_d_notice' : data.ref})}}</div></i><br><hr>
|
||||
{{if data.censored}}
|
||||
<div class='statusDisplay'>
|
||||
<b class='bad'>ATTENTION: </b>This channel has been deemed as threatening to the welfare of the station, and marked with a Nanotrasen D-Notice.
|
||||
No further feed story additions are allowed while the D-Notice is in effect.
|
||||
</div>
|
||||
{{else}}
|
||||
{{for data.messages}}
|
||||
<div class='statusDisplay'>
|
||||
<b>{{:value.title}}</b><br>
|
||||
{{:value.body}}<br>
|
||||
{{if value.img}}
|
||||
<img src='{{:value.img}}' width=180><br>
|
||||
{{/if}}
|
||||
<i><font size=1>[{{:value.message_type}} by <span class='good'>{{:value.author}}</span>]</font></i>
|
||||
</div>
|
||||
{{empty}}
|
||||
<i>No feed messages found in channel...</i>
|
||||
{{/for}}
|
||||
{{/if}}
|
||||
<div class='item'>
|
||||
<div class='line'>{{:helper.link('Back', 'arrow-left', {'setScreen' : 7})}}</div>
|
||||
</div>
|
||||
{{else data.screen == 10}} <!-- NEWSCASTER_W_ISSUE_H -->
|
||||
<b>Wanted Issue handler:</b><br>
|
||||
{{if data.wanted_already}}
|
||||
<i>A wanted issue is already in Feed Circulation. You can edit or cancel it below.</i><br>
|
||||
{{/if}}
|
||||
<div class='item'>
|
||||
<div class='itemLabel'>Criminal name:</div>
|
||||
<div class='itemContent'>
|
||||
<a style='float: left; padding-right: 6px;'>{{:data.channel_name}}</a>
|
||||
{{:helper.link('Edit', 'pencil', {'set_wanted_name' : 1})}}
|
||||
</div>
|
||||
</div>
|
||||
<div class='item'>
|
||||
<div class='itemLabel'>Description:</div>
|
||||
<div class='itemContent'>
|
||||
<a style='float: left; padding-right: 6px;'>{{:data.msg}}</a>
|
||||
{{:helper.link('Edit', 'pencil', {'set_wanted_desc' : 1})}}
|
||||
</div>
|
||||
</div>
|
||||
<div class='item'>
|
||||
<div class='itemLabel'>Attached photo:</div>
|
||||
<div class='itemContent'>{{:helper.link(data.photo ? 'Dettach photo' : 'Attach photo', data.photo ? 'times' : 'paperclip', {'set_attachment' : 1})}}</div>
|
||||
</div>
|
||||
<div class='item'>
|
||||
<div class='itemLabel'>Author:</div>
|
||||
<div class='itemContent good'>{{:data.wanted_already ? data.author : data.scanned_user}}</div>
|
||||
</div>
|
||||
<hr>
|
||||
<div class='item'>
|
||||
{{:helper.link(data.wanted_already ? 'Edit issue' : 'Submit', data.wanted_already ? 'pencil' : 'share', {'submit_wanted' : data.end_param})}}
|
||||
{{if data.wanted_already}}
|
||||
{{:helper.link('Take down issue', 'times', {'cancel_wanted' : 1})}}
|
||||
{{/if}}
|
||||
{{:helper.link('Back', 'arrow-left', {'setScreen' : 0})}}
|
||||
</div>
|
||||
{{else data.screen == 11}} <!-- NEWSCASTER_W_ISSUE -->
|
||||
<center><h3 class='bad'>STATIONWIDE WANTED ISSUE</h3>
|
||||
<i><font size=1>[Submitted by: <span class='good'>{{:data.author}}</span>]</font></i></center><hr>
|
||||
<div class='item'>
|
||||
<div class='itemLabel'>Criminal:</div>
|
||||
<div class='itemContent'>{{:data.criminal}}</div>
|
||||
</div>
|
||||
<div class='item'>
|
||||
<div class='itemLabel'>Description:</div>
|
||||
<div class='itemContent'>{{:data.description}}</div>
|
||||
</div>
|
||||
<div class='item'>
|
||||
<div class='itemLabel'>Photo:</div><br>
|
||||
<div class='itemContent'>
|
||||
{{if data.photo}}
|
||||
<img src='tmp_photow.png' width=350><br>
|
||||
{{else}}
|
||||
None
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
<div class='item'>
|
||||
<div class='line'>{{:helper.link('Back', 'arrow-left', {'setScreen' : 0})}}</div>
|
||||
</div>
|
||||
{{else data.screen == 12}} <!-- NEWSCASTER_JOBS -->
|
||||
<center><h3 class='good'>CAREERS AT NANOTRASEN</h3>
|
||||
<i><font size=1>Work for a better future.</font></i></center>
|
||||
<hr>
|
||||
{{for data.jobs}}
|
||||
<div class='jobValues'>
|
||||
{{:value.title}}<br>
|
||||
</div>
|
||||
{{empty}}
|
||||
<i>No available jobs...</i>
|
||||
{{/for}}
|
||||
|
||||
<hr>
|
||||
<div class='item'>
|
||||
<div class='line'>{{:helper.link('Refresh', 'refresh', {'refresh' : 0})}}</div>
|
||||
<div class='line'>{{:helper.link('Back', 'arrow-left', {'setScreen' : 1})}}</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
+4
-1
@@ -667,7 +667,6 @@
|
||||
#include "code\game\machinery\magnet.dm"
|
||||
#include "code\game\machinery\mass_driver.dm"
|
||||
#include "code\game\machinery\navbeacon.dm"
|
||||
#include "code\game\machinery\newscaster.dm"
|
||||
#include "code\game\machinery\OpTable.dm"
|
||||
#include "code\game\machinery\overview.dm"
|
||||
#include "code\game\machinery\PDApainter.dm"
|
||||
@@ -2103,6 +2102,10 @@
|
||||
#include "code\modules\nano\modules\human_appearance.dm"
|
||||
#include "code\modules\nano\modules\law_manager.dm"
|
||||
#include "code\modules\nano\modules\nano_module.dm"
|
||||
#include "code\modules\nano\modules\power_monitor.dm"
|
||||
#include "code\modules\newscaster\datums.dm"
|
||||
#include "code\modules\newscaster\defines.dm"
|
||||
#include "code\modules\newscaster\newscaster.dm"
|
||||
#include "code\modules\ninja\energy_katana.dm"
|
||||
#include "code\modules\ninja\suit\gloves.dm"
|
||||
#include "code\modules\ninja\suit\head.dm"
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
* the response with already existing state.
|
||||
*/
|
||||
|
||||
import { UI_DISABLED, UI_INTERACTIVE } from './constants';
|
||||
import { callByond } from './byond';
|
||||
import { UI_DISABLED, UI_INTERACTIVE } from './constants';
|
||||
|
||||
export const backendUpdate = state => ({
|
||||
type: 'backend/update',
|
||||
@@ -20,6 +20,11 @@ export const backendSetSharedState = (key, nextState) => ({
|
||||
payload: { key, nextState },
|
||||
});
|
||||
|
||||
export const backendDeleteSharedState = keys => ({
|
||||
type: 'backend/deleteSharedState',
|
||||
payload: keys,
|
||||
});
|
||||
|
||||
export const backendReducer = (state, action) => {
|
||||
const { type, payload } = action;
|
||||
|
||||
@@ -73,6 +78,15 @@ export const backendReducer = (state, action) => {
|
||||
};
|
||||
}
|
||||
|
||||
if (type === 'backend/deleteSharedState') {
|
||||
let shared = { ...state.shared };
|
||||
payload.forEach(key => delete shared[key]);
|
||||
return {
|
||||
...state,
|
||||
shared: shared,
|
||||
};
|
||||
}
|
||||
|
||||
return state;
|
||||
};
|
||||
|
||||
@@ -148,6 +162,17 @@ export const useLocalState = (context, key, initialState) => {
|
||||
];
|
||||
};
|
||||
|
||||
/**
|
||||
* Deletes local states from the Redux store.
|
||||
*
|
||||
* @param {any} context React context.
|
||||
* @param {string} ...keys Keys of states to delete from the store.
|
||||
*/
|
||||
export const deleteLocalState = (context, ...keys) => {
|
||||
const { store } = context;
|
||||
store.dispatch(backendDeleteSharedState(keys));
|
||||
};
|
||||
|
||||
/**
|
||||
* Allocates state on Redux store, and **shares** it with other clients
|
||||
* in the game.
|
||||
|
||||
@@ -110,6 +110,11 @@ export class Input extends Component {
|
||||
maxLength,
|
||||
placeholder,
|
||||
autofocus,
|
||||
disabled,
|
||||
// Multiline props
|
||||
multiline,
|
||||
cols = 32,
|
||||
rows = 4,
|
||||
...boxProps
|
||||
} = props;
|
||||
// Box props
|
||||
@@ -123,21 +128,37 @@ export class Input extends Component {
|
||||
className={classes([
|
||||
'Input',
|
||||
fluid && 'Input--fluid',
|
||||
disabled && 'Input--disabled',
|
||||
className,
|
||||
])}
|
||||
{...rest}>
|
||||
<div className="Input__baseline">
|
||||
.
|
||||
</div>
|
||||
<input
|
||||
ref={this.inputRef}
|
||||
className="Input__input"
|
||||
placeholder={placeholder}
|
||||
onInput={this.handleInput}
|
||||
onFocus={this.handleFocus}
|
||||
onBlur={this.handleBlur}
|
||||
onKeyDown={this.handleKeyDown}
|
||||
maxLength={maxLength} />
|
||||
{multiline ? (
|
||||
<textarea
|
||||
ref={this.inputRef}
|
||||
className="Input__textarea"
|
||||
placeholder={placeholder}
|
||||
onInput={this.handleInput}
|
||||
onFocus={this.handleFocus}
|
||||
onBlur={this.handleBlur}
|
||||
maxLength={maxLength}
|
||||
cols={cols}
|
||||
rows={rows}
|
||||
disabled={disabled} />
|
||||
) : (
|
||||
<input
|
||||
ref={this.inputRef}
|
||||
className="Input__input"
|
||||
placeholder={placeholder}
|
||||
onInput={this.handleInput}
|
||||
onFocus={this.handleFocus}
|
||||
onBlur={this.handleBlur}
|
||||
onKeyDown={this.handleKeyDown}
|
||||
maxLength={maxLength}
|
||||
disabled={disabled} />
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,885 @@
|
||||
import { classes } from 'common/react';
|
||||
import { Fragment } from 'inferno';
|
||||
import { deleteLocalState, useBackend, useLocalState } from "../backend";
|
||||
import { Box, Button, Divider, Dropdown, Flex, Icon, Input, LabeledList, Modal, Section } from '../components';
|
||||
import { Window } from '../layouts';
|
||||
import { ComplexModal, modalAnswer, modalClose, modalOpen, modalRegisterBodyOverride } from './common/ComplexModal';
|
||||
import { TemporaryNotice } from './common/TemporaryNotice';
|
||||
|
||||
const HEADLINE_MAX_LENGTH = 128;
|
||||
|
||||
const jobOpeningCategoriesOrder = ["security", "engineering", "medical", "science", "service", "supply"];
|
||||
const jobOpeningCategories = {
|
||||
security: {
|
||||
title: "Security",
|
||||
fluff_text: "Help keep the crew safe",
|
||||
},
|
||||
engineering: {
|
||||
title: "Engineering",
|
||||
fluff_text: "Ensure the station runs smoothly",
|
||||
},
|
||||
medical: {
|
||||
title: "Medical",
|
||||
fluff_text: "Practice medicine and save lives",
|
||||
},
|
||||
science: {
|
||||
title: "Science",
|
||||
fluff_text: "Develop new technologies",
|
||||
},
|
||||
service: {
|
||||
title: "Service",
|
||||
fluff_text: "Provide amenities to the crew",
|
||||
},
|
||||
supply: {
|
||||
title: "Supply",
|
||||
fluff_text: "Keep the station supplied",
|
||||
},
|
||||
};
|
||||
|
||||
// This handles both creation and editing
|
||||
const manageChannelModalBodyOverride = (modal, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
// Additional data
|
||||
const channel = !!modal.args.uid && data.channels.filter(c => c.uid === modal.args.uid).pop();
|
||||
if (modal.id === "manage_channel" && !channel) {
|
||||
modalClose(context); // ?
|
||||
return;
|
||||
}
|
||||
const isEditing = modal.id === "manage_channel";
|
||||
const isAdmin = !!modal.args.is_admin;
|
||||
const scannedUser = modal.args.scanned_user;
|
||||
// Temp data
|
||||
const [author, setAuthor] = useLocalState(context, "author", channel?.author || scannedUser || "Unknown");
|
||||
const [name, setName] = useLocalState(context, "name", channel?.name || "");
|
||||
const [description, setDescription] = useLocalState(context, "description", channel?.description || "");
|
||||
const [icon, setIcon] = useLocalState(context, "icon", channel?.icon || "newspaper");
|
||||
const [isPublic, setIsPublic] = useLocalState(context, "isPublic", isEditing ? !!(channel?.public) : false);
|
||||
const [adminLocked, setAdminLocked] = useLocalState(context, "adminLocked", (channel?.admin === 1) || false);
|
||||
return (
|
||||
<Section
|
||||
level="2"
|
||||
m="-1rem"
|
||||
pb="1rem"
|
||||
title={isEditing ? ("Manage " + channel.name) : "Create New Channel"}>
|
||||
<Box mx="0.5rem">
|
||||
<LabeledList>
|
||||
<LabeledList.Item label="Owner">
|
||||
<Input
|
||||
disabled={!isAdmin}
|
||||
width="100%"
|
||||
value={author}
|
||||
onInput={(_e, v) => setAuthor(v)}
|
||||
/>
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Name">
|
||||
<Input
|
||||
width="100%"
|
||||
placeholder="50 characters max."
|
||||
maxLength="50"
|
||||
value={name}
|
||||
onInput={(_e, v) => setName(v)}
|
||||
/>
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Description (optional)" verticalAlign="top">
|
||||
<Input
|
||||
multiline
|
||||
width="100%"
|
||||
placeholder="128 characters max."
|
||||
maxLength="128"
|
||||
value={description}
|
||||
onInput={(_e, v) => setDescription(v)}
|
||||
/>
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Icon">
|
||||
<Input
|
||||
disabled={!isAdmin}
|
||||
value={icon}
|
||||
width="35%"
|
||||
mr="0.5rem"
|
||||
onInput={(_e, v) => setIcon(v)}
|
||||
/>
|
||||
<Icon name={icon} size="2" verticalAlign="middle" mr="0.5rem" />
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Accept Public Stories?">
|
||||
<Button
|
||||
selected={isPublic}
|
||||
icon={isPublic ? "toggle-on" : "toggle-off"}
|
||||
content={isPublic ? "Yes" : "No"}
|
||||
onClick={() => setIsPublic(!isPublic)}
|
||||
/>
|
||||
</LabeledList.Item>
|
||||
{isAdmin && (
|
||||
<LabeledList.Item label="CentComm Lock" verticalAlign="top">
|
||||
<Button
|
||||
selected={adminLocked}
|
||||
icon={adminLocked ? "lock" : "lock-open"}
|
||||
content={adminLocked ? "On" : "Off"}
|
||||
tooltip="Locking this channel will make it editable by nobody but CentComm officers."
|
||||
tooltipPosition="top"
|
||||
onClick={() => setAdminLocked(!adminLocked)}
|
||||
/>
|
||||
</LabeledList.Item>
|
||||
)}
|
||||
</LabeledList>
|
||||
</Box>
|
||||
<Button.Confirm
|
||||
disabled={author.trim().length === 0 || name.trim().length === 0}
|
||||
icon="check"
|
||||
color="good"
|
||||
content="Submit"
|
||||
position="absolute"
|
||||
right="1rem"
|
||||
bottom="-0.75rem"
|
||||
onClick={() => {
|
||||
modalAnswer(context, modal.id, "", {
|
||||
author: author,
|
||||
name: name.substr(0, 49),
|
||||
description: description.substr(0, 128),
|
||||
icon: icon,
|
||||
public: isPublic ? 1 : 0,
|
||||
admin_locked: adminLocked ? 1 : 0,
|
||||
});
|
||||
// Clean up
|
||||
deleteLocalState(context, "author", "name", "description", "icon", "public");
|
||||
}}
|
||||
/>
|
||||
</Section>
|
||||
);
|
||||
};
|
||||
|
||||
const createStoryModalBodyOverride = (modal, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
const {
|
||||
photo,
|
||||
channels,
|
||||
channel_idx = -1,
|
||||
} = data;
|
||||
// Additional data
|
||||
const isAdmin = !!modal.args.is_admin;
|
||||
const scannedUser = modal.args.scanned_user;
|
||||
let availableChannels = channels.slice()
|
||||
.sort((a, b) => {
|
||||
if (channel_idx < 0) {
|
||||
return 0;
|
||||
}
|
||||
const selected = channels[channel_idx - 1];
|
||||
if (selected.uid === a.uid) {
|
||||
return -1;
|
||||
} else if (selected.uid === b.uid) {
|
||||
return 1;
|
||||
}
|
||||
})
|
||||
.filter(c => isAdmin || (!c.frozen && (c.author === scannedUser || !!c.public)));
|
||||
// Temp data
|
||||
const [author, setAuthor] = useLocalState(context, "author", scannedUser || "Unknown");
|
||||
const [channel, setChannel] = useLocalState(context, "channel", availableChannels.length > 0 ? availableChannels[0].name : "");
|
||||
const [title, setTitle] = useLocalState(context, "title", "");
|
||||
const [body, setBody] = useLocalState(context, "body", "");
|
||||
const [adminLocked, setAdminLocked] = useLocalState(context, "adminLocked", false);
|
||||
return (
|
||||
<Section
|
||||
level={2}
|
||||
m="-1rem"
|
||||
pb="1rem"
|
||||
title="Create New Story">
|
||||
<Box mx="0.5rem">
|
||||
<LabeledList>
|
||||
<LabeledList.Item label="Author">
|
||||
<Input
|
||||
disabled={!isAdmin}
|
||||
width="100%"
|
||||
value={author}
|
||||
onInput={(_e, v) => setAuthor(v)}
|
||||
/>
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Channel" verticalAlign="top">
|
||||
<Dropdown
|
||||
selected={channel}
|
||||
options={availableChannels.map(c => c.name)}
|
||||
mb="0"
|
||||
width="100%"
|
||||
onSelected={c => setChannel(c)}
|
||||
/>
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Divider />
|
||||
<LabeledList.Item label="Title">
|
||||
<Input
|
||||
width="100%"
|
||||
placeholder="128 characters max."
|
||||
maxLength="128"
|
||||
value={title}
|
||||
onInput={(_e, v) => setTitle(v)}
|
||||
/>
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Story Text" verticalAlign="top">
|
||||
<Input
|
||||
fluid
|
||||
multiline
|
||||
placeholder="1024 characters max."
|
||||
maxLength="1024"
|
||||
rows="8"
|
||||
width="100%"
|
||||
value={body}
|
||||
onInput={(_e, v) => setBody(v)}
|
||||
/>
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Photo (optional)" verticalAlign="top">
|
||||
<Button
|
||||
icon="image"
|
||||
selected={photo}
|
||||
content={photo ? ("Eject: " + photo.name) : "Insert Photo"}
|
||||
tooltip={!photo && "Attach a photo to this story by holding the photograph in your hand."}
|
||||
onClick={() => act(photo ? 'eject_photo' : 'attach_photo')}
|
||||
/>
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Preview" verticalAlign="top">
|
||||
<Section
|
||||
noTopPadding
|
||||
title={title}
|
||||
maxHeight="13.5rem"
|
||||
overflow="auto">
|
||||
<Box mt="0.5rem">
|
||||
{!!photo && (
|
||||
<PhotoThumbnail
|
||||
name={"inserted_photo_" + photo.uid + ".png"}
|
||||
float="right"
|
||||
/>
|
||||
)}
|
||||
{body.split("\n").map(p => (
|
||||
<Box key={p}>
|
||||
{p || <br />}
|
||||
</Box>
|
||||
))}
|
||||
<Box clear="right" />
|
||||
</Box>
|
||||
</Section>
|
||||
</LabeledList.Item>
|
||||
{isAdmin && (
|
||||
<LabeledList.Item label="CentComm Lock" verticalAlign="top">
|
||||
<Button
|
||||
selected={adminLocked}
|
||||
icon={adminLocked ? "lock" : "lock-open"}
|
||||
content={adminLocked ? "On" : "Off"}
|
||||
tooltip="Locking this story will make it censorable by nobody but CentComm officers."
|
||||
tooltipPosition="top"
|
||||
onClick={() => setAdminLocked(!adminLocked)}
|
||||
/>
|
||||
</LabeledList.Item>
|
||||
)}
|
||||
</LabeledList>
|
||||
</Box>
|
||||
<Button.Confirm
|
||||
disabled={author.trim().length === 0
|
||||
|| channel.trim().length === 0
|
||||
|| title.trim().length === 0
|
||||
|| body.trim().length === 0}
|
||||
icon="check"
|
||||
color="good"
|
||||
content="Submit"
|
||||
position="absolute"
|
||||
right="1rem"
|
||||
bottom="-0.75rem"
|
||||
onClick={() => {
|
||||
modalAnswer(context, "create_story", "", {
|
||||
author: author,
|
||||
channel: channel,
|
||||
title: title.substr(0, 127),
|
||||
body: body.substr(0, 1023),
|
||||
admin_locked: adminLocked ? 1 : 0,
|
||||
});
|
||||
// Clean up
|
||||
deleteLocalState(context, "author", "channel", "title", "body");
|
||||
}}
|
||||
/>
|
||||
</Section>
|
||||
);
|
||||
};
|
||||
|
||||
const wantedNoticeModalBodyOverride = (modal, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
const {
|
||||
photo,
|
||||
wanted,
|
||||
} = data;
|
||||
// Additional data
|
||||
const isAdmin = !!modal.args.is_admin;
|
||||
const scannedUser = modal.args.scanned_user;
|
||||
// Temp data
|
||||
const [author, setAuthor] = useLocalState(context, "author", wanted?.author || scannedUser || "Unknown");
|
||||
const [name, setName] = useLocalState(context, "name", wanted?.title.substr(8) || "");
|
||||
const [description, setDescription] = useLocalState(context, "description", wanted?.body || "");
|
||||
const [adminLocked, setAdminLocked] = useLocalState(context, "adminLocked", (wanted?.admin_locked === 1) || false);
|
||||
return (
|
||||
<Section
|
||||
level="2"
|
||||
m="-1rem"
|
||||
pb="1rem"
|
||||
title="Manage Wanted Notice">
|
||||
<Box mx="0.5rem">
|
||||
<LabeledList>
|
||||
<LabeledList.Item label="Authority">
|
||||
<Input
|
||||
disabled={!isAdmin}
|
||||
width="100%"
|
||||
value={author}
|
||||
onInput={(_e, v) => setAuthor(v)}
|
||||
/>
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Name">
|
||||
<Input
|
||||
width="100%"
|
||||
value={name}
|
||||
maxLength="128"
|
||||
onInput={(_e, v) => setName(v)}
|
||||
/>
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Description" verticalAlign="top">
|
||||
<Input
|
||||
multiline
|
||||
width="100%"
|
||||
value={description}
|
||||
maxLength="512"
|
||||
rows="4"
|
||||
onInput={(_e, v) => setDescription(v)}
|
||||
/>
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Photo (optional)" verticalAlign="top">
|
||||
<Button
|
||||
icon="image"
|
||||
selected={photo}
|
||||
content={photo ? ("Eject: " + photo.name) : "Insert Photo"}
|
||||
tooltip={!photo && "Attach a photo to this wanted notice by holding the photograph in your hand."}
|
||||
tooltipPosition="top"
|
||||
onClick={() => act(photo ? 'eject_photo' : 'attach_photo')}
|
||||
/>
|
||||
{!!photo && (
|
||||
<PhotoThumbnail
|
||||
name={"inserted_photo_" + photo.uid + ".png"}
|
||||
float="right"
|
||||
/>
|
||||
)}
|
||||
</LabeledList.Item>
|
||||
{isAdmin && (
|
||||
<LabeledList.Item label="CentComm Lock" verticalAlign="top">
|
||||
<Button
|
||||
selected={adminLocked}
|
||||
icon={adminLocked ? "lock" : "lock-open"}
|
||||
content={adminLocked ? "On" : "Off"}
|
||||
tooltip="Locking this wanted notice will make it editable by nobody but CentComm officers."
|
||||
tooltipPosition="top"
|
||||
onClick={() => setAdminLocked(!adminLocked)}
|
||||
/>
|
||||
</LabeledList.Item>
|
||||
)}
|
||||
</LabeledList>
|
||||
</Box>
|
||||
<Button.Confirm
|
||||
disabled={!wanted}
|
||||
icon="eraser"
|
||||
color="danger"
|
||||
content="Clear"
|
||||
position="absolute"
|
||||
right="7.25rem"
|
||||
bottom="-0.75rem"
|
||||
onClick={() => {
|
||||
act("clear_wanted_notice");
|
||||
modalClose(context);
|
||||
// Clean up
|
||||
deleteLocalState(context, "author", "name", "description", "admin_locked");
|
||||
}}
|
||||
/>
|
||||
<Button.Confirm
|
||||
disabled={author.trim().length === 0 || name.trim().length === 0 || description.trim().length === 0}
|
||||
icon="check"
|
||||
color="good"
|
||||
content="Submit"
|
||||
position="absolute"
|
||||
right="1rem"
|
||||
bottom="-0.75rem"
|
||||
onClick={() => {
|
||||
modalAnswer(context, modal.id, "", {
|
||||
author: author,
|
||||
name: name.substr(0, 127),
|
||||
description: description.substr(0, 511),
|
||||
admin_locked: adminLocked ? 1 : 0,
|
||||
});
|
||||
// Clean up
|
||||
deleteLocalState(context, "author", "name", "description", "admin_locked");
|
||||
}}
|
||||
/>
|
||||
</Section>
|
||||
);
|
||||
};
|
||||
|
||||
// TODO: make this common
|
||||
const timeAgo = (ref_time, now_time) => {
|
||||
if (ref_time > now_time) {
|
||||
return "in the future";
|
||||
}
|
||||
|
||||
// deciseconds -> seconds
|
||||
ref_time = ref_time / 10;
|
||||
now_time = now_time / 10;
|
||||
|
||||
const diff = now_time - ref_time;
|
||||
if (diff > 3600) {
|
||||
const hours = Math.round(diff / 3600);
|
||||
return hours + " hour" + (hours === 1 ? "" : "s") + " ago";
|
||||
} else if (diff > 60) {
|
||||
const mins = Math.round(diff / 60);
|
||||
return mins + " minute" + (mins === 1 ? "" : "s") + " ago";
|
||||
} else {
|
||||
const secs = Math.round(diff);
|
||||
return secs + " second" + (secs === 1 ? "" : "s") + " ago";
|
||||
}
|
||||
|
||||
return "just now";
|
||||
};
|
||||
|
||||
export const Newscaster = (properties, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
const {
|
||||
is_security,
|
||||
is_admin,
|
||||
is_silent,
|
||||
screen,
|
||||
channels,
|
||||
channel_idx = -1,
|
||||
} = data;
|
||||
const [menuOpen, setMenuOpen] = useLocalState(context, "menuOpen", false);
|
||||
const [viewingPhoto, _setViewingPhoto] = useLocalState(context, "viewingPhoto", "");
|
||||
const [censorMode, setCensorMode] = useLocalState(context, "censorMode", false);
|
||||
let body;
|
||||
if (screen === 13 || screen === 14) {
|
||||
body = <NewscasterFeed />;
|
||||
} else if (screen === 12) {
|
||||
body = <NewscasterJobs />;
|
||||
}
|
||||
const totalUnread = channels.reduce((a, c) => a + c.unread, 0);
|
||||
return (
|
||||
<Window theme={is_security && "security"}>
|
||||
{viewingPhoto ? (
|
||||
<PhotoZoom />
|
||||
) : (
|
||||
<ComplexModal
|
||||
maxWidth={window.innerWidth / 1.5 + "px"}
|
||||
maxHeight={window.innerHeight / 1.5 + "px"}
|
||||
/>
|
||||
)}
|
||||
<Window.Content>
|
||||
<Flex width="100%" height="100%">
|
||||
<Section
|
||||
className={classes([
|
||||
"Newscaster__menu",
|
||||
menuOpen && "Newscaster__menu--open",
|
||||
])}>
|
||||
<MenuButton
|
||||
icon="bars"
|
||||
title="Toggle Menu"
|
||||
onClick={() => setMenuOpen(!menuOpen)}
|
||||
/>
|
||||
<MenuButton
|
||||
icon="newspaper"
|
||||
title="Headlines"
|
||||
selected={screen === 13}
|
||||
onClick={() => act('headlines')}>
|
||||
{totalUnread > 0 && (
|
||||
<Box className="Newscaster__menuButton--unread">
|
||||
{totalUnread >= 10 ? "9+" : totalUnread}
|
||||
</Box>
|
||||
)}
|
||||
</MenuButton>
|
||||
<MenuButton
|
||||
icon="briefcase"
|
||||
title="Job Openings"
|
||||
selected={screen === 12}
|
||||
onClick={() => act('jobs')}
|
||||
/>
|
||||
<Divider />
|
||||
<Box>
|
||||
{channels.map(channel => (
|
||||
<MenuButton
|
||||
key={channel}
|
||||
icon={channel.icon}
|
||||
title={channel.name}
|
||||
selected={screen === 14 && channels[channel_idx - 1] === channel}
|
||||
onClick={() => act('channel', { uid: channel.uid })}>
|
||||
{channel.unread > 0 && (
|
||||
<Box className="Newscaster__menuButton--unread">
|
||||
{channel.unread >= 10 ? "9+" : channel.unread}
|
||||
</Box>
|
||||
)}
|
||||
</MenuButton>
|
||||
))}
|
||||
</Box>
|
||||
<Box position="absolute" bottom="0.5rem" width="100%">
|
||||
<Box mr="1rem">
|
||||
<Divider />
|
||||
{(!!is_security || !!is_admin) && (
|
||||
<Fragment>
|
||||
<MenuButton
|
||||
security
|
||||
icon="exclamation-circle"
|
||||
title="Edit Wanted Notice"
|
||||
mb="0.5rem"
|
||||
onClick={() => modalOpen(context, "wanted_notice")}
|
||||
/>
|
||||
<MenuButton
|
||||
security
|
||||
icon={censorMode ? "minus-square" : "minus-square-o"}
|
||||
title={"Censor Mode: " + (censorMode ? "On" : "Off")}
|
||||
mb="0.5rem"
|
||||
onClick={() => setCensorMode(!censorMode)}
|
||||
/>
|
||||
<Divider />
|
||||
</Fragment>
|
||||
)}
|
||||
<MenuButton
|
||||
icon="pen-alt"
|
||||
title="New Story"
|
||||
mb="0.5rem"
|
||||
onClick={() => modalOpen(context, "create_story")}
|
||||
/>
|
||||
<MenuButton
|
||||
icon="plus-circle"
|
||||
title="New Channel"
|
||||
onClick={() => modalOpen(context, "create_channel")}
|
||||
/>
|
||||
<Divider />
|
||||
<MenuButton
|
||||
icon={is_silent ? "volume-mute" : "volume-up"}
|
||||
title={"Mute: " + (is_silent ? "On" : "Off")}
|
||||
onClick={() => act("toggle_mute")}
|
||||
/>
|
||||
</Box>
|
||||
</Box>
|
||||
</Section>
|
||||
<Flex direction="column" height="100%" flex="1">
|
||||
<TemporaryNotice />
|
||||
{body}
|
||||
</Flex>
|
||||
</Flex>
|
||||
</Window.Content>
|
||||
</Window>
|
||||
);
|
||||
};
|
||||
|
||||
const MenuButton = (properties, context) => {
|
||||
const { act } = useBackend(context);
|
||||
const {
|
||||
icon = "",
|
||||
selected = false,
|
||||
security = false,
|
||||
onClick,
|
||||
title,
|
||||
children,
|
||||
...rest
|
||||
} = properties;
|
||||
return (
|
||||
<Box
|
||||
className={classes([
|
||||
"Newscaster__menuButton",
|
||||
selected && "Newscaster__menuButton--selected",
|
||||
security && "Newscaster__menuButton--security",
|
||||
])}
|
||||
onClick={onClick}
|
||||
{...rest}>
|
||||
<Icon
|
||||
name={icon}
|
||||
size="2"
|
||||
/>
|
||||
<Box className="Newscaster__menuButton--title">
|
||||
{title}
|
||||
</Box>
|
||||
{children}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
const NewscasterFeed = (properties, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
const {
|
||||
screen,
|
||||
is_admin,
|
||||
channel_idx,
|
||||
channel_can_manage,
|
||||
channels,
|
||||
stories,
|
||||
wanted,
|
||||
} = data;
|
||||
const [fullStories, _setFullStories] = useLocalState(context, "fullStories", []);
|
||||
const [censorMode, _setCensorMode] = useLocalState(context, "censorMode", false);
|
||||
const channel = (screen === 14 && channel_idx > -1) ? channels[channel_idx - 1] : null;
|
||||
return (
|
||||
<Flex direction="column" height="100%" flex="1">
|
||||
{!!wanted && <Story story={wanted} wanted />}
|
||||
<Section
|
||||
title={(
|
||||
<Fragment >
|
||||
<Icon name={channel ? channel.icon : "newspaper"} mr="0.5rem" />
|
||||
{channel ? channel.name : "Headlines"}
|
||||
</Fragment>
|
||||
)}
|
||||
flexGrow="1">
|
||||
{stories.length > 0
|
||||
? stories.slice()
|
||||
.reverse()
|
||||
.map(story =>
|
||||
(!fullStories.includes(story.uid) && story.body.length + 3 > HEADLINE_MAX_LENGTH)
|
||||
? { ...story, body_short: story.body.substr(0, HEADLINE_MAX_LENGTH - 4) + "..." }
|
||||
: story
|
||||
)
|
||||
.map(story => <Story key={story} story={story} />)
|
||||
: (
|
||||
<Box className="Newscaster__emptyNotice">
|
||||
<Icon name="times" size="3" /><br />
|
||||
There are no stories at this time.
|
||||
</Box>
|
||||
)}
|
||||
</Section>
|
||||
{!!channel && (
|
||||
<Section
|
||||
flexShrink="1"
|
||||
title={(
|
||||
<Fragment>
|
||||
<Icon name="info-circle" mr="0.5rem" />
|
||||
About
|
||||
</Fragment>
|
||||
)}
|
||||
buttons={(
|
||||
<Fragment>
|
||||
{censorMode && (
|
||||
<Button
|
||||
disabled={!!channel.admin && !is_admin}
|
||||
selected={channel.frozen}
|
||||
icon={channel.frozen ? "comment-slash" : "comment"}
|
||||
content={channel.frozen ? "Unfreeze Channel" : "Freeze Channel"}
|
||||
mr="0.5rem"
|
||||
onClick={() => act("freeze_channel", { uid: channel.uid })}
|
||||
/>
|
||||
)}
|
||||
<Button
|
||||
disabled={!channel_can_manage}
|
||||
icon="cog"
|
||||
content="Manage"
|
||||
onClick={() => modalOpen(context, "manage_channel", {
|
||||
uid: channel.uid,
|
||||
})}
|
||||
/>
|
||||
</Fragment>
|
||||
)}>
|
||||
<LabeledList>
|
||||
<LabeledList.Item label="Description">
|
||||
{channel.description || "N/A"}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Owner">
|
||||
{channel.author || "N/A"}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Public">
|
||||
{channel.public ? "Yes" : "No"}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Total Views">
|
||||
<Icon name="eye" mr="0.5rem" />
|
||||
{stories.reduce((a, c) => a + c.view_count, 0).toLocaleString()}
|
||||
</LabeledList.Item>
|
||||
</LabeledList>
|
||||
</Section>
|
||||
)}
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
const NewscasterJobs = (properties, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
const {
|
||||
jobs,
|
||||
wanted,
|
||||
} = data;
|
||||
const numOpenings = Object.entries(jobs)
|
||||
.reduce((a, [k, v]) => a + v.length, 0);
|
||||
return (
|
||||
<Flex direction="column" height="100%" flex="1">
|
||||
{!!wanted && <Story story={wanted} wanted />}
|
||||
<Section
|
||||
flexGrow="1"
|
||||
title={(
|
||||
<Fragment>
|
||||
<Icon name="briefcase" mr="0.5rem" />
|
||||
Job Openings
|
||||
</Fragment>
|
||||
)}
|
||||
buttons={(
|
||||
<Box mt="0.25rem" color="label">
|
||||
Work for a better future at Nanotrasen
|
||||
</Box>
|
||||
)}>
|
||||
{numOpenings > 0
|
||||
? jobOpeningCategoriesOrder
|
||||
.map(catId => Object.assign({}, jobOpeningCategories[catId], {
|
||||
id: catId,
|
||||
jobs: jobs[catId],
|
||||
}))
|
||||
.filter(cat => !!cat && cat.jobs.length > 0)
|
||||
.map(cat => (
|
||||
<Section
|
||||
key={cat.id}
|
||||
className={classes([
|
||||
"Newscaster__jobCategory",
|
||||
"Newscaster__jobCategory--" + cat.id,
|
||||
])}
|
||||
title={cat.title}
|
||||
buttons={(
|
||||
<Box mt="0.25rem" color="label">
|
||||
{cat.fluff_text}
|
||||
</Box>
|
||||
)}>
|
||||
{cat.jobs.map(job => (
|
||||
<Box
|
||||
key={job.title}
|
||||
class={classes([
|
||||
"Newscaster__jobOpening",
|
||||
!!job.is_command && "Newscaster__jobOpening--command",
|
||||
])}>
|
||||
• {job.title}
|
||||
</Box>
|
||||
))}
|
||||
</Section>
|
||||
))
|
||||
: (
|
||||
<Box className="Newscaster__emptyNotice">
|
||||
<Icon name="times" size="3" /><br />
|
||||
There are no openings at this time.
|
||||
</Box>
|
||||
)}
|
||||
</Section>
|
||||
<Section flexShrink="1">
|
||||
Interested in serving Nanotrasen?<br />
|
||||
Sign up for any of the above position now at the <b>Head of Personnel's Office!</b><br />
|
||||
<Box as="small" color="label">
|
||||
By signing up for a job at Nanotrasen, you agree to transfer your soul to the loyalty department of the omnipresent and helpful watcher of humanity.
|
||||
</Box>
|
||||
</Section>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
const Story = (properties, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
const {
|
||||
story,
|
||||
wanted = false,
|
||||
} = properties;
|
||||
const [fullStories, setFullStories] = useLocalState(context, "fullStories", []);
|
||||
const [censorMode, _setCensorMode] = useLocalState(context, "censorMode", false);
|
||||
return (
|
||||
<Section
|
||||
className={classes([
|
||||
"Newscaster__story",
|
||||
wanted && "Newscaster__story--wanted",
|
||||
])}
|
||||
title={
|
||||
<Fragment>
|
||||
{wanted && <Icon name="exclamation-circle" mr="0.5rem" />}
|
||||
{((story.censor_flags & 2) && "[REDACTED]") || story.title || "News from " + story.author}
|
||||
</Fragment>
|
||||
}
|
||||
buttons={(
|
||||
<Box mt="0.25rem">
|
||||
<Box color="label">
|
||||
{(!wanted && censorMode) && (
|
||||
<Box display="inline">
|
||||
<Button
|
||||
enabled={story.censor_flags & 2}
|
||||
icon={(story.censor_flags & 2) ? "comment-slash" : "comment"}
|
||||
content={(story.censor_flags & 2) ? "Uncensor" : "Censor"}
|
||||
mr="0.5rem"
|
||||
mt="-0.25rem"
|
||||
onClick={() => act("censor_story", { uid: story.uid })}
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
<Box display="inline">
|
||||
<Icon name="user" /> {story.author} |
|
||||
{!wanted && (
|
||||
<Fragment>
|
||||
<Icon name="eye" /> {story.view_count.toLocaleString()} |
|
||||
</Fragment>
|
||||
)}
|
||||
<Icon name="clock" /> {timeAgo(story.publish_time, data.world_time)}
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
)}>
|
||||
<Box>
|
||||
{(story.censor_flags & 2) ? (
|
||||
"[REDACTED]"
|
||||
) : (
|
||||
<Fragment>
|
||||
{!!story.has_photo && (
|
||||
<PhotoThumbnail
|
||||
name={"story_photo_" + story.uid + ".png"}
|
||||
float="right"
|
||||
ml="0.5rem"
|
||||
/>
|
||||
)}
|
||||
{(story.body_short || story.body).split("\n").map(p => (
|
||||
<Box key={p}>
|
||||
{p || <br />}
|
||||
</Box>
|
||||
))}
|
||||
{story.body_short && (
|
||||
<Button
|
||||
content="Read more.."
|
||||
mt="0.5rem"
|
||||
onClick={() => setFullStories([...fullStories, story.uid])}
|
||||
/>
|
||||
)}
|
||||
<Box clear="right" />
|
||||
</Fragment>
|
||||
)}
|
||||
</Box>
|
||||
</Section>
|
||||
);
|
||||
};
|
||||
|
||||
const PhotoThumbnail = (properties, context) => {
|
||||
const {
|
||||
name,
|
||||
...rest
|
||||
} = properties;
|
||||
const [viewingPhoto, setViewingPhoto] = useLocalState(context, "viewingPhoto", "");
|
||||
return (
|
||||
<Box
|
||||
as="img"
|
||||
className="Newscaster__photo"
|
||||
src={name}
|
||||
onClick={() => setViewingPhoto(name)}
|
||||
{...rest}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const PhotoZoom = (properties, context) => {
|
||||
const [viewingPhoto, setViewingPhoto] = useLocalState(context, "viewingPhoto", "");
|
||||
return (
|
||||
<Modal
|
||||
className="Newscaster__photoZoom">
|
||||
<Box
|
||||
as="img"
|
||||
src={viewingPhoto}
|
||||
/>
|
||||
<Button
|
||||
icon="times"
|
||||
content="Close"
|
||||
color="grey"
|
||||
mt="1rem"
|
||||
onClick={() => setViewingPhoto("")}
|
||||
/>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
modalRegisterBodyOverride("create_channel", manageChannelModalBodyOverride);
|
||||
modalRegisterBodyOverride("manage_channel", manageChannelModalBodyOverride);
|
||||
modalRegisterBodyOverride("create_story", createStoryModalBodyOverride);
|
||||
modalRegisterBodyOverride("wanted_notice", wantedNoticeModalBodyOverride);
|
||||
@@ -29,7 +29,7 @@ export const modalRegisterBodyOverride = (id, bodyOverride) => {
|
||||
bodyOverrides[id] = bodyOverride;
|
||||
};
|
||||
|
||||
const modalAnswer = (context, id, answer, args) => {
|
||||
export const modalAnswer = (context, id, answer, args) => {
|
||||
const { act, data } = useBackend(context);
|
||||
if (!data.modal) {
|
||||
return;
|
||||
@@ -43,7 +43,7 @@ const modalAnswer = (context, id, answer, args) => {
|
||||
});
|
||||
};
|
||||
|
||||
const modalClose = (context, id) => {
|
||||
export const modalClose = (context, id) => {
|
||||
const { act } = useBackend(context);
|
||||
act('modal_close', {
|
||||
id: id,
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -129,7 +129,8 @@ Byond.call('', {
|
||||
<body>
|
||||
|
||||
<!-- tgui container -->
|
||||
<div id="react-root"></div>
|
||||
<div id="react-root"><div class="theme-nanotrasen"><div class="Layout Window "><div class="TitleBar Window__titleBar "><i class="TitleBar__statusIcon fas fa-eye color-good "></i><div class="TitleBar__title">Security Newscaster</div><div class="TitleBar__dragZone"></div><div class="TitleBar__close TitleBar__clickable">×</div></div><div class="Window__rest "><div class="Layout__content "
|
||||
id="Layout__content"><table class="Table color-bg-blue " style="height: 100%;"><tbody><tr class="Table__row "><td class="Table__cell Table__cell--collapsing "><div class="Section Section--level--1 "><div class="Section__content "><div tabindex="0" class="Button Button--hasContent Button--color--transparent " unselectable="false"><i class="fas fa-newspaper " style="font-size: 250%;"></i></div></div></div></td><td class="Table__cell "><div class="Section Section--level--1 "><div class="Section__title"><span class="Section__titleText">Main Content</span><div class="Section__buttons"></div></div><div class="Section__content ">sdfsf</div></div></td></tr></tbody></table></div></div></div></div></div>
|
||||
|
||||
<!-- Fatal error container -->
|
||||
<div id="FatalError" style="display: none">
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
@use 'sass:color';
|
||||
@use '../base.scss';
|
||||
@use '../functions.scss' as *;
|
||||
|
||||
$border-color: #88bfff !default;
|
||||
$border-color-disabled: color.adjust($border-color, $saturation: -100%, $lightness: -25%) !default;
|
||||
$border-radius: base.$border-radius !default;
|
||||
|
||||
.Input {
|
||||
@@ -18,6 +20,15 @@ $border-radius: base.$border-radius !default;
|
||||
margin-right: 2px;
|
||||
line-height: 17px;
|
||||
overflow: visible;
|
||||
white-space: nowrap;
|
||||
|
||||
&--disabled {
|
||||
color: #777;
|
||||
border-color: $border-color-disabled;
|
||||
border-color: rgba($border-color-disabled, 0.75);
|
||||
background-color: #333;
|
||||
background-color: rgba(0, 0, 0, 0.25);
|
||||
}
|
||||
}
|
||||
|
||||
.Input--fluid {
|
||||
@@ -56,3 +67,24 @@ $border-radius: base.$border-radius !default;
|
||||
color: rgba(255, 255, 255, 0.45);
|
||||
}
|
||||
}
|
||||
|
||||
.Input__textarea {
|
||||
border: 0;
|
||||
width: calc(100% + 4px);
|
||||
font-size: 12px;
|
||||
line-height: 17px;
|
||||
margin-left: -4px;
|
||||
font-family: Verdana, sans-serif;
|
||||
background-color: transparent;
|
||||
color: #fff;
|
||||
color: inherit;
|
||||
resize: both;
|
||||
overflow: auto;
|
||||
white-space: pre-wrap;
|
||||
|
||||
&:-ms-input-placeholder {
|
||||
font-style: italic;
|
||||
color: #777;
|
||||
color: rgba(255, 255, 255, 0.45);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,174 @@
|
||||
@use 'sass:color';
|
||||
@use '../colors.scss';
|
||||
|
||||
.Newscaster__menu {
|
||||
width: 40px;
|
||||
height: 100%;
|
||||
margin-right: 0.5rem;
|
||||
flex-basis: content;
|
||||
|
||||
&Button {
|
||||
color: colors.$grey;
|
||||
cursor: pointer;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
transition: color 0.1s;
|
||||
|
||||
&--title {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&--unread {
|
||||
background-color: color.scale(colors.$bad, $lightness: 25%);
|
||||
color: white;
|
||||
font-size: 10px;
|
||||
text-align: center;
|
||||
border-radius: 32px;
|
||||
display: inline-block;
|
||||
width: 12px;
|
||||
position: absolute;
|
||||
left: 22px;
|
||||
margin-top: 14px;
|
||||
}
|
||||
|
||||
&--selected {
|
||||
color: colors.$white;
|
||||
|
||||
&:before {
|
||||
content: "";
|
||||
background-color: colors.$primary;
|
||||
width: 2px;
|
||||
height: 24px;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&--security {
|
||||
color: colors.$primary;
|
||||
}
|
||||
|
||||
i {
|
||||
width: 30px;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
margin-left: -1px;
|
||||
margin-right: 0.5rem;
|
||||
margin-top: 1px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: colors.$white;
|
||||
|
||||
&:before {
|
||||
background-color: colors.$white;
|
||||
}
|
||||
}
|
||||
|
||||
&:not(:last-of-type) {
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
&--open {
|
||||
width: 175px;
|
||||
|
||||
.Newscaster__menuButton {
|
||||
&--title {
|
||||
display: inline;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$_gen_map: (
|
||||
'security': #a14c49,
|
||||
'engineering': #a17849,
|
||||
'medical': #499ea1,
|
||||
'science': #a14972,
|
||||
'service': #a1499e,
|
||||
'supply': #9ea149,
|
||||
);
|
||||
|
||||
.Newscaster__jobCategory {
|
||||
@each $dept-id, $dept-color in $_gen_map {
|
||||
&--#{$dept-id} {
|
||||
.Section__title {
|
||||
color: $dept-color;
|
||||
border-bottom: 2px solid $dept-color !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.Newscaster__jobOpening {
|
||||
&--command {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
&:not(:last-child) {
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.Newscaster__emptyNotice {
|
||||
color: colors.$label;
|
||||
text-align: center;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translateY(-50%) translateX(-50%);
|
||||
|
||||
i {
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
}
|
||||
|
||||
.Newscaster__photo {
|
||||
cursor: pointer;
|
||||
width: 96px;
|
||||
border: 1px solid colors.$black;
|
||||
transition: border-color 0.1s;
|
||||
-ms-interpolation-mode: nearest-neighbor;
|
||||
|
||||
&:hover {
|
||||
border-color: color.scale(colors.$black, $lightness: 50%);;
|
||||
}
|
||||
}
|
||||
|
||||
.Newscaster__photoZoom {
|
||||
text-align: center;
|
||||
|
||||
& > img {
|
||||
transform: scale(2);
|
||||
-ms-interpolation-mode: nearest-neighbor;
|
||||
}
|
||||
|
||||
& > .Button {
|
||||
position: absolute;
|
||||
width: 64px;
|
||||
left: 50%;
|
||||
margin-left: -32px;
|
||||
bottom: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
.Newscaster__story {
|
||||
&--wanted {
|
||||
background-color: rgba(colors.$bad, 0.1);
|
||||
|
||||
.Section__title {
|
||||
color: colors.$bad;
|
||||
border-bottom: 2px solid #a14c49 !important;
|
||||
}
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
}
|
||||
@@ -46,6 +46,7 @@
|
||||
@include meta.load-css('./interfaces/PoolController.scss');
|
||||
@include meta.load-css('./interfaces/Roulette.scss');
|
||||
@include meta.load-css('./interfaces/SecurityRecords.scss');
|
||||
@include meta.load-css('./interfaces/Newscaster.scss');
|
||||
|
||||
// Layouts
|
||||
@include meta.load-css('./layouts/Layout.scss');
|
||||
|
||||
@@ -23,4 +23,7 @@
|
||||
));
|
||||
@include meta.load-css('../components/NoticeBox.scss');
|
||||
@include meta.load-css('../components/Section.scss');
|
||||
|
||||
// Interfaces
|
||||
@include meta.load-css('../interfaces/Newscaster.scss');
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user