diff --git a/_maps/map_files/BoxStation/BoxStation.dmm b/_maps/map_files/BoxStation/BoxStation.dmm
index 444837a49a..8595d321fb 100644
--- a/_maps/map_files/BoxStation/BoxStation.dmm
+++ b/_maps/map_files/BoxStation/BoxStation.dmm
@@ -1492,6 +1492,10 @@
/obj/structure/lattice/catwalk,
/turf/open/space,
/area/solar/starboard/fore)
+"adu" = (
+/obj/effect/landmark/barthpot,
+/turf/open/floor/wood,
+/area/library)
"adw" = (
/obj/structure/cable,
/obj/structure/lattice/catwalk,
@@ -105520,7 +105524,7 @@ aIt
aPd
aIt
aPb
-aIt
+adu
aXu
aYW
bat
diff --git a/code/__DEFINES/say.dm b/code/__DEFINES/say.dm
index ca97d52539..3521a6d637 100644
--- a/code/__DEFINES/say.dm
+++ b/code/__DEFINES/say.dm
@@ -79,8 +79,14 @@
#define LINGHIVE_LINK 3
//whether the emote is visible or audible.
+// Requires sight
#define EMOTE_VISIBLE 1
+// Requires hearing
#define EMOTE_AUDIBLE 2
+// Requires sight or hearing
+#define EMOTE_BOTH 3
+// Always able to be seen
+#define EMOTE_OMNI 4
//Don't set this very much higher then 1024 unless you like inviting people in to dos your server with message spam
#define MAX_MESSAGE_LEN 2048 //Citadel edit: What's the WORST that could happen?
diff --git a/code/__DEFINES/species.dm b/code/__DEFINES/species.dm
index 087d317919..13206ec0fa 100644
--- a/code/__DEFINES/species.dm
+++ b/code/__DEFINES/species.dm
@@ -33,6 +33,7 @@
#define SPECIES_SYNTH "synth"
#define SPECIES_SYNTH_MIL "military_synth"
#define SPECIES_VAMPIRE "vampire"
+ #define SPECIES_VAMPIRE_WEAK "vampire_roundstart"
#define SPECIES_XENOHYBRID "xeno"
#define SPECIES_ZOMBIE "zombie"
diff --git a/code/__DEFINES/traits.dm b/code/__DEFINES/traits.dm
index 520f205381..82302a2e06 100644
--- a/code/__DEFINES/traits.dm
+++ b/code/__DEFINES/traits.dm
@@ -208,10 +208,9 @@
#define TRAIT_FAST_PUMP "fast_pump"
#define TRAIT_NO_PROCESS_FOOD "no-process-food" // You don't get benefits from nutriment, nor nutrition from reagent consumables
#define TRAIT_NICE_SHOT "nice_shot" //hnnnnnnnggggg..... you're pretty good...
-/// Prevents stamina buffer regeneration
-#define TRAIT_NO_STAMINA_BUFFER_REGENERATION "block_stamina_buffer_regen"
-/// Prevents stamina regeneration
-#define TRAIT_NO_STAMINA_REGENERATION "block_stamina_regen"
+#define TRAIT_NO_STAMINA_BUFFER_REGENERATION "block_stamina_buffer_regen" /// Prevents stamina buffer regeneration
+#define TRAIT_NO_STAMINA_REGENERATION "block_stamina_regen" /// Prevents stamina regeneration
+#define TRAIT_ARMOR_BROKEN "armor_broken" //acts as if you are wearing no clothing when taking damage, does not affect non-clothing sources of protection
// mobility flag traits
// IN THE FUTURE, IT WOULD BE NICE TO DO SOMETHING SIMILAR TO https://github.com/tgstation/tgstation/pull/48923/files (ofcourse not nearly the same because I have my.. thoughts on it)
diff --git a/code/datums/components/squeak.dm b/code/datums/components/squeak.dm
index b8191a377b..0552a791ea 100644
--- a/code/datums/components/squeak.dm
+++ b/code/datums/components/squeak.dm
@@ -46,6 +46,21 @@
if(isnum(use_delay_override))
use_delay = use_delay_override
+/datum/component/squeak/UnregisterFromParent()
+ if(!isatom(parent))
+ return
+ UnregisterSignal(parent, list(COMSIG_ATOM_ENTERED, COMSIG_ATOM_BLOB_ACT, COMSIG_ATOM_HULK_ATTACK, COMSIG_PARENT_ATTACKBY))
+ if(ismovable(parent))
+ UnregisterSignal(parent, list(COMSIG_MOVABLE_BUMP, COMSIG_MOVABLE_IMPACT,
+ COMSIG_MOVABLE_CROSSED, COMSIG_ITEM_WEARERCROSSED, COMSIG_MOVABLE_CROSS,
+ COMSIG_CROSS_SQUEAKED, COMSIG_MOVABLE_DISPOSING))
+ if(isitem(parent))
+ UnregisterSignal(parent, list(COMSIG_ITEM_ATTACK, COMSIG_ITEM_ATTACK_OBJ, COMSIG_ITEM_HIT_REACT, COMSIG_ITEM_ATTACK_SELF,
+ COMSIG_ITEM_EQUIPPED, COMSIG_ITEM_DROPPED))
+ if(istype(parent, /obj/item/clothing/shoes))
+ UnregisterSignal(parent, COMSIG_SHOES_STEP_ACTION)
+ return ..()
+
/datum/component/squeak/proc/play_squeak()
do_play_squeak()
diff --git a/code/datums/emotes.dm b/code/datums/emotes.dm
index 1749026f53..94ab538e9d 100644
--- a/code/datums/emotes.dm
+++ b/code/datums/emotes.dm
@@ -69,9 +69,13 @@
M.show_message(dchatmsg) //SKYRAT CHANGE
if(emote_type == EMOTE_AUDIBLE)
- user.audible_message(msg, user_msg = TRUE, runechat_popup = chat_popup) //SKYRAT CHANGE
- else
- user.visible_message(msg, user_msg = TRUE, runechat_popup = chat_popup) //SKYRAT CHANGE
+ user.audible_message(msg, runechat_popup = chat_popup)
+ else if(emote_type == EMOTE_VISIBLE)
+ user.visible_message(msg, runechat_popup = chat_popup)
+ else if(emote_type == EMOTE_BOTH)
+ user.visible_message(msg, blind_message = msg, runechat_popup = chat_popup)
+ else if(emote_type == EMOTE_OMNI)
+ user.visible_message(msg, omni = TRUE, runechat_popup = chat_popup)
//Skyrat change
if(image_popup)
flick_emote_popup_on_mob(user, image_popup, 40)
diff --git a/code/datums/mutations/_mutations.dm b/code/datums/mutations/_mutations.dm
index 3bddfdaa63..b72874c329 100644
--- a/code/datums/mutations/_mutations.dm
+++ b/code/datums/mutations/_mutations.dm
@@ -150,7 +150,6 @@
overlays_standing[CM.layer_used] = mut_overlay
apply_overlay(CM.layer_used)
-
/datum/mutation/human/proc/modify() //called when a genome is applied so we can properly update some stats without having to remove and reapply the mutation from someone
if(modified || !power || !owner)
return
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/defib.dm b/code/game/objects/items/defib.dm
index 4777796331..ecc8f9187a 100644
--- a/code/game/objects/items/defib.dm
+++ b/code/game/objects/items/defib.dm
@@ -616,7 +616,7 @@
if(defib.healdisk)
H.heal_overall_damage(25, 25)
var/list/policies = CONFIG_GET(keyed_list/policyconfig)
- var/timelimit = CONFIG_GET(number/defib_cmd_time_limit)
+ var/timelimit = CONFIG_GET(number/defib_cmd_time_limit) * 10 //the config is in seconds, not deciseconds
var/late = timelimit && (tplus > timelimit)
var/policy = late? policies[POLICYCONFIG_ON_DEFIB_LATE] : policies[POLICYCONFIG_ON_DEFIB_INTACT]
if(policy)
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/atmospherics/machinery/components/unary_devices/outlet_injector.dm b/code/modules/atmospherics/machinery/components/unary_devices/outlet_injector.dm
index a07f131d62..9aa3c8c16d 100644
--- a/code/modules/atmospherics/machinery/components/unary_devices/outlet_injector.dm
+++ b/code/modules/atmospherics/machinery/components/unary_devices/outlet_injector.dm
@@ -196,8 +196,8 @@
icon_state = "inje_map-1"
/obj/machinery/atmospherics/components/unary/outlet_injector/layer3
- piping_layer = 2
- icon_state = "inje_map-2"
+ piping_layer = 3
+ icon_state = "inje_map-3"
/obj/machinery/atmospherics/components/unary/outlet_injector/on
on = TRUE
@@ -207,8 +207,8 @@
icon_state = "inje_map-1"
/obj/machinery/atmospherics/components/unary/outlet_injector/on/layer3
- piping_layer = 2
- icon_state = "inje_map-2"
+ piping_layer = 3
+ icon_state = "inje_map-3"
/obj/machinery/atmospherics/components/unary/outlet_injector/atmos
frequency = FREQ_ATMOS_STORAGE
diff --git a/code/modules/events/anomaly.dm b/code/modules/events/anomaly.dm
index 394294db37..1b0c19274b 100644
--- a/code/modules/events/anomaly.dm
+++ b/code/modules/events/anomaly.dm
@@ -44,7 +44,13 @@
priority_announce("Localized energetic flux wave detected on long range scanners. Expected location of impact: [impact_area.name].", "Anomaly Alert")
/datum/round_event/anomaly/start()
- var/turf/T = pick(get_area_turfs(impact_area))
+ var/list/turf/valid = list()
+ for(var/i in get_area_turfs(impact_area))
+ var/turf/T = i
+ if(T.density)
+ continue
+ valid += T
+ var/turf/T = pick(valid)
var/newAnomaly
if(T)
newAnomaly = new anomaly_path(T)
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/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm
index 87dcd73e83..9c23c8b5de 100644
--- a/code/modules/mob/living/carbon/human/human.dm
+++ b/code/modules/mob/living/carbon/human/human.dm
@@ -244,6 +244,7 @@
// called when something steps onto a human
// this could be made more general, but for now just handle mulebot
/mob/living/carbon/human/Crossed(atom/movable/AM)
+ ..()
var/mob/living/simple_animal/bot/mulebot/MB = AM
if(istype(MB))
MB.RunOver(src)
diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm
index 19277b6cc9..5d15180b95 100644
--- a/code/modules/mob/living/carbon/human/human_defense.dm
+++ b/code/modules/mob/living/carbon/human/human_defense.dm
@@ -1,4 +1,6 @@
/mob/living/carbon/human/getarmor(def_zone, type)
+ if(HAS_TRAIT(src, TRAIT_ARMOR_BROKEN)) //trait that makes it act as if you have no armor at all, you take natural damage from all sources
+ return 0
var/armorval = 0
var/organnum = 0
@@ -18,7 +20,6 @@
organnum++
return (armorval/max(organnum, 1))
-
/mob/living/carbon/human/proc/checkarmor(obj/item/bodypart/def_zone, d_type)
if(!d_type || !def_zone)
return 0
diff --git a/code/modules/mob/living/carbon/human/species_types/jellypeople.dm b/code/modules/mob/living/carbon/human/species_types/jellypeople.dm
index 683829f133..f78173dcfb 100644
--- a/code/modules/mob/living/carbon/human/species_types/jellypeople.dm
+++ b/code/modules/mob/living/carbon/human/species_types/jellypeople.dm
@@ -715,18 +715,25 @@
return FALSE
/datum/action/innate/slime_puddle/Activate()
+ var/mob/living/carbon/human/H = owner
+ //if they have anything stuck to their hands, we immediately say 'no' and return
+ for(var/obj/item/I in H.held_items)
+ if(HAS_TRAIT(I, TRAIT_NODROP))
+ to_chat(owner, "There's something stuck to your hand, stopping you from transforming!")
+ return
if(isjellyperson(owner) && IsAvailable())
transforming = TRUE
UpdateButtonIcon()
- var/mob/living/carbon/human/H = owner
var/mutcolor = "#" + H.dna.features["mcolor"]
if(!is_puddle)
- if(CHECK_MOBILITY(H, MOBILITY_USE))
- is_puddle = TRUE
- owner.cut_overlays()
+ if(CHECK_MOBILITY(H, MOBILITY_USE)) //if we can use items, we can turn into a puddle
+ is_puddle = TRUE //so we know which transformation to use when its used
+ owner.cut_overlays() //we dont show our normal sprite, we show a puddle sprite
var/obj/effect/puddle_effect = new puddle_into_effect(get_turf(owner), owner.dir)
puddle_effect.color = mutcolor
- H.Stun(in_transformation_duration, ignore_canstun = TRUE)
+ H.Stun(in_transformation_duration, ignore_canstun = TRUE) //cant move while transforming
+
+ //series of traits that make up the puddle behaviour
ADD_TRAIT(H, TRAIT_PARALYSIS_L_ARM, SLIMEPUDDLE_TRAIT)
ADD_TRAIT(H, TRAIT_PARALYSIS_R_ARM, SLIMEPUDDLE_TRAIT)
ADD_TRAIT(H, TRAIT_MOBILITY_NOPICKUP, SLIMEPUDDLE_TRAIT)
@@ -734,19 +741,30 @@
ADD_TRAIT(H, TRAIT_SPRINT_LOCKED, SLIMEPUDDLE_TRAIT)
ADD_TRAIT(H, TRAIT_COMBAT_MODE_LOCKED, SLIMEPUDDLE_TRAIT)
ADD_TRAIT(H, TRAIT_MOBILITY_NOREST, SLIMEPUDDLE_TRAIT)
+ ADD_TRAIT(H, TRAIT_ARMOR_BROKEN, SLIMEPUDDLE_TRAIT)
+ H.update_disabled_bodyparts(silent = TRUE) //silently update arms to be paralysed
+
H.add_movespeed_modifier(/datum/movespeed_modifier/slime_puddle)
- H.update_disabled_bodyparts(silent = TRUE)
+
H.layer -= 1 //go one layer down so people go over you
- ENABLE_BITFIELD(H.pass_flags, PASSMOB)
- squeak = H.AddComponent(/datum/component/squeak, custom_sounds = list('sound/effects/blobattack.ogg'))
- sleep(in_transformation_duration)
+ ENABLE_BITFIELD(H.pass_flags, PASSMOB) //this actually lets people pass over you
+ squeak = H.AddComponent(/datum/component/squeak, custom_sounds = list('sound/effects/blobattack.ogg')) //blorble noise when people step on you
+
+ //if the user is a changeling, retract their sting
+ H.unset_sting()
+
+ sleep(in_transformation_duration) //wait for animation to end
+
+ //set the puddle overlay up
var/mutable_appearance/puddle_overlay = mutable_appearance(icon = puddle_icon, icon_state = puddle_state)
puddle_overlay.color = mutcolor
tracked_overlay = puddle_overlay
owner.add_overlay(puddle_overlay)
+
transforming = FALSE
UpdateButtonIcon()
else
+ //like the above, but reverse everything done!
owner.cut_overlay(tracked_overlay)
var/obj/effect/puddle_effect = new puddle_from_effect(get_turf(owner), owner.dir)
puddle_effect.color = mutcolor
@@ -759,6 +777,7 @@
REMOVE_TRAIT(H, TRAIT_SPRINT_LOCKED, SLIMEPUDDLE_TRAIT)
REMOVE_TRAIT(H, TRAIT_COMBAT_MODE_LOCKED, SLIMEPUDDLE_TRAIT)
REMOVE_TRAIT(H, TRAIT_MOBILITY_NOREST, SLIMEPUDDLE_TRAIT)
+ REMOVE_TRAIT(H, TRAIT_ARMOR_BROKEN, SLIMEPUDDLE_TRAIT)
H.update_disabled_bodyparts(silent = TRUE)
H.remove_movespeed_modifier(/datum/movespeed_modifier/slime_puddle)
H.layer += 1 //go one layer back above!
diff --git a/code/modules/mob/living/carbon/human/species_types/vampire.dm b/code/modules/mob/living/carbon/human/species_types/vampire.dm
index 676248c463..558fdc6594 100644
--- a/code/modules/mob/living/carbon/human/species_types/vampire.dm
+++ b/code/modules/mob/living/carbon/human/species_types/vampire.dm
@@ -15,8 +15,13 @@
skinned_type = /obj/item/stack/sheet/animalhide/human
var/info_text = "You are a Vampire. You will slowly but constantly lose blood if outside of a coffin. If inside a coffin, you will slowly heal. You may gain more blood by grabbing a live victim and using your drain ability."
species_category = SPECIES_CATEGORY_UNDEAD
+ var/batform_enabled = TRUE
-/datum/species/vampire/check_roundstart_eligible()
+/datum/species/vampire/roundstart
+ id = SPECIES_VAMPIRE_WEAK
+ batform_enabled = FALSE
+
+/datum/species/vampire/roundstart/check_roundstart_eligible()
if(SSevents.holidays && SSevents.holidays[HALLOWEEN])
return TRUE
return FALSE
@@ -27,8 +32,9 @@
if(!C.dna.skin_tone_override)
C.skin_tone = "albino"
C.update_body(0)
- var/obj/effect/proc_holder/spell/targeted/shapeshift/bat/B = new
- C.AddSpell(B)
+ if(batform_enabled)
+ var/obj/effect/proc_holder/spell/targeted/shapeshift/bat/B = new
+ C.AddSpell(B)
/datum/species/vampire/on_species_loss(mob/living/carbon/C)
. = ..()
diff --git a/code/modules/mob/living/emote.dm b/code/modules/mob/living/emote.dm
index eaab9eb450..a71352c4be 100644
--- a/code/modules/mob/living/emote.dm
+++ b/code/modules/mob/living/emote.dm
@@ -451,6 +451,7 @@
key = "me"
key_third_person = "custom"
message = null
+ emote_type = EMOTE_BOTH
/datum/emote/living/custom/proc/check_invalid(mob/user, input)
if(stop_bad_mime.Find(input, 1, 1))
@@ -470,24 +471,12 @@
else if(!params)
var/custom_emote = stripped_multiline_input_or_reflect(user, "Choose an emote to display.", "Custom Emote", null, MAX_MESSAGE_LEN)
if(custom_emote && !check_invalid(user, custom_emote))
- var/type = input("Is this a visible or hearable emote?") as null|anything in list("Visible", "Hearable")
- switch(type)
- if("Visible")
- emote_type = EMOTE_VISIBLE
- if("Hearable")
- emote_type = EMOTE_AUDIBLE
- else
- alert("Unable to use this emote, must be either hearable or visible.")
- return
message = custom_emote
else
message = params
- if(type_override)
- emote_type = type_override
message = user.say_emphasis(message)
. = ..()
message = null
- emote_type = EMOTE_VISIBLE
/datum/emote/living/custom/replace_pronoun(mob/user, message)
return message
diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/drake.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/drake.dm
index 1346302aeb..f7d044831a 100644
--- a/code/modules/mob/living/simple_animal/hostile/megafauna/drake.dm
+++ b/code/modules/mob/living/simple_animal/hostile/megafauna/drake.dm
@@ -87,7 +87,7 @@ Difficulty: Medium
return FALSE
return ..()
-/mob/living/simple_animal/hostile/megafauna/dragon/visible_message(message, self_message, blind_message, vision_distance = DEFAULT_MESSAGE_RANGE, list/ignored_mobs, mob/target, target_message, user_msg, runechat_popup) //SKYRAT CHANGE
+/mob/living/simple_animal/hostile/megafauna/dragon/visible_message(message, self_message, blind_message, vision_distance = DEFAULT_MESSAGE_RANGE, list/ignored_mobs, mob/target, target_message, omni = FALSE, runechat_popup)
if(swooping & SWOOP_INVULNERABLE) //to suppress attack messages without overriding every single proc that could send a message saying we got hit
return
return ..()
diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm
index 8d09249e1b..75318f9f85 100644
--- a/code/modules/mob/mob.dm
+++ b/code/modules/mob/mob.dm
@@ -127,12 +127,13 @@
* * ignored_mobs (optional) doesn't show any message to any given mob in the list.
* * target (optional) is the other mob involved with the visible message. For example, the attacker in many combat messages.
* * target_message (optional) is what the target mob will see e.g. "[src] does something to you!"
+ * * omni (optional) if TRUE, will show to users no matter what.
*/
-/atom/proc/visible_message(message, self_message, blind_message, vision_distance = DEFAULT_MESSAGE_RANGE, ignored_mobs, mob/target, target_message, user_msg, runechat_popup) //SKYRAT CHANGE
+/atom/proc/visible_message(message, self_message, blind_message, vision_distance = DEFAULT_MESSAGE_RANGE, ignored_mobs, mob/target, target_message, omni = FALSE, runechat_popup)
var/turf/T = get_turf(src)
if(!T)
return
- var/final_msg = user_msg ? "[src] " + message : message //SKYRAT CHANGE
+ var/final_msg = !(src == null) ? "[src] " + message : "Unknown " + message //SKYRAT CHANGE
var/list/hearers = get_hearers_in_view(vision_distance, src) //caches the hearers and then removes ignored mobs.
if(!length(hearers))
return
@@ -140,20 +141,26 @@
if(target_message && target && istype(target) && target.client)
hearers -= target
- //This entire if/else chain could be in two lines but isn't for readibilties sake.
- var/msg = target_message
- if(target.see_invisible[src] " + self_message : self_message, MSG_VISUAL, blind_message, MSG_AUDIBLE) //SKYRAT CHANGE
+ if(!omni)
+ show_message(self_message, MSG_VISUAL, blind_message, MSG_AUDIBLE)
+ if(runechat_popup && client?.prefs.chat_on_map && client.prefs.see_chat_emotes) //SKYRAT CHANGE
+ create_chat_message(src, null, self_message, list("emote", "italics"), null) //Skyrat change
+ else
+ show_message(self_message)
/**
* Show a message to all mobs in earshot of this atom
diff --git a/code/modules/mob/say_vr.dm b/code/modules/mob/say_vr.dm
index cc2d6a60bb..e03f36c344 100644
--- a/code/modules/mob/say_vr.dm
+++ b/code/modules/mob/say_vr.dm
@@ -39,15 +39,6 @@ proc/get_top_level_mob(var/mob/S)
else if(!params)
var/subtle_emote = stripped_multiline_input_or_reflect(user, "Choose an emote to display.", "Subtle", null, MAX_MESSAGE_LEN)
if(subtle_emote && !check_invalid(user, subtle_emote))
- var/type = input("Is this a visible or hearable emote?") as null|anything in list("Visible", "Hearable")
- switch(type)
- if("Visible")
- emote_type = EMOTE_VISIBLE
- if("Hearable")
- emote_type = EMOTE_AUDIBLE
- else
- alert("Unable to use this emote, must be either hearable or visible.")
- return
message = subtle_emote
else
return FALSE
@@ -69,11 +60,7 @@ proc/get_top_level_mob(var/mob/S)
if(M.stat == DEAD && M.client && (M.client.prefs.chat_toggles & CHAT_GHOSTSIGHT) && !(M in viewers(T, null)) && (user.client)) //SKYRAT CHANGE - only user controlled mobs show their emotes to all-seeing ghosts, to reduce chat spam
M.show_message(message)
- if(emote_type == EMOTE_AUDIBLE)
- user.audible_message(message=message,hearing_distance=1)
- else
- user.visible_message(message=message,self_message=message,vision_distance=1)
-
+ user.visible_message(message = message, self_message = message, vision_distance = 1, omni = TRUE)
///////////////// SUBTLE 2: NO GHOST BOOGALOO
@@ -122,10 +109,7 @@ proc/get_top_level_mob(var/mob/S)
user.log_message(message, LOG_SUBTLER)
message = "[user] " + "[user.say_emphasis(message)]"
- if(emote_type == EMOTE_AUDIBLE)
- user.audible_message(message=message,hearing_distance=1, ignored_mobs = GLOB.dead_mob_list)
- else
- user.visible_message(message=message,self_message=message,vision_distance=1, ignored_mobs = GLOB.dead_mob_list)
+ user.visible_message(message = message, self_message = message, vision_distance = 1, ignored_mobs = GLOB.dead_mob_list, omni = TRUE)
///////////////// VERB CODE
/mob/living/verb/subtle()
diff --git a/code/modules/movespeed/modifiers/innate.dm b/code/modules/movespeed/modifiers/innate.dm
index 946d93e96b..f5a599f940 100644
--- a/code/modules/movespeed/modifiers/innate.dm
+++ b/code/modules/movespeed/modifiers/innate.dm
@@ -20,5 +20,6 @@
flags = IGNORE_NOSLOW
/datum/movespeed_modifier/slime_puddle
- multiplicative_slowdown = 2
+ multiplicative_slowdown = 4.5
flags = IGNORE_NOSLOW
+ variable = TRUE
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/reagents/chemistry/reagents/medicine_reagents.dm b/code/modules/reagents/chemistry/reagents/medicine_reagents.dm
index b67fac2423..6867058faa 100644
--- a/code/modules/reagents/chemistry/reagents/medicine_reagents.dm
+++ b/code/modules/reagents/chemistry/reagents/medicine_reagents.dm
@@ -973,7 +973,7 @@
M.emote("gasp")
log_combat(M, M, "revived", src)
var/list/policies = CONFIG_GET(keyed_list/policyconfig)
- var/timelimit = CONFIG_GET(number/defib_cmd_time_limit)
+ var/timelimit = CONFIG_GET(number/defib_cmd_time_limit) * 10 //the config is in seconds, not deciseconds
var/late = timelimit && (tplus > timelimit)
var/policy = late? policies[POLICYCONFIG_ON_DEFIB_LATE] : policies[POLICYCONFIG_ON_DEFIB_INTACT]
if(policy)
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/code/modules/surgery/advanced/revival.dm b/code/modules/surgery/advanced/revival.dm
index c61ee330e6..0b864958a0 100644
--- a/code/modules/surgery/advanced/revival.dm
+++ b/code/modules/surgery/advanced/revival.dm
@@ -70,7 +70,7 @@
if(O.organ_flags & ORGAN_FAILING)
O.applyOrganDamage(-5)
var/list/policies = CONFIG_GET(keyed_list/policyconfig)
- var/timelimit = CONFIG_GET(number/defib_cmd_time_limit)
+ var/timelimit = CONFIG_GET(number/defib_cmd_time_limit) * 10 //the config is in seconds, not deciseconds
var/late = timelimit && (tplus > timelimit)
var/policy = late? policies[POLICYCONFIG_ON_DEFIB_LATE] : policies[POLICYCONFIG_ON_DEFIB_INTACT]
if(policy)
diff --git a/html/changelogs/AutoChangeLog-pr-13567.yml b/html/changelogs/AutoChangeLog-pr-13567.yml
new file mode 100644
index 0000000000..d51db8e3b8
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-13567.yml
@@ -0,0 +1,8 @@
+author: "timothyteakettle"
+delete-after: True
+changes:
+ - tweak: "changeling stings retract upon turning into a slime puddle"
+ - tweak: "you cannot transform into a slime puddle with a no drop item in your hands"
+ - tweak: "slime puddles are now transparent and their colour looks more natural in comparison to the user"
+ - tweak: "slime puddles are now even slower"
+ - tweak: "slime puddles now get no protection from worn clothing"
diff --git a/html/changelogs/AutoChangeLog-pr-13629.yml b/html/changelogs/AutoChangeLog-pr-13629.yml
new file mode 100644
index 0000000000..954f6972ec
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-13629.yml
@@ -0,0 +1,4 @@
+author: "silicons"
+delete-after: True
+changes:
+ - rscdel: "anomalies no longer spawn in walls"
diff --git a/html/changelogs/AutoChangeLog-pr-13632.yml b/html/changelogs/AutoChangeLog-pr-13632.yml
new file mode 100644
index 0000000000..bf478ec97c
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-13632.yml
@@ -0,0 +1,4 @@
+author: "timothyteakettle"
+delete-after: True
+changes:
+ - bugfix: "time for memory loss message to show up when being revived is now correctly 300 seconds, instead of 30"
diff --git a/html/changelogs/AutoChangeLog-pr-13633.yml b/html/changelogs/AutoChangeLog-pr-13633.yml
new file mode 100644
index 0000000000..d9246cfff6
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-13633.yml
@@ -0,0 +1,4 @@
+author: "ArchieBeepBoop"
+delete-after: True
+changes:
+ - bugfix: "Outlet Injector Mapping Asset Layer Fix"
diff --git a/html/changelogs/AutoChangeLog-pr-13647.yml b/html/changelogs/AutoChangeLog-pr-13647.yml
new file mode 100644
index 0000000000..b3211ee759
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-13647.yml
@@ -0,0 +1,4 @@
+author: "zeroisthebiggay"
+delete-after: True
+changes:
+ - bugfix: "jacqueline spawns on boxstation"
diff --git a/html/changelogs/AutoChangeLog-pr-13648.yml b/html/changelogs/AutoChangeLog-pr-13648.yml
new file mode 100644
index 0000000000..4148230385
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-13648.yml
@@ -0,0 +1,13 @@
+author: "DeltaFire15"
+delete-after: True
+changes:
+ - bugfix: "The 'Your body is in a cloner' notification works again"
+ - bugfix: "Hijack implants should work properly again (or, at least better)"
+ - bugfix: "Liches are now good skeletons again instead of weak ones"
+ - bugfix: "The piratepad control cannot be destroyed again."
+ - bugfix: "Pirates have received new supplies of jetpacks instead of useless oxygen tanks"
+ - bugfix: "Ratvarian AIs are once again able to show their linked borgs Ratvar's light"
+ - bugfix: "Hijackers are once again unable to detonate borgs without being adjacent to the console"
+ - bugfix: "Automated annoucement systems and gulag ore consoles no longer waste emag charges"
+ - bugfix: "Automated announcement systems once again can be remote controlled by non-AIs with silicon access"
+ - bugfix: "APCs being hijacked multiple times at once is no longer possible, preventing some issues"
diff --git a/icons/mob/mob.dmi b/icons/mob/mob.dmi
index 10b733bae4..3d1cbfa273 100644
Binary files a/icons/mob/mob.dmi and b/icons/mob/mob.dmi differ
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 && (
+