[alias]"
+ temp_html += "
[mut_desc]
"
+ if(active && !storage_slot)
+ if(HM?.can_chromosome && (HM in viable_occupant.dna.mutations))
+ var/i = viable_occupant.dna.mutations.Find(HM)
+ var/chromosome_name = "
----"
+ if(HM.chromosome_name)
+ chromosome_name = HM.chromosome_name
+ temp_html += "
Chromosome status: [chromosome_name]
"
+ temp_html += "
Sequence:
"
+ if(!scrambled)
+ for(var/block in 1 to A.blocks)
+ var/whole_sequence = get_valid_gene_string(mutation)
+ var/sequence = copytext(whole_sequence, 1+(block-1)*(DNA_SEQUENCE_LENGTH*2),(DNA_SEQUENCE_LENGTH*2*block+1))
+ temp_html += "
"
+ for(var/i in 1 to DNA_SEQUENCE_LENGTH)
+ var/num = 1+(i-1)*2
+ var/genenum = num+(DNA_SEQUENCE_LENGTH*2*(block-1))
+ temp_html += " | "
+ temp_html += "
"
+ for(var/i in 1 to DNA_SEQUENCE_LENGTH)
+ temp_html += "| | "
+ temp_html += "
"
+ for(var/i in 1 to DNA_SEQUENCE_LENGTH)
+ var/num = i*2
+ var/genenum = num+(DNA_SEQUENCE_LENGTH*2*(block-1))
+ temp_html += " | "
+ temp_html += "
"
+ temp_html += "
"
+ else
+ temp_html = "
Sequence unreadable due to unpredictable mutation.
"
+ if((active || storage_slot) && (injectorready < world.time) && !scrambled)
+ temp_html += "
Print Activator"
+ temp_html += "
Print Mutator"
+ else
+ temp_html += "
Print Activator"
+ temp_html += "
Print Mutator"
+ temp_html += "
"
+ if(storage_slot)
+ temp_html += "
Delete"
+ if((LAZYLEN(stored_mutations) < max_storage) && diskette && !diskette.read_only)
+ temp_html += "
Export"
+ else
+ temp_html += "
Export"
+ temp_html += "
Back"
+ else if(active && !scrambled)
+ temp_html += "
Store"
+ if(extra || scrambled)
+ temp_html += "
Nullify"
+ else
+ temp_html += "
Nullify"
+ temp_html += "
"
+ return temp_html
/obj/machinery/computer/scan_consolenew/Topic(href, href_list)
if(..())
@@ -340,13 +547,16 @@
radstrength = WRAP(num, 1, RADIATION_STRENGTH_MAX+1)
if("screen")
current_screen = href_list["text"]
- if("rejuv")
- if(viable_occupant && viable_occupant.reagents)
- var/potassiodide_amount = viable_occupant.reagents.get_reagent_amount("potass_iodide")
- var/can_add = max(min(REJUVENATORS_MAX - potassiodide_amount, REJUVENATORS_INJECT), 0)
- viable_occupant.reagents.add_reagent("potass_iodide", can_add)
+ if("scramble")
+ if(viable_occupant && (scrambleready < world.time))
+ viable_occupant.dna.remove_all_mutations(list(MUT_NORMAL, MUT_EXTRA))
+ viable_occupant.dna.generate_dna_blocks()
+ scrambleready = world.time + SCRAMBLE_TIMEOUT
+ to_chat(usr,"
DNA scrambled.")
+ viable_occupant.radiation += RADIATION_STRENGTH_MULTIPLIER*50/(connected.damage_coeff ** 2)
+
if("setbufferlabel")
- var/text = sanitize(input(usr, "Input a new label:", "Input an Text", null) as text|null)
+ var/text = sanitize(input(usr, "Input a new label:", "Input a Text", null) as text|null)
if(num && text)
num = CLAMP(num, 1, NUMBER_OF_BUFFERS)
var/list/buffer_slot = buffer[num]
@@ -358,7 +568,6 @@
buffer[num] = list(
"label"="Buffer[num]:[viable_occupant.real_name]",
"UI"=viable_occupant.dna.uni_identity,
- "SE"=viable_occupant.dna.struc_enzymes,
"UE"=viable_occupant.dna.unique_enzymes,
"name"=viable_occupant.real_name,
"blood_type"=viable_occupant.dna.blood_type
@@ -372,8 +581,6 @@
if("transferbuffer")
if(num && viable_occupant)
switch(href_list["text"]) //Numbers are this high because other way upgrading laser is just not worth the hassle, and i cant think of anything better to inmrove
- if("se")
- apply_buffer(SCANNER_ACTION_SE,num)
if("ui")
apply_buffer(SCANNER_ACTION_UI,num)
if("ue")
@@ -387,28 +594,6 @@
if(istype(buffer_slot))
var/obj/item/dnainjector/timed/I
switch(href_list["text"])
- if("se")
- if(buffer_slot["SE"])
- I = new /obj/item/dnainjector/timed(loc)
- var/powers = 0
- for(var/datum/mutation/human/HM in GLOB.good_mutations + GLOB.bad_mutations + GLOB.not_good_mutations)
- if(HM.check_block_string(buffer_slot["SE"]))
- I.add_mutations.Add(HM)
- if(HM in GLOB.good_mutations)
- powers += 1
- if(HM in GLOB.bad_mutations + GLOB.not_good_mutations)
- powers -= 1 //To prevent just unlocking everything to get all powers to a syringe for max tech
- else
- I.remove_mutations.Add(HM)
- var/time_coeff
- for(var/datum/mutation/human/HM in I.add_mutations)
- if(!time_coeff)
- time_coeff = HM.time_coeff
- continue
- time_coeff = min(time_coeff,HM.time_coeff)
- if(connected)
- I.duration = I.duration * time_coeff * connected.damage_coeff
- I.damage_coeff = connected.damage_coeff
if("ui")
if(buffer_slot["UI"])
I = new /obj/item/dnainjector/timed(loc)
@@ -447,7 +632,7 @@
if("setdelayed")
if(num)
delayed_action = list("action"=text2num(href_list["delayaction"]),"buffer"=num)
- if("pulseui","pulsese")
+ if("pulseui")
if(num && viable_occupant && connected)
radduration = WRAP(radduration, 1, RADIATION_DURATION_MAX+1)
radstrength = WRAP(radstrength, 1, RADIATION_STRENGTH_MAX+1)
@@ -459,7 +644,7 @@
ui_interact(usr)
sleep(radduration*10)
- current_screen = "mainmenu"
+ current_screen = "ui"
if(viable_occupant && connected && connected.occupant==viable_occupant)
viable_occupant.radiation += (RADIATION_IRRADIATION_MULTIPLIER*radduration*radstrength)/(connected.damage_coeff ** 2) //Read comment in "transferbuffer" section above for explanation
@@ -468,7 +653,7 @@
var/len = length(viable_occupant.dna.uni_identity)
num = WRAP(num, 1, len+1)
num = randomize_radiation_accuracy(num, radduration + (connected.precision_coeff ** 2), len) //Each manipulator level above 1 makes randomization as accurate as selected time + manipulator lvl^2
- //Value is this high for the same reason as with laser - not worth the hassle of upgrading if the bonus is low
+ //Value is this high for the same reason as with laser - not worth the hassle of upgrading if the bonus is low
var/block = round((num-1)/DNA_BLOCK_SIZE)+1
var/subblock = num - block*DNA_BLOCK_SIZE
last_change = "UI #[block]-[subblock]; "
@@ -480,35 +665,173 @@
viable_occupant.dna.uni_identity = copytext(viable_occupant.dna.uni_identity, 1, num) + hex + copytext(viable_occupant.dna.uni_identity, num+1, 0)
viable_occupant.updateappearance(mutations_overlay_update=1)
- if("pulsese")
- var/len = length(viable_occupant.dna.struc_enzymes)
- num = WRAP(num, 1, len+1)
- num = randomize_radiation_accuracy(num, radduration + (connected.precision_coeff ** 2), len)
-
- var/block = round((num-1)/DNA_BLOCK_SIZE)+1
- var/subblock = num - block*DNA_BLOCK_SIZE
- last_change = "SE #[block]-[subblock]; "
-
- var/hex = copytext(viable_occupant.dna.struc_enzymes, num, num+1)
- last_change += "[hex]"
- hex = scramble(hex, radstrength, radduration)
- last_change += "->[hex]"
-
- viable_occupant.dna.struc_enzymes = copytext(viable_occupant.dna.struc_enzymes, 1, num) + hex + copytext(viable_occupant.dna.struc_enzymes, num+1, 0)
- viable_occupant.domutcheck()
else
current_screen = "mainmenu"
if(connected)
connected.locked = locked_state
+ if("inspect")
+ if(viable_occupant)
+ var/list/mutations = get_mutation_list(TRUE)
+ if(current_mutation == mutations[num])
+ current_mutation = null
+ else
+ current_mutation = mutations[num]
+
+ if("inspectstorage")
+ current_storage = num
+ current_screen = "info"
+ if("savemut")
+ if(viable_occupant)
+ var/succes
+ if(LAZYLEN(stored_mutations) < max_storage)
+ var/mutation = text2path(href_list["path"])
+ if(ispath(mutation, /datum/mutation/human)) //sanity checks
+ var/datum/mutation/human/HM = viable_occupant.dna.get_mutation(mutation)
+ if(HM)
+ var/datum/mutation/human/A = new HM.type()
+ A.copy_mutation(HM)
+ succes = TRUE
+ stored_mutations += A
+ to_chat(usr,"
Mutation succesfully stored.")
+ if(!succes) //we can exactly return here
+ to_chat(usr,"
Mutation storage is full.")
+ if("deletemut")
+ var/datum/mutation/human/HM = stored_mutations[num]
+ if(HM)
+ stored_mutations.Remove(HM)
+ qdel(HM)
+ current_screen = "mutations"
+ if("activator")
+ if(injectorready < world.time)
+ var/mutation = text2path(href_list["path"])
+ if(ispath(mutation, /datum/mutation/human))
+ var/datum/mutation/human/HM = get_valid_mutation(mutation)
+ if(HM)
+ var/obj/item/dnainjector/activator/I = new /obj/item/dnainjector/activator(loc)
+ I.add_mutations += new HM.type (copymut = HM)
+ I.name = "[HM.name] activator"
+ I.research = TRUE
+ if(connected)
+ I.damage_coeff = connected.damage_coeff*4
+ injectorready = world.time + INJECTOR_TIMEOUT * (1 - 0.1 * connected.precision_coeff) //precision_coeff being the matter bin rating
+ else
+ injectorready = world.time + INJECTOR_TIMEOUT
+ if("mutator")
+ if(injectorready < world.time)
+ var/mutation = text2path(href_list["path"])
+ if(ispath(mutation, /datum/mutation/human))
+ var/datum/mutation/human/HM = get_valid_mutation(mutation)
+ if(HM)
+ var/obj/item/dnainjector/activator/I = new /obj/item/dnainjector/activator(loc)
+ I.add_mutations += new HM.type (copymut = HM)
+ I.doitanyway = TRUE
+ I.name = "[HM.name] injector"
+ if(connected)
+ I.damage_coeff = connected.damage_coeff
+ injectorready = world.time + INJECTOR_TIMEOUT * 5 * (1 - 0.1 * connected.precision_coeff)
+ else
+ injectorready = world.time + INJECTOR_TIMEOUT * 5
+ if("nullify")
+ if(viable_occupant)
+ var/datum/mutation/human/A = viable_occupant.dna.get_mutation(current_mutation)
+ if(A && (!viable_occupant.dna.mutation_in_sequence(current_mutation) || A.scrambled))
+ viable_occupant.dna.remove_mutation(current_mutation)
+ current_screen = "mainmenu"
+ current_mutation = null
+ if("pulsegene")
+ if(current_screen != "info")
+ var/path = text2path(href_list["path"])
+ if(viable_occupant && num && (path in viable_occupant.dna.mutation_index))
+ var/list/genes = list("A","T","G","C","X")
+ if(jokerready < world.time)
+ genes += "JOKER"
+ var/sequence = GET_GENE_STRING(path, viable_occupant.dna)
+ var/original = sequence[num]
+ var/new_gene = input("From [original] to-", "New block", original) as null|anything in genes
+ if(!new_gene)
+ new_gene = original
+ if(viable_occupant == get_viable_occupant()) //No cheesing
+ if((new_gene == "JOKER") && (jokerready < world.time))
+ var/true_genes = GET_SEQUENCE(current_mutation)
+ new_gene = true_genes[num]
+ jokerready = world.time + JOKER_TIMEOUT - (JOKER_UPGRADE * (connected.precision_coeff-1))
+ sequence = copytext(sequence, 1, num) + new_gene + copytext(sequence, num+1, length(sequence)+1)
+ viable_occupant.dna.mutation_index[path] = sequence
+ viable_occupant.radiation += RADIATION_STRENGTH_MULTIPLIER/connected.damage_coeff
+ viable_occupant.domutcheck()
+ if("exportdiskmut")
+ if(diskette && !diskette.read_only)
+ var/path = text2path(href_list["path"])
+ if(ispath(path, /datum/mutation/human))
+ var/datum/mutation/human/A = get_valid_mutation(path)
+ if(A && diskette && (LAZYLEN(diskette.mutations) < diskette.max_mutations))
+ var/datum/mutation/human/HM = new A.type()
+ diskette.mutations += HM
+ HM.copy_mutation(A)
+ to_chat(usr, "
Succesfully written [A.name] to [diskette.name].")
+ if("deletediskmut")
+ if(diskette && !diskette.read_only)
+ if(num && (LAZYLEN(diskette.mutations) >= num))
+ var/datum/mutation/human/A = diskette.mutations[num]
+ diskette.mutations.Remove(A)
+ qdel(A)
+ if("importdiskmut")
+ if(diskette && (LAZYLEN(diskette.mutations) >= num))
+ if(LAZYLEN(stored_mutations) < max_storage)
+ var/datum/mutation/human/A = diskette.mutations[num]
+ var/datum/mutation/human/HM = new A.type()
+ HM.copy_mutation(A)
+ stored_mutations += HM
+ to_chat(usr,"
Succesfully written [A.name] to storage.")
+ if("combine")
+ if(num && (LAZYLEN(stored_mutations) >= num))
+ if(LAZYLEN(stored_mutations) < max_storage)
+ var/datum/mutation/human/A = stored_mutations[num]
+ var/path = A.type
+ if(combine)
+ var/result_path = get_mixed_mutation(combine, path)
+ if(result_path)
+ stored_mutations += new result_path()
+ to_chat(usr, "Succes! New mutation has been added to storage")
+ discover(result_path)
+ combine = null
+ else
+ to_chat(usr, "Failed. No mutation could be created.")
+ combine = null
+ else
+ combine = path
+ to_chat(usr,"Selected [A.name] for combining")
+ else
+ to_chat(usr, "Not enough space to store potential mutation.")
+ if("ejectchromosome")
+ if(LAZYLEN(stored_chromosomes) <= num)
+ var/obj/item/chromosome/CM = stored_chromosomes[num]
+ CM.forceMove(drop_location())
+ adjust_item_drop_location(CM)
+ stored_chromosomes -= CM
+ if("applychromosome")
+ if(viable_occupant && (LAZYLEN(viable_occupant.dna.mutations) <= num))
+ var/datum/mutation/human/HM = viable_occupant.dna.mutations[num]
+ var/list/chromosomes = list()
+ for(var/obj/item/chromosome/CM in stored_chromosomes)
+ if(CM.can_apply(HM))
+ chromosomes += CM
+ if(chromosomes.len)
+ var/obj/item/chromosome/CM = input("Select a chromosome to apply", "Apply Chromosome") as null|anything in sortNames(chromosomes)
+ if(CM)
+ to_chat(usr, "You apply [CM] to [HM.name].")
+ stored_chromosomes -= CM
+ CM.apply(HM)
+
ui_interact(usr,last_change)
-/obj/machinery/computer/scan_consolenew/proc/scramble(input,rs,rd)
+/obj/machinery/computer/scan_consolenew/proc/scramble(input,rs,rd) //hexadecimal genetics. dont confuse with scramble button
var/length = length(input)
var/ran = gaussian(0, rs*RADIATION_STRENGTH_MULTIPLIER)
if(ran == 0)
- ran = pick(-1,1) //hacky, statistically should almost never happen. 0-change makes people mad though
+ ran = pick(-1,1) //hacky, statistically should almost never happen. 0-chance makes people mad though
else if(ran < 0)
ran = round(ran) //negative, so floor it
else
@@ -537,10 +860,6 @@
//Each laser level reduces damage by lvl^2, so no effect on 1 lvl, 4 times less damage on 2 and 9 times less damage on 3
//Numbers are this high because other way upgrading laser is just not worth the hassle, and i cant think of anything better to inmrove
switch(action)
- if(SCANNER_ACTION_SE)
- if(buffer_slot["SE"])
- viable_occupant.dna.struc_enzymes = buffer_slot["SE"]
- viable_occupant.domutcheck()
if(SCANNER_ACTION_UI)
if(buffer_slot["UI"])
viable_occupant.dna.uni_identity = buffer_slot["UI"]
@@ -562,15 +881,54 @@
viable_occupant.dna.blood_type = buffer_slot["blood_type"]
/obj/machinery/computer/scan_consolenew/proc/on_scanner_close()
- if(delayed_action && connected)
+ if(delayed_action && get_viable_occupant())
to_chat(connected.occupant, "[src] activates!")
apply_buffer(delayed_action["action"],delayed_action["buffer"])
delayed_action = null //or make it stick + reset button ?
+/obj/machinery/computer/scan_consolenew/proc/get_valid_mutation(mutation)
+ var/mob/living/carbon/C = get_viable_occupant()
+ if(C)
+ var/datum/mutation/human/HM = C.dna.get_mutation(mutation)
+ if(HM)
+ return HM
+ for(var/datum/mutation/human/A in stored_mutations)
+ if(A.type == mutation)
+ return A
+
+
+/obj/machinery/computer/scan_consolenew/proc/get_mutation_list(include_storage) //Returns a list of the mutation index types and any extra mutations
+ var/mob/living/carbon/viable_occupant = get_viable_occupant()
+ var/list/paths = list()
+ if(viable_occupant)
+ for(var/A in viable_occupant.dna.mutation_index)
+ paths += A
+ for(var/datum/mutation/human/A in viable_occupant.dna.mutations)
+ if(A.class == MUT_EXTRA)
+ paths += A.type
+ if(include_storage)
+ for(var/datum/mutation/human/A in stored_mutations)
+ paths += A.type
+ return paths
+
+/obj/machinery/computer/scan_consolenew/proc/get_valid_gene_string(mutation)
+ var/mob/living/carbon/C = get_viable_occupant()
+ if(C && (mutation in C.dna.mutation_index))
+ return GET_GENE_STRING(mutation, C.dna)
+ else if(C && (LAZYLEN(C.dna.mutations)))
+ for(var/datum/mutation/human/A in C.dna.mutations)
+ if(A.type == mutation)
+ return GET_SEQUENCE(mutation)
+ for(var/datum/mutation/human/A in stored_mutations)
+ if(A.type == mutation)
+ return GET_SEQUENCE(mutation)
+
+/obj/machinery/computer/scan_consolenew/proc/discover(mutation)
+ if(stored_research && !(mutation in stored_research.discovered_mutations))
+ stored_research.discovered_mutations += mutation
+ return TRUE
/////////////////////////// DNA MACHINES
#undef INJECTOR_TIMEOUT
-#undef REJUVENATORS_INJECT
-#undef REJUVENATORS_MAX
#undef NUMBER_OF_BUFFERS
#undef RADIATION_STRENGTH_MAX
diff --git a/code/game/machinery/exp_cloner.dm b/code/game/machinery/exp_cloner.dm
index 25e5948b51..d5388035bb 100644
--- a/code/game/machinery/exp_cloner.dm
+++ b/code/game/machinery/exp_cloner.dm
@@ -9,7 +9,7 @@
internal_radio = FALSE
//Start growing a human clone in the pod!
-/obj/machinery/clonepod/experimental/growclone(ckey, clonename, ui, se, datum/species/mrace, list/features, factions)
+/obj/machinery/clonepod/experimental/growclone(clonename, ui, mutation_index, mindref, last_death, blood_type, datum/species/mrace, list/features, factions, list/quirks, datum/bank_account/insurance)
if(panel_open)
return FALSE
if(mess || attempting)
@@ -20,15 +20,15 @@
var/mob/living/carbon/human/H = new /mob/living/carbon/human(src)
- H.hardset_dna(ui, se, H.real_name, null, mrace, features)
+ H.hardset_dna(ui, mutation_index, H.real_name, blood_type, mrace, features)
if(efficiency > 2)
var/list/unclean_mutations = (GLOB.not_good_mutations|GLOB.bad_mutations)
H.dna.remove_mutation_group(unclean_mutations)
if(efficiency > 5 && prob(20))
- H.randmutvg()
+ H.easy_randmut(POSITIVE)
if(efficiency < 3 && prob(50))
- var/mob/M = H.randmutb()
+ var/mob/M = H.easy_randmut(NEGATIVE+MINOR_NEGATIVE)
if(ismob(M))
H = M
@@ -42,18 +42,17 @@
icon_state = "pod_1"
//Get the clone body ready
maim_clone(H)
- ADD_TRAIT(H, TRAIT_STABLEHEART, CLONING_POD_TRAIT)
- ADD_TRAIT(H, TRAIT_STABLELIVER, CLONING_POD_TRAIT)
- ADD_TRAIT(H, TRAIT_EMOTEMUTE, CLONING_POD_TRAIT)
- ADD_TRAIT(H, TRAIT_MUTE, CLONING_POD_TRAIT)
- ADD_TRAIT(H, TRAIT_NOBREATH, CLONING_POD_TRAIT)
- ADD_TRAIT(H, TRAIT_NOCRITDAMAGE, CLONING_POD_TRAIT)
+ ADD_TRAIT(H, TRAIT_STABLEHEART, "cloning")
+ ADD_TRAIT(H, TRAIT_EMOTEMUTE, "cloning")
+ ADD_TRAIT(H, TRAIT_MUTE, "cloning")
+ ADD_TRAIT(H, TRAIT_NOBREATH, "cloning")
+ ADD_TRAIT(H, TRAIT_NOCRITDAMAGE, "cloning")
H.Unconscious(80)
- var/list/candidates = pollCandidatesForMob("Do you want and agree to play as a [clonename]'s defective clone, respect their character and not engage in ERP without permission from the original?", null, null, null, 100, H, POLL_IGNORE_CLONE)
+ var/list/candidates = pollCandidatesForMob("Do you want to play as [clonename]'s defective clone?", null, null, null, 100, H)
if(LAZYLEN(candidates))
var/mob/dead/observer/C = pick(candidates)
- C.transfer_ckey(H)
+ H.key = C.key
if(grab_ghost_when == CLONER_FRESH_CLONE)
H.grab_ghost()
@@ -293,6 +292,7 @@
temp = "Cloning cycle already in progress."
playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0)
else
- pod.growclone(null, mob_occupant.real_name, dna.uni_identity, dna.struc_enzymes, clone_species, dna.features, mob_occupant.faction)
+ pod.growclone(mob_occupant.real_name, dna.uni_identity, dna.mutation_index, null, null, dna.blood_type, clone_species, dna.features, mob_occupant.faction)
temp = "[mob_occupant.real_name] => Cloning data sent to pod."
playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, 0)
+
diff --git a/code/game/mecha/equipment/tools/other_tools.dm b/code/game/mecha/equipment/tools/other_tools.dm
index a8ba9850ff..9632943c19 100644
--- a/code/game/mecha/equipment/tools/other_tools.dm
+++ b/code/game/mecha/equipment/tools/other_tools.dm
@@ -284,12 +284,12 @@
if(equip_ready) //disabled
return
var/area/A = get_area(chassis)
- var/pow_chan = get_power_channel(A)
+ var/pow_chan = get_MUTATION_POWER_channel(A)
if(pow_chan)
return 1000 //making magic
-/obj/item/mecha_parts/mecha_equipment/tesla_energy_relay/proc/get_power_channel(var/area/A)
+/obj/item/mecha_parts/mecha_equipment/tesla_energy_relay/proc/get_MUTATION_POWER_channel(var/area/A)
var/pow_chan
if(A)
for(var/c in use_channels)
diff --git a/code/game/objects/items/chromosome.dm b/code/game/objects/items/chromosome.dm
new file mode 100644
index 0000000000..1340e7f35b
--- /dev/null
+++ b/code/game/objects/items/chromosome.dm
@@ -0,0 +1,92 @@
+/obj/item/chromosome
+ name = "blank chromosome"
+ icon = 'icons/obj/chromosomes.dmi'
+ icon_state = ""
+ desc = "A tube holding chromosomic data."
+ force = 0
+ w_class = WEIGHT_CLASS_SMALL
+
+ var/stabilizer_coeff = 1 //lower is better, affects genetic stability
+ var/synchronizer_coeff = 1 //lower is better, affects chance to backfire
+ var/power_coeff = 1 //higher is better, affects "strength"
+ var/energy_coeff = 1 //lower is better. affects recharge time
+
+ var/weight = 5
+
+/obj/item/chromosome/proc/can_apply(datum/mutation/human/HM)
+ if(!HM || !(HM.can_chromosome == CHROMOSOME_NONE))
+ return FALSE
+ if((stabilizer_coeff != 1) && (HM.stabilizer_coeff != -1)) //if the chromosome is 1, we dont change anything. If the mutation is -1, we cant change it. sorry
+ return TRUE
+ if((synchronizer_coeff != 1) && (HM.synchronizer_coeff != -1))
+ return TRUE
+ if((power_coeff != 1) && (HM.power_coeff != -1))
+ return TRUE
+ if((energy_coeff != 1) && (HM.energy_coeff != -1))
+ return TRUE
+
+/obj/item/chromosome/proc/apply(datum/mutation/human/HM)
+ if(HM.stabilizer_coeff != -1)
+ HM.stabilizer_coeff = stabilizer_coeff
+ if(HM.synchronizer_coeff != -1)
+ HM.synchronizer_coeff = synchronizer_coeff
+ if(HM.power_coeff != -1)
+ HM.power_coeff = power_coeff
+ if(HM.energy_coeff != -1)
+ HM.energy_coeff = energy_coeff
+ HM.can_chromosome = 2
+ HM.chromosome_name = name
+ HM.modify()
+ qdel(src)
+
+/proc/generate_chromosome()
+ var/static/list/chromosomes
+ if(!chromosomes)
+ chromosomes = list()
+ for(var/A in subtypesof(/obj/item/chromosome))
+ var/obj/item/chromosome/CM = A
+ if(!initial(CM.weight))
+ break
+ chromosomes[A] = initial(CM.weight)
+ return pickweight(chromosomes)
+
+
+/obj/item/chromosome/stabilizer
+ name = "stabilizer chromosome"
+ desc = "A chromosome that adjusts to the body to reduce genetic damage by 20%."
+ icon_state = "stabilizer"
+ stabilizer_coeff = 0.8
+ weight = 1
+
+/obj/item/chromosome/synchronizer
+ name = "synchronizer chromosome"
+ desc = "A chromosome that gives the mind more controle over the mutation, reducing knockback and downsides by 50%."
+ icon_state = "synchronizer"
+ synchronizer_coeff = 0.5
+
+/obj/item/chromosome/power
+ name = "power chromosome"
+ desc = "A power chromosome for boosting certain mutation's power by 50%."
+ icon_state = "power"
+ power_coeff = 1.5
+
+/obj/item/chromosome/energy
+ name = "energetic chromosome"
+ desc = "A chromosome that reduces cooldown on action based mutations by 50%."
+ icon_state = "energy"
+ energy_coeff = 0.5
+
+/obj/item/chromosome/reinforcer
+ name = "reinforcement chromosome"
+ desc = "Renders the mutation immune to mutadone."
+ icon_state = "reinforcer"
+ weight = 3
+
+/obj/item/chromosome/reinforcer/can_apply(datum/mutation/human/HM)
+ if(!HM || !(HM.can_chromosome == CHROMOSOME_NONE))
+ return FALSE
+ return !HM.mutadone_proof
+
+/obj/item/chromosome/reinforcer/apply(datum/mutation/human/HM)
+ HM.mutadone_proof = TRUE
+ ..()
\ No newline at end of file
diff --git a/code/game/objects/items/circuitboards/machine_circuitboards.dm b/code/game/objects/items/circuitboards/machine_circuitboards.dm
index 10a2b2c807..2b942b36ec 100644
--- a/code/game/objects/items/circuitboards/machine_circuitboards.dm
+++ b/code/game/objects/items/circuitboards/machine_circuitboards.dm
@@ -66,7 +66,7 @@
build_path = /obj/machinery/dna_scannernew
req_components = list(
/obj/item/stock_parts/scanning_module = 1,
- /obj/item/stock_parts/manipulator = 1,
+ /obj/item/stock_parts/matter_bin = 1,
/obj/item/stock_parts/micro_laser = 1,
/obj/item/stack/sheet/glass = 1,
/obj/item/stack/cable_coil = 2)
diff --git a/code/game/objects/items/devices/scanners.dm b/code/game/objects/items/devices/scanners.dm
index 00381b9838..29009a88f7 100644
--- a/code/game/objects/items/devices/scanners.dm
+++ b/code/game/objects/items/devices/scanners.dm
@@ -150,7 +150,10 @@ SLIME SCANNER
msg += "\n\tSubject appears to have [M.getCloneLoss() > 30 ? "Severe" : "Minor"] cellular damage."
if(advanced)
msg += "\n\tCellular Damage Level: [M.getCloneLoss()]."
-
+ if(ishuman(M))
+ var/mob/living/carbon/human/H = M
+ if(advanced && H.has_dna())
+ to_chat(user, "\tGenetic Stability: [H.dna.stability]%.")
to_chat(user, msg)
msg = ""
@@ -777,3 +780,62 @@ SLIME SCANNER
var/response = SEND_SIGNAL(M, COMSIG_NANITE_SCAN, user, TRUE)
if(!response)
to_chat(user, "No nanites detected in the subject.")
+
+/obj/item/sequence_scanner
+ name = "genetic sequence scanner"
+ icon = 'icons/obj/device.dmi'
+ icon_state = "gene"
+ item_state = "healthanalyzer"
+ lefthand_file = 'icons/mob/inhands/equipment/medical_lefthand.dmi'
+ righthand_file = 'icons/mob/inhands/equipment/medical_righthand.dmi'
+ desc = "A hand-held scanner able to swiftly scan someone for potential mutations. Hold near a DNA console to update from their database."
+ flags_1 = CONDUCT_1
+ item_flags = NOBLUDGEON
+ slot_flags = ITEM_SLOT_BELT
+ throwforce = 3
+ w_class = WEIGHT_CLASS_TINY
+ throw_speed = 3
+ throw_range = 7
+ materials = list(MAT_METAL=200)
+ var/list/discovered = list() //hit a dna console to update the scanners database
+
+/obj/item/sequence_scanner/attack(mob/living/M, mob/living/carbon/human/user)
+ user.visible_message("[user] has analyzed [M]'s genetic sequence.")
+
+ add_fingerprint(user)
+
+ gene_scan(M, user, src)
+
+/obj/item/sequence_scanner/afterattack(obj/O, mob/user, proximity)
+ . = ..()
+ if(!istype(O) || !proximity)
+ return
+
+ if(istype(O, /obj/machinery/computer/scan_consolenew))
+ var/obj/machinery/computer/scan_consolenew/C = O
+ if(C.stored_research)
+ to_chat(user, "[name] database updated.")
+ discovered = C.stored_research.discovered_mutations
+ else
+ to_chat(user,"No database to update from.")
+
+/proc/gene_scan(mob/living/carbon/C, mob/living/user, obj/item/sequence_scanner/G)
+ if(!iscarbon(C) || !C.has_dna())
+ return
+ to_chat(user, "[C.name]'s potential mutations.")
+ for(var/A in C.dna.mutation_index)
+ var/datum/mutation/human/HM = GET_INITIALIZED_MUTATION(A)
+ var/mut_name
+ if(G && (A in G.discovered))
+ mut_name = "[HM.name] ([HM.alias])"
+ else
+ mut_name = HM.alias
+ var/temp = GET_GENE_STRING(HM.type, C.dna)
+ var/display
+ for(var/i in 0 to length(temp) / DNA_MUTATION_BLOCKS-1)
+ if(i)
+ display += "-"
+ display += copytext(temp, 1 + i*DNA_MUTATION_BLOCKS, DNA_MUTATION_BLOCKS*(1+i) + 1)
+
+
+ to_chat(user, "- [mut_name] > [display]")
\ No newline at end of file
diff --git a/code/game/objects/items/dna_injector.dm b/code/game/objects/items/dna_injector.dm
index 10c81e9dbb..125c356a7f 100644
--- a/code/game/objects/items/dna_injector.dm
+++ b/code/game/objects/items/dna_injector.dm
@@ -14,33 +14,25 @@
var/list/add_mutations = list()
var/list/remove_mutations = list()
- var/list/add_mutations_static = list()
- var/list/remove_mutations_static = list()
-
var/used = 0
/obj/item/dnainjector/attack_paw(mob/user)
return attack_hand(user)
-/obj/item/dnainjector/proc/prepare()
- for(var/mut_key in add_mutations_static)
- add_mutations.Add(GLOB.mutations_list[mut_key])
- for(var/mut_key in remove_mutations_static)
- remove_mutations.Add(GLOB.mutations_list[mut_key])
-
/obj/item/dnainjector/proc/inject(mob/living/carbon/M, mob/user)
- prepare()
-
if(M.has_dna() && !HAS_TRAIT(M, TRAIT_RADIMMUNE) && !HAS_TRAIT(M, TRAIT_NOCLONE))
M.radiation += rand(20/(damage_coeff ** 2),50/(damage_coeff ** 2))
var/log_msg = "[key_name(user)] injected [key_name(M)] with the [name]"
- for(var/datum/mutation/human/HM in remove_mutations)
- HM.force_lose(M)
- for(var/datum/mutation/human/HM in add_mutations)
- if(HM.name == RACEMUT)
+ for(var/HM in remove_mutations)
+ M.dna.remove_mutation(HM)
+ for(var/HM in add_mutations)
+ if(HM == RACEMUT)
message_admins("[ADMIN_LOOKUPFLW(user)] injected [key_name_admin(M)] with the [name] (MONKEY)")
log_msg += " (MONKEY)"
- HM.force_give(M)
+ if(M.dna.mutation_in_sequence(HM))
+ M.dna.activate_mutation(HM)
+ else
+ M.dna.add_mutation(HM, MUT_EXTRA)
if(fields)
if(fields["name"] && fields["UE"] && fields["blood_type"])
M.real_name = fields["name"]
@@ -90,123 +82,123 @@
/obj/item/dnainjector/antihulk
name = "\improper DNA injector (Anti-Hulk)"
desc = "Cures green skin."
- remove_mutations_static = list(HULK)
+ remove_mutations = list(HULK)
/obj/item/dnainjector/hulkmut
name = "\improper DNA injector (Hulk)"
desc = "This will make you big and strong, but give you a bad skin condition."
- add_mutations_static = list(HULK)
+ add_mutations = list(HULK)
/obj/item/dnainjector/xraymut
name = "\improper DNA injector (X-ray)"
desc = "Finally you can see what the Captain does."
- add_mutations_static = list(XRAY)
+ add_mutations = list(XRAY)
/obj/item/dnainjector/antixray
name = "\improper DNA injector (Anti-X-ray)"
desc = "It will make you see harder."
- remove_mutations_static = list(XRAY)
+ remove_mutations = list(XRAY)
/////////////////////////////////////
/obj/item/dnainjector/antiglasses
name = "\improper DNA injector (Anti-Glasses)"
desc = "Toss away those glasses!"
- remove_mutations_static = list(BADSIGHT)
+ remove_mutations = list(BADSIGHT)
/obj/item/dnainjector/glassesmut
name = "\improper DNA injector (Glasses)"
desc = "Will make you need dorkish glasses."
- add_mutations_static = list(BADSIGHT)
+ add_mutations = list(BADSIGHT)
/obj/item/dnainjector/epimut
name = "\improper DNA injector (Epi.)"
desc = "Shake shake shake the room!"
- add_mutations_static = list(EPILEPSY)
+ add_mutations = list(EPILEPSY)
/obj/item/dnainjector/antiepi
name = "\improper DNA injector (Anti-Epi.)"
desc = "Will fix you up from shaking the room."
- remove_mutations_static = list(EPILEPSY)
+ remove_mutations = list(EPILEPSY)
////////////////////////////////////
/obj/item/dnainjector/anticough
name = "\improper DNA injector (Anti-Cough)"
desc = "Will stop that awful noise."
- remove_mutations_static = list(COUGH)
+ remove_mutations = list(COUGH)
/obj/item/dnainjector/coughmut
name = "\improper DNA injector (Cough)"
desc = "Will bring forth a sound of horror from your throat."
- add_mutations_static = list(COUGH)
+ add_mutations = list(COUGH)
/obj/item/dnainjector/antidwarf
name = "\improper DNA injector (Anti-Dwarfism)"
desc = "Helps you grow big and strong."
- remove_mutations_static = list(DWARFISM)
+ remove_mutations = list(DWARFISM)
/obj/item/dnainjector/dwarf
name = "\improper DNA injector (Dwarfism)"
desc = "It's a small world after all."
- add_mutations_static = list(DWARFISM)
+ add_mutations = list(DWARFISM)
/obj/item/dnainjector/clumsymut
name = "\improper DNA injector (Clumsy)"
desc = "Makes clown minions."
- add_mutations_static = list(CLOWNMUT)
+ add_mutations = list(CLOWNMUT)
/obj/item/dnainjector/anticlumsy
name = "\improper DNA injector (Anti-Clumsy)"
desc = "Apply this for Security Clown."
- remove_mutations_static = list(CLOWNMUT)
+ remove_mutations = list(CLOWNMUT)
/obj/item/dnainjector/antitour
name = "\improper DNA injector (Anti-Tour.)"
desc = "Will cure Tourette's."
- remove_mutations_static = list(TOURETTES)
+ remove_mutations = list(TOURETTES)
/obj/item/dnainjector/tourmut
name = "\improper DNA injector (Tour.)"
desc = "Gives you a nasty case of Tourette's."
- add_mutations_static = list(TOURETTES)
+ add_mutations = list(TOURETTES)
/obj/item/dnainjector/stuttmut
name = "\improper DNA injector (Stutt.)"
desc = "Makes you s-s-stuttterrr."
- add_mutations_static = list(NERVOUS)
+ add_mutations = list(NERVOUS)
/obj/item/dnainjector/antistutt
name = "\improper DNA injector (Anti-Stutt.)"
desc = "Fixes that speaking impairment."
- remove_mutations_static = list(NERVOUS)
+ remove_mutations = list(NERVOUS)
/obj/item/dnainjector/antifire
name = "\improper DNA injector (Anti-Fire)"
desc = "Cures fire."
- remove_mutations_static = list(COLDRES)
+ remove_mutations = list(SPACEMUT)
/obj/item/dnainjector/firemut
name = "\improper DNA injector (Fire)"
desc = "Gives you fire."
- add_mutations_static = list(COLDRES)
+ add_mutations = list(SPACEMUT)
/obj/item/dnainjector/blindmut
name = "\improper DNA injector (Blind)"
desc = "Makes you not see anything."
- add_mutations_static = list(BLINDMUT)
+ add_mutations = list(BLINDMUT)
/obj/item/dnainjector/antiblind
name = "\improper DNA injector (Anti-Blind)"
desc = "IT'S A MIRACLE!!!"
- remove_mutations_static = list(BLINDMUT)
+ remove_mutations = list(BLINDMUT)
/obj/item/dnainjector/antitele
name = "\improper DNA injector (Anti-Tele.)"
desc = "Will make you not able to control your mind."
- remove_mutations_static = list(TK)
+ remove_mutations = list(TK)
/obj/item/dnainjector/telemut
name = "\improper DNA injector (Tele.)"
desc = "Super brain man!"
- add_mutations_static = list(TK)
+ add_mutations = list(TK)
/obj/item/dnainjector/telemut/darkbundle
name = "\improper DNA injector"
@@ -215,100 +207,171 @@
/obj/item/dnainjector/deafmut
name = "\improper DNA injector (Deaf)"
desc = "Sorry, what did you say?"
- add_mutations_static = list(DEAFMUT)
+ add_mutations = list(DEAFMUT)
/obj/item/dnainjector/antideaf
name = "\improper DNA injector (Anti-Deaf)"
desc = "Will make you hear once more."
- remove_mutations_static = list(DEAFMUT)
+ remove_mutations = list(DEAFMUT)
/obj/item/dnainjector/h2m
name = "\improper DNA injector (Human > Monkey)"
desc = "Will make you a flea bag."
- add_mutations_static = list(RACEMUT)
+ add_mutations = list(RACEMUT)
/obj/item/dnainjector/m2h
name = "\improper DNA injector (Monkey > Human)"
desc = "Will make you...less hairy."
- remove_mutations_static = list(RACEMUT)
+ remove_mutations = list(RACEMUT)
/obj/item/dnainjector/antichameleon
name = "\improper DNA injector (Anti-Chameleon)"
- remove_mutations_static = list(CHAMELEON)
+ remove_mutations = list(CHAMELEON)
/obj/item/dnainjector/chameleonmut
name = "\improper DNA injector (Chameleon)"
- add_mutations_static = list(CHAMELEON)
+ add_mutations = list(CHAMELEON)
/obj/item/dnainjector/antiwacky
name = "\improper DNA injector (Anti-Wacky)"
- remove_mutations_static = list(WACKY)
+ remove_mutations = list(WACKY)
/obj/item/dnainjector/wackymut
name = "\improper DNA injector (Wacky)"
- add_mutations_static = list(WACKY)
+ add_mutations = list(WACKY)
/obj/item/dnainjector/antimute
name = "\improper DNA injector (Anti-Mute)"
- remove_mutations_static = list(MUT_MUTE)
+ remove_mutations = list(MUT_MUTE)
/obj/item/dnainjector/mutemut
name = "\improper DNA injector (Mute)"
- add_mutations_static = list(MUT_MUTE)
+ add_mutations = list(MUT_MUTE)
/obj/item/dnainjector/antismile
name = "\improper DNA injector (Anti-Smile)"
- remove_mutations_static = list(SMILE)
+ remove_mutations = list(SMILE)
/obj/item/dnainjector/smilemut
name = "\improper DNA injector (Smile)"
- add_mutations_static = list(SMILE)
+ add_mutations = list(SMILE)
/obj/item/dnainjector/unintelligiblemut
name = "\improper DNA injector (Unintelligible)"
- add_mutations_static = list(UNINTELLIGIBLE)
+ add_mutations = list(UNINTELLIGIBLE)
/obj/item/dnainjector/antiunintelligible
name = "\improper DNA injector (Anti-Unintelligible)"
- remove_mutations_static = list(UNINTELLIGIBLE)
+ remove_mutations = list(UNINTELLIGIBLE)
/obj/item/dnainjector/swedishmut
name = "\improper DNA injector (Swedish)"
- add_mutations_static = list(SWEDISH)
+ add_mutations = list(SWEDISH)
/obj/item/dnainjector/antiswedish
name = "\improper DNA injector (Anti-Swedish)"
- remove_mutations_static = list(SWEDISH)
+ remove_mutations = list(SWEDISH)
/obj/item/dnainjector/chavmut
name = "\improper DNA injector (Chav)"
- add_mutations_static = list(CHAV)
+ add_mutations = list(CHAV)
/obj/item/dnainjector/antichav
name = "\improper DNA injector (Anti-Chav)"
- remove_mutations_static = list(CHAV)
+ remove_mutations = list(CHAV)
/obj/item/dnainjector/elvismut
name = "\improper DNA injector (Elvis)"
- add_mutations_static = list(ELVIS)
+ add_mutations = list(ELVIS)
/obj/item/dnainjector/antielvis
name = "\improper DNA injector (Anti-Elvis)"
- remove_mutations_static = list(ELVIS)
+ remove_mutations = list(ELVIS)
/obj/item/dnainjector/lasereyesmut
name = "\improper DNA injector (Laser Eyes)"
- add_mutations_static = list(LASEREYES)
+ add_mutations = list(LASEREYES)
/obj/item/dnainjector/antilasereyes
name = "\improper DNA injector (Anti-Laser Eyes)"
- remove_mutations_static = list(LASEREYES)
+ remove_mutations = list(LASEREYES)
+
+/obj/item/dnainjector/thermalmut
+ name = "\improper DNA injector (Thermal Vision)"
+ add_mutations = list(THERMAL)
+
+/obj/item/dnainjector/antithermal
+ name = "\improper DNA injector (Anti-Thermal Vision)"
+ remove_mutations = list(THERMAL)
+
+/obj/item/dnainjector/telepathymut
+ name = "\improper DNA injector (Telepathy)"
+ add_mutations = list(TELEPATHY)
+
+/obj/item/dnainjector/antitelepathy
+ name = "\improper DNA injector (Anti-Telepathy)"
+ remove_mutations = list(TELEPATHY)
+
+/obj/item/dnainjector/voidmut
+ name = "\improper DNA injector (Void Magnet)"
+ add_mutations = list(VOID)
+
+/obj/item/dnainjector/antivoid
+ name = "\improper DNA injector (Anti-Void Magnet)"
+ remove_mutations = list(VOID)
+
+/obj/item/dnainjector/firebreathmut
+ name = "\improper DNA injector (Firebreath)"
+ add_mutations = list(FIREBREATH)
+
+/obj/item/dnainjector/antifirebreath
+ name = "\improper DNA injector (Anti-Firebreath)"
+ remove_mutations = list(FIREBREATH)
+
+/obj/item/dnainjector/insulatedmut
+ name = "\improper DNA injector (Insulated)"
+ add_mutations = list(INSULATED)
+
+/obj/item/dnainjector/antiinsulated
+ name = "\improper DNA injector (Anti-Insulated)"
+ remove_mutations = list(INSULATED)
+
+/obj/item/dnainjector/shocktouchmut
+ name = "\improper DNA injector (Shock Touch)"
+ add_mutations = list(SHOCKTOUCH)
+
+/obj/item/dnainjector/antishocktouch
+ name = "\improper DNA injector (Anti-Shock Touch)"
+ remove_mutations = list(SHOCKTOUCH)
+
+/obj/item/dnainjector/antenna
+ name = "\improper DNA injector (Antenna)"
+ add_mutations = list(ANTENNA)
+
+/obj/item/dnainjector/antiantenna
+ name = "\improper DNA injector (Anti-Antenna)"
+ remove_mutations = list(ANTENNA)
+
+/obj/item/dnainjector/paranoia
+ name = "\improper DNA injector (Paranoia)"
+ add_mutations = list(PARANOIA)
+
+/obj/item/dnainjector/antiparanoia
+ name = "\improper DNA injector (Anti-Paranoia)"
+ remove_mutations = list(PARANOIA)
+
+/obj/item/dnainjector/mindread
+ name = "\improper DNA injector (Mindread)"
+ add_mutations = list(MINDREAD)
+
+/obj/item/dnainjector/antimindread
+ name = "\improper DNA injector (Anti-Mindread)"
+ remove_mutations = list(MINDREAD)
/obj/item/dnainjector/timed
var/duration = 600
/obj/item/dnainjector/timed/inject(mob/living/carbon/M, mob/user)
- prepare()
if(M.stat == DEAD) //prevents dead people from having their DNA changed
to_chat(user, "You can't modify [M]'s DNA while [M.p_theyre()] dead.")
return FALSE
@@ -317,23 +380,22 @@
M.radiation += rand(20/(damage_coeff ** 2),50/(damage_coeff ** 2))
var/log_msg = "[key_name(user)] injected [key_name(M)] with the [name]"
var/endtime = world.time+duration
- for(var/datum/mutation/human/HM in remove_mutations)
- if(HM.name == RACEMUT)
+ for(var/mutation in remove_mutations)
+ if(mutation == RACEMUT)
if(ishuman(M))
continue
- M = HM.force_lose(M)
+ M = M.dna.remove_mutation(mutation)
else
- HM.force_lose(M)
- for(var/datum/mutation/human/HM in add_mutations)
- if((HM in M.dna.mutations) && !(M.dna.temporary_mutations[HM.name]))
+ M.dna.remove_mutation(mutation)
+ for(var/mutation in add_mutations)
+ if(M.dna.get_mutation(mutation))
continue //Skip permanent mutations we already have.
- if(HM.name == RACEMUT && ishuman(M))
+ if(mutation == RACEMUT && ishuman(M))
message_admins("[ADMIN_LOOKUPFLW(user)] injected [key_name_admin(M)] with the [name] (MONKEY)")
log_msg += " (MONKEY)"
- M = HM.force_give(M)
+ M = M.dna.add_mutation(mutation, MUT_OTHER, endtime)
else
- HM.force_give(M)
- M.dna.temporary_mutations[HM.name] = endtime
+ M.dna.add_mutation(mutation, MUT_OTHER, endtime)
if(fields)
if(fields["name"] && fields["UE"] && fields["blood_type"])
if(!M.dna.previous["name"])
@@ -361,9 +423,41 @@
/obj/item/dnainjector/timed/hulk
name = "\improper DNA injector (Hulk)"
desc = "This will make you big and strong, but give you a bad skin condition."
- add_mutations_static = list(HULK)
+ add_mutations = list(HULK)
/obj/item/dnainjector/timed/h2m
name = "\improper DNA injector (Human > Monkey)"
desc = "Will make you a flea bag."
- add_mutations_static = list(RACEMUT)
+ add_mutations = list(RACEMUT)
+
+/obj/item/dnainjector/activator
+ name = "\improper DNA activator"
+ desc = "Activates the current mutation on injection, if the subject has it."
+ var/doitanyway = FALSE
+ var/research = FALSE //Set to true to get expended and filled injectors for chromosomes
+ var/filled = FALSE
+
+/obj/item/dnainjector/activator/inject(mob/living/carbon/M, mob/user)
+ if(M.has_dna() && !HAS_TRAIT(M, TRAIT_RADIMMUNE) && !HAS_TRAIT(M,TRAIT_NOCLONE))
+ M.radiation += rand(20/(damage_coeff ** 2),50/(damage_coeff ** 2))
+ var/log_msg = "[key_name(user)] injected [key_name(M)] with the [name]"
+ for(var/mutation in add_mutations)
+ var/datum/mutation/human/HM = mutation
+ if(istype(HM, /datum/mutation/human))
+ mutation = HM.type
+ if(!M.dna.activate_mutation(HM))
+ if(!doitanyway)
+ log_msg += "(FAILED)"
+ else
+ M.dna.add_mutation(HM, MUT_EXTRA)
+ name = "expended [name]"
+ else if(research && M.client)
+ filled = TRUE
+ name = "filled [name]"
+ else
+ name = "expended [name]"
+ log_msg += "([mutation])"
+ log_attack("[log_msg] [loc_name(user)]")
+ return TRUE
+ return FALSE
+
diff --git a/code/modules/antagonists/changeling/powers/chameleon_skin.dm b/code/modules/antagonists/changeling/powers/chameleon_skin.dm
index b7545353b2..03f3aab473 100644
--- a/code/modules/antagonists/changeling/powers/chameleon_skin.dm
+++ b/code/modules/antagonists/changeling/powers/chameleon_skin.dm
@@ -13,17 +13,14 @@
var/mob/living/carbon/human/H = user //SHOULD always be human, because req_human = 1
if(!istype(H)) // req_human could be done in can_sting stuff.
return
- var/datum/mutation/human/HM = GLOB.mutations_list[CHAMELEON]
- if(HM in H.dna.mutations)
- HM.force_lose(H)
+ if(H.dna.get_mutation(CHAMELEON))
+ H.dna.remove_mutation(CHAMELEON)
else
- HM.force_give(H)
+ H.dna.add_mutation(CHAMELEON)
return TRUE
/obj/effect/proc_holder/changeling/chameleon_skin/on_refund(mob/user)
action.Remove(user)
if(user.has_dna())
var/mob/living/carbon/C = user
- var/datum/mutation/human/HM = GLOB.mutations_list[CHAMELEON]
- if(HM in C.dna.mutations)
- HM.force_lose(C)
\ No newline at end of file
+ C.dna.remove_mutation(CHAMELEON)
\ No newline at end of file
diff --git a/code/modules/antagonists/wishgranter/wishgranter.dm b/code/modules/antagonists/wishgranter/wishgranter.dm
index 21cab26d1e..67359b28d3 100644
--- a/code/modules/antagonists/wishgranter/wishgranter.dm
+++ b/code/modules/antagonists/wishgranter/wishgranter.dm
@@ -25,5 +25,5 @@
return
H.dna.add_mutation(HULK)
H.dna.add_mutation(XRAY)
- H.dna.add_mutation(COLDRES)
+ H.dna.add_mutation(SPACEMUT)
H.dna.add_mutation(TK)
\ No newline at end of file
diff --git a/code/modules/awaymissions/mission_code/wildwest.dm b/code/modules/awaymissions/mission_code/wildwest.dm
index 4971d82053..af0ddd3357 100644
--- a/code/modules/awaymissions/mission_code/wildwest.dm
+++ b/code/modules/awaymissions/mission_code/wildwest.dm
@@ -94,7 +94,7 @@
to_chat(user, "Your wish is granted, but at a terrible cost...")
to_chat(user, "The Wish Granter punishes you for your selfishness, claiming your soul and warping your body to match the darkness in your heart.")
user.dna.add_mutation(LASEREYES)
- user.dna.add_mutation(COLDRES)
+ user.dna.add_mutation(SPACEMUT)
user.dna.add_mutation(XRAY)
user.set_species(/datum/species/shadow)
if("Wealth")
diff --git a/code/modules/client/asset_cache.dm b/code/modules/client/asset_cache.dm
index 643d072469..88db23e05c 100644
--- a/code/modules/client/asset_cache.dm
+++ b/code/modules/client/asset_cache.dm
@@ -679,3 +679,10 @@ GLOBAL_LIST_EMPTY(asset_datums)
Insert(initial(D.id), I)
return ..()
+
+/datum/asset/simple/genetics
+ assets = list(
+ "dna_discovered.gif" = 'html/dna_discovered.gif',
+ "dna_undiscovered.gif" = 'html/dna_undiscovered.gif',
+ "dna_extra.gif" = 'html/dna_extra.gif'
+)
\ No newline at end of file
diff --git a/code/modules/events/disease_outbreak.dm b/code/modules/events/disease_outbreak.dm
index 07a399a1b6..9573bcf519 100644
--- a/code/modules/events/disease_outbreak.dm
+++ b/code/modules/events/disease_outbreak.dm
@@ -57,7 +57,7 @@
var/datum/disease/dnaspread/DS = D
DS.strain_data["name"] = H.real_name
DS.strain_data["UI"] = H.dna.uni_identity
- DS.strain_data["SE"] = H.dna.struc_enzymes
+ DS.strain_data["SE"] = H.dna.mutation_index
else
D = new virus_type()
else
diff --git a/code/modules/jobs/job_types/geneticist.dm b/code/modules/jobs/job_types/geneticist.dm
index d7f59ff883..6efa95cd91 100644
--- a/code/modules/jobs/job_types/geneticist.dm
+++ b/code/modules/jobs/job_types/geneticist.dm
@@ -32,4 +32,5 @@
backpack = /obj/item/storage/backpack/genetics
satchel = /obj/item/storage/backpack/satchel/gen
duffelbag = /obj/item/storage/backpack/duffelbag/med
+ l_pocket = /obj/item/sequence_scanner
diff --git a/code/modules/mining/lavaland/necropolis_chests.dm b/code/modules/mining/lavaland/necropolis_chests.dm
index ab3b20ac9f..b79cbd846a 100644
--- a/code/modules/mining/lavaland/necropolis_chests.dm
+++ b/code/modules/mining/lavaland/necropolis_chests.dm
@@ -463,30 +463,45 @@
if(cooldown < world.time)
SSblackbox.record_feedback("amount", "immortality_talisman_uses", 1)
cooldown = world.time + 600
- user.visible_message("[user] vanishes from reality, leaving a hole in [user.p_their()] place!")
- var/obj/effect/immortality_talisman/Z = new(get_turf(src.loc))
- Z.name = "hole in reality"
- Z.desc = "It's shaped an awful lot like [user.name]."
- Z.setDir(user.dir)
- user.forceMove(Z)
- user.notransform = 1
- user.status_flags |= GODMODE
- addtimer(CALLBACK(src, .proc/return_to_reality, user, Z), 100)
+ new /obj/effect/immortality_talisman(get_turf(user), user)
else
to_chat(user, "[src] is not ready yet!")
-/obj/item/immortality_talisman/proc/return_to_reality(mob/user, obj/effect/immortality_talisman/Z)
- user.status_flags &= ~GODMODE
- user.notransform = 0
- user.forceMove(get_turf(Z))
- user.visible_message("[user] pops back into reality!")
- Z.can_destroy = TRUE
- qdel(Z)
-
/obj/effect/immortality_talisman
+ name = "hole in reality"
+ desc = "It's shaped an awful lot like a person."
icon_state = "blank"
icon = 'icons/effects/effects.dmi'
- var/can_destroy = FALSE
+ var/vanish_description = "vanishes from reality"
+ var/can_destroy = TRUE
+
+/obj/effect/immortality_talisman/Initialize(mapload, mob/new_user)
+ . = ..()
+ if(new_user)
+ vanish(new_user)
+
+/obj/effect/immortality_talisman/proc/vanish(mob/user)
+ user.visible_message("[user] [vanish_description], leaving a hole in [user.p_their()] place!")
+
+ desc = "It's shaped an awful lot like [user.name]."
+ setDir(user.dir)
+
+ user.forceMove(src)
+ user.notransform = TRUE
+ user.status_flags |= GODMODE
+
+ can_destroy = FALSE
+
+ addtimer(CALLBACK(src, .proc/unvanish, user), 10 SECONDS)
+
+/obj/effect/immortality_talisman/proc/unvanish(mob/user)
+ user.status_flags &= ~GODMODE
+ user.notransform = FALSE
+ user.forceMove(get_turf(src))
+
+ user.visible_message("[user] pops back into reality!")
+ can_destroy = TRUE
+ qdel(src)
/obj/effect/immortality_talisman/attackby()
return
@@ -503,6 +518,9 @@
else
. = ..()
+/obj/effect/immortality_talisman/void
+ vanish_description = "is dragged into the void"
+
//Shared Bag
diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm
index 52cf82a06a..0ffa299b0b 100644
--- a/code/modules/mob/living/carbon/carbon.dm
+++ b/code/modules/mob/living/carbon/carbon.dm
@@ -181,7 +181,7 @@
if(HAS_TRAIT(src, TRAIT_PACIFISM))
to_chat(src, "You gently let go of [throwable_mob].")
return
-
+
adjustStaminaLossBuffered(25)//CIT CHANGE - throwing an entire person shall be very tiring
var/turf/start_T = get_turf(loc) //Get the start and target tile for the descriptors
var/turf/end_T = get_turf(target)
@@ -623,7 +623,13 @@
if(M.name == XRAY)
sight |= (SEE_TURFS|SEE_MOBS|SEE_OBJS)
see_in_dark = max(see_in_dark, 8)
+ if(HAS_TRAIT(src, TRAIT_THERMAL_VISION))
+ sight |= (SEE_MOBS)
+ lighting_alpha = min(lighting_alpha, LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE)
+ if(HAS_TRAIT(src, TRAIT_XRAY_VISION))
+ sight |= (SEE_TURFS|SEE_MOBS|SEE_OBJS)
+ see_in_dark = max(see_in_dark, 8)
if(see_override)
see_invisible = see_override
. = ..()
diff --git a/code/modules/mob/living/carbon/human/species.dm b/code/modules/mob/living/carbon/human/species.dm
index 8bf6b4ad3a..c22144f087 100644
--- a/code/modules/mob/living/carbon/human/species.dm
+++ b/code/modules/mob/living/carbon/human/species.dm
@@ -69,6 +69,7 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
var/siemens_coeff = 1 //base electrocution coefficient
var/damage_overlay_type = "human" //what kind of damage overlays (if any) appear on our species when wounded?
var/fixed_mut_color = "" //to use MUTCOLOR with a fixed color that's independent of dna.feature["mcolor"]
+ var/inert_mutation = DWARFISM
var/list/special_step_sounds //Sounds to override barefeet walkng
var/grab_sound //Special sound for grabbing
@@ -361,6 +362,14 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
else
C.type_of_meat = initial(meat)
+ //If their inert mutation is not the same, swap it out
+ if((inert_mutation != new_species.inert_mutation) && LAZYLEN(C.dna.mutation_index) && (inert_mutation in C.dna.mutation_index))
+ C.dna.remove_mutation(inert_mutation)
+ //keep it at the right spot, so we can't have people taking shortcuts
+ var/location = C.dna.mutation_index.Find(inert_mutation)
+ C.dna.mutation_index[location] = new_species.inert_mutation
+ C.dna.mutation_index[new_species.inert_mutation] = create_sequence(new_species.inert_mutation)
+
SEND_SIGNAL(C, COMSIG_SPECIES_LOSS, src)
/datum/species/proc/handle_hair(mob/living/carbon/human/H, forced_colour)
@@ -1378,7 +1387,7 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
if(radiation > RAD_MOB_MUTATE)
if(prob(1))
to_chat(H, "You mutate!")
- H.randmutb()
+ H.easy_randmut(NEGATIVE+MINOR_NEGATIVE)
H.emote("gasp")
H.domutcheck()
diff --git a/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm b/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm
index 9317a51050..23fcf2648b 100644
--- a/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm
+++ b/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm
@@ -23,6 +23,7 @@
exotic_bloodtype = "L"
disliked_food = GRAIN | DAIRY
liked_food = GROSS | MEAT
+ inert_mutation = FIREBREATH
/datum/species/lizard/after_equip_job(datum/job/J, mob/living/carbon/human/H)
H.grant_language(/datum/language/draconic)
diff --git a/code/modules/mob/living/carbon/human/species_types/podpeople.dm b/code/modules/mob/living/carbon/human/species_types/podpeople.dm
index f0dd48c6c1..172fa5a6c3 100644
--- a/code/modules/mob/living/carbon/human/species_types/podpeople.dm
+++ b/code/modules/mob/living/carbon/human/species_types/podpeople.dm
@@ -60,9 +60,9 @@
H.Knockdown(100)
H.visible_message("[H] writhes in pain as [H.p_their()] vacuoles boil.", "You writhe in pain as your vacuoles boil!", "You hear the crunching of leaves.")
if(prob(80))
- H.randmutb()
+ H.easy_randmut(NEGATIVE+MINOR_NEGATIVE)
else
- H.randmutg()
+ H.easy_randmut(POSITIVE)
H.domutcheck()
else
H.adjustFireLoss(rand(5,15))
diff --git a/code/modules/mob/living/carbon/life.dm b/code/modules/mob/living/carbon/life.dm
index 0061c7ed70..ec4e45a084 100644
--- a/code/modules/mob/living/carbon/life.dm
+++ b/code/modules/mob/living/carbon/life.dm
@@ -424,7 +424,6 @@
/mob/living/carbon/handle_mutations_and_radiation()
if(dna && dna.temporary_mutations.len)
- var/datum/mutation/human/HM
for(var/mut in dna.temporary_mutations)
if(dna.temporary_mutations[mut] < world.time)
if(mut == UI_CHANGED)
@@ -447,9 +446,9 @@
dna.previous.Remove("blood_type")
dna.temporary_mutations.Remove(mut)
continue
- HM = GLOB.mutations_list[mut]
- HM.force_lose(src)
- dna.temporary_mutations.Remove(mut)
+ for(var/datum/mutation/human/HM in dna.mutations)
+ if(HM && HM.timed)
+ dna.remove_mutation(HM.type)
radiation -= min(radiation, RAD_LOSS_PER_TICK)
if(radiation > RAD_MOB_SAFE)
diff --git a/code/modules/mob/living/carbon/monkey/life.dm b/code/modules/mob/living/carbon/monkey/life.dm
index 906e138b0a..1c4ca28c58 100644
--- a/code/modules/mob/living/carbon/monkey/life.dm
+++ b/code/modules/mob/living/carbon/monkey/life.dm
@@ -38,7 +38,7 @@
if(radiation > RAD_MOB_MUTATE)
if(prob(1))
to_chat(src, "You mutate!")
- randmutb()
+ easy_randmut(NEGATIVE+MINOR_NEGATIVE)
emote("gasp")
domutcheck()
diff --git a/code/modules/mob/transform_procs.dm b/code/modules/mob/transform_procs.dm
index 6394b45aa7..2b6d1a7427 100644
--- a/code/modules/mob/transform_procs.dm
+++ b/code/modules/mob/transform_procs.dm
@@ -50,9 +50,8 @@
O.updateappearance(icon_update=0)
if(tr_flags & TR_KEEPSE)
- O.dna.struc_enzymes = dna.struc_enzymes
- var/datum/mutation/human/race/R = GLOB.mutations_list[RACEMUT]
- O.dna.struc_enzymes = R.set_se(O.dna.struc_enzymes, on=1)//we don't want to keep the race block inactive
+ O.dna.mutation_index = dna.mutation_index
+ O.dna.set_se(1, GET_INITIALIZED_MUTATION(RACEMUT))
if(suiciding)
O.suiciding = suiciding
@@ -210,9 +209,8 @@
O.name = O.real_name
if(tr_flags & TR_KEEPSE)
- O.dna.struc_enzymes = dna.struc_enzymes
- var/datum/mutation/human/race/R = GLOB.mutations_list[RACEMUT]
- O.dna.struc_enzymes = R.set_se(O.dna.struc_enzymes, on=0)//we don't want to keep the race block active
+ O.dna.mutation_index = dna.mutation_index
+ O.dna.set_se(0, GET_INITIALIZED_MUTATION(RACEMUT))
O.domutcheck()
if(suiciding)
diff --git a/code/modules/reagents/chemistry/reagents/medicine_reagents.dm b/code/modules/reagents/chemistry/reagents/medicine_reagents.dm
index d8ff084227..6adce8f204 100644
--- a/code/modules/reagents/chemistry/reagents/medicine_reagents.dm
+++ b/code/modules/reagents/chemistry/reagents/medicine_reagents.dm
@@ -983,7 +983,7 @@ datum/reagent/medicine/styptic_powder/overdose_start(mob/living/M)
/datum/reagent/medicine/mutadone/on_mob_life(mob/living/carbon/M)
M.jitteriness = 0
if(M.has_dna())
- M.dna.remove_all_mutations()
+ M.dna.remove_all_mutations(mutadone = TRUE)
if(!QDELETED(M)) //We were a monkey, now a human
..()
diff --git a/code/modules/reagents/chemistry/reagents/toxin_reagents.dm b/code/modules/reagents/chemistry/reagents/toxin_reagents.dm
index f97204b45e..62f2da33ef 100644
--- a/code/modules/reagents/chemistry/reagents/toxin_reagents.dm
+++ b/code/modules/reagents/chemistry/reagents/toxin_reagents.dm
@@ -43,9 +43,9 @@
if((method==VAPOR && prob(min(33, reac_volume))) || method==INGEST || method==PATCH || method==INJECT)
M.randmuti()
if(prob(98))
- M.randmutb()
+ M.easy_randmut(NEGATIVE+MINOR_NEGATIVE)
else
- M.randmutg()
+ M.easy_randmut(POSITIVE)
M.updateappearance()
M.domutcheck()
..()
diff --git a/code/modules/research/designs/medical_designs.dm b/code/modules/research/designs/medical_designs.dm
index e70362553c..75b705fc35 100644
--- a/code/modules/research/designs/medical_designs.dm
+++ b/code/modules/research/designs/medical_designs.dm
@@ -162,6 +162,16 @@
category = list("Medical Designs")
departmental_flags = DEPARTMENTAL_FLAG_MEDICAL
+/datum/design/genescanner
+ name = "Genetic Sequence Analyzer"
+ desc = "A handy hand-held analyzers for quickly determining mutations and collecting the full sequence."
+ id = "genescanner"
+ build_path = /obj/item/sequence_scanner
+ build_type = PROTOLATHE
+ materials = list(MAT_METAL = 1000, MAT_GLASS = 500)
+ category = list("Medical Designs")
+ departmental_flags = DEPARTMENTAL_FLAG_MEDICAL
+
/datum/design/healthanalyzer_advanced
name = "Advanced Health Analyzer"
desc = "A hand-held body scanner able to distinguish vital signs of the subject with high accuracy."
diff --git a/code/modules/research/techweb/_techweb.dm b/code/modules/research/techweb/_techweb.dm
index 17294fe108..dc1add8e58 100644
--- a/code/modules/research/techweb/_techweb.dm
+++ b/code/modules/research/techweb/_techweb.dm
@@ -20,6 +20,7 @@
var/largest_bomb_value = 0
var/organization = "Third-Party" //Organization name, used for display.
var/list/last_bitcoins = list() //Current per-second production, used for display only.
+ var/list/discovered_mutations = list() //Mutations discovered by genetics, this way they are shared and cant be destroyed by destroying a single console
var/list/tiers = list() //Assoc list, id = number, 1 is available, 2 is all reqs are 1, so on
/datum/techweb/New()
diff --git a/code/modules/research/techweb/all_nodes.dm b/code/modules/research/techweb/all_nodes.dm
index 6877ba71f6..1a3601beba 100644
--- a/code/modules/research/techweb/all_nodes.dm
+++ b/code/modules/research/techweb/all_nodes.dm
@@ -71,7 +71,7 @@
display_name = "Biological Technology"
description = "What makes us tick." //the MC, silly!
prereq_ids = list("base")
- design_ids = list("medicalkit", "chem_heater", "chem_master", "chem_dispenser", "sleeper", "vr_sleeper", "pandemic", "defibmount", "operating", "soda_dispenser", "beer_dispenser", "healthanalyzer", "blood_bag", "bloodbankgen")
+ design_ids = list("medicalkit", "chem_heater", "chem_master", "chem_dispenser", "sleeper", "vr_sleeper", "pandemic", "defibmount", "operating", "soda_dispenser", "beer_dispenser", "healthanalyzer", "blood_bag", "bloodbankgen","genescanner")
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
export_price = 5000
diff --git a/code/modules/spells/spell_types/touch_attacks.dm b/code/modules/spells/spell_types/touch_attacks.dm
index 794ed797ad..b4c236d150 100644
--- a/code/modules/spells/spell_types/touch_attacks.dm
+++ b/code/modules/spells/spell_types/touch_attacks.dm
@@ -1,6 +1,8 @@
/obj/effect/proc_holder/spell/targeted/touch
var/hand_path = /obj/item/melee/touch_attack
var/obj/item/melee/touch_attack/attached_hand = null
+ var/drawmessage = "You channel the power of the spell to your hand."
+ var/dropmessage = "You draw the power out of your hand."
invocation_type = "none" //you scream on connecting, not summoning
include_user = 1
range = -1
@@ -21,7 +23,7 @@
/obj/effect/proc_holder/spell/targeted/touch/cast(list/targets,mob/user = usr)
if(!QDELETED(attached_hand))
remove_hand(TRUE)
- to_chat(user, "You draw the power out of your hand.")
+ to_chat(user, "[dropmessage]")
return
for(var/mob/living/carbon/C in targets)
@@ -43,7 +45,7 @@
remove_hand(TRUE)
to_chat(user, "Your hands are full!")
return FALSE
- to_chat(user, "You channel the power of the spell to your hand.")
+ to_chat(user, "[drawmessage]")
return TRUE
diff --git a/code/modules/surgery/bodyparts/dismemberment.dm b/code/modules/surgery/bodyparts/dismemberment.dm
index 31f08f0e87..fdc7ac9fe9 100644
--- a/code/modules/surgery/bodyparts/dismemberment.dm
+++ b/code/modules/surgery/bodyparts/dismemberment.dm
@@ -108,7 +108,7 @@
for(var/X in C.dna.mutations) //some mutations require having specific limbs to be kept.
var/datum/mutation/human/MT = X
if(MT.limb_req && MT.limb_req == body_zone)
- MT.force_lose(C)
+ C.dna.force_lose(MT)
for(var/X in C.internal_organs) //internal organs inside the dismembered limb are dropped.
var/obj/item/organ/O = X
diff --git a/html/browser/scannernew.css b/html/browser/scannernew.css
index 0e809a6760..6746a61a0d 100644
--- a/html/browser/scannernew.css
+++ b/html/browser/scannernew.css
@@ -16,3 +16,24 @@
float: left;
}
+img.selected
+{
+ border: 1px solid blue;
+}
+img.unselected
+{
+ border: 2px solid black;
+}
+div>table {
+ float: left;
+}
+td
+{
+ text-align: center;
+}
+a.clean
+{
+ background: none;
+ border: none;
+ marging: none;
+}
\ No newline at end of file
diff --git a/html/dna_discovered.gif b/html/dna_discovered.gif
new file mode 100644
index 0000000000..bc6b75f2f9
Binary files /dev/null and b/html/dna_discovered.gif differ
diff --git a/html/dna_extra.gif b/html/dna_extra.gif
new file mode 100644
index 0000000000..c92218a661
Binary files /dev/null and b/html/dna_extra.gif differ
diff --git a/html/dna_undiscovered.gif b/html/dna_undiscovered.gif
new file mode 100644
index 0000000000..a3c182a4a1
Binary files /dev/null and b/html/dna_undiscovered.gif differ
diff --git a/icons/effects/genetics.dmi b/icons/effects/genetics.dmi
index a9f5f433d5..373a9de623 100644
Binary files a/icons/effects/genetics.dmi and b/icons/effects/genetics.dmi differ
diff --git a/icons/mob/actions/actions_genetic.dmi b/icons/mob/actions/actions_genetic.dmi
new file mode 100644
index 0000000000..72bd87f97e
Binary files /dev/null and b/icons/mob/actions/actions_genetic.dmi differ
diff --git a/icons/mob/actions/actions_spells.dmi b/icons/mob/actions/actions_spells.dmi
index 072bfc8fe3..7798b85c43 100644
Binary files a/icons/mob/actions/actions_spells.dmi and b/icons/mob/actions/actions_spells.dmi differ
diff --git a/icons/obj/chromosomes.dmi b/icons/obj/chromosomes.dmi
new file mode 100644
index 0000000000..a8ff6186d8
Binary files /dev/null and b/icons/obj/chromosomes.dmi differ
diff --git a/icons/obj/device.dmi b/icons/obj/device.dmi
index f467da6fbf..60f15b5543 100644
Binary files a/icons/obj/device.dmi and b/icons/obj/device.dmi differ
diff --git a/modular_citadel/code/game/machinery/wishgranter.dm b/modular_citadel/code/game/machinery/wishgranter.dm
index 48024a2228..7758c0d613 100644
--- a/modular_citadel/code/game/machinery/wishgranter.dm
+++ b/modular_citadel/code/game/machinery/wishgranter.dm
@@ -17,7 +17,7 @@
to_chat(user, "Your head pounds for a moment, before your vision clears. The Wish Granter, sensing the darkness in your heart, has given you limitless power, and it's all yours!")
user.dna.add_mutation(HULK)
user.dna.add_mutation(XRAY)
- user.dna.add_mutation(COLDRES)
+ user.dna.add_mutation(SPACEMUT)
user.dna.add_mutation(TK)
user.next_move_modifier *= 0.5 //half the delay between attacks!
to_chat(user, "Things around you feel slower!")
diff --git a/tgstation.dme b/tgstation.dme
index 5526e5eaf0..744374b987 100755
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -119,6 +119,7 @@
#include "code\__HELPERS\AStar.dm"
#include "code\__HELPERS\cmp.dm"
#include "code\__HELPERS\dates.dm"
+#include "code\__HELPERS\dna.dm"
#include "code\__HELPERS\donator_groupings.dm"
#include "code\__HELPERS\files.dm"
#include "code\__HELPERS\game.dm"
@@ -444,6 +445,7 @@
#include "code\datums\diseases\advance\symptoms\fever.dm"
#include "code\datums\diseases\advance\symptoms\fire.dm"
#include "code\datums\diseases\advance\symptoms\flesh_eating.dm"
+#include "code\datums\diseases\advance\symptoms\genetics.dm"
#include "code\datums\diseases\advance\symptoms\hallucigen.dm"
#include "code\datums\diseases\advance\symptoms\headache.dm"
#include "code\datums\diseases\advance\symptoms\heal.dm"
@@ -492,10 +494,14 @@
#include "code\datums\mood_events\generic_positive_events.dm"
#include "code\datums\mood_events\mood_event.dm"
#include "code\datums\mood_events\needs_events.dm"
+#include "code\datums\mutations\actions.dm"
+#include "code\datums\mutations\antenna.dm"
#include "code\datums\mutations\body.dm"
#include "code\datums\mutations\chameleon.dm"
#include "code\datums\mutations\cold_resistance.dm"
+#include "code\datums\mutations\combined.dm"
#include "code\datums\mutations\hulk.dm"
+#include "code\datums\mutations\radioactive.dm"
#include "code\datums\mutations\sight.dm"
#include "code\datums\mutations\speech.dm"
#include "code\datums\mutations\telekinesis.dm"
@@ -832,6 +838,7 @@
#include "code\game\objects\items\cardboard_cutouts.dm"
#include "code\game\objects\items\cards_ids.dm"
#include "code\game\objects\items\charter.dm"
+#include "code\game\objects\items\chromosome.dm"
#include "code\game\objects\items\chrono_eraser.dm"
#include "code\game\objects\items\cigs_lighters.dm"
#include "code\game\objects\items\clown_items.dm"