diff --git a/code/datums/periodic_news.dm b/code/datums/periodic_news.dm index d0d1f01ebf3..fb6dcd3fb1b 100644 --- a/code/datums/periodic_news.dm +++ b/code/datums/periodic_news.dm @@ -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) diff --git a/code/game/machinery/newscaster.dm b/code/game/machinery/newscaster.dm deleted file mode 100644 index c2396ab1d9f..00000000000 --- a/code/game/machinery/newscaster.dm +++ /dev/null @@ -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 = "\[REDACTED\]" - 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 = "ERROR: Could not submit feed channel to Network." - 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 = "Feed channel '[channel_name]' created successfully." - 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 = "ERROR: Could not submit feed story to Network." - 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 = "Feed story successfully submitted to [channel_name]." - 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 = "Unable to print newspaper. Insufficient paper. Please notify maintenance personnell to refill machine storage." - temp_back_screen = NEWSCASTER_MAIN - else - print_paper() - temp = "Printing successful. Please receive your newspaper from the bottom of the machine." - 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 = "ERROR: Wanted issue rejected by Network." - 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 = "Wanted issue for [channel_name] is now in Network Circulation." - 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 = "Wanted issue for [channel_name] successfully edited." - 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 = "Wanted Issue successfully deleted from Circulation" - 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, "Now [anchored ? "un" : ""]securing [name]") - 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, "The broken remains of [src] fall on the ground.") - new /obj/item/stack/sheet/metal(loc, 5) - new /obj/item/shard(loc) - new /obj/item/shard(loc) - else - to_chat(user, "You [anchored ? "un" : ""]secure [name].") - 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+="
The Griffon
" - dat+="
Nanotrasen-standard newspaper, for use on Nanotrasen Space Facilities

" - if(isemptylist(news_content)) - if(important_message) - dat+="Contents:
" - else - dat+="Other than the title, the rest of the newspaper is unprinted..." - else - dat+="Contents:
" - if(scribble_page==curr_page) - dat+="
There is a small scribble near the end of this page... It reads: \"[scribble]\"" - dat+= "
Next Page
Done reading
" - 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+="[C.channel_name] \[created by: [C.author]\]

" - if(C.censored) - dat+="This channel was deemed dangerous to the general welfare of the station and therefore marked with a D-Notice. 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+="" - if(scribble_page==curr_page) - dat+="
There is a small scribble near the end of this page... It reads: \"[scribble]\"" - dat+= "

Previous Page
Next Page
" - if(2) //Last page - for(var/datum/feed_channel/NP in news_content) - pages++ - if(important_message!=null) - dat+="
Wanted Issue:


" - dat+="Criminal name: [important_message.author]
" - dat+="Description: [important_message.body]
" - dat+="Photo:: " - if(important_message.img) - user << browse_rsc(important_message.img, "tmp_photow.png") - dat+="
" - else - dat+="None" - else - dat+="Apart from some uninteresting Classified ads, there's nothing on this page..." - if(scribble_page==curr_page) - dat+="
There is a small scribble near the end of this page... It reads: \"[scribble]\"" - dat+= "
Previous Page
" - else - dat+="I'm sorry to break your immersion. This shit's bugged. Report this bug to Agouri, polyxenitopalidou@gmail.com" - - dat+="

[curr_page+1]
" - 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, "There's already a scribble in this page... You wouldn't want to make things too cluttered, would you?") - 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 diff --git a/code/modules/admin/admin.dm b/code/modules/admin/admin.dm index 45971e6993e..b5bfc01ea79 100644 --- a/code/modules/admin/admin.dm +++ b/code/modules/admin/admin.dm @@ -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("Admin Newscaster

Admin Newscaster Unit

") - - switch(admincaster_screen) - if(0) - dat += {"Welcome to the admin newscaster.
Here you can add, edit and censor every newspiece on the network. -
Feed channels and stories entered through here will be uneditable and handled as official news by the rest of the units. -
Note that this panel allows full freedom over the news network, there are no constrictions except the few basic ones. Don't break things! - "} - if(GLOB.news_network.wanted_issue) - dat+= "
Read Wanted Issue" - - dat+= {"

Create Feed Channel -
View Feed Channels -
Submit new Feed story -

Exit - "} - - var/wanted_already = 0 - if(GLOB.news_network.wanted_issue) - wanted_already = 1 - - dat+={"
Feed Security functions:
-
[(wanted_already) ? ("Manage") : ("Publish")] \"Wanted\" Issue -
Censor Feed Stories -
Mark Feed Channel with Nanotrasen D-Notice (disables and locks the channel. -

The newscaster recognises you as:
[src.admincaster_signature]
- "} - if(1) - dat+= "Station Feed Channels
" - if( isemptylist(GLOB.news_network.network_channels) ) - dat+="No active channels found..." - else - for(var/datum/feed_channel/CHANNEL in GLOB.news_network.network_channels) - if(CHANNEL.is_admin_channel) - dat+="[CHANNEL.channel_name]
" - else - dat+="[CHANNEL.channel_name] [(CHANNEL.censored) ? ("***") : ""]
" - dat+={"

Refresh -
Back - "} - - if(2) - dat+={" - Creating new Feed Channel... -
Channel Name: [src.admincaster_feed_channel.channel_name]
- Channel Author: [src.admincaster_signature]
- Will Accept Public Feeds: [(src.admincaster_feed_channel.locked) ? ("NO") : ("YES")]

-
Submit

Cancel
- "} - if(3) - dat+={" - Creating new Feed Message... -
Receiving Channel: [src.admincaster_feed_channel.channel_name]
- Message Author: [src.admincaster_signature]
- Message Body: [src.admincaster_feed_message.body]
-
Submit

Cancel
- "} - if(4) - dat+={" - Feed story successfully submitted to [src.admincaster_feed_channel.channel_name].

-
Return
- "} - if(5) - dat+={" - Feed Channel [src.admincaster_feed_channel.channel_name] created successfully.

-
Return
- "} - if(6) - dat+="ERROR: Could not submit Feed story to Network.

" - if(src.admincaster_feed_channel.channel_name=="") - dat+="•Invalid receiving channel name.
" - if(src.admincaster_feed_message.body == "" || src.admincaster_feed_message.body == "\[REDACTED\]") - dat+="•Invalid message body.
" - dat+="
Return
" - if(7) - dat+="ERROR: Could not submit Feed Channel to Network.

" - if(src.admincaster_feed_channel.channel_name =="" || src.admincaster_feed_channel.channel_name == "\[REDACTED\]") - dat+="•Invalid channel name.
" - 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+="•Channel name already in use.
" - dat+="
Return
" - if(9) - dat+="[src.admincaster_feed_channel.channel_name]: \[created by: [src.admincaster_feed_channel.author]\]
" - if(src.admincaster_feed_channel.censored) - dat+={" - ATTENTION: 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.

- "} - else - if( isemptylist(src.admincaster_feed_channel.messages) ) - dat+="No feed messages found in channel...
" - else - var/i = 0 - for(var/datum/feed_message/MESSAGE in src.admincaster_feed_channel.messages) - i++ - dat+="-[MESSAGE.body]
" - if(MESSAGE.img) - usr << browse_rsc(MESSAGE.img, "tmp_photo[i].png") - dat+="

" - dat+="\[Story by [MESSAGE.author]\]
" - dat+={" -

Refresh -
Back - "} - if(10) - dat+={" - Nanotrasen Feed Censorship Tool
- 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 \[REDACTED\] tag above it.
-
Select Feed channel to get Stories from:
- "} - if(isemptylist(GLOB.news_network.network_channels)) - dat+="No feed channels found active...
" - else - for(var/datum/feed_channel/CHANNEL in GLOB.news_network.network_channels) - dat+="[CHANNEL.channel_name] [(CHANNEL.censored) ? ("***") : ""]
" - dat+="
Cancel" - if(11) - dat+={" - Nanotrasen D-Notice Handler
- 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.
- "} - if(isemptylist(GLOB.news_network.network_channels)) - dat+="No feed channels found active...
" - else - for(var/datum/feed_channel/CHANNEL in GLOB.news_network.network_channels) - dat+="[CHANNEL.channel_name] [(CHANNEL.censored) ? ("***") : ""]
" - - dat+="
Back" - if(12) - dat+={" - [src.admincaster_feed_channel.channel_name]: \[ created by: [src.admincaster_feed_channel.author] \]
- [(src.admincaster_feed_channel.author=="\[REDACTED\]") ? ("Undo Author censorship") : ("Censor channel Author")]
- "} - if( isemptylist(src.admincaster_feed_channel.messages) ) - dat+="No feed messages found in channel...
" - else - for(var/datum/feed_message/MESSAGE in src.admincaster_feed_channel.messages) - dat+={" - -[MESSAGE.body]
\[Story by [MESSAGE.author]\]
- [(MESSAGE.body == "\[REDACTED\]") ? ("Undo story censorship") : ("Censor story")] - [(MESSAGE.author == "\[REDACTED\]") ? ("Undo Author Censorship") : ("Censor message Author")]
- "} - dat+="
Back" - if(13) - dat+={" - [src.admincaster_feed_channel.channel_name]: \[ created by: [src.admincaster_feed_channel.author] \]
- Channel messages listed below. If you deem them dangerous to the station, you can Bestow a D-Notice upon the channel.
- "} - if(src.admincaster_feed_channel.censored) - dat+={" - ATTENTION: 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.

- "} - else - if( isemptylist(src.admincaster_feed_channel.messages) ) - dat+="No feed messages found in channel...
" - else - for(var/datum/feed_message/MESSAGE in src.admincaster_feed_channel.messages) - dat+="-[MESSAGE.body]
\[Story by [MESSAGE.author]\]
" - - dat+="
Back" - if(14) - dat+="Wanted Issue Handler:" - var/wanted_already = 0 - var/end_param = 1 - if(GLOB.news_network.wanted_issue) - wanted_already = 1 - end_param = 2 - if(wanted_already) - dat+="
A wanted issue is already in Feed Circulation. You can edit or cancel it below.
" - dat+={" -
- Criminal Name: [src.admincaster_feed_message.author]
- Description: [src.admincaster_feed_message.body]
- "} - if(wanted_already) - dat+="Wanted Issue created by: [GLOB.news_network.wanted_issue.backup_author]
" - else - dat+="Wanted Issue will be created under prosecutor: [src.admincaster_signature]
" - dat+="
[(wanted_already) ? ("Edit Issue") : ("Submit")]" - if(wanted_already) - dat+="
Take down Issue" - dat+="
Cancel" - if(15) - dat+={" - Wanted issue for [src.admincaster_feed_message.author] is now in Network Circulation.

-
Return
- "} - if(16) - dat+="ERROR: Wanted Issue rejected by Network.

" - if(src.admincaster_feed_message.author =="" || src.admincaster_feed_message.author == "\[REDACTED\]") - dat+="•Invalid name for person wanted.
" - if(src.admincaster_feed_message.body == "" || src.admincaster_feed_message.body == "\[REDACTED\]") - dat+="•Invalid description.
" - dat+="
Return
" - if(17) - dat+={" - Wanted Issue successfully deleted from Circulation
-
Return
- "} - if(18) - dat+={" - -- STATIONWIDE WANTED ISSUE --
\[Submitted by: [GLOB.news_network.wanted_issue.backup_author]\]
- Criminal: [GLOB.news_network.wanted_issue.author]
- Description: [GLOB.news_network.wanted_issue.body]
- Photo:: - "} - if(GLOB.news_network.wanted_issue.img) - usr << browse_rsc(GLOB.news_network.wanted_issue.img, "tmp_photow.png") - dat+="
" - else - dat+="None" - dat+="
Back
" - if(19) - dat+={" - Wanted issue for [src.admincaster_feed_message.author] successfully edited.

-
Return
- "} - 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 diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index 70476905971..58529a80834 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -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*/ diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm index 262fab1775c..0b56014dd21 100644 --- a/code/modules/admin/topic.dm +++ b/code/modules/admin/topic.dm @@ -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 != "\[REDACTED\]") - FC.backup_author = FC.author - FC.author = "\[REDACTED\]" - 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 != "\[REDACTED\]") - MSG.backup_author = MSG.author - MSG.author = "\[REDACTED\]" - 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 != "\[REDACTED\]") - MSG.backup_body = MSG.body - MSG.body = "\[REDACTED\]" - 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") diff --git a/code/modules/economy/Economy.dm b/code/modules/economy/Economy.dm index 8f06db02f34..19ad16ac9a5 100644 --- a/code/modules/economy/Economy.dm +++ b/code/modules/economy/Economy.dm @@ -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 diff --git a/code/modules/economy/Economy_Events.dm b/code/modules/economy/Economy_Events.dm index d7754f122ea..a699e3d2887 100644 --- a/code/modules/economy/Economy_Events.dm +++ b/code/modules/economy/Economy_Events.dm @@ -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) diff --git a/code/modules/economy/Economy_Events_Mundane.dm b/code/modules/economy/Economy_Events_Mundane.dm index cfabd6cdbe7..d8043f031f1 100644 --- a/code/modules/economy/Economy_Events_Mundane.dm +++ b/code/modules/economy/Economy_Events_Mundane.dm @@ -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") diff --git a/code/modules/events/money_lotto.dm b/code/modules/events/money_lotto.dm index 4656aa5aa8f..de5346805de 100644 --- a/code/modules/events/money_lotto.dm +++ b/code/modules/events/money_lotto.dm @@ -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 [winner_name] for recieving the Nyx Stellar Slam Lottery, and receiving the out of this world sum of [winner_sum] credits!" if(!deposit_success) newMsg.body += "
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") diff --git a/code/modules/newscaster/datums.dm b/code/modules/newscaster/datums.dm new file mode 100644 index 00000000000..9bb660d2a02 --- /dev/null +++ b/code/modules/newscaster/datums.dm @@ -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]" diff --git a/code/modules/newscaster/defines.dm b/code/modules/newscaster/defines.dm new file mode 100644 index 00000000000..03b6868c58d --- /dev/null +++ b/code/modules/newscaster/defines.dm @@ -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) diff --git a/code/modules/newscaster/newscaster.dm b/code/modules/newscaster/newscaster.dm new file mode 100644 index 00000000000..ca5d4ec1d02 --- /dev/null +++ b/code/modules/newscaster/newscaster.dm @@ -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 = "\[REDACTED\]" + /// 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("[usr] inserts [P] into [src]'s photo slot.",\ + "You insert [P] into [src]'s photo slot.") + 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("[src]'s photo slot quietly whirs as it prints [P] inside it.") + 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("[src] ejects [P] from its photo slot into [user]'s hand.") + else + visible_message("[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, "Now [anchored ? "un" : ""]securing [name]") + 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, "The broken remains of [src] fall on the ground.") + new /obj/item/stack/sheet/metal(loc, 5) + new /obj/item/shard(loc) + new /obj/item/shard(loc) + else + to_chat(user, "You [anchored ? "un" : ""]secure [name].") + 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+="
The Griffon
" + dat+="
Nanotrasen-standard newspaper, for use on Nanotrasen Space Facilities

" + if(isemptylist(news_content)) + if(important_message) + dat+="Contents:
" + else + dat+="Other than the title, the rest of the newspaper is unprinted..." + else + dat+="Contents:
" + if(scribble_page==curr_page) + dat+="
There is a small scribble near the end of this page... It reads: \"[scribble]\"" + dat+= "
Next Page
Done reading
" + 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+="[C.channel_name] \[created by: [C.author]\]

" + if(C.frozen) + dat+="This channel was deemed dangerous to the general welfare of the station and therefore marked with a D-Notice. 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+="" + if(scribble_page==curr_page) + dat+="
There is a small scribble near the end of this page... It reads: \"[scribble]\"" + dat+= "

Previous Page
Next Page
" + if(2) //Last page + for(var/datum/feed_channel/NP in news_content) + pages++ + if(important_message!=null) + dat+="
Wanted Issue:


" + dat+="Criminal name: [important_message.author]
" + dat+="Description: [important_message.body]
" + dat+="Photo:: " + if(important_message.img) + user << browse_rsc(important_message.img, "tmp_photow.png") + dat+="
" + else + dat+="None" + else + dat+="Apart from some uninteresting Classified ads, there's nothing on this page..." + if(scribble_page==curr_page) + dat+="
There is a small scribble near the end of this page... It reads: \"[scribble]\"" + dat+= "
Previous Page
" + else + dat+="I'm sorry to break your immersion. This shit's bugged. Report this bug to Agouri, polyxenitopalidou@gmail.com" + + dat+="

[curr_page+1]
" + 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, "There's already a scribble in this page... You wouldn't want to make things too cluttered, would you?") + 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 diff --git a/nano/templates/newscaster.tmpl b/nano/templates/newscaster.tmpl deleted file mode 100644 index 4ddf376e71f..00000000000 --- a/nano/templates/newscaster.tmpl +++ /dev/null @@ -1,333 +0,0 @@ - - - - -

Welcome to Newscasting Unit #{{:data.unit_no}}.

-Interface & News networks Operational.
-Property of Nanotrasen

-
-{{if data.temp}} -
{{:data.temp}}
-
{{:helper.link('Return', 'arrow-left', {'setScreen' : data.temp_back_screen})}}
-{{else}} - {{if data.screen == 0}} -
- {{if data.wanted_issue}} -
{{:helper.link('Read Wanted issue', 'exclamation-triangle', {'view_wanted' : 1})}}
- {{/if}} -
{{:helper.link('View available NT jobs', 'share', {'jobs' : 1})}}
-
{{:helper.link('Create feed channel', 'plus', {'create_channel' : 1})}}
-
{{:helper.link('View feed channels', 'arrow-right', {'view' : 1})}}
-
{{:helper.link('Submit new feed story', 'share', {'create_feed_story' : 1})}}
-
{{:helper.link('Print newspaper', 'print', {'menu_paper' : 1})}}
-
{{:helper.link(data.silence ? 'Unsilence unit' : 'Silence unit', data.silence ? 'volume-up' : 'volume-off', {'silence_unit' : 1})}}
-
{{:helper.link('Re-scan user', 'refresh', {'refresh' : 1})}}
- {{if data.securityCaster}} -
Feed Security functions:
-
{{:helper.link(data.wanted_issue ? 'Manage wanted issue' : 'Publish wanted issue', data.wanted_issue ? 'pencil' : 'share', {'menu_wanted' : 1})}}
-
{{:helper.link('Censor feed stories', 'ban', {'menu_censor_story' : 1})}}
-
{{:helper.link('Mark feed channel with Nanotrasen D-Notice', 'thumb-tack', {'menu_censor_channel' : 1})}}
-
The newscaster recognises you as: {{:data.scanned_user}}
- {{/if}} -
- {{else data.screen == 1}} -

Station Feed Channels

-
- {{for data.channels}} -
- {{if value.admin}} - {{:helper.link('' + value.name + '', 'arrow-right', {'show_channel' : value.ref})}} - {{else}} - {{:helper.link(value.name, 'arrow-right', {'show_channel' : value.ref}, null, value.censored ? 'link linkDanger' : null)}} - {{/if}} -
- {{empty}} - No active channels found... - {{/for}} -
-
-
-
{{:helper.link('Refresh', 'refresh', {'refresh' : 1})}}
-
{{:helper.link('Back', 'arrow-left', {'setScreen' : 0})}}
-
- {{else data.screen == 2}} -

Creating new feed channel...

-
-
Channel name:
-
- {{:data.channel_name}} - {{:helper.link('Edit', 'pencil', {'set_channel_name' : 1})}} -
-
-
-
Channel author:
-
{{:data.scanned_user}}
-
-
-
Accept public feeds:
-
{{:helper.link(data.c_locked ? 'NO' : 'YES', data.c_locked ? 'times' : 'check', {'set_channel_lock' : 1})}}
-
-
- {{:helper.link('Submit', 'share', {'submit_new_channel' : 1})}} - {{:helper.link('Cancel', 'arrow-left', {'setScreen' : 0})}} -
- {{else data.screen == 3}} - Creating new feed message... -
-
Receiving channel:
-
- {{:data.channel_name}} - {{:helper.link('Edit', 'pencil', {'set_channel_receiving' : 1})}} -
-
-
-
Title:
-
- {{:data.title}} - {{:helper.link('Edit', 'pencil', {'set_message_title' : 1})}} -
-
-
-
Message body:
-
- {{:data.msg}} - {{:helper.link('Edit', 'pencil', {'set_new_message' : 1})}} -
-
-
-
Attached photo:
-
{{:helper.link(data.photo ? 'Dettach photo' : 'Attach photo', data.photo ? 'times' : 'paperclip', {'set_attachment' : 1})}}
-
-
- {{:helper.link('Submit', 'share', {'submit_new_message' : 1})}} - {{:helper.link('Cancel', 'arrow-left', {'setScreen' : 0})}} -
- {{else data.screen == 4}} - 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. -
-
Liquid paper remaining:
-
{{:data.paper_remaining}} cm3
-
-
- {{:helper.link('Print paper', 'print', {'print_paper' : 0})}} - {{:helper.link('Cancel', 'arrow-left', {'setScreen' : 0})}} -
- {{else data.screen == 5}} - {{:data.channel_name}} [created by: {{:data.author}}]
- Feed view count: {{:data.total_view_count}}
- {{if data.censored}} -
- ATTENTION: 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. -
- {{else}} - {{for data.messages}} -
-

- {{:value.title}}
-

- {{if value.img}} -
- {{/if}} -

- {{:value.body}}
-

- [{{:value.message_type}} by {{:value.author}}]
- Message view count: {{:value.view_count}} -
- {{empty}} - No feed messages found in channel... - {{/for}} - {{/if}} -
-
-
{{:helper.link('Refresh', 'refresh', {'refresh' : 0})}}
-
{{:helper.link('Back', 'arrow-left', {'setScreen' : 1})}}
-
- {{else data.screen == 6}} - Nanotrasen Feed Censorship Tool
- 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 [REDACTED] tag above it. -
- Select feed channel to get stories from: -
- {{for data.channels}} -
- {{if value.admin}} - {{:helper.link('' + value.name + '', '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}} -
- {{empty}} - No feed channels found active... - {{/for}} -
-
-
{{:helper.link('Back', 'arrow-left', {'setScreen' : 0})}}
-
- {{else data.screen == 7}} - Nanotrasen D-Notice Handler
- 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. -
-
- {{for data.channels}} -
- {{if value.admin}} - {{:helper.link('' + value.name + '', '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}} -
- {{empty}} - No feed channels found active... - {{/for}} -
-
-
-
{{:helper.link('Back', 'arrow-left', {'setScreen' : 0})}}
-
- {{else data.screen == 8}} - {{:data.channel_name}} [created by: {{:data.author}}]
- {{:helper.link(data.author_redacted ? 'Undo author censorship' : 'Censor channel author', null, {'censor_channel_author' : data.ref})}}

- {{for data.messages}} -
- {{:value.title}}
- {{:value.body}}
- {{if value.img}} -
- {{/if}} - [{{:value.message_type}} by {{:value.author}}]
- - {{: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})}} - -
- {{empty}} - No feed messages found in channel... - {{/for}} -
-
-
{{:helper.link('Back', 'arrow-left', {'setScreen' : 6})}}
-
- {{else data.screen == 9}} - {{:data.channel_name}} [created by: {{:data.author}}]
- Channel messages listed below. If you deem them dangerous to the station, you can
{{:helper.link(data.censored ? 'Remove the D-Notice on the channel' : 'Bestow a D-Notice upon the channel', null, {'toggle_d_notice' : data.ref})}}


- {{if data.censored}} -
- ATTENTION: 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. -
- {{else}} - {{for data.messages}} -
- {{:value.title}}
- {{:value.body}}
- {{if value.img}} -
- {{/if}} - [{{:value.message_type}} by {{:value.author}}] -
- {{empty}} - No feed messages found in channel... - {{/for}} - {{/if}} -
-
{{:helper.link('Back', 'arrow-left', {'setScreen' : 7})}}
-
- {{else data.screen == 10}} - Wanted Issue handler:
- {{if data.wanted_already}} - A wanted issue is already in Feed Circulation. You can edit or cancel it below.
- {{/if}} -
-
Criminal name:
-
- {{:data.channel_name}} - {{:helper.link('Edit', 'pencil', {'set_wanted_name' : 1})}} -
-
-
-
Description:
-
- {{:data.msg}} - {{:helper.link('Edit', 'pencil', {'set_wanted_desc' : 1})}} -
-
-
-
Attached photo:
-
{{:helper.link(data.photo ? 'Dettach photo' : 'Attach photo', data.photo ? 'times' : 'paperclip', {'set_attachment' : 1})}}
-
-
-
Author:
-
{{:data.wanted_already ? data.author : data.scanned_user}}
-
-
-
- {{: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})}} -
- {{else data.screen == 11}} -

STATIONWIDE WANTED ISSUE

- [Submitted by: {{:data.author}}]

-
-
Criminal:
-
{{:data.criminal}}
-
-
-
Description:
-
{{:data.description}}
-
-
-
Photo:

-
- {{if data.photo}} -
- {{else}} - None - {{/if}} -
-
-
-
{{:helper.link('Back', 'arrow-left', {'setScreen' : 0})}}
-
- {{else data.screen == 12}} -

CAREERS AT NANOTRASEN

- Work for a better future.
-
- {{for data.jobs}} -
- {{:value.title}}
-
- {{empty}} - No available jobs... - {{/for}} - -
-
-
{{:helper.link('Refresh', 'refresh', {'refresh' : 0})}}
-
{{:helper.link('Back', 'arrow-left', {'setScreen' : 1})}}
-
- {{/if}} -{{/if}} \ No newline at end of file diff --git a/paradise.dme b/paradise.dme index 61d25a2730f..122706e1971 100644 --- a/paradise.dme +++ b/paradise.dme @@ -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" diff --git a/tgui/packages/tgui/backend.js b/tgui/packages/tgui/backend.js index ed0d9428620..87cbf890c4d 100644 --- a/tgui/packages/tgui/backend.js +++ b/tgui/packages/tgui/backend.js @@ -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. diff --git a/tgui/packages/tgui/components/Input.js b/tgui/packages/tgui/components/Input.js index b7f2dcb1f15..d287f8c72f4 100644 --- a/tgui/packages/tgui/components/Input.js +++ b/tgui/packages/tgui/components/Input.js @@ -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}>
.
- + {multiline ? ( +