From 29ecfa47bf3c67d2ff3f04f0d6c0ed4402600f88 Mon Sep 17 00:00:00 2001 From: Time-Green Date: Sun, 9 Dec 2018 11:26:04 +0100 Subject: [PATCH] [READY] Goon Genetics (#41258) GENETICS The random hexadecimal rng game has been replaced with gene sequencing from goon. Adds mutation activators and mutators You can now store mutations Everyone now has their own set of unique mutations Limited mutations per person to 8 (including one always being monkey) Adds race specific mutations (See fire breathing for lizads) You can inspect discovered mutations, undiscovered mutations use an alias to recognize them by Adds a sequence analyzer. Can be used to scan someones genes sequence. Useful for determing what mutations they can safely have and or collecting data for very difficult sequences Adds mutation combining. It's currently only RADIOACTIVE + STRONG = HULK (So yes you will now need 2 mutations for to get hulk) Adds several other mutations. Telepathy, firebreath, glowy, radioactive and strength cl Time-Green add: Goon genetics! add: More mutations! Fire breath for lizards! Radioactive! Telepathy! Glowy! Strength, though its cosmetic and should be combined with radioactivity instead! Fiery sweat! add: Adds void magnet mutation by @tralezab ! /cl --- code/__DEFINES/DNA.dm | 71 ++- code/__HELPERS/dna.dm | 8 + code/_globalvars/genetics.dm | 8 +- code/_onclick/other_mobs.dm | 4 +- code/controllers/subsystem/atoms.dm | 23 +- code/datums/brain_damage/mild.dm | 2 +- code/datums/components/spill.dm | 112 ++-- code/datums/datacore.dm | 1 - .../diseases/advance/symptoms/genetics.dm | 6 +- code/datums/dna.dm | 270 +++++++-- code/datums/footstep.dm | 196 +++---- code/datums/mutations.dm | 115 ++-- code/datums/mutations/actions.dm | 116 ++++ code/datums/mutations/body.dm | 83 ++- code/datums/mutations/chameleon.dm | 11 +- code/datums/mutations/cold_resistance.dm | 34 +- code/datums/mutations/combined.dm | 16 + code/datums/mutations/hulk.dm | 11 +- code/datums/mutations/radioactive.dm | 19 + code/datums/mutations/sight.dm | 15 +- code/datums/mutations/speech.dm | 21 +- code/datums/mutations/telekinesis.dm | 14 +- .../weather/weather_types/radiation_storm.dm | 4 +- code/game/machinery/cloning.dm | 10 +- code/game/machinery/computer/cloning.dm | 8 +- code/game/machinery/computer/dna_console.dm | 532 ++++++++++++++---- code/game/machinery/exp_cloner.dm | 6 +- .../circuitboards/machine_circuitboards.dm | 6 +- code/game/objects/items/devices/scanners.dm | 60 ++ code/game/objects/items/dna_injector.dm | 161 +++--- .../changeling/powers/chameleon_skin.dm | 13 +- code/modules/antagonists/revenant/revenant.dm | 2 +- .../revenant/revenant_abilities.dm | 30 +- .../antagonists/wishgranter/wishgranter.dm | 2 +- .../awaymissions/mission_code/wildwest.dm | 2 +- code/modules/client/asset_cache.dm | 7 + code/modules/events/disease_outbreak.dm | 2 +- code/modules/jobs/job_types/medical.dm | 1 + code/modules/mob/living/carbon/carbon.dm | 2 +- .../mob/living/carbon/human/human_movement.dm | 2 +- code/modules/mob/living/carbon/human/life.dm | 2 +- .../mob/living/carbon/human/species.dm | 7 +- .../human/species_types/lizardpeople.dm | 1 + .../carbon/human/species_types/podpeople.dm | 4 +- code/modules/mob/living/carbon/life.dm | 7 +- code/modules/mob/living/carbon/monkey/life.dm | 2 +- code/modules/mob/transform_procs.dm | 10 +- .../chemistry/reagents/toxin_reagents.dm | 4 +- .../research/designs/medical_designs.dm | 10 + code/modules/research/techweb/_techweb.dm | 1 + code/modules/research/techweb/all_nodes.dm | 2 +- code/modules/spells/spell_types/genetic.dm | 2 +- code/modules/spells/spell_types/telepathy.dm | 30 + .../surgery/bodyparts/dismemberment.dm | 2 +- code/modules/tgs/core/tgs_version.dm | 44 +- code/modules/tgs/v4/commands.dm | 138 ++--- code/modules/unit_tests/component_tests.dm | 22 +- html/browser/common.css | 1 - html/browser/scannernew.css | 23 +- html/dna_discovered.png | Bin 0 -> 1715 bytes html/dna_extra.png | Bin 0 -> 1726 bytes html/dna_undiscovered.png | Bin 0 -> 1758 bytes icons/effects/genetics.dmi | Bin 20768 -> 22607 bytes icons/mob/actions/actions_genetic.dmi | Bin 0 -> 413 bytes icons/mob/actions/actions_spells.dmi | Bin 21434 -> 22440 bytes icons/obj/device.dmi | Bin 47436 -> 47768 bytes tgstation.dme | 5 + 67 files changed, 1574 insertions(+), 749 deletions(-) create mode 100644 code/__HELPERS/dna.dm create mode 100644 code/datums/mutations/actions.dm create mode 100644 code/datums/mutations/combined.dm create mode 100644 code/datums/mutations/radioactive.dm create mode 100644 code/modules/spells/spell_types/telepathy.dm create mode 100644 html/dna_discovered.png create mode 100644 html/dna_extra.png create mode 100644 html/dna_undiscovered.png create mode 100644 icons/mob/actions/actions_genetic.dmi 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 += "" + temp_html += "" + if(LAZYLEN(stored_mutations) < max_storage) + temp_html += "" + else + temp_html += "" + temp_html += "" + temp_html += "
[A.name]DeleteImportImport
" + 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 += "" + temp_html += "" + temp_html += "" + if(combine == HM.type) + temp_html += "" + else + temp_html += "" + temp_html += "
[HM.name]ExportDeleteCombine
Combine
" 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(viable_occupant) - temp_html += "
1
" - var/len = length(viable_occupant.dna.struc_enzymes) - for(var/i=1, i<=len, i++) - temp_html += "[copytext(viable_occupant.dna.struc_enzymes,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 += "
" + 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 += "Extra Mutation" + else if(mutation in stored_research.discovered_mutations) + temp_html += "Discovered Mutation" + else + temp_html += "Undiscovered" + 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 0000000000000000000000000000000000000000..86e569fc34c8efc197d048e193d78e4e49008efc GIT binary patch literal 1715 zcmV;k22A;hP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!~g&e!~vBn4jTXf21rRnK~!i%?U`>( zl~o+af0qp}e{34FNF#H_J9TANTW|cgNc%HMG*ItwND4aBg!RW1B*_$-qZblGMIja_ zq7g4#maH~jG+tc+D>XIMOA*GEDE>RbI1(}SJ`geWKH%zkVS#t#`VBUSdMt@z^EOjPT`e0&aE9xT zq~x$0Rm5z2Xr(_QdEkSc)ZkgeXCfHUxHX?@I&afxOLa8mWx89kk9}x{%k8GMvy%D9 zoGTIL=#(Z8Dv$=#vEFCvVqV1Gr-Wk zO?l+Kb0=V`8LHXYb7%6WP&RBjOf_}C(r+C@d?rB7+DGYl{RTdA288!-FA6cz44txN zr1_CVx2-@x1`mArF*6isHUCG>UF1(haeInKqrl2Ul?fA~DF3bLRQcJPd`2ILzWeEZ zZ8K%$?6RX5z}zg)R7=fA=PhE|WVB4JZof?4p@5J}*~L_Tb+)BAA*ihU8r{1$!scLX zoNE_**2PFPFp9#ah6Wn&Me@+`lYGX{rOZMqzx9l^nKEXXd`S~YSqGHx%u{oyq z+zd-Yh33ibkJ#@eDO30-e$5CBmHzx~L54;zm`4v@Zl~3kF6c!nKoorUw1>*3Z=wFE zcs}bFv@QQ}N>5MYGokbj74j$Y{p}2`5>RuklSaC__{cAMVpRn*P6z01U9*=y>0M-7 zFGycqNe8QM@)0q^1#0%Vm%c`EWhJB9v`yV!@Hy?;alt1rj?WM+ef1$~ZP~&{<_vg? zY?T1i(Ej}Q`6yJhE>W?&sJr)1KGN4KkadfV_So_aUy0et<^Z&LZeHOdKibFClCrBo zhAzJSy|zHL1YpB315qFw3Lx69p+Gitad!)CN<7O)dSDR*h+20{0iD|a1E29{4;EWR z%Tz>Zch4{%`32@)Kfp)f!3rZn1x7Ur7;Q?ZpH6^gncQ1d>;PkjoUCaHn1XVMTI~W~McV*pR*cDCRB)rHL+bnx5O~q=* zOx_(FrM|Qm*jah2#vePqe88}~DXC% zZMhY}&big|03X?3c^M%_KHdq&Vkkg^FMwu|S-|Q2MMg&>e+mF^P|8$eElrhITeKpp z&V0>IDFPg0by4f8k)c*;rNfcx#vt9$EbFyfR|RQZ&D3x)lNR>2@)K<0301S0nLk0DX9sG$X`N=q@Sw6gsCA*@%!?gKE z1uF+=u>|-oudk@!BMVcF3cVn9+dKTpFH$$9P@h&TOXMRzozYWRjY^w!jMXU|6yzVX zx@80c;q;l``6#f6$SxVXB>)+vqJ9^Y9HG_FRJDbY`!#^{bed9VQ(l_=7axTOb*rT8 z7fXXNhtJ+Cyi9$4fq#2nssE@7#!>=sBsBux<@skP@ll}08k(v`sZo>fG^M=FU(v=> z8|lmTm4-FS3fW-!2Xpd<3e-SQ_1f7B0%dE_@>7t{}wJa8W97vSFbY#c4@w zOsG-sF6?17%IHU~TXrxyjzX`vWC6N+ftvbi&4;mVt#1cu4LbY7(Bjf7K`DeUXGZ;; z=hzLr!0J-iY^T@VnA8gn6`hL`T(a~e&*CGZ__ETl@7>gk9ji-vL6E6Yam67~YIVMc z$ljTAgVG<-8(Xv^=ye7*2Kimg*8dI_x)rGV8`r2WB~Kx!eCbZ?+gtMH%6ZI%`PrlCOH3lNF83`f0} zQ7@r!#ixsF{*tpH_o=x3wyI5Ln7z&bPVYqT2kKrB4!hbzOw~>M|1wsKZ-j2z3xZmz z|AV0b5hvbdVZ(H%FXx|xBKM`bXunR0ppW5+D#F-^m^$td{SDRYZq?~E$lU+{002ov JPDHLkV1oU7JevRj literal 0 HcmV?d00001 diff --git a/html/dna_extra.png b/html/dna_extra.png new file mode 100644 index 0000000000000000000000000000000000000000..2fda1339e15cc96c895c34302cb7f49b0a367e13 GIT binary patch literal 1726 zcmV;v20{6WP)Y00001b5ch_0Itp) z=>Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!~g&e!~vBn4jTXf22)8yK~!i%?U{W{ zR#hCwf3UF!h*r6k)1kG-f6^+g)>>J!TD2O4FT6d&5xxMTCe#uMvY06}qcw<72_I2F zKt{H?D6uxJ#wu+qWx45`uMKHN76@~b_@3t+IL~?Rz0bK9o?PpGwrAXPc>CV({k@!X zZbWi&@+i54g-HI@B@DRch7s4?FyfjUMwq$n>x#&C`A=#%dV+m$-RfP+e!#^!Y_$M0 zZ=Aotr_70*6;CmRYbdR$kuJ_T%zRsY`LzYG7ucY*j zRzCCZ0Av3yD5TM=SA)$pLp42T-W+}vOkTk!WSKCw>v!F1`qUfv)d+Xfj zv6~S56n8&u>4`TLT2t^P#hpG$!vm#E5DXRwlXVcy!N^$LK=xg?1*LmRS{ivz|H?{N z^zv*r$Mhb($I@7#8NY|G+Y7t-cl??WAS#1J#R0}fJvE>1ICY#ddw;eUxd2h|)dOCt zn!T2G_f9-T2rDkSn^IF3@|j@dvhsaHk5*~$ox4mo`uq9FFFvlmmKmr6^d4Tih29%j zU|TOp&8(y6UweX&0(*^`O`fEeD7LC%T$}A3+7G`Bj~V-FfKcE1Lv*aCFr-kVmcS?q zR8V7OVe2&VPuLUy#UQu)(x}4-bHCa#(FQF3D9QS+={BRV?uNIh-llaTsqvn zmXEAaO9L8<8HkF(qV;^_M-{J2{WpvG)tKIy%c)@ck9?#@(u;TUD`HXXi7urB)!*?M ze>NdvRcx7CQo4)kk1XONztG(C+xRFnY8047uTk?teRTpf%j90FXB#0qtjwMnp!?%* zGpKHwmygIan{1hjsFi_GU}wh`szI{_*)Q*({ft^UO+CX4sAa7LcHZPT&6+415%_-20W|o$?htCi_ccq(#cegNa zj7prAxp?~W5(b@io%dEgvL6XDLX^C16imigfQ^Cxnnk7or{^PSeeL{e0(gTGry8tf zuKe`H5+>u|=WN#_!1h=dweA`jYn5&~H0*2-&<)M9Ub}Tyu&{qWU5|*O>iAWB#*fiD zIHb91Ly(s+9;wL;8!Yn8Q0wkrS<5JQ#%5N-0E~57V+GCaef)~(jpWamW_c4dnQV(1 zy-sT`)Uq;xvl0RB<>ke-e57HnQ9}^K6u-`|{9<)e4fU(J>}x*qQ+IMTt6>0jOdQ;c zk6B$a0s+x=@OM6%c$mmG8M~zc8K$D%OVWlX$F*9Tt1i8;_?AK1I!!e!Z|X9d_$V}} zYmekSYH2j)@V$hThp4e<37;j}TIt^cgUOTzY;}#my;NjRJUCH@q( zK!es5fQWT&4AY|Xdv=QJ;3g3GD|L}$l0b7y(l{Z-CzhZ7rUCr^B&`)(8daM z-o2?z#gfY@y05K}Y3gCPPSk>chK6#m-FK0X{3)hmEI^&bKb4nfR@_Wufw~DoWa3aB z{Xj-N49C%;E-Z4fd{MhBz-hcy?J~pcH49J=ly4?#uLy-*?aW-co(Ocy!G9U6ATUC= z90&~8x&H@a0U}VmYeQr8q)s$(6_hc%7_JsvriPGb3og!a%?%^2xnaaLH*6671AlvQ U8@Rt~`Tzg`07*qoM6N<$g56&~(EtDd literal 0 HcmV?d00001 diff --git a/html/dna_undiscovered.png b/html/dna_undiscovered.png new file mode 100644 index 0000000000000000000000000000000000000000..7184f097ac3b9ba1ccedf4660ed4498f8c15cd12 GIT binary patch literal 1758 zcmV<41|j*0P)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!~g&e!~vBn4jTXf26IV7K~!i%?U`F_ zR8JR5oqcc}3}mq{Bp4pdgCXeHuFv?8 zGry%iLdp3rk-xrgmpslWs26xudrIxm+4n(*4RxGCltwBEW z&j4{R=H}D5p2HN3@`x-2I?Ays>4lCmD%$SQ^CqfRP**yKB5n9VBoE4 zZDD2Pt~1FOKjt}prs%+|V6;|Yozr)Xpt-tW!I2T4=L9ykb|MA=s_JA{c zjPW= z&h1P@9gy##tO|Ogcb0L!AS1Je0{%#GKzxB}3m0F^qm-&jOK!#jmz3_I@-;_8$0=pZ~At||>+V)Q6k$%1cId3sZ?@jkJ zn-n0o!0F>WieS7|uB?wA-OZM~k+YnysSgX5 z9ob61-h3C8JzT{{!Z@vyJq1=LiNoFbLu<#8ix#qDzI9;b{mRV+wOAv2ee|}3U+PhX>Q13%OAT#9JMIad`;#KdCM-Z z%V9qo*4`F%b^#o>%BWscg1~Kix%=CnhQ#gPRuk2VD(W^9xcRPn9-xa;&JEM1)2Omi zc8!YGd3gYPz+54a&m?5zT=5-Sfg$et&BrZm=JXE&Ibq;-!0ohR(4Ex0#MU{)!2wW` z&&Q+C1l`o?`JPcdbaK-}##Z2@l=V^G>vrCNgnK7HV6g~PM)JBfmRB~IROAzXfL+>f zmSeFZcb!fZ!-51R1ra4Mc0XxPj^~lLxB{Jbc`8?8ITkCrufWi8>gy@p>IIfb4%YB) z?P(r`+f7GY0N=%b3=h`f-xL?98zodI4)!rW$fyVEIF>YlRWOz|>g)?}s_*7yPQWty zLr;Bl)n+~tA=We9c%aT5Uc{0+6Ik{afvz?DmvJiuJ4~!WV7m_fKZpxZiQ?Uz-HTEg zSXwODt|Ag#IXurI5)6-oz;PxaaGXg999M|`1{~pay6K{Sm;e9(07*qoM6N<$f|4Cb AfB*mh literal 0 HcmV?d00001 diff --git a/icons/effects/genetics.dmi b/icons/effects/genetics.dmi index a9f5f433d5b5c85fa4c9a31ae97fe869700e2527..7cb002d909ed2b1f09d3fe97eed1510e2dc7b259 100644 GIT binary patch literal 22607 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 diff --git a/icons/mob/actions/actions_genetic.dmi b/icons/mob/actions/actions_genetic.dmi new file mode 100644 index 0000000000000000000000000000000000000000..72bd87f97ebb5494980bd7cd46d0337223ff2a5b GIT binary patch literal 413 zcmV;O0b>4%P)OhBM-Bu2;Ctd zd=9|k4C#|C$adh^cmO^@!qWsIq&ODfIAtmm9E0Q`3!o5;PM)8{(xbA8VorcU=F%fv zEEk*hSg-lBrYYd9YFfv*FWD~}gr<0C=bYAR*n5Rl5AmD6M~pFCg|xs800000NkvXX Hu0mjf#PYOQ literal 0 HcmV?d00001 diff --git a/icons/mob/actions/actions_spells.dmi b/icons/mob/actions/actions_spells.dmi index dd8a6fb869041345db39e53d6e17c77bb69fe1b6..44b47f5fa00651fc09d17342036bdab5413291e7 100644 GIT binary patch literal 22440 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 literal 21434 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 Ef;RkrTL1t6 diff --git a/icons/obj/device.dmi b/icons/obj/device.dmi index 71695f770bc36a6dfac83f8543cc621dd70a460e..41f326111b44cddff1e9c5cb9f564a5441aefce5 100644 GIT binary patch delta 7733 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%0Aq4>asKQsU z&^IF-qLZU8?#|LfsA@^Xy53`B}sZoWpjKYnBgf6mDz zg8%D@KbarsZ!R4sfTg9SXl}o$&{NfRW(D8P)Umc{PW)YIh4BH`n!LPA%gf83k?gom z*yY9)!wdRU{1Suu#V0h6y1IIJPD&bEL_p`!4y~a`e_-w6Yk@m=g3R(GNjNB?kx#eW z9vN2VsAjCzD|w-p-N#JaHzG)I!}~#;Q38hG)n0{~*W2M}P%(7Djq94sIbu~_<;B@F z8uGBk*2YG3DhP{mJ>H&%kLu~`YXk-=T3A>RYXgl*F<`AGMYqspyHiam-{P4|ONwF8 znM3Q3n+%No++|-_`G`-scswz=s%dGFr^~u=5c6l5e2-K4!#N*My{9U5pT*Qn+iej_HB*>AGYfR6Sme5kt3|udkm%7 z+Fc{XmXZIoXAO8TMC9%}cAr-Lm_MZh5K&-#aK|&&Io)^sPqu$n-{IusYzz2YPyVC; z4&Qyt`5iWFO9L7iy(LCU>0GpVO8}WOy(6(4#>R}T+ow;Lbfmtgrx#N4$jU1I^=tAe zpHXW*Bw1WsTw3Q-Y7G6D`|n!)ZjWQVf7oQJ`;YEgqeCfRa?{@Lb~cuvCw14sx?9DX zK;ulnDmTA2Bbj{r-M2Pmb<+?5e*T4yx7RTn8>E2w1Hymsr&Qyg28~7?QeMDJp}jl> z9r%p&NIB?8T%lZLZEY>QMDE3qAeVxt#*tRae}PGDgO>3hKhjbi^LB80lEX3<78Vc_ zyn;S^MC5K6FLfvTN0ky^`fn3!dkbA^8$k5LzaKYjK3PWp!TxE#&uxm_z%Rq@83tj* zx5Fe+O+=nli~JwdEJzq(=e>9tF7O`=ts?pSzlFiY3kZa^{k{Ds*C2rS>1rBil&IN- F{U1|-6+i$0 delta 7399 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

Xa^1@S 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 Zpc-}pj$Hwzl>qK}q^$YytD<@E{{w8LHY5N5 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"