Skip to content

Commit

Permalink
Add new midround antag: the divergent clone (#37334)
Browse files Browse the repository at this point in the history
* Add new midround antag: the divergent clone

Divergent clones are perfect copies of existing, potentially still living crew members,
created in a freak cloner accident. They can remember the original's antag status,
and may sometimes be evil which means they get traitor objectives. If the original
is also a traitor, the evil clone will share their objectives.

* give the spirits-in-waiting the default ghost sprite instead

* skip records check if forcespawn is enabled

* Forgot the role

* Review fixes

* duh
  • Loading branch information
brndd authored Dec 19, 2024
1 parent d569e4a commit 2620ad7
Show file tree
Hide file tree
Showing 44 changed files with 1,135 additions and 184 deletions.
2 changes: 2 additions & 0 deletions __DEFINES/_macros.dm
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,8 @@

#define istimeagent(H) (H.mind && (H.mind.GetRole(TIMEAGENT) || (H.mind.GetRole(TIMEAGENTTWIN))))

#define isdivergentclone(H) (H.mind && (H.mind.GetRole(DIVERGENTCLONE)))

#define isERT(H) (H.mind && H.mind.GetRole(RESPONDER))

#define isclownling(H) (H.mind && H.mind.GetRole(CLOWN_LING))
Expand Down
1 change: 1 addition & 0 deletions __DEFINES/role_datums_defines.dm
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@
#define JUDGE "judge"
#define GRUE "grue"
#define NANOTRASENOFFICIAL "nanotrasen official"
#define DIVERGENTCLONE "divergent clone"

#define GREET_DEFAULT "default"
#define GREET_ROUNDSTART "roundstart"
Expand Down
81 changes: 81 additions & 0 deletions code/datums/gamemode/dynamic/dynamic_rulesets_midround.dm
Original file line number Diff line number Diff line change
Expand Up @@ -1095,3 +1095,84 @@
requirements = list(10,10,10,10,10,10,10,10,10,10)
logo = "gun-logo"
repeatable = TRUE

//////////////////////////////////////////////
// //
// DIVERGENT CLONE //
// //
//////////////////////////////////////////////

/datum/dynamic_ruleset/midround/from_ghosts/divergentclone
name = "Divergent Clone"
role_category = /datum/role/divergentclone
required_candidates = 1
weight = 3
weight_category = "Clone"
cost = 5
requirements = list(40,30,20,10,10,10,10,10,10,10)
high_population_requirement = 10
logo = "divergentclone-logo"
repeatable = TRUE
makeBody = FALSE
flags = MINOR_RULESET

/datum/dynamic_ruleset/midround/from_ghosts/divergentclone/trim_candidates()
..()
for(var/mob/M in dead_players)
if(isdivergentclone(M))
dead_players -= M
for(var/mob/M in list_observers)
if(isdivergentclone(M))
list_observers -= M


/datum/dynamic_ruleset/midround/from_ghosts/divergentclone/ready(var/forced = 0)
if(!config.revival_cloning)
return 0

//Check that we have at least one ghost who isn't already a clone waiting to spawn
var/list/candies = dead_players + list_observers
var/list/valids[0]
for(var/mob/dead/observer/G in candies)
if(isdivergentclone(G))
continue
valids += G
if(valids.len == 0)
if(forced)
message_admins("Tried to force divergent clone, but no valid candidates found.")
return 0

var/list/clonepods = list()
for(var/obj/machinery/cloning/clonepod/pod in machines)
//Check that the pod has cloned something before
if(pod.cloned_records.len)
clonepods += pod
if(!forced && clonepods.len == 0)
return 0
return ..()

/datum/dynamic_ruleset/midround/from_ghosts/divergentclone/finish_setup(mob/new_character, index)
//Create a new dummy body to create a new mind for the ghost.
var/L = get_turf(new_character)
var/mob/living/carbon/human/H = new /mob/living/carbon/human(pick(latejoin))
H.ckey = new_character.ckey
H.fully_replace_character_name(null, "\improper spirit of a divergent clone")

//Give the about-to-be-a-ghost mob the provisional role and objective
var/datum/role/divergentclone/new_role = new /datum/role/divergentclone(H.mind, override=TRUE)
new_role.ForgeObjectives()
new_role.AnnounceObjectives()

var/mob/dead/observer/G = H.ghostize(FALSE)
//Give it a more spirity looking icon.
G.icon = initial(G.icon)
G.icon_state = initial(G.icon_state)
QDEL_NULL(H)
G.forceMove(L)
G.add_spell(new /spell/targeted/ghost/divergentclone)

to_chat(G, "<span class='notice'><b>You were selected to be a divergent clone, but will not be spawned in yet!</b></span>")
to_chat(G, "<span class='notice'>You have been granted the \"Spawn as Divergent Clone\" ghost spell. Use this near a cloning pod to spawn in as a divergent clone of someone who was cloned, or is currently being cloned, in that pod.</span>")
to_chat(G, "<span class='notice'>Using this spell on an unoccupied cloning pod will allow you to choose a record of a person previously cloned in that pod. Using it on an occupied pod will cause you to become a twin of the person currently in the pod, and be ejected from the pod at the same time as them.</span>")
to_chat(G, "<span class='notice'>Remember: you can only use this spell once, and re-entering your corpse will remove it permanently. In fact, for your convenience we have removed your ability to re-enter your corpse.</span>")

2 changes: 1 addition & 1 deletion code/datums/gamemode/factions/bloodcult/bloodcult.dm
Original file line number Diff line number Diff line change
Expand Up @@ -506,7 +506,7 @@
if (cult_reminders.len)
to_chat(R.antag.current, "<span class='notice'>Other cultists have shared some of their knowledge. It will be stored in your memory (check your Notes under the IC tab).</span>")
for (var/reminder in cult_reminders)
R.antag.store_memory("Shared Cultist Knowledge: [reminder].")
R.antag.store_memory("Shared Cultist Knowledge: [reminder].", category=MIND_MEMORY_ANTAGONIST, forced=TRUE)
previously_converted |= R.antag
if (R.antag.name in deconverted)
deconverted -= R.antag.name
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -463,7 +463,7 @@
if (iscultist(M.current))//failsafe for cultist brains put in MMIs
to_chat(M.current, "<span class='game say'><b>[user.real_name]</b>'s voice echoes in your head, <B><span class='sinisterbig'>[reminder]</span></span>")
to_chat(M.current, "<span class='notice'>This message will be remembered by all current cultists, and by new converts as well.</span>")
M.store_memory("Cult reminder: [text].")
M.store_memory("Cult reminder: [text].", category=MIND_MEMORY_ANTAGONIST, forced=TRUE)

for(var/mob/living/simple_animal/astral_projection/A in astral_projections)
to_chat(A, "<span class='game say'><b>[user.real_name]</b> communicates, <span class='sinisterbig'>[reminder]</span></span>. (Cult reminder)")
Expand Down
2 changes: 1 addition & 1 deletion code/datums/gamemode/factions/legacy_cult/cult.dm
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ var/global/list/rnwords = list("ire","ego","nahlizet","certum","veri","jatkaa","
word=pick(allwords)
var/wordexp = "[cult_words[word]] is [word]..."
to_chat(cult_mob, "<span class='sinister'>You remember one thing from the dark teachings of your master... [wordexp]</span>")
cult_mob.mind.store_memory("<B>You remember that</B> [wordexp]", 0, 0)
cult_mob.mind.store_memory("<B>You remember that</B> [wordexp]", category=MIND_MEMORY_ANTAGONIST, forced=TRUE)

/datum/faction/cult/narsie/AdminPanelEntry()
var/list/dat = ..()
Expand Down
3 changes: 1 addition & 2 deletions code/datums/gamemode/factions/plague_mice.dm
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,7 @@

/* With the disease set-up, store the detials of the disease in the mouse's memory */
var/datum/mind/mouse_mind = R.antag
mouse_mind.store_memory(plague.get_info(TRUE), forced = 1)
mouse_mind.store_memory("<hr>", forced = 1)
mouse_mind.store_memory(plague.get_info(TRUE), category=MIND_MEMORY_ANTAGONIST, forced = 1)
var/dat = "<span class='notice'>You carry a deadly plague with the following traits:</span>"
dat += "<br><span class='notice'>Strength / Robustness:</span> <b>[plague.strength]%</b> / <b>[plague.robustness]%</b>"
dat += "<br><span class='notice'>Infection chance:</span> <b>[plague.infectionchance]%</b>"
Expand Down
2 changes: 1 addition & 1 deletion code/datums/gamemode/factions/syndicate/nukeops.dm
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@
nuke_name_assign(nuke_last_name(synd_mind.current), title, members) //Allows time for the rest of the syndies to be chosen

if(nuke_code)
synd_mind.store_memory("<B>Syndicate Nuclear Bomb Code</B>: [nuke_code]", 0, 0)
synd_mind.store_memory("<B>Syndicate Nuclear Bomb Code</B>: [nuke_code]", category=MIND_MEMORY_ANTAGONIST, forced=TRUE)
to_chat(synd_mind.current, "The nuclear authorization code is: <B>[nuke_code]</B><br>Make sure to share it with your subordinates.")
var/obj/item/weapon/paper/P = new
P.info = "The nuclear authorization code is: <b>[nuke_code]</b>"
Expand Down
2 changes: 1 addition & 1 deletion code/datums/gamemode/factions/vox_shoal.dm
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ var/list/potential_bonus_items = list(
var/datum/outfit/striketeam/voxraider/concrete_outfit = new
concrete_outfit.equip(vox)
vox.regenerate_icons()
vox.store_memory("The priority items for the day are: [english_list(bonus_items_of_the_day)]")
vox.mind.store_memory("The priority items for the day are: [english_list(bonus_items_of_the_day)]", category=MIND_MEMORY_ANTAGONIST, forced=TRUE)

/*
spawn()
Expand Down
6 changes: 3 additions & 3 deletions code/datums/gamemode/misc_gamemode_procs.dm
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@
if(!apprentice)
to_chat(wizard_mob, "You will find a list of available spells in your spell book. Choose your magic arsenal carefully.")
to_chat(wizard_mob, "In your pockets you will find a teleport scroll. Use it as needed.")
wizard_mob.mind.store_memory("<B>Remember:</B> do not forget to prepare your spells.")
wizard_mob.mind.store_memory("<B>Remember:</B> do not forget to prepare your spells.", category=MIND_MEMORY_ANTAGONIST, forced=TRUE)
return 1

/proc/name_wizard(mob/living/carbon/human/wizard_mob, role_name = "Space Wizard")
Expand Down Expand Up @@ -346,13 +346,13 @@
if (syndicate_code_phrase)
var/phrases = syndicate_code_phrase.Join(", ")
words += "<span class='warning'>Code Phrases: </span>[phrases].<br>"
agent.mind.store_memory("<b>Code Phrases</b>: [phrases].")
agent.mind.store_memory("<b>Code Phrases</b>: [phrases].", category=MIND_MEMORY_ANTAGONIST, forced=TRUE)
else
words += "Unfortunately, the Syndicate did not provide you with a code phrase.<br>"
if (syndicate_code_response)
var/response = syndicate_code_response.Join(", ")
words += "<span class='warning'>Code Response: </span>[response].<br>"
agent.mind.store_memory("<b>Code Response</b>: [response].")
agent.mind.store_memory("<b>Code Response</b>: [response].", category=MIND_MEMORY_ANTAGONIST, forced=TRUE)
else
words += "Unfortunately, the Syndicate did not provide you with a code response.<br>"

Expand Down
63 changes: 63 additions & 0 deletions code/datums/gamemode/objectives/divergentclone.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/datum/objective/freeform/divergentclone_neutral
explanation_text = "Convince the world that you are the real <person>, or at least as real as the other copy."

/datum/objective/freeform/divergentclone_neutral/format_explanation()
return "Convince the world that you are the real [owner.name], or at least as real as the other copy."

/datum/objective/freeform/divergentclone_neutral/PostAppend()
explanation_text = format_explanation()
return TRUE

/datum/objective/freeform/divergentclone_evil
explanation_text = "Convince the world that you are the real <person> at any cost, be that by talk, violence or subterfuge. Do not let anyone get in your way, including your original copy."

/datum/objective/freeform/divergentclone_evil/format_explanation()
return "Convince the world that you are the real [owner.name] at any cost, be that by talk, violence or subterfuge. Do not let anyone get in your way, including your original copy."

/datum/objective/freeform/divergentclone_evil/PostAppend()
explanation_text = format_explanation()
return TRUE

/datum/objective/divergentclone/spawn_in
explanation_text = "Reincarnate as a divergent clone."
name = "Reincarnate"

/datum/objective/divergentclone/spawn_in/IsFulfilled()
if(..())
return TRUE
if(!owner || !owner.current)
return FALSE

var/datum/role/divergentclone/role = owner.GetRole(DIVERGENTCLONE)
if(role?.has_spawned_in)
return TRUE

/datum/objective/acquire_personal_id
explanation_text = "Acquire an ID card matching your name or DNA."
name = "Acquire personal ID card"

/datum/objective/acquire_personal_id/IsFulfilled()
if(..())
return TRUE

if(!owner || !owner.current)
return FALSE

for(var/obj/O in get_contents_in_object(owner.current))
var/obj/item/weapon/card/id/I
if(istype(O, /obj/item/weapon/card/id))
I = O
else if(istype(O, /obj/item/device/pda))
var/obj/item/device/pda/P = O
I = P.id
else
continue
var/datum/dna/D = owner.current.dna
if((I?.dna_hash == D.unique_enzymes) || (I?.registered_name == owner.name))
return TRUE

return FALSE




2 changes: 1 addition & 1 deletion code/datums/gamemode/powers/powers.dm
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
if (granttext)
to_chat(R.antag.current, "<span class = 'notice'>[granttext]</span>")
if (store_in_memory)
R.antag.store_memory("<font size = '1'>[granttext]</font>")
R.antag.store_memory("<font size = '1'>[granttext]</font>", category=MIND_MEMORY_ANTAGONIST, forced=TRUE)
R.current_powers += src
role = R
grant_spell()
Expand Down
3 changes: 1 addition & 2 deletions code/datums/gamemode/role/catbeast.dm
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,7 @@ var/list/catbeast_names = list("Meowth","Fluffy","Subject 246","Experiment 35a",
D1.origin = "Loose Catbeast"
D1.makerandom(str, rob, anti, bad)
H.infect_disease2(D1, 1, "Loose Catbeast")
antag.store_memory(D1.get_info(TRUE), forced = 1)
antag.store_memory("<hr>")
antag.store_memory(D1.get_info(TRUE), category=MIND_MEMORY_ANTAGONIST, forced=TRUE)

/datum/role/catbeast/proc/infect_catbeast_tier1(mob/living/carbon/human/H)
var/list/anti = list(
Expand Down
Loading

0 comments on commit 2620ad7

Please sign in to comment.