diff --git a/code/modules/newscaster/datums.dm b/code/modules/newscaster/datums.dm index 9bb660d2a02..9d5860897b6 100644 --- a/code/modules/newscaster/datums.dm +++ b/code/modules/newscaster/datums.dm @@ -9,7 +9,7 @@ var/datum/feed_message/wanted_issue /** - * Returns the [/datum/get_channel_by_name] with the given name, or null if not found. + * Returns the [/datum/feed_channel] with the given name, or null if not found. * * Arguments: * * name - The name @@ -20,6 +20,18 @@ if(FC.channel_name == name) return FC +/** + * Returns the [/datum/feed_channel] with the given author, or null if not found. + * + * Arguments: + * * author - The author + */ +/datum/feed_network/proc/get_channel_by_author(author) + for(var/fc in channels) + var/datum/feed_channel/FC = fc + if(FC.author == author) + return FC + /** * Returns the [/datum/feed_channel] at the given index, or null if not found. * @@ -31,6 +43,7 @@ return return channels[idx] + /** * # Feed Message * @@ -94,6 +107,8 @@ var/is_public = FALSE /// Whether the channel is frozen or not. var/frozen = FALSE + /// Whether the channel is censored or not. + var/censored = FALSE /// Whether the channel is admin-locked. var/admin_locked = FALSE @@ -133,6 +148,7 @@ messages = list() is_public = FALSE frozen = FALSE + censored = FALSE admin_locked = FALSE /** diff --git a/code/modules/newscaster/newscaster.dm b/code/modules/newscaster/obj/newscaster.dm similarity index 68% rename from code/modules/newscaster/newscaster.dm rename to code/modules/newscaster/obj/newscaster.dm index ca5d4ec1d02..f94e6fecd3e 100644 --- a/code/modules/newscaster/newscaster.dm +++ b/code/modules/newscaster/obj/newscaster.dm @@ -6,6 +6,14 @@ #define WANTED_NOTICE_DESC_MAX_LENGTH 512 #define STORIES_PER_LOAD 9999 // TODO during QP... +/** + * # Newscaster + * + * For all of the crew's news need. Includes reading, submitting and printing stories. + * + * Includes a security variant which can be used to issue wanted notices, censor channels and stories. + * Allows full access when aghosting. + */ /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!" @@ -35,25 +43,10 @@ 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\]" + /// Whether the newscaster is currently printing a newspaper or not. + var/is_printing = FALSE + /// Static list of jobs that shouldn't be advertised if a position is available. + var/static/list/jobblacklist /// Static, lazy list containing a user's last view time per channel. var/static/last_views @@ -63,7 +56,7 @@ /obj/machinery/newscaster/New() GLOB.allNewscasters += src - unit_number = GLOB.allNewscasters.len + unit_number = length(GLOB.allNewscasters) update_icon() //for any custom ones on the map... if(!last_views) last_views = list() @@ -72,6 +65,24 @@ /obj/machinery/newscaster/Initialize(mapload) . = ..() + if(!jobblacklist) + 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 + ) /obj/machinery/newscaster/Destroy() GLOB.allNewscasters -= src @@ -107,6 +118,53 @@ . = ..() update_icon() +/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/attack_ghost(mob/user) tgui_interact(user) @@ -117,7 +175,7 @@ /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) + scanned_user = get_scanned_user(user)["name"] ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) ui = new(user, src, ui_key, "Newscaster", name, 800, 600) @@ -130,6 +188,7 @@ data["is_security"] = is_security data["is_admin"] = user.can_admin_interact() data["is_silent"] = is_silent + data["is_printing"] = is_printing data["screen"] = screen data["modal"] = tgui_modal_data(src) if(data["modal"] && !isnull(data["modal"]["args"]["is_admin"])) @@ -141,7 +200,7 @@ data["wanted"] = get_message_data(GLOB.news_network.wanted_issue, user)[1] data["world_time"] = world.time - var/user_name = get_scanned_user(usr) + var/user_name = get_scanned_user(user)["name"] 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 @@ -177,19 +236,18 @@ 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 + 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 @@ -197,7 +255,7 @@ jobs[cat] = list() for(var/datum/job/job in SSjobs.occupations) - if(job_blacklisted(job)) + if(job.type in jobblacklist) continue if(job.is_position_available()) var/list/opening_data = list("title" = job.title) @@ -221,7 +279,7 @@ jobs["supply"] += opening_data // Append temp photo - if(photo && data["modal"] && data["modal"]["id"] in list("create_story", "wanted_notice")) + if(photo && data["modal"] && (data["modal"]["id"] in list("create_story", "wanted_notice"))) data["photo"] = list( name = photo.name, uid = photo.UID(), @@ -243,6 +301,7 @@ icon = C.icon, public = C.is_public, frozen = C.frozen, + censored = C.censored, admin = C.admin_locked, unread = 0, ) @@ -331,19 +390,19 @@ if("eject_photo") eject_photo(usr) return FALSE // Updating handled in that proc - if("freeze_channel") - if(is_security && !has_security_access(usr)) + if("censor_channel") + if(is_security && !get_scanned_user(usr)["security"]) 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") + set_temp("This channel has been locked by CentComm and thus cannot be (un)censored.", "danger") return - FC.frozen = !FC.frozen + FC.censored = !FC.censored if("censor_author", "censor_story") - if(is_security && !has_security_access(usr)) + if(is_security && !get_scanned_user(usr)["security"]) 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"]) @@ -359,7 +418,7 @@ else return FALSE if("clear_wanted_notice") - if(is_security && !has_security_access(usr)) + if(is_security && !get_scanned_user(usr)["security"]) 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 @@ -373,6 +432,13 @@ return FALSE if("toggle_mute") is_silent = !is_silent + if("print_newspaper") + if(is_printing) + return + if(paper_remaining <= 0) + set_temp("There is no more paper available.", "danger") + return + print_newspaper() else return FALSE @@ -396,7 +462,7 @@ // 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))) + if(!istype(FC) || !FC.can_modify(usr, get_scanned_user(usr)["name"])) return tgui_modal_message(src, id, "", arguments = list( uid = arguments["uid"], // Only when managing a channel @@ -429,7 +495,9 @@ 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 + if(GLOB.news_network.get_channel_by_author(author)) + set_temp("A channel with this author name already exists.", "danger") + return FC = new GLOB.news_network.channels += FC feedback_inc("newscaster_channels", 1) @@ -438,7 +506,7 @@ viewing_channel = FC else if (id == "manage_channel") // Channel management FC = locateUID(arguments["uid"]) - if(!FC || !FC.can_modify(usr, get_scanned_user(usr))) + if(!FC || !FC.can_modify(usr, get_scanned_user(usr)["name"])) return // Add/update the information FC.channel_name = copytext(name, 1, CHANNEL_NAME_MAX_LENGTH) @@ -457,7 +525,7 @@ 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/user_name = get_scanned_user(usr)["name"] var/datum/feed_channel/FC for(var/fc in GLOB.news_network.channels) var/datum/feed_channel/_FC = fc @@ -553,277 +621,71 @@ 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) - +/** + * Tries to obtain a mob's name and security access based on their ID. + * + * Arguments: + * * user - The user + */ /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) + . = list(name = "Unknown", security = user.can_admin_interact()) if(ishuman(user)) var/mob/living/carbon/human/M = user + // No ID, no luck if(!M.wear_id) - return FALSE + return + // Try to get the ID 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() + if(istype(ID)) + return list(name = "[ID.registered_name] ([ID.assignment])", security = has_access(list(), list(ACCESS_SECURITY), ID.access)) + else if(issilicon(user)) + var/mob/living/silicon/ai_user = user + return list(name = "[ai_user.name] ([ai_user.job])", security = TRUE) +/** + * Returns whether the machine's [/obj/machinery/newscaster/var/scanned_user] should update on interact. + * + * Arguments: + * * user - The user to check + */ /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) +/** + * Tries to print a newspaper with all of the content so far. + */ +/obj/machinery/newscaster/proc/print_newspaper() + if(paper_remaining <= 0 || is_printing) + return paper_remaining-- - return + feedback_inc("newscaster_newspapers_printed", 1) + // Print it + is_printing = TRUE + playsound(loc, 'sound/goonstation/machines/printer_dotmatrix.ogg', 50, TRUE) + visible_message("[src] whirs as it prints a newspaper.") + addtimer(CALLBACK(src, .proc/print_newspaper_finish), 5 SECONDS) + +/** + * Called when the timer following a call to [/obj/machinery/newscaster/proc/print_newspaper] finishes. + */ +/obj/machinery/newscaster/proc/print_newspaper_finish() + is_printing = FALSE + SStgui.update_uis(src) + // Create the newspaper + var/obj/item/newspaper/NP = new + NP.forceMove(loc) + // Populate the newspaper + NP.important_message = GLOB.news_network.wanted_issue + for(var/fc in GLOB.news_network.channels) + var/datum/feed_channel/FC = fc + NP.news_content += FC /** * Makes the newscaster say a message and change its icon state for a while. @@ -847,12 +709,15 @@ update_icon() /** - * Called when the timer following a news alert finishes. + * Called when the timer following a call to [/obj/machinery/newscaster/proc/alert_news] finishes. */ /obj/machinery/newscaster/proc/alert_timer_finish() alert = FALSE update_icon() +/** + * Ejects the currently loaded photo if there is one. + */ /obj/machinery/newscaster/verb/eject_photo_verb() set name = "Eject Photo" set category = "Object" diff --git a/code/modules/newscaster/obj/newspaper.dm b/code/modules/newscaster/obj/newspaper.dm new file mode 100644 index 00000000000..62266993651 --- /dev/null +++ b/code/modules/newscaster/obj/newspaper.dm @@ -0,0 +1,165 @@ +#define SCREEN_COVER 0 +#define SCREEN_PAGE_INNER 1 +#define SCREEN_PAGE_LAST 2 + +/** + * # Newspaper + * + * A newspaper displaying the stories of all channels contained within. + */ +/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 + attack_verb = list("bapped") + /// The current screen to display. + var/screen = 0 + /// The number of pages. + var/pages = 0 + /// The currently selected page. + var/curr_page = 0 + /// The channels to display as content. + var/list/datum/feed_channel/news_content + /// The security notice to display optionally. + var/datum/feed_message/important_message = null + /// The contents of a scribble made through pen, if any. + var/scribble = "" + /// The page of said scribble. + var/scribble_page = null + +/obj/item/newspaper/Initialize(mapload) + . = ..() + if(!news_content) + news_content = list() + +/obj/item/newspaper/attack_self(mob/user) + if(ishuman(user)) + var/mob/living/carbon/human/human_user = user + var/dat + pages = 0 + switch(screen) + if(SCREEN_COVER) //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(SCREEN_PAGE_INNER) // 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(SCREEN_PAGE_LAST) //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 unintelligible symbols!") + +/obj/item/newspaper/Topic(href, href_list) + ..() + var/mob/living/M = usr + if(!Adjacent(M)) + return + M.set_machine(src) + if(href_list["next_page"]) + if(curr_page == pages + 1) + return //Don't need that at all, but anyway. + else if(curr_page == pages) //We're at the middle, get to the end + screen = SCREEN_PAGE_LAST + else if(curr_page == 0) //We're at the start, get to the middle + screen = SCREEN_PAGE_INNER + curr_page++ + playsound(loc, "pageturn", 50, TRUE) + else if(href_list["prev_page"]) + if(curr_page == 0) + return + else if(curr_page == 1) + screen = SCREEN_COVER + else if(curr_page == pages + 1) //we're at the end, let's go back to the middle. + screen = SCREEN_PAGE_INNER + curr_page-- + playsound(loc, "pageturn", 50, TRUE) + if(loc == M) + attack_self(M) + +/obj/item/newspaper/attackby(obj/item/W, mob/user, 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 || !Adjacent(user)) + return + scribble_page = curr_page + scribble = s + user.visible_message("[user] scribbles something on [src].",\ + "You scribble on page number [curr_page] of [src].") + attack_self(user) + return + return ..() + +#undef SCREEN_COVER +#undef SCREEN_PAGE_INNER +#undef SCREEN_PAGE_LAST diff --git a/paradise.dme b/paradise.dme index 122706e1971..3091b299832 100644 --- a/paradise.dme +++ b/paradise.dme @@ -2102,10 +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\newscaster\obj\newscaster.dm" +#include "code\modules\newscaster\obj\newspaper.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/interfaces/Newscaster.js b/tgui/packages/tgui/interfaces/Newscaster.js index 8121f889f8f..13b6d6c0050 100644 --- a/tgui/packages/tgui/interfaces/Newscaster.js +++ b/tgui/packages/tgui/interfaces/Newscaster.js @@ -442,6 +442,7 @@ export const Newscaster = (properties, context) => { is_security, is_admin, is_silent, + is_printing, screen, channels, channel_idx = -1, @@ -469,51 +470,54 @@ export const Newscaster = (properties, context) => {
- setMenuOpen(!menuOpen)} - /> - act('headlines')}> - {totalUnread > 0 && ( - - {totalUnread >= 10 ? "9+" : totalUnread} - - )} - - act('jobs')} - /> - - - {channels.map(channel => ( + + act('channel', { uid: channel.uid })}> - {channel.unread > 0 && ( + icon="bars" + title="Toggle Menu" + onClick={() => setMenuOpen(!menuOpen)} + /> + act('headlines')}> + {totalUnread > 0 && ( - {channel.unread >= 10 ? "9+" : channel.unread} + {totalUnread >= 10 ? "9+" : totalUnread} )} - ))} - - - + act('jobs')} + /> + + + + {channels.map(channel => ( + act('channel', { uid: channel.uid })}> + {channel.unread > 0 && ( + + {channel.unread >= 10 ? "9+" : channel.unread} + + )} + + ))} + + {(!!is_security || !!is_admin) && ( @@ -546,13 +550,19 @@ export const Newscaster = (properties, context) => { onClick={() => modalOpen(context, "create_channel")} /> + act("print_newspaper")} + /> act("toggle_mute")} /> - +
@@ -568,6 +578,7 @@ const MenuButton = (properties, context) => { const { act } = useBackend(context); const { icon = "", + iconSpin, selected = false, security = false, onClick, @@ -584,8 +595,10 @@ const MenuButton = (properties, context) => { ])} onClick={onClick} {...rest}> + {selected && } @@ -651,11 +664,11 @@ const NewscasterFeed = (properties, context) => { {censorMode && (