mirror of
https://github.com/Aurorastation/Aurora.3.git
synced 2026-07-21 21:10:30 +01:00
94d92803b4
Depends on #21458. Ports https://github.com/cmss13-devs/cmss13/pull/4229, with the original authors as: - https://github.com/tgstation/TerraGov-Marine-Corps/pull/1964 for the lighting controller (A-lexa) - https://github.com/tgstation/TerraGov-Marine-Corps/pull/4747 and https://github.com/tgstation/TerraGov-Marine-Corps/pull/7263 for the lighting (TiviPlus) - https://github.com/tgstation/tgstation/pull/54520 for the dir lighting component - https://github.com/tgstation/tgstation/pull/75018 for the out of bounds fix in lighting - https://github.com/tgstation/TerraGov-Marine-Corps/pull/6678 for the emissives (TiviPlus) The main driving reason behind this is that current lighting consumes way too much processing power, especially for things like odysseys/away sites where a billion light sources are processing/moving at once and the game slows down to a crawl. Hopefully this improves the situation by a good margin, but we will need some testmerging to confirm that. <img width="1349" height="1349" alt="image" src="https://github.com/user-attachments/assets/1059ba2b-c0c5-495a-9c76-2d75d0c42bf2" /> <img width="1349" height="1349" alt="image" src="https://github.com/user-attachments/assets/9704b0f6-4cf6-4dfd-a6cb-5702ad07d677" /> - [x] Resolve todos - [x] Look into open space fuckery (border objects) --------- Co-authored-by: Matt Atlas <liermattia@gmail.com> Co-authored-by: JohnWildkins <john.wildkins@gmail.com>
367 lines
11 KiB
Plaintext
367 lines
11 KiB
Plaintext
#define MAX_STATIC_WIDTH 24
|
|
#define FONT_STYLE "12pt 'TinyUnicode'"
|
|
#define SCROLL_RATE (0.04 SECONDS) // time per pixel
|
|
#define SCROLL_PADDING 2 // how many pixels we chop to make a smooth loop
|
|
#define LINE1_X 1
|
|
#define LINE1_Y -4
|
|
#define LINE2_X 1
|
|
#define LINE2_Y -11
|
|
#define STATUS_DISPLAY_FONT_DATUM /datum/font/tiny_unicode/size_12pt
|
|
|
|
// Status display
|
|
// (formerly Countdown timer display)
|
|
|
|
// Use to show shuttle ETA/ETD times
|
|
// Alert status
|
|
// And arbitrary messages set by comms computer
|
|
/obj/machinery/status_display
|
|
name = "status display"
|
|
icon = 'icons/obj/status_display.dmi'
|
|
icon_state = "frame"
|
|
layer = ABOVE_WINDOW_LAYER
|
|
anchored = 1
|
|
density = 0
|
|
idle_power_usage = 10
|
|
obj_flags = OBJ_FLAG_MOVES_UNSUPPORTED
|
|
var/hears_arrivals = FALSE
|
|
var/mode = 1 // 0 = Blank
|
|
// 1 = Shuttle timer
|
|
// 2 = Arbitrary message(s)
|
|
// 3 = alert picture
|
|
// 4 = Supply shuttle timer
|
|
|
|
var/picture_state // icon_state of alert picture
|
|
|
|
var/obj/effect/overlay/status_display_text/message1_overlay
|
|
var/obj/effect/overlay/status_display_text/message2_overlay
|
|
var/message1 = ""
|
|
var/message2 = ""
|
|
|
|
var/frequency = 1435 // radio frequency
|
|
|
|
var/friendc = 0 // track if Friend Computer mode
|
|
var/ignore_friendc = 0
|
|
|
|
var/const/STATUS_DISPLAY_BLANK = 0
|
|
var/const/STATUS_DISPLAY_TRANSFER_SHUTTLE_TIME = 1
|
|
var/const/STATUS_DISPLAY_MESSAGE = 2
|
|
var/const/STATUS_DISPLAY_ALERT = 3
|
|
var/const/STATUS_DISPLAY_TIME = 4
|
|
var/const/STATUS_DISPLAY_CUSTOM = 99
|
|
|
|
/// Normal text color
|
|
var/text_color = COLOR_DISPLAY_BLUE
|
|
/// Color for headers, eg. "- ETA -"
|
|
var/header_text_color = COLOR_DISPLAY_PURPLE
|
|
|
|
/obj/machinery/status_display/Destroy()
|
|
SSmachinery.all_status_displays -= src
|
|
SSradio.remove_object(src,frequency)
|
|
return ..()
|
|
|
|
// screens have a layer above, so we can't attach here
|
|
/obj/machinery/status_display/can_attach_sticker(var/mob/user, var/obj/item/sticker/S)
|
|
to_chat(user, SPAN_WARNING("\The [src]'s non-stick surface prevents you from attaching a sticker to it!"))
|
|
return FALSE
|
|
|
|
// register for radio system
|
|
/obj/machinery/status_display/Initialize()
|
|
. = ..()
|
|
SSmachinery.all_status_displays += src
|
|
if (hears_arrivals)
|
|
SSradio.add_object(src, frequency, RADIO_ARRIVALS)
|
|
else
|
|
SSradio.add_object(src, frequency)
|
|
update_lighting()
|
|
|
|
// timed process
|
|
/obj/machinery/status_display/process()
|
|
if(stat & NOPOWER)
|
|
remove_display()
|
|
update_lighting()
|
|
return
|
|
update()
|
|
|
|
/obj/machinery/status_display/emp_act(severity)
|
|
. = ..()
|
|
|
|
if(stat & (BROKEN|NOPOWER))
|
|
return
|
|
|
|
set_picture("ai_bsod")
|
|
|
|
// set what is displayed
|
|
/obj/machinery/status_display/proc/update()
|
|
remove_display()
|
|
if(friendc && !ignore_friendc)
|
|
set_picture("ai_friend")
|
|
AddOverlays(emissive_appearance(icon, "outline", src, alpha = src.alpha))
|
|
return 1
|
|
|
|
switch(mode)
|
|
if(STATUS_DISPLAY_BLANK) //blank
|
|
remove_messages()
|
|
return 1
|
|
if(STATUS_DISPLAY_TRANSFER_SHUTTLE_TIME) //emergency shuttle timer
|
|
if(GLOB.evacuation_controller)
|
|
if(GLOB.evacuation_controller.is_prepared())
|
|
message1 = "-ETD-"
|
|
if (GLOB.evacuation_controller.waiting_to_leave())
|
|
message2 = "Launch"
|
|
else
|
|
message2 = get_shuttle_timer()
|
|
set_messages(message1, message2)
|
|
AddOverlays(emissive_appearance(icon, "outline", src, alpha = src.alpha))
|
|
else if(GLOB.evacuation_controller.has_eta())
|
|
message1 = "-ETA-"
|
|
message2 = get_shuttle_timer()
|
|
set_messages(message1, message2)
|
|
AddOverlays(emissive_appearance(icon, "outline", src, alpha = src.alpha))
|
|
return 1
|
|
if(STATUS_DISPLAY_MESSAGE) //custom messages
|
|
arrange_displayed_texts(message1, message2)
|
|
if(STATUS_DISPLAY_ALERT)
|
|
set_picture(picture_state)
|
|
return 1
|
|
if(STATUS_DISPLAY_TIME)
|
|
message1 = "-Time-"
|
|
message2 = worldtime2text()
|
|
set_messages(message1, message2)
|
|
arrange_displayed_texts(message1, message2)
|
|
return 0
|
|
|
|
/obj/machinery/status_display/proc/arrange_displayed_texts(message1, message2)
|
|
var/line1_metric
|
|
var/line2_metric
|
|
var/line_pair
|
|
var/datum/font/display_font = new STATUS_DISPLAY_FONT_DATUM()
|
|
line1_metric = display_font.get_metrics(message1)
|
|
line2_metric = display_font.get_metrics(message2)
|
|
line_pair = (line1_metric > line2_metric ? line1_metric : line2_metric)
|
|
var/overlay = update_message(message1_overlay, LINE1_Y, message1, LINE1_X, line_pair)
|
|
if(overlay)
|
|
message1_overlay = overlay
|
|
overlay = update_message(message2_overlay, LINE2_Y, message2, LINE2_X, line_pair)
|
|
if(overlay)
|
|
message2_overlay = overlay
|
|
if(message1 == "" && message2 == "")
|
|
return 1
|
|
else
|
|
AddOverlays(emissive_appearance(icon, "outline", src, alpha = src.alpha))
|
|
return 1
|
|
|
|
/obj/machinery/status_display/get_examine_text(mob/user, distance, is_adjacent, infix, suffix)
|
|
. = ..()
|
|
if(mode != STATUS_DISPLAY_BLANK && mode != STATUS_DISPLAY_ALERT)
|
|
if(message1_overlay || message2_overlay)
|
|
. += "The display says:"
|
|
if(message1_overlay.message)
|
|
. += "<br>\t<tt>[html_encode(message1_overlay.message)]</tt>"
|
|
if(message2_overlay.message)
|
|
. += "<br>\t<tt>[html_encode(message2_overlay.message)]</tt>"
|
|
|
|
/obj/machinery/status_display/proc/set_picture(state)
|
|
remove_display()
|
|
picture_state = state
|
|
AddOverlays(picture_state)
|
|
update_lighting()
|
|
|
|
/obj/machinery/status_display/proc/set_messages(line1, line2)
|
|
var/message_changed = FALSE
|
|
if(line1 != message1)
|
|
message1 = line1
|
|
message_changed = TRUE
|
|
|
|
if(line2 != message2)
|
|
message2 = line2
|
|
message_changed = TRUE
|
|
|
|
if(message_changed)
|
|
update_lighting()
|
|
|
|
/**
|
|
* Remove both message objs and null the fields.
|
|
* Don't call this in subclasses.
|
|
*/
|
|
/obj/machinery/status_display/proc/remove_messages()
|
|
if(message1_overlay)
|
|
QDEL_NULL(message1_overlay)
|
|
if(message2_overlay)
|
|
QDEL_NULL(message2_overlay)
|
|
|
|
/**
|
|
* Create/update message overlay.
|
|
* They must be handled as real objects for the animation to run.
|
|
* Don't call this in subclasses.
|
|
* Arguments:
|
|
* * overlay - the current /obj/effect/overlay/status_display_text instance
|
|
* * line_y - The Y offset to render the text.
|
|
* * x_offset - Used to offset the text on the X coordinates, not usually needed.
|
|
* * message - the new message text.
|
|
* Returns new /obj/effect/overlay/status_display_text or null if unchanged.
|
|
*/
|
|
/obj/machinery/status_display/proc/update_message(obj/effect/overlay/status_display_text/overlay, line_y, message, x_offset, line_pair)
|
|
if(overlay && message == overlay.message)
|
|
return null
|
|
|
|
if(overlay)
|
|
qdel(overlay)
|
|
|
|
var/obj/effect/overlay/status_display_text/new_status_display_text = new(src, line_y, message, text_color, header_text_color, x_offset, line_pair)
|
|
// Draw our object visually "in front" of this display, taking advantage of sidemap
|
|
new_status_display_text.pixel_y = -32
|
|
new_status_display_text.pixel_z = 32
|
|
vis_contents += new_status_display_text
|
|
return new_status_display_text
|
|
|
|
/obj/machinery/status_display/proc/update_lighting()
|
|
if( \
|
|
(stat & (NOPOWER|BROKEN)) || \
|
|
(mode == STATUS_DISPLAY_BLANK) || \
|
|
(mode != STATUS_DISPLAY_ALERT && message1 == "" && message2 == "") \
|
|
)
|
|
set_light(0)
|
|
return
|
|
set_light(L_WALLMOUNT_RANGE, L_WALLMOUNT_POWER, LIGHT_COLOR_FAINT_CYAN) // blue light
|
|
|
|
/obj/machinery/status_display/proc/get_shuttle_timer()
|
|
var/timeleft = GLOB.evacuation_controller.get_eta()
|
|
if(timeleft < 0)
|
|
return ""
|
|
return "[add_zero(num2text((timeleft / 60) % 60),2)]:[add_zero(num2text(timeleft % 60), 2)]"
|
|
|
|
/obj/machinery/status_display/proc/get_supply_shuttle_timer()
|
|
var/datum/shuttle/autodock/ferry/supply/shuttle = SScargo.shuttle
|
|
if (!shuttle)
|
|
return "Error"
|
|
|
|
if(shuttle.has_arrive_time())
|
|
var/timeleft = round((shuttle.arrive_time - world.time) / 10,1)
|
|
if(timeleft < 0)
|
|
return "Late"
|
|
return "[add_zero(num2text((timeleft / 60) % 60),2)]:[add_zero(num2text(timeleft % 60), 2)]"
|
|
return ""
|
|
|
|
/obj/machinery/status_display/proc/get_arrivals_shuttle_timer()
|
|
var/datum/shuttle/autodock/ferry/arrival/shuttle = SSarrivals.shuttle
|
|
if (!shuttle)
|
|
return "Error"
|
|
|
|
if(shuttle.has_arrive_time())
|
|
var/timeleft = round((shuttle.arrive_time - world.time) / 10,1)
|
|
if(timeleft < 0)
|
|
return ""
|
|
return "[add_zero(num2text((timeleft / 60) % 60),2)]:[add_zero(num2text(timeleft % 60), 2)]"
|
|
return ""
|
|
|
|
/obj/machinery/status_display/proc/get_arrivals_shuttle_timer2()
|
|
if (!SSarrivals)
|
|
return "Error"
|
|
|
|
if(SSarrivals.launch_time)
|
|
var/timeleft = round((SSarrivals.launch_time - world.time) / 10,1)
|
|
if(timeleft < 0)
|
|
return ""
|
|
return "[add_zero(num2text((timeleft / 60) % 60),2)]:[add_zero(num2text(timeleft % 60), 2)]"
|
|
else
|
|
return "Launch"
|
|
|
|
/obj/machinery/status_display/proc/remove_display()
|
|
ClearOverlays()
|
|
vis_contents.Cut()
|
|
if(message1_overlay)
|
|
QDEL_NULL(message1_overlay)
|
|
if(message2_overlay)
|
|
QDEL_NULL(message2_overlay)
|
|
|
|
/**
|
|
* Nice overlay to make text smoothly scroll with no client updates after setup.
|
|
*/
|
|
/obj/effect/overlay/status_display_text
|
|
icon = 'icons/obj/status_display.dmi'
|
|
vis_flags = VIS_INHERIT_LAYER | VIS_INHERIT_PLANE | VIS_INHERIT_ID
|
|
|
|
/// The message this overlay is displaying.
|
|
var/message
|
|
|
|
// If the line is short enough to not marquee, and it matches this, it's a header.
|
|
var/static/regex/header_regex = regex("^-.*-$")
|
|
|
|
/obj/effect/overlay/status_display_text/Initialize(mapload, yoffset, line, text_color, header_text_color, xoffset = 0, line_pair)
|
|
. = ..()
|
|
|
|
maptext_y = yoffset
|
|
message = line
|
|
|
|
var/datum/font/display_font = new STATUS_DISPLAY_FONT_DATUM()
|
|
var/line_width = display_font.get_metrics(line)
|
|
|
|
if(line_width > MAX_STATIC_WIDTH)
|
|
// Marquee text
|
|
var/marquee_message = "[line] [line] [line]"
|
|
|
|
// Width of full content. Must of these is never revealed unless the user inputted a single character.
|
|
var/full_marquee_width = display_font.get_metrics("[marquee_message] ")
|
|
// We loop after only this much has passed.
|
|
var/looping_marquee_width = (display_font.get_metrics("[line] ]") - SCROLL_PADDING)
|
|
|
|
maptext = generate_text(marquee_message, center = FALSE, text_color = text_color)
|
|
maptext_width = full_marquee_width
|
|
maptext_x = 0
|
|
|
|
// Mask off to fit in screen.
|
|
add_filter("mask", 1, list(type = "alpha", icon = icon(icon, "outline")))
|
|
|
|
// Scroll.
|
|
var/time = line_pair * SCROLL_RATE
|
|
animate(src, maptext_x = (-looping_marquee_width) + MAX_STATIC_WIDTH, time = time, loop = -1)
|
|
animate(maptext_x = MAX_STATIC_WIDTH, time = 0)
|
|
else
|
|
// Centered text
|
|
var/color = header_regex.Find(line) ? header_text_color : text_color
|
|
maptext = generate_text(line, center = TRUE, text_color = color)
|
|
maptext_x = xoffset //Defaults to 0, this would be centered unless overided
|
|
|
|
/**
|
|
* Generate the actual maptext.
|
|
* Arguments:
|
|
* * text - the text to display
|
|
* * center - center the text if TRUE, otherwise right-align (the direction the text is coming from)
|
|
* * text_color - the text color
|
|
*/
|
|
/obj/effect/overlay/status_display_text/proc/generate_text(text, center, text_color)
|
|
return {"<div style="color:[text_color];font:[FONT_STYLE][center ? ";text-align:center" : "text-align:right"]" valign="top">[text]</div>"}
|
|
|
|
/obj/machinery/status_display/receive_signal(datum/signal/signal)
|
|
switch(signal.data["command"])
|
|
if("blank")
|
|
mode = STATUS_DISPLAY_BLANK
|
|
|
|
if("shuttle")
|
|
mode = STATUS_DISPLAY_TRANSFER_SHUTTLE_TIME
|
|
|
|
if("message")
|
|
message1 = signal.data["msg1"]
|
|
message2 = signal.data["msg2"]
|
|
mode = STATUS_DISPLAY_MESSAGE
|
|
set_messages(message1, message2)
|
|
|
|
if("alert")
|
|
mode = STATUS_DISPLAY_ALERT
|
|
set_picture(signal.data["picture_state"])
|
|
|
|
if("time")
|
|
mode = STATUS_DISPLAY_TIME
|
|
update()
|
|
|
|
#undef MAX_STATIC_WIDTH
|
|
#undef FONT_STYLE
|
|
#undef SCROLL_RATE
|
|
#undef LINE1_X
|
|
#undef LINE1_Y
|
|
#undef LINE2_X
|
|
#undef LINE2_Y
|
|
#undef STATUS_DISPLAY_FONT_DATUM
|
|
|
|
#undef SCROLL_PADDING
|