[MIRROR] Adds Trait Genetics (#10142)

Co-authored-by: Cameron Lennox <killer65311@gmail.com>
Co-authored-by: Kashargul <144968721+Kashargul@users.noreply.github.com>
This commit is contained in:
CHOMPStation2StaffMirrorBot
2025-02-13 07:41:59 -07:00
committed by GitHub
parent d81e145924
commit 7bfffc808d
138 changed files with 3818 additions and 2996 deletions

View File

@@ -1,20 +1,10 @@
/**
* DNA 2: The Spaghetti Strikes Back
*
* @author N3X15 <nexisentertainment@gmail.com>
*/
// What each index means:
#define DNA_OFF_LOWERBOUND 1
#define DNA_OFF_UPPERBOUND 2
#define DNA_ON_LOWERBOUND 3
#define DNA_ON_UPPERBOUND 4
// For later:
//# define DNA_SE_LENGTH 50 // Was STRUCDNASIZE, size 27. 15 new blocks added = 42, plus room to grow.
// Defines which values mean "on" or "off".
// Defines which values mean "on" or "off".
// This is to make some of the more OP superpowers a larger PITA to activate,
// and to tell our new DNA datum which values to set in order to turn something
// on or off.
@@ -23,7 +13,19 @@ var/global/list/dna_activity_bounds[DNA_SE_LENGTH]
// Used to determine what each block means (admin hax and species stuff on /vg/, mostly)
var/global/list/assigned_blocks[DNA_SE_LENGTH]
var/global/list/datum/dna/gene/dna_genes[0]
// Traitgenes Genes accessible by global VV, and lists for good and bad mutations for quick randomized selection of traitgenes. Removed dna from gene's path
GLOBAL_LIST_EMPTY_TYPED(dna_genes, /datum/gene)
GLOBAL_LIST_EMPTY(trait_to_dna_genes) // Reverse lookup genes, use get_gene_from_trait(var/trait_path) to read this
GLOBAL_LIST_EMPTY_TYPED(dna_genes_good, /datum/gene/trait)
GLOBAL_LIST_EMPTY_TYPED(dna_genes_neutral, /datum/gene/trait)
GLOBAL_LIST_EMPTY_TYPED(dna_genes_bad, /datum/gene/trait)
/proc/get_gene_from_trait(var/trait_path) // ALWAYS USE THIS
RETURN_TYPE(/datum/gene/trait)
var/G = GLOB.trait_to_dna_genes[trait_path]
if(!G) // This SHOULD NOT HAPPEN, be sure any viruses or injectors that give trait paths are actually traitgenes.
stack_trace("[trait_path] was used as a traitgene, without being flagged as one.")
return G
/datum/dna
// READ-ONLY, GETS OVERWRITTEN
@@ -70,7 +72,6 @@ var/global/list/datum/dna/gene/dna_genes[0]
var/list/custom_heat = list()
var/list/custom_cold = list()
var/digitigrade = 0 //0, Not FALSE, for future use as indicator for digitigrade types
// VOREStation
// New stuff
var/species = SPECIES_HUMAN
@@ -88,10 +89,10 @@ var/global/list/datum/dna/gene/dna_genes[0]
new_dna.real_name=real_name
new_dna.species=species
new_dna.body_markings=body_markings.Copy()
new_dna.base_species=base_species //VOREStation Edit
new_dna.custom_species=custom_species //VOREStaton Edit
new_dna.species_traits=species_traits.Copy() //VOREStation Edit
new_dna.blood_color=blood_color //VOREStation Edit
new_dna.base_species=base_species
new_dna.custom_species=custom_species
new_dna.species_traits=species_traits.Copy()
new_dna.blood_color=blood_color
new_dna.blood_reagents=blood_reagents
new_dna.scale_appearance = scale_appearance
new_dna.offset_override = offset_override
@@ -105,13 +106,13 @@ var/global/list/datum/dna/gene/dna_genes[0]
new_dna.r_grad = r_grad
new_dna.g_grad = g_grad
new_dna.b_grad = b_grad
new_dna.custom_say=custom_say //VOREStaton Edit
new_dna.custom_ask=custom_ask //VOREStaton Edit
new_dna.custom_whisper=custom_whisper //VOREStaton Edit
new_dna.custom_exclaim=custom_exclaim //VOREStaton Edit
new_dna.custom_heat=custom_heat //VOREStation Edit
new_dna.custom_cold=custom_cold //VOREStation Edit
new_dna.digitigrade=src.digitigrade //VOREStation Edit
new_dna.custom_say=custom_say
new_dna.custom_ask=custom_ask
new_dna.custom_whisper=custom_whisper
new_dna.custom_exclaim=custom_exclaim
new_dna.custom_heat=custom_heat
new_dna.custom_cold=custom_cold
new_dna.digitigrade=src.digitigrade
var/list/body_markings_genetic = (body_markings - body_marking_nopersist_list)
new_dna.body_markings=body_markings_genetic.Copy()
for(var/b=1;b<=DNA_SE_LENGTH;b++)
@@ -259,7 +260,6 @@ var/global/list/datum/dna/gene/dna_genes[0]
SetUIValueRange(offset, red, 255, 1)
SetUIValueRange(offset + 1, green, 255, 1)
SetUIValueRange(offset + 2, blue, 255, 1)
// VORE Station Edit End
SetUIValueRange(DNA_UI_HAIR_R, character.r_hair, 255, 1)
SetUIValueRange(DNA_UI_HAIR_G, character.g_hair, 255, 1)
@@ -312,7 +312,7 @@ var/global/list/datum/dna/gene/dna_genes[0]
if (block<=0) return
ASSERT(maxvalue<=4095)
var/range = (4095 / maxvalue)
if(value!=null) //CHOMPEdit DO NOT PORT VIRGO'S FIX FOR RESLEEVING. IT IS BAD. Also fuck travis
if(value!=null)
SetUIValue(block,round(value * range),defer)
// Getter version of above.
@@ -429,6 +429,19 @@ var/global/list/datum/dna/gene/dna_genes[0]
/datum/dna/proc/GetSEBlock(var/block)
return EncodeDNABlock(GetSEValue(block))
// Get activation intensity, returns 0 to 1, you MUST check if the gene is active first! This is used for future expansion where genetraits can have multiple levels of activation/intensity
/datum/dna/proc/GetSEActivationIntensity(var/block)
if (block<=0) return 0
var/list/BOUNDS=GetDNABounds(block)
var/value=GetSEValue(block)
var/val = (value - BOUNDS[DNA_ON_LOWERBOUND]) / (BOUNDS[DNA_ON_UPPERBOUND] - BOUNDS[DNA_ON_LOWERBOUND])
return val
// Gets the activation intensity index. ex: if a genetrait has 5 levels of activations, the gene will have 5 possible levels of activation. this is a future TODO.
/datum/dna/proc/GetSEActivationLevel(var/block,var/number_of_levels)
var/raw_val = GetSEActivationIntensity(block)
return round(raw_val * number_of_levels) // TODO - If this should be round/floor/ceil
// Do not use this unless you absolutely have to.
// Set a block from a hex string. This is inefficient. If you can, use SetUIValue().
// Used in DNA modifiers.

View File

@@ -3,8 +3,27 @@
// M: Mob to mess with
// connected: Machine we're in, type unchecked so I doubt it's used beyond monkeying
// flags: See below, bitfield.
/proc/domutcheck(var/mob/living/M, var/connected=null, var/flags=0)
for(var/datum/dna/gene/gene in dna_genes)
// Traitgenes NO_SCAN and Synthetics cannot be mutated
if(M.isSynthetic())
return
if(ishuman(M))
var/mob/living/carbon/human/H = M
if(!H.species || H.species.flags & NO_SCAN)
return
// Traitgenes Sort genes into currently active, and deactivated... Genes that are active and may deactivate should do so before attempting to activate genes(to avoid conflicts blocking them!)
var/list/enabled_genes = list()
var/list/disabled_genes = list()
for(var/datum/gene/gene in GLOB.dna_genes) // Traitgenes Genes accessible by global VV, removed dna from path
if(!M || !M.dna)
return
if(gene.block)
if(gene.name in M.active_genes || gene.flags & GENE_ALWAYS_ACTIVATE)
enabled_genes.Add(gene)
else
disabled_genes.Add(gene)
for(var/datum/gene/gene in enabled_genes + disabled_genes) // Traitgenes Removed /dna/ from path
if(!M || !M.dna)
return
if(!gene.block)
@@ -21,22 +40,31 @@
gene_active = M.dna.GetSEState(gene.block)
// Prior state
var/gene_prior_status = (gene.type in M.active_genes)
var/gene_prior_status = (gene.name in M.active_genes) // Traitgenes Use name instead, cannot use type with dynamically setup traitgenes
var/changed = gene_active != gene_prior_status || (gene.flags & GENE_ALWAYS_ACTIVATE)
// If gene state has changed:
if(changed)
if(changed || flags & MUTCHK_FORCED) // Traitgenes MUTCHK_FORCED always applies or removes genes
// Gene active (or ALWAYS ACTIVATE)
if(gene_active || (gene.flags & GENE_ALWAYS_ACTIVATE))
testing("[gene.name] activated!")
// Traitgenes Handle trait conflicts, do not activate if so! Has to be done here so that SE activation without trait activation is possible.
if(istype(gene,/datum/gene/trait))
var/datum/gene/trait/TG = gene
if(!ishuman(M))
continue // Trait genes are human only
var/mob/living/carbon/human/H = M
if(TG.has_conflict(H.species.traits))
continue // The SE is on, but the gene is denied...
//testing("[gene.name] activated!")
gene.activate(M,connected,flags)
if(M)
M.active_genes |= gene.type
M.active_genes |= gene.name // Traitgenes Use name instead, cannot use type with dynamically setup traitgenes
M.update_icon = 1
// If Gene is NOT active:
else
testing("[gene.name] deactivated!")
//testing("[gene.name] deactivated!")
gene.deactivate(M,connected,flags)
if(M)
M.active_genes -= gene.type
M.active_genes -= gene.name // Traitgenes Use name instead, cannot use type with dynamically setup traitgenes
M.update_icon = 1
M.update_mutations()

View File

@@ -23,42 +23,51 @@
// Give Random Bad Mutation to M
/proc/randmutb(var/mob/living/M)
if(!M || !(M.dna)) return
// Traitgenes NO_SCAN and Synthetics cannot be mutated
if(M.isSynthetic())
return
if(ishuman(M))
var/mob/living/carbon/human/H = M
if(!H.species || H.species.flags & NO_SCAN)
return
M.dna.check_integrity()
//var/block = pick(GLASSESBLOCK,COUGHBLOCK,FAKEBLOCK,NERVOUSBLOCK,CLUMSYBLOCK,TWITCHBLOCK,HEADACHEBLOCK,BLINDBLOCK,DEAFBLOCK,HALLUCINATIONBLOCK) // Most of these are disabled anyway.
var/block = pick(FAKEBLOCK,CLUMSYBLOCK,BLINDBLOCK,DEAFBLOCK)
M.dna.SetSEState(block, 1)
// Traitgenes Pick from bad traitgenes
var/datum/gene/trait/T = pick(GLOB.dna_genes_bad + (prob(10) ? GLOB.dna_genes_neutral : list()) ) // Chance for neutrals as well
M.dna.SetSEState(T.block, TRUE)
// Give Random Good Mutation to M
/proc/randmutg(var/mob/living/M)
if(!M || !(M.dna)) return
// Traitgenes NO_SCAN and Synthetics cannot be mutated
if(M.isSynthetic())
return
if(ishuman(M))
var/mob/living/carbon/human/H = M
if(!H.species || H.species.flags & NO_SCAN)
return
M.dna.check_integrity()
//var/block = pick(HULKBLOCK,XRAYBLOCK,FIREBLOCK,TELEBLOCK,NOBREATHBLOCK,REMOTEVIEWBLOCK,REGENERATEBLOCK,INCREASERUNBLOCK,REMOTETALKBLOCK,MORPHBLOCK,BLENDBLOCK,NOPRINTSBLOCK,SHOCKIMMUNITYBLOCK,SMALLSIZEBLOCK) // Much like above, most of these blocks are disabled in code.
var/block = pick(HULKBLOCK,XRAYBLOCK,FIREBLOCK,TELEBLOCK,REGENERATEBLOCK,REMOTETALKBLOCK)
M.dna.SetSEState(block, 1)
// Random Appearance Mutation
/proc/randmuti(var/mob/living/M)
if(!M || !(M.dna)) return
M.dna.check_integrity()
M.dna.SetUIValue(rand(1,DNA_UI_LENGTH),rand(1,4095))
// Traitgenes Pick from good traitgenes
var/datum/gene/trait/T = pick(GLOB.dna_genes_good + (prob(10) ? GLOB.dna_genes_neutral : list()) ) // Chance for neutrals as well
M.dna.SetSEState(T.block, TRUE)
// Scramble UI or SE.
/proc/scramble(var/UI, var/mob/M, var/prob)
if(!M || !(M.dna)) return
// Traitgenes edit begin - NO_SCAN and Synthetics cannot be mutated
if(M.isSynthetic())
return
if(ishuman(M))
var/mob/living/carbon/human/H = M
if(!H.species || H.species.flags & NO_SCAN)
return
// Traitgenes edit end
M.dna.check_integrity()
if(UI)
for(var/i = 1, i <= DNA_UI_LENGTH-1, i++)
if(prob(prob))
M.dna.SetUIValue(i,rand(1,4095),1)
M.dna.UpdateUI()
M.UpdateAppearance()
else
for(var/i = 1, i <= DNA_SE_LENGTH-1, i++)
if(prob(prob))
M.dna.SetSEValue(i,rand(1,4095),1)
M.dna.UpdateSE()
domutcheck(M, null)
for(var/i = 1, i <= DNA_SE_LENGTH-1, i++)
if(prob(prob))
M.dna.SetSEValue(i,rand(1,4095),1)
M.dna.UpdateSE()
domutcheck(M, null)
M.UpdateAppearance()
return
// I haven't yet figured out what the fuck this is supposed to do.
@@ -122,8 +131,7 @@
return output
// /proc/updateappearance has changed behavior, so it's been removed
// Use mob.UpdateAppearance() instead.
// Use mob.UpdateAppearance()
// Simpler. Don't specify UI in order for the mob to use its own.
/mob/proc/UpdateAppearance(var/list/UI=null)
@@ -175,8 +183,6 @@
if((0 < beard) && (beard <= facial_hair_styles_list.len))
H.f_style = facial_hair_styles_list[beard]
// VORE StationEdit Start
// Ears
var/ears = dna.GetUIValueRange(DNA_UI_EAR_STYLE, ear_styles_list.len + 1) - 1
if(ears < 1)
@@ -257,6 +263,7 @@
H.custom_ask = dna.custom_ask
H.custom_whisper = dna.custom_whisper
H.custom_exclaim = dna.custom_exclaim
H.species.blood_color = dna.blood_color
H.fuzzy = dna.scale_appearance
H.offset_override = dna.offset_override
H.synth_markings = dna.synth_markings
@@ -281,9 +288,9 @@
H.species.species_sounds_female = dna.species_sounds_female
// CHOMPEnable ENd
H.force_update_organs()
H.force_update_organs() //VOREStation Add - Gotta do this too
H.force_update_limbs()
//H.update_body(0) //Done in force_update_limbs already
//H.update_body(0) //VOREStation Edit - Done in force_update_limbs already
H.update_eyes()
H.update_hair()

View File

@@ -1,6 +1,5 @@
#define DNA_BLOCK_SIZE 3
#define PAGE_UI "ui"
#define PAGE_SE "se"
#define PAGE_BUFFER "buffer"
#define PAGE_REJUVENATORS "rejuvenators"
@@ -69,7 +68,7 @@
interact_offline = 1
circuit = /obj/item/circuitboard/clonescanner
var/locked = 0
var/mob/living/carbon/occupant = null
var/datum/weakref/occupant = null
var/obj/item/reagent_containers/glass/beaker = null
var/opened = 0
var/damage_coeff
@@ -81,6 +80,10 @@
default_apply_parts()
RefreshParts()
/obj/machinery/dna_scannernew/Destroy()
eject_occupant()
. = ..()
/obj/machinery/dna_scannernew/RefreshParts()
scan_level = 0
damage_coeff = 0
@@ -112,16 +115,24 @@
return
/obj/machinery/dna_scannernew/proc/eject_occupant()
src.go_out()
var/mob/living/carbon/WC = occupant?.resolve()
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.loc = get_turf(src)//Ejects items that manage to get in there (exluding the components)
if(!occupant)
O.forceMove(get_turf(src)) //Ejects items that manage to get in there (exluding the components)
if(!WC)
for(var/mob/M in src)//Failsafe so you can get mobs out
M.loc = get_turf(src)
M.forceMove(get_turf(src))
/obj/machinery/dna_scannernew/MouseDrop_T(var/mob/target, var/mob/user) //Allows borgs to clone people without external assistance
if(user.stat || user.lying || !Adjacent(user) || !target.Adjacent(user)|| !ishuman(target))
var/mob/living/carbon/WC = occupant?.resolve()
if(user.stat || user.lying || !Adjacent(user) || !target.Adjacent(user)|| !ishuman(target) || WC)
return
// Traitgenes Do not allow buckled or ridden mobs
if(target.buckled)
return
if(target.has_buckled_mobs())
to_chat(user, span_warning("\The [target] has other entities attached to it. Remove them first."))
return
put_in(target)
@@ -130,27 +141,37 @@
set 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)
if(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
var/mob/living/carbon/WC = occupant?.resolve()
if(WC)
to_chat(usr, span_warning("There is already something inside."))
return
usr.stop_pulling()
usr.client.perspective = EYE_PERSPECTIVE
usr.client.eye = src
usr.loc = src
src.occupant = usr
src.icon_state = "scanner_1"
src.add_fingerprint(usr)
usr.forceMove(src)
occupant = WEAKREF(usr)
icon_state = "scanner_1"
add_fingerprint(usr)
SStgui.update_uis(src)
/obj/machinery/dna_scannernew/attackby(var/obj/item/item as obj, var/mob/user as mob)
// Traitgenes Deconstructable dna scanner
if(default_deconstruction_screwdriver(user, item))
return
if(default_deconstruction_crowbar(user, item))
return
if(istype(item, /obj/item/reagent_containers/glass))
if(beaker)
to_chat(user, span_warning("A beaker is already loaded into the machine."))
@@ -158,19 +179,19 @@
beaker = item
user.drop_item()
item.loc = src
item.forceMove(src)
user.visible_message("\The [user] adds \a [item] to \the [src]!", "You add \a [item] to \the [src]!")
SStgui.update_uis(src)
return
else if(istype(item, /obj/item/organ/internal/brain))
if(src.occupant)
if(occupant)
to_chat(user, span_warning("The scanner is already occupied!"))
return
var/obj/item/organ/internal/brain/brain = item
if(brain.clone_source)
user.drop_item()
brain.loc = src
brain.forceMove(src)
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]!")
@@ -184,7 +205,7 @@
var/obj/item/grab/G = item
if(!ismob(G.affecting))
return
if(src.occupant)
if(occupant)
to_chat(user, span_warning("The scanner is already occupied!"))
return
if(G.affecting.abiotic())
@@ -195,13 +216,32 @@
qdel(G)
return
// Traitgenes Deconstructable dna scanner
/obj/machinery/dna_scannernew/dismantle()
// release contents
if(beaker)
beaker.forceMove(get_turf(src))
beaker = null
if(occupant)
var/mob/living/carbon/WC = occupant.resolve()
WC.forceMove(get_turf(src))
occupant = null
// Disconnect from our terminal
for(var/dirfind in cardinal)
var/obj/machinery/computer/scan_consolenew/console = locate(/obj/machinery/computer/scan_consolenew, get_step(src, dirfind))
if(console && console.connected == src)
console.connected = null
SStgui.close_uis(console)
break
. = ..()
/obj/machinery/dna_scannernew/proc/put_in(var/mob/M)
if(M.client)
M.client.perspective = EYE_PERSPECTIVE
M.client.eye = src
M.loc = src
src.occupant = M
src.icon_state = "scanner_1"
M.forceMove(src)
occupant = WEAKREF(M)
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)) \
@@ -217,49 +257,45 @@
SStgui.update_uis(src)
/obj/machinery/dna_scannernew/proc/go_out()
if((!( src.occupant ) || src.locked))
if((!(occupant) || locked))
return
if(src.occupant.client)
src.occupant.client.eye = src.occupant.client.mob
src.occupant.client.perspective = MOB_PERSPECTIVE
if(istype(occupant,/mob/living/carbon/brain))
var/mob/living/carbon/WC = occupant.resolve()
if(WC.client)
WC.client.eye = WC.client.mob
WC.client.perspective = MOB_PERSPECTIVE
if(istype(WC,/mob/living/carbon/brain))
for(var/obj/O in src)
if(istype(O,/obj/item/organ/internal/brain))
O.loc = get_turf(src)
src.occupant.loc = O
O.forceMove(get_turf(src))
WC.forceMove(O)
break
else
src.occupant.loc = src.loc
src.occupant = null
src.icon_state = "scanner_0"
WC.forceMove(loc)
occupant = null
icon_state = "scanner_0"
SStgui.update_uis(src)
/obj/machinery/dna_scannernew/ex_act(severity)
var/our_tile = loc //This is done here as if you try to feed loc in the A.forcemove, it will runtime as src id qdel'd before they can be moved.
switch(severity)
if(1.0)
for(var/atom/movable/A as mob|obj in src)
A.loc = src.loc
A.forceMove(our_tile)
ex_act(severity)
//Foreach goto(35)
//SN src = null
qdel(src)
return
if(2.0)
if(prob(50))
for(var/atom/movable/A as mob|obj in src)
A.loc = src.loc
A.forceMove(our_tile)
ex_act(severity)
//Foreach goto(108)
//SN src = null
qdel(src)
return
if(3.0)
if(prob(25))
for(var/atom/movable/A as mob|obj in src)
A.loc = src.loc
A.forceMove(our_tile)
ex_act(severity)
//Foreach goto(181)
//SN src = null
qdel(src)
return
else
@@ -280,25 +316,31 @@
var/selected_ui_target_hex = 1
var/radiation_duration = 2.0
var/radiation_intensity = 1.0
var/list/datum/dna2/record/buffers[3]
var/list/datum/transhuman/body_record/buffers[3] // Traitgenes Use bodyrecords
var/irradiating = 0
var/injector_ready = 0 //Quick fix for issue 286 (screwdriver the screen twice to restore injector) -Pete
var/obj/machinery/dna_scannernew/connected = null
var/obj/item/disk/data/disk = null
var/selected_menu_key = PAGE_UI
// Traitgenes body record disks are used instead of a unique disk
var/obj/item/disk/body_record/disk = null
var/selected_menu_key = PAGE_SE
anchored = TRUE
use_power = USE_POWER_IDLE
idle_power_usage = 10
active_power_usage = 400
/obj/machinery/computer/scan_consolenew/attackby(obj/item/I as obj, mob/user as mob)
if(istype(I, /obj/item/disk/data)) //INSERT SOME diskS
if(!src.disk)
user.drop_item()
I.loc = src
src.disk = I
to_chat(user, "You insert [I].")
SStgui.update_uis(src) // update all UIs attached to src
// Traitgenes body record disks are used instead of a unique disk
if(istype(I, /obj/item/disk/body_record)) //INSERT SOME diskS
if(connected)
if(!disk)
user.drop_item()
I.forceMove(src)
disk = I
to_chat(user, "You insert [I].")
SStgui.update_uis(src) // update all UIs attached to src
return
else
to_chat(user, "\The [src] will not accept a disk without a DNA modifier connected.")
return
else
..()
@@ -308,12 +350,10 @@
switch(severity)
if(1.0)
//SN src = null
qdel(src)
return
if(2.0)
if(prob(50))
//SN src = null
qdel(src)
return
else
@@ -322,9 +362,16 @@
/obj/machinery/computer/scan_consolenew/Initialize()
. = ..()
for(var/i=0;i<3;i++)
buffers[i+1]=new /datum/dna2/record
for(dir in list(NORTH,EAST,SOUTH,WEST))
connected = locate(/obj/machinery/dna_scannernew, get_step(src, dir))
// Traitgenes Use bodyrecords
var/datum/transhuman/body_record/R = new /datum/transhuman/body_record()
R.mydna = new
R.mydna.dna = new
R.mydna.dna.ResetUI()
R.mydna.dna.ResetSE()
buffers[i+1]=R
// Traitgenes don't alter direction of computer as this scans for neighbour
for(var/dirfind in cardinal)
connected = locate(/obj/machinery/dna_scannernew, get_step(src, dirfind))
if(connected)
break
VARSET_IN(src, injector_ready, TRUE, 25 SECONDS)
@@ -335,7 +382,7 @@
arr += "[i]:[EncodeDNABlock(buffer[i])]"
return arr
/obj/machinery/computer/scan_consolenew/proc/setInjectorBlock(var/obj/item/dnainjector/I, var/blk, var/datum/dna2/record/buffer)
/obj/machinery/computer/scan_consolenew/proc/setInjectorBlock(var/obj/item/dnainjector/I, var/blk, var/datum/transhuman/body_record/buffer) // Traitgenes Stores the entire body record
var/pos = findtext(blk,":")
if(!pos) return 0
var/id = text2num(copytext(blk,1,pos))
@@ -344,15 +391,6 @@
I.buf = buffer
return 1
/*
/obj/machinery/computer/scan_consolenew/process() //not really used right now
if(stat & (NOPOWER|BROKEN))
return
if(!( src.status )) //remove this
return
return
*/
/obj/machinery/computer/scan_consolenew/attack_ai(user as mob)
src.add_hiddenprint(user)
tgui_interact(user)
@@ -362,7 +400,8 @@
tgui_interact(user)
/obj/machinery/computer/scan_consolenew/tgui_interact(mob/user, datum/tgui/ui)
if(!connected || user == connected.occupant || user.stat)
var/mob/living/carbon/WC = connected?.occupant?.resolve()
if(!connected || user == WC || user.stat)
return
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
@@ -381,19 +420,24 @@
data["hasDisk"] = disk ? 1 : 0
var/diskData[0]
if(!disk || !disk.buf)
if(!disk || !disk.stored || !disk.stored.mydna) // Traitgenesbody record disks are used instead of a unique disk
diskData["data"] = null
diskData["owner"] = null
diskData["label"] = null
diskData["type"] = null
diskData["ue"] = null
else
diskData = disk.buf.GetData()
diskData = disk.stored.mydna.GetData() // Traitgenes body record disks are used instead of a unique disk
data["disk"] = diskData
var/list/new_buffers = list()
for(var/datum/dna2/record/buf in src.buffers)
new_buffers += list(buf.GetData())
// Traitgenes Fixed buffer menu
var/list/new_buffers[src.buffers.len]
for(var/i=1;i<=src.buffers.len;i++)
var/datum/transhuman/body_record/R = buffers[i]
if(R && R.mydna)
new_buffers[i]=R.mydna.GetData()
else
new_buffers[i]=list("data" = list(), "owner" = null, "label" = null, "type" = DNA2_BUF_SE, "ue" = 0)
data["buffers"]=new_buffers
data["radiationIntensity"] = radiation_intensity
@@ -409,7 +453,8 @@
data["selectedUITargetHex"] = selected_ui_target_hex
var/occupantData[0]
if(!src.connected.occupant || !src.connected.occupant.dna)
var/mob/living/carbon/WC = connected?.occupant?.resolve()
if(!WC || !WC.dna)
occupantData["name"] = null
occupantData["stat"] = null
occupantData["isViableSubject"] = null
@@ -421,18 +466,26 @@
occupantData["structuralEnzymes"] = null
occupantData["radiationLevel"] = null
else
occupantData["name"] = connected.occupant.real_name
occupantData["stat"] = connected.occupant.stat
occupantData["name"] = WC.real_name
occupantData["stat"] = WC.stat
occupantData["isViableSubject"] = 1
if(NOCLONE in connected.occupant.mutations || !src.connected.occupant.dna)
// Traitgenes NO_SCAN and Synthetics cannot be mutated
var/allowed = TRUE
if(WC.isSynthetic())
allowed = FALSE
if(ishuman(WC))
var/mob/living/carbon/human/H = WC
if(!H.species || (H.species.flags & NO_SCAN))
allowed = FALSE
if(!allowed || (NOCLONE in WC.mutations) || !WC.dna)
occupantData["isViableSubject"] = 0
occupantData["health"] = connected.occupant.health
occupantData["maxHealth"] = connected.occupant.maxHealth
occupantData["health"] = WC.health
occupantData["maxHealth"] = WC.maxHealth
occupantData["minHealth"] = CONFIG_GET(number/health_threshold_dead)
occupantData["uniqueEnzymes"] = connected.occupant.dna.unique_enzymes
occupantData["uniqueIdentity"] = connected.occupant.dna.uni_identity
occupantData["structuralEnzymes"] = connected.occupant.dna.struc_enzymes
occupantData["radiationLevel"] = connected.occupant.radiation
occupantData["uniqueEnzymes"] = WC.dna.unique_enzymes
occupantData["uniqueIdentity"] = WC.dna.uni_identity
occupantData["structuralEnzymes"] = WC.dna.struc_enzymes
occupantData["radiationLevel"] = WC.radiation
data["occupant"] = occupantData;
data["isBeakerLoaded"] = connected.beaker ? 1 : 0
@@ -454,7 +507,7 @@
return TRUE
if(!istype(ui.user.loc, /turf))
return TRUE
if(!src || !src.connected)
if(!src || !connected)
return TRUE
if(irradiating) // Make sure that it isn't already irradiating someone...
return TRUE
@@ -464,107 +517,60 @@
if(tgui_act_modal(action, params))
return TRUE
. = TRUE
switch(action)
if("selectMenuKey")
playsound(src, 'sound/machines/button.ogg', 30, 1, 0)
var/key = params["key"]
if(!(key in list(PAGE_UI, PAGE_SE, PAGE_BUFFER, PAGE_REJUVENATORS)))
return
if(!(key in list(/*PAGE_UI,*/ PAGE_SE, PAGE_BUFFER, PAGE_REJUVENATORS))) // Traitgenes Body design console is used to edit UIs now
return TRUE
selected_menu_key = key
return TRUE
if("toggleLock")
playsound(src, 'sound/machines/button.ogg', 30, 1, 0)
if(connected && connected.occupant)
connected.locked = !(connected.locked)
return TRUE
if("pulseRadiation")
playsound(src, 'sound/machines/button.ogg', 30, 1, 0)
irradiating = radiation_duration
var/lock_state = connected.locked
connected.locked = TRUE //lock it
SStgui.update_uis(src) // update all UIs attached to src
sleep(10 * radiation_duration) // sleep for radiation_duration seconds
irradiating = 0
connected.locked = lock_state
if(!connected.occupant)
return
if(prob(95))
if(prob(75))
randmutb(connected.occupant)
else
randmuti(connected.occupant)
else
if(prob(95))
randmutg(connected.occupant)
else
randmuti(connected.occupant)
connected.occupant.apply_effect(((radiation_intensity*3)+radiation_duration*3), IRRADIATE, check_protection = 0)
addtimer(CALLBACK(src, PROC_REF(do_pulse), lock_state), radiation_duration SECONDS, TIMER_DELETE_ME)
return TRUE
if("radiationDuration")
radiation_duration = clamp(text2num(params["value"]), 1, 20)
return TRUE
if("radiationIntensity")
radiation_intensity = clamp(text2num(params["value"]), 1, 10)
////////////////////////////////////////////////////////
if("changeUITarget")
selected_ui_target = clamp(text2num(params["value"]), 1, 15)
selected_ui_target_hex = num2text(selected_ui_target, 1, 16)
if("selectUIBlock") // This chunk of code updates selected block / sub-block based on click
var/select_block = text2num(params["block"])
var/select_subblock = text2num(params["subblock"])
if(!select_block || !select_subblock)
return
selected_ui_block = clamp(select_block, 1, DNA_UI_LENGTH)
selected_ui_subblock = clamp(select_subblock, 1, DNA_BLOCK_SIZE)
if("pulseUIRadiation")
var/block = connected.occupant.dna.GetUISubBlock(selected_ui_block,selected_ui_subblock)
irradiating = radiation_duration
var/lock_state = connected.locked
connected.locked = TRUE //lock it
SStgui.update_uis(src) // update all UIs attached to src
sleep(10 * radiation_duration) // sleep for radiation_duration seconds
irradiating = 0
connected.locked = lock_state
if(!connected.occupant)
return
if(prob((80 + (radiation_duration / 2))))
block = miniscrambletarget(num2text(selected_ui_target), radiation_intensity, radiation_duration)
connected.occupant.dna.SetUISubBlock(selected_ui_block,selected_ui_subblock,block)
connected.occupant.UpdateAppearance()
connected.occupant.apply_effect((radiation_intensity+radiation_duration), IRRADIATE, check_protection = 0)
else
if(prob(20 + radiation_intensity))
randmutb(connected.occupant)
domutcheck(connected.occupant,connected)
else
randmuti(connected.occupant)
connected.occupant.UpdateAppearance()
connected.occupant.apply_effect(((radiation_intensity*2)+radiation_duration), IRRADIATE, check_protection = 0)
////////////////////////////////////////////////////////
return TRUE
if("injectRejuvenators")
playsound(src, 'sound/machines/button.ogg', 30, 1, 0)
if(!connected.occupant || !connected.beaker)
return
return TRUE
var/mob/living/carbon/WC = connected?.occupant?.resolve()
var/inject_amount = clamp(round(text2num(params["amount"]), 5), 0, 50) // round to nearest 5 and clamp to 0-50
if(!inject_amount)
return
connected.beaker.reagents.trans_to_mob(connected.occupant, inject_amount, CHEM_BLOOD)
return TRUE
connected.beaker.reagents.trans_to_mob(WC, inject_amount, CHEM_BLOOD)
return TRUE
////////////////////////////////////////////////////////
if("selectSEBlock") // This chunk of code updates selected block / sub-block based on click (se stands for strutural enzymes)
playsound(src, "keyboard", 40)
var/select_block = text2num(params["block"])
var/select_subblock = text2num(params["subblock"])
if(!select_block || !select_subblock)
return
return TRUE
selected_se_block = clamp(select_block, 1, DNA_SE_LENGTH)
selected_se_subblock = clamp(select_subblock, 1, DNA_BLOCK_SIZE)
return TRUE
if("pulseSERadiation")
var/block = connected.occupant.dna.GetSESubBlock(selected_se_block,selected_se_subblock)
if(!connected?.occupant)
return TRUE
var/mob/living/carbon/WC = connected?.occupant?.resolve()
playsound(src, "keyboard", 40)
var/block = WC.dna.GetSESubBlock(selected_se_block,selected_se_subblock)
//var/original_block=block
//testing("Irradiating SE block [selected_se_block]:[selected_se_subblock] ([block])...")
@@ -572,156 +578,124 @@
var/lock_state = connected.locked
connected.locked = TRUE //lock it
SStgui.update_uis(src) // update all UIs attached to src
sleep(10 * radiation_duration) // sleep for radiation_duration seconds
//We call the do_irradiate proc here after radation_duration SECONDS
addtimer(CALLBACK(src, PROC_REF(do_irradiate), lock_state, block), radiation_duration SECONDS, TIMER_DELETE_ME)
return TRUE
irradiating = 0
connected.locked = lock_state
if(connected.occupant)
if(prob((80 + (radiation_duration / 2))))
// FIXME: Find out what these corresponded to and change them to the WHATEVERBLOCK they need to be.
//if((selected_se_block != 2 || selected_se_block != 12 || selected_se_block != 8 || selected_se_block || 10) && prob (20))
var/real_SE_block=selected_se_block
block = miniscramble(block, radiation_intensity, radiation_duration)
if(prob(20))
if(selected_se_block > 1 && selected_se_block < DNA_SE_LENGTH/2)
real_SE_block++
else if(selected_se_block > DNA_SE_LENGTH/2 && selected_se_block < DNA_SE_LENGTH)
real_SE_block--
//testing("Irradiated SE block [real_SE_block]:[selected_se_subblock] ([original_block] now [block]) [(real_SE_block!=selected_se_block) ? "(SHIFTED)":""]!")
connected.occupant.dna.SetSESubBlock(real_SE_block,selected_se_subblock,block)
connected.occupant.apply_effect((radiation_intensity+radiation_duration), IRRADIATE, check_protection = 0)
domutcheck(connected.occupant,connected)
else
connected.occupant.apply_effect(((radiation_intensity*2)+radiation_duration), IRRADIATE, check_protection = 0)
if (prob(80-radiation_duration))
//testing("Random bad mut!")
randmutb(connected.occupant)
domutcheck(connected.occupant,connected)
else
randmuti(connected.occupant)
//testing("Random identity mut!")
connected.occupant.UpdateAppearance()
if("ejectBeaker")
playsound(src, 'sound/machines/button.ogg', 30, 1, 0)
if(connected.beaker)
var/obj/item/reagent_containers/glass/B = connected.beaker
B.loc = connected.loc
B.forceMove(connected.loc)
connected.beaker = null
return TRUE
if("ejectOccupant")
playsound(src, 'sound/machines/button.ogg', 30, 1, 0)
connected.eject_occupant()
// Eject disk too, because we can't get to the UI otherwise
if(!disk)
return TRUE
disk.forceMove(get_turf(src))
disk = null
// Transfer Buffer Management
if("bufferOption")
var/bufferOption = params["option"]
var/bufferId = text2num(params["id"])
if(bufferId < 1 || bufferId > 3) // Not a valid buffer id
return
return TRUE
var/datum/dna2/record/buffer = buffers[bufferId]
var/datum/transhuman/body_record/buffer = buffers[bufferId] // Traitgenes Use bodyrecords
switch(bufferOption)
if("saveUI")
if(connected.occupant && connected.occupant.dna)
var/datum/dna2/record/databuf=new
databuf.types = DNA2_BUF_UI // DNA2_BUF_UE
databuf.dna = connected.occupant.dna.Clone()
if(ishuman(connected.occupant))
var/mob/living/carbon/human/H = connected.occupant
databuf.dna.real_name = H.dna.real_name
databuf.gender = H.gender
databuf.body_descriptors = H.descriptors
databuf.name = "Unique Identifier"
buffers[bufferId] = databuf
if("saveUIAndUE")
if(connected.occupant && connected.occupant.dna)
var/datum/dna2/record/databuf=new
databuf.types = DNA2_BUF_UI|DNA2_BUF_UE
databuf.dna = connected.occupant.dna.Clone()
if(ishuman(connected.occupant))
var/mob/living/carbon/human/H = connected.occupant
databuf.dna.real_name = H.dna.real_name
databuf.gender = H.gender
databuf.body_descriptors = H.descriptors
databuf.name = "Unique Identifier + Unique Enzymes"
buffers[bufferId] = databuf
if("saveSE")
if(connected.occupant && connected.occupant.dna)
var/datum/dna2/record/databuf=new
databuf.types = DNA2_BUF_SE
databuf.dna = connected.occupant.dna.Clone()
if(ishuman(connected.occupant))
var/mob/living/carbon/human/H = connected.occupant
databuf.dna.real_name = H.dna.real_name
databuf.gender = H.gender
databuf.body_descriptors = H.descriptors
databuf.name = "Structural Enzymes"
// Traitgenes Moved SE and UI saves to storing the entire body record
if("saveDNA")
playsound(src, "keyboard", 40) // into console
var/mob/living/carbon/WC = connected?.occupant?.resolve()
if(WC && WC.dna)
// Traitgenes Properly clone records
var/datum/transhuman/body_record/databuf = new /datum/transhuman/body_record()
databuf.init_from_mob(WC)
databuf.mydna.types = DNA2_BUF_SE // structurals only
if(ishuman(WC))
var/mob/living/carbon/human/H = WC
databuf.mydna.dna.real_name = H.dna.real_name
databuf.mydna.gender = H.gender
databuf.mydna.body_descriptors = H.descriptors
buffers[bufferId] = databuf
return TRUE
if("clear")
buffers[bufferId] = new /datum/dna2/record()
playsound(src, "keyboard", 40)
// Traitgenes Storing the entire body record
var/datum/transhuman/body_record/R = new /datum/transhuman/body_record()
R.mydna = new
R.mydna.dna = new
R.mydna.dna.ResetUI()
R.mydna.dna.ResetSE()
buffers[bufferId] = R
return TRUE
if("changeLabel")
tgui_modal_input(src, "changeBufferLabel", "Please enter the new buffer label:", null, list("id" = bufferId), buffer.name, TGUI_MODAL_INPUT_MAX_LENGTH_NAME)
playsound(src, "keyboard", 40)
tgui_modal_input(src, "changeBufferLabel", "Please enter the new buffer label:", null, list("id" = bufferId), buffer.mydna.name, TGUI_MODAL_INPUT_MAX_LENGTH_NAME)
return TRUE
if("transfer")
if(!connected.occupant || (NOCLONE in connected.occupant.mutations) || !connected.occupant.dna)
return
var/mob/living/carbon/WC = connected?.occupant?.resolve()
if(!WC || (NOCLONE in WC.mutations) || !WC.dna)
return TRUE
irradiating = 2
var/lock_state = connected.locked
connected.locked = 1//lock it
SStgui.update_uis(src) // update all UIs attached to src
sleep(2 SECONDS) // sleep for 2 seconds
irradiating = 0
connected.locked = lock_state
var/datum/dna2/record/buf = buffers[bufferId]
if((buf.types & DNA2_BUF_UI))
if((buf.types & DNA2_BUF_UE))
connected.occupant.real_name = buf.dna.real_name
connected.occupant.name = buf.dna.real_name
if(ishuman(connected.occupant))
var/mob/living/carbon/human/H = connected.occupant
H.gender = buf.gender
H.descriptors = buf.body_descriptors
connected.occupant.UpdateAppearance(buf.dna.UI.Copy())
else if(buf.types & DNA2_BUF_SE)
connected.occupant.dna.SE = buf.dna.SE
connected.occupant.dna.UpdateSE()
if(ishuman(connected.occupant))
var/mob/living/carbon/human/H = connected.occupant
H.gender = buf.gender
H.descriptors = buf.body_descriptors
domutcheck(connected.occupant,connected)
connected.occupant.apply_effect(rand(20,50), IRRADIATE, check_protection = 0)
addtimer(CALLBACK(src, PROC_REF(do_transfer), lock_state, bufferId), 2 SECONDS, TIMER_DELETE_ME)
return TRUE
if("createInjector")
if(!injector_ready)
return
return TRUE
if(text2num(params["block"]) > 0)
var/list/choices = all_dna_blocks((buffer.types & DNA2_BUF_SE) ? buffer.dna.SE : buffer.dna.UI)
var/list/choices = all_dna_blocks(buffer.mydna.dna.SE) // Traitgenes Storing the entire body record, and no more using UIs
tgui_modal_choice(src, "createInjectorBlock", "Please select the block to create an injector from:", null, list("id" = bufferId), null, choices)
else
create_injector(bufferId, TRUE)
return TRUE
// Traitgenes Storing the entire body record
if("loadDisk")
if(isnull(disk) || disk.read_only)
playsound(src, "keyboard", 40)
if(isnull(disk) || !disk.stored)
return
buffers[bufferId] = disk.buf.copy()
// Traitgenes Properly clone records
var/datum/transhuman/body_record/databuf = new /datum/transhuman/body_record()
databuf.init_from_br(disk.stored)
databuf.mydna.types = DNA2_BUF_SE // structurals only
buffers[bufferId] = databuf
if("saveDisk")
if(isnull(disk) || disk.read_only)
return
var/datum/dna2/record/buf = buffers[bufferId]
disk.buf = buf.copy()
disk.name = "data disk - '[buf.dna.real_name]'"
playsound(src, "keyboard", 40)
if(isnull(disk)) // Traitgenes Removed readonly
return TRUE
var/datum/transhuman/body_record/buf = buffers[bufferId]
// Traitgenes Properly clone records
disk.stored = new /datum/transhuman/body_record()
disk.stored.init_from_br(buf)
disk.stored.mydna.types = DNA2_BUF_UI|DNA2_BUF_UE|DNA2_BUF_SE // DNA disks need to maintain their data
disk.name = "Body Design Disk ('[buf.mydna.name]')"
return TRUE
if("sleeveDisk")
playsound(src, "keyboard", 40)
var/datum/transhuman/body_record/buf = buffers[bufferId]
// Send printable record to first sleevepod in area
print_sleeve(usr, buf)
return TRUE
if("wipeDisk")
if(isnull(disk) || disk.read_only)
return
disk.buf = null
playsound(src, "keyboard", 40)
// Traitgenes Storing the entire body record
if(isnull(disk))
return TRUE
disk.stored = null
return TRUE
if("ejectDisk")
playsound(src, 'sound/machines/button.ogg', 30, 1, 0)
if(!disk)
return
return TRUE
disk.forceMove(get_turf(src))
disk = null
return TRUE
/**
* Creates a blank injector with the name of the buffer at the given buffer_id
@@ -739,12 +713,13 @@
addtimer(CALLBACK(src, PROC_REF(injector_cooldown_finish)), 30 SECONDS)
// Create it
var/datum/dna2/record/buf = buffers[buffer_id]
var/datum/transhuman/body_record/buf = buffers[buffer_id] // Traitgenes Use bodyrecords
var/obj/item/dnainjector/I = new()
buf.mydna.types = DNA2_BUF_SE // Traitgenes SE only, use the designer for UI and UEs, super broken in this codebase due to years of no one respecting genetics...
I.forceMove(loc)
I.name += " ([buf.name])"
I.name += " ([buf.mydna.name])"
if(copy_buffer)
I.buf = buf.copy()
I.buf = buf.mydna.copy()
return I
/**
@@ -772,24 +747,164 @@
var/buffer_id = text2num(arguments["id"])
if(buffer_id < 1 || buffer_id > length(buffers))
return
var/datum/dna2/record/buf = buffers[buffer_id]
var/datum/transhuman/body_record/buf = buffers[buffer_id] // Traitgenes Use bodyrecords
var/obj/item/dnainjector/I = create_injector(buffer_id)
setInjectorBlock(I, answer, buf.copy())
setInjectorBlock(I, answer, buf.mydna.copy()) // Traitgenes Use bodyrecords
I.name += " - Block [answer]" // Traitgenes By default show the block of a block injector
if("changeBufferLabel")
var/buffer_id = text2num(arguments["id"])
if(buffer_id < 1 || buffer_id > length(buffers))
return
var/datum/dna2/record/buf = buffers[buffer_id]
buf.name = answer
var/datum/transhuman/body_record/buf = buffers[buffer_id] // Traitgenes Use bodyrecords
buf.mydna.name = answer // Traitgenes Use bodyrecords
buffers[buffer_id] = buf
else
return FALSE
else
return FALSE
/**
* Triggers sleeve growing in a clonepod within the area
*
* Arguments:
* * active_br - Body record to print
*/
/obj/machinery/computer/scan_consolenew/proc/print_sleeve(var/mob/user, var/datum/transhuman/body_record/active_br)
//deleted record
if(!istype(active_br))
to_chat(user, span_danger( "Error: Data corruption."))
return
//Trying to make an fbp
if(active_br.synthetic )
to_chat(user, span_danger( "Error: Cannot grow synthetic."))
return
//No pods
var/obj/machinery/clonepod/transhuman/pod = locate() in get_area(src)
if(!pod)
to_chat(user, span_danger( "Error: No growpods detected."))
return
//Already doing someone.
if(pod.occupant)
to_chat(user, span_danger( "Error: Growpod is currently occupied."))
return
//Not enough materials.
if(pod.get_biomass() < CLONE_BIOMASS)
to_chat(user, span_danger( "Error: Not enough biomass."))
return
//Gross pod (broke mid-cloning or something).
if(pod.mess)
to_chat(user, span_danger( "Error: Growpod malfunction."))
return
//Disabled in config.
if(!CONFIG_GET(flag/revival_cloning))
to_chat(user, span_danger( "Error: Unable to initiate growing cycle."))
return
//Invalid genes!
if(active_br.mydna.name == "Empty" || active_br.mydna.id == null)
to_chat(user, span_danger( "Error: Data corruption."))
return
//Do the cloning!
if(!pod.growclone(active_br))
to_chat(user, span_danger( "Initiating growing cycle... Error: Post-initialisation failed. Growing cycle aborted."))
return
to_chat(user, span_notice( "Initiating growing cycle..."))
/obj/machinery/computer/scan_consolenew/proc/do_irradiate(var/lock_state, var/block)
var/mob/living/carbon/WC = connected?.occupant?.resolve()
irradiating = 0
connected.locked = lock_state
if(!WC)
return
if(prob((80 + (radiation_duration / 2))))
// FIXME: Find out what these corresponded to and change them to the WHATEVERBLOCK they need to be.
//if((selected_se_block != 2 || selected_se_block != 12 || selected_se_block != 8 || selected_se_block || 10) && prob (20))
var/real_SE_block=selected_se_block
block = miniscramble(block, radiation_intensity, radiation_duration)
if(prob(20))
if(selected_se_block > 1 && selected_se_block < DNA_SE_LENGTH/2)
real_SE_block++
else if(selected_se_block > DNA_SE_LENGTH/2 && selected_se_block < DNA_SE_LENGTH)
real_SE_block--
//testing("Irradiated SE block [real_SE_block]:[selected_se_subblock] ([original_block] now [block]) [(real_SE_block!=selected_se_block) ? "(SHIFTED)":""]!")
WC.dna.SetSESubBlock(real_SE_block,selected_se_subblock,block)
WC.apply_effect((radiation_intensity+radiation_duration), IRRADIATE, check_protection = 0)
else
WC.apply_effect(((radiation_intensity*2)+radiation_duration), IRRADIATE, check_protection = 0)
if (prob(80-radiation_duration))
//testing("Random bad mut!")
randmutb(WC)
domutcheck(WC,null,MUTCHK_FORCED)
WC.UpdateAppearance()
// Traitgenes Do gene updates here, and more comprehensively
if(ishuman(WC))
var/mob/living/carbon/human/H = WC
H.sync_dna_traits(FALSE,TRUE)
H.sync_organ_dna()
WC.regenerate_icons()
/obj/machinery/computer/scan_consolenew/proc/do_pulse(var/lock_state)
var/mob/living/carbon/WC = connected?.occupant?.resolve()
irradiating = 0
connected.locked = lock_state
if(!WC)
return
// Traitgenes Make the fullbody irritation more risky
if(prob((radiation_intensity*2) + (radiation_duration*2)))
if(prob(95))
if(prob(75))
randmutb(WC)
else
if(prob(95))
randmutg(WC)
domutcheck(WC,null,MUTCHK_FORCED)
WC.UpdateAppearance()
// Traitgenes Do gene updates here, and more comprehensively
if(ishuman(WC))
var/mob/living/carbon/human/H = WC
H.sync_dna_traits(FALSE,FALSE)
H.sync_organ_dna()
WC.regenerate_icons()
WC.apply_effect(((radiation_intensity*3)+radiation_duration*3), IRRADIATE, check_protection = 0)
/obj/machinery/computer/scan_consolenew/proc/do_transfer(var/lock_state, var/bufferId)
irradiating = 0
connected.locked = lock_state
playsound(src, "keyboard", 40)
var/mob/living/carbon/WC = connected?.occupant?.resolve()
if(!WC)
return TRUE
var/datum/transhuman/body_record/buf = buffers[bufferId] // Traitgenes- Use bodyrecords
if(buf.mydna.types & DNA2_BUF_SE)
// Apply SEs only to the current occupant!
WC.dna.SE = buf.mydna.dna.SE.Copy()
WC.dna.UpdateSE()
domutcheck(WC,connected, MUTCHK_FORCED | MUTCHK_HIDEMSG) // TOO MANY MUTATIONS FOR MESSAGES
WC.UpdateAppearance()
to_chat(WC, span_warning("Your body stings as it wildly changes!"))
// apply genes
if(ishuman(WC))
var/mob/living/carbon/human/H = WC
H.sync_organ_dna()
//Apply genetic modifiers
WC.dna.genetic_modifiers.Cut() // clear em!
for(var/modifier_type in buf.genetic_modifiers)
WC.add_modifier(modifier_type)
WC.apply_effect(rand(20,50), IRRADIATE, check_protection = 0)
#undef DNA_BLOCK_SIZE
#undef PAGE_UI
#undef PAGE_SE
#undef PAGE_BUFFER
#undef PAGE_REJUVENATORS

View File

@@ -1,15 +1,4 @@
/**
* Gene Datum
*
* domutcheck was getting pretty hairy. This is the solution.
*
* All genes are stored in a global variable to cut down on memory
* usage.
*
* @author N3X15 <nexisentertainment@gmail.com>
*/
/datum/dna/gene
/datum/gene // Traitgenes Removed /dna/ from path... WHY WAS THIS A SUBTYPE OF DNA!? It's taking a huge struct and making 50 of them at startup, growing with every new var and list stuffed in /datum/dna - Willbird
// Display name
var/name="BASE GENE"
@@ -23,100 +12,152 @@
// Any of a number of GENE_ flags.
var/flags=0
/**
* Is the gene active in this mob's DNA?
*/
/datum/dna/gene/proc/is_active(var/mob/M)
return (M.active_genes && (type in M.active_genes))
/datum/gene/proc/is_active(var/mob/M) // Traitgenes edit - Removed /dna/ from path
return (M.active_genes && (name in M.active_genes)) // Traitgenes edit - Use name instead, cannot use type with dynamically setup traitgenes. It is always unique due to the block number being appended to it.
// Return 1 if we can activate.
// HANDLE MUTCHK_FORCED HERE!
/datum/dna/gene/proc/can_activate(var/mob/M, var/flags)
/datum/gene/proc/can_activate(var/mob/M, var/mut_flags) // Traitgenes edit - Removed /dna/ from path. mut_flags instead of flags for clarity
return 0
// Called when the gene activates. Do your magic here.
/datum/dna/gene/proc/activate(var/mob/M, var/connected, var/flags)
/datum/gene/proc/activate(var/mob/M, var/connected, var/mut_flags) // Traitgenes edit - Removed /dna/ from path. mut_flags instead of flags for clarity
return
/**
* Called when the gene deactivates. Undo your magic here.
* Only called when the block is deactivated.
*/
/datum/dna/gene/proc/deactivate(var/mob/M, var/connected, var/flags)
/datum/gene/proc/deactivate(var/mob/M, var/connected, var/mut_flags) // Traitgenes edit - Removed /dna/ from path. mut_flags instead of flags for clarity
return
// This section inspired by goone's bioEffects.
/**
* Called in each life() tick.
*/
/datum/dna/gene/proc/OnMobLife(var/mob/M)
return
/**
* Called when the mob dies
*/
/datum/dna/gene/proc/OnMobDeath(var/mob/M)
return
/**
* Called when the mob says shit
*/
/datum/dna/gene/proc/OnSay(var/mob/M, var/message)
return message
/**
* Called after the mob runs update_icons.
*
* @params M The subject.
* @params g Gender (m or f)
* @params fat Fat? (0 or 1)
*/
/datum/dna/gene/proc/OnDrawUnderlays(var/mob/M, var/g, var/fat)
return 0
// Traitgenes edit - Genes are linked to traits now. Because no one bothered to maintain genes, and instead jumped through two different trait systems to avoid them. So here we are. - Willbird
/////////////////////
// BASIC GENES
// TRAIT GENES
//
// These just chuck in a mutation and display a message.
//
// Gene is activated:
// 1. If mutation already exists in mob
// 2. If the probability roll succeeds
// 3. Activation is forced (done in domutcheck)
// Activate traits with a message when enabled
// IMPORTANT - in 99% of situations you should NOT need to edit gene code when adding a new traitgene. Genes only handle the on/off state of traits, traits control the changes and behaviors!
// Just keep pretending genecode doesn't exist and you should be fine. Traitgenes were made with that in mind, and are not intended to be something you need to edit every time you add a traitgene.
// Traitgenes only require that your trait has both an apply() and unapply() if it does anything like adding verbs. Otherwise, you don't even need to add trait exceptions. traitgenes handle it automatically.
// You probably shouldn't mark traits as traitgenes if they are custom species only, species locked, or species banned traits however... - Willbird
/////////////////////
/datum/dna/gene/basic
name="BASIC GENE"
/datum/gene/trait
desc="Gene linked to a trait."
var/datum/trait/linked_trait = null // Internal use, do not assign.
var/list/conflict_traits = list() // Cache known traits that don't work with this one, instead of doing it all at once, or EVERY time we do a mutation check
// Mutation to give
var/mutation=0
/datum/gene/trait/Destroy()
// unlink circular reference
if(linked_trait)
linked_trait.linked_gene = null
linked_trait = null
. = ..()
// Activation probability
var/activation_prob=45
// Use these when displaying info to players
/datum/gene/trait/proc/get_name()
if(linked_trait)
return linked_trait.name
return name
// Possible activation messages
var/list/activation_messages=list()
/datum/gene/trait/proc/get_desc()
if(linked_trait)
return linked_trait.desc
return desc
// Possible deactivation messages
var/list/deactivation_messages=list()
/datum/gene/trait/can_activate(var/mob/M,var/mut_flags)
return TRUE // We don't do probability checks for trait genes, due to the spiderweb of logic they are with conflicting genes. Check has_conflicts() for how conflicting traits are handled.
/datum/dna/gene/basic/can_activate(var/mob/M,var/flags)
if(flags & MUTCHK_FORCED)
return 1
// Probability check
return probinj(activation_prob,(flags&MUTCHK_FORCED))
/**
* Behold the CONFLICT-O-TRON. Checks for trait conflicts the same way code\modules\client\preference_setup\vore\07_traits.dm does,
* and then caches the results. Cause traits can't change conflicts mid-round. Unless that changes someday, god help us all if so.
* You do not need to add any unique exceptions here, check trait code instead if you want to handle conflicts and forced exceptions.
* They already handle it. This just uses the same system. - Willbird
*
* quick_scan = FALSE, will do a deep conflict check. Checking every trait in the list, and not just failing out at the first one that conflicts.
*
* Recommended way to write has_conflict calls:
* ```
* has_conflict(traits_to_check = list(trait_path))
* ```
*/
/datum/gene/trait/proc/has_conflict(var/list/traits_to_check, var/quick_scan = TRUE)
var/has_conflict = FALSE
var/path = linked_trait.type
for(var/P in traits_to_check)
// don't get triggered by self
if(P == path)
continue
/datum/dna/gene/basic/activate(var/mob/M)
M.mutations.Add(mutation)
if(activation_messages.len)
var/msg = pick(activation_messages)
to_chat(M, span_notice("[msg]"))
// check if cached first...
if(!isnull(conflict_traits[P]))
if(quick_scan && conflict_traits[P])
return TRUE
continue
/datum/dna/gene/basic/deactivate(var/mob/M)
M.mutations.Remove(mutation)
if(deactivation_messages.len)
var/msg = pick(deactivation_messages)
to_chat(M, span_warning("[msg]"))
// check trait if not. CONFLICT-O-TRON ENGAGE
conflict_traits[P] = FALSE
var/datum/trait/instance_test = all_traits[P]
if(path in instance_test.excludes)
conflict_traits[P] = TRUE
has_conflict = TRUE
// depending on scan mode we want to scan all, or only the first failure
if(quick_scan)
return TRUE
continue
for(var/V in linked_trait.var_changes)
if(V == "flags")
continue
if(V in instance_test.var_changes)
conflict_traits[P] = TRUE
has_conflict = TRUE
// depending on scan mode we want to scan all, or only the first failure
if(quick_scan)
return TRUE
continue
for(var/V in linked_trait.var_changes_pref)
if(V in instance_test.var_changes_pref)
conflict_traits[P] = TRUE
has_conflict = TRUE
// depending on scan mode we want to scan all, or only the first failure
if(quick_scan)
return TRUE
continue
return has_conflict
/datum/gene/trait/activate(var/mob/M, var/connected, var/mut_flags)
if(linked_trait && ishuman(M))
var/mob/living/carbon/human/H = M
if(H.species) // Lets avoid runtime assertions
// Add trait
if(linked_trait.type in H.species.traits)
return
linked_trait.apply( H.species, H, H.species.traits[linked_trait.type])
H.species.traits.Add(linked_trait.type)
if(!(linked_trait.type in H.dna.species_traits)) // Set species traits too
H.dna.species_traits.Add(linked_trait.type)
// message player with change
if(!(mut_flags & MUTCHK_HIDEMSG))
linked_trait.send_message( H, TRUE)
/datum/gene/trait/deactivate(var/mob/M, var/connected, var/mut_flags)
if(linked_trait && ishuman(M))
var/mob/living/carbon/human/H = M
if(H.species) // Lets avoid runtime assertions
// Remove trait
if(!(linked_trait.type in H.species.traits))
return
linked_trait.unapply( H.species, H, H.species.traits[linked_trait.type])
linked_trait.remove(H.species) // Does nothing, but may as well call it because it exists and has a place now
H.species.traits.Remove(linked_trait.type)
if(linked_trait.type in H.dna.species_traits) // Clear species traits too
H.dna.species_traits.Remove(linked_trait.type)
// message player with change
if(!(mut_flags & MUTCHK_HIDEMSG))
linked_trait.send_message( H, FALSE)