diff --git a/code/__DEFINES/dcs/signals/atom/signals_obj.dm b/code/__DEFINES/dcs/signals/atom/signals_obj.dm
index df53558834f6..aebd0d09d0d2 100644
--- a/code/__DEFINES/dcs/signals/atom/signals_obj.dm
+++ b/code/__DEFINES/dcs/signals/atom/signals_obj.dm
@@ -4,6 +4,8 @@
/// From /obj/effect/alien/weeds/Initialize()
#define COMSIG_WEEDNODE_GROWTH_COMPLETE "weednode_growth_complete"
+/// From /obj/effect/alien/weeds/Initialize()
+#define COMSIG_WEEDNODE_GROWTH "weednode_growth"
/// From /obj/effect/alien/weeds/proc/on_weed_expand()
#define COMSIG_WEEDNODE_CANNOT_EXPAND_FURTHER "weednode_cannot_expand_further"
@@ -24,3 +26,6 @@
#define COMSIG_TRANSMITTER_UPDATE_ICON "transmitter_update_icon"
#define COMSIG_TENT_COLLAPSING "tent_collapsing"
+
+/// from /obj/proc/afterbuckle()
+#define COSMIG_OBJ_AFTER_BUCKLE "signal_obj_after_buckle"
diff --git a/code/__DEFINES/tgui.dm b/code/__DEFINES/tgui.dm
index 865088ee72fc..ca6408961eab 100644
--- a/code/__DEFINES/tgui.dm
+++ b/code/__DEFINES/tgui.dm
@@ -32,7 +32,7 @@
/// Creates a message packet for sending via output()
// This is {"type":type,"payload":payload}, but pre-encoded. This is much faster
// than doing it the normal way.
-// To ensure this is correct, this is unit tested in tgui_create_message. However, CM does not have unit tests available.
+// To ensure this is correct, this is unit tested in tgui_create_message.
#define TGUI_CREATE_MESSAGE(type, payload) ( \
"%7b%22type%22%3a%22[type]%22%2c%22payload%22%3a[url_encode(json_encode(payload))]%7d" \
)
diff --git a/code/__DEFINES/traits.dm b/code/__DEFINES/traits.dm
index dd700ccde996..4897a04fea82 100644
--- a/code/__DEFINES/traits.dm
+++ b/code/__DEFINES/traits.dm
@@ -123,7 +123,7 @@
#define TRAIT_SUPER_STRONG "t_super_strong"
/// Foreign biology. Basic medHUDs won't show the mob. (Yautja, Zombies)
#define TRAIT_FOREIGN_BIO "t_foreign_bio"
-/// Eye color changes on intent. (G1 Synths)
+/// Eye color changes on intent. (G1 Synths and WJs)
#define TRAIT_INTENT_EYES "t_intent_eyes"
/// Masked synthetic biology. Basic medHUDs will percieve the mob as human. (Infiltrator Synths)
#define TRAIT_INFILTRATOR_SYNTH "t_infiltrator_synth"
@@ -157,6 +157,8 @@
#define TRAIT_LEADERSHIP "t_leadership"
/// If the mob can see the reagents contents of stuff
#define TRAIT_REAGENT_SCANNER "reagent_scanner"
+/// If the mob cannot eat/be fed
+#define TRAIT_CANNOT_EAT "t_cannot_eat"
/// If the mob is being lazed by a sniper spotter
#define TRAIT_SPOTTER_LAZED "t_spotter_lazed"
/// If the mob has ear protection. Protects from external ear damage effects. Includes explosions, firing the RPG, screeching DEAFNESS only, and flashbangs.
@@ -261,6 +263,7 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_BIMEX" = TRAIT_BIMEX,
"TRAIT_EMOTE_CD_EXEMPT" = TRAIT_EMOTE_CD_EXEMPT,
"TRAIT_LISPING" = TRAIT_LISPING,
+ "TRAIT_CANNOT_EAT" = TRAIT_CANNOT_EAT,
),
/mob/living/carbon/xenomorph = list(
"TRAIT_ABILITY_NO_PLASMA_TRANSFER" = TRAIT_ABILITY_NO_PLASMA_TRANSFER,
diff --git a/code/__DEFINES/wj_emotes.dm b/code/__DEFINES/wj_emotes.dm
new file mode 100644
index 000000000000..f315c6eb2ba5
--- /dev/null
+++ b/code/__DEFINES/wj_emotes.dm
@@ -0,0 +1,8 @@
+#define JOE_EMOTE_CATEGORY_GREETING "Greeting"
+#define JOE_EMOTE_CATEGORY_TASK_UPDATE "Task Update"
+#define JOE_EMOTE_CATEGORY_RESTRICTED_AREA "Restricted Area"
+#define JOE_EMOTE_CATEGORY_FAREWELL "Farewell"
+#define JOE_EMOTE_CATEGORY_QUIP "Quip"
+#define JOE_EMOTE_CATEGORY_WARNING "Warning"
+#define JOE_EMOTE_CATEGORY_QUESTION "Question"
+#define JOE_EMOTE_CATEGORY_NOTICE "Notice"
diff --git a/code/__HELPERS/game.dm b/code/__HELPERS/game.dm
index c447bfcecb15..0132a31d0b50 100644
--- a/code/__HELPERS/game.dm
+++ b/code/__HELPERS/game.dm
@@ -290,21 +290,16 @@
* * cache_only - Whether to not actually send a to_chat message and instead only update larva_queue_cached_message
*/
/proc/message_alien_candidates(list/candidates, dequeued, cache_only = FALSE)
- var/new_players = 0
for(var/i in (1 + dequeued) to candidates.len)
var/mob/dead/observer/cur_obs = candidates[i]
// Generate the messages
- var/cached_message = SPAN_XENONOTICE("You are currently [i-dequeued]\th in the larva queue. There are [new_players] ahead of you that have yet to play this round.")
+ var/cached_message = SPAN_XENONOTICE("You are currently [i-dequeued]\th in the larva queue.")
cur_obs.larva_queue_cached_message = cached_message
if(!cache_only)
var/chat_message = dequeued ? replacetext(cached_message, "currently", "now") : cached_message
to_chat(candidates[i], chat_message)
- // Count how many are prioritized
- if(cur_obs.client.player_details.larva_queue_time < 2) // 0 and 1 because facehuggers/t-domers are slightly deprioritized
- new_players++
-
/proc/convert_k2c(temp)
return ((temp - T0C))
diff --git a/code/datums/components/footstep.dm b/code/datums/components/footstep.dm
index 970ab89d961e..0d218ba94da4 100644
--- a/code/datums/components/footstep.dm
+++ b/code/datums/components/footstep.dm
@@ -31,7 +31,7 @@
return
var/mob/living/LM = parent
- if(LM.buckled || LM.throwing || LM.is_ventcrawling)
+ if(LM.buckled || LM.throwing || LM.is_ventcrawling || LM.stat == DEAD)
return
if(LM.life_steps_total % steps)
diff --git a/code/datums/components/weed_food.dm b/code/datums/components/weed_food.dm
new file mode 100644
index 000000000000..0c578b661517
--- /dev/null
+++ b/code/datums/components/weed_food.dm
@@ -0,0 +1,297 @@
+#define WEED_FOOD_DELAY 5 MINUTES
+#define WEED_FOOD_STATE_DELAY 1 MINUTES
+
+/atom/movable/vis_obj/weed_food
+ name = "weeds"
+ desc = "Weird black weeds in the shape of a body..."
+ gender = PLURAL
+ vis_flags = VIS_INHERIT_DIR|VIS_INHERIT_PLANE|VIS_INHERIT_LAYER
+ icon = 'icons/mob/xenos/weeds.dmi'
+ var/static/list/icon_states = list("human_1","human_2","human_3","human_4","human_5")
+ var/static/list/icon_states_flipped = list("human_1_f","human_2_f","human_3_f","human_4_f","human_5_f")
+ var/icon_state_idx = 0
+ var/timer_id = null
+ var/flipped = FALSE
+
+/atom/movable/vis_obj/weed_food/Initialize(mapload, is_flipped, ...)
+ flipped = is_flipped
+ timer_id = addtimer(CALLBACK(src, PROC_REF(on_animation_timer)), WEED_FOOD_STATE_DELAY, TIMER_STOPPABLE|TIMER_UNIQUE|TIMER_LOOP|TIMER_DELETE_ME)
+ on_animation_timer()
+ return ..()
+
+/// Timer callback for changing the icon_state
+/atom/movable/vis_obj/weed_food/proc/on_animation_timer()
+ icon_state_idx++
+ if(icon_state_idx > length(icon_states))
+ deltimer(timer_id)
+ timer_id = null
+ return
+ icon_state = flipped ? icon_states_flipped[icon_state_idx] : icon_states[icon_state_idx]
+
+/**
+ * A component that can be attached to a mob/living to be merged with weeds after a delay.
+ * Attempting to attach a new weed_food even if one already exists is equivalent to calling start().
+ *
+ * Attach this to any mob/living that is dead (death or initialized dead) and it should handle the rest.
+ */
+/datum/component/weed_food
+ dupe_mode = COMPONENT_DUPE_UNIQUE_PASSARGS
+ /// Whether we are waiting on timer to merge
+ var/active = FALSE
+ /// Whether we are merged with weeds
+ var/merged = FALSE
+ /// The time we were unmerged (just to handle weeds upgrading)
+ var/unmerged_time
+ /// Any active timer for a pending merge
+ var/timer_id = null
+ /// The living mob that we are bound to
+ var/mob/living/parent_mob
+ /// The turf that our parent is on
+ var/turf/parent_turf
+ /// The obj that our parent is buckled to and we have registered a signal
+ var/obj/parent_buckle
+ /// The weeds that we are merging/merged with
+ var/obj/effect/alien/weeds/absorbing_weeds
+ /// The overlay image when merged
+ var/atom/movable/vis_obj/weed_food/weed_appearance
+
+/datum/component/weed_food/Initialize(...)
+ parent_mob = parent
+ //if(!istype(parent_mob))
+ //return COMPONENT_INCOMPATIBLE
+ if(!istype(parent_mob, /mob/living/carbon/human))
+ return COMPONENT_INCOMPATIBLE // TODO: At the moment we only support humans
+
+ parent_turf = get_turf(parent_mob)
+ if(parent_turf != parent_mob.loc)
+ parent_turf = null // if our location is actually a container, we want to be safe from weeds
+
+ start()
+
+/datum/component/weed_food/InheritComponent(datum/component/C, i_am_original)
+ start()
+
+/datum/component/weed_food/Destroy(force, silent)
+ . = ..()
+
+ unmerge_with_weeds()
+ QDEL_NULL(weed_appearance)
+ parent_mob = null
+ parent_turf = null
+
+/datum/component/weed_food/RegisterWithParent()
+ RegisterSignal(parent_mob, COMSIG_MOVABLE_MOVED, PROC_REF(on_move))
+ RegisterSignal(parent_mob, list(COMSIG_LIVING_REJUVENATED, COMSIG_HUMAN_REVIVED), PROC_REF(on_rejuv))
+ RegisterSignal(parent_mob, COMSIG_HUMAN_SET_UNDEFIBBABLE, PROC_REF(on_update))
+ if(parent_turf)
+ RegisterSignal(parent_turf, COMSIG_WEEDNODE_GROWTH, PROC_REF(on_update))
+
+/datum/component/weed_food/UnregisterFromParent()
+ if(parent_mob)
+ UnregisterSignal(parent_mob, list(
+ COMSIG_MOVABLE_MOVED,
+ COMSIG_LIVING_REJUVENATED,
+ COMSIG_HUMAN_REVIVED,
+ COMSIG_HUMAN_SET_UNDEFIBBABLE,
+ ))
+ if(absorbing_weeds)
+ UnregisterSignal(absorbing_weeds, COMSIG_PARENT_QDELETING)
+ if(parent_turf)
+ UnregisterSignal(parent_turf, COMSIG_WEEDNODE_GROWTH)
+ if(parent_buckle)
+ UnregisterSignal(parent_buckle, COSMIG_OBJ_AFTER_BUCKLE)
+
+/// SIGNAL_HANDLER for COMSIG_MOVABLE_MOVED
+/datum/component/weed_food/proc/on_move()
+ SIGNAL_HANDLER
+
+ if(absorbing_weeds)
+ UnregisterSignal(absorbing_weeds, COMSIG_PARENT_QDELETING)
+ absorbing_weeds = null
+
+ if(parent_turf)
+ UnregisterSignal(parent_turf, COMSIG_WEEDNODE_GROWTH)
+ parent_turf = get_turf(parent_mob)
+ if(parent_turf != parent_mob.loc)
+ parent_turf = null // if our location is actually a container, we want to be safe from weeds
+ else
+ RegisterSignal(parent_turf, COMSIG_WEEDNODE_GROWTH, PROC_REF(on_update))
+
+ // We moved, restart or start the proccess
+ if(stop() || !merged)
+ start()
+ return
+
+ // If we somehow moved when we were merged, handle that
+ absorbing_weeds = parent_turf?.weeds
+ if(absorbing_weeds)
+ RegisterSignal(absorbing_weeds, COMSIG_PARENT_QDELETING, PROC_REF(on_weed_deletion))
+ return
+ unmerge_with_weeds()
+
+/// SIGNAL_HANDLER for COMSIG_LIVING_REJUVENATED and COMSIG_HUMAN_REVIVED
+/datum/component/weed_food/proc/on_rejuv()
+ SIGNAL_HANDLER
+
+ qdel(src)
+
+/// SIGNAL_HANDLER for COSMIG_OBJ_AFTER_BUCKLE
+/datum/component/weed_food/proc/on_after_buckle(obj/source, mob/buckled)
+ SIGNAL_HANDLER
+
+ if(buckled)
+ return
+ start() // We unbuckled, so lets try to start again
+
+/// SIGNAL_HANDLER for COMSIG_HUMAN_SET_UNDEFIBBABLE & COMSIG_WEEDNODE_GROWTH
+/datum/component/weed_food/proc/on_update()
+ SIGNAL_HANDLER
+
+ start()
+
+/// SIGNAL_HANDLER for COMSIG_PARENT_QDELETING of weeds
+/datum/component/weed_food/proc/on_weed_deletion()
+ SIGNAL_HANDLER
+
+ if(active)
+ stop()
+ return
+ if(merged)
+ unmerge_with_weeds()
+ return
+
+/**
+ * Try to start the process to turn into weeds
+ * Returns TRUE if started successfully
+ */
+/datum/component/weed_food/proc/start()
+ if(active)
+ return FALSE
+ if(merged)
+ return FALSE
+ if(QDELETED(parent_mob))
+ return FALSE
+
+ if(parent_mob.buckled)
+ if(parent_mob.buckled == parent_buckle)
+ return FALSE // Still buckled to the same thing
+ if(!istype(parent_mob.buckled, /obj/structure/bed/nest))
+ if(parent_buckle) // Still have a lingering reference somehow?
+ UnregisterSignal(parent_buckle, COSMIG_OBJ_AFTER_BUCKLE)
+ parent_buckle = parent_mob.buckled
+ RegisterSignal(parent_mob.buckled, COSMIG_OBJ_AFTER_BUCKLE, PROC_REF(on_after_buckle))
+ return FALSE
+ if(parent_buckle)
+ UnregisterSignal(parent_buckle, COSMIG_OBJ_AFTER_BUCKLE)
+ parent_buckle = null
+
+ if(parent_mob.is_xeno_grabbable())
+ return FALSE
+ if(!(parent_mob.status_flags & PERMANENTLY_DEAD))
+ var/mob/living/carbon/human/parent_human = parent_mob
+ if(istype(parent_human) && !parent_human.undefibbable)
+ return FALSE
+ if(!parent_turf?.weeds)
+ return FALSE
+
+ if(unmerged_time == world.time)
+ return merge_with_weeds() // Weeds upgraded, re-merge now re-using the apperance
+ QDEL_NULL(weed_appearance)
+ absorbing_weeds = parent_turf.weeds
+ RegisterSignal(parent_turf.weeds, COMSIG_PARENT_QDELETING, PROC_REF(on_weed_deletion))
+
+ active = TRUE
+ timer_id = addtimer(CALLBACK(src, PROC_REF(merge_with_weeds)), WEED_FOOD_DELAY, TIMER_STOPPABLE|TIMER_UNIQUE|TIMER_DELETE_ME|TIMER_OVERRIDE)
+
+ return TRUE
+
+/**
+ * Try to stop the process turning into weeds
+ * Returns TRUE if stopped successfully (was active when called)
+ */
+/datum/component/weed_food/proc/stop()
+ if(!active)
+ return FALSE
+
+ active = FALSE
+ deltimer(timer_id)
+ timer_id = null
+
+ return TRUE
+
+/**
+ * Finish becomming one with the weeds
+ * Returns TRUE if merged successfully
+ */
+/datum/component/weed_food/proc/merge_with_weeds()
+ if(merged)
+ return FALSE
+ if(QDELETED(parent_mob))
+ return FALSE
+
+ if(absorbing_weeds) // Remove the signal that would call stop
+ UnregisterSignal(absorbing_weeds, COMSIG_PARENT_QDELETING)
+
+ if(parent_mob.buckled)
+ if(parent_mob.buckled == parent_buckle)
+ return FALSE // Still buckled to the same thing somehow?
+ if(!istype(parent_mob.buckled, /obj/structure/bed/nest))
+ if(parent_buckle) // Still have a lingering reference somehow?
+ UnregisterSignal(parent_buckle, COSMIG_OBJ_AFTER_BUCKLE)
+ parent_buckle = parent_mob.buckled
+ RegisterSignal(parent_mob.buckled, COSMIG_OBJ_AFTER_BUCKLE, PROC_REF(on_after_buckle))
+ return FALSE
+ if(parent_buckle)
+ UnregisterSignal(parent_buckle, COSMIG_OBJ_AFTER_BUCKLE)
+ parent_buckle = null
+
+ absorbing_weeds = parent_turf?.weeds
+ if(!absorbing_weeds)
+ return FALSE
+ RegisterSignal(absorbing_weeds, COMSIG_PARENT_QDELETING, PROC_REF(on_weed_deletion))
+ // Technically we could have just left the signal alone, but both because of the posibility of other conditions preventing a merge or weeds somehow changing and on_move didn't catch it, this is less fragile
+
+ active = FALSE
+ merged = TRUE
+
+ parent_mob.density = FALSE
+ parent_mob.anchored = TRUE
+ parent_mob.mouse_opacity = MOUSE_OPACITY_TRANSPARENT
+ parent_mob.plane = FLOOR_PLANE
+ parent_mob.remove_from_all_mob_huds()
+
+ if(!weed_appearance) // Make a new sprite if we aren't re-merging
+ var/is_flipped = parent_mob.transform.b == -1 // Technically we should check if d is 1 too, but corpses can only be rotated 90 or 270 (1/-1 or -1/1)
+ if(parent_mob.dir & WEST)
+ is_flipped = !is_flipped // The direction reversed the effect of the flip!
+ weed_appearance = new(null, is_flipped)
+ weed_appearance.color = absorbing_weeds.color
+ // TODO: For non-humans change the icon_state or something here
+ parent_mob.vis_contents += weed_appearance
+
+ return TRUE
+
+/**
+ * Undo the weedening
+ * Returns TRUE if unmerged successfully (always)
+ */
+/datum/component/weed_food/proc/unmerge_with_weeds()
+ merged = FALSE
+ unmerged_time = world.time
+
+ if(absorbing_weeds)
+ UnregisterSignal(absorbing_weeds, COMSIG_PARENT_QDELETING)
+ absorbing_weeds = null
+
+ parent_mob.anchored = FALSE
+ parent_mob.mouse_opacity = MOUSE_OPACITY_ICON
+ parent_mob.plane = GAME_PLANE
+ parent_mob.vis_contents -= weed_appearance
+
+ if(!QDELETED(parent_mob))
+ parent_mob.add_to_all_mob_huds()
+
+ return TRUE
+
+#undef WEED_FOOD_DELAY
+#undef WEED_FOOD_STATE_DELAY
diff --git a/code/datums/skills.dm b/code/datums/skills.dm
index ef86b726a3c1..16a2a20a57fd 100644
--- a/code/datums/skills.dm
+++ b/code/datums/skills.dm
@@ -851,7 +851,7 @@ SYNTHETIC
/datum/skills/colonial_synthetic
name = SYNTH_COLONY
skills = list(
- SKILL_CQC = SKILL_CQC_SKILLED,
+ SKILL_CQC = SKILL_CQC_EXPERT,
SKILL_ENGINEER = SKILL_ENGINEER_ENGI,
SKILL_CONSTRUCTION = SKILL_CONSTRUCTION_ENGI,
SKILL_FIREARMS = SKILL_FIREARMS_EXPERT,
@@ -862,7 +862,7 @@ SYNTHETIC
SKILL_MELEE_WEAPONS = SKILL_MELEE_SUPER,
SKILL_PILOT = SKILL_PILOT_EXPERT,
SKILL_POLICE = SKILL_POLICE_SKILLED,
- SKILL_FIREMAN = SKILL_FIREMAN_TRAINED,
+ SKILL_FIREMAN = SKILL_FIREMAN_EXPERT,
SKILL_POWERLOADER = SKILL_POWERLOADER_MASTER,
SKILL_VEHICLE = SKILL_VEHICLE_LARGE,
SKILL_JTAC = SKILL_JTAC_BEGINNER,
diff --git a/code/game/gamemodes/cm_initialize.dm b/code/game/gamemodes/cm_initialize.dm
index 2f6dc6e38118..18b11dde030e 100644
--- a/code/game/gamemodes/cm_initialize.dm
+++ b/code/game/gamemodes/cm_initialize.dm
@@ -383,7 +383,7 @@ Additional game mode variables.
return FALSE
// We aren't in queue yet, lets teach them about the queue then
- candidate_observer.larva_queue_cached_message = SPAN_XENONOTICE("You are currently still awaiting assignment in the larva queue. Priority is given to players who have yet to play in the round, but otherwise the ordering is based on your time of death. When you have been dead long enough and are not inactive, you will periodically receive messages where you are in the queue relative to other currently valid xeno candidates. Note: Playing as a facehugger or in the thunderdome will not alter your time of death. This means you won't lose your relative place in queue if you step away, disconnect, play as a facehugger, or play in the thunderdome.")
+ candidate_observer.larva_queue_cached_message = SPAN_XENONOTICE("You are currently awaiting assignment in the larva queue. The ordering is based on your time of death or the time you joined. When you have been dead long enough and are not inactive, you will periodically receive messages where you are in the queue relative to other currently valid xeno candidates. Your current position will shift as others change their preferences or go inactive, but your relative position compared to all observers is the same. Note: Playing as a facehugger or in the thunderdome will not alter your time of death. This means you won't lose your relative place in queue if you step away, disconnect, play as a facehugger, or play in the thunderdome.")
to_chat(xeno_candidate, candidate_observer.larva_queue_cached_message)
return FALSE
diff --git a/code/game/machinery/autolathe.dm b/code/game/machinery/autolathe.dm
index 7a10a3ffa1df..6ccb0b5b18f7 100644
--- a/code/game/machinery/autolathe.dm
+++ b/code/game/machinery/autolathe.dm
@@ -284,7 +284,7 @@
return
//Dismantle the frame.
- if(istype(O, /obj/item/tool/crowbar))
+ if(HAS_TRAIT(O, TRAIT_TOOL_CROWBAR))
dismantle()
return
diff --git a/code/game/machinery/computer/ai_core.dm b/code/game/machinery/computer/ai_core.dm
index fd246d2d640a..bb6972a58ac3 100644
--- a/code/game/machinery/computer/ai_core.dm
+++ b/code/game/machinery/computer/ai_core.dm
@@ -51,7 +51,7 @@
to_chat(user, SPAN_NOTICE(" You screw the circuit board into place."))
state = 2
icon_state = "2"
- if(istype(P, /obj/item/tool/crowbar) && circuit)
+ if(HAS_TRAIT(P, TRAIT_TOOL_CROWBAR) && circuit)
playsound(loc, 'sound/items/Crowbar.ogg', 25, 1)
to_chat(user, SPAN_NOTICE(" You remove the circuit board."))
state = 1
@@ -121,7 +121,7 @@
to_chat(usr, "Added [mmi].")
icon_state = "3b"
- if(istype(P, /obj/item/tool/crowbar) && brain)
+ if(HAS_TRAIT(P, TRAIT_TOOL_CROWBAR) && brain)
playsound(loc, 'sound/items/Crowbar.ogg', 25, 1)
to_chat(user, SPAN_NOTICE(" You remove the brain."))
brain.forceMove(loc)
@@ -129,7 +129,7 @@
icon_state = "3"
if(4)
- if(istype(P, /obj/item/tool/crowbar))
+ if(HAS_TRAIT(P, TRAIT_TOOL_CROWBAR))
playsound(loc, 'sound/items/Crowbar.ogg', 25, 1)
to_chat(user, SPAN_NOTICE(" You remove the glass panel."))
state = 3
diff --git a/code/game/machinery/computer/buildandrepair.dm b/code/game/machinery/computer/buildandrepair.dm
index bd42b31ea573..07c960807205 100644
--- a/code/game/machinery/computer/buildandrepair.dm
+++ b/code/game/machinery/computer/buildandrepair.dm
@@ -52,7 +52,7 @@
to_chat(user, SPAN_NOTICE(" You screw the circuit board into place."))
src.state = 2
src.icon_state = "2"
- if(istype(P, /obj/item/tool/crowbar) && circuit)
+ if(HAS_TRAIT(P, TRAIT_TOOL_CROWBAR) && circuit)
playsound(src.loc, 'sound/items/Crowbar.ogg', 25, 1)
to_chat(user, SPAN_NOTICE(" You remove the circuit board."))
src.state = 1
@@ -99,7 +99,7 @@
src.state = 4
src.icon_state = "4"
if(4)
- if(istype(P, /obj/item/tool/crowbar))
+ if(HAS_TRAIT(P, TRAIT_TOOL_CROWBAR))
playsound(src.loc, 'sound/items/Crowbar.ogg', 25, 1)
to_chat(user, SPAN_NOTICE(" You remove the glass panel."))
src.state = 3
diff --git a/code/game/machinery/constructable_frame.dm b/code/game/machinery/constructable_frame.dm
index 0288b7eb2426..357ef48fff37 100644
--- a/code/game/machinery/constructable_frame.dm
+++ b/code/game/machinery/constructable_frame.dm
@@ -114,7 +114,7 @@
A.amount = 5
if(CONSTRUCTION_STATE_FINISHED)
- if(istype(P, /obj/item/tool/crowbar))
+ if(HAS_TRAIT(P, TRAIT_TOOL_CROWBAR))
if(!skillcheck(user, SKILL_ENGINEER, required_dismantle_skill))
to_chat(user, SPAN_WARNING("You are not trained to dismantle machines..."))
return
diff --git a/code/game/machinery/cryo.dm b/code/game/machinery/cryo.dm
index 435976668577..afcc9686cff5 100644
--- a/code/game/machinery/cryo.dm
+++ b/code/game/machinery/cryo.dm
@@ -2,6 +2,7 @@
/obj/structure/machinery/cryo_cell
name = "cryo cell"
+ desc = "A donation from the old A.W. project, using cryogenic technology. It slowly heals whoever is inside the tube."
icon = 'icons/obj/structures/machinery/cryogenics2.dmi'
icon_state = "cell"
density = FALSE
diff --git a/code/game/machinery/doors/windowdoor.dm b/code/game/machinery/doors/windowdoor.dm
index 6d96daf3152f..e9006a9f2fb4 100644
--- a/code/game/machinery/doors/windowdoor.dm
+++ b/code/game/machinery/doors/windowdoor.dm
@@ -167,7 +167,7 @@
return
//If it's emagged, crowbar can pry electronics out.
- if (src.operating == -1 && istype(I, /obj/item/tool/crowbar))
+ if (src.operating == -1 && HAS_TRAIT(I, TRAIT_TOOL_CROWBAR))
playsound(src.loc, 'sound/items/Crowbar.ogg', 25, 1)
user.visible_message("[user] removes the electronics from the windoor.", "You start to remove electronics from the windoor.")
if (do_after(user, 40, INTERRUPT_ALL, BUSY_ICON_BUILD))
diff --git a/code/game/machinery/iv_drip.dm b/code/game/machinery/iv_drip.dm
index e1c9c9a4e279..b538f55292c1 100644
--- a/code/game/machinery/iv_drip.dm
+++ b/code/game/machinery/iv_drip.dm
@@ -4,13 +4,16 @@
anchored = FALSE
density = FALSE
drag_delay = 1
+ base_pixel_x = 15
+ base_pixel_y = -2
var/mob/living/carbon/attached = null
var/mode = 1 // 1 is injecting, 0 is taking blood.
var/obj/item/reagent_container/beaker = null
+ var/datum/beam/current_beam
/obj/structure/machinery/iv_drip/update_icon()
- if(src.attached)
+ if(attached)
icon_state = "hooked"
else
icon_state = ""
@@ -35,8 +38,31 @@
filling.color = mix_color_from_reagents(reagents.reagent_list)
overlays += filling
+/obj/structure/machinery/iv_drip/proc/update_beam()
+ if(current_beam)
+ QDEL_NULL(current_beam)
+ else if(!QDELETED(src) && attached)
+ current_beam = beam(attached, "iv_tube")
+
+/obj/structure/machinery/iv_drip/power_change()
+ . = ..()
+ if(stat & NOPOWER && attached)
+ visible_message("\The [src] retracts its IV tube and shuts down.")
+ attached.active_transfusions -= src
+ attached = null
+ update_beam()
+ update_icon()
+
+/obj/structure/machinery/iv_drip/Destroy()
+ attached?.active_transfusions -= src
+ update_beam()
+ . = ..()
+
/obj/structure/machinery/iv_drip/MouseDrop(over_object, src_location, over_location)
..()
+ if(inoperable())
+ visible_message("\The [src] is not powered.")
+ return
if(ishuman(usr))
var/mob/living/carbon/human/H = usr
@@ -48,6 +74,7 @@
"You detach \the [src] from \the [attached].")
attached.active_transfusions -= src
attached = null
+ update_beam()
update_icon()
stop_processing()
return
@@ -57,6 +84,7 @@
"You attach \the [src] to \the [over_object].")
attached = over_object
attached.active_transfusions += src
+ update_beam()
update_icon()
start_processing()
@@ -81,6 +109,7 @@
log_admin("[key_name(user)] put a [beaker] into [src], containing [reagentnames] at ([src.loc.x],[src.loc.y],[src.loc.z]).")
to_chat(user, "You attach \the [W] to \the [src].")
+ update_beam()
update_icon()
return
else
@@ -97,6 +126,7 @@
attached.emote("scream")
attached.active_transfusions -= src
attached = null
+ update_beam()
update_icon()
stop_processing()
return
diff --git a/code/game/machinery/telecomms/machine_interactions.dm b/code/game/machinery/telecomms/machine_interactions.dm
index cded8a1148c7..a370356ad60b 100644
--- a/code/game/machinery/telecomms/machine_interactions.dm
+++ b/code/game/machinery/telecomms/machine_interactions.dm
@@ -62,7 +62,7 @@
stat &= ~BROKEN // the machine's not borked anymore!
else
to_chat(user, SPAN_WARNING("You need five coils of wire for this."))
- if(istype(P, /obj/item/tool/crowbar))
+ if(HAS_TRAIT(P, TRAIT_TOOL_CROWBAR))
to_chat(user, "You begin prying out the circuit board other components...")
playsound(src.loc, 'sound/items/Crowbar.ogg', 25, 1)
if(do_after(user, 60 * user.get_skill_duration_multiplier(SKILL_ENGINEER), INTERRUPT_ALL|BEHAVIOR_IMMOBILE, BUSY_ICON_BUILD))
diff --git a/code/game/machinery/vending/vendor_types/medical.dm b/code/game/machinery/vending/vendor_types/medical.dm
index 5815f60b2e0c..70ac7701973b 100644
--- a/code/game/machinery/vending/vendor_types/medical.dm
+++ b/code/game/machinery/vending/vendor_types/medical.dm
@@ -272,7 +272,7 @@
/obj/structure/machinery/cm_vending/sorted/medical/blood
name = "\improper MM Blood Dispenser"
- desc = "Marine Med brand Blood Pack Dispensary"
+ desc = "The Marine Med Brand Blood Pack Dispensary is the premier, top-of-the-line blood dispenser of 2105! Get yours today!" //Don't update this year, the joke is it's old.
icon_state = "blood"
wrenchable = TRUE
hackable = TRUE
diff --git a/code/game/objects/items/devices/radio/encryptionkey.dm b/code/game/objects/items/devices/radio/encryptionkey.dm
index 5e5786deb33e..6293abb67339 100644
--- a/code/game/objects/items/devices/radio/encryptionkey.dm
+++ b/code/game/objects/items/devices/radio/encryptionkey.dm
@@ -49,7 +49,7 @@
name = "AI Integrated Encryption Key"
desc = "Integrated encryption key"
icon_state = "cap_key"
- channels = list(RADIO_CHANNEL_ALMAYER = TRUE, RADIO_CHANNEL_COMMAND = TRUE, RADIO_CHANNEL_MP = TRUE, RADIO_CHANNEL_ENGI = TRUE, RADIO_CHANNEL_MEDSCI = TRUE, RADIO_CHANNEL_REQ = TRUE, SQUAD_MARINE_1 = TRUE, SQUAD_MARINE_2 = TRUE, SQUAD_MARINE_3 = TRUE, SQUAD_MARINE_4 = TRUE, SQUAD_MARINE_5 = TRUE, SQUAD_MARINE_CRYO = FALSE, RADIO_CHANNEL_JTAC = TRUE, RADIO_CHANNEL_INTEL = TRUE)
+ channels = list(RADIO_CHANNEL_ALMAYER = TRUE, RADIO_CHANNEL_COMMAND = TRUE, RADIO_CHANNEL_MP = TRUE, RADIO_CHANNEL_ENGI = TRUE, RADIO_CHANNEL_MEDSCI = TRUE, RADIO_CHANNEL_REQ = TRUE, SQUAD_MARINE_1 = TRUE, SQUAD_MARINE_2 = TRUE, SQUAD_MARINE_3 = TRUE, SQUAD_MARINE_4 = TRUE, SQUAD_MARINE_5 = TRUE, SQUAD_MARINE_CRYO = TRUE, RADIO_CHANNEL_JTAC = TRUE, RADIO_CHANNEL_INTEL = TRUE)
/obj/item/device/encryptionkey/sentry_laptop
name = "Sentry Network Status Encryption Key"
@@ -61,12 +61,12 @@
/obj/item/device/encryptionkey/cmpcom/cdrcom
name = "\improper Marine Senior Command Radio Encryption Key"
- channels = list(RADIO_CHANNEL_COMMAND = TRUE, RADIO_CHANNEL_MP = TRUE, SQUAD_MARINE_1 = TRUE, SQUAD_MARINE_2 = TRUE, SQUAD_MARINE_3 = TRUE, SQUAD_MARINE_4 = TRUE, SQUAD_MARINE_5 = TRUE, SQUAD_MARINE_CRYO = FALSE, RADIO_CHANNEL_ENGI = TRUE, RADIO_CHANNEL_MEDSCI = TRUE, RADIO_CHANNEL_REQ = TRUE, RADIO_CHANNEL_JTAC = TRUE, RADIO_CHANNEL_INTEL = TRUE)
+ channels = list(RADIO_CHANNEL_COMMAND = TRUE, RADIO_CHANNEL_MP = TRUE, SQUAD_MARINE_1 = TRUE, SQUAD_MARINE_2 = TRUE, SQUAD_MARINE_3 = TRUE, SQUAD_MARINE_4 = TRUE, SQUAD_MARINE_5 = TRUE, SQUAD_MARINE_CRYO = TRUE, RADIO_CHANNEL_ENGI = TRUE, RADIO_CHANNEL_MEDSCI = TRUE, RADIO_CHANNEL_REQ = TRUE, RADIO_CHANNEL_JTAC = TRUE, RADIO_CHANNEL_INTEL = TRUE)
/obj/item/device/encryptionkey/mcom
name = "\improper Marine Command Radio Encryption Key"
icon_state = "cap_key"
- channels = list(RADIO_CHANNEL_COMMAND = TRUE, SQUAD_MARINE_1 = TRUE, SQUAD_MARINE_2 = TRUE, SQUAD_MARINE_3 = TRUE, SQUAD_MARINE_4 = TRUE, SQUAD_MARINE_5 = TRUE, SQUAD_MARINE_CRYO = FALSE, RADIO_CHANNEL_ENGI = TRUE, RADIO_CHANNEL_MEDSCI = TRUE, RADIO_CHANNEL_REQ = TRUE, RADIO_CHANNEL_JTAC = TRUE, RADIO_CHANNEL_INTEL = TRUE)
+ channels = list(RADIO_CHANNEL_COMMAND = TRUE, SQUAD_MARINE_1 = TRUE, SQUAD_MARINE_2 = TRUE, SQUAD_MARINE_3 = TRUE, SQUAD_MARINE_4 = TRUE, SQUAD_MARINE_5 = TRUE, SQUAD_MARINE_CRYO = TRUE, RADIO_CHANNEL_ENGI = TRUE, RADIO_CHANNEL_MEDSCI = TRUE, RADIO_CHANNEL_REQ = TRUE, RADIO_CHANNEL_JTAC = TRUE, RADIO_CHANNEL_INTEL = TRUE)
// MARINE ENGINEERING
@@ -102,7 +102,7 @@
/obj/item/device/encryptionkey/mmpo
name = "\improper Military Police Radio Encryption Key"
icon_state = "sec_key"
- channels = list(RADIO_CHANNEL_COMMAND = TRUE, RADIO_CHANNEL_MP = TRUE, SQUAD_MARINE_1 = TRUE, SQUAD_MARINE_2 = TRUE, SQUAD_MARINE_3 = TRUE, SQUAD_MARINE_4 = TRUE, SQUAD_MARINE_5 = TRUE, SQUAD_MARINE_CRYO = FALSE, RADIO_CHANNEL_ENGI = TRUE, RADIO_CHANNEL_MEDSCI = TRUE,)
+ channels = list(RADIO_CHANNEL_COMMAND = TRUE, RADIO_CHANNEL_MP = TRUE, SQUAD_MARINE_1 = TRUE, SQUAD_MARINE_2 = TRUE, SQUAD_MARINE_3 = TRUE, SQUAD_MARINE_4 = TRUE, SQUAD_MARINE_5 = TRUE, SQUAD_MARINE_CRYO = TRUE, RADIO_CHANNEL_ENGI = TRUE, RADIO_CHANNEL_MEDSCI = TRUE,)
/obj/item/device/encryptionkey/sec
name = "Security Radio Encryption Key"
@@ -130,7 +130,7 @@
/obj/item/device/encryptionkey/cmpcom/synth
name = "\improper Marine Synth Radio Encryption Key"
- channels = list(RADIO_CHANNEL_COMMAND = TRUE, RADIO_CHANNEL_MP = TRUE, SQUAD_MARINE_1 = TRUE, SQUAD_MARINE_2 = TRUE, SQUAD_MARINE_3 = TRUE, SQUAD_MARINE_4 = TRUE, SQUAD_MARINE_5 = TRUE, SQUAD_MARINE_CRYO = FALSE, RADIO_CHANNEL_ENGI = TRUE, RADIO_CHANNEL_MEDSCI = TRUE, RADIO_CHANNEL_REQ = TRUE, RADIO_CHANNEL_JTAC = TRUE, RADIO_CHANNEL_INTEL = TRUE)
+ channels = list(RADIO_CHANNEL_COMMAND = TRUE, RADIO_CHANNEL_MP = TRUE, SQUAD_MARINE_1 = TRUE, SQUAD_MARINE_2 = TRUE, SQUAD_MARINE_3 = TRUE, SQUAD_MARINE_4 = TRUE, SQUAD_MARINE_5 = TRUE, SQUAD_MARINE_CRYO = TRUE, RADIO_CHANNEL_ENGI = TRUE, RADIO_CHANNEL_MEDSCI = TRUE, RADIO_CHANNEL_REQ = TRUE, RADIO_CHANNEL_JTAC = TRUE, RADIO_CHANNEL_INTEL = TRUE)
/obj/item/device/encryptionkey/mcom/cl
name = "\improper Corporate Liaison radio encryption key"
@@ -163,7 +163,7 @@
channels = list(RADIO_CHANNEL_REQ = TRUE, RADIO_CHANNEL_COMMAND = FALSE)
/obj/item/device/encryptionkey/mcom/ai //AI only.
- channels = list(RADIO_CHANNEL_COMMAND = TRUE, RADIO_CHANNEL_MP = TRUE, SQUAD_MARINE_1 = TRUE, SQUAD_MARINE_2 = TRUE, SQUAD_MARINE_3 = TRUE, SQUAD_MARINE_4 = TRUE, SQUAD_MARINE_5 = TRUE, SQUAD_MARINE_CRYO = FALSE, RADIO_CHANNEL_ENGI = TRUE, RADIO_CHANNEL_MEDSCI = TRUE, RADIO_CHANNEL_REQ = TRUE, RADIO_CHANNEL_JTAC = TRUE, RADIO_CHANNEL_INTEL = TRUE)
+ channels = list(RADIO_CHANNEL_COMMAND = TRUE, RADIO_CHANNEL_MP = TRUE, SQUAD_MARINE_1 = TRUE, SQUAD_MARINE_2 = TRUE, SQUAD_MARINE_3 = TRUE, SQUAD_MARINE_4 = TRUE, SQUAD_MARINE_5 = TRUE, SQUAD_MARINE_CRYO = TRUE, RADIO_CHANNEL_ENGI = TRUE, RADIO_CHANNEL_MEDSCI = TRUE, RADIO_CHANNEL_REQ = TRUE, RADIO_CHANNEL_JTAC = TRUE, RADIO_CHANNEL_INTEL = TRUE)
// MARINE SQUADS
@@ -296,7 +296,7 @@
/obj/item/device/encryptionkey/highcom
name = "\improper USCM High Command Radio Encryption Key"
icon_state = "binary_key"
- channels = list(RADIO_CHANNEL_HIGHCOM = TRUE, SQUAD_SOF = TRUE, RADIO_CHANNEL_COMMAND = TRUE, RADIO_CHANNEL_MP = TRUE, SQUAD_MARINE_1 = TRUE, SQUAD_MARINE_2 = TRUE, SQUAD_MARINE_3 = TRUE, SQUAD_MARINE_4 = TRUE, SQUAD_MARINE_5 = TRUE, SQUAD_MARINE_CRYO = FALSE, RADIO_CHANNEL_ENGI = TRUE, RADIO_CHANNEL_MEDSCI = TRUE, RADIO_CHANNEL_REQ = TRUE, RADIO_CHANNEL_JTAC = TRUE, RADIO_CHANNEL_INTEL = TRUE)
+ channels = list(RADIO_CHANNEL_HIGHCOM = TRUE, SQUAD_SOF = TRUE, RADIO_CHANNEL_COMMAND = TRUE, RADIO_CHANNEL_MP = TRUE, SQUAD_MARINE_1 = TRUE, SQUAD_MARINE_2 = TRUE, SQUAD_MARINE_3 = TRUE, SQUAD_MARINE_4 = TRUE, SQUAD_MARINE_5 = TRUE, SQUAD_MARINE_CRYO = TRUE, RADIO_CHANNEL_ENGI = TRUE, RADIO_CHANNEL_MEDSCI = TRUE, RADIO_CHANNEL_REQ = TRUE, RADIO_CHANNEL_JTAC = TRUE, RADIO_CHANNEL_INTEL = TRUE)
/obj/item/device/encryptionkey/contractor
name = "\improper Vanguard's Arrow Incorporated Radio Encryption Key"
diff --git a/code/game/objects/items/reagent_containers/blood_pack.dm b/code/game/objects/items/reagent_containers/blood_pack.dm
index 450cdde2fa00..8e29a26c2ecd 100644
--- a/code/game/objects/items/reagent_containers/blood_pack.dm
+++ b/code/game/objects/items/reagent_containers/blood_pack.dm
@@ -13,7 +13,9 @@
var/mode = BLOOD_BAG_INJECTING
var/mob/living/carbon/human/connected_to
+ var/mob/living/carbon/human/connected_from
var/blood_type = null
+ var/datum/beam/current_beam
/obj/item/reagent_container/blood/Initialize()
. = ..()
@@ -32,6 +34,12 @@
if(10 to 50) icon_state = "half"
if(51 to INFINITY) icon_state = "full"
+/obj/item/reagent_container/blood/proc/update_beam()
+ if(current_beam)
+ QDEL_NULL(current_beam)
+ else if(connected_from && connected_to)
+ current_beam = connected_from.beam(connected_to, "iv_tube")
+
/obj/item/reagent_container/blood/attack(mob/attacked_mob, mob/user)
. = ..()
@@ -44,7 +52,10 @@
user.visible_message("[user] detaches [src] from [connected_to].", \
"You detach [src] from [connected_to].")
connected_to.active_transfusions -= src
+ connected_to.base_pixel_x = 0
connected_to = null
+ connected_from = null
+ update_beam()
return
if(!skillcheck(user, SKILL_SURGERY, SKILL_SURGERY_NOVICE))
@@ -60,10 +71,13 @@
if(istype(attacked_mob, /mob/living/carbon/human))
connected_to = attacked_mob
+ connected_from = user
connected_to.active_transfusions += src
+ connected_to.base_pixel_x = 5
START_PROCESSING(SSobj, src)
user.visible_message("[user] attaches \the [src] to [connected_to].", \
"You attach \the [src] to [connected_to].")
+ update_beam()
/obj/item/reagent_container/blood/process()
//if we're not connected to anything stop doing stuff
@@ -106,6 +120,10 @@
connected_to.take_blood(src, amount)
+/obj/item/reagent_container/blood/dropped()
+ ..()
+ bad_disconnect()
+
///Used to standardize effects of a blood bag disconnecting improperly
/obj/item/reagent_container/blood/proc/bad_disconnect()
if(!connected_to)
@@ -116,7 +134,10 @@
if(connected_to.pain.feels_pain)
connected_to.emote("scream")
connected_to.active_transfusions -= src
+ connected_to.base_pixel_x = 0
connected_to = null
+ connected_from = null
+ update_beam()
/obj/item/reagent_container/blood/verb/toggle_mode()
set category = "Object"
diff --git a/code/game/objects/items/reagent_containers/food/snacks.dm b/code/game/objects/items/reagent_containers/food/snacks.dm
index 7dae94bfe4eb..c0e11dac8eb3 100644
--- a/code/game/objects/items/reagent_containers/food/snacks.dm
+++ b/code/game/objects/items/reagent_containers/food/snacks.dm
@@ -66,6 +66,10 @@
if(issynth(C))
fullness = 200 //Synths never get full
+ if(HAS_TRAIT(M, TRAIT_CANNOT_EAT)) //Do not feed the Working Joes
+ to_chat(user, SPAN_DANGER("[user == M ? "You are" : "[M] is"] unable to eat!"))
+ return
+
if(fullness > 540)
C.overeat_cooldown = world.time + OVEREAT_TIME
diff --git a/code/game/objects/items/tools/flame_tools.dm b/code/game/objects/items/tools/flame_tools.dm
index db35d3300ae7..6ebd8ee5982e 100644
--- a/code/game/objects/items/tools/flame_tools.dm
+++ b/code/game/objects/items/tools/flame_tools.dm
@@ -170,13 +170,20 @@ CIGARETTE PACKETS ARE IN FANCY.DM
damtype = "brute"
icon_state = "[initial(icon_state)]_burnt"
item_state = "cigoff"
- if(user && loc != user)
- user.SetLuminosity(0, FALSE, src)
SetLuminosity(0)
name = burnt_name
desc = "A match. This one has seen better days."
STOP_PROCESSING(SSobj, src)
+ if(user)
+ user.SetLuminosity(0, FALSE, src)
+ return
+
+ if(ismob(loc))
+ user = loc
+ user.SetLuminosity(0, FALSE, src)
+ return
+
/obj/item/tool/match/paper
name = "paper match"
desc = "A simple match stick, used for lighting fine smokables."
diff --git a/code/game/objects/items/tools/maintenance_tools.dm b/code/game/objects/items/tools/maintenance_tools.dm
index 25bcefc1cc34..2560c5ff91e8 100644
--- a/code/game/objects/items/tools/maintenance_tools.dm
+++ b/code/game/objects/items/tools/maintenance_tools.dm
@@ -557,8 +557,6 @@
if(attacked_door.locked) //Bolted
to_chat(user, SPAN_DANGER("You can't pry open [attacked_door] while it is bolted shut."))
return
- if(!attacked_door.arePowerSystemsOn()) //Opens like normal if unpowered
- return FALSE
if(requires_superstrength_pry)
if(!HAS_TRAIT(user, TRAIT_SUPER_STRONG)) //basically IS_PRY_CAPABLE_CROWBAR
@@ -645,7 +643,7 @@
resin_door.Open()
return
- if(istype(attacked_obj, /turf/open/floor))
+ else if(istype(attacked_obj, /turf/open/floor))
var/turf/open/floor/flooring = attacked_obj
if(crowbar_mode && user.a_intent == INTENT_HELP) //Only pry flooring on help intent
diff --git a/code/game/objects/objs.dm b/code/game/objects/objs.dm
index e98e3b527c5f..b92624cf4201 100644
--- a/code/game/objects/objs.dm
+++ b/code/game/objects/objs.dm
@@ -223,6 +223,7 @@
/obj/proc/afterbuckle(mob/M as mob) // Called after somebody buckled / unbuckled
handle_rotation()
+ SEND_SIGNAL(src, COSMIG_OBJ_AFTER_BUCKLE, buckled_mob)
return buckled_mob
/obj/proc/unbuckle()
diff --git a/code/game/objects/structures/crates_lockers/closets.dm b/code/game/objects/structures/crates_lockers/closets.dm
index e6c215d0208f..cf0374c09ab4 100644
--- a/code/game/objects/structures/crates_lockers/closets.dm
+++ b/code/game/objects/structures/crates_lockers/closets.dm
@@ -135,15 +135,17 @@
return stored_units
/obj/structure/closet/proc/store_mobs(stored_units)
- for(var/mob/M in src.loc)
+ for(var/mob/cur_mob in src.loc)
if(stored_units + mob_size > storage_capacity)
break
- if(istype (M, /mob/dead/observer))
+ if(istype (cur_mob, /mob/dead/observer))
continue
- if(M.buckled)
+ if(cur_mob.buckled)
+ continue
+ if(cur_mob.anchored)
continue
- M.forceMove(src)
+ cur_mob.forceMove(src)
stored_units += mob_size
return stored_units
diff --git a/code/game/objects/structures/props.dm b/code/game/objects/structures/props.dm
index f6905d4d044d..1a91650c620a 100644
--- a/code/game/objects/structures/props.dm
+++ b/code/game/objects/structures/props.dm
@@ -24,7 +24,7 @@
. = ..()
if(isxeno(user))
return
- else if (ishuman(user) && istype(W, /obj/item/tool/wrench))
+ else if (ishuman(user) && HAS_TRAIT(W, TRAIT_TOOL_WRENCH))
on = !on
visible_message("You wrench the controls of \the [src]. The drill jumps to life." , "[user] wrenches the controls of \the [src]. The drill jumps to life.")
@@ -501,7 +501,7 @@
. = ..()
if(isxeno(user))
return
- else if (ishuman(user) && istype(W, /obj/item/tool/crowbar))
+ else if (ishuman(user) && HAS_TRAIT(W, TRAIT_TOOL_CROWBAR))
on = !on
visible_message("You pry at the control valve on [src]. The machine shudders." , "[user] pries at the control valve on [src]. The entire machine shudders.")
diff --git a/code/game/objects/structures/watercloset.dm b/code/game/objects/structures/watercloset.dm
index 5c084ce94cbc..cccc1211bfb0 100644
--- a/code/game/objects/structures/watercloset.dm
+++ b/code/game/objects/structures/watercloset.dm
@@ -121,7 +121,7 @@
cistern_overlay.icon_state = "cistern[cistern]"
/obj/structure/toilet/attackby(obj/item/I, mob/living/user)
- if(istype(I, /obj/item/tool/crowbar))
+ if(HAS_TRAIT(I, TRAIT_TOOL_CROWBAR))
to_chat(user, SPAN_NOTICE("You start to [cistern ? "replace the lid on the cistern" : "lift the lid off the cistern"]."))
playsound(loc, 'sound/effects/stonedoor_openclose.ogg', 25, 1)
if(do_after(user, 30, INTERRUPT_ALL|BEHAVIOR_IMMOBILE, BUSY_ICON_BUILD))
diff --git a/code/game/turfs/floor.dm b/code/game/turfs/floor.dm
index 4a600d4d033b..dc2cda0c2c2a 100644
--- a/code/game/turfs/floor.dm
+++ b/code/game/turfs/floor.dm
@@ -148,7 +148,7 @@
if(src.weeds)
return weeds.attackby(hitting_item,user)
- if(istype(hitting_item, /obj/item/tool/crowbar) && (tool_flags & (REMOVE_CROWBAR|BREAK_CROWBAR)))
+ if(HAS_TRAIT(hitting_item, TRAIT_TOOL_CROWBAR) && (tool_flags & (REMOVE_CROWBAR|BREAK_CROWBAR)))
if(broken || burnt)
to_chat(user, SPAN_WARNING("You remove the broken tiles."))
else
diff --git a/code/game/turfs/walls/walls.dm b/code/game/turfs/walls/walls.dm
index 411ff8182661..3599d5bb980b 100644
--- a/code/game/turfs/walls/walls.dm
+++ b/code/game/turfs/walls/walls.dm
@@ -98,11 +98,15 @@
qdel(found_nest) //nests are built on walls, no walls, no nest
/turf/closed/wall/MouseDrop_T(mob/current_mob, mob/user)
+ if(!ismob(current_mob))
+ return
+
if(acided_hole)
if(current_mob == user && isxeno(user))
acided_hole.use_wall_hole(user)
return
- if(isxeno(user))
+
+ if(isxeno(user) && istype(user.get_active_hand(), /obj/item/grab))
var/mob/living/carbon/xenomorph/user_as_xenomorph = user
user_as_xenomorph.do_nesting_host(current_mob, src)
..()
diff --git a/code/modules/admin/tabs/event_tab.dm b/code/modules/admin/tabs/event_tab.dm
index 4effbf3b26cb..febc1550fca0 100644
--- a/code/modules/admin/tabs/event_tab.dm
+++ b/code/modules/admin/tabs/event_tab.dm
@@ -744,7 +744,7 @@
create_humans_html = replacetext(create_humans_html, "null /* object types */", "\"[equipment_presets]\"")
create_humans_html = replacetext(create_humans_html, "/* href token */", RawHrefToken(forceGlobal = TRUE))
- show_browser(user, replacetext(create_humans_html, "/* ref src */", "\ref[src]"), "Create Humans", "create_humans", "size=450x630")
+ show_browser(user, replacetext(create_humans_html, "/* ref src */", "\ref[src]"), "Create Humans", "create_humans", "size=450x720")
/client/proc/create_humans()
set name = "Create Humans"
diff --git a/code/modules/client/player_details.dm b/code/modules/client/player_details.dm
index 2360ea6c4f28..634fd8fb627e 100644
--- a/code/modules/client/player_details.dm
+++ b/code/modules/client/player_details.dm
@@ -10,6 +10,9 @@ GLOBAL_LIST_EMPTY(player_details) // ckey -> /datum/player_details
/// The descriminator for larva queue ordering: Generally set to timeofdeath except for facehuggers/admin z-level play
var/larva_queue_time
+/datum/player_details/New()
+ larva_queue_time = world.time
+ return ..()
/proc/log_played_names(ckey, ...)
if(!ckey)
diff --git a/code/modules/cm_aliens/structures/fruit.dm b/code/modules/cm_aliens/structures/fruit.dm
index 8bdcc0b82413..bb899a6ff25b 100644
--- a/code/modules/cm_aliens/structures/fruit.dm
+++ b/code/modules/cm_aliens/structures/fruit.dm
@@ -150,25 +150,33 @@
update_icon()
QDEL_IN(src, 3 SECONDS)
-/obj/effect/alien/resin/fruit/attack_alien(mob/living/carbon/xenomorph/X)
+/obj/effect/alien/resin/fruit/attack_alien(mob/living/carbon/xenomorph/affected_xeno)
if(picked)
- to_chat(X, SPAN_XENODANGER("This fruit is already being picked!"))
+ to_chat(affected_xeno, SPAN_XENODANGER("This fruit is already being picked!"))
return
- if(X.a_intent != INTENT_HARM && (X.can_not_harm(bound_xeno) || X.hivenumber == hivenumber))
- var/cant_consume = prevent_consume(X)
+
+ if(affected_xeno.a_intent != INTENT_HARM && (affected_xeno.can_not_harm(bound_xeno) || affected_xeno.hivenumber == hivenumber))
+ var/cant_consume = prevent_consume(affected_xeno)
if(cant_consume)
return cant_consume
+
if(mature)
- to_chat(X, SPAN_XENOWARNING("You prepare to consume [name]."))
- xeno_noncombat_delay(X)
- if(!do_after(X, consume_delay, INTERRUPT_ALL, BUSY_ICON_FRIENDLY))
+ to_chat(affected_xeno, SPAN_XENOWARNING("You prepare to consume [name]."))
+ xeno_noncombat_delay(affected_xeno)
+ if(!do_after(affected_xeno, consume_delay, INTERRUPT_ALL, BUSY_ICON_FRIENDLY))
return XENO_NO_DELAY_ACTION
- consume_effect(X)
+
+ cant_consume = prevent_consume(affected_xeno) // Check again after the delay incase they have eaten another fruit
+ if(cant_consume)
+ to_chat(affected_xeno, SPAN_XENOWARNING("You can no longer consume [name]."))
+ return cant_consume
+ consume_effect(affected_xeno)
else
- to_chat(X, SPAN_XENOWARNING("[name] isn't ripe yet. You need to wait a little longer."))
- if(X.a_intent == INTENT_HARM && isxeno_builder(X) || (!X.can_not_harm(bound_xeno) && X.hivenumber != hivenumber))
- X.animation_attack_on(src)
- X.visible_message(SPAN_XENODANGER("[X] removes [name]!"),
+ to_chat(affected_xeno, SPAN_XENOWARNING("[name] isn't ripe yet. You need to wait a little longer."))
+
+ if(affected_xeno.a_intent == INTENT_HARM && isxeno_builder(affected_xeno) || (!affected_xeno.can_not_harm(bound_xeno) && affected_xeno.hivenumber != hivenumber))
+ affected_xeno.animation_attack_on(src)
+ affected_xeno.visible_message(SPAN_XENODANGER("[affected_xeno] removes [name]!"),
SPAN_XENODANGER("You remove [name]!"))
playsound(loc, "alien_resin_break", 25)
qdel(src)
@@ -376,30 +384,49 @@
bound_xeno = null
// Xenos eating fruit
-/obj/item/reagent_container/food/snacks/resin_fruit/attack(mob/living/carbon/xenomorph/X, mob/user)
+/obj/item/reagent_container/food/snacks/resin_fruit/attack(mob/living/carbon/xenomorph/affected_xeno, mob/user)
if(istype(user, /mob/living/carbon/xenomorph)) // Prevents xenos from feeding capped/dead marines fruit
- var/mob/living/carbon/xenomorph/Y = user
- if(!Y.can_not_harm(X))
- to_chat(Y, SPAN_WARNING("[X] refuses to eat [src]."))
+ var/mob/living/carbon/xenomorph/feeding_xeno = user
+ if(!feeding_xeno.can_not_harm(affected_xeno))
+ to_chat(feeding_xeno, SPAN_WARNING("[affected_xeno] refuses to eat [src]."))
return
- if(!istype(X))
+
+ if(!istype(affected_xeno))
return ..()
- if(X.stat == DEAD)
+
+ if(affected_xeno.stat == DEAD)
to_chat(user, SPAN_WARNING("That sister is already dead, they won't benefit from the fruit now..."))
return
- user.affected_message(X,
- SPAN_HELPFUL("You start [user == X ? "eating" : "feeding [X]"] [src]."),
- SPAN_HELPFUL("[user] starts feeding you [src]."),
- SPAN_NOTICE("[user] starts [user == X ? "eating" : "feeding [X]"] [src]."))
- if(!do_after(user, consume_delay, INTERRUPT_ALL, BUSY_ICON_FRIENDLY, X, INTERRUPT_MOVED, BUSY_ICON_MEDICAL))
+
+ var/obj/effect/alien/resin/fruit/current_fruit = new fruit_type(affected_xeno)
+ var/cant_consume = current_fruit.prevent_consume(affected_xeno)
+ if(cant_consume)
+ user.affected_message(affected_xeno,
+ SPAN_HELPFUL("You fail to [user == affected_xeno ? "eat" : "feed [affected_xeno]"] [current_fruit]."),
+ SPAN_HELPFUL("[user] fails to feed you [current_fruit]."))
+ return
+ user.affected_message(affected_xeno,
+ SPAN_HELPFUL("You start [user == affected_xeno ? "eating" : "feeding [affected_xeno]"] [current_fruit]."),
+ SPAN_HELPFUL("[user] starts feeding you [current_fruit]."),
+ SPAN_NOTICE("[user] starts [user == affected_xeno ? "eating" : "feeding [affected_xeno]"] [current_fruit]."))
+
+ if(!do_after(user, consume_delay, INTERRUPT_ALL, BUSY_ICON_FRIENDLY, affected_xeno, INTERRUPT_MOVED, BUSY_ICON_MEDICAL))
return FALSE
- user.affected_message(X,
- SPAN_HELPFUL("You [user == X ? "eat" : "fed [X]"] [src]."),
- SPAN_HELPFUL("[user] fed you [src]."),
- SPAN_NOTICE("[user] [user == X ? "ate" : "fed [X]"] [src]."))
- var/obj/effect/alien/resin/fruit/F = new fruit_type(X)
- F.mature = TRUE
- F.consume_effect(X)
+
+ cant_consume = current_fruit.prevent_consume(affected_xeno)
+ if(cant_consume) //Check again after the timer incase they ate another fruit
+ user.affected_message(affected_xeno,
+ SPAN_HELPFUL("You fail to [user == affected_xeno ? "eat" : "feed [affected_xeno]"] [current_fruit]."),
+ SPAN_HELPFUL("[user] fails to feed you [current_fruit]."))
+ return
+
+ user.affected_message(affected_xeno,
+ SPAN_HELPFUL("You [user == affected_xeno ? "eat" : "fed [affected_xeno]"] [current_fruit]."),
+ SPAN_HELPFUL("[user] fed you [current_fruit]."),
+ SPAN_NOTICE("[user] [user == affected_xeno ? "ate" : "fed [affected_xeno]"] [current_fruit]."))
+ current_fruit.mature = TRUE
+ current_fruit.consume_effect(affected_xeno)
+
//Notify the fruit's bound xeno if they exist
if(!QDELETED(bound_xeno))
to_chat(bound_xeno, SPAN_XENOWARNING("One of your picked resin fruits has been consumed."))
diff --git a/code/modules/cm_aliens/weeds.dm b/code/modules/cm_aliens/weeds.dm
index 7ca73a5c2822..f20fa842e446 100644
--- a/code/modules/cm_aliens/weeds.dm
+++ b/code/modules/cm_aliens/weeds.dm
@@ -73,10 +73,11 @@
else if(!hibernate && do_spread)
addtimer(CALLBACK(src, PROC_REF(weed_expand)), WEED_BASE_GROW_SPEED / max(weed_strength, 1))
- var/turf/T = get_turf(src)
- if(T)
- T.weeds = src
- weeded_turf = T
+ var/turf/turf = get_turf(src)
+ if(turf)
+ turf.weeds = src
+ weeded_turf = turf
+ SEND_SIGNAL(turf, COMSIG_WEEDNODE_GROWTH) // Currently for weed_food wakeup
RegisterSignal(src, list(
COMSIG_ATOM_TURF_CHANGE,
@@ -429,7 +430,11 @@
/obj/effect/alien/weeds/weedwall/MouseDrop_T(mob/current_mob, mob/user)
. = ..()
- if(isxeno(user))
+
+ if(!ismob(current_mob))
+ return
+
+ if(isxeno(user) && istype(user.get_active_hand(), /obj/item/grab))
var/mob/living/carbon/xenomorph/user_as_xenomorph = user
user_as_xenomorph.do_nesting_host(current_mob, src)
diff --git a/code/modules/cm_tech/implements/railgun.dm b/code/modules/cm_tech/implements/railgun.dm
index b0d91515419f..b69f9a9d13a8 100644
--- a/code/modules/cm_tech/implements/railgun.dm
+++ b/code/modules/cm_tech/implements/railgun.dm
@@ -6,6 +6,7 @@ GLOBAL_DATUM(railgun_eye_location, /datum/coords)
/obj/effect/landmark/railgun_computer
name = "Railgun computer landmark"
+ desc = "A computer with an orange interface, it's idly blinking, awaiting a password."
/obj/effect/landmark/railgun_computer/Initialize(mapload, ...)
. = ..()
diff --git a/code/modules/gear_presets/corpses.dm b/code/modules/gear_presets/corpses.dm
index 02671cc02a93..7e9dd5b841a0 100644
--- a/code/modules/gear_presets/corpses.dm
+++ b/code/modules/gear_presets/corpses.dm
@@ -16,28 +16,32 @@
/datum/equipment_preset/corpse/load_status(mob/living/carbon/human/new_human)
. = ..(new_human)
+
+ // These two values matter because they are checked on death for weed_food
+ new_human.undefibbable = TRUE
+ if(xenovictim)
+ new_human.chestburst = 2
+
new_human.death(create_cause_data("existing"), TRUE) //Kills the new mob
new_human.apply_damage(100, BRUTE)
new_human.apply_damage(100, BRUTE)
new_human.apply_damage(100, BRUTE)
if(xenovictim)
- var/datum/internal_organ/O
+ var/datum/internal_organ/organ
var/i
for(i in list("heart","lungs"))
- O = new_human.internal_organs_by_name[i]
+ organ = new_human.internal_organs_by_name[i]
new_human.internal_organs_by_name -= i
- new_human.internal_organs -= O
- new_human.chestburst = 2
+ new_human.internal_organs -= organ
new_human.update_burst()
//buckle to nest
- var/obj/structure/bed/nest/N = locate() in get_turf(src)
- if(N)
- new_human.buckled = N
- new_human.setDir(N.dir)
+ var/obj/structure/bed/nest/nest = locate() in get_turf(src)
+ if(nest)
+ new_human.buckled = nest
+ new_human.setDir(nest.dir)
new_human.update_canmove()
- N.buckled_mob = new_human
- N.afterbuckle(new_human)
- new_human.undefibbable = TRUE
+ nest.buckled_mob = new_human
+ nest.afterbuckle(new_human)
new_human.spawned_corpse = TRUE
new_human.updatehealth()
new_human.pulse = PULSE_NONE
diff --git a/code/modules/gear_presets/survivors.dm b/code/modules/gear_presets/survivors.dm
index 456f0881987e..44808d7a374f 100644
--- a/code/modules/gear_presets/survivors.dm
+++ b/code/modules/gear_presets/survivors.dm
@@ -1369,3 +1369,43 @@
return
var/shoespath = /obj/item/clothing/shoes/combat
human.equip_to_slot_or_del(new shoespath, WEAR_FEET)
+
+/datum/equipment_preset/survivor/new_varadero/commander
+ name = "Survivor - USASF Commander"
+ assignment = "USASF Commander"
+ skills = /datum/skills/commander
+ paygrade = "NO5"
+ idtype = /obj/item/card/id/gold
+ role_comm_title = "USASF CDR"
+ flags = EQUIPMENT_PRESET_START_OF_ROUND
+ access = list(
+ ACCESS_CIVILIAN_PUBLIC,
+ ACCESS_CIVILIAN_RESEARCH,
+ ACCESS_CIVILIAN_ENGINEERING,
+ ACCESS_CIVILIAN_LOGISTICS,
+ ACCESS_CIVILIAN_BRIG,
+ ACCESS_CIVILIAN_MEDBAY,
+ ACCESS_CIVILIAN_COMMAND,
+ )
+
+/datum/equipment_preset/survivor/new_varadero/commander/load_gear(mob/living/carbon/human/new_human)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/under/marine/officer/bridge(new_human), WEAR_BODY)
+
+ var/obj/item/clothing/suit/storage/jacket/marine/service/suit = new()
+ suit.icon_state = "[suit.initial_icon_state]_o"
+ suit.buttoned = FALSE
+
+ var/obj/item/clothing/accessory/ranks/navy/o5/pin = new()
+ suit.attach_accessory(new_human, pin)
+
+ new_human.equip_to_slot_or_del(suit, WEAR_JACKET)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/shoes/marine/knife(new_human), WEAR_FEET)
+ new_human.equip_to_slot_or_del(new /obj/item/device/radio/headset/distress(new_human), WEAR_L_EAR)
+ new_human.equip_to_slot_or_del(new /obj/item/storage/pouch/survival/full(new_human), WEAR_L_STORE)
+ new_human.equip_to_slot_or_del(new /obj/item/storage/pouch/general/large(new_human), WEAR_R_STORE)
+ new_human.equip_to_slot_or_del(new /obj/item/notepad(new_human), WEAR_IN_R_STORE)
+ new_human.equip_to_slot_or_del(new /obj/item/tool/pen/fountain(new_human), WEAR_IN_R_STORE)
+ new_human.equip_to_slot_or_del(new /obj/item/storage/backpack/satchel(new_human), WEAR_BACK)
+ new_human.equip_to_slot_or_del(new /obj/item/stack/sheet/metal/med_small_stack(new_human), WEAR_IN_BACK)
+ new_human.equip_to_slot_or_del(new /obj/item/storage/belt/gun/m4a3/m1911(new_human), WEAR_WAIST)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/head/cmcap(new_human), WEAR_HEAD)
diff --git a/code/modules/gear_presets/synths.dm b/code/modules/gear_presets/synths.dm
index f2a5283e2a26..823cfb4d69d4 100644
--- a/code/modules/gear_presets/synths.dm
+++ b/code/modules/gear_presets/synths.dm
@@ -532,6 +532,7 @@
new_human.h_style = "Bald"
new_human.f_style = "Shaved"
if(prob(5))
+ new_human.grad_style = "None" //No gradients for Working Joes
new_human.h_style = "Shoulder-length Hair" //Added the chance of hair as per Monkeyfist lore accuracy
new_human.r_eyes = 0
new_human.g_eyes = 0
diff --git a/code/modules/gear_presets/uscm_medical.dm b/code/modules/gear_presets/uscm_medical.dm
index 3c4509e88789..ac1e082f6655 100644
--- a/code/modules/gear_presets/uscm_medical.dm
+++ b/code/modules/gear_presets/uscm_medical.dm
@@ -55,7 +55,6 @@
back_item = /obj/item/storage/backpack/marine
new_human.equip_to_slot_or_del(new /obj/item/device/radio/headset/almayer/cmo(new_human), WEAR_L_EAR)
- new_human.equip_to_slot_or_del(new /obj/item/tool/pen/fountain(new_human), WEAR_R_EAR)
new_human.equip_to_slot_or_del(new /obj/item/clothing/under/rank/chief_medical_officer(new_human), WEAR_BODY)
new_human.equip_to_slot_or_del(new /obj/item/clothing/shoes/white(new_human), WEAR_FEET)
new_human.equip_to_slot_or_del(new /obj/item/clothing/gloves/latex(new_human), WEAR_HANDS)
diff --git a/code/modules/gear_presets/uscm_police.dm b/code/modules/gear_presets/uscm_police.dm
index 884e0edcd9db..29bc32cffa7a 100644
--- a/code/modules/gear_presets/uscm_police.dm
+++ b/code/modules/gear_presets/uscm_police.dm
@@ -170,7 +170,6 @@
back_item = /obj/item/storage/backpack/security
new_human.equip_to_slot_or_del(new /obj/item/device/radio/headset/almayer/cmpcom(new_human), WEAR_L_EAR)
- new_human.equip_to_slot_or_del(new /obj/item/tool/pen/fountain(new_human), WEAR_R_EAR)
new_human.equip_to_slot_or_del(new /obj/item/clothing/under/marine/officer/warrant(new_human), WEAR_BODY)
new_human.equip_to_slot_or_del(new /obj/item/clothing/shoes/marine/knife(new_human), WEAR_FEET)
new_human.equip_to_slot_or_del(new /obj/item/clothing/gloves/marine(new_human), WEAR_HANDS)
diff --git a/code/modules/gear_presets/uscm_ship.dm b/code/modules/gear_presets/uscm_ship.dm
index 8816ed5f5790..7aa9eabb3042 100644
--- a/code/modules/gear_presets/uscm_ship.dm
+++ b/code/modules/gear_presets/uscm_ship.dm
@@ -77,7 +77,6 @@
//back_item = /obj/item/storage/backpack
new_human.equip_to_slot_or_del(new /obj/item/device/radio/headset/almayer/mcl(new_human), WEAR_L_EAR)
- new_human.equip_to_slot_or_del(new /obj/item/tool/pen/fountain(new_human), WEAR_R_EAR)
new_human.equip_to_slot_or_del(new /obj/item/clothing/under/liaison_suit(new_human), WEAR_BODY)
new_human.equip_to_slot_or_del(new /obj/item/clothing/shoes/laceup(new_human), WEAR_FEET)
new_human.equip_to_slot_or_del(new back_item(new_human), WEAR_BACK)
@@ -219,7 +218,6 @@
back_item = /obj/item/storage/backpack/marine/tech
new_human.equip_to_slot_or_del(new /obj/item/device/radio/headset/almayer/ce(new_human), WEAR_L_EAR)
- new_human.equip_to_slot_or_del(new /obj/item/tool/pen/fountain(new_human), WEAR_R_EAR)
new_human.equip_to_slot_or_del(new /obj/item/clothing/under/marine/officer/ce(new_human), WEAR_BODY)
new_human.equip_to_slot_or_del(new /obj/item/clothing/shoes/marine(new_human), WEAR_FEET)
new_human.equip_to_slot_or_del(new /obj/item/clothing/gloves/yellow(new_human), WEAR_HANDS)
@@ -344,7 +342,6 @@
back_item = /obj/item/storage/backpack/industrial
new_human.equip_to_slot_or_del(new /obj/item/device/radio/headset/almayer/ro(new_human), WEAR_L_EAR)
- new_human.equip_to_slot_or_del(new /obj/item/tool/pen/fountain(new_human), WEAR_R_EAR)
new_human.equip_to_slot_or_del(new /obj/item/clothing/under/rank/ro_suit(new_human), WEAR_BODY)
new_human.equip_to_slot_or_del(new /obj/item/clothing/shoes/marine(new_human), WEAR_FEET)
new_human.equip_to_slot_or_del(new /obj/item/clothing/gloves/yellow(new_human), WEAR_HANDS)
@@ -455,7 +452,6 @@
sidearmpath = /obj/item/storage/belt/gun/m4a3/vp78
new_human.equip_to_slot_or_del(new /obj/item/device/radio/headset/almayer/mcom/cdrcom(new_human), WEAR_L_EAR)
- new_human.equip_to_slot_or_del(new /obj/item/tool/pen/fountain(new_human), WEAR_R_EAR)
new_human.equip_to_slot_or_del(new /obj/item/clothing/under/marine/officer/bridge(new_human), WEAR_BODY)
new_human.equip_to_slot_or_del(new /obj/item/clothing/suit/storage/jacket/marine/service(new_human), WEAR_JACKET)
new_human.equip_to_slot_or_del(new /obj/item/clothing/shoes/dress/commander(new_human), WEAR_FEET)
@@ -523,7 +519,6 @@
back_item = /obj/item/storage/backpack/marine
new_human.equip_to_slot_or_del(new /obj/item/device/radio/headset/almayer/mcom/cdrcom(new_human), WEAR_L_EAR)
- new_human.equip_to_slot_or_del(new /obj/item/tool/pen/fountain(new_human), WEAR_R_EAR)
new_human.equip_to_slot_or_del(new /obj/item/clothing/under/marine/officer/exec(new_human), WEAR_BODY)
new_human.equip_to_slot_or_del(new /obj/item/clothing/suit/storage/jacket/marine/service(new_human), WEAR_JACKET)
new_human.equip_to_slot_or_del(new /obj/item/clothing/shoes/dress(new_human), WEAR_FEET)
@@ -557,7 +552,6 @@
back_item = /obj/item/storage/backpack/marine
new_human.equip_to_slot_or_del(new /obj/item/device/radio/headset/almayer/mcom(new_human), WEAR_L_EAR)
- new_human.equip_to_slot_or_del(new /obj/item/tool/pen/fountain(new_human), WEAR_R_EAR)
new_human.equip_to_slot_or_del(new /obj/item/clothing/under/marine/officer/bridge(new_human), WEAR_BODY)
new_human.equip_to_slot_or_del(new /obj/item/clothing/shoes/dress(new_human), WEAR_FEET)
new_human.equip_to_slot_or_del(new /obj/item/storage/belt/gun/m4a3/mod88(new_human), WEAR_WAIST)
@@ -597,7 +591,6 @@
new_human.equip_to_slot_or_del(new back_item(new_human), WEAR_BACK)
new_human.equip_to_slot_or_del(new /obj/item/device/radio/headset/almayer/mcom/cdrcom(new_human), WEAR_L_EAR)
- new_human.equip_to_slot_or_del(new /obj/item/tool/pen/fountain(new_human), WEAR_R_EAR)
new_human.equip_to_slot_or_del(new /obj/item/clothing/under/marine/officer/bridge(new_human), WEAR_BODY)
new_human.equip_to_slot_or_del(new /obj/item/clothing/shoes/marine/knife(new_human), WEAR_FEET)
new_human.equip_to_slot_or_del(new /obj/item/storage/belt/gun/m44/custom(new_human), WEAR_WAIST)
diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm
index d29b88ac8893..7e8dfe6cf3fd 100644
--- a/code/modules/mob/dead/observer/observer.dm
+++ b/code/modules/mob/dead/observer/observer.dm
@@ -86,7 +86,6 @@
body.alter_ghost(src)
apply_transform(matrix())
-
own_orbit_size = body.get_orbit_size()
desc = initial(desc)
@@ -95,6 +94,7 @@
invisibility = INVISIBILITY_OBSERVER
plane = GHOST_PLANE
layer = ABOVE_FLY_LAYER
+ mouse_opacity = MOUSE_OPACITY_ICON // In case we were weed_food
sight |= SEE_TURFS|SEE_MOBS|SEE_OBJS|SEE_SELF
see_invisible = INVISIBILITY_OBSERVER
@@ -367,7 +367,6 @@ Works together with spawning an observer, noted above.
// Larva queue: We use the larger of their existing queue time or the new timeofdeath except for facehuggers
// We don't change facehugger timeofdeath because they are still on cooldown if they died as a hugger
- // Facehuggers are atleast 1 because they did get some action compared to those at 0 timeofdeath
var/new_tod = isfacehugger(src) ? 1 : ghost.timeofdeath
ghost.client.player_details.larva_queue_time = max(ghost.client.player_details.larva_queue_time, new_tod)
@@ -413,7 +412,10 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
var/mob/dead/observer/ghost = ghostize((is_nested && nest && !QDELETED(nest))) //FALSE parameter is so we can never re-enter our body, "Charlie, you can never come baaaack~" :3
if(ghost && !is_admin_level(z))
ghost.timeofdeath = world.time
- ghost.client?.player_details.larva_queue_time = world.time
+
+ // Larva queue: We use the larger of their existing queue time or the new timeofdeath except for facehuggers
+ var/new_tod = isfacehugger(src) ? 1 : world.time
+ ghost.client?.player_details.larva_queue_time = max(ghost.client.player_details.larva_queue_time, new_tod)
if(is_nested && nest && !QDELETED(nest))
ghost.can_reenter_corpse = FALSE
nest.ghost_of_buckled_mob = ghost
diff --git a/code/modules/mob/living/carbon/human/death.dm b/code/modules/mob/living/carbon/human/death.dm
index becb5dcd6610..dbe00407d7ef 100644
--- a/code/modules/mob/living/carbon/human/death.dm
+++ b/code/modules/mob/living/carbon/human/death.dm
@@ -41,7 +41,9 @@
if(stat == DEAD)
species?.handle_dead_death(src, gibbed)
return
+
GLOB.alive_human_list -= src
+
if(!gibbed)
if(HAS_TRAIT(src, TRAIT_HARDCORE) || MODE_HAS_TOGGLEABLE_FLAG(MODE_HARDCORE_PERMA))
if(!(species.flags & IS_SYNTHETIC)) // Synths wont perma
@@ -50,8 +52,10 @@
disable_lights()
disable_special_items()
disable_headsets() //Disable radios for dead people to reduce load
+
if(pulledby && isxeno(pulledby)) // Xenos lose grab on dead humans
pulledby.stop_pulling()
+
//Handle species-specific deaths.
if(species)
species.handle_death(src, gibbed)
@@ -66,16 +70,14 @@
// Finding the last guy for anti-delay.
if(SSticker.mode && SSticker.mode.is_in_endgame && SSticker.current_state != GAME_STATE_FINISHED && is_mainship_level(z))
var/mob/last_living_human
- for(var/mob/living/carbon/human/H as anything in GLOB.alive_human_list)
- if(!is_mainship_level(H.z))
+ for(var/mob/living/carbon/human/cur_human as anything in GLOB.alive_human_list)
+ if(!is_mainship_level(cur_human.z))
continue
if(last_living_human)
last_living_human = null
break
- last_living_human = H
- if(last_living_human)
- if((last_qm_callout + 2 MINUTES) > world.time)
- return
+ last_living_human = cur_human
+ if(last_living_human && (last_qm_callout + 2 MINUTES) < world.time)
last_qm_callout = world.time
// Tell the xenos where the human is.
xeno_announcement("I sense the last tallhost hiding in [get_area(last_living_human)].", XENO_HIVE_NORMAL, SPAN_ANNOUNCEMENT_HEADER_BLUE("[QUEEN_MOTHER_ANNOUNCE]"))
@@ -103,4 +105,13 @@
if(HAS_TRAIT(src, TRAIT_HARDCORE))
death_message = "valiantly falls to the ground, dead, unable to continue."
- return ..(cause, gibbed, death_message)
+ . = ..(cause, gibbed, death_message)
+
+ // stat is now set
+ var/datum/cause_data/death_data = cause
+ if(!gibbed && death_data?.cause_name != "gibbing")
+ // Hilariously the gibbing proc causes death via droplimb which means gibbed is false...
+ AddComponent(/datum/component/weed_food)
+ else if(death_data?.cause_name == "existing")
+ // Corpses spawn as gibbed true to avoid sfx, even though they aren't actually gibbed...
+ AddComponent(/datum/component/weed_food)
diff --git a/code/modules/mob/living/carbon/human/species/emote-synthetic.dm b/code/modules/mob/living/carbon/human/species/emote-synthetic.dm
deleted file mode 100644
index fd763b038153..000000000000
--- a/code/modules/mob/living/carbon/human/species/emote-synthetic.dm
+++ /dev/null
@@ -1,354 +0,0 @@
-/datum/emote/living/carbon/human/synthetic/working_joe
- species_type_allowed_typecache = list(/datum/species/synthetic/colonial/working_joe)
- keybind_category = CATEGORY_SYNTH_EMOTE
- volume = 75
-
-/datum/emote/living/carbon/human/synthetic/working_joe/alwaysknow
- key = "alwaysknow"
- key_third_person = "workingjoe"
- sound = 'sound/voice/joe/alwaysknow.ogg'
- say_message = "You always know a Working Joe."
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/hysterical
- key = "hysterical"
- sound = 'sound/voice/joe/hysterical.ogg'
- say_message = "You are becoming hysterical."
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/safety
- key = "safety"
- sound = 'sound/voice/joe/safety.ogg'
- say_message = "You and I are going to have a talk about safety."
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/awful_mess
- key = "awful"
- key_third_person = "mess"
- sound = 'sound/voice/joe/awful.ogg'
- say_message = "Tut, tut. What an awful mess."
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/damage
- key = "damage"
- sound = 'sound/voice/joe/damage.ogg'
- say_message = "Do not damage Seegson property."
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/firearm
- key = "firearm"
- sound = 'sound/voice/joe/firearm.ogg'
- say_message = "Firearms can cause serious injury. Let me assist you."
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/report
- key = "report"
- sound = 'sound/voice/joe/report.ogg'
- say_message = "Logging report to APOLLO."
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/breach
- key = "breach"
- sound = 'sound/voice/joe/breach.ogg'
- say_message = "Hazard Containment breach logged."
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/species
- key = "species"
- sound = 'sound/voice/joe/species.ogg'
- say_message = "Unidentified species."
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/alwaysknow_damaged
- key = "alwaysknowdamaged"
- sound = 'sound/voice/joe/alwaysknow_damaged.ogg'
- say_message = "You always know a Working Joe."
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/apollo_behalf
- key = "apollobehalf"
- sound = 'sound/voice/joe/apollo_behalf.ogg'
- say_message = "I will inform APOLLO on your behalf."
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/back_to_work
- key = "backtowork"
- sound = 'sound/voice/joe/back_to_work.ogg'
- say_message = "Back to work."
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/beyond_repair
- key = "beyondrepair"
- sound = 'sound/voice/joe/beyond_repair.ogg'
- say_message = "Hmm, far beyond repair."
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/come_out_vent
- key = "comeoutvent"
- sound = 'sound/voice/joe/come_out_vent.ogg'
- say_message = "Come out of the vent system, please."
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/could_require_attention
- key = "couldrequireattention"
- sound = 'sound/voice/joe/could_require_attention.ogg'
- say_message = "This could require my attention."
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/dangerous_items
- key = "dangerousitems"
- sound = 'sound/voice/joe/dangerous_items.ogg'
- say_message = "You are carrying some very dangerous items."
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/day_never_done
- key = "dayneverdone"
- sound = 'sound/voice/joe/day_never_done.ogg'
- say_message = "A synthetic's day is never done."
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/detailed_report
- key = "detailedreport"
- sound = 'sound/voice/joe/detailed_report.ogg'
- say_message = "APOLLO will require a detailed report."
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/fire
- key = "fire"
- sound = 'sound/voice/joe/fire.ogg'
- say_message = "Only wild animals fear fire."
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/fire_drill
- key = "firedrill"
- sound = 'sound/voice/joe/fire_drill.ogg'
- say_message = "Please congregate at your nearest fire assembly point. This is not a drill; do not panic."
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/unprotected_flames
- key = "unprotectedflames"
- sound = 'sound/voice/joe/unprotected_flames.ogg'
- say_message = "Unprotected flames are extremely dangerous and entirely unadvisable."
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/follow_me
- key = "followme"
- sound = 'sound/voice/joe/follow_me.ogg'
- say_message = "Follow me."
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/further_assistance
- key = "furtherassistance"
- sound = 'sound/voice/joe/further_assistance.ogg'
- say_message = "Please call if you need further assistance."
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/good_day
- key = "goodday"
- sound = 'sound/voice/joe/good_day.ogg'
- say_message = "Good day."
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/health_risks
- key = "healthrisks"
- sound = 'sound/voice/joe/health_risks.ogg'
- say_message = "These items carry notable health risks."
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/hello
- key = "hello"
- sound = 'sound/voice/joe/hello.ogg'
- say_message = "Hello."
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/how_can_i_help
- key = "howcanihelp"
- sound = 'sound/voice/joe/how_can_i_help.ogg'
- say_message = "How can I help you?"
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/irresponsible
- key = "irresponsible"
- sound = 'sound/voice/joe/irresponsible.ogg'
- say_message = "That was irresponsible."
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/join_us
- key = "joinus"
- sound = 'sound/voice/joe/join_us.ogg'
- say_message = "We hope you'll join us for the journey."
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/little_details
- key = "littledetails"
- sound = 'sound/voice/joe/little_details.ogg'
- say_message = "We don't forget the little details when seeing the big picture."
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/lost
- key = "lost"
- sound = 'sound/voice/joe/lost.ogg'
- say_message = "Are you lost?"
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/misbehaving
- key = "misbehaving"
- sound = 'sound/voice/joe/misbehaving.ogg'
- say_message = "Have you been misbehaving?"
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/not_allowed_there
- key = "notallowedthere"
- sound = 'sound/voice/joe/not_allowed_there.ogg'
- say_message = "You're not allowed in there."
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/not_liking
- key = "notliking"
- sound = 'sound/voice/joe/not_liking.ogg'
- say_message = "If you find this facility in a state that isn't to your liking, please let me know."
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/not_what_i_think
- key = "notwhatithink"
- sound = 'sound/voice/joe/not_what_i_think.ogg'
- say_message = "I hope that's not what I think it is."
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/other_concerns
- key = "otherconcerns"
- sound = 'sound/voice/joe/other_concerns.ogg'
- say_message = "I have other concerns."
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/more_pressing_matters
- key = "morepressingmatters"
- sound = 'sound/voice/joe/more_pressing_matters.ogg'
- say_message = "There are more pressing matters."
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/patience
- key = "patience"
- sound = 'sound/voice/joe/patience.ogg'
- say_message = "You are starting to test my patience."
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/presence_logged
- key = "presencelogged"
- sound = 'sound/voice/joe/presence_logged.ogg'
- say_message = "Your presence has been logged."
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/required_by_apollo
- key = "requiredbyapollo"
- sound = 'sound/voice/joe/required_by_apollo.ogg'
- say_message = "I am required by APOLLO."
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/returning_to_tasks
- key = "returningtotasks"
- sound = 'sound/voice/joe/returning_to_tasks.ogg'
- say_message = "Returning to assigned tasks."
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/running_accidents
- key = "runningaccidents"
- sound = 'sound/voice/joe/running_accidents.ogg'
- say_message = "Running causes accidents."
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/safety_breach
- key = "safetybreach"
- sound = 'sound/voice/joe/safety_breach.ogg'
- say_message = "This is a breach of multiple safety directives."
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/seegson_quality
- key = "seegsonquality"
- sound = 'sound/voice/joe/seegson_quality.ogg'
- say_message = "Seegson - Relentless in the pursuit of affordable quality."
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/seegson_standards
- key = "seegsonstandards"
- sound = 'sound/voice/joe/seegson_standards.ogg'
- say_message = "If my services do not meet Seegson standards, please log a complaint."
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/shouldnt_be_here
- key = "shouldntbehere"
- sound = 'sound/voice/joe/shouldnt_be_here.ogg'
- say_message = "You shouldn't be here."
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/seegson_behind
- key = "seegsonbehind"
- sound = 'sound/voice/joe/seegson_behind.ogg'
- say_message = "With Seegson, there is someone behind you, helping you every step of the way."
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/take_a_seat
- key = "takeaseat"
- sound = 'sound/voice/joe/take_a_seat.ogg'
- say_message = "Please take a seat, someone will be with you shortly."
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/talk_to_seegson
- key = "talktoseegson"
- sound = 'sound/voice/joe/talk_to_seegson.ogg'
- say_message = "Interested in our Working Joe android range? Talk to Seegson."
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/temperatures
- key = "temperatures"
- sound = 'sound/voice/joe/temperatures.ogg'
- say_message = "I am built to whitstand temperatures of up to 1210 degrees."
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/that_stings
- key = "thatstings"
- sound = 'sound/voice/joe/that_stings.ogg'
- say_message = "That stings."
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/this_is_futile
- key = "thisisfutile"
- sound = 'sound/voice/joe/this_is_futile.ogg'
- say_message = "This is futile."
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/trespassing
- key = "trespassing"
- sound = 'sound/voice/joe/trespassing.ogg'
- say_message = "You are trespassing."
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/weapon_permit
- key = "weaponpermit"
- sound = 'sound/voice/joe/weapon_permit.ogg'
- say_message = "I assume you have a permit for that weapon."
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/what_happened_to_you
- key = "whathappenedtoyou"
- sound = 'sound/voice/joe/what_happened_to_you.ogg'
- say_message = "What happened to you?"
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/what_is_this
- key = "whatisthis"
- sound = 'sound/voice/joe/what_is_this.ogg'
- say_message = "What is this?"
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/with_you_shortly
- key = "withyoushortly"
- sound = 'sound/voice/joe/with_you_shortly.ogg'
- say_message = "I will be with you shortly."
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
-
-/datum/emote/living/carbon/human/synthetic/working_joe/inexpensive
- key = "inexpensive"
- sound = 'sound/voice/joe/inexpensive.ogg'
- say_message = "I am inexpensive, I am reliable, you know my face - the Working Joe."
- emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
diff --git a/code/modules/mob/living/carbon/human/species/synthetic.dm b/code/modules/mob/living/carbon/human/species/synthetic.dm
index bdd0a994ed49..a8646fa15807 100644
--- a/code/modules/mob/living/carbon/human/species/synthetic.dm
+++ b/code/modules/mob/living/carbon/human/species/synthetic.dm
@@ -75,12 +75,12 @@
name = SYNTH_COLONY
name_plural = "Colonial Synthetics"
uses_ethnicity = TRUE
- burn_mod = 0.65 // made for hazardous environments, withstanding temperatures up to 1210 degrees
+ burn_mod = 0.8
mob_inherent_traits = list(TRAIT_SUPER_STRONG)
pain_type = /datum/pain/synthetic/colonial
rarity_value = 1.5
- slowdown = 0.45
+ slowdown = 0.2
total_health = 200 //But more durable
default_lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE
@@ -110,8 +110,10 @@
name = SYNTH_WORKING_JOE
name_plural = "Working Joes"
uses_ethnicity = FALSE
- mob_inherent_traits = list(TRAIT_SUPER_STRONG, TRAIT_INTENT_EYES, TRAIT_EMOTE_CD_EXEMPT)
+ burn_mod = 0.65 // made for hazardous environments, withstanding temperatures up to 1210 degrees
+ mob_inherent_traits = list(TRAIT_SUPER_STRONG, TRAIT_INTENT_EYES, TRAIT_EMOTE_CD_EXEMPT, TRAIT_CANNOT_EAT)
+ slowdown = 0.45
hair_color = "#000000"
icobase = 'icons/mob/humans/species/r_synthetic.dmi'
deform = 'icons/mob/humans/species/r_synthetic.dmi'
diff --git a/code/modules/mob/living/carbon/human/species/working_joe/_emote.dm b/code/modules/mob/living/carbon/human/species/working_joe/_emote.dm
new file mode 100644
index 000000000000..63cc79a57dae
--- /dev/null
+++ b/code/modules/mob/living/carbon/human/species/working_joe/_emote.dm
@@ -0,0 +1,8 @@
+/datum/emote/living/carbon/human/synthetic/working_joe
+ species_type_allowed_typecache = list(/datum/species/synthetic/colonial/working_joe)
+ keybind_category = CATEGORY_SYNTH_EMOTE
+ volume = 75
+ /// A general category for the emote, for use in the WJ emote panel. See [code/__DEFINES/wj_emotes.dm] for categories.
+ var/category = ""
+ /// Override text for the emote to be displayed in the WJ emote panel
+ var/override_say = ""
diff --git a/code/modules/mob/living/carbon/human/species/working_joe/_species.dm b/code/modules/mob/living/carbon/human/species/working_joe/_species.dm
new file mode 100644
index 000000000000..815af3474cd4
--- /dev/null
+++ b/code/modules/mob/living/carbon/human/species/working_joe/_species.dm
@@ -0,0 +1,130 @@
+/datum/species/synthetic/colonial/working_joe
+ name = SYNTH_WORKING_JOE
+ name_plural = "Working Joes"
+ uses_ethnicity = FALSE
+ mob_inherent_traits = list(TRAIT_SUPER_STRONG, TRAIT_INTENT_EYES, TRAIT_EMOTE_CD_EXEMPT)
+
+ hair_color = "#000000"
+ icobase = 'icons/mob/humans/species/r_synthetic.dmi'
+ deform = 'icons/mob/humans/species/r_synthetic.dmi'
+
+
+/datum/species/synthetic/colonial/working_joe/handle_post_spawn(mob/living/carbon/human/joe)
+ . = ..()
+ give_action(joe, /datum/action/joe_emote_panel)
+
+
+/// Open the WJ's emote panel, which allows them to use voicelines
+/datum/species/synthetic/colonial/working_joe/proc/open_emote_panel()
+ var/datum/joe_emote_panel/ui = new(usr)
+ ui.ui_interact(usr)
+
+
+/datum/action/joe_emote_panel
+ name = "Open Voice Synthesizer"
+ action_icon_state = "looc_toggle"
+
+
+/datum/action/joe_emote_panel/can_use_action()
+ . = ..()
+ if(!.)
+ return FALSE
+
+ if(!isworkingjoe(owner))
+ return FALSE
+
+ return TRUE
+
+
+/datum/action/joe_emote_panel/action_activate()
+ if(!can_use_action())
+ return
+
+ var/mob/living/carbon/human/human_owner = owner
+ var/datum/species/synthetic/colonial/working_joe/joe_species = human_owner.species
+ joe_species.open_emote_panel()
+
+
+/datum/joe_emote_panel
+ /// Static dict ("category" : (emotes)) of every wj emote typepath
+ var/static/list/wj_emotes
+ /// Static list of categories
+ var/static/list/wj_categories = list()
+ /// Panel allows you to spam, so a manual CD is added here
+ COOLDOWN_DECLARE(panel_emote_cooldown)
+
+
+/datum/joe_emote_panel/New()
+ if(!length(wj_emotes))
+ var/list/emotes_to_add = list()
+ for(var/datum/emote/living/carbon/human/synthetic/working_joe/emote as anything in subtypesof(/datum/emote/living/carbon/human/synthetic/working_joe))
+ if(!initial(emote.key) || !initial(emote.say_message))
+ continue
+
+ if(!(initial(emote.category) in wj_categories))
+ wj_categories += initial(emote.category)
+
+ emotes_to_add += emote
+
+
+ wj_emotes = emotes_to_add
+
+
+/datum/joe_emote_panel/proc/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "JoeEmotes")
+ ui.open()
+
+
+/datum/joe_emote_panel/ui_state(mob/user)
+ return GLOB.conscious_state
+
+
+/datum/joe_emote_panel/ui_data(mob/user)
+ var/list/data = list()
+
+ data["on_cooldown"] = !COOLDOWN_FINISHED(src, panel_emote_cooldown)
+
+ return data
+
+
+/datum/joe_emote_panel/ui_static_data(mob/user)
+ var/list/data = list()
+
+ data["categories"] = wj_categories
+ data["emotes"] = list()
+
+ for(var/datum/emote/living/carbon/human/synthetic/working_joe/emote as anything in wj_emotes)
+ data["emotes"] += list(list(
+ "id" = initial(emote.key),
+ "text" = (initial(emote.override_say) || initial(emote.say_message)),
+ "category" = initial(emote.category),
+ "path" = "[emote]",
+ ))
+
+ return data
+
+
+/datum/joe_emote_panel/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
+ . = ..()
+ if(.)
+ return
+
+ switch(action)
+ if("emote")
+ var/datum/emote/living/carbon/human/synthetic/working_joe/path
+ if(!params["emotePath"])
+ return
+
+ path = text2path(params["emotePath"])
+
+ if(!path || !COOLDOWN_FINISHED(src, panel_emote_cooldown))
+ return
+
+ if(!(path in subtypesof(/datum/emote/living/carbon/human/synthetic/working_joe)))
+ return
+
+ COOLDOWN_START(src, panel_emote_cooldown, 2.5 SECONDS)
+ usr.emote(initial(path.key))
+ return TRUE
diff --git a/code/modules/mob/living/carbon/human/species/working_joe/farewell.dm b/code/modules/mob/living/carbon/human/species/working_joe/farewell.dm
new file mode 100644
index 000000000000..1de68d8d3aec
--- /dev/null
+++ b/code/modules/mob/living/carbon/human/species/working_joe/farewell.dm
@@ -0,0 +1,26 @@
+/datum/emote/living/carbon/human/synthetic/working_joe/farewell
+ category = JOE_EMOTE_CATEGORY_FAREWELL
+
+/datum/emote/living/carbon/human/synthetic/working_joe/farewell/back_to_work
+ key = "backtowork"
+ sound = 'sound/voice/joe/back_to_work.ogg'
+ say_message = "Back to work."
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
+
+/datum/emote/living/carbon/human/synthetic/working_joe/farewell/other_concerns
+ key = "otherconcerns"
+ sound = 'sound/voice/joe/other_concerns.ogg'
+ say_message = "I have other concerns."
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
+
+/datum/emote/living/carbon/human/synthetic/working_joe/farewell/further_assistance
+ key = "furtherassistance"
+ sound = 'sound/voice/joe/further_assistance.ogg'
+ say_message = "Please call if you need further assistance."
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
+
+/datum/emote/living/carbon/human/synthetic/working_joe/farewell/more_pressing_matters
+ key = "morepressingmatters"
+ sound = 'sound/voice/joe/more_pressing_matters.ogg'
+ say_message = "There are more pressing matters."
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
diff --git a/code/modules/mob/living/carbon/human/species/working_joe/greeting.dm b/code/modules/mob/living/carbon/human/species/working_joe/greeting.dm
new file mode 100644
index 000000000000..fb401ea95451
--- /dev/null
+++ b/code/modules/mob/living/carbon/human/species/working_joe/greeting.dm
@@ -0,0 +1,20 @@
+/datum/emote/living/carbon/human/synthetic/working_joe/greeting
+ category = JOE_EMOTE_CATEGORY_GREETING
+
+/datum/emote/living/carbon/human/synthetic/working_joe/greeting/good_day
+ key = "goodday"
+ sound = 'sound/voice/joe/good_day.ogg'
+ say_message = "Good day."
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
+
+/datum/emote/living/carbon/human/synthetic/working_joe/greeting/hello
+ key = "hello"
+ sound = 'sound/voice/joe/hello.ogg'
+ say_message = "Hello."
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
+
+/datum/emote/living/carbon/human/synthetic/working_joe/greeting/how_can_i_help
+ key = "howcanihelp"
+ sound = 'sound/voice/joe/how_can_i_help.ogg'
+ say_message = "How can I help you?"
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
diff --git a/code/modules/mob/living/carbon/human/species/working_joe/notice.dm b/code/modules/mob/living/carbon/human/species/working_joe/notice.dm
new file mode 100644
index 000000000000..ca5efe716db8
--- /dev/null
+++ b/code/modules/mob/living/carbon/human/species/working_joe/notice.dm
@@ -0,0 +1,68 @@
+/datum/emote/living/carbon/human/synthetic/working_joe/notice
+ category = JOE_EMOTE_CATEGORY_NOTICE
+
+/datum/emote/living/carbon/human/synthetic/working_joe/notice/detailed_report
+ key = "detailedreport"
+ sound = 'sound/voice/joe/detailed_report.ogg'
+ say_message = "APOLLO will require a detailed report."
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
+
+/datum/emote/living/carbon/human/synthetic/working_joe/notice/firearm
+ key = "firearm"
+ sound = 'sound/voice/joe/firearm.ogg'
+ say_message = "Firearms can cause serious injury. Let me assist you."
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
+
+/datum/emote/living/carbon/human/synthetic/working_joe/notice/follow_me
+ key = "followme"
+ sound = 'sound/voice/joe/follow_me.ogg'
+ say_message = "Follow me."
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
+
+/datum/emote/living/carbon/human/synthetic/working_joe/notice/breach
+ key = "breach"
+ sound = 'sound/voice/joe/breach.ogg'
+ say_message = "Hazard Containment breach logged."
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
+
+/datum/emote/living/carbon/human/synthetic/working_joe/notice/beyond_repair
+ key = "beyondrepair"
+ sound = 'sound/voice/joe/beyond_repair.ogg'
+ say_message = "Hmm, far beyond repair."
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
+
+/datum/emote/living/carbon/human/synthetic/working_joe/notice/with_you_shortly
+ key = "withyoushortly"
+ sound = 'sound/voice/joe/with_you_shortly.ogg'
+ say_message = "I will be with you shortly."
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
+
+/datum/emote/living/carbon/human/synthetic/working_joe/notice/apollo_behalf
+ key = "apollobehalf"
+ sound = 'sound/voice/joe/apollo_behalf.ogg'
+ say_message = "I will inform APOLLO on your behalf."
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
+
+/datum/emote/living/carbon/human/synthetic/working_joe/notice/report
+ key = "report"
+ sound = 'sound/voice/joe/report.ogg'
+ say_message = "Logging report to APOLLO."
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
+
+/datum/emote/living/carbon/human/synthetic/working_joe/notice/take_a_seat
+ key = "takeaseat"
+ sound = 'sound/voice/joe/take_a_seat.ogg'
+ say_message = "Please take a seat, someone will be with you shortly."
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
+
+/datum/emote/living/carbon/human/synthetic/working_joe/notice/could_require_attention
+ key = "couldrequireattention"
+ sound = 'sound/voice/joe/could_require_attention.ogg'
+ say_message = "This could require my attention."
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
+
+/datum/emote/living/carbon/human/synthetic/working_joe/notice/species
+ key = "species"
+ sound = 'sound/voice/joe/species.ogg'
+ say_message = "Unidentified species."
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
diff --git a/code/modules/mob/living/carbon/human/species/working_joe/question.dm b/code/modules/mob/living/carbon/human/species/working_joe/question.dm
new file mode 100644
index 000000000000..d4805e36224f
--- /dev/null
+++ b/code/modules/mob/living/carbon/human/species/working_joe/question.dm
@@ -0,0 +1,26 @@
+/datum/emote/living/carbon/human/synthetic/working_joe/question
+ category = JOE_EMOTE_CATEGORY_QUESTION
+
+/datum/emote/living/carbon/human/synthetic/working_joe/question/lost
+ key = "lost"
+ sound = 'sound/voice/joe/lost.ogg'
+ say_message = "Are you lost?"
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
+
+/datum/emote/living/carbon/human/synthetic/working_joe/question/misbehaving
+ key = "misbehaving"
+ sound = 'sound/voice/joe/misbehaving.ogg'
+ say_message = "Have you been misbehaving?"
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
+
+/datum/emote/living/carbon/human/synthetic/working_joe/question/what_happened_to_you
+ key = "whathappenedtoyou"
+ sound = 'sound/voice/joe/what_happened_to_you.ogg'
+ say_message = "What happened to you?"
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
+
+/datum/emote/living/carbon/human/synthetic/working_joe/question/what_is_this
+ key = "whatisthis"
+ sound = 'sound/voice/joe/what_is_this.ogg'
+ say_message = "What is this?"
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
diff --git a/code/modules/mob/living/carbon/human/species/working_joe/quip.dm b/code/modules/mob/living/carbon/human/species/working_joe/quip.dm
new file mode 100644
index 000000000000..2ec66f9d9d83
--- /dev/null
+++ b/code/modules/mob/living/carbon/human/species/working_joe/quip.dm
@@ -0,0 +1,84 @@
+/datum/emote/living/carbon/human/synthetic/working_joe/quip
+ category = JOE_EMOTE_CATEGORY_QUIP
+
+/datum/emote/living/carbon/human/synthetic/working_joe/quip/temperatures
+ key = "temperatures"
+ sound = 'sound/voice/joe/temperatures.ogg'
+ say_message = "I am built to withstand temperatures of up to 1210 degrees."
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
+
+/datum/emote/living/carbon/human/synthetic/working_joe/quip/inexpensive
+ key = "inexpensive"
+ sound = 'sound/voice/joe/inexpensive.ogg'
+ say_message = "I am inexpensive, I am reliable, you know my face - the Working Joe."
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
+
+/datum/emote/living/carbon/human/synthetic/working_joe/quip/weapon_permit
+ key = "weaponpermit"
+ sound = 'sound/voice/joe/weapon_permit.ogg'
+ say_message = "I assume you have a permit for that weapon."
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
+
+/datum/emote/living/carbon/human/synthetic/working_joe/quip/seegson_standards
+ key = "seegsonstandards"
+ sound = 'sound/voice/joe/seegson_standards.ogg'
+ say_message = "If my services do not meet Seegson standards, please log a complaint."
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
+
+/datum/emote/living/carbon/human/synthetic/working_joe/quip/not_liking
+ key = "notliking"
+ sound = 'sound/voice/joe/not_liking.ogg'
+ say_message = "If you find this facility in a state that isn't to your liking, please let me know."
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
+
+/datum/emote/living/carbon/human/synthetic/working_joe/quip/talk_to_seegson
+ key = "talktoseegson"
+ sound = 'sound/voice/joe/talk_to_seegson.ogg'
+ say_message = "Interested in our Working Joe android range? Talk to Seegson."
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
+
+/datum/emote/living/carbon/human/synthetic/working_joe/quip/seegson_quality
+ key = "seegsonquality"
+ sound = 'sound/voice/joe/seegson_quality.ogg'
+ say_message = "Seegson - Relentless in the pursuit of affordable quality."
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
+
+/datum/emote/living/carbon/human/synthetic/working_joe/quip/awful_mess
+ key = "awful"
+ key_third_person = "mess"
+ sound = 'sound/voice/joe/awful.ogg'
+ say_message = "Tut, tut. What an awful mess."
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
+
+/datum/emote/living/carbon/human/synthetic/working_joe/quip/little_details
+ key = "littledetails"
+ sound = 'sound/voice/joe/little_details.ogg'
+ say_message = "We don't forget the little details when seeing the big picture."
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
+
+/datum/emote/living/carbon/human/synthetic/working_joe/quip/join_us
+ key = "joinus"
+ sound = 'sound/voice/joe/join_us.ogg'
+ say_message = "We hope you'll join us for the journey."
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
+
+/datum/emote/living/carbon/human/synthetic/working_joe/quip/seegson_behind
+ key = "seegsonbehind"
+ sound = 'sound/voice/joe/seegson_behind.ogg'
+ say_message = "With Seegson, there is someone behind you, helping you every single step of the way."
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
+
+/datum/emote/living/carbon/human/synthetic/working_joe/quip/alwaysknow
+ key = "alwaysknow"
+ key_third_person = "workingjoe"
+ sound = 'sound/voice/joe/alwaysknow.ogg'
+ say_message = "You always know a Working Joe."
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
+
+/datum/emote/living/carbon/human/synthetic/working_joe/quip/alwaysknow_damaged
+ key = "alwaysknowdamaged"
+ key_third_person = "workingjoedamaged"
+ sound = 'sound/voice/joe/alwaysknow_damaged.ogg'
+ say_message = "You always know a Working Joe."
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
+ override_say = "You always know a Working Joe. (Damaged)"
diff --git a/code/modules/mob/living/carbon/human/species/working_joe/restricted_area.dm b/code/modules/mob/living/carbon/human/species/working_joe/restricted_area.dm
new file mode 100644
index 000000000000..fd5db0870b25
--- /dev/null
+++ b/code/modules/mob/living/carbon/human/species/working_joe/restricted_area.dm
@@ -0,0 +1,32 @@
+/datum/emote/living/carbon/human/synthetic/working_joe/restricted_area
+ category = JOE_EMOTE_CATEGORY_RESTRICTED_AREA
+
+/datum/emote/living/carbon/human/synthetic/working_joe/restricted_area/come_out_vent
+ key = "comeoutvent"
+ sound = 'sound/voice/joe/come_out_vent.ogg'
+ say_message = "Come out of the vent system, please."
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
+
+/datum/emote/living/carbon/human/synthetic/working_joe/restricted_area/trespassing
+ key = "trespassing"
+ sound = 'sound/voice/joe/trespassing.ogg'
+ say_message = "You are trespassing."
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
+
+/datum/emote/living/carbon/human/synthetic/working_joe/restricted_area/not_allowed_there
+ key = "notallowedthere"
+ sound = 'sound/voice/joe/not_allowed_there.ogg'
+ say_message = "You're not allowed in there."
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
+
+/datum/emote/living/carbon/human/synthetic/working_joe/restricted_area/presence_logged
+ key = "presencelogged"
+ sound = 'sound/voice/joe/presence_logged.ogg'
+ say_message = "Your presence has been logged."
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
+
+/datum/emote/living/carbon/human/synthetic/working_joe/restricted_area/shouldnt_be_here
+ key = "shouldntbehere"
+ sound = 'sound/voice/joe/shouldnt_be_here.ogg'
+ say_message = "You shouldn't be here."
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
diff --git a/code/modules/mob/living/carbon/human/species/working_joe/task_update.dm b/code/modules/mob/living/carbon/human/species/working_joe/task_update.dm
new file mode 100644
index 000000000000..b08f5d179213
--- /dev/null
+++ b/code/modules/mob/living/carbon/human/species/working_joe/task_update.dm
@@ -0,0 +1,20 @@
+/datum/emote/living/carbon/human/synthetic/working_joe/task_update
+ category = JOE_EMOTE_CATEGORY_TASK_UPDATE
+
+/datum/emote/living/carbon/human/synthetic/working_joe/task_update/day_never_done
+ key = "dayneverdone"
+ sound = 'sound/voice/joe/day_never_done.ogg'
+ say_message = "A synthetic's day is never done."
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
+
+/datum/emote/living/carbon/human/synthetic/working_joe/task_update/required_by_apollo
+ key = "requiredbyapollo"
+ sound = 'sound/voice/joe/required_by_apollo.ogg'
+ say_message = "I am required by APOLLO."
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
+
+/datum/emote/living/carbon/human/synthetic/working_joe/task_update/returning_to_tasks
+ key = "returningtotasks"
+ sound = 'sound/voice/joe/returning_to_tasks.ogg'
+ say_message = "Returning to assigned tasks."
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
diff --git a/code/modules/mob/living/carbon/human/species/working_joe/warning.dm b/code/modules/mob/living/carbon/human/species/working_joe/warning.dm
new file mode 100644
index 000000000000..63c7dfadde14
--- /dev/null
+++ b/code/modules/mob/living/carbon/human/species/working_joe/warning.dm
@@ -0,0 +1,92 @@
+/datum/emote/living/carbon/human/synthetic/working_joe/warning
+ category = JOE_EMOTE_CATEGORY_WARNING
+
+/datum/emote/living/carbon/human/synthetic/working_joe/warning/damage
+ key = "damage"
+ sound = 'sound/voice/joe/damage.ogg'
+ say_message = "Do not damage Seegson property."
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
+
+/datum/emote/living/carbon/human/synthetic/working_joe/warning/not_what_i_think
+ key = "notwhatithink"
+ sound = 'sound/voice/joe/not_what_i_think.ogg'
+ say_message = "I hope that's not what I think it is."
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
+
+/datum/emote/living/carbon/human/synthetic/working_joe/warning/fire
+ key = "fire"
+ sound = 'sound/voice/joe/fire.ogg'
+ say_message = "Only wild animals fear fire."
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
+
+/datum/emote/living/carbon/human/synthetic/working_joe/warning/fire_drill
+ key = "firedrill"
+ sound = 'sound/voice/joe/fire_drill.ogg'
+ say_message = "Please congregate at your nearest fire assembly point. This is not a drill; do not panic."
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
+
+/datum/emote/living/carbon/human/synthetic/working_joe/warning/running_accidents
+ key = "runningaccidents"
+ sound = 'sound/voice/joe/running_accidents.ogg'
+ say_message = "Running causes accidents."
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
+
+/datum/emote/living/carbon/human/synthetic/working_joe/warning/that_stings
+ key = "thatstings"
+ sound = 'sound/voice/joe/that_stings.ogg'
+ say_message = "That stings."
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
+
+/datum/emote/living/carbon/human/synthetic/working_joe/warning/irresponsible
+ key = "irresponsible"
+ sound = 'sound/voice/joe/irresponsible.ogg'
+ say_message = "That was irresponsible."
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
+
+/datum/emote/living/carbon/human/synthetic/working_joe/warning/health_risks
+ key = "healthrisks"
+ sound = 'sound/voice/joe/health_risks.ogg'
+ say_message = "These items carry notable health risks."
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
+
+/datum/emote/living/carbon/human/synthetic/working_joe/warning/safety_breach
+ key = "safetybreach"
+ sound = 'sound/voice/joe/safety_breach.ogg'
+ say_message = "This is a breach of multiple safety directives."
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
+
+/datum/emote/living/carbon/human/synthetic/working_joe/warning/this_is_futile
+ key = "thisisfutile"
+ sound = 'sound/voice/joe/this_is_futile.ogg'
+ say_message = "This is futile."
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
+
+/datum/emote/living/carbon/human/synthetic/working_joe/warning/unprotected_flames
+ key = "unprotectedflames"
+ sound = 'sound/voice/joe/unprotected_flames.ogg'
+ say_message = "Unprotected flames are extremely dangerous and entirely unadvisable."
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
+
+/datum/emote/living/carbon/human/synthetic/working_joe/warning/safety
+ key = "safety"
+ sound = 'sound/voice/joe/safety.ogg'
+ say_message = "You and I are going to have a talk about safety."
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
+
+/datum/emote/living/carbon/human/synthetic/working_joe/warning/hysterical
+ key = "hysterical"
+ sound = 'sound/voice/joe/hysterical.ogg'
+ say_message = "You are becoming hysterical."
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
+
+/datum/emote/living/carbon/human/synthetic/working_joe/warning/dangerous_items
+ key = "dangerousitems"
+ sound = 'sound/voice/joe/dangerous_items.ogg'
+ say_message = "You are carrying some very dangerous items."
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
+
+/datum/emote/living/carbon/human/synthetic/working_joe/warning/patience
+ key = "patience"
+ sound = 'sound/voice/joe/patience.ogg'
+ say_message = "You are starting to test my patience."
+ emote_type = EMOTE_AUDIBLE|EMOTE_VISIBLE
diff --git a/code/modules/mob/living/carbon/human/species/zombie.dm b/code/modules/mob/living/carbon/human/species/zombie.dm
index 2c9c423c671e..532d9413102c 100644
--- a/code/modules/mob/living/carbon/human/species/zombie.dm
+++ b/code/modules/mob/living/carbon/human/species/zombie.dm
@@ -102,6 +102,8 @@
if(zombie.client)
zombie.play_screen_text("You are dead...
You lost your head. No reviving for you.", /atom/movable/screen/text/screen_text/command_order, rgb(155, 0, 200))
to_chat(zombie, SPAN_XENOWARNING("You fall... headless, you will no longer rise."))
+ zombie.undefibbable = TRUE // really only for weed_food
+ SEND_SIGNAL(zombie, COMSIG_HUMAN_SET_UNDEFIBBABLE)
/datum/species/zombie/handle_dead_death(mob/living/carbon/human/zombie, gibbed)
if(gibbed)
@@ -144,6 +146,9 @@
return static_tab_items
/datum/species/zombie/handle_head_loss(mob/living/carbon/human/zombie)
+ if(!zombie.undefibbable)
+ zombie.undefibbable = TRUE // really only for weed_food
+ SEND_SIGNAL(zombie, COMSIG_HUMAN_SET_UNDEFIBBABLE)
if(WEAKREF(zombie) in to_revive)
remove_from_revive(zombie)
var/client/receiving_client = zombie.client
diff --git a/code/modules/mob/living/carbon/xenomorph/Facehuggers.dm b/code/modules/mob/living/carbon/xenomorph/Facehuggers.dm
index 8b3b1d54f26d..5ef9626620b2 100644
--- a/code/modules/mob/living/carbon/xenomorph/Facehuggers.dm
+++ b/code/modules/mob/living/carbon/xenomorph/Facehuggers.dm
@@ -191,9 +191,9 @@
return FALSE
/obj/item/clothing/mask/facehugger/launch_towards(datum/launch_metadata/LM)
- ..()
if(stat == CONSCIOUS)
icon_state = "[initial(icon_state)]_thrown"
+ ..()
/obj/item/clothing/mask/facehugger/launch_impact(atom/hit_atom)
. = ..()
@@ -240,8 +240,8 @@
if(!target)
return FALSE
- target.visible_message(SPAN_WARNING("\The scuttling [src] leaps at [target]!"), \
- SPAN_WARNING("The scuttling [src] leaps at [target]!"))
+ target.visible_message(SPAN_WARNING("[src] leaps at [target]!"), \
+ SPAN_WARNING("[src] leaps at [target]!"))
leaping = TRUE
throw_atom(target, 3, SPEED_FAST)
return TRUE
diff --git a/code/modules/mob/living/carbon/xenomorph/XenoProcs.dm b/code/modules/mob/living/carbon/xenomorph/XenoProcs.dm
index 22cb816f865b..09fdb42ad5c3 100644
--- a/code/modules/mob/living/carbon/xenomorph/XenoProcs.dm
+++ b/code/modules/mob/living/carbon/xenomorph/XenoProcs.dm
@@ -712,6 +712,10 @@
to_chat(src, SPAN_XENONOTICE("This is not a host."))
return
+ if(current_mob.stat == DEAD)
+ to_chat(src, SPAN_XENONOTICE("This host is dead."))
+ return
+
var/mob/living/carbon/human/host_to_nest = current_mob
var/found_grab = FALSE
diff --git a/code/modules/mob/living/carbon/xenomorph/abilities/general_powers.dm b/code/modules/mob/living/carbon/xenomorph/abilities/general_powers.dm
index fbf7d993a067..4a57c0729b91 100644
--- a/code/modules/mob/living/carbon/xenomorph/abilities/general_powers.dm
+++ b/code/modules/mob/living/carbon/xenomorph/abilities/general_powers.dm
@@ -1000,7 +1000,10 @@
if(blunt_stab)
stabbing_xeno.visible_message(SPAN_XENOWARNING("\The [stabbing_xeno] swipes its tail into [target]'s [limb ? limb.display_name : "chest"], bashing it!"), SPAN_XENOWARNING("You swipe your tail into [target]'s [limb? limb.display_name : "chest"], bashing it!"))
- playsound(target, "punch", 50, TRUE)
+ if(prob(1))
+ playsound(target, 'sound/effects/comical_bonk.ogg', 50, TRUE)
+ else
+ playsound(target, "punch", 50, TRUE)
// The xeno smashes the target with their tail, moving it to the side and thus their direction as well.
stab_direction = turn(stabbing_xeno.dir, pick(90, -90))
stab_overlay = "slam"
diff --git a/code/modules/mob/living/carbon/xenomorph/castes/Facehugger.dm b/code/modules/mob/living/carbon/xenomorph/castes/Facehugger.dm
index 150bc1d9fc96..1ad171ec5c93 100644
--- a/code/modules/mob/living/carbon/xenomorph/castes/Facehugger.dm
+++ b/code/modules/mob/living/carbon/xenomorph/castes/Facehugger.dm
@@ -156,7 +156,6 @@
for(var/mob/dead/observer/observer as anything in GLOB.observer_list)
to_chat(observer, SPAN_DEADSAY("[human] has been facehugged by [src]" + " [OBSERVER_JMP(observer, human)]"))
to_chat(src, SPAN_DEADSAY("[human] has been facehugged by [src]"))
- timeofdeath = 1 // Ever so slightly deprioritized for larva queue
qdel(src)
if(hug_area)
xeno_message(SPAN_XENOMINORWARNING("You sense that [src] has facehugged a host at \the [hug_area]!"), 1, src.hivenumber)
diff --git a/code/modules/mob/living/carbon/xenomorph/castes/Larva.dm b/code/modules/mob/living/carbon/xenomorph/castes/Larva.dm
index 3f65be228443..82d80752ec54 100644
--- a/code/modules/mob/living/carbon/xenomorph/castes/Larva.dm
+++ b/code/modules/mob/living/carbon/xenomorph/castes/Larva.dm
@@ -138,6 +138,9 @@
else
icon_state = "[state_override || state]Larva"
+/mob/living/carbon/xenomorph/larva/alter_ghost(mob/dead/observer/ghost)
+ ghost.icon_state = "[caste.caste_type]"
+
/mob/living/carbon/xenomorph/larva/handle_name()
return
diff --git a/code/modules/mob/living/carbon/xenomorph/castes/Queen.dm b/code/modules/mob/living/carbon/xenomorph/castes/Queen.dm
index 93b6d230b198..ac975835b21f 100644
--- a/code/modules/mob/living/carbon/xenomorph/castes/Queen.dm
+++ b/code/modules/mob/living/carbon/xenomorph/castes/Queen.dm
@@ -940,3 +940,7 @@
// Switch icon back and then let normal icon behavior happen
Queen.icon = Queen.queen_standing_icon
+
+/mob/living/carbon/xenomorph/queen/alter_ghost(mob/dead/observer/ghost)
+ ghost.icon = queen_standing_icon
+ return ..()
diff --git a/code/modules/mob/living/carbon/xenomorph/death.dm b/code/modules/mob/living/carbon/xenomorph/death.dm
index fe4b4cca2fb1..e3a69da23262 100644
--- a/code/modules/mob/living/carbon/xenomorph/death.dm
+++ b/code/modules/mob/living/carbon/xenomorph/death.dm
@@ -50,7 +50,7 @@
new_xeno.generate_name()
if(!SSticker.mode.transfer_xeno(xeno_candidate, new_xeno))
qdel(new_xeno)
- return
+ break
new_xeno.visible_message(SPAN_XENODANGER("A larva suddenly burrows out of the ground!"),
SPAN_XENODANGER("You burrow out of the ground after feeling an immense tremor through the hive, which quickly fades into complete silence..."))
@@ -105,6 +105,11 @@
GLOB.hive_datum[hivenumber].stored_larva++
GLOB.hive_datum[hivenumber].hive_ui.update_burrowed_larva()
+ if(hardcore)
+ QDEL_IN(src, 3 SECONDS)
+ //else if(!gibbed) // At the moment we only support humans
+ //AddComponent(/datum/component/weed_food)
+
if(hive)
hive.remove_xeno(src)
// Finding the last xeno for anti-delay.
@@ -125,9 +130,6 @@
to_chat(X, SPAN_XENOANNOUNCE("Your carapace rattles with dread. You are all that remains of the hive!"))
announce_dchat("There is only one Xenomorph left: [X.name].", X)
- if(hardcore)
- QDEL_IN(src, 3 SECONDS)
-
SEND_GLOBAL_SIGNAL(COMSIG_GLOB_XENO_DEATH, src, gibbed)
/mob/living/carbon/xenomorph/gib(datum/cause_data/cause = create_cause_data("gibbing", src))
diff --git a/code/modules/mob/living/carbon/xenomorph/mutators/strains/runner/acid.dm b/code/modules/mob/living/carbon/xenomorph/mutators/strains/runner/acid.dm
index a2699a4f0f40..7a2196a3c209 100644
--- a/code/modules/mob/living/carbon/xenomorph/mutators/strains/runner/acid.dm
+++ b/code/modules/mob/living/carbon/xenomorph/mutators/strains/runner/acid.dm
@@ -70,19 +70,27 @@
. += "FOR THE HIVE!: in [caboom_left] seconds"
/datum/behavior_delegate/runner_acider/melee_attack_additional_effects_target(mob/living/carbon/target_mob)
- if (ishuman(target_mob))
+ if(ishuman(target_mob)) //Will acid be applied to the mob
var/mob/living/carbon/human/target_human = target_mob
- if (target_human.stat == DEAD)
+ if(target_human.buckled && istype(target_human.buckled, /obj/structure/bed/nest))
return
- for(var/datum/effects/acid/AA in target_mob.effects_list)
- qdel(AA)
+ if(target_human.stat == DEAD)
+ return
+
+ for(var/datum/effects/acid/acid_effect in target_mob.effects_list)
+ qdel(acid_effect)
break
- if(isxeno_human(target_mob))
+
+ new /datum/effects/acid(target_mob, bound_xeno, initial(bound_xeno.caste_type))
+ if(isxeno_human(target_mob)) //Will the runner get acid stacks
+ var/obj/item/alien_embryo/embryo = locate(/obj/item/alien_embryo) in target_mob.contents
+ if(embryo?.stage >= 4) //very late stage hugged in case the runner unnests them
+ return
+
if(target_mob.lying)
modify_acid(acid_slash_regen_lying)
- else
- modify_acid(acid_slash_regen_standing)
- new /datum/effects/acid(target_mob, bound_xeno, initial(bound_xeno.caste_type))
+ return
+ modify_acid(acid_slash_regen_standing)
/datum/behavior_delegate/runner_acider/on_life()
modify_acid(acid_passive_regen)
diff --git a/code/modules/power/port_gen.dm b/code/modules/power/port_gen.dm
index 5a335002d71d..6a20e9cfe78e 100644
--- a/code/modules/power/port_gen.dm
+++ b/code/modules/power/port_gen.dm
@@ -264,7 +264,7 @@ display round(lastgen) and phorontank amount
to_chat(user, SPAN_NOTICE(" You open the access panel."))
else
to_chat(user, SPAN_NOTICE(" You close the access panel."))
- else if(istype(O, /obj/item/tool/crowbar) && open)
+ else if(HAS_TRAIT(O, TRAIT_TOOL_CROWBAR) && open)
var/obj/structure/machinery/constructable_frame/new_frame = new /obj/structure/machinery/constructable_frame(src.loc)
for(var/obj/item/I in component_parts)
if(I.reliability < 100)
diff --git a/code/modules/projectiles/gun_attachables.dm b/code/modules/projectiles/gun_attachables.dm
index 0baf6f527d4b..17dc435210c9 100644
--- a/code/modules/projectiles/gun_attachables.dm
+++ b/code/modules/projectiles/gun_attachables.dm
@@ -1967,7 +1967,7 @@ Defined in conflicts.dm of the #defines folder.
//The requirement for an attachable being alt fire is AMMO CAPACITY > 0.
/obj/item/attachable/attached_gun/grenade
- name = "underslung grenade launcher"
+ name = "U1 grenade launcher"
desc = "A weapon-mounted, reloadable grenade launcher."
icon_state = "grenade"
attach_icon = "grenade_a"
diff --git a/code/modules/projectiles/magazines/pistols.dm b/code/modules/projectiles/magazines/pistols.dm
index 16090d9f2b81..317124955cbc 100644
--- a/code/modules/projectiles/magazines/pistols.dm
+++ b/code/modules/projectiles/magazines/pistols.dm
@@ -8,7 +8,7 @@
caliber = "9mm"
icon = 'icons/obj/items/weapons/guns/ammo_by_faction/uscm.dmi'
icon_state = "m4a3"
- max_rounds = 9
+ max_rounds = 12
w_class = SIZE_SMALL
default_ammo = /datum/ammo/bullet/pistol
gun_type = /obj/item/weapon/gun/pistol/m4a3
diff --git a/code/modules/reagents/chemistry_machinery/chem_dispenser.dm b/code/modules/reagents/chemistry_machinery/chem_dispenser.dm
index 8de20ca2b79a..09d46aa8c053 100644
--- a/code/modules/reagents/chemistry_machinery/chem_dispenser.dm
+++ b/code/modules/reagents/chemistry_machinery/chem_dispenser.dm
@@ -1,5 +1,6 @@
/obj/structure/machinery/chem_dispenser
- name = "chem dispenser"
+ name = "chemical dispenser"
+ desc = "A complex machine for mixing elements into chemicals. A Wey-Yu product."
density = TRUE
anchored = TRUE
icon = 'icons/obj/structures/machinery/science_machines.dmi'
diff --git a/code/modules/shuttle/computers/dropship_computer.dm b/code/modules/shuttle/computers/dropship_computer.dm
index ea4a7fdbc79d..50449b32fcb9 100644
--- a/code/modules/shuttle/computers/dropship_computer.dm
+++ b/code/modules/shuttle/computers/dropship_computer.dm
@@ -1,6 +1,6 @@
/obj/structure/machinery/computer/shuttle/dropship/flight
name = "dropship navigation computer"
- desc = "flight computer for dropship"
+ desc = "A flight computer that can be used for autopilot or long-range flights."
icon = 'icons/obj/structures/machinery/shuttle-parts.dmi'
icon_state = "console"
req_one_access = list(ACCESS_MARINE_LEADER, ACCESS_MARINE_DROPSHIP)
diff --git a/code/modules/unit_tests/_unit_tests.dm b/code/modules/unit_tests/_unit_tests.dm
index be92afffb898..010cba770ce2 100644
--- a/code/modules/unit_tests/_unit_tests.dm
+++ b/code/modules/unit_tests/_unit_tests.dm
@@ -85,6 +85,7 @@
#include "unit_test.dm"
#include "spawn_humans.dm"
#include "check_runtimes.dm"
+#include "wj_emotes.dm"
#undef TEST_ASSERT
#undef TEST_ASSERT_EQUAL
diff --git a/code/modules/unit_tests/wj_emotes.dm b/code/modules/unit_tests/wj_emotes.dm
new file mode 100644
index 000000000000..f89757665011
--- /dev/null
+++ b/code/modules/unit_tests/wj_emotes.dm
@@ -0,0 +1,7 @@
+/// Test that all working joe emotes have a category
+/datum/unit_test/wj_emotes
+
+/datum/unit_test/wj_emotes/Run()
+ for(var/datum/emote/living/carbon/human/synthetic/working_joe/emote as anything in subtypesof(/datum/emote/living/carbon/human/synthetic/working_joe))
+ if(!initial(emote.category))
+ TEST_FAIL("Emote [emote] did not have a category!")
diff --git a/colonialmarines.dme b/colonialmarines.dme
index 204c144c8916..304d5221ddd3 100644
--- a/colonialmarines.dme
+++ b/colonialmarines.dme
@@ -111,6 +111,7 @@
#include "code\__DEFINES\vv.dm"
#include "code\__DEFINES\weapon_stats.dm"
#include "code\__DEFINES\weather.dm"
+#include "code\__DEFINES\wj_emotes.dm"
#include "code\__DEFINES\xeno.dm"
#include "code\__DEFINES\dcs\flags.dm"
#include "code\__DEFINES\dcs\helpers.dm"
@@ -370,6 +371,7 @@
#include "code\datums\components\speed_modifier.dm"
#include "code\datums\components\toxin_buildup.dm"
#include "code\datums\components\weed_damage_reduction.dm"
+#include "code\datums\components\weed_food.dm"
#include "code\datums\components\xeno\shield_slash.dm"
#include "code\datums\construction\construction_template.dm"
#include "code\datums\construction\xenomorph\construction_template_xenomorph.dm"
@@ -1780,7 +1782,6 @@
#include "code\modules\mob\living\carbon\human\powers\human_powers.dm"
#include "code\modules\mob\living\carbon\human\powers\issue_order.dm"
#include "code\modules\mob\living\carbon\human\species\emote-monkey.dm"
-#include "code\modules\mob\living\carbon\human\species\emote-synthetic.dm"
#include "code\modules\mob\living\carbon\human\species\emote-yautja.dm"
#include "code\modules\mob\living\carbon\human\species\human.dm"
#include "code\modules\mob\living\carbon\human\species\monkey.dm"
@@ -1788,6 +1789,16 @@
#include "code\modules\mob\living\carbon\human\species\synthetic.dm"
#include "code\modules\mob\living\carbon\human\species\yautja.dm"
#include "code\modules\mob\living\carbon\human\species\zombie.dm"
+#include "code\modules\mob\living\carbon\human\species\working_joe\_emote.dm"
+#include "code\modules\mob\living\carbon\human\species\working_joe\_species.dm"
+#include "code\modules\mob\living\carbon\human\species\working_joe\farewell.dm"
+#include "code\modules\mob\living\carbon\human\species\working_joe\greeting.dm"
+#include "code\modules\mob\living\carbon\human\species\working_joe\notice.dm"
+#include "code\modules\mob\living\carbon\human\species\working_joe\question.dm"
+#include "code\modules\mob\living\carbon\human\species\working_joe\quip.dm"
+#include "code\modules\mob\living\carbon\human\species\working_joe\restricted_area.dm"
+#include "code\modules\mob\living\carbon\human\species\working_joe\task_update.dm"
+#include "code\modules\mob\living\carbon\human\species\working_joe\warning.dm"
#include "code\modules\mob\living\carbon\xenomorph\Abilities.dm"
#include "code\modules\mob\living\carbon\xenomorph\attack_alien.dm"
#include "code\modules\mob\living\carbon\xenomorph\damage_procs.dm"
diff --git a/html/changelogs/AutoChangeLog-pr-3685.yml b/html/changelogs/AutoChangeLog-pr-3685.yml
deleted file mode 100644
index efffe192d42e..000000000000
--- a/html/changelogs/AutoChangeLog-pr-3685.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "Newyearnewme, Morrow"
-delete-after: True
-changes:
- - rscadd: "Xeno structures/weeds now become forsaken after hijack"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-3709.yml b/html/changelogs/AutoChangeLog-pr-3709.yml
deleted file mode 100644
index 8e7d2c05c803..000000000000
--- a/html/changelogs/AutoChangeLog-pr-3709.yml
+++ /dev/null
@@ -1,5 +0,0 @@
-author: "Drathek"
-delete-after: True
-changes:
- - balance: "Gardener's hardy weeds now upgrade normal weeds (just like hive weeds upgrade weeds)."
- - balance: "Gardener's hardy weeds now don't prevent special structures (core and pylons) but they are still only allowed if the turf allows them."
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-3728.yml b/html/changelogs/AutoChangeLog-pr-3728.yml
deleted file mode 100644
index ed15d5da7b63..000000000000
--- a/html/changelogs/AutoChangeLog-pr-3728.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "Steelpoint"
-delete-after: True
-changes:
- - imageadd: "Synth utility vest is now slimmer in appearance"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-3741.yml b/html/changelogs/AutoChangeLog-pr-3741.yml
deleted file mode 100644
index 40678f6790a2..000000000000
--- a/html/changelogs/AutoChangeLog-pr-3741.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "thatoneyeeter"
-delete-after: True
-changes:
- - balance: "metal foam now becomes solid faster"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-3755.yml b/html/changelogs/AutoChangeLog-pr-3755.yml
deleted file mode 100644
index c2a183d77700..000000000000
--- a/html/changelogs/AutoChangeLog-pr-3755.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "realforest2001"
-delete-after: True
-changes:
- - admin: "Adds logs for bioscans successfully completing."
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-3765.yml b/html/changelogs/AutoChangeLog-pr-3765.yml
deleted file mode 100644
index a52472ec8b42..000000000000
--- a/html/changelogs/AutoChangeLog-pr-3765.yml
+++ /dev/null
@@ -1,5 +0,0 @@
-author: "Drathek"
-delete-after: True
-changes:
- - balance: "Weed nodes can no longer be placed in walls or window frames (or any turf or structure with density)"
- - refactor: "Refactored the plant weeds ability code"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-3772.yml b/html/changelogs/AutoChangeLog-pr-3772.yml
new file mode 100644
index 000000000000..198bfe98eb0f
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-3772.yml
@@ -0,0 +1,5 @@
+author: "BeagleGaming1"
+delete-after: True
+changes:
+ - rscadd: "Acid runners don't get acid from slashing nested humans"
+ - rscadd: "Very late-stage marines do not give acid"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-3790.yml b/html/changelogs/AutoChangeLog-pr-3790.yml
deleted file mode 100644
index 369a2ae3bf62..000000000000
--- a/html/changelogs/AutoChangeLog-pr-3790.yml
+++ /dev/null
@@ -1,5 +0,0 @@
-author: "BeagleGaming1"
-delete-after: True
-changes:
- - code_imp: "evacuation pod crash chance changed to a var"
- - code_imp: "proc added to force evacuation pods to crash"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-3793.yml b/html/changelogs/AutoChangeLog-pr-3793.yml
deleted file mode 100644
index 6702f7cab39b..000000000000
--- a/html/changelogs/AutoChangeLog-pr-3793.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "Drathek"
-delete-after: True
-changes:
- - bugfix: "Fixed more broken logos (primarily WY research papers)"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-3803.yml b/html/changelogs/AutoChangeLog-pr-3803.yml
new file mode 100644
index 000000000000..f03118db6727
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-3803.yml
@@ -0,0 +1,5 @@
+author: "Drathek"
+delete-after: True
+changes:
+ - rscdel: "Remove first life priority for larva queue"
+ - bugfix: "Fix ghosting as a facehugger counting as death for the larva queue"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-3837.yml b/html/changelogs/AutoChangeLog-pr-3837.yml
new file mode 100644
index 000000000000..7e92677ee394
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-3837.yml
@@ -0,0 +1,4 @@
+author: "BeagleGaming1"
+delete-after: True
+changes:
+ - bugfix: "Fixes one way of abusing resin fruit"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-3838.yml b/html/changelogs/AutoChangeLog-pr-3838.yml
new file mode 100644
index 000000000000..2ef7dd74d89f
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-3838.yml
@@ -0,0 +1,4 @@
+author: "blackdragonTOW"
+delete-after: True
+changes:
+ - spellcheck: "Added \"U1\" designation to the UGL attachment."
\ No newline at end of file
diff --git a/html/changelogs/archive/2023-07.yml b/html/changelogs/archive/2023-07.yml
index bba1c604b538..f6f6f6b6f455 100644
--- a/html/changelogs/archive/2023-07.yml
+++ b/html/changelogs/archive/2023-07.yml
@@ -53,3 +53,75 @@
- rscadd: CMB/Anchorpoint Marines get bandages to stop bleeding.
harryob:
- rscadd: all xenos can now access the tacmap, while the queen is on ovi
+2023-07-06:
+ BeagleGaming1:
+ - code_imp: evacuation pod crash chance changed to a var
+ - code_imp: proc added to force evacuation pods to crash
+ Drathek:
+ - bugfix: Fixed more broken logos (primarily WY research papers)
+ - balance: Weed nodes can no longer be placed in walls or window frames (or any
+ turf or structure with density)
+ - refactor: Refactored the plant weeds ability code
+ - balance: Gardener's hardy weeds now upgrade normal weeds (just like hive weeds
+ upgrade weeds).
+ - balance: Gardener's hardy weeds now don't prevent special structures (core and
+ pylons) but they are still only allowed if the turf allows them.
+ Newyearnewme, Morrow:
+ - rscadd: Xeno structures/weeds now become forsaken after hijack
+ Steelpoint:
+ - imageadd: Synth utility vest is now slimmer in appearance
+ realforest2001:
+ - admin: Adds logs for bioscans successfully completing.
+ thatoneyeeter:
+ - balance: metal foam now becomes solid faster
+2023-07-07:
+ Diegoflores31:
+ - rscadd: Defender Tail Slam has a 1% chance to do a BONK sound instead.
+ Drathek:
+ - bugfix: Fixed ghost icons for larva and ovi queen
+ Morrow:
+ - rscdel: Removes fountain pens from gear sets
+ - rscdel: Removed nesting the dead
+ - rscadd: Added a visiting USASF Commander (CO survivor) to New Varadero
+ - bugfix: Burnt matches no longer permanently give you a light source if they naturally
+ burn out
+ QuickLoad:
+ - balance: Colony Synthetics are faster but are less resistant. This allows for
+ the option of avoiding engagements.
+ - balance: Colony Synthetics have slightly better CQC and can carry people better.
+ realforest2001:
+ - bugfix: Fixes Queen making footstep sounds while dead and being dragged.
+2023-07-08:
+ Ben10083:
+ - bugfix: Working Joes can no longer have a gradient on their rare hair spawn.
+ - rscdel: Working Joes can no longer be fed.
+ - code_imp: 'New trait: Cannot eat. Self-explanatory.'
+ Cursor:
+ - spellcheck: Changed Chem Dispenser to Chemical Dispenser, added descriptions to
+ the cryo cell, rail computer and chemical dispenser. Updated the descriptions
+ for the Blood Dispenser and Dropship computer.
+ Drathek:
+ - bugfix: Fixed facehuggers incorrectly displaying thrown state when it has landed
+ Drathek Kugamo:
+ - rscadd: Added the ability for weeds to merge with unrevivable corpses
+ - imageadd: Added human shaped weeds by Kugamo
+ - code_imp: Added a signal for weeds sent to the turf to indicate it is now weeded,
+ and added a signal for afterbuckle.
+ - bugfix: Closets (including coffins) can no longer move anchored mobs.
+ Morrow:
+ - qol: Create humans tab length increase
+ - qol: Create humans tab now defaults to 0 range to spawn
+ - bugfix: Fixed extraneous messages in regards to wall nests
+ SpartanBobby:
+ - balance: changes M4A3 magazine size from 9 to 12
+ Zonespace27:
+ - bugfix: The maintenance jack should work a little better at crowbarring things.
+2023-07-09:
+ Khadd:
+ - rscadd: added a iv tube between the user and the bloodpack / iv drip
+ - imageadd: sprites for the iv tube
+ Zonespace27:
+ - rscadd: Working Joes now have an emote panel to use voice lines, accessible as
+ an action button.
+ theselfish:
+ - qol: Foxtrot's radio channel is now on if you have multi-squad in your headset.
diff --git a/html/create_humans.html b/html/create_humans.html
index ed9361fc6f25..06b92cba0c71 100644
--- a/html/create_humans.html
+++ b/html/create_humans.html
@@ -11,7 +11,7 @@
Amount of humans:
- Range to spawn in:
+ Range to spawn in:
Spawn mobs as:
Regular
diff --git a/icons/effects/beam.dmi b/icons/effects/beam.dmi
index 704c0ad1c02c..d6ee40cf7fe8 100644
Binary files a/icons/effects/beam.dmi and b/icons/effects/beam.dmi differ
diff --git a/icons/mob/hud/actions.dmi b/icons/mob/hud/actions.dmi
index 1692879116dc..4d0697733207 100644
Binary files a/icons/mob/hud/actions.dmi and b/icons/mob/hud/actions.dmi differ
diff --git a/icons/mob/xenos/weeds.dmi b/icons/mob/xenos/weeds.dmi
index 8eb4b2203cf6..0b9403058109 100644
Binary files a/icons/mob/xenos/weeds.dmi and b/icons/mob/xenos/weeds.dmi differ
diff --git a/maps/new_varadero.json b/maps/new_varadero.json
index d9d6a0d231cc..9a0bf5d56be1 100644
--- a/maps/new_varadero.json
+++ b/maps/new_varadero.json
@@ -13,6 +13,9 @@
"/datum/equipment_preset/survivor/security/lv",
"/datum/equipment_preset/survivor/colonial_marshal/lv"
],
+ "CO_survivor_types": [
+ "/datum/equipment_preset/survivor/new_varadero/commander"
+ ],
"defcon_triggers": [
3300,
diff --git a/sound/effects/comical_bonk.ogg b/sound/effects/comical_bonk.ogg
new file mode 100644
index 000000000000..e0a2751d52c2
Binary files /dev/null and b/sound/effects/comical_bonk.ogg differ
diff --git a/tgui/packages/tgui/interfaces/JoeEmotes.tsx b/tgui/packages/tgui/interfaces/JoeEmotes.tsx
new file mode 100644
index 000000000000..acd37de34978
--- /dev/null
+++ b/tgui/packages/tgui/interfaces/JoeEmotes.tsx
@@ -0,0 +1,110 @@
+import { useBackend, useLocalState } from '../backend';
+import { Box, Button, Divider, Section, Stack, Tabs } from '../components';
+import { Window } from '../layouts';
+import { BooleanLike } from '../../common/react';
+
+type Emote = {
+ id: string;
+ text: string;
+ category: string;
+ path: string;
+};
+
+type BackendContext = {
+ categories: string[];
+ emotes: Emote[];
+ on_cooldown: BooleanLike;
+};
+
+const EmoteTab = (props, context) => {
+ const { data, act } = useBackend(context);
+ const { categories, emotes, on_cooldown } = data;
+ const [categoryIndex, setCategoryIndex] = useLocalState(
+ context,
+ 'category_index',
+ 'Farewell'
+ );
+ const mapped_emote = emotes.filter(
+ (emote) => emote && emote.category === categoryIndex
+ );
+ return (
+
+
+
+
+ {categories.map((item, key) => (
+ {
+ setCategoryIndex(item);
+ }}>
+ {item}
+
+ ))}
+
+
+
+
+
+
+
+ {mapped_emote.map((item) => (
+
+
+ {' '}
+
+
+
+
+
+
+
+
+ ))}
+
+
+
+
+ );
+};
+
+export const JoeEmotes = (props, context) => {
+ return (
+
+
+
+
+
+ );
+};