mirror of
https://github.com/Aurorastation/Aurora.3.git
synced 2026-07-19 03:49:10 +01:00
Storyteller Narration Filters (#21767)
This PR adds a feature requested of me by @Fyniiy , adding optional Filters to the Narrate panel that allow Storytellers to narrate only to people that meet a set condition. The conditions themselves were specifically requested to be "Psi Narrates" and "Synth Narrates", with optional variations on Synth narrates that include people with cranial implants. While I was touching these files, I've also fixed the improper message sanitization bug with admin messages in general. <img width="926" height="536" alt="image" src="https://github.com/user-attachments/assets/08dcfd2a-5399-48b0-a13d-2bfd3a2c88f0" /> Here's a breakdown of the filter options. 1. **Skrell-like Psi-sensitives**: Anyone with a Psi-sensitivity score of "At least 1". This will include Skrell, anyone with a Psi-receiver, and people with the High Psi-sensitivity trait. It will exclude people who are blocked from receiving telepathy for any reason, such as having a mindshield, mindblanker, or having the psi-suppression power active. Skrell with the Low Psi-sensitivity trait are counted as "Human-like Psi-sensitives", and so won't be caught by this filter. 2. **Human-like Psi-sensitives**: Anyone who isn't blocked from receiving telepathy, and also has a Psi-sensitivity score of "At least 0". This will basically catch "Most people who aren't a Vaurca, Synthetic, or Diona". It will ignore Humanoids with the Low Psi-sensitivity trait. 3. **Silicons**: Basically all of the possible robots/IPCs. 4. **Silicons + Implants**: It's fairly common practice during Hivebot related events for Storytellers to also include characters with brain implants. This filter automates that by including said brain implant owners in the "Are you a synthetic?" check. 5. **Hivenet**: Pretty simple, people with the Vaurca language. This would be Vaurca, plus anyone with the hivenet implants.
This commit is contained in:
@@ -80,3 +80,17 @@
|
||||
#define isenergy(A) istype(A, /obj/projectile/energy)
|
||||
|
||||
#define istransparentturf(A) (HAS_TRAIT(A, TURF_Z_TRANSPARENT_TRAIT))
|
||||
|
||||
/**
|
||||
* A common layered filter pattern for psionics. Helps with reducing the size of guard clauses.
|
||||
* Includes every psionic check for RECEIVING starting with the Zona Bovina, and ending with Psi-sensitivity.
|
||||
* Set the sensitivity_threshold for your desired minimum Psi-sensitivity to pass.
|
||||
* The most common ones you might want are:
|
||||
* 2 for "Antag-like".
|
||||
* 1 for "Skrell-like".
|
||||
* 0 for "Human-like".
|
||||
*
|
||||
* This one is a define instead of a proc, which gives better code performance.
|
||||
* This define is only usable if user is at least an /atom/movable/, such as /mob/
|
||||
*/
|
||||
#define IS_TELEPATHY_BLOCKED(user, sensitivity_threshold) (!user.has_zona_bovinae() || user.is_psi_blocked() || user.check_psi_sensitivity() < sensitivity_threshold)
|
||||
|
||||
@@ -49,6 +49,7 @@ GLOBAL_LIST_INIT(admin_verbs_admin, list(
|
||||
/client/proc/cmd_admin_local_narrate, //sends text to all mobs within 7 tiles of src.mob
|
||||
/client/proc/cmd_admin_local_screen_text,
|
||||
/client/proc/cmd_admin_global_screen_text,
|
||||
/client/proc/cmd_admin_open_narrate_panel, // Admin logged variant of the narrator panel.
|
||||
/client/proc/cmd_admin_world_narrate, //sends text to all players with no padding,
|
||||
/client/proc/cmd_admin_create_centcom_report,
|
||||
/client/proc/check_ai_laws, //shows AI and borg laws,
|
||||
@@ -309,6 +310,7 @@ GLOBAL_LIST_INIT(admin_verbs_hideable, list(
|
||||
/client/proc/cmd_admin_local_narrate,
|
||||
/client/proc/cmd_admin_local_screen_text,
|
||||
/client/proc/cmd_admin_global_screen_text,
|
||||
/client/proc/cmd_admin_open_narrate_panel, // Admin logged variant of the narrator panel.
|
||||
/client/proc/cmd_admin_world_narrate,
|
||||
/client/proc/cmd_admin_grab_observers,
|
||||
/client/proc/cmd_admin_create_centcom_report,
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
to_chat(src, "Only administrators may use this command.")
|
||||
return
|
||||
|
||||
var/msg = sanitize(input("Message:", "Subtle PM to [M.key]"))
|
||||
var/msg = tgui_input_text(usr, "Enter your subtle message", "Subtle PM to [M.key]")
|
||||
|
||||
if (!msg)
|
||||
return
|
||||
@@ -83,7 +83,7 @@
|
||||
to_chat(src, "Only administrators may use this command.")
|
||||
return
|
||||
|
||||
var/msg = html_decode(sanitize(input("Message:", "Enter the text you wish to appear to everyone:")))
|
||||
var/msg = tgui_input_text(usr, "Enter the text you wish to appear to everyone:", "Message:")
|
||||
|
||||
if (!msg)
|
||||
return
|
||||
@@ -110,7 +110,7 @@
|
||||
else
|
||||
return
|
||||
|
||||
var/msg = html_decode(sanitize(input("Message:", "Enter the text you wish to appear to everyone within seven tiles of you:")))
|
||||
var/msg = tgui_input_text(usr, "Enter the text you wish to appear to everyone within seven tiles of you:", "Message:")
|
||||
if(!msg)
|
||||
return
|
||||
for(var/M in message_mobs)
|
||||
@@ -192,7 +192,7 @@
|
||||
if(!M)
|
||||
return
|
||||
|
||||
var/msg = html_decode(sanitize(input("Message:", "Enter the text you wish to appear to your target:")))
|
||||
var/msg = tgui_input_text(usr, "Enter the text you wish to appear to your target:", "Message:")
|
||||
|
||||
if( !msg )
|
||||
return
|
||||
@@ -1126,3 +1126,18 @@ Traitors and the like can also be revived with the previous role mostly intact.
|
||||
message_admins("[key_name_admin(usr)] sent a pregenerated tip of the round.")
|
||||
log_admin("[key_name(usr)] sent a pregenerated Tip of the Round.")
|
||||
feedback_add_details("admin_verb","FAP")
|
||||
|
||||
// Variant of the narrate panel that is for admins. This is logged to check if people are abusing it.
|
||||
/client/proc/cmd_admin_open_narrate_panel()
|
||||
set category = "Special Verbs"
|
||||
set name = "Narration Panel"
|
||||
|
||||
if (!check_rights(R_ADMIN, TRUE))
|
||||
return
|
||||
|
||||
var/datum/tgui_module/narrate_panel/NP = new /datum/tgui_module/narrate_panel(usr)
|
||||
NP.ui_interact(usr)
|
||||
|
||||
message_admins("[key_name_admin(usr)] used the Narration Panel")
|
||||
log_admin("[key_name(usr)] used the Narration Panel")
|
||||
feedback_add_details("admin_verb", "AONP")
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
var/list/data = list()
|
||||
data["narrate_styles"] = list("danger", "notice", "warning", "alien", "cult")
|
||||
data["narrate_locations"] = list("View", "Range", "Z-Level", "Global")
|
||||
data["narrate_filters"] = list("None", "Skrell-like Psi-sensitives", "Human-like Psi-sensitives", "Silicons", "Silicons + Implants", "Hivenet")
|
||||
return data
|
||||
|
||||
/datum/tgui_module/narrate_panel/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
|
||||
@@ -63,5 +64,65 @@
|
||||
if("Global")
|
||||
mobs_to_message = GLOB.player_list.Copy()
|
||||
|
||||
// Skip the switch if no filter was selected.
|
||||
if (params["narrate_filter"] == "None")
|
||||
for(var/mob/actor in mobs_to_message)
|
||||
to_chat(actor, "<font size=[narrate_size]><span class='[narrate_style]'>[narrate_text]</span></font>")
|
||||
return
|
||||
|
||||
// Create a copy of the message receivers. We have to iterate the copy of the list in order to safely remove entries from the list of message receivers.
|
||||
// Otherwise we'll get memory exceptions.
|
||||
var/list/filtered_list = mobs_to_message.Copy()
|
||||
var/narrate_filter = params["narrate_filter"]
|
||||
switch(narrate_filter)
|
||||
if ("Skrell-like Psi-sensitives")
|
||||
for(var/mob/filteree in filtered_list)
|
||||
// This will check for anyone who is capable of receiving and interpreting telepathic messages.
|
||||
// It will include Skrell who don't have a mindshield or the Low Psi-sensitivity trait.
|
||||
// It will also include characters who have Psi-receivers, the High Psi-sensitivity trait, or are under the effects of Psycho-nootropic drugs.
|
||||
if (IS_TELEPATHY_BLOCKED(filteree, PSI_RANK_SENSITIVE))
|
||||
mobs_to_message.Remove(filteree)
|
||||
continue
|
||||
if ("Human-like Psi-sensitives")
|
||||
for(var/mob/filteree in filtered_list)
|
||||
// This will check for anyone who has a Zona Bovina capable of hearing psionics at all.
|
||||
if (IS_TELEPATHY_BLOCKED(filteree, 0))
|
||||
mobs_to_message.Remove(filteree)
|
||||
continue
|
||||
if ("Silicons")
|
||||
for(var/mob/filteree in filtered_list)
|
||||
// List will include Borgs, Robots, Shipbounds, pAIs, and IPCs.
|
||||
if(issilicon(filteree) || isipc(filteree) || ispAI(filteree))
|
||||
continue
|
||||
|
||||
// Remove anyone not made of silicon.
|
||||
mobs_to_message.Remove(filteree)
|
||||
if ("Silicons + Implants")
|
||||
for(var/mob/filteree in filtered_list)
|
||||
// List will include Borgs, Robots, Shipbounds, pAIs, and IPCs.
|
||||
if(issilicon(filteree) || isipc(filteree) || ispAI(filteree))
|
||||
continue
|
||||
|
||||
|
||||
if (!ishuman(filteree))
|
||||
// The mob in question can't have a brain at all, filter it early.
|
||||
mobs_to_message.Remove(filteree)
|
||||
continue
|
||||
|
||||
var/mob/living/carbon/human/brain_haver = filteree
|
||||
// Check for silicon brain implants.
|
||||
var/obj/item/organ/internal/brain = brain_haver.internal_organs_by_name[BP_BRAIN]
|
||||
if (brain && ((brain.status & ORGAN_ASSISTED) || (brain.status & ORGAN_ROBOT)))
|
||||
continue
|
||||
|
||||
// Remove anyone not made of silicon or doesn't have a cranial implant.
|
||||
mobs_to_message.Remove(filteree)
|
||||
if ("Hivenet")
|
||||
for(var/mob/filteree in filtered_list)
|
||||
// Filter anyone who doesn't have the Vaurca language at all.
|
||||
if (!(GLOB.all_languages[LANGUAGE_VAURCA] in filteree.languages))
|
||||
mobs_to_message.Remove(filteree)
|
||||
continue
|
||||
|
||||
for(var/mob/actor in mobs_to_message)
|
||||
to_chat(actor, "<font size=[narrate_size]><span class='[narrate_style]'>[narrate_text]</span></font>")
|
||||
|
||||
@@ -74,3 +74,15 @@
|
||||
|
||||
/mob/living/simple_animal/is_psi_pingable()
|
||||
return psi_pingable
|
||||
|
||||
/**
|
||||
* A common layered filter pattern for psionics. Helps with reducing the size of guard clauses.
|
||||
* Includes every psionic check for RECEIVING starting with the Zona Bovina, and ending with Psi-sensitivity.
|
||||
* Set the sensitivity_threshold for your desired minimum Psi-sensitivity to pass.
|
||||
* The most common ones you might want are:
|
||||
* 2 for "Antag-like".
|
||||
* 1 for "Skrell-like".
|
||||
* 0 for "Human-like".
|
||||
*/
|
||||
/atom/movable/proc/is_telepathy_blocked(var/sensitivity_threshold = 0)
|
||||
return (!has_zona_bovinae() || is_psi_blocked() || check_psi_sensitivity() < sensitivity_threshold)
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
author: Hellfirejag
|
||||
delete-after: True
|
||||
changes:
|
||||
- rscadd: "The Narrator panel now includes optional filter selections, allowing admins and storytellers to narrate to certain kinds of players, such as Synthetics or Psi-sensitives."
|
||||
- rscadd: "The Narration panel can now be found in the Special Verbs tab for admins. Its usage will be logged."
|
||||
- bugfix: "Fixed issues with admin announcements being improperly sanitized."
|
||||
@@ -13,6 +13,7 @@ import { Window } from '../layouts';
|
||||
export type NarrateData = {
|
||||
narrate_styles: string[];
|
||||
narrate_locations: string[];
|
||||
narrate_filters: string[];
|
||||
};
|
||||
|
||||
export const NarratePanel = (props, context) => {
|
||||
@@ -42,6 +43,11 @@ export const NarratePanel = (props, context) => {
|
||||
'narrateLocation',
|
||||
'View',
|
||||
);
|
||||
const [narrateFilter, setNarrateFilter] = useLocalState(
|
||||
context,
|
||||
'narrateFilter',
|
||||
'None',
|
||||
);
|
||||
|
||||
return (
|
||||
<Window resizable theme="admin" width={600} height={300}>
|
||||
@@ -61,6 +67,7 @@ export const NarratePanel = (props, context) => {
|
||||
narrate_range: narrateRange,
|
||||
narrate_style: narrateStyle,
|
||||
narrate_location: narrateLocation,
|
||||
narrate_filter: narrateFilter,
|
||||
})
|
||||
}
|
||||
/>
|
||||
@@ -116,6 +123,15 @@ export const NarratePanel = (props, context) => {
|
||||
onSelected={(value) => setNarrateLocation(value)}
|
||||
/>
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Filter">
|
||||
<Dropdown
|
||||
options={data.narrate_filters}
|
||||
selected={data.narrate_filters[1]}
|
||||
displayText={narrateFilter}
|
||||
width="50%"
|
||||
onSelected={(value) => setNarrateFilter(value)}
|
||||
/>
|
||||
</LabeledList.Item>
|
||||
</LabeledList>
|
||||
</Section>
|
||||
</Window.Content>
|
||||
|
||||
Reference in New Issue
Block a user