Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Evil Clone (2) #2515

Merged
merged 6 commits into from
Aug 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions monkestation/code/game/machinery/computer/cloning.dm
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
/obj/machinery/computer/cloning/Initialize()
. = ..()
updatemodules(TRUE)
AddElement(/datum/element/empprotection, EMP_PROTECT_SELF) // So when an experimental cloner gets emped, it's cloning console doesn't break nullifying the threat.

/obj/machinery/computer/cloning/Destroy()
if(pods)
Expand Down
76 changes: 74 additions & 2 deletions monkestation/code/game/machinery/exp_cloner.dm
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,37 @@
req_access = null
circuit = /obj/item/circuitboard/machine/clonepod/experimental
internal_radio = FALSE
grab_ghost_when = CLONER_FRESH_CLONE // This helps with getting the objective for evil clones to display.
VAR_PRIVATE
static/list/image/cached_clone_images
/// Am I producing evil clones?
var/datum/objective/evil_clone/evil_objective = null
/// Can my objective be changed?
var/locked = FALSE
/// The custom objective given by the traitor item.
var/custom_objective = null

/obj/machinery/clonepod/experimental/Destroy()
clear_human_dummy(REF(src))
return ..()

/obj/machinery/clonepod/experimental/examine(mob/user)
. = ..()
if((evil_objective || custom_objective) && (in_range(user, src) || isobserver(user)))
if(!isnull(evil_objective) || !isnull(custom_objective))
. += span_warning("You notice an ominous, flashing red LED light.")
if(isobserver(user))
if(!isnull(custom_objective))
. += span_notice("Those cloned will have the objective: [custom_objective]") //This doesn't look the best I think.
else
. += span_notice("Those cloned will have the objective: [evil_objective.explanation_text]")

/obj/machinery/clonepod/experimental/RefreshParts()
. = ..()
if(!isnull(evil_objective) || !isnull(custom_objective))
speed_coeff = round(speed_coeff / 2) // So better parts have half the speed increase.
speed_coeff += 1 // I still want basic parts to have base 100% speed.

//Start growing a human clone in the pod!
/obj/machinery/clonepod/experimental/growclone(clonename, ui, mutation_index, mindref, blood_type, datum/species/mrace, list/features, factions, list/quirks, datum/bank_account/insurance)
if(panel_open || mess || attempting)
Expand Down Expand Up @@ -54,13 +78,25 @@
ADD_TRAIT(clonee, TRAIT_NOCRITDAMAGE, CLONING_POD_TRAIT)
clonee.Unconscious(80)

var/role_text
var/poll_text
if(!isnull(custom_objective))
role_text = "syndicate clone"
poll_text = "Do you want to play as [clonename]'s syndicate clone?"
else if(!isnull(evil_objective))
role_text = "evil clone"
poll_text = "Do you want to play as [clonename]'s evil clone?"
else
role_text = "defective clone"
poll_text = "Do you want to play as [clonename]'s defective clone?"

var/list/mob/dead/observer/candidates = SSpolling.poll_ghost_candidates_for_mob(
"Do you want to play as [clonename]'s defective clone?",
poll_text,
poll_time = 10 SECONDS,
target_mob = clonee,
ignore_category = POLL_IGNORE_DEFECTIVECLONE,
pic_source = get_clone_preview(clonee.dna) || clonee,
role_name_text = "defective clone"
role_name_text = role_text
)
if(LAZYLEN(candidates))
var/mob/dead/observer/candidate = pick(candidates)
Expand All @@ -85,6 +121,23 @@
if(!mob_occupant?.mind) //When experimental cloner fails to get a ghost, it won't spit out a body, so we don't get an army of brainless rejects.
qdel(mob_occupant)
return FALSE
else if(!isnull(custom_objective))
var/datum/antagonist/evil_clone/antag_object = new
var/datum/objective/evil_clone/custom = new
custom.explanation_text = custom_objective
antag_object.objectives += custom
mob_occupant.mind.add_antag_datum(antag_object)
mob_occupant.grant_language(/datum/language/codespeak) // So you don't have to remember to grant each and every identical clone codespeak with the manual.
mob_occupant.remove_blocked_language(/datum/language/codespeak, source=LANGUAGE_ALL) // All the effects the codespeak manual would have.
ADD_TRAIT(mob_occupant, TRAIT_TOWER_OF_BABEL, MAGIC_TRAIT)
var/obj/item/implant/radio/syndicate/imp = new(src)
imp.implant(mob_occupant)
mob_occupant.faction |= ROLE_SYNDICATE
mob_occupant.AddComponent(/datum/component/simple_access, list(ACCESS_SYNDICATE, ACCESS_MAINT_TUNNELS, ACCESS_GENETICS, ACCESS_MINERAL_STOREROOM)) //Basic/syndicate access, and genetics too because clones are genetics stuff.
else if(!isnull(evil_objective))
var/datum/antagonist/evil_clone/antag_object = new
antag_object.objectives += new evil_objective()
mob_occupant.mind.add_antag_datum(antag_object)
return TRUE

/obj/machinery/clonepod/experimental/proc/get_clone_preview(datum/dna/clone_dna)
Expand All @@ -104,6 +157,25 @@
LAZYSET(cached_clone_images, key, preview)
return preview

/obj/machinery/clonepod/experimental/emag_act(mob/user)
if(!locked)
evil_objective = /datum/objective/evil_clone/murder //Emags will give a nasty objective.
locked = TRUE
to_chat(user, span_warning("You corrupt the genetic compiler."))
add_fingerprint(user)
log_cloning("[key_name(user)] emagged [src] at [AREACOORD(src)], causing it to malfunction.")
RefreshParts()
else
to_chat(user, span_warning("The cloner is already malfunctioning."))

/obj/machinery/clonepod/experimental/emp_act(severity)
. = ..()
if (!(. & EMP_PROTECT_SELF))
if(prob(100/severity) && !locked)
evil_objective = pick(subtypesof(/datum/objective/evil_clone) - /datum/objective/evil_clone/murder)
RefreshParts()
log_cloning("[src] at [AREACOORD(src)] corrupted due to EMP pulse.")

//Prototype cloning console, much more rudimental and lacks modern functions such as saving records, autocloning, or safety checks.
/obj/machinery/computer/prototype_cloning
name = "prototype cloning console"
Expand Down
39 changes: 39 additions & 0 deletions monkestation/code/game/objects/items/storage/uplink_kits.dm
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,45 @@
new /obj/item/lighter(src)
new /obj/item/jammer(src)

/obj/item/storage/box/clonearmy
name = "Syndicate clone army kit"
desc = "A box containing everything you need to make a clone army. The disk inside cunningly disguised as a DNA data disk is used to give all clones a directive they must follow."
icon_state = "syndiebox"

/obj/item/storage/box/clonearmy/PopulateContents()
var/static/items_inside = list(
/obj/item/disk/clonearmy = 1,
/obj/item/stack/sheet/iron = 15,
/obj/item/stack/sheet/glass = 4,
/obj/item/stack/cable_coil = 1,
/obj/item/circuitboard/machine/clonepod/experimental = 1,
/obj/item/circuitboard/machine/clonescanner = 1,
/obj/item/circuitboard/computer/cloning = 1,
/obj/item/stock_parts/manipulator/femto = 2, // The syndicate is so cool they gave you tier four parts. RIP my joke about tier 2 parts.
/obj/item/stock_parts/scanning_module/triphasic = 3,
/obj/item/stock_parts/micro_laser/quadultra = 1,
/obj/item/stock_parts/matter_bin/bluespace = 1,
/obj/item/wrench = 1,
/obj/item/screwdriver/nuke = 1,
/obj/item/multitool = 1, // For those who want space between the cloning console and pod.
/obj/item/language_manual/codespeak_manual/unlimited = 1,
/obj/item/implanter/radio/syndicate = 1, // So you can communicate with your clones, instead of having random evil clones roaming the halls with no direction.
/obj/item/paper/clone_guide = 1,)
generate_items_inside(items_inside, src)

/obj/item/paper/clone_guide
name = "Clone Army User Manual" // Start and end shamelessly copied from contractor guide. I am not a good writer. This is also ugly.
default_raw_text = {"Welcome agent, thank you for purchasing the clone army kit.<br>\
<ul>\
<li>The "DNA data disk" inside is actually a sophisticated device that can be used to hijack an experimental cloner, giving the clones a directive they must follow.</li>\
<li>In order to use this disk, use it in your hand, and input your desired directive, before hitting the cloner with the disk. You can input and upload a new objective to replace the old one if you ever feel like it, the disk is infinitely reusable.</li>\
<li>The clones will be given basic access, including syndicate, maintenance, genetics, and mineral storage. They will also be given an implanted syndicate radio and automatically taught codespeak. Syndicate turrets and the like will recognize the clones as a member of the syndicate.</li>\
<li>Be wary, the clones will have obviously evil red eyes, which will alert anyone who sees them with no eye covering that something is wrong with them. Also, don't try to use this on newer cloning models, Nanotrasen fixed the vulnerability that lets the disk work in their newer models.</li>\
<li>When hacked, a cloner will begin to operate slower, and anyone who examines it closely will be able to see that the cloner is malfunctioning.</li>\
<li>A tip, any activated mutations in the person being scanned, will be present in the clones produced, allowing you to give the clones some intrinsic powers. Make sure to use activators, not mutators.</li>\
</ul>
Good luck agent. You can burn this document."}

#undef KIT_ITEM_CATEGORY_SUPPORT
#undef KIT_ITEM_CATEGORY_WEAPONS
#undef KIT_ITEM_CATEGORY_MISC
23 changes: 23 additions & 0 deletions monkestation/code/modules/antagonists/evil_clone/evil_clone.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/datum/antagonist/evil_clone
name = "\improper Evil Clone"
show_in_antagpanel = TRUE
roundend_category = "evil clones"
antagpanel_category = "Evil Clones"
show_name_in_check_antagonists = TRUE
show_to_ghosts = TRUE

/datum/antagonist/evil_clone/greet()
. = ..()
owner.announce_objectives()
owner.current.playsound_local(get_turf(owner.current), 'sound/ambience/antag/revolutionary_tide.ogg', 100, FALSE, pressure_affected = FALSE, use_reverb = FALSE)

/datum/antagonist/evil_clone/apply_innate_effects(mob/living/mob_override)
. = ..()
var/mob/living/current = owner.current
current.AddElement(/datum/element/cult_eyes, initial_delay = 0 SECONDS)

/datum/antagonist/evil_clone/remove_innate_effects(mob/living/mob_override)
. = ..()
var/mob/living/current = owner.current
if (HAS_TRAIT(current, TRAIT_UNNATURAL_RED_GLOWY_EYES))
current.RemoveElement(/datum/element/cult_eyes)
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/datum/objective/evil_clone/murder // The existance of the murderbone objective makes evil clones properly feared, so even when they aren't murderboning they will still be shunned and persecuted.
name = "clone supremacy"
explanation_text = "Make sure clones of yourself are the only ones alive. Do not spare the original."

/datum/objective/evil_clone/sole
name = "real one"
explanation_text = "All other versions of you are imposters, eliminate them."

/datum/objective/evil_clone/rule
name = "rightful rule"
explanation_text = "You and your fellow clones of yourself are the rightful rulers of the station, take control."

/datum/objective/evil_clone/minion
name = "minion"
explanation_text = "Find the most evil being you can, and become their minion."

/datum/objective/evil_clone/dud // Relies on more destructive objectives, to create conflict from crew hating evil clones because they MIGHT have a more evil objective.
name = "peaceful clone"
explanation_text = "You find it really mean that some people don't like you because of your red eyes."

/datum/objective/evil_clone/tide
name = "tider"
explanation_text = "Crime is your religion, commit as much crime as possible. Only seriously injure people if they try to stop crime."

/datum/objective/evil_clone/fake_cult
name = "fake cultist"
explanation_text = "Praise"

/datum/objective/evil_clone/fake_cult/New()
var/god = pick(list("Rat'var", "Nar'sie")) //So clones with different gods will fight eachother.
explanation_text+=" [god]! They haven't answered your prayers yet, but surely if you pray enough and make elaborate enough rituals they will inevitably come. Make sure no heretical religions prosper."

/datum/objective/evil_clone/territorial
name = "territorial"
explanation_text = "The clonepod which created you is a holy site only you and your fellow clones of yourself are worthy to be in the presence of. Secure the area around the clonepod and ensure no non-clones threaten it."

/datum/objective/evil_clone/check_completion()
return TRUE
19 changes: 19 additions & 0 deletions monkestation/code/modules/antagonists/evil_clone/evil_event.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/datum/round_event_control/cloner_corruption
name = "Experimental Cloner Corruption"
typepath = /datum/round_event/cloner_corruption
max_occurrences = 1
weight = 3
category = EVENT_CATEGORY_ENTITIES //Kinda, evil clones ARE entities.
track = EVENT_TRACK_MODERATE
tags = list(TAG_COMBAT) // Clones will likely start a fight, but will usually not cause wanton destruction.
earliest_start = 35 MINUTES //This requires an experimental cloner to be made, so should wait until later to fire when there's better chance one has been set up.

/datum/round_event/cloner_corruption/start()
var/found = FALSE
for(var/obj/machinery/clonepod/experimental/cloner in GLOB.machines)
if(!cloner.locked)
cloner.evil_objective = pick(subtypesof(/datum/objective/evil_clone))
cloner.RefreshParts()
found = TRUE
if(!found) // Refund if no experimental cloners are found.
control.occurrences--
16 changes: 13 additions & 3 deletions monkestation/code/modules/research/designs/machine_designs.dm
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
category = list(
RND_CATEGORY_MACHINE + RND_SUBCATEGORY_MACHINE_MEDICAL
)
departmental_flags = DEPARTMENT_BITFLAG_MEDICAL
departmental_flags = DEPARTMENT_BITFLAG_MEDICAL | DEPARTMENT_BITFLAG_SCIENCE


/datum/design/board/clonepod
Expand All @@ -18,7 +18,17 @@
category = list(
RND_CATEGORY_MACHINE + RND_SUBCATEGORY_MACHINE_MEDICAL
)
departmental_flags = DEPARTMENT_BITFLAG_MEDICAL
departmental_flags = DEPARTMENT_BITFLAG_MEDICAL | DEPARTMENT_BITFLAG_SCIENCE

/datum/design/board/clonepod_experimental
name = "Machine Design (Experimental Clone Pod)"
desc = "Allows for the construction of circuit boards used to build an Experimental Cloning Pod."
id = "clonepod_experimental"
build_path = /obj/item/circuitboard/machine/clonepod/experimental
category = list(
RND_CATEGORY_MACHINE + RND_SUBCATEGORY_MACHINE_MEDICAL
)
departmental_flags = DEPARTMENT_BITFLAG_SCIENCE


/datum/design/board/clonescanner //hippie end, re-add cloning
Expand All @@ -29,7 +39,7 @@
category = list(
RND_CATEGORY_MACHINE + RND_SUBCATEGORY_MACHINE_MEDICAL
)
departmental_flags = DEPARTMENT_BITFLAG_MEDICAL
departmental_flags = DEPARTMENT_BITFLAG_MEDICAL | DEPARTMENT_BITFLAG_SCIENCE

/datum/design/board/nanite_chamber
name = "Machine Design (Nanite Chamber Board)"
Expand Down
2 changes: 1 addition & 1 deletion monkestation/code/modules/research/techweb/all_nodes.dm
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
display_name = "Cloning"
description = "We have the technology to make him."
prereq_ids = list("biotech")
design_ids = list("clonecontrol", "clonepod", "clonescanner", "dnascanner", "dna_disk")
design_ids = list("clonecontrol", "clonepod", "clonescanner", "dnascanner", "dna_disk", "clonepod_experimental")
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)

/////////////////////////Nanites/////////////////////////
Expand Down
52 changes: 52 additions & 0 deletions monkestation/code/modules/uplink/uplink_items/job.dm
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,55 @@

/datum/uplink_item/role_restricted/modified_syringe_gun
surplus = 50

/datum/uplink_item/role_restricted/clonekit
name = "Clone Army Kit"
desc = "Everything you need for a clone army, armaments not included."
progression_minimum = 5 MINUTES
cost = 20
item = /obj/item/storage/box/clonearmy
restricted_roles = list(JOB_GENETICIST, JOB_RESEARCH_DIRECTOR, JOB_MEDICAL_DOCTOR, JOB_CHIEF_MEDICAL_OFFICER, JOB_CARGO_TECHNICIAN, JOB_QUARTERMASTER) // Experimental cloners were traditionally bought by cargo.

///I know this probably isn't the right place to put it, but I don't know where I should put it, and I can move it later.
/obj/item/disk/clonearmy
name = "DNA data disk" //Cunning disguise.
var/objective = ""
icon_state = "datadisk0"

/obj/item/disk/clonearmy/Initialize(mapload)
. = ..()
icon_state = "datadisk[rand(0,7)]"
add_overlay("datadisk_gene")

/obj/item/disk/clonearmy/attack_self(mob/user)
var/targName = tgui_input_text(user, "Enter a directive for the evil clones.", "Clone Directive Entry", objective, CONFIG_GET(number/max_law_len), TRUE)
if(!targName)
return
if(is_ic_filtered(targName))
to_chat(user, span_warning("Error: Directive contains invalid text."))
return
var/list/soft_filter_result = is_soft_ooc_filtered(targName)
if(soft_filter_result)
if(tgui_alert(user,"Your directive contains \"[soft_filter_result[CHAT_FILTER_INDEX_WORD]]\". \"[soft_filter_result[CHAT_FILTER_INDEX_REASON]]\", Are you sure you want to use it?", "Soft Blocked Word", list("Yes", "No")) != "Yes")
return
message_admins("[ADMIN_LOOKUPFLW(user)] has passed the soft filter for \"[soft_filter_result[CHAT_FILTER_INDEX_WORD]]\" they may be using a disallowed term for a clone directive. Directive: \"[html_encode(targName)]\"")
log_admin_private("[key_name(user)] has passed the soft filter for \"[soft_filter_result[CHAT_FILTER_INDEX_WORD]]\" they may be using a disallowed term for a clone directive. Directive: \"[targName]\"")
objective = targName
..()

/obj/item/disk/clonearmy/attack()
return

/obj/item/disk/clonearmy/afterattack(atom/target, mob/user, proximity)
. = ..()
var/atom/A = target
if(!proximity)
return
. |= AFTERATTACK_PROCESSED_ITEM
if(!istype(A, /obj/machinery/clonepod/experimental))
return
to_chat(user, "You upload the directive to the experimental cloner.")
var/obj/machinery/clonepod/experimental/pod = target
pod.custom_objective = objective
pod.RefreshParts()
pod.locked = TRUE // The pod shouldn't be eligible for cloner event.
3 changes: 3 additions & 0 deletions tgstation.dme
Original file line number Diff line number Diff line change
Expand Up @@ -6078,6 +6078,9 @@
#include "monkestation\code\modules\antagonists\contractor\items\modsuit\theme.dm"
#include "monkestation\code\modules\antagonists\cult\blood_magic.dm"
#include "monkestation\code\modules\antagonists\ert\moff_inspectors.dm"
#include "monkestation\code\modules\antagonists\evil_clone\evil_clone.dm"
#include "monkestation\code\modules\antagonists\evil_clone\evil_clone_objective.dm"
#include "monkestation\code\modules\antagonists\evil_clone\evil_event.dm"
#include "monkestation\code\modules\antagonists\florida_man\__outfits.dm"
#include "monkestation\code\modules\antagonists\florida_man\_defines.dm"
#include "monkestation\code\modules\antagonists\florida_man\_florida_man.dm"
Expand Down
Loading