The Link to the Changeling Hivemind has been Reestablished (#70193)

Readds the changeling hivemind, but in a simplified form.

    There is no linglinking. It was rarely used and imma be real with you, stupid.
    Hivemind isn't an unlocked ability, it is just a default.
    Bz still mutes lings
    Fallen changelings now get an agonizing message about being locked out. We love fallen changeling flavor
    Changelings have their Honorific names back! Hello, Mr. Omnicron!
    This does NOT readd changeling team objectives, or really anything encouraging teaming beyond the hivemind itself.

    I think antagonists have generally lost a lot of power versus the station, and this is one big way to pump up the heat for this antag in particular. Changelings, given an ability to possibly team up, are much MUCH scarier.
    One reason it was removed years ago was because teaming was too often, but back then there were entire gamemodes about changelings having team objectives and many other things to encourage it. I'm bringing us back to a point BEFORE all those design ideas were explored, because I don't think they worked out.

Add information about the changeling hivemind, and the dangers of being turned into a fallen changeling if you get betrayed to the Changeling Antag Info UI

Changelog

add: Changelings once again have reestablished their changeling hivemind, and can secretly communicate between each other.
fix: Fixed up the Changeling UI a bit, like for example some dimmers would never render.
This commit is contained in:
tralezab
2022-09-29 18:00:23 -07:00
committed by GitHub
parent cd8d67df68
commit 4e041ce765
10 changed files with 163 additions and 53 deletions
+4
View File
@@ -45,6 +45,10 @@
#define MODE_ALIEN "alientalk"
#define MODE_HOLOPAD "holopad"
#define MODE_CHANGELING "changeling"
#define MODE_KEY_CHANGELING "g"
#define MODE_TOKEN_CHANGELING ":g"
#define MODE_VOCALCORDS "cords"
#define MODE_KEY_VOCALCORDS "x"
+2
View File
@@ -748,6 +748,8 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
// unique trait sources, still defines
#define STATUE_MUTE "statue"
#define CHANGELING_DRAIN "drain"
/// changelings with this trait can no longer talk over the hivemind
#define CHANGELING_HIVEMIND_MUTE "ling_mute"
#define ABYSSAL_GAZE_BLIND "abyssal_gaze"
#define HIGHLANDER "highlander"
#define TRAIT_HULK "hulk"
+2 -1
View File
@@ -30,8 +30,9 @@ GLOBAL_LIST_INIT(martial_prefix, world.file2list("strings/names/martial_prefix.t
GLOBAL_LIST_INIT(adverbs, world.file2list("strings/names/adverbs.txt"))
GLOBAL_LIST_INIT(adjectives, world.file2list("strings/names/adjectives.txt"))
GLOBAL_LIST_INIT(gross_adjectives, world.file2list("strings/names/gross_adjectives.txt"))
GLOBAL_LIST_INIT(hive_names, world.file2list("strings/names/hive_names.txt"))
GLOBAL_LIST_INIT(dream_strings, world.file2list("strings/dreamstrings.txt"))
GLOBAL_LIST_INIT(vampire_house_names, world.file2list("strings/names/vampire_house_names.txt"))
GLOBAL_LIST_INIT(greek_alphabet, world.file2list("strings/greek_alphabet.txt"))
GLOBAL_LIST_INIT(hive_names, world.file2list("strings/names/hive_names.txt"))
//loaded on startup because of "
//would include in rsc if ' was used
+38
View File
@@ -9,6 +9,44 @@
/datum/saymode/proc/handle_message(mob/living/user, message, datum/language/language)
return TRUE
/datum/saymode/changeling
key = MODE_KEY_CHANGELING
mode = MODE_CHANGELING
/datum/saymode/changeling/handle_message(mob/living/user, message, datum/language/language)
//we can send the message
if(!user.mind)
return FALSE
if(user.mind.has_antag_datum(/datum/antagonist/fallen_changeling))
to_chat(user, span_changeling("<b>We're cut off from the hivemind! We've lost everything! EVERYTHING!!</b>"))
return FALSE
var/datum/antagonist/changeling/ling_sender = user.mind.has_antag_datum(/datum/antagonist/changeling)
if(!ling_sender)
return FALSE
if(HAS_TRAIT(user, CHANGELING_HIVEMIND_MUTE))
to_chat(user, span_warning("The poison in the air hinders our ability to interact with the hivemind."))
return FALSE
user.log_talk(message, LOG_SAY, tag="changeling [ling_sender.changelingID]")
var/msg = span_changeling("<b>[ling_sender.changelingID]:</b> [message]")
//the recipients can recieve the message
for(var/datum/antagonist/changeling/ling_reciever in GLOB.antagonists)
if(!ling_reciever.owner)
continue
var/mob/living/ling_mob = ling_reciever.owner.current
//removes types that override the presence of being changeling (for example, borged lings still can't hivemind chat)
if(!isliving(ling_mob) || issilicon(ling_mob) || isbrain(ling_mob))
continue
// can't recieve messages on the hivemind right now
if(HAS_TRAIT(ling_mob, CHANGELING_HIVEMIND_MUTE))
continue
to_chat(ling_mob, msg)
for(var/mob/dead/ghost as anything in GLOB.dead_mob_list)
to_chat(ghost, "[FOLLOW_LINK(ghost, user)] [msg]")
return FALSE
/datum/saymode/xeno
key = "a"
mode = MODE_ALIEN
@@ -1,5 +1,4 @@
#define ABDUCTOR_MAX_TEAMS 4
GLOBAL_LIST_INIT(possible_abductor_names, list("Alpha","Beta","Gamma","Delta","Epsilon","Zeta","Eta","Theta","Iota","Kappa","Lambda","Mu","Nu","Xi","Omicron","Pi","Rho","Sigma","Tau","Upsilon","Phi","Chi","Psi","Omega"))
/datum/antagonist/abductor
name = "\improper Abductor"
@@ -157,7 +156,7 @@ GLOBAL_LIST_INIT(possible_abductor_names, list("Alpha","Beta","Gamma","Delta","E
/datum/team/abductor_team/New()
..()
team_number = team_count++
name = "Mothership [pick(GLOB.possible_abductor_names)]" //TODO Ensure unique and actual alieny names
name = "Mothership [pick(GLOB.greek_letters)]" //TODO Ensure unique and actual alieny names
add_objective(new /datum/objective/experiment)
/datum/team/abductor_team/roundend_report()
@@ -46,6 +46,8 @@
var/chem_recharge_slowdown = 0
/// The range this ling can sting things.
var/sting_range = 2
/// Changeling name, what other lings see over the hivemind when talking.
var/changelingID = "Changeling"
/// The number of genetics points (to buy powers) this ling currently has.
var/genetic_points = 10
/// The max number of genetics points (to buy powers) this ling can have..
@@ -78,6 +80,9 @@
/// Static typecache of all changeling powers that are usable.
var/static/list/all_powers = typecacheof(/datum/action/changeling, ignore_root_path = TRUE)
/// Static list of possible ids. Initialized into the greek alphabet the first time it is used
var/static/list/possible_changeling_IDs
/// Satic list of what each slot associated with (in regard to changeling flesh items).
var/static/list/slot2type = list(
"head" = /obj/item/clothing/head/changeling,
@@ -117,6 +122,7 @@
return ..()
/datum/antagonist/changeling/on_gain()
generate_name()
create_emporium()
create_innate_actions()
create_initial_profile()
@@ -158,6 +164,22 @@
our_ling_brain.organ_flags &= ~ORGAN_VITAL
our_ling_brain.decoy_override = TRUE
/datum/antagonist/changeling/proc/generate_name()
var/honorific
if(owner.current.gender == FEMALE)
honorific = "Ms."
else if(owner.current.gender == MALE)
honorific = "Mr."
else
honorific = "Mx."
if(!possible_changeling_IDs)
possible_changeling_IDs = GLOB.greek_letters.Copy()
if(possible_changeling_IDs.len)
changelingID = "[honorific] [pick_n_take(possible_changeling_IDs)]"
else
changelingID = "[honorific] [rand(1,999)]"
/datum/antagonist/changeling/proc/on_hud_created(datum/source)
SIGNAL_HANDLER
@@ -941,6 +963,7 @@
memories += list(list("name" = memory_key, "story" = stolen_memories[memory_key]))
data["memories"] = memories
data["true_name"] = changelingID
data["hive_name"] = hive_name
data["stolen_antag_info"] = antag_memory
data["objectives"] = get_objectives()
+1
View File
@@ -64,6 +64,7 @@ GLOBAL_LIST_INIT(department_radio_keys, list(
*/
GLOBAL_LIST_INIT(message_modes_stat_limits, list(
MODE_INTERCOM = HARD_CRIT,
MODE_CHANGELING = HARD_CRIT,
MODE_ALIEN = HARD_CRIT,
MODE_BINARY = HARD_CRIT, //extra stat check on human/binarycheck()
MODE_MONKEY = HARD_CRIT,
-3
View File
@@ -92,9 +92,6 @@ global procs
hivecheck()
Returns TRUE if the mob can hear and talk in the alien hivemind.
lingcheck()
Returns a bitflag representing who is trying to talk to the changeling hivemind.
/mob/living
say(message, bubble_type, var/list/spans, sanitize, datum/language/languag, ignore_spam, forced)
The say() of mob_living is significantly more complex than that of objects.
@@ -2378,6 +2378,14 @@
metabolization_rate = 0.2 * REAGENTS_METABOLISM
chemical_flags = REAGENT_CAN_BE_SYNTHESIZED|REAGENT_NO_RANDOM_RECIPE
/datum/reagent/bz_metabolites/on_mob_metabolize(mob/living/ling)
..()
ADD_TRAIT(ling, CHANGELING_HIVEMIND_MUTE, type)
/datum/reagent/bz_metabolites/on_mob_end_metabolize(mob/living/ling)
..()
REMOVE_TRAIT(ling, CHANGELING_HIVEMIND_MUTE, type)
/datum/reagent/bz_metabolites/on_mob_life(mob/living/carbon/target, delta_time, times_fired)
if(target.mind)
var/datum/antagonist/changeling/changeling = target.mind.has_antag_datum(/datum/antagonist/changeling)
@@ -1,6 +1,6 @@
import { multiline } from 'common/string';
import { useBackend, useSharedState } from '../backend';
import { Box, Button, Dimmer, Dropdown, Section, Stack } from '../components';
import { Button, Dimmer, Dropdown, Section, Stack, NoticeBox } from '../components';
import { Window } from '../layouts';
const hivestyle = {
@@ -28,6 +28,16 @@ const storestyle = {
fontWeight: 'bold',
};
const hivemindstyle = {
color: 'violet',
fontWeight: 'bold',
};
const fallenstyle = {
color: 'black',
fontWeight: 'bold',
};
type Objective = {
count: number;
name: string;
@@ -40,12 +50,46 @@ type Memory = {
};
type Info = {
true_name: string;
hive_name: string;
stolen_antag_info: string;
memories: Memory[];
objectives: Objective[];
};
export const AntagInfoChangeling = (props, context) => {
return (
<Window width={720} height={720}>
<Window.Content
style={{
'backgroundImage': 'none',
}}>
<Stack vertical fill>
<Stack.Item maxHeight={13.2}>
<IntroductionSection />
</Stack.Item>
<Stack.Item grow={4}>
<AbilitiesSection />
</Stack.Item>
<Stack.Item>
<HivemindSection />
</Stack.Item>
<Stack.Item grow={3}>
<Stack fill>
<Stack.Item grow basis={0}>
<MemoriesSection />
</Stack.Item>
<Stack.Item grow basis={0}>
<VictimPatternsSection />
</Stack.Item>
</Stack>
</Stack.Item>
</Stack>
</Window.Content>
</Window>
);
};
const ObjectivePrintout = (props, context) => {
const { data } = useBackend<Info>(context);
const { objectives } = data;
@@ -64,9 +108,37 @@ const ObjectivePrintout = (props, context) => {
);
};
const HivemindSection = (props, context) => {
const { act, data } = useBackend<Info>(context);
const { true_name } = data;
return (
<Section fill title="Hivemind">
<Stack vertical fill>
<Stack.Item textColor="label">
All Changelings, regardless of origin, are linked together by the{' '}
<span style={hivemindstyle}>hivemind</span>. You may communicate to
other Changelings under your mental alias,{' '}
<span style={hivemindstyle}>{true_name}</span>, by starting a message
with <span style={hivemindstyle}>:g</span>. Work together, and you
will bring the station to new heights of terror.
</Stack.Item>
<Stack.Item>
<NoticeBox danger>
Other Changelings are strong allies, but some Changelings may betray
you. Changelings grow in power greatly by absorbing their kind, and
getting absorbed by another Changeling will leave you as a{' '}
<span style={fallenstyle}>Fallen Changeling</span>. There is no
greater humiliation.
</NoticeBox>
</Stack.Item>
</Stack>
</Section>
);
};
const IntroductionSection = (props, context) => {
const { act, data } = useBackend<Info>(context);
const { hive_name, objectives } = data;
const { true_name, hive_name, objectives } = data;
return (
<Section
fill
@@ -74,8 +146,8 @@ const IntroductionSection = (props, context) => {
scrollable={!!objectives && objectives.length > 4}>
<Stack vertical fill>
<Stack.Item fontSize="25px">
You are the Changeling from the
<span style={hivestyle}>&ensp;{hive_name}</span>.
You are {true_name} from the
<span style={hivestyle}> {hive_name}</span>.
</Stack.Item>
<Stack.Item>
<ObjectivePrintout />
@@ -136,8 +208,8 @@ const AbilitiesSection = (props, context) => {
};
const MemoriesSection = (props, context) => {
const { act, data } = useBackend<Info>(context);
const { memories, stolen_antag_info } = data;
const { data } = useBackend<Info>(context);
const { memories } = data;
const [selectedMemory, setSelectedMemory] = useSharedState(
context,
'memory',
@@ -151,7 +223,7 @@ const MemoriesSection = (props, context) => {
return (
<Section
fill
scrollable={!!memories}
scrollable={!!memories && !!memories.length}
title="Stolen Memories"
buttons={
<Button
@@ -164,14 +236,8 @@ const MemoriesSection = (props, context) => {
`}
/>
}>
{(!memories && (
<Box>
{!stolen_antag_info && (
<Dimmer mr="-100%" bold>
You need to absorb a victim first!
</Dimmer>
)}
</Box>
{(!!memories && !memories.length && (
<Dimmer fontSize="20px">Absorb a victim first!</Dimmer>
)) || (
<Stack vertical>
<Stack.Item>
@@ -199,38 +265,9 @@ const VictimPatternsSection = (props, context) => {
fill
scrollable={!!stolen_antag_info}
title="Additional Stolen Information">
{!!stolen_antag_info && stolen_antag_info}
{(!!stolen_antag_info && stolen_antag_info) || (
<Dimmer fontSize="20px">Absorb a victim first!</Dimmer>
)}
</Section>
);
};
export const AntagInfoChangeling = (props, context) => {
const { data } = useBackend<Info>(context);
return (
<Window width={620} height={580}>
<Window.Content
style={{
'backgroundImage': 'none',
}}>
<Stack vertical fill>
<Stack.Item maxHeight={13.2}>
<IntroductionSection />
</Stack.Item>
<Stack.Item grow={4}>
<AbilitiesSection />
</Stack.Item>
<Stack.Item grow={3}>
<Stack fill>
<Stack.Item grow basis={0}>
<MemoriesSection />
</Stack.Item>
<Stack.Item grow ml={0} basis={0}>
<VictimPatternsSection />
</Stack.Item>
</Stack>
</Stack.Item>
</Stack>
</Window.Content>
</Window>
);
};