diff --git a/code/datums/repositories/decls.dm b/code/datums/repositories/decls.dm
new file mode 100644
index 0000000000..e87be74f53
--- /dev/null
+++ b/code/datums/repositories/decls.dm
@@ -0,0 +1,39 @@
+/var/repository/decls/decls_repository = new()
+
+/repository/decls
+ var/list/fetched_decls
+ var/list/fetched_decl_types
+ var/list/fetched_decl_subtypes
+
+/repository/decls/New()
+ ..()
+ fetched_decls = list()
+ fetched_decl_types = list()
+ fetched_decl_subtypes = list()
+
+/repository/decls/proc/decls_of_type(var/decl_prototype)
+ . = fetched_decl_types[decl_prototype]
+ if(!.)
+ . = get_decls(typesof(decl_prototype))
+ fetched_decl_types[decl_prototype] = .
+
+/repository/decls/proc/decls_of_subtype(var/decl_prototype)
+ . = fetched_decl_subtypes[decl_prototype]
+ if(!.)
+ . = get_decls(subtypesof(decl_prototype))
+ fetched_decl_subtypes[decl_prototype] = .
+
+/repository/decls/proc/get_decl(var/decl_type)
+ . = fetched_decls[decl_type]
+ if(!.)
+ . = new decl_type()
+ fetched_decls[decl_type] = .
+
+/repository/decls/proc/get_decls(var/list/decl_types)
+ . = list()
+ for(var/decl_type in decl_types)
+ .[decl_type] = get_decl(decl_type)
+
+/decls/Destroy()
+ crash_with("Prevented attempt to delete a decl instance: [log_info_line(src)]")
+ return 1 // Prevents Decl destruction
\ No newline at end of file
diff --git a/code/datums/repositories/repository.dm b/code/datums/repositories/repository.dm
index 6267099c93..04eee50540 100644
--- a/code/datums/repositories/repository.dm
+++ b/code/datums/repositories/repository.dm
@@ -1,4 +1,19 @@
+/repository/New()
+ return
+
/datum/cache_entry
var/timestamp
var/data
+/datum/cache_entry/New()
+ timestamp = world.time
+
+/datum/cache_entry/proc/is_valid()
+ return FALSE
+
+/datum/cache_entry/valid_until/New(var/valid_duration)
+ ..()
+ timestamp += valid_duration
+
+/datum/cache_entry/valid_until/is_valid()
+ return world.time < timestamp
\ No newline at end of file
diff --git a/code/modules/hydroponics/seed_controller.dm b/code/modules/hydroponics/seed_controller.dm
index 89cb299f5f..fc2b6ea018 100644
--- a/code/modules/hydroponics/seed_controller.dm
+++ b/code/modules/hydroponics/seed_controller.dm
@@ -13,11 +13,11 @@
if(!holder) return
if(!plant_controller || !plant_controller.gene_tag_masks)
- usr << "Gene masks not set."
+ to_chat(usr, "Gene masks not set.")
return
for(var/mask in plant_controller.gene_tag_masks)
- usr << "[mask]: [plant_controller.gene_tag_masks[mask]]"
+ to_chat(usr, "[mask]: [plant_controller.gene_tag_masks[mask]]")
var/global/datum/controller/plants/plant_controller // Set in New().
@@ -33,6 +33,8 @@ var/global/datum/controller/plants/plant_controller // Set in New().
var/list/plant_sprites = list() // List of all harvested product sprites.
var/list/plant_product_sprites = list() // List of all growth sprites plus number of growth stages.
var/processing = 0 // Off/on.
+ var/list/gene_masked_list = list() // Stored gene masked list, rather than recreating it when needed.
+ var/list/plant_gene_datums = list() // Stored datum versions of the gene masked list.
/datum/controller/plants/New()
if(plant_controller && plant_controller != src)
@@ -83,18 +85,29 @@ var/global/datum/controller/plants/plant_controller // Set in New().
S.update_seed()
//Might as well mask the gene types while we're at it.
+ var/list/gene_datums = decls_repository.decls_of_subtype(/decl/plantgene)
var/list/used_masks = list()
var/list/plant_traits = ALL_GENES
while(plant_traits && plant_traits.len)
+ world.log << "2"
var/gene_tag = pick(plant_traits)
var/gene_mask = "[uppertext(num2hex(rand(0,255)))]"
while(gene_mask in used_masks)
gene_mask = "[uppertext(num2hex(rand(0,255)))]"
+ var/decl/plantgene/G
+
+ for(var/D in gene_datums)
+ var/decl/plantgene/P = gene_datums[D]
+ if(gene_tag == P.gene_tag)
+ G = P
+ gene_datums -= D
used_masks += gene_mask
plant_traits -= gene_tag
gene_tag_masks[gene_tag] = gene_mask
+ plant_gene_datums[gene_mask] = G
+ gene_masked_list.Add(list(list("tag" = gene_tag, "mask" = gene_mask)))
// Proc for creating a random seed type.
/datum/controller/plants/proc/create_random_seed(var/survive_on_station)
@@ -147,4 +160,4 @@ var/global/datum/controller/plants/plant_controller // Set in New().
plant_queue |= plant
/datum/controller/plants/proc/remove_plant(var/obj/effect/plant/plant)
- plant_queue -= plant
+ plant_queue -= plant
\ No newline at end of file
diff --git a/code/modules/hydroponics/seed_gene_mut.dm b/code/modules/hydroponics/seed_gene_mut.dm
new file mode 100644
index 0000000000..cdb7048957
--- /dev/null
+++ b/code/modules/hydroponics/seed_gene_mut.dm
@@ -0,0 +1,135 @@
+/datum/seed/proc/diverge_mutate_gene(var/decl/plantgene/G, var/turf/T)
+ if(!istype(G))
+ log_debug("Attempted to mutate [src] with a non-plantgene var.")
+ return src
+
+ var/datum/seed/S = diverge() //Let's not modify all of the seeds.
+ T.visible_message("\The [S.display_name] quivers!") //Mimicks the normal mutation.
+ G.mutate(S, T)
+
+ return S
+
+/decl/plantgene
+ var/gene_tag
+
+/decl/plantgene/biochem
+ gene_tag = GENE_BIOCHEMISTRY
+
+/decl/plantgene/hardiness
+ gene_tag = GENE_HARDINESS
+
+/decl/plantgene/environment
+ gene_tag = GENE_ENVIRONMENT
+
+/decl/plantgene/metabolism
+ gene_tag = GENE_METABOLISM
+
+/decl/plantgene/structure
+ gene_tag = GENE_STRUCTURE
+
+/decl/plantgene/diet
+ gene_tag = GENE_DIET
+
+/decl/plantgene/pigment
+ gene_tag = GENE_PIGMENT
+
+/decl/plantgene/output
+ gene_tag = GENE_OUTPUT
+
+/decl/plantgene/atmosphere
+ gene_tag = GENE_ATMOSPHERE
+
+/decl/plantgene/vigour
+ gene_tag = GENE_VIGOUR
+
+/decl/plantgene/fruit
+ gene_tag = GENE_FRUIT
+
+/decl/plantgene/special
+ gene_tag = GENE_SPECIAL
+
+/decl/plantgene/proc/mutate(var/datum/seed/S)
+ return
+
+/decl/plantgene/biochem/mutate(var/datum/seed/S)
+ S.set_trait(TRAIT_POTENCY, S.get_trait(TRAIT_POTENCY)+rand(-20,20),200, 0)
+
+/decl/plantgene/hardiness/mutate(var/datum/seed/S)
+ if(prob(60))
+ S.set_trait(TRAIT_TOXINS_TOLERANCE, S.get_trait(TRAIT_TOXINS_TOLERANCE)+rand(-2,2),10,0)
+ if(prob(60))
+ S.set_trait(TRAIT_PEST_TOLERANCE, S.get_trait(TRAIT_PEST_TOLERANCE)+rand(-2,2),10,0)
+ if(prob(60))
+ S.set_trait(TRAIT_WEED_TOLERANCE, S.get_trait(TRAIT_WEED_TOLERANCE)+rand(-2,2),10,0)
+ if(prob(60))
+ S.set_trait(TRAIT_ENDURANCE, S.get_trait(TRAIT_ENDURANCE)+rand(-5,5),100,0)
+
+/decl/plantgene/environment/mutate(var/datum/seed/S)
+ if(prob(60))
+ S.set_trait(TRAIT_IDEAL_HEAT, S.get_trait(TRAIT_IDEAL_HEAT)+rand(-2,2),10,0)
+ if(prob(60))
+ S.set_trait(TRAIT_IDEAL_LIGHT, S.get_trait(TRAIT_IDEAL_LIGHT)+rand(-2,2),10,0)
+ if(prob(60))
+ S.set_trait(TRAIT_LIGHT_TOLERANCE, S.get_trait(TRAIT_LIGHT_TOLERANCE)+rand(-5,5),100,0)
+
+/decl/plantgene/metabolism/mutate(var/datum/seed/S)
+ if(prob(65))
+ S.set_trait(TRAIT_REQUIRES_NUTRIENTS, S.get_trait(TRAIT_REQUIRES_NUTRIENTS)+rand(-2,2),10,0)
+ if(prob(65))
+ S.set_trait(TRAIT_REQUIRES_WATER, S.get_trait(TRAIT_REQUIRES_WATER)+rand(-2,2),10,0)
+ if(prob(40))
+ S.set_trait(TRAIT_ALTER_TEMP, S.get_trait(TRAIT_ALTER_TEMP)+rand(-5,5),100,0)
+
+/decl/plantgene/diet/mutate(var/datum/seed/S)
+ if(prob(60))
+ S.set_trait(TRAIT_CARNIVOROUS, S.get_trait(TRAIT_CARNIVOROUS)+rand(-1,1),2,0)
+ if(prob(60))
+ S.set_trait(TRAIT_PARASITE, !S.get_trait(TRAIT_PARASITE))
+ if(prob(65))
+ S.set_trait(TRAIT_NUTRIENT_CONSUMPTION, S.get_trait(TRAIT_NUTRIENT_CONSUMPTION)+rand(-0.1,0.1),5,0)
+ if(prob(65))
+ S.set_trait(TRAIT_WATER_CONSUMPTION, S.get_trait(TRAIT_WATER_CONSUMPTION)+rand(-1,1),50,0)
+
+/decl/plantgene/output/mutate(var/datum/seed/S, var/turf/T)
+ if(prob(50))
+ S.set_trait(TRAIT_BIOLUM, !S.get_trait(TRAIT_BIOLUM))
+ if(S.get_trait(TRAIT_BIOLUM))
+ T.visible_message("\The [S.display_name] begins to glow!")
+ if(prob(50))
+ S.set_trait(TRAIT_BIOLUM_COLOUR,get_random_colour(0,75,190))
+ T.visible_message("\The [S.display_name]'s glow changes colour!")
+ else
+ T.visible_message("\The [S.display_name]'s glow dims...")
+ if(prob(60))
+ S.set_trait(TRAIT_PRODUCES_POWER, !S.get_trait(TRAIT_PRODUCES_POWER))
+
+/decl/plantgene/atmosphere/mutate(var/datum/seed/S)
+ if(prob(60))
+ S.set_trait(TRAIT_TOXINS_TOLERANCE, S.get_trait(TRAIT_TOXINS_TOLERANCE)+rand(-2,2),10,0)
+ if(prob(60))
+ S.set_trait(TRAIT_PEST_TOLERANCE, S.get_trait(TRAIT_PEST_TOLERANCE)+rand(-2,2),10,0)
+ if(prob(60))
+ S.set_trait(TRAIT_WEED_TOLERANCE, S.get_trait(TRAIT_WEED_TOLERANCE)+rand(-2,2),10,0)
+ if(prob(60))
+ S.set_trait(TRAIT_ENDURANCE, S.get_trait(TRAIT_ENDURANCE)+rand(-5,5),100,0)
+
+/decl/plantgene/vigour/mutate(var/datum/seed/S, var/turf/T)
+ if(prob(65))
+ S.set_trait(TRAIT_PRODUCTION, S.get_trait(TRAIT_PRODUCTION)+rand(-1,1),10,0)
+ if(prob(65))
+ S.set_trait(TRAIT_MATURATION, S.get_trait(TRAIT_MATURATION)+rand(-1,1),30,0)
+ if(prob(55))
+ S.set_trait(TRAIT_SPREAD, S.get_trait(TRAIT_SPREAD)+rand(-1,1),2,0)
+ T.visible_message("\The [S.display_name] spasms visibly, shifting in the tray.")
+
+/decl/plantgene/fruit/mutate(var/datum/seed/S)
+ if(prob(65))
+ S.set_trait(TRAIT_STINGS, !S.get_trait(TRAIT_STINGS))
+ if(prob(65))
+ S.set_trait(TRAIT_EXPLOSIVE, !S.get_trait(TRAIT_EXPLOSIVE))
+ if(prob(65))
+ S.set_trait(TRAIT_JUICY, !S.get_trait(TRAIT_JUICY))
+
+/decl/plantgene/special/mutate(var/datum/seed/S)
+ if(prob(65))
+ S.set_trait(TRAIT_TELEPORTING, !S.get_trait(TRAIT_TELEPORTING))
diff --git a/code/modules/hydroponics/seed_machines.dm b/code/modules/hydroponics/seed_machines.dm
index 1479ffdc5c..bc96f064c9 100644
--- a/code/modules/hydroponics/seed_machines.dm
+++ b/code/modules/hydroponics/seed_machines.dm
@@ -137,9 +137,7 @@
var/list/data = list()
- var/list/geneMasks[0]
- for(var/gene_tag in plant_controller.gene_tag_masks)
- geneMasks.Add(list(list("tag" = gene_tag, "mask" = plant_controller.gene_tag_masks[gene_tag])))
+ var/list/geneMasks = plant_controller.gene_masked_list
data["geneMasks"] = geneMasks
data["activity"] = active
diff --git a/code/modules/hydroponics/trays/tray.dm b/code/modules/hydroponics/trays/tray.dm
index efdae46db7..e69c3eec8a 100644
--- a/code/modules/hydroponics/trays/tray.dm
+++ b/code/modules/hydroponics/trays/tray.dm
@@ -181,9 +181,14 @@
return
//Override for somatoray projectiles.
- if(istype(Proj ,/obj/item/projectile/energy/floramut) && prob(20))
- mutate(1)
- return
+ if(istype(Proj ,/obj/item/projectile/energy/floramut)&& prob(20))
+ if(istype(Proj, /obj/item/projectile/energy/floramut/gene))
+ var/obj/item/projectile/energy/floramut/gene/G = Proj
+ if(seed)
+ seed = seed.diverge_mutate_gene(G.gene, get_turf(loc)) //get_turf just in case it's not in a turf.
+ else
+ mutate(1)
+ return
else if(istype(Proj ,/obj/item/projectile/energy/florayield) && prob(20))
yield_mod = min(10,yield_mod+rand(1,2))
return
diff --git a/code/modules/projectiles/guns/energy/special.dm b/code/modules/projectiles/guns/energy/special.dm
index 8fbfc0e657..46988c9934 100644
--- a/code/modules/projectiles/guns/energy/special.dm
+++ b/code/modules/projectiles/guns/energy/special.dm
@@ -44,10 +44,12 @@
origin_tech = list(TECH_MATERIAL = 2, TECH_BIO = 3, TECH_POWER = 3)
modifystate = "floramut"
self_recharge = 1
+ var/decl/plantgene/gene = null
firemodes = list(
list(mode_name="induce mutations", projectile_type=/obj/item/projectile/energy/floramut, modifystate="floramut"),
list(mode_name="increase yield", projectile_type=/obj/item/projectile/energy/florayield, modifystate="florayield"),
+ list(mode_name="induce specific mutations", projectile_type=/obj/item/projectile/energy/floramut/gene, modifystate="floramut"),
)
/obj/item/weapon/gun/energy/floragun/afterattack(obj/target, mob/user, adjacent_flag)
@@ -58,6 +60,28 @@
return
..()
+/obj/item/weapon/gun/energy/floragun/verb/select_gene()
+ set name = "Select Gene"
+ set category = "Object"
+ set src in view(1)
+
+ var/genemask = input("Choose a gene to modify.") as null|anything in plant_controller.plant_gene_datums
+
+ if(!genemask)
+ return
+
+ gene = plant_controller.plant_gene_datums[genemask]
+
+ to_chat(usr, "You set the [src]'s targeted genetic area to [genemask].")
+
+ return
+
+/obj/item/weapon/gun/energy/floragun/consume_next_projectile()
+ . = ..()
+ var/obj/item/projectile/energy/floramut/gene/G = .
+ if(istype(G))
+ G.gene = gene
+
/obj/item/weapon/gun/energy/meteorgun
name = "meteor gun"
desc = "For the love of god, make sure you're aiming this the right way!"
diff --git a/code/modules/projectiles/projectile/special.dm b/code/modules/projectiles/projectile/special.dm
index dd81d713fe..c8c26f5f39 100644
--- a/code/modules/projectiles/projectile/special.dm
+++ b/code/modules/projectiles/projectile/special.dm
@@ -121,6 +121,15 @@
else
return 1
+/obj/item/projectile/energy/floramut/gene
+ name = "gamma somatoray"
+ icon_state = "energy2"
+ damage = 0
+ damage_type = TOX
+ nodamage = 1
+ check_armour = "energy"
+ var/decl/plantgene/gene = null
+
/obj/item/projectile/energy/florayield
name = "beta somatoray"
icon_state = "energy2"
diff --git a/polaris.dme b/polaris.dme
index 23fc2fc0c4..e758a5eb7f 100644
--- a/polaris.dme
+++ b/polaris.dme
@@ -212,6 +212,7 @@
#include "code\datums\observation\~cleanup.dm"
#include "code\datums\repositories\cameras.dm"
#include "code\datums\repositories\crew.dm"
+#include "code\datums\repositories\decls.dm"
#include "code\datums\repositories\repository.dm"
#include "code\datums\supplypacks\atmospherics.dm"
#include "code\datums\supplypacks\contraband.dm"
@@ -1412,6 +1413,7 @@
#include "code\modules\hydroponics\seed.dm"
#include "code\modules\hydroponics\seed_controller.dm"
#include "code\modules\hydroponics\seed_datums.dm"
+#include "code\modules\hydroponics\seed_gene_mut.dm"
#include "code\modules\hydroponics\seed_machines.dm"
#include "code\modules\hydroponics\seed_mobs.dm"
#include "code\modules\hydroponics\seed_packets.dm"