diff --git a/code/__DEFINES/dcs/signals.dm b/code/__DEFINES/dcs/signals.dm
index d19d1f7e9633..fbc851a6d5c4 100644
--- a/code/__DEFINES/dcs/signals.dm
+++ b/code/__DEFINES/dcs/signals.dm
@@ -225,8 +225,8 @@
/////////////////
-#define COMSIG_ENTER_AREA "enter_area" //from base of area/Entered(): (/area)
-#define COMSIG_EXIT_AREA "exit_area" //from base of area/Exited(): (/area)
+#define COMSIG_ENTER_AREA "enter_area" //from base of area/Entered(): (/area). Sent to "area-sensitive" movables, see __DEFINES/traits.dm for info.
+#define COMSIG_EXIT_AREA "exit_area" //from base of area/Exited(): (/area). Sent to "area-sensitive" movables, see __DEFINES/traits.dm for info.
#define COMSIG_CLICK "atom_click" //from base of atom/Click(): (location, control, params, mob/user)
#define COMSIG_CLICK_SHIFT "shift_click" //from base of atom/ShiftClick(): (/mob)
diff --git a/code/__DEFINES/important_recursive_contents.dm b/code/__DEFINES/important_recursive_contents.dm
index 62be5b38e539..f1dc0bd3ea18 100644
--- a/code/__DEFINES/important_recursive_contents.dm
+++ b/code/__DEFINES/important_recursive_contents.dm
@@ -1,2 +1,4 @@
+///the area channel of the important_recursive_contents list, everything in here will be sent a signal when their last holding object changes areas
+#define RECURSIVE_CONTENTS_AREA_SENSITIVE "recursive_contents_area_sensitive"
///the hearing channel of the important_recursive_contents list, everything in here will count as a hearing atom
#define RECURSIVE_CONTENTS_HEARING_SENSITIVE "recursive_contents_hearing_sensitive"
diff --git a/code/__DEFINES/traits.dm b/code/__DEFINES/traits.dm
index 69de29d4cd70..f1289d15bdbc 100644
--- a/code/__DEFINES/traits.dm
+++ b/code/__DEFINES/traits.dm
@@ -272,6 +272,13 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
#define TRAIT_HEARING_SENSITIVE "hearing_sensitive"
+/*
+ * Used for movables that need to be updated, via COMSIG_ENTER_AREA and COMSIG_EXIT_AREA, when transitioning areas.
+ * Use [/atom/movable/proc/become_area_sensitive(trait_source)] to properly enable it. How you remove it isn't as important.
+ */
+#define TRAIT_AREA_SENSITIVE "area-sensitive"
+
+///Used for managing KEEP_TOGETHER in [/atom/var/appearance_flags]
#define TRAIT_KEEP_TOGETHER "keep-together"
// item traits
@@ -426,6 +433,9 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
#define TRAIT_FISH_CASE_COMPATIBILE "fish_case_compatibile"
/// Granted by prismwine
#define TRAIT_REFLECTIVE "reflective"
+/// Self-explainatory.
+#define BEAUTY_ELEMENT_TRAIT "beauty_element"
+#define MOOD_COMPONENT_TRAIT "mood_component"
/// Trait granted by [mob/living/silicon/ai]
/// Applied when the ai anchors itself
diff --git a/code/__HELPERS/unsorted.dm b/code/__HELPERS/unsorted.dm
index 94039f138721..8e9a1dbc9979 100644
--- a/code/__HELPERS/unsorted.dm
+++ b/code/__HELPERS/unsorted.dm
@@ -1558,3 +1558,14 @@ GLOBAL_DATUM_INIT(dview_mob, /mob/dview, new)
return call(source, proctype)(arglist(arguments))
#define TURF_FROM_COORDS_LIST(List) (locate(List[1], List[2], List[3]))
+
+/proc/normalize_dir_to_cardinals(dir)
+ if(dir & NORTH)
+ return NORTH
+ if(dir & SOUTH)
+ return SOUTH
+ if(dir & EAST)
+ return EAST
+ if(dir & WEST)
+ return WEST
+ return 0
diff --git a/code/datums/components/beauty.dm b/code/datums/components/beauty.dm
deleted file mode 100644
index fe3c06e3ad5a..000000000000
--- a/code/datums/components/beauty.dm
+++ /dev/null
@@ -1,38 +0,0 @@
-/datum/component/beauty
- var/beauty = 0
-
-/datum/component/beauty/Initialize(beautyamount)
- if(!isatom(parent) || isarea(parent))
- return COMPONENT_INCOMPATIBLE
-
- beauty = beautyamount
-
- if(ismovable(parent))
- RegisterSignal(parent, COMSIG_ENTER_AREA, PROC_REF(enter_area))
- RegisterSignal(parent, COMSIG_EXIT_AREA, PROC_REF(exit_area))
-
- var/area/A = get_area(parent)
- if(A)
- enter_area(null, A)
-
-/datum/component/beauty/proc/enter_area(datum/source, area/A)
- SIGNAL_HANDLER
-
- if(A.outdoors)
- return
- A.totalbeauty += beauty
- A.update_beauty()
-
-/datum/component/beauty/proc/exit_area(datum/source, area/A)
- SIGNAL_HANDLER
-
- if(A.outdoors)
- return
- A.totalbeauty -= beauty
- A.update_beauty()
-
-/datum/component/beauty/Destroy()
- . = ..()
- var/area/A = get_area(parent)
- if(A)
- exit_area(null, A)
diff --git a/code/datums/components/fantasy/prefixes.dm b/code/datums/components/fantasy/prefixes.dm
index 7445ab582bf5..bff1c6ec3dd0 100644
--- a/code/datums/components/fantasy/prefixes.dm
+++ b/code/datums/components/fantasy/prefixes.dm
@@ -66,3 +66,29 @@
var/obj/item/master = comp.parent
comp.appliedComponents += master.AddComponent(/datum/component/lifesteal, comp.quality)
return "vampiric [newName]"
+
+/datum/fantasy_affix/beautiful
+ placement = AFFIX_PREFIX
+ alignment = AFFIX_GOOD
+
+/datum/fantasy_affix/beautiful/apply(datum/component/fantasy/comp, newName)
+ var/obj/item/master = comp.parent
+ master.AddElement(/datum/element/beauty, max(comp.quality, 1) * 250)
+ return "[pick("aesthetic", "beautiful", "gorgeous", "pretty")] [newName]"
+
+/datum/fantasy_affix/beautiful/remove(datum/component/fantasy/comp)
+ var/obj/item/master = comp.parent
+ master.RemoveElement(/datum/element/beauty, max(comp.quality, 1) * 250)
+
+/datum/fantasy_affix/ugly
+ placement = AFFIX_PREFIX
+ alignment = AFFIX_EVIL
+
+/datum/fantasy_affix/ugly/apply(datum/component/fantasy/comp, newName)
+ var/obj/item/master = comp.parent
+ master.AddElement(/datum/element/beauty, min(comp.quality, -1) * 250)
+ return "[pick("fugly", "ugly", "grotesque", "hideous")] [newName]"
+
+/datum/fantasy_affix/ugly/remove(datum/component/fantasy/comp)
+ var/obj/item/master = comp.parent
+ master.RemoveElement(/datum/element/beauty, min(comp.quality, -1) * 250)
diff --git a/code/datums/components/mood.dm b/code/datums/components/mood.dm
index 64fc96146527..de334598f141 100644
--- a/code/datums/components/mood.dm
+++ b/code/datums/components/mood.dm
@@ -27,6 +27,7 @@
RegisterSignal(parent, COMSIG_JOB_RECEIVED, PROC_REF(register_job_signals))
var/mob/living/owner = parent
+ owner.become_area_sensitive(MOOD_COMPONENT_TRAIT)
if(owner.hud_used)
modify_hud()
var/datum/hud/hud = owner.hud_used
@@ -35,6 +36,9 @@
/datum/component/mood/Destroy()
STOP_PROCESSING(SSmood, src)
unmodify_hud()
+
+ var/mob/living/owner = parent
+ owner.lose_area_sensitivity(MOOD_COMPONENT_TRAIT)
return ..()
/datum/component/mood/proc/register_job_signals(datum/source, job)
diff --git a/code/datums/components/pellet_cloud.dm b/code/datums/components/pellet_cloud.dm
index ae90dae17c55..e7f5174c9102 100644
--- a/code/datums/components/pellet_cloud.dm
+++ b/code/datums/components/pellet_cloud.dm
@@ -29,7 +29,7 @@
var/list/pellets = list()
/// An associated list with the atom hit as the key and how many pellets they've eaten for the value, for printing aggregate messages
var/list/targets_hit = list()
- /// For grenades, any /mob/living's the grenade is moved onto, see [/datum/component/pellet_cloud/proc/handle_martyrs()]
+ /// LAZY LIST. For grenades, any /mob/living's the grenade is moved onto, see [/datum/component/pellet_cloud/proc/handle_martyrs()]
var/list/bodies
/// For grenades, tracking people who die covering a grenade for achievement purposes, see [/datum/component/pellet_cloud/proc/handle_martyrs()]
var/list/purple_hearts
@@ -65,7 +65,7 @@
purple_hearts = null
pellets = null
targets_hit = null
- bodies = null
+ LAZYNULL(bodies)
return ..()
/datum/component/pellet_cloud/RegisterWithParent()
@@ -288,5 +288,5 @@
/datum/component/pellet_cloud/proc/on_target_qdel(atom/target)
UnregisterSignal(target, COMSIG_PARENT_QDELETING)
targets_hit -= target
- LAZYREMOVE(target, bodies)
+ LAZYREMOVE(bodies, target)
purple_hearts -= target
diff --git a/code/datums/components/weatherannouncer.dm b/code/datums/components/weatherannouncer.dm
index 3821f9a1b559..a5e622d8669e 100644
--- a/code/datums/components/weatherannouncer.dm
+++ b/code/datums/components/weatherannouncer.dm
@@ -110,7 +110,7 @@
/datum/component/weather_announcer/proc/time_till_storm()
var/datum/weather_controller/local_weather_controller = SSmapping.get_map_zone_weather_controller(parent)
- if(!local_weather_controller.next_weather)
+ if(!local_weather_controller?.next_weather)
return null
for(var/type_index in local_weather_controller.current_weathers)
var/datum/weather/check_weather = local_weather_controller.current_weathers[type_index]
diff --git a/code/datums/elements/beauty.dm b/code/datums/elements/beauty.dm
new file mode 100644
index 000000000000..88cd6eb3adea
--- /dev/null
+++ b/code/datums/elements/beauty.dm
@@ -0,0 +1,76 @@
+/**
+ * Beauty element. It makes the indoor area the parent is in prettier or uglier depending on the beauty var value.
+ * Clean and well decorated areas lead to positive moodlets for passerbies;
+ * Shabbier, dirtier ones lead to negative moodlets EXCLUSIVE to characters with the snob quirk.
+ */
+/datum/element/beauty
+ element_flags = ELEMENT_BESPOKE|ELEMENT_DETACH
+ id_arg_index = 2
+ var/beauty = 0
+ /**
+ * Assoc list of atoms as keys and number of time the same element instance has been attached to them as assoc value.
+ * So things don't get odd with same-valued yet dissimilar beauty modifiers being added to the same atom.
+ */
+ var/beauty_counter = list()
+
+/datum/element/beauty/Attach(datum/target, beauty)
+ . = ..()
+ if(!isatom(target) || isarea(target))
+ return ELEMENT_INCOMPATIBLE
+
+ src.beauty = beauty
+
+ if(!beauty_counter[target] && ismovable(target))
+ var/atom/movable/mov_target = target
+ mov_target.become_area_sensitive(BEAUTY_ELEMENT_TRAIT)
+ RegisterSignal(mov_target, COMSIG_ENTER_AREA, PROC_REF(enter_area))
+ RegisterSignal(mov_target, COMSIG_EXIT_AREA, PROC_REF(exit_area))
+
+ beauty_counter[target]++
+
+ var/area/current_area = get_area(target)
+ if(current_area && !current_area.outdoors)
+ current_area.totalbeauty += beauty
+ current_area.update_beauty()
+
+/datum/element/beauty/proc/enter_area(datum/source, area/new_area)
+ SIGNAL_HANDLER
+
+ if(new_area.outdoors)
+ return
+ new_area.totalbeauty += beauty * beauty_counter[source]
+ new_area.update_beauty()
+
+/datum/element/beauty/proc/exit_area(datum/source, area/old_area)
+ SIGNAL_HANDLER
+
+ if(old_area.outdoors)
+ return
+ old_area.totalbeauty -= beauty * beauty_counter[source]
+ old_area.update_beauty()
+
+/datum/element/beauty/Detach(datum/source)
+ if(!beauty_counter[source])
+ return ..()
+ var/area/current_area = get_area(source)
+ if(QDELETED(source))
+ . = ..()
+ UnregisterSignal(source, list(COMSIG_ENTER_AREA, COMSIG_EXIT_AREA))
+ if(current_area)
+ exit_area(source, current_area)
+ beauty_counter -= source
+ var/atom/movable/movable_source = source
+ if(istype(movable_source))
+ movable_source.lose_area_sensitivity(BEAUTY_ELEMENT_TRAIT)
+ else //lower the 'counter' down by one, update the area, and call parent if it's reached zero.
+ beauty_counter[source]--
+ if(current_area && !current_area.outdoors)
+ current_area.totalbeauty -= beauty
+ current_area.update_beauty()
+ if(!beauty_counter[source])
+ . = ..()
+ UnregisterSignal(source, list(COMSIG_ENTER_AREA, COMSIG_EXIT_AREA))
+ beauty_counter -= source
+ var/atom/movable/movable_source = source
+ if(istype(movable_source))
+ movable_source.lose_area_sensitivity(BEAUTY_ELEMENT_TRAIT)
diff --git a/code/datums/materials/_material.dm b/code/datums/materials/_material.dm
index 79d3a5e68a89..4f505cb2a3e7 100644
--- a/code/datums/materials/_material.dm
+++ b/code/datums/materials/_material.dm
@@ -65,7 +65,7 @@ Simple datum which is instanced once per type and is used for every object of sa
source.name = "[name] [source.name]"
if(beauty_modifier)
- addtimer(CALLBACK(source, TYPE_PROC_REF(/datum, _AddComponent), list(/datum/component/beauty, beauty_modifier * amount)), 0)
+ source.AddElement(/datum/element/beauty, beauty_modifier * amount)
if(istype(source, /obj)) //objs
on_applied_obj(source, amount, material_flags)
@@ -121,7 +121,7 @@ Simple datum which is instanced once per type and is used for every object of sa
return
///This proc is called when the material is removed from an object.
-/datum/material/proc/on_removed(atom/source, material_flags)
+/datum/material/proc/on_removed(atom/source, amount, material_flags)
if(material_flags & MATERIAL_COLOR) //Prevent changing things with pre-set colors, to keep colored toolboxes their looks for example
if(color)
source.remove_atom_colour(FIXED_COLOUR_PRIORITY, color)
@@ -133,6 +133,9 @@ Simple datum which is instanced once per type and is used for every object of sa
if(material_flags & MATERIAL_ADD_PREFIX)
source.name = initial(source.name)
+ if(beauty_modifier)
+ source.RemoveElement(/datum/element/beauty, beauty_modifier * amount)
+
if(istype(source, /obj)) //objs
on_removed_obj(source, material_flags)
diff --git a/code/datums/materials/basemats.dm b/code/datums/materials/basemats.dm
index bf540f08459f..98db1541c953 100644
--- a/code/datums/materials/basemats.dm
+++ b/code/datums/materials/basemats.dm
@@ -86,7 +86,7 @@ Unless you know what you're doing, only use the first three numbers. They're in
. = ..()
source.AddComponent(/datum/component/radioactive, amount / 20, source, 0) //half-life of 0 because we keep on going.
-/datum/material/uranium/on_removed(atom/source, material_flags)
+/datum/material/uranium/on_removed(atom/source, amount, material_flags)
. = ..()
qdel(source.GetComponent(/datum/component/radioactive))
@@ -109,7 +109,7 @@ Unless you know what you're doing, only use the first three numbers. They're in
source.AddElement(/datum/element/firestacker, amount=1)
source.AddComponent(/datum/component/explodable, 0, 0, amount / 2500, amount / 1250)
-/datum/material/plasma/on_removed(atom/source, material_flags)
+/datum/material/plasma/on_removed(atom/source, amount, material_flags)
. = ..()
source.RemoveElement(/datum/element/firestacker, amount=1)
qdel(source.GetComponent(/datum/component/explodable))
diff --git a/code/datums/materials/meat.dm b/code/datums/materials/meat.dm
index d8a9fb5cc01c..9539b2847774 100644
--- a/code/datums/materials/meat.dm
+++ b/code/datums/materials/meat.dm
@@ -14,7 +14,7 @@
turf_sound_override = FOOTSTEP_MEAT
texture_layer_icon_state = "meat"
-/datum/material/meat/on_removed(atom/source, material_flags)
+/datum/material/meat/on_removed(atom/source, amount, material_flags)
. = ..()
qdel(source.GetComponent(/datum/component/edible))
diff --git a/code/datums/materials/pizza.dm b/code/datums/materials/pizza.dm
index 6ab79e3a2065..aed6577a5af9 100644
--- a/code/datums/materials/pizza.dm
+++ b/code/datums/materials/pizza.dm
@@ -13,7 +13,7 @@
turf_sound_override = FOOTSTEP_MEAT
texture_layer_icon_state = "pizza"
-/datum/material/pizza/on_removed(atom/source, material_flags)
+/datum/material/pizza/on_removed(atom/source, amount, material_flags)
. = ..()
qdel(source.GetComponent(/datum/component/edible))
diff --git a/code/game/area/areas.dm b/code/game/area/areas.dm
index 35712cb768ae..e8bdb66c1898 100644
--- a/code/game/area/areas.dm
+++ b/code/game/area/areas.dm
@@ -575,18 +575,21 @@ GLOBAL_LIST_EMPTY(teleportlocs)
/**
* Call back when an atom enters an area
*
- * Sends signals COMSIG_AREA_ENTERED and COMSIG_ENTER_AREA (to the atom)
+ * Sends signals COMSIG_AREA_ENTERED and COMSIG_ENTER_AREA (to a list of atoms)
*
* If the area has ambience, then it plays some ambience music to the ambience channel
*/
-/area/Entered(atom/movable/M, area/old_area)
+/area/Entered(atom/movable/arrived, area/old_area)
set waitfor = FALSE
- SEND_SIGNAL(src, COMSIG_AREA_ENTERED, M, old_area)
- SEND_SIGNAL(M, COMSIG_ENTER_AREA, src) //The atom that enters the area
- if(!isliving(M))
+ SEND_SIGNAL(src, COMSIG_AREA_ENTERED, arrived, old_area)
+ if(!LAZYACCESS(arrived.important_recursive_contents, RECURSIVE_CONTENTS_AREA_SENSITIVE))
+ return
+ for(var/atom/movable/recipient as anything in arrived.important_recursive_contents[RECURSIVE_CONTENTS_AREA_SENSITIVE])
+ SEND_SIGNAL(recipient, COMSIG_ENTER_AREA, src)
+ if(!isliving(arrived))
return
- var/mob/living/L = M
+ var/mob/living/L = arrived
if(!L.ckey)
return
@@ -608,11 +611,14 @@ GLOBAL_LIST_EMPTY(teleportlocs)
/**
* Called when an atom exits an area
*
- * Sends signals COMSIG_AREA_EXITED and COMSIG_EXIT_AREA (to the atom)
+ * Sends signals COMSIG_AREA_EXITED and COMSIG_EXIT_AREA (to a list of atoms)
*/
/area/Exited(atom/movable/gone, direction)
SEND_SIGNAL(src, COMSIG_AREA_EXITED, gone, direction)
- SEND_SIGNAL(gone, COMSIG_EXIT_AREA, src) //The atom that exits the area
+ if(!LAZYACCESS(gone.important_recursive_contents, RECURSIVE_CONTENTS_AREA_SENSITIVE))
+ return
+ for(var/atom/movable/recipient as anything in gone.important_recursive_contents[RECURSIVE_CONTENTS_AREA_SENSITIVE])
+ SEND_SIGNAL(recipient, COMSIG_EXIT_AREA, src)
/**
diff --git a/code/game/atoms.dm b/code/game/atoms.dm
index 350b80907f70..6c6849724bde 100644
--- a/code/game/atoms.dm
+++ b/code/game/atoms.dm
@@ -1525,7 +1525,7 @@
if(custom_materials) //Only runs if custom materials existed at first. Should usually be the case but check anyways
for(var/i in custom_materials)
var/datum/material/custom_material = SSmaterials.GetMaterialRef(i)
- custom_material.on_removed(src, material_flags) //Remove the current materials
+ custom_material.on_removed(src, custom_materials[i] * material_modifier, material_flags) //Remove the current materials
if(!length(materials))
return
diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm
index 54ac77bb0a8c..96ba335e378c 100644
--- a/code/game/atoms_movable.dm
+++ b/code/game/atoms_movable.dm
@@ -630,6 +630,71 @@
return
A.Bumped(src)
+/atom/movable/Exited(atom/movable/gone, direction)
+ . = ..()
+
+ if(!LAZYLEN(gone.important_recursive_contents))
+ return
+
+ var/list/nested_locs = get_nested_locs(src) + src
+ for(var/channel in gone.important_recursive_contents)
+ for(var/atom/movable/location as anything in nested_locs)
+ var/list/recursive_contents = location.important_recursive_contents // blue hedgehog velocity
+ recursive_contents[channel] -= gone.important_recursive_contents[channel]
+ ASSOC_UNSETEMPTY(recursive_contents, channel)
+ UNSETEMPTY(location.important_recursive_contents)
+
+/atom/movable/Entered(atom/movable/arrived, atom/old_loc, list/atom/old_locs)
+ . = ..()
+
+ if(!LAZYLEN(arrived.important_recursive_contents))
+ return
+
+ var/list/nested_locs = get_nested_locs(src) + src
+ for(var/channel in arrived.important_recursive_contents)
+ for(var/atom/movable/location as anything in nested_locs)
+ LAZYINITLIST(location.important_recursive_contents)
+ var/list/recursive_contents = location.important_recursive_contents // blue hedgehog velocity
+ LAZYINITLIST(recursive_contents[channel])
+ recursive_contents[channel] |= arrived.important_recursive_contents[channel]
+
+/// See traits.dm. Use this in place of ADD_TRAIT.
+/atom/movable/proc/become_area_sensitive(trait_source = TRAIT_GENERIC)
+ if(!HAS_TRAIT(src, TRAIT_AREA_SENSITIVE))
+ for(var/atom/movable/location as anything in get_nested_locs(src) + src)
+ LAZYADDASSOCLIST(location.important_recursive_contents, RECURSIVE_CONTENTS_AREA_SENSITIVE, src)
+ ADD_TRAIT(src, TRAIT_AREA_SENSITIVE, trait_source)
+
+/atom/movable/proc/lose_area_sensitivity(trait_source = TRAIT_GENERIC)
+ if(!HAS_TRAIT(src, TRAIT_AREA_SENSITIVE))
+ return
+ REMOVE_TRAIT(src, TRAIT_AREA_SENSITIVE, trait_source)
+ if(HAS_TRAIT(src, TRAIT_AREA_SENSITIVE))
+ return
+
+///allows this movable to hear and adds itself to the important_recursive_contents list of itself and every movable loc its in
+/atom/movable/proc/become_hearing_sensitive(trait_source = TRAIT_GENERIC)
+ ADD_TRAIT(src, TRAIT_HEARING_SENSITIVE, trait_source)
+ if(!HAS_TRAIT(src, TRAIT_HEARING_SENSITIVE))
+ return
+
+ for(var/atom/movable/location as anything in get_nested_locs(src) + src)
+ LAZYINITLIST(location.important_recursive_contents)
+ var/list/recursive_contents = location.important_recursive_contents // blue hedgehog velocity
+ recursive_contents[RECURSIVE_CONTENTS_HEARING_SENSITIVE] += list(src)
+
+/atom/movable/proc/lose_hearing_sensitivity(trait_source = TRAIT_GENERIC)
+ if(!HAS_TRAIT(src, TRAIT_HEARING_SENSITIVE))
+ return
+ REMOVE_TRAIT(src, TRAIT_HEARING_SENSITIVE, trait_source)
+ if(HAS_TRAIT(src, TRAIT_HEARING_SENSITIVE))
+ return
+ for(var/atom/movable/location as anything in get_nested_locs(src) + src)
+ var/list/recursive_contents = location.important_recursive_contents // blue hedgehog velocity
+ recursive_contents[RECURSIVE_CONTENTS_HEARING_SENSITIVE] -= src
+ ASSOC_UNSETEMPTY(recursive_contents, RECURSIVE_CONTENTS_HEARING_SENSITIVE)
+ UNSETEMPTY(location.important_recursive_contents)
+
///Sets the anchored var and returns if it was sucessfully changed or not.
/atom/movable/proc/set_anchored(anchorvalue)
SHOULD_CALL_PARENT(TRUE)
@@ -1181,54 +1246,3 @@
animate(pickup_animation, alpha = 175, pixel_x = to_x, pixel_y = to_y, time = 3, transform = M, easing = CUBIC_EASING)
sleep(1)
animate(pickup_animation, alpha = 0, transform = matrix(), time = 1)
-
-/atom/movable/Exited(atom/movable/gone, direction)
- . = ..()
-
- if(!LAZYLEN(gone.important_recursive_contents))
- return
-
- var/list/nested_locs = get_nested_locs(src) + src
- for(var/channel in gone.important_recursive_contents)
- for(var/atom/movable/location as anything in nested_locs)
- var/list/recursive_contents = location.important_recursive_contents // blue hedgehog velocity
- recursive_contents[channel] -= gone.important_recursive_contents[channel]
- ASSOC_UNSETEMPTY(recursive_contents, channel)
- UNSETEMPTY(location.important_recursive_contents)
-
-/atom/movable/Entered(atom/movable/arrived, atom/old_loc, list/atom/old_locs)
- . = ..()
-
- if(!LAZYLEN(arrived.important_recursive_contents))
- return
-
- var/list/nested_locs = get_nested_locs(src) + src
- for(var/channel in arrived.important_recursive_contents)
- for(var/atom/movable/location as anything in nested_locs)
- LAZYINITLIST(location.important_recursive_contents)
- var/list/recursive_contents = location.important_recursive_contents // blue hedgehog velocity
- LAZYINITLIST(recursive_contents[channel])
- recursive_contents[channel] |= arrived.important_recursive_contents[channel]
-
-///allows this movable to hear and adds itself to the important_recursive_contents list of itself and every movable loc its in
-/atom/movable/proc/become_hearing_sensitive(trait_source = TRAIT_GENERIC)
- ADD_TRAIT(src, TRAIT_HEARING_SENSITIVE, trait_source)
- if(!HAS_TRAIT(src, TRAIT_HEARING_SENSITIVE))
- return
-
- for(var/atom/movable/location as anything in get_nested_locs(src) + src)
- LAZYINITLIST(location.important_recursive_contents)
- var/list/recursive_contents = location.important_recursive_contents // blue hedgehog velocity
- recursive_contents[RECURSIVE_CONTENTS_HEARING_SENSITIVE] += list(src)
-
-/atom/movable/proc/lose_hearing_sensitivity(trait_source = TRAIT_GENERIC)
- if(!HAS_TRAIT(src, TRAIT_HEARING_SENSITIVE))
- return
- REMOVE_TRAIT(src, TRAIT_HEARING_SENSITIVE, trait_source)
- if(HAS_TRAIT(src, TRAIT_HEARING_SENSITIVE))
- return
- for(var/atom/movable/location as anything in get_nested_locs(src) + src)
- var/list/recursive_contents = location.important_recursive_contents // blue hedgehog velocity
- recursive_contents[RECURSIVE_CONTENTS_HEARING_SENSITIVE] -= src
- ASSOC_UNSETEMPTY(recursive_contents, RECURSIVE_CONTENTS_HEARING_SENSITIVE)
- UNSETEMPTY(location.important_recursive_contents)
diff --git a/code/game/machinery/_machinery.dm b/code/game/machinery/_machinery.dm
index c81a58ad73b9..a17b9c705dc1 100644
--- a/code/game/machinery/_machinery.dm
+++ b/code/game/machinery/_machinery.dm
@@ -167,12 +167,14 @@ Class Procs:
/obj/machinery/LateInitialize()
. = ..()
power_change()
+ become_area_sensitive(ROUNDSTART_TRAIT)
RegisterSignal(src, COMSIG_ENTER_AREA, PROC_REF(power_change))
/obj/machinery/Destroy()
GLOB.machines.Remove(src)
end_processing()
dropContents()
+ lose_area_sensitivity(ROUNDSTART_TRAIT)
QDEL_NULL(circuit)
QDEL_LIST(component_parts)
return ..()
diff --git a/code/game/objects/effects/contraband.dm b/code/game/objects/effects/contraband.dm
index a6d2fff1571f..a86a74c2c3d4 100644
--- a/code/game/objects/effects/contraband.dm
+++ b/code/game/objects/effects/contraband.dm
@@ -97,7 +97,7 @@
name = "poster - [name]"
desc = "A large piece of space-resistant printed paper. [desc]"
- addtimer(CALLBACK(src, TYPE_PROC_REF(/datum, _AddComponent), list(/datum/component/beauty, 300)), 0)
+ AddElement(/datum/element/beauty, 300)
/obj/structure/sign/poster/proc/randomise()
var/obj/structure/sign/poster/selected
diff --git a/code/game/objects/effects/decals/cleanable.dm b/code/game/objects/effects/decals/cleanable.dm
index a0909bb0b994..4161403fefd9 100644
--- a/code/game/objects/effects/decals/cleanable.dm
+++ b/code/game/objects/effects/decals/cleanable.dm
@@ -30,8 +30,7 @@
COMSIG_ATOM_ENTERED = PROC_REF(on_entered),
)
AddElement(/datum/element/connect_loc, loc_connections)
-
- addtimer(CALLBACK(src, TYPE_PROC_REF(/datum, _AddComponent), list(/datum/component/beauty, beauty)), 0)
+ AddElement(/datum/element/beauty, beauty)
SSblackbox.record_feedback("tally", "station_mess_created", 1, name)
diff --git a/code/game/objects/items/stacks/sheets/glass.dm b/code/game/objects/items/stacks/sheets/glass.dm
index 1296e889181d..da08c87a5a35 100644
--- a/code/game/objects/items/stacks/sheets/glass.dm
+++ b/code/game/objects/items/stacks/sheets/glass.dm
@@ -66,13 +66,13 @@ GLOBAL_LIST_INIT(glass_recipes, list ( \
else if(istype(W, /obj/item/stack/rods))
var/obj/item/stack/rods/V = W
if (V.get_amount() >= 1 && get_amount() >= 1)
- var/obj/item/stack/sheet/rglass/RG = new (get_turf(user))
- RG.add_fingerprint(user)
+ var/obj/item/stack/sheet/rglass/reinforced = new(get_turf(user)) || locate(/obj/item/stack/sheet/rglass) in get_turf(user) // Get the stack it's merged into if it is
+ reinforced.add_fingerprint(user)
var/replace = user.get_inactive_held_item()==src
V.use(1)
use(1)
if(QDELETED(src) && replace)
- user.put_in_hands(RG)
+ user.put_in_hands(reinforced)
else
to_chat(user, "You need one rod and one sheet of glass to make reinforced glass!")
return
@@ -119,13 +119,13 @@ GLOBAL_LIST_INIT(pglass_recipes, list ( \
if(istype(W, /obj/item/stack/rods))
var/obj/item/stack/rods/V = W
if (V.get_amount() >= 1 && get_amount() >= 1)
- var/obj/item/stack/sheet/plasmarglass/RG = new (get_turf(user))
- RG.add_fingerprint(user)
- var/replace = user.get_inactive_held_item()==src
+ var/obj/item/stack/sheet/plasmarglass/reinforced = new(get_turf(user)) || locate(/obj/item/stack/sheet/plasmarglass) in get_turf(user) // Get the stack it's merged into if it is
+ reinforced.add_fingerprint(user)
+ var/replace = user.get_inactive_held_item() == src
V.use(1)
use(1)
if(QDELETED(src) && replace)
- user.put_in_hands(RG)
+ user.put_in_hands(reinforced)
else
to_chat(user, "You need one rod and one sheet of plasma glass to make reinforced plasma glass!")
return
diff --git a/code/game/objects/items/weaponry.dm b/code/game/objects/items/weaponry.dm
index c019471e3cdd..9e8d1e3c17b9 100644
--- a/code/game/objects/items/weaponry.dm
+++ b/code/game/objects/items/weaponry.dm
@@ -515,7 +515,7 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
/obj/item/statuebust/Initialize()
. = ..()
AddComponent(/datum/component/art, impressiveness)
- addtimer(CALLBACK(src, TYPE_PROC_REF(/datum, _AddComponent), list(/datum/component/beauty, 1000)), 0)
+ AddElement(/datum/element/beauty, 1000)
/obj/item/statuebust/hippocratic
name = "hippocrates bust"
diff --git a/code/game/objects/objs.dm b/code/game/objects/objs.dm
index 46090aa86658..f295bfab8950 100644
--- a/code/game/objects/objs.dm
+++ b/code/game/objects/objs.dm
@@ -205,6 +205,7 @@
ui_interact(user)
/mob/proc/unset_machine()
+ SIGNAL_HANDLER
if(!machine)
return
UnregisterSignal(machine, COMSIG_PARENT_QDELETING)
@@ -216,6 +217,8 @@
return
/mob/proc/set_machine(obj/O)
+ if(QDELETED(src) || QDELETED(O))
+ return
if(machine)
unset_machine()
machine = O
diff --git a/code/game/objects/structures/flora.dm b/code/game/objects/structures/flora.dm
index 50b578a3aee2..8ba938dae8ee 100644
--- a/code/game/objects/structures/flora.dm
+++ b/code/game/objects/structures/flora.dm
@@ -368,8 +368,8 @@
/obj/item/kirbyplants/ComponentInitialize()
. = ..()
AddComponent(/datum/component/tactical)
- addtimer(CALLBACK(src, TYPE_PROC_REF(/datum, _AddComponent), list(/datum/component/beauty, 500)), 0)
AddComponent(/datum/component/two_handed, require_twohands=TRUE, force_unwielded=10, force_wielded=10)
+ AddElement(/datum/element/beauty, 500)
/obj/item/kirbyplants/random
icon = 'icons/obj/flora/_flora.dmi'
diff --git a/code/game/objects/structures/girders.dm b/code/game/objects/structures/girders.dm
index 7a8aec8a97f2..25822d69ff00 100644
--- a/code/game/objects/structures/girders.dm
+++ b/code/game/objects/structures/girders.dm
@@ -44,12 +44,16 @@
M.add_fingerprint(user)
qdel(src)
+ return
+
else if(istype(W, /obj/item/pickaxe/drill/jackhammer))
to_chat(user, "You smash through the girder!")
new /obj/item/stack/sheet/metal(get_turf(src))
W.play_tool_sound(src)
qdel(src)
+ return
+
else if(istype(W, /obj/item/stack))
if(iswallturf(loc))
@@ -77,6 +81,8 @@
var/obj/structure/falsewall/iron/FW = new (loc)
transfer_fingerprints_to(FW)
qdel(src)
+
+ return
else
if(S.get_amount() < 5)
to_chat(user, "You need at least five rods to add plating!")
@@ -111,6 +117,8 @@
var/obj/structure/falsewall/F = new (loc)
transfer_fingerprints_to(F)
qdel(src)
+
+ return
else
if(S.get_amount() < 2)
to_chat(user, "You need two sheets of metal to finish a wall!")
@@ -141,6 +149,8 @@
var/obj/structure/falsewall/reinforced/FW = new (loc)
transfer_fingerprints_to(FW)
qdel(src)
+
+ return
else
if(state == GIRDER_REINF)
if(S.get_amount() < 1)
@@ -185,6 +195,8 @@
var/obj/structure/FW = new F (loc)
transfer_fingerprints_to(FW)
qdel(src)
+
+ return
else
if(S.get_amount() < 2)
to_chat(user, "You need at least two sheets to add plating!")
@@ -210,8 +222,6 @@
qdel(src)
return
- add_hiddenprint(user)
-
else if(istype(W, /obj/item/pipe))
var/obj/item/pipe/P = W
if (P.pipe_type in list(0, 1, 5)) //simple pipes, simple bends, and simple manifolds.
diff --git a/code/game/objects/structures/grille.dm b/code/game/objects/structures/grille.dm
index 56f50eb1768e..5bca53e84dd6 100644
--- a/code/game/objects/structures/grille.dm
+++ b/code/game/objects/structures/grille.dm
@@ -221,8 +221,8 @@
/obj/structure/grille/deconstruct(disassembled = TRUE)
if(!loc) //if already qdel'd somehow, we do nothing
return
- if(!(flags_1&NODECONSTRUCT_1))
- var/obj/R = new rods_type(drop_location(), rods_amount)
+ if(!(flags_1 & NODECONSTRUCT_1))
+ var/obj/R = new rods_type(drop_location(), rods_amount) || locate(rods_type) in drop_location() // if the rods get merged, find the stack
transfer_fingerprints_to(R)
qdel(src)
..()
@@ -230,7 +230,7 @@
/obj/structure/grille/obj_break()
if(!broken && !(flags_1 & NODECONSTRUCT_1))
new broken_type(src.loc)
- var/obj/R = new rods_type(drop_location(), rods_broken)
+ var/obj/R = new rods_type(drop_location(), rods_broken) || locate(rods_type) in drop_location() // see above
transfer_fingerprints_to(R)
qdel(src)
diff --git a/code/game/objects/structures/headpike.dm b/code/game/objects/structures/headpike.dm
index aff870c6eedf..c42983a5e0e5 100644
--- a/code/game/objects/structures/headpike.dm
+++ b/code/game/objects/structures/headpike.dm
@@ -13,24 +13,53 @@
icon_state = "headpike-bone"
bonespear = TRUE
+/obj/structure/headpike/Initialize(mapload)
+ . = ..()
+ if(mapload)
+ CheckParts()
+
/obj/structure/headpike/CheckParts(list/parts_list)
- ..()
victim = locate(/obj/item/bodypart/head) in parts_list
- update_appearance()
- if(bonespear)
- spear = locate(/obj/item/spear/bonespear) in parts_list
- else
- spear = locate(/obj/item/spear) in parts_list
+ if(!victim) //likely a mapspawned one
+ victim = new(src)
+ victim.real_name = random_unique_name(prob(50))
-/obj/structure/headpike/Initialize()
- . = ..()
- pixel_x = rand(-8, 8)
+ spear = locate(bonespear ? /obj/item/spear/bonespear : /obj/item/spear) in parts_list
+ if(!spear)
+ spear = bonespear ? new/obj/item/spear/bonespear(src) : new/obj/item/spear(src)
+
+ update_appearance()
+ return ..()
/obj/structure/headpike/Destroy()
QDEL_NULL(victim)
QDEL_NULL(spear)
return ..()
+/obj/structure/headpike/handle_atom_del(atom/A)
+ if(A == victim)
+ victim = null
+ if(A == spear)
+ spear = null
+ if(!QDELETED(src))
+ deconstruct(TRUE)
+ return ..()
+
+/obj/structure/headpike/deconstruct(disassembled)
+ if(!disassembled)
+ return ..()
+ if(victim)
+ victim.forceMove(drop_location())
+ victim = null
+ if(spear)
+ spear.forceMove(drop_location())
+ spear = null
+ return ..()
+
+/obj/structure/headpike/Initialize()
+ . = ..()
+ pixel_x = rand(-8, 8)
+
/obj/structure/headpike/update_overlays()
. = ..()
var/obj/item/bodypart/head/H = locate() in contents
@@ -45,12 +74,7 @@
if(.)
return
to_chat(user, "You take down [src].")
- if(victim)
- victim.forceMove(drop_location())
- victim = null
- spear.forceMove(drop_location())
- spear = null
- qdel(src)
+ deconstruct(TRUE)
/obj/structure/headpike/update_name()
name = "[victim.real_name] on a [spear]"
diff --git a/code/game/objects/structures/statues.dm b/code/game/objects/structures/statues.dm
index a4155003dcdb..642c2c2efdd4 100644
--- a/code/game/objects/structures/statues.dm
+++ b/code/game/objects/structures/statues.dm
@@ -15,7 +15,7 @@
/obj/structure/statue/Initialize()
. = ..()
AddComponent(art_type, impressiveness)
- addtimer(CALLBACK(src, TYPE_PROC_REF(/datum, _AddComponent), list(/datum/component/beauty, impressiveness * 75)), 0)
+ AddElement(/datum/element/beauty, impressiveness * 75)
/obj/structure/statue/attackby(obj/item/W, mob/living/user, params)
add_fingerprint(user)
diff --git a/code/game/turfs/change_turf.dm b/code/game/turfs/change_turf.dm
index 07976f05631f..13e8ae7877c7 100644
--- a/code/game/turfs/change_turf.dm
+++ b/code/game/turfs/change_turf.dm
@@ -39,10 +39,6 @@ GLOBAL_LIST_INIT(blacklisted_automated_baseturfs, typecacheof(list(
/turf/open/copyTurf(turf/T, copy_air = FALSE)
. = ..()
if(isopenturf(T))
- var/datum/component/wet_floor/slip = GetComponent(/datum/component/wet_floor)
- if(slip)
- var/datum/component/wet_floor/WF = T.AddComponent(/datum/component/wet_floor)
- WF.InheritComponent(slip)
if(copy_air)
var/turf/open/openTurf = T
openTurf.air.copy_from(air)
diff --git a/code/game/turfs/open/floor/reinf_floor.dm b/code/game/turfs/open/floor/reinf_floor.dm
index 68a96846adbb..a164b159d6f6 100644
--- a/code/game/turfs/open/floor/reinf_floor.dm
+++ b/code/game/turfs/open/floor/reinf_floor.dm
@@ -40,6 +40,9 @@
/turf/open/floor/engine/crowbar_act(mob/living/user, obj/item/I)
return
+/turf/open/floor/engine/handle_decompression_floor_rip(sum)
+ return
+
/turf/open/floor/engine/wrench_act(mob/living/user, obj/item/I)
..()
to_chat(user, "You begin removing the sheet...")
diff --git a/code/modules/atmospherics/environmental/LINDA_turf_tile.dm b/code/modules/atmospherics/environmental/LINDA_turf_tile.dm
index f28a9a898588..715139dfd6ba 100644
--- a/code/modules/atmospherics/environmental/LINDA_turf_tile.dm
+++ b/code/modules/atmospherics/environmental/LINDA_turf_tile.dm
@@ -197,6 +197,7 @@
FD.emergency_pressure_stop()
/turf/proc/handle_decompression_floor_rip()
+
/turf/open/floor/handle_decompression_floor_rip(sum)
if(sum > 20 && prob(clamp(sum / 10, 0, 30)) && !blocks_air)
remove_tile()
diff --git a/code/modules/awaymissions/mission_code/snowdin.dm b/code/modules/awaymissions/mission_code/snowdin.dm
index f5cfc93c6eee..dec456b8ef2f 100644
--- a/code/modules/awaymissions/mission_code/snowdin.dm
+++ b/code/modules/awaymissions/mission_code/snowdin.dm
@@ -225,7 +225,8 @@
if(plasma_parts.len)
var/obj/item/bodypart/NB = pick(plasma_parts) //using the above-mentioned list to get a choice of limbs for dismember() to use
PP.emote("scream")
- NB.limb_id = "plasmaman"//change the species_id of the limb to that of a plasmaman
+ NB.limb_id = "plasmaman" //change the species_id of the limb to that of a plasmaman
+ NB.static_icon = 'icons/mob/species/plasmaman/bodyparts.dmi'
NB.no_update = TRUE
NB.change_bodypart_status()
PP.visible_message(
diff --git a/code/modules/cargo/centcom_podlauncher.dm b/code/modules/cargo/centcom_podlauncher.dm
index c0c316a1354a..61e416e9d4f1 100644
--- a/code/modules/cargo/centcom_podlauncher.dm
+++ b/code/modules/cargo/centcom_podlauncher.dm
@@ -684,8 +684,8 @@
return
var/obj/structure/closet/supplypod/centcompod/toLaunch = DuplicateObject(temp_pod) //Duplicate the temp_pod (which we have been varediting or configuring with the UI) and store the result
toLaunch.update_appearance()//we update_appearance() here so that the door doesnt "flicker on" right after it lands
- var/shippingLane = GLOB.areas_by_type[/area/centcom/supplypod/supplypod_temp_holding]
- toLaunch.forceMove(shippingLane)
+ var/area/shipping_lane = GLOB.areas_by_type[/area/centcom/supplypod/supplypod_temp_holding]
+ toLaunch.forceMove(pick(shipping_lane.contents))
if (launchClone) //We arent launching the actual items from the bay, rather we are creating clones and launching those
if(launchRandomItem)
var/launch_candidate = pick_n_take(launchList)
diff --git a/code/modules/cargo/supplypod.dm b/code/modules/cargo/supplypod.dm
index 16b43704df58..3ff822a4f943 100644
--- a/code/modules/cargo/supplypod.dm
+++ b/code/modules/cargo/supplypod.dm
@@ -81,8 +81,8 @@
/obj/structure/closet/supplypod/Initialize(mapload, customStyle = FALSE)
. = ..()
if (!loc)
- var/shippingLane = GLOB.areas_by_type[/area/centcom/supplypod/supplypod_temp_holding] //temporary holder for supplypods mid-transit
- forceMove(shippingLane)
+ var/area/shipping_lane = GLOB.areas_by_type[/area/centcom/supplypod/supplypod_temp_holding] //temporary holder for supplypods mid-transit
+ forceMove(pick(shipping_lane.contents))
if (customStyle)
style = customStyle
setStyle(style) //Upon initialization, give the supplypod an iconstate, name, and description based on the "style" variable. This system is important for the centcom_podlauncher to function correctly
@@ -200,8 +200,8 @@
stay_after_drop = FALSE
holder.pixel_z = initial(holder.pixel_z)
holder.alpha = initial(holder.alpha)
- var/shippingLane = GLOB.areas_by_type[/area/centcom/supplypod/supplypod_temp_holding]
- forceMove(shippingLane) //Move to the centcom-z-level until the pod_landingzone says we can drop back down again
+ var/area/shipping_lane = GLOB.areas_by_type[/area/centcom/supplypod/supplypod_temp_holding]
+ forceMove(pick(shipping_lane.contents)) //Move to the centcom-z-level until the pod_landingzone says we can drop back down again
if (!reverse_dropoff_coords) //If we're centcom-launched, the reverse dropoff turf will be a centcom loading bay. If we're an extraction pod, it should be the ninja jail. Thus, this shouldn't ever really happen.
var/obj/error_landmark = locate(/obj/effect/landmark/error) in GLOB.landmarks_list
var/turf/error_landmark_turf = get_turf(error_landmark)
diff --git a/code/modules/events/spacevine.dm b/code/modules/events/spacevine.dm
index ede24c643c43..b1c01d16c636 100644
--- a/code/modules/events/spacevine.dm
+++ b/code/modules/events/spacevine.dm
@@ -368,13 +368,16 @@
/obj/structure/spacevine/attack_hand(mob/user)
for(var/datum/spacevine_mutation/SM in mutations)
SM.on_hit(src, user)
- user_unbuckle_mob(user, user)
- . = ..()
+ if(user.buckled == src)
+ user_unbuckle_mob(user, user)
+ return ..()
/obj/structure/spacevine/attack_paw(mob/living/user)
for(var/datum/spacevine_mutation/SM in mutations)
SM.on_hit(src, user)
- user_unbuckle_mob(user,user)
+ if(user.buckled == src)
+ user_unbuckle_mob(user, user)
+ return ..()
/obj/structure/spacevine/attack_alien(mob/living/user)
eat(user)
diff --git a/code/modules/mining/lavaland/necropolis_chests.dm b/code/modules/mining/lavaland/necropolis_chests.dm
index fe446513c521..96954cd59b4d 100644
--- a/code/modules/mining/lavaland/necropolis_chests.dm
+++ b/code/modules/mining/lavaland/necropolis_chests.dm
@@ -861,7 +861,7 @@
/obj/item/freeze_cube/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
icon_state = initial(icon_state)
var/caught = hit_atom.hitby(src, FALSE, FALSE, throwingdatum=throwingdatum)
- var/mob/thrown_by = thrownby
+ var/mob/thrown_by = thrownby.resolve()
if(ismovable(hit_atom) && !caught && (!thrown_by || thrown_by && COOLDOWN_FINISHED(src, freeze_cooldown)))
freeze(hit_atom)
if(thrown_by && !caught)
diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm
index 5d8c44bbc188..6fffc48e76a1 100644
--- a/code/modules/mob/dead/observer/observer.dm
+++ b/code/modules/mob/dead/observer/observer.dm
@@ -435,7 +435,12 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
if(!thearea)
return
- usr.abstract_move(pick(get_area_turfs(thearea)))
+ var/list/area_turfs = get_area_turfs(thearea)
+
+ if(!length(area_turfs))
+ return
+
+ usr.abstract_move(pick(area_turfs))
update_parallax_contents()
/mob/dead/observer/verb/follow()
diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm
index cd80b13bcda6..93276a6710a6 100644
--- a/code/modules/mob/living/carbon/human/human.dm
+++ b/code/modules/mob/living/carbon/human/human.dm
@@ -781,6 +781,16 @@
* Called when this human should be washed
*/
/mob/living/carbon/human/wash(clean_types)
+ // Check and wash stuff that can be covered
+ var/list/obscured = check_obscured_slots()
+
+ // Wash hands if exposed
+ // This runs before the parent call since blood_in_hands should be cleared before the blood DNA is removed
+ if(!gloves && (clean_types & CLEAN_TYPE_BLOOD) && blood_in_hands > 0 && !(ITEM_SLOT_GLOVES in obscured))
+ blood_in_hands = 0
+ update_inv_gloves()
+ . = TRUE
+
. = ..()
// Wash equipped stuff that cannot be covered
@@ -792,9 +802,6 @@
update_inv_belt()
. = TRUE
- // Check and wash stuff that can be covered
- var/list/obscured = check_obscured_slots()
-
if(w_uniform && !(ITEM_SLOT_ICLOTHING in obscured) && w_uniform.wash(clean_types))
update_inv_w_uniform()
. = TRUE
@@ -802,12 +809,6 @@
if(!is_mouth_covered() && clean_lips())
. = TRUE
- // Wash hands if exposed
- if(!gloves && (clean_types & CLEAN_TYPE_BLOOD) && blood_in_hands > 0 && !(ITEM_SLOT_GLOVES in obscured))
- blood_in_hands = 0
- update_inv_gloves()
- . = TRUE
-
//Turns a mob black, flashes a skeleton overlay
//Just like a cartoon!
/mob/living/carbon/human/proc/electrocution_animation(anim_duration)
diff --git a/code/modules/mob/living/carbon/human/species_types/vox.dm b/code/modules/mob/living/carbon/human/species_types/vox.dm
index 1f3a00e8641b..fd03e184b9ba 100644
--- a/code/modules/mob/living/carbon/human/species_types/vox.dm
+++ b/code/modules/mob/living/carbon/human/species_types/vox.dm
@@ -104,16 +104,15 @@
return ..()
/datum/species/vox/get_item_offsets_for_dir(dir, hand)
- ////LEFT/RIGHT
- switch(dir)
- if(SOUTH)
- return list(list("x" = 10, "y" = -1), list("x" = 8, "y" = -1))
- if(NORTH)
- return list(list("x" = 9, "y" = 0), list("x" = 9, "y" = 0))
- if(EAST)
- return list(list("x" = 18, "y" = 2), list("x" = 21, "y" = -1))
- if(WEST)
- return list(list("x" = -5, "y" = -1), list("x" = -1, "y" = 2))
+ //LEFT/RIGHT
+ if(dir & NORTH)
+ return list(list("x" = 9, "y" = 0), list("x" = 9, "y" = 0))
+ if(dir & SOUTH)
+ return list(list("x" = 10, "y" = -1), list("x" = 8, "y" = -1))
+ if(dir & EAST)
+ return list(list("x" = 18, "y" = 2), list("x" = 21, "y" = -1))
+ if(dir & WEST)
+ return list(list("x" = -5, "y" = -1), list("x" = -1, "y" = 2))
/datum/action/innate/tail_hold
name = "Tail Hold"
@@ -174,11 +173,14 @@
owner.cut_overlay(held_item_overlay)
held_item_overlay = null
return
+
if(olddir == newdir && !force)
return
newdir ||= owner.dir
+ newdir = normalize_dir_to_cardinals(newdir)
+
owner.cut_overlay(held_item_overlay)
var/dirtext = dir2text(newdir)
var/icon_file = held_item.lefthand_file
diff --git a/code/modules/mob/living/carbon/human/update_icons.dm b/code/modules/mob/living/carbon/human/update_icons.dm
index a0e409c2053d..09445cef4c4e 100644
--- a/code/modules/mob/living/carbon/human/update_icons.dm
+++ b/code/modules/mob/living/carbon/human/update_icons.dm
@@ -191,7 +191,9 @@ There are several things that need to be remembered:
bloody_overlay.icon_state = "bloodyhands_left"
else if(has_right_hand(FALSE))
bloody_overlay.icon_state = "bloodyhands_right"
- bloody_overlay.color = get_blood_dna_color(return_blood_DNA())
+ var/list/blood_dna = return_blood_DNA()
+ if(length(blood_dna))
+ bloody_overlay.color = get_blood_dna_color(return_blood_DNA())
overlays_standing[GLOVES_LAYER] = bloody_overlay
//Bloody hands end
diff --git a/code/modules/research/rdconsole.dm b/code/modules/research/rdconsole.dm
index 2115a3c6aba4..8b6acd39ae34 100644
--- a/code/modules/research/rdconsole.dm
+++ b/code/modules/research/rdconsole.dm
@@ -1023,13 +1023,17 @@ Nothing else in the console has ID requirements.
linked_imprinter.linked_console = null
linked_imprinter = null
if(ls["eject_design"]) //Eject the design disk.
+ if(QDELETED(d_disk))
+ say("No Design Disk Inserted!")
+ return
eject_disk("design",usr)
screen = RDSCREEN_MENU
- say("Ejecting [d_disk.name]")
if(ls["eject_tech"]) //Eject the technology disk.
+ if(QDELETED(t_disk))
+ say("No Technology Disk Inserted!")
+ return
eject_disk("tech", usr)
screen = RDSCREEN_MENU
- say("Ejecting [t_disk.name]")
if(ls["deconstruct"])
if(QDELETED(linked_destroy))
say("No Destructive Analyzer Linked!")
diff --git a/code/modules/screen_alerts/_screen_alerts.dm b/code/modules/screen_alerts/_screen_alerts.dm
index 08a21635ea93..e9d93a59735b 100644
--- a/code/modules/screen_alerts/_screen_alerts.dm
+++ b/code/modules/screen_alerts/_screen_alerts.dm
@@ -79,6 +79,8 @@
continue
maptext = "[style_open][copytext_char(text_to_play, 1, letter)][style_close]"
sleep(play_delay)
+ if(QDELETED(user))
+ return
addtimer(CALLBACK(src, PROC_REF(after_play), user), fade_out_delay)
///handles post-play effects like fade out after the fade out delay
diff --git a/code/modules/shuttle/special.dm b/code/modules/shuttle/special.dm
index fd18f0c5a1ee..c00a62546b3f 100644
--- a/code/modules/shuttle/special.dm
+++ b/code/modules/shuttle/special.dm
@@ -145,9 +145,14 @@
/mob/living/simple_animal/drone/snowflake/bardrone/Initialize()
. = ..()
access_card.access |= ACCESS_CENT_BAR
+ become_area_sensitive(ROUNDSTART_TRAIT)
RegisterSignal(src, COMSIG_ENTER_AREA, PROC_REF(check_barstaff_godmode))
check_barstaff_godmode()
+/mob/living/simple_animal/drone/snowflake/bardrone/Destroy()
+ lose_area_sensitivity(ROUNDSTART_TRAIT)
+ return ..()
+
/mob/living/simple_animal/hostile/alien/maid/barmaid
gold_core_spawnable = NO_SPAWN
name = "Barmaid"
@@ -165,12 +170,14 @@
access_card.access = C.get_access()
access_card.access |= ACCESS_CENT_BAR
ADD_TRAIT(access_card, TRAIT_NODROP, ABSTRACT_ITEM_TRAIT)
+ become_area_sensitive(ROUNDSTART_TRAIT)
RegisterSignal(src, COMSIG_ENTER_AREA, PROC_REF(check_barstaff_godmode))
check_barstaff_godmode()
/mob/living/simple_animal/hostile/alien/maid/barmaid/Destroy()
qdel(access_card)
- . = ..()
+ lose_area_sensitivity(ROUNDSTART_TRAIT)
+ return ..()
/mob/living/simple_animal/proc/check_barstaff_godmode()
SIGNAL_HANDLER
diff --git a/shiptest.dme b/shiptest.dme
index 29479c092022..35a38e567054 100644
--- a/shiptest.dme
+++ b/shiptest.dme
@@ -451,7 +451,6 @@
#include "code\datums\components\armor_plate.dm"
#include "code\datums\components\art.dm"
#include "code\datums\components\bane.dm"
-#include "code\datums\components\beauty.dm"
#include "code\datums\components\beetlejuice.dm"
#include "code\datums\components\bloodysoles.dm"
#include "code\datums\components\butchering.dm"
@@ -617,6 +616,7 @@
#include "code\datums\diseases\advance\symptoms\weight.dm"
#include "code\datums\diseases\advance\symptoms\youth.dm"
#include "code\datums\elements\_element.dm"
+#include "code\datums\elements\beauty.dm"
#include "code\datums\elements\bed_tucking.dm"
#include "code\datums\elements\bsa_blocker.dm"
#include "code\datums\elements\cleaning.dm"