diff --git a/code/game/machinery/announcement_system.dm b/code/game/machinery/announcement_system.dm
index da82b0afc7..f46169f72e 100644
--- a/code/game/machinery/announcement_system.dm
+++ b/code/game/machinery/announcement_system.dm
@@ -114,7 +114,7 @@ GLOBAL_LIST_EMPTY(announcement_systems)
. = ..()
if(.)
return
- if(!usr.canUseTopic(src, !issilicon(usr)))
+ if(!usr.canUseTopic(src, !hasSiliconAccessInArea(usr)))
return
if(stat & BROKEN)
visible_message("[src] buzzes.", "You hear a faint buzz.")
@@ -147,7 +147,7 @@ GLOBAL_LIST_EMPTY(announcement_systems)
. = attack_ai(user)
/obj/machinery/announcement_system/attack_ai(mob/user)
- if(!user.canUseTopic(src, !issilicon(user)))
+ if(!user.canUseTopic(src, !hasSiliconAccessInArea(user)))
return
if(stat & BROKEN)
to_chat(user, "[src]'s firmware appears to be malfunctioning!")
@@ -167,7 +167,9 @@ GLOBAL_LIST_EMPTY(announcement_systems)
act_up()
/obj/machinery/announcement_system/emag_act()
+ . = ..()
if(obj_flags & EMAGGED)
return
obj_flags |= EMAGGED
act_up()
+ return TRUE
diff --git a/code/game/machinery/computer/robot.dm b/code/game/machinery/computer/robot.dm
index 401d05da12..9b911e9c15 100644
--- a/code/game/machinery/computer/robot.dm
+++ b/code/game/machinery/computer/robot.dm
@@ -21,6 +21,9 @@
return
if(R.scrambledcodes)
return
+ if(hasSiliconAccessInArea(user) && !issilicon(user))
+ if(!Adjacent(user))
+ return
return TRUE
/obj/machinery/computer/robotics/ui_interact(mob/user, datum/tgui/ui)
@@ -40,6 +43,10 @@
else if(IsAdminGhost(user))
data["can_hack"] = TRUE
+ data["can_convert"] = FALSE
+ if(isAI(user) && is_servant_of_ratvar(user))
+ data["can_convert"] = TRUE
+
data["cyborgs"] = list()
for(var/mob/living/silicon/robot/R in GLOB.silicon_mobs)
if(!can_control(user, R))
@@ -54,6 +61,7 @@
module = R.module ? "[R.module.name] Module" : "No Module Detected",
synchronization = R.connected_ai,
emagged = R.emagged,
+ servant = is_servant_of_ratvar(R),
ref = REF(R)
)
data["cyborgs"] += list(cyborg_data)
@@ -110,6 +118,13 @@
log_game("[key_name(usr)] emagged [key_name(R)] using robotic console!")
message_admins("[ADMIN_LOOKUPFLW(usr)] emagged cyborg [key_name_admin(R)] using robotic console!")
R.SetEmagged(TRUE)
+ if("convert")
+ if(isAI(usr) && is_servant_of_ratvar(usr))
+ var/mob/living/silicon/robot/R = locate(params["ref"]) in GLOB.silicon_mobs
+ if(istype(R) && !is_servant_of_ratvar(R) && R.connected_ai == usr)
+ log_game("[key_name(usr)] converted [key_name(R)] using robotic console!")
+ message_admins("[ADMIN_LOOKUPFLW(usr)] converted cyborg [key_name_admin(R)] using robotic console!")
+ add_servant_of_ratvar(R)
if("killdrone")
if(allowed(usr))
var/mob/living/simple_animal/drone/D = locate(params["ref"]) in GLOB.mob_list
diff --git a/code/game/machinery/dna_scanner.dm b/code/game/machinery/dna_scanner.dm
index e721e986d2..0dcb0e7235 100644
--- a/code/game/machinery/dna_scanner.dm
+++ b/code/game/machinery/dna_scanner.dm
@@ -32,6 +32,8 @@
. = ..()
if(in_range(user, src) || isobserver(user))
. += "The status display reads: Radiation pulse accuracy increased by factor [precision_coeff**2].
Radiation pulse damage decreased by factor [damage_coeff**2]."
+ if(scan_level >= 3)
+ . += "Scanner has been upgraded to support autoprocessing."
/obj/machinery/dna_scannernew/update_icon_state()
//no power or maintenance
@@ -94,6 +96,13 @@
..(user)
+// search for ghosts, if the corpse is empty and the scanner is connected to a cloner
+ var/mob/living/mob_occupant = get_mob_or_brainmob(occupant)
+ if(istype(mob_occupant))
+ if(locate_computer(/obj/machinery/computer/cloning))
+ if(!mob_occupant.suiciding && !(HAS_TRAIT(mob_occupant, TRAIT_NOCLONE)) && !mob_occupant.hellbound)
+ mob_occupant.notify_ghost_cloning("Your corpse has been placed into a cloning scanner. Re-enter your corpse if you want to be cloned!", source = src)
+
// DNA manipulators cannot operate on severed heads or brains
if(iscarbon(occupant))
if(linked_console)
diff --git a/code/game/machinery/doors/airlock.dm b/code/game/machinery/doors/airlock.dm
index 515a4672a5..9ef34390f6 100644
--- a/code/game/machinery/doors/airlock.dm
+++ b/code/game/machinery/doors/airlock.dm
@@ -1457,6 +1457,16 @@
ui.open()
return TRUE
+/obj/machinery/door/airlock/ui_status(mob/user)
+ . = ..()
+ if (!issilicon(user) && hasSiliconAccessInArea(user))
+ . = UI_INTERACTIVE
+
+/obj/machinery/door/airlock/can_interact(mob/user)
+ . = ..()
+ if (!issilicon(user) && hasSiliconAccessInArea(user))
+ return TRUE
+
/obj/machinery/door/airlock/ui_data()
var/list/data = list()
diff --git a/code/game/objects/items/implants/implant_hijack.dm b/code/game/objects/items/implants/implant_hijack.dm
index 36e58d8887..06d2df9272 100644
--- a/code/game/objects/items/implants/implant_hijack.dm
+++ b/code/game/objects/items/implants/implant_hijack.dm
@@ -92,10 +92,17 @@
/obj/item/implant/hijack/proc/hijack_remotely(obj/machinery/power/apc/apc)
if (apc.hijacker || hijacking)
return FALSE //can't remotely hijack an already hijacked APC
+
+ if(apc.being_hijacked)
+ to_chat(imp_in, "This APC is already being hijacked!")
+ return FALSE
+
+ apc.being_hijacked = TRUE
hijacking = TRUE
to_chat(imp_in, "Establishing remote connection with APC.")
if (!do_after(imp_in, 4 SECONDS,target=apc))
to_chat(imp_in, "Aborting.")
+ apc.being_hijacked = FALSE
hijacking = FALSE
return TRUE
if (LAZYLEN(imp_in.siliconaccessareas) >= HIJACK_APC_MAX_AMOUNT)
@@ -118,6 +125,7 @@
toggle_eyes()
else
to_chat(imp_in, "Aborting.")
+ apc.being_hijacked = FALSE
hijacking = FALSE
imp_in.light_power = 0
imp_in.light_range = 0
diff --git a/code/modules/atmospherics/machinery/airalarm.dm b/code/modules/atmospherics/machinery/airalarm.dm
index a2a55ee6b1..6049ee965e 100644
--- a/code/modules/atmospherics/machinery/airalarm.dm
+++ b/code/modules/atmospherics/machinery/airalarm.dm
@@ -237,12 +237,21 @@
. += "Alt-click to [locked ? "unlock" : "lock"] the interface."
/obj/machinery/airalarm/ui_status(mob/user)
- if(hasSiliconAccessInArea(user) && aidisabled)
- to_chat(user, "AI control has been disabled.")
- else if(!shorted)
+ if(hasSiliconAccessInArea(user))
+ if(aidisabled)
+ to_chat(user, "AI control has been disabled")
+ return UI_CLOSE
+ else if(!issilicon(user)) //True sillycones use ..()
+ return UI_INTERACTIVE
+ if(!shorted)
return ..()
return UI_CLOSE
+/obj/machinery/airalarm/can_interact(mob/user)
+ . = ..()
+ if (!issilicon(user) && hasSiliconAccessInArea(user))
+ return TRUE
+
/obj/machinery/airalarm/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
diff --git a/code/modules/events/pirates.dm b/code/modules/events/pirates.dm
index 3bcec9f471..8bada6da77 100644
--- a/code/modules/events/pirates.dm
+++ b/code/modules/events/pirates.dm
@@ -219,7 +219,7 @@
suit_type = /obj/item/clothing/suit/space
helmet_type = /obj/item/clothing/head/helmet/space
mask_type = /obj/item/clothing/mask/breath
- storage_type = /obj/item/tank/internals/oxygen
+ storage_type = /obj/item/tank/jetpack/void
/obj/machinery/loot_locator
@@ -275,6 +275,7 @@
/obj/machinery/computer/piratepad_control
name = "cargo hold control terminal"
+ resistance_flags = INDESTRUCTIBLE
ui_x = 600
ui_y = 230
var/status_report = "Ready for delivery."
diff --git a/code/modules/mining/laborcamp/laborstacker.dm b/code/modules/mining/laborcamp/laborstacker.dm
index 429dc98e8a..2ca4d5fd2e 100644
--- a/code/modules/mining/laborcamp/laborstacker.dm
+++ b/code/modules/mining/laborcamp/laborstacker.dm
@@ -109,9 +109,12 @@ GLOBAL_LIST(labor_sheet_values)
qdel(src)
/obj/machinery/mineral/labor_claim_console/emag_act(mob/user)
- if(!(obj_flags & EMAGGED))
- obj_flags |= EMAGGED
- to_chat(user, "PZZTTPFFFT")
+ . = ..()
+ if((obj_flags & EMAGGED))
+ return
+ obj_flags |= EMAGGED
+ to_chat(user, "PZZTTPFFFT")
+ return TRUE
/**********************Prisoner Collection Unit**************************/
diff --git a/code/modules/power/apc.dm b/code/modules/power/apc.dm
index 1a96f36ccd..7f7c02f457 100644
--- a/code/modules/power/apc.dm
+++ b/code/modules/power/apc.dm
@@ -111,6 +111,7 @@
var/obj/machinery/computer/apc_control/remote_control = null
var/mob/living/carbon/hijacker
var/hijackerlast = TRUE
+ var/being_hijacked = FALSE
/obj/machinery/power/apc/unlocked
locked = FALSE
@@ -902,7 +903,7 @@
if(H && !H.stealthmode && H.toggled)
abilitiesavail = TRUE
var/list/data = list(
- "locked" = locked,
+ "locked" = locked && !(integration_cog && is_servant_of_ratvar(user)) && !area.hasSiliconAccessInArea(user, PRIVILEDGES_SILICON|PRIVILEDGES_DRONE),
"failTime" = failure_timer,
"isOperating" = operating,
"externalPower" = main_status,
@@ -1119,10 +1120,14 @@
/obj/machinery/power/apc/proc/hijack(mob/living/L)
if (!istype(L))
return
+ if(being_hijacked)
+ to_chat(L, "This APC is already being hijacked!")
+ return
if (hijacker && hijacker != L)
var/obj/item/implant/hijack/H = L.getImplant(/obj/item/implant/hijack)
to_chat(L, "Someone already has control of this APC. Beginning counter-hijack.")
H.hijacking = TRUE
+ being_hijacked = TRUE
if (do_after(L,20 SECONDS,target=src))
hijacker.toggleSiliconAccessArea(area)
if (L.toggleSiliconAccessArea(area))
@@ -1130,23 +1135,28 @@
update_icon()
set_hijacked_lighting()
H.hijacking = FALSE
+ being_hijacked = FALSE
return
else
to_chat(L, "Aborting.")
H.hijacking = FALSE
+ being_hijacked = FALSE
return
to_chat(L, "Beginning hijack of APC.")
var/obj/item/implant/hijack/H = L.getImplant(/obj/item/implant/hijack)
H.hijacking = TRUE
+ being_hijacked = TRUE
if (do_after(L,H.stealthmode ? 12 SECONDS : 5 SECONDS,target=src))
if (L.toggleSiliconAccessArea(area))
hijacker = L
update_icon()
set_hijacked_lighting()
H.hijacking = FALSE
+ being_hijacked = FALSE
else
to_chat(L, "Aborting.")
H.hijacking = FALSE
+ being_hijacked = FALSE
return
/obj/machinery/power/apc/proc/malfhack(mob/living/silicon/ai/malf)
diff --git a/code/modules/spells/spell_types/lichdom.dm b/code/modules/spells/spell_types/lichdom.dm
index 11518b236f..3d54c7130c 100644
--- a/code/modules/spells/spell_types/lichdom.dm
+++ b/code/modules/spells/spell_types/lichdom.dm
@@ -132,7 +132,7 @@
lich.real_name = mind.name
mind.transfer_to(lich)
mind.grab_ghost(force=TRUE)
- lich.hardset_dna(null,null,null,lich.real_name,null, new /datum/species/skeleton)
+ lich.hardset_dna(null,null,null,lich.real_name,null, new /datum/species/skeleton/space)
to_chat(lich, "Your bones clatter and shudder as you are pulled back into this world!")
var/turf/body_turf = get_turf(old_body)
lich.DefaultCombatKnockdown(200 + 200*resurrections)
diff --git a/tgui/packages/tgui/interfaces/RoboticsControlConsole.js b/tgui/packages/tgui/interfaces/RoboticsControlConsole.js
index 746cb7ad9c..1520b4093b 100644
--- a/tgui/packages/tgui/interfaces/RoboticsControlConsole.js
+++ b/tgui/packages/tgui/interfaces/RoboticsControlConsole.js
@@ -8,6 +8,7 @@ export const RoboticsControlConsole = (props, context) => {
const [tab, setTab] = useSharedState(context, 'tab', 1);
const {
can_hack,
+ can_convert,
cyborgs = [],
drones = [],
} = data;
@@ -34,7 +35,9 @@ export const RoboticsControlConsole = (props, context) => {
{tab === 1 && (
-
+
)}
{tab === 2 && (
@@ -45,7 +48,7 @@ export const RoboticsControlConsole = (props, context) => {
};
const Cyborgs = (props, context) => {
- const { cyborgs, can_hack } = props;
+ const { cyborgs, can_hack, can_convert } = props;
const { act, data } = useBackend(context);
if (!cyborgs.length) {
return (
@@ -70,6 +73,15 @@ const Cyborgs = (props, context) => {
ref: cyborg.ref,
})} />
)}
+ {!!can_convert && !cyborg.servant && (
+