diff --git a/code/game/machinery/cloning.dm b/code/game/machinery/cloning.dm index 85b3363e073..a6d8287c1db 100644 --- a/code/game/machinery/cloning.dm +++ b/code/game/machinery/cloning.dm @@ -353,13 +353,8 @@ /obj/item/weapon/storage/box/disks/New() ..() - new /obj/item/weapon/disk/data(src) - new /obj/item/weapon/disk/data(src) - new /obj/item/weapon/disk/data(src) - new /obj/item/weapon/disk/data(src) - new /obj/item/weapon/disk/data(src) - new /obj/item/weapon/disk/data(src) - new /obj/item/weapon/disk/data(src) + for(var/i in 1 to 7) + new /obj/item/weapon/disk/data(src) /* * Manual -- A big ol' manual. diff --git a/code/game/machinery/constructable_frame.dm b/code/game/machinery/constructable_frame.dm index 79f88ef5797..b0775204b67 100644 --- a/code/game/machinery/constructable_frame.dm +++ b/code/game/machinery/constructable_frame.dm @@ -791,3 +791,14 @@ obj/item/weapon/circuitboard/rdserver req_components = list( /obj/item/weapon/stock_parts/console_screen = 1, /obj/item/weapon/stock_parts/matter_bin = 3) + +/obj/item/weapon/circuitboard/plantgenes + name = "circuit board (Plant DNA Manipulator)" + build_path = /obj/machinery/plantgenes + board_type = "machine" + origin_tech = "programming=2;biotech=3" + req_components = list( + /obj/item/weapon/stock_parts/manipulator = 1, + /obj/item/weapon/stock_parts/micro_laser = 2, + /obj/item/weapon/stock_parts/console_screen = 1, + /obj/item/weapon/stock_parts/scanning_module = 1,) \ No newline at end of file diff --git a/code/modules/hydroponics/gene_modder.dm b/code/modules/hydroponics/gene_modder.dm new file mode 100644 index 00000000000..37695f75e65 --- /dev/null +++ b/code/modules/hydroponics/gene_modder.dm @@ -0,0 +1,387 @@ +/obj/machinery/plantgenes + name = "plant DNA manipulator" + desc = "An advanced device designed to manipulate plant genetic makeup." + icon = 'icons/obj/hydroponics/equipment.dmi' + icon_state = "dnamod" + density = 1 + anchored = 1 + + var/obj/item/seeds/seed + var/obj/item/weapon/disk/plantgene/disk + + var/list/core_genes = list() + var/list/reagent_genes = list() + var/list/trait_genes = list() + + var/datum/plant_gene/target + var/operation = "" + var/rating = 0 // TODO: add failures, make it useful + +/obj/machinery/plantgenes/New() + ..() + component_parts = list() + component_parts += new /obj/item/weapon/circuitboard/plantgenes(src) + component_parts += new /obj/item/weapon/stock_parts/console_screen(src) + component_parts += new /obj/item/weapon/stock_parts/scanning_module(src) + component_parts += new /obj/item/weapon/stock_parts/micro_laser(src) + component_parts += new /obj/item/weapon/stock_parts/micro_laser(src) + component_parts += new /obj/item/weapon/stock_parts/manipulator(src) + RefreshParts() + +/obj/machinery/plantgenes/RefreshParts() + rating = 0 + for(var/obj/item/weapon/stock_parts/S in component_parts) + rating += S.rating-1 + +/obj/machinery/plantgenes/update_icon() + ..() + overlays.Cut() + if((stat & (BROKEN|NOPOWER))) + icon_state = "dnamod-off" + else + icon_state = "dnamod" + if(seed) + overlays += "dnamod-dna" + if(panel_open) + overlays += "dnamod-open" + +/obj/machinery/plantgenes/attackby(obj/item/I, mob/user, params) + if(default_deconstruction_screwdriver(user, "dnamod", "dnamod", I)) + update_icon() + return + if(exchange_parts(user, I)) + return + if(default_deconstruction_crowbar(I)) + return + if(isrobot(user)) + return + + if(istype(I, /obj/item/seeds)) + if(seed) + user << "A sample is already loaded into the machine!" + else + if(!user.drop_item()) + return + insert_seed(I) + user << "You add [I] to the machine." + interact(user) + return + else if(istype(I, /obj/item/weapon/disk/plantgene)) + if(disk) + user << "A data disk is already loaded into the machine!" + else + if(!user.drop_item()) + return + disk = I + disk.loc = src + user << "You add [I] to the machine." + interact(user) + else + ..() + + +/obj/machinery/plantgenes/attack_hand(mob/user) + if(..()) + return + interact(user) + +/obj/machinery/plantgenes/interact(mob/user) + user.set_machine(src) + if(!user) + return + + var/datum/browser/popup = new(user, "plantdna", "Plant DNA Manipulator", 450, 600) // Set up the popup browser window + if(!( in_range(src, user) || istype(user, /mob/living/silicon) )) + popup.close() + return + + var/dat = "" + + if(operation) + if(!seed || (!target && operation != "insert")) + operation = "" + target = null + interact(user) + return + if((operation == "replace" || operation == "insert") && (!disk || !disk.gene)) + operation = "" + target = null + interact(user) + return + + dat += "

Confirm Operation

" + dat += "
Are you sure you want to [operation] " + switch(operation) + if("remove") + dat += "[target.get_name()] gene from \the [seed]?
" +// dat += "Failure may damage or destroy the sample!" // No failures yet. TODO: add failures + if("extract") + dat += "[target.get_name()] gene from \the [seed]?
" + dat += "The sample will be destroyed in process!" + if("replace") + dat += "[target.get_name()] gene with [disk.gene.get_name()]?
" +// dat += "Failure may damage or destroy the sample!" // No failures yet. TODO: add failures + if("insert") + dat += "[disk.gene.get_name()] gene into \the [seed]?
" +// dat += "Failure may damage or destroy the sample!" // No failures yet. TODO: add failures + dat += "
Confirm " + dat += "Abort
" + popup.set_content(dat) + popup.open() + return + + dat+= "
" + + dat += "
Plant Sample:
" + + dat += "
Data Disk:
" + + dat += "
" + + if(seed) + var/can_insert = disk && disk.gene && disk.gene.can_add(seed) + var/can_extract = disk && !disk.read_only + + dat += "

Core Genes

" + for(var/a in core_genes) + var/datum/plant_gene/G = a + if(!G) + continue + dat += "" + dat += "
[G.get_name()]" + if(can_extract) + dat += "Extract" + if(can_insert && istype(disk.gene, G.type)) + dat += "Replace" + dat += "
" + + if(seed.yield != -1) + dat += "

Content Genes

" + if(reagent_genes.len) + dat += "" + for(var/a in reagent_genes) + var/datum/plant_gene/G = a + dat += "" + dat += "
[G.get_name()]" + if(can_extract) + dat += "Extract" + dat += "Remove" + dat += "
" + else + dat += "No content-related genes detected in sample.
" + dat += "
" + if(can_insert && istype(disk.gene, /datum/plant_gene/reagent)) + dat += "Insert: [disk.gene.get_name()]" + + dat += "

Trait Genes

" + if(trait_genes.len) + dat += "" + for(var/a in trait_genes) + var/datum/plant_gene/G = a + dat += "" + dat += "
[G.get_name()]" + if(can_extract) + dat += "Extract" + dat += "Remove" + dat += "
" + else + dat += "No trait-related genes detected in sample.
" + if(can_insert && istype(disk.gene, /datum/plant_gene/trait)) + dat += "Insert: [disk.gene.get_name()]" + dat += "
" + else + dat += "
No sample found.

Please, insert a plant sample to use this device." + popup.set_content(dat) + popup.open() + + +/obj/machinery/plantgenes/Topic(var/href, var/list/href_list) + if(..()) + return + usr.set_machine(src) + + if(href_list["eject_seed"] && !operation) + if (seed) + seed.loc = src.loc + seed.verb_pickup() + seed = null + update_genes() + update_icon() + else + var/obj/item/I = usr.get_active_hand() + if (istype(I, /obj/item/seeds)) + if(!usr.drop_item()) + return + insert_seed(I) + usr << "You add [I] to the machine." + update_icon() + else if(href_list["eject_disk"] && !operation) + if (disk) + disk.loc = src.loc + disk.verb_pickup() + disk = null + update_genes() + else + var/obj/item/I = usr.get_active_hand() + if(istype(I, /obj/item/weapon/disk/plantgene)) + if(!usr.drop_item()) + return + disk = I + disk.loc = src + usr << "You add [I] to the machine." + else if(href_list["op"] == "insert" && disk && disk.gene && seed) + if(!operation) // Wait for confirmation + operation = "insert" + else + if(!istype(disk.gene, /datum/plant_gene/core) && disk.gene.can_add(seed)) + seed.genes += disk.gene.Copy() + if(istype(disk.gene, /datum/plant_gene/reagent)) + seed.reagents_from_genes() + update_genes() + operation = "" + target = null + + else if(href_list["gene"] && seed) + var/datum/plant_gene/G = seed.get_gene(href_list["gene"]) + if(!G || !href_list["op"] || !(href_list["op"] in list("remove", "extract", "replace"))) + interact(usr) + return + + if(!operation || target != G) // Wait for confirmation + target = G + operation = href_list["op"] + + else if(operation == href_list["op"] && target == G) + switch(href_list["op"]) + if("remove") + if(!istype(G, /datum/plant_gene/core)) + seed.genes -= G + if(istype(G, /datum/plant_gene/reagent)) + seed.reagents_from_genes() + if("extract") + if(disk && !disk.read_only) + disk.gene = G + disk.update_name() + qdel(seed) + seed = null + update_icon() + if("replace") + if(disk && disk.gene && istype(disk.gene, G.type) && istype(G, /datum/plant_gene/core)) + seed.genes -= G + var/datum/plant_gene/core/C = disk.gene.Copy() + seed.genes += C + C.apply_stat(seed) + if("insert") + if(disk && disk.gene && !istype(disk.gene, /datum/plant_gene/core) && disk.gene.can_add(seed)) + seed.genes += disk.gene.Copy() + if(istype(disk.gene, /datum/plant_gene/reagent)) + seed.reagents_from_genes() + + update_genes() + operation = "" + target = null + else if(href_list["abort"]) + operation = "" + target = null + + interact(usr) + +/obj/machinery/plantgenes/proc/insert_seed(obj/item/seeds/S) + if(!istype(S) || seed) + return + S.loc = src + seed = S + update_genes() + update_icon() + +/obj/machinery/plantgenes/proc/update_genes() + core_genes = list() + reagent_genes = list() + trait_genes = list() + + if(seed) + var/gene_paths = list( + /datum/plant_gene/core/potency, + /datum/plant_gene/core/yield, + /datum/plant_gene/core/production, + /datum/plant_gene/core/endurance, + /datum/plant_gene/core/lifespan + ) + for(var/a in gene_paths) + core_genes += seed.get_gene(a) + + for(var/datum/plant_gene/reagent/G in seed.genes) + reagent_genes += G + for(var/datum/plant_gene/trait/G in seed.genes) + trait_genes += G + + + +/* + * Plant DNA disk + */ + +/obj/item/weapon/disk/plantgene + name = "plant data disk" + desc = "A disk for storing plant genetic data." + icon_state = "datadisk2" + materials = list(MAT_METAL=30, MAT_GLASS=10) + var/datum/plant_gene/gene + var/read_only = 0 //Well, it's still a floppy disk + +/obj/item/weapon/disk/plantgene/New() + ..() + overlays += "datadisk_gene" + src.pixel_x = rand(-5, 5) + src.pixel_y = rand(-5, 5) + +/obj/item/weapon/disk/plantgene/attackby(obj/item/weapon/W, mob/user, params) + ..() + if(istype(W, /obj/item/weapon/pen)) + var/t = stripped_input(user, "What would you like the label to be?", name, null) + if(user.get_active_hand() != W) + return + if(!in_range(src, user) && loc != user) + return + if(t) + name = "plant data disk - '[t]'" + else + name = "plant data disk" + +/obj/item/weapon/disk/plantgene/proc/update_name() + if(gene) + name = "plant data disk - '[gene.get_name()]'" + else + name = "plant data disk" + +/obj/item/weapon/disk/plantgene/attack_self(mob/user) + read_only = !read_only + user << "You flip the write-protect tab to [src.read_only ? "protected" : "unprotected"]." + +/obj/item/weapon/disk/plantgene/examine(mob/user) + ..() + user << "The write-protect tab is set to [src.read_only ? "protected" : "unprotected"]." + + +/* + * Plant DNA Disks Box + */ +/obj/item/weapon/storage/box/disks_plantgene + name = "plant data disks box" + icon_state = "disk_kit" + +/obj/item/weapon/storage/box/disks_plantgene/New() + ..() + for(var/i in 1 to 7) + new /obj/item/weapon/disk/plantgene(src) \ No newline at end of file diff --git a/code/modules/hydroponics/grown.dm b/code/modules/hydroponics/grown.dm index 02226ad0ade..59caa40fe7c 100644 --- a/code/modules/hydroponics/grown.dm +++ b/code/modules/hydroponics/grown.dm @@ -23,7 +23,7 @@ else if(ispath(seed)) // This is for adminspawn or map-placed growns. They get the default stats of their seed type. seed = new seed() - seed.potency = 50 + seed.adjust_potency(50-seed.potency) else // Something is terribly wrong qdel(src) return @@ -77,7 +77,7 @@ if(reag_txt) msg += reag_txt - msg += "*---------*" + msg += "
*---------*" user << msg return return @@ -128,8 +128,8 @@ var/datum/plant_gene/trait/slip/S = seed.get_gene(/datum/plant_gene/trait/slip) if(S && !ispath(trash, /obj/item/weapon/grown) && iscarbon(AM)) var/mob/living/carbon/M = AM - var/stun = min(seed.potency * S.rate * 2, 1) - var/weaken = min(seed.potency * S.rate, 0.5) + var/stun = max(seed.potency * S.rate * 2, 1) + var/weaken = max(seed.potency * S.rate, 0.5) if(M.slip(stun, weaken, src)) for(var/datum/plant_gene/trait/T in seed.genes) T.on_slip(src, M) diff --git a/code/modules/hydroponics/grown/citrus.dm b/code/modules/hydroponics/grown/citrus.dm index 7f5bc5e7f27..cf05ca2b1a8 100644 --- a/code/modules/hydroponics/grown/citrus.dm +++ b/code/modules/hydroponics/grown/citrus.dm @@ -30,7 +30,7 @@ // Orange /obj/item/seeds/orange - name = "pack of orange seed" + name = "pack of orange seeds" desc = "Sour seeds." icon_state = "seed-orange" species = "orange" diff --git a/code/modules/hydroponics/growninedible.dm b/code/modules/hydroponics/growninedible.dm index a97cf99a19e..6af91f91aa9 100644 --- a/code/modules/hydroponics/growninedible.dm +++ b/code/modules/hydroponics/growninedible.dm @@ -17,7 +17,7 @@ else if(ispath(seed)) // This is for adminspawn or map-placed growns. They get the default stats of their seed type. seed = new seed() - seed.potency = 50 + seed.adjust_potency(50-seed.potency) else // Something is terribly wrong qdel(src) return @@ -53,8 +53,8 @@ var/datum/plant_gene/trait/slip/S = seed.get_gene(/datum/plant_gene/trait/slip) if(S && iscarbon(AM)) var/mob/living/carbon/M = AM - var/stun = min(seed.potency * S.rate * 2, 1) - var/weaken = min(seed.potency * S.rate, 0.5) + var/stun = max(seed.potency * S.rate * 2, 1) + var/weaken = max(seed.potency * S.rate, 0.5) if(M.slip(stun, weaken, src)) for(var/datum/plant_gene/trait/T in seed.genes) T.on_slip(src, M) diff --git a/code/modules/hydroponics/plant_genes.dm b/code/modules/hydroponics/plant_genes.dm index 364a0b68d2a..24e3e9e0527 100644 --- a/code/modules/hydroponics/plant_genes.dm +++ b/code/modules/hydroponics/plant_genes.dm @@ -4,11 +4,15 @@ /datum/plant_gene/proc/get_name() // Used for manipulator display and gene disk name. return name +/datum/plant_gene/proc/can_add(obj/item/seeds/S) + return TRUE + /datum/plant_gene/proc/Copy() return new type + // Core plant genes store 5 main variables: lifespan, endurance, production, yield, potency /datum/plant_gene/core var/value @@ -16,6 +20,9 @@ /datum/plant_gene/core/get_name() return "[name] [value]" +/datum/plant_gene/core/proc/apply_stat(obj/item/seeds/S) + return + /datum/plant_gene/core/New(var/i = null) ..() if(!isnull(i)) @@ -26,27 +33,47 @@ C.value = value return C +/datum/plant_gene/core/can_add(obj/item/seeds/S) + return S.get_gene(src.type) /datum/plant_gene/core/lifespan name = "Lifespan" value = 25 +/datum/plant_gene/core/lifespan/apply_stat(obj/item/seeds/S) + S.lifespan = value + + /datum/plant_gene/core/endurance name = "Endurance" value = 15 +/datum/plant_gene/core/endurance/apply_stat(obj/item/seeds/S) + S.endurance = value + + /datum/plant_gene/core/production name = "Production Speed" value = 6 +/datum/plant_gene/core/production/apply_stat(obj/item/seeds/S) + S.production = value + + /datum/plant_gene/core/yield name = "Yield" value = 3 +/datum/plant_gene/core/yield/apply_stat(obj/item/seeds/S) + S.yield = value + + /datum/plant_gene/core/potency name = "Potency" value = 10 +/datum/plant_gene/core/potency/apply_stat(obj/item/seeds/S) + S.potency = value // Reagent genes store reagent ID and reagent ratio. Amount of reagent in the plant = 1 + (potency * rate) @@ -79,6 +106,11 @@ G.rate = rate return G +/datum/plant_gene/reagent/can_add(obj/item/seeds/S) + for(var/datum/plant_gene/reagent/R in S.genes) + if(R.reagent_id == reagent_id) + return FALSE + return TRUE // Various traits affecting the product. Each must be somehow useful. @@ -86,12 +118,21 @@ var/rate = 0.05 var/examine_line = "" var/list/origin_tech = null + var/trait_id // must be set and equal for any two traits of the same type /datum/plant_gene/trait/Copy() var/datum/plant_gene/trait/G = ..() G.rate = rate return G +/datum/plant_gene/trait/can_add(obj/item/seeds/S) + for(var/datum/plant_gene/trait/R in S.genes) + if(trait_id && R.trait_id == trait_id) + return FALSE + if(type == R.type) + return FALSE + return TRUE + /datum/plant_gene/trait/proc/on_new(obj/item/weapon/reagent_containers/food/snacks/grown/G, newloc) if(origin_tech) // This ugly code segment adds RnD tech levels to resulting plants. if(G.origin_tech) @@ -179,6 +220,7 @@ name = "Bioluminescence" rate = 0.1 examine_line = "It emits a soft glow." + trait_id = "glow" /datum/plant_gene/trait/glow/proc/get_lum(obj/item/seeds/S) return round(S.potency*rate) diff --git a/code/modules/hydroponics/seeds.dm b/code/modules/hydroponics/seeds.dm index 26ce249e646..b13dd503231 100644 --- a/code/modules/hydroponics/seeds.dm +++ b/code/modules/hydroponics/seeds.dm @@ -78,6 +78,7 @@ for(var/g in genes) var/datum/plant_gene/G = g S.genes += G.Copy() + S.reagents_add = reagents_add.Copy() // Faster than grabbing the list from genes. return S /obj/item/seeds/proc/get_gene(typepath) @@ -141,10 +142,10 @@ if(T.reagents) for(var/reagent_id in reagents_add) if(reagent_id == "blood") // Hack to make blood in plants always O- - T.reagents.add_reagent(reagent_id, 1 + round(potency * reagents_add[reagent_id]), list("blood_type"="O-")) + T.reagents.add_reagent(reagent_id, 1 + round(potency * reagents_add[reagent_id], 1), list("blood_type"="O-")) continue - T.reagents.add_reagent(reagent_id, 1 + round(potency * reagents_add[reagent_id])) + T.reagents.add_reagent(reagent_id, 1 + round(potency * reagents_add[reagent_id]), 1) return 1 diff --git a/icons/obj/hydroponics/equipment.dmi b/icons/obj/hydroponics/equipment.dmi index 904eb38c53c..9b35f0851ad 100644 Binary files a/icons/obj/hydroponics/equipment.dmi and b/icons/obj/hydroponics/equipment.dmi differ diff --git a/tgstation.dme b/tgstation.dme index 9927e75c18f..d64ff1487ef 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -1089,6 +1089,7 @@ #include "code\modules\html_interface\cards\cards.dm" #include "code\modules\html_interface\nanotrasen\nanotrasen.dm" #include "code\modules\hydroponics\biogenerator.dm" +#include "code\modules\hydroponics\gene_modder.dm" #include "code\modules\hydroponics\grown.dm" #include "code\modules\hydroponics\growninedible.dm" #include "code\modules\hydroponics\hydroitemdefines.dm"