diff --git a/code/__DEFINES/DNA.dm b/code/__DEFINES/DNA.dm
index a093142f02f..97a1654a424 100644
--- a/code/__DEFINES/DNA.dm
+++ b/code/__DEFINES/DNA.dm
@@ -2,32 +2,39 @@
#define CHECK_DNA_AND_SPECIES(C) if((!(C.dna)) || (!(C.dna.species))) return
-//Defines copying names of mutations in all cases, make sure to change this if you change mutation's name
-#define HULK "Hulk"
-#define XRAY "X Ray Vision"
-#define COLDRES "Cold Resistance"
-#define TK "Telekinesis"
-#define NERVOUS "Nervousness"
-#define EPILEPSY "Epilepsy"
-#define MUTATE "Unstable DNA"
-#define COUGH "Cough"
-#define DWARFISM "Dwarfism"
-#define CLOWNMUT "Clumsiness"
-#define TOURETTES "Tourettes Syndrome"
-#define DEAFMUT "Deafness"
-#define BLINDMUT "Blindness"
-#define RACEMUT "Monkified"
-#define BADSIGHT "Near Sightness"
-#define LASEREYES "Laser Eyes"
-#define CHAMELEON "Chameleon"
-#define WACKY "Wacky"
-#define MUT_MUTE "Mute"
-#define SMILE "Smile"
-#define STONER "Stoner"
-#define UNINTELLIGIBLE "Unintelligible"
-#define SWEDISH "Swedish"
-#define CHAV "Chav"
-#define ELVIS "Elvis"
+//Defines copying names of mutations in all cases, make sure to change this if you change mutation's type
+#define HULK /datum/mutation/human/hulk
+#define XRAY /datum/mutation/human/x_ray
+#define SPACEMUT /datum/mutation/human/space_adaptation
+#define TK /datum/mutation/human/telekinesis
+#define NERVOUS /datum/mutation/human/nervousness
+#define EPILEPSY /datum/mutation/human/epilepsy
+#define MUTATE /datum/mutation/human/bad_dna
+#define COUGH /datum/mutation/human/cough
+#define DWARFISM /datum/mutation/human/dwarfism
+#define CLOWNMUT /datum/mutation/human/clumsy
+#define TOURETTES /datum/mutation/human/tourettes
+#define DEAFMUT /datum/mutation/human/deaf
+#define BLINDMUT /datum/mutation/human/blind
+#define RACEMUT /datum/mutation/human/race
+#define BADSIGHT /datum/mutation/human/nearsight
+#define LASEREYES /datum/mutation/human/laser_eyes
+#define CHAMELEON /datum/mutation/human/chameleon
+#define WACKY /datum/mutation/human/wacky
+#define MUT_MUTE /datum/mutation/human/mute
+#define SMILE /datum/mutation/human/smile
+#define STONER /datum/mutation/human/stoner
+#define UNINTELLIGIBLE /datum/mutation/human/unintelligible
+#define SWEDISH /datum/mutation/human/swedish
+#define CHAV /datum/mutation/human/chav
+#define ELVIS /datum/mutation/human/elvis
+#define RADIOACTIVE /datum/mutation/human/radioactive
+#define GLOWY /datum/mutation/human/glow
+#define TELEPATHY /datum/mutation/human/telepath
+#define FIREBREATH /datum/mutation/human/firebreath
+#define VOID /datum/mutation/human/void
+#define STRONG /datum/mutation/human/strong
+#define FIRESWEAT /datum/mutation/human/fire
#define UI_CHANGED "ui changed"
#define UE_CHANGED "ue changed"
@@ -39,10 +46,13 @@
//Types of usual mutations
#define POSITIVE 1
#define NEGATIVE 2
-#define MINOR_NEGATIVE 3
+#define MINOR_NEGATIVE 4
-//Mutations that cant be taken from genetics and are not in SE
-#define NON_SCANNABLE -1
+
+//Mutation classes. Normal being on them, extra being additional mutations with instability and other being stuff you dont want people to fuck with like wizard mutate
+#define MUT_NORMAL 1
+#define MUT_EXTRA 2
+#define MUT_OTHER 3
//DNA - Because fuck you and your magic numbers being all over the codebase.
#define DNA_BLOCK_SIZE 3
@@ -56,7 +66,8 @@
#define DNA_FACIAL_HAIR_STYLE_BLOCK 6
#define DNA_HAIR_STYLE_BLOCK 7
-#define DNA_STRUC_ENZYMES_BLOCKS 18
+#define DNA_SEQUENCE_LENGTH 4
+#define DNA_MUTATION_BLOCKS 8
#define DNA_UNIQUE_ENZYMES_LEN 32
//Transformation proc stuff
diff --git a/code/__HELPERS/dna.dm b/code/__HELPERS/dna.dm
new file mode 100644
index 00000000000..47681f65e65
--- /dev/null
+++ b/code/__HELPERS/dna.dm
@@ -0,0 +1,8 @@
+//////////////////////////////////////////////////////////
+//A bunch of helpers to make genetics less of a headache//
+//////////////////////////////////////////////////////////
+
+#define get_initialized_mutation(A) GLOB.all_mutations[A]
+#define mutation_in_sequence(A, B) ((A) in B.mutation_index)
+#define get_gene_string(A, B) (B.mutation_index[A])
+#define get_sequence(A) (GLOB.full_sequences[A])
\ No newline at end of file
diff --git a/code/_globalvars/genetics.dm b/code/_globalvars/genetics.dm
index a0557e8e1bc..c95743e9dab 100644
--- a/code/_globalvars/genetics.dm
+++ b/code/_globalvars/genetics.dm
@@ -21,8 +21,10 @@ GLOBAL_LIST(op_se_blocks)
GLOBAL_VAR(NULLED_SE)
GLOBAL_VAR(NULLED_UI)
-GLOBAL_LIST_EMPTY(global_mutations) // list of hidden mutation things
-
+GLOBAL_LIST_EMPTY(all_mutations)
+GLOBAL_LIST_EMPTY(full_sequences)
GLOBAL_LIST_EMPTY(bad_mutations)
GLOBAL_LIST_EMPTY(good_mutations)
-GLOBAL_LIST_EMPTY(not_good_mutations)
\ No newline at end of file
+GLOBAL_LIST_EMPTY(not_good_mutations)
+
+GLOBAL_LIST_EMPTY(mutation_recipes)
\ No newline at end of file
diff --git a/code/_onclick/other_mobs.dm b/code/_onclick/other_mobs.dm
index 30e6883d249..62fbb66e05d 100644
--- a/code/_onclick/other_mobs.dm
+++ b/code/_onclick/other_mobs.dm
@@ -20,7 +20,7 @@
var/override = 0
for(var/datum/mutation/human/HM in dna.mutations)
- override += HM.on_attack_hand(src, A, proximity)
+ override += HM.on_attack_hand(A, proximity)
if(override)
return
@@ -93,7 +93,7 @@
return
for(var/datum/mutation/human/HM in dna.mutations)
- HM.on_ranged_attack(src, A, mouseparams)
+ HM.on_ranged_attack(A, mouseparams)
if(isturf(A) && get_dist(src,A) <= 1)
src.Move_Pulled(A)
diff --git a/code/controllers/subsystem/atoms.dm b/code/controllers/subsystem/atoms.dm
index 58b67d22641..38a18112508 100644
--- a/code/controllers/subsystem/atoms.dm
+++ b/code/controllers/subsystem/atoms.dm
@@ -16,7 +16,7 @@ SUBSYSTEM_DEF(atoms)
/datum/controller/subsystem/atoms/Initialize(timeofday)
GLOB.fire_overlay.appearance_flags = RESET_COLOR
- setupGenetics() //to set the mutations' place in structural enzymes, so monkey.initialize() knows where to put the monkey mutation.
+ setupGenetics() //to set the mutations' sequence
initialized = INITIALIZATION_INNEW_MAPLOAD
InitializeAtoms()
return ..()
@@ -106,16 +106,19 @@ SUBSYSTEM_DEF(atoms)
BadInitializeCalls = SSatoms.BadInitializeCalls
/datum/controller/subsystem/atoms/proc/setupGenetics()
- var/list/avnums = new /list(DNA_STRUC_ENZYMES_BLOCKS)
- for(var/i=1, i<=DNA_STRUC_ENZYMES_BLOCKS, i++)
- avnums[i] = i
- CHECK_TICK
-
- for(var/A in subtypesof(/datum/mutation/human))
- var/datum/mutation/human/B = new A()
- if(B.dna_block == NON_SCANNABLE)
+ var/list/mutations = subtypesof(/datum/mutation/human)
+ shuffle_inplace(mutations)
+ for(var/A in subtypesof(/datum/generecipe))
+ var/datum/generecipe/GR = A
+ GLOB.mutation_recipes[initial(GR.required)] = initial(GR.result)
+ for(var/i in 1 to LAZYLEN(mutations))
+ var/path = mutations[i] //byond gets pissy when we do it in one line
+ var/datum/mutation/human/B = new path ()
+ B.alias = "Mutation #[i]"
+ GLOB.all_mutations[B.type] = B
+ GLOB.full_sequences[B.type] = generate_gene_sequence(B.blocks)
+ if(B.locked)
continue
- B.dna_block = pick_n_take(avnums)
if(B.quality == POSITIVE)
GLOB.good_mutations |= B
else if(B.quality == NEGATIVE)
diff --git a/code/datums/brain_damage/mild.dm b/code/datums/brain_damage/mild.dm
index 72d7adab8d6..9e7ca54122e 100644
--- a/code/datums/brain_damage/mild.dm
+++ b/code/datums/brain_damage/mild.dm
@@ -73,7 +73,7 @@
//no fiddling with genetics to get out of this one
/datum/brain_trauma/mild/speech_impediment/on_life()
- if(!(GLOB.mutations_list[UNINTELLIGIBLE] in owner.dna.mutations))
+ if(!(owner.dna.check_mutation(UNINTELLIGIBLE)))
on_gain()
..()
diff --git a/code/datums/components/spill.dm b/code/datums/components/spill.dm
index ee189e4d9de..c9a750e069e 100644
--- a/code/datums/components/spill.dm
+++ b/code/datums/components/spill.dm
@@ -1,56 +1,56 @@
-// This component is for forcing strange things into your pocket that fall out if you fall down
-// Yes this exists purely for the spaghetti meme
-
-/datum/component/spill
- var/preexisting_item_flags
-
- var/list/droptext
- var/list/dropsound
-
-// droptext is an arglist for visible_message
-// dropsound is a list of potential sounds that gets picked from
-/datum/component/spill/Initialize(list/_droptext, list/_dropsound)
- if(!isitem(parent))
- return COMPONENT_INCOMPATIBLE
-
- if(_droptext && !islist(_droptext))
- _droptext = list(_droptext)
- droptext = _droptext
-
- if(_dropsound && !islist(_dropsound))
- _dropsound = list(_dropsound)
- dropsound = _dropsound
-
-/datum/component/spill/PostTransfer()
- if(!isitem(parent))
- return COMPONENT_INCOMPATIBLE
-
-/datum/component/spill/RegisterWithParent()
- RegisterSignal(parent, COMSIG_ITEM_EQUIPPED, .proc/equip_react)
- RegisterSignal(parent, COMSIG_ITEM_DROPPED, .proc/drop_react)
- var/obj/item/master = parent
- preexisting_item_flags = master.item_flags
- master.item_flags |= ITEM_SLOT_POCKET
-
-/datum/component/spill/UnregisterFromParent()
- UnregisterSignal(parent, list(COMSIG_ITEM_EQUIPPED, COMSIG_ITEM_DROPPED))
- var/obj/item/master = parent
- if(!(preexisting_item_flags & ITEM_SLOT_POCKET))
- master.item_flags &= ~ITEM_SLOT_POCKET
-
-/datum/component/spill/proc/equip_react(obj/item/source, mob/equipper, slot)
- if(slot == SLOT_L_STORE || slot == SLOT_R_STORE)
- RegisterSignal(equipper, COMSIG_LIVING_STATUS_KNOCKDOWN, .proc/knockdown_react, TRUE)
- else
- UnregisterSignal(equipper, COMSIG_LIVING_STATUS_KNOCKDOWN)
-
-/datum/component/spill/proc/drop_react(obj/item/source, mob/dropper)
- UnregisterSignal(dropper, COMSIG_LIVING_STATUS_KNOCKDOWN)
-
-/datum/component/spill/proc/knockdown_react(mob/living/fool)
- var/obj/item/master = parent
- fool.dropItemToGround(master)
- if(droptext)
- fool.visible_message(arglist(droptext))
- if(dropsound)
- playsound(master, pick(dropsound), 30)
+// This component is for forcing strange things into your pocket that fall out if you fall down
+// Yes this exists purely for the spaghetti meme
+
+/datum/component/spill
+ var/preexisting_item_flags
+
+ var/list/droptext
+ var/list/dropsound
+
+// droptext is an arglist for visible_message
+// dropsound is a list of potential sounds that gets picked from
+/datum/component/spill/Initialize(list/_droptext, list/_dropsound)
+ if(!isitem(parent))
+ return COMPONENT_INCOMPATIBLE
+
+ if(_droptext && !islist(_droptext))
+ _droptext = list(_droptext)
+ droptext = _droptext
+
+ if(_dropsound && !islist(_dropsound))
+ _dropsound = list(_dropsound)
+ dropsound = _dropsound
+
+/datum/component/spill/PostTransfer()
+ if(!isitem(parent))
+ return COMPONENT_INCOMPATIBLE
+
+/datum/component/spill/RegisterWithParent()
+ RegisterSignal(parent, COMSIG_ITEM_EQUIPPED, .proc/equip_react)
+ RegisterSignal(parent, COMSIG_ITEM_DROPPED, .proc/drop_react)
+ var/obj/item/master = parent
+ preexisting_item_flags = master.item_flags
+ master.item_flags |= ITEM_SLOT_POCKET
+
+/datum/component/spill/UnregisterFromParent()
+ UnregisterSignal(parent, list(COMSIG_ITEM_EQUIPPED, COMSIG_ITEM_DROPPED))
+ var/obj/item/master = parent
+ if(!(preexisting_item_flags & ITEM_SLOT_POCKET))
+ master.item_flags &= ~ITEM_SLOT_POCKET
+
+/datum/component/spill/proc/equip_react(obj/item/source, mob/equipper, slot)
+ if(slot == SLOT_L_STORE || slot == SLOT_R_STORE)
+ RegisterSignal(equipper, COMSIG_LIVING_STATUS_KNOCKDOWN, .proc/knockdown_react, TRUE)
+ else
+ UnregisterSignal(equipper, COMSIG_LIVING_STATUS_KNOCKDOWN)
+
+/datum/component/spill/proc/drop_react(obj/item/source, mob/dropper)
+ UnregisterSignal(dropper, COMSIG_LIVING_STATUS_KNOCKDOWN)
+
+/datum/component/spill/proc/knockdown_react(mob/living/fool)
+ var/obj/item/master = parent
+ fool.dropItemToGround(master)
+ if(droptext)
+ fool.visible_message(arglist(droptext))
+ if(dropsound)
+ playsound(master, pick(dropsound), 30)
\ No newline at end of file
diff --git a/code/datums/datacore.dm b/code/datums/datacore.dm
index 60bb24c8c2a..4fde3987c7b 100644
--- a/code/datums/datacore.dm
+++ b/code/datums/datacore.dm
@@ -277,7 +277,6 @@
L.fields["sex"] = H.gender
L.fields["blood_type"] = H.dna.blood_type
L.fields["b_dna"] = H.dna.unique_enzymes
- L.fields["enzymes"] = H.dna.struc_enzymes
L.fields["identity"] = H.dna.uni_identity
L.fields["species"] = H.dna.species.type
L.fields["features"] = H.dna.features
diff --git a/code/datums/diseases/advance/symptoms/genetics.dm b/code/datums/diseases/advance/symptoms/genetics.dm
index c01fccee134..527fb522cd4 100644
--- a/code/datums/diseases/advance/symptoms/genetics.dm
+++ b/code/datums/diseases/advance/symptoms/genetics.dm
@@ -58,12 +58,12 @@ Bonus
symptom_delay_max = 60
if(A.properties["resistance"] >= 8) //mutate twice
power = 2
- possible_mutations = (GLOB.bad_mutations | GLOB.not_good_mutations) - GLOB.mutations_list[RACEMUT]
+ possible_mutations = (GLOB.bad_mutations | GLOB.not_good_mutations) - GLOB.all_mutations[RACEMUT]
var/mob/living/carbon/M = A.affected_mob
if(M)
if(!M.has_dna())
return
- archived_dna = M.dna.struc_enzymes
+ archived_dna = M.dna.mutation_index
// Give them back their old DNA when cured.
/datum/symptom/genetic_mutation/End(datum/disease/advance/A)
@@ -74,5 +74,5 @@ Bonus
if(M && archived_dna)
if(!M.has_dna())
return
- M.dna.struc_enzymes = archived_dna
+ M.dna.mutation_index = archived_dna
M.domutcheck()
diff --git a/code/datums/dna.dm b/code/datums/dna.dm
index be18f08ce3e..0fbf22613fc 100644
--- a/code/datums/dna.dm
+++ b/code/datums/dna.dm
@@ -2,17 +2,19 @@
/////////////////////////// DNA DATUM
/datum/dna
var/unique_enzymes
- var/struc_enzymes
var/uni_identity
var/blood_type
var/datum/species/species = new /datum/species/human //The type of mutant race the player is if applicable (i.e. potato-man)
var/list/features = list("FFF") //first value is mutant color
var/real_name //Stores the real name of the person who originally got this dna datum. Used primarely for changelings,
var/list/mutations = list() //All mutations are from now on here
- var/list/temporary_mutations = list() //Timers for temporary mutations
+ var/list/temporary_mutations = list() //Temporary changes to the UE
var/list/previous = list() //For temporary name/ui/ue/blood_type modifications
var/mob/living/holder
var/delete_species = TRUE //Set to FALSE when a body is scanned by a cloner to fix #38875
+ var/mutation_index[DNA_MUTATION_BLOCKS] //List of which mutations this carbon has and its assigned block
+ var/stability = 100
+ var/scrambled = FALSE //Did we take something like mutagen? In that case we cant get our genes scanned to instantly cheese all the powers.
/datum/dna/New(mob/living/new_holder)
if(istype(new_holder))
@@ -45,11 +47,11 @@
destination.dna.real_name = real_name
destination.dna.temporary_mutations = temporary_mutations.Copy()
if(transfer_SE)
- destination.dna.struc_enzymes = struc_enzymes
+ destination.dna.mutation_index = mutation_index
/datum/dna/proc/copy_dna(datum/dna/new_dna)
new_dna.unique_enzymes = unique_enzymes
- new_dna.struc_enzymes = struc_enzymes
+ new_dna.mutation_index = mutation_index
new_dna.uni_identity = uni_identity
new_dna.blood_type = blood_type
new_dna.features = features.Copy()
@@ -57,26 +59,28 @@
new_dna.real_name = real_name
new_dna.mutations = mutations.Copy()
-/datum/dna/proc/add_mutation(mutation_name)
- var/datum/mutation/human/HM = GLOB.mutations_list[mutation_name]
- HM.on_acquiring(holder)
+//See mutation.dm for what 'class' does. 'time' is time till it removes itself in decimals. 0 for no timer
+/datum/dna/proc/add_mutation(mutation_type, class = MUT_OTHER, time)
+ if(get_mutation(mutation_type))
+ return
+ force_give(new mutation_type (class, time))
-/datum/dna/proc/remove_mutation(mutation_name)
- var/datum/mutation/human/HM = GLOB.mutations_list[mutation_name]
- HM.on_losing(holder)
+/datum/dna/proc/remove_mutation(mutation_type)
+ force_lose(get_mutation(mutation_type))
-/datum/dna/proc/check_mutation(mutation_name)
- var/datum/mutation/human/HM = GLOB.mutations_list[mutation_name]
- return mutations.Find(HM)
+/datum/dna/proc/check_mutation(mutation_type)
+ return get_mutation(mutation_type)
-/datum/dna/proc/remove_all_mutations()
- remove_mutation_group(mutations)
+/datum/dna/proc/remove_all_mutations(list/classes = list(MUT_NORMAL, MUT_EXTRA, MUT_OTHER))
+ remove_mutation_group(mutations, classes)
+ scrambled = FALSE
-/datum/dna/proc/remove_mutation_group(list/group)
+/datum/dna/proc/remove_mutation_group(list/group, list/classes = list(MUT_NORMAL, MUT_EXTRA, MUT_OTHER))
if(!group)
return
for(var/datum/mutation/human/HM in group)
- HM.force_lose(holder)
+ if(HM.class in classes)
+ force_lose(HM)
/datum/dna/proc/generate_uni_identity()
. = ""
@@ -103,19 +107,49 @@
. += random_string(DNA_BLOCK_SIZE,GLOB.hex_characters)
return .
-/datum/dna/proc/generate_struc_enzymes()
- var/list/sorting = new /list(DNA_STRUC_ENZYMES_BLOCKS)
- var/result = ""
- for(var/datum/mutation/human/A in GLOB.good_mutations + GLOB.bad_mutations + GLOB.not_good_mutations)
- if(A.name == RACEMUT && ismonkey(holder))
- sorting[A.dna_block] = num2hex(A.lowest_value + rand(0, 256 * 6), DNA_BLOCK_SIZE)
- mutations |= A
- else
- sorting[A.dna_block] = random_string(DNA_BLOCK_SIZE, list("0","1","2","3","4","5","6"))
+/datum/dna/proc/generate_dna_blocks()
+ var/bonus
+ if(species && species.inert_mutation)
+ bonus = get_initialized_mutation(species.inert_mutation)
+ var/list/mutations_temp = GLOB.good_mutations + GLOB.bad_mutations + GLOB.not_good_mutations + bonus
+ if(!LAZYLEN(mutations_temp))
+ return
+ mutation_index.Cut()
+ shuffle_inplace(mutations_temp)
+ if(ismonkey(holder))
+ mutations |= new RACEMUT(MUT_NORMAL)
+ mutation_index[RACEMUT] = get_sequence(RACEMUT)
+ else
+ mutation_index[RACEMUT] = create_sequence(RACEMUT, FALSE)
+ for(var/i in 2 to DNA_MUTATION_BLOCKS)
+ var/datum/mutation/human/M = mutations_temp[i]
+ mutation_index[M.type] = create_sequence(M.type, FALSE,M.difficulty)
+ shuffle_inplace(mutation_index)
- for(var/B in sorting)
- result += B
- return result
+//Used to generate original gene sequences for every mutation
+/proc/generate_gene_sequence(length=4)
+ var/static/list/active_sequences = list("AT","TA","GC","CG")
+ var/sequence
+ for(var/i in 1 to length*DNA_SEQUENCE_LENGTH)
+ sequence += pick(active_sequences)
+ return sequence
+
+//Used to create a chipped gene sequence
+/proc/create_sequence(mutation, active, difficulty)
+ if(!difficulty)
+ var/datum/mutation/human/A = get_initialized_mutation(mutation) //leaves the possibility to change difficulty mid-round
+ if(A)
+ return
+ difficulty = A.difficulty
+ difficulty += rand(-2,4)
+ var/sequence = get_sequence(mutation)
+ if(active)
+ return sequence
+ while(difficulty)
+ var/randnum = rand(1, length(sequence))
+ sequence = copytext(sequence, 1, randnum) + "X" + copytext(sequence, randnum+1, length(sequence)+1)
+ difficulty--
+ return sequence
/datum/dna/proc/generate_unique_enzymes()
. = ""
@@ -146,6 +180,21 @@
if(DNA_HAIR_STYLE_BLOCK)
setblock(uni_identity, blocknumber, construct_block(GLOB.hair_styles_list.Find(H.hair_style), GLOB.hair_styles_list.len))
+//Please use add_mutation or activate_mutation instead
+/datum/dna/proc/force_give(datum/mutation/human/HM)
+ if(holder && HM)
+ if(HM.class == MUT_NORMAL)
+ set_se(1, HM)
+ . = HM.on_acquiring(holder)
+ if(.)
+ qdel(HM)
+
+//Use remove_mutation instead
+/datum/dna/proc/force_lose(datum/mutation/human/HM)
+ if(holder && (HM in mutations))
+ set_se(0, HM)
+ return HM.on_losing(holder)
+
/datum/dna/proc/mutations_say_mods(message)
if(message)
for(var/datum/mutation/human/M in mutations)
@@ -166,11 +215,76 @@
/datum/dna/proc/is_same_as(datum/dna/D)
- if(uni_identity == D.uni_identity && struc_enzymes == D.struc_enzymes && real_name == D.real_name)
+ if(uni_identity == D.uni_identity && mutation_index == D.mutation_index && real_name == D.real_name)
if(species.type == D.species.type && features == D.features && blood_type == D.blood_type)
return 1
return 0
+/datum/dna/proc/update_instability(alert=FALSE)
+ stability = 100
+ for(var/datum/mutation/human/M in mutations)
+ if(M.class == MUT_EXTRA)
+ stability -= M.instability
+ if(holder && alert)
+ var/message
+ switch(stability)
+ if(90 to 70)
+ message = "You shiver."
+ if(69 to 60)
+ message = "You feel cold."
+ if(59 to 40)
+ message = "You feel sick."
+ if(39 to 20)
+ message = "It feels like your skin is moving."
+ if(19 to 1)
+ message = "You can feel your cells burning."
+ if(0 to -INFINITY)
+ message = "You can feel your DNA exploding, we need to do something fast!"
+ addtimer(CALLBACK(src, .proc/something_horrible), 600) //you've got 60 seconds to get your shit togheter
+
+ if(message)
+ to_chat(holder,message)
+
+/datum/dna/proc/something_horrible()
+ if(!holder || (stability > 0))
+ return
+ var/instability = -stability
+ remove_all_mutations()
+ stability = 100
+ if(!ishuman(holder))
+ holder.gib()
+ return
+ var/mob/living/carbon/human/H = holder
+ if(prob(max(70-instability,0)))
+ switch(rand(0,3)) //not complete and utter death
+ if(0)
+ H.monkeyize()
+ if(1)
+ H.gain_trauma(/datum/brain_trauma/severe/paralysis)
+ if(2)
+ H.corgize()
+ if(3)
+ to_chat(H, "Oh, we actually feel quite alright!")
+ else
+ switch(rand(0,3))
+ if(0)
+ H.gib()
+ if(1)
+ H.dust()
+
+ if(2)
+ H.death()
+ H.petrify(INFINITY)
+ if(3)
+ if(prob(90))
+ var/obj/item/bodypart/BP = H.get_bodypart(pick(BODY_ZONE_CHEST,BODY_ZONE_HEAD))
+ if(BP)
+ BP.dismember()
+ else
+ H.gib()
+ else
+ H.set_species(/datum/species/dullahan)
+
//used to update dna UI, UE, and dna.real_name.
/datum/dna/proc/update_dna_identity()
uni_identity = generate_uni_identity()
@@ -181,7 +295,7 @@
blood_type = newblood_type
unique_enzymes = generate_unique_enzymes()
uni_identity = generate_uni_identity()
- struc_enzymes = generate_struc_enzymes()
+ generate_dna_blocks()
features = random_features()
@@ -246,7 +360,7 @@
return dna
-/mob/living/carbon/human/proc/hardset_dna(ui, se, newreal_name, newblood_type, datum/species/mrace, newfeatures)
+/mob/living/carbon/human/proc/hardset_dna(ui, list/mutation_index, newreal_name, newblood_type, datum/species/mrace, newfeatures)
if(newfeatures)
dna.features = newfeatures
@@ -267,8 +381,8 @@
dna.uni_identity = ui
updateappearance(icon_update=0)
- if(se)
- dna.struc_enzymes = se
+ if(LAZYLEN(mutation_index))
+ dna.mutation_index = mutation_index
domutcheck()
if(mrace || newfeatures || ui)
@@ -311,19 +425,56 @@
/mob/proc/domutcheck()
return
-/mob/living/carbon/domutcheck(force_powers=0) //Set force_powers to 1 to bypass the power chance
+/mob/living/carbon/domutcheck()
if(!has_dna())
return
- for(var/datum/mutation/human/A in GLOB.good_mutations | GLOB.bad_mutations | GLOB.not_good_mutations)
- if(ismob(A.check_block(src, force_powers)))
+ for(var/mutation in dna.mutation_index)
+ if(ismob(dna.check_block(mutation)))
return //we got monkeyized/humanized, this mob will be deleted, no need to continue.
update_mutations_overlay()
+/datum/dna/proc/check_block(mutation)
+ var/datum/mutation/human/HM = get_mutation(mutation)
+ if(check_block_string(mutation))
+ if(!HM)
+ . = add_mutation(mutation, MUT_NORMAL)
+ return
+ return force_lose(HM)
+//Return the active mutation of a type if there is one
+/datum/dna/proc/get_mutation(A)
+ for(var/datum/mutation/human/HM in mutations)
+ if(HM.type == A)
+ return HM
+
+/datum/dna/proc/check_block_string(mutation)
+ if((LAZYLEN(mutation_index) > DNA_MUTATION_BLOCKS) || !(mutation in mutation_index))
+ return 0
+ return is_gene_active(mutation)
+
+/datum/dna/proc/is_gene_active(mutation)
+ return (mutation_index[mutation] == get_sequence(mutation))
+
+/datum/dna/proc/set_se(on=TRUE, datum/mutation/human/HM)
+ if(!HM || !(HM.type in mutation_index) || (LAZYLEN(mutation_index) < DNA_MUTATION_BLOCKS))
+ return
+ . = TRUE
+ if(on)
+ mutation_index[HM.type] = get_sequence(HM.type)
+ else if(get_sequence(HM.type) == mutation_index[HM.type])
+ mutation_index[HM.type] = create_sequence(HM.type, FALSE, HM.difficulty)
+
+/datum/dna/proc/activate_mutation(mutation)
+ if(!mutation)
+ return
+ if(!mutation_in_sequence(mutation, src)) //cant activate what we dont have, use add_mutation
+ return FALSE
+ return add_mutation(mutation, MUT_NORMAL)
/////////////////////////// DNA HELPER-PROCS //////////////////////////////
+
/proc/getleftblocks(input,blocknumber,blocksize)
if(blocknumber > 1)
return copytext(input,1,((blocksize*blocknumber)-(blocksize-1)))
@@ -343,26 +494,33 @@
/mob/living/carbon/proc/randmut(list/candidates, difficulty = 2)
if(!has_dna())
return
- var/datum/mutation/human/num = pick(candidates)
- . = num.force_give(src)
+ var/mutation = pick(candidates)
+ . = dna.add_mutation(mutation)
-/mob/living/carbon/proc/randmutb()
+/mob/living/carbon/proc/easy_randmut(quality = POSITIVE + NEGATIVE + MINOR_NEGATIVE, scrambled = TRUE, sequence = TRUE, exclude_monkey = TRUE)
if(!has_dna())
return
- var/datum/mutation/human/HM = pick((GLOB.bad_mutations | GLOB.not_good_mutations) - GLOB.mutations_list[RACEMUT])
- . = HM.force_give(src)
-
-/mob/living/carbon/proc/randmutg()
- if(!has_dna())
- return
- var/datum/mutation/human/HM = pick(GLOB.good_mutations)
- . = HM.force_give(src)
-
-/mob/living/carbon/proc/randmutvg()
- if(!has_dna())
- return
- var/datum/mutation/human/HM = pick((GLOB.good_mutations) - GLOB.mutations_list[HULK] - GLOB.mutations_list[DWARFISM])
- . = HM.force_give(src)
+ var/list/mutations = list()
+ if(quality & POSITIVE)
+ mutations += GLOB.good_mutations
+ if(quality & NEGATIVE)
+ mutations += GLOB.bad_mutations
+ if(quality & MINOR_NEGATIVE)
+ mutations += GLOB.not_good_mutations
+ var/list/possible = list()
+ for(var/datum/mutation/human/A in mutations)
+ if((!sequence || mutation_in_sequence(A.type, dna)) && !dna.get_mutation(A.type))
+ possible += A.type
+ if(exclude_monkey)
+ possible.Remove(RACEMUT)
+ if(LAZYLEN(possible))
+ var/mutation = pick(possible)
+ . = dna.activate_mutation(mutation)
+ if(scrambled)
+ var/datum/mutation/human/HM = dna.get_mutation(mutation)
+ if(HM)
+ HM.scrambled = TRUE
+ return TRUE
/mob/living/carbon/proc/randmuti()
if(!has_dna())
@@ -385,9 +543,9 @@
if(!M.has_dna())
return 0
if(se)
- for(var/i=1, i<=DNA_STRUC_ENZYMES_BLOCKS, i++)
+ for(var/i=1, i<=DNA_MUTATION_BLOCKS, i++)
if(prob(probability))
- M.dna.struc_enzymes = setblock(M.dna.struc_enzymes, i, random_string(DNA_BLOCK_SIZE, GLOB.hex_characters))
+ M.dna.generate_dna_blocks()
M.domutcheck()
if(ui)
for(var/i=1, i<=DNA_UNI_IDENTITY_BLOCKS, i++)
diff --git a/code/datums/footstep.dm b/code/datums/footstep.dm
index 1197a3b9dd5..f0e38ed1133 100644
--- a/code/datums/footstep.dm
+++ b/code/datums/footstep.dm
@@ -1,99 +1,99 @@
-/datum/component/footstep
- var/steps = 0
- var/volume
- var/e_range
-
-/datum/component/footstep/Initialize(volume_ = 0.5, e_range_ = -1)
- if(!isliving(parent))
- return COMPONENT_INCOMPATIBLE
- volume = volume_
- e_range = e_range_
- RegisterSignal(parent, list(COMSIG_MOVABLE_MOVED), .proc/play_footstep)
-
-/datum/component/footstep/proc/play_footstep()
- var/turf/open/T = get_turf(parent)
- if(!istype(T))
- return
-
- var/mob/living/LM = parent
- var/v = volume
- var/e = e_range
- if(!T.footstep || LM.buckled || !CHECK_MULTIPLE_BITFIELDS(LM.mobility_flags, MOBILITY_STAND | MOBILITY_MOVE) || LM.throwing || LM.movement_type & (VENTCRAWLING | FLYING) || (LM.stat != CONSCIOUS) || LM.incapacitated() || LM.IsStun() || LM.IsParalyzed())
- return
-
- if(iscarbon(LM))
- var/mob/living/carbon/C = LM
- if(!C.get_bodypart(BODY_ZONE_L_LEG) && !C.get_bodypart(BODY_ZONE_R_LEG))
- return
- if(ishuman(C) && C.m_intent == MOVE_INTENT_WALK)
- v /= 2
- e -= 5
- steps++
-
- if(steps >= 6)
- steps = 0
-
- if(steps % 2)
- return
-
- if(!LM.has_gravity(T) && steps != 0) // don't need to step as often when you hop around
- return
-
- //begin playsound shenanigans//
-
- //for barefooted non-clawed mobs like monkeys
- if(isbarefoot(LM))
- playsound(T, pick(GLOB.barefootstep[T.barefootstep][1]),
- GLOB.barefootstep[T.barefootstep][2] * v,
- TRUE,
- GLOB.barefootstep[T.barefootstep][3] + e)
- return
-
- //for xenomorphs, dogs, and other clawed mobs
- if(isclawfoot(LM))
- if(isalienadult(LM)) //xenos are stealthy and get quieter footsteps
- v /= 3
- e -= 3
-
- playsound(T, pick(GLOB.clawfootstep[T.clawfootstep][1]),
- GLOB.clawfootstep[T.clawfootstep][2] * v,
- TRUE,
- GLOB.clawfootstep[T.clawfootstep][3] + e)
- return
-
- //for megafauna and other large and imtimidating mobs such as the bloodminer
- if(isheavyfoot(LM))
- playsound(T, pick(GLOB.heavyfootstep[T.heavyfootstep][1]),
- GLOB.heavyfootstep[T.heavyfootstep][2] * v,
- TRUE,
- GLOB.heavyfootstep[T.heavyfootstep][3] + e)
- return
-
- //for slimes
- if(isslime(LM))
- playsound(T, 'sound/effects/footstep/slime1.ogg', 15 * v)
- return
-
- //for (simple) humanoid mobs (clowns, russians, pirates, etc.)
- if(isshoefoot(LM))
- if(!ishuman(LM))
- playsound(T, pick(GLOB.footstep[T.footstep][1]),
- GLOB.footstep[T.footstep][2] * v,
- TRUE,
- GLOB.footstep[T.footstep][3] + e)
- return
- if(ishuman(LM)) //for proper humans, they're special
- var/mob/living/carbon/human/H = LM
- var/feetCover = (H.wear_suit && (H.wear_suit.body_parts_covered & FEET)) || (H.w_uniform && (H.w_uniform.body_parts_covered & FEET))
-
- if(H.shoes || feetCover) //are we wearing shoes
- playsound(T, pick(GLOB.footstep[T.footstep][1]),
- GLOB.footstep[T.footstep][2] * v,
- TRUE,
- GLOB.footstep[T.footstep][3] + e)
-
- if((!H.shoes && !feetCover) || !(H.mobility_flags & MOBILITY_STAND)) //are we NOT wearing shoes or are we lying/crawling (using hands to move around)?
- playsound(T, pick(GLOB.barefootstep[T.barefootstep][1]),
- GLOB.barefootstep[T.barefootstep][2] * v,
- TRUE,
+/datum/component/footstep
+ var/steps = 0
+ var/volume
+ var/e_range
+
+/datum/component/footstep/Initialize(volume_ = 0.5, e_range_ = -1)
+ if(!isliving(parent))
+ return COMPONENT_INCOMPATIBLE
+ volume = volume_
+ e_range = e_range_
+ RegisterSignal(parent, list(COMSIG_MOVABLE_MOVED), .proc/play_footstep)
+
+/datum/component/footstep/proc/play_footstep()
+ var/turf/open/T = get_turf(parent)
+ if(!istype(T))
+ return
+
+ var/mob/living/LM = parent
+ var/v = volume
+ var/e = e_range
+ if(!T.footstep || LM.buckled || !CHECK_MULTIPLE_BITFIELDS(LM.mobility_flags, MOBILITY_STAND | MOBILITY_MOVE) || LM.throwing || LM.movement_type & (VENTCRAWLING | FLYING) || (LM.stat != CONSCIOUS) || LM.incapacitated() || LM.IsStun() || LM.IsParalyzed())
+ return
+
+ if(iscarbon(LM))
+ var/mob/living/carbon/C = LM
+ if(!C.get_bodypart(BODY_ZONE_L_LEG) && !C.get_bodypart(BODY_ZONE_R_LEG))
+ return
+ if(ishuman(C) && C.m_intent == MOVE_INTENT_WALK)
+ v /= 2
+ e -= 5
+ steps++
+
+ if(steps >= 6)
+ steps = 0
+
+ if(steps % 2)
+ return
+
+ if(!LM.has_gravity(T) && steps != 0) // don't need to step as often when you hop around
+ return
+
+ //begin playsound shenanigans//
+
+ //for barefooted non-clawed mobs like monkeys
+ if(isbarefoot(LM))
+ playsound(T, pick(GLOB.barefootstep[T.barefootstep][1]),
+ GLOB.barefootstep[T.barefootstep][2] * v,
+ TRUE,
+ GLOB.barefootstep[T.barefootstep][3] + e)
+ return
+
+ //for xenomorphs, dogs, and other clawed mobs
+ if(isclawfoot(LM))
+ if(isalienadult(LM)) //xenos are stealthy and get quieter footsteps
+ v /= 3
+ e -= 3
+
+ playsound(T, pick(GLOB.clawfootstep[T.clawfootstep][1]),
+ GLOB.clawfootstep[T.clawfootstep][2] * v,
+ TRUE,
+ GLOB.clawfootstep[T.clawfootstep][3] + e)
+ return
+
+ //for megafauna and other large and imtimidating mobs such as the bloodminer
+ if(isheavyfoot(LM))
+ playsound(T, pick(GLOB.heavyfootstep[T.heavyfootstep][1]),
+ GLOB.heavyfootstep[T.heavyfootstep][2] * v,
+ TRUE,
+ GLOB.heavyfootstep[T.heavyfootstep][3] + e)
+ return
+
+ //for slimes
+ if(isslime(LM))
+ playsound(T, 'sound/effects/footstep/slime1.ogg', 15 * v)
+ return
+
+ //for (simple) humanoid mobs (clowns, russians, pirates, etc.)
+ if(isshoefoot(LM))
+ if(!ishuman(LM))
+ playsound(T, pick(GLOB.footstep[T.footstep][1]),
+ GLOB.footstep[T.footstep][2] * v,
+ TRUE,
+ GLOB.footstep[T.footstep][3] + e)
+ return
+ if(ishuman(LM)) //for proper humans, they're special
+ var/mob/living/carbon/human/H = LM
+ var/feetCover = (H.wear_suit && (H.wear_suit.body_parts_covered & FEET)) || (H.w_uniform && (H.w_uniform.body_parts_covered & FEET))
+
+ if(H.shoes || feetCover) //are we wearing shoes
+ playsound(T, pick(GLOB.footstep[T.footstep][1]),
+ GLOB.footstep[T.footstep][2] * v,
+ TRUE,
+ GLOB.footstep[T.footstep][3] + e)
+
+ if((!H.shoes && !feetCover) || !(H.mobility_flags & MOBILITY_STAND)) //are we NOT wearing shoes or are we lying/crawling (using hands to move around)?
+ playsound(T, pick(GLOB.barefootstep[T.barefootstep][1]),
+ GLOB.barefootstep[T.barefootstep][2] * v,
+ TRUE,
GLOB.barefootstep[T.barefootstep][3] + e)
\ No newline at end of file
diff --git a/code/datums/mutations.dm b/code/datums/mutations.dm
index 7de9ca8b4a0..2c60ce69bda 100644
--- a/code/datums/mutations.dm
+++ b/code/datums/mutations.dm
@@ -1,93 +1,84 @@
-GLOBAL_LIST_EMPTY(mutations_list)
-
/datum/mutation
var/name
-/datum/mutation/New()
- GLOB.mutations_list[name] = src
-
/datum/mutation/human
- var/dna_block
+ name = "mutation"
+ var/desc = "A mutation."
+ var/locked
var/quality
var/get_chance = 100
var/lowest_value = 256 * 8
var/text_gain_indication = ""
var/text_lose_indication = ""
- var/list/mutable_appearance/visual_indicators = list()
+ var/static/list/mutable_appearance/visual_indicators = list()
+ var/obj/effect/proc_holder/spell/power
var/layer_used = MUTATIONS_LAYER //which mutation layer to use
var/list/species_allowed = list() //to restrict mutation to only certain species
var/health_req //minimum health required to acquire the mutation
var/limb_req //required limbs to acquire this mutation
var/time_coeff = 1 //coefficient for timed mutations
+ var/datum/dna/dna
+ var/mob/living/carbon/human/owner
+ var/instability = 0 //instability the holder gets when the mutation is not native
+ var/blocks = 4 //Amount of those big blocks with gene sequences
+ var/difficulty = 8 //Amount of missing sequences. Sometimes it removes an entire pair for 2 points
+ var/timed = FALSE //Boolean to easily check if we're going to self destruct
+ var/alias //'Mutation #49', decided every round to get some form of distinction between undiscovered mutations
+ var/scrambled = FALSE //Wheter we can read it if it's active. To avoid cheesing with mutagen
+ var/class //Decides player accesibility, sorta
+ //MUT_NORMAL - A mutation that can be activated and deactived by completing a sequence
+ //MUT_EXTRA - A mutation that is in the mutations tab, and can be given and taken away through though the DNA console. Has a 0 before it's name in the mutation section of the dna console
+ //MUT_OTHER Cannot be interacted with by players through normal means. I.E. wizards mutate
-/datum/mutation/human/proc/force_give(mob/living/carbon/human/owner)
- set_block(owner)
- . = on_acquiring(owner)
+/datum/mutation/human/New(class_ = MUT_OTHER, timer)
+ . = ..()
+ class = class_
+ if(timer)
+ addtimer(CALLBACK(src, .proc/remove), timer)
+ timed = TRUE
-/datum/mutation/human/proc/force_lose(mob/living/carbon/human/owner)
- set_block(owner, 0)
- . = on_losing(owner)
-
-/datum/mutation/human/proc/set_se(se_string, on = 1)
- if(!se_string || lentext(se_string) < DNA_STRUC_ENZYMES_BLOCKS * DNA_BLOCK_SIZE)
- return
- var/before = copytext(se_string, 1, ((dna_block - 1) * DNA_BLOCK_SIZE) + 1)
- var/injection = num2hex(on ? rand(lowest_value, (256 * 16) - 1) : rand(0, lowest_value - 1), DNA_BLOCK_SIZE)
- var/after = copytext(se_string, (dna_block * DNA_BLOCK_SIZE) + 1, 0)
- return before + injection + after
-
-/datum/mutation/human/proc/set_block(mob/living/carbon/owner, on = 1)
- if(owner && owner.has_dna())
- owner.dna.struc_enzymes = set_se(owner.dna.struc_enzymes, on)
-
-/datum/mutation/human/proc/check_block_string(se_string)
- if(!se_string || lentext(se_string) < DNA_STRUC_ENZYMES_BLOCKS * DNA_BLOCK_SIZE)
- return 0
- if(hex2num(getblock(se_string, dna_block)) >= lowest_value)
- return 1
-
-/datum/mutation/human/proc/check_block(mob/living/carbon/human/owner, force_powers=0)
- if(check_block_string(owner.dna.struc_enzymes))
- if(prob(get_chance)||force_powers)
- . = on_acquiring(owner)
- else
- . = on_losing(owner)
-
-/datum/mutation/human/proc/on_acquiring(mob/living/carbon/human/owner)
- if(!owner || !istype(owner) || owner.stat == DEAD || (src in owner.dna.mutations))
+/datum/mutation/human/proc/on_acquiring(mob/living/carbon/human/H)
+ if(!H || !istype(H) || H.stat == DEAD || (src in H.dna.mutations))
return TRUE
- if(species_allowed.len && !species_allowed.Find(owner.dna.species.id))
+ if(species_allowed.len && !species_allowed.Find(H.dna.species.id))
return TRUE
- if(health_req && owner.health < health_req)
+ if(health_req && H.health < health_req)
return TRUE
- if(limb_req && !owner.get_bodypart(limb_req))
+ if(limb_req && !H.get_bodypart(limb_req))
return TRUE
- owner.dna.mutations.Add(src)
+ owner = H
+ dna = H.dna
+ dna.mutations += src
if(text_gain_indication)
to_chat(owner, text_gain_indication)
if(visual_indicators.len)
- var/list/mut_overlay = list(get_visual_indicator(owner))
+ var/list/mut_overlay = list(get_visual_indicator())
if(owner.overlays_standing[layer_used])
mut_overlay = owner.overlays_standing[layer_used]
- mut_overlay |= get_visual_indicator(owner)
+ mut_overlay |= get_visual_indicator()
owner.remove_overlay(layer_used)
owner.overlays_standing[layer_used] = mut_overlay
owner.apply_overlay(layer_used)
+ if(power)
+ power = new power()
+ power.action_background_icon_state = "bg_tech_blue_on"
+ power.panel = "Genetic"
+ owner.AddSpell(power)
-/datum/mutation/human/proc/get_visual_indicator(mob/living/carbon/human/owner)
+/datum/mutation/human/proc/get_visual_indicator()
return
-/datum/mutation/human/proc/on_attack_hand(mob/living/carbon/human/owner, atom/target, proximity)
+/datum/mutation/human/proc/on_attack_hand( atom/target, proximity)
return
-/datum/mutation/human/proc/on_ranged_attack(mob/living/carbon/human/owner, atom/target)
+/datum/mutation/human/proc/on_ranged_attack(atom/target)
return
-/datum/mutation/human/proc/on_move(mob/living/carbon/human/owner, new_loc)
+/datum/mutation/human/proc/on_move(new_loc)
return
-/datum/mutation/human/proc/on_life(mob/living/carbon/human/owner)
+/datum/mutation/human/proc/on_life()
return
/datum/mutation/human/proc/on_losing(mob/living/carbon/human/owner)
@@ -99,9 +90,12 @@ GLOBAL_LIST_EMPTY(mutations_list)
if(owner.overlays_standing[layer_used])
mut_overlay = owner.overlays_standing[layer_used]
owner.remove_overlay(layer_used)
- mut_overlay.Remove(get_visual_indicator(owner))
+ mut_overlay.Remove(get_visual_indicator())
owner.overlays_standing[layer_used] = mut_overlay
owner.apply_overlay(layer_used)
+ if(power)
+ owner.RemoveSpell(power)
+ qdel(src)
return 0
return 1
@@ -118,17 +112,26 @@ GLOBAL_LIST_EMPTY(mutations_list)
/mob/living/carbon/human/update_mutations_overlay()
for(var/datum/mutation/human/CM in dna.mutations)
if(CM.species_allowed.len && !CM.species_allowed.Find(dna.species.id))
- CM.force_lose(src) //shouldn't have that mutation at all
+ dna.force_lose(CM) //shouldn't have that mutation at all
continue
if(CM.visual_indicators.len)
var/list/mut_overlay = list()
if(overlays_standing[CM.layer_used])
mut_overlay = overlays_standing[CM.layer_used]
- var/mutable_appearance/V = CM.get_visual_indicator(src)
+ var/mutable_appearance/V = CM.get_visual_indicator()
if(!mut_overlay.Find(V)) //either we lack the visual indicator or we have the wrong one
remove_overlay(CM.layer_used)
- for(var/mutable_appearance/MA in CM.visual_indicators)
+ for(var/mutable_appearance/MA in CM.visual_indicators[CM.type])
mut_overlay.Remove(MA)
mut_overlay |= V
overlays_standing[CM.layer_used] = mut_overlay
apply_overlay(CM.layer_used)
+
+/datum/mutation/human/proc/copy_mutation(datum/mutation/human/HM) //Not yet implemented, useful for when assigning specific stats.
+
+/datum/mutation/human/proc/remove()
+ if(dna)
+ dna.force_lose(src)
+ else
+ qdel(src)
+
diff --git a/code/datums/mutations/actions.dm b/code/datums/mutations/actions.dm
new file mode 100644
index 00000000000..a24db4666d3
--- /dev/null
+++ b/code/datums/mutations/actions.dm
@@ -0,0 +1,116 @@
+/datum/mutation/human/telepathy
+ name = "Telepathy"
+ desc = "A rare mutation that allows the user to telepathically communicate to others."
+ quality = POSITIVE
+ text_gain_indication = "You can hear your own voice echoing in your mind!"
+ text_lose_indication = "You don't hear your mind echo anymore."
+ difficulty = 12
+ power = /obj/effect/proc_holder/spell/targeted/telepathy/genetic
+ instability = 10
+
+/datum/mutation/human/telepathy/on_acquiring(mob/living/carbon/human/owner)
+ . = ..()
+
+/datum/mutation/human/telepathy/on_losing(mob/living/carbon/human/owner)
+ . = ..()
+
+/obj/effect/proc_holder/spell/targeted/telepathy/genetic
+ magic_check = FALSE
+
+/datum/mutation/human/firebreath
+ name = "Fire Breath"
+ desc = "An ancient mutation that gives lizards breath of fire."
+ quality = POSITIVE
+ difficulty = 12
+ locked = TRUE
+ text_gain_indication = "Your throat is burning!"
+ text_lose_indication = "Your throat is cooling down."
+ power = /obj/effect/proc_holder/spell/aimed/firebreath
+ instability = 30
+
+/obj/effect/proc_holder/spell/aimed/firebreath
+ name = "Fire Breath"
+ desc = "You can breathe fire at a target."
+ school = "evocation"
+ charge_max = 600
+ clothes_req = FALSE
+ range = 20
+ projectile_type = /obj/item/projectile/magic/aoe/fireball/firebreath
+ base_icon_state = "fireball"
+ action_icon_state = "fireball0"
+ sound = 'sound/magic/demon_dies.ogg' //horrifying lizard noises
+ active_msg = "You built up heat in your mouth."
+ deactive_msg = "You swallow the flame."
+
+/obj/effect/proc_holder/spell/aimed/firebreath/before_cast(list/targets)
+ . = ..()
+ if(iscarbon(usr))
+ var/mob/living/carbon/C = usr
+ if(C.is_mouth_covered())
+ C.adjust_fire_stacks(2)
+ C.IgniteMob()
+ to_chat(C,"Something in front of your mouth caught fire!")
+ return FALSE
+
+/obj/item/projectile/magic/aoe/fireball/firebreath
+ name = "fire breath"
+ exp_heavy = 0
+ exp_light = 0
+ exp_flash = 0
+ exp_fire= 4
+
+/datum/mutation/human/void
+ name = "Void Magnet"
+ desc = "A rare genome that attracts odd forces not usually observed."
+ quality = MINOR_NEGATIVE //upsides and downsides
+ text_gain_indication = "You feel a heavy, dull force just beyond the walls watching you."
+ instability = 30
+ power = /obj/effect/proc_holder/spell/self/void
+
+/datum/mutation/human/void/on_life()
+ var/obj/effect/proc_holder/spell/self/void/voidpower = power
+ if(voidpower.in_use) //i don't know how rare this is but coughs are 10% on life so in theory this should be okay
+ return
+ if(prob(0.5+((100-dna.stability)/20))) //very rare, but enough to annoy you hopefully. +0.5 probability for every 10 points lost in stability
+ if(voidpower.action)
+ voidpower.action.UpdateButtonIcon()
+ voidpower.invocation_type = "none"
+ voidpower.cast(user=owner)
+
+/obj/effect/proc_holder/spell/self/void
+ name = "Convoke Void" //magic the gathering joke here
+ desc = "A rare genome that attracts odd forces not usually observed. May sometimes pull you in randomly."
+ school = "evocation"
+ clothes_req = FALSE
+ charge_max = 600
+ invocation = "DOOOOOOOOOOOOOOOOOOOOM!!!"
+ invocation_type = "shout"
+ action_icon_state = "void_magnet"
+ var/in_use = FALSE //so it doesnt cast while you are already deep innit
+
+/obj/effect/proc_holder/spell/self/void/can_cast(mob/user = usr)
+ . = ..()
+ if(in_use)
+ return FALSE
+
+/obj/effect/proc_holder/spell/self/void/cast(mob/user = usr)
+ in_use = TRUE
+ user.visible_message("[user] is dragged into the void, leaving a hole in [user.p_their()] place!")
+ var/obj/effect/immortality_talisman/Z = new(get_turf(user))
+ 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)
+
+/obj/effect/proc_holder/spell/self/void/proc/return_to_reality(mob/user, obj/effect/immortality_talisman/Z)
+ in_use = FALSE
+ invocation_type = initial(invocation_type)
+ 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)
\ No newline at end of file
diff --git a/code/datums/mutations/body.dm b/code/datums/mutations/body.dm
index e114a437360..37477f9fe5c 100644
--- a/code/datums/mutations/body.dm
+++ b/code/datums/mutations/body.dm
@@ -3,18 +3,19 @@
//Epilepsy gives a very small chance to have a seizure every life tick, knocking you unconscious.
/datum/mutation/human/epilepsy
name = "Epilepsy"
+ desc = "A genetic defect that sporadically causes seizures."
quality = NEGATIVE
text_gain_indication = "You get a headache."
-/datum/mutation/human/epilepsy/on_life(mob/living/carbon/human/owner)
+/datum/mutation/human/epilepsy/on_life()
if(prob(1) && owner.stat == CONSCIOUS)
owner.visible_message("[owner] starts having a seizure!", "You have a seizure!")
owner.Unconscious(200)
owner.Jitter(1000)
SEND_SIGNAL(owner, COMSIG_ADD_MOOD_EVENT, "epilepsy", /datum/mood_event/epilepsy)
- addtimer(CALLBACK(src, .proc/jitter_less, owner), 90)
+ addtimer(CALLBACK(src, .proc/jitter_less), 90)
-/datum/mutation/human/epilepsy/proc/jitter_less(mob/living/carbon/human/owner)
+/datum/mutation/human/epilepsy/proc/jitter_less()
if(owner)
owner.jitteriness = 10
@@ -22,19 +23,23 @@
//Unstable DNA induces random mutations!
/datum/mutation/human/bad_dna
name = "Unstable DNA"
+ desc = "Strange mutation that causes the holder to randomly mutate."
quality = NEGATIVE
text_gain_indication = "You feel strange."
+ locked = TRUE
/datum/mutation/human/bad_dna/on_acquiring(mob/living/carbon/human/owner)
+ if(..())
+ return
to_chat(owner, text_gain_indication)
var/mob/new_mob
if(prob(95))
if(prob(50))
- new_mob = owner.randmutb()
+ new_mob = owner.easy_randmut(NEGATIVE + MINOR_NEGATIVE)
else
new_mob = owner.randmuti()
else
- new_mob = owner.randmutg()
+ new_mob = owner.easy_randmut(POSITIVE)
if(new_mob && ismob(new_mob))
owner = new_mob
. = owner
@@ -44,10 +49,11 @@
//Cough gives you a chronic cough that causes you to drop items.
/datum/mutation/human/cough
name = "Cough"
+ desc = "A chronic cough."
quality = MINOR_NEGATIVE
text_gain_indication = "You start coughing."
-/datum/mutation/human/cough/on_life(mob/living/carbon/human/owner)
+/datum/mutation/human/cough/on_life()
if(prob(5) && owner.stat == CONSCIOUS)
owner.drop_all_held_items()
owner.emote("cough")
@@ -56,9 +62,11 @@
//Dwarfism shrinks your body and lets you pass tables.
/datum/mutation/human/dwarfism
name = "Dwarfism"
+ desc = "A mutation believed to be the cause of dwarfism."
quality = POSITIVE
- get_chance = 15
- lowest_value = 256 * 12
+ difficulty = 16
+ instability = 5
+ locked = TRUE // Default intert species for now, so locked from regular pool.
/datum/mutation/human/dwarfism/on_acquiring(mob/living/carbon/human/owner)
if(..())
@@ -80,6 +88,7 @@
//Clumsiness has a very large amount of small drawbacks depending on item.
/datum/mutation/human/clumsy
name = "Clumsiness"
+ desc = "A genome that inhibits certain brain functions, causing the holder to appear clumsy. Honk"
quality = MINOR_NEGATIVE
text_gain_indication = "You feel lightheaded."
@@ -97,6 +106,7 @@
//Tourettes causes you to randomly stand in place and shout.
/datum/mutation/human/tourettes
name = "Tourette's Syndrome"
+ desc = "A chronic twitch that forces the user to scream bad words." //definitely needs rewriting
quality = NEGATIVE
text_gain_indication = "You twitch."
@@ -119,6 +129,7 @@
//Deafness makes you deaf.
/datum/mutation/human/deaf
name = "Deafness"
+ desc = "The holder of this genome is completely deaf."
quality = NEGATIVE
text_gain_indication = "You can't seem to hear anything."
@@ -136,8 +147,10 @@
//Monified turns you into a monkey.
/datum/mutation/human/race
name = "Monkified"
+ desc = "A strange genome, believing to be what differentiates monkeys from humans."
quality = NEGATIVE
time_coeff = 2
+ locked = TRUE //Species specific, keep out of actual gene pool
/datum/mutation/human/race/on_acquiring(mob/living/carbon/human/owner)
if(..())
@@ -147,3 +160,57 @@
/datum/mutation/human/race/on_losing(mob/living/carbon/monkey/owner)
if(owner && istype(owner) && owner.stat != DEAD && (owner.dna.mutations.Remove(src)))
. = owner.humanize(TR_KEEPITEMS | TR_KEEPIMPLANTS | TR_KEEPORGANS | TR_KEEPDAMAGE | TR_KEEPVIRUS | TR_KEEPSE)
+
+/datum/mutation/human/glow
+ name = "Glowy"
+ desc = "You permanently emit a light with a random color and intensity."
+ quality = POSITIVE
+ text_gain_indication = "Your skin begins to glow softly."
+ instability = 5
+ var/obj/effect/dummy/luminescent_glow/glowth //shamelessly copied from luminescents
+ var/glow = 1.5
+
+/datum/mutation/human/glow/on_acquiring(mob/living/carbon/human/owner)
+ if(..())
+ return
+ glowth = new(owner)
+ glowth.set_light(glow, glow, dna.features["mcolor"])
+
+/datum/mutation/human/glow/on_losing(mob/living/carbon/human/owner)
+ if(..())
+ return
+ qdel(glowth)
+
+/datum/mutation/human/strong
+ name = "Strength"
+ desc = "The user's muscles slightly expand."
+ quality = POSITIVE
+ text_gain_indication = "You feel strong."
+ difficulty = 16
+
+/datum/mutation/human/fire
+ name = "Fiery Sweat"
+ desc = "The user's skin will randomly combust, but is generally alot more resilient to burning."
+ quality = NEGATIVE
+ text_gain_indication = "You feel hot."
+ text_lose_indication = "You feel a lot cooler."
+ difficulty = 14
+
+/datum/mutation/human/fire/on_life()
+ if(prob(1+(100-dna.stability)/10))
+ owner.adjust_fire_stacks(2)
+ owner.IgniteMob()
+
+/datum/mutation/human/fire/on_acquiring(mob/living/carbon/human/owner)
+ if(..())
+ return
+ owner.physiology.burn_mod *= 0.5
+
+/datum/mutation/human/fire/on_losing(mob/living/carbon/human/owner)
+ if(..())
+ return
+ owner.physiology.burn_mod *= 2
+
+
+
+
diff --git a/code/datums/mutations/chameleon.dm b/code/datums/mutations/chameleon.dm
index da10510970c..d8ee7728a78 100644
--- a/code/datums/mutations/chameleon.dm
+++ b/code/datums/mutations/chameleon.dm
@@ -1,25 +1,26 @@
//Chameleon causes the owner to slowly become transparent when not moving.
/datum/mutation/human/chameleon
name = "Chameleon"
+ desc = "A genome that causes the holder's skin to become transparent over time."
quality = POSITIVE
- get_chance = 20
- lowest_value = 256 * 12
+ difficulty = 16
text_gain_indication = "You feel one with your surroundings."
text_lose_indication = "You feel oddly exposed."
time_coeff = 5
+ instability = 25
/datum/mutation/human/chameleon/on_acquiring(mob/living/carbon/human/owner)
if(..())
return
owner.alpha = CHAMELEON_MUTATION_DEFAULT_TRANSPARENCY
-/datum/mutation/human/chameleon/on_life(mob/living/carbon/human/owner)
+/datum/mutation/human/chameleon/on_life()
owner.alpha = max(0, owner.alpha - 25)
-/datum/mutation/human/chameleon/on_move(mob/living/carbon/human/owner)
+/datum/mutation/human/chameleon/on_move()
owner.alpha = CHAMELEON_MUTATION_DEFAULT_TRANSPARENCY
-/datum/mutation/human/chameleon/on_attack_hand(mob/living/carbon/human/owner, atom/target, proximity)
+/datum/mutation/human/chameleon/on_attack_hand(atom/target, proximity)
if(proximity) //stops tk from breaking chameleon
owner.alpha = CHAMELEON_MUTATION_DEFAULT_TRANSPARENCY
return
diff --git a/code/datums/mutations/cold_resistance.dm b/code/datums/mutations/cold_resistance.dm
index b221ac95cac..58525cecaa8 100644
--- a/code/datums/mutations/cold_resistance.dm
+++ b/code/datums/mutations/cold_resistance.dm
@@ -1,32 +1,30 @@
//Cold Resistance gives your entire body an orange halo, and makes you immune to the effects of vacuum and cold.
-/datum/mutation/human/cold_resistance
- name = "Cold Resistance"
+/datum/mutation/human/space_adaptation
+ name = "Space Adaptation"
+ desc = "A strange mutation that renders the host immune to the vacuum if space. Will still need an oxygen supply."
quality = POSITIVE
- get_chance = 25
- lowest_value = 256 * 12
+ difficulty = 16
text_gain_indication = "Your body feels warm!"
time_coeff = 5
+ instability = 30
-/datum/mutation/human/cold_resistance/New()
+/datum/mutation/human/space_adaptation/New()
..()
- visual_indicators |= mutable_appearance('icons/effects/genetics.dmi', "fire", -MUTATIONS_LAYER)
+ if(!(type in visual_indicators))
+ visual_indicators[type] = list(mutable_appearance('icons/effects/genetics.dmi', "fire", -MUTATIONS_LAYER))
-/datum/mutation/human/cold_resistance/get_visual_indicator(mob/living/carbon/human/owner)
- return visual_indicators[1]
+/datum/mutation/human/space_adaptation/get_visual_indicator()
+ return visual_indicators[type][1]
-/datum/mutation/human/cold_resistance/on_acquiring(mob/living/carbon/human/owner)
+/datum/mutation/human/space_adaptation/on_acquiring(mob/living/carbon/human/owner)
if(..())
return
- owner.add_trait(TRAIT_RESISTCOLD, "cold_resistance")
- owner.add_trait(TRAIT_RESISTLOWPRESSURE, "cold_resistance")
+ owner.add_trait(TRAIT_RESISTCOLD, "space_adaptation")
+ owner.add_trait(TRAIT_RESISTLOWPRESSURE, "space_adaptation")
-/datum/mutation/human/cold_resistance/on_losing(mob/living/carbon/human/owner)
+/datum/mutation/human/space_adaptation/on_losing(mob/living/carbon/human/owner)
if(..())
return
- owner.remove_trait(TRAIT_RESISTCOLD, "cold_resistance")
- owner.remove_trait(TRAIT_RESISTLOWPRESSURE, "cold_resistance")
+ owner.remove_trait(TRAIT_RESISTCOLD, "space_adaptation")
+ owner.remove_trait(TRAIT_RESISTLOWPRESSURE, "space_adaptation")
-/datum/mutation/human/cold_resistance/on_life(mob/living/carbon/human/owner)
- if(owner.getFireLoss())
- if(prob(1))
- owner.heal_bodypart_damage(0,1) //Is this really needed?
diff --git a/code/datums/mutations/combined.dm b/code/datums/mutations/combined.dm
new file mode 100644
index 00000000000..fef0a56dc03
--- /dev/null
+++ b/code/datums/mutations/combined.dm
@@ -0,0 +1,16 @@
+/datum/generecipe
+ var/required = "" //it hurts so bad but initial is not compatible with lists
+ var/result = null
+
+/proc/get_mixed_mutation(mutation1, mutation2)
+ if(!mutation1 || !mutation2)
+ return FALSE
+ if(mutation1 == mutation2) //this could otherwise be bad
+ return FALSE
+ for(var/A in GLOB.mutation_recipes)
+ if(findtext(A, "[mutation1]") && findtext(A, "[mutation2]"))
+ return GLOB.mutation_recipes[A]
+
+/datum/generecipe/hulk
+ required = "/datum/mutation/human/strong; /datum/mutation/human/radioactive"
+ result = HULK
\ No newline at end of file
diff --git a/code/datums/mutations/hulk.dm b/code/datums/mutations/hulk.dm
index 7bcd056fab2..683f5b72593 100644
--- a/code/datums/mutations/hulk.dm
+++ b/code/datums/mutations/hulk.dm
@@ -1,12 +1,15 @@
//Hulk turns your skin green, and allows you to punch through walls.
/datum/mutation/human/hulk
name = "Hulk"
+ desc = "A poorly understood genome that causes the holder's muscles to expand, inhibit speech and gives the person a bad skin condition."
quality = POSITIVE
- get_chance = 15
- lowest_value = 256 * 12
+ locked = TRUE
+ difficulty = 16
text_gain_indication = "Your muscles hurt!"
species_allowed = list("human") //no skeleton/lizard hulk
health_req = 25
+ instability = 40
+ locked = TRUE
/datum/mutation/human/hulk/on_acquiring(mob/living/carbon/human/owner)
if(..())
@@ -16,11 +19,11 @@
owner.update_body_parts()
SEND_SIGNAL(owner, COMSIG_ADD_MOOD_EVENT, "hulk", /datum/mood_event/hulk)
-/datum/mutation/human/hulk/on_attack_hand(mob/living/carbon/human/owner, atom/target, proximity)
+/datum/mutation/human/hulk/on_attack_hand(atom/target, proximity)
if(proximity) //no telekinetic hulk attack
return target.attack_hulk(owner)
-/datum/mutation/human/hulk/on_life(mob/living/carbon/human/owner)
+/datum/mutation/human/hulk/on_life()
if(owner.health < 0)
on_losing(owner)
to_chat(owner, "You suddenly feel very weak.")
diff --git a/code/datums/mutations/radioactive.dm b/code/datums/mutations/radioactive.dm
new file mode 100644
index 00000000000..e772fbbe31b
--- /dev/null
+++ b/code/datums/mutations/radioactive.dm
@@ -0,0 +1,19 @@
+/datum/mutation/human/radioactive
+ name = "Radioactivity"
+ desc = "A volatile mutation that causes the host to sent out deadly beta radiation. This affects both the hosts and their surroundings."
+ quality = NEGATIVE
+ text_gain_indication = "You can feel it in your bones!"
+ time_coeff = 5
+ instability = 5
+ difficulty = 8
+
+/datum/mutation/human/radioactive/on_life()
+ radiation_pulse(owner, 20)
+
+/datum/mutation/human/radioactive/New()
+ ..()
+ if(!(type in visual_indicators))
+ visual_indicators[type] = list(mutable_appearance('icons/effects/genetics.dmi', "radiation", -MUTATIONS_LAYER))
+
+/datum/mutation/human/radioactive/get_visual_indicator()
+ return visual_indicators[type][1]
\ No newline at end of file
diff --git a/code/datums/mutations/sight.dm b/code/datums/mutations/sight.dm
index f16dad20ddd..fba82b1074d 100644
--- a/code/datums/mutations/sight.dm
+++ b/code/datums/mutations/sight.dm
@@ -1,6 +1,7 @@
//Nearsightedness restricts your vision by several tiles.
/datum/mutation/human/nearsight
name = "Near Sightness"
+ desc = "The holder of this mutation has poor eyesight."
quality = MINOR_NEGATIVE
text_gain_indication = "You can't see very well."
@@ -18,6 +19,7 @@
//Blind makes you blind. Who knew?
/datum/mutation/human/blind
name = "Blindness"
+ desc = "Renders the subject completely blind."
quality = NEGATIVE
text_gain_indication = "You can't seem to see anything."
@@ -35,11 +37,12 @@
//X-ray Vision lets you see through walls.
/datum/mutation/human/x_ray
name = "X Ray Vision"
+ desc = "A strange genome that allows the user to see between the spaces of walls." //actual x-ray would mean you'd constantly be blasting rads, wich might be fun for later //hmb
quality = POSITIVE
- get_chance = 25
- lowest_value = 256 * 12
+ difficulty = 18
text_gain_indication = "The walls suddenly disappear!"
time_coeff = 2
+ instability = 25
/datum/mutation/human/x_ray/on_acquiring(mob/living/carbon/human/owner)
if(..())
@@ -58,8 +61,10 @@
//Laser Eyes lets you shoot lasers from your eyes!
/datum/mutation/human/laser_eyes
name = "Laser Eyes"
+ desc = "Reflects concentrated light back from the eyes."
quality = POSITIVE
- dna_block = NON_SCANNABLE
+ locked = TRUE
+ difficulty = 16
text_gain_indication = "You feel pressure building up behind your eyes."
layer_used = FRONT_MUTATIONS_LAYER
limb_req = BODY_ZONE_HEAD
@@ -68,9 +73,9 @@
..()
visual_indicators |= mutable_appearance('icons/effects/genetics.dmi', "lasereyes", -FRONT_MUTATIONS_LAYER)
-/datum/mutation/human/laser_eyes/get_visual_indicator(mob/living/carbon/human/owner)
+/datum/mutation/human/laser_eyes/get_visual_indicator()
return visual_indicators[1]
-/datum/mutation/human/laser_eyes/on_ranged_attack(mob/living/carbon/human/owner, atom/target, mouseparams)
+/datum/mutation/human/laser_eyes/on_ranged_attack(atom/target, mouseparams)
if(owner.a_intent == INTENT_HARM)
owner.LaserEyes(target, mouseparams)
diff --git a/code/datums/mutations/speech.dm b/code/datums/mutations/speech.dm
index f91fc9fd4ea..1239854ccdd 100644
--- a/code/datums/mutations/speech.dm
+++ b/code/datums/mutations/speech.dm
@@ -3,16 +3,18 @@
/datum/mutation/human/nervousness
name = "Nervousness"
+ desc = "Causes the holder to stutter."
quality = MINOR_NEGATIVE
text_gain_indication = "You feel nervous."
-/datum/mutation/human/nervousness/on_life(mob/living/carbon/human/owner)
+/datum/mutation/human/nervousness/on_life()
if(prob(10))
owner.stuttering = max(10, owner.stuttering)
/datum/mutation/human/wacky
name = "Wacky"
+ desc = "Unknown."
quality = MINOR_NEGATIVE
text_gain_indication = "You feel an off sensation in your voicebox."
text_lose_indication = "The off sensation passes."
@@ -23,6 +25,7 @@
/datum/mutation/human/mute
name = "Mute"
+ desc = "Completely inhibits the vocal section of the brain."
quality = NEGATIVE
text_gain_indication = "You feel unable to express yourself at all."
text_lose_indication = "You feel able to speak freely again."
@@ -40,8 +43,8 @@
/datum/mutation/human/smile
name = "Smile"
+ desc = "Causes the user to be in constant mania."
quality = MINOR_NEGATIVE
- dna_block = NON_SCANNABLE
text_gain_indication = "You feel so happy. Nothing can be wrong with anything. :)"
text_lose_indication = "Everything is terrible again. :("
@@ -97,8 +100,8 @@
/datum/mutation/human/unintelligible
name = "Unintelligible"
+ desc = "Partially inhibits the vocal center of the brain, severely distorting speech."
quality = NEGATIVE
- dna_block = NON_SCANNABLE
text_gain_indication = "You can't seem to form any coherent thoughts!"
text_lose_indication = "Your mind feels more clear."
@@ -130,8 +133,8 @@
/datum/mutation/human/swedish
name = "Swedish"
+ desc = "A horrible mutation originating from the distant past. Thought to be eradicated after the incident in 2037."
quality = MINOR_NEGATIVE
- dna_block = NON_SCANNABLE
text_gain_indication = "You feel Swedish, however that works."
text_lose_indication = "The feeling of Swedishness passes."
@@ -149,8 +152,8 @@
/datum/mutation/human/chav
name = "Chav"
+ desc = "Unknown"
quality = MINOR_NEGATIVE
- dna_block = NON_SCANNABLE
text_gain_indication = "Ye feel like a reet prat like, innit?"
text_lose_indication = "You no longer feel like being rude and sassy."
@@ -183,12 +186,13 @@
/datum/mutation/human/elvis
name = "Elvis"
+ desc = "A terrifying mutation named after its 'patient-zero'."
quality = MINOR_NEGATIVE
- dna_block = NON_SCANNABLE
+ locked = TRUE
text_gain_indication = "You feel pretty good, honeydoll."
text_lose_indication = "You feel a little less conversation would be great."
-/datum/mutation/human/elvis/on_life(mob/living/carbon/human/owner)
+/datum/mutation/human/elvis/on_life()
switch(pick(1,2))
if(1)
if(prob(15))
@@ -216,8 +220,9 @@
/datum/mutation/human/stoner
name = "Stoner"
+ desc = "A common mutation that severely decreases intelligence."
quality = NEGATIVE
- dna_block = NON_SCANNABLE
+ locked = TRUE
text_gain_indication = "You feel...totally chill, man!"
text_lose_indication = "You feel like you have a better sense of time."
diff --git a/code/datums/mutations/telekinesis.dm b/code/datums/mutations/telekinesis.dm
index a5b15eacb80..49c39d59f54 100644
--- a/code/datums/mutations/telekinesis.dm
+++ b/code/datums/mutations/telekinesis.dm
@@ -1,18 +1,20 @@
//Telekinesis lets you interact with objects from range, and gives you a light blue halo around your head.
/datum/mutation/human/telekinesis
name = "Telekinesis"
+ desc = "A strange mutation that allows the holder to interact with objects through thought."
quality = POSITIVE
- get_chance = 20
- lowest_value = 256 * 12
+ difficulty = 18
text_gain_indication = "You feel smarter!"
limb_req = BODY_ZONE_HEAD
+ instability = 30
/datum/mutation/human/telekinesis/New()
..()
- visual_indicators |= mutable_appearance('icons/effects/genetics.dmi', "telekinesishead", -MUTATIONS_LAYER)
+ if(!(type in visual_indicators))
+ visual_indicators[type] = list(mutable_appearance('icons/effects/genetics.dmi', "telekinesishead", -MUTATIONS_LAYER))
-/datum/mutation/human/telekinesis/get_visual_indicator(mob/living/carbon/human/owner)
- return visual_indicators[1]
+/datum/mutation/human/telekinesis/get_visual_indicator()
+ return visual_indicators[type][1]
-/datum/mutation/human/telekinesis/on_ranged_attack(mob/living/carbon/human/owner, atom/target)
+/datum/mutation/human/telekinesis/on_ranged_attack(atom/target)
target.attack_tk(owner)
diff --git a/code/datums/weather/weather_types/radiation_storm.dm b/code/datums/weather/weather_types/radiation_storm.dm
index 0906a7e053a..070c19331e0 100644
--- a/code/datums/weather/weather_types/radiation_storm.dm
+++ b/code/datums/weather/weather_types/radiation_storm.dm
@@ -38,9 +38,9 @@
H.randmuti()
if(prob(50))
if(prob(90))
- H.randmutb()
+ H.easy_randmut(NEGATIVE+MINOR_NEGATIVE)
else
- H.randmutg()
+ H.easy_randmut(POSITIVE)
H.domutcheck()
L.rad_act(20)
diff --git a/code/game/machinery/cloning.dm b/code/game/machinery/cloning.dm
index a6770c3a29c..caee9c27249 100644
--- a/code/game/machinery/cloning.dm
+++ b/code/game/machinery/cloning.dm
@@ -88,6 +88,8 @@
name = "cloning data disk"
icon_state = "datadisk0" //Gosh I hope syndies don't mistake them for the nuke disk.
var/list/fields = list()
+ var/list/mutations = list()
+ var/max_mutations = 6
var/read_only = 0 //Well,it's still a floppy disk
//Disk stuff.
@@ -135,7 +137,7 @@
return examine(user)
//Start growing a human clone in the pod!
-/obj/machinery/clonepod/proc/growclone(ckey, clonename, ui, se, mindref, datum/species/mrace, list/features, factions, list/quirks, datum/bank_account/insurance)
+/obj/machinery/clonepod/proc/growclone(ckey, clonename, ui, mutation_index, mindref, datum/species/mrace, list/features, factions, list/quirks, datum/bank_account/insurance)
if(panel_open)
return FALSE
if(mess || attempting)
@@ -170,18 +172,18 @@
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, null, 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)
if(prob(50))
H.gain_trauma_type(BRAIN_TRAUMA_MILD, TRAUMA_RESILIENCE_BASIC)
if(prob(50))
- var/mob/M = H.randmutb()
+ var/mob/M = H.easy_randmut(NEGATIVE+MINOR_NEGATIVE)
if(ismob(M))
H = M
diff --git a/code/game/machinery/computer/cloning.dm b/code/game/machinery/computer/cloning.dm
index c51b69bc9f9..afbe25a269b 100644
--- a/code/game/machinery/computer/cloning.dm
+++ b/code/game/machinery/computer/cloning.dm
@@ -52,7 +52,6 @@
else if(!. && pod.is_operational() && !(pod.occupant || pod.mess) && pod.efficiency > 5)
. = pod
-
/obj/machinery/computer/cloning/proc/updatemodules(findfirstcloner)
src.scanner = findscanner()
if(findfirstcloner && !LAZYLEN(pods))
@@ -125,7 +124,6 @@
dat += "
Cloning Pod Status
"
dat += "[temp]
"
-
// Modules
if (isnull(src.scanner) || !LAZYLEN(pods))
dat += "Modules
"
@@ -159,7 +157,6 @@
else
dat += "Start Scan"
-
var/datum/browser/popup = new(user, "cloning", "Cloning System Control")
popup.set_content(dat)
popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state))
@@ -201,7 +198,6 @@
src.updateUsrDialog()
playsound(src, "terminal_type", 25, 0)
-
src.add_fingerprint(usr)
src.updateUsrDialog()
return
@@ -254,7 +250,7 @@
var/mind
if(mob_occupant.mind)
- mind = "[REF(mob_occupant.mind)]"
+ mind = "[REF(mob_occupant.mind)]"
var/quirks = list()
for(var/V in mob_occupant.roundstart_quirks)
@@ -275,7 +271,7 @@
else if(pod.occupant)
temp = "Cloning cycle already in progress."
playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0)
- else if(pod.growclone(mob_occupant.ckey, mob_occupant.real_name, dna.uni_identity, dna.struc_enzymes, mind, mrace, dna.features, mob_occupant.faction, quirks, has_bank_account))
+ else if(pod.growclone(mob_occupant.ckey, mob_occupant.real_name, dna.uni_identity, dna.mutation_index, mind, mrace, dna.features, mob_occupant.faction, quirks, has_bank_account))
temp = "[mob_occupant.real_name] => Cloning cycle in progress..."
playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, 0)
else
diff --git a/code/game/machinery/computer/dna_console.dm b/code/game/machinery/computer/dna_console.dm
index 2902c463093..a86e989b113 100644
--- a/code/game/machinery/computer/dna_console.dm
+++ b/code/game/machinery/computer/dna_console.dm
@@ -1,14 +1,16 @@
#define INJECTOR_TIMEOUT 100
-#define REJUVENATORS_INJECT 15
-#define REJUVENATORS_MAX 90
#define NUMBER_OF_BUFFERS 3
+#define SCRAMBLE_TIMEOUT 600
+#define JOKER_TIMEOUT 12000 //20 minutes
+#define JOKER_UPGRADE 1800
#define RADIATION_STRENGTH_MAX 15
-#define RADIATION_STRENGTH_MULTIPLIER 1 //larger has a more range
+#define RADIATION_STRENGTH_MULTIPLIER 1 //larger has more range
#define RADIATION_DURATION_MAX 30
#define RADIATION_ACCURACY_MULTIPLIER 3 //larger is less accurate
+
#define RADIATION_IRRADIATION_MULTIPLIER 1 //multiplier for how much radiation a test subject receives
#define SCANNER_ACTION_SE 1
@@ -23,21 +25,30 @@
icon_keyboard = "med_key"
density = TRUE
circuit = /obj/item/circuitboard/computer/scan_consolenew
+
+ use_power = IDLE_POWER_USE
+ idle_power_usage = 10
+ active_power_usage = 400
+ light_color = LIGHT_COLOR_BLUE
+
+ var/datum/techweb/stored_research
+ var/max_storage = 6
+ var/combine
var/radduration = 2
var/radstrength = 1
var/list/buffer[NUMBER_OF_BUFFERS]
+ var/list/stored_mutations = list()
var/injectorready = 0 //world timer cooldown var
+ var/jokerready = 0
+ var/scrambleready = 0
var/current_screen = "mainmenu"
+ var/current_mutation //what block are we inspecting? only used when screen = "info"
+ var/current_storage //what storage block are we looking at?
var/obj/machinery/dna_scannernew/connected = null
var/obj/item/disk/data/diskette = null
var/list/delayed_action = null
- use_power = IDLE_POWER_USE
- idle_power_usage = 10
- active_power_usage = 400
-
- light_color = LIGHT_COLOR_BLUE
/obj/machinery/computer/scan_consolenew/attackby(obj/item/I, mob/user, params)
if (istype(I, /obj/item/disk/data)) //INSERT SOME DISKETTES
@@ -58,12 +69,26 @@
if(!isnull(connected))
break
injectorready = world.time + INJECTOR_TIMEOUT
+ scrambleready = world.time + SCRAMBLE_TIMEOUT
+ jokerready = world.time + JOKER_TIMEOUT
+
+ stored_research = SSresearch.science_tech
+
+/obj/machinery/computer/scan_consolenew/examine(mob/user)
+ ..()
+ if(jokerready < world.time)
+ to_chat(user, "JOKER algorithm available.")
+ else
+ to_chat(user, "JOKER algorithm available in about [round(0.00166666667 * (jokerready - world.time))] minutes.")
/obj/machinery/computer/scan_consolenew/ui_interact(mob/user, last_change)
. = ..()
if(!user)
return
var/datum/browser/popup = new(user, "scannernew", "DNA Modifier Console", 800, 630) // Set up the popup browser window
+ if(user.client)
+ var/datum/asset/simple/assets = get_asset_datum(/datum/asset/simple/genetics)
+ assets.send(user.client)
if(!(in_range(src, user) || issilicon(user)))
popup.close()
return
@@ -88,8 +113,6 @@
occupant_status += ""
occupant_status += "Health:
[viable_occupant.health] %
"
occupant_status += "Radiation Level:
[viable_occupant.radiation/(RAD_MOB_SAFE/100)] %
"
- var/rejuvenators = viable_occupant.reagents.get_reagent_amount("potass_iodide")
- occupant_status += "Rejuvenators:
[rejuvenators] units
"
occupant_status += "Unique Enzymes :
[viable_occupant.dna.unique_enzymes]
"
occupant_status += "Last Operation:
[last_change ? last_change : "----"]
"
else
@@ -147,28 +170,53 @@
buttons += "[connected.locked ? "Unlock" : "Lock"] Scanner"
else
buttons += "Open Scanner Lock Scanner"
- if(viable_occupant)
- buttons += "Inject Rejuvenators"
+ if(viable_occupant && (scrambleready < world.time))
+ buttons += "Scramble DNA"
else
- buttons += "Inject Rejuvenators"
+ buttons += "Scramble DNA"
if(diskette)
- buttons += "Eject Disk"
+ buttons += "Disk"
else
- buttons += "Eject Disk"
- if(current_screen == "buffer")
- buttons += "Radiation Emitter Menu"
+ buttons += "Disk"
+ if(current_screen == "mutations")
+ buttons += "Mutations"
else
- buttons += "Buffer Menu"
+ buttons += "Mutations"
+ if((current_screen == "mainmenu") || !current_screen)
+ buttons += "Genetic Sequencer"
+ else
+ buttons += "Genetic Sequencer"
+ if(current_screen == "ui")
+ buttons += "Unique Identifiers"
+ else
+ buttons += "Unique Identifiers"
switch(current_screen)
if("working")
temp_html += status
temp_html += "System Busy
"
temp_html += "Working ... Please wait ([DisplayTimeText(radduration*10)])"
- if("buffer")
+ if("ui")
temp_html += status
temp_html += buttons
- temp_html += "Buffer Menu
"
+ temp_html += "Unique Identifiers
"
+ temp_html += "-- Output Level ++"
+ temp_html += "
-- Pulse Duration ++"
+ temp_html += "Irradiate Subject
"
+ temp_html += "Unique Identifier:
"
+ var/max_line_len = 7*DNA_BLOCK_SIZE
+ if(viable_occupant)
+ temp_html += "
1
"
+ var/len = length(viable_occupant.dna.uni_identity)
+ for(var/i=1, i<=len, i++)
+ temp_html += "
[copytext(viable_occupant.dna.uni_identity,i,i+1)]"
+ if ((i % max_line_len) == 0)
+ temp_html += "
"
+ if((i % DNA_BLOCK_SIZE) == 0 && i < len)
+ temp_html += "
[(i / DNA_BLOCK_SIZE) + 1]
"
+ else
+ temp_html += "---------"
+ temp_html += "
Buffer Menu
"
if(istype(buffer))
for(var/i=1, i<=buffer.len, i++)
@@ -188,7 +236,6 @@
temp_html += "
Save to Disk"
else
var/ui = buffer_slot["UI"]
- var/se = buffer_slot["SE"]
var/ue = buffer_slot["UE"]
var/name = buffer_slot["name"]
var/label = buffer_slot["label"]
@@ -234,19 +281,6 @@
temp_html += "
UI+UE Injector"
else
temp_html += "
UI+UE Injector"
- if(se)
- temp_html += "
\tSE: [se] "
- if(viable_occupant)
- temp_html += "
Occupant"
- else
- temp_html += "
Occupant"
- temp_html += "
Occupant:Delayed"
- if(injectorready < world.time )
- temp_html += "
Injector"
- else
- temp_html += "
Injector"
- else
- temp_html += "
\tSE: No Data"
if(viable_occupant)
temp_html += "
Save to Buffer"
else
@@ -260,48 +294,180 @@
temp_html += "
Save to Disk"
else
temp_html += "
Save to Disk"
+ if("disk")
+ temp_html += status
+ temp_html += buttons
+ if(diskette)
+ temp_html += "
[diskette.name]
"
+ temp_html += "
Eject Disk"
+ if(LAZYLEN(diskette.mutations))
+ temp_html += "
"
+ for(var/datum/mutation/human/A in diskette.mutations)
+ temp_html += "| [A.name] | "
+ temp_html += "Delete | "
+ if(LAZYLEN(stored_mutations) < max_storage)
+ temp_html += "Import | "
+ else
+ temp_html += " | Import | "
+ temp_html += "
"
+ temp_html += "
"
+ else
+ temp_html += "
Load diskette to start ----------"
+ if("info")
+ if(LAZYLEN(stored_mutations))
+ if(LAZYLEN(stored_mutations) >= current_storage)
+ var/datum/mutation/human/HM = stored_mutations[current_storage]
+ if(HM)
+ temp_html += display_sequence(HM.type, current_storage)
+ else
+ current_screen = "mainmenu"
+ if("mutations")
+ temp_html += status
+ temp_html += buttons
+ temp_html += "
Mutation Storage:
"
+ temp_html += "
"
+ for(var/datum/mutation/human/HM in stored_mutations)
+ var/i = stored_mutations.Find(HM)
+ temp_html += "| [HM.name] | "
+ temp_html += "Export | "
+ temp_html += "Delete | "
+ if(combine == HM.type)
+ temp_html += "Combine |
"
+ else
+ temp_html += "Combine | "
+ temp_html += "
"
else
temp_html += status
temp_html += buttons
- temp_html += "
Radiation Emitter Menu
"
-
- temp_html += "
-- Output Level ++"
- temp_html += "
-- Pulse Duration ++"
-
- temp_html += "
Irradiate Subject
"
- temp_html += "
Unique Identifier:
"
-
- var/max_line_len = 7*DNA_BLOCK_SIZE
+ temp_html += "
Genetic Sequence:
"
if(viable_occupant)
- temp_html += "
1
"
- var/len = length(viable_occupant.dna.uni_identity)
- for(var/i=1, i<=len, i++)
- temp_html += "
[copytext(viable_occupant.dna.uni_identity,i,i+1)]"
- if ((i % max_line_len) == 0)
- temp_html += "
"
- if((i % DNA_BLOCK_SIZE) == 0 && i < len)
- temp_html += "
[(i / DNA_BLOCK_SIZE) + 1]
"
+ if(viable_occupant)
+ for(var/A in get_mutation_list())
+ temp_html += display_inactive_sequence(A)
+ temp_html += "
"
+ else
+ temp_html += "----"
+ if(viable_occupant && (current_mutation in get_mutation_list(viable_occupant)))
+ temp_html += display_sequence(current_mutation)
+ temp_html += "
"
else
- temp_html += "----"
- temp_html += "
"
-
- temp_html += "
Structural Enzymes:
"
- if((i % DNA_BLOCK_SIZE) == 0 && i < len)
- temp_html += "
[(i / DNA_BLOCK_SIZE) + 1]
"
- else
- temp_html += "----"
- temp_html += "
"
+ temp_html += "----------"
popup.set_content(temp_html.Join())
popup.open()
+/obj/machinery/computer/scan_consolenew/proc/display_inactive_sequence(mutation)
+ var/temp_html = ""
+ var/class = "unselected"
+ var/mob/living/carbon/viable_occupant = get_viable_occupant()
+ if(!viable_occupant)
+ return
+
+ var/location = viable_occupant.dna.mutation_index.Find(mutation) //We do this because we dont want people using sysexp or similair tools to just read the mutations.
+
+ if(!location) //Do this only when needed, dont make a list with mutations for every iteration if you dont need to
+ var/list/mutations = get_mutation_list(TRUE)
+ if(mutation in mutations)
+ location = mutations.Find(mutation)
+ if(mutation == current_mutation)
+ class = "selected"
+ if(location > DNA_MUTATION_BLOCKS)
+ temp_html += "

"
+ else if(mutation in stored_research.discovered_mutations)
+ temp_html += "

"
+ else
+ temp_html += "

"
+ return temp_html
+
+/obj/machinery/computer/scan_consolenew/proc/display_sequence(mutation, storage_slot) //Storage slot is for when viewing from the stored mutations
+ var/temp_html = ""
+ if(!mutation)
+ temp_html += "ERR-"
+ return
+ var/mut_name = "Unknown gene"
+ var/mut_desc = "No information available."
+ var/alias
+ var/discovered = FALSE
+ var/active = FALSE
+ var/scrambled = FALSE
+ var/mob/living/carbon/viable_occupant = get_viable_occupant()
+
+ if(viable_occupant)
+ var/datum/mutation/human/HM = viable_occupant.dna.get_mutation(mutation)
+ if(HM)
+ if(HM.scrambled)
+ scrambled = TRUE
+ active = TRUE
+ var/datum/mutation/human/A = get_initialized_mutation(mutation)
+ alias = A.alias
+ if(active && !scrambled)
+ discover(mutation)
+ if(stored_research && (mutation in stored_research.discovered_mutations))
+ mut_name = A.name
+ mut_desc = A.desc
+ discovered = TRUE
+ var/extra
+ if(viable_occupant && !(storage_slot || mutation_in_sequence(mutation, viable_occupant.dna)))
+ extra = TRUE
+ var/datum/mutation/human/HM = get_initialized_mutation(mutation)
+
+ if(discovered && !scrambled)
+ var/mutcolor
+ switch(A.quality)
+ if(POSITIVE)
+ mutcolor = "good"
+ if(MINOR_NEGATIVE)
+ mutcolor = "average"
+ if(NEGATIVE)
+ mutcolor = "bad"
+ temp_html += "
[mut_name] ([alias])"
+ else
+ temp_html += "
[alias]"
+ temp_html += "
[mut_desc]
"
+ temp_html += "
Sequence:
"
+ if(!scrambled)
+ for(var/block in 1 to HM.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,11 +506,14 @@
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 a Text", null) as text|null)
if(num && text)
@@ -358,7 +527,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 +540,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 +553,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 +591,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 +603,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
@@ -480,35 +624,146 @@
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] = get_sequence(mutation) //We only store active mutations and all active mutations have the full sequence.
+ 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 += HM.type
+ I.name = "[HM.name] activator"
+ I.damage_coeff = connected.damage_coeff*4
+ 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 += HM.type
+ I.doitanyway = TRUE
+ I.name = "[HM.name] injector"
+ I.damage_coeff = connected.damage_coeff
+ 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 && (!mutation_in_sequence(current_mutation, viable_occupant.dna) || A.scrambled))
+ viable_occupant.dna.remove_mutation(current_mutation)
+ viable_occupant.dna.update_instability(TRUE)
+ 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] = get_sequence(HM.type)
+ 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()] = get_sequence(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.")
ui_interact(usr,last_change)
/obj/machinery/computer/scan_consolenew/proc/scramble(input,rs,rd)
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 +792,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"]
@@ -567,10 +818,47 @@
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)
+ for(var/datum/mutation/human/A in stored_mutations)
+ if(A.type == mutation)
+ return A
+ var/mob/living/carbon/C = get_viable_occupant()
+ if(C)
+ return C.dna.get_mutation(mutation)
+
+
+/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 stored_mutations[A]
+
+/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 683451945b0..9598717c9f8 100644
--- a/code/game/machinery/exp_cloner.dm
+++ b/code/game/machinery/exp_cloner.dm
@@ -26,9 +26,9 @@
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
@@ -294,6 +294,6 @@
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(null, mob_occupant.real_name, dna.uni_identity, dna.mutation_index, 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/objects/items/circuitboards/machine_circuitboards.dm b/code/game/objects/items/circuitboards/machine_circuitboards.dm
index c914a35aee4..148f029a699 100644
--- a/code/game/objects/items/circuitboards/machine_circuitboards.dm
+++ b/code/game/objects/items/circuitboards/machine_circuitboards.dm
@@ -57,7 +57,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)
@@ -500,8 +500,8 @@
name = "Scanner Gate (Machine Board)"
build_path = /obj/machinery/scanner_gate
req_components = list(
- /obj/item/stock_parts/scanning_module = 3)
-
+ /obj/item/stock_parts/scanning_module = 3)
+
/obj/item/circuitboard/machine/pacman
name = "PACMAN-type Generator (Machine Board)"
build_path = /obj/machinery/power/port_gen/pacman
diff --git a/code/game/objects/items/devices/scanners.dm b/code/game/objects/items/devices/scanners.dm
index 2ef1e3e01ae..b5c5cd88491 100644
--- a/code/game/objects/items/devices/scanners.dm
+++ b/code/game/objects/items/devices/scanners.dm
@@ -6,6 +6,7 @@ T-RAY
HEALTH ANALYZER
GAS ANALYZER
SLIME SCANNER
+NANITE SCANNER
*/
/obj/item/t_scanner
@@ -652,3 +653,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]")
diff --git a/code/game/objects/items/dna_injector.dm b/code/game/objects/items/dna_injector.dm
index d5ac5d9c523..a2390c6e758 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() && !M.has_trait(TRAIT_RADIMMUNE) && !M.has_trait(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(mutation_in_sequence(HM, M.dna))
+ 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,99 @@
/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/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 +308,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 +351,30 @@
/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
+
+/obj/item/dnainjector/activator/inject(mob/living/carbon/M, mob/user)
+ if(M.has_dna() && !M.has_trait(TRAIT_RADIMMUNE) && !M.has_trait(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)
+ if(!M.dna.activate_mutation(mutation) && !doitanyway)
+ log_msg += "(FAILED)"
+ else if(doitanyway)
+ M.dna.add_mutation(mutation, MUT_EXTRA)
+ log_msg += "([mutation])"
+ log_attack("[log_msg] [loc_name(user)]")
+ M.dna.update_instability(TRUE)
+ 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 9cb63ce675c..0888711b67d 100644
--- a/code/modules/antagonists/changeling/powers/chameleon_skin.dm
+++ b/code/modules/antagonists/changeling/powers/chameleon_skin.dm
@@ -11,16 +11,15 @@
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)
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)
+ C.dna.remove_mutation(CHAMELEON)
\ No newline at end of file
diff --git a/code/modules/antagonists/revenant/revenant.dm b/code/modules/antagonists/revenant/revenant.dm
index 9e81c1d8027..8f8f2a309f4 100644
--- a/code/modules/antagonists/revenant/revenant.dm
+++ b/code/modules/antagonists/revenant/revenant.dm
@@ -70,7 +70,7 @@
/mob/living/simple_animal/revenant/Initialize(mapload)
. = ..()
AddSpell(new /obj/effect/proc_holder/spell/targeted/night_vision/revenant(null))
- AddSpell(new /obj/effect/proc_holder/spell/targeted/revenant_transmit(null))
+ AddSpell(new /obj/effect/proc_holder/spell/targeted/telepathy/revenant(null))
AddSpell(new /obj/effect/proc_holder/spell/aoe_turf/revenant/defile(null))
AddSpell(new /obj/effect/proc_holder/spell/aoe_turf/revenant/overload(null))
AddSpell(new /obj/effect/proc_holder/spell/aoe_turf/revenant/blight(null))
diff --git a/code/modules/antagonists/revenant/revenant_abilities.dm b/code/modules/antagonists/revenant/revenant_abilities.dm
index 4e73c83ccef..51d48697276 100644
--- a/code/modules/antagonists/revenant/revenant_abilities.dm
+++ b/code/modules/antagonists/revenant/revenant_abilities.dm
@@ -106,36 +106,14 @@
action_background_icon_state = "bg_revenant"
//Transmit: the revemant's only direct way to communicate. Sends a single message silently to a single mob
-/obj/effect/proc_holder/spell/targeted/revenant_transmit
- name = "Transmit"
- desc = "Telepathically transmits a message to the target."
+/obj/effect/proc_holder/spell/targeted/telepathy/revenant
+ name = "Revenant Transmit"
panel = "Revenant Abilities"
- charge_max = 0
- clothes_req = 0
- range = 7
- include_user = 0
action_icon = 'icons/mob/actions/actions_revenant.dmi'
action_icon_state = "r_transmit"
action_background_icon_state = "bg_revenant"
-
-/obj/effect/proc_holder/spell/targeted/revenant_transmit/cast(list/targets, mob/living/simple_animal/revenant/user = usr)
- for(var/mob/living/M in targets)
- var/msg = stripped_input(usr, "What do you wish to tell [M]?", null, "")
- if(!msg)
- charge_counter = charge_max
- return
- log_directed_talk(user, M, msg, LOG_SAY, "revenant whisper")
- to_chat(user, "You transmit to [M]: [msg]")
- if(!M.anti_magic_check(FALSE, TRUE)) //hear no evil
- to_chat(M, "You hear something behind you talking... [msg]")
- for(var/ded in GLOB.dead_mob_list)
- if(!isobserver(ded))
- continue
- var/follow_rev = FOLLOW_LINK(ded, user)
- var/follow_whispee = FOLLOW_LINK(ded, M)
- to_chat(ded, "[follow_rev] [user] Revenant Transmit: \"[msg]\" to [follow_whispee] [M]")
-
-
+ notice = "revennotice"
+ boldnotice = "revenboldnotice"
/obj/effect/proc_holder/spell/aoe_turf/revenant
clothes_req = 0
diff --git a/code/modules/antagonists/wishgranter/wishgranter.dm b/code/modules/antagonists/wishgranter/wishgranter.dm
index 21cab26d1e0..67359b28d3c 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 beb6804b6e6..964d546be67 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 7c08a3332ce..efa0ee02019 100644
--- a/code/modules/client/asset_cache.dm
+++ b/code/modules/client/asset_cache.dm
@@ -632,3 +632,10 @@ GLOBAL_LIST_EMPTY(asset_datums)
Insert(initial(D.id), I)
return ..()
+
+/datum/asset/simple/genetics
+ assets = list(
+ "dna_discovered.png" = 'html/dna_discovered.png',
+ "dna_undiscovered.png" = 'html/dna_undiscovered.png',
+ "dna_extra.png" = 'html/dna_extra.png'
+)
diff --git a/code/modules/events/disease_outbreak.dm b/code/modules/events/disease_outbreak.dm
index f09c0481f61..c03b81c031f 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/medical.dm b/code/modules/jobs/job_types/medical.dm
index e9b39bbc8a5..14140941729 100644
--- a/code/modules/jobs/job_types/medical.dm
+++ b/code/modules/jobs/job_types/medical.dm
@@ -168,6 +168,7 @@ Geneticist
shoes = /obj/item/clothing/shoes/sneakers/white
suit = /obj/item/clothing/suit/toggle/labcoat/genetics
suit_store = /obj/item/flashlight/pen
+ l_pocket = /obj/item/sequence_scanner
backpack = /obj/item/storage/backpack/genetics
satchel = /obj/item/storage/backpack/satchel/gen
diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm
index 7a11cb7e5f6..251b3b1878d 100644
--- a/code/modules/mob/living/carbon/carbon.dm
+++ b/code/modules/mob/living/carbon/carbon.dm
@@ -921,7 +921,7 @@
/mob/living/carbon/can_resist()
return bodyparts.len > 2 && ..()
-
+
/mob/living/carbon/proc/hypnosis_vulnerable()
if(has_trait(TRAIT_MINDSHIELD))
return FALSE
diff --git a/code/modules/mob/living/carbon/human/human_movement.dm b/code/modules/mob/living/carbon/human/human_movement.dm
index 89949132ca1..eb64b162ab8 100644
--- a/code/modules/mob/living/carbon/human/human_movement.dm
+++ b/code/modules/mob/living/carbon/human/human_movement.dm
@@ -44,7 +44,7 @@
/mob/living/carbon/human/Move(NewLoc, direct)
. = ..()
for(var/datum/mutation/human/HM in dna.mutations)
- HM.on_move(src, NewLoc)
+ HM.on_move(NewLoc)
if(shoes)
if(mobility_flags & MOBILITY_STAND)
diff --git a/code/modules/mob/living/carbon/human/life.dm b/code/modules/mob/living/carbon/human/life.dm
index 94e8121ad58..b1e6136f808 100644
--- a/code/modules/mob/living/carbon/human/life.dm
+++ b/code/modules/mob/living/carbon/human/life.dm
@@ -308,7 +308,7 @@
/mob/living/carbon/human/proc/handle_active_genes()
for(var/datum/mutation/human/HM in dna.mutations)
- HM.on_life(src)
+ HM.on_life()
/mob/living/carbon/human/proc/handle_heart()
var/we_breath = !has_trait(TRAIT_NOBREATH, SPECIES_TRAIT)
diff --git a/code/modules/mob/living/carbon/human/species.dm b/code/modules/mob/living/carbon/human/species.dm
index c381c4f3770..8c192d36f98 100644
--- a/code/modules/mob/living/carbon/human/species.dm
+++ b/code/modules/mob/living/carbon/human/species.dm
@@ -45,6 +45,7 @@ GLOBAL_LIST_EMPTY(roundstart_races)
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 //special mutation that can be found in the genepool. Dont leave empty or changing species will be a headache
var/deathsound //used to set the mobs deathsound on species change
// species-only traits. Can be found in DNA.dm
@@ -301,6 +302,10 @@ GLOBAL_LIST_EMPTY(roundstart_races)
C.Digitigrade_Leg_Swap(TRUE)
for(var/X in inherent_traits)
C.remove_trait(X, SPECIES_TRAIT)
+ if((inert_mutation != new_species.inert_mutation) && LAZYLEN(C.dna.mutation_index))
+ C.dna.remove_mutation(inert_mutation)
+ C.dna.mutation_index[C.dna.mutation_index.Find(inert_mutation)] = new_species.inert_mutation
+ C.dna.mutation_index[new_species.inert_mutation] = create_sequence(new_species.inert_mutation)
C.remove_movespeed_modifier(MOVESPEED_ID_SPECIES)
SEND_SIGNAL(C, COMSIG_SPECIES_LOSS, src)
@@ -1045,7 +1050,7 @@ GLOBAL_LIST_EMPTY(roundstart_races)
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 4911b79a09b..9c7482c1020 100644
--- a/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm
+++ b/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm
@@ -20,6 +20,7 @@
exotic_bloodtype = "L"
disliked_food = GRAIN | DAIRY
liked_food = GROSS | MEAT
+ inert_mutation = FIREBREATH
deathsound = 'sound/voice/lizard/deathsound.ogg'
/datum/species/lizard/after_equip_job(datum/job/J, mob/living/carbon/human/H)
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 d140f761dc7..33f3e2c604c 100644
--- a/code/modules/mob/living/carbon/human/species_types/podpeople.dm
+++ b/code/modules/mob/living/carbon/human/species_types/podpeople.dm
@@ -55,9 +55,9 @@
H.Paralyze(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 13a9693d45d..c5046329189 100644
--- a/code/modules/mob/living/carbon/life.dm
+++ b/code/modules/mob/living/carbon/life.dm
@@ -372,7 +372,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)
@@ -395,9 +394,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 2641ed064f0..358144a7df1 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 b346048561f..6609c8f3ac3 100644
--- a/code/modules/mob/transform_procs.dm
+++ b/code/modules/mob/transform_procs.dm
@@ -49,9 +49,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.set_suicide(suiciding)
@@ -205,9 +204,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/toxin_reagents.dm b/code/modules/reagents/chemistry/reagents/toxin_reagents.dm
index 80fda51b48f..94918db936d 100644
--- a/code/modules/reagents/chemistry/reagents/toxin_reagents.dm
+++ b/code/modules/reagents/chemistry/reagents/toxin_reagents.dm
@@ -42,9 +42,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 8263c0ba05c..97b0cf245ce 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 1603038eada..79d79e1d2a5 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 491237781ce..71050353425 100644
--- a/code/modules/research/techweb/all_nodes.dm
+++ b/code/modules/research/techweb/all_nodes.dm
@@ -56,7 +56,7 @@
display_name = "Biological Technology"
description = "What makes us tick." //the MC, silly!
prereq_ids = list("base")
- design_ids = list("chem_heater", "chem_master", "chem_dispenser", "sleeper", "vr_sleeper", "pandemic", "defibrillator", "defibmount", "operating", "soda_dispenser", "beer_dispenser", "healthanalyzer", "medspray")
+ design_ids = list("chem_heater", "chem_master", "chem_dispenser", "sleeper", "vr_sleeper", "pandemic", "defibrillator", "defibmount", "operating", "soda_dispenser", "beer_dispenser", "healthanalyzer", "medspray","genescanner")
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
export_price = 5000
diff --git a/code/modules/spells/spell_types/genetic.dm b/code/modules/spells/spell_types/genetic.dm
index 37fca86f46b..ae92995a9d0 100644
--- a/code/modules/spells/spell_types/genetic.dm
+++ b/code/modules/spells/spell_types/genetic.dm
@@ -4,7 +4,7 @@
var/list/active_on = list()
var/list/traits = list() //disabilities
- var/list/mutations = list() //mutation strings
+ var/list/mutations = list() //mutation defines
var/duration = 100 //deciseconds
/*
Disabilities
diff --git a/code/modules/spells/spell_types/telepathy.dm b/code/modules/spells/spell_types/telepathy.dm
new file mode 100644
index 00000000000..db7e284ea29
--- /dev/null
+++ b/code/modules/spells/spell_types/telepathy.dm
@@ -0,0 +1,30 @@
+/obj/effect/proc_holder/spell/targeted/telepathy
+ name = "Telepathy"
+ desc = "Telepathically transmits a message to the target."
+ charge_max = 0
+ clothes_req = 0
+ range = 7
+ include_user = 0
+ action_icon = 'icons/mob/actions/actions_revenant.dmi'
+ action_icon_state = "r_transmit"
+ action_background_icon_state = "bg_spell"
+ var/notice = "notice"
+ var/boldnotice = "boldnotice"
+ var/magic_check = TRUE
+
+/obj/effect/proc_holder/spell/targeted/telepathy/cast(list/targets, mob/living/simple_animal/revenant/user = usr)
+ for(var/mob/living/M in targets)
+ var/msg = stripped_input(usr, "What do you wish to tell [M]?", null, "")
+ if(!msg)
+ charge_counter = charge_max
+ return
+ log_directed_talk(user, M, msg, LOG_SAY, "[name]")
+ to_chat(user, "You transmit to [M]: [msg]")
+ if(!magic_check || !M.anti_magic_check(FALSE, TRUE)) //hear no evil
+ to_chat(M, "You hear something behind you talking... [msg]")
+ for(var/ded in GLOB.dead_mob_list)
+ if(!isobserver(ded))
+ continue
+ var/follow_rev = FOLLOW_LINK(ded, user)
+ var/follow_whispee = FOLLOW_LINK(ded, M)
+ to_chat(ded, "[follow_rev] [user] [name]: \"[msg]\" to [follow_whispee] [M]")
\ No newline at end of file
diff --git a/code/modules/surgery/bodyparts/dismemberment.dm b/code/modules/surgery/bodyparts/dismemberment.dm
index 893c7e17435..23be73e7158 100644
--- a/code/modules/surgery/bodyparts/dismemberment.dm
+++ b/code/modules/surgery/bodyparts/dismemberment.dm
@@ -110,7 +110,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/code/modules/tgs/core/tgs_version.dm b/code/modules/tgs/core/tgs_version.dm
index 6b3aff677d5..b97745611fc 100644
--- a/code/modules/tgs/core/tgs_version.dm
+++ b/code/modules/tgs/core/tgs_version.dm
@@ -1,22 +1,22 @@
-/datum/tgs_version/New(raw_parameter)
- src.raw_parameter = raw_parameter
- deprefixed_parameter = replacetext(raw_parameter, "/tg/station 13 Server v", "")
- var/list/version_bits = splittext(deprefixed_parameter, ".")
-
- suite = text2num(version_bits[1])
- if(version_bits.len > 1)
- major = text2num(version_bits[2])
- if(version_bits.len > 2)
- minor = text2num(version_bits[3])
- if(version_bits.len == 4)
- patch = text2num(version_bits[4])
-
-/datum/tgs_version/proc/Valid(allow_wildcards = FALSE)
- if(suite == null)
- return FALSE
- if(allow_wildcards)
- return TRUE
- return !Wildcard()
-
-/datum/tgs_version/Wildcard()
- return major == null || minor == null || patch == null
+/datum/tgs_version/New(raw_parameter)
+ src.raw_parameter = raw_parameter
+ deprefixed_parameter = replacetext(raw_parameter, "/tg/station 13 Server v", "")
+ var/list/version_bits = splittext(deprefixed_parameter, ".")
+
+ suite = text2num(version_bits[1])
+ if(version_bits.len > 1)
+ major = text2num(version_bits[2])
+ if(version_bits.len > 2)
+ minor = text2num(version_bits[3])
+ if(version_bits.len == 4)
+ patch = text2num(version_bits[4])
+
+/datum/tgs_version/proc/Valid(allow_wildcards = FALSE)
+ if(suite == null)
+ return FALSE
+ if(allow_wildcards)
+ return TRUE
+ return !Wildcard()
+
+/datum/tgs_version/Wildcard()
+ return major == null || minor == null || patch == null
diff --git a/code/modules/tgs/v4/commands.dm b/code/modules/tgs/v4/commands.dm
index 1e19119ecc5..1d9951bc041 100644
--- a/code/modules/tgs/v4/commands.dm
+++ b/code/modules/tgs/v4/commands.dm
@@ -1,69 +1,69 @@
-/datum/tgs_api/v4/proc/ListCustomCommands()
- var/results = list()
- custom_commands = list()
- for(var/I in typesof(/datum/tgs_chat_command) - /datum/tgs_chat_command)
- var/datum/tgs_chat_command/stc = new I
- var/command_name = stc.name
- if(!command_name || findtext(command_name, " ") || findtext(command_name, "'") || findtext(command_name, "\""))
- TGS_ERROR_LOG("Custom command [command_name] ([I]) can't be used as it is empty or contains illegal characters!")
- continue
-
- if(results[command_name])
- var/datum/other = custom_commands[command_name]
- TGS_ERROR_LOG("Custom commands [other.type] and [I] have the same name (\"[command_name]\"), only [other.type] will be available!")
- continue
- results += list(list("name" = command_name, "help_text" = stc.help_text, "admin_only" = stc.admin_only))
- custom_commands[command_name] = stc
-
- var/commands_file = chat_commands_json_path
- if(!commands_file)
- return
- text2file(json_encode(results), commands_file)
-
-/datum/tgs_api/v4/proc/HandleCustomCommand(command_json)
- var/list/data = json_decode(command_json)
- var/command = data["command"]
- var/user = data["user"]
- var/params = data["params"]
-
- var/datum/tgs_chat_user/u = new
- u.id = user["id"]
- u.friendly_name = user["friendlyName"]
- u.mention = user["mention"]
- u.channel = DecodeChannel(user["channel"])
-
- var/datum/tgs_chat_command/sc = custom_commands[command]
- if(sc)
- var/result = sc.Run(u, params)
- if(result == null)
- result = ""
- return result
- return "Unknown command: [command]!"
-
-/*
-
-The MIT License
-
-Copyright (c) 2017 Jordan Brown
-
-Permission is hereby granted, free of charge,
-to any person obtaining a copy of this software and
-associated documentation files (the "Software"), to
-deal in the Software without restriction, including
-without limitation the rights to use, copy, modify,
-merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom
-the Software is furnished to do so,
-subject to the following conditions:
-
-The above copyright notice and this permission notice
-shall be included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
-OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
-ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
-TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
-SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-*/
+/datum/tgs_api/v4/proc/ListCustomCommands()
+ var/results = list()
+ custom_commands = list()
+ for(var/I in typesof(/datum/tgs_chat_command) - /datum/tgs_chat_command)
+ var/datum/tgs_chat_command/stc = new I
+ var/command_name = stc.name
+ if(!command_name || findtext(command_name, " ") || findtext(command_name, "'") || findtext(command_name, "\""))
+ TGS_ERROR_LOG("Custom command [command_name] ([I]) can't be used as it is empty or contains illegal characters!")
+ continue
+
+ if(results[command_name])
+ var/datum/other = custom_commands[command_name]
+ TGS_ERROR_LOG("Custom commands [other.type] and [I] have the same name (\"[command_name]\"), only [other.type] will be available!")
+ continue
+ results += list(list("name" = command_name, "help_text" = stc.help_text, "admin_only" = stc.admin_only))
+ custom_commands[command_name] = stc
+
+ var/commands_file = chat_commands_json_path
+ if(!commands_file)
+ return
+ text2file(json_encode(results), commands_file)
+
+/datum/tgs_api/v4/proc/HandleCustomCommand(command_json)
+ var/list/data = json_decode(command_json)
+ var/command = data["command"]
+ var/user = data["user"]
+ var/params = data["params"]
+
+ var/datum/tgs_chat_user/u = new
+ u.id = user["id"]
+ u.friendly_name = user["friendlyName"]
+ u.mention = user["mention"]
+ u.channel = DecodeChannel(user["channel"])
+
+ var/datum/tgs_chat_command/sc = custom_commands[command]
+ if(sc)
+ var/result = sc.Run(u, params)
+ if(result == null)
+ result = ""
+ return result
+ return "Unknown command: [command]!"
+
+/*
+
+The MIT License
+
+Copyright (c) 2017 Jordan Brown
+
+Permission is hereby granted, free of charge,
+to any person obtaining a copy of this software and
+associated documentation files (the "Software"), to
+deal in the Software without restriction, including
+without limitation the rights to use, copy, modify,
+merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom
+the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice
+shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
diff --git a/code/modules/unit_tests/component_tests.dm b/code/modules/unit_tests/component_tests.dm
index 7cf465a3314..409d7f43222 100644
--- a/code/modules/unit_tests/component_tests.dm
+++ b/code/modules/unit_tests/component_tests.dm
@@ -1,12 +1,12 @@
-/datum/unit_test/component_duping/Run()
- var/list/bad_dms = list()
- var/list/bad_dts = list()
- for(var/t in typesof(/datum/component))
- var/datum/component/comp = t
- if(!isnum(initial(comp.dupe_mode)))
- bad_dms += t
- var/dupe_type = initial(comp.dupe_type)
- if(dupe_type && !ispath(dupe_type))
- bad_dts += t
- if(length(bad_dms) || length(bad_dts))
+/datum/unit_test/component_duping/Run()
+ var/list/bad_dms = list()
+ var/list/bad_dts = list()
+ for(var/t in typesof(/datum/component))
+ var/datum/component/comp = t
+ if(!isnum(initial(comp.dupe_mode)))
+ bad_dms += t
+ var/dupe_type = initial(comp.dupe_type)
+ if(dupe_type && !ispath(dupe_type))
+ bad_dts += t
+ if(length(bad_dms) || length(bad_dts))
Fail("Components with invalid dupe modes: ([bad_dms.Join(",")]) ||| Components with invalid dupe types: ([bad_dts.Join(",")])")
\ No newline at end of file
diff --git a/html/browser/common.css b/html/browser/common.css
index 0883a2df92f..2e84e022f02 100644
--- a/html/browser/common.css
+++ b/html/browser/common.css
@@ -76,7 +76,6 @@ a.icon img, .linkOn.icon img
width: 18px;
height: 18px;
}
-
ul
{
padding: 4px 0 0 10px;
diff --git a/html/browser/scannernew.css b/html/browser/scannernew.css
index c540ab8f3ce..9e9f8ce1f48 100644
--- a/html/browser/scannernew.css
+++ b/html/browser/scannernew.css
@@ -9,10 +9,29 @@
margin: 2px 2px 0 10px;
text-align: center;
}
-
.dnaBlock
{
font-family: Fixed, monospace;
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.png b/html/dna_discovered.png
new file mode 100644
index 00000000000..86e569fc34c
Binary files /dev/null and b/html/dna_discovered.png differ
diff --git a/html/dna_extra.png b/html/dna_extra.png
new file mode 100644
index 00000000000..2fda1339e15
Binary files /dev/null and b/html/dna_extra.png differ
diff --git a/html/dna_undiscovered.png b/html/dna_undiscovered.png
new file mode 100644
index 00000000000..7184f097ac3
Binary files /dev/null and b/html/dna_undiscovered.png differ
diff --git a/icons/effects/genetics.dmi b/icons/effects/genetics.dmi
index a9f5f433d5b..7cb002d909e 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 00000000000..72bd87f97eb
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 dd8a6fb8690..44b47f5fa00 100644
Binary files a/icons/mob/actions/actions_spells.dmi and b/icons/mob/actions/actions_spells.dmi differ
diff --git a/icons/obj/device.dmi b/icons/obj/device.dmi
index 71695f770bc..41f326111b4 100644
Binary files a/icons/obj/device.dmi and b/icons/obj/device.dmi differ
diff --git a/tgstation.dme b/tgstation.dme
index 485135fd207..e35878932c9 100755
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -111,6 +111,7 @@
#include "code\__HELPERS\AStar.dm"
#include "code\__HELPERS\cmp.dm"
#include "code\__HELPERS\dates.dm"
+#include "code\__HELPERS\dna.dm"
#include "code\__HELPERS\files.dm"
#include "code\__HELPERS\game.dm"
#include "code\__HELPERS\global_lists.dm"
@@ -458,10 +459,13 @@
#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\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"
@@ -2585,6 +2589,7 @@
#include "code\modules\spells\spell_types\shapeshift.dm"
#include "code\modules\spells\spell_types\spacetime_distortion.dm"
#include "code\modules\spells\spell_types\summonitem.dm"
+#include "code\modules\spells\spell_types\telepathy.dm"
#include "code\modules\spells\spell_types\the_traps.dm"
#include "code\modules\spells\spell_types\touch_attacks.dm"
#include "code\modules\spells\spell_types\trigger.dm"