Willburd / CHOMPost21

An update of Space Station 13 "Outpost 21" using code derived from CHOMPStation which is derived from YawnWider, which is derived from VOREStation, which is derived from Polaris
Other
2 stars 2 forks source link

Genetics rewrite #160

Closed Willburd closed 1 month ago

Willburd commented 2 months ago

Major refactor of genetics.

-Genes have been replaced by TraitGenes Genes suffered from a constant lack of maintenance due to difficulty adding new ones, and more complex features being desired. They were later replaced by a trait system. This rework finally throws genes and the GENEBLOCK defines out, and replaces them with a fully dynamic system that requires no code changes to genes to add a new genetic trait.

This is achieved by setting the is_genetrait variable in a /datum/trait to true. By default it will have DNA_DEFAULT_BOUNDS and not be hidden. If your trait only edits vars, and does not do anything special such as giving verbs... This is all you will need to do. The genetraits even handle conflict resolution for you ingame, in the exact same way that traits decide if they conflict with another trait.

Traits with complex functions need both an apply() and unapply() proc that calls its parent. unapply() is called when a gene is disabled, and allows you to reset various things, such as removing a verb.(be sure it's not an innate one to a species! I've already given some traits code for this to show how to do it properly)

-TraitGenes handle conflict resolution for you Unlike DNA2 genes, traits have exclusions and cannot be stacked with other traits that modify the same vars. Due to this, genes have been altered to have a cached blacklist of traits that prevent their activation. This is based on reading the trait's data directly, and performing the same logic as the trait menu does.

During startup, all genetraits are compared for conflicts, and cache them. This is just prepwork however, as I do not want to fully cache a 250+ list of trait conflicts per gene. Instead, during runtime, any trait that tries to activate, will check the traits currently active, and if it does not know the trait yet, it will then check it and add it to the conflict cache. Minimizing the list size to only traits that have been encountered so far.

Due to this system, you do not need to add any special logic to handle genetraits and what ones should be excluded from them. As well, the admin player panel has been upgrade to show genes that have enough genetic potential to activate, but are currently suppressed due to a trait conflict (show in yellow). As well, traits that are NOT genetic still apply for conflict resolution. Meaning your character with a major conductive weakness, will override a non-conductive trait if they get it through mutation. Genes that are not genetic always take priority, and cannot be disabled by genetics.

The logic for gene activation and deactivation has been altered to accomplish this behavior in a sane way. Traits that are already active and checked if they should disable first during mutation checks, and then disabled genes check if they should activate. This is to prevent situations where a gene that will be turned off, comes after one that has enough potential to turn on... Causing the gene to stay suppressed until the next mutation check. Instead it will be handled in a sane way, if the gene should turn on, because its inhibitor turned off, the gene will turn on.

-TraitGenes handle randomized mutation for you Originally randmutb() and randmutg() applied a set list of genes from defined GENEBLOCKs. These GENEBLOCKs are now gone entirely, and all genes pick a random block index on creation. Because of this, during setup genes will be sorted into positive, negative, and neutral lists. As well, all genes are also stored in a primary list. All of these are now global, and can be checked with globalVV now.

randmutb() and randmutg() now read from the GLOB.dna_genes_good, GLOB.dna_genes_neutral, and GLOB.dna_genes_bad for a random gene to apply a mutation from. Traitgenes are sorted into each list by the path of the trait they are based off of. EX: a /datum/trait/positive/ will be a potential positive mutation. Automatically with no need to write code to do so. Trait conflicts are handled as explained in the above paragraphs, and getting a random mutation that would conflict will be suppressed as explained above.

By default, injectors no longer have specific map place-able templates per trait.. As a replacement, several dnainjector subtypes have been added. These include: -Fully random, with and without labels. (can disable and enable genes) -Good+neutral random, with and without labels. (can disable and enable genes) -Bad+neutral random, with and without labels. (can disable and enable genes) -Only good random, with and without labels. (can only enable genes) -Only bad random, with and without labels. (can only enable genes)

These injectors are intended to either stock simply containers by default, appear as loot drops, or be purchasable from vendors. As well, dnainjectors have been updated to use Initialize() instead of New(). A new subtype of set_trait has been added to dna injectors, these take a trait's path, and on initialization find the block they are meant to activate. These are primarily for loot piles and vendors like the casino reward machine.

-Traits are expressed by monkeys To help spice up genetics, and so you don't need to abuse the crew constantly. Monkeys rarely give feedback in the form of unique emotes based on the traits they have. Monkeys start with none, so all traits they have should be genetic by default. They perform these passively, and some are more dangerous then others!

Genetics gameplay loop mostly becomes: put monkey in machine, scramble it, begin observing for anything unique. Record what genes are highly active, and try to isolate them on another monkey to learn what they are. Once you know, make block injectors for changing crew's genes. Giving super powers or curing genetic disabilities.

TODO maybe: -redo genetics gameplay, but this is likely beyond this PR's scope.