From f0251fefebc79f199331ee716d276d2ccfc17ccc Mon Sep 17 00:00:00 2001
From: Drathek <76988376+Drulikar@users.noreply.github.com>
Date: Tue, 13 Feb 2024 05:32:02 -0800
Subject: [PATCH] Revive Lesser Drone and Facehugger temporary communication
restrictions (#5688)
# About the pull request
This PR revives and expands #5352. By expand I mean that in addition to
a 3 minute restriction for say and hivemind speak, custom emotes and
pointing are also restricted during the 3 minute window. Past that I did
some additional refactoring.
# Explain why it's good for the game
As a ghost role, this acts as a measure to mechanically limit the
communication of meta info to other members in the hive. For now the
restriction is only 3 minutes.
# Testing Photographs and Procedure
Screenshots & Videos
![image](https://github.com/cmss13-devs/cmss13/assets/76988376/fd64c2f4-9142-4fe1-9953-b29c480c17ee)
# Changelog
:cl: Zonespace Drathek
balance: Lesser drones and Facehuggers cannot speak, custom emote, or
point for 3 minutes after spawning
/:cl:
---------
Co-authored-by: John Doe
---
.../signals/atom/mob/living/signals_xeno.dm | 4 +
.../dcs/signals/atom/mob/signals_mob.dm | 6 ++
code/datums/components/temporary_mute.dm | 90 +++++++++++++++++++
code/modules/mob/emote.dm | 12 +--
.../carbon/xenomorph/castes/Facehugger.dm | 14 +++
.../carbon/xenomorph/castes/lesser_drone.dm | 14 +++
.../mob/living/carbon/xenomorph/say.dm | 3 +
code/modules/mob/mob_verbs.dm | 31 ++++---
colonialmarines.dme | 1 +
9 files changed, 154 insertions(+), 21 deletions(-)
create mode 100644 code/datums/components/temporary_mute.dm
diff --git a/code/__DEFINES/dcs/signals/atom/mob/living/signals_xeno.dm b/code/__DEFINES/dcs/signals/atom/mob/living/signals_xeno.dm
index 73b10d700247..e6e1e64e9c7e 100644
--- a/code/__DEFINES/dcs/signals/atom/mob/living/signals_xeno.dm
+++ b/code/__DEFINES/dcs/signals/atom/mob/living/signals_xeno.dm
@@ -74,3 +74,7 @@
/// From /mob/living/carbon/xenomorph/proc/handle_crit()
#define COMSIG_XENO_ENTER_CRIT "xeno_entering_critical"
+
+/// From /mob/living/carbon/xenomorph/proc/hivemind_talk(): (message)
+#define COMSIG_XENO_TRY_HIVEMIND_TALK "xeno_try_hivemind_talk"
+ #define COMPONENT_OVERRIDE_HIVEMIND_TALK (1<<0)
diff --git a/code/__DEFINES/dcs/signals/atom/mob/signals_mob.dm b/code/__DEFINES/dcs/signals/atom/mob/signals_mob.dm
index 710e4d9ae20a..61eb757e9c4d 100644
--- a/code/__DEFINES/dcs/signals/atom/mob/signals_mob.dm
+++ b/code/__DEFINES/dcs/signals/atom/mob/signals_mob.dm
@@ -109,6 +109,12 @@
#define COMSIG_MOB_EMOTED(emote_key) "mob_emoted_[emote_key]"
+#define COMSIG_MOB_TRY_EMOTE "mob_try_emote"
+ #define COMPONENT_OVERRIDE_EMOTE (1<<0)
+
+#define COMSIG_MOB_TRY_POINT "mob_try_point"
+ #define COMPONENT_OVERRIDE_POINT (1<<0)
+
//from /mob/living/set_stat()
#define COMSIG_MOB_STAT_SET_ALIVE "mob_stat_set_alive"
//from /mob/living/set_stat()
diff --git a/code/datums/components/temporary_mute.dm b/code/datums/components/temporary_mute.dm
new file mode 100644
index 000000000000..0fed49c4778e
--- /dev/null
+++ b/code/datums/components/temporary_mute.dm
@@ -0,0 +1,90 @@
+/datum/component/temporary_mute
+ dupe_mode = COMPONENT_DUPE_UNIQUE
+ /// A message to tell the user when they attempt to speak, if any
+ var/on_speak_message = ""
+ /// A message to tell the user when they attempt to emote, if any
+ var/on_emote_message = ""
+ /// A message to tell the user when they become no longer mute, if any
+ var/on_unmute_message = ""
+ /// How long after the component's initialization it should be deleted. -1 means it will never delete
+ var/time_until_unmute = 3 MINUTES
+
+/datum/component/temporary_mute/Initialize(on_speak_message = "", on_emote_message = "", on_unmute_message = "", time_until_unmute = 3 MINUTES)
+ . = ..()
+ if(!ismob(parent))
+ return COMPONENT_INCOMPATIBLE
+
+ src.on_speak_message = on_speak_message
+ src.on_emote_message = on_emote_message
+ src.on_unmute_message = on_unmute_message
+ src.time_until_unmute = time_until_unmute
+ if(time_until_unmute != -1)
+ QDEL_IN(src, time_until_unmute)
+
+/datum/component/temporary_mute/RegisterWithParent()
+ ..()
+ RegisterSignal(parent, COMSIG_LIVING_SPEAK, PROC_REF(on_speak))
+ RegisterSignal(parent, COMSIG_XENO_TRY_HIVEMIND_TALK, PROC_REF(on_hivemind))
+ RegisterSignal(parent, COMSIG_MOB_TRY_EMOTE, PROC_REF(on_emote))
+ RegisterSignal(parent, COMSIG_MOB_TRY_POINT, PROC_REF(on_point))
+
+/datum/component/temporary_mute/UnregisterFromParent()
+ ..()
+ if(parent)
+ UnregisterSignal(parent, COMSIG_LIVING_SPEAK)
+ UnregisterSignal(parent, COMSIG_XENO_TRY_HIVEMIND_TALK)
+ UnregisterSignal(parent, COMSIG_MOB_TRY_EMOTE)
+ UnregisterSignal(parent, COMSIG_MOB_TRY_POINT)
+ if(on_unmute_message)
+ to_chat(parent, SPAN_NOTICE(on_unmute_message))
+
+/datum/component/temporary_mute/proc/on_speak(
+ mob/user,
+ message,
+ datum/language/speaking = null,
+ verb = "says",
+ alt_name = "",
+ italics = FALSE,
+ message_range = GLOB.world_view_size,
+ sound/speech_sound,
+ sound_vol,
+ nolog = FALSE,
+ message_mode = null
+)
+ SIGNAL_HANDLER
+
+ if(!nolog)
+ log_say("[user.name != "Unknown" ? user.name : "([user.real_name])"] attempted to say the following before their spawn mute ended: [message] (CKEY: [user.key]) (JOB: [user.job])")
+ if(on_speak_message)
+ to_chat(parent, SPAN_BOLDNOTICE(on_speak_message))
+ return COMPONENT_OVERRIDE_SPEAK
+
+/datum/component/temporary_mute/proc/on_hivemind(mob/user, message)
+ SIGNAL_HANDLER
+
+ log_say("[user.name != "Unknown" ? user.name : "([user.real_name])"] attempted to hivemind the following before their spawn mute ended: [message] (CKEY: [user.key]) (JOB: [user.job])")
+ if(on_speak_message)
+ to_chat(parent, SPAN_BOLDNOTICE(on_speak_message))
+ return COMPONENT_OVERRIDE_HIVEMIND_TALK
+
+/datum/component/temporary_mute/proc/on_emote(mob/user, datum/emote/current_emote, act, m_type, param, intentional)
+ SIGNAL_HANDLER
+
+ // Allow involuntary emotes or non-custom emotes
+ if(!intentional)
+ return
+ if(!param && !istype(current_emote, /datum/emote/custom))
+ return
+
+ log_say("[user.name != "Unknown" ? user.name : "([user.real_name])"] attempted to emote the following before their spawn mute ended: [param] (CKEY: [user.key]) (JOB: [user.job])")
+ if(on_emote_message)
+ to_chat(parent, SPAN_BOLDNOTICE(on_emote_message))
+ return COMPONENT_OVERRIDE_EMOTE
+
+/datum/component/temporary_mute/proc/on_point(mob/user, atom/target)
+ SIGNAL_HANDLER
+
+ log_say("[user.name != "Unknown" ? user.name : "([user.real_name])"] attempted to point at the following before their spawn mute ended: [target] (CKEY: [user.key]) (JOB: [user.job])")
+ if(on_emote_message)
+ to_chat(parent, SPAN_BOLDNOTICE(on_emote_message))
+ return COMPONENT_OVERRIDE_POINT
diff --git a/code/modules/mob/emote.dm b/code/modules/mob/emote.dm
index f1b600179450..fa7c7ec3176e 100644
--- a/code/modules/mob/emote.dm
+++ b/code/modules/mob/emote.dm
@@ -14,13 +14,15 @@
to_chat(src, SPAN_NOTICE("'[act]' emote does not exist. Say *help for a list."))
return FALSE
var/silenced = FALSE
- for(var/datum/emote/P in key_emotes)
- if(!P.check_cooldown(src, intentional))
+ for(var/datum/emote/current_emote in key_emotes)
+ if(!current_emote.check_cooldown(src, intentional))
silenced = TRUE
continue
- if(P.run_emote(src, param, m_type, intentional))
- SEND_SIGNAL(src, COMSIG_MOB_EMOTE, P, act, m_type, message, intentional)
- SEND_SIGNAL(src, COMSIG_MOB_EMOTED(P.key))
+ if(SEND_SIGNAL(src, COMSIG_MOB_TRY_EMOTE, current_emote, act, m_type, param, intentional) & COMPONENT_OVERRIDE_EMOTE)
+ silenced = TRUE
+ continue
+ if(current_emote.run_emote(src, param, m_type, intentional))
+ SEND_SIGNAL(src, COMSIG_MOB_EMOTE, current_emote, act, m_type, message, intentional)
return TRUE
if(intentional && !silenced && !force_silence)
to_chat(src, SPAN_NOTICE("Unusable emote '[act]'. Say *help for a list."))
diff --git a/code/modules/mob/living/carbon/xenomorph/castes/Facehugger.dm b/code/modules/mob/living/carbon/xenomorph/castes/Facehugger.dm
index 7ce3a8750568..ab7b22b73d81 100644
--- a/code/modules/mob/living/carbon/xenomorph/castes/Facehugger.dm
+++ b/code/modules/mob/living/carbon/xenomorph/castes/Facehugger.dm
@@ -64,6 +64,20 @@
weed_food_states = list("Facehugger_1","Facehugger_2","Facehugger_3")
weed_food_states_flipped = list("Facehugger_1","Facehugger_2","Facehugger_3")
+/mob/living/carbon/xenomorph/facehugger/Login()
+ var/last_ckey_inhabited = persistent_ckey
+ . = ..()
+ if(ckey == last_ckey_inhabited)
+ return
+
+ AddComponent(\
+ /datum/component/temporary_mute,\
+ "We aren't old enough to vocalize anything yet.",\
+ "We aren't old enough to communicate like this yet.",\
+ "We feel old enough to be able to vocalize and speak to the hivemind.",\
+ 3 MINUTES,\
+ )
+
/mob/living/carbon/xenomorph/facehugger/initialize_pass_flags(datum/pass_flags_container/PF)
..()
if (PF)
diff --git a/code/modules/mob/living/carbon/xenomorph/castes/lesser_drone.dm b/code/modules/mob/living/carbon/xenomorph/castes/lesser_drone.dm
index f050a0dcfe8a..5d3f4b38c8f8 100644
--- a/code/modules/mob/living/carbon/xenomorph/castes/lesser_drone.dm
+++ b/code/modules/mob/living/carbon/xenomorph/castes/lesser_drone.dm
@@ -83,6 +83,20 @@
weed_food_states = list("Lesser_Drone_1","Lesser_Drone_2","Lesser_Drone_3")
weed_food_states_flipped = list("Lesser_Drone_1","Lesser_Drone_2","Lesser_Drone_3")
+/mob/living/carbon/xenomorph/lesser_drone/Login()
+ var/last_ckey_inhabited = persistent_ckey
+ . = ..()
+ if(ckey == last_ckey_inhabited)
+ return
+
+ AddComponent(\
+ /datum/component/temporary_mute,\
+ "We aren't old enough to vocalize anything yet.",\
+ "We aren't old enough to communicate like this yet.",\
+ "We feel old enough to be able to vocalize and speak to the hivemind.",\
+ 3 MINUTES,\
+ )
+
/mob/living/carbon/xenomorph/lesser_drone/age_xeno()
if(stat == DEAD || !caste || QDELETED(src) || !client)
return
diff --git a/code/modules/mob/living/carbon/xenomorph/say.dm b/code/modules/mob/living/carbon/xenomorph/say.dm
index 5b8ce1ecd292..c40a50ce7523 100644
--- a/code/modules/mob/living/carbon/xenomorph/say.dm
+++ b/code/modules/mob/living/carbon/xenomorph/say.dm
@@ -98,6 +98,9 @@
to_chat(src, SPAN_WARNING("A headhunter temporarily cut off your psychic connection!"))
return
+ if(SEND_SIGNAL(src, COMSIG_XENO_TRY_HIVEMIND_TALK, message) & COMPONENT_OVERRIDE_HIVEMIND_TALK)
+ return
+
hivemind_broadcast(message, hive)
/mob/living/carbon/proc/hivemind_broadcast(message, datum/hive_status/hive)
diff --git a/code/modules/mob/mob_verbs.dm b/code/modules/mob/mob_verbs.dm
index 1ba8985d56bd..f12d00cc0988 100644
--- a/code/modules/mob/mob_verbs.dm
+++ b/code/modules/mob/mob_verbs.dm
@@ -50,34 +50,33 @@
to_chat(usr, SPAN_DANGER("This mob type cannot throw items."))
return
-/mob/proc/point_to(atom/A in view())
+/mob/proc/point_to(atom/target in view())
//set name = "Point To"
//set category = "Object"
- if(!isturf(src.loc) || !(A in view(src)))//target is no longer visible to us
- return 0
+ if(!isturf(src.loc) || !(target in view(src)))//target is no longer visible to us
+ return FALSE
- if(!A.mouse_opacity)//can't click it? can't point at it.
- return 0
+ if(!target.mouse_opacity)//can't click it? can't point at it.
+ return FALSE
if(is_mob_incapacitated() || (status_flags & FAKEDEATH)) //incapacitated, can't point
- return 0
+ return FALSE
- var/tile = get_turf(A)
- if (!tile)
- return 0
+ var/tile = get_turf(target)
+ if(!tile)
+ return FALSE
if(recently_pointed_to > world.time)
- return 0
-
- next_move = world.time + 2
-
- point_to_atom(A, tile)
- return 1
-
+ return FALSE
+ if(SEND_SIGNAL(src, COMSIG_MOB_TRY_POINT, target) & COMPONENT_OVERRIDE_POINT)
+ return FALSE
+ next_move = world.time + 2
+ point_to_atom(target, tile)
+ return TRUE
/mob/verb/memory()
set name = "Notes"
diff --git a/colonialmarines.dme b/colonialmarines.dme
index 64760c4f2bc9..09370e73b4b1 100644
--- a/colonialmarines.dme
+++ b/colonialmarines.dme
@@ -406,6 +406,7 @@
#include "code\datums\components\overlay_lighting.dm"
#include "code\datums\components\rename.dm"
#include "code\datums\components\speed_modifier.dm"
+#include "code\datums\components\temporary_mute.dm"
#include "code\datums\components\toxin_buildup.dm"
#include "code\datums\components\tutorial_status.dm"
#include "code\datums\components\weed_damage_reduction.dm"