From ef10c0abcc801df9e41b4eaf6defcef3f78625a5 Mon Sep 17 00:00:00 2001 From: silicons <2003111+silicons@users.noreply.github.com> Date: Wed, 15 Oct 2025 09:40:42 -0700 Subject: [PATCH] lore enforcement update (#7358) --- citadel.dme | 6 +- code/__DEFINES/dna.dm | 7 + code/game/dna/dna2_domutcheck.dm | 9 +- .../genetics}/dna_injector.dm | 3 +- code/modules/genetics/dna_scanner.dm | 165 +++++++++++++ .../genetics/dna_scanner_console.dm} | 216 +----------------- code/modules/genetics/misc.dm | 33 +++ .../mob/living/carbon/human/human_helpers.dm | 2 + code/modules/species/protean/protean.dm | 9 + 9 files changed, 240 insertions(+), 210 deletions(-) rename code/{game/objects/items/weapons => modules/genetics}/dna_injector.dm (99%) create mode 100644 code/modules/genetics/dna_scanner.dm rename code/{game/dna/dna_modifier.dm => modules/genetics/dna_scanner_console.dm} (76%) create mode 100644 code/modules/genetics/misc.dm diff --git a/citadel.dme b/citadel.dme index 9ed6274dc3a..10a3c8b44bb 100644 --- a/citadel.dme +++ b/citadel.dme @@ -1258,7 +1258,6 @@ #include "code\game\dna\dna2.dm" #include "code\game\dna\dna2_domutcheck.dm" #include "code\game\dna\dna2_helpers.dm" -#include "code\game\dna\dna_modifier.dm" #include "code\game\dna\genes\_gene.dm" #include "code\game\dna\genes\disabilities.dm" #include "code\game\dna\genes\powers.dm" @@ -1949,7 +1948,6 @@ #include "code\game\objects\items\weapons\candle.dm" #include "code\game\objects\items\weapons\cigs_lighters.dm" #include "code\game\objects\items\weapons\cosmetics.dm" -#include "code\game\objects\items\weapons\dna_injector.dm" #include "code\game\objects\items\weapons\duct_tape.dm" #include "code\game\objects\items\weapons\explosives.dm" #include "code\game\objects\items\weapons\extinguisher.dm" @@ -3163,6 +3161,10 @@ #include "code\modules\gateway\stargate\stargate-destination.dm" #include "code\modules\gateway\stargate\stargate-station.dm" #include "code\modules\gateway\stargate\stargate.dm" +#include "code\modules\genetics\dna_injector.dm" +#include "code\modules\genetics\dna_scanner.dm" +#include "code\modules\genetics\dna_scanner_console.dm" +#include "code\modules\genetics\misc.dm" #include "code\modules\genetics\side_effects.dm" #include "code\modules\roles\role_tweak.dm" #include "code\modules\roles\ghostroles\instantiator.dm" diff --git a/code/__DEFINES/dna.dm b/code/__DEFINES/dna.dm index fd6db84f885..55867491f79 100644 --- a/code/__DEFINES/dna.dm +++ b/code/__DEFINES/dna.dm @@ -70,3 +70,10 @@ var/DNABLOCK_HALLUCINATION = 0 var/DNABLOCK_NOPRINTS = 0 var/DNABLOCK_NOSHOCK = 0 var/DNABLOCK_DWARFISM = 0 + +#define DNA_BLOCK_SIZE 3 + +// Buffer datatype flags. +#define DNA2_BUF_UI 1 +#define DNA2_BUF_UE 2 +#define DNA2_BUF_SE 4 diff --git a/code/game/dna/dna2_domutcheck.dm b/code/game/dna/dna2_domutcheck.dm index d1f75215936..87b4b22c95a 100644 --- a/code/game/dna/dna2_domutcheck.dm +++ b/code/game/dna/dna2_domutcheck.dm @@ -5,9 +5,14 @@ // flags: See below, bitfield. #define MUTCHK_FORCED 1 /proc/domutcheck(mob/living/M, connected=null, flags=0) + if(QDELETED(M) || !M.dna) + return + if(M.isSynthetic()) + return // fuck off powergamers for(var/datum/gene/gene in dna_genes) - if(!M || !M.dna) - return + // incase they get deleted by activation + if(QDELETED(M)) + break if(!gene.block) continue diff --git a/code/game/objects/items/weapons/dna_injector.dm b/code/modules/genetics/dna_injector.dm similarity index 99% rename from code/game/objects/items/weapons/dna_injector.dm rename to code/modules/genetics/dna_injector.dm index 76ca19abec9..cf2873a1b5b 100644 --- a/code/game/objects/items/weapons/dna_injector.dm +++ b/code/modules/genetics/dna_injector.dm @@ -1,3 +1,4 @@ +// TODO: rename to /item/dna_injector /obj/item/dnainjector name = "\improper DNA injector" desc = "This injects the person with DNA." @@ -121,7 +122,7 @@ target.visible_message("\The [target] has been injected with \the [src] by \the [user].") var/mob/living/carbon/human/H = target - if(!istype(H)) + if(!istype(H) || H.isSynthetic()) to_chat(user, "Apparently it didn't work...") return diff --git a/code/modules/genetics/dna_scanner.dm b/code/modules/genetics/dna_scanner.dm new file mode 100644 index 00000000000..323b47f6999 --- /dev/null +++ b/code/modules/genetics/dna_scanner.dm @@ -0,0 +1,165 @@ +// TODO: rename to /machinery/dna_scanner +/obj/machinery/dna_scannernew + name = "\improper DNA modifier" + desc = "It scans DNA structures." + icon = 'icons/obj/medical/cryogenic2.dmi' + icon_state = "scanner_0" + density = TRUE + anchored = TRUE + use_power = USE_POWER_IDLE + idle_power_usage = 50 + active_power_usage = 300 + interaction_flags_machine = INTERACT_MACHINE_OFFLINE | INTERACT_MACHINE_ALLOW_SILICON + circuit = /obj/item/circuitboard/clonescanner + + var/locked = FALSE + var/opened = FALSE + var/mob/living/carbon/occupant = null + var/obj/item/reagent_containers/glass/beaker = null + +/obj/machinery/dna_scannernew/relaymove(mob/user) + if (user.stat) + return + src.go_out() + return + +/obj/machinery/dna_scannernew/verb/eject() + set src in oview(1) + set category = VERB_CATEGORY_OBJECT + set name = "Eject DNA Scanner" + + if (usr.stat != 0) + return + + eject_occupant() + + add_fingerprint(usr) + return + +/obj/machinery/dna_scannernew/proc/eject_occupant() + src.go_out() + for(var/obj/O in src) + if((!istype(O,/obj/item/reagent_containers)) && (!istype(O,/obj/item/circuitboard/clonescanner)) && (!istype(O,/obj/item/stock_parts)) && (!istype(O,/obj/item/stack/cable_coil))) + O.forceMove(get_turf(src)) + if(!occupant) + for(var/mob/M in src)//Failsafe so you can get mobs out + M.forceMove(get_turf(src)) + +/** + *? Allows borgs to clone people without external assistance. + */ +/obj/machinery/dna_scannernew/MouseDroppedOnLegacy(mob/target, mob/user) + if(user.stat || user.lying || !Adjacent(user) || !target.Adjacent(user)|| !ishuman(target)) + return + put_in(target) + +/obj/machinery/dna_scannernew/verb/move_inside() + set src in oview(1) + set category = VERB_CATEGORY_OBJECT + set name = "Enter DNA Scanner" + + if (usr.stat != 0) + return + if (!ishuman(usr) && !issmall(usr)) //Make sure they're a mob that has dna + to_chat(usr, SPAN_NOTICE("Try as you might, you can not climb up into the scanner.")) + return + if (src.occupant) + to_chat(usr, SPAN_WARNING("The scanner is already occupied!")) + return + if (usr.abiotic()) + to_chat(usr, SPAN_WARNING("The subject cannot have abiotic items on.")) + return + usr.stop_pulling() + usr.forceMove(src) + usr.update_perspective() + src.occupant = usr + src.icon_state = "scanner_1" + src.add_fingerprint(usr) + return + +/obj/machinery/dna_scannernew/attackby(obj/item/item, mob/user) + if(istype(item, /obj/item/reagent_containers/glass)) + if(beaker) + to_chat(user, SPAN_WARNING("A beaker is already loaded into the machine.")) + return + if(!user.attempt_insert_item_for_installation(item, src)) + return + beaker = item + user.visible_message("\The [user] adds \a [item] to \the [src]!", "You add \a [item] to \the [src]!") + return + + else if(istype(item, /obj/item/organ/internal/brain)) + if (src.occupant) + to_chat(user, SPAN_WARNING("The scanner is already occupied!")) + return + var/obj/item/organ/internal/brain/brain = item + if(brain.clone_source) + if(!user.attempt_insert_item_for_installation(brain, src)) + return + put_in(brain.brainmob) + src.add_fingerprint(user) + user.visible_message("\The [user] adds \a [item] to \the [src]!", "You add \a [item] to \the [src]!") + return + else + to_chat(user,"\The [brain] is not acceptable for genetic sampling!") + + else if (!istype(item, /obj/item/grab)) + return + var/obj/item/grab/G = item + if (!ismob(G.affecting)) + return + if (src.occupant) + to_chat(user, SPAN_WARNING("The scanner is already occupied!")) + return + if (G.affecting.abiotic()) + to_chat(user, SPAN_WARNING("The subject cannot have abiotic items on.")) + return + put_in(G.affecting) + src.add_fingerprint(user) + qdel(G) + return + +/obj/machinery/dna_scannernew/proc/put_in(mob/M) + occupant = M + M.forceMove(src) + M.update_perspective() + + icon_state = "scanner_1" + + // search for ghosts, if the corpse is empty and the scanner is connected to a cloner + if(locate(/obj/machinery/computer/cloning, get_step(src, NORTH)) \ + || locate(/obj/machinery/computer/cloning, get_step(src, SOUTH)) \ + || locate(/obj/machinery/computer/cloning, get_step(src, EAST)) \ + || locate(/obj/machinery/computer/cloning, get_step(src, WEST))) + + if(!M.client && M.mind) + for(var/mob/observer/dead/ghost in GLOB.player_list) + if(ghost.mind == M.mind) + to_chat(ghost, "Your corpse has been placed into a cloning scanner. Return to your body if you want to be resurrected/cloned! (Verbs -> Ghost -> Re-enter corpse)") + break + return + +/obj/machinery/dna_scannernew/proc/go_out() + if(!occupant|| locked) + return + if(istype(occupant,/mob/living/carbon/brain)) + for(var/obj/O in src) + if(istype(O,/obj/item/organ/internal/brain)) + O.forceMove(loc) + occupant.forceMove(O) + break + else + occupant.forceMove(loc) + occupant.update_perspective() + occupant = null + icon_state = "scanner_0" + +/obj/machinery/dna_scannernew/proc/occupant_valid(mob/victim) + if(isnull(victim)) + victim = occupant + if(isnull(victim)) + return FALSE + if(iscarbon(victim)) + var/mob/living/carbon/casted_carbon = victim + return !casted_carbon.isSynthetic() + return FALSE diff --git a/code/game/dna/dna_modifier.dm b/code/modules/genetics/dna_scanner_console.dm similarity index 76% rename from code/game/dna/dna_modifier.dm rename to code/modules/genetics/dna_scanner_console.dm index 14e2a73daf0..24e960259e3 100644 --- a/code/game/dna/dna_modifier.dm +++ b/code/modules/genetics/dna_scanner_console.dm @@ -1,198 +1,4 @@ -#define DNA_BLOCK_SIZE 3 - -// Buffer datatype flags. -#define DNA2_BUF_UI 1 -#define DNA2_BUF_UE 2 -#define DNA2_BUF_SE 4 - -//list("data" = null, "owner" = null, "label" = null, "type" = null, "ue" = 0), -/datum/dna2/record - var/datum/dna/dna = null - var/types=0 - var/name="Empty" - - // Stuff for cloners - var/id=null - var/implant=null - var/ckey=null - var/mind=null - var/languages=null - var/list/flavor=null - var/gender = null - var/list/body_descriptors = null - var/list/genetic_modifiers = list() // Modifiers with the MODIFIER_GENETIC flag are saved. Note that only the type is saved, not an instance. - -/datum/dna2/record/proc/GetData() - var/list/ser=list("data" = null, "owner" = null, "label" = null, "type" = null, "ue" = 0) - if(dna) - ser["ue"] = (types & DNA2_BUF_UE) == DNA2_BUF_UE - if(types & DNA2_BUF_SE) - ser["data"] = dna.SE - else - ser["data"] = dna.UI - ser["owner"] = src.dna.real_name - ser["label"] = name - if(types & DNA2_BUF_UI) - ser["type"] = "ui" - else - ser["type"] = "se" - return ser - -//! ## DNA MACHINES -/obj/machinery/dna_scannernew - name = "\improper DNA modifier" - desc = "It scans DNA structures." - icon = 'icons/obj/medical/cryogenic2.dmi' - icon_state = "scanner_0" - density = TRUE - anchored = TRUE - use_power = USE_POWER_IDLE - idle_power_usage = 50 - active_power_usage = 300 - interaction_flags_machine = INTERACT_MACHINE_OFFLINE | INTERACT_MACHINE_ALLOW_SILICON - circuit = /obj/item/circuitboard/clonescanner - - var/locked = FALSE - var/opened = FALSE - var/mob/living/carbon/occupant = null - var/obj/item/reagent_containers/glass/beaker = null - -/obj/machinery/dna_scannernew/relaymove(mob/user) - if (user.stat) - return - src.go_out() - return - -/obj/machinery/dna_scannernew/verb/eject() - set src in oview(1) - set category = VERB_CATEGORY_OBJECT - set name = "Eject DNA Scanner" - - if (usr.stat != 0) - return - - eject_occupant() - - add_fingerprint(usr) - return - -/obj/machinery/dna_scannernew/proc/eject_occupant() - src.go_out() - for(var/obj/O in src) - if((!istype(O,/obj/item/reagent_containers)) && (!istype(O,/obj/item/circuitboard/clonescanner)) && (!istype(O,/obj/item/stock_parts)) && (!istype(O,/obj/item/stack/cable_coil))) - O.forceMove(get_turf(src)) - if(!occupant) - for(var/mob/M in src)//Failsafe so you can get mobs out - M.forceMove(get_turf(src)) - -/** - *? Allows borgs to clone people without external assistance. - */ -/obj/machinery/dna_scannernew/MouseDroppedOnLegacy(mob/target, mob/user) - if(user.stat || user.lying || !Adjacent(user) || !target.Adjacent(user)|| !ishuman(target)) - return - put_in(target) - -/obj/machinery/dna_scannernew/verb/move_inside() - set src in oview(1) - set category = VERB_CATEGORY_OBJECT - set name = "Enter DNA Scanner" - - if (usr.stat != 0) - return - if (!ishuman(usr) && !issmall(usr)) //Make sure they're a mob that has dna - to_chat(usr, SPAN_NOTICE("Try as you might, you can not climb up into the scanner.")) - return - if (src.occupant) - to_chat(usr, SPAN_WARNING("The scanner is already occupied!")) - return - if (usr.abiotic()) - to_chat(usr, SPAN_WARNING("The subject cannot have abiotic items on.")) - return - usr.stop_pulling() - usr.forceMove(src) - usr.update_perspective() - src.occupant = usr - src.icon_state = "scanner_1" - src.add_fingerprint(usr) - return - -/obj/machinery/dna_scannernew/attackby(obj/item/item, mob/user) - if(istype(item, /obj/item/reagent_containers/glass)) - if(beaker) - to_chat(user, SPAN_WARNING("A beaker is already loaded into the machine.")) - return - if(!user.attempt_insert_item_for_installation(item, src)) - return - beaker = item - user.visible_message("\The [user] adds \a [item] to \the [src]!", "You add \a [item] to \the [src]!") - return - - else if(istype(item, /obj/item/organ/internal/brain)) - if (src.occupant) - to_chat(user, SPAN_WARNING("The scanner is already occupied!")) - return - var/obj/item/organ/internal/brain/brain = item - if(brain.clone_source) - if(!user.attempt_insert_item_for_installation(brain, src)) - return - put_in(brain.brainmob) - src.add_fingerprint(user) - user.visible_message("\The [user] adds \a [item] to \the [src]!", "You add \a [item] to \the [src]!") - return - else - to_chat(user,"\The [brain] is not acceptable for genetic sampling!") - - else if (!istype(item, /obj/item/grab)) - return - var/obj/item/grab/G = item - if (!ismob(G.affecting)) - return - if (src.occupant) - to_chat(user, SPAN_WARNING("The scanner is already occupied!")) - return - if (G.affecting.abiotic()) - to_chat(user, SPAN_WARNING("The subject cannot have abiotic items on.")) - return - put_in(G.affecting) - src.add_fingerprint(user) - qdel(G) - return - -/obj/machinery/dna_scannernew/proc/put_in(mob/M) - occupant = M - M.update_perspective() - - icon_state = "scanner_1" - - // search for ghosts, if the corpse is empty and the scanner is connected to a cloner - if(locate(/obj/machinery/computer/cloning, get_step(src, NORTH)) \ - || locate(/obj/machinery/computer/cloning, get_step(src, SOUTH)) \ - || locate(/obj/machinery/computer/cloning, get_step(src, EAST)) \ - || locate(/obj/machinery/computer/cloning, get_step(src, WEST))) - - if(!M.client && M.mind) - for(var/mob/observer/dead/ghost in GLOB.player_list) - if(ghost.mind == M.mind) - to_chat(ghost, "Your corpse has been placed into a cloning scanner. Return to your body if you want to be resurrected/cloned! (Verbs -> Ghost -> Re-enter corpse)") - break - return - -/obj/machinery/dna_scannernew/proc/go_out() - if(!occupant|| locked) - return - if(istype(occupant,/mob/living/carbon/brain)) - for(var/obj/O in src) - if(istype(O,/obj/item/organ/internal/brain)) - O.forceMove(loc) - occupant.forceMove(O) - break - else - occupant.forceMove(loc) - occupant.update_perspective() - occupant = null - icon_state = "scanner_0" - +// TODO: rename to /computer/dna_scanner_console /obj/machinery/computer/scan_consolenew name = "DNA Modifier Access Console" desc = "Scan DNA." @@ -323,7 +129,7 @@ var/data[0] data["selectedMenuKey"] = selected_menu_key data["locked"] = src.connected.locked - data["hasOccupant"] = connected.occupant ? 1 : 0 + data["hasOccupant"] = connected.occupant_valid() ? 1 : 0 data["isInjectorReady"] = injector_ready @@ -358,7 +164,7 @@ data["selectedUITargetHex"] = selected_ui_target_hex var/occupantData[0] - if (!src.connected.occupant || !src.connected.occupant.dna) + if (!src.connected.occupant_valid() || !src.connected.occupant.dna) occupantData["name"] = null occupantData["stat"] = null occupantData["isViableSubject"] = null @@ -434,7 +240,7 @@ sleep(10 * 2) irradiating = 0 - if (!src.connected.occupant) + if (!src.connected.occupant_valid()) return 1 // return 1 forces an update to all Nano uis attached to src if (prob(95)) @@ -535,7 +341,7 @@ irradiating = 0 - if (!src.connected.occupant) + if (!src.connected.occupant_valid()) return 1 if (prob((80 + (src.radiation_duration / 2)))) @@ -557,7 +363,7 @@ //////////////////////////////////////////////////////// if (href_list["injectRejuvenators"]) - if (!connected.occupant) + if (!connected.occupant_valid()) return 0 var/inject_amount = round(text2num(href_list["injectRejuvenators"]), 5) // round to nearest 5 if (inject_amount < 0) // Since the user can actually type the commands himself, some sanity checking @@ -593,7 +399,7 @@ irradiating = 0 - if(src.connected.occupant) + if(src.connected.occupant_valid()) if (prob((80 + (src.radiation_duration / 2)))) // FIXME: Find out what these corresponded to and change them to the WHATEVERBLOCK they need to be. //if ((src.selected_se_block != 2 || src.selected_se_block != 12 || src.selected_se_block != 8 || src.selected_se_block || 10) && prob (20)) @@ -664,7 +470,7 @@ return 0 // Not a valid buffer id if (bufferOption == "saveUI") - if(src.connected.occupant && src.connected.occupant.dna) + if(src.connected.occupant_valid() && src.connected.occupant.dna) var/datum/dna2/record/databuf=new databuf.types = DNA2_BUF_UE databuf.dna = src.connected.occupant.dna.Clone() @@ -678,7 +484,7 @@ return 1 if (bufferOption == "saveUIAndUE") - if(src.connected.occupant && src.connected.occupant.dna) + if(src.connected.occupant_valid() && src.connected.occupant.dna) var/datum/dna2/record/databuf=new databuf.types = DNA2_BUF_UI|DNA2_BUF_UE databuf.dna = src.connected.occupant.dna.Clone() @@ -692,7 +498,7 @@ return 1 if (bufferOption == "saveSE") - if(src.connected.occupant && src.connected.occupant.dna) + if(src.connected.occupant_valid() && src.connected.occupant.dna) var/datum/dna2/record/databuf=new databuf.types = DNA2_BUF_SE databuf.dna = src.connected.occupant.dna.Clone() @@ -717,7 +523,7 @@ return 1 if (bufferOption == "transfer") - if (!src.connected.occupant || (MUTATION_NOCLONE in src.connected.occupant.mutations) || !src.connected.occupant.dna) + if (!src.connected.occupant_valid() || (MUTATION_NOCLONE in src.connected.occupant.mutations) || !src.connected.occupant.dna) return irradiating = 2 diff --git a/code/modules/genetics/misc.dm b/code/modules/genetics/misc.dm new file mode 100644 index 00000000000..a3ad7a84d81 --- /dev/null +++ b/code/modules/genetics/misc.dm @@ -0,0 +1,33 @@ + +//list("data" = null, "owner" = null, "label" = null, "type" = null, "ue" = 0), +/datum/dna2/record + var/datum/dna/dna = null + var/types=0 + var/name="Empty" + + // Stuff for cloners + var/id=null + var/implant=null + var/ckey=null + var/mind=null + var/languages=null + var/list/flavor=null + var/gender = null + var/list/body_descriptors = null + var/list/genetic_modifiers = list() // Modifiers with the MODIFIER_GENETIC flag are saved. Note that only the type is saved, not an instance. + +/datum/dna2/record/proc/GetData() + var/list/ser=list("data" = null, "owner" = null, "label" = null, "type" = null, "ue" = 0) + if(dna) + ser["ue"] = (types & DNA2_BUF_UE) == DNA2_BUF_UE + if(types & DNA2_BUF_SE) + ser["data"] = dna.SE + else + ser["data"] = dna.UI + ser["owner"] = src.dna.real_name + ser["label"] = name + if(types & DNA2_BUF_UI) + ser["type"] = "ui" + else + ser["type"] = "se" + return ser diff --git a/code/modules/mob/living/carbon/human/human_helpers.dm b/code/modules/mob/living/carbon/human/human_helpers.dm index 4bae6b3e84c..f2827ef5c24 100644 --- a/code/modules/mob/living/carbon/human/human_helpers.dm +++ b/code/modules/mob/living/carbon/human/human_helpers.dm @@ -117,6 +117,8 @@ if(fast_is_species_type(src, /datum/species/shapeshifter/holosphere)) return TRUE + if(fast_is_species_type(src, /datum/species/protean)) + return TRUE return FALSE diff --git a/code/modules/species/protean/protean.dm b/code/modules/species/protean/protean.dm index 4a3891da23b..4c7d30aa8e4 100644 --- a/code/modules/species/protean/protean.dm +++ b/code/modules/species/protean/protean.dm @@ -170,6 +170,15 @@ /datum/species/protean/create_organs(mob/living/carbon/human/H) H.synth_color = TRUE . = ..() + for(var/obj/item/organ/external/external_organ in H.get_external_organs()) + if(external_organ.robotic < ORGAN_ROBOT) + external_organ.robotize() + if(!external_organ.model) + // let's not play this game + // :trol: + external_organ.model = GLOB.all_robolimbs[1] + // LEGACY: make sure their synthetic-ness is sticky by calling isSynthetic + H.isSynthetic() // todo: this is utter shitcode and will break if we CHECK_TICK in SSticker, and should probably be part of postspawn or something spawn(5) //Let their real nif load if they have one