diff --git a/code/__defines/damage_organs.dm b/code/__defines/damage_organs.dm index 3d55bf87adf..90a77c878b8 100644 --- a/code/__defines/damage_organs.dm +++ b/code/__defines/damage_organs.dm @@ -45,6 +45,7 @@ #define DROPLIMB_EDGE 0 #define DROPLIMB_BLUNT 1 #define DROPLIMB_BURN 2 +#define DROPLIMB_ACID 3 // Damage above this value must be repaired with surgery. #define ROBOLIMB_REPAIR_CAP 30 diff --git a/code/__defines/dna.dm b/code/__defines/dna.dm index bcf5f15e3c0..6140bf37bfb 100644 --- a/code/__defines/dna.dm +++ b/code/__defines/dna.dm @@ -13,6 +13,7 @@ #define NOCLONE 8 #define LASER 9 // Harm intent - click anywhere to shoot lasers from eyes. #define HEAL 10 // Healing people with hands. +#define FLASHPROOF 11 // Flashproof eyes. #define SKELETON 29 #define PLANT 30 @@ -36,14 +37,22 @@ #define COUGHING 0x4 #define TOURETTES 0x8 #define NERVOUS 0x10 - +/* +#define DEPRESSION 0x20 // Roleplay drugs +#define SCHIZOPHRENIA 0x40 // Roleplay drugs +*/ +#define WINGDINGS 0x80 // Better handling as disability +#define DETERIORATE 0x100 // Body melts slowly, medical loves you! +#define GIBBING 0x200 // Landmine for genetics to find +#define CENSORED 0x400 // Cannot swear // sdisabilities #define BLIND 0x1 #define MUTE 0x2 #define DEAF 0x4 +/* Traitgenes (Blocks have finally been retired, huzzah! // The way blocks are handled badly needs a rewrite, this is horrible. -// Too much of a project to handle at the moment, TODO for later. +// Too much of a project to handle at the moment, TODO for later.) var/BLINDBLOCK = 0 var/DEAFBLOCK = 0 var/HULKBLOCK = 0 @@ -74,6 +83,7 @@ var/HALLUCINATIONBLOCK = 0 var/NOPRINTSBLOCK = 0 var/SHOCKIMMUNITYBLOCK = 0 var/SMALLSIZEBLOCK = 0 +*/ // Define block bounds (off-low,off-high,on-low,on-high) // Used in setupgame.dm @@ -147,7 +157,9 @@ var/SMALLSIZEBLOCK = 0 #define DNA_UI_WING3_B 57 // VOREStation snippet end. #define DNA_UI_LENGTH 57 // VOREStation Edit - Needs to match the highest number above. -#define DNA_SE_LENGTH 49 // VOREStation Edit (original was UI+11) +#define DNA_SE_LENGTH 90 // Traitgenes (Expanded from 49 to 84, there have been a considerable expansion of genes. +// This leaves room for future expansion. This can be arbitrarily raised without worry if genes start to get crowded. +// Should have more than 10 empty genes after setup. - Willbird) //DNA modifiers // Buffer datatype flags. @@ -160,4 +172,4 @@ var/SMALLSIZEBLOCK = 0 // Gene flags #define GENE_ALWAYS_ACTIVATE 1 -#define MUTCHK_HIDEMSG 2 +#define MUTCHK_HIDEMSG 2 // Traitgenes (Hide gene activation/deactivation messages, mostly for resleeving so you don't get spammed) diff --git a/code/__defines/gamemode.dm b/code/__defines/gamemode.dm index 74121881ce1..cc6b81eb8a2 100644 --- a/code/__defines/gamemode.dm +++ b/code/__defines/gamemode.dm @@ -55,7 +55,7 @@ var/list/be_special_flags = list( "Wizard" = BE_WIZARD, "Malf AI" = BE_MALF, "Revolutionary" = BE_REV, - "Xenomorph" = BE_ALIEN, + "Genaprawn" = BE_ALIEN, "Positronic Brain" = BE_AI, "Cultist" = BE_CULTIST, "Renegade" = BE_RENEGADE, diff --git a/code/__defines/is_helpers.dm b/code/__defines/is_helpers.dm index 7a59144f8e9..0cb84adfac9 100644 --- a/code/__defines/is_helpers.dm +++ b/code/__defines/is_helpers.dm @@ -44,6 +44,7 @@ #define isslime(A) istype(A, /mob/living/simple_mob/slime) #define isxeno(A) istype(A, /mob/living/simple_mob/animal/space/alien) #define issimplekin(A) istype(A, /mob/living/simple_mob/shadekin) +#define isprotblob(A) istype(A, /mob/living/simple_mob/protean_blob) #define iscarbon(A) istype(A, /mob/living/carbon) #define isalien(A) istype(A, /mob/living/carbon/alien) diff --git a/code/__defines/life.dm b/code/__defines/life.dm index 1f022dec9a5..dec65d9e780 100644 --- a/code/__defines/life.dm +++ b/code/__defines/life.dm @@ -18,3 +18,5 @@ #define ENVIRONMENT_COMFORT_MARKER_HOT 2 #define TECHNOMANCER_INSTABILITY_MIN_GLOW 10 // When above this number, the entity starts glowing, affecting others. + +#define RADIATION_SPEED_COEFFICIENT 0.1 diff --git a/code/__defines/span_vr.dm b/code/__defines/span.dm similarity index 96% rename from code/__defines/span_vr.dm rename to code/__defines/span.dm index 727f04ad5c9..fe949e69b05 100644 --- a/code/__defines/span_vr.dm +++ b/code/__defines/span.dm @@ -239,7 +239,11 @@ // Links! #define span_linkify(str) ("" + str + "") -// Just used downstream #define span_wingdings(str) ("" + str + "") #define span_maptext(str) ("" + str + "") + +#define span_major_announcement_text(str) ("" + str + "") +#define span_major_announcement_title(str) ("" + str + "") +#define span_ooc_announcement_text(str) ("" + str + "") +#define span_subheader_announcement_text(str) ("" + str + "") diff --git a/code/_helpers/announcements.dm b/code/_helpers/announcements.dm new file mode 100644 index 00000000000..5ab52144ff9 --- /dev/null +++ b/code/_helpers/announcements.dm @@ -0,0 +1,86 @@ +/** + * Sends a div formatted chat box announcement + * + * Formatted like: + * + * " Server Announcement " (or sender_override) + * + * " Title " + * + * " Text " + * + * Arguments + * * text - required, the text to announce + * * title - optional, the title of the announcement. + * * players - optional, a list of all players to send the message to. defaults to the entire world + * * play_sound - if TRUE, play a sound with the announcement (based on player option) + * * sound_override - optional, override the default announcement sound + * * sender_override - optional, modifies the sender of the announcement + * * encode_title - if TRUE, the title will be HTML encoded (escaped) + * * encode_text - if TRUE, the text will be HTML encoded (escaped) + */ + +/proc/send_ooc_announcement( + text, + title = "", + players, + play_sound = TRUE, + sound_override = 'sound/misc/bloop.ogg', + sender_override = "Server Admin Announcement", + encode_title = TRUE, + encode_text = FALSE, +) + if(isnull(text)) + return + + var/list/announcement_strings = list() + + if(encode_title && title && length(title) > 0) + title = html_encode(title) + if(encode_text) + text = html_encode(text) + if(!length(text)) + return + + announcement_strings += span_major_announcement_title(sender_override) + announcement_strings += span_subheader_announcement_text(title) + announcement_strings += span_ooc_announcement_text(text) + var/finalized_announcement = create_ooc_announcement_div(jointext(announcement_strings, "")) + + if(islist(players)) + for(var/mob/target in players) + to_chat(target, finalized_announcement) + //if(play_sound && target.client?.prefs.read_preference(/datum/preference/toggle/sound_announcements)) + if(play_sound && target.client?.prefs.read_preference(/datum/preference/toggle/holder/play_adminhelp_ping)) + SEND_SOUND(target, sound(sound_override)) + else + to_chat(world, finalized_announcement) + + if(!play_sound) + return + + for(var/mob/player in player_list) + //if(player.client?.prefs.read_preference(/datum/preference/toggle/sound_announcements)) + if(player.client?.prefs.read_preference(/datum/preference/toggle/holder/play_adminhelp_ping)) + SEND_SOUND(player, sound(sound_override)) + +/** + * Inserts a span styled message into an alert box div + * + * + * Arguments + * * message - required, the message contents + * * color - optional, set a div color other than default + */ +/proc/create_announcement_div(message, color = "default") + return "
[message]
" + +/** + * Inserts a span styled message into an OOC alert style div + * + * + * Arguments + * * message - required, the message contents + */ +/proc/create_ooc_announcement_div(message) + return "
[message]
" diff --git a/code/_helpers/global_lists_vr.dm b/code/_helpers/global_lists_vr.dm index adb7c5cdf81..dbe9ae4e7d7 100644 --- a/code/_helpers/global_lists_vr.dm +++ b/code/_helpers/global_lists_vr.dm @@ -542,6 +542,9 @@ var/global/list/remainless_species = list(SPECIES_PROMETHEAN, traits_costs[path] = cost all_traits[path] = instance + // Traitgenes Initilize trait genes + setupgenetics(all_traits) + // Shakey shakey shake sortTim(all_traits, GLOBAL_PROC_REF(cmp_trait_datums_name), associative = TRUE) @@ -549,19 +552,20 @@ var/global/list/remainless_species = list(SPECIES_PROMETHEAN, for(var/traitpath in all_traits) var/datum/trait/T = all_traits[traitpath] var/category = T.category - switch(category) - if(-INFINITY to -0.1) - negative_traits[traitpath] = T - if(!(T.custom_only)) - everyone_traits_negative[traitpath] = T - if(0) - neutral_traits[traitpath] = T - if(!(T.custom_only)) - everyone_traits_neutral[traitpath] = T - if(0.1 to INFINITY) - positive_traits[traitpath] = T - if(!(T.custom_only)) - everyone_traits_positive[traitpath] = T + if(!T.hidden) // Traitgenes forbid hidden traits from showing, done to hide genetics only traits + switch(category) + if(-INFINITY to -0.1) + negative_traits[traitpath] = T + if(!(T.custom_only)) + everyone_traits_negative[traitpath] = T + if(0) + neutral_traits[traitpath] = T + if(!(T.custom_only)) + everyone_traits_neutral[traitpath] = T + if(0.1 to INFINITY) + positive_traits[traitpath] = T + if(!(T.custom_only)) + everyone_traits_positive[traitpath] = T // Weaver recipe stuff diff --git a/code/_onclick/click.dm b/code/_onclick/click.dm index 6d30059e73d..55255896d61 100644 --- a/code/_onclick/click.dm +++ b/code/_onclick/click.dm @@ -328,7 +328,7 @@ setClickCooldown(4) var/turf/T = get_turf(src) - var/obj/item/projectile/beam/LE = new (T) + var/obj/item/projectile/beam/laser_vision/LE = new (T) LE.icon = 'icons/effects/genetics.dmi' LE.icon_state = "eyelasers" playsound(src, 'sound/weapons/taser2.ogg', 75, 1) diff --git a/code/controllers/subsystems/atoms.dm b/code/controllers/subsystems/atoms.dm index 211ef0517de..e74800a6f80 100644 --- a/code/controllers/subsystems/atoms.dm +++ b/code/controllers/subsystems/atoms.dm @@ -9,7 +9,6 @@ SUBSYSTEM_DEF(atoms) flags = SS_NO_FIRE var/static/initialized = INITIALIZATION_INSSATOMS - // var/list/created_atoms // This is never used, so don't bother. ~Leshana var/static/old_initialized var/list/late_loaders @@ -18,7 +17,6 @@ SUBSYSTEM_DEF(atoms) var/list/BadInitializeCalls = list() /datum/controller/subsystem/atoms/Initialize() - setupgenetics() //to set the mutations' place in structural enzymes, so initializers know where to put mutations. initialized = INITIALIZATION_INNEW_MAPLOAD to_world_log("Initializing objects") admin_notice(span_danger("Initializing objects"), R_DEBUG) @@ -62,11 +60,6 @@ SUBSYSTEM_DEF(atoms) testing("Late initialized [late_loaders.len] atoms") late_loaders.Cut() - // Nothing ever checks return value of this proc, so don't bother. If this ever changes fix code in /atom/New() ~Leshana - // if(atoms) - // . = created_atoms + atoms - // created_atoms = null - /datum/controller/subsystem/atoms/proc/InitAtom(atom/A, list/arguments) var/the_type = A.type if(QDELING(A)) diff --git a/code/datums/diseases/advance/symptoms/telepathy.dm b/code/datums/diseases/advance/symptoms/telepathy.dm index 10a42cc6ad9..952b94a6b8a 100644 --- a/code/datums/diseases/advance/symptoms/telepathy.dm +++ b/code/datums/diseases/advance/symptoms/telepathy.dm @@ -27,13 +27,17 @@ Bonus /datum/symptom/telepathy/Start(datum/disease/advance/A) if(iscarbon(A)) var/mob/living/carbon/human/H = A.affected_mob - H.dna.SetSEState(REMOTETALKBLOCK, 1) + // Traitgenes Locate the gene from trait + var/datum/gene/trait/G = get_gene_from_trait(/datum/trait/positive/superpower_remotetalk) + H.dna.SetSEState(G.block, 1) domutcheck(H, null, TRUE) to_chat(H, span_notice("Your mind expands...")) /datum/symptom/telepathy/End(datum/disease/advance/A) if(iscarbon(A)) var/mob/living/carbon/human/H = A.affected_mob - H.dna.SetSEState(REMOTETALKBLOCK, 0) + // Traitgenes Locate the gene from trait + var/datum/gene/trait/G = get_gene_from_trait(/datum/trait/positive/superpower_remotetalk) + H.dna.SetSEState(G.block, 0) domutcheck(H, null, TRUE) to_chat(H, span_notice("Everything feels... Normal.")) diff --git a/code/game/antagonist/alien/xenomorph.dm b/code/game/antagonist/alien/xenomorph.dm index 2da4170fff9..cac15e03c5f 100644 --- a/code/game/antagonist/alien/xenomorph.dm +++ b/code/game/antagonist/alien/xenomorph.dm @@ -3,8 +3,8 @@ var/datum/antagonist/xenos/xenomorphs /datum/antagonist/xenos id = MODE_XENOMORPH role_type = BE_ALIEN - role_text = "Xenomorph" - role_text_plural = "Xenomorphs" + role_text = "Genaprawn" + role_text_plural = "Genaprawns" mob_path = /mob/living/carbon/alien/larva bantype = "Xenomorph" flags = ANTAG_OVERRIDE_MOB | ANTAG_RANDSPAWN | ANTAG_OVERRIDE_JOB | ANTAG_VOTABLE diff --git a/code/game/dna/dna2.dm b/code/game/dna/dna2.dm index 63b8a4a4de2..e312e48b3d4 100644 --- a/code/game/dna/dna2.dm +++ b/code/game/dna/dna2.dm @@ -1,20 +1,10 @@ -/** -* DNA 2: The Spaghetti Strikes Back -* -* @author N3X15 -*/ - // What each index means: #define DNA_OFF_LOWERBOUND 1 #define DNA_OFF_UPPERBOUND 2 #define DNA_ON_LOWERBOUND 3 #define DNA_ON_UPPERBOUND 4 -// For later: -//# define DNA_SE_LENGTH 50 // Was STRUCDNASIZE, size 27. 15 new blocks added = 42, plus room to grow. - - -// Defines which values mean "on" or "off". +// Defines which values mean "on" or "off". // This is to make some of the more OP superpowers a larger PITA to activate, // and to tell our new DNA datum which values to set in order to turn something // on or off. @@ -23,7 +13,19 @@ var/global/list/dna_activity_bounds[DNA_SE_LENGTH] // Used to determine what each block means (admin hax and species stuff on /vg/, mostly) var/global/list/assigned_blocks[DNA_SE_LENGTH] -var/global/list/datum/dna/gene/dna_genes[0] +// Traitgenes Genes accessible by global VV, and lists for good and bad mutations for quick randomized selection of traitgenes. Removed dna from gene's path +GLOBAL_LIST_EMPTY_TYPED(dna_genes, /datum/gene) +GLOBAL_LIST_EMPTY(trait_to_dna_genes) // Reverse lookup genes, use get_gene_from_trait(var/trait_path) to read this +GLOBAL_LIST_EMPTY_TYPED(dna_genes_good, /datum/gene/trait) +GLOBAL_LIST_EMPTY_TYPED(dna_genes_neutral, /datum/gene/trait) +GLOBAL_LIST_EMPTY_TYPED(dna_genes_bad, /datum/gene/trait) + +/proc/get_gene_from_trait(var/trait_path) // ALWAYS USE THIS + RETURN_TYPE(/datum/gene/trait) + var/G = GLOB.trait_to_dna_genes[trait_path] + if(!G) // This SHOULD NOT HAPPEN, be sure any viruses or injectors that give trait paths are actually traitgenes. + stack_trace("[trait_path] was used as a traitgene, without being flagged as one.") + return G /datum/dna // READ-ONLY, GETS OVERWRITTEN @@ -70,7 +72,6 @@ var/global/list/datum/dna/gene/dna_genes[0] var/list/custom_heat = list() var/list/custom_cold = list() var/digitigrade = 0 //0, Not FALSE, for future use as indicator for digitigrade types - // VOREStation // New stuff var/species = SPECIES_HUMAN @@ -88,10 +89,10 @@ var/global/list/datum/dna/gene/dna_genes[0] new_dna.real_name=real_name new_dna.species=species new_dna.body_markings=body_markings.Copy() - new_dna.base_species=base_species //VOREStation Edit - new_dna.custom_species=custom_species //VOREStaton Edit - new_dna.species_traits=species_traits.Copy() //VOREStation Edit - new_dna.blood_color=blood_color //VOREStation Edit + new_dna.base_species=base_species + new_dna.custom_species=custom_species + new_dna.species_traits=species_traits.Copy() + new_dna.blood_color=blood_color new_dna.blood_reagents=blood_reagents new_dna.scale_appearance = scale_appearance new_dna.offset_override = offset_override @@ -105,13 +106,13 @@ var/global/list/datum/dna/gene/dna_genes[0] new_dna.r_grad = r_grad new_dna.g_grad = g_grad new_dna.b_grad = b_grad - new_dna.custom_say=custom_say //VOREStaton Edit - new_dna.custom_ask=custom_ask //VOREStaton Edit - new_dna.custom_whisper=custom_whisper //VOREStaton Edit - new_dna.custom_exclaim=custom_exclaim //VOREStaton Edit - new_dna.custom_heat=custom_heat //VOREStation Edit - new_dna.custom_cold=custom_cold //VOREStation Edit - new_dna.digitigrade=src.digitigrade //VOREStation Edit + new_dna.custom_say=custom_say + new_dna.custom_ask=custom_ask + new_dna.custom_whisper=custom_whisper + new_dna.custom_exclaim=custom_exclaim + new_dna.custom_heat=custom_heat + new_dna.custom_cold=custom_cold + new_dna.digitigrade=src.digitigrade var/list/body_markings_genetic = (body_markings - body_marking_nopersist_list) new_dna.body_markings=body_markings_genetic.Copy() for(var/b=1;b<=DNA_SE_LENGTH;b++) @@ -259,7 +260,6 @@ var/global/list/datum/dna/gene/dna_genes[0] SetUIValueRange(offset, red, 255, 1) SetUIValueRange(offset + 1, green, 255, 1) SetUIValueRange(offset + 2, blue, 255, 1) - // VORE Station Edit End SetUIValueRange(DNA_UI_HAIR_R, character.r_hair, 255, 1) SetUIValueRange(DNA_UI_HAIR_G, character.g_hair, 255, 1) @@ -312,17 +312,14 @@ var/global/list/datum/dna/gene/dna_genes[0] if (block<=0) return ASSERT(maxvalue<=4095) var/range = (4095 / maxvalue) - if(value == 0) //VOREStation Edit - SetUIValue(block,0,defer) - return - if(value) + if(value!=null) SetUIValue(block,round(value * range),defer) // Getter version of above. /datum/dna/proc/GetUIValueRange(var/block,var/maxvalue) if (block<=0) return 0 - var/value = ((GetUIValue(block) / 4095) * maxvalue) - return round(0.5 + value) + var/value = GetUIValue(block) + return round(0.5 + (value / 4095) * maxvalue) // Is the UI gene "on" or "off"? // For UI, this is simply a check of if the value is > 2050. @@ -407,8 +404,8 @@ var/global/list/datum/dna/gene/dna_genes[0] // Getter version of above. /datum/dna/proc/GetSEValueRange(var/block,var/maxvalue) if (block<=0) return 0 - var/value = ((GetSEValue(block) / 4095) * maxvalue) - return round(0.5 + value) + var/value = GetSEValue(block) + return round(1 +(value / 4095)*maxvalue) // Is the block "on" (1) or "off" (0)? (Un-assigned genes are always off.) /datum/dna/proc/GetSEState(var/block) @@ -432,6 +429,19 @@ var/global/list/datum/dna/gene/dna_genes[0] /datum/dna/proc/GetSEBlock(var/block) return EncodeDNABlock(GetSEValue(block)) +// Get activation intensity, returns 0 to 1, you MUST check if the gene is active first! This is used for future expansion where genetraits can have multiple levels of activation/intensity +/datum/dna/proc/GetSEActivationIntensity(var/block) + if (block<=0) return 0 + var/list/BOUNDS=GetDNABounds(block) + var/value=GetSEValue(block) + var/val = (value - BOUNDS[DNA_ON_LOWERBOUND]) / (BOUNDS[DNA_ON_UPPERBOUND] - BOUNDS[DNA_ON_LOWERBOUND]) + return val + +// Gets the activation intensity index. ex: if a genetrait has 5 levels of activations, the gene will have 5 possible levels of activation. this is a future TODO. +/datum/dna/proc/GetSEActivationLevel(var/block,var/number_of_levels) + var/raw_val = GetSEActivationIntensity(block) + return round(raw_val * number_of_levels) // TODO - If this should be round/floor/ceil + // Do not use this unless you absolutely have to. // Set a block from a hex string. This is inefficient. If you can, use SetUIValue(). // Used in DNA modifiers. diff --git a/code/game/dna/dna2_domutcheck.dm b/code/game/dna/dna2_domutcheck.dm index 2b3f820532c..a922891326e 100644 --- a/code/game/dna/dna2_domutcheck.dm +++ b/code/game/dna/dna2_domutcheck.dm @@ -3,8 +3,27 @@ // M: Mob to mess with // connected: Machine we're in, type unchecked so I doubt it's used beyond monkeying // flags: See below, bitfield. + /proc/domutcheck(var/mob/living/M, var/connected=null, var/flags=0) - for(var/datum/dna/gene/gene in dna_genes) + // Traitgenes NO_SCAN and Synthetics cannot be mutated + if(M.isSynthetic()) + return + if(ishuman(M)) + var/mob/living/carbon/human/H = M + if(!H.species || H.species.flags & NO_SCAN) + return + // Traitgenes Sort genes into currently active, and deactivated... Genes that are active and may deactivate should do so before attempting to activate genes(to avoid conflicts blocking them!) + var/list/enabled_genes = list() + var/list/disabled_genes = list() + for(var/datum/gene/gene in GLOB.dna_genes) // Traitgenes Genes accessible by global VV, removed dna from path + if(!M || !M.dna) + return + if(gene.block) + if(gene.name in M.active_genes || gene.flags & GENE_ALWAYS_ACTIVATE) + enabled_genes.Add(gene) + else + disabled_genes.Add(gene) + for(var/datum/gene/gene in enabled_genes + disabled_genes) // Traitgenes Removed /dna/ from path if(!M || !M.dna) return if(!gene.block) @@ -21,22 +40,30 @@ gene_active = M.dna.GetSEState(gene.block) // Prior state - var/gene_prior_status = (gene.type in M.active_genes) + var/gene_prior_status = (gene.name in M.active_genes) // Traitgenes Use name instead, cannot use type with dynamically setup traitgenes var/changed = gene_active != gene_prior_status || (gene.flags & GENE_ALWAYS_ACTIVATE) // If gene state has changed: - if(changed) + if(changed || flags & MUTCHK_FORCED) // Traitgenes MUTCHK_FORCED always applies or removes genes // Gene active (or ALWAYS ACTIVATE) if(gene_active || (gene.flags & GENE_ALWAYS_ACTIVATE)) - testing("[gene.name] activated!") + // Traitgenes Handle trait conflicts, do not activate if so! Has to be done here so that SE activation without trait activation is possible. + if(istype(gene,/datum/gene/trait)) + var/datum/gene/trait/TG = gene + if(!ishuman(M)) + continue // Trait genes are human only + var/mob/living/carbon/human/H = M + if(TG.has_conflict(H.species.traits)) + continue // The SE is on, but the gene is denied... + //testing("[gene.name] activated!") gene.activate(M,connected,flags) if(M) - M.active_genes |= gene.type + M.active_genes |= gene.name // Traitgenes Use name instead, cannot use type with dynamically setup traitgenes M.update_icon = 1 // If Gene is NOT active: else - testing("[gene.name] deactivated!") + //testing("[gene.name] deactivated!") gene.deactivate(M,connected,flags) if(M) - M.active_genes -= gene.type + M.active_genes -= gene.name // Traitgenes Use name instead, cannot use type with dynamically setup traitgenes M.update_icon = 1 diff --git a/code/game/dna/dna2_helpers.dm b/code/game/dna/dna2_helpers.dm index 978478342e2..730ee294ed0 100644 --- a/code/game/dna/dna2_helpers.dm +++ b/code/game/dna/dna2_helpers.dm @@ -23,42 +23,50 @@ // Give Random Bad Mutation to M /proc/randmutb(var/mob/living/M) if(!M || !(M.dna)) return + // Traitgenes NO_SCAN and Synthetics cannot be mutated + if(M.isSynthetic()) + return + if(ishuman(M)) + var/mob/living/carbon/human/H = M + if(!H.species || H.species.flags & NO_SCAN) + return M.dna.check_integrity() - //var/block = pick(GLASSESBLOCK,COUGHBLOCK,FAKEBLOCK,NERVOUSBLOCK,CLUMSYBLOCK,TWITCHBLOCK,HEADACHEBLOCK,BLINDBLOCK,DEAFBLOCK,HALLUCINATIONBLOCK) // Most of these are disabled anyway. - var/block = pick(FAKEBLOCK,CLUMSYBLOCK,BLINDBLOCK,DEAFBLOCK) - M.dna.SetSEState(block, 1) + // Traitgenes Pick from bad traitgenes + var/datum/gene/trait/T = pick(GLOB.dna_genes_bad + (prob(10) ? GLOB.dna_genes_neutral : list()) ) // Chance for neutrals as well + M.dna.SetSEState(T.block, TRUE) // Give Random Good Mutation to M /proc/randmutg(var/mob/living/M) if(!M || !(M.dna)) return + // Traitgenes NO_SCAN and Synthetics cannot be mutated + if(M.isSynthetic()) + return + if(ishuman(M)) + var/mob/living/carbon/human/H = M + if(!H.species || H.species.flags & NO_SCAN) + return M.dna.check_integrity() - //var/block = pick(HULKBLOCK,XRAYBLOCK,FIREBLOCK,TELEBLOCK,NOBREATHBLOCK,REMOTEVIEWBLOCK,REGENERATEBLOCK,INCREASERUNBLOCK,REMOTETALKBLOCK,MORPHBLOCK,BLENDBLOCK,NOPRINTSBLOCK,SHOCKIMMUNITYBLOCK,SMALLSIZEBLOCK) // Much like above, most of these blocks are disabled in code. - var/block = pick(HULKBLOCK,XRAYBLOCK,FIREBLOCK,TELEBLOCK,REGENERATEBLOCK,REMOTETALKBLOCK) - M.dna.SetSEState(block, 1) - -// Random Appearance Mutation -/proc/randmuti(var/mob/living/M) - if(!M || !(M.dna)) return - M.dna.check_integrity() - M.dna.SetUIValue(rand(1,DNA_UI_LENGTH),rand(1,4095)) + // Traitgenes Pick from good traitgenes + var/datum/gene/trait/T = pick(GLOB.dna_genes_good + (prob(10) ? GLOB.dna_genes_neutral : list()) ) // Chance for neutrals as well + M.dna.SetSEState(T.block, TRUE) // Scramble UI or SE. /proc/scramble(var/UI, var/mob/M, var/prob) if(!M || !(M.dna)) return + // Traitgenes edit begin - NO_SCAN and Synthetics cannot be mutated + if(M.isSynthetic()) + return + if(ishuman(M)) + var/mob/living/carbon/human/H = M + if(!H.species || H.species.flags & NO_SCAN) + return + // Traitgenes edit end M.dna.check_integrity() - if(UI) - for(var/i = 1, i <= DNA_UI_LENGTH-1, i++) - if(prob(prob)) - M.dna.SetUIValue(i,rand(1,4095),1) - M.dna.UpdateUI() - M.UpdateAppearance() - - else - for(var/i = 1, i <= DNA_SE_LENGTH-1, i++) - if(prob(prob)) - M.dna.SetSEValue(i,rand(1,4095),1) - M.dna.UpdateSE() - domutcheck(M, null) + for(var/i = 1, i <= DNA_SE_LENGTH-1, i++) + if(prob(prob)) + M.dna.SetSEValue(i,rand(1,4095),1) + M.dna.UpdateSE() + domutcheck(M, null) return // I haven't yet figured out what the fuck this is supposed to do. @@ -122,8 +130,7 @@ return output -// /proc/updateappearance has changed behavior, so it's been removed -// Use mob.UpdateAppearance() instead. +// Use mob.UpdateAppearance() // Simpler. Don't specify UI in order for the mob to use its own. /mob/proc/UpdateAppearance(var/list/UI=null) @@ -175,8 +182,6 @@ if((0 < beard) && (beard <= facial_hair_styles_list.len)) H.f_style = facial_hair_styles_list[beard] - // VORE StationEdit Start - // Ears var/ears = dna.GetUIValueRange(DNA_UI_EAR_STYLE, ear_styles_list.len + 1) - 1 if(ears < 1) @@ -257,6 +262,7 @@ H.custom_ask = dna.custom_ask H.custom_whisper = dna.custom_whisper H.custom_exclaim = dna.custom_exclaim + H.species.blood_color = dna.blood_color H.fuzzy = dna.scale_appearance H.offset_override = dna.offset_override H.synth_markings = dna.synth_markings @@ -273,17 +279,16 @@ H.dna.blood_color = dna.blood_color H.species.blood_reagents = H.dna.blood_reagents H.species.blood_color = H.dna.blood_color - // VOREStation Edit End - /* Currently not implemented on virgo + /*//TODO: Get these to work as well. H.species.species_sounds = dna.species_sounds H.species.gender_specific_species_sounds = dna.gender_specific_species_sounds H.species.species_sounds_male = dna.species_sounds_male H.species.species_sounds_female = dna.species_sounds_female - */ + *///TODO End - H.force_update_organs() + H.force_update_organs() //VOREStation Add - Gotta do this too H.force_update_limbs() - //H.update_body(0) //Done in force_update_limbs already + //H.update_body(0) //VOREStation Edit - Done in force_update_limbs already H.update_eyes() H.update_hair() diff --git a/code/game/dna/dna_modifier.dm b/code/game/dna/dna_modifier.dm index e2b9e5dcb80..37353e0a4d1 100644 --- a/code/game/dna/dna_modifier.dm +++ b/code/game/dna/dna_modifier.dm @@ -1,6 +1,5 @@ #define DNA_BLOCK_SIZE 3 -#define PAGE_UI "ui" #define PAGE_SE "se" #define PAGE_BUFFER "buffer" #define PAGE_REJUVENATORS "rejuvenators" @@ -69,7 +68,7 @@ interact_offline = 1 circuit = /obj/item/circuitboard/clonescanner var/locked = 0 - var/mob/living/carbon/occupant = null + var/datum/weakref/occupant = null var/obj/item/reagent_containers/glass/beaker = null var/opened = 0 var/damage_coeff @@ -81,6 +80,10 @@ default_apply_parts() RefreshParts() +/obj/machinery/dna_scannernew/Destroy() + eject_occupant() + . = ..() + /obj/machinery/dna_scannernew/RefreshParts() scan_level = 0 damage_coeff = 0 @@ -112,16 +115,24 @@ return /obj/machinery/dna_scannernew/proc/eject_occupant() - src.go_out() + var/mob/living/carbon/WC = occupant.resolve() + go_out() for(var/obj/O in src) if((!istype(O,/obj/item/reagent_containers)) && (!istype(O,/obj/item/circuitboard/clonescanner)) && (!istype(O,/obj/item/stock_parts)) && (!istype(O,/obj/item/stack/cable_coil))) - O.loc = get_turf(src)//Ejects items that manage to get in there (exluding the components) - if(!occupant) + O.forceMove(get_turf(src)) //Ejects items that manage to get in there (exluding the components) + if(!WC) for(var/mob/M in src)//Failsafe so you can get mobs out - M.loc = get_turf(src) + M.forceMove(get_turf(src)) /obj/machinery/dna_scannernew/MouseDrop_T(var/mob/target, var/mob/user) //Allows borgs to clone people without external assistance - if(user.stat || user.lying || !Adjacent(user) || !target.Adjacent(user)|| !ishuman(target)) + var/mob/living/carbon/WC = occupant?.resolve() + if(user.stat || user.lying || !Adjacent(user) || !target.Adjacent(user)|| !ishuman(target) || WC) + return + // Traitgenes Do not allow buckled or ridden mobs + if(target.buckled) + return + if(target.has_buckled_mobs()) + to_chat(user, span_warning("\The [target] has other entities attached to it. Remove them first.")) return put_in(target) @@ -130,27 +141,37 @@ set category = "Object" set name = "Enter DNA Scanner" + if(usr.stat != 0) return if(!ishuman(usr) && !issmall(usr)) //Make sure they're a mob that has dna to_chat(usr, span_notice("Try as you might, you can not climb up into the scanner.")) return - if(src.occupant) + if(occupant) to_chat(usr, span_warning("The scanner is already occupied!")) return if(usr.abiotic()) to_chat(usr, span_warning("The subject cannot have abiotic items on.")) return + var/mob/living/carbon/WC = occupant?.resolve() + if(WC) + to_chat(usr, span_warning("There is already something inside.")) + return usr.stop_pulling() usr.client.perspective = EYE_PERSPECTIVE usr.client.eye = src - usr.loc = src - src.occupant = usr - src.icon_state = "scanner_1" - src.add_fingerprint(usr) + usr.forceMove(src) + occupant = WEAKREF(usr) + icon_state = "scanner_1" + add_fingerprint(usr) SStgui.update_uis(src) /obj/machinery/dna_scannernew/attackby(var/obj/item/item as obj, var/mob/user as mob) + // Traitgenes Deconstructable dna scanner + if(default_deconstruction_screwdriver(user, item)) + return + if(default_deconstruction_crowbar(user, item)) + return if(istype(item, /obj/item/reagent_containers/glass)) if(beaker) to_chat(user, span_warning("A beaker is already loaded into the machine.")) @@ -158,19 +179,19 @@ beaker = item user.drop_item() - item.loc = src + item.forceMove(src) user.visible_message("\The [user] adds \a [item] to \the [src]!", "You add \a [item] to \the [src]!") SStgui.update_uis(src) return else if(istype(item, /obj/item/organ/internal/brain)) - if(src.occupant) + if(occupant) to_chat(user, span_warning("The scanner is already occupied!")) return var/obj/item/organ/internal/brain/brain = item if(brain.clone_source) user.drop_item() - brain.loc = src + brain.forceMove(src) put_in(brain.brainmob) src.add_fingerprint(user) user.visible_message("\The [user] adds \a [item] to \the [src]!", "You add \a [item] to \the [src]!") @@ -184,7 +205,7 @@ var/obj/item/grab/G = item if(!ismob(G.affecting)) return - if(src.occupant) + if(occupant) to_chat(user, span_warning("The scanner is already occupied!")) return if(G.affecting.abiotic()) @@ -195,13 +216,32 @@ qdel(G) return +// Traitgenes Deconstructable dna scanner +/obj/machinery/dna_scannernew/dismantle() + // release contents + if(beaker) + beaker.forceMove(get_turf(src)) + beaker = null + if(occupant) + var/mob/living/carbon/WC = occupant.resolve() + WC.forceMove(get_turf(src)) + occupant = null + // Disconnect from our terminal + for(var/dirfind in cardinal) + var/obj/machinery/computer/scan_consolenew/console = locate(/obj/machinery/computer/scan_consolenew, get_step(src, dirfind)) + if(console && console.connected == src) + console.connected = null + SStgui.close_uis(console) + break + . = ..() + /obj/machinery/dna_scannernew/proc/put_in(var/mob/M) if(M.client) M.client.perspective = EYE_PERSPECTIVE M.client.eye = src - M.loc = src - src.occupant = M - src.icon_state = "scanner_1" + M.forceMove(src) + occupant = WEAKREF(M) + icon_state = "scanner_1" // search for ghosts, if the corpse is empty and the scanner is connected to a cloner if(locate(/obj/machinery/computer/cloning, get_step(src, NORTH)) \ @@ -217,49 +257,45 @@ SStgui.update_uis(src) /obj/machinery/dna_scannernew/proc/go_out() - if((!( src.occupant ) || src.locked)) + if((!(occupant) || locked)) return - if(src.occupant.client) - src.occupant.client.eye = src.occupant.client.mob - src.occupant.client.perspective = MOB_PERSPECTIVE - if(istype(occupant,/mob/living/carbon/brain)) + var/mob/living/carbon/WC = occupant.resolve() + if(WC.client) + WC.client.eye = WC.client.mob + WC.client.perspective = MOB_PERSPECTIVE + if(istype(WC,/mob/living/carbon/brain)) for(var/obj/O in src) if(istype(O,/obj/item/organ/internal/brain)) - O.loc = get_turf(src) - src.occupant.loc = O + O.forceMove(get_turf(src)) + WC.forceMove(O) break else - src.occupant.loc = src.loc - src.occupant = null - src.icon_state = "scanner_0" + WC.forceMove(loc) + occupant = null + icon_state = "scanner_0" SStgui.update_uis(src) /obj/machinery/dna_scannernew/ex_act(severity) + var/our_tile = loc //This is done here as if you try to feed loc in the A.forcemove, it will runtime as src id qdel'd before they can be moved. switch(severity) if(1.0) for(var/atom/movable/A as mob|obj in src) - A.loc = src.loc + A.forceMove(our_tile) ex_act(severity) - //Foreach goto(35) - //SN src = null qdel(src) return if(2.0) if(prob(50)) for(var/atom/movable/A as mob|obj in src) - A.loc = src.loc + A.forceMove(our_tile) ex_act(severity) - //Foreach goto(108) - //SN src = null qdel(src) return if(3.0) if(prob(25)) for(var/atom/movable/A as mob|obj in src) - A.loc = src.loc + A.forceMove(our_tile) ex_act(severity) - //Foreach goto(181) - //SN src = null qdel(src) return else @@ -280,25 +316,31 @@ var/selected_ui_target_hex = 1 var/radiation_duration = 2.0 var/radiation_intensity = 1.0 - var/list/datum/dna2/record/buffers[3] + var/list/datum/transhuman/body_record/buffers[3] // Traitgenes Use bodyrecords var/irradiating = 0 var/injector_ready = 0 //Quick fix for issue 286 (screwdriver the screen twice to restore injector) -Pete var/obj/machinery/dna_scannernew/connected = null - var/obj/item/disk/data/disk = null - var/selected_menu_key = PAGE_UI + // Traitgenes body record disks are used instead of a unique disk + var/obj/item/disk/body_record/disk = null + var/selected_menu_key = PAGE_SE anchored = TRUE use_power = USE_POWER_IDLE idle_power_usage = 10 active_power_usage = 400 /obj/machinery/computer/scan_consolenew/attackby(obj/item/I as obj, mob/user as mob) - if(istype(I, /obj/item/disk/data)) //INSERT SOME diskS - if(!src.disk) - user.drop_item() - I.loc = src - src.disk = I - to_chat(user, "You insert [I].") - SStgui.update_uis(src) // update all UIs attached to src + // Traitgenes body record disks are used instead of a unique disk + if(istype(I, /obj/item/disk/body_record)) //INSERT SOME diskS + if(connected) + if(!disk) + user.drop_item() + I.forceMove(src) + disk = I + to_chat(user, "You insert [I].") + SStgui.update_uis(src) // update all UIs attached to src + return + else + to_chat(user, "\The [src] will not accept a disk without a DNA modifier connected.") return else ..() @@ -308,12 +350,10 @@ switch(severity) if(1.0) - //SN src = null qdel(src) return if(2.0) if(prob(50)) - //SN src = null qdel(src) return else @@ -322,9 +362,16 @@ /obj/machinery/computer/scan_consolenew/Initialize() . = ..() for(var/i=0;i<3;i++) - buffers[i+1]=new /datum/dna2/record - for(dir in list(NORTH,EAST,SOUTH,WEST)) - connected = locate(/obj/machinery/dna_scannernew, get_step(src, dir)) + // Traitgenes Use bodyrecords + var/datum/transhuman/body_record/R = new /datum/transhuman/body_record() + R.mydna = new + R.mydna.dna = new + R.mydna.dna.ResetUI() + R.mydna.dna.ResetSE() + buffers[i+1]=R + // Traitgenes don't alter direction of computer as this scans for neighbour + for(var/dirfind in cardinal) + connected = locate(/obj/machinery/dna_scannernew, get_step(src, dirfind)) if(connected) break VARSET_IN(src, injector_ready, TRUE, 25 SECONDS) @@ -335,7 +382,7 @@ arr += "[i]:[EncodeDNABlock(buffer[i])]" return arr -/obj/machinery/computer/scan_consolenew/proc/setInjectorBlock(var/obj/item/dnainjector/I, var/blk, var/datum/dna2/record/buffer) +/obj/machinery/computer/scan_consolenew/proc/setInjectorBlock(var/obj/item/dnainjector/I, var/blk, var/datum/transhuman/body_record/buffer) // Traitgenes Stores the entire body record var/pos = findtext(blk,":") if(!pos) return 0 var/id = text2num(copytext(blk,1,pos)) @@ -344,15 +391,6 @@ I.buf = buffer return 1 -/* -/obj/machinery/computer/scan_consolenew/process() //not really used right now - if(stat & (NOPOWER|BROKEN)) - return - if(!( src.status )) //remove this - return - return -*/ - /obj/machinery/computer/scan_consolenew/attack_ai(user as mob) src.add_hiddenprint(user) tgui_interact(user) @@ -362,7 +400,8 @@ tgui_interact(user) /obj/machinery/computer/scan_consolenew/tgui_interact(mob/user, datum/tgui/ui) - if(!connected || user == connected.occupant || user.stat) + var/mob/living/carbon/WC = connected?.occupant?.resolve() + if(!connected || user == WC || user.stat) return ui = SStgui.try_update_ui(user, src, ui) if(!ui) @@ -381,19 +420,24 @@ data["hasDisk"] = disk ? 1 : 0 var/diskData[0] - if(!disk || !disk.buf) + if(!disk || !disk.stored || !disk.stored.mydna) // Traitgenesbody record disks are used instead of a unique disk diskData["data"] = null diskData["owner"] = null diskData["label"] = null diskData["type"] = null diskData["ue"] = null else - diskData = disk.buf.GetData() + diskData = disk.stored.mydna.GetData() // Traitgenes body record disks are used instead of a unique disk data["disk"] = diskData - var/list/new_buffers = list() - for(var/datum/dna2/record/buf in src.buffers) - new_buffers += list(buf.GetData()) + // Traitgenes Fixed buffer menu + var/list/new_buffers[src.buffers.len] + for(var/i=1;i<=src.buffers.len;i++) + var/datum/transhuman/body_record/R = buffers[i] + if(R && R.mydna) + new_buffers[i]=R.mydna.GetData() + else + new_buffers[i]=list("data" = list(), "owner" = null, "label" = null, "type" = DNA2_BUF_SE, "ue" = 0) data["buffers"]=new_buffers data["radiationIntensity"] = radiation_intensity @@ -409,7 +453,8 @@ data["selectedUITargetHex"] = selected_ui_target_hex var/occupantData[0] - if(!src.connected.occupant || !src.connected.occupant.dna) + var/mob/living/carbon/WC = connected?.occupant?.resolve() + if(!WC || !WC.dna) occupantData["name"] = null occupantData["stat"] = null occupantData["isViableSubject"] = null @@ -421,18 +466,26 @@ occupantData["structuralEnzymes"] = null occupantData["radiationLevel"] = null else - occupantData["name"] = connected.occupant.real_name - occupantData["stat"] = connected.occupant.stat + occupantData["name"] = WC.real_name + occupantData["stat"] = WC.stat occupantData["isViableSubject"] = 1 - if(NOCLONE in connected.occupant.mutations || !src.connected.occupant.dna) + // Traitgenes NO_SCAN and Synthetics cannot be mutated + var/allowed = TRUE + if(WC.isSynthetic()) + allowed = FALSE + if(ishuman(WC)) + var/mob/living/carbon/human/H = WC + if(!H.species || (H.species.flags & NO_SCAN)) + allowed = FALSE + if(!allowed || (NOCLONE in WC.mutations) || !WC.dna) occupantData["isViableSubject"] = 0 - occupantData["health"] = connected.occupant.health - occupantData["maxHealth"] = connected.occupant.maxHealth + occupantData["health"] = WC.health + occupantData["maxHealth"] = WC.maxHealth occupantData["minHealth"] = CONFIG_GET(number/health_threshold_dead) - occupantData["uniqueEnzymes"] = connected.occupant.dna.unique_enzymes - occupantData["uniqueIdentity"] = connected.occupant.dna.uni_identity - occupantData["structuralEnzymes"] = connected.occupant.dna.struc_enzymes - occupantData["radiationLevel"] = connected.occupant.radiation + occupantData["uniqueEnzymes"] = WC.dna.unique_enzymes + occupantData["uniqueIdentity"] = WC.dna.uni_identity + occupantData["structuralEnzymes"] = WC.dna.struc_enzymes + occupantData["radiationLevel"] = WC.radiation data["occupant"] = occupantData; data["isBeakerLoaded"] = connected.beaker ? 1 : 0 @@ -454,7 +507,7 @@ return TRUE if(!istype(ui.user.loc, /turf)) return TRUE - if(!src || !src.connected) + if(!src || !connected) return TRUE if(irradiating) // Make sure that it isn't already irradiating someone... return TRUE @@ -464,107 +517,60 @@ if(tgui_act_modal(action, params)) return TRUE - . = TRUE switch(action) if("selectMenuKey") + playsound(src, 'sound/machines/button.ogg', 30, 1, 0) var/key = params["key"] - if(!(key in list(PAGE_UI, PAGE_SE, PAGE_BUFFER, PAGE_REJUVENATORS))) - return + if(!(key in list(/*PAGE_UI,*/ PAGE_SE, PAGE_BUFFER, PAGE_REJUVENATORS))) // Traitgenes Body design console is used to edit UIs now + return TRUE selected_menu_key = key + return TRUE if("toggleLock") + playsound(src, 'sound/machines/button.ogg', 30, 1, 0) if(connected && connected.occupant) connected.locked = !(connected.locked) + return TRUE if("pulseRadiation") + playsound(src, 'sound/machines/button.ogg', 30, 1, 0) irradiating = radiation_duration var/lock_state = connected.locked connected.locked = TRUE //lock it - - SStgui.update_uis(src) // update all UIs attached to src - sleep(10 * radiation_duration) // sleep for radiation_duration seconds - - irradiating = 0 - connected.locked = lock_state - - if(!connected.occupant) - return - - if(prob(95)) - if(prob(75)) - randmutb(connected.occupant) - else - randmuti(connected.occupant) - else - if(prob(95)) - randmutg(connected.occupant) - else - randmuti(connected.occupant) - - connected.occupant.apply_effect(((radiation_intensity*3)+radiation_duration*3), IRRADIATE, check_protection = 0) + addtimer(CALLBACK(src, PROC_REF(do_pulse), lock_state), radiation_duration SECONDS, TIMER_DELETE_ME) + return TRUE if("radiationDuration") radiation_duration = clamp(text2num(params["value"]), 1, 20) + return TRUE if("radiationIntensity") radiation_intensity = clamp(text2num(params["value"]), 1, 10) - //////////////////////////////////////////////////////// - if("changeUITarget") - selected_ui_target = clamp(text2num(params["value"]), 1, 15) - selected_ui_target_hex = num2text(selected_ui_target, 1, 16) - if("selectUIBlock") // This chunk of code updates selected block / sub-block based on click - var/select_block = text2num(params["block"]) - var/select_subblock = text2num(params["subblock"]) - if(!select_block || !select_subblock) - return - - selected_ui_block = clamp(select_block, 1, DNA_UI_LENGTH) - selected_ui_subblock = clamp(select_subblock, 1, DNA_BLOCK_SIZE) - if("pulseUIRadiation") - var/block = connected.occupant.dna.GetUISubBlock(selected_ui_block,selected_ui_subblock) - - irradiating = radiation_duration - var/lock_state = connected.locked - connected.locked = TRUE //lock it - - SStgui.update_uis(src) // update all UIs attached to src - sleep(10 * radiation_duration) // sleep for radiation_duration seconds - - irradiating = 0 - connected.locked = lock_state - - if(!connected.occupant) - return - - if(prob((80 + (radiation_duration / 2)))) - block = miniscrambletarget(num2text(selected_ui_target), radiation_intensity, radiation_duration) - connected.occupant.dna.SetUISubBlock(selected_ui_block,selected_ui_subblock,block) - connected.occupant.UpdateAppearance() - connected.occupant.apply_effect((radiation_intensity+radiation_duration), IRRADIATE, check_protection = 0) - else - if(prob(20 + radiation_intensity)) - randmutb(connected.occupant) - domutcheck(connected.occupant,connected) - else - randmuti(connected.occupant) - connected.occupant.UpdateAppearance() - connected.occupant.apply_effect(((radiation_intensity*2)+radiation_duration), IRRADIATE, check_protection = 0) - //////////////////////////////////////////////////////// + return TRUE if("injectRejuvenators") + playsound(src, 'sound/machines/button.ogg', 30, 1, 0) if(!connected.occupant || !connected.beaker) - return + return TRUE + var/mob/living/carbon/WC = connected?.occupant?.resolve() var/inject_amount = clamp(round(text2num(params["amount"]), 5), 0, 50) // round to nearest 5 and clamp to 0-50 if(!inject_amount) - return - connected.beaker.reagents.trans_to_mob(connected.occupant, inject_amount, CHEM_BLOOD) + return TRUE + connected.beaker.reagents.trans_to_mob(WC, inject_amount, CHEM_BLOOD) + return TRUE //////////////////////////////////////////////////////// if("selectSEBlock") // This chunk of code updates selected block / sub-block based on click (se stands for strutural enzymes) + playsound(src, "keyboard", 40) var/select_block = text2num(params["block"]) var/select_subblock = text2num(params["subblock"]) if(!select_block || !select_subblock) - return + return TRUE selected_se_block = clamp(select_block, 1, DNA_SE_LENGTH) selected_se_subblock = clamp(select_subblock, 1, DNA_BLOCK_SIZE) + return TRUE if("pulseSERadiation") - var/block = connected.occupant.dna.GetSESubBlock(selected_se_block,selected_se_subblock) + if(!connected?.occupant) + return TRUE + var/mob/living/carbon/WC = connected?.occupant?.resolve() + playsound(src, "keyboard", 40) + var/block = WC.dna.GetSESubBlock(selected_se_block,selected_se_subblock) //var/original_block=block //testing("Irradiating SE block [selected_se_block]:[selected_se_subblock] ([block])...") @@ -572,156 +578,124 @@ var/lock_state = connected.locked connected.locked = TRUE //lock it - SStgui.update_uis(src) // update all UIs attached to src - sleep(10 * radiation_duration) // sleep for radiation_duration seconds + //We call the do_irradiate proc here after radation_duration SECONDS + addtimer(CALLBACK(src, PROC_REF(do_irradiate), lock_state, block), radiation_duration SECONDS, TIMER_DELETE_ME) + return TRUE - irradiating = 0 - connected.locked = lock_state - - if(connected.occupant) - if(prob((80 + (radiation_duration / 2)))) - // FIXME: Find out what these corresponded to and change them to the WHATEVERBLOCK they need to be. - //if((selected_se_block != 2 || selected_se_block != 12 || selected_se_block != 8 || selected_se_block || 10) && prob (20)) - var/real_SE_block=selected_se_block - block = miniscramble(block, radiation_intensity, radiation_duration) - if(prob(20)) - if(selected_se_block > 1 && selected_se_block < DNA_SE_LENGTH/2) - real_SE_block++ - else if(selected_se_block > DNA_SE_LENGTH/2 && selected_se_block < DNA_SE_LENGTH) - real_SE_block-- - - //testing("Irradiated SE block [real_SE_block]:[selected_se_subblock] ([original_block] now [block]) [(real_SE_block!=selected_se_block) ? "(SHIFTED)":""]!") - connected.occupant.dna.SetSESubBlock(real_SE_block,selected_se_subblock,block) - connected.occupant.apply_effect((radiation_intensity+radiation_duration), IRRADIATE, check_protection = 0) - domutcheck(connected.occupant,connected) - else - connected.occupant.apply_effect(((radiation_intensity*2)+radiation_duration), IRRADIATE, check_protection = 0) - if (prob(80-radiation_duration)) - //testing("Random bad mut!") - randmutb(connected.occupant) - domutcheck(connected.occupant,connected) - else - randmuti(connected.occupant) - //testing("Random identity mut!") - connected.occupant.UpdateAppearance() if("ejectBeaker") + playsound(src, 'sound/machines/button.ogg', 30, 1, 0) if(connected.beaker) var/obj/item/reagent_containers/glass/B = connected.beaker - B.loc = connected.loc + B.forceMove(connected.loc) connected.beaker = null + return TRUE if("ejectOccupant") + playsound(src, 'sound/machines/button.ogg', 30, 1, 0) connected.eject_occupant() + // Eject disk too, because we can't get to the UI otherwise + if(!disk) + return TRUE + disk.forceMove(get_turf(src)) + disk = null // Transfer Buffer Management if("bufferOption") var/bufferOption = params["option"] var/bufferId = text2num(params["id"]) if(bufferId < 1 || bufferId > 3) // Not a valid buffer id - return + return TRUE - var/datum/dna2/record/buffer = buffers[bufferId] + var/datum/transhuman/body_record/buffer = buffers[bufferId] // Traitgenes Use bodyrecords switch(bufferOption) - if("saveUI") - if(connected.occupant && connected.occupant.dna) - var/datum/dna2/record/databuf=new - databuf.types = DNA2_BUF_UI // DNA2_BUF_UE - databuf.dna = connected.occupant.dna.Clone() - if(ishuman(connected.occupant)) - var/mob/living/carbon/human/H = connected.occupant - databuf.dna.real_name = H.dna.real_name - databuf.gender = H.gender - databuf.body_descriptors = H.descriptors - databuf.name = "Unique Identifier" - buffers[bufferId] = databuf - if("saveUIAndUE") - if(connected.occupant && connected.occupant.dna) - var/datum/dna2/record/databuf=new - databuf.types = DNA2_BUF_UI|DNA2_BUF_UE - databuf.dna = connected.occupant.dna.Clone() - if(ishuman(connected.occupant)) - var/mob/living/carbon/human/H = connected.occupant - databuf.dna.real_name = H.dna.real_name - databuf.gender = H.gender - databuf.body_descriptors = H.descriptors - databuf.name = "Unique Identifier + Unique Enzymes" - buffers[bufferId] = databuf - if("saveSE") - if(connected.occupant && connected.occupant.dna) - var/datum/dna2/record/databuf=new - databuf.types = DNA2_BUF_SE - databuf.dna = connected.occupant.dna.Clone() - if(ishuman(connected.occupant)) - var/mob/living/carbon/human/H = connected.occupant - databuf.dna.real_name = H.dna.real_name - databuf.gender = H.gender - databuf.body_descriptors = H.descriptors - databuf.name = "Structural Enzymes" + // Traitgenes Moved SE and UI saves to storing the entire body record + if("saveDNA") + playsound(src, "keyboard", 40) // into console + var/mob/living/carbon/WC = connected?.occupant?.resolve() + if(WC && WC.dna) + // Traitgenes Properly clone records + var/datum/transhuman/body_record/databuf = new /datum/transhuman/body_record() + databuf.init_from_mob(WC) + databuf.mydna.types = DNA2_BUF_SE // structurals only + if(ishuman(WC)) + var/mob/living/carbon/human/H = WC + databuf.mydna.dna.real_name = H.dna.real_name + databuf.mydna.gender = H.gender + databuf.mydna.body_descriptors = H.descriptors buffers[bufferId] = databuf + return TRUE if("clear") - buffers[bufferId] = new /datum/dna2/record() + playsound(src, "keyboard", 40) + // Traitgenes Storing the entire body record + var/datum/transhuman/body_record/R = new /datum/transhuman/body_record() + R.mydna = new + R.mydna.dna = new + R.mydna.dna.ResetUI() + R.mydna.dna.ResetSE() + buffers[bufferId] = R + return TRUE if("changeLabel") - tgui_modal_input(src, "changeBufferLabel", "Please enter the new buffer label:", null, list("id" = bufferId), buffer.name, TGUI_MODAL_INPUT_MAX_LENGTH_NAME) + playsound(src, "keyboard", 40) + tgui_modal_input(src, "changeBufferLabel", "Please enter the new buffer label:", null, list("id" = bufferId), buffer.mydna.name, TGUI_MODAL_INPUT_MAX_LENGTH_NAME) + return TRUE if("transfer") - if(!connected.occupant || (NOCLONE in connected.occupant.mutations) || !connected.occupant.dna) - return - + var/mob/living/carbon/WC = connected?.occupant?.resolve() + if(!WC || (NOCLONE in WC.mutations) || !WC.dna) + return TRUE irradiating = 2 var/lock_state = connected.locked connected.locked = 1//lock it - - SStgui.update_uis(src) // update all UIs attached to src - sleep(2 SECONDS) // sleep for 2 seconds - - irradiating = 0 - connected.locked = lock_state - - var/datum/dna2/record/buf = buffers[bufferId] - - if((buf.types & DNA2_BUF_UI)) - if((buf.types & DNA2_BUF_UE)) - connected.occupant.real_name = buf.dna.real_name - connected.occupant.name = buf.dna.real_name - if(ishuman(connected.occupant)) - var/mob/living/carbon/human/H = connected.occupant - H.gender = buf.gender - H.descriptors = buf.body_descriptors - connected.occupant.UpdateAppearance(buf.dna.UI.Copy()) - else if(buf.types & DNA2_BUF_SE) - connected.occupant.dna.SE = buf.dna.SE - connected.occupant.dna.UpdateSE() - if(ishuman(connected.occupant)) - var/mob/living/carbon/human/H = connected.occupant - H.gender = buf.gender - H.descriptors = buf.body_descriptors - domutcheck(connected.occupant,connected) - connected.occupant.apply_effect(rand(20,50), IRRADIATE, check_protection = 0) + addtimer(CALLBACK(src, PROC_REF(do_transfer), lock_state, bufferId), 2 SECONDS, TIMER_DELETE_ME) + return TRUE if("createInjector") if(!injector_ready) - return + return TRUE if(text2num(params["block"]) > 0) - var/list/choices = all_dna_blocks((buffer.types & DNA2_BUF_SE) ? buffer.dna.SE : buffer.dna.UI) + var/list/choices = all_dna_blocks(buffer.mydna.dna.SE) // Traitgenes Storing the entire body record, and no more using UIs tgui_modal_choice(src, "createInjectorBlock", "Please select the block to create an injector from:", null, list("id" = bufferId), null, choices) else create_injector(bufferId, TRUE) + return TRUE + // Traitgenes Storing the entire body record if("loadDisk") - if(isnull(disk) || disk.read_only) + playsound(src, "keyboard", 40) + if(isnull(disk) || !disk.stored) return - buffers[bufferId] = disk.buf.copy() + // Traitgenes Properly clone records + var/datum/transhuman/body_record/databuf = new /datum/transhuman/body_record() + databuf.init_from_br(disk.stored) + databuf.mydna.types = DNA2_BUF_SE // structurals only + buffers[bufferId] = databuf if("saveDisk") - if(isnull(disk) || disk.read_only) - return - var/datum/dna2/record/buf = buffers[bufferId] - disk.buf = buf.copy() - disk.name = "data disk - '[buf.dna.real_name]'" + playsound(src, "keyboard", 40) + if(isnull(disk)) // Traitgenes Removed readonly + return TRUE + var/datum/transhuman/body_record/buf = buffers[bufferId] + // Traitgenes Properly clone records + disk.stored = new /datum/transhuman/body_record() + disk.stored.init_from_br(buf) + disk.stored.mydna.types = DNA2_BUF_UI|DNA2_BUF_UE|DNA2_BUF_SE // DNA disks need to maintain their data + disk.name = "Body Design Disk ('[buf.mydna.name]')" + return TRUE + if("sleeveDisk") + playsound(src, "keyboard", 40) + var/datum/transhuman/body_record/buf = buffers[bufferId] + // Send printable record to first sleevepod in area + print_sleeve(usr, buf) + return TRUE if("wipeDisk") - if(isnull(disk) || disk.read_only) - return - disk.buf = null + playsound(src, "keyboard", 40) + // Traitgenes Storing the entire body record + if(isnull(disk)) + return TRUE + disk.stored = null + return TRUE if("ejectDisk") + playsound(src, 'sound/machines/button.ogg', 30, 1, 0) if(!disk) - return + return TRUE disk.forceMove(get_turf(src)) disk = null + return TRUE /** * Creates a blank injector with the name of the buffer at the given buffer_id @@ -739,12 +713,13 @@ addtimer(CALLBACK(src, PROC_REF(injector_cooldown_finish)), 30 SECONDS) // Create it - var/datum/dna2/record/buf = buffers[buffer_id] + var/datum/transhuman/body_record/buf = buffers[buffer_id] // Traitgenes Use bodyrecords var/obj/item/dnainjector/I = new() + buf.mydna.types = DNA2_BUF_SE // Traitgenes SE only, use the designer for UI and UEs, super broken in this codebase due to years of no one respecting genetics... I.forceMove(loc) - I.name += " ([buf.name])" + I.name += " ([buf.mydna.name])" if(copy_buffer) - I.buf = buf.copy() + I.buf = buf.mydna.copy() return I /** @@ -772,24 +747,159 @@ var/buffer_id = text2num(arguments["id"]) if(buffer_id < 1 || buffer_id > length(buffers)) return - var/datum/dna2/record/buf = buffers[buffer_id] + var/datum/transhuman/body_record/buf = buffers[buffer_id] // Traitgenes Use bodyrecords var/obj/item/dnainjector/I = create_injector(buffer_id) - setInjectorBlock(I, answer, buf.copy()) + setInjectorBlock(I, answer, buf.mydna.copy()) // Traitgenes Use bodyrecords + I.name += " - Block [answer]" // Traitgenes By default show the block of a block injector if("changeBufferLabel") var/buffer_id = text2num(arguments["id"]) if(buffer_id < 1 || buffer_id > length(buffers)) return - var/datum/dna2/record/buf = buffers[buffer_id] - buf.name = answer + var/datum/transhuman/body_record/buf = buffers[buffer_id] // Traitgenes Use bodyrecords + buf.mydna.name = answer // Traitgenes Use bodyrecords buffers[buffer_id] = buf else return FALSE else return FALSE + +/** + * Triggers sleeve growing in a clonepod within the area + * + * Arguments: + * * active_br - Body record to print + */ +/obj/machinery/computer/scan_consolenew/proc/print_sleeve(var/mob/user, var/datum/transhuman/body_record/active_br) + //deleted record + if(!istype(active_br)) + to_chat(user, span_danger( "Error: Data corruption.")) + return + //Trying to make an fbp + if(active_br.synthetic ) + to_chat(user, span_danger( "Error: Cannot grow synthetic.")) + return + //No pods + var/obj/machinery/clonepod/transhuman/pod = locate() in get_area(src) + if(!pod) + to_chat(user, span_danger( "Error: No growpods detected.")) + return + //Already doing someone. + if(pod.occupant) + to_chat(user, span_danger( "Error: Growpod is currently occupied.")) + return + //Not enough materials. + if(pod.get_biomass() < CLONE_BIOMASS) + to_chat(user, span_danger( "Error: Not enough biomass.")) + return + //Gross pod (broke mid-cloning or something). + if(pod.mess) + to_chat(user, span_danger( "Error: Growpod malfunction.")) + return + //Disabled in config. + if(!CONFIG_GET(flag/revival_cloning)) + to_chat(user, span_danger( "Error: Unable to initiate growing cycle.")) + return + //Invalid genes! + if(active_br.mydna.name == "Empty" || active_br.mydna.id == null) + to_chat(user, span_danger( "Error: Data corruption.")) + return + //Do the cloning! + if(!pod.growclone(active_br)) + to_chat(user, span_danger( "Initiating growing cycle... Error: Post-initialisation failed. Growing cycle aborted.")) + return + to_chat(user, span_notice( "Initiating growing cycle...")) + +/obj/machinery/computer/scan_consolenew/proc/do_irradiate(var/lock_state, var/block) + var/mob/living/carbon/WC = connected?.occupant?.resolve() + irradiating = 0 + connected.locked = lock_state + if(!WC) + return + + if(prob((80 + (radiation_duration / 2)))) + // FIXME: Find out what these corresponded to and change them to the WHATEVERBLOCK they need to be. + //if((selected_se_block != 2 || selected_se_block != 12 || selected_se_block != 8 || selected_se_block || 10) && prob (20)) + var/real_SE_block=selected_se_block + block = miniscramble(block, radiation_intensity, radiation_duration) + if(prob(20)) + if(selected_se_block > 1 && selected_se_block < DNA_SE_LENGTH/2) + real_SE_block++ + else if(selected_se_block > DNA_SE_LENGTH/2 && selected_se_block < DNA_SE_LENGTH) + real_SE_block-- + + //testing("Irradiated SE block [real_SE_block]:[selected_se_subblock] ([original_block] now [block]) [(real_SE_block!=selected_se_block) ? "(SHIFTED)":""]!") + WC.dna.SetSESubBlock(real_SE_block,selected_se_subblock,block) + WC.apply_effect((radiation_intensity+radiation_duration), IRRADIATE, check_protection = 0) + else + WC.apply_effect(((radiation_intensity*2)+radiation_duration), IRRADIATE, check_protection = 0) + if (prob(80-radiation_duration)) + //testing("Random bad mut!") + randmutb(WC) + // Traitgenes Do gene updates here, and more comprehensively + if(ishuman(WC)) + var/mob/living/carbon/human/H = WC + H.sync_dna_traits(FALSE,TRUE) + H.sync_organ_dna() + WC.regenerate_icons() + +/obj/machinery/computer/scan_consolenew/proc/do_pulse(var/lock_state) + var/mob/living/carbon/WC = connected?.occupant?.resolve() + irradiating = 0 + connected.locked = lock_state + + if(!WC) + return + // Traitgenes Make the fullbody irritation more risky + if(prob((radiation_intensity*2) + (radiation_duration*2))) + if(prob(95)) + if(prob(75)) + randmutb(WC) + else + if(prob(95)) + randmutg(WC) + // Traitgenes Do gene updates here, and more comprehensively + if(ishuman(WC)) + var/mob/living/carbon/human/H = WC + H.sync_dna_traits(FALSE,FALSE) + H.sync_organ_dna() + WC.regenerate_icons() + + WC.apply_effect(((radiation_intensity*3)+radiation_duration*3), IRRADIATE, check_protection = 0) + + +/obj/machinery/computer/scan_consolenew/proc/do_transfer(var/lock_state, var/bufferId) + irradiating = 0 + connected.locked = lock_state + + playsound(src, "keyboard", 40) + + var/mob/living/carbon/WC = connected?.occupant?.resolve() + if(!WC) + return TRUE + var/datum/transhuman/body_record/buf = buffers[bufferId] // Traitgenes- Use bodyrecords + if(buf.mydna.types & DNA2_BUF_SE) + // Apply SEs only to the current occupant! + WC.dna.SE = buf.mydna.dna.SE.Copy() + WC.dna.UpdateSE() + domutcheck(WC,connected, MUTCHK_FORCED | MUTCHK_HIDEMSG) // TOO MANY MUTATIONS FOR MESSAGES + to_chat(WC, span_warning("Your body stings as it wildly changes!")) + + // apply genes + if(ishuman(WC)) + var/mob/living/carbon/human/H = WC + H.sync_organ_dna() + + //Apply genetic modifiers + WC.dna.genetic_modifiers.Cut() // clear em! + for(var/modifier_type in buf.genetic_modifiers) + WC.add_modifier(modifier_type) + WC.apply_effect(rand(20,50), IRRADIATE, check_protection = 0) + + + #undef DNA_BLOCK_SIZE -#undef PAGE_UI #undef PAGE_SE #undef PAGE_BUFFER #undef PAGE_REJUVENATORS diff --git a/code/game/dna/genes/gene.dm b/code/game/dna/genes/gene.dm index 5bf3145b339..d7d6608a5a9 100644 --- a/code/game/dna/genes/gene.dm +++ b/code/game/dna/genes/gene.dm @@ -1,15 +1,4 @@ -/** -* Gene Datum -* -* domutcheck was getting pretty hairy. This is the solution. -* -* All genes are stored in a global variable to cut down on memory -* usage. -* -* @author N3X15 -*/ - -/datum/dna/gene +/datum/gene // Traitgenes Removed /dna/ from path... WHY WAS THIS A SUBTYPE OF DNA!? It's taking a huge struct and making 50 of them at startup, growing with every new var and list stuffed in /datum/dna - Willbird // Display name var/name="BASE GENE" @@ -23,100 +12,152 @@ // Any of a number of GENE_ flags. var/flags=0 + /** * Is the gene active in this mob's DNA? */ -/datum/dna/gene/proc/is_active(var/mob/M) - return (M.active_genes && (type in M.active_genes)) +/datum/gene/proc/is_active(var/mob/M) // Traitgenes edit - Removed /dna/ from path + return (M.active_genes && (name in M.active_genes)) // Traitgenes edit - Use name instead, cannot use type with dynamically setup traitgenes. It is always unique due to the block number being appended to it. // Return 1 if we can activate. // HANDLE MUTCHK_FORCED HERE! -/datum/dna/gene/proc/can_activate(var/mob/M, var/flags) +/datum/gene/proc/can_activate(var/mob/M, var/mut_flags) // Traitgenes edit - Removed /dna/ from path. mut_flags instead of flags for clarity return 0 // Called when the gene activates. Do your magic here. -/datum/dna/gene/proc/activate(var/mob/M, var/connected, var/flags) +/datum/gene/proc/activate(var/mob/M, var/connected, var/mut_flags) // Traitgenes edit - Removed /dna/ from path. mut_flags instead of flags for clarity return /** * Called when the gene deactivates. Undo your magic here. * Only called when the block is deactivated. */ -/datum/dna/gene/proc/deactivate(var/mob/M, var/connected, var/flags) +/datum/gene/proc/deactivate(var/mob/M, var/connected, var/mut_flags) // Traitgenes edit - Removed /dna/ from path. mut_flags instead of flags for clarity return -// This section inspired by goone's bioEffects. - -/** -* Called in each life() tick. -*/ -/datum/dna/gene/proc/OnMobLife(var/mob/M) - return - -/** -* Called when the mob dies -*/ -/datum/dna/gene/proc/OnMobDeath(var/mob/M) - return - -/** -* Called when the mob says shit -*/ -/datum/dna/gene/proc/OnSay(var/mob/M, var/message) - return message - -/** -* Called after the mob runs update_icons. -* -* @params M The subject. -* @params g Gender (m or f) -* @params fat Fat? (0 or 1) -*/ -/datum/dna/gene/proc/OnDrawUnderlays(var/mob/M, var/g, var/fat) - return 0 - - +// Traitgenes edit - Genes are linked to traits now. Because no one bothered to maintain genes, and instead jumped through two different trait systems to avoid them. So here we are. - Willbird ///////////////////// -// BASIC GENES +// TRAIT GENES // -// These just chuck in a mutation and display a message. -// -// Gene is activated: -// 1. If mutation already exists in mob -// 2. If the probability roll succeeds -// 3. Activation is forced (done in domutcheck) +// Activate traits with a message when enabled +// IMPORTANT - in 99% of situations you should NOT need to edit gene code when adding a new traitgene. Genes only handle the on/off state of traits, traits control the changes and behaviors! +// Just keep pretending genecode doesn't exist and you should be fine. Traitgenes were made with that in mind, and are not intended to be something you need to edit every time you add a traitgene. +// Traitgenes only require that your trait has both an apply() and unapply() if it does anything like adding verbs. Otherwise, you don't even need to add trait exceptions. traitgenes handle it automatically. +// You probably shouldn't mark traits as traitgenes if they are custom species only, species locked, or species banned traits however... - Willbird ///////////////////// -/datum/dna/gene/basic - name="BASIC GENE" +/datum/gene/trait + desc="Gene linked to a trait." + var/datum/trait/linked_trait = null // Internal use, do not assign. + var/list/conflict_traits = list() // Cache known traits that don't work with this one, instead of doing it all at once, or EVERY time we do a mutation check - // Mutation to give - var/mutation=0 +/datum/gene/trait/Destroy() + // unlink circular reference + if(linked_trait) + linked_trait.linked_gene = null + linked_trait = null + . = ..() - // Activation probability - var/activation_prob=45 +// Use these when displaying info to players +/datum/gene/trait/proc/get_name() + if(linked_trait) + return linked_trait.name + return name - // Possible activation messages - var/list/activation_messages=list() +/datum/gene/trait/proc/get_desc() + if(linked_trait) + return linked_trait.desc + return desc - // Possible deactivation messages - var/list/deactivation_messages=list() +/datum/gene/trait/can_activate(var/mob/M,var/mut_flags) + return TRUE // We don't do probability checks for trait genes, due to the spiderweb of logic they are with conflicting genes. Check has_conflicts() for how conflicting traits are handled. -/datum/dna/gene/basic/can_activate(var/mob/M,var/flags) - if(flags & MUTCHK_FORCED) - return 1 - // Probability check - return probinj(activation_prob,(flags&MUTCHK_FORCED)) +/** + * Behold the CONFLICT-O-TRON. Checks for trait conflicts the same way code\modules\client\preference_setup\vore\07_traits.dm does, + * and then caches the results. Cause traits can't change conflicts mid-round. Unless that changes someday, god help us all if so. + * You do not need to add any unique exceptions here, check trait code instead if you want to handle conflicts and forced exceptions. + * They already handle it. This just uses the same system. - Willbird + * + * quick_scan = FALSE, will do a deep conflict check. Checking every trait in the list, and not just failing out at the first one that conflicts. + * + * Recommended way to write has_conflict calls: + * ``` + * has_conflict(traits_to_check = list(trait_path)) + * ``` + */ +/datum/gene/trait/proc/has_conflict(var/list/traits_to_check, var/quick_scan = TRUE) + var/has_conflict = FALSE + var/path = linked_trait.type + for(var/P in traits_to_check) + // don't get triggered by self + if(P == path) + continue -/datum/dna/gene/basic/activate(var/mob/M) - M.mutations.Add(mutation) - if(activation_messages.len) - var/msg = pick(activation_messages) - to_chat(M, span_notice("[msg]")) + // check if cached first... + if(!isnull(conflict_traits[P])) + if(quick_scan && conflict_traits[P]) + return TRUE + continue -/datum/dna/gene/basic/deactivate(var/mob/M) - M.mutations.Remove(mutation) - if(deactivation_messages.len) - var/msg = pick(deactivation_messages) - to_chat(M, span_warning("[msg]")) + // check trait if not. CONFLICT-O-TRON ENGAGE + conflict_traits[P] = FALSE + + var/datum/trait/instance_test = all_traits[P] + if(path in instance_test.excludes) + conflict_traits[P] = TRUE + has_conflict = TRUE + // depending on scan mode we want to scan all, or only the first failure + if(quick_scan) + return TRUE + continue + for(var/V in linked_trait.var_changes) + if(V == "flags") + continue + if(V in instance_test.var_changes) + conflict_traits[P] = TRUE + has_conflict = TRUE + // depending on scan mode we want to scan all, or only the first failure + if(quick_scan) + return TRUE + continue + for(var/V in linked_trait.var_changes_pref) + if(V in instance_test.var_changes_pref) + conflict_traits[P] = TRUE + has_conflict = TRUE + // depending on scan mode we want to scan all, or only the first failure + if(quick_scan) + return TRUE + continue + return has_conflict + +/datum/gene/trait/activate(var/mob/M, var/connected, var/mut_flags) + if(linked_trait && ishuman(M)) + var/mob/living/carbon/human/H = M + if(H.species) // Lets avoid runtime assertions + // Add trait + if(linked_trait.type in H.species.traits) + return + linked_trait.apply( H.species, H, H.species.traits[linked_trait.type]) + H.species.traits.Add(linked_trait.type) + if(!(linked_trait.type in H.dna.species_traits)) // Set species traits too + H.dna.species_traits.Add(linked_trait.type) + // message player with change + if(!(mut_flags & MUTCHK_HIDEMSG)) + linked_trait.send_message( H, TRUE) + +/datum/gene/trait/deactivate(var/mob/M, var/connected, var/mut_flags) + if(linked_trait && ishuman(M)) + var/mob/living/carbon/human/H = M + if(H.species) // Lets avoid runtime assertions + // Remove trait + if(!(linked_trait.type in H.species.traits)) + return + linked_trait.unapply( H.species, H, H.species.traits[linked_trait.type]) + linked_trait.remove(H.species) // Does nothing, but may as well call it because it exists and has a place now + H.species.traits.Remove(linked_trait.type) + if(linked_trait.type in H.dna.species_traits) // Clear species traits too + H.dna.species_traits.Remove(linked_trait.type) + // message player with change + if(!(mut_flags & MUTCHK_HIDEMSG)) + linked_trait.send_message( H, FALSE) diff --git a/code/game/gamemodes/changeling/powers/transform.dm b/code/game/gamemodes/changeling/powers/transform.dm index 69a7fe6fe91..ea0fb2c1bc2 100644 --- a/code/game/gamemodes/changeling/powers/transform.dm +++ b/code/game/gamemodes/changeling/powers/transform.dm @@ -35,7 +35,7 @@ if(ishuman(src)) var/mob/living/carbon/human/H = src var/newSpecies = chosen_dna.speciesName - H.set_species(newSpecies,1) + H.set_species(newSpecies) qdel_swap(src.dna, chosen_dna.dna.Clone()) src.dna.b_type = "AB+" //This is needed to avoid blood rejection bugs. The fact that the blood type might not match up w/ records could be a *FEATURE* too. diff --git a/code/game/gamemodes/setupgame.dm b/code/game/gamemodes/setupgame.dm index b2cb6a6a6c4..db935e35a4f 100644 --- a/code/game/gamemodes/setupgame.dm +++ b/code/game/gamemodes/setupgame.dm @@ -2,82 +2,63 @@ // (mostly) DNA2 SETUP ///////////////////////// -// Randomize block, assign a reference name, and optionally define difficulty (by making activation zone smaller or bigger) -// The name is used on /vg/ for species with predefined genetic traits, -// and for the DNA panel in the player panel. -/proc/getAssignedBlock(var/name,var/list/blocksLeft, var/activity_bounds=DNA_DEFAULT_BOUNDS) - if(blocksLeft.len==0) - warning("[name]: No more blocks left to assign!") - return 0 - var/assigned = pick(blocksLeft) - blocksLeft.Remove(assigned) - assigned_blocks[assigned]=name - dna_activity_bounds[assigned]=activity_bounds - //testing("[name] assigned to block #[assigned].") - return assigned - -/proc/setupgenetics() - - if (prob(50)) - // Currently unused. Will revisit. - N3X - BLOCKADD = rand(-300,300) - if (prob(75)) - DIFFMUT = rand(0,20) - - var/list/numsToAssign=new() - for(var/i=1;i") + else + dat += "There is no known treatment.
" OX = M.getOxyLoss() > 50 ? "[span_cyan(span_bold("Severe oxygen deprivation detected"))]" : "Subject bloodstream oxygen level normal" TX = M.getToxLoss() > 50 ? "[span_green(span_bold("Dangerous amount of toxins detected"))]" : "Subject bloodstream toxin level minimal" diff --git a/code/game/objects/items/weapons/dna_injector.dm b/code/game/objects/items/weapons/dna_injector.dm index 8a0a4493f82..9ec77026089 100644 --- a/code/game/objects/items/weapons/dna_injector.dm +++ b/code/game/objects/items/weapons/dna_injector.dm @@ -5,21 +5,22 @@ icon_state = "dnainjector" var/block=0 var/datum/dna2/record/buf=null - var/s_time = 10.0 throw_speed = 1 throw_range = 5 w_class = ITEMSIZE_TINY slot_flags = SLOT_EARS var/uses = 1 var/nofail - var/is_bullet = 0 - var/inuse = 0 // USE ONLY IN PREMADE SYRINGES. WILL NOT WORK OTHERWISE. var/datatype=0 var/value=0 -/obj/item/dnainjector/New() + // Traitgenes edit begin - Removed subtype, replaced with flag. Allows for safe injectors. Mostly for admin usage. + var/has_radiation = TRUE + // Traitgenes edit end + +/obj/item/dnainjector/Initialize() // Traitgenes edit - Moved to init if(datatype && block) buf=new buf.dna=new @@ -28,6 +29,7 @@ //testing("[name]: DNA2 SE blocks prior to SetValue: [english_list(buf.dna.SE)]") SetValue(src.value) //testing("[name]: DNA2 SE blocks after SetValue: [english_list(buf.dna.SE)]") + . = ..() // Traitgenes edit - Moved to init /obj/item/dnainjector/proc/GetRealBlock(var/selblock) if(selblock==0) @@ -64,38 +66,54 @@ return buf.dna.SetUIValue(real_block,val) /obj/item/dnainjector/proc/inject(mob/M as mob, mob/user as mob) - if(isliving(M)) + if(isliving(M) && has_radiation) var/mob/living/L = M L.apply_effect(rand(5,20), IRRADIATE, check_protection = 0) L.apply_damage(max(2,L.getCloneLoss()), CLONE) - if (!(NOCLONE in M.mutations)) // prevents drained people from having their DNA changed - if (buf.types & DNA2_BUF_UI) - if (!block) //isolated block? - M.UpdateAppearance(buf.dna.UI.Copy()) - if (buf.types & DNA2_BUF_UE) //unique enzymes? yes - M.real_name = buf.dna.real_name - M.name = buf.dna.real_name + // Traitgenes edit begin - NO_SCAN and Synthetics cannot be mutated + var/allow = TRUE + if(M.isSynthetic()) + allow = FALSE + if(ishuman(M)) + var/mob/living/carbon/human/H = M + if(!H.species || H.species.flags & NO_SCAN) + allow = FALSE + // Traitgenes edit end + if (!(NOCLONE in M.mutations) && allow) // prevents drained people from having their DNA changed, Traitgenes edit - NO_SCAN and Synthetics cannot be mutated + if(buf) + if (buf.types & DNA2_BUF_UI) + if (!block) //isolated block? + M.UpdateAppearance(buf.dna.UI.Copy()) + if (buf.types & DNA2_BUF_UE) //unique enzymes? yes + M.real_name = buf.dna.real_name + M.name = buf.dna.real_name + uses-- + else + M.dna.SetUIValue(block,src.GetValue()) + M.UpdateAppearance() + uses-- + if (buf.types & DNA2_BUF_SE) + if (!block) //isolated block? + M.dna.SE = buf.dna.SE.Copy() + M.dna.UpdateSE() + else + M.dna.SetSEValue(block,src.GetValue()) uses-- - else - M.dna.SetUIValue(block,src.GetValue()) - M.UpdateAppearance() - uses-- - if (buf.types & DNA2_BUF_SE) - if (!block) //isolated block? - M.dna.SE = buf.dna.SE.Copy() - M.dna.UpdateSE() - else - M.dna.SetSEValue(block,src.GetValue()) - domutcheck(M, null, block!=null) - uses-- - if(prob(5)) - trigger_side_effect(M) + // Traitgenes edit - Moved gene checks to after side effects + if(prob(5)) + trigger_side_effect(M) + // Traitgenes edit begin - Do gene updates here, and more comprehensively + if(ishuman(M)) + var/mob/living/carbon/human/H = M + H.sync_dna_traits(FALSE,FALSE) + H.sync_organ_dna() + M.regenerate_icons() + // Traitgenes edit end - spawn(0)//this prevents the collapse of space-time continuum - if (user) - user.drop_from_inventory(src) - qdel(src) + if (user) + user.drop_from_inventory(src) + INVOKE_ASYNC(GLOBAL_PROC, GLOBAL_PROC_REF(qdel), src) return uses /obj/item/dnainjector/attack(mob/M as mob, mob/user as mob) @@ -103,18 +121,19 @@ return if (!user.IsAdvancedToolUser()) return - if(inuse) - return 0 + if (in_use) + return user.visible_message(span_danger("\The [user] is trying to inject \the [M] with \the [src]!")) - inuse = 1 - s_time = world.time - spawn(50) - inuse = 0 + in_use = TRUE + + //addtimer(VARSET_CALLBACK(src, in_use , FALSE), 5 SECONDS, TIMER_DELETE_ME) //Leaving this for reference of how to do the timer here if do_after wasn't present. if(!do_after(user,50)) + in_use = FALSE return + user.setClickCooldown(DEFAULT_QUICK_COOLDOWN) user.do_attack_animation(M) @@ -125,454 +144,126 @@ to_chat(user, span_warning("Apparently it didn't work...")) return - // Used by admin log. - var/injected_with_monkey = "" - if((buf.types & DNA2_BUF_SE) && (block ? (GetState() && block == MONKEYBLOCK) : GetState(MONKEYBLOCK))) - injected_with_monkey = span_danger("(MONKEY)") - - add_attack_logs(user,M,"[injected_with_monkey] used the [name] on") - - // Apply the DNA shit. inject(M, user) return -/obj/item/dnainjector/hulkmut - name = "\improper DNA injector (Hulk)" - desc = "This will make you big and strong, but give you a bad skin condition." - datatype = DNA2_BUF_SE - value = 0xFFF -/obj/item/dnainjector/hulkmut/New() - block = HULKBLOCK - ..() +// Traitgenes Injectors are randomized now due to no hardcoded genes. Split into good or bad, and then versions that specify what they do on the label. +// Otherwise scroll down further for how to make unique injectors +/obj/item/dnainjector/proc/pick_block(var/datum/gene/trait/G, var/labeled, var/allow_disable) + if(G) + block = G.block + datatype = DNA2_BUF_SE + value = 0xFFF + if(allow_disable) + value = pick(0x000,0xFFF) + if(labeled) + name = initial(name) + " - [value == 0x000 ? "Removes" : ""] [G.get_name()]" + +/obj/item/dnainjector/random + name = "\improper DNA injector" + desc = "This injects the person with DNA." + +// Purely rando +/obj/item/dnainjector/random/Initialize() + pick_block( pick(GLOB.dna_genes_good + GLOB.dna_genes_neutral + GLOB.dna_genes_bad), FALSE, TRUE) + . = ..() + +/obj/item/dnainjector/random_labeled/Initialize() + pick_block( pick(GLOB.dna_genes_good + GLOB.dna_genes_neutral + GLOB.dna_genes_bad), TRUE, TRUE) + . = ..() + +// Good/bad but also neutral genes mixed in, less OP selection of genes +/obj/item/dnainjector/random_good/Initialize() + pick_block( pick(GLOB.dna_genes_good + GLOB.dna_genes_neutral ), FALSE, TRUE) + . = ..() + +/obj/item/dnainjector/random_good_labeled/Initialize() + pick_block( pick(GLOB.dna_genes_good + GLOB.dna_genes_neutral ), TRUE, TRUE) + . = ..() + +/obj/item/dnainjector/random_bad/Initialize() + pick_block( pick(GLOB.dna_genes_bad + GLOB.dna_genes_neutral ), FALSE, TRUE) + . = ..() + +/obj/item/dnainjector/random_bad_labeled/Initialize() + pick_block( pick(GLOB.dna_genes_bad + GLOB.dna_genes_neutral ), TRUE, TRUE) + . = ..() + +// Purely good/bad genes, intended to be usually good rewards or punishments +/obj/item/dnainjector/random_verygood/Initialize() + pick_block( pick(GLOB.dna_genes_good), FALSE, FALSE) + . = ..() + +/obj/item/dnainjector/random_verygood_labeled/Initialize() + pick_block( pick(GLOB.dna_genes_good), TRUE, FALSE) + . = ..() + +/obj/item/dnainjector/random_verybad/Initialize() + pick_block( pick(GLOB.dna_genes_bad), FALSE, FALSE) + . = ..() + +/obj/item/dnainjector/random_verybad_labeled/Initialize() + pick_block( pick(GLOB.dna_genes_bad), TRUE, FALSE) + . = ..() + +// Random neutral traits +/obj/item/dnainjector/random_neutral/Initialize() + pick_block( pick(GLOB.dna_genes_neutral ), FALSE, TRUE) + . = ..() + +/obj/item/dnainjector/random_neutral_labeled/Initialize() + pick_block( pick(GLOB.dna_genes_neutral ), TRUE, TRUE) + . = ..() + +// If you want a unique injector, use a subtype of these +/obj/item/dnainjector/set_trait + var/trait_path + +/obj/item/dnainjector/set_trait/Initialize() + var/G = get_gene_from_trait(trait_path) + if(trait_path && G) + pick_block( G, TRUE, FALSE) + else + qdel(src) + return + . = ..() + +// Only has the superpowers for loot tables and other rewards +/obj/item/dnainjector/set_trait/hulk + trait_path = /datum/trait/positive/superpower_hulk + +/obj/item/dnainjector/set_trait/xray + trait_path = /datum/trait/positive/superpower_xray + +/obj/item/dnainjector/set_trait/tk + trait_path = /datum/trait/positive/superpower_tk + +/obj/item/dnainjector/set_trait/remotetalk + trait_path = /datum/trait/positive/superpower_remotetalk + +/obj/item/dnainjector/set_trait/coldadapt + trait_path = /datum/trait/neutral/coldadapt + +/obj/item/dnainjector/set_trait/hotadapt + trait_path = /datum/trait/neutral/hotadapt + +/obj/item/dnainjector/set_trait/nobreathe + trait_path = /datum/trait/positive/superpower_nobreathe + +/obj/item/dnainjector/set_trait/regenerate + trait_path = /datum/trait/positive/superpower_regenerate + +/obj/item/dnainjector/set_trait/haste + trait_path = /datum/trait/positive/speed_fast -/obj/item/dnainjector/antihulk - name = "\improper DNA injector (Anti-Hulk)" - desc = "Cures green skin." - datatype = DNA2_BUF_SE - value = 0x001 +/obj/item/dnainjector/set_trait/flashproof + trait_path = /datum/trait/positive/superpower_flashproof -/obj/item/dnainjector/antihulk/New() - block = HULKBLOCK - ..() +/* Not a gene on virgo +/obj/item/dnainjector/set_trait/nonconduct + trait_path = /datum/trait/positive/nonconductive_plus +*/ -/obj/item/dnainjector/xraymut - name = "\improper DNA injector (Xray)" - desc = "Finally you can see what the " + JOB_SITE_MANAGER + " does." - datatype = DNA2_BUF_SE - value = 0xFFF - -/obj/item/dnainjector/xraymut/New() - block = XRAYBLOCK - ..() - -/obj/item/dnainjector/antixray - name = "\improper DNA injector (Anti-Xray)" - desc = "It will make you see harder." - datatype = DNA2_BUF_SE - value = 0x001 - -/obj/item/dnainjector/antixray/New() - block = XRAYBLOCK - ..() - -/obj/item/dnainjector/firemut - name = "\improper DNA injector (Fire)" - desc = "Gives you fire." - datatype = DNA2_BUF_SE - value = 0xFFF - -/obj/item/dnainjector/firemut/New() - block = FIREBLOCK - ..() - -/obj/item/dnainjector/antifire - name = "\improper DNA injector (Anti-Fire)" - desc = "Cures fire." - datatype = DNA2_BUF_SE - value = 0x001 - -/obj/item/dnainjector/antifire/New() - block = FIREBLOCK - ..() - -/obj/item/dnainjector/telemut - name = "\improper DNA injector (Tele.)" - desc = "Super brain man!" - datatype = DNA2_BUF_SE - value = 0xFFF - -/obj/item/dnainjector/telemut/New() - block = TELEBLOCK - ..() - -/obj/item/dnainjector/antitele - name = "\improper DNA injector (Anti-Tele.)" - desc = "Will make you not able to control your mind." - datatype = DNA2_BUF_SE - value = 0x001 - -/obj/item/dnainjector/antitele/New() - block = TELEBLOCK - ..() - -/obj/item/dnainjector/nobreath - name = "\improper DNA injector (No Breath)" - desc = "Hold your breath and count to infinity." - datatype = DNA2_BUF_SE - value = 0xFFF - -/obj/item/dnainjector/nobreath/New() - block = NOBREATHBLOCK - ..() - -/obj/item/dnainjector/antinobreath - name = "\improper DNA injector (Anti-No Breath)" - desc = "Hold your breath and count to 100." - datatype = DNA2_BUF_SE - value = 0x001 - -/obj/item/dnainjector/antinobreath/New() - block = NOBREATHBLOCK - ..() - -/obj/item/dnainjector/remoteview - name = "\improper DNA injector (Remote View)" - desc = "Stare into the distance for a reason." - datatype = DNA2_BUF_SE - value = 0xFFF - -/obj/item/dnainjector/remoteview/New() - block = REMOTEVIEWBLOCK - ..() - -/obj/item/dnainjector/antiremoteview - name = "\improper DNA injector (Anti-Remote View)" - desc = "Cures green skin." - datatype = DNA2_BUF_SE - value = 0x001 - -/obj/item/dnainjector/antiremoteview/New() - block = REMOTEVIEWBLOCK - ..() - -/obj/item/dnainjector/regenerate - name = "\improper DNA injector (Regeneration)" - desc = "Healthy but hungry." - datatype = DNA2_BUF_SE - value = 0xFFF - -/obj/item/dnainjector/regenerate/New() - block = REGENERATEBLOCK - ..() - -/obj/item/dnainjector/antiregenerate - name = "\improper DNA injector (Anti-Regeneration)" - desc = "Sickly but sated." - datatype = DNA2_BUF_SE - value = 0x001 - -/obj/item/dnainjector/antiregenerate/New() - block = REGENERATEBLOCK - ..() - -/obj/item/dnainjector/runfast - name = "\improper DNA injector (Increase Run)" - desc = "Running Man." - datatype = DNA2_BUF_SE - value = 0xFFF - -/obj/item/dnainjector/runfast/New() - block = INCREASERUNBLOCK - ..() - -/obj/item/dnainjector/antirunfast - name = "\improper DNA injector (Anti-Increase Run)" - desc = "Walking Man." - datatype = DNA2_BUF_SE - value = 0x001 - -/obj/item/dnainjector/antirunfast/New() - block = INCREASERUNBLOCK - ..() - -/obj/item/dnainjector/morph - name = "\improper DNA injector (Morph)" - desc = "A total makeover." - datatype = DNA2_BUF_SE - value = 0xFFF - -/obj/item/dnainjector/morph/New() - block = MORPHBLOCK - ..() - -/obj/item/dnainjector/antimorph - name = "\improper DNA injector (Anti-Morph)" - desc = "Cures identity crisis." - datatype = DNA2_BUF_SE - value = 0x001 - -/obj/item/dnainjector/antimorph/New() - block = MORPHBLOCK - ..() - -/obj/item/dnainjector/noprints - name = "\improper DNA injector (No Prints)" - desc = "Better than a pair of budget insulated gloves." - datatype = DNA2_BUF_SE - value = 0xFFF - -/obj/item/dnainjector/noprints/New() - block = NOPRINTSBLOCK - ..() - -/obj/item/dnainjector/antinoprints - name = "\improper DNA injector (Anti-No Prints)" - desc = "Not quite as good as a pair of budget insulated gloves." - datatype = DNA2_BUF_SE - value = 0x001 - -/obj/item/dnainjector/antinoprints/New() - block = NOPRINTSBLOCK - ..() - -/obj/item/dnainjector/insulation - name = "\improper DNA injector (Shock Immunity)" - desc = "Better than a pair of real insulated gloves." - datatype = DNA2_BUF_SE - value = 0xFFF - -/obj/item/dnainjector/insulation/New() - block = SHOCKIMMUNITYBLOCK - ..() - -/obj/item/dnainjector/antiinsulation - name = "\improper DNA injector (Anti-Shock Immunity)" - desc = "Not quite as good as a pair of real insulated gloves." - datatype = DNA2_BUF_SE - value = 0x001 - -/obj/item/dnainjector/antiinsulation/New() - block = SHOCKIMMUNITYBLOCK - ..() - -/obj/item/dnainjector/midgit - name = "\improper DNA injector (Small Size)" - desc = "Makes you shrink." - datatype = DNA2_BUF_SE - value = 0xFFF - -/obj/item/dnainjector/midgit/New() - block = SMALLSIZEBLOCK - ..() - -/obj/item/dnainjector/antimidgit - name = "\improper DNA injector (Anti-Small Size)" - desc = "Makes you grow. But not too much." - datatype = DNA2_BUF_SE - value = 0x001 - -/obj/item/dnainjector/antimidgit/New() - block = SMALLSIZEBLOCK - ..() - -///////////////////////////////////// -/obj/item/dnainjector/antiglasses - name = "\improper DNA injector (Anti-Glasses)" - desc = "Toss away those glasses!" - datatype = DNA2_BUF_SE - value = 0x001 - -/obj/item/dnainjector/antiglasses/New() - block = GLASSESBLOCK - ..() - -/obj/item/dnainjector/glassesmut - name = "\improper DNA injector (Glasses)" - desc = "Will make you need dorkish glasses." - datatype = DNA2_BUF_SE - value = 0xFFF - -/obj/item/dnainjector/glassesmut/New() - block = GLASSESBLOCK - ..() - -/obj/item/dnainjector/epimut - name = "\improper DNA injector (Epi.)" - desc = "Shake shake shake the room!" - datatype = DNA2_BUF_SE - value = 0xFFF - -/obj/item/dnainjector/epimut/New() - block = HEADACHEBLOCK - ..() - -/obj/item/dnainjector/antiepi - name = "\improper DNA injector (Anti-Epi.)" - desc = "Will fix you up from shaking the room." - datatype = DNA2_BUF_SE - value = 0x001 - -/obj/item/dnainjector/antiepi/New() - block = HEADACHEBLOCK - ..() - -/obj/item/dnainjector/anticough - name = "\improper DNA injector (Anti-Cough)" - desc = "Will stop that awful noise." - datatype = DNA2_BUF_SE - value = 0x001 - -/obj/item/dnainjector/anticough/New() - block = COUGHBLOCK - ..() - -/obj/item/dnainjector/coughmut - name = "\improper DNA injector (Cough)" - desc = "Will bring forth a sound of horror from your throat." - datatype = DNA2_BUF_SE - value = 0xFFF - -/obj/item/dnainjector/coughmut/New() - block = COUGHBLOCK - ..() - -/obj/item/dnainjector/clumsymut - name = "\improper DNA injector (Clumsy)" - desc = "Makes clumsy minions." - datatype = DNA2_BUF_SE - value = 0xFFF - -/obj/item/dnainjector/clumsymut/New() - block = CLUMSYBLOCK - ..() - -/obj/item/dnainjector/anticlumsy - name = "\improper DNA injector (Anti-Clumy)" - desc = "Cleans up confusion." - datatype = DNA2_BUF_SE - value = 0x001 - -/obj/item/dnainjector/anticlumsy/New() - block = CLUMSYBLOCK - ..() - -/obj/item/dnainjector/antitour - name = "\improper DNA injector (Anti-Tour.)" - desc = "Will cure tourrets." - datatype = DNA2_BUF_SE - value = 0x001 - -/obj/item/dnainjector/antitour/New() - block = TWITCHBLOCK - ..() - -/obj/item/dnainjector/tourmut - name = "\improper DNA injector (Tour.)" - desc = "Gives you a nasty case off tourrets." - datatype = DNA2_BUF_SE - value = 0xFFF - -/obj/item/dnainjector/tourmut/New() - block = TWITCHBLOCK - ..() - -/obj/item/dnainjector/stuttmut - name = "\improper DNA injector (Stutt.)" - desc = "Makes you s-s-stuttterrr" - datatype = DNA2_BUF_SE - value = 0xFFF - -/obj/item/dnainjector/stuttmut/New() - block = NERVOUSBLOCK - ..() - -/obj/item/dnainjector/antistutt - name = "\improper DNA injector (Anti-Stutt.)" - desc = "Fixes that speaking impairment." - datatype = DNA2_BUF_SE - value = 0x001 - -/obj/item/dnainjector/antistutt/New() - block = NERVOUSBLOCK - ..() - -/obj/item/dnainjector/blindmut - name = "\improper DNA injector (Blind)" - desc = "Makes you not see anything." - datatype = DNA2_BUF_SE - value = 0xFFF - -/obj/item/dnainjector/blindmut/New() - block = BLINDBLOCK - ..() - -/obj/item/dnainjector/antiblind - name = "\improper DNA injector (Anti-Blind)" - desc = "ITS A MIRACLE!!!" - datatype = DNA2_BUF_SE - value = 0x001 - -/obj/item/dnainjector/antiblind/New() - block = BLINDBLOCK - ..() - -/obj/item/dnainjector/deafmut - name = "\improper DNA injector (Deaf)" - desc = "Sorry, what did you say?" - datatype = DNA2_BUF_SE - value = 0xFFF - -/obj/item/dnainjector/deafmut/New() - block = DEAFBLOCK - ..() - -/obj/item/dnainjector/antideaf - name = "\improper DNA injector (Anti-Deaf)" - desc = "Will make you hear once more." - datatype = DNA2_BUF_SE - value = 0x001 - -/obj/item/dnainjector/antideaf/New() - block = DEAFBLOCK - ..() - -/obj/item/dnainjector/hallucination - name = "\improper DNA injector (Halluctination)" - desc = "What you see isn't always what you get." - datatype = DNA2_BUF_SE - value = 0xFFF - -/obj/item/dnainjector/hallucination/New() - block = HALLUCINATIONBLOCK - ..() - -/obj/item/dnainjector/antihallucination - name = "\improper DNA injector (Anti-Hallucination)" - desc = "What you see is what you get." - datatype = DNA2_BUF_SE - value = 0x001 - -/obj/item/dnainjector/antihallucination/New() - block = HALLUCINATIONBLOCK - ..() - -/obj/item/dnainjector/h2m - name = "\improper DNA injector (Human > Monkey)" - desc = "Will make you a flea bag." - datatype = DNA2_BUF_SE - value = 0xFFF - -/obj/item/dnainjector/h2m/New() - block = MONKEYBLOCK - ..() - -/obj/item/dnainjector/m2h - name = "\improper DNA injector (Monkey > Human)" - desc = "Will make you...less hairy." - datatype = DNA2_BUF_SE - value = 0x001 - -/obj/item/dnainjector/m2h/New() - block = MONKEYBLOCK - ..() +/obj/item/dnainjector/set_trait/table_passer + trait_path = /datum/trait/positive/table_passer diff --git a/code/game/objects/items/weapons/storage/boxes.dm b/code/game/objects/items/weapons/storage/boxes.dm index 2cc7b03fc75..e66059ae9c5 100644 --- a/code/game/objects/items/weapons/storage/boxes.dm +++ b/code/game/objects/items/weapons/storage/boxes.dm @@ -150,9 +150,11 @@ name = "box of DNA injectors" desc = "This box contains injectors it seems." icon_state = "dna" + // Traitgenes New injector loot starts_with = list( - /obj/item/dnainjector/h2m = 3, - /obj/item/dnainjector/m2h = 3 + /obj/item/dnainjector/random_good_labeled = 2, + /obj/item/dnainjector/random_neutral_labeled = 2, + /obj/item/dnainjector/random_labeled = 2 ) /obj/item/storage/box/flashbangs @@ -225,8 +227,8 @@ /obj/item/storage/box/flare name = "box of flares" - desc = "A box containing 4 flares." - starts_with = list(/obj/item/flashlight/flare = 4) + desc = "A box containing 14 flares." + starts_with = list(/obj/item/flashlight/flare = 14) /obj/item/storage/box/trackimp name = "boxed tracking implant kit" diff --git a/code/game/objects/structures/crates_lockers/closets/secure/secure_closets.dm b/code/game/objects/structures/crates_lockers/closets/secure/secure_closets.dm index 5cbb4ec39f6..e49720f98f4 100644 --- a/code/game/objects/structures/crates_lockers/closets/secure/secure_closets.dm +++ b/code/game/objects/structures/crates_lockers/closets/secure/secure_closets.dm @@ -156,3 +156,46 @@ broken = 1 locked = 0 ..() + +/obj/structure/closet/secure_closet/mind + name = "mind secured locker" + var/datum/mind/owner + var/self_del = 1 + anchored = 0 + +/obj/structure/closet/secure_closet/mind/New(var/datum/mind/mind_target, var/del_self = 1) + .=..() + self_del = del_self + if(mind_target) + owner = mind_target + name = "Owned by [owner.name]" + if(owner.current) + var/icon/I = get_flat_icon(owner.current, dir=SOUTH, no_anim=TRUE) + var/image/IM = image(I, pixel_x = (32 - I.Width())) + //icon2base64(get_flat_icon(owner.current,dir=SOUTH,no_anim=TRUE)) + /* + I.appearance_flags |= (RESET_COLOR|PIXEL_SCALE) + I.plane = MOB_PLANE + I.layer = MOB_LAYER + */ + add_overlay(IM) + qdel(I) + +/obj/structure/closet/secure_closet/mind/allowed(mob/user) + if(user.mind == owner) + return TRUE + else + return FALSE + +/obj/structure/closet/secure_closet/mind/open() + .=..() + if(self_del) + qdel(src) + +/obj/structure/closet/secure_closet/mind/LateInitialize() + if(ispath(closet_appearance)) + closet_appearance = GLOB.closet_appearances[closet_appearance] + if(istype(closet_appearance)) + icon = closet_appearance.icon + color = null + update_icon() diff --git a/code/modules/admin/admin.dm b/code/modules/admin/admin.dm index c4d6866767a..7e727d5a72a 100644 --- a/code/modules/admin/admin.dm +++ b/code/modules/admin/admin.dm @@ -141,15 +141,33 @@ var/global/floorIsLava = 0 body += "

" body += "DNA Blocks:
" var/bname - for(var/block=1;block<=DNA_SE_LENGTH;block++) + var/list/output_list = list() + // Traitgenes more reliable way to check gene states + for(var/setup_block=1;setup_block<=DNA_SE_LENGTH;setup_block++) + output_list["[setup_block]"] = null + for(var/datum/gene/gene in GLOB.dna_genes) // Traitgenes Genes accessible by global VV. Removed /dna/ from path + output_list["[gene.block]"] = gene + for(var/block=1;block<=DNA_SE_LENGTH;block++) // Traitgenes more reliable way to check gene states + var/datum/gene/gene = output_list["[block]"] // Traitgenes Removed /dna/ from path if(((block-1)%5)==0) body += "" - bname = assigned_blocks[block] + // Traitgenes more reliable way to check gene states + if(gene) + bname = gene.name + else + bname = "" body += "" @@ -627,7 +645,7 @@ var/global/floorIsLava = 0 if(!check_rights(R_SERVER,0)) message = sanitize(message, 500, extra = 0) message = replacetext(message, "\n", "
") // required since we're putting it in a

tag - to_world(span_notice(span_bold("[usr.client.holder.fakekey ? "Administrator" : usr.key] Announces:") + "

[message]

")) + send_ooc_announcement(message, "From [usr.client.holder.fakekey ? "Administrator" : usr.key]") log_admin("Announce: [key_name(usr)] : [message]") feedback_add_details("admin_verb","A") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! @@ -903,10 +921,6 @@ var/datum/announcement/minor/admin_min_announcer = new set desc="Whether persistent data will be saved from now on." set name="Toggle Persistent Data" CONFIG_SET(flag/persistence_disabled, !CONFIG_GET(flag/persistence_disabled)) - if(!CONFIG_GET(flag/persistence_disabled)) - to_world(span_world("Persistence is now enabled.")) - else - to_world(span_world("Persistence is no longer enabled.")) message_admins(span_blue("[key_name_admin(usr)] toggled persistence to [CONFIG_GET(flag/persistence_disabled) ? "Off" : "On"]."), 1) log_admin("[key_name(usr)] toggled persistence to [CONFIG_GET(flag/persistence_disabled) ? "Off" : "On"].") world.update_status() diff --git a/code/modules/admin/verbs/randomverbs.dm b/code/modules/admin/verbs/randomverbs.dm index e721f66c37c..2f1c1371c56 100644 --- a/code/modules/admin/verbs/randomverbs.dm +++ b/code/modules/admin/verbs/randomverbs.dm @@ -9,8 +9,8 @@ return for(var/obj/item/W in M) - if(istype(W, /obj/item/implant/backup) || istype(W, /obj/item/nif)) //VOREStation Edit - There's basically no reason to remove either of these - continue //VOREStation Edit + if(istype(W, /obj/item/implant/backup) || istype(W, /obj/item/nif)) //There's basically no reason to remove either of these + continue M.drop_from_inventory(W) log_admin("[key_name(usr)] made [key_name(M)] drop everything!") @@ -432,8 +432,8 @@ Traitors and the like can also be revived with the previous role mostly intact. return if(samejob == "Yes") charjob = record_found.fields["real_rank"] - else if(samejob == JOB_ALT_VISITOR) //VOREStation Edit - Visitor not Assistant - charjob = JOB_ALT_VISITOR //VOREStation Edit - Visitor not Assistant + else if(samejob == JOB_ALT_VISITOR) + charjob = JOB_ALT_VISITOR else records = tgui_alert(src,"No data core entry detected. Would you like add them to the manifest, and sec/med/HR records?","Records",list("No", "Yes", "Cancel")) if(!records || records == "Cancel") @@ -465,10 +465,8 @@ Traitors and the like can also be revived with the previous role mostly intact. //For logging later var/admin = key_name_admin(src) var/player_key = picked_client.key - //VOREStation Add - Needed for persistence var/picked_ckey = picked_client.ckey var/picked_slot = picked_client.prefs.default_slot - //VOREStation Add End var/mob/living/carbon/human/new_character var/spawnloc @@ -517,6 +515,7 @@ Traitors and the like can also be revived with the previous role mostly intact. picked_client.prefs.copy_to(new_character) if(new_character.dna) new_character.dna.ResetUIFrom(new_character) + new_character.sync_dna_traits(TRUE) // Traitgenes Sync traits to genetics if needed new_character.sync_organ_dna() if(inhabit) new_character.key = player_key @@ -527,11 +526,9 @@ Traitors and the like can also be revived with the previous role mostly intact. antag_data.add_antagonist(new_character.mind) antag_data.place_mob(new_character) - //VOREStation Add - Required for persistence if(new_character.mind) new_character.mind.loaded_from_ckey = picked_ckey new_character.mind.loaded_from_slot = picked_slot - //VOREStation Add End for(var/lang in picked_client.prefs.alternate_languages) var/datum/language/chosen_language = GLOB.all_languages[lang] @@ -546,7 +543,6 @@ Traitors and the like can also be revived with the previous role mostly intact. if(new_character.mind) new_character.mind.assigned_role = charjob new_character.mind.role_alt_title = job_master.GetPlayerAltTitle(new_character, charjob) - //equip_custom_items(new_character) //VOREStation Removal //If desired, add records. if(records) @@ -555,7 +551,7 @@ Traitors and the like can also be revived with the previous role mostly intact. //A redraw for good measure new_character.regenerate_icons() - new_character.update_transform() //VOREStation Edit + new_character.update_transform() //If we're announcing their arrival if(announce) @@ -700,7 +696,7 @@ Traitors and the like can also be revived with the previous role mostly intact. set category = "Fun.Do Not" set name = "Explosion" - if(!check_rights(R_DEBUG|R_FUN)) return //VOREStation Edit + if(!check_rights(R_DEBUG|R_FUN)) return var/devastation = tgui_input_number(usr, "Range of total devastation. -1 to none", text("Input"), min_value=-1) if(devastation == null) return @@ -728,7 +724,7 @@ Traitors and the like can also be revived with the previous role mostly intact. set category = "Fun.Do Not" set name = "EM Pulse" - if(!check_rights(R_DEBUG|R_FUN)) return //VOREStation Edit + if(!check_rights(R_DEBUG|R_FUN)) return var/heavy = tgui_input_number(usr, "Range of heavy pulse.", text("Input")) if(heavy == null) return @@ -754,7 +750,7 @@ Traitors and the like can also be revived with the previous role mostly intact. set category = "Fun.Do Not" set name = "Gib" - if(!check_rights(R_ADMIN|R_FUN)) return //VOREStation Edit + if(!check_rights(R_ADMIN|R_FUN)) return var/confirm = tgui_alert(src, "You sure?", "Confirm", list("Yes", "No")) if(confirm != "Yes") return @@ -855,7 +851,7 @@ Traitors and the like can also be revived with the previous role mostly intact. /client/proc/cmd_admin_check_contents(mob/living/M as mob in mob_list) set category = "Admin.Investigate" set name = "Check Contents" - set popup_menu = FALSE //VOREStation Edit - Declutter. + set popup_menu = FALSE if(!holder) return @@ -912,7 +908,7 @@ Traitors and the like can also be revived with the previous role mostly intact. mob.set_viewsize(view) log_admin("[key_name(usr)] changed their view range to [view].") - //message_admins("[key_name_admin(usr)] changed their view range to [view].", 1) //why? removed by order of XSI + message_admins("[key_name_admin(usr)] changed their view range to [view].", 1) feedback_add_details("admin_verb","CVRA") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! @@ -923,7 +919,7 @@ Traitors and the like can also be revived with the previous role mostly intact. if ((!( ticker ) || !emergency_shuttle.location())) return - if(!check_rights(R_ADMIN)) return //VOREStation Edit + if(!check_rights(R_ADMIN)) return var/confirm = tgui_alert(src, "You sure?", "Confirm", list("Yes", "No")) if(confirm != "Yes") return @@ -952,7 +948,7 @@ Traitors and the like can also be revived with the previous role mostly intact. set category = "Admin.Events" set name = "Cancel Shuttle" - if(!check_rights(R_ADMIN)) return //VOREStation Edit + if(!check_rights(R_ADMIN|R_FUN)) return if(tgui_alert(src, "You sure?", "Confirm", list("Yes", "No")) != "Yes") return @@ -973,7 +969,7 @@ Traitors and the like can also be revived with the previous role mostly intact. if (!ticker) return - if(!check_rights(R_ADMIN)) return //VOREStation Edit + if(!check_rights(R_ADMIN)) return emergency_shuttle.deny_shuttle = !emergency_shuttle.deny_shuttle @@ -1029,7 +1025,7 @@ Traitors and the like can also be revived with the previous role mostly intact. set name = "Toggle random events on/off" set desc = "Toggles random events such as meteors, black holes, blob (but not space dust) on/off" - if(!check_rights(R_SERVER)) return //VOREStation Edit + if(!check_rights(R_SERVER)) return if(!CONFIG_GET(flag/allow_random_events)) CONFIG_SET(flag/allow_random_events, TRUE) diff --git a/code/modules/client/preference_setup/general/03_body.dm b/code/modules/client/preference_setup/general/03_body.dm index 2ffa019a898..12c8c36dfc8 100644 --- a/code/modules/client/preference_setup/general/03_body.dm +++ b/code/modules/client/preference_setup/general/03_body.dm @@ -56,9 +56,11 @@ var/global/list/valid_bloodtypes = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O var/datum/sprite_accessory/instance = style_list[path] if(!istype(instance)) continue + if(instance.name == DEVELOPER_WARNING_NAME) + continue if(instance.ckeys_allowed && (!client || !(client.ckey in instance.ckeys_allowed))) continue - if(instance.species_allowed && (!species || !(species in instance.species_allowed)) && (!client || !check_rights(R_ADMIN | R_EVENT | R_FUN, 0, client)) && (!custom_base || !(custom_base in instance.species_allowed))) //VOREStation Edit: Custom Species + if(instance.species_allowed && (!species || !(species in instance.species_allowed)) && (!client || !check_rights(R_ADMIN | R_EVENT | R_FUN, 0, client)) && (!custom_base || !(custom_base in instance.species_allowed))) continue .[instance.name] = instance @@ -83,7 +85,6 @@ var/global/list/valid_bloodtypes = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O pref.f_style = save_data["facial_style_name"] pref.grad_style = save_data["grad_style_name"] pref.b_type = save_data["b_type"] - pref.disabilities = save_data["disabilities"] pref.organ_data = check_list_copy(save_data["organ_data"]) pref.rlimb_data = check_list_copy(save_data["rlimb_data"]) pref.body_markings = check_list_copy(save_data["body_markings"]) @@ -109,7 +110,6 @@ var/global/list/valid_bloodtypes = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O save_data["facial_style_name"] = pref.f_style save_data["grad_style_name"] = pref.grad_style save_data["b_type"] = pref.b_type - save_data["disabilities"] = pref.disabilities save_data["organ_data"] = check_list_copy(pref.organ_data) save_data["rlimb_data"] = check_list_copy(pref.rlimb_data) var/list/body_markings = check_list_copy(pref.body_markings) @@ -138,12 +138,11 @@ var/global/list/valid_bloodtypes = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O pref.grad_style = sanitize_inlist(pref.grad_style, GLOB.hair_gradients, initial(pref.grad_style)) pref.b_type = sanitize_text(pref.b_type, initial(pref.b_type)) - pref.disabilities = sanitize_integer(pref.disabilities, 0, 65535, initial(pref.disabilities)) if(!pref.organ_data) pref.organ_data = list() if(!pref.rlimb_data) pref.rlimb_data = list() if(!pref.body_markings) pref.body_markings = list() else pref.body_markings &= body_marking_styles_list - for (var/M in pref.body_markings) //VOREStation Edit + for (var/M in pref.body_markings) if (!islist(pref.body_markings[M])) var/col = istext(pref.body_markings[M]) ? pref.body_markings[M] : "#000000" pref.body_markings[M] = pref.mass_edit_marking_list(M,color=col) @@ -198,8 +197,6 @@ var/global/list/valid_bloodtypes = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O character.set_gender(pref.biological_gender) - // Destroy/cyborgize organs and limbs. - //VOREStation Edit character.synthetic = pref.species == "Protean" ? all_robolimbs["protean"] : null //Clear the existing var. (unless protean, then switch it to the normal protean limb) var/list/organs_to_edit = list() for (var/name in list(BP_TORSO, BP_HEAD, BP_GROIN, BP_L_ARM, BP_R_ARM, BP_L_HAND, BP_R_HAND, BP_L_LEG, BP_R_LEG, BP_L_FOOT, BP_R_FOOT)) @@ -210,7 +207,7 @@ var/global/list/valid_bloodtypes = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O organs_to_edit += name else organs_to_edit.Insert(x+(O.robotic == ORGAN_NANOFORM ? 1 : 0), name) - for(var/name in organs_to_edit) //VOREStation edit end + for(var/name in organs_to_edit) var/status = pref.organ_data[name] var/obj/item/organ/external/O = character.organs_by_name[name] if(O) @@ -247,7 +244,6 @@ var/global/list/valid_bloodtypes = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O for(var/M in pref.body_markings) priority += 1 var/datum/sprite_accessory/marking/mark_datum = body_marking_styles_list[M] - //var/mark_color = "[pref.body_markings[M]]" //VOREStation Edit for(var/BP in mark_datum.body_parts) var/obj/item/organ/external/O = character.organs_by_name[BP] @@ -275,16 +271,14 @@ var/global/list/valid_bloodtypes = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O var/datum/species/mob_species = GLOB.all_species[pref.species] . += "
 12345
[block-1]" if(bname) - var/bstate=M.dna.GetSEState(block) + var/bstate=(bname in M.active_genes) // Traitgenes more reliable way to check gene states + // Traitgenes show trait linked names on mouseover + var/tname = bname + if(istype(gene,/datum/gene/trait)) + var/datum/gene/trait/T = gene + tname = T.get_name() var/bcolor="[(bstate)?"#006600":"#ff0000"]" - body += "[bname][block]" + if(!bstate && M.dna.GetSEState(block)) // Gene isn't active, but the dna says it is... Was blocked by another gene! + bcolor="#d88d00" + body += "[bname][block]" // Traitgenes edit - show trait linked names on mouseover else body += "[block]" body+="
Body " - . += "(®)" + . += "(®)" . += "
" . += "Species: [pref.species]
" . += "Blood Type: [pref.b_type]
" if(has_flag(mob_species, HAS_SKIN_TONE)) . += "Skin Tone: [-pref.s_tone + 35]/220
" - . += "Needs Glasses: [pref.disabilities & NEARSIGHTED ? "Yes" : "No"]
" . += "Limbs: Adjust Reset
" . += "Internal Organs: Adjust
" - //display limbs below var/ind = 0 for(var/name in pref.organ_data) @@ -545,7 +539,7 @@ var/global/list/valid_bloodtypes = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O else return TOPIC_NOACTION - if(((!(setting_species.spawn_flags & SPECIES_CAN_JOIN)) || (!is_alien_whitelisted(preference_mob(),setting_species))) && !check_rights(R_ADMIN|R_EVENT, 0) && !(setting_species.spawn_flags & SPECIES_WHITELIST_SELECTABLE)) //VOREStation Edit: selectability + if(((!(setting_species.spawn_flags & SPECIES_CAN_JOIN)) || (!is_alien_whitelisted(preference_mob(),setting_species))) && !check_rights(R_ADMIN|R_EVENT, 0) && !(setting_species.spawn_flags & SPECIES_WHITELIST_SELECTABLE)) return TOPIC_NOACTION var/prev_species = pref.species @@ -553,7 +547,7 @@ var/global/list/valid_bloodtypes = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O if(prev_species != pref.species) if(!(pref.biological_gender in mob_species.genders)) pref.set_biological_gender(mob_species.genders[1]) - pref.custom_species = null //VOREStation Edit - This is cleared on species changes + pref.custom_species = null //grab one of the valid hair styles for the newly chosen species var/list/valid_hairstyles = pref.get_valid_hairstyles(user) @@ -586,7 +580,7 @@ var/global/list/valid_bloodtypes = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O var/min_age = get_min_age() var/max_age = get_max_age() pref.update_preference_by_type(/datum/preference/numeric/human/age, max(min(pref.read_preference(/datum/preference/numeric/human/age), max_age), min_age)) - pref.blood_color = setting_species.blood_color // VOREstation edit + pref.blood_color = setting_species.blood_color return TOPIC_REFRESH_UPDATE_PREVIEW @@ -713,15 +707,6 @@ var/global/list/valid_bloodtypes = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O else if(href_list["marking_style"]) var/list/usable_markings = pref.body_markings.Copy() ^ body_marking_styles_list.Copy() - /* VOREStation Removal - No markings whitelist, let people mix/match - for(var/M in usable_markings) - var/datum/sprite_accessory/S = usable_markings[M] - var/datum/species/spec = GLOB.all_species[pref.species] - if(!S.species_allowed.len) - continue - else if(!(pref.species in S.species_allowed) && !(pref.custom_base in S.species_allowed) && !(spec.base_species in S.species_allowed)) - usable_markings -= M - */ //VOREStation Removal End var/new_marking = tgui_input_list(user, "Choose a body marking:", "Character Preference", usable_markings) if(new_marking && CanUseTopic(user)) pref.body_markings[new_marking] = pref.mass_edit_marking_list(new_marking) //New markings start black @@ -919,10 +904,8 @@ var/global/list/valid_bloodtypes = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O continue if(tmp_species in M.species_cannot_use) continue - //VOREStation Add - Cyberlimb whitelisting. if(M.whitelisted_to && !(user.ckey in M.whitelisted_to)) continue - //VOREStation Add End usable_manufacturers[company] = M if(!usable_manufacturers.len) return @@ -1019,11 +1002,6 @@ var/global/list/valid_bloodtypes = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O return TOPIC_REFRESH - else if(href_list["disabilities"]) - var/disability_flag = text2num(href_list["disabilities"]) - pref.disabilities ^= disability_flag - return TOPIC_REFRESH_UPDATE_PREVIEW - else if(href_list["toggle_preview_value"]) pref.equip_preview_mob ^= text2num(href_list["toggle_preview_value"]) return TOPIC_REFRESH_UPDATE_PREVIEW @@ -1158,7 +1136,6 @@ var/global/list/valid_bloodtypes = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O if(new_wingc) pref.update_preference_by_type(/datum/preference/color/human/wing_color3, new_wingc) return TOPIC_REFRESH_UPDATE_PREVIEW - return ..() /datum/category_item/player_setup_item/general/body/proc/reset_limbs() @@ -1186,12 +1163,10 @@ var/global/list/valid_bloodtypes = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O dat += "

[current_species.name] \[change\]


" dat += "" dat += "" - //vorestation edit begin if(current_species.wikilink) dat += "" else dat += "" - //vorestation edit end dat += "
[current_species.blurb]

See the wiki for more details.
[current_species.blurb]" if("preview" in cached_icon_states(current_species.icobase)) user << browse_rsc(icon(current_species.icobase,"preview"), "species_preview_[current_species.name].png") @@ -1248,7 +1223,7 @@ var/global/list/valid_bloodtypes = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O dat += "You cannot play as this species.
If you wish to be whitelisted, you can make an application post on the forums.

" else if(restricted == 2) dat += "You cannot play as this species.
This species is not available for play as a station race..

" - if(!restricted || check_rights(R_ADMIN|R_EVENT, 0) || current_species.spawn_flags & SPECIES_WHITELIST_SELECTABLE) //VOREStation Edit: selectability + if(!restricted || check_rights(R_ADMIN|R_EVENT, 0) || current_species.spawn_flags & SPECIES_WHITELIST_SELECTABLE) dat += "\[select\]" dat += "" diff --git a/code/modules/client/preference_setup/loadout/loadout_fluffitems.dm b/code/modules/client/preference_setup/loadout/loadout_fluffitems.dm index 9757c99ae0b..1a9c7dc1a90 100644 --- a/code/modules/client/preference_setup/loadout/loadout_fluffitems.dm +++ b/code/modules/client/preference_setup/loadout/loadout_fluffitems.dm @@ -283,7 +283,7 @@ character_name = list("Cappy Fuzzlyfeathers") /datum/gear/fluff/james_disk - path = /obj/item/disk/data + path = /obj/item/disk/body_record display_name = "James' Disk" ckeywhitelist = list("cockatricexl") character_name = list("James Holder") diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm index 8838514eff1..5d3ebcf82cc 100644 --- a/code/modules/client/preferences.dm +++ b/code/modules/client/preferences.dm @@ -25,7 +25,7 @@ var/list/preferences_datums = list() var/headset = 1 //headset type var/backbag = 2 //backpack type var/pdachoice = 1 //PDA type - var/shoe_hater = FALSE //RS ADD - if true, will spawn with no shoes + var/shoe_hater = FALSE //If true, will spawn with no shoes var/no_jacket = FALSE //if true, will not spawn with outfit's jacket/outer layer var/h_style = "Bald" //Hair type var/grad_style = "none" //Gradient style @@ -41,7 +41,7 @@ var/list/preferences_datums = list() var/gear_slot = 1 //The current gear save slot var/list/traits //Traits which modifier characters for better or worse (mostly worse). var/synth_color = 0 //Lets normally uncolorable synth parts be colorable. - var/synth_markings = 1 //Enable/disable markings on synth parts. //VOREStation Edit - 1 by default + var/synth_markings = 1 //Enable/disable markings on synth parts. var/digitigrade = 0 //Some faction information. @@ -103,7 +103,6 @@ var/list/preferences_datums = list() var/sec_record = "" var/gen_record = "" var/exploit_record = "" - var/disabilities = 0 var/economic_status = "Average" @@ -289,12 +288,13 @@ var/list/preferences_datums = list() return 1 if(href_list["save"]) - save_character() + if(save_character()) + to_chat(usr,span_notice("Character [player_setup?.preferences?.real_name] saved!")) save_preferences() else if(href_list["reload"]) load_preferences() load_character() - attempt_vr(client.prefs_vr,"load_vore","") //VOREStation Edit + attempt_vr(client.prefs_vr,"load_vore","") sanitize_preferences() else if(href_list["load"]) if(!IsGuestKey(usr.key)) @@ -342,7 +342,7 @@ var/list/preferences_datums = list() preference.apply_pref_to(character, read_preference(preference.type)) - // VOREStation Edit - Sync up all their organs and species one final time + // Sync up all their organs and species one final time character.force_update_organs() if(icon_updates) @@ -393,7 +393,7 @@ var/list/preferences_datums = list() load_preferences() load_character(slotnum) - attempt_vr(user.client?.prefs_vr,"load_vore","") //VOREStation Edit + attempt_vr(user.client?.prefs_vr,"load_vore","") sanitize_preferences() save_preferences() ShowChoices(user) diff --git a/code/modules/emotes/emote_define.dm b/code/modules/emotes/emote_define.dm index b66653c553c..327aa69a368 100644 --- a/code/modules/emotes/emote_define.dm +++ b/code/modules/emotes/emote_define.dm @@ -84,6 +84,8 @@ var/global/list/emotes_by_key /decl/emote/proc/do_emote(var/atom/user, var/extra_params) if(ismob(user) && check_restraints) var/mob/M = user + if(M.transforming) //Transforming acts as a stasis. + return if(M.restrained()) to_chat(user, span_warning("You are restrained and cannot do that.")) return diff --git a/code/modules/eventkit/generic_objects/generic_item.dm b/code/modules/eventkit/generic_objects/generic_item.dm index d1b89fb81a9..943de2bc407 100644 --- a/code/modules/eventkit/generic_objects/generic_item.dm +++ b/code/modules/eventkit/generic_objects/generic_item.dm @@ -50,11 +50,11 @@ var/flash_time = 10 if(ishuman(O)) var/mob/living/carbon/human/H = O - //VOREStation Edit Start if(H.nif && H.nif.flag_check(NIF_V_FLASHPROT,NIF_FLAGS_VISION)) H.nif.notify("High intensity light detected, and blocked!",TRUE) continue - //VOREStation Edit End + if(FLASHPROOF in H.mutations) + continue if(!H.eyecheck() <= 0) continue flash_time *= H.species.flash_mod diff --git a/code/modules/eventkit/generic_objects/generic_structure.dm b/code/modules/eventkit/generic_objects/generic_structure.dm index 36d3b4cb262..0878fef63a7 100644 --- a/code/modules/eventkit/generic_objects/generic_structure.dm +++ b/code/modules/eventkit/generic_objects/generic_structure.dm @@ -55,11 +55,11 @@ var/flash_time = 10 if(ishuman(O)) var/mob/living/carbon/human/H = O - //VOREStation Edit Start if(H.nif && H.nif.flag_check(NIF_V_FLASHPROT,NIF_FLAGS_VISION)) H.nif.notify("High intensity light detected, and blocked!",TRUE) continue - //VOREStation Edit End + if(FLASHPROOF in H.mutations) + continue if(!H.eyecheck() <= 0) continue flash_time *= H.species.flash_mod diff --git a/code/modules/genetics/side_effects.dm b/code/modules/genetics/side_effects.dm index 6ee490cd564..286b96927c3 100644 --- a/code/modules/genetics/side_effects.dm +++ b/code/modules/genetics/side_effects.dm @@ -1,81 +1,78 @@ /datum/genetics/side_effect - var/name // name of the side effect, to use as a header in the manual - var/symptom // description of the symptom of the side effect - var/treatment // description of the treatment of the side effect - var/effect // description of what happens when not treated - var/duration = 0 // delay between start() and finish() + var/name // name of the side effect, to use as a header in the manual + var/duration = 0 // delay between start() and finish() + var/antidote_reagent // What type of reagent we require to stop the specific side effect from happening + +/proc/trigger_side_effect(mob/living/carbon/human/H) + if(!ishuman(H)) return + var/tp = pick(subtypesof(/datum/genetics/side_effect)) + var/datum/genetics/side_effect/S = new tp + + S.start(H) + addtimer(CALLBACK(H, TYPE_PROC_REF(/mob/living/carbon/human, Weaken), 4), 2 SECONDS, TIMER_DELETE_ME) + addtimer(CALLBACK(S, TYPE_PROC_REF(/datum/genetics/side_effect, finish), WEAKREF(H)), S.duration, TIMER_DELETE_ME) + //above is doing: Call S.finish(H) in S.duration /datum/genetics/side_effect/proc/start(mob/living/carbon/human/H) + if(H && ishuman(H)) + H.genetic_side_effects += src // start the side effect, this should give some cue as to what's happening, // such as gasping. These cues need to be unique among side-effects. -/datum/genetics/side_effect/proc/finish(mob/living/carbon/human/H) +/datum/genetics/side_effect/proc/finish(var/datum/weakref/WR) + var/mob/living/carbon/human/H = WR.resolve() + if(!H || !ishuman(H)) return FALSE + H.genetic_side_effects -= src + if(antidote_reagent && (H.reagents.has_reagent(antidote_reagent)|| H.ingested.has_reagent(antidote_reagent) || H.touching.has_reagent(antidote_reagent))) + return TRUE + return FALSE // Finish the side-effect. This should first check whether the cure has been // applied, and if not, cause bad things to happen. /datum/genetics/side_effect/genetic_burn name = "Genetic Burn" - symptom = "Subject's skin turns unusualy red." - treatment = "Inject small dose of dexalin." - effect = "Subject's skin burns." duration = 30 SECONDS + antidote_reagent = REAGENT_ID_DEXALIN /datum/genetics/side_effect/genetic_burn/start(mob/living/carbon/human/H) + ..() H.custom_emote(VISIBLE_MESSAGE, "starts turning very red..") -/datum/genetics/side_effect/genetic_burn/finish(mob/living/carbon/human/H) - if(H.reagents.has_reagent(REAGENT_ID_DEXALIN)) - return +/datum/genetics/side_effect/genetic_burn/finish(datum/weakref/WR) + if(..()) return + var/mob/living/carbon/human/H = WR.resolve() for(var/organ_name in BP_ALL) var/obj/item/organ/external/E = H.get_organ(organ_name) E.take_damage(0, 5, 0) /datum/genetics/side_effect/bone_snap - name = "Bone Snap" - symptom = "Subject's limbs tremble notably." - treatment = "Inject small dose of bicaridine." - effect = "Subject's bone breaks." + name = "Genetic Bone Snap" duration = 60 SECONDS + antidote_reagent = REAGENT_ID_BICARIDINE /datum/genetics/side_effect/bone_snap/start(mob/living/carbon/human/H) + ..() H.custom_emote(VISIBLE_MESSAGE, "'s limbs start shivering uncontrollably.") -/datum/genetics/side_effect/bone_snap/finish(mob/living/carbon/human/H) - if(H.reagents.has_reagent(REAGENT_ID_BICARIDINE)) - return +/datum/genetics/side_effect/bone_snap/finish(datum/weakref/WR) + if(..()) return + var/mob/living/carbon/human/H = WR.resolve() var/organ_name = pick(BP_ALL) var/obj/item/organ/external/E = H.get_organ(organ_name) E.take_damage(20, 0, 0) E.fracture() /datum/genetics/side_effect/confuse - name = "Confuse" - symptom = "Subject starts drooling uncontrollably." - treatment = "Inject small dose of dylovene." - effect = "Subject becomes confused." + name = "Genetic Confusion" duration = 30 SECONDS + antidote_reagent = REAGENT_ID_ANTITOXIN /datum/genetics/side_effect/confuse/start(mob/living/carbon/human/H) + ..() var/datum/gender/T = gender_datums[H.get_visible_gender()] H.custom_emote(VISIBLE_MESSAGE, "has drool running down from [T.his] mouth.") -/datum/genetics/side_effect/confuse/finish(mob/living/carbon/human/H) - if(H.reagents.has_reagent(REAGENT_ID_ANTITOXIN)) - return +/datum/genetics/side_effect/confuse/finish(datum/weakref/WR) + if(..()) return + var/mob/living/carbon/human/H = WR.resolve() H.Confuse(100) - -/proc/trigger_side_effect(mob/living/carbon/human/H) - spawn - if(!istype(H)) return - var/tp = pick(subtypesof(/datum/genetics/side_effect)) - var/datum/genetics/side_effect/S = new tp - - S.start(H) - spawn(20) - if(!istype(H)) return - H.Weaken(rand(0, S.duration / 50)) - sleep(S.duration) - - if(!istype(H)) return - H.SetWeakened(0) - S.finish(H) diff --git a/code/modules/mining/abandonedcrates_vr.dm b/code/modules/mining/abandonedcrates_vr.dm index 1a21722ceb3..3e8946e643e 100644 --- a/code/modules/mining/abandonedcrates_vr.dm +++ b/code/modules/mining/abandonedcrates_vr.dm @@ -59,7 +59,8 @@ list(/obj/item/storage/belt/utility/chief/full, 8) = 2, list(/obj/item/personal_shield_generator/belt/mining/loaded, 6) = 2, list(pick(subtypesof(/obj/item/melee/energy/sword) - /obj/item/melee/energy/sword/charge), 6) = 2, - list(pick(/obj/item/dnainjector/xraymut, /obj/item/dnainjector/nobreath, /obj/item/dnainjector/insulation), 6) = 2, + // Traitgenes New injector loot + list(pick(/obj/item/dnainjector/random_good,/obj/item/dnainjector/random_good_labeled,/obj/item/dnainjector/random_labeled,/obj/item/dnainjector/random), 6) = 2, list(/obj/item/gun/energy/netgun, 7) = 2, list(pick(prob(300);/obj/item/gun/energy/mouseray, prob(50);/obj/item/gun/energy/mouseray/corgi, @@ -76,6 +77,7 @@ prob(5);/obj/item/gun/energy/mouseray/giantrat, prob(50);/obj/item/gun/energy/mouseray/redpanda, prob(5);/obj/item/gun/energy/mouseray/catslug, + prob(5);/obj/item/gun/energy/mouseray/teppi, prob(1);/obj/item/gun/energy/mouseray/metamorphosis, prob(1);/obj/item/gun/energy/mouseray/metamorphosis/advanced/random ), 8) = 2, @@ -102,7 +104,8 @@ list(/obj/item/card/emag, 11) = 1, list(/obj/item/melee/shock_maul, 11) = 3, list(/obj/item/clothing/suit/storage/vest/martian_miner/reinforced, 4) = 6, - list(/obj/item/storage/backpack/sport/hyd/catchemall, 11) = 1 + list(/obj/item/storage/backpack/sport/hyd/catchemall, 11) = 1, + list(/obj/item/prop/alien/junk, 12) = 1, )) var/path = choice[1] var/value = choice[2] diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm index 1439c7df41a..1cf7e96dfb6 100644 --- a/code/modules/mob/dead/observer/observer.dm +++ b/code/modules/mob/dead/observer/observer.dm @@ -588,6 +588,7 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp return ..() /mob/observer/dead/Destroy() + visualnet.addVisibility(src, src.client) visualnet = null if(ismob(following)) var/mob/M = following diff --git a/code/modules/mob/freelook/ai/eye.dm b/code/modules/mob/freelook/ai/eye.dm index e4b481c67c9..2646f2150c7 100644 --- a/code/modules/mob/freelook/ai/eye.dm +++ b/code/modules/mob/freelook/ai/eye.dm @@ -16,6 +16,8 @@ var/mob/living/silicon/ai/ai = owner ai.all_eyes -= src owner = null + visualnet.clear_references(src, src.client) + visualnet = null . = ..() /mob/observer/eye/aiEye/setLoc(var/T, var/cancel_tracking = 1) diff --git a/code/modules/mob/freelook/visualnet.dm b/code/modules/mob/freelook/visualnet.dm index d153701cddd..10f0a6c8096 100644 --- a/code/modules/mob/freelook/visualnet.dm +++ b/code/modules/mob/freelook/visualnet.dm @@ -157,3 +157,18 @@ var/datum/chunk/chunk = cameranet.getCameraChunk(x, y, z) usr.client.debug_variables(chunk) */ + +/datum/visualnet/proc/clear_references(list/moved_eyes, client/C) + if(!islist(moved_eyes)) + moved_eyes = moved_eyes ? list(moved_eyes) : list() + + var/list/chunks_pre_seen = list() + + for(var/mob/observer/eye/eye as anything in moved_eyes) + if(C) + chunks_pre_seen |= eye.visibleChunks + + if(C) + for(var/datum/chunk/c as anything in chunks_pre_seen) + for(var/mob/observer/eye/eye as anything in moved_eyes) + c.remove(eye) diff --git a/code/modules/mob/inventory.dm b/code/modules/mob/inventory.dm index 6905a59f58b..d6972e4a728 100644 --- a/code/modules/mob/inventory.dm +++ b/code/modules/mob/inventory.dm @@ -147,10 +147,11 @@ var/list/slot_equipment_priority = list( \ // If canremove or other conditions need to be checked then use unEquip instead. /mob/proc/drop_from_inventory(var/obj/item/W, var/atom/target) - if(W) - remove_from_mob(W, target) - return TRUE - return FALSE + if(!W) + return FALSE + if(isnull(target) && istype( src.loc,/obj/structure/disposalholder)) + return remove_from_mob(W, src.loc) + return remove_from_mob(W, target) //Drops the item in our left hand /mob/proc/drop_l_hand(var/atom/Target) diff --git a/code/modules/mob/language/outsider.dm b/code/modules/mob/language/outsider.dm index 64d8caadb06..928088abcc9 100644 --- a/code/modules/mob/language/outsider.dm +++ b/code/modules/mob/language/outsider.dm @@ -77,7 +77,7 @@ /datum/language/xenocommon name = "Xenomorph" colour = "alien" - desc = "The common tongue of the xenomorphs." + desc = "The common tongue of both the xenomorphs and the Genaprawns." speech_verb = "hisses" ask_verb = "hisses" exclaim_verb = "hisses" diff --git a/code/modules/mob/living/carbon/alien/larva/progression.dm b/code/modules/mob/living/carbon/alien/larva/progression.dm index 1f1f44a3e3b..fdfdb1cbf2d 100644 --- a/code/modules/mob/living/carbon/alien/larva/progression.dm +++ b/code/modules/mob/living/carbon/alien/larva/progression.dm @@ -10,9 +10,9 @@ to_chat(src, span_notice("There are three to choose from:")) to_chat(src, span_bold("Hunters") + span_notice(" are strong and agile, able to hunt away from the hive and rapidly move through ventilation shafts. Hunters generate plasma slowly and have low reserves.")) to_chat(src, span_bold("Sentinels") + span_notice(" are tasked with protecting the hive and are deadly up close and at a range. They are not as physically imposing nor fast as the hunters.")) - to_chat(src, span_bold("Drones") + span_notice(" are the working class, offering the largest plasma storage and generation. They are the only caste which may evolve again, turning into the dreaded alien queen.")) + to_chat(src, span_bold("Drones") + span_notice(" are the working class, offering the largest plasma storage and generation. They are the only caste which may evolve again, turning into the dreaded Genaprawn queen.")) var/alien_caste = tgui_alert(src, "Please choose which alien caste you shall belong to.","Alien Choice",list("Hunter","Sentinel","Drone")) - return alien_caste ? "Xenomorph [alien_caste]" : null + return alien_caste ? "Genaprawn [alien_caste]" : null /mob/living/carbon/alien/larva/show_evolution_blurb() return diff --git a/code/modules/mob/living/carbon/brain/brain.dm b/code/modules/mob/living/carbon/brain/brain.dm index 6ee1c22046e..fcd48007fee 100644 --- a/code/modules/mob/living/carbon/brain/brain.dm +++ b/code/modules/mob/living/carbon/brain/brain.dm @@ -8,7 +8,7 @@ use_me = 0 //Can't use the me verb, it's a freaking immobile brain icon = 'icons/obj/surgery.dmi' icon_state = "brain1" - no_vore = TRUE //VOREStation Edit - PLEASE. lol. + no_vore = TRUE /mob/living/carbon/brain/Initialize() . = ..() diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index bee6dde1986..45568f48fa2 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -27,9 +27,12 @@ var/can_defib = 1 //Horrible damage (like beheadings) will prevent defibbing organics. var/active_regen = FALSE //Used for the regenerate proc in human_powers.dm var/active_regen_delay = 300 + var/last_breath_sound //Allows us to store the value across proc calls per-mob. var/list/teleporters = list() //Used for lleill abilities var/rest_dir = 0 //To lay down in a specific direction + var/gutdeathpressure = 0 //For GIBBING trait + var/list/datum/genetics/side_effect/genetic_side_effects = list() //For any genetic side effects we currently have. /mob/living/carbon/human/Initialize(mapload, var/new_species = null) if(!dna) @@ -38,7 +41,7 @@ if(!species) if(new_species) - set_species(new_species,1) + set_species(new_species) else set_species() @@ -61,18 +64,16 @@ if(dna) dna.ready_dna(src) dna.real_name = real_name + sync_dna_traits(FALSE) // Traitgenes Sync traits to genetics if needed sync_organ_dna() AddComponent(/datum/component/personal_crafting) /mob/living/carbon/human/Destroy() human_mob_list -= src - /* //REMOVE - this is done on mob/living/Destroy - for(var/organ in organs) - qdel(organ) - */ + QDEL_NULL_LIST(organs) if(nif) - QDEL_NULL(nif) //VOREStation Add + QDEL_NULL(nif) worn_clothing.Cut() @@ -114,7 +115,6 @@ . += "Chemical Storage: [mind.changeling.chem_charges]" . += "Genetic Damage Time: [mind.changeling.geneticdamage]" . += "Re-Adaptations: [mind.changeling.readapts]/[mind.changeling.max_readapts]" - if(species) species.get_status_tab_items(src) @@ -193,6 +193,7 @@ if (!get_ear_protection() >= 2) ear_damage += 30 ear_deaf += 120 + // deaf_loop.start() // Used downstream if (prob(70) && !shielded) Paralyse(10) @@ -203,6 +204,7 @@ if (!get_ear_protection() >= 2) ear_damage += 15 ear_deaf += 60 + // deaf_loop.start() // Used downstream if (prob(50) && !shielded) Paralyse(10) @@ -738,7 +740,7 @@ src << browse(null, "window=flavor_changes") return if("general") - var/msg = strip_html_simple(tgui_input_text(usr,"Update the general description of your character. This will be shown regardless of clothing.","Flavor Text",html_decode(flavor_texts[href_list["flavor_change"]]), multiline = TRUE, prevent_enter = TRUE)) //VOREStation Edit: separating out OOC notes + var/msg = strip_html_simple(tgui_input_text(usr,"Update the general description of your character. This will be shown regardless of clothing.","Flavor Text",html_decode(flavor_texts[href_list["flavor_change"]]), multiline = TRUE, prevent_enter = TRUE)) //Separating out OOC notes if(msg) flavor_texts[href_list["flavor_change"]] = msg set_flavor() @@ -816,11 +818,9 @@ return 1 /mob/living/carbon/human/IsAdvancedToolUser(var/silent) - // VOREstation start if(feral) to_chat(src, span_warning("Your primitive mind can't grasp the concept of that thing.")) return 0 - // VOREstation end if(species.has_fine_manipulation) return 1 if(!silent) @@ -956,6 +956,8 @@ return var/list/creatures = list() for(var/mob/living/carbon/h in mob_list) + if(h == src) // Don't target self + continue creatures += h var/mob/target = tgui_input_list(src, "Who do you want to project your mind to?", "Project Mind", creatures) if (isnull(target)) @@ -984,18 +986,20 @@ remoteview_target = null reset_view(0) remove_verb(src, /mob/living/carbon/human/proc/remoteobserve) - return - if(client.eye != client.mob) - remoteview_target = null reset_view(0) return var/list/mob/creatures = list() + var/turf/current = get_turf(src) // Needs to be on station or same z to perform telepathy for(var/mob/living/carbon/h in mob_list) var/turf/temp_turf = get_turf(h) - if((temp_turf.z != 1 && temp_turf.z != 5) || h.stat!=CONSCIOUS) //Not on mining or the station. Or dead + if(!istype(temp_turf,/turf/)) // Nullcheck fix + continue + if(h == src) // Traitgenes edit - Don't target self + continue + if(!((temp_turf.z in using_map.station_levels) || current.z == temp_turf.z) || h.stat!=CONSCIOUS) // Needs to be on station or same z to perform telepathy continue creatures += h @@ -1051,13 +1055,19 @@ H.brainmob.mind.transfer_to(src) qdel(H) - // Vorestation Addition - reapply markings/appearance from prefs for player mobs + // Traitgenes Disable all traits currently active, before prefs.copy_to() is applied, as it refreshes the traits list! + for(var/datum/gene/trait/gene in GLOB.dna_genes) + if(gene.name in active_genes) + gene.deactivate(src) + active_genes -= gene.name + + // Reapply markings/appearance from prefs for player mobs if(client) //just to be sure client.prefs.copy_to(src) if(dna) dna.ResetUIFrom(src) + sync_dna_traits(TRUE) // Traitgenes Sync traits to genetics if needed sync_organ_dna() - // end vorestation addition losebreath = 0 @@ -1220,7 +1230,7 @@ else to_chat(usr, span_warning("You failed to check the pulse. Try again.")) -/mob/living/carbon/human/proc/set_species(var/new_species, var/default_colour, var/regen_icons = TRUE, var/mob/living/carbon/human/example = null) //VOREStation Edit - send an example +/mob/living/carbon/human/proc/set_species(var/new_species) if(!dna) if(!new_species) @@ -1237,7 +1247,7 @@ if(species) - if(species.name && species.name == new_species && species.name != "Custom Species") //VOREStation Edit + if(species.name && species.name == new_species && species.name != "Custom Species") return if(species.language) remove_language(species.language) @@ -1248,7 +1258,7 @@ // Clear out their species abilities. species.remove_inherent_verbs(src) holder_type = null - hunger_rate = initial(hunger_rate) //VOREStation Add + hunger_rate = initial(hunger_rate) species = GLOB.all_species[new_species] @@ -1261,13 +1271,8 @@ if(species.icon_scale_x != DEFAULT_ICON_SCALE_X || species.icon_scale_y != DEFAULT_ICON_SCALE_Y) update_transform() - if(example) //VOREStation Edit begin - if(!(example == src)) - r_skin = example.r_skin - g_skin = example.g_skin - b_skin = example.b_skin - else if(species.base_color) //VOREStation Edit end - //Apply colour. + if(species.base_color) + //Apply color. r_skin = hex2num(copytext(species.base_color,2,4)) g_skin = hex2num(copytext(species.base_color,4,6)) b_skin = hex2num(copytext(species.base_color,6,8)) @@ -1284,16 +1289,18 @@ //icon_state = lowertext(species.name) //Necessary? - //VOREStation Edit start: swap places of those two procs species.handle_post_spawn(src) species.create_organs(src) - //VOREStation Edit end: swap places of those two procs maxHealth = species.total_health - hunger_rate = species.hunger_factor //VOREStation Add + hunger_rate = species.hunger_factor + default_pixel_x = initial(pixel_x) + species.pixel_offset_x //For giving datum/species ways to change 64x64 sprite offsets + default_pixel_y = initial(pixel_y) + species.pixel_offset_y + pixel_x = default_pixel_x + pixel_y = default_pixel_y center_offset = species.center_offset if(LAZYLEN(descriptors)) @@ -1305,6 +1312,23 @@ var/datum/mob_descriptor/descriptor = species.descriptors[desctype] descriptors[desctype] = descriptor.default_value + addtimer(CALLBACK(src, PROC_REF(initialize_vessel)), 0, TIMER_DELETE_ME) //Doing ASYNC fails here. This used to be a spawn(0) + + // Rebuild the HUD. If they aren't logged in then login() should reinstantiate it for them. + update_hud() + + //A slew of bits that may be affected by our species change + regenerate_icons() + + if(species) + return 1 + else + return 0 + +/mob/living/carbon/human/proc/initialize_vessel() //This needs fixing. For some reason mob species is not immediately set in set_species. + PRIVATE_PROC(TRUE) + SHOULD_NOT_OVERRIDE(TRUE) + regenerate_icons() make_blood() if(vessel.total_volume < species.blood_volume) vessel.maximum_volume = species.blood_volume @@ -1316,19 +1340,6 @@ species.update_attack_types() //Required for any trait that updates unarmed_types in setup. species.update_vore_belly_def_variant() - // Rebuild the HUD. If they aren't logged in then login() should reinstantiate it for them. - update_hud() - - //A slew of bits that may be affected by our species change - regenerate_icons() - - if(species) - //if(mind) //VOREStation Removal - //apply_traits() //VOREStation Removal - return 1 - else - return 0 - /mob/living/carbon/human/proc/bloody_doodle() set category = "IC.Game" set name = "Write in blood" @@ -1495,9 +1506,14 @@ if(C.body_parts_covered & FEET) footcoverage_check = TRUE break + if(lying) + playsound(src, 'sound/misc/slip.ogg', 25, 1, -1) + drop_both_hands() + return 0 if((species.flags & NO_SLIP && !footcoverage_check) || (shoes && (shoes.item_flags & NOSLIP))) //Footwear negates a species' natural traction. return 0 if(..(slipped_on,stun_duration)) + drop_both_hands() return 1 /mob/living/carbon/human/proc/relocate() @@ -1553,7 +1569,11 @@ current_limb.relocate() /mob/living/carbon/human/drop_from_inventory(var/obj/item/W, var/atom/target = null) - return !(W in organs) && ..() + if(W in organs) + return FALSE + if(isnull(target) && istype( src.loc,/obj/structure/disposalholder)) + return remove_from_mob(W, src.loc) + return ..() /mob/living/carbon/human/reset_view(atom/A, update_hud = 1) ..() @@ -1563,8 +1583,8 @@ /mob/living/carbon/human/Check_Shoegrip() if(shoes && (shoes.item_flags & NOSLIP) && istype(shoes, /obj/item/clothing/shoes/magboots)) //magboots + dense_object = no floating return 1 - if(flying) //VOREStation Edit. Checks to see if they have wings and are flying. - return 1 //VOREStation Edit. + if(flying) // Checks to see if they have wings and are flying. + return 1 return 0 //Puts the item into our active hand if possible. returns 1 on success. @@ -1661,12 +1681,10 @@ /mob/living/carbon/human/can_feel_pain(var/obj/item/organ/check_organ) if(isSynthetic()) return 0 - //RS ADD START if(!digest_pain && (isbelly(src.loc) || istype(src.loc, /turf/simulated/floor/water/digestive_enzymes))) var/obj/belly/b = src.loc if(b.digest_mode == DM_DIGEST || b.digest_mode == DM_SELECT) return FALSE - //RS ADD END for(var/datum/modifier/M in modifiers) if(M.pain_immunity == TRUE) return 0 @@ -1712,8 +1730,8 @@ /mob/living/carbon/human/proc/get_display_species() //Shows species in tooltip - if(src.custom_species) //VOREStation Add - return custom_species //VOREStation Add + if(src.custom_species) + return custom_species //Beepboops get special text if obviously beepboop if(looksSynthetic()) if(gender == MALE) @@ -1818,7 +1836,7 @@ /mob/living/carbon/human/verb/lay_down_left() set name = "Rest-Left" - rest_dir = -1 + rest_dir = 1 resting = !resting to_chat(src, span_notice("You are now [resting ? "resting" : "getting up"].")) update_canmove() @@ -1826,7 +1844,7 @@ /mob/living/carbon/human/verb/lay_down_right() set name = "Rest-Right" - rest_dir = 1 + rest_dir = 0 resting = !resting to_chat(src, span_notice("You are now [resting ? "resting" : "getting up"].")) update_canmove() diff --git a/code/modules/mob/living/carbon/human/human_defines.dm b/code/modules/mob/living/carbon/human/human_defines.dm index 2e15db12587..08f781288aa 100644 --- a/code/modules/mob/living/carbon/human/human_defines.dm +++ b/code/modules/mob/living/carbon/human/human_defines.dm @@ -166,3 +166,6 @@ var/block_hud var/phobias //For holding a list of phobias + + var/loneliness_stage = 0 + var/next_loneliness_time = 0 diff --git a/code/modules/mob/living/carbon/human/human_organs.dm b/code/modules/mob/living/carbon/human/human_organs.dm index 294cdc86480..eeee03b2f5a 100644 --- a/code/modules/mob/living/carbon/human/human_organs.dm +++ b/code/modules/mob/living/carbon/human/human_organs.dm @@ -5,17 +5,6 @@ update_icons_body() //Body handles eyes update_eyes() //For floating eyes only -/* -/mob/living/carbon/var/list/internal_organs = list() -/mob/living/carbon/human/var/list/organs = list() -/mob/living/carbon/human/var/list/organs_by_name = list() // map organ names to organs -/mob/living/carbon/human/var/list/internal_organs_by_name = list() // so internal organs have less ickiness too - -/mob/living/carbon/human/proc/get_bodypart_name(var/zone) - var/obj/item/organ/external/E = get_organ(zone) - if(E) . = E.name -*/ - /mob/living/carbon/human/proc/recheck_bad_external_organs() var/damage_this_tick = getToxLoss() for(var/obj/item/organ/external/O in organs) @@ -47,7 +36,7 @@ if(!force_process && !bad_external_organs.len) return - number_wounds = 0 //VOREStation Add - You have to reduce this at some point... + number_wounds = 0 for(var/obj/item/organ/external/E in bad_external_organs) if(!E) continue @@ -62,6 +51,7 @@ //Moving around with fractured ribs won't do you any good if (prob(10) && !stat && can_feel_pain() && chem_effects[CE_PAINKILLER] < 50 && E.is_broken() && E.internal_organs.len) custom_pain("Pain jolts through your broken [E.encased ? E.encased : E.name], staggering you!", 50) + emote("pain") drop_item(loc) Stun(2) @@ -104,7 +94,7 @@ else if (E.is_dislocated()) stance_damage += 0.5 - if(E && (!E.is_usable() || E.is_broken() || E.is_dislocated())) //VOREStation Edit + if(E && (!E.is_usable() || E.is_broken() || E.is_dislocated())) limb_pain = E.organ_can_feel_pain() // Canes and crutches help you stand (if the latter is ever added) @@ -117,7 +107,7 @@ // standing is poor if(stance_damage >= 4 || (stance_damage >= 2 && prob(5))) - if(!(lying || resting) && !isbelly(loc)) //VOREStation Edit + if(!(lying || resting) && !isbelly(loc)) if(limb_pain) emote("scream") custom_emote(1, "collapses!") @@ -147,7 +137,6 @@ // Check again... if(!l_hand && !r_hand) return - for (var/obj/item/organ/external/E in organs) if(!E || !E.can_grasp) continue @@ -163,9 +152,11 @@ continue drop_from_inventory(r_hand) - if(!isbelly(loc)) //VOREStation Add + if(!isbelly(loc)) var/emote_scream = pick("screams in pain and ", "lets out a sharp cry and ", "cries out and ") custom_emote(VISIBLE_MESSAGE, "[(can_feel_pain()) ? "" : emote_scream ]drops what they were holding in their [E.name]!") + if(can_feel_pain()) + emote("pain") else if(E.is_malfunctioning()) switch(E.body_part) @@ -178,7 +169,7 @@ continue drop_from_inventory(r_hand) - if(!isbelly(loc)) //VOREStation Add + if(!isbelly(loc)) custom_emote(VISIBLE_MESSAGE, "drops what they were holding, their [E.name] malfunctioning!") var/datum/effect/effect/system/spark_spread/spark_system = new /datum/effect/effect/system/spark_spread() @@ -195,6 +186,31 @@ var/obj/item/organ/O = pick(organs) O.trace_chemicals[A.name] = 100 +// Traitgenes Init genes based on the traits currently active +/mob/living/carbon/human/proc/sync_dna_traits(var/refresh_traits, var/hide_message = TRUE) + if(!dna || !species) + return + // Traitgenes NO_SCAN and Synthetics cannot be mutated + if(isSynthetic()) + return + if(species.flags & NO_SCAN) + return + if(refresh_traits && species.traits) + for(var/TR in species.traits) + var/datum/trait/T = all_traits[TR] + if(!T) + continue + if(!T.linked_gene) + continue + var/datum/gene/trait/gene = T.linked_gene + dna.SetSEState(gene.block, TRUE, TRUE) + // testing("[gene.name] Setup activated!") + dna.UpdateSE() + var/flgs = MUTCHK_FORCED + if(hide_message) + flgs |= MUTCHK_HIDEMSG + domutcheck( src, null, flgs) + /mob/living/carbon/human/proc/sync_organ_dna() var/list/all_bits = internal_organs|organs for(var/obj/item/organ/O in all_bits) diff --git a/code/modules/mob/living/carbon/human/life.dm b/code/modules/mob/living/carbon/human/life.dm index 75cb9c2208d..6d8248047f4 100644 --- a/code/modules/mob/living/carbon/human/life.dm +++ b/code/modules/mob/living/carbon/human/life.dm @@ -4,15 +4,14 @@ #define HUMAN_MAX_OXYLOSS 1 //Defines how much oxyloss humans can get per tick. A tile with no air at all (such as space) applies this value, otherwise it's a percentage of it. #define HUMAN_CRIT_MAX_OXYLOSS ( 2.0 / 6) //The amount of damage you'll get when in critical condition. We want this to be a 5 minute deal = 300s. There are 50HP to get through, so (1/6)*last_tick_duration per second. Breaths however only happen every 4 ticks. last_tick_duration = ~2.0 on average -#define HEAT_DAMAGE_LEVEL_1 2 //VOREStation Edit //Amount of damage applied when your body temperature just passes the 360.15k safety point -#define HEAT_DAMAGE_LEVEL_2 4 //VOREStation Edit //Amount of damage applied when your body temperature passes the 400K point -#define HEAT_DAMAGE_LEVEL_3 8 //VOREStation Edit //Amount of damage applied when your body temperature passes the 1000K point +#define HEAT_DAMAGE_LEVEL_1 2 //Amount of damage applied when your body temperature just passes the 360.15k safety point +#define HEAT_DAMAGE_LEVEL_2 4 //Amount of damage applied when your body temperature passes the 400K point +#define HEAT_DAMAGE_LEVEL_3 8 //Amount of damage applied when your body temperature passes the 1000K point #define COLD_DAMAGE_LEVEL_1 0.5 //Amount of damage applied when your body temperature just passes the 260.15k safety point #define COLD_DAMAGE_LEVEL_2 1.5 //Amount of damage applied when your body temperature passes the 200K point #define COLD_DAMAGE_LEVEL_3 3 //Amount of damage applied when your body temperature passes the 120K point -#define RADIATION_SPEED_COEFFICIENT 0.1 #define HUMAN_COMBUSTION_TEMP 524 //524k is the sustained combustion temperature of human fat /mob/living/carbon/human @@ -53,7 +52,7 @@ Sleeping(20) //No need to update all of these procs if the guy is dead. - fall() //VORESTATION EDIT. Prevents people from floating + fall() //Prevents people from floating if(stat != DEAD && !stasis) //Updates the number of stored chemicals for powers handle_changeling() @@ -61,8 +60,8 @@ //Organs and blood handle_organs() stabilize_body_temperature() //Body temperature adjusts itself (self-regulation) - weightgain() //VOREStation Addition - process_weaver_silk() //VOREStation Addition + weightgain() + process_weaver_silk() handle_shock() handle_pain() @@ -72,7 +71,7 @@ handle_medical_side_effects() handle_heartbeat() - handle_nif() //VOREStation Addition + handle_nif() if(phobias) handle_phobias() if(!client) @@ -182,7 +181,7 @@ return if (disabilities & EPILEPSY) - if ((prob(1) && paralysis < 1)) + if ((prob(1) && prob(1) && paralysis < 1)) to_chat(src, span_red("You have a seizure!")) for(var/mob/O in viewers(src, null)) if(O == src) @@ -193,23 +192,57 @@ if (disabilities & COUGHING) if ((prob(5) && paralysis <= 1)) drop_item() - spawn(0) - emote("cough") - return + emote("cough") + if(dna) + if(disabilities & DETERIORATE && prob(2) && prob(3)) // stacked percents for rarity + // random strange symptoms from organ/limb + custom_emote(VISIBLE_MESSAGE, "flinches slightly.") + switch(rand(1,4)) + if(1) + adjustToxLoss(rand(2,8)) + if(2) + adjustCloneLoss(rand(1,2)) + if(3) + add_chemical_effect(CE_PAINKILLER, rand(8,28)) + else + adjustOxyLoss(rand(13,26)) + // external organs need to fall off if damaged enough + var/obj/item/organ/O = pick(organs) + if(O && !(O.organ_tag == BP_GROIN || O.organ_tag == BP_TORSO) && istype(O,/obj/item/organ/external)) + var/obj/item/organ/external/E = O + if(O.damage >= O.min_broken_damage && O.robotic <= ORGAN_ASSISTED && prob(70)) + add_chemical_effect(CE_PAINKILLER, 120) // what limb? Extreme nerve damage. Can't feel a thing + shock + E.droplimb(TRUE, DROPLIMB_ACID) + if(disabilities & GIBBING) + gutdeathpressure += 0.01 + if(gutdeathpressure > 0 && prob(gutdeathpressure)) + emote(pick("whimper","belch","belch","belch","choke","shiver")) + Weaken(gutdeathpressure / 3) + if((gutdeathpressure/3) >= 1 && prob(gutdeathpressure/3)) + gutdeathpressure = 0 // to stop retriggering + spawn(1) + emote(pick("whimper","shiver")) + spawn(3) + emote(pick("whimper","belch","shiver")) + spawn(4) + emote(pick("whimper","shiver")) + spawn(6) + emote(pick("belch")) + gib() if (disabilities & TOURETTES) - if ((prob(10) && paralysis <= 1)) + if ((prob(1) && prob(2) && paralysis <= 1)) Stun(10) - spawn(0) - switch(rand(1, 3)) - if(1) - emote("twitch") - if(2 to 3) - say("[prob(50) ? ";" : ""][pick("SHIT", "PISS", "FUCK", "CUNT", "COCKSUCKER", "MOTHERFUCKER", "TITS")]") - make_jittery(100) - return + make_jittery(100) + switch(rand(1, 3)) + if(1) + emote("twitch") + if(2 to 3) + say("[prob(50) ? ";" : ""][pick("SHIT", "PISS", "FUCK", "CUNT", "COCKSUCKER", "MOTHERFUCKER", "TITS")]") if (disabilities & NERVOUS) - if (prob(10)) - stuttering = max(10, stuttering) + if (prob(5) && prob(7)) + stuttering = max(15, stuttering) + if(jitteriness < 50) + make_jittery(65) var/rn = rand(0, 200) if(getBrainLoss() >= 5) @@ -261,17 +294,25 @@ if((COLD_RESISTANCE in mutations) || (prob(1))) heal_organ_damage(0,1) - // DNA2 - Gene processing. - // The HULK stuff that was here is now in the hulk gene. - if(!isSynthetic()) - for(var/datum/dna/gene/gene in dna_genes) - if(!gene.block) - continue - if(gene.is_active(src)) - gene.OnMobLife(src) + if(stat != DEAD) + if((mRegen in mutations)) + var/heal = rand(0.2,1.3) + if(prob(50)) + for(var/obj/item/organ/external/O in bad_external_organs) + for(var/datum/wound/W in O.wounds) + if(W.bleeding()) + W.damage = max(W.damage - heal, 0) + if(W.damage <= 0) + O.wounds -= W + if(W.internal) + W.damage = max(W.damage - heal, 0) + if(W.damage <= 0) + O.wounds -= W + else + heal_organ_damage(heal,heal) - radiation = CLAMP(radiation,0,2500) //Max of 50Gy. If you reach that...You're going to wish you were dead. You probably will be dead. - accumulated_rads = CLAMP(accumulated_rads,0,2500) //Max of 50Gy as well. You should never get higher than this. You will be dead before you can reach this. + radiation = CLAMP(radiation,0,5000) //Max of 100Gy. If you reach that...You're going to wish you were dead. You probably will be dead. + accumulated_rads = CLAMP(accumulated_rads,0,5000) //Max of 100Gy as well. You should never get higher than this. You will be dead before you can reach this. var/obj/item/organ/internal/I = null //Used for further down below when an organ is picked. if(!radiation) if(species.appearance_flags & RADIATION_GLOWS) @@ -288,12 +329,12 @@ var/obj/item/organ/internal/diona/nutrients/rad_organ = locate() in internal_organs if(rad_organ && !rad_organ.is_broken()) var/rads = radiation/25 - radiation -= rads - adjust_nutrition(rads) - adjustBruteLoss(-(rads)) - adjustFireLoss(-(rads)) - adjustOxyLoss(-(rads)) - adjustToxLoss(-(rads)) + radiation -= (rads * species.rad_removal_mod) + adjust_nutrition(rads * species.rad_removal_mod) + adjustBruteLoss(-(rads * species.rad_removal_mod)) + adjustFireLoss(-(rads * species.rad_removal_mod)) + adjustOxyLoss(-(rads * species.rad_removal_mod)) + adjustToxLoss(-(rads * species.rad_removal_mod)) updatehealth() return @@ -301,25 +342,26 @@ if(core) return - //VOREStation Addition start: shadekin var/obj/item/organ/internal/brain/shadekin/s_brain = locate() in internal_organs if(s_brain) return - //VOREStation Addition end: shadekin if(reagents.has_reagent(REAGENT_ID_PRUSSIANBLUE)) //Prussian Blue temporarily stops radiation effects. return var/damage = 0 + if(!species.radiation_mod) //If we are rad immune, stop here and remove rads if we have any. + radiation -= 10 * RADIATION_SPEED_COEFFICIENT * species.rad_removal_mod + return - if (radiation < 50) //Less than 1.0 Gy. No side effects. - radiation -= 10 * RADIATION_SPEED_COEFFICIENT + if (radiation < species.rad_levels["safe"]) //Less than 1.0 Gy. No side effects. + radiation -= 10 * RADIATION_SPEED_COEFFICIENT * species.rad_removal_mod accumulated_rads += 10 * RADIATION_SPEED_COEFFICIENT //No escape from accumulated rads. - else if (radiation >= 50 && radiation < 100) //Equivalent of 1.0-2.0 Gy. Minimum stage you start seeing effects. + else if (radiation >= species.rad_levels["safe"] && radiation < species.rad_levels["danger_1"]) //Equivalent of 1.0-2.0 Gy. Minimum stage you start seeing effects. damage = 1 - radiation -= 10 * RADIATION_SPEED_COEFFICIENT + radiation -= 10 * RADIATION_SPEED_COEFFICIENT * species.rad_removal_mod accumulated_rads += 10 * RADIATION_SPEED_COEFFICIENT if(!isSynthetic()) if(prob(5) && prob(100 * RADIATION_SPEED_COEFFICIENT) && !weakened) @@ -334,9 +376,9 @@ if(prob(1) && prob(100 * RADIATION_SPEED_COEFFICIENT)) //Rare chance of vomiting. spawn vomit() - else if (radiation >= 100 && radiation < 300) //Equivalent of 2.0 to 6.0 Gy. Nobody should ever be above this without extreme negligence. + else if (radiation >= species.rad_levels["danger_1"] && radiation < species.rad_levels["danger_2"]) //Equivalent of 2.0 to 6.0 Gy. Nobody should ever be above this without extreme negligence. damage = 3 - radiation -= 30 * RADIATION_SPEED_COEFFICIENT + radiation -= 30 * RADIATION_SPEED_COEFFICIENT * species.rad_removal_mod accumulated_rads += 30 * RADIATION_SPEED_COEFFICIENT if(!isSynthetic()) if(prob(5)) @@ -350,9 +392,9 @@ to_chat(src, span_warning("You feel sick.")) AdjustWeakened(3) - else if (radiation >= 300 && radiation < 400) //Equivalent of 6.0 to 8.0 Gy. + else if (radiation >= species.rad_levels["danger_2"] && radiation < species.rad_levels["danger_3"]) //Equivalent of 6.0 to 8.0 Gy. damage = 5 - radiation -= 50 * RADIATION_SPEED_COEFFICIENT + radiation -= 50 * RADIATION_SPEED_COEFFICIENT * species.rad_removal_mod accumulated_rads += 50 * RADIATION_SPEED_COEFFICIENT if(!isSynthetic()) if(prob(15)) @@ -365,15 +407,15 @@ if(prob(15) && !weakened) to_chat(src, span_warning("You feel horribly ill.")) AdjustWeakened(3) - if(prob(5) && internal_organs.len) + if(prob(5) && internal_organs.len) //TODO: Add malignant organs. - The person that wrote radcode. I = pick(internal_organs) //Internal organ damage...Not good. Not good at all. if(istype(I)) I.add_autopsy_data("Radiation Induced Cancerous Growth", damage) I.take_damage(damage * species.radiation_mod * RADIATION_SPEED_COEFFICIENT) - else if (radiation >= 400 && radiation < 1500) //Equivalent of 8.0 to 30 Gy. + else if (radiation >= species.rad_levels["danger_3"] && radiation < species.rad_levels["danger_4"]) //Equivalent of 8.0 to 30 Gy. damage = 10 - radiation -= 100 * RADIATION_SPEED_COEFFICIENT + radiation -= 100 * RADIATION_SPEED_COEFFICIENT * species.rad_removal_mod accumulated_rads += 100 * RADIATION_SPEED_COEFFICIENT if(!isSynthetic()) if(prob(25)) @@ -396,14 +438,14 @@ if(prob(5)) to_chat(src, span_critical("Your entire body feels like it's on fire!")) adjustHalLoss(5) - if(prob(10) && internal_organs.len) + if(prob(10) && internal_organs.len) //TODO: Add malignant organs. - The person that wrote radcode. I = pick(internal_organs) //Internal organ damage...Not good. Not good at all. if(istype(I)) I.add_autopsy_data("Radiation Induced Cancerous Growth", damage) I.take_damage(damage * species.radiation_mod * RADIATION_SPEED_COEFFICIENT) - else if (radiation >= 1500) //Above 30Gy. You had to get absolutely blasted with rads for this. + else if (radiation >= species.rad_levels["danger_4"]) //Above 30Gy. You had to get absolutely blasted with rads for this. damage = 30 - radiation -= 300 * RADIATION_SPEED_COEFFICIENT + radiation -= 300 * RADIATION_SPEED_COEFFICIENT * species.rad_removal_mod accumulated_rads += 300 * RADIATION_SPEED_COEFFICIENT if(!isSynthetic()) @@ -427,7 +469,7 @@ if(get_active_hand() && prob(15)) //CNS is shutting down. to_chat(src, span_danger("Your hand won't respond properly, you drop what you're holding!")) drop_item() - if(internal_organs.len) + if(internal_organs.len) //TODO: Add malignant organs. - The person that wrote radcode. I = pick(internal_organs) //Internal organ damage...Not good. Not good at all. if(istype(I)) I.add_autopsy_data("Radiation Induced Cancerous Growth", damage * species.radiation_mod * RADIATION_SPEED_COEFFICIENT) I.take_damage(damage * species.radiation_mod * RADIATION_SPEED_COEFFICIENT) @@ -540,6 +582,9 @@ if(status_flags & GODMODE) return + if(mNobreath in mutations) + return + if(suiciding) failed_last_breath = 1 adjustOxyLoss(2)//If you are suiciding, you should die a little bit faster @@ -729,6 +774,16 @@ failed_last_breath = 0 adjustOxyLoss(-5) + if(!does_not_breathe && client) // If we breathe, and have an active client, check if we have synthetic lungs. + var/obj/item/organ/internal/lungs/L = internal_organs_by_name[O_LUNGS] + var/turf = get_turf(src) + var/mob/living/carbon/human/M = src + if(L.robotic < ORGAN_ROBOT && is_below_sound_pressure(turf) && M.internal) // Only non-synthetic lungs, please, and only play these while the pressure is below that which we can hear sounds normally AND we're on internals. + if(!failed_inhale && (world.time >= (last_breath_sound + 7 SECONDS))) // Were we able to inhale successfully? Play inhale. + var/exhale = failed_exhale // Pass through if we passed exhale or not + play_inhale(M, exhale) + last_breath_sound = world.time + // Hot air hurts :( if((breath.temperature <= species.cold_discomfort_level || breath.temperature >= species.heat_discomfort_level) && !(COLD_RESISTANCE in mutations)) @@ -795,6 +850,26 @@ breath.update_values() return 1 +/mob/living/carbon/human/proc/play_inhale(var/mob/living/M, var/exhale) + var/suit_inhale_sound + if(species.suit_inhale_sound) + suit_inhale_sound = species.suit_inhale_sound + else // Failsafe + suit_inhale_sound = 'sound/effects/mob_effects/suit_breathe_in.ogg' + + playsound_local(get_turf(src), suit_inhale_sound, 100, pressure_affected = FALSE, volume_channel = VOLUME_CHANNEL_AMBIENCE) + if(!exhale) // Did we fail exhale? If no, play it after inhale finishes. + addtimer(CALLBACK(src, PROC_REF(play_exhale), M), 5 SECONDS) + +/mob/living/carbon/human/proc/play_exhale(var/mob/living/M) + var/suit_exhale_sound + if(species.suit_exhale_sound) + suit_exhale_sound = species.suit_exhale_sound + else // Failsafe + suit_exhale_sound = 'sound/effects/mob_effects/suit_breathe_out.ogg' + + playsound_local(get_turf(src), suit_exhale_sound, 100, pressure_affected = FALSE, volume_channel = VOLUME_CHANNEL_AMBIENCE) + /mob/living/carbon/human/proc/handle_allergens() if(chem_effects[CE_ALLERGEN]) //first, multiply the basic species-level value by our allergen effect rating, so consuming multiple seperate allergen typess simultaneously hurts more @@ -843,7 +918,7 @@ pl_effects() break - if(istype(loc, /turf/space)) //VOREStation Edit - No FBPs overheating on space turfs inside mechs or people. + if(istype(loc, /turf/space)) //No FBPs overheating on space turfs inside mechs or people. //Don't bother if the temperature drop is less than 0.1 anyways. Hopefully BYOND is smart enough to turn this constant expression into a constant if(bodytemperature > (0.1 * HUMAN_HEAT_CAPACITY/(HUMAN_EXPOSED_SURFACE_AREA*STEFAN_BOLTZMANN_CONSTANT))**(1/4) + COSMIC_RADIATION_TEMPERATURE) //Thermal radiation into space @@ -944,7 +1019,7 @@ throw_alert("pressure", /obj/screen/alert/lowpressure, 1) else if( !(COLD_RESISTANCE in mutations)) - if(!isSynthetic() || !nif || !nif.flag_check(NIF_O_PRESSURESEAL,NIF_FLAGS_OTHER)) //VOREStation Edit - NIF pressure seals + if(!isSynthetic() || !nif || !nif.flag_check(NIF_O_PRESSURESEAL,NIF_FLAGS_OTHER)) var/pressure_damage = LOW_PRESSURE_DAMAGE if(stat==DEAD) pressure_damage = pressure_damage/2 @@ -968,25 +1043,6 @@ return -/* -/mob/living/carbon/human/proc/adjust_body_temperature(current, loc_temp, boost) - var/temperature = current - var/difference = abs(current-loc_temp) //get difference - var/increments// = difference/10 //find how many increments apart they are - if(difference > 50) - increments = difference/5 - else - increments = difference/10 - var/change = increments*boost // Get the amount to change by (x per increment) - var/temp_change - if(current < loc_temp) - temperature = min(loc_temp, temperature+change) - else if(current > loc_temp) - temperature = max(loc_temp, temperature-change) - temp_change = (temperature - current) - return temp_change -*/ - /mob/living/carbon/human/proc/stabilize_body_temperature() // We produce heat naturally. if (species.passive_temp_gain) @@ -996,7 +1052,7 @@ // FBPs will overheat when alive, prosthetic limbs are fine. if(stat != DEAD && robobody_count) - if(!nif || !nif.flag_check(NIF_O_HEATSINKS,NIF_FLAGS_OTHER)) //VOREStation Edit - NIF heatsinks prevent the base heat increase per tick if installed. + if(!nif || !nif.flag_check(NIF_O_HEATSINKS,NIF_FLAGS_OTHER)) bodytemperature += round(robobody_count*1.15) var/obj/item/organ/internal/robotic/heatsink/HS = internal_organs_by_name[O_HEATSINK] if(!HS || HS.is_broken()) // However, NIF Heatsinks will not compensate for a core FBP component (your heatsink) being lost. @@ -1129,8 +1185,8 @@ var/total_phoronloss = 0 for(var/obj/item/I in src) if(I.contaminated) - if(check_belly(I)) continue //VOREStation Edit - if(src.species && src.species.get_bodytype() != "Vox" && src.species.get_bodytype() != "Shadekin") //VOREStation Edit: shadekin + if(check_belly(I)) continue + if(src.species && src.species.get_bodytype() != "Vox" && src.species.get_bodytype() != "Shadekin") // This is hacky, I'm so sorry. if(I != l_hand && I != r_hand) //If the item isn't in your hands, you're probably wearing it. Full damage for you. total_phoronloss += vsc.plc.CONTAMINATION_LOSS @@ -1158,22 +1214,41 @@ take_overall_damage(1,1) else //heal in the dark heal_overall_damage(1,1) - + if(species.photosynthesizing && nutrition < 1000) + var/light_amount = 0 + if(isturf(loc)) + var/turf/T = loc + light_amount = T.get_lumcount() / 10 + adjust_nutrition(light_amount) // nutrition decrease - if (nutrition > 0 && stat != DEAD) + if(nutrition <= 0 && species.shrinks && size_multiplier > RESIZE_TINY) + nutrition = 0.1 + if(nutrition > 0 && stat != DEAD) var/nutrition_reduction = species.hunger_factor for(var/datum/modifier/mod in modifiers) if(!isnull(mod.metabolism_percent)) nutrition_reduction *= mod.metabolism_percent + if(nutrition > 1000 && species.grows) //Removing the strict check against normal max/min size to support dorms/VR oversizing + nutrition_reduction *= 5 + resize(size_multiplier+0.01, animate = FALSE, uncapped = has_large_resize_bounds()) //Bringing this code in line with micro and macro shrooms + if(nutrition < 50 && species.shrinks) + nutrition_reduction *= 0.3 + resize(size_multiplier-0.01, animate = FALSE, uncapped = has_large_resize_bounds()) //Bringing this code in line with micro and macro shrooms adjust_nutrition(-nutrition_reduction) - if(noisy == TRUE && nutrition < 250 && prob(10)) //VOREStation edit for hunger noises. + if(noisy == TRUE && nutrition < 250 && prob(10)) var/sound/growlsound = sound(get_sfx("hunger_sounds")) var/growlmultiplier = 100 - (nutrition / 250 * 100) playsound(src, growlsound, vol = growlmultiplier, vary = 1, falloff = 0.1, ignore_walls = TRUE, preference = /datum/preference/toggle/digestion_noises) - // VOREStation Edit End - + /* //Unused here, used downstream. + if(nutrition > 500 && noisy_full == TRUE) + var/belch_prob = 5 //Maximum belch prob. + if(nutrition < 4075) + belch_prob = ((nutrition-500)/3575)*5 //Scale belch prob with fullness if not already at max. If editing make sure the multiplier matches the max prob above. + if(prob(belch_prob)) + src.emote("belch") + */ if((CE_DARKSIGHT in chem_effects) && chemical_darksight == 0) recalculate_vis() chemical_darksight = 1 @@ -1187,7 +1262,7 @@ updatehealth() - return //TODO: DEFERRED + return //DO NOT CALL handle_statuses() from this proc, it's called from living/Life() as long as this returns a true value. /mob/living/carbon/human/handle_regular_status_updates() @@ -1202,6 +1277,7 @@ if(stat == DEAD) //DEAD. BROWN BREAD. SWIMMING WITH THE SPESS CARP blinded = 1 silent = 0 + // deaf_loop.stop() //Unused. Used downstream. else //ALIVE. LIGHTS ARE ON updatehealth() //TODO @@ -1209,22 +1285,23 @@ death() blinded = 1 silent = 0 + // deaf_loop.stop() //Unused. Used downstream. return 1 //UNCONSCIOUS. NO-ONE IS HOME - if((getOxyLoss() > (species.total_health/2)) || (health <= CONFIG_GET(number/health_threshold_crit))) + if((getOxyLoss() > (species.total_health/2)) || (health <= (CONFIG_GET(number/health_threshold_crit) * species.crit_mod))) Paralyse(3) if(hallucination) if(hallucination >= 20 && !(species.flags & (NO_POISON|IS_PLANT|NO_HALLUCINATION)) ) - if(prob(3)) - fake_attack(src) if(!handling_hal) spawn handle_hallucinations() //The not boring kind! + /* Stop spinning the view, it breaks too much. if(client && prob(5)) client.dir = pick(2,4,8) spawn(rand(20,50)) client.dir = 1 + */ hallucination = max(0, hallucination - 2) else @@ -1278,6 +1355,16 @@ adjustHalLoss(-3) if(sleeping) + if(prob(2)) + if(prob(50)) + adjustBruteLoss(-1) + else + adjustFireLoss(-1) + if(bad_external_organs.len && prob(45)) + var/obj/item/organ/badorgan = pick(bad_external_organs) + if(!badorgan.is_broken() && badorgan.is_bruised()) + badorgan.damage -= 1 + handle_dreams() if (mind) //Are they SSD? If so we'll keep them asleep but work off some of that sleep var in case of stoxin or similar. @@ -1349,6 +1436,7 @@ //Ears if(sdisabilities & DEAF) //disabled-deaf, doesn't get better on its own ear_deaf = max(ear_deaf, 1) + // deaf_loop.start(skip_start_sound = TRUE) //Used downstream. else if(ear_deaf) //deafness, heals slowly over time ear_deaf = max(ear_deaf-1, 0) else if(get_ear_protection() >= 2) //resting your ears with earmuffs heals ear damage faster @@ -1357,6 +1445,11 @@ else if(ear_damage < 25) //ear damage heals slowly under this threshold. otherwise you'll need earmuffs ear_damage = max(ear_damage-0.05, 0) + /* //Used downstream. Add someday to here. + if(ear_deaf <= 0) + deaf_loop.stop() + */ + //Resting if(resting) dizziness = max(0, dizziness - 15) @@ -1530,14 +1623,12 @@ fat_alert = /obj/screen/alert/fat/synth hungry_alert = /obj/screen/alert/hungry/synth starving_alert = /obj/screen/alert/starving/synth - //VOREStation Add - Vampire hunger alert else if(get_species() == SPECIES_CUSTOM) var/datum/species/custom/C = species if(/datum/trait/neutral/bloodsucker in C.traits) fat_alert = /obj/screen/alert/fat/vampire hungry_alert = /obj/screen/alert/hungry/vampire starving_alert = /obj/screen/alert/starving/vampire - //VOREStation Add End switch(nutrition) if(450 to INFINITY) @@ -1567,7 +1658,7 @@ if(G.prescription) apply_nearsighted_overlay = FALSE - if(nif && nif.flag_check(NIF_V_CORRECTIVE, NIF_FLAGS_VISION)) // VOREStation Edit - NIF + if(nif && nif.flag_check(NIF_V_CORRECTIVE, NIF_FLAGS_VISION)) apply_nearsighted_overlay = FALSE set_fullscreen(apply_nearsighted_overlay, "nearsighted", /obj/screen/fullscreen/impaired, 1) @@ -1579,7 +1670,7 @@ else clear_alert("high") - if(!isbelly(loc)) //VOREStation Add - Belly fullscreens safety + if(!isbelly(loc)) clear_fullscreen("belly") clear_fullscreen("belly2") clear_fullscreen("belly3") @@ -1594,7 +1685,7 @@ var/obj/item/clothing/glasses/welding/O = glasses if(!O.up) found_welder = 1 - if(!found_welder && nif && nif.flag_check(NIF_V_UVFILTER,NIF_FLAGS_VISION)) found_welder = 1 //VOREStation Add - NIF + if(!found_welder && nif && nif.flag_check(NIF_V_UVFILTER,NIF_FLAGS_VISION)) found_welder = 1 if(istype(glasses, /obj/item/clothing/glasses/sunglasses/thinblindfold)) found_welder = 1 if(!found_welder && istype(head, /obj/item/clothing/head/welding)) @@ -1606,7 +1697,7 @@ if(O.helmet && O.helmet == head && (O.helmet.body_parts_covered & EYES)) if((O.offline && O.offline_vision_restriction == 1) || (!O.offline && O.vision_restriction == 1)) found_welder = 1 - if(absorbed) found_welder = 1 //VOREStation Code + if(absorbed) found_welder = 1 if(found_welder) client.screen |= global_hud.darkMask @@ -1769,12 +1860,10 @@ if(isturf(loc) && rand(1,1000) == 1) var/turf/T = loc if(T.get_lumcount() <= LIGHTING_SOFT_THRESHOLD) - //VOREStation Add Start if(text2num(time2text(world.timeofday, "MM")) == 4) if(text2num(time2text(world.timeofday, "DD")) == 1) playsound_local(src,pick(scawwySownds),50, 0) return - //VOREStation Add End playsound_local(src,pick(scarySounds),50, 1, -1) /mob/living/carbon/human/handle_stomach() @@ -1842,19 +1931,15 @@ /mob/living/carbon/human/handle_shock() ..() if(status_flags & GODMODE) return 0 //godmode - if(!can_feel_pain()) return - - if(health < CONFIG_GET(number/health_threshold_softcrit))// health 0 makes you immediately collapse - shock_stage = max(shock_stage, 61) - - if(traumatic_shock >= 80) + if(traumatic_shock >= 80 && can_feel_pain()) shock_stage += 1 - else if(health < CONFIG_GET(number/health_threshold_softcrit)) - shock_stage = max(shock_stage, 61) else shock_stage = min(shock_stage, 160) shock_stage = max(shock_stage-1, 0) + if(!can_feel_pain()) return + if(health < (CONFIG_GET(number/health_threshold_softcrit) * species.crit_mod)) + shock_stage = max(shock_stage, 61) if(stat) return 0 @@ -1863,7 +1948,7 @@ custom_pain("[pick("It hurts so much", "You really need some painkillers", "Dear god, the pain")]!", 40) if(shock_stage >= 30) - if(shock_stage == 30 && !isbelly(loc)) //VOREStation Edit + if(shock_stage == 30 && !isbelly(loc)) custom_emote(VISIBLE_MESSAGE, "is having trouble keeping their eyes open.") eye_blurry = max(2, eye_blurry) if(traumatic_shock >= 80) @@ -1875,7 +1960,7 @@ to_chat(src, span_danger("[pick("The pain is excruciating", "Please, just end the pain", "Your whole body is going numb")]!")) if (shock_stage >= 60) - if(shock_stage == 60 && !isbelly(loc)) //VOREStation Edit + if(shock_stage == 60 && !isbelly(loc)) custom_emote(VISIBLE_MESSAGE, "'s body becomes limp.") if (prob(2)) if(traumatic_shock >= 80) @@ -1886,17 +1971,23 @@ if (prob(5)) if(traumatic_shock >= 80) to_chat(src, span_danger("[pick("The pain is excruciating", "Please, just end the pain", "Your whole body is going numb")]!")) + if(prob(20) && !isbelly(loc)) + emote("pain") Weaken(20) if(shock_stage >= 120) if (prob(2)) if(traumatic_shock >= 80) to_chat(src, span_danger("[pick("You black out", "You feel like you could die any moment now", "You are about to lose consciousness")]!")) + if(prob(40) && !isbelly(loc)) + emote("pain") Paralyse(5) if(shock_stage == 150) - if(!isbelly(loc)) //VOREStation Edit + if(!isbelly(loc)) custom_emote(VISIBLE_MESSAGE, "can no longer stand, collapsing!") + if(prob(60)) + emote("pain") Weaken(20) if(shock_stage >= 150) @@ -2057,13 +2148,6 @@ holder2.icon_state = "huddead" else if(has_virus()) holder.icon_state = "hudill" - else if(has_brain_worms()) - var/mob/living/simple_mob/animal/borer/B = has_brain_worms() - if(B.controlling) - holder.icon_state = "hudbrainworm" - else - holder.icon_state = "hudhealthy" - holder2.icon_state = "hudbrainworm" else holder.icon_state = "hudhealthy" if(has_virus()) @@ -2178,7 +2262,7 @@ holder.icon_state = "hudblank" apply_hud(BACKUP_HUD, holder) - //VOREStation Antag Hud + //Vore Antag Hud if (BITTEST(hud_updateflag, VANTAG_HUD)) var/image/vantag = grab_hud(VANTAG_HUD) if(vantag_pref) @@ -2248,5 +2332,4 @@ #undef COLD_DAMAGE_LEVEL_2 #undef COLD_DAMAGE_LEVEL_3 -#undef RADIATION_SPEED_COEFFICIENT #undef HUMAN_COMBUSTION_TEMP diff --git a/code/modules/mob/living/carbon/human/species/shadekin/shadekin.dm b/code/modules/mob/living/carbon/human/species/shadekin/shadekin.dm index 2e02cc4a02a..d907cb7d5d3 100644 --- a/code/modules/mob/living/carbon/human/species/shadekin/shadekin.dm +++ b/code/modules/mob/living/carbon/human/species/shadekin/shadekin.dm @@ -331,7 +331,7 @@ H.health = H.maxHealth -/datum/species/shadekin/produceCopy(var/list/traits, var/mob/living/carbon/human/H, var/custom_base, var/reset_dna = TRUE) +/datum/species/shadekin/produceCopy(var/list/traits, var/mob/living/carbon/human/H, var/custom_base, var/reset_dna = TRUE) // Traitgenes reset_dna flag required, or genes get reset on resleeve var/datum/species/shadekin/new_copy = ..() diff --git a/code/modules/mob/living/carbon/human/species/species.dm b/code/modules/mob/living/carbon/human/species/species.dm index 2f75a753d8f..7e82c61c248 100644 --- a/code/modules/mob/living/carbon/human/species/species.dm +++ b/code/modules/mob/living/carbon/human/species/species.dm @@ -140,6 +140,8 @@ var/poison_type = GAS_PHORON // Poisonous air. var/exhale_type = GAS_CO2 // Exhaled gas type. var/water_breather = FALSE + var/suit_inhale_sound = 'sound/effects/mob_effects/suit_breathe_in.ogg' + var/suit_exhale_sound = 'sound/effects/mob_effects/suit_breathe_out.ogg' var/bad_swimmer = FALSE var/body_temperature = 310.15 // Species will try to stabilize at this temperature. (also affects temperature processing) @@ -226,6 +228,18 @@ var/gluttonous // Can eat some mobs. 1 for mice, 2 for monkeys, 3 for people. var/soft_landing = FALSE // Can fall down and land safely on small falls. + var/drippy = FALSE // If we drip or not. Primarily for goo beings. + var/photosynthesizing = FALSE // If we get nutrition from light or not. + var/shrinks = FALSE // If we shrink when we have no nutrition. Not added but here for downstream's sake. + var/grows = FALSE // Same as above but if we grow when >1000 nutrition. + var/crit_mod = 1 // Used for when we go unconscious. Used downstream. + var/list/env_traits = list() + var/pixel_offset_x = 0 // Used for offsetting 64x64 and up icons. + var/pixel_offset_y = 0 // Used for offsetting 64x64 and up icons. + var/rad_levels = list("safe" = 50, "danger_1" = 100, "danger_2" = 300, "danger_3" = 400, "danger_4" = 1500) //For handle_mutations_and_radiation + var/rad_removal_mod = 1 + + var/rarity_value = 1 // Relative rarity/collector value for this species. var/economic_modifier = 2 // How much money this species makes diff --git a/code/modules/mob/living/carbon/human/species/species_shapeshift.dm b/code/modules/mob/living/carbon/human/species/species_shapeshift.dm index 69ff16c26b1..bf800645405 100644 --- a/code/modules/mob/living/carbon/human/species/species_shapeshift.dm +++ b/code/modules/mob/living/carbon/human/species/species_shapeshift.dm @@ -12,7 +12,6 @@ var/list/wrapped_species_by_ref = list() ) var/list/valid_transform_species = list() - var/monochromatic //var/default_form = SPECIES_HUMAN //VOREStation edit /datum/species/shapeshifter/get_valid_shapeshifter_forms(var/mob/living/carbon/human/H) @@ -64,13 +63,6 @@ var/list/wrapped_species_by_ref = list() /datum/species/shapeshifter/handle_post_spawn(var/mob/living/carbon/human/H) ..() wrapped_species_by_ref["\ref[H]"] = base_species //VOREStation edit - if(monochromatic) - H.r_hair = H.r_skin - H.g_hair = H.g_skin - H.b_hair = H.b_skin - H.r_facial = H.r_skin - H.g_facial = H.g_skin - H.b_facial = H.b_skin for(var/obj/item/organ/external/E in H.organs) E.sync_colour_to_human(H) @@ -193,14 +185,6 @@ var/list/wrapped_species_by_ref = list() g_synth = g_skin b_synth = b_skin - var/datum/species/shapeshifter/S = species - if(S.monochromatic) - r_hair = r_skin - g_hair = g_skin - b_hair = b_skin - r_facial = r_skin - g_facial = g_skin - b_facial = b_skin for(var/obj/item/organ/external/E in organs) E.sync_colour_to_human(src) diff --git a/code/modules/mob/living/carbon/human/species/species_shapeshift_vr.dm b/code/modules/mob/living/carbon/human/species/species_shapeshift_vr.dm index 9c55b9769e0..ca1657bf857 100644 --- a/code/modules/mob/living/carbon/human/species/species_shapeshift_vr.dm +++ b/code/modules/mob/living/carbon/human/species/species_shapeshift_vr.dm @@ -280,7 +280,7 @@ else to_chat(character, span_notice("You need to be aggressively grabbing someone before you can copy their form.")) return - if (!istype(victim)) + if (!ishuman(victim)) to_chat(character, span_warning("You can only perform this on human mobs!")) return if (!victim.client) diff --git a/code/modules/mob/living/carbon/human/species/species_vr.dm b/code/modules/mob/living/carbon/human/species/species_vr.dm index ec8b7459413..1433564c55d 100644 --- a/code/modules/mob/living/carbon/human/species/species_vr.dm +++ b/code/modules/mob/living/carbon/human/species/species_vr.dm @@ -72,7 +72,7 @@ else ..() -/datum/species/proc/produceCopy(var/list/traits, var/mob/living/carbon/human/H, var/custom_base, var/reset_dna = TRUE) +/datum/species/proc/produceCopy(var/list/traits, var/mob/living/carbon/human/H, var/custom_base, var/reset_dna = TRUE) // Traitgenes reset_dna flag required, or genes get reset on resleeve ASSERT(src) ASSERT(istype(H)) var/datum/species/new_copy = new src.type() diff --git a/code/modules/mob/living/carbon/human/species/station/monkey.dm b/code/modules/mob/living/carbon/human/species/station/monkey.dm index c555f4f1dc2..0d8677c6987 100644 --- a/code/modules/mob/living/carbon/human/species/station/monkey.dm +++ b/code/modules/mob/living/carbon/human/species/station/monkey.dm @@ -57,16 +57,46 @@ /datum/species/monkey/handle_npc(var/mob/living/carbon/human/H) if(H.stat != CONSCIOUS) return - if(prob(33) && H.canmove && isturf(H.loc) && !H.pulledby) //won't move if being pulled - step(H, pick(cardinal)) - if(prob(1)) - H.emote(pick("scratch","jump","roll","tail")) + // Traitgenes Monkeys perform emotes based on their traits + if(H.canmove && isturf(H.loc) && !H.pulledby) //won't move if being pulled + if(prob(33)) + step(H, pick(cardinal)) + if(prob(5)) + // Handle generic gene expression emotes + if(!H.species || !H.species.traits || H.species.traits.len == 0) + H.emote(pick("scratch","jump","roll","tail")) // fallbacks + else + var/datum/trait/T = all_traits[pick(H.species.traits)] + if(T) + var/geneexpression + if(T.primitive_expression_messages.len) + geneexpression = pick(T.primitive_expression_messages) + if(geneexpression) + H.custom_emote(VISIBLE_MESSAGE, "[geneexpression]") + else + H.emote(pick("scratch","jump","roll","tail")) + // More... intense, expressions... + if(prob(5) && H.mutations.len) + if((LASER in H.mutations)) + // zappy monkeys + var/list/targs = list() + for(var/atom/X in orange(7, H)) + targs.Add(X) + if(targs.len) + H.LaserEyes(pick(targs)) ..() /datum/species/monkey/get_random_name() return "[lowertext(name)] ([rand(100,999)])" +/datum/species/monkey/handle_post_spawn(var/mob/living/carbon/human/H) + if(!H.ckey) + H.can_be_drop_prey = TRUE + H.digest_leave_remains = 1 + H.low_priority = TRUE + return ..() + /datum/species/monkey/tajaran name = SPECIES_MONKEY_TAJ name_plural = "Farwa" diff --git a/code/modules/mob/living/carbon/human/species/station/protean_vr/protean_rig.dm b/code/modules/mob/living/carbon/human/species/station/protean_vr/protean_rig.dm index d948e336778..6a4f32a8f40 100644 --- a/code/modules/mob/living/carbon/human/species/station/protean_vr/protean_rig.dm +++ b/code/modules/mob/living/carbon/human/species/station/protean_vr/protean_rig.dm @@ -60,8 +60,12 @@ /obj/item/rig/protean/Destroy() if(myprotean) var/mob/living/carbon/human/P = myprotean - var/datum/species/protean/S = P?.species - S?.OurRig = null + if(!ishuman(P) && isprotblob(myprotean)) + var/mob/living/simple_mob/protean_blob/blob = myprotean + P = blob.humanform + if(ishuman(P)) + var/datum/species/protean/S = P?.species + S?.OurRig = null myprotean = null . = ..() diff --git a/code/modules/mob/living/carbon/human/species/station/protean_vr/protean_species.dm b/code/modules/mob/living/carbon/human/species/station/protean_vr/protean_species.dm index b663d748e93..dd713c6a5a7 100755 --- a/code/modules/mob/living/carbon/human/species/station/protean_vr/protean_species.dm +++ b/code/modules/mob/living/carbon/human/species/station/protean_vr/protean_species.dm @@ -118,8 +118,6 @@ var/global/list/abilities = list() - var/monochromatic = FALSE //IGNORE ME - var/blob_appearance = "puddle1" var/blob_color_1 = "#363636" var/blob_color_2 = "#ba3636" diff --git a/code/modules/mob/living/carbon/human/species/station/traits_vr/negative.dm b/code/modules/mob/living/carbon/human/species/station/traits_vr/negative.dm index b8d1ff7b4b9..3b0f9ad02fc 100644 --- a/code/modules/mob/living/carbon/human/species/station/traits_vr/negative.dm +++ b/code/modules/mob/living/carbon/human/species/station/traits_vr/negative.dm @@ -32,6 +32,12 @@ var_changes = list("item_slowdown_mod" = 2.0) custom_only = FALSE banned_species = list(SPECIES_TESHARI) //These are already this weak. + //Traitgenes + is_genetrait = TRUE + hidden = FALSE + + activation_message="Everything feels so much heavier" + primitive_expression_messages=list("hunches forwards") /datum/trait/negative/endurance_low name = "Low Endurance" @@ -112,6 +118,13 @@ custom_only = FALSE varchange_type = TRAIT_VARCHANGE_LESS_BETTER + //Traitgenes + is_genetrait = TRUE + hidden = FALSE + + activation_message="You recieve a sudden shock of static." + primitive_expression_messages=list("shudders as their hair stands on end") + /datum/trait/negative/haemophilia name = "Haemophilia - Organics only" desc = "When you bleed, you bleed a LOT." @@ -139,6 +152,13 @@ var_changes = list("lightweight" = 1) custom_only = FALSE + //Traitgenes + is_genetrait = TRUE + hidden = FALSE + + activation_message="You feel off balance..." + primitive_expression_messages=list("staggers") + /datum/trait/negative/neural_hypersensitivity name = "Neural Hypersensitivity" desc = "Your nerves are particularly sensitive to physical changes, leading to experiencing twice the intensity of pain and pleasure alike. Makes all pain effects twice as strong, and occur at half as much damage." @@ -151,6 +171,13 @@ cost = -2 can_take = ORGANICS + //Traitgenes + is_genetrait = TRUE + hidden = FALSE + + activation_message="You feel like breathing is more difficult..." + primitive_expression_messages=list("gasps") + /datum/trait/negative/breathes/phoron name = "Phoron Breather" desc = "You breathe phoron instead of oxygen (which is poisonous to you), much like a Vox." @@ -161,6 +188,7 @@ desc = "You breathe nitrogen instead of oxygen (which is poisonous to you). Incidentally, phoron isn't poisonous to breathe to you." var_changes = list("breath_type" = GAS_N2, "poison_type" = GAS_O2, "ideal_air_type" = /datum/gas_mixture/belly_air/nitrogen_breather) + /datum/trait/negative/monolingual name = "Monolingual" desc = "You are not good at learning languages." @@ -178,6 +206,13 @@ custom_only = FALSE varchange_type = TRAIT_VARCHANGE_MORE_BETTER + //Traitgenes + is_genetrait = TRUE + hidden = FALSE + + activation_message="The dark seems darker than usual." + primitive_expression_messages=list("looks towards the light") + /datum/trait/negative/bad_shooter name = "Bad Shot" desc = "You are terrible at aiming." @@ -186,6 +221,13 @@ custom_only = FALSE varchange_type = TRAIT_VARCHANGE_MORE_BETTER + //Traitgenes + is_genetrait = TRUE + hidden = FALSE + + activation_message="Your hands quiver" + primitive_expression_messages=list("hands quiver uncontrollably") + /datum/trait/negative/bad_swimmer name = "Bad Swimmer" desc = "You can't swim very well, all water slows you down a lot and you drown in deep water. You also swim up and down 25% slower." @@ -195,6 +237,466 @@ varchange_type = TRAIT_VARCHANGE_LESS_BETTER excludes = list(/datum/trait/positive/good_swimmer) +/datum/trait/negative/less_blood + name = "Low Blood Volume" + desc = "You have 33.3% less blood volume compared to most species, making you more prone to blood loss issues." + cost = -3 + var_changes = list("blood_volume" = 375) + excludes = list(/datum/trait/negative/less_blood_extreme) + can_take = ORGANICS + +/datum/trait/negative/less_blood_extreme + name = "Low Blood Volume, Extreme" + desc = "You have 60% less blood volume compared to most species, making you much more prone to blood loss issues." + cost = -5 + var_changes = list("blood_volume" = 224) + excludes = list(/datum/trait/negative/less_blood) + can_take = ORGANICS + +/datum/trait/negative/extreme_slowdown + name = "Slowdown, Extreme" + desc = "You move EXTREMELY slower than baseline" + cost = -8 + var_changes = list("slowdown" = 4.0) + + //Traitgenes + is_genetrait = TRUE + hidden = FALSE + + activation_message="You feel like moving is more difficult..." + primitive_expression_messages=list("moves sluggishly") + +/datum/trait/negative/low_blood_sugar + name = "Low Blood Sugar" + desc = "If you let your nutrition get too low, you will start to experience adverse affects including hallucinations, unconsciousness, and weakness" + cost = -1 + special_env = TRUE + + // Traitgenes Made into a gene trait + is_genetrait = TRUE + hidden = FALSE + + activation_message="You feel drowsy..." + primitive_expression_messages=list("looks drowsy") + +/datum/trait/negative/low_blood_sugar/handle_environment_special(var/mob/living/carbon/human/H) + if(H.nutrition > 200) //Sanity check because stupid bugs >:v + return + if((H.nutrition < 200) && prob(5)) + if(H.nutrition > 100) + to_chat(H,span_warning("You start to feel noticeably weak as your stomach rumbles, begging for more food. Maybe you should eat something to keep your blood sugar up")) + else if(H.nutrition > 50) + to_chat(H,span_warning("You begin to feel rather weak, and your stomach rumbles loudly. You feel lightheaded and it's getting harder to think. You really need to eat something.")) + else if(H.nutrition > 25) + to_chat(H,span_danger("You're feeling very weak and lightheaded, and your stomach continously rumbles at you. You really need to eat something!")) + else + to_chat(H,span_critical("You're feeling extremely weak and lightheaded. You feel as though you might pass out any moment and your stomach is screaming for food by now! You should really find something to eat!")) + if((H.nutrition < 100) && prob(10)) + H.Confuse(10) + if((H.nutrition < 50) && prob(25)) + H.hallucination = max(30,H.hallucination+8) + if((H.nutrition < 25) && prob(5)) + H.drowsyness = max(100,H.drowsyness+30) + + +/datum/trait/negative/blindness + name = "Permanently blind" + desc = "You are blind. For whatever reason, nothing is able to change this fact, not even surgery. WARNING: YOU WILL NOT BE ABLE TO SEE ANY POSTS USING THE ME VERB, ONLY SUBTLE AND DIALOGUE ARE VIEWABLE TO YOU, YOU HAVE BEEN WARNED." + cost = -12 + special_env = TRUE + custom_only = FALSE + + // Traitgenes Replaces /datum/trait/negative/disability_blind, made into a gene trait + is_genetrait = TRUE + hidden = TRUE //Making this so people can't pick it in character select. While a blind character makes senese, not being able to see posts is a massive issue that needs to be addressed some other time. + + sdisability=BLIND + activation_message="You can't seem to see anything." + primitive_expression_messages=list("stumbles aimlessly.") + +/datum/trait/negative/blindness/handle_environment_special(var/mob/living/carbon/human/H) + H.sdisabilities |= sdisability //no matter what you do, the blindess still comes for you // Traitgenes tweaked to be consistant with other gene traits by using var + +/datum/trait/negative/agoraphobia + name = "Agoraphobia" + desc = "You very much dislike being in crowded places. When in the company of more than two other people, you start to panic and experience adverse effects." + cost = -3 + var/warning_cap = 400 + var/hallucination_cap = 25 + var/escalation_speed = 0.8 + special_env = TRUE + excludes = list(/datum/trait/negative/lonely,/datum/trait/negative/lonely/major) + + //Traitgenes + is_genetrait = TRUE + hidden = FALSE + + activation_message="You feel like you need some space" + primitive_expression_messages=list("keeps their distance from others") + +/datum/trait/negative/agoraphobia/handle_environment_special(var/mob/living/carbon/human/H) + spawn(0) + var/list/in_range = list() + // If they're dead or unconcious they're a bit beyond this kind of thing. + if(H.stat) + return + // No point processing if we're already stressing the hell out. + if(H.hallucination >= hallucination_cap && H.loneliness_stage >= warning_cap) + return + in_range |= check_mob_company(H,H) //Checks our item slots and bellies for any people. + in_range |= belly_check(H,H.loc) //Recursive check if we're in anyones bellies, are they in anyone's belly, etc. + in_range |= holder_check(H,H.loc) //Recursive check if someone's holding us, is anyone holding them, etc. + + // Check for company. + for(var/mob/living/M in viewers(get_turf(H))) + in_range |= check_mob_company(H,M) + + for(var/obj/effect/overlay/aiholo/A in range(5, H)) + in_range |= A + + if(in_range.len > 2) + if(H.loneliness_stage < warning_cap) + H.loneliness_stage = min(warning_cap,H.loneliness_stage+escalation_speed) + handle_loneliness(H) + if(H.loneliness_stage >= warning_cap && H.hallucination < hallucination_cap) + H.hallucination = min(hallucination_cap,H.hallucination+2.5*escalation_speed) + else + H.loneliness_stage = max(H.loneliness_stage-4,0) + + +/datum/trait/negative/agoraphobia/proc/handle_loneliness(var/mob/living/carbon/human/H) + if(world.time < H.next_loneliness_time) + return //Moved this at the top so we dont waste time assigning vars we will never use + var/ms = handle_loneliness_message(H) + if(ms) + to_chat(H, ms) + H.next_loneliness_time = world.time+500 + H.fear = min((H.fear + 3), 102) + +/datum/trait/negative/agoraphobia/proc/handle_loneliness_message(var/mob/living/carbon/human/H) + var/Lonely = H.loneliness_stage + if(Lonely == escalation_speed) + return "You notice there's more people than you feel comfortable with around you..." + else if(Lonely >= 50 && Lonely < 250) + return "You start to feel anxious from the number of people around you." + else if(Lonely >= 250 && Lonely < warning_cap) + if(H.stuttering < hallucination_cap) + H.stuttering += 5 + return "[pick("You don't think you can last much longer with this much company!", "You should go find some space!")]" //if we add more here make it a list for readability + else if(Lonely >= warning_cap) + var/list/panicmessages = list( "Why am I still here? I have to leave and get some space!", + "Please, just let me be alone!", + "I need to be alone!") + return span_bolddanger("[pick(panicmessages)]") + return FALSE + +/datum/trait/negative/agoraphobia/proc/find_held_by(var/atom/item) + if(!item || !istype(item)) + return null + else if(istype(item,/mob/living)) + return item + else + return find_held_by(item.loc) + +/datum/trait/negative/agoraphobia/proc/holder_check(var/mob/living/carbon/human/H,var/obj/item/holder/H_holder) + var/list/in_range = list() + if(istype(H_holder)) + var/mob/living/held_by = find_held_by(H_holder) + if(held_by) + in_range |= check_mob_company(H,held_by,FALSE) + in_range |= holder_check(H,held_by) + return in_range + +/datum/trait/negative/agoraphobia/proc/belly_check(var/mob/living/carbon/human/H,var/obj/belly/B) + var/list/in_range = list() + if(istype(B)) + in_range |= check_mob_company(H,B.owner,FALSE) + if(isbelly(B.owner.loc)) + in_range |= belly_check(H,B.owner.loc) + return in_range + +/datum/trait/negative/agoraphobia/proc/check_mob_company(var/mob/living/carbon/human/H,var/mob/living/M,var/invis_matters = TRUE) + var/list/in_range = list() + if(!istype(M)) + return in_range + var/social_check = !istype(M, /mob/living/carbon) && !istype(M, /mob/living/silicon/robot) + var/ckey_check = !M.ckey + var/overall_checks = M == H || M.stat == DEAD || social_check || ckey_check + if(invis_matters && M.invisibility > H.see_invisible) + return in_range + if(!overall_checks) + in_range |= M + in_range |= check_contents(M,H) + return in_range + +/datum/trait/negative/agoraphobia/proc/check_contents(var/atom/item,var/mob/living/carbon/human/H,var/max_layer = 3,var/current_layer = 1) + var/list/in_range = list() + if(!item || !istype(item) || current_layer > max_layer) + return in_range + for(var/datum/content in item.contents) + if(istype(content,/obj/item/holder)) + var/obj/item/holder/contentholder = content + in_range |= check_mob_company(H,contentholder.held_mob) + else + in_range |= check_contents(content,H,max_layer,current_layer+1) + return in_range + +/datum/trait/negative/lonely + name = "Minor loneliness vulnerability" + desc = "You're very prone to loneliness! Being alone for extended periods of time causes adverse effects. Most mobs will cure this loneliness as long as they aren't hostile." + cost = -1 + var/warning_cap = 400 + var/only_people = FALSE + var/hallucination_cap = 25 + var/escalation_speed = 0.8 + special_env = TRUE + excludes = list(/datum/trait/negative/lonely/major,/datum/trait/negative/agoraphobia) + +/datum/trait/negative/lonely/major + name = "Major loneliness vulnerability" + desc = "You're extremely prone to loneliness! Being alone for extended periods of time causes adverse effects. Most mobs won't be enough to cure this loneliness, you need other social beings." + cost = -3 + warning_cap = 300 + hallucination_cap = 50 + escalation_speed = 1.3 + only_people = TRUE + special_env = TRUE + excludes = list(/datum/trait/negative/lonely,/datum/trait/negative/agoraphobia) + + //Traitgenes + is_genetrait = TRUE + hidden = FALSE + + activation_message="You feel like you are in desperate need of company..." + primitive_expression_messages=list("Looks up at you pleadingly") + +/datum/trait/negative/lonely/proc/sub_loneliness(var/mob/living/carbon/human/H,var/amount = 4) + H.loneliness_stage = max(H.loneliness_stage - 4, 0) + if(world.time >= H.next_loneliness_time && H.loneliness_stage > 0) + to_chat(H, span_infoplain("The nearby company calms you down...")) + H.next_loneliness_time = world.time+500 + +/datum/trait/negative/lonely/proc/check_mob_company(var/mob/living/carbon/human/H,var/mob/living/M) + if(!istype(M)) + return 0 + var/social_check = only_people && !istype(M, /mob/living/carbon) && !istype(M, /mob/living/silicon/robot) + var/self_invisible_check = M == H || M.invisibility > H.see_invisible + var/ckey_check = only_people && !M.ckey + var/overall_checks = M.stat == DEAD || social_check || ckey_check + if(self_invisible_check) + return 0 + if((M.faction == "neutral" || M.faction == H.faction) && !overall_checks) + sub_loneliness(H) + return 1 + else + if(M.vore_organs) + for(var/obj/belly/B in M.vore_organs) + for(var/mob/living/content in B.contents) + if(istype(content)) + check_mob_company(H,content) + return 0 + +/datum/trait/negative/lonely/proc/check_contents(var/atom/item,var/mob/living/carbon/human/H,var/max_layer = 3,var/current_layer = 1) + if(!item || !istype(item) || current_layer > max_layer) + return 0 + for(var/datum/content in item.contents) + if(istype(content,/obj/item/holder)) + var/obj/item/holder/contentholder = content + if(check_mob_company(H,contentholder.held_mob)) + return 1 + else + if(check_contents(content,H,max_layer,current_layer+1)) + return 1 + return 0 + +/datum/trait/negative/lonely/handle_environment_special(var/mob/living/carbon/human/H) + spawn(0) + // If they're dead or unconcious they're a bit beyond this kind of thing. + if(H.stat) + return + // No point processing if we're already stressing the hell out. + if(H.hallucination >= hallucination_cap && H.loneliness_stage >= warning_cap) + return + + // Outpost 21 addition begin - extended loneliness mechanics + if(H.mind && H.mind.changeling) // We are never alone~ + H.loneliness_stage = 0 + return + if(H.has_brain_worms()) // Brain friends! + sub_loneliness(H) + return + // Outpost 21 addition end + + // Vored? Not gonna get frightened. + if(isbelly(H.loc)) + sub_loneliness(H) + return + if(istype(H.loc, /obj/item/holder)) + sub_loneliness(H) + return + // Check for company. + if(check_contents(H,H)) //Check our item slots and storage for any micros. + sub_loneliness(H) + return + for(var/mob/living/M in viewers(get_turf(H))) + if(check_mob_company(H,M)) + return + //Check to see if there's anyone in our belly + if(H.vore_organs) + for(var/obj/belly/B in H.vore_organs) + for(var/mob/living/content in B.contents) + if(istype(content)) + if(check_mob_company(H,content)) + return + for(var/obj/item/holder/micro/M in range(1, H)) + sub_loneliness(H) + for(var/obj/effect/overlay/aiholo/A in range(5, H)) + sub_loneliness(H) + + for(var/obj/item/toy/plushie/teshari/P in range(5, H)) + sub_loneliness(H) + + // No company? Suffer :( + if(H.loneliness_stage < warning_cap) + H.loneliness_stage = min(warning_cap,H.loneliness_stage+escalation_speed) + handle_loneliness(H) + if(H.loneliness_stage >= warning_cap && H.hallucination < hallucination_cap) + H.hallucination = min(hallucination_cap,H.hallucination+2.5*escalation_speed) + +/datum/trait/negative/lonely/proc/handle_loneliness(var/mob/living/carbon/human/H) + var/ms = "" + if(H.loneliness_stage == escalation_speed) + ms = "[pick("Well.. No one is around you anymore...","Well.. You're alone now...","You suddenly feel alone...")]" // Outpost 21 edit - More variety + if(H.loneliness_stage >= 50) + ms = "[pick("You begin to feel alone...","You feel isolated...","You need company...","Where is everyone?...","You need to find someone...")]" // Outpost 21 edit - More variety + if(H.loneliness_stage >= 250) + ms = "[pick("You don't think you can last much longer without some visible company!", "You should go find someone to be with!","You need to find company!","Find someone to be with!")]" // Outpost 21 edit - More variety + if(H.stuttering < hallucination_cap) + H.stuttering += 5 + if(H.loneliness_stage >= warning_cap) + ms = span_danger(span_bold("[pick("Where are the others?", "Please, there has to be someone nearby!", "I don't want to be alone!","Please, anyone! I don't want to be alone!")]")) // Outpost 21 edit - More variety + if(world.time < H.next_loneliness_time) + return + if(ms != "") + to_chat(H, ms) + H.next_loneliness_time = world.time+500 + H.fear = min((H.fear + 3), 102) + + +/datum/trait/negative/endurance_glass // Glass Cannon + name = "Glass Endurance" + desc = "Your body is very fragile. Reduces your maximum hitpoints to 25. Beware sneezes. You require only 50 damage in total to die, compared to 200 normally. You will go into crit after losing 25 HP, compared to crit at 100 HP." + cost = -12 // Similar to Very Low Endurance, this straight up will require you NEVER getting in a fight. This is extremely crippling. I salute the madlad that takes this. + var_changes = list("total_health" = 25) + +/datum/trait/negative/endurance_glass/apply(var/datum/species/S,var/mob/living/carbon/human/H) + ..() + H.setMaxHealth(S.total_health) + +/datum/trait/negative/endurance_glass // Glass Cannon + name = "Glass Endurance" + desc = "Your body is very fragile. Reduces your maximum hitpoints to 25. Beware sneezes. You require only 50 damage in total to die, compared to 200 normally. You will go into crit after losing 25 HP, compared to crit at 100 HP." + cost = -12 // Similar to Very Low Endurance, this straight up will require you NEVER getting in a fight. This is extremely crippling. I salute the madlad that takes this. + var_changes = list("total_health" = 25) + +/datum/trait/negative/endurance_glass/apply(var/datum/species/S,var/mob/living/carbon/human/H) + ..() + H.setMaxHealth(S.total_health) + +/datum/trait/negative/reduced_biocompat_minor + name = "Reduced Biocompatibility, Minor" + desc = "For whatever reason, you're one of the unlucky few who don't get as much benefit from modern-day chemicals. Remember to note this down in your medical records! Chems are only 80% as effective on you!" + cost = -2 + var_changes = list("chem_strength_heal" = 0.8) + can_take = ORGANICS + +/datum/trait/negative/reduced_biocompat + name = "Reduced Biocompatibility" + desc = "For whatever reason, you're one of the unlucky few who don't get as much benefit from modern-day chemicals. Remember to note this down in your medical records! Chems are only 60% as effective on you!" + cost = -4 + var_changes = list("chem_strength_heal" = 0.6) + can_take = ORGANICS + +/datum/trait/negative/reduced_biocompat_extreme + name = "Reduced Biocompatibility, Major" + desc = "For whatever reason, you're one of the unlucky few who don't get as much benefit from modern-day chemicals. Remember to note this down in your medical records! Chems are only 30% as effective on you!" + cost = -8 + var_changes = list("chem_strength_heal" = 0.3) + can_take = ORGANICS + + //Traitgenes + is_genetrait = TRUE + hidden = FALSE + + activation_message="You feel no different" + primitive_expression_messages=list("blinks") + +// Rykkanote: Relocated these here as we're no longer a YW downstream. +/datum/trait/negative/light_sensitivity + name = "Photosensitivity" + desc = "You have trouble dealing with sudden flashes of light, taking some time for you to recover. The effects of flashes from cameras and security equipment leaves you stunned for some time. 50% increased stun duration from flashes." + cost = -1 + var_changes = list("flash_mod" = 1.5) + +/datum/trait/negative/light_sensitivity_plus + name = "Photosensitivity, Major" + desc = "You have trouble dealing with sudden flashes of light, taking quite a long time for you to be able to recover. The effects of flashes from cameras and security equipment leave you stunned for some time. 100% (2x) stun duration from flashes." + cost = -2 + var_changes = list("flash_mod" = 2.0) + + //Traitgenes + is_genetrait = TRUE + hidden = FALSE + + activation_message="Lights feel painful to look at..." + primitive_expression_messages=list("holds a hand up to their eyes") + +/datum/trait/negative/haemophilia_plus + name = "Haemophilia, Major" + desc = "Some say that when it rains, it pours. Unfortunately, this is also true for yourself if you get cut. You bleed much faster than average, at 3x the normal rate." + cost = -3 + can_take = ORGANICS + + activation_message="You feel " + primitive_expression_messages=list("bumps their toe, screaming in pain") + +/datum/trait/negative/haemophilia_plus/apply(var/datum/species/S,var/mob/living/carbon/human/H) + ..() + H.add_modifier(/datum/modifier/trait/haemophilia) + +/datum/trait/negative/haemophilia_plus/unapply(var/datum/species/S,var/mob/living/carbon/human/H) + ..() + H.remove_modifiers_of_type(/datum/modifier/trait/haemophilia) + +/datum/trait/negative/pain_intolerance_basic + name = "Pain Intolerance" + desc = "You are frail and sensitive to pain. You experience 25% more pain from all sources." + cost = -2 + var_changes = list("pain_mod" = 1.2) + +/datum/trait/negative/pain_intolerance_advanced + name = "Pain Intolerance, Major" + desc = "You are highly sensitive to all sources of pain, and experience 50% more pain." + cost = -3 + var_changes = list("pain_mod" = 1.5) //this makes you extremely vulnerable to most sources of pain, a stunbaton bop or shotgun beanbag will do around 90 agony, almost enough to drop you in one hit. + + //Traitgenes + is_genetrait = TRUE + hidden = FALSE + + activation_message="You feel as though the airflow around you is painful..." + primitive_expression_messages=list("bumps their toe, screaming in pain") + +/datum/trait/negative/sensitive_biochem + name = "Sensitive Biochemistry" + desc = "Your biochemistry is a little delicate, rendering you more susceptible to both deadly toxins and the more subtle ones. You'll probably want to list this in your medical records, and perhaps in your exploitable info as well. Toxin damages and knockout drugs are 25% stronger on you." + cost = -1 + var_changes = list("chem_strength_tox" = 1.25) + + //Traitgenes + is_genetrait = TRUE + hidden = FALSE + /datum/trait/negative/slipperydirt name = "Dirt Vulnerability" desc = "Even the tiniest particles of dirt give you uneasy footing, even through several layers of footwear." diff --git a/code/modules/mob/living/carbon/human/species/station/traits_vr/negative_genes.dm b/code/modules/mob/living/carbon/human/species/station/traits_vr/negative_genes.dm new file mode 100644 index 00000000000..5b58c77fdba --- /dev/null +++ b/code/modules/mob/living/carbon/human/species/station/traits_vr/negative_genes.dm @@ -0,0 +1,209 @@ +/datum/trait/negative + category = TRAIT_TYPE_NEGATIVE + +/* Was disabled in setupgame.dm, likely nonfunctional +/datum/trait/negative/disability_hallucinations + name = "Disability: Hallucinations" + desc = "..." + cost = -3 + custom_only = FALSE + + is_genetrait = TRUE + hidden = FALSE + + mutation = mHallucination + activation_message="Your mind says 'Hello'." +*/ + +/datum/trait/negative/disability_epilepsy + name = "Epilepsy" + desc = "You experience periodic seizures." + cost = -3 + custom_only = FALSE + + is_genetrait = TRUE + hidden = FALSE + + disability=EPILEPSY + activation_message="You get a headache." + primitive_expression_messages=list("shudders and twitches.") + +/datum/trait/negative/disability_cough + name = "Coughing Fits" + desc = "You can't stop yourself from coughing." + cost = -1 + custom_only = FALSE + + is_genetrait = TRUE + hidden = FALSE + + disability=COUGHING + activation_message="You start coughing." + +/datum/trait/negative/disability_clumsy + name = "Clumsy" + desc = "You often make silly mistakes, or drop things." + cost = -2 + custom_only = FALSE + + is_genetrait = TRUE + hidden = FALSE + + mutation=CLUMSY + activation_message="You feel lightheaded." + primitive_expression_messages=list("trips.") + +/datum/trait/negative/disability_tourettes + name = "Tourettes Syndrome" + desc = "You have periodic motor seizures, and cannot stop yourself from yelling profanity." + cost = -2 + custom_only = FALSE + + is_genetrait = TRUE + hidden = FALSE + + disability=TOURETTES + activation_message="You twitch." + primitive_expression_messages=list("twitches and chitters.") + +/datum/trait/negative/disability_anxiety + name = "Anxiety Disorder" + desc = "You have extreme anxiety, often stuttering words." + cost = -1 + custom_only = FALSE + + is_genetrait = TRUE + hidden = FALSE + + disability=NERVOUS + activation_message="You feel nervous." + primitive_expression_messages=list("anxiously chitters.") + +/* Replaced by /datum/trait/negative/blindness +/datum/trait/negative/disability_blind + name = "Blinded" + desc = "You are unable to see anything." + cost = -3 + custom_only = FALSE + + is_genetrait = TRUE + hidden = FALSE + + sdisability=BLIND + activation_message="You can't seem to see anything." + +/datum/trait/negative/disability_blind/handle_environment_special(var/mob/living/carbon/human/H) + H.sdisabilities |= sdisability // In space, no one can hear you scream +*/ + +/datum/trait/negative/disability_mute + name = "Mute" + desc = "You are unable to speak." + cost = -3 + custom_only = FALSE + + is_genetrait = TRUE + hidden = FALSE + + sdisability=MUTE + activation_message="Your throat feels strange..." + primitive_expression_messages=list("screams without a sound.") + +/datum/trait/negative/disability_mute/handle_environment_special(var/mob/living/carbon/human/H) + H.sdisabilities |= sdisability // In space, no one can hear you scream + +/datum/trait/negative/disability_deaf + name = "Deaf" + desc = "You are unable to hear anything." + cost = -3 + custom_only = FALSE + + is_genetrait = TRUE + hidden = FALSE + + sdisability=DEAF + activation_message="It's kinda quiet." + primitive_expression_messages=list("stares blanky.") + +/datum/trait/negative/disability_deaf/handle_environment_special(var/mob/living/carbon/human/H) + H.sdisabilities |= sdisability // In space, I can't hear shit + +/datum/trait/negative/disability_deaf/apply(var/datum/species/S,var/mob/living/carbon/human/H) + . = ..() + H.ear_deaf = 1 + /* //Not used here, used downstream. + if(H.stat != DEAD) + H.deaf_loop.start(skip_start_sound = TRUE) // Ear Ringing/Deafness + */ + +/datum/trait/negative/disability_deaf/unapply(datum/species/S, mob/living/carbon/human/H) + . = ..() + H.ear_deaf = 0 + /* //Not used here, used downstream. + H.deaf_loop.stop() + */ + +/datum/trait/negative/disability_nearsighted + name = "Nearsighted" + desc = "You have difficulty seeing things far away." + cost = -2 + custom_only = FALSE + + is_genetrait = TRUE + hidden = FALSE + + disability=NEARSIGHTED + activation_message="Your eyes feel weird..." + primitive_expression_messages=list("squints and stares.") + +/datum/trait/negative/disability_wingdings + name = "Incomprehensible" + desc = "You are unable to speak normally, everything you say comes out as insane gibberish." + cost = -2 + custom_only = FALSE + + is_genetrait = TRUE + hidden = FALSE + + disability=WINGDINGS + activation_message="You feel a little... Ga-hoo!" + primitive_expression_messages=list("zooks!","bloops!","boinks!") + +/datum/trait/negative/disability_deteriorating + name = "Rotting Genetics" + desc = "Your body is slowly failing due to a chronic genetic disorder, expect to lose limbs or have organs shutdown randomly." + cost = -4 + custom_only = FALSE + + is_genetrait = TRUE + hidden = FALSE + + disability=DETERIORATE + activation_message="You feel sore..." + primitive_expression_messages=list("shudders.","gasps.","chokes.") + +/datum/trait/negative/disability_gibbing + name = "Gibbingtons" + desc = "Your body is on the edge of exploding, anything could set it off! A rare genetic disorder, only discovered with the invention of resleeving technology!" + cost = -5 + custom_only = FALSE + + is_genetrait = TRUE + hidden = TRUE + + disability=GIBBING + activation_message="You feel bloated..." + primitive_expression_messages=list("shudders.","gasps.","chokes.") + +/datum/trait/negative/disability_censored + name = "Censored" + desc = "You are unable to speak profanity. To an excessive degree..." + cost = -1 + custom_only = FALSE + + is_genetrait = TRUE + hidden = FALSE + + disability=CENSORED + activation_message="You feel less rude..." + primitive_expression_messages=list("BEEPS!") diff --git a/code/modules/mob/living/carbon/human/species/station/traits_vr/neutral.dm b/code/modules/mob/living/carbon/human/species/station/traits_vr/neutral.dm index 47dfae93d1b..864c9ea6067 100644 --- a/code/modules/mob/living/carbon/human/species/station/traits_vr/neutral.dm +++ b/code/modules/mob/living/carbon/human/species/station/traits_vr/neutral.dm @@ -7,6 +7,7 @@ cost = 0 var_changes = list("metabolic_rate" = 1.2, "hunger_factor" = 0.2, "metabolism" = 0.06) // +20% rate and 4x hunger (Teshari level) excludes = list(/datum/trait/neutral/metabolism_down, /datum/trait/neutral/metabolism_apex) + custom_only = FALSE /datum/trait/neutral/metabolism_down name = "Metabolism, Slow" @@ -14,6 +15,7 @@ cost = 0 var_changes = list("metabolic_rate" = 0.8, "hunger_factor" = 0.04, "metabolism" = 0.0012) // -20% of default. excludes = list(/datum/trait/neutral/metabolism_up, /datum/trait/neutral/metabolism_apex) + custom_only = FALSE /datum/trait/neutral/metabolism_apex name = "Metabolism, Apex" @@ -21,6 +23,7 @@ cost = 0 var_changes = list("metabolic_rate" = 1.4, "hunger_factor" = 0.4, "metabolism" = 0.012) // +40% rate and 8x hunger (Double Teshari) excludes = list(/datum/trait/neutral/metabolism_up, /datum/trait/neutral/metabolism_down) + custom_only = FALSE /datum/trait/neutral/coldadapt name = "Temp. Adapted, Cold" @@ -30,6 +33,13 @@ can_take = ORGANICS excludes = list(/datum/trait/neutral/hotadapt) + // Traitgenes Replaces /datum/trait/positive/superpower_cold_resist, made into a genetrait + is_genetrait = TRUE + hidden = FALSE + + activation_message="Your body is filled with warmth." + primitive_expression_messages=list("pants, sweat dripping down their head.") + /datum/trait/neutral/hotadapt name = "Temp. Adapted, Heat" desc = "You are able to withstand much hotter temperatures than other species, and can even be comfortable in extremely hot environments. You are also more vulnerable to cold environments, and have a higher body temperature as a consequence of these adaptations." @@ -38,6 +48,14 @@ can_take = ORGANICS // negates the need for suit coolers entirely for synths, so no excludes = list(/datum/trait/neutral/coldadapt) + // Traitgenes Made into a genetrait + is_genetrait = TRUE + hidden = FALSE + + activation_message="Your body feels chilly." + primitive_expression_messages=list("shivers.") + + /datum/trait/neutral/autohiss_unathi name = "Autohiss (Unathi)" desc = "You roll your S's and x's" @@ -50,10 +68,8 @@ "x" = list("ks", "kss", "ksss") ), autohiss_exempt = list(LANGUAGE_UNATHI)) - - excludes = list(/datum/trait/neutral/autohiss_tajaran, /datum/trait/neutral/autohiss_zaddat) + excludes = list(/datum/trait/neutral/autohiss_tajaran) custom_only = FALSE - banned_species = list(SPECIES_TAJARAN, SPECIES_UNATHI, SPECIES_ZADDAT) /datum/trait/neutral/autohiss_tajaran name = "Autohiss (Tajaran)" @@ -64,9 +80,8 @@ "r" = list("rr", "rrr", "rrrr") ), autohiss_exempt = list(LANGUAGE_SIIK,LANGUAGE_AKHANI,LANGUAGE_ALAI)) - excludes = list(/datum/trait/neutral/autohiss_unathi, /datum/trait/neutral/autohiss_zaddat) + excludes = list(/datum/trait/neutral/autohiss_unathi) custom_only = FALSE - banned_species = list(SPECIES_TAJARAN, SPECIES_UNATHI, SPECIES_ZADDAT) /datum/trait/neutral/autohiss_zaddat name = "Autohiss (Zaddat)" @@ -84,9 +99,8 @@ "v" = list("vv", "vvv") ), autohiss_exempt = list(LANGUAGE_ZADDAT,LANGUAGE_VESPINAE)) - excludes = list(/datum/trait/neutral/autohiss_tajaran, /datum/trait/neutral/autohiss_unathi) + excludes = list(/datum/trait/neutral/autohiss_tajaran) custom_only = FALSE - banned_species = list(SPECIES_TAJARAN, SPECIES_UNATHI, SPECIES_ZADDAT) /datum/trait/neutral/bloodsucker name = "Bloodsucker, Obligate" @@ -103,7 +117,7 @@ cost = 0 custom_only = FALSE - var_changes = list("organic_food_coeff" = 0, "bloodsucker" = TRUE) + var_changes = list("organic_food_coeff" = 0, "bloodsucker" = TRUE) //The verb is given in human.dm excludes = list(/datum/trait/neutral/bloodsucker_freeform) /datum/trait/neutral/bloodsucker/apply(var/datum/species/S,var/mob/living/carbon/human/H) @@ -225,7 +239,7 @@ add_verb(H, /mob/living/proc/toggle_stuffing_mode) /datum/trait/neutral/hard_vore - name = "Brutal Predation" + name = "Hard Vore" desc = "Allows you to tear off limbs & tear out internal organs." cost = 0 custom_only = FALSE @@ -241,10 +255,25 @@ custom_only = FALSE var_changes = list("trashcan" = 1) + // Traitgenes made into a genetrait + is_genetrait = TRUE + hidden = FALSE + + activation_message="Your stomach feels strange." + primitive_expression_messages=list("eats something off the ground.") + /datum/trait/neutral/trashcan/apply(var/datum/species/S,var/mob/living/carbon/human/H) ..() add_verb(H, /mob/living/proc/eat_trash) - add_verb(H, /mob/living/proc/toggle_trash_catching) //Ported from chompstation + add_verb(H, /mob/living/proc/toggle_trash_catching) + +// Traitgenes made into a genetrait +/datum/trait/neutral/trashcan/unapply(datum/species/S, mob/living/carbon/human/H, trait_prefs) + ..() + if(!(/mob/living/proc/eat_trash in S.inherent_verbs)) + remove_verb(H,/mob/living/proc/eat_trash) + if(!(/mob/living/proc/toggle_trash_catching in S.inherent_verbs)) + remove_verb(H,/mob/living/proc/toggle_trash_catching) /datum/trait/neutral/gem_eater name = "Expensive Taste" @@ -253,17 +282,30 @@ custom_only = FALSE var_changes = list("organic_food_coeff" = 0, "eat_minerals" = 1) + // Traitgenes made into a genetrait + is_genetrait = TRUE + hidden = FALSE + + activation_message="Your stomach feels strange." + primitive_expression_messages=list("picks up and eats something shiny off the ground.") + /datum/trait/neutral/gem_eater/apply(var/datum/species/S,var/mob/living/carbon/human/H) ..() add_verb(H, /mob/living/proc/eat_minerals) +// Traitgenes made into a genetrait +/datum/trait/neutral/gem_eater/unapply(datum/species/S, mob/living/carbon/human/H, trait_prefs) + ..() + if(!(/mob/living/proc/eat_minerals in S.inherent_verbs)) + remove_verb(H,/mob/living/proc/eat_minerals) + /datum/trait/neutral/synth_chemfurnace name = "Biofuel Processor" desc = "You are able to gain energy through consuming and processing normal food, at the cost of significantly slower recharging via cyborg chargers. Energy-dense foods such as protein bars and survival food will yield the best results." cost = 0 custom_only = FALSE can_take = SYNTHETICS - var_changes = list("organic_food_coeff" = 0, "synthetic_food_coeff" = 0.6) + var_changes = list("organic_food_coeff" = 0.75, "synthetic_food_coeff" = 1) excludes = list(/datum/trait/neutral/biofuel_value_down) /datum/trait/neutral/synth_ethanolburner @@ -290,10 +332,23 @@ var_changes = list("has_glowing_eyes" = 1) has_preferences = list("has_glowing_eyes" = list(TRAIT_PREF_TYPE_BOOLEAN, "Glowing on spawn", TRAIT_VAREDIT_TARGET_SPECIES)) + // Traitgenes Made into a genetrait + is_genetrait = TRUE + hidden = FALSE + + activation_message="Your eyes feel brighter." + primitive_expression_messages=list("eyes twinkle.") + /datum/trait/neutral/glowing_eyes/apply(var/datum/species/S,var/mob/living/carbon/human/H) ..() add_verb(H, /mob/living/carbon/human/proc/toggle_eye_glow) +// Traitgenes Made into a genetrait +/datum/trait/neutral/glowing_eyes/unapply(var/datum/species/S,var/mob/living/carbon/human/H) + ..() + if(!(/mob/living/carbon/human/proc/toggle_eye_glow in S.inherent_verbs)) + remove_verb(H,/mob/living/carbon/human/proc/toggle_eye_glow) + /datum/trait/neutral/glowing_body name = "Glowing Body" desc = "Your body glows about as much as a PDA light! Settable color and toggle in Abilities tab ingame." @@ -302,11 +357,26 @@ has_preferences = list("glow_toggle" = list(TRAIT_PREF_TYPE_BOOLEAN, "Glowing on spawn", TRAIT_VAREDIT_TARGET_MOB, FALSE), \ "glow_color" = list(TRAIT_PREF_TYPE_COLOR, "Glow color", TRAIT_VAREDIT_TARGET_MOB)) + // Traitgenes Made into a genetrait + is_genetrait = TRUE + hidden = FALSE + + activation_message="You feel enlightened." + primitive_expression_messages=list("shines and sparkles.") + /datum/trait/neutral/glowing_body/apply(var/datum/species/S,var/mob/living/carbon/human/H) ..() add_verb(H, /mob/living/proc/glow_toggle) add_verb(H, /mob/living/proc/glow_color) +// Traitgenes Made into a genetrait +/datum/trait/neutral/glowing_body/unapply(var/datum/species/S,var/mob/living/carbon/human/H) + ..() + if(!(/mob/living/proc/glow_toggle in S.inherent_verbs)) + remove_verb(H,/mob/living/proc/glow_toggle) + if(!(/mob/living/proc/glow_color in S.inherent_verbs)) + remove_verb(H,/mob/living/proc/glow_color) + //Allergen traits! Not available to any species with a base allergens var. /datum/trait/neutral/allergy name = "Allergy: Gluten" @@ -315,10 +385,22 @@ custom_only = FALSE var/allergen = ALLERGEN_GRAINS + // Traitgenes Made ALL ALLERGYS into gene traits + is_genetrait = TRUE + hidden = FALSE + + activation_message="Something feels odd..." + /datum/trait/neutral/allergy/apply(var/datum/species/S,var/mob/living/carbon/human/H) S.allergens |= allergen ..() +// Traitgenes edit begin - Made ALL ALLERGYS into gene traits +/datum/trait/neutral/allergy/unapply(var/datum/species/S,var/mob/living/carbon/human/H) + S.allergens &= ~allergen + ..() +// Traitgenes edit end + /datum/trait/neutral/allergy/meat name = "Allergy: Meat" desc = "You're highly allergic to just about any form of meat. You're probably better off just sticking to vegetables. NB: By taking this trait, you acknowledge there is a significant risk your character may suffer a fatal reaction if exposed to this substance." @@ -480,6 +562,12 @@ custom_only = FALSE var_changes = list("spice_mod" = 3) // 300% as effective if spice_mod is set to 1. If it's not 1 in species.dm, update this! + // Traitgenes Made into a gene trait + is_genetrait = TRUE + hidden = FALSE + + activation_message="Your belly feels strange..." + /datum/trait/neutral/spice_intolerance_basic name = "Spice Intolerance, Heavy" desc = "Spicy (and chilly) peppers are twice as strong. (This does not affect pepperspray.)" @@ -515,6 +603,12 @@ custom_only = FALSE var_changes = list("spice_mod" = 0.25) // 25% as effective if spice_mod is set to 1. If it's not 1 in species.dm, update this! + // Traitgenes Made into a gene trait + is_genetrait = TRUE + hidden = FALSE + + activation_message="Your belly feels strange..." + // Alcohol Traits Start Here, from negative to positive. /datum/trait/neutral/alcohol_intolerance_advanced name = "Liver of Air" @@ -523,6 +617,12 @@ custom_only = FALSE var_changes = list("chem_strength_alcohol" = 0.33) + // Traitgenes Made into a gene trait + is_genetrait = TRUE + hidden = FALSE + + activation_message="Your belly feels strange..." + /datum/trait/neutral/alcohol_intolerance_basic name = "Liver of Lilies" desc = "You have a hard time with alcohol. Maybe you just never took to it, or maybe it doesn't agree with your system... either way, alcohol hits you twice as hard." @@ -565,35 +665,77 @@ cost = 0 custom_only = FALSE var_changes = list("chem_strength_alcohol" = 4) + + // Traitgenes Made into a gene trait + is_genetrait = TRUE + hidden = FALSE + + activation_message="Your belly feels strange..." // Alcohol Traits End Here. /datum/trait/neutral/colorblind/mono name = "Colorblindness (Monochromancy)" desc = "You simply can't see colors at all, period. You are 100% colorblind." cost = 0 + custom_only = FALSE + + // Traitgenes Made into a gene trait + is_genetrait = TRUE + hidden = FALSE + + activation_message="Your eyes feel strange..." /datum/trait/neutral/colorblind/mono/apply(var/datum/species/S,var/mob/living/carbon/human/H) ..() H.add_modifier(/datum/modifier/trait/colorblind_monochrome) +// Traitgenes Made into a gene trait +/datum/trait/neutral/colorblind/mono/unapply(var/datum/species/S,var/mob/living/carbon/human/H) + ..() + H.remove_a_modifier_of_type(/datum/modifier/trait/colorblind_monochrome) + /datum/trait/neutral/colorblind/para_vulp name = "Colorblindness (Para Vulp)" desc = "You have a severe issue with green colors and have difficulty recognizing them from red colors." cost = 0 + custom_only = FALSE + + // Traitgenes Made into a gene trait + is_genetrait = TRUE + hidden = FALSE + + activation_message="Your eyes feel strange..." /datum/trait/neutral/colorblind/para_vulp/apply(var/datum/species/S,var/mob/living/carbon/human/H) ..() H.add_modifier(/datum/modifier/trait/colorblind_vulp) +// Traitgenes Made into a gene trait +/datum/trait/neutral/colorblind/para_vulp/unapply(var/datum/species/S,var/mob/living/carbon/human/H) + ..() + H.remove_a_modifier_of_type(/datum/modifier/trait/colorblind_vulp) + /datum/trait/neutral/colorblind/para_taj name = "Colorblindness (Para Taj)" desc = "You have a minor issue with blue colors and have difficulty recognizing them from red colors." cost = 0 + custom_only = FALSE + + // Traitgenes - Made into a gene trait + is_genetrait = TRUE + hidden = FALSE + + activation_message="Your eyes feel strange..." /datum/trait/neutral/colorblind/para_taj/apply(var/datum/species/S,var/mob/living/carbon/human/H) ..() H.add_modifier(/datum/modifier/trait/colorblind_taj) +// Traitgenes Made into a gene trait +/datum/trait/neutral/colorblind/para_taj/unapply(var/datum/species/S,var/mob/living/carbon/human/H) + ..() + H.remove_a_modifier_of_type(/datum/modifier/trait/colorblind_taj) + // Body shape traits /datum/trait/neutral/taller name = "Tall" @@ -731,30 +873,66 @@ cost = 0 custom_only = FALSE + // Traitgenes made into a genetrait + is_genetrait = TRUE + hidden = FALSE + + activation_message="Your mind feels more powerful." + /datum/trait/neutral/dominate_predator/apply(var/datum/species/S,var/mob/living/carbon/human/H) ..() add_verb(H, /mob/proc/dominate_predator) +// Traitgenes made into a genetrait +/datum/trait/neutral/dominate_predator/unapply(datum/species/S, mob/living/carbon/human/H, trait_prefs) + ..() + if(!(/mob/proc/dominate_predator in S.inherent_verbs)) + remove_verb(H,/mob/proc/dominate_predator) + /datum/trait/neutral/dominate_prey name = "Dominate Prey" desc = "Connect to and dominate the brain of your prey." cost = 0 custom_only = FALSE + // Traitgenes made into a genetrait + is_genetrait = TRUE + hidden = FALSE + + activation_message="Your mind feels more powerful." + /datum/trait/neutral/dominate_prey/apply(var/datum/species/S,var/mob/living/carbon/human/H) ..() add_verb(H, /mob/living/proc/dominate_prey) +// Traitgenes made into a genetrait +/datum/trait/neutral/dominate_prey/unapply(datum/species/S, mob/living/carbon/human/H, trait_prefs) + ..() + if(!(/mob/living/proc/dominate_prey in S.inherent_verbs)) + remove_verb(H,/mob/living/proc/dominate_prey) + /datum/trait/neutral/submit_to_prey name = "Submit To Prey" desc = "Allow prey's mind to control your own body." cost = 0 custom_only = FALSE + // Traitgenes made into a genetrait + is_genetrait = TRUE + hidden = FALSE + + activation_message="Your mind feels more fluid." + /datum/trait/neutral/submit_to_prey/apply(var/datum/species/S,var/mob/living/carbon/human/H) ..() add_verb(H, /mob/living/proc/lend_prey_control) +// Traitgenes made into a genetrait +/datum/trait/neutral/submit_to_prey/unapply(datum/species/S, mob/living/carbon/human/H, trait_prefs) + ..() + if(!(/mob/living/proc/lend_prey_control in S.inherent_verbs)) + remove_verb(H,/mob/living/proc/lend_prey_control) + /datum/trait/neutral/vertical_nom name = "Vertical Nom" desc = "Allows you to consume people from up above." @@ -1217,6 +1395,19 @@ input += "s" return input +/datum/trait/neutral/drippy + name = "Drippy" + desc = "You cannot hold your form together, or produce a constant film of sludge that drips off of your body. Hope the station has a janitor." + cost = 0 + var_changes = list("drippy" = 1) + + // Traitgenes Made into a genetrait + is_genetrait = TRUE + hidden = FALSE + + activation_message="You feel softer..." + primitive_expression_messages=list("drips.") + /datum/trait/neutral/mudking name = "Mudking" desc = "Somehow you are so filthy that tiles get dirty four times as quick from you walking on them." diff --git a/code/modules/mob/living/carbon/human/species/station/traits_vr/positive.dm b/code/modules/mob/living/carbon/human/species/station/traits_vr/positive.dm index 11db4fa4225..4640702fe1d 100644 --- a/code/modules/mob/living/carbon/human/species/station/traits_vr/positive.dm +++ b/code/modules/mob/living/carbon/human/species/station/traits_vr/positive.dm @@ -9,6 +9,13 @@ // banned_species = list(SPECIES_ALRAUNE, SPECIES_SHADEKIN_CREW, SPECIES_TESHARI, SPECIES_TAJARAN, SPECIES_DIONA, SPECIES_UNATHI) //Either not applicable or buffs ruin species flavour/balance // custom_only = FALSE //Keeping these in comments in case we decide to open them up in future, so the species are already organised. + // Traitgenes Replaces /datum/trait/positive/superpower_increaserun, made into a genetrait + is_genetrait = TRUE + hidden = FALSE + + activation_message="Your leg muscles pulsate." + primitive_expression_messages=list("dances around.") + /datum/trait/positive/hardy name = "Hardy" desc = "Allows you to carry heavy equipment with less slowdown." @@ -27,8 +34,8 @@ /datum/trait/positive/endurance_high name = "High Endurance" - desc = "Increases your maximum total hitpoints to 125" - cost = 4 + desc = "Increases your maximum total hitpoints to 125. You require 250 damage in total to die, compared to 200 normally. You will still go into crit after losing 125 HP, compared to crit at 100 HP." + cost = 3 var_changes = list("total_health" = 125) custom_only = FALSE banned_species = list(SPECIES_TESHARI, SPECIES_UNATHI, SPECIES_SHADEKIN_CREW) //Either not applicable or buffs are too strong @@ -39,19 +46,19 @@ /datum/trait/positive/nonconductive name = "Non-Conductive" - desc = "Decreases your susceptibility to electric shocks by a 10% amount." - cost = 1 //This effects tasers! - var_changes = list("siemens_coefficient" = 0.9) + desc = "Decreases your susceptibility to electric shocks by 25%." + cost = 2 + var_changes = list("siemens_coefficient" = 0.75) /datum/trait/positive/nonconductive_plus name = "Non-Conductive, Major" - desc = "Decreases your susceptibility to electric shocks by a 25% amount." - cost = 2 //Let us not forget this effects tasers! - var_changes = list("siemens_coefficient" = 0.75) + desc = "Decreases your susceptibility to electric shocks by 50%." + cost = 3 + var_changes = list("siemens_coefficient" = 0.5) /datum/trait/positive/darksight name = "Darksight" - desc = "Allows you to see a short distance in the dark." + desc = "Allows you to see a short distance in the dark and 10% more susceptible to flashes." cost = 1 var_changes = list("darksight" = 5, "flash_mod" = 1.1) custom_only = FALSE @@ -59,7 +66,7 @@ /datum/trait/positive/darksight_plus name = "Darksight, Major" - desc = "Allows you to see in the dark for the whole screen." + desc = "Allows you to see in the dark for almost the whole screen and 20% more susceptible to flashes." cost = 2 var_changes = list("darksight" = 8, "flash_mod" = 1.2) custom_only = FALSE @@ -190,6 +197,7 @@ name = "Aquatic" desc = "You can breathe under water and can traverse water more efficiently. Additionally, you can eat others in the water." cost = 1 + custom_only = FALSE var_changes = list("water_breather" = 1, "water_movement" = -4) //Negate shallow water. Half the speed in deep water. allowed_species = list(SPECIES_HANNER, SPECIES_CUSTOM) //So it only shows up for custom species and hanner custom_only = FALSE @@ -301,3 +309,59 @@ varchange_type = TRAIT_VARCHANGE_LESS_BETTER excludes = list(/datum/trait/negative/bad_swimmer) banned_species = list(SPECIES_AKULA) // They already swim better than this + +/datum/trait/positive/table_passer + name = "Table Passer" + desc = "You move over or under tables with ease of a Teshari." + cost = 2 + + // Traitgenes Replacement for /datum/trait/positive/superpower_midget, made into a genetrait + is_genetrait = TRUE + hidden = FALSE + activation_message="Your skin feels rubbery." + + has_preferences = list("pass_table" = list(TRAIT_PREF_TYPE_BOOLEAN, "On spawn", TRAIT_NO_VAREDIT_TARGET, TRUE)) + +/datum/trait/positive/table_passer/apply(var/datum/species/S,var/mob/living/carbon/human/H, var/list/trait_prefs) + ..() + if (trait_prefs?["pass_table"] || !trait_prefs) + H.pass_flags |= PASSTABLE + add_verb(H,/mob/living/proc/toggle_pass_table) + +// Traitgenes All genetraits need an unapply proc if they do anything special +/datum/trait/positive/table_passer/unapply(datum/species/S, mob/living/carbon/human/H) + . = ..() + if (H.pass_flags & PASSTABLE) + H.pass_flags ^= PASSTABLE + if(!(/mob/living/proc/toggle_pass_table in S.inherent_verbs)) // Teshari shouldn't lose agility + remove_verb(H,/mob/living/proc/toggle_pass_table) + +/datum/trait/positive/photosynth + name = "Photosynthesis" + desc = "Your body is able to produce nutrition from being in light." + cost = 3 + var_changes = list("photosynthesizing" = TRUE) + can_take = ORGANICS|SYNTHETICS //Synths actually use nutrition, just with a fancy covering. + +/datum/trait/positive/rad_resistance + name = "Radiation Resistance" + desc = "You are generally more resistant to radiation, and it dissipates faster from your body." + cost = 1 + var_changes = list("radiation_mod" = 0.65, "rad_removal_mod" = 3.5, "rad_levels" = list("safe" = 70, "danger_1" = 150, "danger_2" = 450, "danger_3" = 600, "danger_4" = 2250)) + +/datum/trait/positive/rad_resistance_extreme + name = "Radiation Resistance, Major" + desc = "You are much more resistant to radiation, and it dissipates much faster from your body." + cost = 2 + var_changes = list("radiation_mod" = 0.5, "rad_removal_mod" = 5, "rad_levels" = list("safe" = 150, "danger_1" = 300, "danger_2" = 600, "danger_3" = 1000, "danger_4" = 3000)) + +/datum/trait/positive/rad_immune + name = "Radiation Immunity" + desc = "For whatever reason, be it a more dense build or some quirk of your genetic code, your body is completely immune to radiation." + cost = 3 + var_changes = list("radiation_mod" = 0.0, "rad_removal_mod" = 10, "rad_levels" = list("safe" = 10000, "danger_1" = 10001, "danger_2" = 10002, "danger_3" = 10003, "danger_4" = 10004)) + + // Traitgenes + is_genetrait = TRUE + hidden = FALSE + activation_message="Your body feels mundane." diff --git a/code/modules/mob/living/carbon/human/species/station/traits_vr/positive_genes.dm b/code/modules/mob/living/carbon/human/species/station/traits_vr/positive_genes.dm new file mode 100644 index 00000000000..b2fb2984477 --- /dev/null +++ b/code/modules/mob/living/carbon/human/species/station/traits_vr/positive_genes.dm @@ -0,0 +1,206 @@ +/datum/trait/positive/superpower_nobreathe + name = "No Breathing" + desc = "You do not need to breathe." + cost = 5 + custom_only = FALSE + + is_genetrait = TRUE + hidden = TRUE // Cannot start with superpowers + + mutation = mNobreath + activity_bounds = DNA_HARD_BOUNDS + activation_message="You feel no need to breathe." + +/* +/datum/trait/positive/superpower_remoteview + name = "Remote Viewing" + desc = "Remotely view other locations." + cost = 5 + custom_only = FALSE + + is_genetrait = TRUE + activity_bounds = DNA_HARDER_BOUNDS + hidden = TRUE // Cannot start with superpowers + + mutation = mRemote + activation_message="Your mind expands." + +/datum/trait/positive/superpower_remoteview/apply(datum/species/S, mob/living/carbon/human/H) + . = ..() + add_verb(H, /mob/living/carbon/human/proc/remoteobserve) + +/datum/trait/positive/superpower_remoteview/unapply(datum/species/S, mob/living/carbon/human/H) + . = ..() + if(/mob/living/carbon/human/proc/remoteobserve in S.inherent_verbs) + remove_verb(H, /mob/living/carbon/human/proc/remoteobserve) +*/ + +/datum/trait/positive/superpower_regenerate + name = "Regenerate" + desc = "Repairs wounds slowly, including internal bleeding." + cost = 5 + custom_only = FALSE + + is_genetrait = TRUE + activity_bounds = DNA_HARDER_BOUNDS + hidden = TRUE // Cannot start with superpowers + + mutation = mRegen + activation_message="You feel better." + primitive_expression_messages=list("'s skin shift's strangely.") + +/* Replaced by /datum/trait/positive/speed_fast +/datum/trait/positive/superpower_increaserun + name = "Super Speed" + desc = "Remotely communicate" + cost = 5 + custom_only = FALSE + + is_genetrait = TRUE + activity_bounds = DNA_HARDER_BOUNDS + hidden = TRUE // Cannot start with superpowers + + mutation = mRun + activation_message="Your leg muscles pulsate." +*/ + +/datum/trait/positive/superpower_remotetalk + name = "Telepathy" + desc = "Remotely communicate" + cost = 5 + custom_only = FALSE + + is_genetrait = TRUE + hidden = TRUE // Cannot start with superpowers + + mutation = mRemotetalk + activity_bounds = DNA_HARDER_BOUNDS + activation_message="You expand your mind outwards." + primitive_expression_messages=list("makes noises to itself.") + +/datum/trait/positive/superpower_remotetalk/apply(datum/species/S, mob/living/carbon/human/H) + . = ..() + add_verb(H, /mob/living/carbon/human/proc/remotesay) + +/datum/trait/positive/superpower_remotetalk/unapply(datum/species/S, mob/living/carbon/human/H) + . = ..() + if(!(/mob/living/carbon/human/proc/remotesay in S.inherent_verbs)) + remove_verb(H, /mob/living/carbon/human/proc/remotesay) + +/datum/trait/positive/superpower_noprints + name = "No Prints" + desc = "Your hands leave no fingerprints behind." + cost = 5 + custom_only = FALSE + + is_genetrait = TRUE + hidden = TRUE // Cannot start with superpowers + + mutation = mFingerprints + activation_message="Your fingers feel numb." + primitive_expression_messages=list("flexes its digits.") + + +/datum/trait/positive/superpower_xray //This is effectively thermals. + name = "X-Ray Vision" + desc = "You can see through walls." + cost = 5 + custom_only = FALSE + + is_genetrait = TRUE + activity_bounds = DNA_HARDER_BOUNDS + hidden = TRUE // Cannot start with superpowers + + mutation = XRAY + activation_message="The walls suddenly disappear." + primitive_expression_messages=list("stares at something it cannot see.") + +/datum/trait/positive/superpower_tk + name = "Telekenesis" + desc = "You can move objects with your mind." + cost = 5 + custom_only = FALSE + + is_genetrait = TRUE + activity_bounds = DNA_HARD_BOUNDS + hidden = TRUE // Cannot start with superpowers + + mutation = TK + activation_message="You feel smarter." + primitive_expression_messages=list("grabs at something it cannot reach.") + +/datum/trait/positive/superpower_laser + name = "Laser Vision" + desc = "You can blast lasers from your eyes." + cost = 5 + custom_only = FALSE + + is_genetrait = TRUE + activity_bounds = DNA_HARD_BOUNDS + hidden = TRUE // Cannot start with superpowers + + mutation = LASER + activation_message="Your eyes feel strange..." + +/datum/trait/positive/superpower_hulk + name = "Hulk" + desc = "UURGG SMASH TINY TESHARI" + cost = 5 + custom_only = FALSE + + is_genetrait = TRUE + activity_bounds = DNA_HARD_BOUNDS + hidden = TRUE // Cannot start with superpowers + + mutation = HULK + activation_message="Your muscles hurt." + deactivation_message=span_warning("You suddenly feel very weak.") + +/datum/trait/positive/superpower_hulk/handle_environment_special(mob/living/carbon/human/H) + if(H.health <= 25) + if(H.dna) + H.dna.SetSEState(linked_gene.block, FALSE, FALSE) // Turn this thing off or so help me-- + domutcheck(H,null,MUTCHK_FORCED) + H.mutations.Remove(HULK) + H.Weaken(3) + H.emote("collapse") + +/datum/trait/positive/superpower_flashproof + name = "Flash Resistance" + desc = "Your eyes are protected against sudden flashes of intense light." + cost = 5 + custom_only = FALSE + + is_genetrait = TRUE + activity_bounds = DNA_HARDER_BOUNDS + hidden = TRUE // Cannot start with superpowers + + mutation = FLASHPROOF + activation_message="Your eyes feel more robust, how nifty..." + +/datum/trait/positive/superpower_morph + name = "Morph" + desc = "Allows complex bodily transformations." + cost = 5 + custom_only = FALSE + + is_genetrait = TRUE + activity_bounds = DNA_HARDER_BOUNDS + hidden = TRUE // Cannot start with superpowers + + activation_message="You feel more fluid." + primitive_expression_messages=list("twitches and distorts.") + +/datum/trait/positive/superpower_morph/apply(datum/species/S, mob/living/carbon/human/H) + . = ..() + add_verb(H, /mob/living/carbon/human/proc/shapeshfit_form) + +/datum/trait/positive/superpower_morph/unapply(datum/species/S, mob/living/carbon/human/H) + . = ..() + remove_verb(H, /mob/living/carbon/human/proc/shapeshfit_form) + +/mob/living/carbon/human/proc/shapeshfit_form() + set name = "Transform Shape" + set category = "Abilities.Superpower" + var/datum/tgui_module/appearance_changer/superpower/V = new(src, src) + V.tgui_interact(src) diff --git a/code/modules/mob/living/carbon/human/species/station/traits_vr/trait.dm b/code/modules/mob/living/carbon/human/species/station/traits_vr/trait.dm index fb02575361a..c4c1563e2c0 100644 --- a/code/modules/mob/living/carbon/human/species/station/traits_vr/trait.dm +++ b/code/modules/mob/living/carbon/human/species/station/traits_vr/trait.dm @@ -11,18 +11,44 @@ var/list/excludes // Store a list of paths of traits to exclude, but done automatically if they change the same vars. var/can_take = ORGANICS|SYNTHETICS // Can freaking synths use those. var/list/banned_species // A list of species that can't take this trait - var/list/allowed_species // VORESTATION EDIT:chomp port. A list of species that CAN take this trait, use this if only a few species can use it. -shark + var/list/allowed_species // A list of species that CAN take this trait, use this if only a few species can use it. -shark var/custom_only = TRUE // Trait only available for custom species var/varchange_type = TRAIT_VARCHANGE_ALWAYS_OVERRIDE //Mostly used for non-custom species. var/has_preferences //if set, should be a list of the preferences for this trait in the format: list("identifier/name of var to edit" = list(typeofpref, "text to display in prefs", TRAIT_NO_VAREDIT_TARGET/TRAIT_VAREDIT_TARGET_SPECIES/etc, (optional: default value)), etc) typeofpref should follow the defines in _traits.dm (eg. TRAIT_PREF_TYPE_BOOLEAN) + var/special_env = FALSE + + + + + // Traitgenes Traits can toggle mutations and disabilities + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // IMPORTANT - in 99% of situations you should NOT need to edit gene code when adding a new traitgene. Genes only handle the on/off state of traits, traits control the changes and behaviors! + // Just keep pretending genecode doesn't exist and you should be fine. Traitgenes were made with that in mind, and are not intended to be something you need to edit every time you add a traitgene. + // Traitgenes only require that your trait has both an apply() and unapply() if it does anything like adding verbs. Otherwise, you don't even need to add trait exceptions. traitgenes handle it automatically. + // You probably shouldn't mark traits as traitgenes if they are custom species only, species locked, or species banned traits however... - Willbird + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + var/is_genetrait = FALSE // When their trait's datum is init, it will be added to the library of genes a carbon can be mutated to have or not have + var/list/activity_bounds = DNA_DEFAULT_BOUNDS // Activation requirement for trait to turn on/off. Dna is automatically configured for this when first spawned + var/hidden = FALSE // If a trait does not show on the list, only useful for genetics only traits that cannot be taken at character creation + var/mutation = 0 // Mutation to give (or 0) + var/disability = 0 // Disability to give (or 0) + var/sdisability = 0 // SDisability to give (or 0) + var/activation_message = null // If not null, shows a message when activated as a gene + var/deactivation_message = null // If not null, shows a message when deactivated as a gene + var/list/primitive_expression_messages=list() // Monkey's custom emote when they have this gene! + + var/datum/gene/trait/linked_gene = null // Internal use, do not assign. + + + //Proc can be overridden lower to include special changes, make sure to call up though for the vars changes -/datum/trait/proc/apply(var/datum/species/S,var/mob/living/carbon/human/H, var/trait_prefs = null) //VOREStation edit: trait_prefs is a list in the format: list(identifier = value, etc) +/datum/trait/proc/apply(var/datum/species/S,var/mob/living/carbon/human/H, var/trait_prefs = null) ASSERT(S) if(var_changes) for(var/V in var_changes) - if((category == TRAIT_TYPE_POSITIVE && ((varchange_type == TRAIT_VARCHANGE_LESS_BETTER && var_changes[V] > S.vars[V]) || (varchange_type == TRAIT_VARCHANGE_MORE_BETTER && var_changes[V] < S.vars[V]))) || (category == TRAIT_TYPE_NEGATIVE && ((varchange_type == TRAIT_VARCHANGE_LESS_BETTER && var_changes[V] < S.vars[V]) || (varchange_type == TRAIT_VARCHANGE_MORE_BETTER && var_changes[V] > S.vars[V])))) - continue S.vars[V] = var_changes[V] if (trait_prefs) for (var/trait in trait_prefs) @@ -33,9 +59,55 @@ S.vars[trait] = trait_prefs[trait] if(TRAIT_VAREDIT_TARGET_MOB) H.vars[trait] = trait_prefs[trait] + // Traitgenes Traits can toggle mutations and disabilities + if(mutation) + if(!(mutation in H.mutations)) + H.mutations.Add(mutation) + if(disability) + H.disabilities |= disability // bitflag + if(sdisability) + H.sdisabilities |= sdisability // bitflag add_verb(H, /mob/living/carbon/human/proc/trait_tutorial) + if(special_env) + S.env_traits += src return +// Traitgenes Disabling traits, genes can be turned off after all! +/datum/trait/proc/unapply(var/datum/species/S,var/mob/living/carbon/human/H, var/trait_prefs = null) + ASSERT(S) + if(var_changes) + for(var/V in var_changes) + S.vars[V] = initial(S.vars[V]) + if (trait_prefs) + for (var/trait in trait_prefs) + switch(has_preferences[trait][3]) + if(TRAIT_NO_VAREDIT_TARGET) + continue + if(TRAIT_VAREDIT_TARGET_SPECIES) + S.vars[trait] = initial(S.vars[trait]) + if(TRAIT_VAREDIT_TARGET_MOB) + H.vars[trait] = initial(H.vars[trait]) + if(mutation) + H.mutations.Remove(mutation) + if(disability) + H.disabilities &= ~disability // bitflag + if(sdisability) + H.sdisabilities &= ~sdisability // bitflag + if(special_env) + S.env_traits -= src + return + +/datum/trait/proc/send_message(var/mob/living/carbon/human/H, var/enabled) + if(enabled) + if(!activation_message) + return + to_chat(H,activation_message) + else + if(!deactivation_message) + return + to_chat(H,deactivation_message) +// Traitgenes edit end + //Applying trait to preferences rather than just us. /datum/trait/proc/apply_pref(var/datum/preferences/P) ASSERT(P) @@ -57,7 +129,6 @@ P.vars[V] = initial(P.vars[V]) return -//VOREStation edit: trait preferences /datum/trait/proc/get_default_prefs() if (!LAZYLEN(has_preferences)) return null @@ -86,3 +157,6 @@ if (length(input) <= 0) return default_value_for_pref(pref) return input + +/datum/trait/proc/handle_environment_special(var/mob/living/carbon/human/H) + return diff --git a/code/modules/mob/living/carbon/human/update_icons.dm b/code/modules/mob/living/carbon/human/update_icons.dm index 968f62939b0..5f34187445d 100644 --- a/code/modules/mob/living/carbon/human/update_icons.dm +++ b/code/modules/mob/living/carbon/human/update_icons.dm @@ -2,6 +2,8 @@ Global associative list for caching humanoid icons. Index format m or f, followed by a string of 0 and 1 to represent bodyparts followed by husk fat hulk skeleton 1 or 0. */ + +//TODO: Get all these up to date with Chomp. I'd rather not do an entire caching update with the genetics PR, thanks. var/global/list/human_icon_cache = list() //key is incredibly complex, see update_icons_body() var/global/list/tail_icon_cache = list() //key is [species.race_key][r_skin][g_skin][b_skin] var/global/list/wing_icon_cache = list() // See tail. @@ -599,21 +601,7 @@ var/global/list/damage_icon_parts = list() //see UpdateDamageIcon() if(!LAZYLEN(mutations)) return //No mutations, no icons. - //TODO: THIS PROC??? - var/fat - if(FAT in mutations) - fat = "fat" - var/image/standing = image(icon = 'icons/effects/genetics.dmi', layer = BODY_LAYER+MUTATIONS_LAYER) - var/g = gender == FEMALE ? "f" : "m" - - for(var/datum/dna/gene/gene in dna_genes) - if(!gene.block) - continue - if(gene.is_active(src)) - var/underlay = gene.OnDrawUnderlays(src,g,fat) - if(underlay) - standing.underlays += underlay for(var/mut in mutations) if(mut == LASER) diff --git a/code/modules/mob/living/life.dm b/code/modules/mob/living/life.dm index 5fa23cb8211..14ab1c56cd7 100644 --- a/code/modules/mob/living/life.dm +++ b/code/modules/mob/living/life.dm @@ -6,7 +6,7 @@ if (transforming) return - handle_modifiers() //VOREStation Edit - Needs to be done even if in nullspace. + handle_modifiers() //Needs to be done even if in nullspace. if(!loc) return @@ -16,8 +16,6 @@ else environment = loc.return_air() - //handle_modifiers() // Do this early since it might affect other things later. //VOREStation Edit - handle_light() if(stat != DEAD) @@ -66,7 +64,6 @@ handle_ambience() //stuff in the stomach - //handle_stomach() //VOREStation Code update_gravity(mob_get_gravity()) @@ -85,7 +82,9 @@ handle_vision() - handle_tf_holder() //VOREStation Addition + handle_tf_holder() + + handle_dripping() /mob/living/proc/handle_breathing() return diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index ad75a072b97..3dc61437258 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -840,6 +840,13 @@ /mob/living/carbon/drop_from_inventory(var/obj/item/W, var/atom/target = null) return !(W in internal_organs) && ..() +/mob/living/proc/drop_both_hands() + if(l_hand) + unEquip(l_hand) + if(r_hand) + unEquip(r_hand) + return + /mob/living/touch_map_edge() //check for nuke disks @@ -1352,3 +1359,47 @@ to_chat(src, span_notice("You are [toggled_sleeping ? "now sleeping. Use the Sleep verb again to wake up" : "no longer sleeping"].")) if(toggled_sleeping) Sleeping(1) + +/mob/living/proc/handle_dripping() + if(prob(95)) + return + if(!isturf(src.loc)) + return + if(ishuman(src)) + var/mob/living/carbon/human/H = src + if(H.species && H.species.drippy) + // drip body color if human + var/obj/effect/decal/cleanable/blood/B + var/decal_type = /obj/effect/decal/cleanable/blood/splatter + var/turf/T = get_turf(src.loc) + + // Are we dripping or splattering? + var/list/drips = list() + // Only a certain number of drips (or one large splatter) can be on a given turf. + for(var/obj/effect/decal/cleanable/blood/drip/drop in T) + drips |= drop.drips + qdel(drop) + if(drips.len < 6) + decal_type = /obj/effect/decal/cleanable/blood/drip + + // Find a blood decal or create a new one. + B = locate(decal_type) in T + if(!B) + B = new decal_type(T) + + var/obj/effect/decal/cleanable/blood/drip/drop = B + if(istype(drop) && drips && drips.len) + drop.add_overlay(drips) + drop.drips |= drips + + // Update appearance. + drop.name = "drips of something" + drop.desc = "It's thick and gooey. Perhaps it's the chef's cooking?" + drop.dryname = "dried something" + drop.drydesc = "It's dry and crusty. The janitor isn't doing their job." + drop.basecolor = rgb(H.r_skin,H.g_skin,H.b_skin) + drop.update_icon() + drop.fluorescent = 0 + drop.invisibility = 0 + //else + // come up with drips for other mobs someday diff --git a/code/modules/mob/living/living_movement.dm b/code/modules/mob/living/living_movement.dm index 7313d1f75fd..9cac5ae73f6 100644 --- a/code/modules/mob/living/living_movement.dm +++ b/code/modules/mob/living/living_movement.dm @@ -126,12 +126,10 @@ default behaviour is: tmob.forceMove(oldloc) now_pushing = 0 return - //VOREStation Edit - Begin else if((tmob.mob_always_swap || (tmob.a_intent == I_HELP || tmob.restrained()) && (a_intent == I_HELP || src.restrained())) && canmove && can_swap && handle_micro_bump_helping(tmob)) forceMove(tmob.loc) now_pushing = 0 return - //VOREStation Edit - End if(!can_move_mob(tmob, 0, 0)) now_pushing = 0 @@ -139,11 +137,12 @@ default behaviour is: if(a_intent == I_HELP || src.restrained()) now_pushing = 0 return - // VOREStation Edit - Begin // Plow that nerd. if(ishuman(tmob)) var/mob/living/carbon/human/H = tmob if(H.species.lightweight == 1 && prob(50)) + if(HULK in H.mutations) //No knocking over the hulk + return H.visible_message(span_warning("[src] bumps into [H], knocking them off balance!")) H.Weaken(5) now_pushing = 0 @@ -151,7 +150,6 @@ default behaviour is: // Handle grabbing, stomping, and such of micros! if(step_mechanics_pref && tmob.step_mechanics_pref) if(handle_micro_bump_other(tmob)) return - // VOREStation Edit - End if(ishuman(tmob) && (FAT in tmob.mutations)) if(prob(40) && !(FAT in src.mutations)) to_chat(src, span_danger("You fail to push [tmob]'s fat ass out of the way.")) diff --git a/code/modules/mob/living/say.dm b/code/modules/mob/living/say.dm index d74188914b6..61a247eb10a 100644 --- a/code/modules/mob/living/say.dm +++ b/code/modules/mob/living/say.dm @@ -100,6 +100,10 @@ var/list/channel_to_radio_key = new if(S.speaking && (S.speaking.flags & NO_STUTTER || S.speaking.flags & SIGNLANG)) continue + if(disabilities & CENSORED) + S.message = censor_swears(S.message) // Googlybonkers + . = 1 + if((HULK in mutations) && health >= 25 && length(S.message)) S.message = "[uppertext(S.message)]!!!" verb = pick("yells","roars","hollers") @@ -113,12 +117,15 @@ var/list/channel_to_radio_key = new S.message = stutter(S.message) verb = pick("stammers","stutters") . = 1 - //VOREStation Edit Start if(muffled) verb = pick("muffles") whispering = 1 . = 1 - //VOREStation Edit End + if(disabilities & WINGDINGS) + verb = pick("gibbers","gabbers","gahoos","gazonks") // Yeah lets just be stupid + S.message = Gibberish(S.message, 100) // Googlybonkers + S.message = span_wingdings((S.message)) + . = 1 message_data[1] = message_pieces message_data[2] = verb diff --git a/code/modules/mob/living/silicon/pai/pai.dm b/code/modules/mob/living/silicon/pai/pai.dm index 6ace80b4a04..c00cdb0802b 100644 --- a/code/modules/mob/living/silicon/pai/pai.dm +++ b/code/modules/mob/living/silicon/pai/pai.dm @@ -468,6 +468,14 @@ var/obj/belly/B = loc src.forceMove(card) card.forceMove(B) + + if(istype( src.loc,/obj/structure/disposalholder)) + var/obj/structure/disposalholder/hold = loc + src.loc = card + card.loc = hold + src.forceMove(card) + card.forceMove(hold) + else //Otherwise go on floor src.loc = card card.loc = get_turf(card) diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm index 5a677811afc..b85f6b24865 100644 --- a/code/modules/mob/mob_helpers.dm +++ b/code/modules/mob/mob_helpers.dm @@ -253,8 +253,11 @@ if(p >= 70) letter = "" + var/rand_set = list("#","@","*","&","%","$","/", "<", ">", ";","*","*","*","*","*","*","*") + if(p >= 80) + rand_set += alphabet_uppercase for(var/j = 1, j <= rand(0, 2), j++) - letter += pick("#","@","*","&","%","$","/", "<", ">", ";","*","*","*","*","*","*","*") + letter += pick(rand_set) returntext += letter @@ -686,3 +689,42 @@ var/global/image/backplane /mob/proc/can_feed() return TRUE + +/proc/censor_swears(t) + /* Bleeps our swearing */ + var/static/swear_censoring_list = list("fuck", + "shit", + "damn", + "piss", + "whore", + "cunt", + "bitch", + "bastard", + "dick", + "cock", + "slut", + "dong", + "pussy", + "twat", + "snatch", + "schlong", + "damn", + "dammit", + "damnit", + "ass", + "tit", + "douch", + "prick", + "hell", + "crap") + var/haystack = t + for(var/filter in swear_censoring_list) + var/regex/needle = regex(filter, "i") + while(TRUE) + var/pos = needle.Find(haystack) + if(!pos) + break + var/partial_start = copytext(haystack,1,pos) + var/partial_end = copytext(haystack,pos+length(filter),length(haystack)+1) + haystack = "[partial_start][pick("BEEP","BLEEP","BOINK","BEEEEEP")][partial_end]" + return haystack diff --git a/code/modules/mob/new_player/new_player.dm b/code/modules/mob/new_player/new_player.dm index 0ee5d8b7adb..932b0d9fbb3 100644 --- a/code/modules/mob/new_player/new_player.dm +++ b/code/modules/mob/new_player/new_player.dm @@ -61,9 +61,10 @@ output += "

\[ " + span_linkOn(span_bold("Ready")) + " | Not Ready \]

" else output += "

\[ Ready | " + span_linkOn(span_bold("Not Ready")) + " \]

" + output += "

Join Game!

" else - output += "View the Crew Manifest

" + output += "

View the Crew Manifest

" output += "

Join Game!

" output += "

Observe

" @@ -86,21 +87,31 @@ qdel(query) if(newpoll) - output += "

Show Player Polls (NEW!)

" + output += "

Show Player Polls
(NEW!)

" else - output += "

Show Player Polls

" + output += "

Show Player Polls
No Changes

" */ - output += "

View Changelog

" + if(client?.check_for_new_server_news()) + output += "

Show Server News
(NEW!)

" + else + output += "

Show Server News
No Changes

" if(SSsqlite.can_submit_feedback(client)) output += "

[href(src, list("give_feedback" = 1), "Give Feedback")]

" if(GLOB.news_data.station_newspaper) if(client.prefs.lastlorenews == GLOB.news_data.newsindex) - output += "

Show [using_map.station_name] News

" + output += "

Show [using_map.station_name] News
No Changes

" else - output += "

Show [using_map.station_name] News (NEW!)

" + output += "

Show [using_map.station_name] News
(NEW!)

" + + /* //TODO: Actually add the fukin changelog. We need it DESPERATELY but this isn't the PR for it. + if(client.prefs.lastchangelog == changelog_hash) + output += "

Show Changelog
No Changes

" + else + output += "

Show Changelog
(NEW!)

" + */ output += "" @@ -112,7 +123,7 @@ client.prefs.lastlorenews = GLOB.news_data.newsindex SScharacter_setup.queue_preferences_save(client.prefs) - panel = new(src, "Welcome","Welcome", 210, 320, src) // VOREStation Edit + panel = new(src, "Welcome","Welcome", 210, 500, src) panel.set_window_options("can_close=0") panel.set_content(output) panel.open() @@ -166,7 +177,6 @@ ready = 0 if(href_list["refresh"]) - //src << browse(null, "window=playersetup") //closes the player setup window panel.close() new_player_panel_proc() @@ -179,7 +189,6 @@ client.prefs.dress_preview_mob(mannequin) var/mob/observer/dead/observer = new(mannequin) observer.moveToNullspace() //Let's not stay in our doomed mannequin - //qdel(mannequin) spawning = 1 if(client.media) @@ -207,7 +216,7 @@ observer.client.init_verbs() qdel(src) - return 1 + return TRUE if(href_list["late_join"]) @@ -221,13 +230,6 @@ else if(time_till_respawn) // Nonzero time to respawn to_chat(usr, span_warning("You can't respawn yet! You need to wait another [round(time_till_respawn/10/60, 0.1)] minutes.")) return -/* - if(client.prefs.species != "Human" && !check_rights(R_ADMIN, 0)) //VORESTATION EDITS: THE COMMENTED OUT AREAS FROM LINE 154 TO 178 - if (config.usealienwhitelist) - if(!is_alien_whitelisted(src, client.prefs.species)) - tgui_alert(src, "You are currently not whitelisted to Play [client.prefs.species].") - return 0 -*/ LateChoices() if(href_list["manifest"]) @@ -401,14 +403,12 @@ return 0 if(!job.player_old_enough(src.client)) return 0 - //VOREStation Add if(!job.player_has_enough_playtime(src.client)) return 0 if(!is_job_whitelisted(src,rank)) return 0 if(!job.player_has_enough_pto(src.client)) return 0 - //VOREStation Add End return 1 @@ -424,7 +424,7 @@ if(!IsJobAvailable(rank)) tgui_alert_async(src,"[rank] is not available. Please try another.") return 0 - if(!spawn_checks_vr(rank)) return 0 // VOREStation Insert + if(!spawn_checks_vr(rank)) return 0 if(!client) return 0 @@ -474,11 +474,6 @@ qdel(src) //Deletes new_player return - // Equip our custom items only AFTER deploying to spawn points eh? - //equip_custom_items(character) //VOREStation Removal - - //character.apply_traits() //VOREStation Removal - // Moving wheelchair if they have one if(character.buckled && istype(character.buckled, /obj/structure/bed/chair/wheelchair)) character.buckled.loc = character.loc @@ -547,27 +542,17 @@ if(mind) mind.active = 0 //we wish to transfer the key manually - // VOREStation edit to disable the destructive forced renaming for our responsible whitelist clowns. - //if(mind.assigned_role == JOB_CLOWN) //give them a clownname if they are a clown - // new_character.real_name = pick(clown_names) //I hate this being here of all places but unfortunately dna is based on real_name! - // new_character.rename_self("clown") mind.original = new_character - // VOREStation mind.loaded_from_ckey = client.ckey mind.loaded_from_slot = client.prefs.default_slot - // VOREStation - //mind.traits = client.prefs.traits.Copy() // VOREStation conflict mind.transfer_to(new_character) //won't transfer key since the mind is not active new_character.name = real_name client.init_verbs() new_character.dna.ready_dna(new_character) new_character.dna.b_type = client.prefs.b_type + new_character.sync_dna_traits(TRUE) // Traitgenes Sync traits to genetics if needed new_character.sync_organ_dna() - if(client.prefs.disabilities) - // Set defer to 1 if you add more crap here so it only recalculates struc_enzymes once. - N3X - new_character.dna.SetSEState(GLASSESBLOCK,1,0) - new_character.disabilities |= NEARSIGHTED for(var/lang in client.prefs.alternate_languages) var/datum/language/chosen_language = GLOB.all_languages[lang] @@ -612,7 +597,6 @@ src << browse(null, "window=latechoices") //closes late choices window src << browse(null, "window=preferences_window") //VOREStation Edit? src << browse(null, "window=News") //closes news window - //src << browse(null, "window=playersetup") //closes the player setup window panel.close() /mob/new_player/proc/has_admin_rights() diff --git a/code/modules/mob/new_player/preferences_setup.dm b/code/modules/mob/new_player/preferences_setup.dm index df38a2d66cf..f1780c31ecb 100644 --- a/code/modules/mob/new_player/preferences_setup.dm +++ b/code/modules/mob/new_player/preferences_setup.dm @@ -224,7 +224,7 @@ // Determine what job is marked as 'High' priority, and dress them up as such. if(job_civilian_low & ASSISTANT) previewJob = job_master.GetJob(JOB_ALT_VISITOR) - else if(ispAI(client.mob)) //VOREStation Edit! - pAIs shouldn't wear job gear~! + else if(client && ispAI(client.mob)) //VOREStation Edit! - pAIs shouldn't wear job gear~! //Don't do anything! else for(var/datum/job/job in job_master.occupations) diff --git a/code/modules/mob/transform_procs.dm b/code/modules/mob/transform_procs.dm index dfe813b6b74..69043ad8aa7 100644 --- a/code/modules/mob/transform_procs.dm +++ b/code/modules/mob/transform_procs.dm @@ -33,8 +33,6 @@ for(var/obj/item/W in src) drop_from_inventory(W) set_species(species.primitive_form) - dna.SetSEState(MONKEYBLOCK,1) - dna.SetSEValueRange(MONKEYBLOCK,0xDAC, 0xFFF) to_chat(src, span_infoplain(span_bold("You are now [species.name]. "))) qdel(animation) @@ -51,12 +49,10 @@ for(var/t in organs) qdel(t) - //VOREStation Edit Start - Hologram examine flavor var/mob/living/silicon/ai/O = ..(move) if(O) O.flavor_text = O.client?.prefs?.flavor_texts["general"] return O - //VOREStation Edit End return ..(move) diff --git a/code/modules/organs/internal/brain.dm b/code/modules/organs/internal/brain.dm index ddc0bec59e1..da656f040ec 100644 --- a/code/modules/organs/internal/brain.dm +++ b/code/modules/organs/internal/brain.dm @@ -33,7 +33,7 @@ GLOBAL_LIST_BOILERPLATE(all_brain_organs, /obj/item/organ/internal/brain) if(!owner || owner.stat == DEAD) defib_timer = max(--defib_timer, 0) else - defib_timer = min(++defib_timer, (CONFIG_GET(number/defib_timer) MINUTES) / 20) // Time vars measure things in ticks. Life tick happens every ~2 seconds, therefore dividing by 20 + defib_timer = min(++defib_timer, (CONFIG_GET(number/defib_timer) MINUTES) / 2) /obj/item/organ/internal/brain/proc/can_assist() return can_assist @@ -108,7 +108,7 @@ GLOBAL_LIST_BOILERPLATE(all_brain_organs, /obj/item/organ/internal/brain) if(istype(H)) qdel_swap(brainmob.dna, H.dna.Clone()) brainmob.timeofhostdeath = H.timeofdeath - brainmob.ooc_notes = H.ooc_notes //VOREStation Edit + brainmob.ooc_notes = H.ooc_notes brainmob.ooc_notes_likes = H.ooc_notes_likes brainmob.ooc_notes_dislikes = H.ooc_notes_dislikes @@ -144,7 +144,7 @@ GLOBAL_LIST_BOILERPLATE(all_brain_organs, /obj/item/organ/internal/brain) var/obj/item/organ/internal/brain/B = src if(istype(B) && owner) - if(istype(owner, /mob/living/carbon)) + if(istype(owner, /mob/living/carbon) && owner.ckey) B.transfer_identity(owner) ..() @@ -184,7 +184,7 @@ GLOBAL_LIST_BOILERPLATE(all_brain_organs, /obj/item/organ/internal/brain) can_assist = FALSE /obj/item/organ/internal/brain/slime - icon = 'icons/obj/surgery_vr.dmi' // Vorestation edit + icon = 'icons/obj/surgery_vr.dmi' name = "slime core" desc = "A complex, organic knot of jelly and crystalline particles." icon_state = "core" @@ -258,6 +258,7 @@ GLOBAL_LIST_BOILERPLATE(all_brain_organs, /obj/item/organ/internal/brain) qdel_swap(H.dna, R.dna.Clone()) H.UpdateAppearance() + H.sync_dna_traits(FALSE) // Traitgenes Sync traits to genetics if needed H.sync_organ_dna() if(!R.dna.real_name) //to prevent null names R.dna.real_name = "promethean ([rand(0,999)])" diff --git a/code/modules/organs/organ_external.dm b/code/modules/organs/organ_external.dm index 42386cb8693..fbc3dd2fde7 100644 --- a/code/modules/organs/organ_external.dm +++ b/code/modules/organs/organ_external.dm @@ -960,6 +960,16 @@ Note that amputating the affected organ does in fact remove the infection from t span_bolddanger("Your [src.name] explodes[gore]!"),\ span_danger("You hear the [gore_sound].")) + if(DROPLIMB_ACID) + if(cannot_gib) + return + var/gore = "[(robotic >= ORGAN_ROBOT) ? "": " in gush of gore"]" + var/gore_sound = "[(status >= ORGAN_ROBOT) ? "sizzling sound of melting metal" : "sickening drips of melting flesh"]" + owner.visible_message( + span_danger("\The [owner]'s [src.name] sloughs off[gore]!"),\ + span_bolddanger("Your [src.name] sloughs off of your body[gore]!"),\ + span_danger("You hear the [gore_sound].")) + var/mob/living/carbon/human/victim = owner //Keep a reference for post-removed(). var/obj/item/organ/external/parent_organ = parent @@ -1038,6 +1048,14 @@ Note that amputating the affected organ does in fact remove the infection from t qdel(src) + if(DROPLIMB_ACID) + appearance_flags &= ~PIXEL_SCALE + compile_icon() + add_blood(victim) + var/matrix/M = matrix() + M.Turn(rand(180)) + transform = M + if(victim.l_hand) if(istype(victim.l_hand,/obj/item/material/twohanded)) //if they're holding a two-handed weapon, drop it now they've lost a hand victim.l_hand.update_held_icon() diff --git a/code/modules/organs/wound.dm b/code/modules/organs/wound.dm index 164307ba68a..459be11ca29 100644 --- a/code/modules/organs/wound.dm +++ b/code/modules/organs/wound.dm @@ -396,6 +396,15 @@ "scarred stump" = 0 ) + if(DROPLIMB_ACID) + damage_type = BURN + stages = list( + "disfigured mass" = damage_amt*1.3, + "melted stump" = damage_amt, + "deformed stump" = damage_amt*0.5, + "scarred stump" = 0 + ) + ..(damage_amt) /datum/wound/lost_limb/can_merge(var/datum/wound/other) diff --git a/code/modules/projectiles/projectile/beams.dm b/code/modules/projectiles/projectile/beams.dm index b6878235c56..c4cfe599547 100644 --- a/code/modules/projectiles/projectile/beams.dm +++ b/code/modules/projectiles/projectile/beams.dm @@ -521,3 +521,7 @@ M.adjustToxLoss(-5) M.adjustOxyLoss(-5) return 1 + +/obj/item/projectile/beam/laser_vision + name = "laser" + damage = 10 diff --git a/code/modules/reagents/reagents/medicine.dm b/code/modules/reagents/reagents/medicine.dm index f2f187b0d0c..9c64bd01b9b 100644 --- a/code/modules/reagents/reagents/medicine.dm +++ b/code/modules/reagents/reagents/medicine.dm @@ -718,11 +718,20 @@ M.heal_organ_damage(3 * removed, 0) //Gives the bones a chance to set properly even without other meds if(ishuman(M)) var/mob/living/carbon/human/H = M - for(var/obj/item/organ/external/O in H.bad_external_organs) - if(O.status & ORGAN_BROKEN) - O.mend_fracture() //Only works if the bone won't rebreak, as usual - H.custom_pain("You feel a terrible agony tear through your bones!",60) - H.AdjustWeakened(1) //Bones being regrown will knock you over + var/totalvol = 0 + if(H.ingested) + for(var/datum/reagent/R in H.ingested.reagent_list) + if(istype(R,/datum/reagent/osteodaxon)) + totalvol += R.volume + totalvol += volume + if(totalvol >= 1) + for(var/obj/item/organ/external/O in H.bad_external_organs) + if(O.status & ORGAN_BROKEN) + O.mend_fracture() //Only works if the bone won't rebreak, as usual + H.custom_pain(span_danger("You feel a terrible agony tear through your [O.name]!"),60,TRUE) + H.AdjustWeakened(10) //Bones being regrown will knock you over + H.adjustHalLoss(60) + H.AdjustStunned(1) //Bones being regrown will knock you over /datum/reagent/myelamine name = REAGENT_MYELAMINE @@ -730,11 +739,11 @@ description = "Used to rapidly clot internal hemorrhages by increasing the effectiveness of platelets." reagent_state = LIQUID color = "#4246C7" - metabolism = REM * 0.5 + metabolism = REM * 0.75 overdose = REAGENTS_OVERDOSE * 0.5 overdose_mod = 1.5 scannable = 1 - var/repair_strength = 5 + var/repair_strength = 6 /datum/reagent/myelamine/affect_blood(var/mob/living/carbon/M, var/alien, var/removed) if(alien == IS_DIONA) @@ -908,6 +917,7 @@ overdose = 20 overdose_mod = 1.5 scannable = 1 + metabolism = REM * 0.06 /datum/reagent/immunosuprizine/affect_blood(var/mob/living/carbon/M, var/alien, var/removed) var/strength_mod = 1 * M.species.chem_strength_heal @@ -955,14 +965,14 @@ H.adjustToxLoss((15 / strength_mod)) I.take_damage(1) -/datum/reagent/skrellimmuno +/datum/reagent/skrellimmuno //skrell exist? name = REAGENT_MALISHQUALEM id = REAGENT_ID_MALISHQUALEM description = "A strange, oily powder used by Malish-Katish to prevent organ rejection." taste_description = "mordant" reagent_state = SOLID color = "#84B2B0" - metabolism = REM * 0.75 + metabolism = REM * 0.06 overdose = 20 overdose_mod = 1.5 scannable = 1 @@ -1004,49 +1014,18 @@ /datum/reagent/ryetalyn name = REAGENT_RYETALYN id = REAGENT_ID_RYETALYN - description = REAGENT_RYETALYN + " can cure all genetic abnomalities via a catalytic process." + description = REAGENT_RYETALYN + " can cure DNA, Cloning, and genetic damage via a catalytic process." taste_description = "acid" reagent_state = SOLID color = "#004000" overdose = REAGENTS_OVERDOSE /datum/reagent/ryetalyn/affect_blood(var/mob/living/carbon/M, var/alien, var/removed) - var/needs_update = M.mutations.len > 0 - - M.mutations = list() - M.disabilities = 0 - M.sdisabilities = 0 - - var/mob/living/carbon/human/H = M - if(alien == IS_SLIME && istype(H)) //Shifts them toward white, faster than Rezadone does toward grey. - if(prob(50)) - if(H.r_skin) - H.r_skin = round((H.r_skin + 510)/3) - if(H.r_hair) - H.r_hair = round((H.r_hair + 510)/3) - if(H.r_facial) - H.r_facial = round((H.r_facial + 510)/3) - H.adjustToxLoss(6 * removed) - if(prob(50)) - if(H.g_skin) - H.g_skin = round((H.g_skin + 510)/3) - if(H.g_hair) - H.g_hair = round((H.g_hair + 510)/3) - if(H.g_facial) - H.g_facial = round((H.g_facial + 510)/3) - H.adjustToxLoss(6 * removed) - if(prob(50)) - if(H.b_skin) - H.b_skin = round((H.b_skin + 510)/3) - if(H.b_hair) - H.b_hair = round((H.b_hair + 510)/3) - if(H.b_facial) - H.b_facial = round((H.b_facial + 510)/3) - H.adjustToxLoss(6 * removed) - - // Might need to update appearance for hulk etc. - if(needs_update && ishuman(M)) - H.update_mutations() + //Ryetalyn is for genetics damage curing not resetting mutations, breaks traitgenes + if(alien == IS_DIONA) + return + var/chem_effective = 1 * M.species.chem_strength_heal + M.adjustCloneLoss(-2 * removed * chem_effective) /datum/reagent/ethylredoxrazine name = REAGENT_ETHYLREDOXRAZINE @@ -1198,7 +1177,7 @@ var/obj/item/organ/external/O = pick(H.organs) if(prob(20) && !istype(O, /obj/item/organ/external/chest/unbreakable/slime) && !istype(O, /obj/item/organ/external/groin/unbreakable/slime)) to_chat(M, span_critical("You feel your [O] begin to dissolve, before it sloughs from your body.")) - O.droplimb() //Splat. + O.droplimb(TRUE, DROPLIMB_ACID) return //Based roughly on Levofloxacin's rather severe side-effects @@ -1212,7 +1191,7 @@ M.hallucination = max(M.hallucination, 10) //One of the levofloxacin side effects is 'spontaneous tendon rupture', which I'll immitate here. 1:1000 chance, so, pretty darn rare. - if(ishuman(M) && rand(1,10000) == 1) //VOREStation Edit (more rare) + if(ishuman(M) && rand(1,10000) == 1) //Adjusted to 1:10000 var/obj/item/organ/external/eo = pick(H.organs) //Misleading variable name, 'organs' is only external organs eo.fracture() diff --git a/code/modules/reagents/reagents/toxins.dm b/code/modules/reagents/reagents/toxins.dm index 51dfaf0fb72..ca0d2f3a39d 100644 --- a/code/modules/reagents/reagents/toxins.dm +++ b/code/modules/reagents/reagents/toxins.dm @@ -186,12 +186,12 @@ taste_mult = 0.6 reagent_state = LIQUID color = "#CF3600" - strength = 20 - metabolism = REM * 2 + strength = 15 + metabolism = REM * 0.5 /datum/reagent/toxin/cyanide/affect_blood(var/mob/living/carbon/M, var/alien, var/removed) ..() - M.adjustOxyLoss(20 * removed) + M.adjustOxyLoss(10 * removed) M.Sleeping(1) /datum/reagent/toxin/mold @@ -614,16 +614,12 @@ if(M.dna) if(prob(removed * 10)) // Removed is .2 per tick. Multiplying it by 10 makes it a 2% chance per tick. 10 units has 50 ticks, so 10 units injected should give a single good/bad mutation. - randmuti(M) if(prob(98)) randmutb(M) else randmutg(M) domutcheck(M, null) M.UpdateAppearance() - if(prob(removed * 40)) //Additionally, let's make it so there's an 8% chance per tick for a random cosmetic/not guranteed good/bad mutation. - randmuti(M)//This should equate to 4 random cosmetic mutations per 10 injected/20 ingested/30 touching units - to_chat(M, span_warning("You feel odd!")) M.apply_effect(10 * removed, IRRADIATE, 0) /datum/reagent/slimejelly @@ -884,16 +880,12 @@ if(M.dna) if(prob(removed * 10)) - randmuti(M) if(prob(98)) randmutb(M) else randmutg(M) domutcheck(M, null) M.UpdateAppearance() - if(prob(removed * 40)) - randmuti(M) - to_chat(M, span_warning("You feel odd!")) M.apply_effect(16 * removed, IRRADIATE, 0) /datum/reagent/aslimetoxin @@ -904,7 +896,7 @@ reagent_state = LIQUID color = "#FF69B4" -/datum/reagent/aslimetoxin/affect_blood(var/mob/living/carbon/M, var/alien, var/removed) // TODO: check if there's similar code anywhere else +/datum/reagent/aslimetoxin/affect_blood(var/mob/living/carbon/M, var/alien, var/removed) if(M.isSynthetic()) return @@ -914,16 +906,12 @@ if(M.dna) if(prob(removed * 10)) - randmuti(M) if(prob(98)) randmutb(M) else randmutg(M) domutcheck(M, null) M.UpdateAppearance() - if(prob(removed * 40)) - randmuti(M) - to_chat(M, span_warning("You feel odd!")) M.apply_effect(6 * removed, IRRADIATE, 0) /* diff --git a/code/modules/resleeving/autoresleever.dm b/code/modules/resleeving/autoresleever.dm index af87579bcc8..188306d8681 100644 --- a/code/modules/resleeving/autoresleever.dm +++ b/code/modules/resleeving/autoresleever.dm @@ -133,8 +133,15 @@ spawn_slots -- return + var/slot = ghost.client.prefs.default_slot if(tgui_alert(ghost, "Would you like to be resleeved?", "Resleeve", list("No","Yes")) != "Yes") return + //This keeps people from dying in round, clicking the autoresleever, then swapping savefiles and clicking 'yes' + if(slot != ghost.client.prefs.default_slot && (!equip_body || !ghost_spawns)) + var/turf/T = get_turf(src) + to_chat(ghost, span_warning("It appears as though your loaded character has not been spawned this round, or has quit the round. If you died as a different character, please load them, and try again.")) + message_admins("[key_name_admin(ghost)] swapped savefiles while using the autosleever and tried to spawn as another character! [ADMIN_JMP(T)]") + return var/mob/living/carbon/human/new_character new_character = new(spawnloc) @@ -147,6 +154,7 @@ ghost_client.prefs.copy_to(new_character) if(new_character.dna) new_character.dna.ResetUIFrom(new_character) + new_character.sync_dna_traits(TRUE) // Traitgenes Sync traits to genetics if needed new_character.sync_organ_dna() if(ghost.mind) ghost.mind.transfer_to(new_character) @@ -172,12 +180,10 @@ var/datum/language/keylang = GLOB.all_languages[ghost_client.prefs.language_custom_keys[key]] if(keylang) new_character.language_keys[key] = keylang - // VOREStation Add: Preferred Language Setting; if(ghost_client.prefs.preferred_language) // Do we have a preferred language? var/datum/language/def_lang = GLOB.all_languages[ghost_client.prefs.preferred_language] if(def_lang) new_character.default_language = def_lang - // VOREStation Add End //If desired, apply equipment. if(equip_body) @@ -217,6 +223,9 @@ new path(nif) nif.durability = record.nif_durability + if(!new_character.dna) + CRASH("[new_character] just came out of an autosleever and has no DNA! Species: [new_character.species] as mob: [new_character.type]. NIF Status: [new_character.nif]") + if(spawn_slots == -1) return else if(spawn_slots == 0) diff --git a/code/modules/resleeving/computers.dm b/code/modules/resleeving/computers.dm index 7518d62dc93..6fc44877ef1 100644 --- a/code/modules/resleeving/computers.dm +++ b/code/modules/resleeving/computers.dm @@ -32,6 +32,8 @@ var/db_key var/datum/transcore_db/our_db // These persist all round and are never destroyed, just keep a hard ref + var/gene_sequencing = FALSE // Traitgenes edit - create a dna injector for fixing dna, but don't let it be abusable + /obj/machinery/computer/transhuman/resleeving/Initialize() . = ..() pods = list() @@ -444,10 +446,34 @@ if("menu") menu = clamp(text2num(params["num"]), MENU_MAIN, MENU_MIND) . = TRUE + // Traitgenes edit begin - create a dna injector based off the BR currently selected, to allow normal doctors to reset someone's SEs + if("genereset") + if(gene_sequencing) + set_temp("Sequencing Record... Please wait.") + tgui_modal_clear(src) + else if(istype(active_br)) + set_temp("Sequencing Record...") + tgui_modal_clear(src) + gene_sequencing = TRUE + // Make the injector here, so no desync + var/obj/item/dnainjector/I = new(src) + I.name += " ([active_br.mydna.name] - Resequencer)" + I.desc = "Resequences structural enzymes to match the body record this was created from." + I.buf = active_br.mydna.copy() + I.buf.types = DNA2_BUF_SE + addtimer(CALLBACK(src, PROC_REF(dispense_injector), I), 20 SECONDS, TIMER_DELETE_ME) + . = TRUE if("cleartemp") temp = null . = TRUE +/obj/machinery/computer/transhuman/resleeving/proc/dispense_injector(var/obj/item/dnainjector/I) + I.forceMove(loc) + gene_sequencing = FALSE + set_temp("Injector dispensed...") + visible_message(span_notice("\The [src] ejects \the [I].")) + playsound(src, 'sound/machines/ding.ogg', 50, 1) + // In here because only relevant to computer /obj/item/cmo_disk_holder name = "cmo emergency packet" diff --git a/code/modules/resleeving/machines.dm b/code/modules/resleeving/machines.dm index 157bd8001b3..6e130b399f7 100644 --- a/code/modules/resleeving/machines.dm +++ b/code/modules/resleeving/machines.dm @@ -72,20 +72,6 @@ else if(status == 3) //Digital organ I.digitize() - //Give breathing equipment if needed - if(current_project.breath_type != GAS_O2) - H.equip_to_slot_or_del(new /obj/item/clothing/mask/breath(H), slot_wear_mask) - var/obj/item/tank/tankpath - if(current_project.breath_type == GAS_PHORON) - tankpath = /obj/item/tank/vox - else - tankpath = text2path("/obj/item/tank/" + current_project.breath_type) - - if(tankpath) - H.equip_to_slot_or_del(new tankpath(H), slot_back) - H.internal = H.back - if(istype(H.internal,/obj/item/tank) && H.internals) - H.internals.icon_state = "internal1" occupant = H @@ -108,15 +94,32 @@ H.dna.digitigrade = R.dna.digitigrade // ensure cloned DNA is set appropriately from record??? for some reason it doesn't get set right despite the override to datum/dna/Clone() //Apply damage - H.adjustCloneLoss(H.getMaxHealth()*1.5) + H.adjustCloneLoss((H.getMaxHealth() - CONFIG_GET(number/health_threshold_dead))*-0.75) H.Paralyse(4) H.updatehealth() //Update appearance, remake icons H.UpdateAppearance() + H.sync_dna_traits(FALSE) // Traitgenes Sync traits to genetics if needed H.sync_organ_dna() H.regenerate_icons() + // Traitgenes Moved breathing equipment to AFTER the genes set it + //Give breathing equipment if needed + if(current_project.breath_type != null && current_project.breath_type != GAS_O2) + H.equip_to_slot_or_del(new /obj/item/clothing/mask/breath(H), slot_wear_mask) + var/obj/item/tank/tankpath + if(current_project.breath_type == GAS_PHORON) + tankpath = /obj/item/tank/vox + else + tankpath = text2path("/obj/item/tank/" + current_project.breath_type) + + if(tankpath) + H.equip_to_slot_or_del(new tankpath(H), slot_back) + H.internal = H.back + if(istype(H.internal,/obj/item/tank) && H.internals) + H.internals.icon_state = "internal1" + //Basically all the VORE stuff H.ooc_notes = current_project.body_oocnotes H.ooc_notes_likes = current_project.body_ooclikes @@ -153,7 +156,7 @@ connected_message("Clone Rejected: Deceased.") return - else if(occupant.getCloneLoss() > 0) + else if(occupant.health < heal_level && occupant.getCloneLoss() > 0) //Slowly get that clone healed and finished. occupant.adjustCloneLoss(-3 * heal_rate) @@ -340,6 +343,11 @@ qdel_swap(H.dna, R.dna.Clone()) H.original_player = current_project.ckey + //Apply legs + H.digitigrade = R.dna.digitigrade // ensure clone mob has digitigrade var set appropriately + if(H.dna.digitigrade <> R.dna.digitigrade) + H.dna.digitigrade = R.dna.digitigrade // ensure cloned DNA is set appropriately from record??? for some reason it doesn't get set right despite the override to datum/dna/Clone() + //Apply damage H.adjustBruteLoss(brute_value) H.adjustFireLoss(burn_value) @@ -347,6 +355,7 @@ //Update appearance, remake icons H.UpdateAppearance() + H.sync_dna_traits(FALSE) // Traitgenes Sync traits to genetics if needed H.sync_organ_dna() H.regenerate_icons() @@ -444,7 +453,6 @@ anchored = TRUE var/blur_amount var/confuse_amount - var/sickness_duration var/mob/living/carbon/human/occupant = null var/connected = null @@ -474,10 +482,7 @@ manip_rating += M.rating blur_amount = (48 - manip_rating * 8) - var/total_rating = manip_rating + scan_rating - sickness_duration = (45 - (total_rating-4)*1.875) MINUTES // 45 minutes default, 30 minutes with max non-anomaly upgrades, 15 minutes with max anomaly ones - -/obj/machinery/transhuman/resleever/attack_hand(mob/user) +/obj/machinery/transhuman/resleever/attack_hand(mob/user as mob) tgui_interact(user) /obj/machinery/transhuman/resleever/tgui_interact(mob/user, datum/tgui/ui = null) @@ -500,17 +505,6 @@ data["stat"] = occupant.stat data["mindStatus"] = !!occupant.mind data["mindName"] = occupant.mind?.name - - if(occupant.has_modifier_of_type(/datum/modifier/resleeving_sickness) || occupant.has_modifier_of_type(/datum/modifier/faux_resleeving_sickness)) - data["resleeveSick"] = TRUE - else - data["resleeveSick"] = FALSE - - if(occupant.confused || occupant.eye_blurry) - data["initialSick"] = TRUE - else - data["initialSick"] = FALSE - return data /obj/machinery/transhuman/resleever/attackby(obj/item/W, mob/user) @@ -642,13 +636,6 @@ if(!occupant.mind) log_debug("[occupant] didn't have a mind to check for vore_death, which may be problematic.") - if(occupant.mind?.vore_death) - occupant.add_modifier(/datum/modifier/faux_resleeving_sickness, sickness_duration) - occupant.mind.vore_death = FALSE - // Normal ones get a normal modifier to nerf charging into combat - else - occupant.add_modifier(/datum/modifier/resleeving_sickness, sickness_duration) - if(occupant.mind && occupant.original_player && ckey(occupant.mind.key) != occupant.original_player) log_and_message_admins("is now a cross-sleeved character. Body originally belonged to [occupant.real_name]. Mind is now [occupant.mind.name].",occupant) diff --git a/code/modules/tgui/modules/appearance_changer.dm b/code/modules/tgui/modules/appearance_changer.dm index 6cef529d79d..84bffab6eb2 100644 --- a/code/modules/tgui/modules/appearance_changer.dm +++ b/code/modules/tgui/modules/appearance_changer.dm @@ -990,7 +990,7 @@ // ******************************************************* /datum/tgui_module/appearance_changer/cocoon name ="Appearance Editor (Cocoon)" - flags = APPEARANCE_ALL_HAIR + flags = APPEARANCE_ALL_HAIR | APPEARANCE_EYE_COLOR | APPEARANCE_SKIN customize_usr = TRUE /datum/tgui_module/appearance_changer/cocoon/tgui_status(mob/user, datum/tgui_state/state) @@ -999,6 +999,20 @@ return STATUS_CLOSE return ..() +// ******************************************************* +// Morph Superpower +// ******************************************************* +/datum/tgui_module/appearance_changer/superpower + name ="Appearance Editor (Superpower)" + flags = APPEARANCE_ALL_HAIR | APPEARANCE_EYE_COLOR | APPEARANCE_SKIN + customize_usr = TRUE + +/datum/tgui_module/appearance_changer/superpower/tgui_status(mob/user, datum/tgui_state/state) + var/datum/gene/G = get_gene_from_trait(/datum/trait/positive/superpower_morph) + if(!owner.dna.GetSEState(G.block)) + return STATUS_CLOSE + return ..() + // ******************************************************* // Body design console // ******************************************************* @@ -1076,7 +1090,7 @@ owner.dna.digitigrade = R.dna.digitigrade // ensure cloned DNA is set appropriately from record??? for some reason it doesn't get set right despite the override to datum/dna/Clone() //Update appearance, remake icons owner.UpdateAppearance() - //owner.sync_dna_traits(FALSE) //Needs trait genetics first + owner.sync_dna_traits(FALSE) owner.sync_organ_dna() owner.dna.blood_reagents = R.dna.blood_reagents owner.dna.blood_color = R.dna.blood_color diff --git a/code/modules/vchat/css/ss13styles.css b/code/modules/vchat/css/ss13styles.css index 08cea0956ad..275d93d02f8 100644 --- a/code/modules/vchat/css/ss13styles.css +++ b/code/modules/vchat/css/ss13styles.css @@ -194,6 +194,8 @@ h1.alert, h2.alert {color: #000000;} .say_quote_italics {font-style: italic; font-family: Georgia, Verdana, sans-serif;} .terminus {font-family: "Times New Roman", Times, serif, sans-serif} .interface {color: #330033;} +.psionic {color: #993399;} +.wingdings {color: #0077FF;font-family: Wingdings, Webdings} .spacer {color: #9c660b;} /* VOREStation Add */ .blob {color: #ff950d; font-weight: bold; font-style: italic;} .teppi {color: #816540; word-spacing:4pt; font-family: "Segoe Script Bold","Segoe Script",sans-serif,Verdana;} diff --git a/code/modules/vore/eating/inbelly_spawn.dm b/code/modules/vore/eating/inbelly_spawn.dm index 3a9d1fc4b17..c2934328d32 100644 --- a/code/modules/vore/eating/inbelly_spawn.dm +++ b/code/modules/vore/eating/inbelly_spawn.dm @@ -68,7 +68,7 @@ Please do not abuse this ability. return // Are we cool with this prey spawning in at all? - var/answer = tgui_alert(src, "[potential_prey.ckey] (as [potential_prey.prefs.real_name]) wants to spawn in one of your bellies. Do you accept?", "Inbelly Spawning", list("Yes", "No")) + var/answer = tgui_alert(src, "[potential_prey.prefs.real_name] wants to spawn in one of your bellies. Do you accept?", "Inbelly Spawning", list("Yes", "No")) if(answer != "Yes") to_chat(potential_prey, span_notice("Your request was turned down.")) return @@ -144,6 +144,7 @@ Please do not abuse this ability. prey.prefs.copy_to(new_character) if(new_character.dna) new_character.dna.ResetUIFrom(new_character) + new_character.sync_dna_traits(TRUE) // Traitgenes Sync traits to genetics if needed new_character.sync_organ_dna() new_character.key = player_key if(new_character.mind) diff --git a/code/modules/vore/eating/living_vr.dm b/code/modules/vore/eating/living_vr.dm index ab855a1cbe5..97fb55461db 100644 --- a/code/modules/vore/eating/living_vr.dm +++ b/code/modules/vore/eating/living_vr.dm @@ -396,14 +396,14 @@ return load_character(slotnum) - attempt_vr(user.client?.prefs_vr,"load_vore","") //VOREStation Edit + attempt_vr(user.client?.prefs_vr,"load_vore","") sanitize_preferences() return remember_default /datum/preferences/proc/return_to_character_slot(mob/user, var/remembered_default) load_character(remembered_default) - attempt_vr(user.client?.prefs_vr,"load_vore","") //VOREStation Edit + attempt_vr(user.client?.prefs_vr,"load_vore","") sanitize_preferences() // diff --git a/code/unit_tests/genetics_tests.dm b/code/unit_tests/genetics_tests.dm new file mode 100644 index 00000000000..f5da724d54b --- /dev/null +++ b/code/unit_tests/genetics_tests.dm @@ -0,0 +1,160 @@ +/datum/unit_test/enough_free_gene_slots_must_be_available + name = "GENETICS: Enough free gene slots must be available." + +/datum/unit_test/enough_free_gene_slots_must_be_available/start_test() + var/failed = FALSE + + if(GLOB.dna_genes.len > (DNA_SE_LENGTH - 10)) // Based off of traitgenes scanned on startup + failed = TRUE + + if(failed) + fail("Too few geneslots are empty, minimum 10. Increase DNA_SE_LENGTH.") + else + pass("DNA_SE_LENGTH has enough free space remaining.") + return failed + + +/datum/unit_test/enough_positive_genes_must_exist + name = "GENETICS: Must be at least one positive gene." + +/datum/unit_test/enough_positive_genes_must_exist/start_test() + var/failed = FALSE + + if(GLOB.dna_genes_good.len < 1) // Based off of traitgenes scanned on startup + failed = TRUE + + if(failed) + fail("Must have at least one positive gene.") + else + pass("Has at least one positive gene.") + return failed + + +/datum/unit_test/enough_neutral_genes_must_exist + name = "GENETICS: Must be at least one neutral gene." + +/datum/unit_test/enough_neutral_genes_must_exist/start_test() + var/failed = FALSE + + if(GLOB.dna_genes_neutral.len < 1) // Based off of traitgenes scanned on startup + failed = TRUE + + if(failed) + fail("Must have at least one neutral gene.") + else + pass("Has at least one neutral gene.") + return failed + + +/datum/unit_test/enough_bad_genes_must_exist + name = "GENETICS: Must be at least one bad gene." + +/datum/unit_test/enough_bad_genes_must_exist/start_test() + var/failed = FALSE + + if(GLOB.dna_genes_bad.len < 1) // Based off of traitgenes scanned on startup + failed = TRUE + + if(failed) + fail("Must have at least one bad gene.") + else + pass("Has at least one bad gene.") + return failed + + +/datum/unit_test/all_dna_injectors_must_be_valid + name = "GENETICS: All dna injectors must be valid." + +/datum/unit_test/all_dna_injectors_must_be_valid/start_test() + var/failed = FALSE + + for(var/injector_path in subtypesof(/obj/item/dnainjector/set_trait)) + var/obj/item/dnainjector/D = new injector_path() + if(!D.block) + log_unit_test("[injector_path]: Genetics - Injector could not resolve geneblock for trait. Missing traitgene?") + failed = TRUE + qdel(D) + + if(failed) + fail("Dna injectors have traits that are not genetraits or are missing.") + else + pass("No invalid dna injectors.") + return failed + + +/datum/unit_test/all_genes_shall_have_unique_name + name = "GENETICS: All genes shall be init with unique names." + +/datum/unit_test/all_genes_shall_have_unique_name/start_test() + var/failed = FALSE + + var/collection = list() + for(var/datum/gene/G in GLOB.dna_genes) + if(collection[G.name]) + log_unit_test("[G.name]: Genetics - Gene name was already in use.") + failed = TRUE + else + collection[G.name] = G.name + + if(failed) + fail("Genes shared names. This should not be possible on init, all genes should have their blocknumber attached to them to ensure unique names.") + else + pass("All genes have unique names to use as list ids.") + return failed + + + +/datum/unit_test/genetraits_should_have_valid_dna_bounds + name = "GENETICS: All genes should have valid activation bounds." + +/datum/unit_test/genetraits_should_have_valid_dna_bounds/start_test() + var/failed = FALSE + + for(var/datum/gene/trait/G in GLOB.trait_to_dna_genes) + if(!G.linked_trait) + log_unit_test("[G.name]: Genetics - Has missing linked trait.") + failed = TRUE + continue + + if(!G.linked_trait.activity_bounds) + log_unit_test("[G.name]: Genetics - Has no activation bounds.") + failed = TRUE + continue + + if(!G.linked_trait.activity_bounds.len) + log_unit_test("[G.name]: Genetics - Has empty activation bounds.") + failed = TRUE + continue + + // DNA activation bounds. Usually they are in a list as follows: + // [1]DNA_OFF_LOWERBOUND = 1, begining of the threshold where a gene turns off. + // [2]DNA_OFF_UPPERBOUND = a number above 1, end of the treshold where a gene turns off. + // [3]DNA_ON_LOWERBOUND = a number above DNA_OFF_UPPERBOUND(even if just by 1), threshold where a gene turns on. + // [4]DNA_ON_UPPERBOUND = 4095, end of the threshold where a gene turns on. + + var/list/bounds = G.linked_trait.activity_bounds + if(bounds[1] < 1) // lowest value a gene can be to turn off + log_unit_test("[G.name]: Genetics - DNA_OFF_LOWERBOUND, was smaller than 1.") + failed = TRUE + + if(bounds[2] < bounds[1]) + log_unit_test("[G.name]: Genetics - DNA_OFF_UPPERBOUND must be larger than DNA_OFF_LOWERBOUND, and never equal.") + failed = TRUE + + if(bounds[2] >= bounds[3]) + log_unit_test("[G.name]: Genetics - DNA_OFF_UPPERBOUND must be smaller than DNA_ON_LOWERBOUND, and never equal.") + failed = TRUE + + if(bounds[3] > bounds[4]) + log_unit_test("[G.name]: Genetics - DNA_ON_LOWERBOUND must be smaller than DNA_ON_UPPERBOUND, and never equal.") + failed = TRUE + + if(bounds[4] > 4095) // highest value a gene can be to turn on + log_unit_test("[G.name]: Genetics - DNA_ON_UPPERBOUND, was larger than 4095.") + failed = TRUE + + if(failed) + fail("Invalid activity bounds for one or more traitgenes") + else + pass("All traitgenes have activity bounds, and activity bounds are legal.") + return failed diff --git a/maps/stellar_delight/stellar_delight.dm b/maps/stellar_delight/stellar_delight.dm index 91f26246b7a..d6ee68db943 100644 --- a/maps/stellar_delight/stellar_delight.dm +++ b/maps/stellar_delight/stellar_delight.dm @@ -21,6 +21,6 @@ #elif !defined(MAP_OVERRIDE) - #warn A map has already been included, ignoring Tether + #warn A map has already been included, ignoring Stellar Delight #endif diff --git a/sound/effects/mob_effects/suit_breathe_in.ogg b/sound/effects/mob_effects/suit_breathe_in.ogg new file mode 100644 index 00000000000..945959e0ab7 Binary files /dev/null and b/sound/effects/mob_effects/suit_breathe_in.ogg differ diff --git a/sound/effects/mob_effects/suit_breathe_out.ogg b/sound/effects/mob_effects/suit_breathe_out.ogg new file mode 100644 index 00000000000..14f444b71ef Binary files /dev/null and b/sound/effects/mob_effects/suit_breathe_out.ogg differ diff --git a/sound/misc/bloop.ogg b/sound/misc/bloop.ogg new file mode 100644 index 00000000000..260e9e926e1 Binary files /dev/null and b/sound/misc/bloop.ogg differ diff --git a/tgui/packages/tgui-panel/styles/tgchat/chat-dark.scss b/tgui/packages/tgui-panel/styles/tgchat/chat-dark.scss index f31c0a8054d..1aa5fe24fb3 100644 --- a/tgui/packages/tgui-panel/styles/tgchat/chat-dark.scss +++ b/tgui/packages/tgui-panel/styles/tgchat/chat-dark.scss @@ -1428,5 +1428,6 @@ $border-width-px: $border-width * 1px; } .wingdings { + color: #0077ff; font-family: Wingdings, Webdings; } diff --git a/tgui/packages/tgui-panel/styles/tgchat/chat-light.scss b/tgui/packages/tgui-panel/styles/tgchat/chat-light.scss index 932f1bafd2e..a769314e1b0 100644 --- a/tgui/packages/tgui-panel/styles/tgchat/chat-light.scss +++ b/tgui/packages/tgui-panel/styles/tgchat/chat-light.scss @@ -1454,6 +1454,11 @@ $border-width-px: $border-width * 1px; line-height: 0.3; } +.psionic { + color: #993399; +} + .wingdings { + color: #0077ff; font-family: Wingdings, Webdings; } diff --git a/tgui/packages/tgui-panel/styles/tgchat/chat-vchatdark.scss b/tgui/packages/tgui-panel/styles/tgchat/chat-vchatdark.scss index e63d171d339..b660428ad9e 100644 --- a/tgui/packages/tgui-panel/styles/tgchat/chat-vchatdark.scss +++ b/tgui/packages/tgui-panel/styles/tgchat/chat-vchatdark.scss @@ -1428,6 +1428,11 @@ $border-width-px: $border-width * 1px; line-height: 0.3; } +.psionic { + color: #993399; +} + .wingdings { + color: #0077ff; font-family: Wingdings, Webdings; } diff --git a/tgui/packages/tgui-panel/styles/tgchat/chat-vchatlight.scss b/tgui/packages/tgui-panel/styles/tgchat/chat-vchatlight.scss index ba8b03e0078..0345879cb08 100644 --- a/tgui/packages/tgui-panel/styles/tgchat/chat-vchatlight.scss +++ b/tgui/packages/tgui-panel/styles/tgchat/chat-vchatlight.scss @@ -1452,6 +1452,11 @@ $border-width-px: $border-width * 1px; line-height: 0.3; } +.psionic { + color: #993399; +} + .wingdings { + color: #0077ff; font-family: Wingdings, Webdings; } diff --git a/tgui/packages/tgui/interfaces/DNAModifier/DNAModifierMain.tsx b/tgui/packages/tgui/interfaces/DNAModifier/DNAModifierMain.tsx index b3845437740..28478ff3e65 100644 --- a/tgui/packages/tgui/interfaces/DNAModifier/DNAModifierMain.tsx +++ b/tgui/packages/tgui/interfaces/DNAModifier/DNAModifierMain.tsx @@ -1,73 +1,25 @@ import { useBackend } from 'tgui/backend'; -import { - Box, - Button, - Icon, - Knob, - LabeledList, - Section, - Stack, - Tabs, -} from 'tgui-core/components'; -import { BooleanLike } from 'tgui-core/react'; +import { Icon, Section, Tabs } from 'tgui-core/components'; -import { operations, rejuvenatorsDoses } from './constants'; -import { DNAModifierBlocks } from './DNAModifierBlocks'; +import { operations } from './constants'; import { DNAModifierMainBuffers } from './DNAModifierMainBuffers'; +import { DNAModifierMainRejuvenators } from './DNAMofifierMainTabs/DNAModifierMainRejuvenators'; +import { DNAModifierMainSE } from './DNAMofifierMainTabs/DNAModifierMainSE'; import { Data } from './types'; -export const DNAModifierMain = (props: { isDNAInvalid: BooleanLike }) => { +export const DNAModifierMain = (props) => { const { act, data } = useBackend(); - const { selectedMenuKey, hasOccupant } = data; + const { selectedMenuKey } = data; + + const tabs: React.JSX.Element[] = []; + + tabs['se'] = ; + tabs['buffer'] = ; + tabs['rejuvenators'] = ; - if (!hasOccupant) { - return ( -
- - - -
- No occupant in DNA modifier. -
-
-
- ); - } else if (props.isDNAInvalid) { - return ( -
- - - -
- No operation possible on this subject. -
-
-
- ); - } - let body; - if (selectedMenuKey === 'ui') { - body = ( - <> - - - - ); - } else if (selectedMenuKey === 'se') { - body = ( - <> - - - - ); - } else if (selectedMenuKey === 'buffer') { - body = ; - } else if (selectedMenuKey === 'rejuvenators') { - body = ; - } return ( -
+
{operations.map((op, i) => ( { ))} - {body} -
- ); -}; - -const DNAModifierMainUI = (props) => { - const { act, data } = useBackend(); - - const { - selectedUIBlock, - selectedUISubBlock, - selectedUITarget, - dnaBlockSize, - occupant, - } = data; - - return ( -
- - - - value.toString(16).toUpperCase()} - ml="0" - onChange={(e, val) => act('changeUITarget', { value: val })} - /> - - - -
- ); -}; - -const DNAModifierMainSE = (props) => { - const { act, data } = useBackend(); - - const { selectedSEBlock, selectedSESubBlock, dnaBlockSize, occupant } = data; - - return ( -
- - -
- ); -}; - -const DNAModifierMainRadiationEmitter = (props) => { - const { act, data } = useBackend(); - - const { radiationIntensity, radiationDuration } = data; - - return ( -
- - - act('radiationIntensity', { value: val })} - /> - - - act('radiationDuration', { value: val })} - /> - - - -
- ); -}; - -const DNAModifierMainRejuvenators = (props) => { - const { act, data } = useBackend(); - - const { isBeakerLoaded, beakerVolume, beakerLabel } = data; - - return ( -
act('ejectBeaker')} - > - Eject - - } - > - {isBeakerLoaded ? ( - - - {rejuvenatorsDoses.map((a, i) => ( - - ))} - - - - {beakerLabel ? beakerLabel : 'No label'} - {beakerVolume ? ( - - {beakerVolume} unit{beakerVolume === 1 ? '' : 's'} remaining - - ) : ( - Empty - )} - - - ) : ( - - -
- No beaker loaded. -
- )} + {tabs[selectedMenuKey]}
); }; diff --git a/tgui/packages/tgui/interfaces/DNAModifier/DNAModifierMainBuffers.tsx b/tgui/packages/tgui/interfaces/DNAModifier/DNAModifierMainBuffers.tsx index 3fda2fc960b..f4e16b0f21a 100644 --- a/tgui/packages/tgui/interfaces/DNAModifier/DNAModifierMainBuffers.tsx +++ b/tgui/packages/tgui/interfaces/DNAModifier/DNAModifierMainBuffers.tsx @@ -1,26 +1,42 @@ import { useBackend } from 'tgui/backend'; -import { Box, Button, Icon, LabeledList, Section } from 'tgui-core/components'; +import { + Box, + Button, + Icon, + LabeledList, + Section, + Stack, +} from 'tgui-core/components'; -import { buffData, Data } from './types'; +import type { buffData, Data } from './types'; export const DNAModifierMainBuffers = (props) => { const { data } = useBackend(); const { buffers } = data; - let bufferElements = buffers.map((buffer, i) => ( - + const bufferElements = buffers.map((buffer, i) => ( + + + )); return ( - <> -
{bufferElements}
- - + + +
+ + {bufferElements} + +
+
+ + + +
); }; @@ -31,94 +47,86 @@ const DNAModifierMainBuffersElement = (props: { }) => { const { act, data } = useBackend(); const { id, name, buffer } = props; - const { isInjectorReady } = data; + const { isInjectorReady, hasOccupant, occupant } = data; const realName: string = name + (buffer.data ? ' - ' + buffer.label : ''); return ( - -
- - act('bufferOption', { - option: 'clear', - id: id, - }) - } - > - Clear - - - - - } - > +
+ + act('bufferOption', { + option: 'clear', + id: id, + }) + } + > + Clear + + + + + + } + > + - -
- + +
); }; @@ -206,6 +217,7 @@ const DNAModifierMainBuffersDisk = (props) => { const { hasDisk, disk } = data; return (
diff --git a/tgui/packages/tgui/interfaces/DNAModifier/DNAModifierOccupant.tsx b/tgui/packages/tgui/interfaces/DNAModifier/DNAModifierOccupant.tsx index 1868cac4dc1..570522b9891 100644 --- a/tgui/packages/tgui/interfaces/DNAModifier/DNAModifierOccupant.tsx +++ b/tgui/packages/tgui/interfaces/DNAModifier/DNAModifierOccupant.tsx @@ -7,10 +7,10 @@ import { ProgressBar, Section, } from 'tgui-core/components'; -import { BooleanLike } from 'tgui-core/react'; +import type { BooleanLike } from 'tgui-core/react'; import { stats } from './constants'; -import { Data } from './types'; +import type { Data } from './types'; export const DNAModifierOccupant = (props: { isDNAInvalid: BooleanLike }) => { const { act, data } = useBackend(); diff --git a/tgui/packages/tgui/interfaces/DNAModifier/DNAMofifierMainTabs/DNAModifierMainRadiationEmitter.tsx b/tgui/packages/tgui/interfaces/DNAModifier/DNAMofifierMainTabs/DNAModifierMainRadiationEmitter.tsx new file mode 100644 index 00000000000..bc0e3fe1e56 --- /dev/null +++ b/tgui/packages/tgui/interfaces/DNAModifier/DNAMofifierMainTabs/DNAModifierMainRadiationEmitter.tsx @@ -0,0 +1,95 @@ +import { useBackend } from 'tgui/backend'; +import { + Box, + Button, + Divider, + Knob, + Section, + Stack, +} from 'tgui-core/components'; + +import type { Data } from '../types'; + +export const DNAModifierMainRadiationEmitter = (props) => { + const { act, data } = useBackend(); + + const { radiationIntensity, radiationDuration, occupant } = data; + + return ( + (occupant && occupant.isViableSubject && ( +
+ + + + + + Intensity + + + + act('radiationIntensity', { value: val }) + } + /> + + + + + + + + Duration + + + + act('radiationDuration', { value: val }) + } + /> + + + + + + + + + + + + + + + + + +
+ )) || + null + ); +}; diff --git a/tgui/packages/tgui/interfaces/DNAModifier/DNAMofifierMainTabs/DNAModifierMainRejuvenators.tsx b/tgui/packages/tgui/interfaces/DNAModifier/DNAMofifierMainTabs/DNAModifierMainRejuvenators.tsx new file mode 100644 index 00000000000..b28635c17a3 --- /dev/null +++ b/tgui/packages/tgui/interfaces/DNAModifier/DNAMofifierMainTabs/DNAModifierMainRejuvenators.tsx @@ -0,0 +1,75 @@ +import { useBackend } from 'tgui/backend'; +import { Box, Button, Icon, LabeledList, Section } from 'tgui-core/components'; + +import { rejuvenatorsDoses } from '../constants'; +import type { Data } from '../types'; + +export const DNAModifierMainRejuvenators = (props) => { + const { act, data } = useBackend(); + + const { isBeakerLoaded, beakerVolume, beakerLabel, hasOccupant } = data; + + return ( +
act('ejectBeaker')} + > + Eject + + } + > + {isBeakerLoaded ? ( + + + {rejuvenatorsDoses.map((a, i) => ( + + ))} + + + + {beakerLabel ? beakerLabel : 'No label'} + {beakerVolume ? ( + + {beakerVolume} unit{beakerVolume === 1 ? '' : 's'} remaining + + ) : ( + Empty + )} + + + ) : ( + + +
+ No beaker loaded. +
+ )} +
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/DNAModifier/DNAMofifierMainTabs/DNAModifierMainSE.tsx b/tgui/packages/tgui/interfaces/DNAModifier/DNAMofifierMainTabs/DNAModifierMainSE.tsx new file mode 100644 index 00000000000..d8530e6bda4 --- /dev/null +++ b/tgui/packages/tgui/interfaces/DNAModifier/DNAMofifierMainTabs/DNAModifierMainSE.tsx @@ -0,0 +1,57 @@ +import { useBackend } from 'tgui/backend'; +import { Icon, Section, Stack } from 'tgui-core/components'; + +import { DNAModifierBlocks } from '../DNAModifierBlocks'; +import type { Data } from '../types'; +import { DNAModifierMainRadiationEmitter } from './DNAModifierMainRadiationEmitter'; + +export const DNAModifierMainSE = (props) => { + return ( + + + + + + + + + ); +}; + +const DNAModifierMainBlocks = (props) => { + const { act, data } = useBackend(); + + const { selectedSEBlock, selectedSESubBlock, dnaBlockSize, occupant } = data; + + return !occupant ? ( +
+ + + +
+ No occupant in DNA modifier. +
+
+
+ ) : !occupant.isViableSubject ? ( +
+ + + +
+ No operation possible on this subject. +
+
+
+ ) : ( +
+ +
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/DNAModifier/constants.ts b/tgui/packages/tgui/interfaces/DNAModifier/constants.ts index d247c8a51ac..6541f47f749 100644 --- a/tgui/packages/tgui/interfaces/DNAModifier/constants.ts +++ b/tgui/packages/tgui/interfaces/DNAModifier/constants.ts @@ -5,7 +5,6 @@ export const stats: string[][] = [ ]; export const operations: string[][] = [ - ['ui', 'Modify U.I.', 'dna'], ['se', 'Modify S.E.', 'dna'], ['buffer', 'Transfer Buffers', 'syringe'], ['rejuvenators', 'Rejuvenators', 'flask'], diff --git a/tgui/packages/tgui/interfaces/DNAModifier/index.tsx b/tgui/packages/tgui/interfaces/DNAModifier/index.tsx index f978576e45f..43c100a25c5 100644 --- a/tgui/packages/tgui/interfaces/DNAModifier/index.tsx +++ b/tgui/packages/tgui/interfaces/DNAModifier/index.tsx @@ -1,29 +1,43 @@ import { useBackend } from 'tgui/backend'; import { Window } from 'tgui/layouts'; +import { Stack } from 'tgui-core/components'; import { ComplexModal } from '../common/ComplexModal'; import { DNAModifierIrradiating } from './DNAModifierIrradiating'; import { DNAModifierMain } from './DNAModifierMain'; import { DNAModifierOccupant } from './DNAModifierOccupant'; -import { Data } from './types'; +import type { Data } from './types'; export const DNAModifier = (props) => { const { data } = useBackend(); const { irradiating, occupant } = data; - const isDNAInvalid: boolean = - !occupant.isViableSubject || - !occupant.uniqueIdentity || - !occupant.structuralEnzymes; - return ( - {irradiating && } + {irradiating ? : ''} - - + + + + + + + + ); diff --git a/tgui/packages/tgui/interfaces/DNAModifier/types.ts b/tgui/packages/tgui/interfaces/DNAModifier/types.ts index f2270f88884..253bef96774 100644 --- a/tgui/packages/tgui/interfaces/DNAModifier/types.ts +++ b/tgui/packages/tgui/interfaces/DNAModifier/types.ts @@ -1,4 +1,4 @@ -import { BooleanLike } from 'tgui-core/react'; +import type { BooleanLike } from 'tgui-core/react'; export type Data = { selectedMenuKey: string; @@ -46,7 +46,15 @@ type modalData = { }; export type buffData = { - data: number[] | null; + data: { + data: number[]; + owner: string; + label: string; + type: string; + ue: string; + ui: string; + se: string; + } | null; // Traitgenes Fixed data structure owner: string | null; label: string | null; type: string | null; diff --git a/tgui/packages/tgui/interfaces/ResleevingConsole/BodyRecordModal.tsx b/tgui/packages/tgui/interfaces/ResleevingConsole/BodyRecordModal.tsx index 7aab7736dff..e287219c049 100644 --- a/tgui/packages/tgui/interfaces/ResleevingConsole/BodyRecordModal.tsx +++ b/tgui/packages/tgui/interfaces/ResleevingConsole/BodyRecordModal.tsx @@ -46,6 +46,21 @@ export const BodyRecordModal = (props: { data: ActiveBodyRecordData }) => { > {synthetic ? 'Build' : 'Grow'} + {/* Traitgenes create a dna injector based off the BR currently selected, to allow normal doctors to reset someone's SEs */} + {!synthetic ? ( + + ) : ( + '' + )} diff --git a/vorestation.dme b/vorestation.dme index 9c508495e78..0f2dc3ce24b 100644 --- a/vorestation.dme +++ b/vorestation.dme @@ -131,7 +131,7 @@ #include "code\__defines\simple_mob.dm" #include "code\__defines\size.dm" #include "code\__defines\sound.dm" -#include "code\__defines\span_vr.dm" +#include "code\__defines\span.dm" #include "code\__defines\species_languages.dm" #include "code\__defines\species_languages_vr.dm" #include "code\__defines\speech_channels.dm" @@ -189,6 +189,7 @@ #include "code\_helpers\_global_objects.dm" #include "code\_helpers\_global_objects_vr.dm" #include "code\_helpers\_lists.dm" +#include "code\_helpers\announcements.dm" #include "code\_helpers\atmospherics.dm" #include "code\_helpers\atom_movables.dm" #include "code\_helpers\events.dm" @@ -745,9 +746,7 @@ #include "code\game\dna\dna2_domutcheck.dm" #include "code\game\dna\dna2_helpers.dm" #include "code\game\dna\dna_modifier.dm" -#include "code\game\dna\genes\disabilities.dm" #include "code\game\dna\genes\gene.dm" -#include "code\game\dna\genes\powers.dm" #include "code\game\gamemodes\events.dm" #include "code\game\gamemodes\game_mode.dm" #include "code\game\gamemodes\game_mode_latespawn.dm" @@ -3139,8 +3138,10 @@ #include "code\modules\mob\living\carbon\human\species\station\protean_vr\protean_species.dm" #include "code\modules\mob\living\carbon\human\species\station\traits_vr\_traits.dm" #include "code\modules\mob\living\carbon\human\species\station\traits_vr\negative.dm" +#include "code\modules\mob\living\carbon\human\species\station\traits_vr\negative_genes.dm" #include "code\modules\mob\living\carbon\human\species\station\traits_vr\neutral.dm" #include "code\modules\mob\living\carbon\human\species\station\traits_vr\positive.dm" +#include "code\modules\mob\living\carbon\human\species\station\traits_vr\positive_genes.dm" #include "code\modules\mob\living\carbon\human\species\station\traits_vr\trait.dm" #include "code\modules\mob\living\carbon\human\species\station\traits_vr\traits_tutorial.dm" #include "code\modules\mob\living\carbon\human\species\station\traits_vr\weaver_objs.dm" @@ -4419,6 +4420,7 @@ #include "code\modules\xgm\xgm_gas_mixture.dm" #include "code\unit_tests\cosmetic_tests.dm" #include "code\unit_tests\decl_tests.dm" +#include "code\unit_tests\genetics_tests.dm" #include "code\unit_tests\language_tests.dm" #include "code\unit_tests\loadout_tests.dm" #include "code\unit_tests\map_tests.dm"