mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-08-23 21:18:37 +01:00
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:
@@ -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"
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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}> {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>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user