diff --git a/code/__DEFINES/botany.dm b/code/__DEFINES/botany.dm index 15b63ee34d0..170f944e15f 100644 --- a/code/__DEFINES/botany.dm +++ b/code/__DEFINES/botany.dm @@ -11,6 +11,9 @@ /// MINS: #define MIN_PLANT_ENDURANCE 10 +/// Default reagent volume for grown plants +#define PLANT_REAGENT_VOLUME 100 + /// -- Some botany trait value defines. -- /// Weed Hardy can only reduce plants to 3 yield. #define WEED_HARDY_YIELD_MIN 3 @@ -73,6 +76,9 @@ // obj/machinery/hydroponics/var/plant_status defines +/// How long to wait between plant age ticks, by default. See [/obj/machinery/hydroponics/var/cycledelay] +#define HYDROTRAY_CYCLE_DELAY 20 SECONDS + #define HYDROTRAY_NO_PLANT "missing" #define HYDROTRAY_PLANT_DEAD "dead" #define HYDROTRAY_PLANT_GROWING "growing" diff --git a/code/modules/asset_cache/assets/seeds.dm b/code/modules/asset_cache/assets/seeds.dm new file mode 100644 index 00000000000..f3041408ac1 --- /dev/null +++ b/code/modules/asset_cache/assets/seeds.dm @@ -0,0 +1,12 @@ +/datum/asset/spritesheet/seeds + name = "seeds" + +/datum/asset/spritesheet/seeds/create_spritesheets() + for (var/path in subtypesof(/obj/item/seeds)) + var/obj/item/seeds/seed_type = path + var/icon = initial(seed_type.icon) + var/icon_state = initial(seed_type.icon_state) + var/id = sanitize_css_class_name("[icon][icon_state]") + if(sprites[id]) //no dupes + continue + Insert(id, icon, icon_state) diff --git a/code/modules/hydroponics/grown.dm b/code/modules/hydroponics/grown.dm index 521c41103a5..387cd19c542 100644 --- a/code/modules/hydroponics/grown.dm +++ b/code/modules/hydroponics/grown.dm @@ -14,7 +14,7 @@ icon = 'icons/obj/hydroponics/harvest.dmi' worn_icon = 'icons/mob/clothing/head/hydroponics.dmi' name = "fresh produce" // so recipe text doesn't say 'snack' - max_volume = 100 + max_volume = PLANT_REAGENT_VOLUME w_class = WEIGHT_CLASS_SMALL resistance_flags = FLAMMABLE /// type path, gets converted to item on New(). It's safe to assume it's always a seed item. diff --git a/code/modules/hydroponics/hydroponics.dm b/code/modules/hydroponics/hydroponics.dm index b8c1fc700bb..930b2dd588b 100644 --- a/code/modules/hydroponics/hydroponics.dm +++ b/code/modules/hydroponics/hydroponics.dm @@ -38,7 +38,7 @@ ///Used for timing of cycles. var/lastcycle = 0 ///About 10 seconds / cycle - var/cycledelay = 200 + var/cycledelay = HYDROTRAY_CYCLE_DELAY ///The currently planted seed var/obj/item/seeds/myseed ///Obtained from the quality of the parts used in the tray, determines nutrient drain rate. diff --git a/code/modules/hydroponics/plant_genes.dm b/code/modules/hydroponics/plant_genes.dm index 1c92687ee95..b7b404be770 100644 --- a/code/modules/hydroponics/plant_genes.dm +++ b/code/modules/hydroponics/plant_genes.dm @@ -2,6 +2,8 @@ /datum/plant_gene /// The name of the gene. var/name + /// The font awesome icon name representing the gene in the seed extractor UI + var/icon = "dna" /// Flags that determine if a gene can be modified. var/mutability_flags @@ -115,7 +117,7 @@ /// The rate at which this trait affects something. This can be anything really - why? I dunno. var/rate = 0.05 /// Bonus lines displayed on examine. - var/examine_line = "" + var/description = "" /// Flag - Traits that share an ID cannot be placed on the same plant. var/trait_ids /// Flag - Modifications made to the final product. @@ -175,21 +177,21 @@ return FALSE // Add on any bonus lines on examine - if(examine_line) + if(description) RegisterSignal(our_plant, COMSIG_PARENT_EXAMINE, PROC_REF(examine)) - return TRUE /// Add on any unique examine text to the plant's examine text. /datum/plant_gene/trait/proc/examine(obj/item/our_plant, mob/examiner, list/examine_list) SIGNAL_HANDLER - examine_list += examine_line + examine_list += span_info("[description]") /// Allows the plant to be squashed when thrown or slipped on, leaving a colored mess and trash type item behind. /datum/plant_gene/trait/squash name = "Liquid Contents" - examine_line = "It has a lot of liquid contents inside." + icon = "droplet" + description = "It may burst open from the internal pressure on impact." trait_ids = THROW_IMPACT_ID | REAGENT_TRANSFER_ID | ATTACK_SELF_ID mutability_flags = PLANT_GENE_REMOVABLE | PLANT_GENE_MUTATABLE | PLANT_GENE_GRAFTABLE @@ -243,8 +245,9 @@ */ /datum/plant_gene/trait/slip name = "Slippery Skin" + description = "Watch your step around this." + icon = "person-falling" rate = 1.6 - examine_line = "It has a very slippery skin." mutability_flags = PLANT_GENE_REMOVABLE | PLANT_GENE_MUTATABLE | PLANT_GENE_GRAFTABLE /datum/plant_gene/trait/slip/on_new_plant(obj/item/our_plant, newloc) @@ -276,6 +279,8 @@ */ /datum/plant_gene/trait/cell_charge name = "Electrical Activity" + description = "It can electrocute on interaction or recharge batteries when eaten." + icon = "bolt" rate = 0.2 mutability_flags = PLANT_GENE_REMOVABLE | PLANT_GENE_MUTATABLE | PLANT_GENE_GRAFTABLE @@ -343,8 +348,9 @@ */ /datum/plant_gene/trait/glow name = "Bioluminescence" + icon = "lightbulb" rate = 0.03 - examine_line = "It emits a soft glow." + description = "It emits a soft glow." trait_ids = GLOW_ID mutability_flags = PLANT_GENE_REMOVABLE | PLANT_GENE_MUTATABLE | PLANT_GENE_GRAFTABLE /// The color of our bioluminesence. @@ -371,6 +377,7 @@ */ /datum/plant_gene/trait/glow/shadow name = "Shadow Emission" + icon = "lightbulb-o" rate = 0.04 glow_color = "#AAD84B" @@ -420,6 +427,8 @@ */ /datum/plant_gene/trait/teleport name = "Bluespace Activity" + description = "It causes people to teleport on interaction." + icon = "right-left" rate = 0.1 mutability_flags = PLANT_GENE_REMOVABLE | PLANT_GENE_MUTATABLE | PLANT_GENE_GRAFTABLE @@ -483,6 +492,8 @@ */ /datum/plant_gene/trait/maxchem name = "Densified Chemicals" + description = "The reagent volume is doubled, halving the plant yield instead." + icon = "flask-vial" rate = 2 trait_flags = TRAIT_HALVES_YIELD mutability_flags = PLANT_GENE_REMOVABLE | PLANT_GENE_MUTATABLE | PLANT_GENE_GRAFTABLE @@ -503,6 +514,8 @@ /// Allows a plant to be harvested multiple times. /datum/plant_gene/trait/repeated_harvest name = "Perennial Growth" + description = "It may be harvested multiple times from the same plant." + icon = "cubes-stacked" /// Don't allow replica pods to be multi harvested, please. seed_blacklist = list(/obj/item/seeds/replicapod) mutability_flags = PLANT_GENE_REMOVABLE | PLANT_GENE_MUTATABLE | PLANT_GENE_GRAFTABLE @@ -514,6 +527,8 @@ */ /datum/plant_gene/trait/battery name = "Capacitive Cell Production" + description = "It can work like a power cell when wired properly." + icon = "car-battery" mutability_flags = PLANT_GENE_REMOVABLE | PLANT_GENE_MUTATABLE | PLANT_GENE_GRAFTABLE /// The number of cables needed to make a battery. var/cables_needed_per_battery = 5 @@ -593,7 +608,8 @@ */ /datum/plant_gene/trait/stinging name = "Hypodermic Prickles" - examine_line = "It's quite prickley." + description = "It stings, passing some reagents in the process." + icon = "syringe" trait_ids = REAGENT_TRANSFER_ID mutability_flags = PLANT_GENE_REMOVABLE | PLANT_GENE_MUTATABLE | PLANT_GENE_GRAFTABLE @@ -629,6 +645,8 @@ /// Explodes into reagent-filled smoke when squashed. /datum/plant_gene/trait/smoke name = "Gaseous Decomposition" + description = "It can be smashed to turn its Liquid Contents into smoke." + icon = "cloud" mutability_flags = PLANT_GENE_REMOVABLE | PLANT_GENE_MUTATABLE | PLANT_GENE_GRAFTABLE /datum/plant_gene/trait/smoke/on_new_plant(obj/item/our_plant, newloc) @@ -660,6 +678,8 @@ /// Makes the plant and its seeds fireproof. From lavaland plants. /datum/plant_gene/trait/fire_resistance name = "Fire Resistance" + description = "Makes the seeds, plant and produce fireproof." + icon = "fire" mutability_flags = PLANT_GENE_REMOVABLE | PLANT_GENE_MUTATABLE | PLANT_GENE_GRAFTABLE /datum/plant_gene/trait/fire_resistance/on_new_seed(obj/item/seeds/new_seed) @@ -681,6 +701,8 @@ /// Invasive spreading lets the plant jump to other trays, and the spreading plant won't replace plants of the same type. /datum/plant_gene/trait/invasive name = "Invasive Spreading" + description = "It attempts to spread around if not contained." + icon = "virus" mutability_flags = PLANT_GENE_REMOVABLE | PLANT_GENE_MUTATABLE | PLANT_GENE_GRAFTABLE /datum/plant_gene/trait/invasive/on_new_seed(obj/item/seeds/new_seed) @@ -745,6 +767,8 @@ */ /datum/plant_gene/trait/brewing name = "Auto-Distilling Composition" + description = "Its nutriments undergo fermentation." + icon = "wine-glass" trait_ids = CONTENTS_CHANGE_ID mutability_flags = PLANT_GENE_REMOVABLE | PLANT_GENE_MUTATABLE | PLANT_GENE_GRAFTABLE @@ -755,6 +779,8 @@ */ /datum/plant_gene/trait/juicing name = "Auto-Juicing Composition" + description = "Its nutriments turn into juice." + icon = "glass-water" trait_ids = CONTENTS_CHANGE_ID mutability_flags = PLANT_GENE_REMOVABLE | PLANT_GENE_MUTATABLE | PLANT_GENE_GRAFTABLE @@ -765,6 +791,8 @@ */ /datum/plant_gene/trait/plant_laughter name = "Hallucinatory Feedback" + description = "Makes sounds when people slip on it." + icon = "face-laugh-squint" mutability_flags = PLANT_GENE_REMOVABLE | PLANT_GENE_MUTATABLE | PLANT_GENE_GRAFTABLE /// Sounds that play when this trait triggers var/list/sounds = list('sound/items/SitcomLaugh1.ogg', 'sound/items/SitcomLaugh2.ogg', 'sound/items/SitcomLaugh3.ogg') @@ -799,6 +827,8 @@ */ /datum/plant_gene/trait/eyes name = "Oculary Mimicry" + description = "It will watch after you." + icon = "eye" mutability_flags = PLANT_GENE_REMOVABLE | PLANT_GENE_MUTATABLE | PLANT_GENE_GRAFTABLE /// Our googly eyes appearance. var/mutable_appearance/googly @@ -815,7 +845,8 @@ /// Makes the plant embed on thrown impact. /datum/plant_gene/trait/sticky name = "Prickly Adhesion" - examine_line = "It's quite sticky." + description = "It sticks to people when thrown, also passing reagents if stingy." + icon = "bandage" trait_ids = THROW_IMPACT_ID mutability_flags = PLANT_GENE_REMOVABLE | PLANT_GENE_MUTATABLE | PLANT_GENE_GRAFTABLE @@ -838,6 +869,8 @@ */ /datum/plant_gene/trait/chem_heating name = "Exothermic Activity" + description = "It consumes nutriments to heat up other reagents, halving the yield." + icon = "temperatyre-arrow-up" trait_ids = TEMP_CHANGE_ID trait_flags = TRAIT_HALVES_YIELD mutability_flags = PLANT_GENE_REMOVABLE | PLANT_GENE_MUTATABLE | PLANT_GENE_GRAFTABLE @@ -848,6 +881,8 @@ */ /datum/plant_gene/trait/chem_cooling name = "Endothermic Activity" + description = "It consumes nutriments to cool down other reagents, halving the yield." + icon = "temperature-arrow-down" trait_ids = TEMP_CHANGE_ID trait_flags = TRAIT_HALVES_YIELD mutability_flags = PLANT_GENE_REMOVABLE | PLANT_GENE_MUTATABLE | PLANT_GENE_GRAFTABLE @@ -855,16 +890,20 @@ /// Prevents species mutation, while still allowing wild mutation harvest and Floral Somatoray species mutation. Trait acts as a tag for hydroponics.dm to recognise. /datum/plant_gene/trait/never_mutate name = "Prosophobic Inclination" + description = "The plant does not mutate normally, but may give a mutated produce." mutability_flags = PLANT_GENE_REMOVABLE | PLANT_GENE_MUTATABLE | PLANT_GENE_GRAFTABLE /// Prevents stat mutation caused by instability. Trait acts as a tag for hydroponics.dm to recognise. /datum/plant_gene/trait/stable_stats name = "Symbiotic Resilience" + description = "High instability does not affect the plant stats." mutability_flags = PLANT_GENE_REMOVABLE | PLANT_GENE_MUTATABLE | PLANT_GENE_GRAFTABLE /// Traits for flowers, makes plants not decompose. /datum/plant_gene/trait/preserved name = "Natural Insecticide" + description = "It does not attract ants or decompose." + icon = "bug-slash" mutability_flags = PLANT_GENE_REMOVABLE | PLANT_GENE_MUTATABLE | PLANT_GENE_GRAFTABLE /datum/plant_gene/trait/preserved/on_new_plant(obj/item/our_plant, newloc) @@ -878,6 +917,8 @@ /datum/plant_gene/trait/carnivory name = "Obligate Carnivory" + description = "Pests have positive effect on the plant health." + icon = "spider" /// Plant type traits. Incompatible with one another. /datum/plant_gene/trait/plant_type @@ -888,11 +929,16 @@ /// Weeds don't get annoyed by weeds in their tray. /datum/plant_gene/trait/plant_type/weed_hardy name = "Weed Adaptation" + description = "It is a weed that needs no nutrients and doesn't suffer from other weeds." + icon = "seedling" /// Mushrooms need less light and have a minimum yield. /datum/plant_gene/trait/plant_type/fungal_metabolism name = "Fungal Vitality" + description = "It is a mushroom that needs no water, less light and can't be overtaken by weeds." + icon = "droplet-slash" /// Currently unused and does nothing. Appears in strange seeds. /datum/plant_gene/trait/plant_type/alien_properties name ="?????" + icon = "reddit-alien" diff --git a/code/modules/hydroponics/seed_extractor.dm b/code/modules/hydroponics/seed_extractor.dm index 95e239349f2..1d418e30e67 100644 --- a/code/modules/hydroponics/seed_extractor.dm +++ b/code/modules/hydroponics/seed_extractor.dm @@ -166,9 +166,9 @@ * Arguments: * * O - seed to generate the string from */ -/obj/machinery/seed_extractor/proc/generate_seed_string(obj/item/seeds/O) - return "name=[O.name];lifespan=[O.lifespan];endurance=[O.endurance];maturation=[O.maturation];production=[O.production];yield=[O.yield];potency=[O.potency];instability=[O.instability]" - +/obj/machinery/seed_extractor/proc/generate_seed_hash(obj/item/seeds/O) + var/genes = list2params(O.genes) + return md5("[O.name][O.lifespan][O.endurance][O.maturation][O.production][O.yield][O.potency][O.instability][genes]"); /** Add Seeds Proc. * @@ -188,12 +188,32 @@ else if(!taking_from.atom_storage?.attempt_remove(to_add, src, silent = TRUE)) return FALSE - var/seed_string = generate_seed_string(to_add) - if(piles[seed_string]) - piles[seed_string] += WEAKREF(to_add) + var/seed_id = generate_seed_hash(to_add) + if(piles[seed_id]) + piles[seed_id]["refs"] += WEAKREF(to_add) else - piles[seed_string] = list(WEAKREF(to_add)) - + var/list/seed_data = list() + seed_data["icon"] = sanitize_css_class_name("[initial(to_add.icon)][initial(to_add.icon_state)]") + seed_data["name"] = capitalize(replacetext(to_add.name,"pack of ", "")); + seed_data["lifespan"] = to_add.lifespan + seed_data["endurance"] = to_add.endurance + seed_data["maturation"] = to_add.maturation + seed_data["production"] = to_add.production + seed_data["yield"] = to_add.yield + seed_data["potency"] = to_add.potency + seed_data["instability"] = to_add.instability + seed_data["refs"] = list(WEAKREF(to_add)) + seed_data["traits"] = list() + for(var/datum/plant_gene/trait/trait in to_add.genes) + seed_data["traits"] += trait.type + seed_data["reagents"] = list() + for(var/datum/plant_gene/reagent/reagent in to_add.genes) + seed_data["reagents"] += list(list( + "name" = reagent.name, + "rate" = reagent.rate + )) + seed_data["volume_mod"] = (locate(/datum/plant_gene/trait/maxchem) in to_add.genes) ? 2 : 1 + piles[seed_id] = seed_data return TRUE /obj/machinery/seed_extractor/ui_state(mob/user) @@ -206,15 +226,34 @@ ui.open() /obj/machinery/seed_extractor/ui_data() - var/list/V = list() - for(var/key in piles) - if(piles[key]) - var/len = length(piles[key]) - if(len) - V[key] = len - + var/list/seeds = list() + for(var/seed_id in piles) + if (!length(piles[seed_id]["refs"])) + piles.Remove(seed_id) // This shouldn't happen but still + continue + var/list/seed_data = piles[seed_id] + seed_data = seed_data.Copy() + seed_data["key"] = seed_id + seed_data["amount"] = length(seed_data["refs"]) + seed_data.Remove("refs") + seeds += list(seed_data) . = list() - .["seeds"] = V + .["seeds"] = seeds + +/obj/machinery/seed_extractor/ui_static_data(mob/user) + var/list/data = list() + data["cycle_seconds"] = HYDROTRAY_CYCLE_DELAY / 10 + data["trait_db"] = list() + for(var/trait_path in subtypesof(/datum/plant_gene/trait)) + var/datum/plant_gene/trait/trait = new trait_path + var/trait_data = list(list( + "path" = trait.type, + "name" = trait.name, + "icon" = trait.icon, + "description" = trait.description + )) + data["trait_db"] += trait_data + return data /obj/machinery/seed_extractor/ui_act(action, params) . = ..() @@ -222,15 +261,20 @@ return switch(action) - if("select") + if("scrap") + var/item = params["item"] + if(piles[item]) + piles.Remove(item) + . = TRUE + if("take") var/item = params["item"] if(piles[item] && length(piles[item]) > 0) - var/datum/weakref/found_seed_weakref = piles[item][1] + var/datum/weakref/found_seed_weakref = piles[item]["refs"][1] var/obj/item/seeds/found_seed = found_seed_weakref.resolve() if(!found_seed) return - piles[item] -= found_seed_weakref + piles[item]["refs"] -= found_seed_weakref if(usr) var/mob/user = usr if(user.put_in_hands(found_seed)) @@ -241,3 +285,8 @@ found_seed.forceMove(drop_location()) visible_message(span_notice("[found_seed] falls onto the floor."), null, span_hear("You hear a soft clatter."), COMBAT_MESSAGE_RANGE) . = TRUE + +/obj/machinery/seed_extractor/ui_assets(mob/user) + return list( + get_asset_datum(/datum/asset/spritesheet/seeds) + ) diff --git a/code/modules/hydroponics/unique_plant_genes.dm b/code/modules/hydroponics/unique_plant_genes.dm index 0825350092f..c94dd08b315 100644 --- a/code/modules/hydroponics/unique_plant_genes.dm +++ b/code/modules/hydroponics/unique_plant_genes.dm @@ -5,6 +5,8 @@ /// Holymelon's anti-magic trait. Charges based on potency. /datum/plant_gene/trait/anti_magic name = "Anti-Magic Vacuoles" + description = "You can hide behind it from a fireball!" + icon = "hand-sparkles" /// The amount of anti-magic blocking uses we have. var/shield_uses = 1 @@ -36,6 +38,8 @@ /// Traits that turn a plant into a weapon, giving them force and effects on attack. /datum/plant_gene/trait/attack name = "On Attack Trait" + description = "It is a very dangerous weapon." + icon = "hand-fist" /// The multiplier we apply to the potency to calculate force. Set to 0 to not affect the force. var/force_multiplier = 0 /// If TRUE, our plant will degrade in force every hit until diappearing. @@ -114,6 +118,7 @@ /// Novaflower's attack effects (sets people on fire) + degradation on attack /datum/plant_gene/trait/attack/novaflower_attack name = "Heated Petals" + description = "Hitting with it may cause things to combust." force_multiplier = 0.2 degrades_after_hit = TRUE degradation_noun = "petals" @@ -134,6 +139,7 @@ /// Sunflower's attack effect (shows cute text) /datum/plant_gene/trait/attack/sunflower_attack name = "Bright Petals" + description = "Makes others feel the power on hit." /datum/plant_gene/trait/attack/sunflower_attack/after_attack_effect(obj/item/our_plant, atom/target, mob/user, proximity_flag, click_parameters) if(ismob(target)) @@ -159,6 +165,8 @@ /// Traits for plants with backfire effects. These are negative effects that occur when a plant is handled without gloves/unsafely. /datum/plant_gene/trait/backfire name = "Backfire Trait" + icon = "mitten" + description = "Be careful when holding it without protection." /// Whether our actions are cancelled when the backfire triggers. var/cancel_action_on_backfire = FALSE /// A list of extra traits to check to be considered safe. @@ -190,6 +198,7 @@ /// Rose's prick on backfire /datum/plant_gene/trait/backfire/rose_thorns name = "Rose Thorns" + description = "The stem has a lot of thorns." traits_to_check = list(TRAIT_PIERCEIMMUNE) /datum/plant_gene/trait/backfire/rose_thorns/backfire_effect(obj/item/our_plant, mob/living/carbon/user) @@ -206,6 +215,7 @@ /// Novaflower's hand burn on backfire /datum/plant_gene/trait/backfire/novaflower_heat name = "Burning Stem" + description = "The stem may burn your hand." cancel_action_on_backfire = TRUE /datum/plant_gene/trait/backfire/novaflower_heat/backfire_effect(obj/item/our_plant, mob/living/carbon/user) @@ -217,6 +227,7 @@ /// Normal Nettle hannd burn on backfire /datum/plant_gene/trait/backfire/nettle_burn name = "Stinging Stem" + description = "The stem may sting your hand." /datum/plant_gene/trait/backfire/nettle_burn/backfire_effect(obj/item/our_plant, mob/living/carbon/user) to_chat(user, span_danger("[our_plant] burns your bare hand!")) @@ -240,6 +251,7 @@ /// Ghost-Chili heating up on backfire /datum/plant_gene/trait/backfire/chili_heat name = "Active Capsicum Glands" + description = "You may survive a cold winter with this in hand." genes_to_check = list(/datum/plant_gene/trait/chem_heating) /// The mob currently holding the chili. var/datum/weakref/held_mob @@ -295,6 +307,7 @@ /// Bluespace Tomato squashing on the user on backfire /datum/plant_gene/trait/backfire/bluespace name = "Bluespace Volatility" + description = "You may be spaced out if you hold this unprotected." cancel_action_on_backfire = TRUE genes_to_check = list(/datum/plant_gene/trait/squash) @@ -311,6 +324,8 @@ /// Traits for plants that can be activated to turn into a mob. /datum/plant_gene/trait/mob_transformation name = "Dormant Ferocity" + description = "It comes to life when shaken in hand." + icon = "heart-pulse" trait_ids = ATTACK_SELF_ID /// Whether mobs spawned by this trait are dangerous or not. var/dangerous = FALSE @@ -443,6 +458,8 @@ /// Traiit for plants eaten in 1 bite. /datum/plant_gene/trait/one_bite name = "Large Bites" + description = "You can't hold off from eating this in one bite!" + icon = "drumstick-bite" /datum/plant_gene/trait/one_bite/on_new_plant(obj/item/our_plant, newloc) . = ..() @@ -456,6 +473,8 @@ /// Traits for plants with a different base max_volume. /datum/plant_gene/trait/modified_volume name = "Deep Vesicles" + description = "It has more reagents than usual." + icon = "vials" /// The new number we set the plant's max_volume to. var/new_capcity = 100 @@ -481,6 +500,8 @@ /// Plants that explode when used (based on their reagent contents) /datum/plant_gene/trait/bomb_plant name = "Explosive Contents" + description = "Don't shake it, the contents may explode." + icon = "bomb" trait_ids = ATTACK_SELF_ID /datum/plant_gene/trait/bomb_plant/on_new_plant(obj/item/our_plant, newloc) @@ -587,6 +608,8 @@ /// Can be generalized in the future to spawn any gas, but I don't think that's necessarily a good idea. /datum/plant_gene/trait/gas_production name = "Miasma Gas Production" + description = "This plant stinks when grown." + icon = "wind" /// The location of our tray, if we have one. var/datum/weakref/home_tray /// The seed emitting gas. diff --git a/code/modules/unit_tests/hydroponics_extractor_storage.dm b/code/modules/unit_tests/hydroponics_extractor_storage.dm index 1145c408f02..ea2fc36c9e7 100644 --- a/code/modules/unit_tests/hydroponics_extractor_storage.dm +++ b/code/modules/unit_tests/hydroponics_extractor_storage.dm @@ -37,11 +37,11 @@ TEST_ASSERT_NOTNULL(apple_now_stored, "The apple seed was removed from the dummy's hands, but is not in the plant seed extractor's contents.") // The apple seed's key should be in the extractor's "piles" list - var/apple_seed_key = extractor.generate_seed_string(apple_now_stored) + var/apple_seed_key = extractor.generate_seed_hash(apple_now_stored) TEST_ASSERT(apple_seed_key in extractor.piles, "The apple seed was added to the plant seed extractor's contents correctly, but did not register in the piles list, and is unaccessible.") // And it should be tracked in the piles list as a weakref - TEST_ASSERT_EQUAL(length(extractor.piles[apple_seed_key]), 1, "While 1 apple seed was added to the plant seed extractor, its weakref was not added to the piles list correctly.") + TEST_ASSERT_EQUAL(length(extractor.piles[apple_seed_key]["refs"]), 1, "While 1 apple seed was added to the plant seed extractor, its weakref was not added to the piles list correctly.") // Let's test the plant bag now. // If they fail to pick up the bag, we have an issue. @@ -63,8 +63,8 @@ TEST_ASSERT_NOTNULL(seed_now_stored, "The plant bag transferred its [initial(seed_type.name)] somewhere, but they were not found in the plant seed extractor.") // All keys shold be independently in the piles list - var/stored_seed_key = extractor.generate_seed_string(seed_now_stored) + var/stored_seed_key = extractor.generate_seed_hash(seed_now_stored) TEST_ASSERT(stored_seed_key in extractor.piles, "The [initial(seed_type.name)] was added to the plant seed extractor's contents correctly, but did not register in the piles list, and is unaccessible.") // And all seeds should be tracked as weakrefs - TEST_ASSERT_EQUAL(length(extractor.piles[stored_seed_key]), num_seeds_to_make_of_each, "While [num_seeds_to_make_of_each] [initial(seed_type.name)]s were added to the plant seed extractor, not all weakrefs were added to the piles list correctly.") + TEST_ASSERT_EQUAL(length(extractor.piles[stored_seed_key]["refs"]), num_seeds_to_make_of_each, "While [num_seeds_to_make_of_each] [initial(seed_type.name)]s were added to the plant seed extractor, not all weakrefs were added to the piles list correctly.") diff --git a/tgstation.dme b/tgstation.dme index 7c285058228..d52584019b4 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -2508,6 +2508,7 @@ #include "code\modules\asset_cache\assets\radar.dm" #include "code\modules\asset_cache\assets\research_designs.dm" #include "code\modules\asset_cache\assets\safe.dm" +#include "code\modules\asset_cache\assets\seeds.dm" #include "code\modules\asset_cache\assets\sheetmaterials.dm" #include "code\modules\asset_cache\assets\supplypods.dm" #include "code\modules\asset_cache\assets\tgfont.dm" diff --git a/tgui/packages/tgui/interfaces/SeedExtractor.js b/tgui/packages/tgui/interfaces/SeedExtractor.js deleted file mode 100644 index 0da80af8dc9..00000000000 --- a/tgui/packages/tgui/interfaces/SeedExtractor.js +++ /dev/null @@ -1,90 +0,0 @@ -import { sortBy } from 'common/collections'; -import { flow } from 'common/fp'; -import { toTitleCase } from 'common/string'; -import { useBackend } from '../backend'; -import { Button, Section, Table } from '../components'; -import { Window } from '../layouts'; - -/** - * This method takes a seed string and splits the values - * into an object - */ -const splitSeedString = (text) => { - const re = /([^;=]+)=([^;]+)/g; - const ret = {}; - let m; - do { - m = re.exec(text); - if (m) { - ret[m[1]] = m[2] + ''; - } - } while (m); - return ret; -}; - -/** - * This method splits up the string "name" we get for the seeds - * and creates an object from it include the value that is the - * ammount - * - * @returns {any[]} - */ -const createSeeds = (seedStrings) => { - const objs = Object.keys(seedStrings).map((key) => { - const obj = splitSeedString(key); - obj.amount = seedStrings[key]; - obj.key = key; - obj.name = toTitleCase(obj.name.replace('pack of ', '')); - return obj; - }); - return flow([sortBy((item) => item.name)])(objs); -}; - -export const SeedExtractor = (props, context) => { - const { act, data } = useBackend(context); - const seeds = createSeeds(data.seeds); - return ( - - -
- - - Name - Lifespan - Endurance - Maturation - Production - Yield - Potency - Instability - Stock - - {seeds.map((item) => ( - - {item.name} - {item.lifespan} - {item.endurance} - {item.maturation} - {item.production} - {item.yield} - {item.potency} - {item.instability} - -
-
-
-
- ); -}; diff --git a/tgui/packages/tgui/interfaces/SeedExtractor.tsx b/tgui/packages/tgui/interfaces/SeedExtractor.tsx new file mode 100644 index 00000000000..0f3668e8260 --- /dev/null +++ b/tgui/packages/tgui/interfaces/SeedExtractor.tsx @@ -0,0 +1,354 @@ +import { BooleanLike, classes } from 'common/react'; +import { createSearch } from 'common/string'; +import { flow } from 'common/fp'; +import { sortBy } from 'common/collections'; +import { useBackend, useLocalState } from '../backend'; +import { Input, Tooltip, Box, ProgressBar, Button, Section, Table, NoticeBox, Icon } from '../components'; +import { Window } from '../layouts'; + +type TraitData = { + path: string; + name: string; + icon: string; + description: string; +}; + +type ReagentData = { + name: string; + rate: string; +}; + +type SeedData = { + key: string; + amount: number; + name: string; + lifespan: number; + endurance: number; + maturation: number; + production: number; + yield: number; + potency: number; + instability: number; + icon: string; + volume_mod: BooleanLike; + traits: string[]; + reagents: ReagentData[]; +}; + +type SeedExtractorData = { + // Dynamic + seeds: SeedData[]; + // Static + trait_db: TraitData[]; + cycle_seconds: number; +}; + +export const SeedExtractor = (props, context) => { + const { act, data } = useBackend(context); + const [searchText, setSearchText] = useLocalState(context, 'searchText', ''); + const [sortField, setSortField] = useLocalState(context, 'sortField', 'name'); + const [action, toggleAction] = useLocalState(context, 'action', true); + const search = createSearch(searchText, (item: SeedData) => item.name); + const seeds_filtered = + searchText.length > 0 ? data.seeds.filter(search) : data.seeds; + const seeds = flow([ + sortBy((item: SeedData) => item[sortField as keyof SeedData]), + ])(seeds_filtered || []); + sortField !== 'name' && seeds.reverse(); + return ( + + +
+ + + + setSearchText(value)} + fluid + /> + + + + setSortField('potency')}> + Potency + + + + + + setSortField('yield')}> + Yield + + + + + + setSortField('instability')}> + Instability + + + + + + setSortField('endurance')}> + Endurance + + + + + + setSortField('lifespan')}> + Lifespan + + + + + + setSortField('maturation')}> + Maturation + + + + + + setSortField('production')}> + Production + + + + + setSortField('amount')}> + Amount + + + + {sortField !== 'name' && ( + +
+ {seeds.length === 0 && ( + + No seeds found. + + )} +
+
+
+ ); +}; + +const Level = (props) => { + return ( + + + {props.value} + + + ); +}; + +const ReagentTooltip = (props) => { + return ( + + + Reagents + + {props.reagents?.map((reagent) => ( + + {reagent.name} + + {Math.max( + Math.round(reagent.rate * props.potency * props.volume_mod), + 1 + )} + u + + + ))} +
+ ); +}; + +const TraitTooltip = (props) => { + const trait = props.trait_db.find((t) => { + return t.path === props.path; + }); + return ( + + + + + {trait.name} + + + {!!trait.description && ( + + {trait.description} + + )} + + }> + + + ); +};