diff --git a/code/__defines/mobs.dm b/code/__defines/mobs.dm
index 9343a4590f..bb17deacf4 100644
--- a/code/__defines/mobs.dm
+++ b/code/__defines/mobs.dm
@@ -10,7 +10,7 @@
#define CANPUSH 0x8
#define LEAPING 0x10
#define HIDING 0x20
-#define PASSEMOTES 0x32 // Mob has a cortical borer or holders inside of it that need to see emotes.
+#define PASSEMOTES 0x40 // Mob has a cortical borer or holders inside of it that need to see emotes.
#define GODMODE 0x1000
#define FAKEDEATH 0x2000 // Replaces stuff like changeling.changeling_fakedeath.
#define DISFIGURED 0x4000 // Set but never checked. Remove this sometime and replace occurences with the appropriate organ code
diff --git a/code/_helpers/logging.dm b/code/_helpers/logging.dm
index f9b7a6e3ef..6644cc2efe 100644
--- a/code/_helpers/logging.dm
+++ b/code/_helpers/logging.dm
@@ -25,6 +25,11 @@
if (config.log_admin)
diary << "\[[time_stamp()]]ADMIN: [text][log_end]"
+/proc/log_adminpm(text, client/source, client/dest)
+ admin_log.Add(text)
+ if (config.log_admin)
+ diary << "\[[time_stamp()]]ADMINPM: [key_name(source)]->[key_name(dest)]: [html_decode(text)][log_end]"
+
/proc/log_debug(text)
if (config.log_debug)
debug_log << "\[[time_stamp()]]DEBUG: [text][log_end]"
diff --git a/code/game/gamemodes/changeling/powers/revive.dm b/code/game/gamemodes/changeling/powers/revive.dm
index ab8111000d..0da6c3c3d5 100644
--- a/code/game/gamemodes/changeling/powers/revive.dm
+++ b/code/game/gamemodes/changeling/powers/revive.dm
@@ -33,7 +33,7 @@
H.restore_all_organs(ignore_prosthetic_prefs=1) //Covers things like fractures and other things not covered by the above.
H.restore_blood()
H.mutations.Remove(HUSK)
- H.status_flags -= DISFIGURED
+ H.status_flags &= ~DISFIGURED
H.update_icons_body()
for(var/limb in H.organs_by_name)
var/obj/item/organ/external/current_limb = H.organs_by_name[limb]
diff --git a/code/game/objects/items/devices/communicator/UI.dm b/code/game/objects/items/devices/communicator/UI.dm
index cbdae90c90..f2d3fd39b6 100644
--- a/code/game/objects/items/devices/communicator/UI.dm
+++ b/code/game/objects/items/devices/communicator/UI.dm
@@ -134,11 +134,7 @@
if(href_list["rename"])
var/new_name = sanitizeSafe(input(usr,"Please enter your name.","Communicator",usr.name) )
if(new_name)
- owner = new_name
- name = "[owner]'s [initial(name)]"
- if(camera)
- camera.name = name
- camera.c_tag = name
+ register_device(new_name)
if(href_list["toggle_visibility"])
switch(network_visibility)
diff --git a/code/game/objects/items/devices/communicator/communicator.dm b/code/game/objects/items/devices/communicator/communicator.dm
index f809bcb9b2..7342988916 100644
--- a/code/game/objects/items/devices/communicator/communicator.dm
+++ b/code/game/objects/items/devices/communicator/communicator.dm
@@ -75,12 +75,12 @@ var/global/list/obj/item/device/communicator/all_communicators = list()
//This is a pretty terrible way of doing this.
spawn(5 SECONDS) //Wait for our mob to finish spawning.
if(ismob(loc))
- register_device(loc)
+ register_device(loc.name)
initialize_exonet(loc)
else if(istype(loc, /obj/item/weapon/storage))
var/obj/item/weapon/storage/S = loc
if(ismob(S.loc))
- register_device(S.loc)
+ register_device(S.loc.name)
initialize_exonet(S.loc)
// Proc: examine()
@@ -268,12 +268,12 @@ var/global/list/obj/item/device/communicator/all_communicators = list()
// Proc: register_device()
// Parameters: 1 (user - the person to use their name for)
// Description: Updates the owner's name and the device's name.
-/obj/item/device/communicator/proc/register_device(mob/user)
- if(!user)
+/obj/item/device/communicator/proc/register_device(new_name)
+ if(!new_name)
return
- owner = user.name
+ owner = new_name
- name = "[owner]'s [initial(name)]"
+ name = "[new_name]'s [initial(name)]"
if(camera)
camera.name = name
camera.c_tag = name
diff --git a/code/modules/admin/admin.dm b/code/modules/admin/admin.dm
index a9075a8430..a31748d676 100644
--- a/code/modules/admin/admin.dm
+++ b/code/modules/admin/admin.dm
@@ -6,7 +6,7 @@ var/global/floorIsLava = 0
////////////////////////////////
/proc/message_admins(var/msg)
msg = "ADMIN LOG: [msg]"
- log_adminwarn(msg)
+ //log_adminwarn(msg) //log_and_message_admins is for this
for(var/client/C in admins)
if((R_ADMIN|R_MOD) & C.holder.rights)
C << msg
diff --git a/code/modules/admin/verbs/adminpm.dm b/code/modules/admin/verbs/adminpm.dm
index dcaa745485..4ff94877f4 100644
--- a/code/modules/admin/verbs/adminpm.dm
+++ b/code/modules/admin/verbs/adminpm.dm
@@ -104,7 +104,7 @@
if(C.is_preference_enabled(/datum/client_preference/holder/play_adminhelp_ping))
C << 'sound/effects/adminhelp.ogg'
- log_admin("PM: [key_name(src)]->[key_name(C)]: [msg]")
+ log_adminpm(msg,src,C)
send2adminirc("Reply: [key_name(src)]->[key_name(C)]: [html_decode(msg)]")
//we don't use message_admins here because the sender/receiver might get it too
diff --git a/code/modules/client/preference_setup/general/03_body.dm b/code/modules/client/preference_setup/general/03_body.dm
index ea8f029b88..1fc3b1d8ba 100644
--- a/code/modules/client/preference_setup/general/03_body.dm
+++ b/code/modules/client/preference_setup/general/03_body.dm
@@ -741,7 +741,15 @@ var/global/list/valid_bloodtypes = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O
dat += "Language: [current_species.species_language]
"
dat += ""
if(current_species.spawn_flags & SPECIES_CAN_JOIN)
- dat += "Often present on human stations."
+ switch(current_species.rarity_value)
+ if(1 to 2)
+ dat += "Often present on human stations."
+ if(3 to 4)
+ dat += "Rarely present on human stations."
+ if(5)
+ dat += "Unheard of on human stations."
+ else
+ dat += "May be present on human stations."
if(current_species.spawn_flags & SPECIES_IS_WHITELISTED)
dat += "Whitelist restricted."
if(!current_species.has_organ[O_HEART])
diff --git a/code/modules/client/preference_setup/occupation/occupation.dm b/code/modules/client/preference_setup/occupation/occupation.dm
index 18e1be05f4..1a09ee6ae9 100644
--- a/code/modules/client/preference_setup/occupation/occupation.dm
+++ b/code/modules/client/preference_setup/occupation/occupation.dm
@@ -94,7 +94,7 @@
if(job.minimum_character_age && user.client && (user.client.prefs.age < job.minimum_character_age))
. += "[rank] \[MINIMUM CHARACTER AGE: [job.minimum_character_age]] | "
continue
- if((pref.job_civilian_low & ASSISTANT) && (rank != "Assistant"))
+ if((pref.job_civilian_low & ASSISTANT) && job.flag != ASSISTANT)
. += "[rank] | "
continue
if((rank in command_positions) || (rank == "AI"))//Bold head jobs
@@ -106,7 +106,7 @@
. += ""
- if(rank == "Assistant")//Assistant is special
+ if(job.flag == ASSISTANT)//Assistant is special
if(pref.job_civilian_low & ASSISTANT)
. += " \[Yes]"
else
@@ -180,7 +180,7 @@
if(!job)
return 0
- if(role == "Assistant")
+ if(job.flag == ASSISTANT)
if(pref.job_civilian_low & job.flag)
pref.job_civilian_low &= ~job.flag
else
diff --git a/code/modules/clothing/clothing.dm b/code/modules/clothing/clothing.dm
index ff8d0d94c6..ee815fd558 100644
--- a/code/modules/clothing/clothing.dm
+++ b/code/modules/clothing/clothing.dm
@@ -57,7 +57,7 @@
if (!..())
return 0
- if(species_restricted && istype(M,/mob/living/carbon/human))
+ if(LAZYLEN(species_restricted) && istype(M,/mob/living/carbon/human))
var/exclusive = null
var/wearable = null
var/mob/living/carbon/human/H = M
diff --git a/code/modules/mob/animations.dm b/code/modules/mob/animations.dm
index 9074357afa..edffbb5a87 100644
--- a/code/modules/mob/animations.dm
+++ b/code/modules/mob/animations.dm
@@ -167,7 +167,17 @@ note dizziness decrements automatically in the mob's Life() proc.
/mob/living/do_attack_animation(atom/A)
..()
- is_floating = 0 // If we were without gravity, the bouncing animation got stopped, so we make sure we restart the bouncing after the next movement.
+ //Check for clients with pref enabled
+ var/list/viewing = list()
+ for(var/m in viewers(A))
+ var/mob/M = m
+ var/client/C = M.client
+ if(C && C.is_preference_enabled(/datum/client_preference/attack_icons))
+ viewing += M.client
+
+ //Animals attacking each other in the distance, probably. Forgeddaboutit.
+ if(!viewing.len)
+ return FALSE
// What icon do we use for the attack?
var/obj/used_item
@@ -180,6 +190,9 @@ note dizziness decrements automatically in the mob's Life() proc.
if(!used_item && !(attack_icon && attack_icon_state))
return FALSE //Didn't find an item, not doing animation.
+ // If we were without gravity, the bouncing animation got stopped, so we make sure we restart the bouncing after the next movement.
+ is_floating = 0
+
var/image/I
if(used_item) //Found an in-hand item to animate
@@ -192,13 +205,7 @@ note dizziness decrements automatically in the mob's Life() proc.
I = image(attack_icon, A, attack_icon_state, A.layer + 1)
I.dir = dir
- //Check for clients with pref enabled
- var/list/viewing = list()
- for(var/m in viewers(A))
- var/mob/M = m
- var/client/C = M.client
- if(C && C.is_preference_enabled(/datum/client_preference/attack_icons))
- viewing += M.client
+ // Show the overlay to the clients
flick_overlay(I, viewing, 5, TRUE) // 5 ticks/half a second
// Set the direction of the icon animation.
diff --git a/code/modules/mob/freelook/chunk.dm b/code/modules/mob/freelook/chunk.dm
index 7c50bd3135..41439b4adc 100644
--- a/code/modules/mob/freelook/chunk.dm
+++ b/code/modules/mob/freelook/chunk.dm
@@ -100,6 +100,7 @@
for(var/turf in visRemoved)
var/turf/t = turf
if(obscuredTurfs[t])
+ LAZYINITLIST(t.obfuscations)
if(!t.obfuscations[obfuscation.type])
var/image/ob_image = image(obfuscation.icon, t, obfuscation.icon_state, OBFUSCATION_LAYER)
ob_image.plane = PLANE_FULLSCREEN
@@ -141,6 +142,7 @@
for(var/turf in obscuredTurfs)
var/turf/t = turf
+ LAZYINITLIST(t.obfuscations)
if(!t.obfuscations[obfuscation.type])
var/image/ob_image = image(obfuscation.icon, t, obfuscation.icon_state, OBFUSCATION_LAYER)
ob_image.plane = PLANE_FULLSCREEN
diff --git a/code/modules/mob/freelook/update_triggers.dm b/code/modules/mob/freelook/update_triggers.dm
index 482025426e..0b31e24cce 100644
--- a/code/modules/mob/freelook/update_triggers.dm
+++ b/code/modules/mob/freelook/update_triggers.dm
@@ -8,7 +8,7 @@
VN.updateVisibility(A, opacity_check)
/turf
- var/list/image/obfuscations = new()
+ var/list/image/obfuscations
/turf/drain_power()
return -1
diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm
index b92ab58641..7184308306 100644
--- a/code/modules/mob/living/carbon/human/human.dm
+++ b/code/modules/mob/living/carbon/human/human.dm
@@ -94,6 +94,8 @@
stat("Chemical Storage", mind.changeling.chem_charges)
stat("Genetic Damage Time", mind.changeling.geneticdamage)
stat("Re-Adaptations", "[mind.changeling.readapts]/[mind.changeling.max_readapts]")
+ if(species)
+ species.Stat(src)
/mob/living/carbon/human/ex_act(severity)
if(!blinded)
diff --git a/code/modules/mob/living/carbon/human/life.dm b/code/modules/mob/living/carbon/human/life.dm
index b5aef7ac1f..3ce0c85ef1 100644
--- a/code/modules/mob/living/carbon/human/life.dm
+++ b/code/modules/mob/living/carbon/human/life.dm
@@ -1131,7 +1131,7 @@
else
sight &= ~(SEE_TURFS|SEE_MOBS|SEE_OBJS)
- see_invisible = see_in_dark>2 ? SEE_INVISIBLE_LEVEL_ONE : SEE_INVISIBLE_LIVING
+ see_invisible = see_in_dark>2 ? SEE_INVISIBLE_LEVEL_ONE : see_invisible_default
if(XRAY in mutations)
sight |= SEE_TURFS|SEE_MOBS|SEE_OBJS
@@ -1143,7 +1143,7 @@
if(R && R.word1 == cultwords["see"] && R.word2 == cultwords["hell"] && R.word3 == cultwords["join"])
see_invisible = SEE_INVISIBLE_CULT
else
- see_invisible = SEE_INVISIBLE_LIVING
+ see_invisible = see_invisible_default
seer = 0
if(!seedarkness)
@@ -1154,7 +1154,7 @@
else
sight = species.get_vision_flags(src)
see_in_dark = species.darksight
- see_invisible = see_in_dark>2 ? SEE_INVISIBLE_LEVEL_ONE : SEE_INVISIBLE_LIVING
+ see_invisible = see_in_dark>2 ? SEE_INVISIBLE_LEVEL_ONE : see_invisible_default
var/tmp/glasses_processed = 0
var/obj/item/weapon/rig/rig = back
@@ -1175,7 +1175,7 @@
if(!glasses_processed && (species.get_vision_flags(src) > 0))
sight |= species.get_vision_flags(src)
if(!seer && !glasses_processed && seedarkness)
- see_invisible = SEE_INVISIBLE_LIVING
+ see_invisible = see_invisible_default
if(healths)
if (chem_effects[CE_PAINKILLER] > 100)
@@ -1355,7 +1355,7 @@
if(G.see_invisible >= 0)
see_invisible = G.see_invisible
else if(!druggy && !seer)
- see_invisible = SEE_INVISIBLE_LIVING
+ see_invisible = see_invisible_default
/mob/living/carbon/human/handle_random_events()
if(inStasisNow())
diff --git a/code/modules/mob/living/carbon/human/species/species.dm b/code/modules/mob/living/carbon/human/species/species.dm
index 39b8460907..787ec95719 100644
--- a/code/modules/mob/living/carbon/human/species/species.dm
+++ b/code/modules/mob/living/carbon/human/species/species.dm
@@ -422,3 +422,7 @@
// Used to override normal fall behaviour. Use only when the species does fall down a level.
/datum/species/proc/fall_impact_special(var/mob/living/carbon/human/H, var/atom/A)
return FALSE
+
+// Allow species to display interesting information in the human stat panels
+/datum/species/proc/Stat(var/mob/living/carbon/human/H)
+ return
diff --git a/code/modules/mob/living/carbon/metroid/life.dm b/code/modules/mob/living/carbon/metroid/life.dm
index 0f393a9417..5cf6395b7d 100644
--- a/code/modules/mob/living/carbon/metroid/life.dm
+++ b/code/modules/mob/living/carbon/metroid/life.dm
@@ -100,7 +100,7 @@
src.lying = 1
src.blinded = 1
else
- if (src.paralysis || src.stunned || src.weakened || (status_flags && FAKEDEATH)) //Stunned etc.
+ if (src.paralysis || src.stunned || src.weakened || (status_flags & FAKEDEATH)) //Stunned etc.
if (src.stunned > 0)
src.stat = 0
if (src.weakened > 0)
diff --git a/code/modules/mob/living/living_defines.dm b/code/modules/mob/living/living_defines.dm
index 23dbb6fc09..9739e596b8 100644
--- a/code/modules/mob/living/living_defines.dm
+++ b/code/modules/mob/living/living_defines.dm
@@ -55,5 +55,7 @@
var/glow_intensity = null
var/glow_color = "#FFFFFF" // The color they're glowing!
+ var/see_invisible_default = SEE_INVISIBLE_LIVING
+
var/list/hud_list //Holder for health hud, status hud, wanted hud, etc (not like inventory slots)
var/has_huds = FALSE //Whether or not we should bother initializing the above list
diff --git a/code/modules/mob/living/say.dm b/code/modules/mob/living/say.dm
index d022ef71b8..9e4ede05a1 100644
--- a/code/modules/mob/living/say.dm
+++ b/code/modules/mob/living/say.dm
@@ -298,11 +298,14 @@ proc/get_radio_key_from_channel(var/channel)
else
return 1 //If we're in nullspace, then forget it.
+ //Remember the speech images so we can remove them later and they can get GC'd
+ var/list/images_to_clients = list()
+
//The 'post-say' static speech bubble
var/speech_bubble_test = say_test(message)
var/speech_type = speech_bubble_appearance()
var/image/speech_bubble = image('icons/mob/talk.dmi',src,"[speech_type][speech_bubble_test]")
- spawn(30) qdel(speech_bubble)
+ images_to_clients[speech_bubble] = list()
// Attempt Multi-Z Talking
var/mob/above = src.shadow
@@ -311,7 +314,7 @@ proc/get_radio_key_from_channel(var/channel)
if(ST)
var/list/results = get_mobs_and_objs_in_view_fast(ST, world.view)
var/image/z_speech_bubble = image('icons/mob/talk.dmi', above, "h[speech_bubble_test]")
- spawn(30) qdel(z_speech_bubble)
+ images_to_clients[z_speech_bubble] = list()
for(var/item in results["mobs"])
if(item != above && !(item in listening))
listening[item] = z_speech_bubble
@@ -326,12 +329,17 @@ proc/get_radio_key_from_channel(var/channel)
var/dst = get_dist(get_turf(M),get_turf(src))
if(dst <= message_range || (M.stat == DEAD && !forbid_seeing_deadchat)) //Inside normal message range, or dead with ears (handled in the view proc)
- M << (listening[M] || speech_bubble) // Send the image attached to shadow mob if available
+ if(M.client)
+ var/image/I1 = listening[M] || speech_bubble
+ images_to_clients[I1] |= M.client
+ M << I1
M.hear_say(message, verb, speaking, alt_name, italics, src, speech_sound, sound_vol)
-
if(whispering) //Don't even bother with these unless whispering
if(dst > message_range && dst <= w_scramble_range) //Inside whisper scramble range
- M << (listening[M] || speech_bubble) // Send the image attached to shadow mob if available
+ if(M.client)
+ var/image/I2 = listening[M] || speech_bubble
+ images_to_clients[I2] |= M.client
+ M << I2
M.hear_say(stars(message), verb, speaking, alt_name, italics, src, speech_sound, sound_vol*0.2)
if(dst > w_scramble_range && dst <= world.view) //Inside whisper 'visible' range
M.show_message("[src.name] [w_not_heard].", 2)
@@ -344,6 +352,17 @@ proc/get_radio_key_from_channel(var/channel)
if(dst <= message_range)
O.hear_talk(src, message, verb, speaking)
+ //Remove all those images. At least it's just ONE spawn this time.
+ spawn(30)
+ for(var/img in images_to_clients)
+ var/image/I = img
+ var/list/clients_from_image = images_to_clients[I]
+ for(var/client in clients_from_image)
+ var/client/C = client
+ if(C) //Could have disconnected after message sent, before removing bubble.
+ C.images -= I
+ qdel(I)
+
//Log the message to file
if(whispering)
log_whisper(message,src)
diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm
index 2bdcf1b912..2b11059ff9 100644
--- a/code/modules/mob/living/silicon/ai/ai.dm
+++ b/code/modules/mob/living/silicon/ai/ai.dm
@@ -258,7 +258,7 @@ var/list/ai_verbs_default = list(
aiPDA.name = pickedName + " (" + aiPDA.ownjob + ")"
if(aiCommunicator)
- aiCommunicator.register_device(src)
+ 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.
diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm
index 589f410f01..be725f353d 100644
--- a/code/modules/mob/living/silicon/robot/robot.dm
+++ b/code/modules/mob/living/silicon/robot/robot.dm
@@ -210,7 +210,7 @@
/mob/living/silicon/robot/proc/setup_communicator()
if (!communicator)
communicator = new/obj/item/device/communicator/integrated(src)
- communicator.register_device(src, "[modtype] [braintype]")
+ communicator.register_device(src.name, "[modtype] [braintype]")
//If there's an MMI in the robot, have it ejected when the mob goes away. --NEO
//Improved /N
diff --git a/code/modules/multiz/zshadow.dm b/code/modules/multiz/zshadow.dm
index 10af55f5a3..85685b3d6a 100644
--- a/code/modules/multiz/zshadow.dm
+++ b/code/modules/multiz/zshadow.dm
@@ -56,6 +56,7 @@
overlays = M.overlays
transform = M.transform
dir = M.dir
+ invisibility = M.invisibility
if(shadow)
shadow.sync_icon(src)