From 08ab5d27312d236593eabdb27fb23dccbf8283e6 Mon Sep 17 00:00:00 2001
From: Mothblocks <35135081+Mothblocks@users.noreply.github.com>
Date: Wed, 29 Nov 2023 14:02:51 -0800
Subject: [PATCH] Blood brothers is now a single person conversion antagonist
(#79971)
## About The Pull Request
Instead of choosing 2-3 brothers, *one* person will be selected and
given a flash which can convert one other person over. In accordance to
the existing 10% chance for 3 members, there is a 10% chance that the
first person converted will receive a flash of their own.
Expectation is people will flash a friend or a robust guy or whatever.
My intent is primarily to see if this kind of blood brothers is more
enjoyable to play with/against, and if their inclusion in a round
increases the general chaos of it. My theory is that since most likely
blood brothers will be people who know each other, that it can become
more consistently interesting to the rest of the crew. That or they just
murderbone together idk
Fikou and head admins said they wanted this to replace rather than add
which I agree with.
## Why It's Good For The Game
Keeps the sandboxy aspect of blood brothers (no uplink) while likely
making it more enjoyable to play. Conversion is equally as simple as
revs for the user, and is just as intuitive to the one being converted
since there are no new mechanics thrown in your face.
Blood brothers is currently disabled everywhere on the main servers
except for MRP. I think this form will be more appealing to all
rulesets. If left enabled, Dynamic now has more antagonists to make
rounds diverse with and I want that
## Changelog
:cl:
add: Instead of teaming up random people together, blood brothers will
now start out with one player and let them convert a single other person
over to blood brother using a flash.
/:cl:
---
.../signals/signals_mob/signals_mob_main.dm | 1 +
.../components/can_flash_from_behind.dm | 22 +++
code/datums/memory/general_memories.dm | 12 ++
.../dynamic/dynamic_rulesets_roundstart.dm | 47 +++----
code/modules/antagonists/brother/brother.dm | 132 +++++++++++-------
code/modules/antagonists/cult/cult.dm | 15 +-
.../antagonists/revolution/revolution.dm | 41 ++----
code/modules/mob/living/carbon/inventory.dm | 18 +++
tgstation.dme | 1 +
.../tgui/interfaces/AntagInfoBrother.tsx | 2 +-
10 files changed, 172 insertions(+), 119 deletions(-)
create mode 100644 code/datums/components/can_flash_from_behind.dm
diff --git a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_main.dm b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_main.dm
index b564a114b02cc..0ef458cece958 100644
--- a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_main.dm
+++ b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_main.dm
@@ -212,6 +212,7 @@
/// Return to stop the flash entirely
#define STOP_FLASH (1<<3)
/// from /obj/item/assembly/flash/flash_carbon, to the mob flashing another carbon
+/// (mob/living/carbon/flashed, obj/item/assembly/flash/flash, deviation (from code/__DEFINES/mobs.dm))
#define COMSIG_MOB_SUCCESSFUL_FLASHED_CARBON "mob_success_flashed_carbon"
/// from mob/get_status_tab_items(): (list/items)
diff --git a/code/datums/components/can_flash_from_behind.dm b/code/datums/components/can_flash_from_behind.dm
new file mode 100644
index 0000000000000..c443d160dfbd3
--- /dev/null
+++ b/code/datums/components/can_flash_from_behind.dm
@@ -0,0 +1,22 @@
+/// This mob can flash others from behind and still get at least a partial
+// Component and not element because elements can't stack.
+// I don't want to have a bunch of helpers for that. We need to do this generally
+// because this keeps coming up.
+/datum/component/can_flash_from_behind
+ dupe_mode = COMPONENT_DUPE_SOURCES
+
+/datum/component/can_flash_from_behind/Initialize()
+ if (!ismob(parent))
+ return COMPONENT_INCOMPATIBLE
+
+/datum/component/can_flash_from_behind/RegisterWithParent()
+ RegisterSignal(parent, COMSIG_MOB_PRE_FLASHED_CARBON, PROC_REF(on_pre_flashed_carbon))
+
+/datum/component/can_flash_from_behind/UnregisterFromParent()
+ UnregisterSignal(parent, COMSIG_MOB_PRE_FLASHED_CARBON)
+
+/datum/component/can_flash_from_behind/proc/on_pre_flashed_carbon(source, flashed, flash, deviation)
+ SIGNAL_HANDLER
+
+ // Always partial flash at the very least
+ return (deviation == DEVIATION_FULL) ? DEVIATION_OVERRIDE_PARTIAL : NONE
diff --git a/code/datums/memory/general_memories.dm b/code/datums/memory/general_memories.dm
index 3e5eb05cf4026..c71014b63b46d 100644
--- a/code/datums/memory/general_memories.dm
+++ b/code/datums/memory/general_memories.dm
@@ -915,6 +915,18 @@
"[antagonist_name] lifts an odd device to [protagonist_name]'s eyes and flashes him, imprinting murderous instructions.",
)
+/// Who converted into a blood brother
+/datum/memory/recruited_by_blood_brother
+
+/datum/memory/recruited_by_blood_brother/get_names()
+ return list("[protagonist_name] is converted into a blood brother by [antagonist_name]")
+
+/datum/memory/recruited_by_blood_brother/get_starts()
+ return list(
+ "[antagonist_name] acts just a bit too friendly with [protagonist_name], moments away from converting them into a blood brother.",
+ "[protagonist_name] is brought into [antagonist_name]'s life of crime and espionage.",
+ )
+
/// Saw someone play Russian Roulette.
/datum/memory/witnessed_gods_wrath
memory_flags = MEMORY_CHECK_BLINDNESS|MEMORY_SKIP_UNCONSCIOUS
diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm
index a90eba738c146..767087467233c 100644
--- a/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm
+++ b/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm
@@ -112,45 +112,34 @@ GLOBAL_VAR_INIT(revolutionary_win, FALSE)
JOB_AI,
JOB_CYBORG,
)
- required_candidates = 2
- weight = 2
- cost = 12
+ weight = 5
+ cost = 8
scaling_cost = 15
requirements = list(40,30,30,20,20,15,15,15,10,10)
- antag_cap = 2 // Can pick 3 per team, but rare enough it doesn't matter.
- var/list/datum/team/brother_team/pre_brother_teams = list()
- var/const/min_team_size = 2
-
-/datum/dynamic_ruleset/roundstart/traitorbro/forget_startup()
- pre_brother_teams = list()
- return ..()
+ antag_cap = 1
/datum/dynamic_ruleset/roundstart/traitorbro/pre_execute(population)
. = ..()
- var/num_teams = (get_antag_cap(population)/min_team_size) * (scaled_times + 1) // 1 team per scaling
- for(var/j = 1 to num_teams)
- if(candidates.len < min_team_size || candidates.len < required_candidates)
+
+ for (var/_ in 1 to get_antag_cap(population) * (scaled_times + 1))
+ var/mob/candidate = pick_n_take(candidates)
+ if (isnull(candidate))
break
- var/datum/team/brother_team/team = new
- var/team_size = prob(10) ? min(3, candidates.len) : 2
- for(var/k = 1 to team_size)
- var/mob/bro = pick_n_take(candidates)
- assigned += bro.mind
- team.add_member(bro.mind)
- bro.mind.special_role = "brother"
- bro.mind.restricted_roles = restricted_roles
- GLOB.pre_setup_antags += bro.mind
- pre_brother_teams += team
+
+ assigned += candidate.mind
+ candidate.mind.restricted_roles = restricted_roles
+ GLOB.pre_setup_antags += candidate.mind
+
return TRUE
/datum/dynamic_ruleset/roundstart/traitorbro/execute()
- for(var/datum/team/brother_team/team in pre_brother_teams)
- team.pick_meeting_area()
+ for (var/datum/mind/mind in assigned)
+ var/datum/team/brother_team/team = new
+ team.add_member(mind)
team.forge_brother_objectives()
- for(var/datum/mind/M in team.members)
- M.add_antag_datum(/datum/antagonist/brother, team)
- GLOB.pre_setup_antags -= M
- team.update_name()
+ mind.add_antag_datum(/datum/antagonist/brother, team)
+ GLOB.pre_setup_antags -= mind
+
return TRUE
//////////////////////////////////////////////
diff --git a/code/modules/antagonists/brother/brother.dm b/code/modules/antagonists/brother/brother.dm
index 67fffb4fa19fa..baf6b30f6b178 100644
--- a/code/modules/antagonists/brother/brother.dm
+++ b/code/modules/antagonists/brother/brother.dm
@@ -7,9 +7,10 @@
hijack_speed = 0.5
ui_name = "AntagInfoBrother"
suicide_cry = "FOR MY BROTHER!!"
- var/datum/team/brother_team/team
antag_moodlet = /datum/mood_event/focused
hardcore_random_bonus = TRUE
+ VAR_PRIVATE
+ datum/team/brother_team/team
/datum/antagonist/brother/create_team(datum/team/brother_team/new_team)
if(!new_team)
@@ -25,12 +26,64 @@
objectives += team.objectives
owner.special_role = special_role
finalize_brother()
+
+ var/is_first_brother = team.members.len == 1
+ team.brothers_left -= 1
+
+ if (is_first_brother || team.brothers_left > 0)
+ var/mob/living/carbon/carbon_owner = owner.current
+ if (istype(carbon_owner))
+ carbon_owner.equip_conspicuous_item(new /obj/item/assembly/flash)
+ carbon_owner.AddComponentFrom(REF(src), /datum/component/can_flash_from_behind)
+ RegisterSignal(carbon_owner, COMSIG_MOB_SUCCESSFUL_FLASHED_CARBON, PROC_REF(on_mob_successful_flashed_carbon))
+
+ if (!is_first_brother)
+ to_chat(carbon_owner, span_boldwarning("The Syndicate have higher expectations from you than others. They have granted you an extra flash to convert one other person."))
+
return ..()
/datum/antagonist/brother/on_removal()
owner.special_role = null
+ owner.RemoveComponentSource(REF(src), /datum/component/can_flash_from_behind)
+ UnregisterSignal(owner, COMSIG_MOB_SUCCESSFUL_FLASHED_CARBON)
+
return ..()
+/datum/antagonist/brother/proc/on_mob_successful_flashed_carbon(mob/living/source, mob/living/carbon/flashed, obj/item/assembly/flash/flash)
+ SIGNAL_HANDLER
+
+ if (flashed.stat == DEAD)
+ return
+
+ if (flashed.stat != CONSCIOUS)
+ flashed.balloon_alert(source, "unconscious!")
+ return
+
+ if (isnull(flashed.mind) || !GET_CLIENT(flashed))
+ flashed.balloon_alert(source, "[flashed.p_their()] mind is vacant!")
+ return
+
+ if (flashed.mind.has_antag_datum(/datum/antagonist/brother))
+ flashed.balloon_alert(source, "[flashed.p_theyre()] loyal to someone else!")
+ return
+
+ if (HAS_TRAIT(flashed, TRAIT_MINDSHIELD) || flashed.mind.assigned_role?.departments_bitflags & DEPARTMENT_BITFLAG_SECURITY)
+ flashed.balloon_alert(source, "[flashed.p_they()] resist!")
+ return
+
+ flashed.mind.add_antag_datum(/datum/antagonist/brother, team)
+
+ flashed.balloon_alert(source, "converted")
+ flash.burn_out()
+ flashed.mind.add_memory( \
+ /datum/memory/recruited_by_blood_brother, \
+ protagonist = flashed, \
+ antagonist = owner.current, \
+ )
+
+ UnregisterSignal(source, COMSIG_MOB_SUCCESSFUL_FLASHED_CARBON)
+ source.RemoveComponentSource(REF(src), /datum/component/can_flash_from_behind)
+
/datum/antagonist/brother/antag_panel_data()
return "Conspirators : [get_brother_names()]"
@@ -74,74 +127,36 @@
brother_text += ", "
return brother_text
-/datum/antagonist/brother/proc/give_meeting_area()
- if(!owner.current || !team || !team.meeting_area)
- return
- to_chat(owner.current, "Your designated meeting area: [team.meeting_area]")
- antag_memory += "Meeting Area: [team.meeting_area]
"
-
/datum/antagonist/brother/greet()
- var/brother_text = get_brother_names()
- to_chat(owner.current, span_alertsyndie("You are the [owner.special_role] of [brother_text]."))
- to_chat(owner.current, "The Syndicate only accepts those that have proven themselves. Prove yourself and prove your [team.member_name]s by completing your objectives together!")
+ to_chat(owner.current, span_alertsyndie("You are the [owner.special_role]."))
owner.announce_objectives()
- give_meeting_area()
/datum/antagonist/brother/proc/finalize_brother()
owner.current.playsound_local(get_turf(owner.current), 'sound/ambience/antag/tatoralert.ogg', 100, FALSE, pressure_affected = FALSE, use_reverb = FALSE)
team.update_name()
/datum/antagonist/brother/admin_add(datum/mind/new_owner,mob/admin)
- //show list of possible brothers
- var/list/candidates = list()
- for(var/mob/living/L in GLOB.alive_mob_list)
- if(!L.mind || L.mind == new_owner || !can_be_owned(L.mind))
- continue
- candidates[L.mind.name] = L.mind
-
- sortTim(candidates, GLOBAL_PROC_REF(cmp_text_asc))
- var/choice = tgui_input_list(admin, "Choose the blood brother.", "Brother", candidates)
- if(!choice)
- return
- var/datum/mind/bro = candidates[choice]
- var/datum/team/brother_team/T = new
- T.add_member(new_owner)
- T.add_member(bro)
- T.pick_meeting_area()
- T.forge_brother_objectives()
- new_owner.add_antag_datum(/datum/antagonist/brother,T)
- bro.add_antag_datum(/datum/antagonist/brother, T)
- message_admins("[key_name_admin(admin)] made [key_name_admin(new_owner)] and [key_name_admin(bro)] into blood brothers.")
- log_admin("[key_name(admin)] made [key_name(new_owner)] and [key_name(bro)] into blood brothers.")
+ var/datum/team/brother_team/team = new
+ team.add_member(new_owner)
+ new_owner.add_antag_datum(/datum/antagonist/brother, team)
+ message_admins("[key_name_admin(admin)] made [key_name_admin(new_owner)] into a blood brother.")
+ log_admin("[key_name(admin)] made [key_name(new_owner)] into a blood brother.")
/datum/antagonist/brother/ui_static_data(mob/user)
var/list/data = list()
data["antag_name"] = name
data["objectives"] = get_objectives()
- data["brothers"] = get_brother_names()
return data
/datum/team/brother_team
name = "\improper Blood Brothers"
member_name = "blood brother"
- ///Selected meeting area given to the team members
- var/meeting_area
- ///List of meeting areas that are randomly selected.
- var/static/meeting_areas = list(
- "The Bar",
- "Dorms",
- "Escape Dock",
- "Arrivals",
- "Holodeck",
- "Primary Tool Storage",
- "Recreation Area",
- "Chapel",
- "Library",
- )
+ var/brothers_left = 2
-/datum/team/brother_team/proc/pick_meeting_area()
- meeting_area = pick(meeting_areas)
- meeting_areas -= meeting_area
+/datum/team/brother_team/New()
+ . = ..()
+ if (prob(10))
+ brothers_left += 1
/datum/team/brother_team/proc/update_name()
var/list/last_names = list()
@@ -149,10 +164,16 @@
var/list/split_name = splittext(team_minds.name," ")
last_names += split_name[split_name.len]
- name = "[initial(name)] of " + last_names.Join(" & ")
+ if (last_names.len == 1)
+ name = "[last_names[1]]'s Isolated Intifada"
+ else
+ name = "[initial(name)] of " + last_names.Join(" & ")
/datum/team/brother_team/proc/forge_brother_objectives()
objectives = list()
+
+ add_objective(new /datum/objective/convert_brother)
+
var/is_hijacker = prob(10)
for(var/i = 1 to max(1, CONFIG_GET(number/brother_objectives_amount) + (members.len > 2) - is_hijacker))
forge_single_objective()
@@ -172,3 +193,12 @@
add_objective(new /datum/objective/assassinate, needs_target = TRUE)
else
add_objective(new /datum/objective/steal, needs_target = TRUE)
+
+/datum/objective/convert_brother
+ name = "convert brother"
+ explanation_text = "Convert someone else using your flash."
+ admin_grantable = FALSE
+ martyr_compatible = TRUE
+
+/datum/objective/convert_brother/check_completion()
+ return length(team?.members) > 1
diff --git a/code/modules/antagonists/cult/cult.dm b/code/modules/antagonists/cult/cult.dm
index 0f43b5ae29ef4..9cd24f8e707c7 100644
--- a/code/modules/antagonists/cult/cult.dm
+++ b/code/modules/antagonists/cult/cult.dm
@@ -151,20 +151,13 @@
///Attempts to make a new item and put it in a potential inventory slot in the provided mob.
/datum/antagonist/cult/proc/cult_give_item(obj/item/item_path, mob/living/carbon/human/mob)
- var/list/slots = list(
- "backpack" = ITEM_SLOT_BACKPACK,
- "left pocket" = ITEM_SLOT_LPOCKET,
- "right pocket" = ITEM_SLOT_RPOCKET,
- )
-
- var/T = new item_path(mob)
- var/item_name = initial(item_path.name)
- var/where = mob.equip_in_one_of_slots(T, slots)
+ var/item = new item_path(mob)
+ var/where = mob.equip_conspicuous_item(item)
if(!where)
- to_chat(mob, span_userdanger("Unfortunately, you weren't able to get a [item_name]. This is very bad and you should adminhelp immediately (press F1)."))
+ to_chat(mob, span_userdanger("Unfortunately, you weren't able to get [item]. This is very bad and you should adminhelp immediately (press F1)."))
return FALSE
else
- to_chat(mob, span_danger("You have a [item_name] in your [where]."))
+ to_chat(mob, span_danger("You have [item] in your [where]."))
if(where == "backpack")
mob.back.atom_storage?.show_contents(mob)
return TRUE
diff --git a/code/modules/antagonists/revolution/revolution.dm b/code/modules/antagonists/revolution/revolution.dm
index 68491617aaa0e..6ee5ba5dcc7e3 100644
--- a/code/modules/antagonists/revolution/revolution.dm
+++ b/code/modules/antagonists/revolution/revolution.dm
@@ -196,21 +196,14 @@
/datum/antagonist/rev/head/apply_innate_effects(mob/living/mob_override)
. = ..()
var/mob/living/real_mob = mob_override || owner.current
- RegisterSignal(real_mob, COMSIG_MOB_PRE_FLASHED_CARBON, PROC_REF(on_flash))
+ real_mob.AddComponentFrom(REF(src), /datum/component/can_flash_from_behind)
RegisterSignal(real_mob, COMSIG_MOB_SUCCESSFUL_FLASHED_CARBON, PROC_REF(on_flash_success))
/datum/antagonist/rev/head/remove_innate_effects(mob/living/mob_override)
. = ..()
var/mob/living/real_mob = mob_override || owner.current
- UnregisterSignal(real_mob, list(COMSIG_MOB_PRE_FLASHED_CARBON, COMSIG_MOB_SUCCESSFUL_FLASHED_CARBON))
-
-/// Signal proc for [COMSIG_MOB_PRE_FLASHED_CARBON].
-/// Flashes will always result in partial success even if it's from behind someone
-/datum/antagonist/rev/head/proc/on_flash(mob/living/source, mob/living/carbon/flashed, obj/item/assembly/flash/flash, deviation)
- SIGNAL_HANDLER
-
- // Always partial flash at the very least
- return (deviation == DEVIATION_FULL) ? DEVIATION_OVERRIDE_PARTIAL : NONE
+ real_mob.RemoveComponentSource(REF(src), /datum/component/can_flash_from_behind)
+ UnregisterSignal(real_mob, COMSIG_MOB_SUCCESSFUL_FLASHED_CARBON)
/// Signal proc for [COMSIG_MOB_SUCCESSFUL_FLASHED_CARBON].
/// Bread and butter of revolution conversion, successfully flashing a carbon will make them a revolutionary
@@ -359,31 +352,25 @@
return ..()
/datum/antagonist/rev/head/equip_rev()
- var/mob/living/carbon/C = owner.current
- if(!ishuman(C))
+ var/mob/living/carbon/carbon_owner = owner.current
+ if(!ishuman(carbon_owner))
return
if(give_flash)
- var/obj/item/assembly/flash/handheld/T = new(C)
- var/list/slots = list (
- "backpack" = ITEM_SLOT_BACKPACK,
- "left pocket" = ITEM_SLOT_LPOCKET,
- "right pocket" = ITEM_SLOT_RPOCKET
- )
- var/where = C.equip_in_one_of_slots(T, slots, indirect_action = TRUE)
- if (!where)
- to_chat(C, "The Syndicate were unfortunately unable to get you a flash.")
+ var/where = carbon_owner.equip_conspicuous_item(new /obj/item/assembly/flash/handheld)
+ if (where)
+ to_chat(carbon_owner, "The flash in your [where] will help you to persuade the crew to join your cause.")
else
- to_chat(C, "The flash in your [where] will help you to persuade the crew to join your cause.")
+ to_chat(carbon_owner, "The Syndicate were unfortunately unable to get you a flash.")
if(give_hud)
- var/obj/item/organ/internal/cyberimp/eyes/hud/security/syndicate/S = new()
- S.Insert(C)
- if(C.get_quirk(/datum/quirk/body_purist))
- to_chat(C, "Being a body purist, you would never accept cybernetic implants. Upon hearing this, your employers signed you up for a special program, which... for \
+ var/obj/item/organ/internal/cyberimp/eyes/hud/security/syndicate/hud = new()
+ hud.Insert(carbon_owner)
+ if(carbon_owner.get_quirk(/datum/quirk/body_purist))
+ to_chat(carbon_owner, "Being a body purist, you would never accept cybernetic implants. Upon hearing this, your employers signed you up for a special program, which... for \
some odd reason, you just can't remember... either way, the program must have worked, because you have gained the ability to keep track of who is mindshield-implanted, and therefore unable to be recruited.")
else
- to_chat(C, "Your eyes have been implanted with a cybernetic security HUD which will help you keep track of who is mindshield-implanted, and therefore unable to be recruited.")
+ to_chat(carbon_owner, "Your eyes have been implanted with a cybernetic security HUD which will help you keep track of who is mindshield-implanted, and therefore unable to be recruited.")
/datum/team/revolution
name = "\improper Revolution"
diff --git a/code/modules/mob/living/carbon/inventory.dm b/code/modules/mob/living/carbon/inventory.dm
index fc96815045d92..a375dbe878dfc 100644
--- a/code/modules/mob/living/carbon/inventory.dm
+++ b/code/modules/mob/living/carbon/inventory.dm
@@ -455,3 +455,21 @@
covered_flags |= worn_item.body_parts_covered
return covered_flags
+
+/// Attempts to equip the given item in a conspicious place.
+/// This is used when, for instance, a character spawning with an item
+/// in their hands would be a dead giveaway that they are an antagonist.
+/// Returns the human readable name of where it placed the item, or null otherwise.
+/mob/living/carbon/proc/equip_conspicuous_item(obj/item/item, delete_item_if_failed = TRUE)
+ var/list/slots = list (
+ "backpack" = ITEM_SLOT_BACKPACK,
+ "left pocket" = ITEM_SLOT_LPOCKET,
+ "right pocket" = ITEM_SLOT_RPOCKET
+ )
+
+ var/placed_in = equip_in_one_of_slots(item, slots, indirect_action = TRUE)
+
+ if (isnull(placed_in) && delete_item_if_failed)
+ qdel(item)
+
+ return placed_in
diff --git a/tgstation.dme b/tgstation.dme
index 33a57719ea8e2..c184c6172f065 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -995,6 +995,7 @@
#include "code\datums\components\burning.dm"
#include "code\datums\components\butchering.dm"
#include "code\datums\components\caltrop.dm"
+#include "code\datums\components\can_flash_from_behind.dm"
#include "code\datums\components\chasm.dm"
#include "code\datums\components\chuunibyou.dm"
#include "code\datums\components\cleaner.dm"
diff --git a/tgui/packages/tgui/interfaces/AntagInfoBrother.tsx b/tgui/packages/tgui/interfaces/AntagInfoBrother.tsx
index a1214e0c0b908..07e183ec46900 100644
--- a/tgui/packages/tgui/interfaces/AntagInfoBrother.tsx
+++ b/tgui/packages/tgui/interfaces/AntagInfoBrother.tsx
@@ -18,7 +18,7 @@ export const AntagInfoBrother = (props) => {
- You are the {antag_name} of {brothers}!
+ You are the {antag_name}!