diff --git a/code/__defines/_planes+layers.dm b/code/__defines/_planes+layers.dm
index 96d65c6f495..551e94183c1 100644
--- a/code/__defines/_planes+layers.dm
+++ b/code/__defines/_planes+layers.dm
@@ -93,6 +93,8 @@ What is the naming convention for planes or layers?
#define BELOW_MOB_LAYER 3.9 // Should be converted to plane swaps
#define ABOVE_MOB_LAYER 4.1 // Should be converted to plane swaps
+#define ABOVE_MOB_PLANE -24
+
// Invisible things plane
#define CLOAKED_PLANE -15
@@ -122,6 +124,7 @@ What is the naming convention for planes or layers?
#define PLANE_PLANETLIGHTING 4 //Lighting on planets
#define PLANE_LIGHTING 5 //Where the lighting (and darkness) lives
#define PLANE_LIGHTING_ABOVE 6 //For glowy eyes etc. that shouldn't be affected by darkness
+#define PLANE_RUNECHAT 7
#define PLANE_GHOSTS 10 //Spooooooooky ghooooooosts
#define PLANE_AI_EYE 11 //The AI eye lives here
diff --git a/code/__defines/subsystems.dm b/code/__defines/subsystems.dm
index 49c04851da2..1365584f13d 100644
--- a/code/__defines/subsystems.dm
+++ b/code/__defines/subsystems.dm
@@ -62,7 +62,8 @@ var/global/list/runlevel_flags = list(RUNLEVEL_LOBBY, RUNLEVEL_SETUP, RUNLEVEL_G
#define INIT_ORDER_PLANETS 18
#define INIT_ORDER_JOB 17
#define INIT_ORDER_ALARM 16 // Must initialize before atoms.
-#define INIT_ORDER_ATOMS 15
+#define INIT_ORDER_TRANSCORE 15 // VOREStation Edit
+#define INIT_ORDER_ATOMS 14 // VOREStation Edit
#define INIT_ORDER_MACHINES 10
#define INIT_ORDER_SHUTTLES 3
#define INIT_ORDER_TIMER 1
diff --git a/code/_helpers/time.dm b/code/_helpers/time.dm
index 6e63dabb45a..b75aeb77e38 100644
--- a/code/_helpers/time.dm
+++ b/code/_helpers/time.dm
@@ -20,6 +20,8 @@
#define TICKS2DS(T) ((T) TICKS) // Convert ticks to deciseconds
#define DS2NEARESTTICK(DS) TICKS2DS(-round(-(DS2TICKS(DS))))
+var/world_startup_time
+
/proc/get_game_time()
var/global/time_offset = 0
var/global/last_time = 0
diff --git a/code/_onclick/observer.dm b/code/_onclick/observer.dm
index c2a8a62ea8d..f89d4001476 100644
--- a/code/_onclick/observer.dm
+++ b/code/_onclick/observer.dm
@@ -66,12 +66,21 @@
if(target)
user.loc = get_turf(target)
-/obj/machinery/gateway/centerstation/attack_ghost(mob/user as mob)
- if(awaygate)
- user.loc = awaygate.loc
+// VOREStation Edit Begin
+
+/obj/machinery/gateway/centerstation/attack_ghost(mob/user as mob)
+ if(awaygate)
+ if(user.client.holder)
+ user.loc = awaygate.loc
+ else if(active)
+ user.loc = awaygate.loc
+ else
+ return
else
to_chat(user, "[src] has no destination.")
+// VOREStation Edit End
+
/obj/machinery/gateway/centeraway/attack_ghost(mob/user as mob)
if(stationgate)
user.loc = stationgate.loc
diff --git a/code/controllers/subsystems/mobs.dm b/code/controllers/subsystems/mobs.dm
index b44b9ac5d6c..1c804979838 100644
--- a/code/controllers/subsystems/mobs.dm
+++ b/code/controllers/subsystems/mobs.dm
@@ -25,13 +25,10 @@ SUBSYSTEM_DEF(mobs)
/datum/controller/subsystem/mobs/fire(resumed = 0)
if (!resumed)
src.currentrun = mob_list.Copy()
- process_z.Cut()
+ process_z.len = GLOB.living_players_by_zlevel.len
slept_mobs = 0
- var/level = 1
- while(process_z.len < GLOB.living_players_by_zlevel.len)
- process_z.len++
+ for(var/level in 1 to process_z.len)
process_z[level] = GLOB.living_players_by_zlevel[level].len
- level++
//cache for sanic speed (lists are references anyways)
var/list/currentrun = src.currentrun
diff --git a/code/controllers/subsystems/transcore_vr.dm b/code/controllers/subsystems/transcore_vr.dm
index bd8786ed0c8..36f2a77914d 100644
--- a/code/controllers/subsystems/transcore_vr.dm
+++ b/code/controllers/subsystems/transcore_vr.dm
@@ -10,25 +10,34 @@ SUBSYSTEM_DEF(transcore)
name = "Transcore"
priority = 20
wait = 3 MINUTES
- flags = SS_BACKGROUND|SS_NO_INIT
+ flags = SS_BACKGROUND
runlevels = RUNLEVEL_GAME
+ init_order = INIT_ORDER_TRANSCORE
// THINGS
var/overdue_time = 15 MINUTES
- var/core_dumped = FALSE // Core has been dumped! Also set can_fire = 0 when you set this.
var/current_step = SSTRANSCORE_IMPLANTS
var/cost_backups = 0
var/cost_implants = 0
- var/list/datum/transhuman/mind_record/backed_up = list() // All known mind records, indexed by MR.mindname/mind.name
- var/list/datum/transhuman/mind_record/has_left = list() // Why do we even have this?
- var/list/datum/transhuman/body_record/body_scans = list() // All known body records, indexed by BR.mydna.name
- var/list/obj/item/weapon/implant/backup/implants = list() // All OPERATING implants that are being ticked
+ var/list/datum/transcore_db/databases = list() // Holds instances of each database
+ var/datum/transcore_db/default_db // The default if no specific one is used
var/list/current_run = list()
+/datum/controller/subsystem/transcore/Initialize()
+ default_db = new()
+ databases["default"] = default_db
+ for(var/t in subtypesof(/datum/transcore_db))
+ var/datum/transcore_db/db = new t()
+ if(!db.key)
+ warning("Instantiated transcore DB without a key: [t]")
+ continue
+ databases[db.key] = db
+ return ..()
+
/datum/controller/subsystem/transcore/fire(resumed = 0)
var/timer = TICK_USAGE
@@ -37,30 +46,36 @@ SUBSYSTEM_DEF(transcore)
/datum/controller/subsystem/transcore/proc/process_implants(resumed = 0)
if (!resumed)
- src.current_run = implants.Copy()
+ // Create a flat list of every implant in every db with a value of the db they're in
+ src.current_run.Cut()
+ for(var/key in databases)
+ var/datum/transcore_db/db = databases[key]
+ for(var/obj/item/weapon/implant/backup/imp as anything in db.implants)
+ src.current_run[imp] = db
var/list/current_run = src.current_run
while(current_run.len)
var/obj/item/weapon/implant/backup/imp = current_run[current_run.len]
+ var/datum/transcore_db/db = current_run[imp]
current_run.len--
//Remove if not in a human anymore.
if(!imp || !isorgan(imp.loc))
- implants -= imp
+ db.implants -= imp
continue
//We're in an organ, at least.
var/obj/item/organ/external/EO = imp.loc
var/mob/living/carbon/human/H = EO.owner
if(!H)
- implants -= imp
+ db.implants -= imp
continue
//In a human
BITSET(H.hud_updateflag, BACKUP_HUD)
if(H == imp.imp_in && H.mind && H.stat < DEAD)
- SStranscore.m_backup(H.mind,H.nif)
+ db.m_backup(H.mind,H.nif)
persist_nif_data(H)
if(MC_TICK_CHECK)
@@ -68,18 +83,24 @@ SUBSYSTEM_DEF(transcore)
/datum/controller/subsystem/transcore/proc/process_backups(resumed = 0)
if (!resumed)
- src.current_run = backed_up.Copy()
+ // Create a flat list of every implant in every db with a value of the db they're in
+ src.current_run.Cut()
+ for(var/key in databases)
+ var/datum/transcore_db/db = databases[key]
+ for(var/name in db.backed_up)
+ var/datum/transhuman/mind_record/mr = db.backed_up[name]
+ src.current_run[mr] = db
var/list/current_run = src.current_run
while(current_run.len)
- var/name = current_run[current_run.len]
- var/datum/transhuman/mind_record/curr_MR = current_run[name]
- current_run -= name
+ var/datum/transhuman/mind_record/curr_MR = current_run[current_run.len]
+ var/datum/transcore_db/db = current_run[curr_MR]
+ current_run.len--
//Invalid record
if(!curr_MR)
log_debug("Tried to process [name] in transcore w/o a record!")
- backed_up -= name
+ db.backed_up -= curr_MR.mindname
continue
//Onetimes do not get processing or notifications
@@ -92,7 +113,7 @@ SUBSYSTEM_DEF(transcore)
curr_MR.dead_state = MR_NORMAL
else
if(curr_MR.dead_state != MR_DEAD) //First time switching to dead
- notify(name)
+ db.notify(curr_MR.mindname)
curr_MR.last_notification = world.time
curr_MR.dead_state = MR_DEAD
@@ -101,30 +122,100 @@ SUBSYSTEM_DEF(transcore)
/datum/controller/subsystem/transcore/stat_entry()
var/msg = list()
- if(core_dumped)
- msg += "CORE DUMPED | "
msg += "$:{"
msg += "IM:[round(cost_implants,1)]|"
msg += "BK:[round(cost_backups,1)]"
msg += "} "
msg += "#:{"
- msg += "IM:[implants.len]|"
- msg += "BK:[backed_up.len]"
+ msg += "DB:[databases.len]|"
+ if(!default_db)
+ msg += "DEFAULT DB MISSING"
+ else
+ msg += "DFM:[default_db.backed_up.len]|"
+ msg += "DFB:[default_db.body_scans.len]|"
+ msg += "DFI:[default_db.implants.len]"
msg += "} "
..(jointext(msg, null))
/datum/controller/subsystem/transcore/Recover()
- if (istype(SStranscore.body_scans))
- for(var/N in SStranscore.body_scans)
- if(N && SStranscore.body_scans[N]) body_scans[N] = SStranscore.body_scans[N]
- if(SStranscore.core_dumped)
- core_dumped = TRUE
- can_fire = FALSE
- else if (istype(SStranscore.backed_up))
- for(var/N in SStranscore.backed_up)
- if(N && SStranscore.backed_up[N]) backed_up[N] = SStranscore.backed_up[N]
+ for(var/key in SStranscore.databases)
+ if(!SStranscore.databases[key])
+ warning("SStranscore recovery found missing database value for key: [key]")
+ continue
+ if(key == "default")
+ default_db = SStranscore.databases[key]
-/datum/controller/subsystem/transcore/proc/m_backup(var/datum/mind/mind, var/obj/item/device/nif/nif, var/one_time = FALSE)
+ databases[key] = SStranscore.databases[key]
+
+/datum/controller/subsystem/transcore/proc/leave_round(var/mob/M)
+ if(!istype(M))
+ warning("Non-mob asked to be removed from transcore: [M] [M?.type]")
+ return
+ if(!M.mind)
+ warning("No mind mob asked to be removed from transcore: [M] [M?.type]")
+ return
+
+ for(var/key in databases)
+ var/datum/transcore_db/db = databases[key]
+ if(M.mind.name in db.backed_up)
+ var/datum/transhuman/mind_record/MR = db.backed_up[M.mind.name]
+ db.stop_backup(MR)
+ if(M.mind.name in db.body_scans) //This uses mind names to avoid people cryo'ing a printed body to delete body scans.
+ var/datum/transhuman/body_record/BR = db.body_scans[M.mind.name]
+ db.remove_body(BR)
+
+/datum/controller/subsystem/transcore/proc/db_by_key(var/key)
+ if(isnull(key))
+ return default_db
+ if(!databases[key])
+ warning("Tried to find invalid transcore database: [key]")
+ return default_db
+ return databases[key]
+
+/datum/controller/subsystem/transcore/proc/db_by_mind_name(var/name)
+ if(isnull(name))
+ return null
+ for(var/key in databases)
+ var/datum/transcore_db/db = databases[key]
+ if(name in db.backed_up)
+ return db
+
+// These are now just interfaces to databases
+/datum/controller/subsystem/transcore/proc/m_backup(var/datum/mind/mind, var/obj/item/device/nif/nif, var/one_time = FALSE, var/database_key)
+ var/datum/transcore_db/db = db_by_key(database_key)
+ db.m_backup(mind=mind, nif=nif, one_time=one_time)
+
+/datum/controller/subsystem/transcore/proc/add_backup(var/datum/transhuman/mind_record/MR, var/database_key)
+ var/datum/transcore_db/db = db_by_key(database_key)
+ db.add_backup(MR=MR)
+
+/datum/controller/subsystem/transcore/proc/stop_backup(var/datum/transhuman/mind_record/MR, var/database_key)
+ var/datum/transcore_db/db = db_by_key(database_key)
+ db.stop_backup(MR=MR)
+
+/datum/controller/subsystem/transcore/proc/add_body(var/datum/transhuman/body_record/BR, var/database_key)
+ var/datum/transcore_db/db = db_by_key(database_key)
+ db.add_body(BR=BR)
+
+/datum/controller/subsystem/transcore/proc/remove_body(var/datum/transhuman/body_record/BR, var/database_key)
+ var/datum/transcore_db/db = db_by_key(database_key)
+ db.remove_body(BR=BR)
+
+/datum/controller/subsystem/transcore/proc/core_dump(var/obj/item/weapon/disk/transcore/disk, var/database_key)
+ var/datum/transcore_db/db = db_by_key(database_key)
+ db.core_dump(disk=disk)
+
+
+/datum/transcore_db
+ var/list/datum/transhuman/mind_record/backed_up = list() // All known mind records, indexed by MR.mindname/mind.name
+ var/list/datum/transhuman/mind_record/has_left = list() // Why do we even have this?
+ var/list/datum/transhuman/body_record/body_scans = list() // All known body records, indexed by BR.mydna.name
+ var/list/obj/item/weapon/implant/backup/implants = list() // All OPERATING implants that are being ticked
+
+ var/core_dumped = FALSE
+ var/key // Key for this DB
+
+/datum/transcore_db/proc/m_backup(var/datum/mind/mind, var/obj/item/device/nif/nif, var/one_time = FALSE)
ASSERT(mind)
if(!mind.name || core_dumped)
return 0
@@ -154,12 +245,12 @@ SUBSYSTEM_DEF(transcore)
MR.nif_savedata = null
else
- MR = new(mind, mind.current, add_to_db = TRUE, one_time = one_time)
+ MR = new(mind, mind.current, add_to_db = TRUE, one_time = one_time, database_key = src.key)
return 1
// Send a past-due notification to the medical radio channel.
-/datum/controller/subsystem/transcore/proc/notify(var/name, var/repeated = FALSE)
+/datum/transcore_db/proc/notify(var/name, var/repeated = FALSE)
ASSERT(name)
if(repeated)
global_announcer.autosay("This is a repeat notification that [name] is past-due for a mind backup.", "TransCore Oversight", "Medical")
@@ -167,14 +258,14 @@ SUBSYSTEM_DEF(transcore)
global_announcer.autosay("[name] is past-due for a mind backup.", "TransCore Oversight", "Medical")
// Called from mind_record to add itself to the transcore.
-/datum/controller/subsystem/transcore/proc/add_backup(var/datum/transhuman/mind_record/MR)
+/datum/transcore_db/proc/add_backup(var/datum/transhuman/mind_record/MR)
ASSERT(MR)
backed_up[MR.mindname] = MR
backed_up = sortAssoc(backed_up)
log_debug("Added [MR.mindname] to transcore DB.")
// Remove a mind_record from the backup-checking list. Keeps track of it in has_left // Why do we do that? ~Leshana
-/datum/controller/subsystem/transcore/proc/stop_backup(var/datum/transhuman/mind_record/MR)
+/datum/transcore_db/proc/stop_backup(var/datum/transhuman/mind_record/MR)
ASSERT(MR)
has_left[MR.mindname] = MR
backed_up.Remove("[MR.mindname]")
@@ -182,20 +273,20 @@ SUBSYSTEM_DEF(transcore)
log_debug("Put [MR.mindname] in transcore suspended DB.")
// Called from body_record to add itself to the transcore.
-/datum/controller/subsystem/transcore/proc/add_body(var/datum/transhuman/body_record/BR)
+/datum/transcore_db/proc/add_body(var/datum/transhuman/body_record/BR)
ASSERT(BR)
body_scans[BR.mydna.name] = BR
body_scans = sortAssoc(body_scans)
log_debug("Added [BR.mydna.name] to transcore body DB.")
// Remove a body record from the database (Usually done when someone cryos) // Why? ~Leshana
-/datum/controller/subsystem/transcore/proc/remove_body(var/datum/transhuman/body_record/BR)
+/datum/transcore_db/proc/remove_body(var/datum/transhuman/body_record/BR)
ASSERT(BR)
body_scans.Remove("[BR.mydna.name]")
log_debug("Removed [BR.mydna.name] from transcore body DB.")
// Moves all mind records from the databaes into the disk and shuts down all backup canary processing.
-/datum/controller/subsystem/transcore/proc/core_dump(var/obj/item/weapon/disk/transcore/disk)
+/datum/transcore_db/proc/core_dump(var/obj/item/weapon/disk/transcore/disk)
ASSERT(disk)
global_announcer.autosay("An emergency core dump has been initiated!", "TransCore Oversight", "Command")
global_announcer.autosay("An emergency core dump has been initiated!", "TransCore Oversight", "Medical")
@@ -203,7 +294,6 @@ SUBSYSTEM_DEF(transcore)
disk.stored += backed_up
backed_up.Cut()
core_dumped = TRUE
- can_fire = FALSE
return disk.stored.len
#undef SSTRANSCORE_BACKUPS
diff --git a/code/datums/chat_message.dm b/code/datums/chat_message.dm
new file mode 100644
index 00000000000..82d4aff1678
--- /dev/null
+++ b/code/datums/chat_message.dm
@@ -0,0 +1,334 @@
+#define CHAT_MESSAGE_SPAWN_TIME 0.2 SECONDS
+#define CHAT_MESSAGE_LIFESPAN 5 SECONDS
+#define CHAT_MESSAGE_EOL_FADE 0.7 SECONDS
+#define CHAT_MESSAGE_EXP_DECAY 0.8 // Messages decay at pow(factor, idx in stack)
+#define CHAT_MESSAGE_HEIGHT_DECAY 0.7 // Increase message decay based on the height of the message
+#define CHAT_MESSAGE_APPROX_LHEIGHT 11 // Approximate height in pixels of an 'average' line, used for height decay
+
+#define CHAT_MESSAGE_WIDTH 96 // pixels
+#define CHAT_MESSAGE_EXT_WIDTH 128
+#define CHAT_MESSAGE_LENGTH 68 // characters
+#define CHAT_MESSAGE_EXT_LENGTH 150
+
+#define CHAT_MESSAGE_MOB 1
+#define CHAT_MESSAGE_OBJ 2
+#define WXH_TO_HEIGHT(x) text2num(copytext((x), findtextEx((x), "x") + 1)) // thanks lummox
+
+#define CHAT_RUNE_EMOTE 0x1
+#define CHAT_RUNE_RADIO 0x2
+
+/**
+ * # Chat Message Overlay
+ *
+ * Datum for generating a message overlay on the map
+ * Ported from TGStation; https://github.com/tgstation/tgstation/pull/50608/, author: bobbahbrown
+ */
+
+// Cached runechat icon
+var/list/runechat_image_cache = list()
+
+
+/hook/startup/proc/runechat_images()
+ var/image/radio_image = image('icons/UI_Icons/chat/chat_icons.dmi', icon_state = "radio")
+ runechat_image_cache["radio"] = radio_image
+
+ var/image/emote_image = image('icons/UI_Icons/chat/chat_icons.dmi', icon_state = "emote")
+ runechat_image_cache["emote"] = emote_image
+
+ return TRUE
+
+/datum/chatmessage
+ /// The visual element of the chat messsage
+ var/image/message
+ /// The location in which the message is appearing
+ var/atom/message_loc
+ /// The client who heard this message
+ var/client/owned_by
+ /// Contains the scheduled destruction time
+ var/scheduled_destruction
+ /// Contains the approximate amount of lines for height decay
+ var/approx_lines
+ /// If we are currently processing animation and cleanup at EOL
+ var/ending_life
+
+/**
+ * Constructs a chat message overlay
+ *
+ * Arguments:
+ * * text - The text content of the overlay
+ * * target - The target atom to display the overlay at
+ * * owner - The mob that owns this overlay, only this mob will be able to view it
+ * * extra_classes - Extra classes to apply to the span that holds the text
+ * * lifespan - The lifespan of the message in deciseconds
+ */
+/datum/chatmessage/New(text, atom/target, mob/owner, list/extra_classes = null, lifespan = CHAT_MESSAGE_LIFESPAN)
+ . = ..()
+ if(!istype(target))
+ CRASH("Invalid target given for chatmessage")
+ if(!istype(owner) || QDELETED(owner) || !owner.client)
+ stack_trace("/datum/chatmessage created with [isnull(owner) ? "null" : "invalid"] mob owner")
+ qdel(src)
+ return
+ generate_image(text, target, owner, extra_classes, lifespan)
+
+/datum/chatmessage/Destroy()
+ if(owned_by)
+ UnregisterSignal(owned_by, COMSIG_PARENT_QDELETING)
+ LAZYREMOVEASSOC(owned_by.seen_messages, message_loc, src)
+ owned_by.images.Remove(message)
+ if(message_loc)
+ UnregisterSignal(message_loc, COMSIG_PARENT_QDELETING)
+ owned_by = null
+ message_loc = null
+ message = null
+ return ..()
+
+/**
+ * Generates a chat message image representation
+ *
+ * Arguments:
+ * * text - The text content of the overlay
+ * * target - The target atom to display the overlay at
+ * * owner - The mob that owns this overlay, only this mob will be able to view it
+ * * extra_classes - Extra classes to apply to the span that holds the text
+ * * lifespan - The lifespan of the message in deciseconds
+ */
+/datum/chatmessage/proc/generate_image(text, atom/target, mob/owner, list/extra_classes, lifespan)
+ set waitfor = FALSE
+
+ if(!target || !owner)
+ qdel(src)
+ return
+
+ // Register client who owns this message
+ owned_by = owner.client
+ RegisterSignal(owned_by, COMSIG_PARENT_QDELETING, .proc/qdel_self)
+
+ var/extra_length = owned_by.is_preference_enabled(/datum/client_preference/runechat_long_messages)
+ var/maxlen = extra_length ? CHAT_MESSAGE_EXT_LENGTH : CHAT_MESSAGE_LENGTH
+ var/msgwidth = extra_length ? CHAT_MESSAGE_EXT_WIDTH : CHAT_MESSAGE_WIDTH
+
+ // Clip message
+ if(length_char(text) > maxlen)
+ text = copytext_char(text, 1, maxlen + 1) + "..." // BYOND index moment
+
+ // Calculate target color if not already present
+ if(!target.chat_color || target.chat_color_name != target.name)
+ target.chat_color = colorize_string(target.name)
+ target.chat_color_darkened = colorize_string(target.name, 0.85, 0.85)
+ target.chat_color_name = target.name
+
+ // Get rid of any URL schemes that might cause BYOND to automatically wrap something in an anchor tag
+ var/static/regex/url_scheme = new(@"[A-Za-z][A-Za-z0-9+-\.]*:\/\/", "g")
+ text = replacetext(text, url_scheme, "")
+
+ // Reject whitespace
+ var/static/regex/whitespace = new(@"^\s*$")
+ if(whitespace.Find(text))
+ qdel(src)
+ return
+
+ // Non mobs speakers can be small
+ if(!ismob(target))
+ extra_classes |= "small"
+
+ // If we heard our name, it's important
+ // Differnt from our own system of name emphasis, maybe unify
+ var/list/names = splittext(owner.name, " ")
+ for (var/word in names)
+ text = replacetext(text, word, "[word]")
+
+ var/list/prefixes
+
+ // Append prefixes
+ if(extra_classes.Find("virtual-speaker"))
+ LAZYADD(prefixes, "\icon[runechat_image_cache["radio"]]")
+ if(extra_classes.Find("emote"))
+ // Icon on both ends?
+ //var/image/I = runechat_image_cache["emote"]
+ //text = "\icon[I][text]\icon[I]"
+
+ // Icon on one end?
+ //LAZYADD(prefixes, "\icon[runechat_image_cache["emote"]]")
+
+ // Asterisks instead?
+ text = "* [text] *"
+
+ text = "[prefixes?.Join(" ")][text]"
+
+ // We dim italicized text to make it more distinguishable from regular text
+ var/tgt_color = extra_classes.Find("italics") ? target.chat_color_darkened : target.chat_color
+
+ // Approximate text height
+ var/complete_text = ""
+ var/mheight = WXH_TO_HEIGHT(owned_by.MeasureText(complete_text, null, msgwidth))
+ approx_lines = max(1, mheight / CHAT_MESSAGE_APPROX_LHEIGHT)
+
+ // Translate any existing messages upwards, apply exponential decay factors to timers
+ message_loc = target
+ RegisterSignal(message_loc, COMSIG_PARENT_QDELETING, .proc/qdel_self)
+ if(owned_by.seen_messages)
+ var/idx = 1
+ var/combined_height = approx_lines
+ for(var/msg in owned_by.seen_messages[message_loc])
+ var/datum/chatmessage/m = msg
+ animate(m.message, pixel_y = m.message.pixel_y + mheight, time = CHAT_MESSAGE_SPAWN_TIME)
+ combined_height += m.approx_lines
+
+ if(!m.ending_life) // Don't bother!
+ var/sched_remaining = m.scheduled_destruction - world.time
+ if(sched_remaining > CHAT_MESSAGE_SPAWN_TIME)
+ var/remaining_time = (sched_remaining) * (CHAT_MESSAGE_EXP_DECAY ** idx++) * (CHAT_MESSAGE_HEIGHT_DECAY ** combined_height)
+ m.scheduled_destruction = world.time + remaining_time
+ spawn(remaining_time)
+ m.end_of_life()
+
+ // Build message image
+ message = image(loc = message_loc, layer = ABOVE_MOB_LAYER)
+ message.plane = PLANE_RUNECHAT
+ message.appearance_flags = APPEARANCE_UI_IGNORE_ALPHA | KEEP_APART
+ message.alpha = 0
+ message.pixel_y = (owner.bound_height * 0.95)*owner.size_multiplier
+ message.maptext_width = msgwidth
+ message.maptext_height = mheight
+ message.maptext_x = (msgwidth - owner.bound_width) * -0.5
+ message.maptext = complete_text
+
+ if(owner.contains(target)) // Special case, holding an atom speaking (pAI, recorder...)
+ message.plane = PLANE_PLAYER_HUD_ABOVE
+
+ // View the message
+ LAZYADDASSOCLIST(owned_by.seen_messages, message_loc, src)
+ owned_by.images += message
+ animate(message, alpha = 255, time = CHAT_MESSAGE_SPAWN_TIME)
+
+ // Prepare for destruction
+ scheduled_destruction = world.time + (lifespan - CHAT_MESSAGE_EOL_FADE)
+ spawn(lifespan - CHAT_MESSAGE_EOL_FADE)
+ end_of_life()
+
+/**
+ * Applies final animations to overlay CHAT_MESSAGE_EOL_FADE deciseconds prior to message deletion
+ */
+/datum/chatmessage/proc/end_of_life(fadetime = CHAT_MESSAGE_EOL_FADE)
+ if(gc_destroyed || ending_life)
+ return
+ ending_life = TRUE
+ animate(message, alpha = 0, time = fadetime, flags = ANIMATION_PARALLEL)
+ spawn(fadetime)
+ qdel(src)
+
+/**
+ * Creates a message overlay at a defined location for a given speaker
+ *
+ * Arguments:
+ * * speaker - The atom who is saying this message
+ * * message - The text content of the message
+ * * italics - Decides if this should be small or not, as generally italics text are for whisper/radio overhear
+ * * existing_extra_classes - Additional classes to add to the message
+ */
+/mob/proc/create_chat_message(atom/movable/speaker, message, italics, list/existing_extra_classes, audible = TRUE)
+ if(!client)
+ return
+
+ // Doesn't want to hear
+ if(ismob(speaker) && !client.is_preference_enabled(/datum/client_preference/runechat_mob))
+ return
+ else if(isobj(speaker) && !client.is_preference_enabled(/datum/client_preference/runechat_obj))
+ return
+
+ // Incapable of receiving
+ if((audible && is_deaf()) || (!audible && is_blind()))
+ return
+
+ // Check for virtual speakers (aka hearing a message through a radio)
+ if(existing_extra_classes.Find("radio"))
+ return
+
+ /* Not currently necessary
+ message = strip_html_properly(message)
+ if(!message)
+ return
+ */
+
+ var/list/extra_classes = list()
+ extra_classes += existing_extra_classes
+
+ if(italics)
+ extra_classes |= "italics"
+
+ if(client.is_preference_enabled(/datum/client_preference/runechat_border))
+ extra_classes |= "black_outline"
+
+ var/dist = get_dist(src, speaker)
+ switch (dist)
+ if(4 to 5)
+ extra_classes |= "small"
+ if(5 to 16)
+ extra_classes |= "very_small"
+
+ // Display visual above source
+ new /datum/chatmessage(message, speaker, src, extra_classes)
+
+// Tweak these defines to change the available color ranges
+#define CM_COLOR_SAT_MIN 0.6
+#define CM_COLOR_SAT_MAX 0.95
+#define CM_COLOR_LUM_MIN 0.70
+#define CM_COLOR_LUM_MAX 0.90
+
+/**
+ * Gets a color for a name, will return the same color for a given string consistently within a round.atom
+ *
+ * Note that this proc aims to produce pastel-ish colors using the HSL colorspace. These seem to be favorable for displaying on the map.
+ *
+ * Arguments:
+ * * name - The name to generate a color for
+ * * sat_shift - A value between 0 and 1 that will be multiplied against the saturation
+ * * lum_shift - A value between 0 and 1 that will be multiplied against the luminescence
+ */
+/datum/chatmessage/proc/colorize_string(name, sat_shift = 1, lum_shift = 1)
+ // seed to help randomness
+ var/static/rseed = rand(1,26)
+
+ // get hsl using the selected 6 characters of the md5 hash
+ var/hash = copytext(md5(name + "[world_startup_time]"), rseed, rseed + 6)
+ var/h = hex2num(copytext(hash, 1, 3)) * (360 / 255)
+ var/s = (hex2num(copytext(hash, 3, 5)) >> 2) * ((CM_COLOR_SAT_MAX - CM_COLOR_SAT_MIN) / 63) + CM_COLOR_SAT_MIN
+ var/l = (hex2num(copytext(hash, 5, 7)) >> 2) * ((CM_COLOR_LUM_MAX - CM_COLOR_LUM_MIN) / 63) + CM_COLOR_LUM_MIN
+
+ // adjust for shifts
+ s *= clamp(sat_shift, 0, 1)
+ l *= clamp(lum_shift, 0, 1)
+
+ // convert to rgba
+ var/h_int = round(h/60) // mapping each section of H to 60 degree sections
+ var/c = (1 - abs(2 * l - 1)) * s
+ var/x = c * (1 - abs((h / 60) % 2 - 1))
+ var/m = l - c * 0.5
+ x = (x + m) * 255
+ c = (c + m) * 255
+ m *= 255
+ switch(h_int)
+ if(0)
+ return rgb(c,x,m)
+ if(1)
+ return rgb(x,c,m)
+ if(2)
+ return rgb(m,c,x)
+ if(3)
+ return rgb(m,x,c)
+ if(4)
+ return rgb(x,m,c)
+ if(5)
+ return rgb(c,m,x)
+
+/atom/proc/runechat_message(message, range = world.view, italics, list/classes = list(), audible = TRUE)
+ var/list/hear = get_mobs_and_objs_in_view_fast(get_turf(src), range, remote_ghosts = FALSE)
+
+ var/list/hearing_mobs = hear["mobs"]
+
+ for(var/mob in hearing_mobs)
+ var/mob/M = mob
+ if(!M.client)
+ continue
+ M.create_chat_message(src, message, italics, classes, audible)
diff --git a/code/datums/components/_component.dm b/code/datums/components/_component.dm
index 1cb9e0b1c40..c4ee090ce85 100644
--- a/code/datums/components/_component.dm
+++ b/code/datums/components/_component.dm
@@ -213,10 +213,10 @@
*
* Arguments:
* * datum/target Datum to stop listening to signals from
- * * sig_typeor_types Signal string key or list of signal keys to stop listening to specifically
+ * * sig_type_or_types Signal string key or list of signal keys to stop listening to specifically
*/
/datum/proc/UnregisterSignal(datum/target, sig_type_or_types)
- var/list/lookup = target.comp_lookup
+ var/list/lookup = target?.comp_lookup
if(!signal_procs || !signal_procs[target] || !lookup)
return
if(!islist(sig_type_or_types))
diff --git a/code/datums/outfits/jobs/security.dm b/code/datums/outfits/jobs/security.dm
index 9441a06da02..ca4a0a4b853 100644
--- a/code/datums/outfits/jobs/security.dm
+++ b/code/datums/outfits/jobs/security.dm
@@ -41,7 +41,7 @@
/decl/hierarchy/outfit/job/security/detective/forensic
name = OUTFIT_JOB_NAME("Forensic technician")
head = null
- suit = /datum/gear/uniform/detective_alt2
+ suit = /obj/item/clothing/suit/storage/det_trench/alt2
uniform = /obj/item/clothing/under/det
//VOREStation Edit End
diff --git a/code/datums/supplypacks/voidsuits.dm b/code/datums/supplypacks/voidsuits.dm
index 2066c216d30..667127e79b8 100644
--- a/code/datums/supplypacks/voidsuits.dm
+++ b/code/datums/supplypacks/voidsuits.dm
@@ -16,7 +16,7 @@
/obj/item/clothing/shoes/magboots = 2,
/obj/item/weapon/tank/oxygen = 2,
)
- cost = 40
+ cost = 35
containertype = /obj/structure/closet/crate/secure/aether
containername = "Atmospheric voidsuit crate"
access = access_atmospherics
@@ -30,7 +30,7 @@
/obj/item/clothing/shoes/magboots = 2,
/obj/item/weapon/tank/oxygen = 2,
)
- cost = 50
+ cost = 60
containertype = /obj/structure/closet/crate/secure/aether
containername = "Heavy Duty Atmospheric voidsuit crate"
access = access_atmospherics
@@ -44,7 +44,7 @@
/obj/item/clothing/shoes/magboots = 2,
/obj/item/weapon/tank/oxygen = 2
)
- cost = 40
+ cost = 35
containertype = /obj/structure/closet/crate/secure/xion
containername = "Engineering voidsuit crate"
access = access_engine_equip
@@ -58,7 +58,7 @@
/obj/item/clothing/shoes/magboots = 2,
/obj/item/weapon/tank/oxygen = 2
)
- cost = 40
+ cost = 35
containertype = /obj/structure/closet/crate/secure/xion
containername = "Engineering Construction voidsuit crate"
access = access_engine_equip
@@ -72,7 +72,7 @@
/obj/item/clothing/shoes/magboots = 2,
/obj/item/weapon/tank/oxygen = 2
)
- cost = 45
+ cost = 35
containertype = /obj/structure/closet/crate/secure/xion
containername = "Engineering Hazmat voidsuit crate"
access = access_engine_equip
@@ -86,7 +86,7 @@
/obj/item/clothing/shoes/magboots = 2,
/obj/item/weapon/tank/oxygen = 2
)
- cost = 50
+ cost = 60
containertype = /obj/structure/closet/crate/secure/xion
containername = "Reinforced Engineering voidsuit crate"
access = access_engine_equip
@@ -100,7 +100,7 @@
/obj/item/clothing/shoes/magboots = 2,
/obj/item/weapon/tank/oxygen = 2
)
- cost = 40
+ cost = 35
containertype = /obj/structure/closet/crate/secure/veymed
containername = "Medical voidsuit crate"
access = access_medical_equip
@@ -114,7 +114,7 @@
/obj/item/clothing/shoes/magboots = 2,
/obj/item/weapon/tank/oxygen = 2
)
- cost = 40
+ cost = 35
containertype = /obj/structure/closet/crate/secure/veymed
containername = "Medical EMT voidsuit crate"
access = access_medical_equip
@@ -128,7 +128,7 @@
/obj/item/clothing/shoes/magboots = 2,
/obj/item/weapon/tank/oxygen = 2
)
- cost = 45
+ cost = 35
containertype = /obj/structure/closet/crate/secure/nanomed
containername = "Medical Biohazard voidsuit crate"
access = access_medical_equip
@@ -167,7 +167,7 @@
/obj/item/clothing/shoes/magboots = 2,
/obj/item/weapon/tank/oxygen = 2
)
- cost = 40
+ cost = 35
containertype = /obj/structure/closet/crate/secure/heph
containername = "Security voidsuit crate"
@@ -207,7 +207,7 @@
/obj/item/clothing/mask/breath = 2,
/obj/item/weapon/tank/oxygen = 2
)
- cost = 40
+ cost = 35
containertype = /obj/structure/closet/crate/secure/xion
containername = "Mining voidsuit crate"
access = access_mining
@@ -220,7 +220,7 @@
/obj/item/clothing/mask/breath = 2,
/obj/item/weapon/tank/oxygen = 2
)
- cost = 50
+ cost = 60
containertype = /obj/structure/closet/crate/secure/grayson
containername = "Frontier Mining voidsuit crate"
access = access_mining
diff --git a/code/datums/supplypacks/voidsuits_vr.dm b/code/datums/supplypacks/voidsuits_vr.dm
index 8f085179c2a..b193881004d 100644
--- a/code/datums/supplypacks/voidsuits_vr.dm
+++ b/code/datums/supplypacks/voidsuits_vr.dm
@@ -1,84 +1,13 @@
-/datum/supply_pack/voidsuits/atmos
- contains = list(
- /obj/item/clothing/suit/space/void/atmos = 3,
- /obj/item/clothing/head/helmet/space/void/atmos = 3,
- /obj/item/clothing/mask/breath = 3,
- /obj/item/clothing/shoes/magboots = 3,
- /obj/item/weapon/tank/oxygen = 3,
- )
-
-/datum/supply_pack/voidsuits/engineering
- contains = list(
- /obj/item/clothing/suit/space/void/engineering = 3,
- /obj/item/clothing/head/helmet/space/void/engineering = 3,
- /obj/item/clothing/mask/breath = 3,
- /obj/item/clothing/shoes/magboots = 3,
- /obj/item/weapon/tank/oxygen = 3
- )
-
-/datum/supply_pack/voidsuits/medical
- contains = list(
- /obj/item/clothing/suit/space/void/medical = 3,
- /obj/item/clothing/head/helmet/space/void/medical = 3,
- /obj/item/clothing/mask/breath = 3,
- /obj/item/clothing/shoes/magboots = 3,
- /obj/item/weapon/tank/oxygen = 3
- )
-
-/datum/supply_pack/voidsuits/medical/alt
- contains = list(
- /obj/item/clothing/suit/space/void/medical/alt = 3,
- /obj/item/clothing/head/helmet/space/void/medical/alt = 3,
- /obj/item/clothing/mask/breath = 3,
- /obj/item/clothing/shoes/magboots = 3,
- /obj/item/weapon/tank/oxygen = 3
- )
-
-/datum/supply_pack/voidsuits/security
- contains = list(
- /obj/item/clothing/suit/space/void/security = 3,
- /obj/item/clothing/head/helmet/space/void/security = 3,
- /obj/item/clothing/mask/breath = 3,
- /obj/item/clothing/shoes/magboots = 3,
- /obj/item/weapon/tank/oxygen = 3
- )
-
-/datum/supply_pack/voidsuits/security/crowd
- contains = list(
- /obj/item/clothing/suit/space/void/security/riot = 3,
- /obj/item/clothing/head/helmet/space/void/security/riot = 3,
- /obj/item/clothing/mask/breath = 3,
- /obj/item/clothing/shoes/magboots = 3,
- /obj/item/weapon/tank/oxygen = 3
- )
-
-/datum/supply_pack/voidsuits/security/alt
- contains = list(
- /obj/item/clothing/suit/space/void/security/alt = 3,
- /obj/item/clothing/head/helmet/space/void/security/alt = 3,
- /obj/item/clothing/mask/breath = 3,
- /obj/item/clothing/shoes/magboots = 3,
- /obj/item/weapon/tank/oxygen = 3
- )
-
-/datum/supply_pack/voidsuits/supply
- contains = list(
- /obj/item/clothing/suit/space/void/mining = 3,
- /obj/item/clothing/head/helmet/space/void/mining = 3,
- /obj/item/clothing/mask/breath = 3,
- /obj/item/weapon/tank/oxygen = 3
- )
-
/datum/supply_pack/voidsuits/explorer
name = "Exploration voidsuits"
contains = list(
- /obj/item/clothing/suit/space/void/exploration = 3,
- /obj/item/clothing/head/helmet/space/void/exploration = 3,
- /obj/item/clothing/mask/breath = 3,
- /obj/item/clothing/shoes/magboots = 3,
- /obj/item/weapon/tank/oxygen = 3
+ /obj/item/clothing/suit/space/void/exploration = 2,
+ /obj/item/clothing/head/helmet/space/void/exploration = 2,
+ /obj/item/clothing/mask/breath = 2,
+ /obj/item/clothing/shoes/magboots = 2,
+ /obj/item/weapon/tank/oxygen = 2
)
- cost = 50
+ cost = 45
containertype = /obj/structure/closet/crate/secure
containername = "Exploration voidsuit crate"
access = access_explorer
@@ -92,7 +21,7 @@
/obj/item/clothing/shoes/magboots = 2,
/obj/item/weapon/tank/oxygen = 2
)
- cost = 35
+ cost = 45
containertype = /obj/structure/closet/crate/secure
containername = "Expedition Medic voidsuit crate"
access = access_explorer
diff --git a/code/game/atoms.dm b/code/game/atoms.dm
index 46f001c8e1b..fc27bb9f4e0 100644
--- a/code/game/atoms.dm
+++ b/code/game/atoms.dm
@@ -34,6 +34,15 @@
// Track if we are already had initialize() called to prevent double-initialization.
var/initialized = FALSE
+ /// Last name used to calculate a color for the chatmessage overlays
+ var/chat_color_name
+ /// Last color calculated for the the chatmessage overlays
+ var/chat_color
+ /// A luminescence-shifted value of the last color calculated for chatmessage overlays
+ var/chat_color_darkened
+ /// The chat color var, without alpha.
+ var/chat_color_hover
+
/atom/New(loc, ...)
// Don't call ..() unless /datum/New() ever exists
@@ -490,7 +499,7 @@
// Use for objects performing visible actions
// message is output to anyone who can see, e.g. "The [src] does something!"
// blind_message (optional) is what blind people will hear e.g. "You hear something!"
-/atom/proc/visible_message(var/message, var/blind_message, var/list/exclude_mobs, var/range = world.view)
+/atom/proc/visible_message(var/message, var/blind_message, var/list/exclude_mobs, var/range = world.view, var/runemessage = "ðŸ‘")
//VOREStation Edit
var/list/see
@@ -513,6 +522,8 @@
var/mob/M = mob
if(M.see_invisible >= invisibility && MOB_CAN_SEE_PLANE(M, plane))
M.show_message(message, VISIBLE_MESSAGE, blind_message, AUDIBLE_MESSAGE)
+ if(runemessage != -1)
+ M.create_chat_message(src, "[runemessage]", FALSE, list("emote"), audible = FALSE)
else if(blind_message)
M.show_message(blind_message, AUDIBLE_MESSAGE)
@@ -521,7 +532,7 @@
// message is the message output to anyone who can hear.
// deaf_message (optional) is what deaf people will see.
// hearing_distance (optional) is the range, how many tiles away the message can be heard.
-/atom/proc/audible_message(var/message, var/deaf_message, var/hearing_distance, var/radio_message)
+/atom/proc/audible_message(var/message, var/deaf_message, var/hearing_distance, var/radio_message, var/runemessage)
var/range = hearing_distance || world.view
var/list/hear = get_mobs_and_objs_in_view_fast(get_turf(src),range,remote_ghosts = FALSE)
@@ -542,6 +553,8 @@
var/mob/M = mob
var/msg = message
M.show_message(msg, AUDIBLE_MESSAGE, deaf_message, VISIBLE_MESSAGE)
+ if(runemessage != -1)
+ M.create_chat_message(src, "[runemessage || message]", FALSE, list("emote"))
/atom/movable/proc/dropInto(var/atom/destination)
while(istype(destination))
diff --git a/code/game/machinery/air_alarm.dm b/code/game/machinery/air_alarm.dm
index c8222d00b4b..f330d839f6a 100644
--- a/code/game/machinery/air_alarm.dm
+++ b/code/game/machinery/air_alarm.dm
@@ -191,7 +191,7 @@
update_use_power(USE_POWER_ACTIVE)
regulating_temperature = 1
audible_message("\The [src] clicks as it starts [environment.temperature > target_temperature ? "cooling" : "heating"] the room.",\
- "You hear a click and a faint electronic hum.")
+ "You hear a click and a faint electronic hum.", runemessage = "* click *")
playsound(src, 'sound/machines/click.ogg', 50, 1)
else
//check for when we should stop adjusting temperature
@@ -199,7 +199,7 @@
update_use_power(USE_POWER_IDLE)
regulating_temperature = 0
audible_message("\The [src] clicks quietly as it stops [environment.temperature > target_temperature ? "cooling" : "heating"] the room.",\
- "You hear a click as a faint electronic humming stops.")
+ "You hear a click as a faint electronic humming stops.", runemessage = "* click *")
playsound(src, 'sound/machines/click.ogg', 50, 1)
if(regulating_temperature)
diff --git a/code/game/machinery/cloning.dm b/code/game/machinery/cloning.dm
index 2c34326fa7d..197848d9adb 100644
--- a/code/game/machinery/cloning.dm
+++ b/code/game/machinery/cloning.dm
@@ -206,7 +206,7 @@
else if((occupant.health >= heal_level || occupant.health == occupant.getMaxHealth()) && (!eject_wait))
playsound(src, 'sound/machines/medbayscanner1.ogg', 50, 1)
- audible_message("\The [src] signals that the cloning process is complete.")
+ audible_message("\The [src] signals that the cloning process is complete.", runemessage = "* ding *")
connected_message("Cloning Process Complete.")
locked = 0
go_out()
diff --git a/code/game/machinery/computer/arcade.dm b/code/game/machinery/computer/arcade.dm
index 77e7e037a9d..df72c0d7678 100644
--- a/code/game/machinery/computer/arcade.dm
+++ b/code/game/machinery/computer/arcade.dm
@@ -519,9 +519,9 @@
if(electronics)
sleep(10)
if(oldfuel > fuel && oldfood > food)
- src.audible_message("\The [src] lets out a somehow reassuring chime.")
+ src.audible_message("\The [src] lets out a somehow reassuring chime.", runemessage = "* reassuring chime *")
else if(oldfuel < fuel || oldfood < food)
- src.audible_message("\The [src] lets out a somehow ominous chime.")
+ src.audible_message("\The [src] lets out a somehow ominous chime.", runemessage = "* ominous chime *")
food = oldfood
fuel = oldfuel
diff --git a/code/game/machinery/cryopod.dm b/code/game/machinery/cryopod.dm
index 7ed6abed400..3cd05e69b57 100644
--- a/code/game/machinery/cryopod.dm
+++ b/code/game/machinery/cryopod.dm
@@ -461,12 +461,7 @@
//VOREStation Edit - Resleeving.
if(to_despawn.mind)
- if(to_despawn.mind.name in SStranscore.backed_up)
- var/datum/transhuman/mind_record/MR = SStranscore.backed_up[to_despawn.mind.name]
- SStranscore.stop_backup(MR)
- if(to_despawn.mind.name in SStranscore.body_scans) //This uses mind names to avoid people cryo'ing a printed body to delete body scans.
- var/datum/transhuman/body_record/BR = SStranscore.body_scans[to_despawn.mind.name]
- SStranscore.remove_body(BR)
+ SStranscore.leave_round(to_despawn)
//VOREStation Edit End - Resleeving.
//Handle job slot/tater cleanup.
diff --git a/code/game/machinery/suit_storage_unit.dm b/code/game/machinery/suit_storage_unit.dm
index 416b8544be2..73741f9e47e 100644
--- a/code/game/machinery/suit_storage_unit.dm
+++ b/code/game/machinery/suit_storage_unit.dm
@@ -1104,7 +1104,7 @@
if("Exploration")
parent_helmet = /obj/item/clothing/head/helmet/space/void/exploration
parent_suit = /obj/item/clothing/suit/space/void/exploration
- if("Expedition Medic")
+ if("Field Medic")
parent_helmet = /obj/item/clothing/head/helmet/space/void/expedition_medical
parent_suit = /obj/item/clothing/suit/space/void/expedition_medical
if("Old Exploration")
diff --git a/code/game/objects/effects/map_effects/portal.dm b/code/game/objects/effects/map_effects/portal.dm
index 6eda8b6c726..094de59e2af 100644
--- a/code/game/objects/effects/map_effects/portal.dm
+++ b/code/game/objects/effects/map_effects/portal.dm
@@ -277,7 +277,8 @@ when portals are shortly lived, or when portals are made to be obvious with spec
for(var/thing in mobs_to_relay)
var/mob/mob = thing
- var/message = mob.combine_message(message_pieces, verb, M)
+ var/list/combined = mob.combine_message(message_pieces, verb, M)
+ var/message = combined["formatted"]
var/name_used = M.GetVoice()
var/rendered = null
rendered = "[name_used] [message]"
diff --git a/code/game/objects/items/devices/communicator/phone.dm b/code/game/objects/items/devices/communicator/phone.dm
index bb25dce953f..16543ee2c05 100644
--- a/code/game/objects/items/devices/communicator/phone.dm
+++ b/code/game/objects/items/devices/communicator/phone.dm
@@ -246,7 +246,8 @@
//VOREStation Edit End
for(var/mob/mob in mobs_to_relay)
- var/message = mob.combine_message(message_pieces, verb, M)
+ var/list/combined = mob.combine_message(message_pieces, verb, M)
+ var/message = combined["formatted"]
var/name_used = M.GetVoice()
var/rendered = null
rendered = "[bicon(src)] [name_used] [message]"
diff --git a/code/game/objects/items/devices/defib.dm b/code/game/objects/items/devices/defib.dm
index f938f0eab18..4bd2ac9a307 100644
--- a/code/game/objects/items/devices/defib.dm
+++ b/code/game/objects/items/devices/defib.dm
@@ -460,7 +460,7 @@
return
playsound(src, 'sound/machines/defib_charge.ogg', 50, 0)
- audible_message("\The [src] lets out a steadily rising hum...")
+ audible_message("\The [src] lets out a steadily rising hum...", runemessage = "* whines *")
if(!do_after(user, chargetime, H))
return
@@ -527,7 +527,7 @@
H.setBrainLoss(brain_damage)
/obj/item/weapon/shockpaddles/proc/make_announcement(var/message, var/msg_class)
- audible_message("\The [src] [message]", "\The [src] vibrates slightly.")
+ audible_message("\The [src] [message]", "\The [src] vibrates slightly.", runemessage = "* buzz *")
/obj/item/weapon/shockpaddles/emag_act(mob/user)
if(safety)
diff --git a/code/game/objects/items/devices/megaphone.dm b/code/game/objects/items/devices/megaphone.dm
index 32c9d75959a..86602094c1c 100644
--- a/code/game/objects/items/devices/megaphone.dm
+++ b/code/game/objects/items/devices/megaphone.dm
@@ -31,12 +31,13 @@
/obj/item/device/megaphone/proc/do_broadcast(var/mob/living/user, var/message)
if(emagged)
if(insults)
- user.audible_message("[user.GetVoice()][user.GetAltName()] broadcasts, \"[pick(insultmsg)]\"")
+ var/insult = pick(insultmsg)
+ user.audible_message("[user.GetVoice()][user.GetAltName()] broadcasts, \"[insult]\"", runemessage = insult)
insults--
else
to_chat(user, "*BZZZZzzzzzt*")
else
- user.audible_message("[user.GetVoice()][user.GetAltName()] broadcasts, \"[message]\"")
+ user.audible_message("[user.GetVoice()][user.GetAltName()] broadcasts, \"[message]\"", runemessage = message)
/obj/item/device/megaphone/attack_self(var/mob/living/user)
var/message = sanitize(input(user, "Shout a message?", "Megaphone", null) as text)
@@ -131,7 +132,8 @@
/obj/item/device/megaphone/super/do_broadcast(var/mob/living/user, var/message)
if(emagged)
if(insults)
- user.audible_message("[user.GetVoice()][user.GetAltName()] broadcasts, \"[pick(insultmsg)]\"")
+ var/insult = pick(insultmsg)
+ user.audible_message("[user.GetVoice()][user.GetAltName()] broadcasts, \"[insult]\"", runemessage = insult)
if(broadcast_size >= 11)
var/turf/T = get_turf(user)
playsound(src, 'sound/items/AirHorn.ogg', 100, 1)
@@ -160,4 +162,4 @@
qdel(src)
return
else
- user.audible_message("[user.GetVoice()][user.GetAltName()] broadcasts, \"[message]\"")
+ user.audible_message("[user.GetVoice()][user.GetAltName()] broadcasts, \"[message]\"", runemessage = message)
diff --git a/code/game/objects/items/devices/scanners_vr.dm b/code/game/objects/items/devices/scanners_vr.dm
index 5740a7df862..b5adabdb6e1 100644
--- a/code/game/objects/items/devices/scanners_vr.dm
+++ b/code/game/objects/items/devices/scanners_vr.dm
@@ -19,7 +19,14 @@ var/global/mob/living/carbon/human/dummy/mannequin/sleevemate_mob
var/ooc_notes = null //For holding prefs
+ // Resleeving database this machine interacts with. Blank for default database
+ // Needs a matching /datum/transcore_db with key defined in code
+ var/db_key
+ var/datum/transcore_db/our_db // These persist all round and are never destroyed, just keep a hard ref
+/obj/item/device/sleevemate/Initialize()
+ . = ..()
+ our_db = SStranscore.db_by_key(db_key)
//These don't perform any checks and need to be wrapped by checks
/obj/item/device/sleevemate/proc/clear_mind()
@@ -77,7 +84,7 @@ var/global/mob/living/carbon/human/dummy/mannequin/sleevemate_mob
clear_mind()
if("Backup")
to_chat(user,"Internal copy of [stored_mind.name] backed up to database.")
- SStranscore.m_backup(stored_mind,null,one_time = TRUE)
+ our_db.m_backup(stored_mind,null,one_time = TRUE)
if("Cancel")
return
@@ -183,7 +190,7 @@ var/global/mob/living/carbon/human/dummy/mannequin/sleevemate_mob
usr.visible_message("[usr] begins scanning [target]'s mind.","You begin scanning [target]'s mind.")
if(do_after(usr,8 SECONDS,target))
- SStranscore.m_backup(target.mind,nif,one_time = TRUE)
+ our_db.m_backup(target.mind,nif,one_time = TRUE)
to_chat(usr,"Mind backed up!")
else
to_chat(usr,"You must remain close to your target!")
@@ -200,7 +207,7 @@ var/global/mob/living/carbon/human/dummy/mannequin/sleevemate_mob
usr.visible_message("[usr] begins scanning [target]'s body.","You begin scanning [target]'s body.")
if(do_after(usr,8 SECONDS,target))
var/datum/transhuman/body_record/BR = new()
- BR.init_from_mob(H, TRUE, TRUE)
+ BR.init_from_mob(H, TRUE, TRUE, database_key = db_key)
to_chat(usr,"Body scanned!")
else
to_chat(usr,"You must remain close to your target!")
diff --git a/code/game/objects/items/devices/taperecorder.dm b/code/game/objects/items/devices/taperecorder.dm
index bdb35fb6431..3e16975fbe3 100644
--- a/code/game/objects/items/devices/taperecorder.dm
+++ b/code/game/objects/items/devices/taperecorder.dm
@@ -258,13 +258,13 @@
var/playedmessage = mytape.storedinfo[i]
if (findtextEx(playedmessage,"*",1,2)) //remove marker for action sounds
playedmessage = copytext(playedmessage,2)
- T.audible_message("Tape Recorder: [playedmessage]")
+ T.audible_message("Tape Recorder: [playedmessage]", runemessage = playedmessage)
if(mytape.storedinfo.len < i+1)
playsleepseconds = 1
sleep(10)
T = get_turf(src)
- T.audible_message("Tape Recorder: End of recording.")
+ T.audible_message("Tape Recorder: End of recording.", runemessage = "* click *")
break
else
playsleepseconds = mytape.timestamp[i+1] - mytape.timestamp[i]
@@ -272,7 +272,7 @@
if(playsleepseconds > 14)
sleep(10)
T = get_turf(src)
- T.audible_message("Tape Recorder: Skipping [playsleepseconds] seconds of silence")
+ T.audible_message("Tape Recorder: Skipping [playsleepseconds] seconds of silence", runemessage = "* tape winding *")
playsleepseconds = 1
sleep(10 * playsleepseconds)
@@ -282,7 +282,7 @@
if(emagged)
var/turf/T = get_turf(src)
- T.audible_message("Tape Recorder: This tape recorder will self-destruct in... Five.")
+ T.audible_message("Tape Recorder: This tape recorder will self-destruct in... Five.", runemessage = "* beep beep *")
sleep(10)
T = get_turf(src)
T.audible_message("Tape Recorder: Four.")
diff --git a/code/game/objects/items/devices/text_to_speech.dm b/code/game/objects/items/devices/text_to_speech.dm
index f3feacd86ea..edbb49b6a8a 100644
--- a/code/game/objects/items/devices/text_to_speech.dm
+++ b/code/game/objects/items/devices/text_to_speech.dm
@@ -24,5 +24,6 @@
var/message = sanitize(input(user,"Choose a message to relay to those around you.") as text|null)
if(message)
- var/obj/item/device/text_to_speech/O = src
- audible_message("[bicon(O)] \The [O.name] states, \"[message]\"")
+ audible_message("[bicon(src)] \The [src.name] states, \"[message]\"", runemessage = "* synthesized speech *")
+ if(ismob(loc))
+ loc.audible_message("", runemessage = "\[TTS Voice\] [message]")
diff --git a/code/game/objects/items/devices/whistle.dm b/code/game/objects/items/devices/whistle.dm
index cf8cebeec15..54ed5404757 100644
--- a/code/game/objects/items/devices/whistle.dm
+++ b/code/game/objects/items/devices/whistle.dm
@@ -33,12 +33,12 @@
if(isnull(insults))
playsound(src, 'sound/voice/halt.ogg', 100, 1, vary = 0)
- user.audible_message("[user]'s [name] rasps, \"[use_message]\"", "\The [user] holds up \the [name].")
+ user.audible_message("[user]'s [name] rasps, \"[use_message]\"", "\The [user] holds up \the [name].", runemessage = "\[TTS Voice\] [use_message]")
else
if(insults > 0)
playsound(src, 'sound/voice/binsult.ogg', 100, 1, vary = 0)
// Yes, it used to show the transcription of the sound clip. That was a) inaccurate b) immature as shit.
- user.audible_message("[user]'s [name] gurgles something indecipherable and deeply offensive.", "\The [user] holds up \the [name].")
+ user.audible_message("[user]'s [name] gurgles something indecipherable and deeply offensive.", "\The [user] holds up \the [name].", runemessage = "\[TTS Voice\] #&@&^%(*")
insults--
else
to_chat(user, "*BZZZZZZZZT*")
diff --git a/code/game/objects/items/uav.dm b/code/game/objects/items/uav.dm
index 37dd6ec1469..168eec0a3ff 100644
--- a/code/game/objects/items/uav.dm
+++ b/code/game/objects/items/uav.dm
@@ -305,7 +305,8 @@
for(var/wr_master in masters)
var/weakref/wr = wr_master
var/mob/master = wr.resolve()
- var/message = master.combine_message(message_pieces, verb, M)
+ var/list/combined = master.combine_message(message_pieces, verb, M)
+ var/message = combined["formatted"]
var/rendered = "UAV received: [name_used] [message]"
master.show_message(rendered, 2)
diff --git a/code/game/objects/structures/handrail_vr.dm b/code/game/objects/structures/handrail.dm
similarity index 85%
rename from code/game/objects/structures/handrail_vr.dm
rename to code/game/objects/structures/handrail.dm
index 340d8dedd3d..8519cb4e305 100644
--- a/code/game/objects/structures/handrail_vr.dm
+++ b/code/game/objects/structures/handrail.dm
@@ -1,6 +1,6 @@
/obj/structure/handrail
name = "handrail"
- icon = 'icons/obj/handrail_vr.dmi'
+ icon = 'icons/obj/handrail.dmi'
icon_state = "handrail"
desc = "A safety railing with buckles to secure yourself to when floor isn't stable enough."
density = 0
diff --git a/code/game/objects/structures/trash_pile_vr.dm b/code/game/objects/structures/trash_pile_vr.dm
index 297e514e0d4..97883c6cb92 100644
--- a/code/game/objects/structures/trash_pile_vr.dm
+++ b/code/game/objects/structures/trash_pile_vr.dm
@@ -242,6 +242,7 @@
prob(2);/obj/item/weapon/storage/box/syndie_kit/spy,
prob(2);/obj/item/weapon/grenade/anti_photon,
prob(2);/obj/item/clothing/under/hyperfiber/bluespace,
+ prob(2);/obj/item/weapon/reagent_containers/glass/beaker/vial/amorphorovir,
prob(1);/obj/item/clothing/suit/storage/vest/heavy/merc,
prob(1);/obj/item/device/nif/bad,
prob(1);/obj/item/device/radio_jammer,
diff --git a/code/game/world.dm b/code/game/world.dm
index 9f84772b9fc..2895a191dba 100644
--- a/code/game/world.dm
+++ b/code/game/world.dm
@@ -1,5 +1,6 @@
#define RECOMMENDED_VERSION 501
/world/New()
+ world_startup_time = world.timeofday
to_world_log("Map Loading Complete")
//logs
//VOREStation Edit Start
diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm
index d8deb0dce60..b0f553a4871 100644
--- a/code/modules/admin/admin_verbs.dm
+++ b/code/modules/admin/admin_verbs.dm
@@ -324,8 +324,7 @@
var/message = sanitize(input("What do you want the message to be?", "Make Sound") as text|null)
if(!message)
return
- for (var/mob/V in hearers(O))
- V.show_message(message, 2)
+ O.audible_message(message)
log_admin("[key_name(usr)] made [O] at [O.x], [O.y], [O.z]. make a sound")
message_admins("[key_name_admin(usr)] made [O] at [O.x], [O.y], [O.z]. make a sound.", 1)
feedback_add_details("admin_verb","MS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
diff --git a/code/modules/admin/verbs/smite_vr.dm b/code/modules/admin/verbs/smite_vr.dm
index cbf78c6f5ae..f065293a26b 100644
--- a/code/modules/admin/verbs/smite_vr.dm
+++ b/code/modules/admin/verbs/smite_vr.dm
@@ -104,7 +104,7 @@
sleep(1 SECOND)
shadekin.dir = SOUTH
sleep(1 SECOND)
- shadekin.audible_message("[shadekin] belches loudly!")
+ shadekin.audible_message("[shadekin] belches loudly!", runemessage = "* URRRRRP *")
sleep(2 SECONDS)
shadekin.phase_shift()
target.transforming = FALSE //Undo cheap hack
diff --git a/code/modules/ai/ai_holder_combat_unseen.dm b/code/modules/ai/ai_holder_combat_unseen.dm
index cb45860bf06..0b30bebc9df 100644
--- a/code/modules/ai/ai_holder_combat_unseen.dm
+++ b/code/modules/ai/ai_holder_combat_unseen.dm
@@ -16,6 +16,8 @@
var/obj/O = find_escape_route()
if(istype(O))
return give_destination(get_turf(O), 0, TRUE)
+ else
+ return find_target()
else
return find_target()
else
@@ -91,4 +93,4 @@
return pick(closest_escape)
return null
-
\ No newline at end of file
+
diff --git a/code/modules/client/client defines.dm b/code/modules/client/client defines.dm
index 9fb7295266c..81ec230f9b6 100644
--- a/code/modules/client/client defines.dm
+++ b/code/modules/client/client defines.dm
@@ -76,3 +76,6 @@
var/connection_realtime
///world.timeofday they connected
var/connection_timeofday
+
+ // Runechat messages
+ var/list/seen_messages
diff --git a/code/modules/client/preference_setup/global/setting_datums.dm b/code/modules/client/preference_setup/global/setting_datums.dm
index 24cca9ca18b..25eae07ff1b 100644
--- a/code/modules/client/preference_setup/global/setting_datums.dm
+++ b/code/modules/client/preference_setup/global/setting_datums.dm
@@ -290,6 +290,32 @@ var/list/_client_preferences_by_type
enabled_description = "Show"
disabled_description = "Hide"
+/datum/client_preference/runechat_mob
+ description = "Runechat (Mobs)"
+ key = "RUNECHAT_MOB"
+ enabled_description = "Show"
+ disabled_description = "Hide"
+
+/datum/client_preference/runechat_obj
+ description = "Runechat (Objs)"
+ key = "RUNECHAT_OBJ"
+ enabled_description = "Show"
+ disabled_description = "Hide"
+
+/datum/client_preference/runechat_border
+ description = "Runechat Message Border"
+ key = "RUNECHAT_BORDER"
+ enabled_description = "Show"
+ disabled_description = "Hide"
+ enabled_by_default = FALSE
+
+/datum/client_preference/runechat_long_messages
+ description = "Runechat Message Length"
+ key = "RUNECHAT_LONG"
+ enabled_description = "ERP KING"
+ disabled_description = "Normie"
+ enabled_by_default = FALSE
+
/datum/client_preference/status_indicators/toggled(mob/preference_mob, enabled)
. = ..()
if(preference_mob && preference_mob.plane_holder)
diff --git a/code/modules/clothing/spacesuits/void/ert.dm b/code/modules/clothing/spacesuits/void/ert_vr.dm
similarity index 95%
rename from code/modules/clothing/spacesuits/void/ert.dm
rename to code/modules/clothing/spacesuits/void/ert_vr.dm
index 5bfdfb8c6b3..a123238c792 100644
--- a/code/modules/clothing/spacesuits/void/ert.dm
+++ b/code/modules/clothing/spacesuits/void/ert_vr.dm
@@ -4,11 +4,16 @@
icon_state = "ertsuit"
item_state = "ertsuit"
armor = list(melee = 60, bullet = 50, laser = 30,energy = 15, bomb = 30, bio = 100, rad = 100)
- slowdown = 1
+ slowdown = 0.5
siemens_coefficient = 0.5
species_restricted = list("exclude",SPECIES_DIONA,SPECIES_VOX,SPECIES_TESHARI) //this thing can autoadapt
icon = 'icons/obj/clothing/suits_vr.dmi'
w_class = ITEMSIZE_NORMAL //the mark vii packs itself down when not in use, thanks future-materials
+ breach_threshold = 16 //Extra Thicc
+ resilience = 0.05 //Military Armor
+ min_pressure_protection = 0 * ONE_ATMOSPHERE
+ max_pressure_protection = 15* ONE_ATMOSPHERE
+ max_heat_protection_temperature = SPACE_SUIT_MAX_HEAT_PROTECTION_TEMPERATURE+10000
/obj/item/clothing/suit/space/void/responseteam/command
name = "Mark VII-C Emergency Response Team Commander Suit"
@@ -110,6 +115,9 @@
plane_slots = list(slot_head)
var/hud_active = 1
var/activation_sound = 'sound/items/nif_click.ogg'
+ min_pressure_protection = 0 * ONE_ATMOSPHERE
+ max_pressure_protection = 15* ONE_ATMOSPHERE
+ max_heat_protection_temperature = SPACE_SUIT_MAX_HEAT_PROTECTION_TEMPERATURE+10000
/obj/item/clothing/head/helmet/space/void/responseteam/verb/toggle()
set category = "Object"
diff --git a/code/modules/clothing/spacesuits/void/event.dm b/code/modules/clothing/spacesuits/void/event.dm
index 5e68414a330..a80b3eb972c 100644
--- a/code/modules/clothing/spacesuits/void/event.dm
+++ b/code/modules/clothing/spacesuits/void/event.dm
@@ -17,7 +17,6 @@
desc = "A refurbished early contact era voidsuit of human design. These things aren't especially good against modern weapons but they're sturdy, incredibly easy to come by, and there are lots of spare parts for repairs. Many old-timer spacers swear by these old things, even if new powered hardsuits have more features and better armor. This one is devoid of any identifying markings or rank indicators."
icon_state = "rig-vintagecrew"
item_state_slots = list(slot_r_hand_str = "sec_voidsuitTG", slot_l_hand_str = "sec_voidsuitTG")
- slowdown = 0.5
armor = list(melee = 30, bullet = 15, laser = 15,energy = 5, bomb = 20, bio = 100, rad = 50)
allowed = list(/obj/item/device/flashlight,
/obj/item/weapon/tank,
@@ -40,7 +39,7 @@
armor = list(melee = 40, bullet = 20, laser = 20, energy = 5, bomb = 35, bio = 100, rad = 100)
min_pressure_protection = 0 * ONE_ATMOSPHERE
max_pressure_protection = 15 * ONE_ATMOSPHERE
- max_heat_protection_temperature = FIRE_HELMET_MAX_HEAT_PROTECTION_TEMPERATURE
+ max_heat_protection_temperature = SPACE_SUIT_MAX_HEAT_PROTECTION_TEMPERATURE+10000
/obj/item/clothing/suit/space/void/refurb/engineering
name = "vintage engineering voidsuit"
@@ -51,7 +50,8 @@
armor = list(melee = 40, bullet = 20, laser = 20, energy = 5, bomb = 35, bio = 100, rad = 100)
min_pressure_protection = 0 * ONE_ATMOSPHERE
max_pressure_protection = 15 * ONE_ATMOSPHERE
- max_heat_protection_temperature = FIRESUIT_MAX_HEAT_PROTECTION_TEMPERATURE
+ max_heat_protection_temperature = SPACE_SUIT_MAX_HEAT_PROTECTION_TEMPERATURE+10000
+ breach_threshold = 14 //These are kinda thicc
allowed = list(/obj/item/device/flashlight,
/obj/item/weapon/tank,
/obj/item/device/suit_cooling_unit,
@@ -93,7 +93,6 @@
desc = "A refurbished early contact era voidsuit of human design. These things aren't especially good against modern weapons but they're sturdy, incredibly easy to come by, and there are lots of spare parts for repairs. Many old-timer spacers swear by these old things, even if new powered hardsuits have more features and better armor. The green and white markings indicate this as a medic's suit."
icon_state = "rig-vintagemedic"
item_state_slots = list(slot_r_hand_str = "sec_voidsuitTG", slot_l_hand_str = "sec_voidsuitTG")
- slowdown = 0.5
armor = list(melee = 30, bullet = 15, laser = 15, energy = 5, bomb = 25, bio = 100, rad = 75)
allowed = list(/obj/item/device/flashlight,
/obj/item/weapon/tank,
@@ -126,6 +125,8 @@
item_state_slots = list(slot_r_hand_str = "sec_voidsuitTG", slot_l_hand_str = "sec_voidsuitTG")
slowdown = 1
armor = list(melee = 40, bullet = 35, laser = 35, energy = 5, bomb = 40, bio = 100, rad = 50)
+ breach_threshold = 14 //These are kinda thicc
+ resilience = 0.15 //Armored
siemens_coefficient = 0.8
allowed = list(/obj/item/weapon/gun,
/obj/item/device/flashlight,
@@ -162,6 +163,8 @@
item_state_slots = list(slot_r_hand_str = "sec_voidsuitTG", slot_l_hand_str = "sec_voidsuitTG")
slowdown = 1
armor = list(melee = 50, bullet = 45, laser = 45, energy = 10, bomb = 30, bio = 100, rad = 60)
+ breach_threshold = 16 //Extra Thicc
+ resilience = 0.1 //Heavily Armored
siemens_coefficient = 0.7
allowed = list(/obj/item/weapon/gun,
/obj/item/device/flashlight,
@@ -201,7 +204,7 @@
desc = "A refurbished early contact era voidsuit of human design. These things aren't especially good against modern weapons but they're sturdy, incredibly easy to come by, and there are lots of spare parts for repairs. Many old-timer spacers swear by these old things, even if new powered hardsuits have more features and better armor. The royal blue markings indicate this is the pilot's variant; low protection but ultra-lightweight."
icon_state = "rig-vintagepilot"
item_state_slots = list(slot_r_hand_str = "sec_voidsuitTG", slot_l_hand_str = "sec_voidsuitTG")
- slowdown = 0.25
+ slowdown = 0
armor = list(melee = 25, bullet = 20, laser = 20, energy = 5, bomb = 20, bio = 100, rad = 50)
siemens_coefficient = 0.9
allowed = list(/obj/item/device/flashlight,
@@ -232,7 +235,6 @@
desc = "A refurbished early contact era voidsuit of human design. These things aren't especially good against modern weapons but they're sturdy, incredibly easy to come by, and there are lots of spare parts for repairs. Many old-timer spacers swear by these old things, even if new powered hardsuits have more features and better armor. The purple markings indicate this as a scientist's suit. Keep your eyes open for ropes."
icon_state = "rig-vintagescientist"
item_state_slots = list(slot_r_hand_str = "sec_voidsuitTG", slot_l_hand_str = "sec_voidsuitTG")
- slowdown = 0.5
armor = list(melee = 25, bullet = 10, laser = 10, energy = 50, bomb = 10, bio = 100, rad = 100)
siemens_coefficient = 0.8
allowed = list(/obj/item/device/flashlight,
@@ -275,6 +277,8 @@
item_state_slots = list(slot_r_hand_str = "sec_voidsuitTG", slot_l_hand_str = "sec_voidsuitTG")
slowdown = 1.5 //the tradeoff for being hot shit almost on par with a crimson suit is that it slows you down even more
armor = list(melee = 55, bullet = 45, laser = 45, energy = 25, bomb = 50, bio = 100, rad = 50)
+ breach_threshold = 16 //Extra Thicc
+ resilience = 0.05 //Military Armor
siemens_coefficient = 0.6
allowed = list(/obj/item/weapon/gun,
/obj/item/device/flashlight,
diff --git a/code/modules/clothing/spacesuits/void/merc.dm b/code/modules/clothing/spacesuits/void/merc.dm
index b9aca8d29cc..10f2b0a8386 100644
--- a/code/modules/clothing/spacesuits/void/merc.dm
+++ b/code/modules/clothing/spacesuits/void/merc.dm
@@ -14,11 +14,12 @@
name = "blood-red voidsuit"
desc = "An advanced suit that protects against injuries during special operations. Property of Gorlex Marauders."
item_state_slots = list(slot_r_hand_str = "syndie_voidsuit", slot_l_hand_str = "syndie_voidsuit")
- slowdown = 1
w_class = ITEMSIZE_NORMAL
armor = list(melee = 60, bullet = 50, laser = 30, energy = 15, bomb = 35, bio = 100, rad = 60)
allowed = list(/obj/item/device/flashlight,/obj/item/weapon/tank,/obj/item/device/suit_cooling_unit,/obj/item/weapon/gun,/obj/item/ammo_magazine,/obj/item/ammo_casing,/obj/item/weapon/melee/baton,/obj/item/weapon/melee/energy/sword,/obj/item/weapon/handcuffs)
siemens_coefficient = 0.6
+ breach_threshold = 16 //Extra Thicc
+ resilience = 0.05 //Military Armor
/obj/item/clothing/head/helmet/space/void/merc/fire
icon_state = "rig0-firebug"
@@ -26,6 +27,8 @@
desc = "A blackened helmet that has had many of its protective plates coated in or replaced with high-grade thermal insulation, to protect against incineration. Property of Gorlex Marauders."
armor = list(melee = 40, bullet = 40, laser = 60, energy = 20, bomb = 50, bio = 100, rad = 50)
max_heat_protection_temperature = FIRE_HELMET_MAX_HEAT_PROTECTION_TEMPERATURE
+ min_pressure_protection = 0 * ONE_ATMOSPHERE
+ max_pressure_protection = 20* ONE_ATMOSPHERE
siemens_coefficient = 0.7
light_overlay = "helmet_light_fire"
@@ -33,7 +36,11 @@
icon_state = "rig-firebug"
name = "soot-covered voidsuit"
desc = "A blackened suit that has had many of its protective plates coated in or replaced with high-grade thermal insulation, to protect against incineration. Property of Gorlex Marauders."
- armor = list(melee = 40, bullet = 40, laser = 60, energy = 20, bomb = 50, bio = 100, rad = 50)
+ armor = list(melee = 50, bullet = 40, laser = 60, energy = 20, bomb = 50, bio = 100, rad = 50)
max_heat_protection_temperature = FIRESUIT_MAX_HEAT_PROTECTION_TEMPERATURE
+ min_pressure_protection = 0 * ONE_ATMOSPHERE
+ max_pressure_protection = 20* ONE_ATMOSPHERE
allowed = list(/obj/item/device/flashlight,/obj/item/weapon/tank,/obj/item/device/suit_cooling_unit,/obj/item/weapon/gun,/obj/item/ammo_magazine,/obj/item/ammo_casing,/obj/item/weapon/melee/baton,/obj/item/weapon/melee/energy/sword,/obj/item/weapon/handcuffs,/obj/item/weapon/material/twohanded/fireaxe,/obj/item/weapon/flamethrower)
siemens_coefficient = 0.7
+ breach_threshold = 18 //Super Extra Thicc
+ slowdown = 1
diff --git a/code/modules/clothing/spacesuits/void/military_vr.dm b/code/modules/clothing/spacesuits/void/military_vr.dm
index b8167da8790..94355ba9c43 100644
--- a/code/modules/clothing/spacesuits/void/military_vr.dm
+++ b/code/modules/clothing/spacesuits/void/military_vr.dm
@@ -12,6 +12,8 @@
item_state_slots = list(slot_r_hand_str = "sec_voidsuit", slot_l_hand_str = "sec_voidsuit")
slowdown = 1.5
armor = list(melee = 60, bullet = 35, laser = 35, energy = 15, bomb = 55, bio = 100, rad = 20)
+ breach_threshold = 14 //These are kinda thicc
+ resilience = 0.15 //Armored
/obj/item/clothing/head/helmet/space/void/security/prototype
name = "\improper security prototype voidsuit helmet"
@@ -51,3 +53,6 @@
icon = 'icons/obj/clothing/suits_vr.dmi'
icon_override = 'icons/mob/suit_vr.dmi'
species_restricted = null
+ breach_threshold = 16 //Extra Thicc
+ resilience = 0.05 //Military Armor
+
diff --git a/code/modules/clothing/spacesuits/void/station.dm b/code/modules/clothing/spacesuits/void/station.dm
index 973b0e5309a..1ad116978b6 100644
--- a/code/modules/clothing/spacesuits/void/station.dm
+++ b/code/modules/clothing/spacesuits/void/station.dm
@@ -2,23 +2,26 @@
//Engineering
/obj/item/clothing/head/helmet/space/void/engineering
name = "engineering voidsuit helmet"
- desc = "A special helmet designed for work in a hazardous, low-pressure environment. Has radiation shielding."
+ desc = "A special helmet designed for work in a hazardous, low-pressure environment. Has moderate radiation and pressure shielding."
icon_state = "rig0-engineering"
item_state_slots = list(slot_r_hand_str = "eng_helm", slot_l_hand_str = "eng_helm")
- armor = list(melee = 40, bullet = 5, laser = 20, energy = 5, bomb = 35, bio = 100, rad = 80)
+ armor = list(melee = 30, bullet = 5, laser = 20, energy = 5, bomb = 35, bio = 100, rad = 70)
min_pressure_protection = 0 * ONE_ATMOSPHERE
max_pressure_protection = 15 * ONE_ATMOSPHERE
+ max_heat_protection_temperature = SPACE_SUIT_MAX_HEAT_PROTECTION_TEMPERATURE+5000
/obj/item/clothing/suit/space/void/engineering
name = "engineering voidsuit"
- desc = "A special suit that protects against hazardous, low pressure environments. Has radiation shielding."
+ desc = "A special suit that protects against hazardous, low pressure environments. Has moderate radiation and pressure shielding."
icon_state = "rig-engineering"
item_state_slots = list(slot_r_hand_str = "eng_voidsuit", slot_l_hand_str = "eng_voidsuit")
- slowdown = 1
- armor = list(melee = 40, bullet = 5, laser = 20, energy = 5, bomb = 35, bio = 100, rad = 80)
+ armor = list(melee = 30, bullet = 5, laser = 20, energy = 5, bomb = 35, bio = 100, rad = 70)
allowed = list(/obj/item/device/flashlight,/obj/item/weapon/tank,/obj/item/device/suit_cooling_unit,/obj/item/weapon/storage/bag/ore,/obj/item/device/t_scanner,/obj/item/weapon/pickaxe, /obj/item/weapon/rcd)
min_pressure_protection = 0 * ONE_ATMOSPHERE
max_pressure_protection = 15 * ONE_ATMOSPHERE
+ max_heat_protection_temperature = SPACE_SUIT_MAX_HEAT_PROTECTION_TEMPERATURE+5000
+ breach_threshold = 14 //These are kinda thicc
+ slowdown = 1
//Engineering HAZMAT Voidsuit
@@ -27,26 +30,43 @@
desc = "A engineering helmet designed for work in a low-pressure environment. Extra radiation shielding appears to have been installed at the price of comfort."
icon_state = "rig0-engineering_rad"
item_state_slots = list(slot_r_hand_str = "eng_helm_rad", slot_l_hand_str = "eng_helm_rad")
- armor = list(melee = 30, bullet = 5, laser = 20, energy = 5, bomb = 50, bio = 100, rad = 100)
+ armor = list(melee = 25, bullet = 5, laser = 20, energy = 5, bomb = 50, bio = 100, rad = 100)
+ min_pressure_protection = 0 * ONE_ATMOSPHERE
+ max_pressure_protection = 10 * ONE_ATMOSPHERE
+ max_heat_protection_temperature = SPACE_SUIT_MAX_HEAT_PROTECTION_TEMPERATURE
/obj/item/clothing/suit/space/void/engineering/hazmat
name = "HAZMAT voidsuit"
desc = "A engineering voidsuit that protects against hazardous, low pressure environments. Has enhanced radiation shielding compared to regular engineering voidsuits."
icon_state = "rig-engineering_rad"
item_state_slots = list(slot_r_hand_str = "eng_voidsuit_rad", slot_l_hand_str = "eng_voidsuit_rad")
- armor = list(melee = 30, bullet = 5, laser = 20, energy = 5, bomb = 50, bio = 100, rad = 100)
+ armor = list(melee = 25, bullet = 5, laser = 20, energy = 5, bomb = 50, bio = 100, rad = 100)
+ min_pressure_protection = 0 * ONE_ATMOSPHERE
+ max_pressure_protection = 10 * ONE_ATMOSPHERE
+ max_heat_protection_temperature = SPACE_SUIT_MAX_HEAT_PROTECTION_TEMPERATURE
//Engineering Construction Voidsuit
/obj/item/clothing/head/helmet/space/void/engineering/construction
name = "construction voidsuit helmet"
+ desc = "A special helmet designed for work in a hazardous, low-pressure environment. Exchanges radiation shielding for extra armor and maneuverability for field projects."
icon_state = "rig0-engineering_con"
item_state_slots = list(slot_r_hand_str = "eng_helm_con", slot_l_hand_str = "eng_helm_con")
+ armor = list(melee = 40, bullet = 15, laser = 25, energy = 15, bomb = 35, bio = 100, rad = 50)
+ min_pressure_protection = 0 * ONE_ATMOSPHERE
+ max_pressure_protection = 10 * ONE_ATMOSPHERE
+ max_heat_protection_temperature = SPACE_SUIT_MAX_HEAT_PROTECTION_TEMPERATURE
/obj/item/clothing/suit/space/void/engineering/construction
- name = "contstruction voidsuit"
+ name = "construction voidsuit"
+ desc = "A special suit that protects against hazardous, low pressure environments. Exchanges radiation shielding for extra armor and maneuverability for field projects."
icon_state = "rig-engineering_con"
item_state_slots = list(slot_r_hand_str = "eng_voidsuit_con", slot_l_hand_str = "eng_voidsuit_con")
+ armor = list(melee = 40, bullet = 15, laser = 25, energy = 15, bomb = 35, bio = 100, rad = 50)
+ min_pressure_protection = 0 * ONE_ATMOSPHERE
+ max_pressure_protection = 10 * ONE_ATMOSPHERE
+ max_heat_protection_temperature = SPACE_SUIT_MAX_HEAT_PROTECTION_TEMPERATURE
+ slowdown = 0.5
//Engineering Surplus Voidsuits
@@ -54,14 +74,15 @@
name = "reinforced engineering voidsuit helmet"
desc = "A heavy, radiation-shielded voidsuit helmet with a surprisingly comfortable interior."
icon_state = "rig0-engineeringalt"
- armor = list(melee = 40, bullet = 5, laser = 20,energy = 5, bomb = 45, bio = 100, rad = 100)
+ armor = list(melee = 50, bullet = 15, laser = 25, energy = 5, bomb = 45, bio = 100, rad = 100)
light_overlay = "helmet_light_dual"
/obj/item/clothing/suit/space/void/engineering/alt
name = "reinforced engineering voidsuit"
desc = "A bulky industrial voidsuit. It's a few generations old, but a reliable design and radiation shielding make up for the lack of climate control."
icon_state = "rig-engineeringalt"
- armor = list(melee = 40, bullet = 5, laser = 20,energy = 5, bomb = 45, bio = 100, rad = 100)
+ armor = list(melee = 50, bullet = 15, laser = 25, energy = 15, bomb = 45, bio = 100, rad = 100)
+ slowdown = 0.5
/obj/item/clothing/head/helmet/space/void/engineering/salvage
name = "salvage voidsuit helmet"
@@ -71,14 +92,15 @@
slot_l_hand_str = "eng_helm",
slot_r_hand_str = "eng_helm",
)
- armor = list(melee = 50, bullet = 10, laser = 30,energy = 15, bomb = 35, bio = 100, rad = 80)
+ armor = list(melee = 50, bullet = 15, laser = 25, energy = 15, bomb = 45, bio = 100, rad = 100)
/obj/item/clothing/suit/space/void/engineering/salvage
name = "salvage voidsuit"
desc = "A hand-me-down salvage voidsuit. It has obviously had a lot of repair work done to its radiation shielding."
icon_state = "rig-engineeringsav"
- armor = list(melee = 50, bullet = 10, laser = 30,energy = 15, bomb = 35, bio = 100, rad = 80)
+ armor = list(melee = 50, bullet = 15, laser = 25, energy = 15, bomb = 45, bio = 100, rad = 100)
allowed = list(/obj/item/device/flashlight,/obj/item/weapon/tank,/obj/item/device/suit_cooling_unit,/obj/item/weapon/storage/toolbox,/obj/item/weapon/storage/briefcase/inflatable,/obj/item/device/t_scanner,/obj/item/weapon/rcd)
+ slowdown = 0.5
//Mining
/obj/item/clothing/head/helmet/space/void/mining
@@ -86,7 +108,7 @@
desc = "A special helmet designed for work in a hazardous, low pressure environment. Has reinforced plating."
icon_state = "rig0-mining"
item_state_slots = list(slot_r_hand_str = "mining_helm", slot_l_hand_str = "mining_helm")
- armor = list(melee = 50, bullet = 5, laser = 20, energy = 5, bomb = 55, bio = 100, rad = 20)
+ armor = list(melee = 50, bullet = 15, laser = 25, energy = 15, bomb = 55, bio = 100, rad = 50)
light_overlay = "helmet_light_dual"
/obj/item/clothing/suit/space/void/mining
@@ -95,7 +117,10 @@
icon_state = "rig-mining"
item_state_slots = list(slot_r_hand_str = "mining_voidsuit", slot_l_hand_str = "mining_voidsuit")
allowed = list(/obj/item/device/flashlight,/obj/item/weapon/tank,/obj/item/device/suit_cooling_unit,/obj/item/weapon/pickaxe)
- armor = list(melee = 50, bullet = 5, laser = 20, energy = 5, bomb = 55, bio = 100, rad = 20)
+ armor = list(melee = 50, bullet = 15, laser = 25, energy = 15, bomb = 55, bio = 100, rad = 50)
+ breach_threshold = 14 //These are kinda thicc
+ resilience = 0.15 //Armored
+ slowdown = 1
//Mining Surplus Voidsuit
@@ -103,13 +128,12 @@
name = "frontier mining voidsuit helmet"
desc = "An armored cheap voidsuit helmet. Someone must have through they were pretty cool when they painted a mohawk on it."
icon_state = "rig0-miningalt"
- armor = list(melee = 50, bullet = 15, laser = 20,energy = 5, bomb = 55, bio = 100, rad = 0)
/obj/item/clothing/suit/space/void/mining/alt
icon_state = "rig-miningalt"
name = "frontier mining voidsuit"
desc = "A cheap prospecting voidsuit. What it lacks in comfort it makes up for in armor plating and street cred."
- armor = list(melee = 50, bullet = 15, laser = 20,energy = 5, bomb = 55, bio = 100, rad = 0)
+ slowdown = 0.5
//Medical
/obj/item/clothing/head/helmet/space/void/medical
@@ -117,7 +141,7 @@
desc = "A special helmet designed for work in a hazardous, low pressure environment. Has minor radiation shielding."
icon_state = "rig0-medical"
item_state_slots = list(slot_r_hand_str = "medical_helm", slot_l_hand_str = "medical_helm")
- armor = list(melee = 30, bullet = 5, laser = 20, energy = 5, bomb = 25, bio = 100, rad = 50)
+ armor = list(melee = 30, bullet = 5, laser = 20, energy = 5, bomb = 25, bio = 100, rad = 80)
/obj/item/clothing/suit/space/void/medical
name = "medical voidsuit"
@@ -125,35 +149,48 @@
icon_state = "rig-medical"
item_state_slots = list(slot_r_hand_str = "medical_voidsuit", slot_l_hand_str = "medical_voidsuit")
allowed = list(/obj/item/device/flashlight,/obj/item/weapon/tank,/obj/item/device/suit_cooling_unit,/obj/item/weapon/storage/firstaid,/obj/item/device/healthanalyzer,/obj/item/stack/medical)
- armor = list(melee = 30, bullet = 5, laser = 20, energy = 5, bomb = 25, bio = 100, rad = 50)
+ armor = list(melee = 30, bullet = 5, laser = 20, energy = 5, bomb = 25, bio = 100, rad = 80)
//Medical EMT Voidsuit
/obj/item/clothing/head/helmet/space/void/medical/emt
name = "emergency medical response voidsuit helmet"
+ desc = "A special helmet designed for work in a hazardous, low pressure environment. Exchanges radiation shielding for some additional protection."
icon_state = "rig0-medical_emt"
item_state_slots = list(slot_r_hand_str = "medical_helm_emt", slot_l_hand_str = "medical_helm_emt")
+ armor = list(melee = 40, bullet = 15, laser = 30, energy = 15, bomb = 35, bio = 100, rad = 50)
/obj/item/clothing/suit/space/void/medical/emt
name = "emergency medical response voidsuit"
+ desc = "A special suit that protects against hazardous, low pressure environments. Exchanges radiation shielding for some additional protection."
icon_state = "rig-medical_emt"
item_state_slots = list(slot_r_hand_str = "medical_voidsuit_emt", slot_l_hand_str = "medical_voidsuit_emt")
+ armor = list(melee = 40, bullet = 15, laser = 30, energy = 15, bomb = 35, bio = 100, rad = 50)
+ breach_threshold = 14 //These are kinda thicc
//Medical Biohazard Voidsuit
/obj/item/clothing/head/helmet/space/void/medical/bio
name = "biohazard voidsuit helmet"
- desc = "A special helmet that protects against hazardous environments. Has minor radiation shielding."
+ desc = "A special suit designed to protect the user in hazardous enviornments on the field. It feels heavier than the standard suit with extra protection around the joints."
icon_state = "rig0-medical_bio"
item_state_slots = list(slot_r_hand_str = "medical_helm_bio", slot_l_hand_str = "medical_helm_bio")
- armor = list(melee = 45, bullet = 5, laser = 20, energy = 5, bomb = 15, bio = 100, rad = 75)
+ armor = list(melee = 55, bullet = 15, laser = 20, energy = 15, bomb = 15, bio = 100, rad = 75)
+ max_heat_protection_temperature = FIRE_HELMET_MAX_HEAT_PROTECTION_TEMPERATURE
+ min_pressure_protection = 0 * ONE_ATMOSPHERE
+ max_pressure_protection = 15 * ONE_ATMOSPHERE
/obj/item/clothing/suit/space/void/medical/bio
name = "biohazard voidsuit"
- desc = "A special suit that protects against hazardous, environments. It feels heavier than the standard suit with extra protection around the joints."
+ desc = "A special suit designed to protect the user in hazardous enviornments on the field. It feels heavier than the standard suit with extra protection around the joints."
icon_state = "rig-medical_bio"
item_state_slots = list(slot_r_hand_str = "medical_voidsuit_bio", slot_l_hand_str = "medical_voidsuit_bio")
- armor = list(melee = 45, bullet = 5, laser = 20, energy = 5, bomb = 15, bio = 100, rad = 75)
+ armor = list(melee = 55, bullet = 15, laser = 20, energy = 15, bomb = 15, bio = 100, rad = 75)
+ max_heat_protection_temperature = FIRE_HELMET_MAX_HEAT_PROTECTION_TEMPERATURE
+ min_pressure_protection = 0 * ONE_ATMOSPHERE
+ max_pressure_protection = 15 * ONE_ATMOSPHERE
+ breach_threshold = 16 //Extra Thicc
+ slowdown = 1.5
//Medical Streamlined Voidsuit
/obj/item/clothing/head/helmet/space/void/medical/alt
@@ -234,7 +271,7 @@
desc = "A special helmet designed for work in a hazardous, low pressure environment. Has an additional layer of armor."
icon_state = "rig0-sec"
item_state_slots = list(slot_r_hand_str = "sec_helm", slot_l_hand_str = "sec_helm")
- armor = list(melee = 50, bullet = 25, laser = 25, energy = 5, bomb = 45, bio = 100, rad = 10)
+ armor = list(melee = 50, bullet = 25, laser = 25, energy = 15, bomb = 45, bio = 100, rad = 10)
siemens_coefficient = 0.7
light_overlay = "helmet_light_dual"
@@ -243,9 +280,12 @@
desc = "A special suit that protects against hazardous, low pressure environments. Has an additional layer of armor."
icon_state = "rig-sec"
item_state_slots = list(slot_r_hand_str = "sec_voidsuit", slot_l_hand_str = "sec_voidsuit")
- armor = list(melee = 50, bullet = 25, laser = 25, energy = 5, bomb = 45, bio = 100, rad = 10)
+ armor = list(melee = 50, bullet = 25, laser = 25, energy = 15, bomb = 45, bio = 100, rad = 10)
allowed = list(/obj/item/weapon/gun,/obj/item/device/flashlight,/obj/item/weapon/tank,/obj/item/device/suit_cooling_unit,/obj/item/weapon/melee/baton)
siemens_coefficient = 0.7
+ breach_threshold = 14 //These are kinda thicc
+ resilience = 0.15 //Armored
+ slowdown = 1
//Security Crowd Control Voidsuit
@@ -253,28 +293,32 @@
name = "crowd control voidsuit helmet"
desc = "A heavy-set and ominous looking crowd control suit helmet. Fitted with state of the art shock absorbing materials, to disperse blunt force trauma."
icon_state = "rig0-sec_riot"
- armor = list(melee = 70, bullet = 15, laser = 15, energy = 5, bomb = 40, bio = 100, rad = 10)
+ armor = list(melee = 70, bullet = 15, laser = 15, energy = 15, bomb = 60, bio = 100, rad = 10)
item_state_slots = list(slot_r_hand_str = "sec_helm_riot", slot_l_hand_str = "sec_helm_riot")
/obj/item/clothing/suit/space/void/security/riot
name = "crowd control voidsuit"
desc = "A heavy-set and ominous looking crowd control suit. Fitted with state of the art shock absorbing materials, to disperse blunt force trauma."
icon_state = "rig-sec_riot"
- armor = list(melee = 70, bullet = 15, laser = 15, energy = 5, bomb = 40, bio = 100, rad = 10)
+ armor = list(melee = 70, bullet = 15, laser = 15, energy = 15, bomb = 60, bio = 100, rad = 10)
+ breach_threshold = 16 //Extra Thicc
+ resilience = 0.1 //Heavily Armored
item_state_slots = list(slot_r_hand_str = "sec_voidsuit_riot", slot_l_hand_str = "sec_voidsuit_riot")
//Security Surplus Voidsuit
/obj/item/clothing/head/helmet/space/void/security/alt
name = "security EVA voidsuit helmet"
desc = "A grey-black voidsuit helmet with red highlights. A little tacky, but it offers better protection against modern firearms and radiation than standard-issue security voidsuit helmets."
- armor = list(melee = 30, bullet = 40, laser = 40, energy = 25, bomb = 60, bio = 100, rad = 50)
+ armor = list(melee = 40, bullet = 40, laser = 40, energy = 25, bomb = 40, bio = 100, rad = 50)
icon_state = "rig0-secalt"
item_state_slots = list(slot_r_hand_str = "syndicate-helm-black", slot_l_hand_str = "syndicate-helm-black")
/obj/item/clothing/suit/space/void/security/alt
name = "security EVA voidsuit"
desc = "A grey-black voidsuit with red highlights. A little tacky, but it offers better protection against modern firearms and radiation than standard-issue security voidsuits."
- armor = list(melee = 30, bullet = 40, laser = 40, energy = 25, bomb = 60, bio = 100, rad = 50)
+ armor = list(melee = 40, bullet = 40, laser = 40, energy = 25, bomb = 40, bio = 100, rad = 50)
+ breach_threshold = 16 //Extra Thicc
+ resilience = 0.1 //Heavily Armored
icon_state = "rig-secalt"
item_state_slots = list(slot_r_hand_str = "sec_voidsuitTG", slot_l_hand_str = "sec_voidsuitTG")
@@ -299,6 +343,8 @@
max_heat_protection_temperature = FIRESUIT_MAX_HEAT_PROTECTION_TEMPERATURE
min_pressure_protection = 0 * ONE_ATMOSPHERE
max_pressure_protection = 20* ONE_ATMOSPHERE
+ breach_threshold = 16 //Extra Thicc
+ slowdown = 1.5
//Atmospherics Surplus Voidsuit
@@ -306,16 +352,15 @@
desc = "A special voidsuit helmet designed for work in hazardous, low pressure environments.This one has been plated with an expensive heat and radiation resistant ceramic."
name = "heavy duty atmospherics voidsuit helmet"
icon_state = "rig0-atmosalt"
- armor = list(melee = 20, bullet = 5, laser = 20,energy = 15, bomb = 45, bio = 100, rad = 50)
- max_heat_protection_temperature = FIRE_HELMET_MAX_HEAT_PROTECTION_TEMPERATURE
+ armor = list(melee = 40, bullet = 5, laser = 20, energy = 5, bomb = 35, bio = 100, rad = 70)
light_overlay = "hardhat_light"
/obj/item/clothing/suit/space/void/atmos/alt
desc = "A special suit that protects against hazardous, low pressure environments. Fits better than the standard atmospheric voidsuit while still rated to withstand extreme heat and even minor radiation."
icon_state = "rig-atmosalt"
name = "heavy duty atmos voidsuit"
- armor = list(melee = 20, bullet = 5, laser = 20,energy = 15, bomb = 45, bio = 100, rad = 50)
- max_heat_protection_temperature = FIRESUIT_MAX_HEAT_PROTECTION_TEMPERATURE
+ armor = list(melee = 40, bullet = 5, laser = 20, energy = 5, bomb = 35, bio = 100, rad = 70)
+ slowdown = 1
//Exploration
/obj/item/clothing/head/helmet/space/void/exploration
@@ -324,7 +369,7 @@
icon_state = "helm_explorer"
item_state = "helm_explorer"
item_state_slots = list(slot_r_hand_str = "syndicate-helm-black", slot_l_hand_str = "syndicate-helm-black")
- armor = list(melee = 40, bullet = 15, laser = 25,energy = 35, bomb = 30, bio = 100, rad = 70)
+ armor = list(melee = 50, bullet = 15, laser = 35, energy = 25, bomb = 30, bio = 100, rad = 70)
light_overlay = "helmet_light_dual" //explorer_light
/obj/item/clothing/suit/space/void/exploration
@@ -332,29 +377,34 @@
desc = "A hazard and radiation resistant voidsuit, featuring the Explorer emblem on its chest plate. Designed for exploring unknown planetary environments."
icon_state = "void_explorer"
item_state_slots = list(slot_r_hand_str = "skrell_suit_black", slot_l_hand_str = "skrell_suit_black")
- armor = list(melee = 40, bullet = 15, laser = 25,energy = 35, bomb = 30, bio = 100, rad = 70)
+ armor = list(melee = 50, bullet = 15, laser = 35, energy = 25, bomb = 30, bio = 100, rad = 70)
allowed = list(/obj/item/device/flashlight,/obj/item/weapon/tank,/obj/item/device/suit_cooling_unit,/obj/item/stack/flag,/obj/item/device/healthanalyzer,/obj/item/device/gps,/obj/item/device/radio/beacon, \
/obj/item/weapon/shovel,/obj/item/ammo_magazine,/obj/item/weapon/gun)
+ breach_threshold = 14 //These are kinda thicc
+ resilience = 0.15 //Armored
//SAR
/obj/item/clothing/head/helmet/space/void/expedition_medical
- name = "exploration medic\'s voidsuit helmet"
+ name = "field medic voidsuit helmet"
desc = "A radiation-resistant helmet made especially for exploring unknown planetary environments. Has a reinforced high-vis bubble style visor."
icon_state = "helm_exp_medic"
item_state = "helm_exp_medic"
item_state_slots = list(slot_r_hand_str = "syndicate-helm-black", slot_l_hand_str = "syndicate-helm-black")
- armor = list(melee = 25, bullet = 10, laser = 20,energy = 30, bomb = 25, bio = 100, rad = 70)
+ armor = list(melee = 50, bullet = 15, laser = 25, energy = 15, bomb = 30, bio = 100, rad = 90)
light_overlay = "helmet_light_dual" //explorer_light
+ max_heat_protection_temperature = SPACE_SUIT_MAX_HEAT_PROTECTION_TEMPERATURE+5000
/obj/item/clothing/suit/space/void/expedition_medical
- name = "exploration medic\'s voidsuit"
+ name = "field medic voidsuit"
desc = "A hazard and radiation resistant voidsuit, featuring the Explorer emblem and a green cross on its chest plate. Seems to be a little lighter and more flexible than the regular explorer issue."
icon_state = "void_exp_medic"
- slowdown = 0.75
item_state_slots = list(slot_r_hand_str = "skrell_suit_black", slot_l_hand_str = "skrell_suit_black")
- armor = list(melee = 25, bullet = 10, laser = 20,energy = 30, bomb = 25, bio = 100, rad = 70)
+ armor = list(melee = 50, bullet = 15, laser = 25, energy = 15, bomb = 30, bio = 100, rad = 90)
allowed = list(/obj/item/device/flashlight,/obj/item/weapon/tank,/obj/item/device/suit_cooling_unit,/obj/item/stack/flag,/obj/item/device/healthanalyzer,/obj/item/device/gps,/obj/item/device/radio/beacon, \
/obj/item/weapon/shovel,/obj/item/ammo_magazine,/obj/item/weapon/gun,/obj/item/weapon/storage/firstaid,/obj/item/stack/medical)
+ breach_threshold = 14 //These are kinda thicc
+ resilience = 0.15 //Armored
+ max_heat_protection_temperature = SPACE_SUIT_MAX_HEAT_PROTECTION_TEMPERATURE+5000
/obj/item/clothing/head/helmet/space/void/exploration/alt
desc = "A radiation-resistant helmet retrofitted for exploring unknown planetary environments."
@@ -374,8 +424,7 @@
icon_state = "rig0_pilot"
item_state = "pilot_helm"
item_state_slots = list(slot_r_hand_str = "atmos_helm", slot_l_hand_str = "atmos_helm")
- armor = list(melee = 40, bullet = 5, laser = 20,energy = 5, bomb = 15, bio = 100, rad = 50)
- max_heat_protection_temperature = FIRE_HELMET_MAX_HEAT_PROTECTION_TEMPERATURE
+ armor = list(melee = 40, bullet = 10, laser = 25, energy = 15, bomb = 25, bio = 100, rad = 60)
light_overlay = "helmet_light_dual"
/obj/item/clothing/suit/space/void/pilot
@@ -383,8 +432,7 @@
icon_state = "rig-pilot"
item_state_slots = list(slot_r_hand_str = "atmos_voidsuit", slot_l_hand_str = "atmos_voidsuit")
name = "pilot voidsuit"
- armor = list(melee = 40, bullet = 5, laser = 20,energy = 5, bomb = 15, bio = 100, rad = 50)
- max_heat_protection_temperature = FIRESUIT_MAX_HEAT_PROTECTION_TEMPERATURE
+ armor = list(melee = 40, bullet = 10, laser = 25, energy = 15, bomb = 25, bio = 100, rad = 60)
allowed = list(/obj/item/device/flashlight,/obj/item/weapon/tank,/obj/item/device/suit_cooling_unit,/obj/item/weapon/storage/toolbox,/obj/item/weapon/storage/briefcase/inflatable)
/obj/item/clothing/head/helmet/space/void/pilot/alt
diff --git a/code/modules/clothing/spacesuits/void/void.dm b/code/modules/clothing/spacesuits/void/void.dm
index 88ccc7370e3..3a92bd2be10 100644
--- a/code/modules/clothing/spacesuits/void/void.dm
+++ b/code/modules/clothing/spacesuits/void/void.dm
@@ -5,7 +5,7 @@
icon_state = "void"
item_state_slots = list(slot_r_hand_str = "syndicate", slot_l_hand_str = "syndicate")
heat_protection = HEAD
- armor = list(melee = 40, bullet = 5, laser = 20,energy = 5, bomb = 35, bio = 100, rad = 20)
+ armor = list(melee = 30, bullet = 5, laser = 20,energy = 5, bomb = 35, bio = 100, rad = 20)
max_heat_protection_temperature = SPACE_SUIT_MAX_HEAT_PROTECTION_TEMPERATURE
min_pressure_protection = 0 * ONE_ATMOSPHERE
max_pressure_protection = 10 * ONE_ATMOSPHERE
@@ -35,8 +35,8 @@
icon_state = "void"
item_state_slots = list(slot_r_hand_str = "space_suit_syndicate", slot_l_hand_str = "space_suit_syndicate")
desc = "A high-tech dark red space suit. Used for AI satellite maintenance."
- slowdown = 1
- armor = list(melee = 40, bullet = 5, laser = 20,energy = 5, bomb = 35, bio = 100, rad = 20)
+ slowdown = 0.5
+ armor = list(melee = 30, bullet = 5, laser = 20,energy = 5, bomb = 35, bio = 100, rad = 20)
allowed = list(/obj/item/device/flashlight,/obj/item/weapon/tank,/obj/item/device/suit_cooling_unit)
heat_protection = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS
max_heat_protection_temperature = SPACE_SUIT_MAX_HEAT_PROTECTION_TEMPERATURE
diff --git a/code/modules/clothing/spacesuits/void/wizard.dm b/code/modules/clothing/spacesuits/void/wizard.dm
index d39f553aea4..f86ecf8642d 100644
--- a/code/modules/clothing/spacesuits/void/wizard.dm
+++ b/code/modules/clothing/spacesuits/void/wizard.dm
@@ -16,7 +16,6 @@
name = "gem-encrusted voidsuit"
desc = "A bizarre gem-encrusted suit that radiates magical energies."
item_state_slots = list(slot_r_hand_str = "wiz_voidsuit", slot_l_hand_str = "wiz_voidsuit")
- slowdown = 1
w_class = ITEMSIZE_NORMAL
unacidable = 1
armor = list(melee = 40, bullet = 20, laser = 20,energy = 20, bomb = 35, bio = 100, rad = 60)
diff --git a/code/modules/clothing/under/accessories/accessory_vr.dm b/code/modules/clothing/under/accessories/accessory_vr.dm
index 5422e294b4e..e9d1ae21ac3 100644
--- a/code/modules/clothing/under/accessories/accessory_vr.dm
+++ b/code/modules/clothing/under/accessories/accessory_vr.dm
@@ -124,7 +124,7 @@
if(usr.stat) return
if(!jingled)
- usr.audible_message("[usr] jingles the [src]'s bell.")
+ usr.audible_message("[usr] jingles the [src]'s bell.", runemessage = "* jingle *")
playsound(src, 'sound/items/pickup/ring.ogg', 50, 1)
jingled = 1
addtimer(CALLBACK(src, .proc/jingledreset), 50)
diff --git a/code/modules/emotes/definitions/audible_furry_vr.dm b/code/modules/emotes/definitions/audible_furry_vr.dm
index c4afd79989b..9d4cc5348f9 100644
--- a/code/modules/emotes/definitions/audible_furry_vr.dm
+++ b/code/modules/emotes/definitions/audible_furry_vr.dm
@@ -129,3 +129,11 @@
key = "cackle"
emote_message_3p = "cackles hysterically!"
emote_sound = 'sound/voice/YeenCackle.ogg'
+/decl/emote/audible/spiderchitter
+ key = "spiderchitter"
+ emote_message_3p = "chitters."
+ emote_sound = 'sound/voice/spiderchitter.ogg'
+/decl/emote/audible/spiderpurr
+ key = "spiderpurr"
+ emote_message_3p = "purrs."
+ emote_sound = 'sound/voice/spiderpurr.ogg'
diff --git a/code/modules/emotes/definitions/visible_animated.dm b/code/modules/emotes/definitions/visible_animated.dm
index 142c3e4c0c2..dda87a5b80c 100644
--- a/code/modules/emotes/definitions/visible_animated.dm
+++ b/code/modules/emotes/definitions/visible_animated.dm
@@ -12,7 +12,7 @@
key = "sidestep"
check_restraints = TRUE
emote_message_3p = "steps rhythmically and moves side to side."
- emote_delay = 1.2 SECONDS
+ //emote_delay = 1.2 SECONDS //VOREStation Edit - Delay moved to parent
/decl/emote/visible/sidestep/do_extra(mob/user)
if(istype(user))
@@ -26,7 +26,7 @@
emote_message_1p = "You do a flip!"
emote_message_3p = "does a flip!"
emote_sound = 'sound/effects/bodyfall4.ogg'
- emote_delay = 1.2 SECONDS
+ //emote_delay = 1.2 SECONDS //VOREStation Edit - Delay moved to parent
/decl/emote/visible/flip/do_extra(mob/user)
. = ..()
@@ -42,7 +42,7 @@
key = "floorspin"
emote_message_1p = "You spin around on the floor!"
emote_message_3p = "spins around on the floor!"
- emote_delay = 1.2 SECONDS
+ //emote_delay = 1.2 SECONDS //VOREStation Edit - Delay moved to parent
var/static/list/spin_dirs = list(
NORTH,
SOUTH,
diff --git a/code/modules/emotes/emote_define.dm b/code/modules/emotes/emote_define.dm
index 6b50b09dde1..9a1debb429b 100644
--- a/code/modules/emotes/emote_define.dm
+++ b/code/modules/emotes/emote_define.dm
@@ -35,7 +35,7 @@ var/global/list/emotes_by_key
var/list/emote_sound_synthetic // As above, but used when check_synthetic() is true.
var/emote_volume = 50 // Volume of sound to play.
var/emote_volume_synthetic = 50 // As above, but used when check_synthetic() is true.
- var/emote_delay = 0 // Time in ds that this emote will block further emote use (spam prevention).
+ var/emote_delay = 1.2 SECONDS // Time in ds that this emote will block further emote use (spam prevention). // VOREStation Edit
var/message_type = VISIBLE_MESSAGE // Audible/visual flag
var/check_restraints // Can this emote be used while restrained?
@@ -103,11 +103,14 @@ var/global/list/emotes_by_key
if(target)
use_1p = replace_target_tokens(use_1p, target)
use_1p = "[capitalize(replace_user_tokens(use_1p, user))]"
- var/use_3p = get_emote_message_3p(user, target, extra_params)
- if(use_3p)
+ var/prefinal_3p
+ var/use_3p
+ var/raw_3p = get_emote_message_3p(user, target, extra_params)
+ if(raw_3p)
if(target)
- use_3p = replace_target_tokens(use_3p, target)
- use_3p = "\The [user] [replace_user_tokens(use_3p, user)]"
+ raw_3p = replace_target_tokens(raw_3p, target)
+ prefinal_3p = replace_user_tokens(raw_3p, user)
+ use_3p = "\The [user] [prefinal_3p]"
var/use_radio = get_radio_message(user)
if(use_radio)
if(target)
@@ -124,12 +127,12 @@ var/global/list/emotes_by_key
if(isliving(user))
var/mob/living/L = user
if(L.silent)
- M.visible_message(message = "[user] opens their mouth silently!", self_message = "You cannot say anything!", blind_message = emote_message_impaired)
+ M.visible_message(message = "[user] opens their mouth silently!", self_message = "You cannot say anything!", blind_message = emote_message_impaired, runemessage = "opens their mouth silently!")
return
else
- M.audible_message(message = use_3p, self_message = use_1p, deaf_message = emote_message_impaired, hearing_distance = use_range, radio_message = use_radio)
+ M.audible_message(message = use_3p, self_message = use_1p, deaf_message = emote_message_impaired, hearing_distance = use_range, radio_message = use_radio, runemessage = prefinal_3p)
else
- M.visible_message(message = use_3p, self_message = use_1p, blind_message = emote_message_impaired, range = use_range)
+ M.visible_message(message = use_3p, self_message = use_1p, blind_message = emote_message_impaired, range = use_range, runemessage = prefinal_3p)
do_extra(user, target)
do_sound(user)
diff --git a/code/modules/emotes/emote_mob.dm b/code/modules/emotes/emote_mob.dm
index cbc53019d86..3baccce8e32 100644
--- a/code/modules/emotes/emote_mob.dm
+++ b/code/modules/emotes/emote_mob.dm
@@ -86,7 +86,8 @@
return
if(use_emote.message_type == AUDIBLE_MESSAGE && is_muzzled())
- audible_message("\The [src] [use_emote.emote_message_muffled || "makes a muffled sound."]")
+ var/muffle_message = use_emote.emote_message_muffled || "makes a muffled sound."
+ audible_message("\The [src] [muffle_message]", runemessage = "* [muffle_message] *")
return
next_emote = world.time + use_emote.emote_delay
@@ -149,7 +150,7 @@
subtext = html_encode(subtext)
// Store the player's name in a nice bold, naturalement
nametext = "[emoter]"
- return pretext + nametext + subtext
+ return list("pretext" = pretext, "nametext" = nametext, "subtext" = subtext)
/mob/proc/custom_emote(var/m_type = VISIBLE_MESSAGE, var/message, var/range = world.view)
@@ -163,8 +164,14 @@
else
input = message
+ var/list/formatted
+ var/runemessage
if(input)
- message = format_emote(src, message)
+ formatted = format_emote(src, message)
+ message = formatted["pretext"] + formatted["nametext"] + formatted["subtext"]
+ runemessage = formatted["subtext"]
+ // This is just personal preference (but I'm objectively right) that custom emotes shouldn't have periods at the end in runechat
+ runemessage = replacetext(runemessage,".","",length(runemessage),length(runemessage)+1)
else
return
@@ -192,6 +199,7 @@
if(isobserver(M))
message = "[src] ([ghost_follow_link(src, M)]) [input]"
M.show_message(message, m_type)
+ M.create_chat_message(src, "[runemessage]", FALSE, list("emote"), (m_type == AUDIBLE_MESSAGE))
for(var/obj in o_viewers)
var/obj/O = obj
diff --git a/code/modules/food/food/snacks_vr.dm b/code/modules/food/food/snacks_vr.dm
index 6abc92094d7..6b9f15ff5af 100644
--- a/code/modules/food/food/snacks_vr.dm
+++ b/code/modules/food/food/snacks_vr.dm
@@ -176,7 +176,7 @@
/obj/item/weapon/reagent_containers/food/snacks/bugball
name = "bugball"
- desc = "A hard chitin, dont chip a tooth!"
+ desc = "A hard piece of chitin, don't chip a tooth!"
icon = 'icons/obj/food_vr.dmi'
icon_state = "pillbugball"
slice_path = /obj/item/weapon/reagent_containers/food/snacks/pillbug
@@ -250,7 +250,7 @@
/obj/item/weapon/reagent_containers/food/snacks/lobster
name = "raw lobster"
- desc = "a shifty lobster. You can try eating it, but its shell is extremely tough."
+ desc = "A shifty lobster. You can try eating it, but its shell is extremely tough."
icon = 'icons/obj/food_vr.dmi'
icon_state = "lobster_raw"
nutriment_amt = 5
@@ -261,7 +261,7 @@
/obj/item/weapon/reagent_containers/food/snacks/lobstercooked
name = "cooked lobster"
- desc = "a luxurious plate of cooked lobster, its taste accentuated by lemon juice. Reinvigorating!"
+ desc = "A luxurious plate of cooked lobster, its taste accentuated by lemon juice. Reinvigorating!"
icon = 'icons/obj/food_vr.dmi'
icon_state = "lobster_cooked"
trash = /obj/item/trash/plate
@@ -277,7 +277,7 @@
/obj/item/weapon/reagent_containers/food/snacks/cuttlefish
name = "raw cuttlefish"
- desc = "it's an adorable squid! you can't possible be thinking about eating this right?"
+ desc = "It's an adorable squid! You couldn't possibly be thinking about eating this, right?"
icon = 'icons/obj/food_vr.dmi'
icon_state = "cuttlefish_raw"
nutriment_amt = 5
@@ -288,7 +288,7 @@
/obj/item/weapon/reagent_containers/food/snacks/cuttlefishcooked
name = "cooked cuttlefish"
- desc = "it's a roasted cuttlefish. rubbery, squishy, an acquired taste."
+ desc = "It's a roasted cuttlefish. Rubbery, squishy, an acquired taste."
icon = 'icons/obj/food_vr.dmi'
icon_state = "cuttlefish_cooked"
nutriment_amt = 20
@@ -301,7 +301,7 @@
/obj/item/weapon/reagent_containers/food/snacks/sliceable/monkfish
name = "extra large monkfish"
- desc = "it's a huge monkfish. better clean it first, you can't possibly eat it like this."
+ desc = "It's a huge monkfish. Better clean it first, you can't possibly eat it like this."
icon = 'icons/obj/food48x48_vr.dmi'
icon_state = "monkfish_raw"
nutriment_amt = 30
@@ -316,7 +316,7 @@
/obj/item/weapon/reagent_containers/food/snacks/monkfishfillet
name = "monkfish fillet"
- desc = "it's a fillet sliced from a monkfish."
+ desc = "It's a fillet sliced from a monkfish."
icon = 'icons/obj/food_vr.dmi'
icon_state = "monkfish_fillet"
nutriment_amt = 5
@@ -328,7 +328,7 @@
/obj/item/weapon/reagent_containers/food/snacks/monkfishcooked
name = "seasoned monkfish"
- desc = "a delicious slice of monkfish prepared with sweet chili and spring onion."
+ desc = "A delicious slice of monkfish prepared with sweet chili and spring onion."
icon = 'icons/obj/food_vr.dmi'
icon_state = "monkfish_cooked"
nutriment_amt = 10
@@ -344,7 +344,7 @@
name = "monkfish remains"
icon = 'icons/obj/food_vr.dmi'
icon_state = "monkfish_remains"
- desc = "the work of a madman."
+ desc = "The work of a madman."
w_class = ITEMSIZE_LARGE
nutriment_amt = 10
slice_path = /obj/item/clothing/head/fish
@@ -357,7 +357,7 @@
/obj/item/weapon/reagent_containers/food/snacks/sliceable/sharkchunk
name = "chunk of shark meat"
- desc = "still rough, needs to be cut into even smaller chunks."
+ desc = "Still rough, needs to be cut into even smaller chunks."
icon = 'icons/obj/food_vr.dmi'
icon_state = "sharkmeat_chunk"
nutriment_amt = 15
@@ -371,8 +371,8 @@
reagents.add_reagent("protein", 20)
/obj/item/weapon/reagent_containers/food/snacks/carpmeat/fish/sharkmeat
- name = "a slice of sharkmeat"
- desc = "now it's small enough to cook with."
+ name = "slice of sharkmeat"
+ desc = "Now it's small enough to cook with."
icon = 'icons/obj/food_vr.dmi'
icon_state = "sharkmeat"
nutriment_amt = 2
@@ -385,7 +385,7 @@
/obj/item/weapon/reagent_containers/food/snacks/sharkmeatcooked
name = "shark steak"
- desc = "finally, some food for real men."
+ desc = "Finally, some food for real men."
icon = 'icons/obj/food_vr.dmi'
icon_state = "sharkmeat_cooked"
nutriment_amt = 5
@@ -399,7 +399,7 @@
/obj/item/weapon/reagent_containers/food/snacks/sharkmeatdip
name = "hot shark shank"
- desc = "a shank of shark meat dipped in hot sauce."
+ desc = "A shank of shark meat dipped in hot sauce."
icon = 'icons/obj/food_vr.dmi'
icon_state = "sharkmeat_dip"
nutriment_amt = 5
@@ -414,7 +414,7 @@
/obj/item/weapon/reagent_containers/food/snacks/sharkmeatcubes
name = "shark cubes"
- desc = "foul scented fermented shark cubes, it's said to make men fly, or just make them really fat."
+ desc = "Foul scented fermented shark cubes, it's said to make men fly, or just make them really fat."
icon = 'icons/obj/food_vr.dmi'
icon_state = "sharkmeat_cubes"
nutriment_amt = 8
@@ -616,7 +616,7 @@
/obj/item/weapon/reagent_containers/food/snacks/grub
name = "grub"
- desc = "a still writhing grub, soft and squishy."
+ desc = "A still writhing grub, soft and squishy."
icon = 'icons/obj/food_vr.dmi'
icon_state = "grub"
nutriment_amt = 3
@@ -629,7 +629,7 @@
/obj/item/weapon/reagent_containers/food/snacks/grub_pink
name = "pink candy grub"
- desc = "a thoroughly candied grub, it smells of raspberry."
+ desc = "A thoroughly candied grub, it smells of raspberry."
icon = 'icons/obj/food_vr.dmi'
icon_state = "grub_pink"
nutriment_amt = 5
@@ -641,7 +641,7 @@
/obj/item/weapon/reagent_containers/food/snacks/grub_purple
name = "pink candy grub"
- desc = "a thoroughly candied grub, it smells of grape."
+ desc = "A thoroughly candied grub, it smells of grape."
icon = 'icons/obj/food_vr.dmi'
icon_state = "grub_purple"
nutriment_amt = 5
@@ -653,7 +653,7 @@
/obj/item/weapon/reagent_containers/food/snacks/grub_blue
name = "pink candy grub"
- desc = "a thoroughly candied grub, it smells of blueberry."
+ desc = "A thoroughly candied grub, it smells of blueberry."
icon = 'icons/obj/food_vr.dmi'
icon_state = "grub_blue"
nutriment_amt = 5
@@ -665,7 +665,7 @@
/obj/item/weapon/reagent_containers/food/snacks/scorpion
name = "scorpion"
- desc = "a scorpion from the sandy deserts, don't get stung!"
+ desc = "A scorpion from the sandy deserts, don't get stung!"
icon = 'icons/obj/food_vr.dmi'
icon_state = "scorpion"
nutriment_amt = 8
@@ -677,7 +677,7 @@
/obj/item/weapon/reagent_containers/food/snacks/scorpion_cooked
name = "cooked scorpion"
- desc = "a scorpion baked nice and crispy"
+ desc = "A scorpion. Baked nice and crispy."
icon = 'icons/obj/food_vr.dmi'
icon_state = "scorpion_cooked"
nutriment_amt = 6
@@ -691,7 +691,7 @@
/obj/item/weapon/reagent_containers/food/snacks/ant
name = "giant honey ant"
- desc = "a sweetly scented honey ant. it has a huge swollen abdomen full of yummy."
+ desc = "A sweetly scented honey ant. It has a huge swollen abdomen full of yummy."
icon = 'icons/obj/food_vr.dmi'
icon_state = "honeyant"
nutriment_amt = 2
@@ -707,7 +707,7 @@
/obj/item/weapon/reagent_containers/food/snacks/antball
name = "giant honey ball"
- desc = "a sweetly scented honey ball, minus the ant. For those who don't like bug bits between their teeth."
+ desc = "A sweetly scented honey ball, minus the ant. For those who don't like bug bits between their teeth."
icon = 'icons/obj/food_vr.dmi'
icon_state = "honeyant_clean"
nutriment_amt = 4
@@ -720,7 +720,7 @@
/obj/item/weapon/reagent_containers/food/snacks/honey_candy
name = "honey candy"
- desc = "a clever mimicery of a honey ant abdomen, but it's just a piece of candy. Does not contain actual honey"
+ desc = "A clever mimicry of a honey ant abdomen, but it's just a piece of candy. Does not contain actual honey!"
icon = 'icons/obj/food_vr.dmi'
icon_state = "candy_honey"
nutriment_amt = 4
@@ -735,7 +735,7 @@
/obj/item/weapon/reagent_containers/food/snacks/locust
name = "yellow jacket locust"
- desc = "a vibrant bug that looks like a wasp, but is in fact a locust. Crunchy"
+ desc = "A vibrant bug that looks like a wasp, but is in fact a locust. Crunchy."
icon = 'icons/obj/food_vr.dmi'
icon_state = "locust"
nutriment_amt = 4
@@ -748,7 +748,7 @@
/obj/item/weapon/reagent_containers/food/snacks/locust_cooked
name = "fried locust"
- desc = "a fried locust, extremely crunchy"
+ desc = "A fried locust, extremely crunchy."
icon = 'icons/obj/food_vr.dmi'
icon_state = "locust_cooked"
nutriment_amt = 2
diff --git a/code/modules/integrated_electronics/subtypes/output.dm b/code/modules/integrated_electronics/subtypes/output.dm
index 39dae337848..56a6e543070 100644
--- a/code/modules/integrated_electronics/subtypes/output.dm
+++ b/code/modules/integrated_electronics/subtypes/output.dm
@@ -134,7 +134,7 @@
text = get_pin_data(IC_INPUT, 1)
if(!isnull(text))
var/obj/O = assembly ? loc : assembly
- audible_message("[bicon(O)] \The [O.name] states, \"[text]\"")
+ audible_message("[bicon(O)] \The [O.name] states, \"[text]\"", runemessage = text)
/obj/item/integrated_circuit/output/text_to_speech/advanced
name = "advanced text-to-speech circuit"
diff --git a/code/modules/mining/mine_turfs.dm b/code/modules/mining/mine_turfs.dm
index 10c6701a57c..fd41dc7e137 100644
--- a/code/modules/mining/mine_turfs.dm
+++ b/code/modules/mining/mine_turfs.dm
@@ -240,8 +240,10 @@ turf/simulated/mineral/floor/light_corner
spawn(1) // Otherwise most of the ore is lost to the explosion, which makes this rather moot.
for(var/ore in resources)
var/amount_to_give = rand(CEILING(resources[ore]/2, 1), resources[ore]) // Should result in at least one piece of ore.
+ var/oretype = ore_types[ore]
+ if(!oretype)
+ return // this turf can't give that type
for(var/i=1, i <= amount_to_give, i++)
- var/oretype = ore_types[ore]
new oretype(src)
resources[ore] = 0
diff --git a/code/modules/mob/dead/observer/free_vr.dm b/code/modules/mob/dead/observer/free_vr.dm
index dca361725f6..6cbb09900c4 100644
--- a/code/modules/mob/dead/observer/free_vr.dm
+++ b/code/modules/mob/dead/observer/free_vr.dm
@@ -32,12 +32,8 @@ var/global/list/prevent_respawns = list()
qdel(O)
//Resleeving cleanup
- if(src.mind.name in SStranscore.backed_up)
- var/datum/transhuman/mind_record/MR = SStranscore.backed_up[src.mind.name]
- SStranscore.stop_backup(MR)
- if(src.mind.name in SStranscore.body_scans) //This uses mind names to avoid people cryo'ing a printed body to delete body scans.
- var/datum/transhuman/body_record/BR = SStranscore.body_scans[src.mind.name]
- SStranscore.remove_body(BR)
+ if(mind)
+ SStranscore.leave_round(src)
//Job slot cleanup
var/job = src.mind.assigned_role
diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm
index 865952471be..c4484d40072 100644
--- a/code/modules/mob/dead/observer/observer.dm
+++ b/code/modules/mob/dead/observer/observer.dm
@@ -481,6 +481,9 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
/mob/proc/update_following()
. = get_turf(src)
for(var/mob/observer/dead/M in following_mobs)
+ if(!.)
+ M.stop_following()
+
if(M.following != src)
following_mobs -= M
else
diff --git a/code/modules/mob/dead/observer/observer_vr.dm b/code/modules/mob/dead/observer/observer_vr.dm
index 2e54e6414e4..636db5e3470 100644
--- a/code/modules/mob/dead/observer/observer_vr.dm
+++ b/code/modules/mob/dead/observer/observer_vr.dm
@@ -62,18 +62,22 @@
set name = "Notify Transcore"
set desc = "If your past-due backup notification was missed or ignored, you can use this to send a new one."
- if(src.mind && (src.mind.name in SStranscore.backed_up))
- var/datum/transhuman/mind_record/record = SStranscore.backed_up[src.mind.name]
+ if(!mind)
+ to_chat(src,"Your ghost is missing game values that allow this functionality, sorry.")
+ return
+ var/datum/transcore_db/db = SStranscore.db_by_mind_name(mind.name)
+ if(db)
+ var/datum/transhuman/mind_record/record = db.backed_up[src.mind.name]
if(!(record.dead_state == MR_DEAD))
to_chat(src, "Your backup is not past-due yet.")
else if((world.time - record.last_notification) < 10 MINUTES)
to_chat(src, "Too little time has passed since your last notification.")
else
- SStranscore.notify(record.mindname, TRUE)
+ db.notify(record.mindname, TRUE)
record.last_notification = world.time
to_chat(src, "New notification has been sent.")
else
- to_chat(src, "No mind record found!")
+ to_chat(src,"No backup record could be found, sorry.")
/mob/observer/dead/verb/findghostpod() //Moves the ghost instead of just changing the ghosts's eye -Nodrak
set category = "Ghost"
diff --git a/code/modules/mob/hear_say.dm b/code/modules/mob/hear_say.dm
index c5685aa0dcc..18cd0ab7503 100644
--- a/code/modules/mob/hear_say.dm
+++ b/code/modules/mob/hear_say.dm
@@ -2,7 +2,9 @@
/mob/proc/combine_message(var/list/message_pieces, var/verb, var/mob/speaker, always_stars = FALSE, var/radio = FALSE)
var/iteration_count = 0
var/msg = "" // This is to make sure that the pieces have actually added something
- . = "[verb], \""
+ var/raw_msg = ""
+ . = list("formatted" = "[verb], \"", "raw" = "")
+
for(var/datum/multilingual_say_piece/SP in message_pieces)
iteration_count++
var/piece = SP.message
@@ -11,9 +13,13 @@
if(SP.speaking && SP.speaking.flags & INNATE) // Snowflake for noise lang
if(radio)
- return SP.speaking.format_message_radio(piece)
+ .["formatted"] = SP.speaking.format_message_radio(piece)
+ .["raw"] = piece
+ return
else
- return SP.speaking.format_message(piece)
+ .["formatted"] = SP.speaking.format_message(piece)
+ .["raw"] = piece
+ return
if(iteration_count == 1)
piece = capitalize(piece)
@@ -27,6 +33,9 @@
if(istype(S.say_list) && length(S.say_list.speak))
piece = pick(S.say_list.speak)
+ raw_msg += (piece + " ")
+
+ //HTML formatting
if(!SP.speaking) // Catch the most generic case first
piece = "[piece]"
else if(radio) // SP.speaking == TRUE enforced by previous !SP.speaking
@@ -38,10 +47,11 @@
if(msg == "")
// There is literally no content left in this message, we need to shut this shit down
- . = "" // hear_say will suppress it
+ .["formatted"] = "" // hear_say will suppress it
else
- . = trim(. + trim(msg))
- . += "\""
+ .["formatted"] = trim(.["formatted"] + trim(msg))
+ .["formatted"] += "\""
+ .["raw"] = trim(raw_msg)
/mob/proc/saypiece_scramble(datum/multilingual_say_piece/SP)
if(SP.speaking)
@@ -76,7 +86,8 @@
var/mob/living/carbon/human/H = speaker
speaker_name = H.GetVoice()
- var/message = combine_message(message_pieces, verb, speaker)
+ var/list/combined = combine_message(message_pieces, verb, speaker)
+ var/message = combined["formatted"]
if(message == "")
return
@@ -109,6 +120,7 @@
message_to_send = "[message_to_send]"
on_hear_say(message_to_send)
+ create_chat_message(speaker, combined["raw"], italics, list())
if(speech_sound && (get_dist(speaker, src) <= world.view && z == speaker.z))
var/turf/source = speaker ? get_turf(speaker) : get_turf(src)
@@ -164,7 +176,8 @@
if(!client)
return
- var/message = combine_message(message_pieces, verb, speaker, always_stars = hard_to_hear, radio = TRUE)
+ var/list/combined = combine_message(message_pieces, verb, speaker, always_stars = hard_to_hear, radio = TRUE)
+ var/message = combined["formatted"]
if(sleeping || stat == UNCONSCIOUS) //If unconscious or sleeping
hear_sleep(multilingual_to_message(message_pieces))
return
@@ -272,7 +285,8 @@
return
/mob/proc/hear_holopad_talk(list/message_pieces, var/verb = "says", var/mob/speaker = null)
- var/message = combine_message(message_pieces, verb, speaker)
+ var/list/combined = combine_message(message_pieces, verb, speaker)
+ var/message = combined["formatted"]
var/name = speaker.name
if(!say_understands(speaker))
diff --git a/code/modules/mob/living/carbon/human/life_vr.dm b/code/modules/mob/living/carbon/human/life_vr.dm
index c5248fc8624..fc4f6134189 100644
--- a/code/modules/mob/living/carbon/human/life_vr.dm
+++ b/code/modules/mob/living/carbon/human/life_vr.dm
@@ -38,14 +38,14 @@
for(var/obj/item/organ/external/E in organs)
for(var/obj/item/weapon/implant/I in E.implants)
- if(I.implanted)
- if(istype(I,/obj/item/weapon/implant/backup))
- if(!mind)
- holder.icon_state = "hud_backup_nomind"
- else if(!(mind.name in SStranscore.body_scans))
- holder.icon_state = "hud_backup_nobody"
- else
- holder.icon_state = "hud_backup_norm"
+ if(I.implanted && istype(I,/obj/item/weapon/implant/backup))
+ var/obj/item/weapon/implant/backup/B = I
+ if(!mind)
+ holder.icon_state = "hud_backup_nomind"
+ else if(!(mind.name in B.our_db.body_scans))
+ holder.icon_state = "hud_backup_nobody"
+ else
+ holder.icon_state = "hud_backup_norm"
apply_hud(BACKUP_HUD, holder)
diff --git a/code/modules/mob/living/say.dm b/code/modules/mob/living/say.dm
index ae1b09ae714..5686993d7af 100644
--- a/code/modules/mob/living/say.dm
+++ b/code/modules/mob/living/say.dm
@@ -1,57 +1,63 @@
var/list/department_radio_keys = list(
- ":r" = "right ear", ".r" = "right ear",
- ":l" = "left ear", ".l" = "left ear",
- ":i" = "intercom", ".i" = "intercom",
- ":h" = "department", ".h" = "department",
- ":+" = "special", ".+" = "special", //activate radio-specific special functions
- ":c" = "Command", ".c" = "Command",
- ":n" = "Science", ".n" = "Science",
- ":m" = "Medical", ".m" = "Medical",
- ":e" = "Engineering", ".e" = "Engineering",
- ":k" = "Response Team", ".k" = "Response Team",
- ":s" = "Security", ".s" = "Security",
- ":w" = "whisper", ".w" = "whisper",
- ":t" = "Mercenary", ".t" = "Mercenary",
- ":x" = "Raider", ".x" = "Raider",
- ":u" = "Supply", ".u" = "Supply",
- ":v" = "Service", ".v" = "Service",
- ":p" = "AI Private", ".p" = "AI Private",
- ":y" = "Explorer", ".y" = "Explorer",
- ":a" = "Talon", ".a" = "Talon", //VOREStation Add,
+ ":r" = "right ear", ".r" = "right ear",
+ ":l" = "left ear", ".l" = "left ear",
+ ":i" = "intercom", ".i" = "intercom",
+ ":h" = "department", ".h" = "department",
+ ":+" = "special", ".+" = "special", //activate radio-specific special functions
+ ":c" = "Command", ".c" = "Command",
+ ":n" = "Science", ".n" = "Science",
+ ":m" = "Medical", ".m" = "Medical",
+ ":e" = "Engineering", ".e" = "Engineering",
+ ":k" = "Response Team", ".k" = "Response Team",
+ ":s" = "Security", ".s" = "Security",
+ ":w" = "whisper", ".w" = "whisper",
+ ":t" = "Mercenary", ".t" = "Mercenary",
+ ":x" = "Raider", ".x" = "Raider",
+ ":u" = "Supply", ".u" = "Supply",
+ ":v" = "Service", ".v" = "Service",
+ ":p" = "AI Private", ".p" = "AI Private",
+ ":y" = "Explorer", ".y" = "Explorer",
+ ":a" = "Talon", ".a" = "Talon", //VOREStation Add,
- ":R" = "right ear", ".R" = "right ear",
- ":L" = "left ear", ".L" = "left ear",
- ":I" = "intercom", ".I" = "intercom",
- ":H" = "department", ".H" = "department",
- ":C" = "Command", ".C" = "Command",
- ":N" = "Science", ".N" = "Science",
- ":M" = "Medical", ".M" = "Medical",
- ":E" = "Engineering", ".E" = "Engineering",
- ":k" = "Response Team", ".k" = "Response Team",
- ":S" = "Security", ".S" = "Security",
- ":W" = "whisper", ".W" = "whisper",
- ":T" = "Mercenary", ".T" = "Mercenary",
- ":X" = "Raider", ".X" = "Raider",
- ":U" = "Supply", ".U" = "Supply",
- ":V" = "Service", ".V" = "Service",
- ":P" = "AI Private", ".P" = "AI Private",
- ":Y" = "Explorer", ".Y" = "Explorer",
- ":A" = "Talon", ".A" = "Talon", //VOREStation Add,
+ ":R" = "right ear", ".R" = "right ear",
+ ":L" = "left ear", ".L" = "left ear",
+ ":I" = "intercom", ".I" = "intercom",
+ ":H" = "department", ".H" = "department",
+ ":C" = "Command", ".C" = "Command",
+ ":N" = "Science", ".N" = "Science",
+ ":M" = "Medical", ".M" = "Medical",
+ ":E" = "Engineering", ".E" = "Engineering",
+ ":k" = "Response Team", ".k" = "Response Team",
+ ":S" = "Security", ".S" = "Security",
+ ":W" = "whisper", ".W" = "whisper",
+ ":T" = "Mercenary", ".T" = "Mercenary",
+ ":X" = "Raider", ".X" = "Raider",
+ ":U" = "Supply", ".U" = "Supply",
+ ":V" = "Service", ".V" = "Service",
+ ":P" = "AI Private", ".P" = "AI Private",
+ ":Y" = "Explorer", ".Y" = "Explorer",
+ ":A" = "Talon", ".A" = "Talon", //VOREStation Add,
- //kinda localization -- rastaf0
- //same keys as above, but on russian keyboard layout. This file uses cp1251 as encoding.
- ":ê" = "right ear", ".ê" = "right ear",
- ":ä" = "left ear", ".ä" = "left ear",
- ":ø" = "intercom", ".ø" = "intercom",
- ":ð" = "department", ".ð" = "department",
- ":ñ" = "Command", ".ñ" = "Command",
- ":ò" = "Science", ".ò" = "Science",
- ":ü" = "Medical", ".ü" = "Medical",
- ":ó" = "Engineering", ".ó" = "Engineering",
- ":û" = "Security", ".û" = "Security",
- ":ö" = "whisper", ".ö" = "whisper",
- ":å" = "Mercenary", ".å" = "Mercenary",
- ":é" = "Supply", ".é" = "Supply",
+ // Cyrillic characters on the same keys on the Russian QWERTY (phonetic) layout
+ ":к" = "right ear", ".к" = "right ear",
+ ":д" = "left ear", ".д" = "left ear",
+ ":ш" = "intercom", ".ш" = "intercom",
+ ":Ñ€" = "department", ".Ñ€" = "department",
+ ":+" = "special", ".+" = "special", //activate radio-specific special functions
+ ":Ñ" = "Command", ".Ñ" = "Command",
+ ":Ñ‚" = "Science", ".Ñ‚" = "Science",
+ ":ь" = "Medical", ".ь" = "Medical",
+ ":у" = "Engineering", ".у" = "Engineering",
+ ":л" = "Response Team", ".л" = "Response Team",
+ ":Ñ‹" = "Security", ".Ñ‹" = "Security",
+ ":ц" = "whisper", ".ц" = "whisper",
+ ":е" = "Mercenary", ".е" = "Mercenary",
+ ":ч" = "Raider", ".ч" = "Raider",
+ ":г" = "Supply", ".г" = "Supply",
+ ":м" = "Service", ".м" = "Service",
+ ":з" = "AI Private", ".з" = "AI Private",
+ ":н" = "Explorer", ".н" = "Explorer",
+ ":Ñ„" = "Talon", ".Ñ„" = "Talon" //VOREStation Add
)
@@ -362,16 +368,17 @@ proc/get_radio_key_from_channel(var/channel)
//VOREStation Add End
var/dst = get_dist(get_turf(M),get_turf(src))
+ var/runechat_enabled = M.client?.is_preference_enabled(/datum/client_preference/runechat_mob)
if(dst <= message_range || (M.stat == DEAD && !forbid_seeing_deadchat)) //Inside normal message range, or dead with ears (handled in the view proc)
- if(M.client)
+ if(M.client && !runechat_enabled)
var/image/I1 = listening[M] || speech_bubble
images_to_clients[I1] |= M.client
M << I1
M.hear_say(message_pieces, verb, italics, src, speech_sound, sound_vol)
if(whispering && !isobserver(M)) //Don't even bother with these unless whispering
if(dst > message_range && dst <= w_scramble_range) //Inside whisper scramble range
- if(M.client)
+ if(M.client && !runechat_enabled)
var/image/I2 = listening[M] || speech_bubble
images_to_clients[I2] |= M.client
M << I2
diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm
index f3cdc716e32..a4789aa7250 100644
--- a/code/modules/mob/living/silicon/ai/ai.dm
+++ b/code/modules/mob/living/silicon/ai/ai.dm
@@ -1,1002 +1,1003 @@
-#define AI_CHECK_WIRELESS 1
-#define AI_CHECK_RADIO 2
-
-var/list/ai_verbs_default = list(
- // /mob/living/silicon/ai/proc/ai_recall_shuttle,
- /mob/living/silicon/ai/proc/ai_emergency_message,
- /mob/living/silicon/ai/proc/ai_goto_location,
- /mob/living/silicon/ai/proc/ai_remove_location,
- /mob/living/silicon/ai/proc/ai_hologram_change,
- /mob/living/silicon/ai/proc/ai_network_change,
- /mob/living/silicon/ai/proc/ai_statuschange,
- /mob/living/silicon/ai/proc/ai_store_location,
- /mob/living/silicon/ai/proc/control_integrated_radio,
- /mob/living/silicon/ai/proc/pick_icon,
- /mob/living/silicon/ai/proc/sensor_mode,
- /mob/living/silicon/ai/proc/show_laws_verb,
- /mob/living/silicon/ai/proc/toggle_acceleration,
- /mob/living/silicon/ai/proc/toggle_hologram_movement,
- /mob/living/silicon/ai/proc/ai_announcement,
- /mob/living/silicon/ai/proc/ai_call_shuttle,
- /mob/living/silicon/ai/proc/ai_camera_track,
- /mob/living/silicon/ai/proc/ai_camera_list,
- /mob/living/silicon/ai/proc/ai_checklaws,
- /mob/living/silicon/ai/proc/toggle_camera_light,
- /mob/living/silicon/ai/proc/take_image,
- /mob/living/silicon/ai/proc/view_images,
- /mob/living/silicon/ai/proc/toggle_multicam_verb,
- /mob/living/silicon/ai/proc/add_multicam_verb
-)
-
-//Not sure why this is necessary...
-/proc/AutoUpdateAI(obj/subject)
- var/is_in_use = 0
- if (subject!=null)
- for(var/A in ai_list)
- var/mob/living/silicon/ai/M = A
- if ((M.client && M.machine == subject))
- is_in_use = 1
- subject.attack_ai(M)
- return is_in_use
-
-
-/mob/living/silicon/ai
- name = "AI"
- icon = 'icons/mob/AI.dmi'//
- icon_state = "ai"
- anchored = 1 // -- TLE
- density = 1
- status_flags = CANSTUN|CANPARALYSE|CANPUSH
- shouldnt_see = list(/mob/observer/eye, /obj/effect/rune)
- var/list/network = list(NETWORK_DEFAULT)
- var/obj/machinery/camera/camera = null
- var/aiRestorePowerRoutine = 0
- var/viewalerts = 0
- var/icon/holo_icon//Default is assigned when AI is created.
- var/list/connected_robots = list()
- var/obj/item/device/pda/ai/aiPDA = null
- var/obj/item/device/communicator/aiCommunicator = null
- var/obj/item/device/multitool/aiMulti = null
- var/obj/item/device/radio/headset/heads/ai_integrated/aiRadio = null
- var/camera_light_on = 0 //Defines if the AI toggled the light on the camera it's looking through.
- var/datum/trackable/track = null
- var/last_announcement = ""
- var/control_disabled = 0
- var/datum/announcement/priority/announcement
- var/obj/machinery/ai_powersupply/psupply = null // Backwards reference to AI's powersupply object.
- var/hologram_follow = 1 //This is used for the AI eye, to determine if a holopad's hologram should follow it or not.
- var/is_dummy = 0 //Used to prevent dummy AIs from spawning with communicators.
- //NEWMALF VARIABLES
- var/malfunctioning = 0 // Master var that determines if AI is malfunctioning.
- var/datum/malf_hardware/hardware = null // Installed piece of hardware.
- var/datum/malf_research/research = null // Malfunction research datum.
- var/obj/machinery/power/apc/hack = null // APC that is currently being hacked.
- var/list/hacked_apcs = null // List of all hacked APCs
- var/APU_power = 0 // If set to 1 AI runs on APU power
- var/hacking = 0 // Set to 1 if AI is hacking APC, cyborg, other AI, or running system override.
- var/system_override = 0 // Set to 1 if system override is initiated, 2 if succeeded.
- var/hack_can_fail = 1 // If 0, all abilities have zero chance of failing.
- var/hack_fails = 0 // This increments with each failed hack, and determines the warning message text.
- var/errored = 0 // Set to 1 if runtime error occurs. Only way of this happening i can think of is admin fucking up with varedit.
- var/bombing_core = 0 // Set to 1 if core auto-destruct is activated
- var/bombing_station = 0 // Set to 1 if station nuke auto-destruct is activated
- var/override_CPUStorage = 0 // Bonus/Penalty CPU Storage. For use by admins/testers.
- var/override_CPURate = 0 // Bonus/Penalty CPU generation rate. For use by admins/testers.
-
- var/datum/ai_icon/selected_sprite // The selected icon set
- var/custom_sprite = 0 // Whether the selected icon is custom
- var/carded
-
- // Multicam Vars
- var/multicam_allowed = TRUE
- var/multicam_on = FALSE
- var/obj/screen/movable/pic_in_pic/ai/master_multicam
- var/list/multicam_screens = list()
- var/list/all_eyes = list()
- var/max_multicams = 6
-
- can_be_antagged = TRUE
-
-/mob/living/silicon/ai/proc/add_ai_verbs()
- src.verbs |= ai_verbs_default
- src.verbs |= silicon_subsystems
-
-/mob/living/silicon/ai/proc/remove_ai_verbs()
- src.verbs -= ai_verbs_default
- src.verbs -= silicon_subsystems
-
-/mob/living/silicon/ai/New(loc, var/datum/ai_laws/L, var/obj/item/device/mmi/B, var/safety = 0)
- announcement = new()
- announcement.title = "A.I. Announcement"
- announcement.announcement_type = "A.I. Announcement"
- announcement.newscast = 1
-
- var/list/possibleNames = ai_names
-
- var/pickedName = null
- while(!pickedName)
- pickedName = pick(ai_names)
- for (var/mob/living/silicon/ai/A in mob_list)
- if (A.real_name == pickedName && possibleNames.len > 1) //fixing the theoretically possible infinite loop
- possibleNames -= pickedName
- pickedName = null
-
- if(!is_dummy)
- aiPDA = new/obj/item/device/pda/ai(src)
- SetName(pickedName)
- anchored = 1
- canmove = 0
- density = 1
- loc = loc
-
- if(!is_dummy)
- aiCommunicator = new /obj/item/device/communicator/integrated(src)
-
- holo_icon = getHologramIcon(icon('icons/mob/AI.dmi',"holo1"))
-
- proc_holder_list = new()
-
- if(L)
- if (istype(L, /datum/ai_laws))
- laws = L
- else
- laws = new using_map.default_law_type
-
- aiMulti = new(src)
- aiRadio = new(src)
- common_radio = aiRadio
- aiRadio.myAi = src
- additional_law_channels["Binary"] = "#b"
- additional_law_channels["Holopad"] = ":h"
-
- aiCamera = new/obj/item/device/camera/siliconcam/ai_camera(src)
-
- if (istype(loc, /turf))
- add_ai_verbs(src)
-
- //Languages
- add_language("Robot Talk", 1)
- add_language(LANGUAGE_GALCOM, 1)
- add_language(LANGUAGE_SOL_COMMON, 1)
- add_language(LANGUAGE_UNATHI, 1)
- add_language(LANGUAGE_SIIK, 1)
- add_language(LANGUAGE_AKHANI, 1)
- add_language(LANGUAGE_SKRELLIAN, 1)
- add_language(LANGUAGE_SKRELLIANFAR, 0)
- add_language(LANGUAGE_TRADEBAND, 1)
- add_language(LANGUAGE_GUTTER, 1)
- add_language(LANGUAGE_EAL, 1)
- add_language(LANGUAGE_SCHECHI, 1)
- add_language(LANGUAGE_SIGN, 1)
- add_language(LANGUAGE_ROOTLOCAL, 1)
- add_language(LANGUAGE_TERMINUS, 1)
- add_language(LANGUAGE_ZADDAT, 1)
-
- if(!safety)//Only used by AIize() to successfully spawn an AI.
- if (!B)//If there is no player/brain inside.
- empty_playable_ai_cores += new/obj/structure/AIcore/deactivated(loc)//New empty terminal.
- qdel(src)//Delete AI.
- return
- else
- if (B.brainmob.mind)
- B.brainmob.mind.transfer_to(src)
-
- on_mob_init()
-
- spawn(5)
- new /obj/machinery/ai_powersupply(src)
-
- ai_list += src
- ..()
- return
-
-/mob/living/silicon/ai/proc/on_mob_init()
- to_chat(src, "You are playing the station's AI. The AI cannot move, but can interact with many objects while viewing them (through cameras).")
- to_chat(src, "To look at other parts of the station, click on yourself to get a camera menu.")
- to_chat(src, "While observing through a camera, you can use most (networked) devices which you can see, such as computers, APCs, intercoms, doors, etc.")
- to_chat(src, "To use something, simply click on it.")
- to_chat(src, "Use say #b to speak to your cyborgs through binary. Use say :h to speak from an active holopad.")
- to_chat(src, "For department channels, use the following say commands:")
-
- var/radio_text = ""
- for(var/i = 1 to common_radio.channels.len)
- var/channel = common_radio.channels[i]
- var/key = get_radio_key_from_channel(channel)
- radio_text += "[key] - [channel]"
- if(i != common_radio.channels.len)
- radio_text += ", "
-
- to_chat(src,radio_text)
-
- // Vorestation Edit: Meta Info for AI's. Mostly used for Holograms
- if (client)
- var/meta_info = client.prefs.metadata
- if (meta_info)
- ooc_notes = meta_info
-
- if (malf && !(mind in malf.current_antagonists))
- show_laws()
- to_chat(src, "These laws may be changed by other players, or by you being the traitor.")
-
- job = "AI"
- setup_icon()
-
-/mob/living/silicon/ai/Destroy()
- ai_list -= src
-
- QDEL_NULL(announcement)
- QDEL_NULL(eyeobj)
- QDEL_NULL(psupply)
- QDEL_NULL(aiPDA)
- QDEL_NULL(aiCommunicator)
- QDEL_NULL(aiMulti)
- QDEL_NULL(aiRadio)
- QDEL_NULL(aiCamera)
- hack = null
-
- return ..()
-
-/mob/living/silicon/ai/Stat()
- ..()
- if(statpanel("Status"))
- if(!stat) // Make sure we're not unconscious/dead.
- stat(null, text("System integrity: [(health+100)/2]%"))
- stat(null, text("Connected synthetics: [connected_robots.len]"))
- for(var/mob/living/silicon/robot/R in connected_robots)
- var/robot_status = "Nominal"
- if(R.shell)
- robot_status = "AI SHELL"
- else if(R.stat || !R.client)
- robot_status = "OFFLINE"
- else if(!R.cell || R.cell.charge <= 0)
- robot_status = "DEPOWERED"
- //Name, Health, Battery, Module, Area, and Status! Everything an AI wants to know about its borgies!
- stat(null, text("[R.name] | S.Integrity: [R.health]% | Cell: [R.cell ? "[R.cell.charge]/[R.cell.maxcharge]" : "Empty"] | \
- Module: [R.modtype] | Loc: [get_area_name(R, TRUE)] | Status: [robot_status]"))
- stat(null, text("AI shell beacons detected: [LAZYLEN(GLOB.available_ai_shells)]")) //Count of total AI shells
- else
- stat(null, text("Systems nonfunctional"))
-
-
-/mob/living/silicon/ai/proc/setup_icon()
- var/file = file2text("config/custom_sprites.txt")
- var/lines = splittext(file, "\n")
-
- for(var/line in lines)
- // split & clean up
- var/list/Entry = splittext(line, ":")
- for(var/i = 1 to Entry.len)
- Entry[i] = trim(Entry[i])
-
- if(Entry.len < 2)
- continue;
-
- if(Entry[1] == src.ckey && Entry[2] == src.real_name)
- icon = CUSTOM_ITEM_SYNTH
- custom_sprite = 1
- selected_sprite = new/datum/ai_icon("Custom", "[src.ckey]-ai", "4", "[ckey]-ai-crash", "#FFFFFF", "#FFFFFF", "#FFFFFF")
- else
- selected_sprite = default_ai_icon
- updateicon()
-
-/mob/living/silicon/ai/pointed(atom/A as mob|obj|turf in view())
- set popup_menu = 0
- set src = usr.contents
- return 0
-
-/mob/living/silicon/ai/SetName(pickedName as text)
- ..()
- announcement.announcer = pickedName
- if(eyeobj)
- eyeobj.name = "[pickedName] (AI Eye)"
-
- // Set ai pda name
- if(aiPDA)
- aiPDA.ownjob = "AI"
- aiPDA.owner = pickedName
- aiPDA.name = pickedName + " (" + aiPDA.ownjob + ")"
-
- if(aiCommunicator)
- aiCommunicator.register_device(src.name)
-
-/*
- The AI Power supply is a dummy object used for powering the AI since only machinery should be using power.
- The alternative was to rewrite a bunch of AI code instead here we are.
-*/
-/obj/machinery/ai_powersupply
- name="Power Supply"
- active_power_usage=50000 // Station AIs use significant amounts of power. This, when combined with charged SMES should mean AI lasts for 1hr without external power.
- use_power = USE_POWER_ACTIVE
- power_channel = EQUIP
- var/mob/living/silicon/ai/powered_ai = null
- invisibility = 100
-
-/obj/machinery/ai_powersupply/New(var/mob/living/silicon/ai/ai=null)
- powered_ai = ai
- powered_ai.psupply = src
- if(istype(powered_ai,/mob/living/silicon/ai/announcer)) //Don't try to get a loc for a nullspace announcer mob, just put it into it
- forceMove(powered_ai)
- else
- forceMove(powered_ai.loc)
-
- ..()
- use_power(1) // Just incase we need to wake up the power system.
-
-/obj/machinery/ai_powersupply/Destroy()
- . = ..()
- powered_ai = null
-
-/obj/machinery/ai_powersupply/process()
- if(!powered_ai || powered_ai.stat == DEAD)
- qdel(src)
- return
- if(powered_ai.psupply != src) // For some reason, the AI has different powersupply object. Delete this one, it's no longer needed.
- qdel(src)
- return
- if(powered_ai.APU_power)
- update_use_power(USE_POWER_OFF)
- return
- if(!powered_ai.anchored)
- loc = powered_ai.loc
- update_use_power(USE_POWER_OFF)
- use_power(50000) // Less optimalised but only called if AI is unwrenched. This prevents usage of wrenching as method to keep AI operational without power. Intellicard is for that.
- if(powered_ai.anchored)
- update_use_power(USE_POWER_ACTIVE)
-
-/mob/living/silicon/ai/proc/pick_icon()
- set category = "AI Settings"
- set name = "Set AI Core Display"
- if(stat || aiRestorePowerRoutine)
- return
-
- if (!custom_sprite)
- var/new_sprite = input("Select an icon!", "AI", selected_sprite) as null|anything in ai_icons
- if(new_sprite) selected_sprite = new_sprite
- updateicon()
-
-/mob/living/silicon/ai/var/message_cooldown = 0
-/mob/living/silicon/ai/proc/ai_announcement()
- set category = "AI Commands"
- set name = "Make Station Announcement"
- if(check_unable(AI_CHECK_WIRELESS | AI_CHECK_RADIO))
- return
-
- if(message_cooldown)
- to_chat(src, "Please allow one minute to pass between announcements.")
- return
- var/input = input(usr, "Please write a message to announce to the station crew.", "A.I. Announcement")
- if(!input)
- return
-
- if(check_unable(AI_CHECK_WIRELESS | AI_CHECK_RADIO))
- return
-
- announcement.Announce(input)
- message_cooldown = 1
- spawn(600)//One minute cooldown
- message_cooldown = 0
-
-/mob/living/silicon/ai/proc/ai_call_shuttle()
- set category = "AI Commands"
- set name = "Call Emergency Shuttle"
- if(check_unable(AI_CHECK_WIRELESS))
- return
-
- var/confirm = alert("Are you sure you want to call the shuttle?", "Confirm Shuttle Call", "Yes", "No")
-
- if(check_unable(AI_CHECK_WIRELESS))
- return
-
- if(confirm == "Yes")
- call_shuttle_proc(src)
-
- // hack to display shuttle timer
- if(emergency_shuttle.online())
- post_status(src, "shuttle", user = src)
-
-/mob/living/silicon/ai/proc/ai_recall_shuttle()
- set category = "AI Commands"
- set name = "Recall Emergency Shuttle"
-
- if(check_unable(AI_CHECK_WIRELESS))
- return
-
- var/confirm = alert("Are you sure you want to recall the shuttle?", "Confirm Shuttle Recall", "Yes", "No")
- if(check_unable(AI_CHECK_WIRELESS))
- return
-
- if(confirm == "Yes")
- cancel_call_proc(src)
-
-/mob/living/silicon/ai/var/emergency_message_cooldown = 0
-
-/mob/living/silicon/ai/proc/ai_emergency_message()
- set category = "AI Commands"
- set name = "Send Emergency Message"
-
- if(check_unable(AI_CHECK_WIRELESS))
- return
- if(emergency_message_cooldown)
- to_chat(usr, "Arrays recycling. Please stand by.")
- return
- var/input = sanitize(input(usr, "Please choose a message to transmit to [using_map.boss_short] via quantum entanglement. Please be aware that this process is very expensive, and abuse will lead to... termination. Transmission does not guarantee a response. There is a 30 second delay before you may send another message, be clear, full and concise.", "To abort, send an empty message.", ""))
- if(!input)
- return
- CentCom_announce(input, usr)
- to_chat(usr, "Message transmitted.")
- log_game("[key_name(usr)] has made an IA [using_map.boss_short] announcement: [input]")
- emergency_message_cooldown = 1
- spawn(300)
- emergency_message_cooldown = 0
-/mob/living/silicon/ai/check_eye(var/mob/user as mob)
- if (!camera)
- return -1
- return 0
-
-/mob/living/silicon/ai/restrained()
- return 0
-
-/mob/living/silicon/ai/emp_act(severity)
- disconnect_shell("Disconnected from remote shell due to ionic interfe%*@$^___")
- if (prob(30))
- view_core()
- ..()
-
-/mob/living/silicon/ai/Topic(href, href_list)
- if(..()) //VOREstation edit: So the AI can actually can actually get its OOC prefs read
- return
- if(usr != src)
- return
- /*if(..()) // <------ MOVED FROM HERE
- return*/
- if (href_list["mach_close"])
- if (href_list["mach_close"] == "aialerts")
- viewalerts = 0
- var/t1 = text("window=[]", href_list["mach_close"])
- unset_machine()
- src << browse(null, t1)
- if (href_list["switchcamera"])
- switchCamera(locate(href_list["switchcamera"])) in cameranet.cameras
- if (href_list["showalerts"])
- subsystem_alarm_monitor()
- //Carn: holopad requests
- if (href_list["jumptoholopad"])
- var/obj/machinery/hologram/holopad/H = locate(href_list["jumptoholopad"])
- if(stat == CONSCIOUS)
- if(H)
- H.attack_ai(src) //may as well recycle
- else
- to_chat(src, "Unable to locate the holopad.")
-
- if (href_list["track"])
- var/mob/target = locate(href_list["track"]) in mob_list
-
- if(target && (!istype(target, /mob/living/carbon/human) || html_decode(href_list["trackname"]) == target:get_face_name()))
- ai_actual_track(target)
- else
- to_chat(src, "System error. Cannot locate [html_decode(href_list["trackname"])].")
- return
-
- if(href_list["trackbot"])
- var/mob/living/bot/target = locate(href_list["trackbot"]) in mob_list
- if(target)
- ai_actual_track(target)
- else
- to_chat(src, "Target is not on or near any active cameras on the station.")
- return
-
- if(href_list["open"])
- var/mob/target = locate(href_list["open"]) in mob_list
- if(target)
- open_nearest_door(target)
-
- return
-
-/mob/living/silicon/ai/proc/camera_visibility(mob/observer/eye/aiEye/moved_eye)
- cameranet.visibility(moved_eye, client, all_eyes)
-
-/mob/living/silicon/ai/forceMove(atom/destination)
- . = ..()
- if(.)
- end_multicam()
-
-/mob/living/silicon/ai/reset_view(atom/A)
- if(camera)
- camera.set_light(0)
- if(istype(A,/obj/machinery/camera))
- camera = A
- if(A != GLOB.ai_camera_room_landmark)
- end_multicam()
- . = ..()
- if(.)
- if(!A && isturf(loc) && eyeobj)
- end_multicam()
- client.eye = eyeobj
- client.perspective = MOB_PERSPECTIVE
- if(istype(A,/obj/machinery/camera))
- if(camera_light_on) A.set_light(AI_CAMERA_LUMINOSITY)
- else A.set_light(0)
-
-
-/mob/living/silicon/ai/proc/switchCamera(var/obj/machinery/camera/C)
- if (!C || stat == DEAD) //C.can_use())
- return 0
-
- if(!src.eyeobj)
- view_core()
- return
- // ok, we're alive, camera is good and in our network...
- eyeobj.setLoc(get_turf(C))
- //machine = src
-
- return 1
-
-/mob/living/silicon/ai/cancel_camera()
- set category = "AI Commands"
- set name = "Cancel Camera View"
- view_core()
-
-//Replaces /mob/living/silicon/ai/verb/change_network() in ai.dm & camera.dm
-//Adds in /mob/living/silicon/ai/proc/ai_network_change() instead
-//Addition by Mord_Sith to define AI's network change ability
-/mob/living/silicon/ai/proc/get_camera_network_list()
- if(check_unable())
- return
-
- var/list/cameralist = new()
- for (var/obj/machinery/camera/C in cameranet.cameras)
- if(!C.can_use())
- continue
- var/list/tempnetwork = difflist(C.network,restricted_camera_networks,1)
- for(var/i in tempnetwork)
- cameralist[i] = i
-
- cameralist = sortAssoc(cameralist)
- return cameralist
-
-/mob/living/silicon/ai/proc/ai_network_change(var/network in get_camera_network_list())
- set category = "AI Commands"
- set name = "Jump To Network"
- unset_machine()
-
- if(!network)
- return
-
- if(!eyeobj)
- view_core()
- return
-
- src.network = network
-
- for(var/obj/machinery/camera/C in cameranet.cameras)
- if(!C.can_use())
- continue
- if(network in C.network)
- eyeobj.setLoc(get_turf(C))
- break
- to_chat(src, "Switched to [network] camera network.")
-//End of code by Mord_Sith
-
-/mob/living/silicon/ai/proc/ai_statuschange()
- set category = "AI Settings"
- set name = "AI Status"
-
- if(check_unable(AI_CHECK_WIRELESS))
- return
-
- set_ai_status_displays(src)
- return
-
-//I am the icon meister. Bow fefore me. //>fefore
-/mob/living/silicon/ai/proc/ai_hologram_change()
- set name = "Change Hologram"
- set desc = "Change the default hologram available to AI to something else."
- set category = "AI Settings"
-
- if(check_unable())
- return
-
- var/input
- var/choice = alert("Would you like to select a hologram based on a (visible) crew member, switch to unique avatar, or load your character from your character slot?",,"Crew Member","Unique","My Character")
-
- switch(choice)
- if("Crew Member") //A seeable crew member (or a dog)
- var/list/targets = trackable_mobs()
- if(targets.len)
- input = input("Select a crew member:") as null|anything in targets //The definition of "crew member" is a little loose...
- //This is torture, I know. If someone knows a better way...
- if(!input) return
- var/new_holo = getHologramIcon(getCompoundIcon(targets[input]))
- qdel(holo_icon)
- holo_icon = new_holo
-
- else
- alert("No suitable records found. Aborting.")
-
- if("My Character") //Loaded character slot
- if(!client || !client.prefs) return
- var/mob/living/carbon/human/dummy/dummy = new ()
- //This doesn't include custom_items because that's ... hard.
- client.prefs.dress_preview_mob(dummy)
- sleep(1 SECOND) //Strange bug in preview code? Without this, certain things won't show up. Yay race conditions?
- dummy.regenerate_icons()
-
- var/new_holo = getHologramIcon(getCompoundIcon(dummy))
- qdel(holo_icon)
- qdel(dummy)
- holo_icon = new_holo
-
- else //A premade from the dmi
- var/icon_list[] = list(
- "default",
- "floating face",
- "singularity",
- "drone",
- "carp",
- "spider",
- "bear",
- "slime",
- "ian",
- "runtime",
- "poly",
- "pun pun",
- "male human",
- "female human",
- "male unathi",
- "female unathi",
- "male tajaran",
- "female tajaran",
- "male tesharii",
- "female tesharii",
- "male skrell",
- "female skrell"
- )
- input = input("Please select a hologram:") as null|anything in icon_list
- if(input)
- qdel(holo_icon)
- switch(input)
- if("default")
- holo_icon = getHologramIcon(icon('icons/mob/AI.dmi',"holo1"))
- if("floating face")
- holo_icon = getHologramIcon(icon('icons/mob/AI.dmi',"holo2"))
- if("singularity")
- holo_icon = getHologramIcon(icon('icons/obj/singularity.dmi',"singularity_s1"))
- if("drone")
- holo_icon = getHologramIcon(icon('icons/mob/animal.dmi',"drone0"))
- if("carp")
- holo_icon = getHologramIcon(icon('icons/mob/AI.dmi',"holo4"))
- if("spider")
- holo_icon = getHologramIcon(icon('icons/mob/animal.dmi',"nurse"))
- if("bear")
- holo_icon = getHologramIcon(icon('icons/mob/animal.dmi',"brownbear"))
- if("slime")
- holo_icon = getHologramIcon(icon('icons/mob/slimes.dmi',"cerulean adult slime"))
- if("ian")
- holo_icon = getHologramIcon(icon('icons/mob/animal.dmi',"corgi"))
- if("runtime")
- holo_icon = getHologramIcon(icon('icons/mob/animal.dmi',"cat"))
- if("poly")
- holo_icon = getHologramIcon(icon('icons/mob/animal.dmi',"parrot_fly"))
- if("pun pun")
- holo_icon = getHologramIcon(icon('icons/mob/AI.dmi',"punpun"))
- if("male human")
- holo_icon = getHologramIcon(icon('icons/mob/AI.dmi',"holohumm"))
- if("female human")
- holo_icon = getHologramIcon(icon('icons/mob/AI.dmi',"holohumf"))
- if("male unathi")
- holo_icon = getHologramIcon(icon('icons/mob/AI.dmi',"holounam"))
- if("female unathi")
- holo_icon = getHologramIcon(icon('icons/mob/AI.dmi',"holounaf"))
- if("male tajaran")
- holo_icon = getHologramIcon(icon('icons/mob/AI.dmi',"holotajm"))
- if("female tajaran")
- holo_icon = getHologramIcon(icon('icons/mob/AI.dmi',"holotajf"))
- if("male tesharii")
- holo_icon = getHologramIcon(icon('icons/mob/AI.dmi',"holotesm"))
- if("female tesharii")
- holo_icon = getHologramIcon(icon('icons/mob/AI.dmi',"holotesf"))
- if("male skrell")
- holo_icon = getHologramIcon(icon('icons/mob/AI.dmi',"holoskrm"))
- if("female skrell")
- holo_icon = getHologramIcon(icon('icons/mob/AI.dmi',"holoskrf"))
-
-//Toggles the luminosity and applies it by re-entereing the camera.
-/mob/living/silicon/ai/proc/toggle_camera_light()
- set name = "Toggle Camera Light"
- set desc = "Toggles the light on the camera the AI is looking through."
- set category = "AI Commands"
- if(check_unable())
- return
-
- camera_light_on = !camera_light_on
- to_chat(src, "Camera lights [camera_light_on ? "activated" : "deactivated"].")
- if(!camera_light_on)
- if(camera)
- camera.set_light(0)
- camera = null
- else
- lightNearbyCamera()
-
-
-
-// Handled camera lighting, when toggled.
-// It will get the nearest camera from the eyeobj, lighting it.
-
-/mob/living/silicon/ai/proc/lightNearbyCamera()
- if(camera_light_on && camera_light_on < world.timeofday)
- if(src.camera)
- var/obj/machinery/camera/camera = near_range_camera(src.eyeobj)
- if(camera && src.camera != camera)
- src.camera.set_light(0)
- if(!camera.light_disabled)
- src.camera = camera
- src.camera.set_light(AI_CAMERA_LUMINOSITY)
- else
- src.camera = null
- else if(isnull(camera))
- src.camera.set_light(0)
- src.camera = null
- else
- var/obj/machinery/camera/camera = near_range_camera(src.eyeobj)
- if(camera && !camera.light_disabled)
- src.camera = camera
- src.camera.set_light(AI_CAMERA_LUMINOSITY)
- camera_light_on = world.timeofday + 1 * 20 // Update the light every 2 seconds.
-
-
-/mob/living/silicon/ai/attackby(obj/item/weapon/W as obj, mob/user as mob)
- if(istype(W, /obj/item/device/aicard))
-
- var/obj/item/device/aicard/card = W
- card.grab_ai(src, user)
-
- else if(W.is_wrench())
- if(user == deployed_shell)
- to_chat(user, "The shell's subsystems resist your efforts to tamper with your bolts.")
- return
- if(anchored)
- playsound(src, W.usesound, 50, 1)
- user.visible_message("\The [user] starts to unbolt \the [src] from the plating...")
- if(!do_after(user,40 * W.toolspeed))
- user.visible_message("\The [user] decides not to unbolt \the [src].")
- return
- user.visible_message("\The [user] finishes unfastening \the [src]!")
- anchored = 0
- return
- else
- playsound(src, W.usesound, 50, 1)
- user.visible_message("\The [user] starts to bolt \the [src] to the plating...")
- if(!do_after(user,40 * W.toolspeed))
- user.visible_message("\The [user] decides not to bolt \the [src].")
- return
- user.visible_message("\The [user] finishes fastening down \the [src]!")
- anchored = 1
- return
- else
- return ..()
-
-/mob/living/silicon/ai/proc/control_integrated_radio()
- set name = "Radio Settings"
- set desc = "Allows you to change settings of your radio."
- set category = "AI Settings"
-
- if(check_unable(AI_CHECK_RADIO))
- return
-
- to_chat(src, "Accessing Subspace Transceiver control...")
- if (src.aiRadio)
- src.aiRadio.interact(src)
-
-/mob/living/silicon/ai/proc/sensor_mode()
- set name = "Set Sensor Augmentation"
- set category = "AI Settings"
- set desc = "Augment visual feed with internal sensor overlays"
- toggle_sensor_mode()
-
-/mob/living/silicon/ai/proc/toggle_hologram_movement()
- set name = "Toggle Hologram Movement"
- set category = "AI Settings"
- set desc = "Toggles hologram movement based on moving with your virtual eye."
-
- hologram_follow = !hologram_follow
- //VOREStation Add - Required to stop movement because we use walk_to(wards) in hologram.dm
- if(holo)
- var/obj/effect/overlay/aiholo/hologram = holo.masters[src]
- walk(hologram, 0)
- //VOREStation Add End
- to_chat(usr, "Your hologram will [hologram_follow ? "follow" : "no longer follow"] you now.")
-
-
-/mob/living/silicon/ai/proc/check_unable(var/flags = 0, var/feedback = 1)
- if(stat == DEAD)
- if(feedback)
- to_chat(src, "You are dead!")
- return 1
-
- if(aiRestorePowerRoutine)
- if(feedback)
- to_chat(src, "You lack power!")
- return 1
-
- if((flags & AI_CHECK_WIRELESS) && src.control_disabled)
- if(feedback)
- to_chat(src, "Wireless control is disabled!")
- return 1
- if((flags & AI_CHECK_RADIO) && src.aiRadio.disabledAi)
- if(feedback)
- to_chat(src, "System Error - Transceiver Disabled!")
- return 1
- return 0
-
-/mob/living/silicon/ai/proc/is_in_chassis()
- return istype(loc, /turf)
-
-/mob/living/silicon/ai/proc/open_nearest_door(mob/living/target) // Rykka ports AI opening doors
- if(!istype(target))
- return
-
- if(target && ai_actual_track(target))
- var/obj/machinery/door/airlock/A = null
-
- var/dist = -1
- for(var/obj/machinery/door/airlock/D in range(3, target))
- if(!D.density)
- continue
-
- var/curr_dist = get_dist(D, target)
-
- if(dist < 0)
- dist = curr_dist
- A = D
- else if(dist > curr_dist)
- dist = curr_dist
- A = D
-
- if(istype(A))
- switch(alert(src, "Do you want to open \the [A] for [target]?", "Doorknob_v2a.exe", "Yes", "No"))
- if("Yes")
- A.AIShiftClick()
- to_chat(src, "You open \the [A] for [target].")
- else
- to_chat(src, "You deny the request.")
- else
- to_chat(src, "Unable to locate an airlock near [target].")
-
- else
- to_chat(src, "Target is not on or near any active cameras on the station.")
-
-/mob/living/silicon/ai/ex_act(var/severity)
- if(severity == 1.0)
- qdel(src)
- return
- ..()
-
-/mob/living/silicon/ai/updateicon()
- if(!selected_sprite) selected_sprite = default_ai_icon
-
- if(stat == DEAD)
- icon_state = selected_sprite.dead_icon
- set_light(3, 1, selected_sprite.dead_light)
- else if(aiRestorePowerRoutine)
- icon_state = selected_sprite.nopower_icon
- set_light(1, 1, selected_sprite.nopower_light)
- else
- icon_state = selected_sprite.alive_icon
- set_light(1, 1, selected_sprite.alive_light)
-
-// Pass lying down or getting up to our pet human, if we're in a rig.
-/mob/living/silicon/ai/lay_down()
- set name = "Rest"
- set category = "IC"
-
- resting = 0
- var/obj/item/weapon/rig/rig = src.get_rig()
- if(rig)
- rig.force_rest(src)
-
-/mob/living/silicon/ai/is_sentient()
- // AI cores don't store what brain was used to build them so we're just gonna assume they can think to some degree.
- // If that is ever fixed please update this proc.
- return TRUE
-
-
-/mob/living/silicon/ai/handle_track(message, verb = "says", mob/speaker = null, speaker_name, hard_to_hear)
- if(hard_to_hear)
- return
-
- var/jobname // the mob's "job"
- var/mob/living/carbon/human/impersonating //The crew member being impersonated, if any.
- var/changed_voice
-
- if(ishuman(speaker))
- var/mob/living/carbon/human/H = speaker
-
- if(H.wear_mask && istype(H.wear_mask,/obj/item/clothing/mask/gas/voice))
- changed_voice = 1
- var/list/impersonated = new()
- var/mob/living/carbon/human/I = impersonated[speaker_name]
-
- if(!I)
- for(var/mob/living/carbon/human/M in mob_list)
- if(M.real_name == speaker_name)
- I = M
- impersonated[speaker_name] = I
- break
-
- // If I's display name is currently different from the voice name and using an agent ID then don't impersonate
- // as this would allow the AI to track I and realize the mismatch.
- if(I && !(I.name != speaker_name && I.wear_id && istype(I.wear_id,/obj/item/weapon/card/id/syndicate)))
- impersonating = I
- jobname = impersonating.get_assignment()
- else
- jobname = "Unknown"
- else
- jobname = H.get_assignment()
-
- else if(iscarbon(speaker)) // Nonhuman carbon mob
- jobname = "No id"
- else if(isAI(speaker))
- jobname = "AI"
- else if(isrobot(speaker))
- jobname = "Cyborg"
- else if(istype(speaker, /mob/living/silicon/pai))
- jobname = "Personal AI"
- else
- jobname = "Unknown"
-
- var/track = ""
- if(changed_voice) // They have a fake name
- if(impersonating) // And we found a mob with that name above, track them instead
- track = "[speaker_name] ([jobname])"
- track += "\[OPEN\]" // Rykka ports AI opening doors
- else // We couldn't find a mob with their fake name, don't track at all
- track = "[speaker_name] ([jobname])"
- else // Not faking their name
- if(istype(speaker, /mob/living/bot)) // It's a bot, and no fake name! (That'd be kinda weird.) :p
- track = "[speaker_name] ([jobname])"
- else // It's not a bot, and no fake name!
- track = "[speaker_name] ([jobname])"
- track += "\[OPEN\]" // Rykka ports AI opening doors
-
- return track // Feed variable back to AI
-
-/mob/living/silicon/ai/proc/relay_speech(mob/living/M, list/message_pieces, verb)
- var/message = combine_message(message_pieces, verb, M)
- var/name_used = M.GetVoice()
- //This communication is imperfect because the holopad "filters" voices and is only designed to connect to the master only.
- var/rendered = "Relayed Speech: [name_used] [message]"
- show_message(rendered, 2)
-
-/mob/living/silicon/ai/proc/toggle_multicam_verb()
- set name = "Toggle Multicam"
- set category = "AI Commands"
- toggle_multicam()
-
-/mob/living/silicon/ai/proc/add_multicam_verb()
- set name = "Add Multicam Viewport"
- set category = "AI Commands"
- drop_new_multicam()
-
-//Special subtype kept around for global announcements
-/mob/living/silicon/ai/announcer
- is_dummy = 1
-
-/mob/living/silicon/ai/announcer/Initialize()
- . = ..()
- mob_list -= src
- living_mob_list -= src
- dead_mob_list -= src
- ai_list -= src
- silicon_mob_list -= src
- QDEL_NULL(eyeobj)
-
-/mob/living/silicon/ai/announcer/Life()
- mob_list -= src
- living_mob_list -= src
- dead_mob_list -= src
- ai_list -= src
- silicon_mob_list -= src
- QDEL_NULL(eyeobj)
-
-#undef AI_CHECK_WIRELESS
-#undef AI_CHECK_RADIO
+#define AI_CHECK_WIRELESS 1
+#define AI_CHECK_RADIO 2
+
+var/list/ai_verbs_default = list(
+ // /mob/living/silicon/ai/proc/ai_recall_shuttle,
+ /mob/living/silicon/ai/proc/ai_emergency_message,
+ /mob/living/silicon/ai/proc/ai_goto_location,
+ /mob/living/silicon/ai/proc/ai_remove_location,
+ /mob/living/silicon/ai/proc/ai_hologram_change,
+ /mob/living/silicon/ai/proc/ai_network_change,
+ /mob/living/silicon/ai/proc/ai_statuschange,
+ /mob/living/silicon/ai/proc/ai_store_location,
+ /mob/living/silicon/ai/proc/control_integrated_radio,
+ /mob/living/silicon/ai/proc/pick_icon,
+ /mob/living/silicon/ai/proc/sensor_mode,
+ /mob/living/silicon/ai/proc/show_laws_verb,
+ /mob/living/silicon/ai/proc/toggle_acceleration,
+ /mob/living/silicon/ai/proc/toggle_hologram_movement,
+ /mob/living/silicon/ai/proc/ai_announcement,
+ /mob/living/silicon/ai/proc/ai_call_shuttle,
+ /mob/living/silicon/ai/proc/ai_camera_track,
+ /mob/living/silicon/ai/proc/ai_camera_list,
+ /mob/living/silicon/ai/proc/ai_checklaws,
+ /mob/living/silicon/ai/proc/toggle_camera_light,
+ /mob/living/silicon/ai/proc/take_image,
+ /mob/living/silicon/ai/proc/view_images,
+ /mob/living/silicon/ai/proc/toggle_multicam_verb,
+ /mob/living/silicon/ai/proc/add_multicam_verb
+)
+
+//Not sure why this is necessary...
+/proc/AutoUpdateAI(obj/subject)
+ var/is_in_use = 0
+ if (subject!=null)
+ for(var/A in ai_list)
+ var/mob/living/silicon/ai/M = A
+ if ((M.client && M.machine == subject))
+ is_in_use = 1
+ subject.attack_ai(M)
+ return is_in_use
+
+
+/mob/living/silicon/ai
+ name = "AI"
+ icon = 'icons/mob/AI.dmi'//
+ icon_state = "ai"
+ anchored = 1 // -- TLE
+ density = 1
+ status_flags = CANSTUN|CANPARALYSE|CANPUSH
+ shouldnt_see = list(/mob/observer/eye, /obj/effect/rune)
+ var/list/network = list(NETWORK_DEFAULT)
+ var/obj/machinery/camera/camera = null
+ var/aiRestorePowerRoutine = 0
+ var/viewalerts = 0
+ var/icon/holo_icon//Default is assigned when AI is created.
+ var/list/connected_robots = list()
+ var/obj/item/device/pda/ai/aiPDA = null
+ var/obj/item/device/communicator/aiCommunicator = null
+ var/obj/item/device/multitool/aiMulti = null
+ var/obj/item/device/radio/headset/heads/ai_integrated/aiRadio = null
+ var/camera_light_on = 0 //Defines if the AI toggled the light on the camera it's looking through.
+ var/datum/trackable/track = null
+ var/last_announcement = ""
+ var/control_disabled = 0
+ var/datum/announcement/priority/announcement
+ var/obj/machinery/ai_powersupply/psupply = null // Backwards reference to AI's powersupply object.
+ var/hologram_follow = 1 //This is used for the AI eye, to determine if a holopad's hologram should follow it or not.
+ var/is_dummy = 0 //Used to prevent dummy AIs from spawning with communicators.
+ //NEWMALF VARIABLES
+ var/malfunctioning = 0 // Master var that determines if AI is malfunctioning.
+ var/datum/malf_hardware/hardware = null // Installed piece of hardware.
+ var/datum/malf_research/research = null // Malfunction research datum.
+ var/obj/machinery/power/apc/hack = null // APC that is currently being hacked.
+ var/list/hacked_apcs = null // List of all hacked APCs
+ var/APU_power = 0 // If set to 1 AI runs on APU power
+ var/hacking = 0 // Set to 1 if AI is hacking APC, cyborg, other AI, or running system override.
+ var/system_override = 0 // Set to 1 if system override is initiated, 2 if succeeded.
+ var/hack_can_fail = 1 // If 0, all abilities have zero chance of failing.
+ var/hack_fails = 0 // This increments with each failed hack, and determines the warning message text.
+ var/errored = 0 // Set to 1 if runtime error occurs. Only way of this happening i can think of is admin fucking up with varedit.
+ var/bombing_core = 0 // Set to 1 if core auto-destruct is activated
+ var/bombing_station = 0 // Set to 1 if station nuke auto-destruct is activated
+ var/override_CPUStorage = 0 // Bonus/Penalty CPU Storage. For use by admins/testers.
+ var/override_CPURate = 0 // Bonus/Penalty CPU generation rate. For use by admins/testers.
+
+ var/datum/ai_icon/selected_sprite // The selected icon set
+ var/custom_sprite = 0 // Whether the selected icon is custom
+ var/carded
+
+ // Multicam Vars
+ var/multicam_allowed = TRUE
+ var/multicam_on = FALSE
+ var/obj/screen/movable/pic_in_pic/ai/master_multicam
+ var/list/multicam_screens = list()
+ var/list/all_eyes = list()
+ var/max_multicams = 6
+
+ can_be_antagged = TRUE
+
+/mob/living/silicon/ai/proc/add_ai_verbs()
+ src.verbs |= ai_verbs_default
+ src.verbs |= silicon_subsystems
+
+/mob/living/silicon/ai/proc/remove_ai_verbs()
+ src.verbs -= ai_verbs_default
+ src.verbs -= silicon_subsystems
+
+/mob/living/silicon/ai/New(loc, var/datum/ai_laws/L, var/obj/item/device/mmi/B, var/safety = 0)
+ announcement = new()
+ announcement.title = "A.I. Announcement"
+ announcement.announcement_type = "A.I. Announcement"
+ announcement.newscast = 1
+
+ var/list/possibleNames = ai_names
+
+ var/pickedName = null
+ while(!pickedName)
+ pickedName = pick(ai_names)
+ for (var/mob/living/silicon/ai/A in mob_list)
+ if (A.real_name == pickedName && possibleNames.len > 1) //fixing the theoretically possible infinite loop
+ possibleNames -= pickedName
+ pickedName = null
+
+ if(!is_dummy)
+ aiPDA = new/obj/item/device/pda/ai(src)
+ SetName(pickedName)
+ anchored = 1
+ canmove = 0
+ density = 1
+ loc = loc
+
+ if(!is_dummy)
+ aiCommunicator = new /obj/item/device/communicator/integrated(src)
+
+ holo_icon = getHologramIcon(icon('icons/mob/AI.dmi',"holo1"))
+
+ proc_holder_list = new()
+
+ if(L)
+ if (istype(L, /datum/ai_laws))
+ laws = L
+ else
+ laws = new using_map.default_law_type
+
+ aiMulti = new(src)
+ aiRadio = new(src)
+ common_radio = aiRadio
+ aiRadio.myAi = src
+ additional_law_channels["Binary"] = "#b"
+ additional_law_channels["Holopad"] = ":h"
+
+ aiCamera = new/obj/item/device/camera/siliconcam/ai_camera(src)
+
+ if (istype(loc, /turf))
+ add_ai_verbs(src)
+
+ //Languages
+ add_language("Robot Talk", 1)
+ add_language(LANGUAGE_GALCOM, 1)
+ add_language(LANGUAGE_SOL_COMMON, 1)
+ add_language(LANGUAGE_UNATHI, 1)
+ add_language(LANGUAGE_SIIK, 1)
+ add_language(LANGUAGE_AKHANI, 1)
+ add_language(LANGUAGE_SKRELLIAN, 1)
+ add_language(LANGUAGE_SKRELLIANFAR, 0)
+ add_language(LANGUAGE_TRADEBAND, 1)
+ add_language(LANGUAGE_GUTTER, 1)
+ add_language(LANGUAGE_EAL, 1)
+ add_language(LANGUAGE_SCHECHI, 1)
+ add_language(LANGUAGE_SIGN, 1)
+ add_language(LANGUAGE_ROOTLOCAL, 1)
+ add_language(LANGUAGE_TERMINUS, 1)
+ add_language(LANGUAGE_ZADDAT, 1)
+
+ if(!safety)//Only used by AIize() to successfully spawn an AI.
+ if (!B)//If there is no player/brain inside.
+ empty_playable_ai_cores += new/obj/structure/AIcore/deactivated(loc)//New empty terminal.
+ qdel(src)//Delete AI.
+ return
+ else
+ if (B.brainmob.mind)
+ B.brainmob.mind.transfer_to(src)
+
+ on_mob_init()
+
+ spawn(5)
+ new /obj/machinery/ai_powersupply(src)
+
+ ai_list += src
+ ..()
+ return
+
+/mob/living/silicon/ai/proc/on_mob_init()
+ to_chat(src, "You are playing the station's AI. The AI cannot move, but can interact with many objects while viewing them (through cameras).")
+ to_chat(src, "To look at other parts of the station, click on yourself to get a camera menu.")
+ to_chat(src, "While observing through a camera, you can use most (networked) devices which you can see, such as computers, APCs, intercoms, doors, etc.")
+ to_chat(src, "To use something, simply click on it.")
+ to_chat(src, "Use say #b to speak to your cyborgs through binary. Use say :h to speak from an active holopad.")
+ to_chat(src, "For department channels, use the following say commands:")
+
+ var/radio_text = ""
+ for(var/i = 1 to common_radio.channels.len)
+ var/channel = common_radio.channels[i]
+ var/key = get_radio_key_from_channel(channel)
+ radio_text += "[key] - [channel]"
+ if(i != common_radio.channels.len)
+ radio_text += ", "
+
+ to_chat(src,radio_text)
+
+ // Vorestation Edit: Meta Info for AI's. Mostly used for Holograms
+ if (client)
+ var/meta_info = client.prefs.metadata
+ if (meta_info)
+ ooc_notes = meta_info
+
+ if (malf && !(mind in malf.current_antagonists))
+ show_laws()
+ to_chat(src, "These laws may be changed by other players, or by you being the traitor.")
+
+ job = "AI"
+ setup_icon()
+
+/mob/living/silicon/ai/Destroy()
+ ai_list -= src
+
+ QDEL_NULL(announcement)
+ QDEL_NULL(eyeobj)
+ QDEL_NULL(psupply)
+ QDEL_NULL(aiPDA)
+ QDEL_NULL(aiCommunicator)
+ QDEL_NULL(aiMulti)
+ QDEL_NULL(aiRadio)
+ QDEL_NULL(aiCamera)
+ hack = null
+
+ return ..()
+
+/mob/living/silicon/ai/Stat()
+ ..()
+ if(statpanel("Status"))
+ if(!stat) // Make sure we're not unconscious/dead.
+ stat(null, text("System integrity: [(health+100)/2]%"))
+ stat(null, text("Connected synthetics: [connected_robots.len]"))
+ for(var/mob/living/silicon/robot/R in connected_robots)
+ var/robot_status = "Nominal"
+ if(R.shell)
+ robot_status = "AI SHELL"
+ else if(R.stat || !R.client)
+ robot_status = "OFFLINE"
+ else if(!R.cell || R.cell.charge <= 0)
+ robot_status = "DEPOWERED"
+ //Name, Health, Battery, Module, Area, and Status! Everything an AI wants to know about its borgies!
+ stat(null, text("[R.name] | S.Integrity: [R.health]% | Cell: [R.cell ? "[R.cell.charge]/[R.cell.maxcharge]" : "Empty"] | \
+ Module: [R.modtype] | Loc: [get_area_name(R, TRUE)] | Status: [robot_status]"))
+ stat(null, text("AI shell beacons detected: [LAZYLEN(GLOB.available_ai_shells)]")) //Count of total AI shells
+ else
+ stat(null, text("Systems nonfunctional"))
+
+
+/mob/living/silicon/ai/proc/setup_icon()
+ var/file = file2text("config/custom_sprites.txt")
+ var/lines = splittext(file, "\n")
+
+ for(var/line in lines)
+ // split & clean up
+ var/list/Entry = splittext(line, ":")
+ for(var/i = 1 to Entry.len)
+ Entry[i] = trim(Entry[i])
+
+ if(Entry.len < 2)
+ continue;
+
+ if(Entry[1] == src.ckey && Entry[2] == src.real_name)
+ icon = CUSTOM_ITEM_SYNTH
+ custom_sprite = 1
+ selected_sprite = new/datum/ai_icon("Custom", "[src.ckey]-ai", "4", "[ckey]-ai-crash", "#FFFFFF", "#FFFFFF", "#FFFFFF")
+ else
+ selected_sprite = default_ai_icon
+ updateicon()
+
+/mob/living/silicon/ai/pointed(atom/A as mob|obj|turf in view())
+ set popup_menu = 0
+ set src = usr.contents
+ return 0
+
+/mob/living/silicon/ai/SetName(pickedName as text)
+ ..()
+ announcement.announcer = pickedName
+ if(eyeobj)
+ eyeobj.name = "[pickedName] (AI Eye)"
+
+ // Set ai pda name
+ if(aiPDA)
+ aiPDA.ownjob = "AI"
+ aiPDA.owner = pickedName
+ aiPDA.name = pickedName + " (" + aiPDA.ownjob + ")"
+
+ if(aiCommunicator)
+ aiCommunicator.register_device(src.name)
+
+/*
+ The AI Power supply is a dummy object used for powering the AI since only machinery should be using power.
+ The alternative was to rewrite a bunch of AI code instead here we are.
+*/
+/obj/machinery/ai_powersupply
+ name="Power Supply"
+ active_power_usage=50000 // Station AIs use significant amounts of power. This, when combined with charged SMES should mean AI lasts for 1hr without external power.
+ use_power = USE_POWER_ACTIVE
+ power_channel = EQUIP
+ var/mob/living/silicon/ai/powered_ai = null
+ invisibility = 100
+
+/obj/machinery/ai_powersupply/New(var/mob/living/silicon/ai/ai=null)
+ powered_ai = ai
+ powered_ai.psupply = src
+ if(istype(powered_ai,/mob/living/silicon/ai/announcer)) //Don't try to get a loc for a nullspace announcer mob, just put it into it
+ forceMove(powered_ai)
+ else
+ forceMove(powered_ai.loc)
+
+ ..()
+ use_power(1) // Just incase we need to wake up the power system.
+
+/obj/machinery/ai_powersupply/Destroy()
+ . = ..()
+ powered_ai = null
+
+/obj/machinery/ai_powersupply/process()
+ if(!powered_ai || powered_ai.stat == DEAD)
+ qdel(src)
+ return
+ if(powered_ai.psupply != src) // For some reason, the AI has different powersupply object. Delete this one, it's no longer needed.
+ qdel(src)
+ return
+ if(powered_ai.APU_power)
+ update_use_power(USE_POWER_OFF)
+ return
+ if(!powered_ai.anchored)
+ loc = powered_ai.loc
+ update_use_power(USE_POWER_OFF)
+ use_power(50000) // Less optimalised but only called if AI is unwrenched. This prevents usage of wrenching as method to keep AI operational without power. Intellicard is for that.
+ if(powered_ai.anchored)
+ update_use_power(USE_POWER_ACTIVE)
+
+/mob/living/silicon/ai/proc/pick_icon()
+ set category = "AI Settings"
+ set name = "Set AI Core Display"
+ if(stat || aiRestorePowerRoutine)
+ return
+
+ if (!custom_sprite)
+ var/new_sprite = input("Select an icon!", "AI", selected_sprite) as null|anything in ai_icons
+ if(new_sprite) selected_sprite = new_sprite
+ updateicon()
+
+/mob/living/silicon/ai/var/message_cooldown = 0
+/mob/living/silicon/ai/proc/ai_announcement()
+ set category = "AI Commands"
+ set name = "Make Station Announcement"
+ if(check_unable(AI_CHECK_WIRELESS | AI_CHECK_RADIO))
+ return
+
+ if(message_cooldown)
+ to_chat(src, "Please allow one minute to pass between announcements.")
+ return
+ var/input = input(usr, "Please write a message to announce to the station crew.", "A.I. Announcement")
+ if(!input)
+ return
+
+ if(check_unable(AI_CHECK_WIRELESS | AI_CHECK_RADIO))
+ return
+
+ announcement.Announce(input)
+ message_cooldown = 1
+ spawn(600)//One minute cooldown
+ message_cooldown = 0
+
+/mob/living/silicon/ai/proc/ai_call_shuttle()
+ set category = "AI Commands"
+ set name = "Call Emergency Shuttle"
+ if(check_unable(AI_CHECK_WIRELESS))
+ return
+
+ var/confirm = alert("Are you sure you want to call the shuttle?", "Confirm Shuttle Call", "Yes", "No")
+
+ if(check_unable(AI_CHECK_WIRELESS))
+ return
+
+ if(confirm == "Yes")
+ call_shuttle_proc(src)
+
+ // hack to display shuttle timer
+ if(emergency_shuttle.online())
+ post_status(src, "shuttle", user = src)
+
+/mob/living/silicon/ai/proc/ai_recall_shuttle()
+ set category = "AI Commands"
+ set name = "Recall Emergency Shuttle"
+
+ if(check_unable(AI_CHECK_WIRELESS))
+ return
+
+ var/confirm = alert("Are you sure you want to recall the shuttle?", "Confirm Shuttle Recall", "Yes", "No")
+ if(check_unable(AI_CHECK_WIRELESS))
+ return
+
+ if(confirm == "Yes")
+ cancel_call_proc(src)
+
+/mob/living/silicon/ai/var/emergency_message_cooldown = 0
+
+/mob/living/silicon/ai/proc/ai_emergency_message()
+ set category = "AI Commands"
+ set name = "Send Emergency Message"
+
+ if(check_unable(AI_CHECK_WIRELESS))
+ return
+ if(emergency_message_cooldown)
+ to_chat(usr, "Arrays recycling. Please stand by.")
+ return
+ var/input = sanitize(input(usr, "Please choose a message to transmit to [using_map.boss_short] via quantum entanglement. Please be aware that this process is very expensive, and abuse will lead to... termination. Transmission does not guarantee a response. There is a 30 second delay before you may send another message, be clear, full and concise.", "To abort, send an empty message.", ""))
+ if(!input)
+ return
+ CentCom_announce(input, usr)
+ to_chat(usr, "Message transmitted.")
+ log_game("[key_name(usr)] has made an IA [using_map.boss_short] announcement: [input]")
+ emergency_message_cooldown = 1
+ spawn(300)
+ emergency_message_cooldown = 0
+/mob/living/silicon/ai/check_eye(var/mob/user as mob)
+ if (!camera)
+ return -1
+ return 0
+
+/mob/living/silicon/ai/restrained()
+ return 0
+
+/mob/living/silicon/ai/emp_act(severity)
+ disconnect_shell("Disconnected from remote shell due to ionic interfe%*@$^___")
+ if (prob(30))
+ view_core()
+ ..()
+
+/mob/living/silicon/ai/Topic(href, href_list)
+ if(..()) //VOREstation edit: So the AI can actually can actually get its OOC prefs read
+ return
+ if(usr != src)
+ return
+ /*if(..()) // <------ MOVED FROM HERE
+ return*/
+ if (href_list["mach_close"])
+ if (href_list["mach_close"] == "aialerts")
+ viewalerts = 0
+ var/t1 = text("window=[]", href_list["mach_close"])
+ unset_machine()
+ src << browse(null, t1)
+ if (href_list["switchcamera"])
+ switchCamera(locate(href_list["switchcamera"])) in cameranet.cameras
+ if (href_list["showalerts"])
+ subsystem_alarm_monitor()
+ //Carn: holopad requests
+ if (href_list["jumptoholopad"])
+ var/obj/machinery/hologram/holopad/H = locate(href_list["jumptoholopad"])
+ if(stat == CONSCIOUS)
+ if(H)
+ H.attack_ai(src) //may as well recycle
+ else
+ to_chat(src, "Unable to locate the holopad.")
+
+ if (href_list["track"])
+ var/mob/target = locate(href_list["track"]) in mob_list
+
+ if(target && (!istype(target, /mob/living/carbon/human) || html_decode(href_list["trackname"]) == target:get_face_name()))
+ ai_actual_track(target)
+ else
+ to_chat(src, "System error. Cannot locate [html_decode(href_list["trackname"])].")
+ return
+
+ if(href_list["trackbot"])
+ var/mob/living/bot/target = locate(href_list["trackbot"]) in mob_list
+ if(target)
+ ai_actual_track(target)
+ else
+ to_chat(src, "Target is not on or near any active cameras on the station.")
+ return
+
+ if(href_list["open"])
+ var/mob/target = locate(href_list["open"]) in mob_list
+ if(target)
+ open_nearest_door(target)
+
+ return
+
+/mob/living/silicon/ai/proc/camera_visibility(mob/observer/eye/aiEye/moved_eye)
+ cameranet.visibility(moved_eye, client, all_eyes)
+
+/mob/living/silicon/ai/forceMove(atom/destination)
+ . = ..()
+ if(.)
+ end_multicam()
+
+/mob/living/silicon/ai/reset_view(atom/A)
+ if(camera)
+ camera.set_light(0)
+ if(istype(A,/obj/machinery/camera))
+ camera = A
+ if(A != GLOB.ai_camera_room_landmark)
+ end_multicam()
+ . = ..()
+ if(.)
+ if(!A && isturf(loc) && eyeobj)
+ end_multicam()
+ client.eye = eyeobj
+ client.perspective = MOB_PERSPECTIVE
+ if(istype(A,/obj/machinery/camera))
+ if(camera_light_on) A.set_light(AI_CAMERA_LUMINOSITY)
+ else A.set_light(0)
+
+
+/mob/living/silicon/ai/proc/switchCamera(var/obj/machinery/camera/C)
+ if (!C || stat == DEAD) //C.can_use())
+ return 0
+
+ if(!src.eyeobj)
+ view_core()
+ return
+ // ok, we're alive, camera is good and in our network...
+ eyeobj.setLoc(get_turf(C))
+ //machine = src
+
+ return 1
+
+/mob/living/silicon/ai/cancel_camera()
+ set category = "AI Commands"
+ set name = "Cancel Camera View"
+ view_core()
+
+//Replaces /mob/living/silicon/ai/verb/change_network() in ai.dm & camera.dm
+//Adds in /mob/living/silicon/ai/proc/ai_network_change() instead
+//Addition by Mord_Sith to define AI's network change ability
+/mob/living/silicon/ai/proc/get_camera_network_list()
+ if(check_unable())
+ return
+
+ var/list/cameralist = new()
+ for (var/obj/machinery/camera/C in cameranet.cameras)
+ if(!C.can_use())
+ continue
+ var/list/tempnetwork = difflist(C.network,restricted_camera_networks,1)
+ for(var/i in tempnetwork)
+ cameralist[i] = i
+
+ cameralist = sortAssoc(cameralist)
+ return cameralist
+
+/mob/living/silicon/ai/proc/ai_network_change(var/network in get_camera_network_list())
+ set category = "AI Commands"
+ set name = "Jump To Network"
+ unset_machine()
+
+ if(!network)
+ return
+
+ if(!eyeobj)
+ view_core()
+ return
+
+ src.network = network
+
+ for(var/obj/machinery/camera/C in cameranet.cameras)
+ if(!C.can_use())
+ continue
+ if(network in C.network)
+ eyeobj.setLoc(get_turf(C))
+ break
+ to_chat(src, "Switched to [network] camera network.")
+//End of code by Mord_Sith
+
+/mob/living/silicon/ai/proc/ai_statuschange()
+ set category = "AI Settings"
+ set name = "AI Status"
+
+ if(check_unable(AI_CHECK_WIRELESS))
+ return
+
+ set_ai_status_displays(src)
+ return
+
+//I am the icon meister. Bow fefore me. //>fefore
+/mob/living/silicon/ai/proc/ai_hologram_change()
+ set name = "Change Hologram"
+ set desc = "Change the default hologram available to AI to something else."
+ set category = "AI Settings"
+
+ if(check_unable())
+ return
+
+ var/input
+ var/choice = alert("Would you like to select a hologram based on a (visible) crew member, switch to unique avatar, or load your character from your character slot?",,"Crew Member","Unique","My Character")
+
+ switch(choice)
+ if("Crew Member") //A seeable crew member (or a dog)
+ var/list/targets = trackable_mobs()
+ if(targets.len)
+ input = input("Select a crew member:") as null|anything in targets //The definition of "crew member" is a little loose...
+ //This is torture, I know. If someone knows a better way...
+ if(!input) return
+ var/new_holo = getHologramIcon(getCompoundIcon(targets[input]))
+ qdel(holo_icon)
+ holo_icon = new_holo
+
+ else
+ alert("No suitable records found. Aborting.")
+
+ if("My Character") //Loaded character slot
+ if(!client || !client.prefs) return
+ var/mob/living/carbon/human/dummy/dummy = new ()
+ //This doesn't include custom_items because that's ... hard.
+ client.prefs.dress_preview_mob(dummy)
+ sleep(1 SECOND) //Strange bug in preview code? Without this, certain things won't show up. Yay race conditions?
+ dummy.regenerate_icons()
+
+ var/new_holo = getHologramIcon(getCompoundIcon(dummy))
+ qdel(holo_icon)
+ qdel(dummy)
+ holo_icon = new_holo
+
+ else //A premade from the dmi
+ var/icon_list[] = list(
+ "default",
+ "floating face",
+ "singularity",
+ "drone",
+ "carp",
+ "spider",
+ "bear",
+ "slime",
+ "ian",
+ "runtime",
+ "poly",
+ "pun pun",
+ "male human",
+ "female human",
+ "male unathi",
+ "female unathi",
+ "male tajaran",
+ "female tajaran",
+ "male tesharii",
+ "female tesharii",
+ "male skrell",
+ "female skrell"
+ )
+ input = input("Please select a hologram:") as null|anything in icon_list
+ if(input)
+ qdel(holo_icon)
+ switch(input)
+ if("default")
+ holo_icon = getHologramIcon(icon('icons/mob/AI.dmi',"holo1"))
+ if("floating face")
+ holo_icon = getHologramIcon(icon('icons/mob/AI.dmi',"holo2"))
+ if("singularity")
+ holo_icon = getHologramIcon(icon('icons/obj/singularity.dmi',"singularity_s1"))
+ if("drone")
+ holo_icon = getHologramIcon(icon('icons/mob/animal.dmi',"drone0"))
+ if("carp")
+ holo_icon = getHologramIcon(icon('icons/mob/AI.dmi',"holo4"))
+ if("spider")
+ holo_icon = getHologramIcon(icon('icons/mob/animal.dmi',"nurse"))
+ if("bear")
+ holo_icon = getHologramIcon(icon('icons/mob/animal.dmi',"brownbear"))
+ if("slime")
+ holo_icon = getHologramIcon(icon('icons/mob/slimes.dmi',"cerulean adult slime"))
+ if("ian")
+ holo_icon = getHologramIcon(icon('icons/mob/animal.dmi',"corgi"))
+ if("runtime")
+ holo_icon = getHologramIcon(icon('icons/mob/animal.dmi',"cat"))
+ if("poly")
+ holo_icon = getHologramIcon(icon('icons/mob/animal.dmi',"parrot_fly"))
+ if("pun pun")
+ holo_icon = getHologramIcon(icon('icons/mob/AI.dmi',"punpun"))
+ if("male human")
+ holo_icon = getHologramIcon(icon('icons/mob/AI.dmi',"holohumm"))
+ if("female human")
+ holo_icon = getHologramIcon(icon('icons/mob/AI.dmi',"holohumf"))
+ if("male unathi")
+ holo_icon = getHologramIcon(icon('icons/mob/AI.dmi',"holounam"))
+ if("female unathi")
+ holo_icon = getHologramIcon(icon('icons/mob/AI.dmi',"holounaf"))
+ if("male tajaran")
+ holo_icon = getHologramIcon(icon('icons/mob/AI.dmi',"holotajm"))
+ if("female tajaran")
+ holo_icon = getHologramIcon(icon('icons/mob/AI.dmi',"holotajf"))
+ if("male tesharii")
+ holo_icon = getHologramIcon(icon('icons/mob/AI.dmi',"holotesm"))
+ if("female tesharii")
+ holo_icon = getHologramIcon(icon('icons/mob/AI.dmi',"holotesf"))
+ if("male skrell")
+ holo_icon = getHologramIcon(icon('icons/mob/AI.dmi',"holoskrm"))
+ if("female skrell")
+ holo_icon = getHologramIcon(icon('icons/mob/AI.dmi',"holoskrf"))
+
+//Toggles the luminosity and applies it by re-entereing the camera.
+/mob/living/silicon/ai/proc/toggle_camera_light()
+ set name = "Toggle Camera Light"
+ set desc = "Toggles the light on the camera the AI is looking through."
+ set category = "AI Commands"
+ if(check_unable())
+ return
+
+ camera_light_on = !camera_light_on
+ to_chat(src, "Camera lights [camera_light_on ? "activated" : "deactivated"].")
+ if(!camera_light_on)
+ if(camera)
+ camera.set_light(0)
+ camera = null
+ else
+ lightNearbyCamera()
+
+
+
+// Handled camera lighting, when toggled.
+// It will get the nearest camera from the eyeobj, lighting it.
+
+/mob/living/silicon/ai/proc/lightNearbyCamera()
+ if(camera_light_on && camera_light_on < world.timeofday)
+ if(src.camera)
+ var/obj/machinery/camera/camera = near_range_camera(src.eyeobj)
+ if(camera && src.camera != camera)
+ src.camera.set_light(0)
+ if(!camera.light_disabled)
+ src.camera = camera
+ src.camera.set_light(AI_CAMERA_LUMINOSITY)
+ else
+ src.camera = null
+ else if(isnull(camera))
+ src.camera.set_light(0)
+ src.camera = null
+ else
+ var/obj/machinery/camera/camera = near_range_camera(src.eyeobj)
+ if(camera && !camera.light_disabled)
+ src.camera = camera
+ src.camera.set_light(AI_CAMERA_LUMINOSITY)
+ camera_light_on = world.timeofday + 1 * 20 // Update the light every 2 seconds.
+
+
+/mob/living/silicon/ai/attackby(obj/item/weapon/W as obj, mob/user as mob)
+ if(istype(W, /obj/item/device/aicard))
+
+ var/obj/item/device/aicard/card = W
+ card.grab_ai(src, user)
+
+ else if(W.is_wrench())
+ if(user == deployed_shell)
+ to_chat(user, "The shell's subsystems resist your efforts to tamper with your bolts.")
+ return
+ if(anchored)
+ playsound(src, W.usesound, 50, 1)
+ user.visible_message("\The [user] starts to unbolt \the [src] from the plating...")
+ if(!do_after(user,40 * W.toolspeed))
+ user.visible_message("\The [user] decides not to unbolt \the [src].")
+ return
+ user.visible_message("\The [user] finishes unfastening \the [src]!")
+ anchored = 0
+ return
+ else
+ playsound(src, W.usesound, 50, 1)
+ user.visible_message("\The [user] starts to bolt \the [src] to the plating...")
+ if(!do_after(user,40 * W.toolspeed))
+ user.visible_message("\The [user] decides not to bolt \the [src].")
+ return
+ user.visible_message("\The [user] finishes fastening down \the [src]!")
+ anchored = 1
+ return
+ else
+ return ..()
+
+/mob/living/silicon/ai/proc/control_integrated_radio()
+ set name = "Radio Settings"
+ set desc = "Allows you to change settings of your radio."
+ set category = "AI Settings"
+
+ if(check_unable(AI_CHECK_RADIO))
+ return
+
+ to_chat(src, "Accessing Subspace Transceiver control...")
+ if (src.aiRadio)
+ src.aiRadio.interact(src)
+
+/mob/living/silicon/ai/proc/sensor_mode()
+ set name = "Set Sensor Augmentation"
+ set category = "AI Settings"
+ set desc = "Augment visual feed with internal sensor overlays"
+ toggle_sensor_mode()
+
+/mob/living/silicon/ai/proc/toggle_hologram_movement()
+ set name = "Toggle Hologram Movement"
+ set category = "AI Settings"
+ set desc = "Toggles hologram movement based on moving with your virtual eye."
+
+ hologram_follow = !hologram_follow
+ //VOREStation Add - Required to stop movement because we use walk_to(wards) in hologram.dm
+ if(holo)
+ var/obj/effect/overlay/aiholo/hologram = holo.masters[src]
+ walk(hologram, 0)
+ //VOREStation Add End
+ to_chat(usr, "Your hologram will [hologram_follow ? "follow" : "no longer follow"] you now.")
+
+
+/mob/living/silicon/ai/proc/check_unable(var/flags = 0, var/feedback = 1)
+ if(stat == DEAD)
+ if(feedback)
+ to_chat(src, "You are dead!")
+ return 1
+
+ if(aiRestorePowerRoutine)
+ if(feedback)
+ to_chat(src, "You lack power!")
+ return 1
+
+ if((flags & AI_CHECK_WIRELESS) && src.control_disabled)
+ if(feedback)
+ to_chat(src, "Wireless control is disabled!")
+ return 1
+ if((flags & AI_CHECK_RADIO) && src.aiRadio.disabledAi)
+ if(feedback)
+ to_chat(src, "System Error - Transceiver Disabled!")
+ return 1
+ return 0
+
+/mob/living/silicon/ai/proc/is_in_chassis()
+ return istype(loc, /turf)
+
+/mob/living/silicon/ai/proc/open_nearest_door(mob/living/target) // Rykka ports AI opening doors
+ if(!istype(target))
+ return
+
+ if(target && ai_actual_track(target))
+ var/obj/machinery/door/airlock/A = null
+
+ var/dist = -1
+ for(var/obj/machinery/door/airlock/D in range(3, target))
+ if(!D.density)
+ continue
+
+ var/curr_dist = get_dist(D, target)
+
+ if(dist < 0)
+ dist = curr_dist
+ A = D
+ else if(dist > curr_dist)
+ dist = curr_dist
+ A = D
+
+ if(istype(A))
+ switch(alert(src, "Do you want to open \the [A] for [target]?", "Doorknob_v2a.exe", "Yes", "No"))
+ if("Yes")
+ A.AIShiftClick()
+ to_chat(src, "You open \the [A] for [target].")
+ else
+ to_chat(src, "You deny the request.")
+ else
+ to_chat(src, "Unable to locate an airlock near [target].")
+
+ else
+ to_chat(src, "Target is not on or near any active cameras on the station.")
+
+/mob/living/silicon/ai/ex_act(var/severity)
+ if(severity == 1.0)
+ qdel(src)
+ return
+ ..()
+
+/mob/living/silicon/ai/updateicon()
+ if(!selected_sprite) selected_sprite = default_ai_icon
+
+ if(stat == DEAD)
+ icon_state = selected_sprite.dead_icon
+ set_light(3, 1, selected_sprite.dead_light)
+ else if(aiRestorePowerRoutine)
+ icon_state = selected_sprite.nopower_icon
+ set_light(1, 1, selected_sprite.nopower_light)
+ else
+ icon_state = selected_sprite.alive_icon
+ set_light(1, 1, selected_sprite.alive_light)
+
+// Pass lying down or getting up to our pet human, if we're in a rig.
+/mob/living/silicon/ai/lay_down()
+ set name = "Rest"
+ set category = "IC"
+
+ resting = 0
+ var/obj/item/weapon/rig/rig = src.get_rig()
+ if(rig)
+ rig.force_rest(src)
+
+/mob/living/silicon/ai/is_sentient()
+ // AI cores don't store what brain was used to build them so we're just gonna assume they can think to some degree.
+ // If that is ever fixed please update this proc.
+ return TRUE
+
+
+/mob/living/silicon/ai/handle_track(message, verb = "says", mob/speaker = null, speaker_name, hard_to_hear)
+ if(hard_to_hear)
+ return
+
+ var/jobname // the mob's "job"
+ var/mob/living/carbon/human/impersonating //The crew member being impersonated, if any.
+ var/changed_voice
+
+ if(ishuman(speaker))
+ var/mob/living/carbon/human/H = speaker
+
+ if(H.wear_mask && istype(H.wear_mask,/obj/item/clothing/mask/gas/voice))
+ changed_voice = 1
+ var/list/impersonated = new()
+ var/mob/living/carbon/human/I = impersonated[speaker_name]
+
+ if(!I)
+ for(var/mob/living/carbon/human/M in mob_list)
+ if(M.real_name == speaker_name)
+ I = M
+ impersonated[speaker_name] = I
+ break
+
+ // If I's display name is currently different from the voice name and using an agent ID then don't impersonate
+ // as this would allow the AI to track I and realize the mismatch.
+ if(I && !(I.name != speaker_name && I.wear_id && istype(I.wear_id,/obj/item/weapon/card/id/syndicate)))
+ impersonating = I
+ jobname = impersonating.get_assignment()
+ else
+ jobname = "Unknown"
+ else
+ jobname = H.get_assignment()
+
+ else if(iscarbon(speaker)) // Nonhuman carbon mob
+ jobname = "No id"
+ else if(isAI(speaker))
+ jobname = "AI"
+ else if(isrobot(speaker))
+ jobname = "Cyborg"
+ else if(istype(speaker, /mob/living/silicon/pai))
+ jobname = "Personal AI"
+ else
+ jobname = "Unknown"
+
+ var/track = ""
+ if(changed_voice) // They have a fake name
+ if(impersonating) // And we found a mob with that name above, track them instead
+ track = "[speaker_name] ([jobname])"
+ track += "\[OPEN\]" // Rykka ports AI opening doors
+ else // We couldn't find a mob with their fake name, don't track at all
+ track = "[speaker_name] ([jobname])"
+ else // Not faking their name
+ if(istype(speaker, /mob/living/bot)) // It's a bot, and no fake name! (That'd be kinda weird.) :p
+ track = "[speaker_name] ([jobname])"
+ else // It's not a bot, and no fake name!
+ track = "[speaker_name] ([jobname])"
+ track += "\[OPEN\]" // Rykka ports AI opening doors
+
+ return track // Feed variable back to AI
+
+/mob/living/silicon/ai/proc/relay_speech(mob/living/M, list/message_pieces, verb)
+ var/list/combined = combine_message(message_pieces, verb, M)
+ var/message = combined["formatted"]
+ var/name_used = M.GetVoice()
+ //This communication is imperfect because the holopad "filters" voices and is only designed to connect to the master only.
+ var/rendered = "Relayed Speech: [name_used] [message]"
+ show_message(rendered, 2)
+
+/mob/living/silicon/ai/proc/toggle_multicam_verb()
+ set name = "Toggle Multicam"
+ set category = "AI Commands"
+ toggle_multicam()
+
+/mob/living/silicon/ai/proc/add_multicam_verb()
+ set name = "Add Multicam Viewport"
+ set category = "AI Commands"
+ drop_new_multicam()
+
+//Special subtype kept around for global announcements
+/mob/living/silicon/ai/announcer
+ is_dummy = 1
+
+/mob/living/silicon/ai/announcer/Initialize()
+ . = ..()
+ mob_list -= src
+ living_mob_list -= src
+ dead_mob_list -= src
+ ai_list -= src
+ silicon_mob_list -= src
+ QDEL_NULL(eyeobj)
+
+/mob/living/silicon/ai/announcer/Life()
+ mob_list -= src
+ living_mob_list -= src
+ dead_mob_list -= src
+ ai_list -= src
+ silicon_mob_list -= src
+ QDEL_NULL(eyeobj)
+
+#undef AI_CHECK_WIRELESS
+#undef AI_CHECK_RADIO
diff --git a/code/modules/mob/living/silicon/say.dm b/code/modules/mob/living/silicon/say.dm
index 34f6096ede0..daa144bf898 100644
--- a/code/modules/mob/living/silicon/say.dm
+++ b/code/modules/mob/living/silicon/say.dm
@@ -1,126 +1,127 @@
-/mob/living/silicon/robot/handle_message_mode(message_mode, message, verb, speaking, used_radios)
- ..()
- if(message_mode)
- if(!is_component_functioning("radio"))
- to_chat(src, "Your radio isn't functional at this time.")
- return 0
- if(message_mode == "general")
- message_mode = null
- return radio.talk_into(src,message,message_mode,verb,speaking)
-
-/mob/living/silicon/speech_bubble_appearance()
- return "synthetic"
-
-/mob/living/silicon/ai/handle_message_mode(message_mode, message, verb, speaking, used_radios)
- ..()
- if(message_mode == "department")
- return holopad_talk(message, verb, speaking)
- else if(message_mode)
- if (aiRadio.disabledAi || aiRestorePowerRoutine || stat)
- to_chat(src, "System Error - Transceiver Disabled.")
- return 0
- if(message_mode == "general")
- message_mode = null
- return aiRadio.talk_into(src,message,message_mode,verb,speaking)
-
-/mob/living/silicon/pai/handle_message_mode(message_mode, message, verb, speaking, used_radios)
- ..()
- if(message_mode)
- if(message_mode == "general")
- message_mode = null
- return radio.talk_into(src,message,message_mode,verb,speaking)
-
-/mob/living/silicon/say_quote(var/text)
- var/ending = copytext(text, length(text))
-
- if (ending == "?")
- return speak_query
- else if (ending == "!")
- return speak_exclamation
-
- return speak_statement
-
-#define IS_AI 1
-#define IS_ROBOT 2
-#define IS_PAI 3
-
-/mob/living/silicon/say_understands(var/other, var/datum/language/speaking = null)
- //These only pertain to common. Languages are handled by mob/say_understands()
- if(!speaking)
- if(iscarbon(other))
- return TRUE
- if(issilicon(other))
- return TRUE
- if(isbrain(other))
- return TRUE
- return ..()
-
-//For holopads only. Usable by AI.
-/mob/living/silicon/ai/proc/holopad_talk(list/message_pieces, verb)
- log_say("(HPAD) [multilingual_to_message(message_pieces)]",src)
-
- var/obj/machinery/hologram/holopad/T = src.holo
- if(T && T.masters[src])//If there is a hologram and its master is the user.
- var/list/listeners = get_mobs_and_objs_in_view_fast(get_turf(T), world.view)
- var/list/listening = listeners["mobs"]
- var/list/listening_obj = listeners["objs"]
- for(var/mob/M in listening)
- M.hear_holopad_talk(message_pieces, verb, src)
- for(var/obj/O in listening_obj)
- if(O == T) //Don't recieve your own speech
- continue
- O.hear_talk(src, message_pieces, verb)
- /*Radios "filter out" this conversation channel so we don't need to account for them.
- This is another way of saying that we won't bother dealing with them.*/
- to_chat(src, "Holopad transmitted, [real_name] [combine_message(message_pieces, verb, src)]")
- else
- to_chat(src, "No holopad connected.")
- return 0
- return 1
-
-/mob/living/silicon/ai/proc/holopad_emote(var/message) //This is called when the AI uses the 'me' verb while using a holopad.
- message = trim(message)
-
- if(!message)
- return
-
- var/obj/machinery/hologram/holopad/T = src.holo
- if(T && T.masters[src])
- var/rendered = "[name] [message]"
- to_chat(src, "Holopad action relayed, [real_name] [message]")
- var/obj/effect/overlay/aiholo/hologram = T.masters[src] //VOREStation Add for people in the hologram to hear the messages
-
- //var/obj/effect/overlay/hologram = T.masters[src] //VOREStation edit. Done above.
- var/list/in_range = get_mobs_and_objs_in_view_fast(get_turf(hologram), world.view, 2) //Emotes are displayed from the hologram, not the pad
- var/list/m_viewers = in_range["mobs"]
- var/list/o_viewers = in_range["objs"]
-
- for(var/mob/M in m_viewers)
- spawn(0)
- if(M)
- M.show_message(rendered, 2)
-
- for(var/obj/O in o_viewers)
- if(O == T)
- continue
- spawn(0)
- if(O)
- O.see_emote(src, message)
-
- log_emote("(HPAD) [message]", src)
-
- else //This shouldn't occur, but better safe then sorry.
- to_chat(src, "No holopad connected.")
- return 0
- return 1
-
-/mob/living/silicon/ai/emote(var/act, var/m_type, var/message)
- var/obj/machinery/hologram/holopad/T = holo
- if(T && T.masters[src]) //Is the AI using a holopad?
- . = holopad_emote(message)
- else //Emote normally, then.
- . = ..()
-
-#undef IS_AI
-#undef IS_ROBOT
-#undef IS_PAI
+/mob/living/silicon/robot/handle_message_mode(message_mode, message, verb, speaking, used_radios)
+ ..()
+ if(message_mode)
+ if(!is_component_functioning("radio"))
+ to_chat(src, "Your radio isn't functional at this time.")
+ return 0
+ if(message_mode == "general")
+ message_mode = null
+ return radio.talk_into(src,message,message_mode,verb,speaking)
+
+/mob/living/silicon/speech_bubble_appearance()
+ return "synthetic"
+
+/mob/living/silicon/ai/handle_message_mode(message_mode, message, verb, speaking, used_radios)
+ ..()
+ if(message_mode == "department")
+ return holopad_talk(message, verb, speaking)
+ else if(message_mode)
+ if (aiRadio.disabledAi || aiRestorePowerRoutine || stat)
+ to_chat(src, "System Error - Transceiver Disabled.")
+ return 0
+ if(message_mode == "general")
+ message_mode = null
+ return aiRadio.talk_into(src,message,message_mode,verb,speaking)
+
+/mob/living/silicon/pai/handle_message_mode(message_mode, message, verb, speaking, used_radios)
+ ..()
+ if(message_mode)
+ if(message_mode == "general")
+ message_mode = null
+ return radio.talk_into(src,message,message_mode,verb,speaking)
+
+/mob/living/silicon/say_quote(var/text)
+ var/ending = copytext(text, length(text))
+
+ if (ending == "?")
+ return speak_query
+ else if (ending == "!")
+ return speak_exclamation
+
+ return speak_statement
+
+#define IS_AI 1
+#define IS_ROBOT 2
+#define IS_PAI 3
+
+/mob/living/silicon/say_understands(var/other, var/datum/language/speaking = null)
+ //These only pertain to common. Languages are handled by mob/say_understands()
+ if(!speaking)
+ if(iscarbon(other))
+ return TRUE
+ if(issilicon(other))
+ return TRUE
+ if(isbrain(other))
+ return TRUE
+ return ..()
+
+//For holopads only. Usable by AI.
+/mob/living/silicon/ai/proc/holopad_talk(list/message_pieces, verb)
+ log_say("(HPAD) [multilingual_to_message(message_pieces)]",src)
+
+ var/obj/machinery/hologram/holopad/T = src.holo
+ if(T && T.masters[src])//If there is a hologram and its master is the user.
+ var/list/listeners = get_mobs_and_objs_in_view_fast(get_turf(T), world.view)
+ var/list/listening = listeners["mobs"]
+ var/list/listening_obj = listeners["objs"]
+ for(var/mob/M in listening)
+ M.hear_holopad_talk(message_pieces, verb, src)
+ for(var/obj/O in listening_obj)
+ if(O == T) //Don't recieve your own speech
+ continue
+ O.hear_talk(src, message_pieces, verb)
+ /*Radios "filter out" this conversation channel so we don't need to account for them.
+ This is another way of saying that we won't bother dealing with them.*/
+ var/list/combined = combine_message(message_pieces, verb, src)
+ to_chat(src, "Holopad transmitted, [real_name] [combined["formatted"]]")
+ else
+ to_chat(src, "No holopad connected.")
+ return 0
+ return 1
+
+/mob/living/silicon/ai/proc/holopad_emote(var/message) //This is called when the AI uses the 'me' verb while using a holopad.
+ message = trim(message)
+
+ if(!message)
+ return
+
+ var/obj/machinery/hologram/holopad/T = src.holo
+ if(T && T.masters[src])
+ var/rendered = "[name] [message]"
+ to_chat(src, "Holopad action relayed, [real_name] [message]")
+ var/obj/effect/overlay/aiholo/hologram = T.masters[src] //VOREStation Add for people in the hologram to hear the messages
+
+ //var/obj/effect/overlay/hologram = T.masters[src] //VOREStation edit. Done above.
+ var/list/in_range = get_mobs_and_objs_in_view_fast(get_turf(hologram), world.view, 2) //Emotes are displayed from the hologram, not the pad
+ var/list/m_viewers = in_range["mobs"]
+ var/list/o_viewers = in_range["objs"]
+
+ for(var/mob/M in m_viewers)
+ spawn(0)
+ if(M)
+ M.show_message(rendered, 2)
+
+ for(var/obj/O in o_viewers)
+ if(O == T)
+ continue
+ spawn(0)
+ if(O)
+ O.see_emote(src, message)
+
+ log_emote("(HPAD) [message]", src)
+
+ else //This shouldn't occur, but better safe then sorry.
+ to_chat(src, "No holopad connected.")
+ return 0
+ return 1
+
+/mob/living/silicon/ai/emote(var/act, var/m_type, var/message)
+ var/obj/machinery/hologram/holopad/T = holo
+ if(T && T.masters[src]) //Is the AI using a holopad?
+ . = holopad_emote(message)
+ else //Emote normally, then.
+ . = ..()
+
+#undef IS_AI
+#undef IS_ROBOT
+#undef IS_PAI
diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm
index 00d705f8e1a..547bb19304e 100644
--- a/code/modules/mob/mob.dm
+++ b/code/modules/mob/mob.dm
@@ -77,7 +77,7 @@
// message is the message output to anyone who can see e.g. "[src] does something!"
// self_message (optional) is what the src mob sees e.g. "You do something!"
// blind_message (optional) is what blind people will hear e.g. "You hear something!"
-/mob/visible_message(var/message, var/self_message, var/blind_message, var/list/exclude_mobs = null, var/range = world.view)
+/mob/visible_message(var/message, var/self_message, var/blind_message, var/list/exclude_mobs = null, var/range = world.view, var/runemessage)
if(self_message)
if(LAZYLEN(exclude_mobs))
exclude_mobs |= src
@@ -87,7 +87,9 @@
// Transfer messages about what we are doing to upstairs
if(shadow)
shadow.visible_message(message, self_message, blind_message, exclude_mobs, range)
- . = ..(message, blind_message, exclude_mobs, range) // Really not ideal that atom/visible_message has different arg numbering :(
+ if(isnull(runemessage))
+ runemessage = -1
+ . = ..(message, blind_message, exclude_mobs, range, runemessage) // Really not ideal that atom/visible_message has different arg numbering :(
// Returns an amount of power drawn from the object (-1 if it's not viable).
// If drain_check is set it will not actually drain power, just return a value.
@@ -102,7 +104,7 @@
// self_message (optional) is what the src mob hears.
// deaf_message (optional) is what deaf people will see.
// hearing_distance (optional) is the range, how many tiles away the message can be heard.
-/mob/audible_message(var/message, var/deaf_message, var/hearing_distance, var/self_message, var/radio_message)
+/mob/audible_message(var/message, var/deaf_message, var/hearing_distance, var/self_message, var/radio_message, var/runemessage)
var/range = hearing_distance || world.view
var/list/hear = get_mobs_and_objs_in_view_fast(get_turf(src),range,remote_ghosts = FALSE)
@@ -110,6 +112,9 @@
var/list/hearing_mobs = hear["mobs"]
var/list/hearing_objs = hear["objs"]
+ if(isnull(runemessage))
+ runemessage = -1 // Symmetry with mob/audible_message, despite the fact this one doesn't call parent. Maybe it should!
+
if(radio_message)
for(var/obj in hearing_objs)
var/obj/O = obj
@@ -125,6 +130,8 @@
if(self_message && M==src)
msg = self_message
M.show_message(msg, AUDIBLE_MESSAGE, deaf_message, VISIBLE_MESSAGE)
+ if(runemessage != -1)
+ M.create_chat_message(src, "[runemessage || message]", FALSE, list("emote"), audible = FALSE)
/mob/proc/findname(msg)
for(var/mob/M in mob_list)
diff --git a/code/modules/mob/mob_defines_vr.dm b/code/modules/mob/mob_defines_vr.dm
index ff5f1ae5826..3854620e7c1 100644
--- a/code/modules/mob/mob_defines_vr.dm
+++ b/code/modules/mob/mob_defines_vr.dm
@@ -7,6 +7,8 @@
var/obj/screen/shadekin/shadekin_display = null
var/obj/screen/xenochimera/danger_level/xenochimera_danger_display = null
+ var/size_multiplier = 1 //multiplier for the mob's icon size
+
/mob/drop_location()
if(temporary_form)
return temporary_form.drop_location()
diff --git a/code/modules/multiz/ladders.dm b/code/modules/multiz/ladders.dm
index cca3019543c..c9797203027 100644
--- a/code/modules/multiz/ladders.dm
+++ b/code/modules/multiz/ladders.dm
@@ -98,7 +98,7 @@
"You begin climbing [direction] \the [src]!",
"You hear the grunting and clanging of a metal ladder being used.")
- target_ladder.audible_message("You hear something coming [direction] \the [src]")
+ target_ladder.audible_message("You hear something coming [direction] \the [src]", runemessage = "* clank clank *")
if(do_after(M, climb_time, src))
var/turf/T = get_turf(target_ladder)
diff --git a/code/modules/multiz/movement.dm b/code/modules/multiz/movement.dm
index 40447a5a4b9..1c197530e5e 100644
--- a/code/modules/multiz/movement.dm
+++ b/code/modules/multiz/movement.dm
@@ -59,7 +59,7 @@
if(lattice)
var/pull_up_time = max(5 SECONDS + (src.movement_delay() * 10), 1)
to_chat(src, "You grab \the [lattice] and start pulling yourself upward...")
- destination.audible_message("You hear something climbing up \the [lattice].")
+ destination.audible_message("You hear something climbing up \the [lattice].", runemessage = "* clank clang *")
if(do_after(src, pull_up_time))
to_chat(src, "You pull yourself up.")
else
@@ -74,7 +74,7 @@
if(!destination?.Enter(src, old_dest))
to_chat(src, "There's something in the way up above in that direction, try another.")
return 0
- destination.audible_message("You hear something climbing up \the [catwalk].")
+ destination.audible_message("You hear something climbing up \the [catwalk].", runemessage = "* clank clang *")
if(do_after(src, pull_up_time))
to_chat(src, "You pull yourself up.")
else
@@ -90,8 +90,8 @@
return 0
var/fly_time = max(7 SECONDS + (H.movement_delay() * 10), 1) //So it's not too useful for combat. Could make this variable somehow, but that's down the road.
to_chat(src, "You begin to fly upwards...")
- destination.audible_message("You hear the flapping of wings.")
- H.audible_message("[H] begins to flap \his wings, preparing to move upwards!")
+ destination.audible_message("You hear the flapping of wings.", runemessage = "* flap flap *")
+ H.audible_message("[H] begins to flap \his wings, preparing to move upwards!", runemessage = "* flap flap *")
if(do_after(H, fly_time) && H.flying)
to_chat(src, "You fly upwards.")
else
diff --git a/code/modules/nifsoft/software/14_commlink.dm b/code/modules/nifsoft/software/14_commlink.dm
index 34e36f65276..93e1af799c3 100644
--- a/code/modules/nifsoft/software/14_commlink.dm
+++ b/code/modules/nifsoft/software/14_commlink.dm
@@ -77,7 +77,8 @@
mobs_to_relay = in_range["mobs"]
for(var/mob/mob in mobs_to_relay)
- var/message = mob.combine_message(message_pieces, verb, M)
+ var/list/combined = mob.combine_message(message_pieces, verb, M)
+ var/message = combined["formatted"]
var/name_used = M.GetVoice()
var/rendered = null
rendered = "[bicon(icon_object)] [name_used] [message]"
diff --git a/code/modules/overmap/ships/engines/gas_thruster.dm b/code/modules/overmap/ships/engines/gas_thruster.dm
index 667cbae441b..182fd6c3f32 100644
--- a/code/modules/overmap/ships/engines/gas_thruster.dm
+++ b/code/modules/overmap/ships/engines/gas_thruster.dm
@@ -150,7 +150,7 @@
if(!is_on())
return 0
if(!check_fuel() || (use_power_oneoff(charge_per_burn) < charge_per_burn) || check_blockage())
- audible_message(src,"[src] coughs once and goes silent!")
+ audible_message(src,"[src] coughs once and goes silent!", runemessage = "* sputtercough *")
update_use_power(USE_POWER_OFF)
return 0
diff --git a/code/modules/paperwork/photocopier.dm b/code/modules/paperwork/photocopier.dm
index 07009a99c09..2306b3eca1f 100644
--- a/code/modules/paperwork/photocopier.dm
+++ b/code/modules/paperwork/photocopier.dm
@@ -111,28 +111,28 @@
playsound(src, "sound/machines/copier.ogg", 100, 1)
sleep(11)
copy(copyitem)
- audible_message("You can hear [src] whirring as it finishes printing.")
+ audible_message("You can hear [src] whirring as it finishes printing.", runemessage = "* whirr *")
playsound(src, "sound/machines/buzzbeep.ogg", 30)
else if (istype(copyitem, /obj/item/weapon/photo))
playsound(src, "sound/machines/copier.ogg", 100, 1)
sleep(11)
photocopy(copyitem)
- audible_message("You can hear [src] whirring as it finishes printing.")
+ audible_message("You can hear [src] whirring as it finishes printing.", runemessage = "* whirr *")
playsound(src, "sound/machines/buzzbeep.ogg", 30)
else if (istype(copyitem, /obj/item/weapon/paper_bundle))
sleep(11)
playsound(src, "sound/machines/copier.ogg", 100, 1)
var/obj/item/weapon/paper_bundle/B = bundlecopy(copyitem)
sleep(11*B.pages.len)
- audible_message("You can hear [src] whirring as it finishes printing.")
+ audible_message("You can hear [src] whirring as it finishes printing.", runemessage = "* whirr *")
playsound(src, "sound/machines/buzzbeep.ogg", 30)
else if (has_buckled_mobs()) // VOREStation EDIT: For ass-copying.
playsound(src, "sound/machines/copier.ogg", 100, 1)
- audible_message("You can hear [src] whirring as it attempts to scan.")
+ audible_message("You can hear [src] whirring as it attempts to scan.", runemessage = "* whirr *")
sleep(rand(20,45)) // Sit with your bare ass on the copier for a random time, feel like a fool, get stared at.
copyass(user)
sleep(15)
- audible_message("You can hear [src] whirring as it finishes printing.")
+ audible_message("You can hear [src] whirring as it finishes printing.", runemessage = "* whirr *")
playsound(src, "sound/machines/buzzbeep.ogg", 30)
else
to_chat(user, "\The [copyitem] can't be copied by [src].")
diff --git a/code/modules/projectiles/guns/energy/cell_loaded_vr/cell_loaded.dm b/code/modules/projectiles/guns/energy/cell_loaded_vr/cell_loaded.dm
index 4f95b54d5a3..b5d6f8172ba 100644
--- a/code/modules/projectiles/guns/energy/cell_loaded_vr/cell_loaded.dm
+++ b/code/modules/projectiles/guns/energy/cell_loaded_vr/cell_loaded.dm
@@ -243,6 +243,7 @@
desc = "A storage case for a multi-purpose handgun. Variety hour!"
w_class = ITEMSIZE_NORMAL
max_w_class = ITEMSIZE_NORMAL
+ can_hold = list(/obj/item/weapon/gun/projectile/cell_loaded,/obj/item/ammo_magazine/cell_mag,/obj/item/ammo_casing/microbattery)
/obj/item/weapon/storage/secure/briefcase/nsfw_pack_hybrid/New()
..()
@@ -264,6 +265,7 @@
desc = "A storage case for a multi-purpose handgun. Variety hour!"
w_class = ITEMSIZE_NORMAL
max_w_class = ITEMSIZE_NORMAL
+ can_hold = list(/obj/item/weapon/gun/projectile/cell_loaded,/obj/item/ammo_magazine/cell_mag,/obj/item/ammo_casing/microbattery)
/obj/item/weapon/storage/secure/briefcase/nsfw_pack_hybrid_combat/New()
..()
diff --git a/code/modules/projectiles/guns/energy/cell_loaded_vr/ml3m.dm b/code/modules/projectiles/guns/energy/cell_loaded_vr/ml3m.dm
index b7aab78f0df..0632b3603d5 100644
--- a/code/modules/projectiles/guns/energy/cell_loaded_vr/ml3m.dm
+++ b/code/modules/projectiles/guns/energy/cell_loaded_vr/ml3m.dm
@@ -47,6 +47,7 @@
desc = "A storage case for a multi-purpose healing gun. Variety hour!"
w_class = ITEMSIZE_NORMAL
max_w_class = ITEMSIZE_NORMAL
+ can_hold = list(/obj/item/weapon/gun/projectile/cell_loaded/medical,/obj/item/ammo_magazine/cell_mag/medical,/obj/item/ammo_casing/microbattery/medical)
/obj/item/weapon/storage/secure/briefcase/ml3m_pack_med/New()
..()
@@ -61,6 +62,7 @@
desc = "A storage case for a multi-purpose healing gun. Variety hour!"
w_class = ITEMSIZE_NORMAL
max_w_class = ITEMSIZE_NORMAL
+ can_hold = list(/obj/item/weapon/gun/projectile/cell_loaded/medical,/obj/item/ammo_magazine/cell_mag/medical,/obj/item/ammo_casing/microbattery/medical)
/obj/item/weapon/storage/secure/briefcase/ml3m_pack_cmo/New()
..()
diff --git a/code/modules/projectiles/guns/energy/cell_loaded_vr/nsfw.dm b/code/modules/projectiles/guns/energy/cell_loaded_vr/nsfw.dm
index 241e2e2c1dc..65342ce26a4 100644
--- a/code/modules/projectiles/guns/energy/cell_loaded_vr/nsfw.dm
+++ b/code/modules/projectiles/guns/energy/cell_loaded_vr/nsfw.dm
@@ -48,6 +48,7 @@
desc = "A storage case for a multi-purpose handgun. Variety hour!"
w_class = ITEMSIZE_NORMAL
max_w_class = ITEMSIZE_NORMAL
+ can_hold = list(/obj/item/weapon/gun/projectile/cell_loaded/combat,/obj/item/ammo_magazine/cell_mag/combat,/obj/item/ammo_casing/microbattery/combat)
/obj/item/weapon/storage/secure/briefcase/nsfw_pack/New()
..()
@@ -61,6 +62,7 @@
desc = "A storage case for a multi-purpose handgun. Variety hour!"
w_class = ITEMSIZE_NORMAL
max_w_class = ITEMSIZE_NORMAL
+ can_hold = list(/obj/item/weapon/gun/projectile/cell_loaded/combat,/obj/item/ammo_magazine/cell_mag/combat,/obj/item/ammo_casing/microbattery/combat)
/obj/item/weapon/storage/secure/briefcase/nsfw_pack_hos/New()
..()
diff --git a/code/modules/projectiles/guns/magnetic/bore.dm b/code/modules/projectiles/guns/magnetic/bore.dm
index 897a43d8880..a39e383afff 100644
--- a/code/modules/projectiles/guns/magnetic/bore.dm
+++ b/code/modules/projectiles/guns/magnetic/bore.dm
@@ -208,7 +208,7 @@
/obj/item/weapon/gun/magnetic/matfed/phoronbore/process()
if(generator_state && !mat_storage)
- audible_message(SPAN_NOTICE("\The [src] goes quiet."),SPAN_NOTICE("A motor noise cuts out."))
+ audible_message(SPAN_NOTICE("\The [src] goes quiet."),SPAN_NOTICE("A motor noise cuts out."), runemessage = "* goes quiet *")
soundloop.stop()
generator_state = GEN_OFF
@@ -258,12 +258,12 @@
soundloop.start()
time_started = world.time
cell?.use(100)
- audible_message(SPAN_NOTICE("\The [src] starts chugging."),SPAN_NOTICE("A motor noise starts up."))
+ audible_message(SPAN_NOTICE("\The [src] starts chugging."),SPAN_NOTICE("A motor noise starts up."), runemessage = "* whirr *")
generator_state = GEN_IDLE
else if(generator_state > GEN_OFF && time_started + 3 SECONDS < world.time)
soundloop.stop()
- audible_message(SPAN_NOTICE("\The [src] goes quiet."),SPAN_NOTICE("A motor noise cuts out."))
+ audible_message(SPAN_NOTICE("\The [src] goes quiet."),SPAN_NOTICE("A motor noise cuts out."), runemessage = "* goes quiet *")
generator_state = GEN_OFF
/obj/item/weapon/gun/magnetic/matfed/phoronbore/loaded
diff --git a/code/modules/projectiles/guns/magnetic/magnetic.dm b/code/modules/projectiles/guns/magnetic/magnetic.dm
index 1b13bd8f8c4..babeb7643a1 100644
--- a/code/modules/projectiles/guns/magnetic/magnetic.dm
+++ b/code/modules/projectiles/guns/magnetic/magnetic.dm
@@ -284,7 +284,7 @@
visible_message("\The [src] begins to rattle, its acceleration chamber collapsing in on itself!")
removable_components = FALSE
spawn(15)
- audible_message("\The [src]'s power supply begins to overload as the device crumples!") //Why are you still holding this?
+ audible_message("\The [src]'s power supply begins to overload as the device crumples!", runemessage = "* VWRRRRRRRR *") //Why are you still holding this?
playsound(src, 'sound/effects/grillehit.ogg', 10, 1)
var/datum/effect/effect/system/spark_spread/sparks = new /datum/effect/effect/system/spark_spread()
var/turf/T = get_turf(src)
diff --git a/code/modules/random_map/automata/caves.dm b/code/modules/random_map/automata/caves.dm
index 58e61045b35..ee9d4ddf542 100644
--- a/code/modules/random_map/automata/caves.dm
+++ b/code/modules/random_map/automata/caves.dm
@@ -47,18 +47,19 @@
if(!current_cell)
return 0
var/turf/simulated/mineral/T = locate((origin_x-1)+x,(origin_y-1)+y,origin_z)
- if(istype(T) && !T.ignore_mapgen && !T.ignore_cavegen) //VOREStation Edit: ignore cavegen
- if(map[current_cell] == FLOOR_CHAR)
- T.make_floor() //VOREStation Edit - Don't make cracked sand on surface map, jerk.
- //if(prob(90))
- //T.make_floor()
- //else
- //T.ChangeTurf(/turf/space/cracked_asteroid)
- else
- T.make_wall()
+ //VOREStation Edit Start
+ if(istype(T) && !T.ignore_mapgen)
+ if(!T.ignore_cavegen)
+ if(map[current_cell] == FLOOR_CHAR)
+ T.make_floor()
+ else
+ T.make_wall()
+
+ if(T.density && !T.ignore_oregen)
if(map[current_cell] == DOOR_CHAR)
T.make_ore()
else if(map[current_cell] == EMPTY_CHAR)
T.make_ore(1)
get_additional_spawns(map[current_cell],T,get_spawn_dir(x, y))
+ //VOREStation Edit End
return T
\ No newline at end of file
diff --git a/code/modules/reagents/reactions/instant/instant_vr.dm b/code/modules/reagents/reactions/instant/instant_vr.dm
index b2fef2977c7..22f6ea95110 100644
--- a/code/modules/reagents/reactions/instant/instant_vr.dm
+++ b/code/modules/reagents/reactions/instant/instant_vr.dm
@@ -47,6 +47,58 @@
s.start()
holder.clear_reagents()
+///////////////////////////////////////////////////////////////////////////////////
+/// TF chemicals
+/decl/chemical_reaction/instant/amorphorovir
+ name = "Amorphorovir"
+ id = "amorphorovir"
+ result = "amorphorovir"
+ required_reagents = list("cryptobiolin" = 30, "biomass" = 30, "hyperzine" = 20)
+ catalysts = list("phoron" = 5)
+ result_amount = 1
+
+/decl/chemical_reaction/instant/androrovir
+ name = "Androrovir"
+ id = "androrovir"
+ result = "androrovir"
+ required_reagents = list("amorphorovir" = 1, "bicaridine" = 20, "iron" = 20, "ethanol" = 20)
+ result_amount = 1
+
+/decl/chemical_reaction/instant/gynorovir
+ name = "Gynorovir"
+ id = "gynorovir"
+ result = "gynorovir"
+ required_reagents = list("amorphorovir" = 1, "inaprovaline" = 20, "silicon" = 20, "sugar" = 20)
+ result_amount = 1
+
+/decl/chemical_reaction/instant/androgynorovir
+ name = "Androgynorovir"
+ id = "androgynorovir"
+ result = "androgynorovir"
+ required_reagents = list("amorphorovir" = 1, "anti_toxin" = 20, "fluorine" = 20, "tungsten" = 20)
+ result_amount = 1
+
+/decl/chemical_reaction/instant/androrovir_bootleg
+ name = "Bootleg Androrovir"
+ id = "androrovir_bootleg"
+ result = "androrovir"
+ required_reagents = list("amorphorovir" = 1, "protein" = 10, "capsaicin" = 10)
+ result_amount = 1
+
+/decl/chemical_reaction/instant/gynorovir_bootleg
+ name = "Bootleg Gynorovir"
+ id = "gynorovir_bootleg"
+ result = "gynorovir"
+ required_reagents = list("amorphorovir" = 1, "soymilk" = 10, "sugar" = 10)
+ result_amount = 1
+
+/decl/chemical_reaction/instant/androgynorovir_bootleg
+ name = "Bootleg Androgynorovir"
+ id = "androgynorovir_bootleg"
+ result = "androgynorovir"
+ required_reagents = list("amorphorovir" = 1, "cola" = 10, "berryjuice" = 10)
+ result_amount = 1
+
///////////////////////////////////////////////////////////////////////////////////
/// Miscellaneous Reactions
@@ -85,7 +137,7 @@
required_reagents = list("water" = 1)
catalysts = list("fluorine" = 10)
result_amount = 1
-
+
/decl/chemical_reaction/instant/firefightingfoamqol //Please don't abuse this and make us remove it. Seriously.
name = "Firefighting Foam EZ"
id = "firefighting foam ez"
diff --git a/code/modules/reagents/reagent_containers/glass_vr.dm b/code/modules/reagents/reagent_containers/glass_vr.dm
index 79c19b82880..86601ea777a 100644
--- a/code/modules/reagents/reagent_containers/glass_vr.dm
+++ b/code/modules/reagents/reagent_containers/glass_vr.dm
@@ -49,6 +49,10 @@
name = "vial (hyronalin)"
prefill = list("hyronalin" = 30)
+/obj/item/weapon/reagent_containers/glass/beaker/vial/amorphorovir
+ name = "vial (amorphorovir)"
+ prefill = list("amorphorovir" = 1)
+
/obj/item/weapon/reagent_containers/glass/beaker/measuring_cup
name = "measuring cup"
desc = "A measuring cup."
diff --git a/code/modules/reagents/reagents/other_vr.dm b/code/modules/reagents/reagents/other_vr.dm
index b1146960ed9..f0286667df1 100644
--- a/code/modules/reagents/reagents/other_vr.dm
+++ b/code/modules/reagents/reagents/other_vr.dm
@@ -6,6 +6,8 @@
color = "#13BC5E"
/datum/reagent/advmutationtoxin/affect_blood(var/mob/living/carbon/M, var/alien, var/removed)
+ if(!(M.allow_spontaneous_tf))
+ return
if(ishuman(M))
var/mob/living/carbon/human/H = M
if(H.species.name != "Promethean")
@@ -119,3 +121,16 @@
if(nif.stat == NIF_TEMPFAIL)
nif.stat = NIF_INSTALLING
nif.durability = min(nif.durability + removed*0.1, initial(nif.durability))
+
+//Special toxins for solargrubs
+/datum/reagent/grubshock
+ name = "200 V" //in other words a painful shock
+ id = "shockchem"
+ description = "A liquid that quickly dissapates to deliver a painful shock."
+ reagent_state = LIQUID
+ color = "#E4EC2F"
+ metabolism = 2.50
+ var/power = 9
+
+/datum/reagent/grubshock/affect_blood(var/mob/living/carbon/M, var/alien, var/removed)
+ M.take_organ_damage(0, removed * power * 0.2)
diff --git a/code/modules/reagents/reagents/vore_vr.dm b/code/modules/reagents/reagents/vore_vr.dm
index 20af3a57e3f..a86c755c3de 100644
--- a/code/modules/reagents/reagents/vore_vr.dm
+++ b/code/modules/reagents/reagents/vore_vr.dm
@@ -117,16 +117,74 @@
P.absorbed = 0
M.visible_message("Something spills into [M]'s [lowertext(B.name)]!")
-//Special toxins for solargrubs
+////////////////////////// TF Drugs //////////////////////////
-/datum/reagent/grubshock
- name = "200 V" //in other words a painful shock
- id = "shockchem"
- description = "A liquid that quickly dissapates to deliver a painful shock."
+/datum/reagent/amorphorovir
+ name = "Amorphorovir"
+ id = "amorphorovir"
+ description = "A base medical concoction, capable of rapidly altering genetic and physical structure of the body. Requires extra processing to allow for a targeted transformation."
reagent_state = LIQUID
- color = "#E4EC2F"
- metabolism = 2.50
- var/power = 9
+ color = "#AAAAAA"
-/datum/reagent/grubshock/affect_blood(var/mob/living/carbon/M, var/alien, var/removed)
- M.take_organ_damage(0, removed * power * 0.2)
+/datum/reagent/androrovir
+ name = "Androrovir"
+ id = "androrovir"
+ description = "A medical concoction, capable of rapidly altering genetic and physical structure of the body. This one seems to realign the target's gender to be male."
+ reagent_state = LIQUID
+ color = "#00BBFF"
+
+/datum/reagent/androrovir/affect_blood(var/mob/living/carbon/M, var/alien, var/removed)
+ if(!(M.allow_spontaneous_tf))
+ return
+ if(ishuman(M))
+ var/mob/living/carbon/human/H = M
+ if(M.reagents.has_reagent("gynorovir") || M.reagents.has_reagent("androgynorovir"))
+ H.Confuse(1)
+ else
+ if(!(H.gender == MALE))
+ H.set_gender(MALE)
+ H.change_gender_identity(MALE)
+ H.visible_message("[H] suddenly twitches as some of their features seem to contort and reshape, adjusting... In the end, it seems they are now male.",
+ "Your body suddenly contorts, feeling very different in various ways... By the time the rushing feeling is over it seems you just became male.")
+
+/datum/reagent/gynorovir
+ name = "Gynorovir"
+ id = "gynorovir"
+ description = "A medical concoction, capable of rapidly altering genetic and physical structure of the body. This one seems to realign the target's gender to be female."
+ reagent_state = LIQUID
+ color = "#FF00AA"
+
+/datum/reagent/gynorovir/affect_blood(var/mob/living/carbon/M, var/alien, var/removed)
+ if(!(M.allow_spontaneous_tf))
+ return
+ if(ishuman(M))
+ var/mob/living/carbon/human/H = M
+ if(M.reagents.has_reagent("androrovir") || M.reagents.has_reagent("androgynorovir"))
+ H.Confuse(1)
+ else
+ if(!(H.gender == FEMALE))
+ H.set_gender(FEMALE)
+ H.change_gender_identity(FEMALE)
+ H.visible_message("[H] suddenly twitches as some of their features seem to contort and reshape, adjusting... In the end, it seems they are now female.",
+ "Your body suddenly contorts, feeling very different in various ways... By the time the rushing feeling is over it seems you just became female.")
+
+/datum/reagent/androgynorovir
+ name = "Androgynorovir"
+ id = "androgynorovir"
+ description = "A medical concoction, capable of rapidly altering genetic and physical structure of the body. This one seems to realign the target's gender to be mixed."
+ reagent_state = LIQUID
+ color = "#6600FF"
+
+/datum/reagent/androgynorovir/affect_blood(var/mob/living/carbon/M, var/alien, var/removed)
+ if(!(M.allow_spontaneous_tf))
+ return
+ if(ishuman(M))
+ var/mob/living/carbon/human/H = M
+ if(M.reagents.has_reagent("gynorovir") || M.reagents.has_reagent("androrovir"))
+ H.Confuse(1)
+ else
+ if(!(H.gender == PLURAL))
+ H.set_gender(PLURAL)
+ H.change_gender_identity(PLURAL)
+ H.visible_message("[H] suddenly twitches as some of their features seem to contort and reshape, adjusting... In the end, it seems they are now of mixed gender.",
+ "Your body suddenly contorts, feeling very different in various ways... By the time the rushing feeling is over it seems you just became of mixed gender.")
\ No newline at end of file
diff --git a/code/modules/research/message_server.dm b/code/modules/research/message_server.dm
index 81f888155d1..ae745dcc1fd 100644
--- a/code/modules/research/message_server.dm
+++ b/code/modules/research/message_server.dm
@@ -130,12 +130,12 @@ var/global/list/obj/machinery/message_server/message_servers = list()
if(2)
if(!Console.silent)
playsound(Console, 'sound/machines/twobeep.ogg', 50, 1)
- Console.audible_message(text("[bicon(Console)] *The Requests Console beeps: 'PRIORITY Alert in [sender]'"),,5)
+ Console.audible_message(text("[bicon(Console)] *The Requests Console beeps: 'PRIORITY Alert in [sender]'"),,5, runemessage = "* beep! beep! *")
Console.message_log += list(list("High Priority message from [sender]", "[authmsg]"))
else
if(!Console.silent)
playsound(Console, 'sound/machines/twobeep.ogg', 50, 1)
- Console.audible_message(text("[bicon(Console)] *The Requests Console beeps: 'Message from [sender]'"),,4)
+ Console.audible_message(text("[bicon(Console)] *The Requests Console beeps: 'Message from [sender]'"),,4, runemessage = "* beep beep *")
Console.message_log += list(list("Message from [sender]", "[authmsg]"))
Console.set_light(2)
diff --git a/code/modules/resleeving/computers.dm b/code/modules/resleeving/computers.dm
index c8f6f21083f..be2a694780c 100644
--- a/code/modules/resleeving/computers.dm
+++ b/code/modules/resleeving/computers.dm
@@ -25,11 +25,17 @@
var/obj/machinery/transhuman/synthprinter/selected_printer
var/obj/machinery/transhuman/resleever/selected_sleever
+ // Resleeving database this machine interacts with. Blank for default database
+ // Needs a matching /datum/transcore_db with key defined in code
+ var/db_key
+ var/datum/transcore_db/our_db // These persist all round and are never destroyed, just keep a hard ref
+
/obj/machinery/computer/transhuman/resleeving/Initialize()
. = ..()
pods = list()
spods = list()
sleevers = list()
+ our_db = SStranscore.db_by_key(db_key)
updatemodules()
/obj/machinery/computer/transhuman/resleeving/Destroy()
@@ -82,7 +88,7 @@
P.connected = src
P.name = "[initial(P.name)] #[pods.len]"
to_chat(user, "You connect [P] to [src].")
- else if(istype(W, /obj/item/weapon/disk/transcore) && SStranscore && !SStranscore.core_dumped)
+ else if(istype(W, /obj/item/weapon/disk/transcore) && !our_db.core_dumped)
user.unEquip(W)
disk = W
disk.forceMove(src)
@@ -172,7 +178,7 @@
data["sleevers"] = temppods.Copy()
temppods.Cut()
- data["coredumped"] = SStranscore.core_dumped
+ data["coredumped"] = our_db.core_dumped
data["emergency"] = disk
data["temp"] = temp
data["selected_pod"] = "\ref[selected_pod]"
@@ -180,14 +186,14 @@
data["selected_sleever"] = "\ref[selected_sleever]"
var/bodyrecords_list_ui[0]
- for(var/N in SStranscore.body_scans)
- var/datum/transhuman/body_record/BR = SStranscore.body_scans[N]
+ for(var/N in our_db.body_scans)
+ var/datum/transhuman/body_record/BR = our_db.body_scans[N]
bodyrecords_list_ui[++bodyrecords_list_ui.len] = list("name" = N, "recref" = "\ref[BR]")
data["bodyrecords"] = bodyrecords_list_ui
var/mindrecords_list_ui[0]
- for(var/N in SStranscore.backed_up)
- var/datum/transhuman/mind_record/MR = SStranscore.backed_up[N]
+ for(var/N in our_db.backed_up)
+ var/datum/transhuman/mind_record/MR = our_db.backed_up[N]
mindrecords_list_ui[++mindrecords_list_ui.len] = list("name" = N, "recref" = "\ref[MR]")
data["mindrecords"] = mindrecords_list_ui
@@ -251,7 +257,7 @@
set_temp("Error: Record missing.", "danger")
if("coredump")
if(disk)
- SStranscore.core_dump(disk)
+ our_db.core_dump(disk)
sleep(5)
visible_message("\The [src] spits out \the [disk].")
disk.forceMove(get_turf(src))
@@ -407,7 +413,7 @@
return TRUE
//They were dead, or otherwise available.
- sleever.putmind(active_mr,mode,override)
+ sleever.putmind(active_mr,mode,override,db_key = db_key)
set_temp("Initiating resleeving...")
tgui_modal_clear(src)
diff --git a/code/modules/resleeving/designer.dm b/code/modules/resleeving/designer.dm
index 9f745f692a7..ba65e52e62d 100644
--- a/code/modules/resleeving/designer.dm
+++ b/code/modules/resleeving/designer.dm
@@ -29,6 +29,11 @@
var/mob/living/carbon/human/dummy/mannequin/mannequin = null
var/obj/item/weapon/disk/body_record/disk = null
+ // Resleeving database this machine interacts with. Blank for default database
+ // Needs a matching /datum/transcore_db with key defined in code
+ var/db_key
+ var/datum/transcore_db/our_db // These persist all round and are never destroyed, just keep a hard ref
+
/obj/machinery/computer/transhuman/designer/Initialize()
. = ..()
map_name = "transhuman_designer_[REF(src)]_map"
@@ -51,6 +56,8 @@
west_preview.del_on_map_removal = FALSE
west_preview.screen_loc = "[map_name]:0,1"
+ our_db = SStranscore.db_by_key(db_key)
+
/obj/machinery/computer/transhuman/designer/Destroy()
active_br = null
mannequin = null
@@ -100,8 +107,8 @@
if(menu == MENU_BODYRECORDS)
var/bodyrecords_list_ui[0]
- for(var/N in SStranscore.body_scans)
- var/datum/transhuman/body_record/BR = SStranscore.body_scans[N]
+ for(var/N in our_db.body_scans)
+ var/datum/transhuman/body_record/BR = our_db.body_scans[N]
bodyrecords_list_ui[++bodyrecords_list_ui.len] = list("name" = N, "recref" = "\ref[BR]")
if(bodyrecords_list_ui.len)
data["bodyrecords"] = bodyrecords_list_ui
diff --git a/code/modules/resleeving/implant.dm b/code/modules/resleeving/implant.dm
index 59048870025..f15bfe6bace 100644
--- a/code/modules/resleeving/implant.dm
+++ b/code/modules/resleeving/implant.dm
@@ -13,6 +13,11 @@
icon_state = "backup_implant"
known_implant = TRUE
+ // Resleeving database this machine interacts with. Blank for default database
+ // Needs a matching /datum/transcore_db with key defined in code
+ var/db_key
+ var/datum/transcore_db/our_db // These persist all round and are never destroyed, just keep a hard ref
+
/obj/item/weapon/implant/backup/get_data()
var/dat = {"
Implant Specifications:
@@ -26,14 +31,22 @@
Integrity: Generally very survivable. Susceptible to being destroyed by acid."}
return dat
+/obj/item/weapon/implant/backup/New(newloc, db_key)
+ . = ..()
+ src.db_key = db_key
+
+/obj/item/weapon/implant/backup/Initialize()
+ . = ..()
+ our_db = SStranscore.db_by_key(db_key)
+
/obj/item/weapon/implant/backup/Destroy()
- SStranscore.implants -= src
+ our_db.implants -= src
return ..()
/obj/item/weapon/implant/backup/post_implant(var/mob/living/carbon/human/H)
if(istype(H))
BITSET(H.hud_updateflag, BACKUP_HUD)
- SStranscore.implants |= src
+ our_db.implants |= src
return 1
@@ -53,10 +66,12 @@
var/list/obj/item/weapon/implant/backup/imps = list()
var/max_implants = 4 //Iconstates need to exist due to the update proc!
+ var/db_key // To give to the baby implants
+
/obj/item/weapon/backup_implanter/New()
..()
for(var/i = 1 to max_implants)
- var/obj/item/weapon/implant/backup/imp = new(src)
+ var/obj/item/weapon/implant/backup/imp = new(src, db_key)
imps |= imp
imp.germ_level = 0
update()
diff --git a/code/modules/resleeving/infocore_records.dm b/code/modules/resleeving/infocore_records.dm
index e32c28e66bb..25844ec7650 100644
--- a/code/modules/resleeving/infocore_records.dm
+++ b/code/modules/resleeving/infocore_records.dm
@@ -31,7 +31,7 @@
var/one_time = FALSE
-/datum/transhuman/mind_record/New(var/datum/mind/mind, var/mob/living/carbon/human/M, var/add_to_db = TRUE, var/one_time = FALSE)
+/datum/transhuman/mind_record/New(var/datum/mind/mind, var/mob/living/carbon/human/M, var/add_to_db = TRUE, var/one_time = FALSE, var/database_key)
ASSERT(mind)
src.one_time = one_time
@@ -62,7 +62,7 @@
last_update = world.time
if(add_to_db)
- SStranscore.add_backup(src)
+ SStranscore.add_backup(src, database_key = database_key)
/////// Body Record ///////
/datum/transhuman/body_record
@@ -100,7 +100,7 @@
organ_data.Cut()
return QDEL_HINT_HARDDEL // For now at least there is no easy way to clear references to this in machines etc.
-/datum/transhuman/body_record/proc/init_from_mob(var/mob/living/carbon/human/M, var/add_to_db = 0, var/ckeylock = 0)
+/datum/transhuman/body_record/proc/init_from_mob(var/mob/living/carbon/human/M, var/add_to_db = 0, var/ckeylock = 0, var/database_key)
ASSERT(!QDELETED(M))
ASSERT(istype(M))
@@ -185,7 +185,7 @@
genetic_modifiers.Add(mod.type)
if(add_to_db)
- SStranscore.add_body(src)
+ SStranscore.add_body(src, database_key = database_key)
/**
diff --git a/code/modules/resleeving/infomorph.dm b/code/modules/resleeving/infomorph.dm
index 77eae4e51d3..f9bc21804a4 100644
--- a/code/modules/resleeving/infomorph.dm
+++ b/code/modules/resleeving/infomorph.dm
@@ -58,6 +58,7 @@ var/list/infomorph_emotions = list(
var/obj/item/weapon/pai_cable/cable // The cable we produce and use when door or camera jacking
var/silence_time // Timestamp when we were silenced (normally via EMP burst), set to null after silence has faded
+ var/db_key
// Various software-specific vars
@@ -83,7 +84,7 @@ var/list/infomorph_emotions = list(
var/datum/data/record/securityActive1 // Could probably just combine all these into one
var/datum/data/record/securityActive2
-/mob/living/silicon/infomorph/New(var/obj/item/device/sleevecard/SC, var/name = "Unknown")
+/mob/living/silicon/infomorph/New(var/obj/item/device/sleevecard/SC, var/name = "Unknown", var/db_key)
ASSERT(SC)
name = "[initial(name)] ([name])"
src.forceMove(SC)
@@ -95,6 +96,8 @@ var/list/infomorph_emotions = list(
card.radio = new (card)
radio = card.radio
+ src.db_key = db_key
+
//Default languages without universal translator software
add_language(LANGUAGE_EAL, 1)
add_language(LANGUAGE_SIGN, 0)
@@ -406,9 +409,7 @@ var/list/infomorph_emotions = list(
close_up()
//Resleeving 'cryo'
- if(mind && (mind.name in SStranscore.backed_up))
- var/datum/transhuman/mind_record/MR = SStranscore.backed_up[mind.name]
- SStranscore.stop_backup(MR)
+ SStranscore.leave_round(src)
card.removePersonality()
clear_client()
@@ -588,7 +589,7 @@ var/global/list/default_infomorph_software = list()
//Only every so often
if(air_master.current_cycle%30 == 1)
- SStranscore.m_backup(mind)
+ SStranscore.m_backup(mind, database_key = db_key)
if(health <= 0)
death(null,"gives one shrill beep before falling lifeless.")
diff --git a/code/modules/resleeving/machines.dm b/code/modules/resleeving/machines.dm
index 1ddab8b9b42..fc8d464fea1 100644
--- a/code/modules/resleeving/machines.dm
+++ b/code/modules/resleeving/machines.dm
@@ -148,7 +148,7 @@
else if(((occupant.health == occupant.maxHealth)) && (!eject_wait))
playsound(src, 'sound/machines/ding.ogg', 50, 1)
- audible_message("\The [src] signals that the growing process is complete.")
+ audible_message("\The [src] signals that the growing process is complete.", runemessage = "* ding *")
connected_message("Growing Process Complete.")
locked = 0
go_out()
@@ -536,13 +536,13 @@
add_fingerprint(user)
-/obj/machinery/transhuman/resleever/proc/putmind(var/datum/transhuman/mind_record/MR, mode = 1, var/mob/living/carbon/human/override = null)
+/obj/machinery/transhuman/resleever/proc/putmind(var/datum/transhuman/mind_record/MR, mode = 1, var/mob/living/carbon/human/override = null, var/db_key)
if((!occupant || !istype(occupant) || occupant.stat >= DEAD) && mode == 1)
return 0
if(mode == 2 && sleevecards) //Card sleeving
var/obj/item/device/sleevecard/card = new /obj/item/device/sleevecard(get_turf(src))
- card.sleeveInto(MR)
+ card.sleeveInto(MR, db_key = db_key)
sleevecards--
return 1
diff --git a/code/modules/resleeving/sleevecard.dm b/code/modules/resleeving/sleevecard.dm
index 1fc6b8decaf..6de17f5574c 100644
--- a/code/modules/resleeving/sleevecard.dm
+++ b/code/modules/resleeving/sleevecard.dm
@@ -53,8 +53,8 @@
to_chat(user,"\The [src] displays the name '[infomorph]'.")
//This is a 'hard' proc, it does no permission checking, do that on the computer
-/obj/item/device/sleevecard/proc/sleeveInto(var/datum/transhuman/mind_record/MR)
- infomorph = new(src,MR.mindname)
+/obj/item/device/sleevecard/proc/sleeveInto(var/datum/transhuman/mind_record/MR, var/db_key)
+ infomorph = new(src,MR.mindname,db_key=db_key)
for(var/datum/language/L in MR.languages)
infomorph.add_language(L.name)
diff --git a/code/modules/shuttles/shuttles_web.dm b/code/modules/shuttles/shuttles_web.dm
index ea11f21d482..7f4cc22abaa 100644
--- a/code/modules/shuttles/shuttles_web.dm
+++ b/code/modules/shuttles/shuttles_web.dm
@@ -119,7 +119,7 @@
continue
if(!H.shuttle_comp || !(get_area(H) in shuttle_area))
H.shuttle_comp = null
- H.audible_message("\The [H] pings as it loses it's connection with the ship.")
+ H.audible_message("\The [H] pings as it loses it's connection with the ship.", runemessage = "* ping *")
H.update_hud("discon")
helmets -= H
else
diff --git a/code/modules/turbolift/turbolift.dm b/code/modules/turbolift/turbolift.dm
index 25a5f70601f..d8f8373f096 100644
--- a/code/modules/turbolift/turbolift.dm
+++ b/code/modules/turbolift/turbolift.dm
@@ -28,7 +28,7 @@
priority_mode = TRUE
cancel_pending_floors()
update_ext_panel_icons()
- control_panel_interior.audible_message("This turbolift is responding to a priority call. Please exit the lift when it stops and make way.")
+ control_panel_interior.audible_message("This turbolift is responding to a priority call. Please exit the lift when it stops and make way.", runemessage = "* BUZZ *")
spawn(time)
priority_mode = FALSE
update_ext_panel_icons()
@@ -158,7 +158,7 @@
doors_closing = 0
if(!fire_mode)
open_doors()
- control_panel_interior.audible_message("\The [current_floor.ext_panel] buzzes loudly.")
+ control_panel_interior.audible_message("\The [current_floor.ext_panel] buzzes loudly.", runemessage = "* BUZZ *")
playsound(control_panel_interior, "sound/machines/buzz-two.ogg", 50, 1)
return 0
diff --git a/code/modules/turbolift/turbolift_console.dm b/code/modules/turbolift/turbolift_console.dm
index 9868188d189..e3719bc97ec 100644
--- a/code/modules/turbolift/turbolift_console.dm
+++ b/code/modules/turbolift/turbolift_console.dm
@@ -130,10 +130,10 @@
return
lift.update_fire_mode(!lift.fire_mode)
if(lift.fire_mode)
- audible_message("Firefighter Mode Activated. Door safeties disabled. Manual control engaged.")
+ audible_message("Firefighter Mode Activated. Door safeties disabled. Manual control engaged.", runemessage = "* SCREECH *")
playsound(src, 'sound/machines/airalarm.ogg', 25, 0, 4, volume_channel = VOLUME_CHANNEL_ALARMS)
else
- audible_message("Firefighter Mode Deactivated. Door safeties enabled. Automatic control engaged.")
+ audible_message("Firefighter Mode Deactivated. Door safeties enabled. Automatic control engaged.", runemessage = "* ding *")
return
. = ..()
diff --git a/code/modules/turbolift/turbolift_door.dm b/code/modules/turbolift/turbolift_door.dm
index 4b83bc60cf9..d21e344097f 100644
--- a/code/modules/turbolift/turbolift_door.dm
+++ b/code/modules/turbolift/turbolift_door.dm
@@ -40,7 +40,7 @@
if(!moved) // nowhere to go....
LM.gib()
else // the mob is too big to just move, so we need to give up what we're doing
- audible_message("\The [src]'s motors grind as they quickly reverse direction, unable to safely close.")
+ audible_message("\The [src]'s motors grind as they quickly reverse direction, unable to safely close.", runemessage = "* WRRRRR *")
cur_command = null // the door will just keep trying otherwise
return 0
return ..()
\ No newline at end of file
diff --git a/code/modules/ventcrawl/ventcrawl_atmospherics.dm b/code/modules/ventcrawl/ventcrawl_atmospherics.dm
index 1fd9dd077ca..80348c76994 100644
--- a/code/modules/ventcrawl/ventcrawl_atmospherics.dm
+++ b/code/modules/ventcrawl/ventcrawl_atmospherics.dm
@@ -42,7 +42,16 @@
user.client.eye = target_move //if we don't do this, Byond only updates the eye every tick - required for smooth movement
if(world.time > user.next_play_vent)
user.next_play_vent = world.time+30
- playsound(src, 'sound/machines/ventcrawl.ogg', 50, 1, -3)
+ var/turf/T = get_turf(src)
+ playsound(T, 'sound/machines/ventcrawl.ogg', 50, 1, -3)
+ var/message = pick(
+ prob(90);"* clunk *",
+ prob(90);"* thud *",
+ prob(90);"* clatter *",
+ prob(1);"* à¶ž *"
+ )
+ T.runechat_message(message)
+
else
if((direction & initialize_directions) || is_type_in_list(src, ventcrawl_machinery) && src.can_crawl_through()) //if we move in a way the pipe can connect, but doesn't - or we're in a vent
user.remove_ventcrawl()
diff --git a/code/modules/vore/eating/living_vr.dm b/code/modules/vore/eating/living_vr.dm
index 37c3f277829..f2eb242256b 100644
--- a/code/modules/vore/eating/living_vr.dm
+++ b/code/modules/vore/eating/living_vr.dm
@@ -28,6 +28,7 @@
var/permit_healbelly = TRUE
var/can_be_drop_prey = FALSE
var/can_be_drop_pred = TRUE // Mobs are pred by default.
+ var/allow_spontaneous_tf = FALSE // Obviously.
var/next_preyloop // For Fancy sound internal loop
var/adminbus_trash = FALSE // For abusing trash eater for event shenanigans.
var/adminbus_eat_minerals = FALSE // This creature subsists on a diet of pure adminium.
@@ -236,6 +237,7 @@
P.show_vore_fx = src.show_vore_fx
P.can_be_drop_prey = src.can_be_drop_prey
P.can_be_drop_pred = src.can_be_drop_pred
+ P.allow_spontaneous_tf = src.allow_spontaneous_tf
P.step_mechanics_pref = src.step_mechanics_pref
P.pickup_pref = src.pickup_pref
@@ -271,6 +273,7 @@
show_vore_fx = P.show_vore_fx
can_be_drop_prey = P.can_be_drop_prey
can_be_drop_pred = P.can_be_drop_pred
+ allow_spontaneous_tf = P.allow_spontaneous_tf
step_mechanics_pref = P.step_mechanics_pref
pickup_pref = P.pickup_pref
@@ -857,6 +860,7 @@
dispvoreprefs += "Healbelly permission: [permit_healbelly ? "Allowed" : "Disallowed"]
"
dispvoreprefs += "Spontaneous vore prey: [can_be_drop_prey ? "Enabled" : "Disabled"]
"
dispvoreprefs += "Spontaneous vore pred: [can_be_drop_pred ? "Enabled" : "Disabled"]
"
+ dispvoreprefs += "Spontaneous transformation: [allow_spontaneous_tf ? "Enabled" : "Disabled"]
"
dispvoreprefs += "Can be stepped on/over: [step_mechanics_pref ? "Allowed" : "Disallowed"]
"
dispvoreprefs += "Can be picked up: [pickup_pref ? "Allowed" : "Disallowed"]
"
user << browse("
Vore prefs: [src][dispvoreprefs]", "window=[name]mvp;size=200x300;can_resize=0;can_minimize=0")
diff --git a/code/modules/vore/eating/vore_vr.dm b/code/modules/vore/eating/vore_vr.dm
index 82df33ccf2a..e3e7b9befa1 100644
--- a/code/modules/vore/eating/vore_vr.dm
+++ b/code/modules/vore/eating/vore_vr.dm
@@ -49,6 +49,7 @@ V::::::V V::::::VO:::::::OOO:::::::ORR:::::R R:::::REE::::::EEEEEE
var/feeding = TRUE
var/can_be_drop_prey = FALSE
var/can_be_drop_pred = FALSE
+ var/allow_spontaneous_tf = FALSE
var/digest_leave_remains = FALSE
var/allowmobvore = TRUE
var/permit_healbelly = TRUE
@@ -136,6 +137,7 @@ V::::::V V::::::VO:::::::OOO:::::::ORR:::::R R:::::REE::::::EEEEEE
show_vore_fx = json_from_file["show_vore_fx"]
can_be_drop_prey = json_from_file["can_be_drop_prey"]
can_be_drop_pred = json_from_file["can_be_drop_pred"]
+ allow_spontaneous_tf = json_from_file["allow_spontaneous_tf"]
step_mechanics_pref = json_from_file["step_mechanics_pref"]
pickup_pref = json_from_file["pickup_pref"]
belly_prefs = json_from_file["belly_prefs"]
@@ -163,6 +165,8 @@ V::::::V V::::::VO:::::::OOO:::::::ORR:::::R R:::::REE::::::EEEEEE
can_be_drop_prey = FALSE
if(isnull(can_be_drop_pred))
can_be_drop_pred = FALSE
+ if(isnull(allow_spontaneous_tf))
+ allow_spontaneous_tf = FALSE
if(isnull(step_mechanics_pref))
step_mechanics_pref = TRUE
if(isnull(pickup_pref))
@@ -192,6 +196,7 @@ V::::::V V::::::VO:::::::OOO:::::::ORR:::::R R:::::REE::::::EEEEEE
"show_vore_fx" = show_vore_fx,
"can_be_drop_prey" = can_be_drop_prey,
"can_be_drop_pred" = can_be_drop_pred,
+ "allow_spontaneous_tf" = allow_spontaneous_tf,
"step_mechanics_pref" = step_mechanics_pref,
"pickup_pref" = pickup_pref,
"belly_prefs" = belly_prefs,
diff --git a/code/modules/vore/eating/vorepanel_vr.dm b/code/modules/vore/eating/vorepanel_vr.dm
index 27382bda456..249b3051cbd 100644
--- a/code/modules/vore/eating/vorepanel_vr.dm
+++ b/code/modules/vore/eating/vorepanel_vr.dm
@@ -224,6 +224,7 @@
"show_vore_fx" = host.show_vore_fx,
"can_be_drop_prey" = host.can_be_drop_prey,
"can_be_drop_pred" = host.can_be_drop_pred,
+ "allow_spontaneous_tf" = host.allow_spontaneous_tf,
"step_mechanics_active" = host.step_mechanics_pref,
"pickup_mechanics_active" = host.pickup_pref,
"noisy" = host.noisy,
@@ -354,6 +355,12 @@
host.client.prefs_vr.can_be_drop_prey = host.can_be_drop_prey
unsaved_changes = TRUE
return TRUE
+ if("toggle_allow_spontaneous_tf")
+ host.allow_spontaneous_tf = !host.allow_spontaneous_tf
+ if(host.client.prefs_vr)
+ host.client.prefs_vr.allow_spontaneous_tf = host.allow_spontaneous_tf
+ unsaved_changes = TRUE
+ return TRUE
if("toggle_digest")
host.digestable = !host.digestable
if(host.client.prefs_vr)
diff --git a/code/modules/vore/fluffstuff/custom_items_vr.dm b/code/modules/vore/fluffstuff/custom_items_vr.dm
index ba633a1cf24..8ba4a14b3a9 100644
--- a/code/modules/vore/fluffstuff/custom_items_vr.dm
+++ b/code/modules/vore/fluffstuff/custom_items_vr.dm
@@ -551,7 +551,7 @@
//He's dead, jim
if((state == 1) && owner && (owner.stat == DEAD))
update_state(2)
- audible_message("The [name] begins flashing red.")
+ visible_message("The [name] begins flashing red.")
sleep(30)
visible_message("The [name] shatters into dust!")
if(owner_c)
diff --git a/code/modules/vore/resizing/resize_vr.dm b/code/modules/vore/resizing/resize_vr.dm
index 04c3e47dba2..8dc575ce208 100644
--- a/code/modules/vore/resizing/resize_vr.dm
+++ b/code/modules/vore/resizing/resize_vr.dm
@@ -1,7 +1,6 @@
// Adding needed defines to /mob/living
// Note: Polaris had this on /mob/living/carbon/human We need it higher up for animals and stuff.
/mob/living
- var/size_multiplier = 1 //multiplier for the mob's icon size
var/holder_default
var/step_mechanics_pref = TRUE // Allow participation in macro-micro step mechanics
var/pickup_pref = TRUE // Allow participation in macro-micro pickup mechanics
diff --git a/icons/_nanomaps/tether_nanomap_z2.png b/icons/_nanomaps/tether_nanomap_z2.png
index bc56a101644..b803f01d103 100644
Binary files a/icons/_nanomaps/tether_nanomap_z2.png and b/icons/_nanomaps/tether_nanomap_z2.png differ
diff --git a/icons/_nanomaps/tether_nanomap_z3.png b/icons/_nanomaps/tether_nanomap_z3.png
index b8748884df3..cffca4b1835 100644
Binary files a/icons/_nanomaps/tether_nanomap_z3.png and b/icons/_nanomaps/tether_nanomap_z3.png differ
diff --git a/icons/mob/human_races/r_lizard_vr.dmi b/icons/mob/human_races/r_lizard_vr.dmi
index 0bfbfb04eea..2927cae5aba 100644
Binary files a/icons/mob/human_races/r_lizard_vr.dmi and b/icons/mob/human_races/r_lizard_vr.dmi differ
diff --git a/icons/mob/vore/ears_vr.dmi b/icons/mob/vore/ears_vr.dmi
index 5d018fa0e41..39a86f5ced3 100644
Binary files a/icons/mob/vore/ears_vr.dmi and b/icons/mob/vore/ears_vr.dmi differ
diff --git a/icons/mob/vore/tails_vr.dmi b/icons/mob/vore/tails_vr.dmi
index 339e0c96c59..bbe6a5e90c7 100644
Binary files a/icons/mob/vore/tails_vr.dmi and b/icons/mob/vore/tails_vr.dmi differ
diff --git a/icons/obj/handrail_vr.dmi b/icons/obj/handrail.dmi
similarity index 100%
rename from icons/obj/handrail_vr.dmi
rename to icons/obj/handrail.dmi
diff --git a/icons/obj/statue.dmi b/icons/obj/statue.dmi
index 7407e123fa8..c6cdc780628 100644
Binary files a/icons/obj/statue.dmi and b/icons/obj/statue.dmi differ
diff --git a/interface/skin.dmf b/interface/skin.dmf
index fc14eb8a69d..b1d9a6267e2 100644
--- a/interface/skin.dmf
+++ b/interface/skin.dmf
@@ -1282,7 +1282,7 @@ window "mapwindow"
saved-params = "icon-size"
on-show = ".winset\"mainwindow.mainvsplit.left=mapwindow\""
on-hide = ".winset\"mainwindow.mainvsplit.left=\""
- style=".center { text-align: center; } .maptext { font-family: 'Small Fonts'; font-size: 7px; -dm-text-outline: 1px black; color: white; line-height: 1.1; } .small { font-size: 6px; } .big { font-size: 8px; } .reallybig { font-size: 8px; } .extremelybig { font-size: 8px; } .clown { color: #FF69Bf;} .tajaran {color: #803B56;} .skrell {color: #00CED1;} .solcom {color: #22228B;} .com_srus {color: #7c4848;} .zombie {color: #ff0000;} .soghun {color: #228B22;} .vox {color: #AA00AA;} .diona {color: #804000; font-weight: bold;} .trinary {color: #727272;} .kidan {color: #664205;} .slime {color: #0077AA;} .drask {color: #a3d4eb;} .vulpkanin {color: #B97A57;} .abductor {color: #800080; font-style: italic;} .his_grace { color: #15D512; } .hypnophrase { color: #0d0d0d; font-weight: bold; } .yell { font-weight: bold; }"
+ style=".center { text-align: center; } .runechatdiv {background-color: #20202070} .black_outline { -dm-text-outline: 1px black } .boldtext { font-weight: bold; } .maptext { font-family: 'Small Fonts'; font-size: 7px; color: white; line-height: 1.1; } .command_headset { font-weight: bold; font-size: 8px; } .small { font-size: 6px; } .very_small { font-size: 5px;} .big { font-size: 8px; } .reallybig { font-size: 8px; } .extremelybig { font-size: 8px; } .greentext { color: #00FF00; font-size: 7px; } .redtext { color: #FF0000; font-size: 7px; } .clown { color: #FF69Bf; font-size: 7px; font-weight: bold; } .his_grace { color: #15D512; } .hypnophrase { color: #0d0d0d; font-weight: bold; } .yell { font-weight: bold; } .italics { font-size: 7px; font-style: italic; }"
window "outputwindow"
elem "outputwindow"
diff --git a/maps/tether/tether-02-surface2.dmm b/maps/tether/tether-02-surface2.dmm
index 7554e561034..b0e23a28289 100644
--- a/maps/tether/tether-02-surface2.dmm
+++ b/maps/tether/tether-02-surface2.dmm
@@ -20215,6 +20215,9 @@
},
/turf/simulated/floor/tiled/techfloor,
/area/maintenance/asmaint2)
+"aJA" = (
+/turf/simulated/wall/r_wall,
+/area/ai_upload_foyer)
"aJB" = (
/obj/machinery/atmospherics/unary/vent_pump/on{
dir = 4
@@ -20430,6 +20433,9 @@
/obj/structure/catwalk,
/turf/simulated/floor/plating,
/area/maintenance/lower/atmos)
+"aJY" = (
+/turf/simulated/wall/r_wall,
+/area/ai_upload)
"aJZ" = (
/obj/structure/railing{
dir = 8
@@ -20921,6 +20927,9 @@
},
/turf/simulated/floor,
/area/maintenance/substation/research)
+"aKX" = (
+/turf/simulated/wall/r_wall,
+/area/ai_cyborg_station)
"aKY" = (
/obj/machinery/atmospherics/pipe/zpipe/up/supply{
dir = 1
@@ -21078,6 +21087,14 @@
},
/turf/simulated/floor/lino,
/area/chapel/office)
+"aLp" = (
+/obj/structure/cable{
+ d1 = 1;
+ d2 = 2;
+ icon_state = "1-2"
+ },
+/turf/simulated/wall/r_wall,
+/area/ai)
"aLq" = (
/obj/machinery/door/morgue{
dir = 2;
@@ -32928,9 +32945,6 @@
/obj/structure/railing,
/turf/simulated/floor/plating,
/area/maintenance/readingrooms)
-"eTp" = (
-/turf/simulated/wall/durasteel,
-/area/ai_upload_foyer)
"eZG" = (
/obj/machinery/camera/network/command,
/obj/effect/floor_decal/techfloor{
@@ -32973,9 +32987,6 @@
},
/turf/simulated/floor/tiled/techfloor/grid,
/area/ai)
-"fBS" = (
-/turf/simulated/wall/durasteel,
-/area/ai_upload)
"fIS" = (
/obj/effect/floor_decal/corner/paleblue/diagonal,
/obj/structure/table/glass,
@@ -33704,9 +33715,6 @@
},
/turf/simulated/wall/r_wall,
/area/maintenance/readingrooms)
-"nUP" = (
-/turf/simulated/wall/durasteel,
-/area/ai_cyborg_station)
"nUS" = (
/obj/machinery/atmospherics/pipe/simple/hidden/supply,
/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers,
@@ -33777,14 +33785,6 @@
"pBU" = (
/turf/simulated/wall/r_wall,
/area/ai)
-"pIJ" = (
-/obj/structure/cable{
- d1 = 1;
- d2 = 2;
- icon_state = "1-2"
- },
-/turf/simulated/wall/durasteel,
-/area/ai)
"pKA" = (
/obj/structure/table/standard,
/obj/item/weapon/phone,
@@ -46962,15 +46962,15 @@ aLX
blE
blE
blE
-eTp
-eTp
+aJA
+aJA
kad
-eTp
-nUP
-nUP
-nUP
-nUP
-nUP
+aJA
+aKX
+aKX
+aKX
+aKX
+aKX
adt
adt
adt
@@ -47104,15 +47104,15 @@ aLX
aLY
adt
adt
-eTp
+aJA
hki
keC
mDr
-nUP
+aKX
pKA
rqo
sSw
-nUP
+aKX
adt
adt
adt
@@ -47246,15 +47246,15 @@ aLX
aLY
adt
adt
-eTp
+aJA
hxk
keS
mKS
-nUP
+aKX
pLN
rqU
sSw
-nUP
+aKX
adt
adt
adt
@@ -47388,7 +47388,7 @@ aLX
aLY
adt
adt
-eTp
+aJA
hCO
klz
mNZ
@@ -47396,7 +47396,7 @@ nUS
qia
rJt
sWW
-nUP
+aKX
adt
adt
adt
@@ -47530,15 +47530,15 @@ aLX
aLY
adt
adt
-eTp
+aJA
hHc
kpg
nbT
-nUP
+aKX
qtD
rKB
sWW
-nUP
+aKX
adt
adt
adt
@@ -47672,21 +47672,21 @@ aLX
aLY
adt
adt
-eTp
-eTp
+aJA
+aJA
kqp
-eTp
-nUP
-nUP
-nUP
-nUP
-nUP
-fBS
-fBS
-fBS
-fBS
-fBS
-fBS
+aJA
+aKX
+aKX
+aKX
+aKX
+aKX
+aJY
+aJY
+aJY
+aJY
+aJY
+aJY
adt
aab
aab
@@ -47814,7 +47814,7 @@ aLX
aLY
adt
adt
-fBS
+aJY
hIk
kPg
hJu
@@ -47822,13 +47822,13 @@ nXW
qwb
gXE
usS
-fBS
+aJY
usS
gXE
nXW
fxG
gOd
-fBS
+aJY
adt
aab
aab
@@ -47956,7 +47956,7 @@ aLX
aLY
adt
adt
-fBS
+aJY
nKV
kPR
ndB
@@ -47970,7 +47970,7 @@ oXH
wJB
gnh
tgC
-fBS
+aJY
adt
adt
adt
@@ -48098,7 +48098,7 @@ aLX
aLY
adt
adt
-fBS
+aJY
hIk
kTB
rii
@@ -48106,13 +48106,13 @@ bDr
qPo
wKz
quu
-fBS
+aJY
vcT
efL
wOp
jAs
xBA
-fBS
+aJY
adt
adt
adt
@@ -48240,21 +48240,21 @@ aLY
aLY
adt
adt
-fBS
-fBS
+aJY
+aJY
lha
-fBS
-fBS
-fBS
-fBS
-fBS
-fBS
+aJY
+aJY
+aJY
+aJY
+aJY
+aJY
qah
gaR
wTg
jzV
pUj
-fBS
+aJY
adt
adt
adt
@@ -48382,21 +48382,21 @@ aLY
adt
adt
adt
-fBS
+aJY
ilH
kTB
nAh
-xGf
+pBU
qSJ
rRj
sYS
-fBS
-fBS
-fBS
+aJY
+aJY
+aJY
xfa
-fBS
-fBS
-fBS
+aJY
+aJY
+aJY
adt
adt
adt
@@ -48524,11 +48524,11 @@ aLY
adt
adt
adt
-fBS
+aJY
ilX
lnG
nBC
-xGf
+pBU
eZG
rYR
tfK
@@ -48538,7 +48538,7 @@ wyu
xld
kEg
xMP
-xGf
+pBU
adt
adt
aab
@@ -48666,11 +48666,11 @@ aLY
adt
adt
adt
-fBS
+aJY
iol
lMw
nJO
-xGf
+pBU
lfv
skm
tlw
@@ -48680,7 +48680,7 @@ uYa
xKF
kEg
tZV
-xGf
+pBU
adt
adt
aab
@@ -48808,11 +48808,11 @@ aLY
adt
adt
adt
-fBS
-fBS
-fBS
-fBS
-xGf
+aJY
+aJY
+aJY
+aJY
+pBU
rlN
slS
tqq
@@ -48822,7 +48822,7 @@ pkt
xMf
dmW
rlN
-xGf
+pBU
adt
adt
aab
@@ -48954,7 +48954,7 @@ adt
adt
adt
adt
-xGf
+pBU
rlN
srN
rlN
@@ -48964,7 +48964,7 @@ fyj
rXR
kEC
rlN
-xGf
+pBU
adt
adt
adt
@@ -49096,17 +49096,17 @@ adt
adt
adt
adt
-xGf
+pBU
rlN
srN
-pBU
-pBU
+xGf
+xGf
vzk
-pBU
-pBU
+xGf
+xGf
kmV
rlN
-xGf
+pBU
adt
adt
adt
@@ -49238,17 +49238,17 @@ adt
adt
adt
adt
-xGf
+pBU
xMP
srN
-pBU
+xGf
uwv
vUD
wIA
-pBU
+xGf
kmV
gOC
-xGf
+pBU
adt
adt
adt
@@ -49380,17 +49380,17 @@ adt
adt
adt
adt
-xGf
+pBU
rlN
srN
-pBU
-pBU
+xGf
+xGf
vYb
-pBU
-pBU
+xGf
+xGf
kmV
rlN
-xGf
+pBU
adt
adt
adt
@@ -49522,7 +49522,7 @@ bax
bax
lZx
lZx
-xGf
+pBU
pYH
geH
cOj
@@ -49532,7 +49532,7 @@ xBj
cOj
sLP
xHO
-xGf
+pBU
adt
adt
aab
@@ -49664,7 +49664,7 @@ gsU
jtj
jtj
lZx
-xGf
+pBU
kiL
suL
tyG
@@ -49674,7 +49674,7 @@ uNq
xWd
kdz
kEg
-xGf
+pBU
adt
adt
aab
@@ -49806,7 +49806,7 @@ gus
jxi
jxi
nSl
-pIJ
+aLp
roB
sDE
tHY
@@ -49816,7 +49816,7 @@ rXR
qKR
pQO
kEg
-xGf
+pBU
adt
adt
aab
@@ -49948,7 +49948,7 @@ gzK
jtj
mkm
lZx
-xGf
+pBU
cjM
bld
kEg
@@ -49958,7 +49958,7 @@ rlN
kiL
bld
kAa
-xGf
+pBU
adt
adt
aab
@@ -50090,17 +50090,17 @@ gzK
bax
lZx
lZx
-xGf
-xGf
-xGf
-xGf
-xGf
-xGf
-xGf
-xGf
-xGf
-xGf
-xGf
+pBU
+pBU
+pBU
+pBU
+pBU
+pBU
+pBU
+pBU
+pBU
+pBU
+pBU
adt
adt
aab
diff --git a/maps/tether/tether-03-surface3.dmm b/maps/tether/tether-03-surface3.dmm
index f544e2ab095..b99df23bbfc 100644
--- a/maps/tether/tether-03-surface3.dmm
+++ b/maps/tether/tether-03-surface3.dmm
@@ -5695,9 +5695,16 @@
/turf/simulated/floor/tiled/monotile,
/area/tether/surfacebase/surface_three_hall)
"aiq" = (
-/obj/machinery/portable_atmospherics/hydroponics/soil,
-/obj/machinery/camera/network/civilian,
-/turf/simulated/floor/grass,
+/obj/structure/grille,
+/obj/structure/window/reinforced/full,
+/obj/machinery/door/firedoor,
+/obj/structure/window/reinforced{
+ dir = 8
+ },
+/obj/structure/window/reinforced{
+ dir = 1
+ },
+/turf/simulated/floor/plating,
/area/tether/surfacebase/public_garden_three)
"air" = (
/obj/effect/floor_decal/borderfloor{
@@ -5848,12 +5855,13 @@
/turf/simulated/wall,
/area/rnd/outpost/xenobiology/outpost_north_airlock)
"aiF" = (
-/obj/machinery/portable_atmospherics/hydroponics/soil,
-/obj/item/device/radio/intercom{
- dir = 8;
- pixel_x = -24
+/obj/structure/grille,
+/obj/structure/window/reinforced/full,
+/obj/machinery/door/firedoor,
+/obj/structure/window/reinforced{
+ dir = 1
},
-/turf/simulated/floor/grass,
+/turf/simulated/floor/plating,
/area/tether/surfacebase/public_garden_three)
"aiG" = (
/obj/machinery/light,
@@ -6381,12 +6389,6 @@
},
/turf/simulated/floor/tiled,
/area/tether/surfacebase/public_garden_three)
-"aju" = (
-/obj/machinery/atmospherics/unary/vent_pump/on{
- dir = 4
- },
-/turf/simulated/floor/tiled,
-/area/tether/surfacebase/public_garden_three)
"ajv" = (
/obj/machinery/portable_atmospherics/canister/nitrogen,
/obj/effect/floor_decal/industrial/warning{
@@ -6887,14 +6889,11 @@
/turf/simulated/floor/tiled/monotile,
/area/tether/surfacebase/surface_three_hall)
"akm" = (
-/obj/machinery/atmospherics/pipe/simple/hidden/supply{
- dir = 4
+/obj/effect/floor_decal/borderfloor{
+ dir = 1
},
-/obj/effect/floor_decal/borderfloor/corner{
- dir = 4
- },
-/obj/effect/floor_decal/corner/lime/bordercorner{
- dir = 4
+/obj/effect/floor_decal/corner/lime/border{
+ dir = 1
},
/turf/simulated/floor/tiled,
/area/tether/surfacebase/public_garden_three)
@@ -17903,41 +17902,22 @@
/turf/simulated/floor/tiled,
/area/tether/surfacebase/botanystorage)
"aCY" = (
-/obj/structure/grille,
-/obj/structure/window/reinforced/full,
-/obj/machinery/door/firedoor,
-/obj/structure/window/reinforced{
- dir = 1
- },
-/turf/simulated/floor/plating,
-/area/tether/surfacebase/public_garden_three)
-"aCZ" = (
-/obj/structure/grille,
-/obj/structure/window/reinforced/full,
-/obj/machinery/door/firedoor,
-/obj/structure/window/reinforced{
- dir = 1
- },
-/obj/structure/window/reinforced{
+/obj/structure/table/bench/wooden,
+/obj/machinery/atmospherics/pipe/simple/hidden/supply{
dir = 8
},
-/turf/simulated/floor/plating,
+/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{
+ dir = 4
+ },
+/turf/simulated/floor/wood,
/area/tether/surfacebase/public_garden_three)
-"aDa" = (
-/obj/machinery/portable_atmospherics/hydroponics/soil,
+"aCZ" = (
+/obj/structure/flora/ausbushes/brflowers,
/turf/simulated/floor/grass,
/area/tether/surfacebase/public_garden_three)
"aDb" = (
-/obj/structure/grille,
-/obj/structure/window/reinforced/full,
-/obj/machinery/door/firedoor,
-/obj/structure/window/reinforced{
- dir = 1
- },
-/obj/structure/window/reinforced{
- dir = 4
- },
-/turf/simulated/floor/plating,
+/obj/structure/flora/ausbushes/fernybush,
+/turf/simulated/floor/grass,
/area/tether/surfacebase/public_garden_three)
"aDc" = (
/obj/structure/grille,
@@ -17946,31 +17926,22 @@
/turf/simulated/floor/plating,
/area/tether/surfacebase/public_garden_three)
"aDd" = (
-/obj/machinery/light{
+/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{
dir = 1
},
-/obj/structure/table/bench/wooden,
+/obj/machinery/atmospherics/pipe/simple/hidden/supply,
/turf/simulated/floor/grass,
/area/tether/surfacebase/public_garden_three)
"aDe" = (
/turf/simulated/floor/grass,
/area/tether/surfacebase/public_garden_three)
"aDf" = (
-/obj/structure/table/bench/wooden,
+/obj/structure/flora/rocks1,
/turf/simulated/floor/grass,
/area/tether/surfacebase/public_garden_three)
"aDg" = (
/turf/simulated/floor/tiled,
/area/tether/surfacebase/public_garden_three)
-"aDh" = (
-/obj/effect/floor_decal/borderfloor{
- dir = 5
- },
-/obj/effect/floor_decal/corner/lime/border{
- dir = 5
- },
-/turf/simulated/floor/tiled,
-/area/tether/surfacebase/public_garden_three)
"aDi" = (
/obj/structure/window/reinforced,
/obj/effect/floor_decal/steeldecal/steel_decals6{
@@ -17979,6 +17950,10 @@
/obj/effect/floor_decal/steeldecal/steel_decals6{
dir = 5
},
+/obj/machinery/camera/network/civilian{
+ dir = 1;
+ pixel_y = 6
+ },
/turf/simulated/floor/tiled,
/area/tether/surfacebase/public_garden_three)
"aDj" = (
@@ -17998,32 +17973,36 @@
/obj/effect/floor_decal/steeldecal/steel_decals6{
dir = 6
},
+/obj/machinery/light{
+ pixel_y = 6
+ },
/turf/simulated/floor/tiled,
/area/tether/surfacebase/public_garden_three)
"aDl" = (
-/obj/machinery/atmospherics/pipe/simple/hidden/supply,
-/turf/simulated/floor/tiled,
+/obj/machinery/light{
+ dir = 1
+ },
+/turf/simulated/floor/grass,
/area/tether/surfacebase/public_garden_three)
"aDm" = (
-/obj/machinery/atmospherics/pipe/simple/hidden/supply{
- dir = 10
- },
/obj/effect/floor_decal/borderfloor{
dir = 1
},
/obj/effect/floor_decal/corner/lime/border{
dir = 1
},
+/obj/machinery/atmospherics/pipe/simple/hidden/supply{
+ dir = 5
+ },
+/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{
+ dir = 5
+ },
/turf/simulated/floor/tiled,
/area/tether/surfacebase/public_garden_three)
"aDn" = (
-/obj/effect/floor_decal/borderfloor{
- dir = 1
+/turf/simulated/mineral/floor/cave{
+ name = "dirt"
},
-/obj/effect/floor_decal/corner/lime/border{
- dir = 1
- },
-/turf/simulated/floor/tiled,
/area/tether/surfacebase/public_garden_three)
"aDo" = (
/obj/machinery/shower{
@@ -18050,13 +18029,8 @@
/turf/simulated/floor/tiled/white,
/area/tether/surfacebase/medical/triage)
"aDq" = (
-/obj/structure/grille,
-/obj/structure/window/reinforced/full,
-/obj/machinery/door/firedoor,
-/obj/structure/window/reinforced{
- dir = 8
- },
-/turf/simulated/floor/plating,
+/obj/structure/table/bench/wooden,
+/turf/simulated/floor/grass,
/area/tether/surfacebase/public_garden_three)
"aDr" = (
/obj/effect/floor_decal/borderfloor{
@@ -18073,7 +18047,6 @@
"aDt" = (
/obj/structure/table/woodentable,
/obj/item/weapon/material/minihoe,
-/obj/machinery/atmospherics/pipe/simple/hidden/supply,
/obj/item/device/analyzer/plant_analyzer,
/obj/item/device/analyzer/plant_analyzer,
/obj/effect/floor_decal/steeldecal/steel_decals6{
@@ -18085,10 +18058,16 @@
/obj/structure/window/reinforced{
dir = 8
},
+/obj/machinery/atmospherics/unary/vent_scrubber/on{
+ dir = 4
+ },
/turf/simulated/floor/tiled,
/area/tether/surfacebase/public_garden_three)
"aDu" = (
/obj/structure/table/bench/wooden,
+/obj/machinery/atmospherics/pipe/simple/hidden/supply{
+ dir = 6
+ },
/turf/simulated/floor/tiled,
/area/tether/surfacebase/public_garden_three)
"aDv" = (
@@ -18101,21 +18080,13 @@
/turf/simulated/floor/tiled,
/area/tether/surfacebase/public_garden_three)
"aDw" = (
-/obj/machinery/power/apc{
- dir = 1;
- name = "north bump";
- pixel_y = 28
- },
/obj/effect/floor_decal/borderfloor{
dir = 1
},
/obj/effect/floor_decal/corner/lime/border{
dir = 1
},
-/obj/structure/cable/green{
- d2 = 4;
- icon_state = "0-4"
- },
+/obj/machinery/vending/hydronutrients,
/turf/simulated/floor/tiled,
/area/tether/surfacebase/public_garden_three)
"aDx" = (
@@ -18131,11 +18102,8 @@
/obj/effect/floor_decal/corner/lime/border{
dir = 5
},
-/obj/structure/cable/green{
- d1 = 4;
- d2 = 8;
- icon_state = "4-8"
- },
+/obj/machinery/camera/network/tether,
+/obj/machinery/seed_storage/garden,
/turf/simulated/floor/tiled,
/area/tether/surfacebase/public_garden_three)
"aDy" = (
@@ -18156,21 +18124,13 @@
/obj/effect/floor_decal/techfloor/hole/right{
dir = 8
},
-/obj/structure/cable/green{
- d1 = 2;
- d2 = 8;
- icon_state = "2-8"
- },
/turf/simulated/floor/tiled/techmaint,
/area/tether/surfacebase/public_garden_three)
"aDz" = (
-/obj/structure/grille,
-/obj/structure/window/reinforced/full,
-/obj/machinery/door/firedoor,
-/obj/structure/window/reinforced{
- dir = 4
+/obj/structure/bed/chair/wood{
+ dir = 1
},
-/turf/simulated/floor/plating,
+/turf/simulated/floor/grass,
/area/tether/surfacebase/public_garden_three)
"aDA" = (
/obj/effect/floor_decal/borderfloor{
@@ -18191,6 +18151,9 @@
/obj/effect/floor_decal/steeldecal/steel_decals6,
/obj/structure/table/woodentable,
/obj/random/maintenance/clean,
+/obj/machinery/atmospherics/unary/vent_pump/on{
+ dir = 8
+ },
/turf/simulated/floor/tiled,
/area/tether/surfacebase/public_garden_three)
"aDC" = (
@@ -18227,12 +18190,10 @@
/area/tether/surfacebase/botanystorage)
"aDD" = (
/obj/structure/table/bench/wooden,
-/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{
- dir = 4
- },
-/obj/machinery/atmospherics/pipe/simple/hidden/supply{
- dir = 4
+/obj/machinery/atmospherics/pipe/manifold/hidden/scrubbers{
+ dir = 8
},
+/obj/machinery/atmospherics/pipe/manifold4w/hidden/supply,
/turf/simulated/floor/tiled,
/area/tether/surfacebase/public_garden_three)
"aDE" = (
@@ -18250,13 +18211,12 @@
/obj/effect/floor_decal/techfloor{
dir = 4
},
-/obj/structure/cable/green{
- d1 = 1;
- d2 = 2;
- icon_state = "1-2"
- },
/obj/machinery/atmospherics/pipe/simple/hidden/supply,
/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers,
+/obj/machinery/alarm{
+ dir = 8;
+ pixel_x = 28
+ },
/turf/simulated/floor/tiled/techmaint,
/area/tether/surfacebase/public_garden_three)
"aDG" = (
@@ -18268,6 +18228,9 @@
},
/obj/effect/floor_decal/steeldecal/steel_decals6,
/obj/structure/table/woodentable,
+/obj/machinery/atmospherics/unary/vent_scrubber/on{
+ dir = 8
+ },
/turf/simulated/floor/tiled,
/area/tether/surfacebase/public_garden_three)
"aDH" = (
@@ -18281,7 +18244,15 @@
},
/obj/structure/cable/green{
d1 = 32;
- icon_state = "32-1"
+ d2 = 2;
+ icon_state = "32-2"
+ },
+/obj/structure/railing,
+/obj/structure/railing{
+ dir = 1
+ },
+/obj/structure/railing{
+ dir = 8
},
/turf/simulated/open,
/area/tether/surfacebase/public_garden_three)
@@ -18314,8 +18285,7 @@
/turf/simulated/floor/tiled,
/area/tether/surfacebase/public_garden_three)
"aDL" = (
-/obj/machinery/portable_atmospherics/hydroponics/soil,
-/obj/machinery/light,
+/obj/machinery/camera/network/civilian,
/turf/simulated/floor/grass,
/area/tether/surfacebase/public_garden_three)
"aDM" = (
@@ -18329,40 +18299,42 @@
/turf/simulated/floor/tiled,
/area/tether/surfacebase/botanystorage)
"aDN" = (
+/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{
+ dir = 9
+ },
+/obj/effect/floor_decal/borderfloor,
+/obj/effect/floor_decal/corner/lime/border,
+/obj/machinery/atmospherics/pipe/simple/hidden/supply{
+ dir = 9
+ },
+/turf/simulated/floor/tiled,
+/area/tether/surfacebase/public_garden_three)
+"aDO" = (
/obj/effect/floor_decal/borderfloor,
/obj/effect/floor_decal/corner/lime/border,
/turf/simulated/floor/tiled,
/area/tether/surfacebase/public_garden_three)
-"aDO" = (
-/obj/machinery/alarm{
- dir = 8;
- pixel_x = 25
- },
+"aDP" = (
/obj/effect/floor_decal/borderfloor{
dir = 6
},
/obj/effect/floor_decal/corner/lime/border{
dir = 6
},
-/obj/machinery/camera/network/civilian{
- dir = 1
+/obj/machinery/light{
+ dir = 4
+ },
+/obj/item/device/radio/intercom{
+ dir = 4;
+ name = "Station Intercom (General)";
+ pixel_x = 24
},
/turf/simulated/floor/tiled,
/area/tether/surfacebase/public_garden_three)
-"aDP" = (
-/obj/structure/grille,
-/obj/structure/window/reinforced/full,
-/obj/machinery/door/firedoor,
-/obj/structure/window/reinforced,
-/turf/simulated/floor/plating,
-/area/tether/surfacebase/public_garden_three)
"aDQ" = (
/obj/structure/grille,
/obj/structure/window/reinforced/full,
/obj/machinery/door/firedoor,
-/obj/structure/window/reinforced{
- dir = 8
- },
/obj/structure/window/reinforced,
/turf/simulated/floor/plating,
/area/tether/surfacebase/public_garden_three)
@@ -18373,21 +18345,16 @@
/obj/effect/floor_decal/corner/lime/bordercorner{
dir = 8
},
+/obj/machinery/atmospherics/pipe/simple/hidden/supply,
+/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers,
/turf/simulated/floor/tiled,
/area/tether/surfacebase/public_garden_three)
"aDS" = (
-/obj/structure/grille,
-/obj/structure/window/reinforced/full,
-/obj/machinery/door/firedoor,
-/obj/structure/window/reinforced{
- dir = 4
- },
-/obj/structure/window/reinforced,
-/turf/simulated/floor/plating,
+/obj/structure/bed/chair/wood,
+/turf/simulated/floor/grass,
/area/tether/surfacebase/public_garden_three)
"aDT" = (
/obj/machinery/light,
-/obj/structure/table/bench/wooden,
/turf/simulated/floor/grass,
/area/tether/surfacebase/public_garden_three)
"aDU" = (
@@ -27318,9 +27285,16 @@
/turf/simulated/floor/tiled,
/area/tether/surfacebase/servicebackroom)
"aUC" = (
-/obj/effect/floor_decal/borderfloor/corner,
-/obj/effect/floor_decal/corner/lime/bordercorner,
-/turf/simulated/floor/tiled,
+/obj/structure/grille,
+/obj/structure/window/reinforced/full,
+/obj/machinery/door/firedoor,
+/obj/structure/window/reinforced{
+ dir = 4
+ },
+/obj/structure/window/reinforced{
+ dir = 1
+ },
+/turf/simulated/floor/plating,
/area/tether/surfacebase/public_garden_three)
"aUD" = (
/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{
@@ -27341,13 +27315,7 @@
/turf/simulated/floor/tiled/eris/dark/golden,
/area/shuttle/tourbus/general)
"aUF" = (
-/obj/effect/floor_decal/borderfloor,
-/obj/effect/floor_decal/corner/lime/border,
-/obj/machinery/seed_storage/garden{
- dir = 1
- },
-/obj/machinery/light,
-/turf/simulated/floor/tiled,
+/turf/simulated/floor/wood,
/area/tether/surfacebase/public_garden_three)
"aUG" = (
/obj/effect/floor_decal/industrial/warning{
@@ -27404,13 +27372,10 @@
/area/tether/surfacebase/medical/chemistry)
"aUM" = (
/obj/effect/floor_decal/borderfloor{
- dir = 6
+ dir = 4
},
/obj/effect/floor_decal/corner/lime/border{
- dir = 6
- },
-/obj/machinery/vending/hydronutrients{
- dir = 1
+ dir = 4
},
/turf/simulated/floor/tiled,
/area/tether/surfacebase/public_garden_three)
@@ -27434,6 +27399,12 @@
/obj/effect/floor_decal/techfloor{
dir = 6
},
+/obj/structure/cable/green,
+/obj/machinery/power/apc{
+ dir = 4;
+ name = "east bump";
+ pixel_x = 28
+ },
/turf/simulated/floor/tiled/techmaint,
/area/tether/surfacebase/public_garden_three)
"aUP" = (
@@ -27591,8 +27562,11 @@
/turf/simulated/floor/tiled/white,
/area/tether/surfacebase/medical/chemistry)
"aVb" = (
-/obj/machinery/atmospherics/unary/vent_scrubber/on{
- dir = 4
+/obj/machinery/atmospherics/pipe/manifold/hidden/scrubbers{
+ dir = 1
+ },
+/obj/machinery/atmospherics/pipe/manifold/hidden/supply{
+ dir = 1
},
/turf/simulated/floor/tiled,
/area/tether/surfacebase/public_garden_three)
@@ -27794,19 +27768,8 @@
/turf/simulated/floor/tiled,
/area/tether/surfacebase/surface_three_hall)
"aVr" = (
-/obj/effect/floor_decal/borderfloor{
- dir = 10
- },
-/obj/effect/floor_decal/corner/lime/border{
- dir = 10
- },
-/obj/effect/floor_decal/borderfloor/corner2{
- dir = 9
- },
-/obj/effect/floor_decal/corner/lime/bordercorner2{
- dir = 9
- },
-/turf/simulated/floor/tiled,
+/obj/structure/table/bench/wooden,
+/turf/simulated/floor/wood,
/area/tether/surfacebase/public_garden_three)
"aVs" = (
/obj/structure/lattice,
@@ -27873,11 +27836,17 @@
/turf/simulated/floor/tiled/white,
/area/crew_quarters/barrestroom)
"aVx" = (
+/obj/effect/floor_decal/borderfloor{
+ dir = 8
+ },
+/obj/effect/floor_decal/corner/lime/border{
+ dir = 8
+ },
/obj/machinery/firealarm{
dir = 8;
pixel_x = -24
},
-/turf/simulated/floor/grass,
+/turf/simulated/floor/tiled,
/area/tether/surfacebase/public_garden_three)
"aVy" = (
/obj/structure/disposalpipe/segment{
@@ -37927,6 +37896,14 @@
},
/turf/simulated/floor/tiled,
/area/hallway/lower/third_south)
+"bSW" = (
+/obj/structure/table/bench/wooden,
+/obj/machinery/atmospherics/pipe/simple/hidden/supply,
+/obj/machinery/atmospherics/pipe/manifold/hidden/scrubbers{
+ dir = 4
+ },
+/turf/simulated/floor/tiled,
+/area/tether/surfacebase/public_garden_three)
"bTC" = (
/obj/structure/table/reinforced,
/obj/machinery/computer/skills{
@@ -38094,13 +38071,13 @@
/turf/simulated/floor/tiled,
/area/tether/surfacebase/security/briefingroom)
"cwI" = (
-/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{
- dir = 9
- },
/obj/effect/floor_decal/borderfloor,
/obj/effect/floor_decal/corner/lime/border,
/obj/machinery/atmospherics/pipe/simple/hidden/supply{
- dir = 9
+ dir = 4
+ },
+/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{
+ dir = 4
},
/turf/simulated/floor/tiled,
/area/tether/surfacebase/public_garden_three)
@@ -38111,6 +38088,12 @@
/obj/machinery/light,
/turf/simulated/floor/wood,
/area/library)
+"cAZ" = (
+/obj/structure/bed/chair/wood{
+ dir = 8
+ },
+/turf/simulated/floor/grass,
+/area/tether/surfacebase/public_garden_three)
"cEx" = (
/obj/structure/table/reinforced,
/obj/item/weapon/stamp/internalaffairs,
@@ -38321,11 +38304,8 @@
/turf/simulated/floor/plating/eris/under,
/area/shuttle/tourbus/cockpit)
"dgA" = (
-/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers,
-/obj/machinery/atmospherics/pipe/simple/hidden/supply{
- dir = 1
- },
-/turf/simulated/floor/tiled,
+/obj/structure/flora/bboulder2,
+/turf/simulated/floor/grass,
/area/tether/surfacebase/public_garden_three)
"dlX" = (
/obj/structure/table/glass,
@@ -38587,19 +38567,14 @@
/turf/simulated/floor/tiled,
/area/tether/surfacebase/southhall)
"erS" = (
+/obj/effect/floor_decal/steeldecal/steel_decals6{
+ dir = 10
+ },
/obj/effect/floor_decal/borderfloor{
- dir = 6
+ dir = 4
},
/obj/effect/floor_decal/corner/lime/border{
- dir = 6
- },
-/obj/effect/floor_decal/borderfloor/corner2,
-/obj/effect/floor_decal/corner/lime/bordercorner2,
-/obj/machinery/atmospherics/pipe/simple/hidden/supply{
- dir = 9
- },
-/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{
- dir = 9
+ dir = 4
},
/turf/simulated/floor/tiled,
/area/tether/surfacebase/public_garden_three)
@@ -38607,6 +38582,15 @@
/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers,
/turf/simulated/floor/tiled/white,
/area/tether/surfacebase/medical/lobby)
+"eCg" = (
+/obj/structure/grille,
+/obj/structure/window/reinforced/full,
+/obj/machinery/door/firedoor,
+/obj/structure/window/reinforced{
+ dir = 4
+ },
+/turf/simulated/floor/plating,
+/area/tether/surfacebase/public_garden_three)
"eCG" = (
/obj/machinery/computer/ship/helm,
/turf/simulated/floor/tiled/eris/white/orangecorner,
@@ -38739,6 +38723,12 @@
},
/turf/simulated/floor/tiled/eris/dark/golden,
/area/shuttle/tourbus/general)
+"fdB" = (
+/obj/machinery/light/flamp/noshade{
+ pixel_y = 5
+ },
+/turf/simulated/floor/grass,
+/area/tether/surfacebase/public_garden_three)
"feu" = (
/obj/machinery/alarm{
dir = 1;
@@ -38878,6 +38868,16 @@
/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers,
/turf/simulated/floor/tiled,
/area/tether/surfacebase/surface_three_hall)
+"fQa" = (
+/obj/structure/grille,
+/obj/structure/window/reinforced/full,
+/obj/machinery/door/firedoor,
+/obj/structure/window/reinforced{
+ dir = 8
+ },
+/obj/structure/window/reinforced,
+/turf/simulated/floor/plating,
+/area/tether/surfacebase/public_garden_three)
"gae" = (
/obj/structure/bed/chair{
dir = 4
@@ -39027,6 +39027,13 @@
},
/turf/simulated/floor/tiled,
/area/crew_quarters/pool)
+"gOS" = (
+/obj/structure/table/bench/wooden,
+/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{
+ dir = 9
+ },
+/turf/simulated/floor/wood,
+/area/tether/surfacebase/public_garden_three)
"gRG" = (
/obj/structure/closet/hydrant{
pixel_x = -32
@@ -39095,6 +39102,16 @@
},
/turf/simulated/floor/tiled,
/area/tether/surfacebase/security/briefingroom)
+"hal" = (
+/obj/structure/table/bench/wooden,
+/obj/machinery/atmospherics/pipe/manifold/hidden/supply{
+ dir = 4
+ },
+/obj/machinery/atmospherics/pipe/manifold/hidden/scrubbers{
+ dir = 1
+ },
+/turf/simulated/floor/wood,
+/area/tether/surfacebase/public_garden_three)
"haS" = (
/obj/machinery/door/firedoor,
/obj/machinery/door/airlock/glass{
@@ -39153,16 +39170,23 @@
/turf/simulated/floor/tiled/monotile,
/area/tether/surfacebase/shuttle_pad)
"hxc" = (
+/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{
+ dir = 4
+ },
+/obj/machinery/atmospherics/pipe/simple/hidden/supply{
+ dir = 4
+ },
/obj/effect/floor_decal/borderfloor/corner,
/obj/effect/floor_decal/corner/lime/bordercorner,
-/obj/machinery/atmospherics/pipe/simple/hidden/supply{
- dir = 6
- },
-/obj/machinery/atmospherics/pipe/manifold/hidden/scrubbers{
- dir = 1
- },
/turf/simulated/floor/tiled,
/area/tether/surfacebase/public_garden_three)
+"hxr" = (
+/obj/structure/flora/ausbushes/ywflowers{
+ pixel_x = -5;
+ pixel_y = -10
+ },
+/turf/simulated/floor/grass,
+/area/tether/surfacebase/public_garden_three)
"hxR" = (
/obj/structure/cable/green{
d1 = 2;
@@ -39264,6 +39288,12 @@
/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers,
/turf/simulated/floor/tiled,
/area/tether/surfacebase/security/briefingroom)
+"hMw" = (
+/obj/machinery/hologram/holopad,
+/turf/simulated/mineral/floor/cave{
+ name = "dirt"
+ },
+/area/tether/surfacebase/public_garden_three)
"hVc" = (
/obj/structure/table/woodentable,
/obj/machinery/photocopier/faxmachine{
@@ -39278,6 +39308,25 @@
},
/turf/simulated/floor/wood,
/area/tether/surfacebase/security/hos)
+"hVd" = (
+/obj/effect/floor_decal/borderfloor{
+ dir = 1
+ },
+/obj/effect/floor_decal/corner/lime/border{
+ dir = 1
+ },
+/obj/machinery/atmospherics/pipe/simple/hidden/supply{
+ dir = 10
+ },
+/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{
+ dir = 10
+ },
+/turf/simulated/floor/tiled,
+/area/tether/surfacebase/public_garden_three)
+"hXh" = (
+/obj/structure/flora/tree/jungle,
+/turf/simulated/floor/grass,
+/area/tether/surfacebase/public_garden_three)
"ieb" = (
/obj/effect/floor_decal/borderfloorblack,
/obj/effect/floor_decal/industrial/danger,
@@ -39315,13 +39364,9 @@
/turf/simulated/floor/tiled/monotile,
/area/tether/surfacebase/shuttle_pad)
"iiv" = (
-/obj/machinery/atmospherics/pipe/simple/hidden/supply{
- dir = 6
- },
-/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{
- dir = 6
- },
-/turf/simulated/floor/tiled,
+/obj/structure/table/woodentable,
+/obj/machinery/atmospherics/unary/vent_pump/on,
+/turf/simulated/floor/wood,
/area/tether/surfacebase/public_garden_three)
"ikh" = (
/obj/machinery/vending/sovietsoda{
@@ -39457,6 +39502,12 @@
},
/turf/simulated/floor/tiled,
/area/rnd/outpost/xenobiology/outpost_main)
+"iTV" = (
+/obj/machinery/camera/network/civilian{
+ dir = 1
+ },
+/turf/simulated/floor/grass,
+/area/tether/surfacebase/public_garden_three)
"iUu" = (
/obj/structure/grille,
/obj/structure/window/reinforced/full,
@@ -39496,6 +39547,15 @@
},
/turf/simulated/floor/tiled,
/area/rnd/research/researchdivision)
+"jbL" = (
+/obj/structure/flora/ausbushes/ywflowers,
+/turf/simulated/floor/grass,
+/area/tether/surfacebase/public_garden_three)
+"jcR" = (
+/obj/machinery/atmospherics/pipe/simple/hidden/supply,
+/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers,
+/turf/simulated/floor/tiled,
+/area/tether/surfacebase/public_garden_three)
"jmQ" = (
/obj/structure/cable/green{
d1 = 1;
@@ -39607,6 +39667,12 @@
},
/turf/simulated/floor/tiled/monotile,
/area/tether/surfacebase/southhall)
+"jDF" = (
+/obj/machinery/light{
+ dir = 8
+ },
+/turf/simulated/floor/grass,
+/area/tether/surfacebase/public_garden_three)
"jFq" = (
/obj/structure/table/reinforced,
/obj/item/weapon/folder{
@@ -39749,6 +39815,15 @@
},
/turf/simulated/floor/tiled,
/area/tether/surfacebase/security/breakroom)
+"jQV" = (
+/obj/structure/grille,
+/obj/structure/window/reinforced/full,
+/obj/machinery/door/firedoor,
+/obj/structure/window/reinforced{
+ dir = 8
+ },
+/turf/simulated/floor/plating,
+/area/tether/surfacebase/public_garden_three)
"jRO" = (
/obj/effect/floor_decal/spline/plain,
/obj/item/weapon/stool/padded,
@@ -40036,6 +40111,16 @@
},
/turf/simulated/floor/tiled/dark,
/area/tether/surfacebase/security/iaa/officea)
+"kKV" = (
+/obj/machinery/light/flamp/noshade{
+ pixel_y = 5
+ },
+/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{
+ dir = 1
+ },
+/obj/machinery/atmospherics/pipe/simple/hidden/supply,
+/turf/simulated/floor/grass,
+/area/tether/surfacebase/public_garden_three)
"kQb" = (
/obj/effect/floor_decal/borderfloor{
dir = 5
@@ -40071,6 +40156,11 @@
},
/turf/simulated/floor/tiled,
/area/rnd/outpost/xenobiology/outpost_main)
+"kTt" = (
+/obj/machinery/portable_atmospherics/hydroponics/soil,
+/obj/machinery/light,
+/turf/simulated/floor/grass,
+/area/tether/surfacebase/public_garden_three)
"kXo" = (
/obj/structure/noticeboard{
pixel_y = -26
@@ -40091,6 +40181,10 @@
},
/turf/simulated/floor/tiled,
/area/rnd/research/researchdivision)
+"kZr" = (
+/obj/machinery/portable_atmospherics/hydroponics/soil,
+/turf/simulated/floor/grass,
+/area/tether/surfacebase/public_garden_three)
"laB" = (
/obj/structure/cable/orange{
d1 = 4;
@@ -40618,6 +40712,12 @@
/obj/machinery/atmospherics/unary/vent_scrubber/on,
/turf/simulated/floor/tiled,
/area/tether/surfacebase/security/briefingroom)
+"nio" = (
+/obj/machinery/camera/network/civilian{
+ dir = 9
+ },
+/turf/simulated/floor/outdoors/grass/sif/virgo3b,
+/area/tether/surfacebase/outside/outside3)
"nlf" = (
/obj/effect/floor_decal/borderfloor{
dir = 8
@@ -40656,6 +40756,18 @@
},
/turf/simulated/floor/tiled,
/area/tether/surfacebase/security/breakroom)
+"noU" = (
+/obj/structure/table/woodentable,
+/turf/simulated/floor/wood,
+/area/tether/surfacebase/public_garden_three)
+"ntl" = (
+/obj/structure/table/bench/wooden,
+/obj/machinery/atmospherics/pipe/simple/hidden/supply,
+/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{
+ dir = 6
+ },
+/turf/simulated/floor/tiled,
+/area/tether/surfacebase/public_garden_three)
"nue" = (
/obj/structure/cable/green{
icon_state = "0-2"
@@ -40767,6 +40879,10 @@
/obj/machinery/hologram/holopad,
/turf/simulated/floor/tiled,
/area/tether/surfacebase/security/breakroom)
+"nUE" = (
+/obj/structure/flora/ausbushes/ppflowers,
+/turf/simulated/floor/grass,
+/area/tether/surfacebase/public_garden_three)
"nXZ" = (
/obj/structure/disposalpipe/segment{
dir = 8
@@ -41279,6 +41395,16 @@
/obj/machinery/hologram/holopad,
/turf/simulated/floor/tiled/dark,
/area/tether/surfacebase/security/iaa/officeb)
+"pIJ" = (
+/obj/structure/table/bench/wooden,
+/obj/machinery/atmospherics/pipe/simple/hidden/supply{
+ dir = 8
+ },
+/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{
+ dir = 5
+ },
+/turf/simulated/floor/wood,
+/area/tether/surfacebase/public_garden_three)
"pJL" = (
/obj/structure/bed/chair{
dir = 4
@@ -41295,6 +41421,21 @@
},
/turf/simulated/floor/tiled/eris/white/orangecorner,
/area/shuttle/tourbus/cockpit)
+"pNF" = (
+/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{
+ dir = 4
+ },
+/obj/machinery/atmospherics/pipe/simple/hidden/supply{
+ dir = 4
+ },
+/obj/effect/floor_decal/borderfloor/corner{
+ dir = 8
+ },
+/obj/effect/floor_decal/corner/lime/bordercorner{
+ dir = 8
+ },
+/turf/simulated/floor/tiled,
+/area/tether/surfacebase/public_garden_three)
"pOZ" = (
/turf/simulated/floor/carpet,
/area/tether/surfacebase/security/hos)
@@ -41414,6 +41555,12 @@
/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers,
/turf/simulated/floor/tiled/dark,
/area/tether/surfacebase/security/iaa/officeb)
+"qpD" = (
+/obj/structure/flora/ausbushes/ppflowers{
+ pixel_y = -12
+ },
+/turf/simulated/floor/grass,
+/area/tether/surfacebase/public_garden_three)
"qqP" = (
/obj/machinery/hologram/holopad,
/turf/simulated/floor/tiled,
@@ -41424,6 +41571,13 @@
/obj/structure/window/reinforced/full,
/turf/simulated/floor/plating,
/area/crew_quarters/recreation_area)
+"qtp" = (
+/obj/structure/table/bench/wooden,
+/obj/machinery/atmospherics/pipe/simple/hidden/supply{
+ dir = 5
+ },
+/turf/simulated/floor/wood,
+/area/tether/surfacebase/public_garden_three)
"qtt" = (
/obj/structure/bed/chair{
dir = 1
@@ -41431,6 +41585,12 @@
/obj/machinery/atmospherics/pipe/simple/hidden/supply,
/turf/simulated/floor/carpet/blue,
/area/tether/surfacebase/security/breakroom)
+"qum" = (
+/obj/structure/bed/chair/wood{
+ dir = 4
+ },
+/turf/simulated/floor/grass,
+/area/tether/surfacebase/public_garden_three)
"qvp" = (
/obj/structure/disposalpipe/segment{
dir = 4
@@ -41520,6 +41680,12 @@
/obj/effect/floor_decal/steeldecal/steel_decals7,
/turf/simulated/floor/tiled,
/area/tether/surfacebase/surface_three_hall)
+"qJd" = (
+/obj/machinery/light{
+ dir = 4
+ },
+/turf/simulated/floor/grass,
+/area/tether/surfacebase/public_garden_three)
"qKO" = (
/obj/structure/closet/hydrant{
pixel_x = 32
@@ -41694,9 +41860,6 @@
"rEZ" = (
/obj/structure/table/woodentable,
/obj/item/weapon/reagent_containers/glass/bucket,
-/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{
- dir = 6
- },
/obj/effect/floor_decal/steeldecal/steel_decals6{
dir = 8
},
@@ -41706,8 +41869,8 @@
/obj/structure/window/reinforced{
dir = 8
},
-/obj/machinery/atmospherics/pipe/manifold/hidden/supply{
- dir = 8
+/obj/machinery/atmospherics/unary/vent_pump/on{
+ dir = 4
},
/turf/simulated/floor/tiled,
/area/tether/surfacebase/public_garden_three)
@@ -42032,6 +42195,13 @@
},
/turf/simulated/floor/carpet,
/area/tether/surfacebase/security/hos)
+"sTZ" = (
+/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers,
+/obj/machinery/atmospherics/pipe/simple/hidden/supply{
+ dir = 1
+ },
+/turf/simulated/floor/tiled,
+/area/tether/surfacebase/public_garden_three)
"sWs" = (
/obj/effect/floor_decal/borderfloorwhite{
dir = 8
@@ -42058,11 +42228,9 @@
/turf/simulated/floor/carpet/blue,
/area/tether/surfacebase/security/breakroom)
"sXa" = (
-/obj/machinery/holoposter{
- pixel_x = -30;
- pixel_y = 30
- },
-/turf/simulated/floor/grass,
+/obj/structure/table/woodentable,
+/obj/machinery/atmospherics/unary/vent_scrubber/on,
+/turf/simulated/floor/wood,
/area/tether/surfacebase/public_garden_three)
"sXR" = (
/obj/item/device/radio/intercom/department/security{
@@ -42331,6 +42499,10 @@
},
/turf/simulated/floor/tiled/freezer,
/area/crew_quarters/pool)
+"ufJ" = (
+/obj/structure/table/woodentable,
+/turf/simulated/floor/grass,
+/area/tether/surfacebase/public_garden_three)
"uhm" = (
/obj/structure/bed/chair/bay/chair{
dir = 8
@@ -42354,6 +42526,21 @@
},
/turf/simulated/floor/tiled/white,
/area/tether/surfacebase/medical/chemistry)
+"ukn" = (
+/obj/effect/floor_decal/borderfloor{
+ dir = 10
+ },
+/obj/effect/floor_decal/corner/lime/border{
+ dir = 10
+ },
+/obj/machinery/atmospherics/pipe/simple/hidden/supply{
+ dir = 5
+ },
+/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{
+ dir = 5
+ },
+/turf/simulated/floor/tiled,
+/area/tether/surfacebase/public_garden_three)
"und" = (
/obj/structure/cable/green{
d1 = 1;
@@ -42416,6 +42603,13 @@
/obj/structure/disposalpipe/segment,
/turf/simulated/floor/tiled/white,
/area/tether/surfacebase/medical/lobby)
+"uBR" = (
+/obj/structure/flora/ausbushes/ppflowers{
+ pixel_x = 5;
+ pixel_y = -7
+ },
+/turf/simulated/floor/grass,
+/area/tether/surfacebase/public_garden_three)
"uFI" = (
/obj/effect/floor_decal/borderfloor{
dir = 1
@@ -42430,6 +42624,10 @@
/obj/machinery/atmospherics/unary/vent_scrubber/on,
/turf/simulated/floor/tiled,
/area/tether/surfacebase/surface_three_hall)
+"uMD" = (
+/obj/machinery/hologram/holopad,
+/turf/simulated/floor/tiled,
+/area/tether/surfacebase/public_garden_three)
"uNt" = (
/turf/simulated/open,
/area/tether/surfacebase/security/upperhall)
@@ -43222,6 +43420,10 @@
},
/turf/simulated/floor/tiled/white,
/area/crew_quarters/recreation_area_restroom)
+"xwQ" = (
+/obj/machinery/light/flamp/noshade,
+/turf/simulated/floor/grass,
+/area/tether/surfacebase/public_garden_three)
"xxe" = (
/obj/effect/floor_decal/steeldecal/steel_decals_central5{
dir = 4
@@ -43266,6 +43468,15 @@
},
/turf/simulated/floor/tiled,
/area/tether/surfacebase/surface_three_hall)
+"xFm" = (
+/obj/machinery/atmospherics/pipe/simple/hidden/supply{
+ dir = 8
+ },
+/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{
+ dir = 4
+ },
+/turf/simulated/floor/wood,
+/area/tether/surfacebase/public_garden_three)
"xGw" = (
/obj/effect/floor_decal/spline/plain{
dir = 4
@@ -46588,7 +46799,7 @@ aab
aab
aab
aab
-aab
+aMG
aac
aac
aac
@@ -46729,8 +46940,8 @@ aab
aab
aab
aab
-aab
-aab
+aNU
+adG
aac
aac
aac
@@ -46869,10 +47080,10 @@ aab
aab
aab
aab
-aab
-aab
-aab
-aab
+aMG
+aNU
+adG
+adG
aac
aac
aac
@@ -47010,9 +47221,9 @@ aab
aab
aab
aab
-aab
-aab
-aab
+aNU
+adG
+adG
aac
aac
aac
@@ -47151,9 +47362,8 @@ aab
aab
aab
aab
-aab
-aab
-aac
+aNU
+adG
aac
aac
aac
@@ -47168,6 +47378,7 @@ aac
aac
aac
aac
+nio
aac
aac
aac
@@ -47292,25 +47503,25 @@ aab
aab
aab
aab
-aab
-aab
-aab
-aac
-aac
-aac
-aac
-aac
-aac
-aac
-aac
-aac
-aac
+aNU
+adG
aac
aac
aac
aac
aac
aac
+aiq
+jQV
+arW
+jQV
+jQV
+jQV
+jQV
+jQV
+jQV
+arW
+fQa
aac
aac
aac
@@ -47433,27 +47644,27 @@ aab
aab
aab
aab
-aab
-aab
-aab
-aab
-aac
-aac
-aac
-aac
-aac
-aac
-aac
-aac
-aac
-aac
-aac
+aNU
+adG
aac
aac
aac
aac
aac
aac
+aiq
+aDc
+iTV
+arW
+aDe
+aDe
+aDe
+aDe
+aDe
+iTV
+arW
+aDc
+fQa
aac
aac
aac
@@ -47574,29 +47785,29 @@ aab
aab
aab
aab
-aab
-aab
-aab
-aab
-aab
-aac
-aac
-aac
-aac
-aac
-aac
-aac
-aac
-aac
-aac
-aac
-aac
+aOm
+adG
aac
aac
aac
aac
aac
aac
+aiq
+aDc
+aDe
+nUE
+jDF
+aDe
+aDe
+aDe
+aDe
+aDe
+aDe
+jDF
+nUE
+aDc
+fQa
aac
aac
aac
@@ -47717,29 +47928,29 @@ aab
aab
aab
aab
-aab
-aab
-aab
-aab
-aac
-aac
-aac
-aac
-aac
-aac
-aac
-aac
-aac
-aac
-aac
-aac
-aac
aac
aac
aac
aac
aac
aac
+aiq
+aDc
+aDe
+aDe
+aDe
+aDe
+aCZ
+aDe
+aDe
+aDe
+aDe
+aDe
+aDe
+aDe
+aDe
+aDc
+fQa
aac
aac
aac
@@ -47859,14 +48070,6 @@ aab
aab
aab
aab
-aab
-aab
-aab
-aac
-aac
-aac
-aac
-aac
aac
aac
aac
@@ -47874,14 +48077,22 @@ aac
aac
aac
arW
-aDq
-aDq
-aDq
-aDq
arW
-aac
-aac
-aac
+aDb
+aDe
+aDe
+jbL
+aDe
+jbL
+aDe
+aDe
+aDq
+aDq
+aDe
+aDe
+aDe
+aDe
+aDQ
aac
aac
aac
@@ -48001,26 +48212,28 @@ aab
aab
aab
aab
-aab
-aab
-aab
aac
aac
aac
aac
aac
aac
-aac
-aac
-aac
-aac
-aCZ
-arW
-aDa
-aDa
-aDa
-aDL
-arW
+aiF
+aDe
+aDe
+aDe
+aDe
+aDe
+nUE
+aDe
+aDe
+aDe
+aDe
+hXh
+aDe
+aDe
+aDe
+aDe
aDQ
aac
aac
@@ -48073,8 +48286,6 @@ aac
aac
aac
aac
-aac
-aac
aab
aab
aab
@@ -48143,27 +48354,28 @@ aab
aab
aab
aab
-aab
-aab
-aab
aac
aac
aac
aac
aac
aac
-aac
-aac
-aac
-aCZ
-aDc
-aiq
-sXa
-aDe
-aDe
-aDe
aiF
-aDc
+aCZ
+aUF
+dgA
+aVr
+aDe
+aDe
+aDe
+aDe
+aDe
+aDe
+nUE
+aDe
+aDe
+aDe
+aDe
aDQ
aac
aac
@@ -48216,7 +48428,6 @@ aac
aac
aac
aac
-aac
aab
aab
aab
@@ -48285,19 +48496,19 @@ aab
aab
aab
aab
-aab
-aab
-aab
aac
aac
aac
aac
aac
aac
-aac
-aac
-aCZ
-aDc
+aiF
+aDl
+aVr
+iiv
+qtp
+aDe
+aDe
aDf
aDe
aDj
@@ -48305,8 +48516,8 @@ aDr
aDr
aDA
aDe
-aDf
-aDc
+aDe
+aDe
aDQ
aac
aac
@@ -48427,27 +48638,27 @@ aab
aab
aab
aab
-aab
-aab
aac
aac
aac
aac
aac
aac
-aac
-aac
-arW
-arW
-aDd
-aDf
+aiF
+aDe
+aVr
+sXa
+pIJ
+xwQ
+aDe
+fdB
aDj
ajt
aDu
-aDu
+ntl
aDR
-aDA
-aDf
+ukn
+aDe
aDT
arW
arW
@@ -48569,28 +48780,28 @@ aab
aab
aab
aab
-aab
-aab
-aab
aac
aac
aac
aac
aac
aac
-aac
-aCY
-aDa
+aiF
aDe
-aDj
-ajt
+aVr
+noU
+aCY
+aDn
+aDn
+aDn
+akm
aDg
aDB
aDG
-aDg
-aDR
-aVr
-aDe
+uMD
+pNF
+aDr
+aDr
aVx
aDc
aip
@@ -48711,27 +48922,27 @@ aab
aab
aab
aab
-aab
-aab
aac
aac
aac
aac
aac
aac
-aac
-aac
-aCY
-aDe
-aDe
+aiF
+aDL
+aVr
+aUF
+xFm
aDn
-aju
+hMw
+aDn
+akm
aDi
aDs
aDs
aUw
aVb
-iiv
+tqY
tqY
tqY
haS
@@ -48853,20 +49064,20 @@ aab
aab
aab
aab
-aab
-aab
aac
aac
aac
aac
aac
aac
-aac
-aac
-aCY
-aDa
+aiF
aDe
-aDh
+aVr
+noU
+aCY
+aDn
+aDn
+aDn
akm
aDk
aDs
@@ -48874,8 +49085,8 @@ aDs
aUz
hxc
erS
-aDe
-aDe
+erS
+erS
aDc
aVA
sDG
@@ -48995,30 +49206,30 @@ aab
aab
aab
aab
-aab
-aab
aac
aac
aac
aac
aac
aac
-aac
-aac
-arW
-arW
+aiF
+aDe
+aVr
+iiv
+hal
+kKV
aDd
-aDf
+kKV
aDm
-aDl
+aDg
aDt
rEZ
-dgA
+aDg
cwI
-aDf
+uBR
+qum
aDT
arW
-arW
aVE
aWF
akn
@@ -49137,28 +49348,28 @@ aab
aab
aab
aab
-aab
-aab
-aab
-aac
-aac
aac
aac
aac
aac
aac
aac
+aiF
+aDl
+aVr
+sXa
+gOS
aDb
-aDc
-aDf
-aDn
-aDg
-aDu
+aDe
+aDe
+hVd
+jcR
+bSW
aDD
-aDg
+sTZ
aDN
-aDf
-aDc
+aDS
+ufJ
aDz
agw
air
@@ -49279,29 +49490,29 @@ aab
aab
aab
aab
-aab
-aab
aac
aac
aac
aac
aac
aac
-aac
-aac
-aac
-aac
-aDb
-aDc
+aiF
+aDe
+aUF
+aDe
+aVr
+aDe
+aDe
+aDe
acm
aDv
aDg
aDJ
-aUC
+aDg
aDO
-aDc
aDS
-aac
+ufJ
+aDz
agw
air
aWF
@@ -49421,29 +49632,29 @@ aab
aab
aab
aab
-aab
-aab
aac
aac
aac
aac
aac
aac
-aac
-aac
-aac
-aac
-aac
-aDb
+aiF
+aDe
+aDe
+aDe
+hXh
+kZr
+qpD
+kTt
arW
aDw
aDg
aDJ
-aUF
-arW
+aDg
+aDO
aDS
-aac
-aac
+ufJ
+aDz
agw
air
aWF
@@ -49563,29 +49774,29 @@ aab
aab
aab
aab
-aab
aac
aac
aac
aac
aac
aac
-aac
-aac
-aac
-aac
-aac
-aac
-aac
-aCY
+arW
+arW
+aDe
+aCZ
+aDe
+kZr
+aDe
+kZr
+arW
aDx
aDE
aDK
aUM
aDP
-aac
-aac
-aac
+hxr
+cAZ
+aDe
agw
aVG
aWF
@@ -49705,7 +49916,6 @@ aab
aab
aab
aab
-aab
aac
aac
aac
@@ -49713,19 +49923,20 @@ aac
aac
aac
aac
-aac
-aac
-aac
-aac
-aac
-aac
-aCY
+aUC
+aDc
+qJd
+aDe
+kZr
+aDe
+kZr
+arW
aDy
aDF
aDH
aUO
-aDP
-aac
+arW
+eCg
agw
agw
agw
@@ -49855,17 +50066,17 @@ aac
aac
aac
aac
-aac
-aac
-aac
-aac
-aac
-aac
+aUC
+arW
+aDL
+kZr
+aDe
+kZr
+arW
+arW
+arW
+arW
arW
-aDz
-aDz
-aDz
-aDz
arW
aac
agw
@@ -49998,12 +50209,12 @@ aac
aac
aac
aac
-aac
-aac
-aac
-aac
-aac
-aac
+arW
+eCg
+eCg
+eCg
+eCg
+arW
aac
aac
aac
diff --git a/maps/tether/tether_turfs.dm b/maps/tether/tether_turfs.dm
index e1fadf2653f..5bec9aef79e 100644
--- a/maps/tether/tether_turfs.dm
+++ b/maps/tether/tether_turfs.dm
@@ -70,7 +70,7 @@ VIRGO3B_TURF_CREATE(/turf/simulated/mineral/floor)
"platinum" = 10,
"hematite" = 20,
"copper" = 8,
- "tin" = 4,
+// "tin" = 4,
"bauxite" = 4,
"rutile" = 4,
"carbon" = 20,
@@ -87,7 +87,7 @@ VIRGO3B_TURF_CREATE(/turf/simulated/mineral/floor)
"platinum" = 5,
"hematite" = 35,
"copper" = 15,
- "tin" = 10,
+// "tin" = 10,
"bauxite" = 10,
"rutile" = 10,
"carbon" = 35,
diff --git a/sound/voice/spiderchitter.ogg b/sound/voice/spiderchitter.ogg
new file mode 100644
index 00000000000..108871a3027
Binary files /dev/null and b/sound/voice/spiderchitter.ogg differ
diff --git a/sound/voice/spiderpurr.ogg b/sound/voice/spiderpurr.ogg
new file mode 100644
index 00000000000..3a5eba50119
Binary files /dev/null and b/sound/voice/spiderpurr.ogg differ
diff --git a/tgui/packages/tgui-dev-server/package.json b/tgui/packages/tgui-dev-server/package.json
index bcb02196e65..969f5dcd7f6 100644
--- a/tgui/packages/tgui-dev-server/package.json
+++ b/tgui/packages/tgui-dev-server/package.json
@@ -7,6 +7,6 @@
"glob": "^7.1.4",
"source-map": "^0.7.3",
"stacktrace-parser": "^0.1.7",
- "ws": "^7.1.2"
+ "ws": "^7.4.6"
}
}
diff --git a/tgui/packages/tgui/interfaces/VorePanel.js b/tgui/packages/tgui/interfaces/VorePanel.js
index 34722433ff1..bc91fb376ce 100644
--- a/tgui/packages/tgui/interfaces/VorePanel.js
+++ b/tgui/packages/tgui/interfaces/VorePanel.js
@@ -607,6 +607,7 @@ const VoreUserPreferences = (props, context) => {
show_vore_fx,
can_be_drop_prey,
can_be_drop_pred,
+ allow_spontaneous_tf,
step_mechanics_active,
pickup_mechanics_active,
noisy,
@@ -767,7 +768,7 @@ const VoreUserPreferences = (props, context) => {
+ " Click this to enable showing FX.")}
content={show_vore_fx ? "Show Vore FX" : "Do Not Show Vore FX"} />
-
+
-
+
+
+