Fixes borg/drone lights and adds some emissives. (#22602)

This PR fixes the intent-based lights and emissives on silicons
(borgs/drones). It also adds an emissive to the combat shielding robot
module, since it seems logical for that module to have it, and adds a
slight emissive to robots in emergency power mode to differentiate
between the out-of-power and disabled states.

Due to issues with tables in particular, a drone's emissives will be
disabled when it hides to prevent through-table emissive displays.

Robots with various states below:
Top: dead/out of power
Bottom: normal/hidden drone/unhidden drone/milborg with shield
<img width="391" height="149" alt="image"
src="https://github.com/user-attachments/assets/87da7210-4670-4fc2-8d4a-b53f1f2aec49"
/>
This commit is contained in:
Benjamin
2026-06-10 17:11:33 +00:00
committed by GitHub
parent 4c8c231672
commit 52a066371a
9 changed files with 107 additions and 33 deletions
+1
View File
@@ -12,6 +12,7 @@
else
layer = MOB_LAYER
to_chat(src, SPAN_NOTICE("You have stopped hiding."))
SEND_SIGNAL(src, COMSIG_MOB_ON_HIDE)
/mob/living/verb/set_walk_speed()
set category = "IC.Maneuver"
@@ -94,6 +94,7 @@
default_language = GLOB.all_languages[LANGUAGE_LOCAL_DRONE]
add_verb(src, /mob/living/proc/hide)
RegisterSignal(src, COMSIG_MOB_ON_HIDE, PROC_REF(on_hide))
remove_language(LANGUAGE_ROBOT)
add_language(LANGUAGE_ROBOT, FALSE)
add_language(LANGUAGE_DRONE, TRUE)
@@ -297,29 +298,31 @@
/mob/living/silicon/robot/drone/setup_eye_cache()
cached_eye_overlays = list(
I_HELP = image(icon, "[icon_state]-eyes_help"),
I_HURT = image(icon, "[icon_state]-eyes_harm"),
"emag" = image(icon, "[icon_state]-eyes_emag")
I_HELP = mutable_appearance(icon, "[icon_state]-eyes_help"),
I_HURT = mutable_appearance(icon, "[icon_state]-eyes_harm"),
"emag" = mutable_appearance(icon, "[icon_state]-eyes_emag")
)
if(eye_overlay)
CutOverlays(eye_overlay)
CutOverlays(list(eye_overlay, eye_emissive))
eye_overlay = cached_eye_overlays[a_intent]
AddOverlays(eye_overlay)
// Disables emissives while hiding due to table-clipping issues
eye_emissive = (layer == MOB_LAYER ? emissive_appearance(icon, "[icon_state]-eyes_help") : null)
AddOverlays(list(eye_overlay, eye_emissive))
/mob/living/silicon/robot/drone/setup_panel_cache()
cached_panel_overlays = list(
ROBOT_PANEL_EXPOSED = image(icon, "[icon_state]-openpanel+w"),
ROBOT_PANEL_CELL = image(icon, "[icon_state]-openpanel+c"),
ROBOT_PANEL_NO_CELL = image(icon, "[icon_state]-openpanel-c")
ROBOT_PANEL_EXPOSED = mutable_appearance(icon, "[icon_state]-openpanel+w"),
ROBOT_PANEL_CELL = mutable_appearance(icon, "[icon_state]-openpanel+c"),
ROBOT_PANEL_NO_CELL = mutable_appearance(icon, "[icon_state]-openpanel-c")
)
/mob/living/silicon/robot/drone/set_intent(var/set_intent)
a_intent = set_intent
CutOverlays(eye_overlay)
CutOverlays(list(eye_overlay, eye_emissive))
if(!stat)
eye_overlay = cached_eye_overlays[emagged ? "emag" : set_intent]
AddOverlays(eye_overlay)
AddOverlays(list(eye_overlay, eye_emissive))
/mob/living/silicon/robot/drone/choose_icon()
return
@@ -411,7 +414,7 @@
to_chat(src, "<b>Obey these laws:</b>")
laws.show_laws(src)
to_chat(src, SPAN_DANGER("ALERT: [user.real_name] is your new master. Obey your new laws and their commands."))
set_intent(I_HURT) // force them to hurt to update the eyes, they can swap to and fro if they wish, though - geeves
set_intent(a_intent) // Send an intent update so their eyes change to "emag"
return TRUE
/mob/living/silicon/robot/drone/proc/ai_hack(var/mob/user)
@@ -1,4 +1,8 @@
// DRONE ABILITIES
/**
* Sets a drone's mail tag, allowing it to traverse the mail system like a package would.
*/
/mob/living/silicon/robot/drone/verb/set_mail_tag()
set name = "Set Mail Tag"
set desc = "Tag yourself for delivery through the disposals system."
@@ -19,6 +23,11 @@
to_chat(src, SPAN_NOTICE("\The [D] acknowledges your signal."))
D.flush_count = D.flush_every_ticks
/// Override to allow us to process our eye cache
/mob/living/silicon/robot/drone/proc/on_hide()
SIGNAL_HANDLER
setup_eye_cache()
/mob/living/silicon/robot/drone/mouse_drop_dragged(atom/over, mob/user, src_location, over_location, params)
var/mob/living/carbon/H = over
if(!istype(H) || !Adjacent(H))
@@ -1,4 +1,7 @@
//Personal shielding for the combat module.
/**
* Personal shield for the military robot module.
* Deploys an energy shield that blocks a configurable percentage of damage using a robot's internal cell.
*/
/obj/item/borg/combat/shield
name = "personal shielding"
desc = "A powerful experimental module that turns aside or absorbs incoming attacks at the cost of charge."
@@ -8,10 +11,11 @@
/obj/item/borg/combat/shield/on_module_activate(mob/living/silicon/robot/R)
R.shield_overlay = image(R.icon, "[R.module_sprites[R.icontype][ROBOT_CHASSIS]]-shield")
R.AddOverlays(R.shield_overlay)
R.shield_emissive = emissive_appearance(R.icon, "[R.module_sprites[R.icontype][ROBOT_CHASSIS]]-shield")
R.AddOverlays(list(R.shield_overlay, R.shield_emissive))
/obj/item/borg/combat/shield/on_module_deactivate(mob/living/silicon/robot/R)
R.CutOverlays(R.shield_overlay)
R.CutOverlays(list(R.shield_overlay, R.shield_emissive))
/obj/item/borg/combat/shield/verb/set_shield_level()
set name = "Set shield level"
@@ -17,10 +17,16 @@
if(stat)
CutOverlays(eye_overlay)
has_cut_eye_overlay = TRUE
if(stat == DEAD) // Leave a bit of glow in the eyes while we're on backup power
CutOverlays(eye_emissive)
has_cut_eye_emissive = TRUE
else if(has_cut_eye_overlay)
eye_overlay = cached_eye_overlays[a_intent]
AddOverlays(eye_overlay)
has_cut_eye_overlay = null
has_cut_eye_overlay = FALSE
if(has_cut_eye_emissive)
AddOverlays(eye_emissive)
has_cut_eye_emissive = FALSE
if(stat != DEAD) //still using power
use_power()
process_killswitch()
+12 -1
View File
@@ -129,13 +129,24 @@
)
// Overlays
/// Used to keep track of our eye_overlay when we remove it due to being offline/depowered
var/has_cut_eye_overlay
/// Used to keep track of our eye_emissive overlay, so that it can be removed/added seperately from the eye overlay.
var/has_cut_eye_emissive
/// The robot's active eye overlay
var/image/eye_overlay
/// The robot's active eye emissive
var/image/eye_emissive
/// A list of cached eye overlays relevant to the robot's current module
var/list/image/cached_eye_overlays = list()
/// The robot's active panel overlay, used when a robot is open and undergoing maintenance
var/image/panel_overlay
/// A list of cached panel overlays relevant to the robot's current module.
var/list/image/cached_panel_overlays = list()
/// The robot's active shield overlay. Used by the military module's shielding systems.
var/image/shield_overlay
var/datum/weakref/holo_map
/// The robot's active shield emissive.
var/image/shield_emissive
/mob/living/silicon/robot/Initialize(mapload, unfinished = FALSE)
spark_system = bind_spark(src, 5)
@@ -1,14 +1,21 @@
/**
* Sets our eyes to match our a_intent if we have that set up, and builds the eye cache if we haven't gotten that built yet
*/
/mob/living/silicon/robot/set_intent(var/set_intent)
a_intent = set_intent
CutOverlays(eye_overlay)
CutOverlays(list(eye_overlay, eye_emissive))
if(!length(module_sprites))
return
if(!length(cached_eye_overlays))
setup_eye_cache()
if(!stat)
eye_overlay = cached_eye_overlays[a_intent]
AddOverlays(eye_overlay)
AddOverlays(list(eye_overlay, eye_emissive))
/**
* Handles updating the panel's sprite on the robot.
* panel_overlay is scoped to the robot.
*/
/mob/living/silicon/robot/proc/handle_panel_overlay()
CutOverlays(panel_overlay)
if(!length(cached_panel_overlays))
@@ -28,41 +35,53 @@
setup_eye_cache()
setup_panel_cache()
/**
* Creates a list of mutable_appearances and emissive_appearances for our robot indexed by intent.
* eye_overlay is scoped to the robot.
*/
/mob/living/silicon/robot/proc/setup_eye_cache()
if(!module_sprites?[icontype]?[ROBOT_EYES])
return
var/eye_plane = src.plane
if(layer == MOB_LAYER) // In case you're hiding. So eyes don't go through tables.
eye_plane = MOB_EMISSIVE_LAYER
var/eyeprefix = module_sprites[icontype][ROBOT_EYES]
var/cached_eye_emissive
if(speed == -2) // For combat drones with the mobility module.
cached_eye_overlays = list(
I_HELP = image(icon, "[eyeprefix]-roll-eyes_help"),
I_HURT = image(icon, "[eyeprefix]-roll-eyes_harm")
I_HELP = mutable_appearance(icon, "[eyeprefix]-roll-eyes_help"),
I_HURT = mutable_appearance(icon, "[eyeprefix]-roll-eyes_harm")
)
cached_eye_emissive = emissive_appearance(icon, "[eyeprefix]-roll-eyes_help")
else
cached_eye_overlays = list(
I_HELP = image(icon, "[eyeprefix]-eyes_help"), //Changed so icontype goes in front. Helps with parsing in this godforsaken engine known as BYOND.
I_HURT = image(icon, "[eyeprefix]-eyes_harm")
I_HELP = mutable_appearance(icon, "[eyeprefix]-eyes_help"),
I_HURT = mutable_appearance(icon, "[eyeprefix]-eyes_harm")
)
if(eye_overlay)
CutOverlays(eye_overlay)
cached_eye_emissive = emissive_appearance(icon, "[eyeprefix]-eyes_help")
if(eye_overlay) // We should always have both an eye_overlay and an eye_emissive.
CutOverlays(list(eye_overlay, eye_emissive))
eye_overlay = cached_eye_overlays[a_intent]
eye_overlay.plane = eye_plane
AddOverlays(eye_overlay)
eye_emissive = cached_eye_emissive
AddOverlays(list(eye_overlay, eye_emissive))
/**
* Creates a list of panel appearances for our robot based on the current state of the robot's panel.
* If the robot doesn't have a panel, we don't create the cache.
*/
/mob/living/silicon/robot/proc/setup_panel_cache()
if(!length(module_sprites))
return
if(!module_sprites?[icontype]?[ROBOT_PANEL])
return
var/panelprefix = custom_sprite ? src.ckey : module_sprites[icontype][ROBOT_PANEL] // Shoutout to Geeves.
var/panelprefix = custom_sprite ? src.ckey : module_sprites[icontype][ROBOT_PANEL]
cached_panel_overlays = list(
ROBOT_PANEL_EXPOSED = image(icon, "[panelprefix]-openpanel+w"),
ROBOT_PANEL_CELL = image(icon, "[panelprefix]-openpanel+c"),
ROBOT_PANEL_NO_CELL = image(icon, "[panelprefix]-openpanel-c")
ROBOT_PANEL_EXPOSED = mutable_appearance(icon, "[panelprefix]-openpanel+w"),
ROBOT_PANEL_CELL = mutable_appearance(icon, "[panelprefix]-openpanel+c"),
ROBOT_PANEL_NO_CELL = mutable_appearance(icon, "[panelprefix]-openpanel-c")
)
/**
* Handles module activation/deactivation effects.
*/
/mob/living/silicon/robot/proc/set_module_active(var/obj/item/given_module)
if(module_active)
module_active.on_module_deactivate(src)
@@ -70,6 +89,9 @@
if(given_module)
given_module.on_module_activate(src)
/**
* Handles module storing effects and clears up the module's icon.
*/
/mob/living/silicon/robot/proc/store_module(var/obj/item/stored_module)
stored_module.on_module_store(src)
stored_module.loc = module