mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-08-30 00:29:02 +01:00
Streamlines Life() a little (#96215)
## About The Pull Request Main changes - `handle_mutations` is gone, DNA Injectors are now managed via a status effect - `handle_diseases` is gone, disease stages are just handled via life signal - `handle_bodyparts` is gone, it was unused and in the future any implementations should use a life signal - `spec_life` is gone, the main content of it is now in `human/Life`, most children implementations now use life signal, zombie tongues now handle zombie groans - Life signal was split in two (pre and active) Other changes - DNA injector code was cleaned up considerably - HARS now alerts admins when you inject someone else with it like Monkey - `COPY_DNA_SE` is no longer mistakenly unused (meaning stuff like transformation sting no longer copies "active mutations") ## Why It's Good For The Game Across the course of a full round we spend the same amount of time doing literally nothing in life as we spend on handling human breathing. Now in the context of a full round this is 8 seconds. Which in the grand scheme of things, not a whole lot, but if we can get a tiny performance gain from... not doing literally nothing (especially when we can do these things cleaner with signals) that's a win in my book ## Changelog 🆑 Melbert refactor: Refactored dna injectors (both the ones that change appearance and activate mutations), report any oddities with them like failing to revert your appearance or mutations not applying correctly code: Ever so slightly changed how diseases tick, report any oddities code: Ever so slightly changed how some species mechanics tick, like golems and slimes, report any oddities code: The code behind printing appearance modifying dna injectors from genetics has changed, report any oddities code: Some backend transformation sting code changed slightly, report any oddities code: Zombie "idle" groaning is now tied to the tongue rather than the species itself admin: Force-injecting someone with HARS give an admin alert, the same as force-injecting someone with Monkey fix: Several methods of copying DNA (including transformation sting) mistakenly copied "active mutations", this has been fixed /🆑 --------- Co-authored-by: John Willard <53777086+JohnFulpWillard@users.noreply.github.com>
This commit is contained in:
co-authored by
John Willard
parent
7cf48344fb
commit
e68c1edad1
@@ -1303,93 +1303,19 @@
|
||||
// number later
|
||||
// params["type"] - Type of injector to create
|
||||
// Expected results:
|
||||
// "ue" - Unique Enzyme, changes name and blood type
|
||||
// "ue" - Unique Enzyme, changes name and blood type
|
||||
// "ui" - Unique Identity, changes looks
|
||||
// "uf" - Unique Features, changes mutant bodyparts and mutcolors
|
||||
// "mixed" - Combination of both ue and ui
|
||||
if("makeup_injector")
|
||||
if(!COOLDOWN_FINISHED(src, enzyme_copy_timer))
|
||||
return
|
||||
// Convert the index to a number and clamp within the array range, then
|
||||
// copy the data from the disk to that buffer
|
||||
var/buffer_index = text2num(params["index"])
|
||||
buffer_index = clamp(buffer_index, 1, NUMBER_OF_BUFFERS)
|
||||
var/list/buffer_slot = genetic_makeup_buffer[buffer_index]
|
||||
|
||||
// GUARD CHECK - This shouldn't be possible to execute this on a null
|
||||
// buffer. Unexpected resut
|
||||
if(!istype(buffer_slot))
|
||||
// Convert the index to a number and clamp within the array range, then copy the data from the disk to that buffer
|
||||
var/buffer_index = clamp(text2num(params["index"]), 1, NUMBER_OF_BUFFERS)
|
||||
if(!make_cosmetic_dna_injector(dna_injector_type_to_flag(params["type"]), genetic_makeup_buffer[buffer_index]))
|
||||
to_chat(usr, span_warning("Genetic data corrupted, unable to create injector."))
|
||||
return
|
||||
|
||||
var/type = params["type"]
|
||||
var/obj/item/dnainjector/timed/I
|
||||
|
||||
switch(type)
|
||||
if("ui")
|
||||
// GUARD CHECK - There's currently no way to save partial genetic data.
|
||||
// However, if this is the case, we can't make a complete injector and
|
||||
// this catches that edge case
|
||||
if(!buffer_slot["UI"])
|
||||
to_chat(usr,span_warning("Genetic data corrupted, unable to create injector."))
|
||||
return
|
||||
|
||||
I = new /obj/item/dnainjector/timed(loc)
|
||||
I.fields = list("UI"=buffer_slot["UI"])
|
||||
|
||||
// If there is a connected scanner, we can use its upgrades to reduce
|
||||
// the genetic damage generated by this injector
|
||||
if(scanner_operational())
|
||||
I.damage_coeff = connected_scanner.damage_coeff
|
||||
if("ue")
|
||||
// GUARD CHECK - There's currently no way to save partial genetic data.
|
||||
// However, if this is the case, we can't make a complete injector and
|
||||
// this catches that edge case
|
||||
if(!buffer_slot["name"] || !buffer_slot["UE"] || !buffer_slot["blood_type"])
|
||||
to_chat(usr,span_warning("Genetic data corrupted, unable to create injector."))
|
||||
return
|
||||
|
||||
I = new /obj/item/dnainjector/timed(loc)
|
||||
I.fields = list("name"=buffer_slot["name"], "UE"=buffer_slot["UE"], "blood_type"=buffer_slot["blood_type"])
|
||||
|
||||
// If there is a connected scanner, we can use its upgrades to reduce
|
||||
// the genetic damage generated by this injector
|
||||
if(scanner_operational())
|
||||
I.damage_coeff = connected_scanner.damage_coeff
|
||||
if("uf")
|
||||
// GUARD CHECK - There's currently no way to save partial genetic data.
|
||||
// However, if this is the case, we can't make a complete injector and
|
||||
// this catches that edge case
|
||||
if(!buffer_slot["name"] || !buffer_slot["UF"] || !buffer_slot["blood_type"])
|
||||
to_chat(usr,span_warning("Genetic data corrupted, unable to create injector."))
|
||||
return
|
||||
|
||||
I = new /obj/item/dnainjector/timed(loc)
|
||||
I.fields = list("name"=buffer_slot["name"], "UF"=buffer_slot["UF"])
|
||||
|
||||
// If there is a connected scanner, we can use its upgrades to reduce
|
||||
// the genetic damage generated by this injector
|
||||
if(scanner_operational())
|
||||
I.damage_coeff = connected_scanner.damage_coeff
|
||||
if("mixed")
|
||||
// GUARD CHECK - There's currently no way to save partial genetic data.
|
||||
// However, if this is the case, we can't make a complete injector and
|
||||
// this catches that edge case
|
||||
if(!buffer_slot["UI"] || !buffer_slot["name"] || !buffer_slot["UE"] || !buffer_slot["UF"] || !buffer_slot["blood_type"])
|
||||
to_chat(usr,span_warning("Genetic data corrupted, unable to create injector."))
|
||||
return
|
||||
|
||||
I = new /obj/item/dnainjector/timed(loc)
|
||||
I.fields = list("UI"=buffer_slot["UI"],"name"=buffer_slot["name"], "UE"=buffer_slot["UE"], "UF"=buffer_slot["UF"], "blood_type"=buffer_slot["blood_type"])
|
||||
|
||||
// If there is a connected scanner, we can use its upgrades to reduce
|
||||
// the genetic damage generated by this injector
|
||||
if(scanner_operational())
|
||||
I.damage_coeff = connected_scanner.damage_coeff
|
||||
|
||||
// If we successfully created an injector, don't forget to set the new
|
||||
// ready timer.
|
||||
if(I)
|
||||
injector_ready = world.time + MISC_INJECTOR_TIMEOUT
|
||||
injector_ready = world.time + MISC_INJECTOR_TIMEOUT
|
||||
if(connected_scanner)
|
||||
connected_scanner.use_energy(connected_scanner.active_power_usage)
|
||||
else
|
||||
@@ -1785,12 +1711,90 @@
|
||||
return TRUE
|
||||
|
||||
return FALSE
|
||||
|
||||
/**
|
||||
* Checks if there is a connected DNA Scanner that is operational
|
||||
*/
|
||||
/obj/machinery/computer/dna_console/proc/scanner_operational()
|
||||
return connected_scanner?.is_operational
|
||||
|
||||
/**
|
||||
* Gets the damage coefficient of the connected DNA Scanner, or 1 if there isn't an operational one
|
||||
*/
|
||||
/obj/machinery/computer/dna_console/proc/get_injector_damage_coeff()
|
||||
if(scanner_operational())
|
||||
return connected_scanner.damage_coeff
|
||||
return 1
|
||||
|
||||
/// Copy UI to the dna injector
|
||||
#define DNA_INJECTOR_FLAG_UI (1<<0)
|
||||
/// Copy UE to the dna injector
|
||||
#define DNA_INJECTOR_FLAG_UE (1<<1)
|
||||
/// Copy UF to the dna injector
|
||||
#define DNA_INJECTOR_FLAG_UF (1<<2)
|
||||
/// Copy name to the dna injector
|
||||
#define DNA_INJECTOR_FLAG_NAME (1<<3)
|
||||
/// Copy blood type to the dna injector
|
||||
#define DNA_INJECTOR_FLAG_BLOOD (1<<4)
|
||||
|
||||
/**
|
||||
* Converts a string (from tgui) to a series of flags determine what we should put in a DNA Injector
|
||||
*/
|
||||
/obj/machinery/computer/dna_console/proc/dna_injector_type_to_flag(injector_type)
|
||||
switch(injector_type)
|
||||
if("ui")
|
||||
return DNA_INJECTOR_FLAG_UI
|
||||
if("ue")
|
||||
return DNA_INJECTOR_FLAG_UE | DNA_INJECTOR_FLAG_NAME | DNA_INJECTOR_FLAG_BLOOD
|
||||
if("uf")
|
||||
return DNA_INJECTOR_FLAG_UF | DNA_INJECTOR_FLAG_NAME
|
||||
if("mixed")
|
||||
return ALL
|
||||
return NONE
|
||||
|
||||
/**
|
||||
* Pass an injector flag and a genetic makeup buffer slot to create a DNA Injector
|
||||
*/
|
||||
/obj/machinery/computer/dna_console/proc/make_cosmetic_dna_injector(dna_flag, list/buffer_slot = list())
|
||||
if(!dna_flag || !length(buffer_slot))
|
||||
return FALSE
|
||||
|
||||
var/datum/dna/stored_dna = new()
|
||||
|
||||
if(dna_flag & DNA_INJECTOR_FLAG_NAME)
|
||||
if(!buffer_slot["name"])
|
||||
return FALSE
|
||||
stored_dna.real_name = buffer_slot["name"]
|
||||
|
||||
if(dna_flag & DNA_INJECTOR_FLAG_BLOOD)
|
||||
if(!buffer_slot["blood_type"])
|
||||
return FALSE
|
||||
stored_dna.blood_type = buffer_slot["blood_type"]
|
||||
|
||||
if(dna_flag & DNA_INJECTOR_FLAG_UI)
|
||||
if(!buffer_slot["UI"])
|
||||
return FALSE
|
||||
stored_dna.unique_identity = buffer_slot["UI"]
|
||||
|
||||
if(dna_flag & DNA_INJECTOR_FLAG_UE)
|
||||
if(!buffer_slot["UE"])
|
||||
return FALSE
|
||||
stored_dna.unique_enzymes = buffer_slot["UE"]
|
||||
|
||||
if(dna_flag & DNA_INJECTOR_FLAG_UF)
|
||||
if(!buffer_slot["UF"])
|
||||
return FALSE
|
||||
stored_dna.unique_features = buffer_slot["UF"]
|
||||
|
||||
new /obj/item/dnainjector/timed(loc, stored_dna, get_injector_damage_coeff())
|
||||
return TRUE
|
||||
|
||||
#undef DNA_INJECTOR_FLAG_UI
|
||||
#undef DNA_INJECTOR_FLAG_UE
|
||||
#undef DNA_INJECTOR_FLAG_UF
|
||||
#undef DNA_INJECTOR_FLAG_NAME
|
||||
#undef DNA_INJECTOR_FLAG_BLOOD
|
||||
|
||||
/**
|
||||
* Checks if there is a valid DNA Scanner occupant for genetic modification
|
||||
*
|
||||
|
||||
@@ -163,7 +163,7 @@ GLOBAL_VAR_INIT(experimental_cloner_fuckup_chance, 50)
|
||||
|
||||
if (prob(75))
|
||||
var/static/list/permitted_heights = list(HUMAN_HEIGHT_SHORTEST, HUMAN_HEIGHT_SHORT, HUMAN_HEIGHT_MEDIUM, HUMAN_HEIGHT_TALL, HUMAN_HEIGHT_TALLER, HUMAN_HEIGHT_TALLEST)
|
||||
new_clone.dna.remove_mutation(/datum/mutation/dwarfism, list(MUTATION_SOURCE_ACTIVATED, MUTATION_SOURCE_MUTATOR))
|
||||
new_clone.dna.remove_mutation(/datum/mutation/dwarfism, GLOB.standard_mutation_sources)
|
||||
new_clone.set_mob_height(pick(permitted_heights - loaded_record.height)) // To differentiate the clones
|
||||
|
||||
return new_clone
|
||||
|
||||
@@ -11,18 +11,34 @@
|
||||
throw_range = 5
|
||||
w_class = WEIGHT_CLASS_TINY
|
||||
|
||||
/// Modified to damage caused from injection, currently unused though
|
||||
var/damage_coeff = 1
|
||||
var/list/fields
|
||||
var/list/add_mutations = list()
|
||||
var/list/remove_mutations = list()
|
||||
/// Adds all mutations in this list to the injected mob, activating it rather than mutating it if possible.
|
||||
var/list/add_mutations
|
||||
/// If the injected mob has any mutations in this list activated or mutated, they will be removed.
|
||||
var/list/remove_mutations
|
||||
/// Tracks if it's been used
|
||||
VAR_FINAL/used = FALSE
|
||||
/// Duration of the mutations added/activated or DNA changes. Does not affect removed mutations.
|
||||
var/duration = INFINITY
|
||||
/// A DNA datum that has its fields copied to the target on injection.
|
||||
var/datum/dna/stored_dna
|
||||
|
||||
var/used = FALSE
|
||||
|
||||
/obj/item/dnainjector/Initialize(mapload)
|
||||
/obj/item/dnainjector/Initialize(mapload, datum/dna/stored_dna, damage_coeff = 1)
|
||||
. = ..()
|
||||
AddElement(/datum/element/update_icon_updates_onmob)
|
||||
if(used)
|
||||
update_appearance()
|
||||
src.stored_dna = stored_dna
|
||||
src.damage_coeff = damage_coeff
|
||||
|
||||
/obj/item/dnainjector/Destroy()
|
||||
if(stored_dna?.holder)
|
||||
stack_trace("DNA injector got owned DNA somehow")
|
||||
stored_dna = null
|
||||
else
|
||||
QDEL_NULL(stored_dna)
|
||||
return ..()
|
||||
|
||||
/obj/item/dnainjector/vv_edit_var(vname, vval)
|
||||
. = ..()
|
||||
@@ -43,27 +59,26 @@
|
||||
/obj/item/dnainjector/proc/inject(mob/living/carbon/target, mob/user)
|
||||
if(!target.can_mutate())
|
||||
return FALSE
|
||||
if(target.stat == DEAD) //prevents dead people from having their DNA changed
|
||||
to_chat(user, span_notice("You can't modify [target]'s DNA while [target.p_theyre()] dead."))
|
||||
return FALSE
|
||||
for(var/removed_mutation in remove_mutations)
|
||||
target.dna.remove_mutation(removed_mutation, list(MUTATION_SOURCE_ACTIVATED, MUTATION_SOURCE_MUTATOR))
|
||||
for(var/added_mutation in add_mutations)
|
||||
if(added_mutation == /datum/mutation/race)
|
||||
message_admins("[ADMIN_LOOKUPFLW(user)] injected [key_name_admin(target)] with \the [src] [span_danger("(MONKEY)")]")
|
||||
target.dna.remove_mutation(removed_mutation, GLOB.standard_mutation_sources)
|
||||
|
||||
for(var/datum/mutation/added_mutation as anything in add_mutations)
|
||||
if(added_mutation::warn_admins_on_inject && target != user && !ismonkey(target))
|
||||
message_admins("[ADMIN_LOOKUPFLW(user)] injected [key_name_admin(target)] with [src] containing [added_mutation::name].")
|
||||
if(target.dna.mutation_in_sequence(added_mutation))
|
||||
target.dna.activate_mutation(added_mutation)
|
||||
if(duration != INFINITY)
|
||||
addtimer(CALLBACK(target.dna, TYPE_PROC_REF(/datum/dna, remove_mutation), added_mutation, MUTATION_SOURCE_ACTIVATED), duration)
|
||||
else
|
||||
target.dna.add_mutation(added_mutation, MUTATION_SOURCE_MUTATOR)
|
||||
if(fields)
|
||||
if(fields["name"] && fields["UE"] && fields["blood_type"])
|
||||
target.real_name = fields["name"]
|
||||
target.dna.unique_enzymes = fields["UE"]
|
||||
target.name = target.real_name
|
||||
target.set_blood_type(fields["blood_type"])
|
||||
if(fields["UI"]) //UI+UE
|
||||
target.dna.unique_identity = merge_text(target.dna.unique_identity, fields["UI"])
|
||||
if(fields["UF"])
|
||||
target.dna.unique_features = merge_text(target.dna.unique_features, fields["UF"])
|
||||
if(fields["UI"] || fields["UF"])
|
||||
target.updateappearance(mutcolor_update = TRUE, mutations_overlay_update = TRUE)
|
||||
if(duration != INFINITY)
|
||||
addtimer(CALLBACK(target.dna, TYPE_PROC_REF(/datum/dna, remove_mutation), added_mutation, MUTATION_SOURCE_MUTATOR), duration)
|
||||
|
||||
if(stored_dna)
|
||||
target.apply_status_effect(/datum/status_effect/temporary_transformation/dna_injector, duration, stored_dna)
|
||||
return TRUE
|
||||
|
||||
/obj/item/dnainjector/attack(mob/target, mob/user)
|
||||
@@ -100,53 +115,7 @@
|
||||
update_appearance()
|
||||
|
||||
/obj/item/dnainjector/timed
|
||||
var/duration = 60 SECONDS
|
||||
|
||||
/obj/item/dnainjector/timed/inject(mob/living/carbon/target, mob/user)
|
||||
if(target.stat == DEAD) //prevents dead people from having their DNA changed
|
||||
to_chat(user, span_notice("You can't modify [target]'s DNA while [target.p_theyre()] dead."))
|
||||
return FALSE
|
||||
if(!target.can_mutate())
|
||||
return FALSE
|
||||
var/endtime = world.time + duration
|
||||
for(var/mutation in remove_mutations)
|
||||
target.dna.remove_mutation(mutation, list(MUTATION_SOURCE_ACTIVATED, MUTATION_SOURCE_MUTATOR))
|
||||
for(var/mutation in add_mutations)
|
||||
if(target.dna.get_mutation(mutation))
|
||||
continue //Skip permanent mutations we already have.
|
||||
if(mutation == /datum/mutation/race && !ismonkey(target))
|
||||
message_admins("[ADMIN_LOOKUPFLW(user)] injected [key_name_admin(target)] with \the [src] [span_danger("(MONKEY)")]")
|
||||
target.dna.add_mutation(mutation, MUTATION_SOURCE_TIMED_INJECTOR)
|
||||
addtimer(CALLBACK(target.dna, TYPE_PROC_REF(/datum/dna, remove_mutation), mutation, MUTATION_SOURCE_TIMED_INJECTOR), duration)
|
||||
if(fields)
|
||||
if(fields["name"] && fields["UE"] && fields["blood_type"])
|
||||
LAZYINITLIST(target.dna.previous)
|
||||
if(!target.dna.previous["name"])
|
||||
target.dna.previous["name"] = target.real_name
|
||||
if(!target.dna.previous["UE"])
|
||||
target.dna.previous["UE"] = target.dna.unique_enzymes
|
||||
if(!target.dna.previous["blood_type"])
|
||||
target.dna.previous["blood_type"] = target.get_bloodtype()
|
||||
target.real_name = fields["name"]
|
||||
target.dna.unique_enzymes = fields["UE"]
|
||||
target.name = target.real_name
|
||||
target.set_blood_type(fields["blood_type"])
|
||||
LAZYSET(target.dna.temporary_mutations, UE_CHANGED, endtime)
|
||||
if(fields["UI"]) //UI+UE
|
||||
LAZYINITLIST(target.dna.previous)
|
||||
if(!target.dna.previous["UI"])
|
||||
target.dna.previous["UI"] = target.dna.unique_identity
|
||||
target.dna.unique_identity = merge_text(target.dna.unique_identity, fields["UI"])
|
||||
LAZYSET(target.dna.temporary_mutations, UI_CHANGED, endtime)
|
||||
if(fields["UF"]) //UI+UE
|
||||
LAZYINITLIST(target.dna.previous)
|
||||
if(!target.dna.previous["UF"])
|
||||
target.dna.previous["UF"] = target.dna.unique_features
|
||||
target.dna.unique_features = merge_text(target.dna.unique_features, fields["UF"])
|
||||
LAZYSET(target.dna.temporary_mutations, UF_CHANGED, endtime)
|
||||
if(fields["UI"] || fields["UF"])
|
||||
target.updateappearance(mutcolor_update = TRUE, mutations_overlay_update = TRUE)
|
||||
return TRUE
|
||||
duration = 60 SECONDS
|
||||
|
||||
/obj/item/dnainjector/timed/hulk
|
||||
name = "\improper DNA injector (Hulk)"
|
||||
|
||||
@@ -263,12 +263,10 @@
|
||||
/mob/living/basic/soulscythe/Initialize(mapload)
|
||||
. = ..()
|
||||
add_traits(list(TRAIT_ASHSTORM_IMMUNE, TRAIT_SNOWSTORM_IMMUNE, TRAIT_LAVA_IMMUNE), INNATE_TRAIT)
|
||||
RegisterSignal(src, COMSIG_LIVING_LIFE, PROC_REF(on_life))
|
||||
|
||||
/mob/living/basic/soulscythe/proc/on_life(datum/source, seconds_per_tick) // done like this because there's no need to go through all of life since the item does the work anyways
|
||||
/mob/living/basic/soulscythe/Life(seconds_per_tick)
|
||||
if(stat == CONSCIOUS)
|
||||
adjust_blood_volume(round(1 * seconds_per_tick), maximum = MAX_BLOOD_LEVEL)
|
||||
return COMPONENT_LIVING_CANCEL_LIFE_PROCESSING
|
||||
|
||||
/// Special projectile for the soulscythe.
|
||||
/obj/projectile/soulscythe
|
||||
|
||||
Reference in New Issue
Block a user