Skip to content

Commit

Permalink
Blood brothers is now a single person conversion antagonist (tgstatio…
Browse files Browse the repository at this point in the history
…n#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:
  • Loading branch information
Mothblocks authored Nov 29, 2023
1 parent 02954ca commit 08ab5d2
Show file tree
Hide file tree
Showing 10 changed files with 172 additions and 119 deletions.
1 change: 1 addition & 0 deletions code/__DEFINES/dcs/signals/signals_mob/signals_mob_main.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
22 changes: 22 additions & 0 deletions code/datums/components/can_flash_from_behind.dm
Original file line number Diff line number Diff line change
@@ -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
12 changes: 12 additions & 0 deletions code/datums/memory/general_memories.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
47 changes: 18 additions & 29 deletions code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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

//////////////////////////////////////////////
Expand Down
132 changes: 81 additions & 51 deletions code/modules/antagonists/brother/brother.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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()]"

Expand Down Expand Up @@ -74,85 +127,53 @@
brother_text += ", "
return brother_text

/datum/antagonist/brother/proc/give_meeting_area()
if(!owner.current || !team || !team.meeting_area)
return
to_chat(owner.current, "<span class='infoplain'><B>Your designated meeting area:</B> [team.meeting_area]</span>")
antag_memory += "<b>Meeting Area</b>: [team.meeting_area]<br>"

/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()
for(var/datum/mind/team_minds as anything in members)
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()
Expand All @@ -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
15 changes: 4 additions & 11 deletions code/modules/antagonists/cult/cult.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading

0 comments on commit 08ab5d2

Please sign in to comment.