Skip to content

Commit

Permalink
Squashed commit of the following:
Browse files Browse the repository at this point in the history
commit 453d9a9
Author: brndd <[email protected]>
Date:   Fri Dec 13 12:38:07 2024 +0200

    fix candidate trimming

commit 4049045
Author: brndd <[email protected]>
Date:   Fri Dec 13 12:32:59 2024 +0200

    trim candidates

commit 8ee8fc7
Author: brndd <[email protected]>
Date:   Fri Dec 13 12:19:43 2024 +0200

    remove the ugly comment

commit 3bb6131
Author: brndd <[email protected]>
Date:   Fri Dec 13 12:11:56 2024 +0200

    fix steal and assassinate explanations

commit 0d9a7ae
Author: brndd <[email protected]>
Date:   Fri Dec 13 12:00:30 2024 +0200

    fix these

commit a55b95d
Author: brndd <[email protected]>
Date:   Fri Dec 13 11:59:00 2024 +0200

    fix objective copying

commit e916750
Author: brndd <[email protected]>
Date:   Fri Dec 13 11:36:49 2024 +0200

    allow cloning in an occupied pod

commit 638eb86
Author: brndd <[email protected]>
Date:   Fri Dec 13 11:30:11 2024 +0200

    sanity

commit 18f4b20
Author: brndd <[email protected]>
Date:   Fri Dec 13 11:28:20 2024 +0200

    sanity

commit d29fd67
Author: brndd <[email protected]>
Date:   Fri Dec 13 11:27:13 2024 +0200

    add confirmation to force spawn

commit 8efff39
Author: brndd <[email protected]>
Date:   Fri Dec 13 11:16:31 2024 +0200

    fix force spawning

commit 09a9103
Author: brndd <[email protected]>
Date:   Fri Dec 13 11:10:42 2024 +0200

    fix setting amnesia

commit 282c413
Author: brndd <[email protected]>
Date:   Fri Dec 13 11:06:10 2024 +0200

    fix force spawn

commit 960b50f
Author: brndd <[email protected]>
Date:   Fri Dec 13 11:03:21 2024 +0200

    fix force spawning

commit 95e06df
Author: brndd <[email protected]>
Date:   Fri Dec 13 10:55:14 2024 +0200

    fix errors

commit 609dc03
Author: brndd <[email protected]>
Date:   Fri Dec 13 10:53:43 2024 +0200

    fix errors

commit 84210d5
Author: brndd <[email protected]>
Date:   Fri Dec 13 10:44:48 2024 +0200

    admin button to force spawn etc.

commit 4dcd863
Author: brndd <[email protected]>
Date:   Fri Dec 13 09:35:59 2024 +0200

    embarassing!
  • Loading branch information
brndd committed Dec 13, 2024
1 parent fa8285e commit 04caa12
Show file tree
Hide file tree
Showing 3 changed files with 217 additions and 102 deletions.
17 changes: 12 additions & 5 deletions code/datums/gamemode/dynamic/dynamic_rulesets_midround.dm
Original file line number Diff line number Diff line change
Expand Up @@ -1116,6 +1116,16 @@
makeBody = FALSE
flags = MINOR_RULESET

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


/datum/dynamic_ruleset/midround/from_ghosts/divergentclone/ready(var/forced = 0)
if(!config.revival_cloning)
return 0
Expand All @@ -1124,10 +1134,9 @@
var/list/candies = dead_players + list_observers
var/list/valids[0]
for(var/mob/dead/observer/G in candies)
if(locate(/spell/targeted/ghost/divergentclone) in G.spell_list)
if(G.mind.GetRole(DIVERGENTCLONE))
continue
valids += G
message_admins("Found [valids.len] valid divergent clone candidates.")
if(valids.len == 0)
if(forced)
message_admins("Tried to force divergent clone, but no valid candidates found.")
Expand All @@ -1138,9 +1147,7 @@
//Check that the pod has cloned something before
if(pod.cloned_records.len > 0)
clonepods += pod
if(clonepods.len == 0)
if(forced)
message_admins("Tried to force divergent clone, but no valid pods found.")
if(!forced && clonepods.len == 0)
return 0
return ..()

Expand Down
253 changes: 156 additions & 97 deletions code/datums/gamemode/role/divergentclone.dm
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
default_admin_voice = "The Ancient Reptilian Brain"
admin_voice_style = "bold"
var/has_spawned_in = FALSE
var/datum/mind/force_spawn_as = null //for admins to force the clone to spawn as a clone of a specific character
var/datum/mind/original_mind = null
var/extra_role_memory = ""
var/uplink_pw_revealed = FALSE
Expand Down Expand Up @@ -160,7 +161,28 @@
var/datum/role/traitor/orig_role = original_mind.antag_roles[TRAITOR]
var/datum/objective_holder/holder = orig_role.objectives
for(var/datum/objective/O in holder.GetObjectives())
AppendObjective(O)
//AppendObjective(O)
//There really seems to be no other way to do this than check all of these one by one
if(istype(O, /datum/objective/target/assassinate))
var/datum/objective/target/assassinate/orig_obj = O
var/datum/objective/target/assassinate/new_obj = new(auto_target = FALSE)
new_obj.target_amount = orig_obj.target_amount
if(orig_obj.delayed_target)
new_obj.target = orig_obj.delayed_target
else
new_obj.target = orig_obj.target
new_obj.explanation_text = new_obj.format_explanation()
AppendObjective(new_obj)
else if(istype(O, /datum/objective/target/steal))
var/datum/objective/target/steal/orig_obj = O
var/datum/objective/target/steal/new_obj = new(auto_target = FALSE)
new_obj.target_amount = orig_obj.target_amount
new_obj.target_category = orig_obj.target_category
new_obj.steal_target = orig_obj.steal_target
new_obj.explanation_text = new_obj.format_explanation()
AppendObjective(new_obj)
else //Just create a new instance instead of deep copying
AppendObjective(new O.type)

else //copypasted from syndicate.dm and yes I feel bad about it
if(prob(50))
Expand Down Expand Up @@ -289,14 +311,21 @@
if(amnesia != 2)
dat += " <a href='?src=\ref[antag];mind=\ref[antag];role=\ref[src];setAmnesia=2;'>(Set to Hazy)</a>"
dat += "<br>"
dat += "<b>Will spawn in as: </b> [force_spawn_as ? force_spawn_as.name : "player's choice"] "
if (force_spawn_as)
dat += "<a href='?src=\ref[antag];mind=\ref[antag];role=\ref[src];clearForceSpawn=1;'>(Clear)</a><br>"
else
dat += "<a href='?src=\ref[antag];mind=\ref[antag];role=\ref[src];setForceSpawn=1;'>(Pick character)</a><br>"


if (force_spawn_as)
dat += " - <a href='?src=\ref[antag];mind=\ref[antag];role=\ref[src];forceSpawn=1;'>(Force spawn NOW at nearest cloning pod)</a><br>"
else
if(uplink)
var/obj/item/device/pda/P = uplink.parent
var/uplink_name = P ? P.name : "unknown PDA"
dat += "<b>Uplink found:</b> [uplink_name] [uplink_pw_revealed ? "(knows the passcode)" : "(does not know passcode)"]<br>"
dat += " - <a href='?src=\ref[antag];mind=\ref[antag];role=\ref[src];revealUplinkPW=1;'>(Reveal passcode)</a><br>"
if(!uplink_pw_revealed)
dat += " - <a href='?src=\ref[antag];mind=\ref[antag];role=\ref[src];revealUplinkPW=1;'>(Reveal passcode)</a><br>"
dat += " - <a href='?src=\ref[antag];mind=\ref[antag];role=\ref[src];telecrystalsSet=1;'>Telecrystals: [uplink.telecrystals] (Set telecrystals)</a><br>"
dat += " - <a href='?src=\ref[antag];mind=\ref[antag];role=\ref[src];removeuplink=1;'>(Remove uplink)</a><br>"
dat += " - <a href='?src=\ref[antag];mind=\ref[antag];role=\ref[src];jumpToUplink=1;'>(Jump to uplink's position)</a><br>"
Expand All @@ -313,7 +342,64 @@
var/new_amnesia = text2num(href_list["setAmnesia"])
if(new_amnesia < 0 || new_amnesia > 2)
return
to_chat(usr, "<span class='notice'>The clone's amnesia level has been set to [amnesia].</span>")
amnesia = new_amnesia
to_chat(usr, "<span class='notice'>The clone's amnesia level has been set to [new_amnesia].</span>")
if(href_list["setForceSpawn"])
var/list/used_keys[0]
var/list/minds[0]
for(var/datum/mind/mind in ticker.minds)
if(mind == antag || (!mind.current && !mind.body_archive))
continue
var/key = avoid_assoc_duplicate_keys(mind.name, used_keys)
minds[key] = mind
var/selection = input("Which character should the clone spawn in as?", "Choose a character", null, null) as null|anything in minds
if(selection)
var/datum/mind/mind = minds[selection]
if(!mind.current && !mind.body_archive)
to_chat(usr, "<span class='warning'>You picked some nonsense that has no body and no body archive. Pick something else.</span>")
return
if(mind == antag)
to_chat(usr, "<span class='warning'>You can't forcespawn them as themselves! Pick something else.</span>")
return
force_spawn_as = mind
to_chat(usr, "<span class='notice'>The clone will now spawn in as [mind].</span>")
if(href_list["clearForceSpawn"])
force_spawn_as = null
to_chat(usr, "<span class='notice'>The clone will now spawn in as the player's choice.</span>")
if(href_list["forceSpawn"])
if(!force_spawn_as)
to_chat(usr, "<span class='warning'>Cannot force spawn without a character selected!</span>")
return
if(alert(usr, "Are you sure you want to force spawn the clone as [force_spawn_as]?", "Force spawn?", "Yes", "No") != "Yes")
return
var/obj/machinery/cloning/clonepod/pod
var/dist = 100
for(var/obj/machinery/cloning/clonepod/P in range(usr, 7))
var/new_dist = get_dist(P, usr)
if(new_dist < dist)
dist = new_dist
pod = P
if(!pod)
to_chat(usr, "<span class='warning'>No nearby cloning pods found!</span>")
return

var/mob/living/clone = null
if(force_spawn_as.current && istype(force_spawn_as.current, /mob/living/carbon/human))
var/mob/living/O = force_spawn_as.current
original_mind = force_spawn_as
clone = pod.clone_divergent_twin(O, antag)
else
//Try to get a body from the mind's body archive
var/datum/dna2/record/D = force_spawn_as.body_archive.data["dna_records"]
original_mind = force_spawn_as
clone = pod.clone_divergent_record(D, antag)
if(!clone)
to_chat(usr, "<span class='warning'>Failed to spawn in the clone! This shouldn't happen, but maybe try again?</span>")
return
if(!on_spawn_in(force_spawn_as))
stack_trace("Divergent clone failed to spawn in.")
return

if(href_list["jumpToUplink"])
if(uplink)
usr.forceMove(get_turf(uplink.parent))
Expand All @@ -326,6 +412,10 @@
find_or_create_uplink()
var/obj/item/device/pda/P = uplink.parent
if(P)
if(uplink_pw_revealed)
to_chat(antag.current, "<span class='warning'>You remember that your [P.name] is actually a Syndicate Uplink. If you manage to recover it, you may enter the code \"[uplink.unlock_code]\" as its ringtone to unlock its hidden features.</span>")
else
to_chat(antag.current, "<span class='warning'>You remember that your [P.name] is actually a Syndicate Uplink. However, you can't seem to remember the passcode off the top of your head. It will come back to you if you manage to recover the device.</span>")
to_chat(usr, "<span class='notice'>[P.name] is now the clone's uplink.</span>")
if(href_list["telecrystalsSet"])
if(!uplink)
Expand Down Expand Up @@ -390,6 +480,13 @@
var/mob/dead/observer/ghost = holder
ASSERT(istype(ghost))

var/datum/role/divergentclone/role = ghost.mind.GetRole(DIVERGENTCLONE)
if(!role) //if they somehow don't have the role already, give it to them
role = new /datum/role/divergentclone(ghost.mind, override=TRUE)
if(!role)
to_chat(ghost, "<span class='warning'>Clone divergence failed. Please try again.</span>")
return

//Find nearest cloning pod and move to it
var/obj/machinery/cloning/clonepod/pod
var/dist = 100
Expand Down Expand Up @@ -417,67 +514,75 @@

var/mob/living/clone = null
var/datum/mind/original_mind = null
if(pod.occupants.len > 0)
if(pod.occupants.len == 1)
var/mob/living/O = pod.occupants[1]
var/occupant_name = O.real_name
switch(alert(ghost, "This pod is currently cloning [occupant_name]. Do you want to insert yourself as their twin?", "Insert as twin?", "Yes", "No"))
if("Yes")
if(!(O in pod.occupants))
to_chat(ghost, "<span class='warning'>The occupant seems to have exited the pod. Please try again.</span>")
if(role.force_spawn_as)
if(role.force_spawn_as.current && istype(role.force_spawn_as.current, /mob/living/carbon/human))
to_chat(ghost, "<span class='warning'>A mysterious force causes you to reincarnate as a clone of [role.force_spawn_as.name]!</span>")
var/mob/living/O = role.force_spawn_as.current
original_mind = role.force_spawn_as
clone = pod.clone_divergent_twin(O, ghost.mind)
else
//Try to get a body from the mind's body archive
var/datum/dna2/record/D = role.force_spawn_as.body_archive.data["dna_records"]
to_chat(ghost, "<span class='warning'>A mysterious force causes you to reincarnate as a clone of [D.dna.real_name]!</span>")
original_mind = role.force_spawn_as
clone = pod.clone_divergent_record(D, ghost.mind)
//If something fails, remove force_spawn_as so the player can try again
if(!clone)
role.force_spawn_as = null
else
if(pod.occupants.len > 0)
var/mob/living/O = null
if(pod.occupants.len == 1)
O = pod.occupants[1]
var/occupant_name = O.real_name
switch(alert(ghost, "This pod is currently cloning [occupant_name]. Do you want to insert yourself as their twin?", "Insert as twin?", "Yes", "No"))
if("Yes")
if(!(O in pod.occupants))
to_chat(ghost, "<span class='warning'>The occupant seems to have exited the pod. Please try again.</span>")
return
else
to_chat(ghost, "<span class='notice'>Twin selection cancelled.</span>")
return
original_mind = O.mind
clone = clone_twin(pod, O, ghost.mind)
else
else
var/list/used_keys[0]
var/list/occupants[0]
for(var/mob/living/occupant in pod.occupants)
var/key = avoid_assoc_duplicate_keys(occupant.name, used_keys)
occupants[key] = occupant
var/selection = input("This pod is currently cloning multiple people. Please select the person you would like to twin.", "Select twin target", null, null) as null|anything in occupants
if(!selection)
to_chat(ghost, "<span class='notice'>Twin selection cancelled.</span>")
return
else
O = occupants[selection]
if(!(O in pod.occupants))
to_chat(ghost, "<span class='warning'>The occupant seems to have exited the pod. Please try again.</span>")
return
original_mind = O.mind
clone = pod.clone_divergent_twin(O, ghost.mind)
else if(pod.cloned_records.len > 0)
var/list/used_keys[0]
var/list/occupants[0]
for(var/mob/living/O in pod.occupants)
var/key = avoid_assoc_duplicate_keys(O.name, used_keys)
occupants[key] = O
var/selection = input("This pod is currently cloning multiple people. Please select the person you would like to twin.", "Select twin target", null, null) as null|anything in occupants
var/list/records[0]
for(var/datum/dna2/record/R in pod.cloned_records)
var/key = avoid_assoc_duplicate_keys(R.name, used_keys)
records[key] = R
var/selection = input("This pod has no occupants. Please select a record to clone.", "Divergent clone", null, null) as null|anything in records
if(!selection)
to_chat(ghost, "<span class='notice'>Twin selection cancelled.</span>")
return
var/mob/living/O = occupants[selection]
if(!(O in pod.occupants))
to_chat(ghost, "<span class='warning'>The occupant seems to have exited the pod. Please try again.</span>")
return
original_mind = O.mind
clone = clone_twin(pod, O, ghost.mind)
else if(pod.cloned_records.len > 0)
var/list/used_keys[0]
var/list/records[0]
for(var/datum/dna2/record/R in pod.cloned_records)
var/key = avoid_assoc_duplicate_keys(R.name, used_keys)
records[key] = R
var/selection = input("This pod has no occupants. Please select a record to clone.", "Divergent clone", null, null) as null|anything in records
if(!selection)
to_chat(ghost, "<span class='notice'>Twin selection cancelled.</span>")
return
var/datum/dna2/record/record = records[selection]
original_mind = locate(record.mind)
clone = clone_record(pod, record, ghost)
var/datum/dna2/record/record = records[selection]
original_mind = locate(record.mind)
clone = pod.clone_divergent_record(record, ghost.mind)

if(!clone)
original_mind = null
to_chat(ghost, "<span class='warning'>Clone divergence failed. Please try again.</span>")
return

var/datum/role/divergentclone/role = clone.mind.GetRole(DIVERGENTCLONE)
if(!role) //if they somehow don't have the role already, give it to them
role = new /datum/role/divergentclone(clone.mind, override=TRUE)
if(!role)
to_chat(ghost, "<span class='warning'>Clone divergence failed. Please try again.</span>")
return
if(!role.on_spawn_in(original_mind))
to_chat(ghost, "<span class='warning'>Clone divergence failed. Please try again.</span>")
return

//Remove the spell from the ghost just to be safe
ghost.remove_spell(/spell/targeted/ghost/divergentclone)

if(!role.on_spawn_in(original_mind))
stack_trace("Divergent clone failed to spawn in.")


/spell/targeted/ghost/divergentclone/proc/find_eligible_pod(var/mob/dead/observer/ghost)
var/list/clonepods = list()
Expand All @@ -497,49 +602,3 @@
pod = P
return pod

/spell/targeted/ghost/divergentclone/proc/clone_twin(var/obj/machinery/cloning/clonepod/pod, var/mob/living/original, var/datum/mind/clonemind)
var/mob/living/clone = pod.growtwin(original, clonemind, do_mind_transfer=TRUE, allow_multiple=TRUE, force_clone=TRUE)
if(!clone)
return null
var/datum/mind/new_mind = clone.mind
var/datum/mind/orig_mind = original.mind
new_mind.name = orig_mind.name
//new_mind.memory = orig_mind.memory
new_mind.assigned_role = orig_mind.assigned_role
new_mind.body_archive = orig_mind.body_archive
new_mind.role_alt_title = orig_mind.role_alt_title
new_mind.miming = orig_mind.miming
new_mind.faith = orig_mind.faith
new_mind.initial_account = orig_mind.initial_account
new_mind.initial_wallet_funds = orig_mind.initial_wallet_funds

return clone

/spell/targeted/ghost/divergentclone/proc/clone_record(var/obj/machinery/cloning/clonepod/pod, var/datum/dna2/record/orig_record, var/mob/dead/observer/G)
var/datum/dna2/record/R = new /datum/dna2/record()
R.dna = orig_record.dna.Clone()
R.ckey = G.ckey
R.mind = "\ref[G.mind]"
R.id = copytext(md5(R.dna.real_name), 2, 6)
R.name = R.dna.real_name
R.types = DNA2_BUF_UI | DNA2_BUF_UE | DNA2_BUF_SE
R.languages = orig_record.languages.Copy()
R.attack_log = orig_record.attack_log.Copy()
R.default_language = orig_record.default_language
R.times_cloned = orig_record.times_cloned
R.talkcount = orig_record.talkcount

var/mob/living/carbon/human/clone = pod.growclone(R, copy_progress_from=null, do_mind_transfer=TRUE, allow_multiple=FALSE, force_clone=TRUE)
var/datum/mind/new_mind = clone.mind
var/datum/mind/orig_mind = locate(orig_record.mind)
new_mind.name = orig_mind.name
//new_mind.memory = orig_mind.memory
new_mind.assigned_role = orig_mind.assigned_role
new_mind.body_archive = orig_mind.body_archive
new_mind.role_alt_title = orig_mind.role_alt_title
new_mind.miming = orig_mind.miming
new_mind.faith = orig_mind.faith
new_mind.initial_account = orig_mind.initial_account
new_mind.initial_wallet_funds = orig_mind.initial_wallet_funds

return clone
Loading

0 comments on commit 04caa12

Please sign in to comment.