diff --git a/code/__DEFINES/misc.dm b/code/__DEFINES/misc.dm
index a8ea046657..18db6de769 100644
--- a/code/__DEFINES/misc.dm
+++ b/code/__DEFINES/misc.dm
@@ -543,9 +543,6 @@ GLOBAL_LIST_INIT(pda_reskins, list(
#define FALL_NO_MESSAGE (1<<1) //Used to suppress the "[A] falls through [old_turf]" messages where it'd make little sense at all, like going downstairs.
#define FALL_STOP_INTERCEPTING (1<<2) //Used in situations where halting the whole "intercept" loop would be better, like supermatter dusting (and thus deleting) the atom.
-//Misc text define. Does 4 spaces. Used as a makeshift tabulator.
-#define FOURSPACES " "
-
#define CRYOMOBS 'icons/obj/cryo_mobs.dmi'
#define CUSTOM_HOLOFORM_DELAY 10 SECONDS //prevents spamming to make lag. it's pretty expensive to do this.
diff --git a/code/__DEFINES/text.dm b/code/__DEFINES/text.dm
index 58c24747e0..bf60d6e725 100644
--- a/code/__DEFINES/text.dm
+++ b/code/__DEFINES/text.dm
@@ -1,5 +1,50 @@
+/// Does 4 spaces. Used as a makeshift tabulator.
+#define FOURSPACES " "
+
+/// Standard maptext
/// Prepares a text to be used for maptext. Use this so it doesn't look hideous.
#define MAPTEXT(text) {"[##text]"}
-/// Macro from Lummox used to get height from a MeasureText proc
-#define WXH_TO_HEIGHT(x) text2num(copytext(x, findtextEx(x, "x") + 1))
+/**
+ * Pixel-perfect scaled fonts for use in the MAP element as defined in skin.dmf
+ *
+ * Four sizes to choose from, use the sizes as mentioned below.
+ * Between the variations and a step there should be an option that fits your use case.
+ * BYOND uses pt sizing, different than px used in TGUI. Using px will make it look blurry due to poor antialiasing.
+ *
+ * Default sizes are prefilled in the macro for ease of use and a consistent visual look.
+ * To use a step other than the default in the macro, specify it in a span style.
+ * For example: MAPTEXT_PIXELLARI("Some large maptext here")
+ */
+/// Large size (ie: context tooltips) - Size options: 12pt 24pt.
+#define MAPTEXT_PIXELLARI(text) {"[##text]"}
+
+/// Standard size (ie: normal runechat) - Size options: 6pt 12pt 18pt.
+#define MAPTEXT_GRAND9K(text) {"[##text]"}
+
+/// Small size. (ie: context subtooltips, spell delays) - Size options: 12pt 24pt.
+#define MAPTEXT_TINY_UNICODE(text) {"[##text]"}
+
+/// Smallest size. (ie: whisper runechat) - Size options: 6pt 12pt 18pt.
+#define MAPTEXT_SPESSFONT(text) {"[##text]"}
+
+/**
+ * Prepares a text to be used for maptext, using a variable size font.
+ *
+ * More flexible but doesn't scale pixel perfect to BYOND icon resolutions.
+ * (May be blurry.) Can use any size in pt or px.
+ *
+ * You MUST Specify the size when using the macro
+ * For example: MAPTEXT_VCR_OSD_MONO("Some large maptext here")
+ */
+/// Prepares a text to be used for maptext, using a variable size font.
+/// Variable size font. More flexible but doesn't scale pixel perfect to BYOND icon resolutions. (May be blurry.) Can use any size in pt or px.
+#define MAPTEXT_VCR_OSD_MONO(text) {"[##text]"}
+
+/// Macro from Lummox used to get height from a MeasureText proc.
+/// resolves the MeasureText() return value once, then resolves the height, then sets return_var to that.
+#define WXH_TO_HEIGHT(measurement, return_var) \
+ do { \
+ var/_measurement = measurement; \
+ return_var = text2num(copytext(_measurement, findtextEx(_measurement, "x") + 1)); \
+ } while(FALSE);
diff --git a/code/__HELPERS/icons.dm b/code/__HELPERS/icons.dm
index 579126257f..2ccd9deaf2 100644
--- a/code/__HELPERS/icons.dm
+++ b/code/__HELPERS/icons.dm
@@ -992,7 +992,7 @@ world
letter = lowertext(letter)
var/image/text_image = new(loc = A)
- text_image.maptext = MAPTEXT("[letter]")
+ text_image.maptext = MAPTEXT("[letter]")
text_image.pixel_x = 7
text_image.pixel_y = 5
qdel(atom_icon)
diff --git a/code/__HELPERS/yelling.dm b/code/__HELPERS/yelling.dm
index 4383a245d4..25ec9580a1 100644
--- a/code/__HELPERS/yelling.dm
+++ b/code/__HELPERS/yelling.dm
@@ -30,7 +30,7 @@
#define RUN_YELL(_T, _P, _D) \
returned = max(_P - max(_T.get_yelling_resistance(_P), 0) - 1, 0); \
processed[_T] = returned;
- // _T.maptext = "[returned]";
+ // _T.maptext = MAPTEXT(returned);
var/list/turf/edges_next
var/list/turf/powers_next
diff --git a/code/_onclick/hud/_defines.dm b/code/_onclick/hud/_defines.dm
index 6f390fc19a..302edcc54b 100644
--- a/code/_onclick/hud/_defines.dm
+++ b/code/_onclick/hud/_defines.dm
@@ -133,24 +133,27 @@
#define ui_construct_pull "EAST,CENTER-2:15"
#define ui_construct_health "EAST,CENTER:15"
-// AI
-#define ui_ai_core "SOUTH:6,WEST"
-#define ui_ai_camera_list "SOUTH:6,WEST+1"
-#define ui_ai_track_with_camera "SOUTH:6,WEST+2"
-#define ui_ai_camera_light "SOUTH:6,WEST+3"
-#define ui_ai_crew_monitor "SOUTH:6,WEST+4"
-#define ui_ai_crew_manifest "SOUTH:6,WEST+5"
-#define ui_ai_alerts "SOUTH:6,WEST+6"
-#define ui_ai_announcement "SOUTH:6,WEST+7"
-#define ui_ai_shuttle "SOUTH:6,WEST+8"
-#define ui_ai_state_laws "SOUTH:6,WEST+9"
-#define ui_ai_pda_send "SOUTH:6,WEST+10"
-#define ui_ai_pda_log "SOUTH:6,WEST+11"
-#define ui_ai_take_picture "SOUTH:6,WEST+12"
-#define ui_ai_view_images "SOUTH:6,WEST+13"
-#define ui_ai_sensor "SOUTH:6,WEST+14"
-#define ui_ai_multicam "SOUTH+1:6,WEST+13"
-#define ui_ai_add_multicam "SOUTH+1:6,WEST+14"
+//AI
+#define ui_ai_core "BOTTOM:6,RIGHT-4"
+#define ui_ai_shuttle "BOTTOM:6,RIGHT-3"
+#define ui_ai_announcement "BOTTOM:6,RIGHT-2"
+#define ui_ai_state_laws "BOTTOM:6,RIGHT-1"
+#define ui_ai_pda_log "BOTTOM:6,RIGHT"
+#define ui_ai_pda_send "BOTTOM+1:6,RIGHT"
+#define ui_ai_language_menu "BOTTOM+1:8,RIGHT-2:30"
+
+#define ui_ai_crew_monitor "BOTTOM:6,CENTER-1"
+#define ui_ai_crew_manifest "BOTTOM:6,CENTER"
+#define ui_ai_alerts "BOTTOM:6,CENTER+1"
+
+#define ui_ai_view_images "BOTTOM:6,LEFT+4"
+#define ui_ai_camera_list "BOTTOM:6,LEFT+3"
+#define ui_ai_track_with_camera "BOTTOM:6,LEFT+2"
+#define ui_ai_camera_light "BOTTOM:6,LEFT+1"
+#define ui_ai_sensor "BOTTOM:6,LEFT"
+#define ui_ai_multicam "BOTTOM+1:6,LEFT+1"
+#define ui_ai_add_multicam "BOTTOM+1:6,LEFT"
+#define ui_ai_take_picture "BOTTOM+2:6,LEFT"
// pAI
diff --git a/code/_onclick/hud/ai.dm b/code/_onclick/hud/ai.dm
index 17b5a9ed61..5f8b10fd31 100644
--- a/code/_onclick/hud/ai.dm
+++ b/code/_onclick/hud/ai.dm
@@ -194,7 +194,7 @@
// Language menu
using = new /atom/movable/screen/language_menu
- using.screen_loc = ui_borg_language_menu
+ using.screen_loc = ui_ai_language_menu
using.hud = src
static_inventory += using
diff --git a/code/_onclick/hud/credits.dm b/code/_onclick/hud/credits.dm
index aaf423ecbc..3d6bbdc277 100644
--- a/code/_onclick/hud/credits.dm
+++ b/code/_onclick/hud/credits.dm
@@ -44,7 +44,7 @@
icon = I
parent = P
icon_state = credited
- maptext = MAPTEXT(credited)
+ maptext = MAPTEXT_PIXELLARI(credited)
maptext_x = world.icon_size + 8
maptext_y = (world.icon_size / 2) - 4
maptext_width = world.icon_size * 3
diff --git a/code/_onclick/hud/human.dm b/code/_onclick/hud/human.dm
index 88451a2c3b..eac3ab807b 100644
--- a/code/_onclick/hud/human.dm
+++ b/code/_onclick/hud/human.dm
@@ -78,7 +78,7 @@
/atom/movable/screen/devil/soul_counter/proc/update_counter(souls = 0)
invisibility = 0
- maptext = "
")
/mob/living/carbon/alien/larva/updatePlasmaDisplay()
return
diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm
index d83907be9c..8705a83681 100644
--- a/code/modules/mob/living/silicon/robot/robot.dm
+++ b/code/modules/mob/living/silicon/robot/robot.dm
@@ -28,11 +28,13 @@
inv2 = new /atom/movable/screen/robot/module2()
inv3 = new /atom/movable/screen/robot/module3()
+ ident = rand(1, 999)
+
if(!shell)
aiPDA = new/obj/item/pda/ai(src)
aiPDA.owner = real_name
aiPDA.ownjob = "Cyborg"
- aiPDA.name = real_name + " (" + aiPDA.ownjob + ")"
+ aiPDA.name = real_name + " ([aiPDA.ownjob])"
previous_health = health
@@ -59,26 +61,29 @@
update_icons()
. = ..()
+ //If this body is meant to be a borg controlled by the AI player
//If this body is meant to be a borg controlled by the AI player
if(shell)
- make_shell()
-
- //MMI stuff. Held togheter by magic. ~Miauw
- else if(!mmi || !mmi.brainmob)
- mmi = new (src)
- mmi.brain = new /obj/item/organ/brain(mmi)
- mmi.brain.organ_flags |= ORGAN_FROZEN
- mmi.brain.name = "[real_name]'s brain"
- mmi.icon_state = "mmi_full"
- mmi.name = "Man-Machine Interface: [real_name]"
- mmi.brainmob = new(mmi)
- mmi.brainmob.name = src.real_name
- mmi.brainmob.real_name = src.real_name
- mmi.brainmob.container = mmi
+ var/obj/item/borg/upgrade/ai/board = new(src)
+ make_shell(board)
+ add_to_upgrades(board)
+ else
+ //MMI stuff. Held togheter by magic. ~Miauw
+ if(!mmi?.brainmob)
+ mmi = new (src)
+ mmi.brain = new /obj/item/organ/brain(mmi)
+ mmi.brain.organ_flags |= ORGAN_FROZEN
+ mmi.brain.name = "[real_name]'s brain"
+ mmi.icon_state = "mmi_full"
+ mmi.name = "[initial(mmi.name)]: [real_name]"
+ mmi.brainmob = new(mmi)
+ mmi.brainmob.name = src.real_name
+ mmi.brainmob.real_name = src.real_name
+ mmi.brainmob.container = mmi
+ mmi.update_appearance()
INVOKE_ASYNC(src, .proc/updatename)
- playsound(loc, 'sound/voice/liveagain.ogg', 75, TRUE)
aicamera = new/obj/item/camera/siliconcam/robot_camera(src)
toner = tonermax
diag_hud_set_borgcell()
@@ -330,194 +335,145 @@
return FALSE
return ISINRANGE(T1.x, T0.x - interaction_range, T0.x + interaction_range) && ISINRANGE(T1.y, T0.y - interaction_range, T0.y + interaction_range)
-/mob/living/silicon/robot/proc/attempt_welder_repair(obj/item/W, mob/user)
- if(!W.tool_behaviour == TOOL_WELDER)
- return
- if(!getBruteLoss())
- to_chat(user, "[src] is already in good condition!")
- return
- if(!W.tool_start_check(user, amount=0)) //The welder has 1u of fuel consumed by it's afterattack, so we don't need to worry about taking any away.
- return
- user.DelayNextAction(CLICK_CD_MELEE)
- if(src == user)
- to_chat(user, "You start fixing yourself...")
- if(!W.use_tool(src, user, 50))
- return
- adjustBruteLoss(-10)
- else
- to_chat(user, "You start fixing [src]...")
- if(!do_after(user, 30, target = src))
- return
- adjustBruteLoss(-30)
- updatehealth()
- add_fingerprint(user)
- visible_message("[user] has fixed some of the dents on [src].")
-
-/mob/living/silicon/robot/proc/attempt_cable_repair(obj/item/stack/cable_coil/W, mob/user)
- if (getFireLoss() > 0 || getToxLoss() > 0)
- user.DelayNextAction(CLICK_CD_MELEE)
- if(src == user)
- to_chat(user, "You start fixing yourself...")
- if(!W.use_tool(src, user, 50, 1, skill_gain_mult = TRIVIAL_USE_TOOL_MULT))
- to_chat(user, "You need more cable to repair [src]!")
- return
- adjustFireLoss(-10)
- adjustToxLoss(-10)
- else
- to_chat(user, "You start fixing [src]...")
- if(!W.use_tool(src, user, 30, 1))
- to_chat(user, "You need more cable to repair [src]!")
- return
- adjustFireLoss(-30)
- adjustToxLoss(-30)
- updatehealth()
- user.visible_message("[user] has fixed some of the burnt wires on [src].", "You fix some of the burnt wires on [src].")
- else
- to_chat(user, "The wires seem fine, there's no need to fix them.")
-
/mob/living/silicon/robot/attackby(obj/item/W, mob/user, params)
- if(W.tool_behaviour == TOOL_WELDER && (user.a_intent != INTENT_HARM || user == src))
- INVOKE_ASYNC(src, .proc/attempt_welder_repair, W, user)
- return
-
- else if(istype(W, /obj/item/stack/cable_coil) && wiresexposed)
- INVOKE_ASYNC(src, .proc/attempt_cable_repair, W, user)
- return
-
- else if(W.tool_behaviour == TOOL_CROWBAR) // crowbar means open or close the cover
- if(opened)
- to_chat(user, "You close the cover.")
- opened = 0
- update_icons()
- else
- if(locked)
- to_chat(user, "The cover is locked and cannot be opened!")
+ if(istype(W, /obj/item/stack/cable_coil) && wiresexposed)
+ user.DelayNextAction(CLICK_CD_MELEE)
+ if (getFireLoss() > 0 || getToxLoss() > 0)
+ if(src == user)
+ to_chat(user, span_notice("You start fixing yourself..."))
+ if(!W.use_tool(src, user, 5 SECONDS, 1, skill_gain_mult = TRIVIAL_USE_TOOL_MULT))
+ to_chat(user, span_warning("You need more cable to repair [src]!"))
+ return
+ adjustFireLoss(-10)
+ adjustToxLoss(-10)
else
- to_chat(user, "You open the cover.")
- opened = 1
- update_icons()
+ to_chat(user, span_notice("You start fixing [src]..."))
+ if(!W.use_tool(src, user, 3 SECONDS, 1))
+ to_chat(user, span_warning("You need more cable to repair [src]!"))
+ adjustFireLoss(-30)
+ adjustToxLoss(-30)
+ updatehealth()
+ user.visible_message(span_notice("[user] has fixed some of the burnt wires on [src]."), span_notice("You fix some of the burnt wires on [src]."))
+ else
+ to_chat(user, span_warning("The wires seem fine, there's no need to fix them."))
+ return
- else if(istype(W, /obj/item/stock_parts/cell) && opened) // trying to put a cell inside
+ if(istype(W, /obj/item/stock_parts/cell) && opened) // trying to put a cell inside
if(wiresexposed)
- to_chat(user, "Close the cover first!")
+ to_chat(user, span_warning("Close the cover first!"))
else if(cell)
- to_chat(user, "There is a power cell already installed!")
+ to_chat(user, span_warning("There is a power cell already installed!"))
else
if(!user.transferItemToLoc(W, src))
return
cell = W
- to_chat(user, "You insert the power cell.")
+ to_chat(user, span_notice("You insert the power cell."))
update_icons()
diag_hud_set_borgcell()
+ return
- else if(is_wire_tool(W))
+ if(is_wire_tool(W))
if (wiresexposed)
wires.interact(user)
else
- to_chat(user, "You can't reach the wiring!")
+ to_chat(user, span_warning("You can't reach the wiring!"))
+ return
- else if(W.tool_behaviour == TOOL_SCREWDRIVER && opened && !cell) // haxing
- wiresexposed = !wiresexposed
- to_chat(user, "The wires have been [wiresexposed ? "exposed" : "unexposed"]")
- update_icons()
-
- else if((W.tool_behaviour == TOOL_SCREWDRIVER) && opened && cell) // radio
- if(shell)
- to_chat(user, "You cannot seem to open the radio compartment") //Prevent AI radio key theft
- else if(radio)
- radio.attackby(W,user)//Push it to the radio to let it handle everything
- else
- to_chat(user, "Unable to locate a radio!")
- update_icons()
-
- else if(W.tool_behaviour == TOOL_WRENCH && opened && !cell) //Deconstruction. The flashes break from the fall, to prevent this from being a ghetto reset module.
- if(!locked_down)
- to_chat(user, "[src]'s bolts spark! Maybe you should lock them down first!")
- spark_system.start()
- return
- else
- to_chat(user, "You start to unfasten [src]'s securing bolts...")
- if(W.use_tool(src, user, 50, volume=50) && !cell)
- user.visible_message("[user] deconstructs [src]!", "You unfasten the securing bolts, and [src] falls to pieces!")
- deconstruct()
-
- else if(istype(W, /obj/item/aiModule))
+ if(istype(W, /obj/item/aiModule))
var/obj/item/aiModule/MOD = W
if(!opened)
- to_chat(user, "You need access to the robot's insides to do that!")
+ to_chat(user, span_warning("You need access to the robot's insides to do that!"))
return
if(wiresexposed)
- to_chat(user, "You need to close the wire panel to do that!")
+ to_chat(user, span_warning("You need to close the wire panel to do that!"))
return
if(!cell)
- to_chat(user, "You need to install a power cell to do that!")
+ to_chat(user, span_warning("You need to install a power cell to do that!"))
return
if(shell) //AI shells always have the laws of the AI
- to_chat(user, "[src] is controlled remotely! You cannot upload new laws this way!")
+ to_chat(user, span_warning("[src] is controlled remotely! You cannot upload new laws this way!"))
return
- if(emagged || (connected_ai && lawupdate)) //Can't be sure which, metagamers
- emote("buzz-[user.name]")
+ if(connected_ai && lawupdate)
+ to_chat(user, span_warning("[src] is receiving laws remotely from a synced AI!"))
+ return
+ if(emagged)
+ to_chat(user, span_warning("The law interface glitches out!"))
+ emote("buzz")
return
if(!mind) //A player mind is required for law procs to run antag checks.
- to_chat(user, "[src] is entirely unresponsive!")
+ to_chat(user, span_warning("[src] is entirely unresponsive!"))
return
MOD.install(laws, user) //Proc includes a success mesage so we don't need another one
return
- else if(istype(W, /obj/item/encryptionkey/) && opened)
+ if(istype(W, /obj/item/encryptionkey) && opened)
if(radio)//sanityyyyyy
radio.attackby(W,user)//GTFO, you have your own procs
else
- to_chat(user, "Unable to locate a radio!")
+ to_chat(user, span_warning("Unable to locate a radio!"))
+ return
- else if (istype(W, /obj/item/card/id)||istype(W, /obj/item/pda)) // trying to unlock the interface with an ID card
- if(emagged)//still allow them to open the cover
- to_chat(user, "The interface seems slightly damaged.")
+ if (W.GetID()) // trying to unlock the interface with an ID card
if(opened)
- to_chat(user, "You must close the cover to swipe an ID card!")
+ to_chat(user, span_warning("You must close the cover to swipe an ID card!"))
else
if(allowed(usr))
locked = !locked
- to_chat(user, "You [ locked ? "lock" : "unlock"] [src]'s cover.")
+ to_chat(user, span_notice("You [ locked ? "lock" : "unlock"] [src]'s cover."))
update_icons()
+ if(emagged)
+ to_chat(user, span_notice("The cover interface glitches out for a split second."))
+ logevent("ChÃ¥vÃis cover lock has been [locked ? "engaged" : "released"]") //ChÃ¥vÃis: see above line
+ else
+ logevent("Chassis cover lock has been [locked ? "engaged" : "released"]")
else
- to_chat(user, "Access denied.")
+ to_chat(user, span_danger("Access denied."))
+ return
- else if(istype(W, /obj/item/borg/upgrade/))
+ if(istype(W, /obj/item/borg/upgrade))
var/obj/item/borg/upgrade/U = W
if(!opened)
- to_chat(user, "You must access the borg's internals!")
- else if(!src.module && U.require_module)
- to_chat(user, "The borg must choose a module before it can be upgraded!")
- else if(U.locked)
- to_chat(user, "The upgrade is locked and cannot be used yet!")
- else
- if(!user.temporarilyRemoveItemFromInventory(U))
- return
- if(U.action(src))
- to_chat(user, "You apply the upgrade to [src].")
- if(U.one_use)
- U.afterInstall(src)
- qdel(U)
- else
- U.forceMove(src)
- upgrades += U
- U.afterInstall(src)
- else
- to_chat(user, "Upgrade error.")
- U.forceMove(drop_location())
+ to_chat(user, span_warning("You must access the cyborg's internals!"))
+ return
+ if(!module && U.require_module)
+ to_chat(user, span_warning("The cyborg must choose a model before it can be upgraded!"))
+ return
+ if(U.locked)
+ to_chat(user, span_warning("The upgrade is locked and cannot be used yet!"))
+ return
+ if(!user.canUnEquip(U))
+ to_chat(user, span_warning("The upgrade is stuck to you and you can't seem to let go of it!"))
+ return
+ apply_upgrade(U, user)
+ return
- else if(istype(W, /obj/item/toner))
+ if(istype(W, /obj/item/toner))
if(toner >= tonermax)
- to_chat(user, "The toner level of [src] is at its highest level possible!")
- else
- if(!user.temporarilyRemoveItemFromInventory(W))
- return
- toner = tonermax
- qdel(W)
- to_chat(user, "You fill the toner level of [src] to its max capacity.")
- else
- return ..()
+ to_chat(user, span_warning("The toner level of [src] is at its highest level possible!"))
+ return
+ if(!user.temporarilyRemoveItemFromInventory(W))
+ return
+ toner = tonermax
+ qdel(W)
+ to_chat(user, span_notice("You fill the toner level of [src] to its max capacity."))
+ return
+
+ if(istype(W, /obj/item/flashlight))
+ if(!opened)
+ to_chat(user, span_warning("You need to open the panel to repair the headlamp!"))
+ return
+ if(lamp_functional)
+ to_chat(user, span_warning("The headlamp is already functional!"))
+ return
+ if(!user.temporarilyRemoveItemFromInventory(W))
+ to_chat(user, span_warning("[W] seems to be stuck to your hand. You'll have to find a different light."))
+ return
+ lamp_functional = TRUE
+ qdel(W)
+ to_chat(user, span_notice("You replace the headlamp bulbs."))
+ return
+
+ return ..()
/mob/living/silicon/robot/crowbar_act(mob/living/user, obj/item/I) //TODO: make fucking everything up there in that attackby() proc use the proper tool_act() procs. But honestly, who has time for that? 'cause I know for sure that you, the person reading this, sure as hell doesn't.
var/validbreakout = FALSE
@@ -1064,33 +1020,49 @@
*Checking Exited() to detect if a hat gets up and walks off.
*Drones and pAIs might do this, after all.
*/
-/mob/living/silicon/robot/Exited(atom/A)
- if(hat && hat == A)
+/mob/living/silicon/robot/Exited(atom/movable/gone)
+ . = ..()
+ if(hat == gone)
hat = null
if(!QDELETED(src)) //Don't update icons if we are deleted.
update_icons()
- return ..()
-///Use this to add upgrades to robots. It'll register signals for when the upgrade is moved or deleted, if not single use.
-/mob/living/silicon/robot/proc/add_to_upgrades(obj/item/borg/upgrade/new_upgrade, mob/user)
+ if(gone == cell)
+ cell = null
+
+ if(gone == mmi)
+ mmi = null
+
+///Called when a mob uses an upgrade on an open borg. Checks to make sure the upgrade can be applied
+/mob/living/silicon/robot/proc/apply_upgrade(obj/item/borg/upgrade/new_upgrade, mob/user, admin_added)
+ if(!admin_added && isnull(user))
+ return FALSE
if(new_upgrade in upgrades)
return FALSE
- if(!user.temporarilyRemoveItemFromInventory(new_upgrade)) //calling the upgrade's dropped() proc /before/ we add action buttons
+ if(!admin_added && !user.temporarilyRemoveItemFromInventory(new_upgrade)) //calling the upgrade's dropped() proc /before/ we add action buttons
return FALSE
if(!new_upgrade.action(src, user))
- to_chat(user, "Upgrade error.")
- new_upgrade.forceMove(loc) //gets lost otherwise
+ to_chat(user, span_danger("Upgrade error."))
+ if(admin_added)
+ qdel(new_upgrade)
+ else
+ new_upgrade.forceMove(loc) //gets lost otherwise
return FALSE
- to_chat(user, "You apply the upgrade to [src].")
+ to_chat(user, span_notice("You apply the upgrade to [src]."))
+ add_to_upgrades(new_upgrade)
+ return TRUE
+
+///Moves the upgrade inside the robot and registers relevant signals.
+/mob/living/silicon/robot/proc/add_to_upgrades(obj/item/borg/upgrade/new_upgrade)
to_chat(src, "----------------\nNew hardware detected...Identified as \"[new_upgrade]\"...Setup complete.\n----------------")
if(new_upgrade.one_use)
logevent("Firmware [new_upgrade] run successfully.")
qdel(new_upgrade)
- return FALSE
+ return
upgrades += new_upgrade
new_upgrade.forceMove(src)
- RegisterSignal(new_upgrade, COMSIG_MOVABLE_MOVED, .proc/remove_from_upgrades)
- RegisterSignal(new_upgrade, COMSIG_PARENT_QDELETING, .proc/on_upgrade_deleted)
+ RegisterSignal(new_upgrade, COMSIG_MOVABLE_MOVED, PROC_REF(remove_from_upgrades))
+ RegisterSignal(new_upgrade, COMSIG_PARENT_QDELETING, PROC_REF(on_upgrade_deleted))
logevent("Hardware [new_upgrade] installed successfully.")
///Called when an upgrade is moved outside the robot. So don't call this directly, use forceMove etc.
@@ -1117,15 +1089,17 @@
* * board - B.O.R.I.S. module board used for transforming the cyborg into AI shell
*/
/mob/living/silicon/robot/proc/make_shell(obj/item/borg/upgrade/ai/board)
- if(!board)
- upgrades |= new /obj/item/borg/upgrade/ai(src)
+ if(isnull(board))
+ stack_trace("make_shell was called without a board argument! This is never supposed to happen!")
+ return FALSE
+
shell = TRUE
braintype = "AI Shell"
name = "Empty AI Shell-[ident]"
real_name = name
GLOB.available_ai_shells |= src
if(!QDELETED(builtInCamera))
- builtInCamera.c_tag = real_name //update the camera name too
+ builtInCamera.c_tag = real_name //update the camera name too
diag_hud_set_aishell()
notify_ai(AI_SHELL)
diff --git a/code/modules/mob/living/silicon/robot/robot_defense.dm b/code/modules/mob/living/silicon/robot/robot_defense.dm
index d5a1058a61..f8217945db 100644
--- a/code/modules/mob/living/silicon/robot/robot_defense.dm
+++ b/code/modules/mob/living/silicon/robot/robot_defense.dm
@@ -94,6 +94,76 @@ GLOBAL_LIST_INIT(blacklisted_borg_hats, typecacheof(list( //Hats that don't real
apply_status_effect(/datum/status_effect/vtec_disabled, time)
update_movespeed()
+/mob/living/silicon/robot/welder_act(mob/living/user, obj/item/tool)
+ if(user.a_intent == INTENT_HARM)
+ return FALSE
+ . = TRUE
+ if (!getBruteLoss())
+ to_chat(user, span_warning("[src] is already in good condition!"))
+ return
+ if (!tool.tool_start_check(user, amount=0)) //The welder has 1u of fuel consumed by it's afterattack, so we don't need to worry about taking any away.
+ return
+ user.DelayNextAction(CLICK_CD_MELEE)
+ if(src == user)
+ to_chat(user, span_notice("You start fixing yourself..."))
+ if(!tool.use_tool(src, user, 50))
+ return
+ adjustBruteLoss(-10)
+ else
+ to_chat(user, span_notice("You start fixing [src]..."))
+ if(!do_after(user, 3 SECONDS, target = src))
+ return
+ adjustBruteLoss(-30)
+ updatehealth()
+ add_fingerprint(user)
+ visible_message(span_notice("[user] has fixed some of the dents on [src]."))
+
+/mob/living/silicon/robot/crowbar_act(mob/living/user, obj/item/tool)
+ . = TRUE
+ if(opened)
+ to_chat(user, span_notice("You close the cover."))
+ opened = FALSE
+ update_icons()
+ else
+ if(locked)
+ to_chat(user, span_warning("The cover is locked and cannot be opened!"))
+ else
+ to_chat(user, span_notice("You open the cover."))
+ opened = TRUE
+ update_icons()
+
+ return TRUE
+
+/mob/living/silicon/robot/screwdriver_act(mob/living/user, obj/item/tool)
+ if(!opened)
+ return FALSE
+ . = TRUE
+ if(!cell) // haxing
+ wiresexposed = !wiresexposed
+ to_chat(user, span_notice("The wires have been [wiresexposed ? "exposed" : "unexposed"]."))
+ else // radio
+ if(shell)
+ to_chat(user, span_warning("You cannot seem to open the radio compartment!")) //Prevent AI radio key theft
+ else if(radio)
+ radio.screwdriver_act(user, tool) // Push it to the radio to let it handle everything
+ else
+ to_chat(user, span_warning("Unable to locate a radio!"))
+ update_icons()
+
+/mob/living/silicon/robot/wrench_act(mob/living/user, obj/item/tool)
+ if(!(opened && !cell)) // Deconstruction. The flashes break from the fall, to prevent this from being a ghetto reset module.
+ return FALSE
+ . = TRUE
+ if(!locked_down)
+ to_chat(user, span_boldannounce("[src]'s bolts spark! Maybe you should lock them down first!"))
+ spark_system.start()
+ return
+ to_chat(user, span_notice("You start to unfasten [src]'s securing bolts..."))
+ if(tool.use_tool(src, user, 5 SECONDS, volume = 50) && !cell)
+ user.visible_message(span_notice("[user] deconstructs [src]!"), span_notice("You unfasten the securing bolts, and [src] falls to pieces!"))
+ deconstruct()
+ return
+
/mob/living/silicon/robot/fire_act()
if(!on_fire) //Silicons don't gain stacks from hotspots, but hotspots can ignite them
IgniteMob()
diff --git a/code/modules/mob/living/simple_animal/guardian/guardian.dm b/code/modules/mob/living/simple_animal/guardian/guardian.dm
index 6faf8f0db5..4c8872ffbf 100644
--- a/code/modules/mob/living/simple_animal/guardian/guardian.dm
+++ b/code/modules/mob/living/simple_animal/guardian/guardian.dm
@@ -257,7 +257,7 @@ GLOBAL_LIST_EMPTY(parasites) //all currently existing/living guardians
resulthealth = round((abs(HEALTH_THRESHOLD_DEAD - summoner.health) / abs(HEALTH_THRESHOLD_DEAD - summoner.maxHealth)) * 100)
else
resulthealth = round((summoner.health / summoner.maxHealth) * 100, 0.5)
- hud_used.healths.maptext = "
[resulthealth]%
"
+ hud_used.healths.maptext = MAPTEXT("
[resulthealth]%
")
/mob/living/simple_animal/hostile/guardian/adjustHealth(amount, updating_health = TRUE, forced = FALSE) //The spirit is invincible, but passes on damage to the summoner
. = amount
diff --git a/code/modules/paperwork/ticketmachine.dm b/code/modules/paperwork/ticketmachine.dm
index 9683601670..6022853fad 100644
--- a/code/modules/paperwork/ticketmachine.dm
+++ b/code/modules/paperwork/ticketmachine.dm
@@ -142,7 +142,7 @@
maptext_x = 10
if(100)
maptext_x = 8
- maptext = "[current_number]" //Finally, apply the maptext
+ maptext = MAPTEXT("[current_number]") //Finally, apply the maptext
/obj/machinery/ticket_machine/attackby(obj/item/I, mob/user, params)
..()
@@ -182,11 +182,7 @@
playsound(src, 'sound/machines/terminal_insert_disc.ogg', 100, FALSE)
ticket_number ++
to_chat(user, "You take a ticket from [src], looks like you're ticket number #[ticket_number]...")
- var/obj/item/ticket_machine_ticket/theirticket = new /obj/item/ticket_machine_ticket(get_turf(src))
- theirticket.name = "Ticket #[ticket_number]"
- theirticket.maptext = "[ticket_number]"
- theirticket.saved_maptext = "[ticket_number]"
- theirticket.ticket_number = ticket_number
+ var/obj/item/ticket_machine_ticket/theirticket = new /obj/item/ticket_machine_ticket(get_turf(src), ticket_number)
theirticket.source = src
theirticket.owner = user
user.put_in_hands(theirticket)
@@ -211,10 +207,25 @@
w_class = WEIGHT_CLASS_TINY
resistance_flags = FLAMMABLE
max_integrity = 50
+ var/number
var/saved_maptext = null
var/mob/living/carbon/owner
var/obj/machinery/ticket_machine/source
- var/ticket_number
+
+/obj/item/ticket_machine_ticket/Initialize(mapload, num)
+ . = ..()
+ number = num
+ if(!isnull(num))
+ name += " #[num]"
+ saved_maptext = MAPTEXT(num)
+ maptext = saved_maptext
+
+/obj/item/ticket_machine_ticket/examine(mob/user)
+ . = ..()
+ if(!isnull(number))
+ . += span_notice("The ticket reads shimmering text that tells you that you are number [number] in queue.")
+ if(source)
+ . += span_notice("Below that, you can see that you are [number - source.current_number] spot\s away from being served.")
/obj/item/ticket_machine_ticket/attack_hand(mob/user)
. = ..()
@@ -233,7 +244,7 @@
/obj/item/ticket_machine_ticket/Destroy()
if(owner && source)
source.ticket_holders -= owner
- source.tickets[ticket_number] = null
+ source.tickets[number] = null
owner = null
source = null
return ..()
diff --git a/code/modules/projectiles/guns/energy/kinetic_accelerator.dm b/code/modules/projectiles/guns/energy/kinetic_accelerator.dm
index 5fd158c028..9c9ccd2b8d 100644
--- a/code/modules/projectiles/guns/energy/kinetic_accelerator.dm
+++ b/code/modules/projectiles/guns/energy/kinetic_accelerator.dm
@@ -57,25 +57,24 @@
. = ..()
if(max_mod_capacity)
. += "[get_remaining_mod_capacity()]% mod capacity remaining."
- for(var/A in get_modkits())
- var/obj/item/borg/upgrade/modkit/M = A
+ for(var/obj/item/borg/upgrade/modkit/M in modkits)
. += "There is \a [M] installed, using [M.cost]% capacity."
/obj/item/gun/energy/kinetic_accelerator/crowbar_act(mob/living/user, obj/item/I)
. = TRUE
if(modkits.len)
- to_chat(user, "You pry the modifications out.")
+ to_chat(user, span_notice("You pry all the modifications out."))
I.play_tool_sound(src, 100)
for(var/obj/item/borg/upgrade/modkit/M in modkits)
- M.uninstall(src)
+ M.forceMove(drop_location()) //uninstallation handled in Exited(), or /mob/living/silicon/robot/remove_from_upgrades() for borgs
else
- to_chat(user, "There are no modifications currently installed.")
+ to_chat(user, span_notice("There are no modifications currently installed."))
-/obj/item/gun/energy/kinetic_accelerator/Exited(atom/movable/AM)
- . = ..()
- if((AM in modkits) && istype(AM, /obj/item/borg/upgrade/modkit))
- var/obj/item/borg/upgrade/modkit/M = AM
- M.uninstall(src, FALSE)
+/obj/item/gun/energy/kinetic_accelerator/Exited(atom/movable/gone)
+ if(gone in modkits)
+ var/obj/item/borg/upgrade/modkit/MK = gone
+ MK.uninstall(src)
+ return ..()
/obj/item/gun/energy/kinetic_accelerator/attackby(obj/item/I, mob/user)
if(istype(I, /obj/item/borg/upgrade/modkit))
@@ -86,20 +85,13 @@
/obj/item/gun/energy/kinetic_accelerator/proc/get_remaining_mod_capacity()
var/current_capacity_used = 0
- for(var/A in get_modkits())
- var/obj/item/borg/upgrade/modkit/M = A
+ for(var/obj/item/borg/upgrade/modkit/M in modkits)
current_capacity_used += M.cost
return max_mod_capacity - current_capacity_used
-/obj/item/gun/energy/kinetic_accelerator/proc/get_modkits()
- . = list()
- for(var/A in modkits)
- . += A
-
/obj/item/gun/energy/kinetic_accelerator/proc/modify_projectile(obj/item/projectile/kinetic/K)
K.kinetic_gun = src //do something special on-hit, easy!
- for(var/A in get_modkits())
- var/obj/item/borg/upgrade/modkit/M = A
+ for(var/obj/item/borg/upgrade/modkit/M in modkits)
M.modify_projectile(K)
/obj/item/gun/energy/kinetic_accelerator/cyborg
@@ -259,7 +251,7 @@
if(!target_turf)
target_turf = get_turf(src)
if(kinetic_gun) //hopefully whoever shot this was not very, very unfortunate.
- var/list/mods = kinetic_gun.get_modkits()
+ var/list/mods = kinetic_gun.modkits
for(var/obj/item/borg/upgrade/modkit/M in mods)
M.projectile_strike_predamage(src, target_turf, target, kinetic_gun)
for(var/obj/item/borg/upgrade/modkit/M in mods)
@@ -278,7 +270,7 @@
icon = 'icons/obj/objects.dmi'
icon_state = "modkit"
w_class = WEIGHT_CLASS_SMALL
- require_module = 1
+ require_module = TRUE
module_type = list(/obj/item/robot_module/miner)
module_flags = BORG_MODULE_MINER
var/denied_type = null
@@ -298,26 +290,24 @@
else
..()
-/obj/item/borg/upgrade/modkit/afterInstall(mob/living/silicon/robot/R)
- for(var/obj/item/gun/energy/kinetic_accelerator/H in R.module.modules)
- if(install(H, R)) //It worked
- return
- to_chat(R, "Upgrade error - Aborting Kinetic Accelerator linking.") //No applicable KA found, insufficient capacity, or some other problem.
+/obj/item/borg/upgrade/modkit/action(mob/living/silicon/robot/R)
+ . = ..()
+ if (.)
+ for(var/obj/item/gun/energy/kinetic_accelerator/H in R.module.modules)
+ return install(H, usr, FALSE)
-/obj/item/borg/upgrade/modkit/proc/install(obj/item/gun/energy/kinetic_accelerator/KA, mob/user)
+/obj/item/borg/upgrade/modkit/proc/install(obj/item/gun/energy/kinetic_accelerator/KA, mob/user, transfer_to_loc = TRUE)
. = TRUE
- if(src in KA.modkits) // Sanity check to prevent installing the same modkit twice thanks to occasional click/lag delays.
- return FALSE
if(minebot_upgrade)
if(minebot_exclusive && !istype(KA.loc, /mob/living/simple_animal/hostile/mining_drone))
- to_chat(user, "The modkit you're trying to install is only rated for minebot use.")
+ to_chat(user, span_notice("The modkit you're trying to install is only rated for minebot use."))
return FALSE
else if(istype(KA.loc, /mob/living/simple_animal/hostile/mining_drone))
- to_chat(user, "The modkit you're trying to install is not rated for minebot use.")
+ to_chat(user, span_notice("The modkit you're trying to install is not rated for minebot use."))
return FALSE
if(denied_type)
var/number_of_denied = 0
- for(var/A in KA.get_modkits())
+ for(var/A in KA.modkits)
var/obj/item/borg/upgrade/modkit/M = A
if(istype(M, denied_type))
number_of_denied++
@@ -326,21 +316,25 @@
break
if(KA.get_remaining_mod_capacity() >= cost)
if(.)
- if(!user.transferItemToLoc(src, KA))
- return FALSE
- to_chat(user, "You install the modkit.")
- playsound(loc, 'sound/items/screwdriver.ogg', 100, 1)
+ if(transfer_to_loc && !user.transferItemToLoc(src, KA))
+ return
+ to_chat(user, span_notice("You install the modkit."))
+ playsound(loc, 'sound/items/screwdriver.ogg', 100, TRUE)
KA.modkits += src
else
- to_chat(user, "The modkit you're trying to install would conflict with an already installed modkit. Use a crowbar to remove existing modkits.")
+ to_chat(user, span_notice("The modkit you're trying to install would conflict with an already installed modkit. Use a crowbar to remove existing modkits."))
else
- to_chat(user, "You don't have room([KA.get_remaining_mod_capacity()]% remaining, [cost]% needed) to install this modkit. Use a crowbar to remove existing modkits.")
+ to_chat(user, span_notice("You don't have room([KA.get_remaining_mod_capacity()]% remaining, [cost]% needed) to install this modkit. Use a crowbar to remove existing modkits."))
. = FALSE
-/obj/item/borg/upgrade/modkit/proc/uninstall(obj/item/gun/energy/kinetic_accelerator/KA, forcemove = TRUE)
+/obj/item/borg/upgrade/modkit/deactivate(mob/living/silicon/robot/R, user = usr)
+ . = ..()
+ if (.)
+ for(var/obj/item/gun/energy/kinetic_accelerator/KA in R.module.modules)
+ uninstall(KA)
+
+/obj/item/borg/upgrade/modkit/proc/uninstall(obj/item/gun/energy/kinetic_accelerator/KA)
KA.modkits -= src
- if(forcemove)
- forceMove(get_turf(KA))
/obj/item/borg/upgrade/modkit/proc/modify_projectile(obj/item/projectile/kinetic/K)
@@ -467,6 +461,7 @@
/obj/item/borg/upgrade/modkit/minebot_passthrough
name = "minebot passthrough"
desc = "Causes kinetic accelerator shots to pass through minebots."
+ denied_type = /obj/item/borg/upgrade/modkit/minebot_passthrough // If you have something cost zero, you can keep adding forever, why
cost = 0
//Tendril-unique modules
diff --git a/tgui/packages/tgui-panel/chat/renderer.js b/tgui/packages/tgui-panel/chat/renderer.js
index 32a9b74ed0..495ed32228 100644
--- a/tgui/packages/tgui-panel/chat/renderer.js
+++ b/tgui/packages/tgui-panel/chat/renderer.js
@@ -443,7 +443,9 @@ class ChatRenderer {
const cssRules = styleSheets[i].cssRules;
for (let i = 0; i < cssRules.length; i++) {
const rule = cssRules[i];
- cssText += rule.cssText + '\n';
+ if (rule && typeof rule.cssText === 'string') {
+ cssText += rule.cssText + '\n';
+ }
}
}
cssText += 'body, html { background-color: #141414 }\n';
diff --git a/tgui/packages/tgui/interfaces/BorgPanel.js b/tgui/packages/tgui/interfaces/BorgPanel.js
index 40d612679f..d92b7dd408 100644
--- a/tgui/packages/tgui/interfaces/BorgPanel.js
+++ b/tgui/packages/tgui/interfaces/BorgPanel.js
@@ -1,5 +1,5 @@
import { useBackend } from '../backend';
-import { Box, Button, LabeledList, ProgressBar, Section } from '../components';
+import { Box, Button, Icon, LabeledList, ProgressBar, Section } from '../components';
import { Window } from '../layouts';
export const BorgPanel = (props, context) => {
@@ -12,6 +12,9 @@ export const BorgPanel = (props, context) => {
const upgrades = data.upgrades || [];
const ais = data.ais || [];
const laws = data.laws || [];
+
+ const active_upgrades = data.active_upgrades || [];
+ const ka_remaining_capacity = data.ka_remaining_capacity || 0;
return (
{
))}
- {upgrades.map(upgrade => (
-
+ {
+ ka_remaining_capacity !== null
+ && (
+
+ {ka_remaining_capacity}
+
+ )
+ }
{ais.map(ai => (
{
)}
{tab_sub === 2 && (
- {borgUpgrades.map(upgrade => (
-
- {upgrade}
-
- ))}
+ {borgUpgrades.filter((upgrade, index, arr) =>
+ arr.indexOf(upgrade) === index).map(upgrade => {
+ const upgradeCount = borgUpgrades.filter(u =>
+ u === upgrade).length;
+ return (
+
+ {upgrade} {upgradeCount > 1 ? `x${upgradeCount}` : ''}
+
+ );
+ })}
)}
{tab_sub === 3 && (