mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2025-12-10 09:42:29 +00:00
Mind readers can read what people are typing (#93059)
## About The Pull Request When a Mind Reader examines someone who is typing, it will show them what they are actively typing <img width="518" height="97" alt="image" src="https://github.com/user-attachments/assets/8d54aa56-85fc-4e03-b0a3-bfb8e475beff" /> No, it won't read OOC messages. ## Why It's Good For The Game Your next line is, "This sounds really funny for gimmicks like security interrogations or fortune telling, or for getting the jump on someone as they try to get the jump on you, or just to be a badass by finishing people sentences" ## Changelog 🆑 Melbert add: When a mind reader examines a mob, they'll get a glimpse into what that mob is currently typing, before they even send the message. qol: Mind Reader now groups up all the information it gives you in a box admin: Mind Reader now logs everything the reader gleamed from the read-ee /🆑
This commit is contained in:
@@ -68,10 +68,12 @@
|
||||
if (!owner)
|
||||
return
|
||||
ADD_TRAIT(grant_to, TRAIT_MIND_READER, GENETIC_MUTATION)
|
||||
RegisterSignal(grant_to, COMSIG_MOB_EXAMINATE, PROC_REF(on_examining))
|
||||
|
||||
/datum/action/cooldown/spell/pointed/mindread/Remove(mob/remove_from)
|
||||
. = ..()
|
||||
REMOVE_TRAIT(remove_from, TRAIT_MIND_READER, GENETIC_MUTATION)
|
||||
UnregisterSignal(remove_from, COMSIG_MOB_EXAMINATE)
|
||||
|
||||
/datum/action/cooldown/spell/pointed/mindread/is_valid_target(atom/cast_on)
|
||||
if(!isliving(cast_on))
|
||||
@@ -83,12 +85,15 @@
|
||||
if(living_cast_on.stat == DEAD)
|
||||
to_chat(owner, span_warning("[cast_on] is dead!"))
|
||||
return FALSE
|
||||
if(living_cast_on.mob_biotypes & MOB_ROBOTIC)
|
||||
to_chat(owner, span_warning("[cast_on] is robotic, you can't read [cast_on.p_their()] mind!"))
|
||||
return FALSE
|
||||
|
||||
return TRUE
|
||||
|
||||
/datum/action/cooldown/spell/pointed/mindread/cast(mob/living/cast_on)
|
||||
. = ..()
|
||||
if(cast_on.can_block_magic(MAGIC_RESISTANCE_MIND, charge_cost = 0))
|
||||
if(cast_on.can_block_magic(antimagic_flags, charge_cost = 0))
|
||||
to_chat(owner, span_warning("As you reach into [cast_on]'s mind, \
|
||||
you are stopped by a mental blockage. It seems you've been foiled."))
|
||||
return
|
||||
@@ -102,21 +107,66 @@
|
||||
you feel the overwhelming emptiness within. A truly evil being. \
|
||||
[HAS_TRAIT(owner, TRAIT_EVIL) ? "It's nice to find someone who is like-minded." : "What is wrong with this person?"]"))
|
||||
|
||||
to_chat(owner, span_boldnotice("You plunge into [cast_on]'s mind..."))
|
||||
var/list/log_info = list()
|
||||
var/list/discovered_info = list("<i>You plunge into [cast_on]'s mind and discover...</i>")
|
||||
if(prob(20))
|
||||
// chance to alert the read-ee
|
||||
to_chat(cast_on, span_danger("You feel something foreign enter your mind."))
|
||||
log_info += "Target alerted!"
|
||||
|
||||
var/list/recent_speech = cast_on.copy_recent_speech(copy_amount = 3, line_chance = 50)
|
||||
if(length(recent_speech))
|
||||
to_chat(owner, span_boldnotice("You catch some drifting memories of their past conversations..."))
|
||||
discovered_info += "...Drifting memories of past conversations:"
|
||||
var/list/speech_block = list()
|
||||
for(var/spoken_memory in recent_speech)
|
||||
to_chat(owner, span_notice("[spoken_memory]"))
|
||||
speech_block += " \"[spoken_memory]\"..."
|
||||
log_info += "Recent speech: \"[spoken_memory]\""
|
||||
discovered_info += jointext(speech_block, "<br>")
|
||||
|
||||
if(iscarbon(cast_on))
|
||||
var/mob/living/carbon/carbon_cast_on = cast_on
|
||||
to_chat(owner, span_boldnotice("You find that their intent is to [carbon_cast_on.combat_mode ? "harm" : "help"]..."))
|
||||
to_chat(owner, span_boldnotice("You uncover that [carbon_cast_on.p_their()] true identity is [carbon_cast_on.mind.name]."))
|
||||
discovered_info += "...Intent to <b>[carbon_cast_on.combat_mode ? "harm" : "help"]</b>."
|
||||
discovered_info += "...True identity of <b>[carbon_cast_on.mind.name]</b>."
|
||||
log_info += "Intent: \"[carbon_cast_on.combat_mode ? "harm" : "help"]\""
|
||||
log_info += "Identity: \"[carbon_cast_on.mind.name]\""
|
||||
|
||||
to_chat(owner, boxed_message(span_notice(jointext(discovered_info, "<br>"))))
|
||||
log_combat(owner, cast_on, "mind read (cast intentionally)", null, "info: [english_list(log_info, and_text = ", ")]")
|
||||
|
||||
/datum/action/cooldown/spell/pointed/mindread/proc/on_examining(mob/examiner, atom/examining)
|
||||
SIGNAL_HANDLER
|
||||
if(!isliving(examining) || examiner == examining)
|
||||
return
|
||||
|
||||
INVOKE_ASYNC(src, PROC_REF(read_mind), examiner, examining)
|
||||
|
||||
/datum/action/cooldown/spell/pointed/mindread/proc/read_mind(mob/living/examiner, mob/living/examined)
|
||||
if(examined.stat >= UNCONSCIOUS || isnull(examined.mind) || (examined.mob_biotypes & MOB_ROBOTIC))
|
||||
return
|
||||
|
||||
var/antimagic = examined.can_block_magic(antimagic_flags, charge_cost = 0)
|
||||
var/read_text = ""
|
||||
if(!antimagic)
|
||||
read_text = examined.get_typing_text()
|
||||
if(!read_text)
|
||||
return
|
||||
|
||||
sleep(0.5 SECONDS) // small pause so it comes after all examine text and effects
|
||||
if(QDELETED(examiner))
|
||||
return
|
||||
if(antimagic)
|
||||
to_chat(examiner, boxed_message(span_warning("You attempt to analyze [examined]'s current thoughts, but fail to penetrate [examined.p_their()] mind - It seems you've been foiled.")))
|
||||
return
|
||||
|
||||
var/list/log_info = list()
|
||||
if(prob(10))
|
||||
to_chat(examined, span_danger("You feel something foreign enter your mind."))
|
||||
log_info += "Target alerted!"
|
||||
|
||||
to_chat(examiner, boxed_message(span_notice("<i>You analyze [examined]'s current thoughts...</i><br> \"[read_text]\"...")))
|
||||
log_info += "Current thought: \"[read_text]\""
|
||||
|
||||
log_combat(examiner, examined, "mind read (triggered on examine)", null, "info: [english_list(log_info, and_text = ", ")]")
|
||||
|
||||
/datum/mutation/mindreader/New(datum/mutation/copymut)
|
||||
..()
|
||||
|
||||
@@ -24,13 +24,15 @@
|
||||
/// The user who opened the window
|
||||
var/client/client
|
||||
/// Injury phrases to blurt out
|
||||
var/list/hurt_phrases = list("GACK!", "GLORF!", "OOF!", "AUGH!", "OW!", "URGH!", "HRNK!")
|
||||
var/static/list/hurt_phrases = list("GACK!", "GLORF!", "OOF!", "AUGH!", "OW!", "URGH!", "HRNK!")
|
||||
/// Max message length
|
||||
var/max_length = MAX_MESSAGE_LEN
|
||||
/// The modal window
|
||||
var/datum/tgui_window/window
|
||||
/// Boolean for whether the tgui_say was opened by the user.
|
||||
var/window_open
|
||||
/// What text was present in the say box the last time save_text was called
|
||||
var/saved_text = ""
|
||||
|
||||
/** Creates the new input window to exist in the background. */
|
||||
/datum/tgui_say/New(client/client, id)
|
||||
@@ -128,7 +130,7 @@
|
||||
if (type == "typing")
|
||||
start_typing()
|
||||
return TRUE
|
||||
if (type == "entry" || type == "force")
|
||||
if (type == "entry" || type == "force" || type == "save")
|
||||
handle_entry(type, payload)
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
@@ -57,6 +57,13 @@
|
||||
window.send_message("force")
|
||||
stop_typing()
|
||||
|
||||
/**
|
||||
* Exports whatever text is currently in the input box to this datum
|
||||
*/
|
||||
/datum/tgui_say/proc/save_text()
|
||||
saved_text = null
|
||||
window.send_message("save")
|
||||
|
||||
/**
|
||||
* Makes the player force say what's in their current input box.
|
||||
*/
|
||||
@@ -70,6 +77,19 @@
|
||||
log_speech_indicators("[key_name(client)] FORCED to stop typing, indicators DISABLED.")
|
||||
SEND_SIGNAL(src, COMSIG_HUMAN_FORCESAY)
|
||||
|
||||
/**
|
||||
* Gets whatever text is currently in this mob's say box and returns it.
|
||||
*
|
||||
* Note: Sleeps, due to waiting for say to respond.
|
||||
*/
|
||||
/mob/proc/get_typing_text()
|
||||
if(!client?.tgui_say?.window_open)
|
||||
return
|
||||
client.tgui_say.save_text()
|
||||
var/safety = world.time
|
||||
UNTIL(istext(client?.tgui_say?.saved_text) || world.time - safety > 2 SECONDS)
|
||||
return client?.tgui_say?.saved_text
|
||||
|
||||
/**
|
||||
* Handles text entry and forced speech.
|
||||
*
|
||||
@@ -93,4 +113,10 @@
|
||||
target_channel = SAY_CHANNEL // No ooc leaks
|
||||
delegate_speech(alter_entry(payload), target_channel)
|
||||
return TRUE
|
||||
if(type == "save")
|
||||
saved_text = "" // so we can differentiate null (nothing saved) and empty (nothing typed)
|
||||
var/target_channel = payload["channel"]
|
||||
if(target_channel == SAY_CHANNEL || target_channel == RADIO_CHANNEL)
|
||||
saved_text = payload["entry"] // only save IC text
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
@@ -82,7 +82,11 @@ export function TguiSay() {
|
||||
setButtonContent(currentPrefix.current ?? iterator.current());
|
||||
|
||||
// Empty input, resets the channel
|
||||
} else if (currentPrefix.current && iterator.isSay() && value?.length === 0) {
|
||||
} else if (
|
||||
currentPrefix.current &&
|
||||
iterator.isSay() &&
|
||||
value?.length === 0
|
||||
) {
|
||||
setCurrentPrefix(null);
|
||||
setButtonContent(iterator.current());
|
||||
}
|
||||
@@ -152,6 +156,15 @@ export function TguiSay() {
|
||||
handleClose();
|
||||
}
|
||||
|
||||
function handleSaveText(): void {
|
||||
const iterator = channelIterator.current;
|
||||
const currentValue = innerRef.current?.value;
|
||||
|
||||
if (!currentValue || !iterator.isVisible()) return;
|
||||
|
||||
messages.current.saveText(currentValue, iterator.current());
|
||||
}
|
||||
|
||||
function handleIncrementChannel(): void {
|
||||
const iterator = channelIterator.current;
|
||||
|
||||
@@ -248,6 +261,7 @@ export function TguiSay() {
|
||||
Byond.subscribeTo('props', handleProps);
|
||||
Byond.subscribeTo('force', handleForceSay);
|
||||
Byond.subscribeTo('open', handleOpen);
|
||||
Byond.subscribeTo('save', handleSaveText);
|
||||
}, []);
|
||||
|
||||
/** Value has changed, we need to check if the size of the window is ok */
|
||||
|
||||
@@ -17,6 +17,12 @@ export const byondMessages = {
|
||||
1 * SECONDS,
|
||||
true,
|
||||
),
|
||||
saveText: debounce(
|
||||
(entry: string, channel: Channel) =>
|
||||
Byond.sendMessage('save', { entry, channel }),
|
||||
1 * SECONDS,
|
||||
true,
|
||||
),
|
||||
// Throttle: Prevents spamming the server
|
||||
typingMsg: throttle(() => Byond.sendMessage('typing'), 4 * SECONDS),
|
||||
} as const;
|
||||
|
||||
Reference in New Issue
Block a user