mirror of
https://github.com/CHOMPStation2/CHOMPStation2.git
synced 2025-12-10 02:09:41 +00:00
[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:
committed by
GitHub
parent
d81e145924
commit
7bfffc808d
@@ -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.
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user