Files
nevimer 00ccf0c6b5 Merge remote-tracking branch 'tgstation/master' into upstream-feb12-2026
# Conflicts:
#	.github/CODEOWNERS
#	.github/workflows/compile_changelogs.yml
#	.github/workflows/stale.yml
#	SQL/database_changelog.md
#	_maps/map_files/CatwalkStation/CatwalkStation_2023.dmm
#	code/__DEFINES/atom_hud.dm
#	code/__DEFINES/inventory.dm
#	code/__DEFINES/mobs.dm
#	code/__DEFINES/species_clothing_paths.dm
#	code/__DEFINES/subsystems.dm
#	code/__DEFINES/surgery.dm
#	code/__HELPERS/global_lists.dm
#	code/_globalvars/lists/maintenance_loot.dm
#	code/_globalvars/traits/_traits.dm
#	code/controllers/subsystem/minor_mapping.dm
#	code/controllers/subsystem/processing/quirks.dm
#	code/controllers/subsystem/shuttle.dm
#	code/datums/components/palette.dm
#	code/datums/components/surgery_initiator.dm
#	code/datums/diseases/advance/advance.dm
#	code/datums/hud.dm
#	code/datums/mood.dm
#	code/datums/mutations/chameleon.dm
#	code/datums/quirks/negative_quirks/nyctophobia.dm
#	code/datums/status_effects/debuffs/debuffs.dm
#	code/datums/status_effects/debuffs/drunk.dm
#	code/datums/status_effects/debuffs/slime/slime_leech.dm
#	code/datums/weather/weather.dm
#	code/game/data_huds.dm
#	code/game/objects/items.dm
#	code/game/objects/items/devices/scanners/health_analyzer.dm
#	code/game/objects/items/frog_statue.dm
#	code/game/objects/items/rcd/RLD.dm
#	code/game/objects/items/robot/items/hypo.dm
#	code/game/objects/items/stacks/medical.dm
#	code/game/objects/items/stacks/wrap.dm
#	code/game/objects/items/storage/garment.dm
#	code/game/objects/items/tools/medical/defib.dm
#	code/game/objects/items/weaponry.dm
#	code/game/objects/items/weaponry/melee/misc.dm
#	code/game/objects/structures/crates_lockers/closets/secure/security.dm
#	code/game/objects/structures/curtains.dm
#	code/game/objects/structures/dresser.dm
#	code/game/objects/structures/girders.dm
#	code/game/objects/structures/maintenance.dm
#	code/game/objects/structures/mirror.dm
#	code/modules/admin/greyscale_modify_menu.dm
#	code/modules/admin/verbs/light_debug.dm
#	code/modules/antagonists/ashwalker/ashwalker.dm
#	code/modules/antagonists/heretic/knowledge/starting_lore.dm
#	code/modules/antagonists/ninja/ninjaDrainAct.dm
#	code/modules/art/paintings.dm
#	code/modules/client/preferences.dm
#	code/modules/client/verbs/ooc.dm
#	code/modules/clothing/head/wig.dm
#	code/modules/events/disease_outbreak.dm
#	code/modules/holodeck/holo_effect.dm
#	code/modules/jobs/job_types/head_of_security.dm
#	code/modules/jobs/job_types/security_officer.dm
#	code/modules/library/skill_learning/generic_skillchips/point.dm
#	code/modules/mining/lavaland/ash_flora.dm
#	code/modules/mining/lavaland/mining_loot/megafauna/ash_drake.dm
#	code/modules/mob/dead/new_player/new_player.dm
#	code/modules/mob/living/basic/guardian/guardian.dm
#	code/modules/mob/living/basic/space_fauna/space_dragon/space_dragon.dm
#	code/modules/mob/living/carbon/carbon.dm
#	code/modules/mob/living/carbon/human/human.dm
#	code/modules/mob/living/carbon/human/human_defines.dm
#	code/modules/mob/living/carbon/life.dm
#	code/modules/mob/living/living.dm
#	code/modules/mob/living/living_defines.dm
#	code/modules/mob/mob.dm
#	code/modules/mob_spawn/ghost_roles/mining_roles.dm
#	code/modules/mod/mod_control.dm
#	code/modules/mod/modules/modules_general.dm
#	code/modules/modular_computers/computers/item/computer_ui.dm
#	code/modules/paperwork/paper.dm
#	code/modules/paperwork/paperbin.dm
#	code/modules/power/lighting/light.dm
#	code/modules/projectiles/guns/energy/kinetic_accelerator.dm
#	code/modules/projectiles/projectile.dm
#	code/modules/reagents/chemistry/reagents/drinks/alcohol_reagents.dm
#	code/modules/reagents/chemistry/reagents/food_reagents.dm
#	code/modules/reagents/chemistry/reagents/other_reagents.dm
#	code/modules/reagents/chemistry/reagents/pyrotechnic_reagents.dm
#	code/modules/research/xenobiology/crossbreeding/_clothing.dm
#	code/modules/research/xenobiology/crossbreeding/prismatic.dm
#	code/modules/surgery/advanced/brainwashing.dm
#	code/modules/surgery/advanced/lobotomy.dm
#	code/modules/surgery/amputation.dm
#	code/modules/surgery/blood_filter.dm
#	code/modules/surgery/bodyparts/_bodyparts.dm
#	code/modules/surgery/brain_surgery.dm
#	code/modules/surgery/cavity_implant.dm
#	code/modules/surgery/coronary_bypass.dm
#	code/modules/surgery/gastrectomy.dm
#	code/modules/surgery/healing.dm
#	code/modules/surgery/limb_augmentation.dm
#	code/modules/surgery/organ_manipulation.dm
#	code/modules/surgery/revival.dm
#	code/modules/surgery/sleeper_protocol.dm
#	code/modules/surgery/surgery_helpers.dm
#	code/modules/surgery/surgery_step.dm
#	code/modules/unit_tests/_unit_tests.dm
#	code/modules/unit_tests/designs.dm
#	code/modules/unit_tests/icon_state_worn.dm
#	code/modules/unit_tests/screenshots/screenshot_antag_icons_cultist.png
#	code/modules/unit_tests/screenshots/screenshot_antag_icons_headrevolutionary.png
#	code/modules/unit_tests/screenshots/screenshot_antag_icons_provocateur.png
#	code/modules/unit_tests/screenshots/screenshot_husk_body.png
#	code/modules/unit_tests/screenshots/screenshot_husk_body_missing_limbs.png
#	icons/map_icons/clothing/head/_head.dmi
#	icons/map_icons/clothing/shoes.dmi
#	icons/map_icons/items/_item.dmi
#	icons/mob/huds/hud.dmi
#	icons/mob/inhands/64x64_lefthand.dmi
#	icons/mob/inhands/64x64_righthand.dmi
#	icons/obj/machines/computer.dmi
#	tgui/packages/tgui/interfaces/OperatingComputer.jsx
#	tgui/packages/tgui/interfaces/PreferencesMenu/CharacterPreferences/MainPage.tsx
#	tgui/packages/tgui/interfaces/PreferencesMenu/types.ts
#	tgui/packages/tgui/interfaces/SurgeryInitiator.tsx
#	tools/icon_cutter/check.py
2026-02-12 23:50:09 -05:00

663 lines
25 KiB
Plaintext

// ********************************************************
// Here's all the seeds (plants) that can be used in hydro
// ********************************************************
/obj/item/seeds
icon = 'icons/obj/service/hydroponics/seeds.dmi'
icon_state = "seed" // Unknown plant seed - these shouldn't exist in-game.
worn_icon_state = "seed"
abstract_type = /obj/item/seeds
w_class = WEIGHT_CLASS_TINY
resistance_flags = FLAMMABLE
obj_flags = UNIQUE_RENAME
/// Name of plant when planted.
var/plantname = "Plants"
/// A type path. The thing that is created when the plant is harvested.
var/obj/item/product
///Describes the product on the product path.
var/productdesc
/// Used to update icons. Should match the name in the sprites unless all icon_* are overridden.
var/species = ""
///the file that stores the sprites of the growing plant from this seed.
var/growing_icon = 'icons/obj/service/hydroponics/growing.dmi'
/// Used to override grow icon (default is `"[species]-grow"`). You can use one grow icon for multiple closely related plants with it.
var/icon_grow
/// Used to override dead icon (default is `"[species]-dead"`). You can use one dead icon for multiple closely related plants with it.
var/icon_dead
/// Used to override harvest icon (default is `"[species]-harvest"`). If null, plant will use `[icon_grow][growthstages]`.
var/icon_harvest
/// Used to offset the plant sprite so that it appears at proper height in the tray
var/plant_icon_offset = 8
/// How long before the plant begins to take damage from age.
var/lifespan = 25
/// Amount of health the plant has.
var/endurance = 15
/// Used to determine which sprite to switch to when growing.
var/maturation = 6
/// Changes the amount of time needed for a plant to become harvestable.
var/production = 6
/// Amount of growns created per harvest. If is -1, the plant/shroom/weed is never meant to be harvested.
var/yield = 3
/// The 'power' of a plant. Generally effects the amount of reagent in a plant, also used in other ways.
var/potency = 10
/// Amount of growth sprites the plant has.
var/growthstages = 6
// Chance that a plant will mutate in each stage of its life.
var/instability = 5
/// How rare the plant is. Used for giving points to cargo when shipping off to CentCom.
var/rarity = 0
/// The type of plants that this plant can mutate into.
var/list/mutatelist
/// Starts as a list of paths, is converted to a list of types on init. Plant gene datums are stored here, see plant_genes.dm for more info.
var/list/genes = list()
/// A list of reagents to add to product.
var/list/reagents_add
// Format: /datum/reagent/type = potency multiplier
// Stronger reagents must always come first to avoid being displaced by weaker ones.
// Total amount of any reagent in plant is calculated by formula: max(round(potency * multiplier), 1)
///If the chance below passes, then this many weeds sprout during growth
var/weed_rate = 1
///Percentage chance per tray update to grow weeds
var/weed_chance = 5
///How many grafts or cuttings have been taken from this plant.
var/grafts_taken = 0
///Type-path of trait to be applied when grafting a plant.
var/datum/plant_gene/graft_gene = /datum/plant_gene/trait/repeated_harvest
///Determines if the plant should be allowed to mutate early at 30+ instability.
var/seed_flags = MUTATE_EARLY
/obj/item/seeds/Initialize(mapload, nogenes = FALSE)
. = ..()
pixel_x = base_pixel_x + rand(-8, 8)
pixel_y = base_pixel_y + rand(-8, 8)
if(!icon_grow)
icon_grow = "[species]-grow"
if(!icon_dead)
icon_dead = "[species]-dead"
if(!icon_harvest && !get_gene(/datum/plant_gene/trait/plant_type/fungal_metabolism) && yield != -1)
icon_harvest = "[species]-harvest"
if(!nogenes)
// START Bubber edit
genes += new /datum/plant_gene/core/lifespan(lifespan)
genes += new /datum/plant_gene/core/endurance(endurance)
genes += new /datum/plant_gene/core/weed_rate(weed_rate)
genes += new /datum/plant_gene/core/weed_chance(weed_chance)
if(yield != -1)
genes += new /datum/plant_gene/core/yield(yield)
genes += new /datum/plant_gene/core/production(production)
if(potency != -1)
genes += new /datum/plant_gene/core/potency(potency)
genes += new /datum/plant_gene/core/instability(instability)
// END Bubber edit
for(var/plant_gene in genes)
if(ispath(plant_gene))
genes -= plant_gene
genes += new plant_gene
// Go through all traits in their genes and call on_new_seed from them.
for(var/datum/plant_gene/trait/traits in genes)
traits.on_new_seed(src)
for(var/reag_id in reagents_add)
genes += new /datum/plant_gene/reagent(reag_id, reagents_add[reag_id])
reagents_from_genes() //quality coding
var/static/list/hovering_item_typechecks = list(
/obj/item/plant_analyzer = list(
SCREENTIP_CONTEXT_LMB = "Scan seed stats",
SCREENTIP_CONTEXT_RMB = "Scan seed chemicals"
),
)
AddElement(/datum/element/contextual_screentip_item_typechecks, hovering_item_typechecks)
/obj/item/seeds/Destroy()
// No AS ANYTHING here, because the list/genes could have typepaths in it.
for(var/datum/plant_gene/gene in genes)
gene.on_removed(src)
qdel(gene)
genes.Cut()
return ..()
/obj/item/seeds/examine(mob/user)
. = ..()
. += span_notice("Use a pen on it to rename it or change its description.")
if(reagents_add && user.can_see_reagents())
. += span_notice("- Plant Reagents -")
for(var/datum/plant_gene/reagent/reagent_gene in genes)
. += span_notice("- [reagent_gene.get_name()] -")
/// Copy all the variables from one seed to a new instance of the same seed and return it.
/obj/item/seeds/proc/Copy()
var/obj/item/seeds/copy_seed = new type(null, TRUE)
// Copy all the stats
copy_seed.lifespan = lifespan
copy_seed.endurance = endurance
copy_seed.maturation = maturation
copy_seed.production = production
copy_seed.yield = yield
copy_seed.potency = potency
copy_seed.instability = instability
copy_seed.weed_rate = weed_rate
copy_seed.weed_chance = weed_chance
copy_seed.name = name
copy_seed.plantname = plantname
copy_seed.desc = desc
copy_seed.productdesc = productdesc
copy_seed.genes = list()
for(var/datum/plant_gene/gene in genes)
var/datum/plant_gene/copied_gene = gene.Copy()
copy_seed.genes += copied_gene
copied_gene.on_new_seed(copy_seed)
copy_seed.reagents_add = reagents_add.Copy() // Faster than grabbing the list from genes.
return copy_seed
/obj/item/seeds/proc/get_gene(typepath)
return (locate(typepath) in genes)
/obj/item/seeds/proc/reagents_from_genes()
reagents_add = list()
for(var/datum/plant_gene/reagent/R in genes)
reagents_add[R.reagent_id] = R.rate
/obj/item/seeds/proc/mutate(lifemut = 2, endmut = 5, productmut = 1, yieldmut = 2, potmut = 25, wrmut = 2, wcmut = 5, traitmut = 0, stabmut = 3)
adjust_lifespan(rand(-lifemut,lifemut))
adjust_endurance(rand(-endmut,endmut))
adjust_production(rand(-productmut,productmut))
adjust_yield(rand(-yieldmut,yieldmut))
adjust_potency(rand(-potmut,potmut))
adjust_instability(rand(-stabmut,stabmut))
adjust_weed_rate(rand(-wrmut, wrmut))
adjust_weed_chance(rand(-wcmut, wcmut))
if(prob(traitmut))
if(prob(50))
add_random_traits(1, 1)
else
add_random_reagents(1, 1)
/obj/item/seeds/bullet_act(obj/projectile/proj) //Works with the Somatoray to modify plant variables.
if(!istype(proj, /obj/projectile/energy/flora/yield))
return ..()
var/rating = 1
if(istype(loc, /obj/machinery/hydroponics))
var/obj/machinery/hydroponics/H = loc
rating = H.rating
if(yield == 0)//Oh god don't divide by zero you'll doom us all.
adjust_yield(1 * rating)
else if(prob(1/(yield * yield) * 100))//This formula gives you diminishing returns based on yield. 100% with 1 yield, decreasing to 25%, 11%, 6, 4, 2...
adjust_yield(1 * rating)
return BULLET_ACT_HIT
// Harvest procs
/obj/item/seeds/proc/getYield()
var/return_yield = yield
var/obj/machinery/hydroponics/parent = loc
// Handle hydroponics malus for soil lovers like rootcrops.
if(!(parent.tray_flags & SOIL) && get_gene(/datum/plant_gene/trait/soil_lover))
return_yield *= SOIL_LOVER_HYDRO_YIELD_MALUS
// Determine if the plant should get a yield bonus from bee pollination.
for(var/datum/plant_gene/trait/trait in genes)
if(trait.trait_flags & TRAIT_NO_POLLINATION)
return return_yield
if(istype(loc, /obj/machinery/hydroponics))
if(parent.yieldmod == 0)
return_yield = min(return_yield, 1)//1 if above zero, 0 otherwise
else
return_yield *= (parent.yieldmod)
return round(return_yield)
/obj/item/seeds/proc/harvest(mob/user)
///Reference to the tray/soil the seeds are planted in.
var/obj/machinery/hydroponics/parent = loc //for ease of access
///Count used for creating the correct amount of results to the harvest.
var/t_amount = 0
///List of plants all harvested from the same batch.
var/list/result = list()
///Tile of the harvester to deposit the growables.
var/output_loc = parent.Adjacent(user) ? user.drop_location() : parent.drop_location() //needed for TK
///Name of the grown products.
var/product_name
///The Number of products produced by the plant, typically the yield. Modified by certain traits.
var/product_count = getYield()
while(t_amount < product_count)
var/obj/item/food/grown/t_prod
if(instability >= 30 && (seed_flags & MUTATE_EARLY) && LAZYLEN(mutatelist) && prob(instability/3))
var/obj/item/seeds/mutated_seed = pick(mutatelist)
t_prod = initial(mutated_seed.product)
if(!t_prod)
continue
mutated_seed = new mutated_seed
for(var/datum/plant_gene/trait/trait in parent.myseed.genes)
if((trait.mutability_flags & PLANT_GENE_MUTATABLE) && trait.can_add(mutated_seed))
mutated_seed.genes += trait.Copy()
t_prod = new t_prod(output_loc, new_seed = mutated_seed)
t_prod.transform = initial(t_prod.transform)
t_prod.transform *= TRANSFORM_USING_VARIABLE(t_prod.seed.potency, 100) + 0.5
ADD_TRAIT(t_prod, TRAIT_PLANT_WILDMUTATE, INNATE_TRAIT)
t_amount++
if(t_prod.seed)
t_prod.seed.set_instability(round(instability * 0.5))
continue
else
// Create a descendent seed so we can modify our offspring before creating a grown from it.
var/obj/item/seeds/descendent_seed = Copy()
// Cuck potency if a soil lover such as a root crop is grown in water.
if(!(parent.tray_flags & SOIL) && get_gene(/datum/plant_gene/trait/soil_lover))
descendent_seed.set_potency(round(potency * RANDOM_DECIMAL(SOIL_LOVER_HYDRO_POTENCY_MIN, SOIL_LOVER_HYDRO_POTENCY_MAX)))
t_prod = new product(output_loc, new_seed = descendent_seed)
if(parent.myseed.plantname != initial(parent.myseed.plantname))
t_prod.name = LOWER_TEXT(parent.myseed.plantname)
if(productdesc)
t_prod.desc = productdesc
t_prod.seed.name = parent.myseed.name
t_prod.seed.desc = parent.myseed.desc
t_prod.seed.plantname = parent.myseed.plantname
result.Add(t_prod) // User gets a consumable
if(!t_prod)
return
t_amount++
product_name = parent.myseed.plantname
if(product_count >= 1)
SSblackbox.record_feedback("tally", "food_harvested", product_count, product_name)
parent.update_tray(user, product_count)
return result
/**
* This is where plant chemical products are handled.
*
* Individually, the formula for individual amounts of chemicals is Potency * the chemical production %, rounded to the fullest 1.
* Specific chem handling is also handled here, like bloodtype, food taste within nutriment, and the auto-distilling/autojuicing traits.
* This is where chemical reactions can occur, and the heating / cooling traits effect the reagent container.
*/
/obj/item/seeds/proc/prepare_result(obj/item/T)
if(!T.reagents)
CRASH("[T] has no reagents.")
var/reagent_max = 0
for(var/rid in reagents_add)
reagent_max += reagents_add[rid]
if(IS_EDIBLE(T) || istype(T, /obj/item/grown))
var/obj/item/food/grown/grown_edible = T
var/reagent_purity = get_reagent_purity()
for(var/rid in reagents_add)
var/reagent_overflow_mod = reagents_add[rid]
if(reagent_max > 1)
reagent_overflow_mod = (reagents_add[rid]/ reagent_max)
var/edible_vol = grown_edible.reagents ? grown_edible.reagents.maximum_volume : 0
var/amount = max(1, round((edible_vol)*(potency/100) * reagent_overflow_mod, 1)) //the plant will always have at least 1u of each of the reagents in its reagent production traits
var/list/data
if(rid == /datum/reagent/blood) // Hack to make blood in plants always O-
data = list("blood_type" = get_blood_type(BLOOD_TYPE_O_MINUS))
if(istype(grown_edible) && (rid == /datum/reagent/consumable/nutriment || rid == /datum/reagent/consumable/nutriment/vitamin))
data = grown_edible.tastes // apple tastes of apple.
T.reagents.add_reagent(rid, amount, data, added_purity = reagent_purity)
//Handles the juicing trait, swaps nutriment and vitamins for that species various juices if they exist. Mutually exclusive with distilling.
if(get_gene(/datum/plant_gene/trait/juicing) && grown_edible.juice_typepath())
grown_edible.juice(juicer = FALSE) //we pass FALSE & not null because Byond default args will subtitute it with the default value
else if(get_gene(/datum/plant_gene/trait/brewing))
grown_edible.ferment()
/// The number of nutriments we have inside of our plant, for use in our heating / cooling genes
var/num_nutriment = T.reagents.get_reagent_amount(/datum/reagent/consumable/nutriment)
// Heats up the plant's contents by 25 kelvin per 1 unit of nutriment. Mutually exclusive with cooling.
if(get_gene(/datum/plant_gene/trait/chem_heating))
T.visible_message(span_notice("[T] releases freezing air, consuming its nutriments to heat its contents."))
T.reagents.remove_reagent(/datum/reagent/consumable/nutriment, num_nutriment)
T.reagents.chem_temp = min(1000, (T.reagents.chem_temp + num_nutriment * 25))
T.reagents.handle_reactions()
playsound(T.loc, 'sound/effects/wounds/sizzle2.ogg', 5)
// Cools down the plant's contents by 5 kelvin per 1 unit of nutriment. Mutually exclusive with heating.
else if(get_gene(/datum/plant_gene/trait/chem_cooling))
T.visible_message(span_notice("[T] releases a blast of hot air, consuming its nutriments to cool its contents."))
T.reagents.remove_reagent(/datum/reagent/consumable/nutriment, num_nutriment)
T.reagents.chem_temp = max(3, (T.reagents.chem_temp + num_nutriment * -5))
T.reagents.handle_reactions()
playsound(T.loc, 'sound/effects/space_wind.ogg', 50)
/// Returns reagent purity based on seed stats
/obj/item/seeds/proc/get_reagent_purity()
var/purity_from_lifespan = lifespan / 400 //up to +25% for lifespan
var/purity_from_endurance = endurance / 400 //up to +25% for endurance
var/purity_from_instability = rand(-instability, instability) / 400 //up to +-25% at random for instability
var/result_purity = clamp(0.5 + purity_from_lifespan + purity_from_endurance + purity_from_instability, 0, 1) //50% base + stats
return result_purity
/// Setters procs ///
/**
* Adjusts seed yield up or down according to adjustamt. (Max 10)
*/
/obj/item/seeds/proc/adjust_yield(adjustamt)
if(yield == -1) // Unharvestable shouldn't suddenly turn harvestable
return
var/max_yield = MAX_PLANT_YIELD
var/min_yield = 0
for(var/datum/plant_gene/trait/trait in genes)
if(trait.trait_flags & TRAIT_HALVES_YIELD)
max_yield = round(max_yield/2)
break
if(get_gene(/datum/plant_gene/trait/plant_type/fungal_metabolism))
min_yield = FUNGAL_METAB_YIELD_MIN
yield = clamp(yield + adjustamt, min_yield, max_yield)
/**
* Adjusts seed lifespan up or down according to adjustamt. (Max 100)
*/
/obj/item/seeds/proc/adjust_lifespan(adjustamt)
lifespan = clamp(lifespan + adjustamt, 10, MAX_PLANT_LIFESPAN)
/**
* Adjusts seed endurance up or down according to adjustamt. (Max 100)
*/
/obj/item/seeds/proc/adjust_endurance(adjustamt)
endurance = clamp(endurance + adjustamt, MIN_PLANT_ENDURANCE, MAX_PLANT_ENDURANCE)
/**
* Adjusts seed production seed up or down according to adjustamt. (Max 10)
*/
/obj/item/seeds/proc/adjust_production(adjustamt)
if(yield == -1)
return
production = clamp(production + adjustamt, 1, MAX_PLANT_PRODUCTION)
/**
* Adjusts seed potency up or down according to adjustamt. (Max 100)
*/
/obj/item/seeds/proc/adjust_potency(adjustamt)
if(potency == -1)
return
potency = clamp(potency + adjustamt, 0, MAX_PLANT_POTENCY)
/**
* Adjusts seed instability up or down according to adjustamt. (Max 100)
*/
/obj/item/seeds/proc/adjust_instability(adjustamt)
if(instability == -1)
return
instability = clamp(instability + adjustamt, 0, MAX_PLANT_INSTABILITY)
/**
* Adjusts seed weed grwoth speed up or down according to adjustamt. (Max 10)
*/
/obj/item/seeds/proc/adjust_weed_rate(adjustamt)
weed_rate = clamp(weed_rate + adjustamt, 0, MAX_PLANT_WEEDRATE)
/**
* Adjusts seed weed chance up or down according to adjustamt. (Max 67%)
*/
/obj/item/seeds/proc/adjust_weed_chance(adjustamt)
weed_chance = clamp(weed_chance + adjustamt, 0, MAX_PLANT_WEEDCHANCE)
//Directly setting stats
/**
* Sets the plant's yield stat to the value of adjustamt. (Max 10, or 5 with some traits)
*/
/obj/item/seeds/proc/set_yield(adjustamt)
if(yield == -1) // Unharvestable shouldn't suddenly turn harvestable
return
var/max_yield = MAX_PLANT_YIELD
var/min_yield = 0
for(var/datum/plant_gene/trait/trait in genes)
if(trait.trait_flags & TRAIT_HALVES_YIELD)
max_yield = round(max_yield/2)
break
if(get_gene(/datum/plant_gene/trait/plant_type/fungal_metabolism))
min_yield = FUNGAL_METAB_YIELD_MIN
yield = clamp(adjustamt, min_yield, max_yield)
/**
* Sets the plant's lifespan stat to the value of adjustamt. (Max 100)
*/
/obj/item/seeds/proc/set_lifespan(adjustamt)
lifespan = clamp(adjustamt, 10, MAX_PLANT_LIFESPAN)
/**
* Sets the plant's endurance stat to the value of adjustamt. (Max 100)
*/
/obj/item/seeds/proc/set_endurance(adjustamt)
endurance = clamp(adjustamt, MIN_PLANT_ENDURANCE, MAX_PLANT_ENDURANCE)
/**
* Sets the plant's production stat to the value of adjustamt. (Max 10)
*/
/obj/item/seeds/proc/set_production(adjustamt)
if(yield == -1)
return
production = clamp(adjustamt, 1, MAX_PLANT_PRODUCTION)
/**
* Sets the plant's potency stat to the value of adjustamt. (Max 100)
*/
/obj/item/seeds/proc/set_potency(adjustamt)
if(potency == -1)
return
potency = clamp(adjustamt, 0, MAX_PLANT_POTENCY)
/**
* Sets the plant's instability stat to the value of adjustamt. (Max 100)
*/
/obj/item/seeds/proc/set_instability(adjustamt)
if(instability == -1)
return
instability = clamp(adjustamt, 0, MAX_PLANT_INSTABILITY)
/**
* Sets the plant's weed production rate to the value of adjustamt. (Max 10)
*/
/obj/item/seeds/proc/set_weed_rate(adjustamt)
weed_rate = clamp(adjustamt, 0, MAX_PLANT_WEEDRATE)
/**
* Sets the plant's weed growth percentage to the value of adjustamt. (Max 67%)
*/
/obj/item/seeds/proc/set_weed_chance(adjustamt)
weed_chance = clamp(adjustamt, 0, MAX_PLANT_WEEDCHANCE)
/**
* Override for seeds with unique text for their analyzer. (No newlines at the start or end of unique text!)
* Returns null if no unique data
* Return an assoc list (label = text) to add a new line to the analyzer
* Return an assoc list (label = list(text = tooltip, text = tooltip)) to add a new collapsible section to the analyzer
*/
/obj/item/seeds/proc/get_unique_analyzer_data()
return null
/**
* Override for seeds with special chem reactions.
*/
/obj/item/seeds/proc/on_chem_reaction(datum/reagents/reagents)
return
/obj/item/seeds/nameformat(input, user)
plantname = input
return "[LOWER_TEXT(input)]"
/obj/item/seeds/descformat(input, mob/living/user)
. = input
if(product && !productdesc)
productdesc = initial(product.desc)
var/newproductdesc = tgui_input_text(user, "Write a new product description", "Product Description", productdesc, max_length = MAX_DESC_LEN)
if(isnull(newproductdesc))
return
if(!user.can_perform_action(src))
return
productdesc = newproductdesc
/obj/item/seeds/rename_reset()
plantname = initial(plantname)
productdesc = initial(productdesc)
/obj/item/seeds/proc/randomize_stats()
set_lifespan(rand(25, 60))
set_endurance(rand(15, 35))
set_production(rand(2, 10))
set_yield(rand(1, 10))
set_potency(rand(10, 35))
set_weed_rate(rand(1, 10))
set_weed_chance(rand(5, 100))
maturation = rand(6, 12)
/obj/item/seeds/proc/add_random_reagents(lower = 0, upper = 2)
var/amount_random_reagents = rand(lower, upper)
for(var/i in 1 to amount_random_reagents)
var/random_amount = rand(4, 15) * 0.01 // this must be multiplied by 0.01, otherwise, it will not properly associate
var/datum/plant_gene/reagent/R = new(get_random_reagent_id(), random_amount)
if(R.can_add(src))
if(!R.try_upgrade_gene(src))
genes += R
else
qdel(R)
reagents_from_genes()
/obj/item/seeds/proc/add_random_traits(lower = 0, upper = 2)
var/amount_random_traits = rand(lower, upper)
for(var/i in 1 to amount_random_traits)
var/random_trait = pick(subtypesof(/datum/plant_gene/trait))
var/datum/plant_gene/trait/picked_random_trait = new random_trait
if((picked_random_trait.mutability_flags & PLANT_GENE_MUTATABLE) && picked_random_trait.can_add(src))
genes += picked_random_trait
else
qdel(picked_random_trait)
/obj/item/seeds/proc/add_random_plant_type(normal_plant_chance = 75)
if(prob(normal_plant_chance))
var/random_plant_type = pick(subtypesof(/datum/plant_gene/trait/plant_type))
var/datum/plant_gene/trait/plant_type/P = new random_plant_type
if(P.can_add(src))
genes += P
else
qdel(P)
/obj/item/seeds/proc/remove_random_reagents(lower = 0, upper = 2)
var/amount_random_reagents = rand(lower, upper)
for(var/i in 1 to amount_random_reagents)
var/datum/reagent/chemical = pick(reagents_add)
qdel(chemical)
/**
* Creates a graft from this plant.
*
* Creates a new graft from this plant.
* Passes this plant to the new graft so it can make a copy.
* Returns the created graft.
*/
/obj/item/seeds/proc/create_graft()
var/obj/item/graft/snip = new(loc, src)
return snip
/**
* Applies a graft to this plant.
*
* Adds the graft trait to this plant if possible.
* Increases plant stats by 2/3 of the grafts stats to a maximum of 100 (10 for yield).
* Returns TRUE if the graft could apply its trait successfully, FALSE if it fails to apply the trait.
* NOTE even if the graft fails to apply the trait it still adjusts the plant's stats and reagents.
*
* Arguments:
* - [snip][/obj/item/graft]: The graft being used applied to this plant.
*/
/obj/item/seeds/proc/apply_graft(obj/item/graft/snip)
var/datum/plant_gene/new_trait = snip.plant_dna.graft_gene || /datum/plant_gene/trait/repeated_harvest
new_trait = new new_trait()
if(new_trait?.can_add(src))
genes += new_trait
else
new_trait = null
// Adjust stats based on graft stats
set_lifespan(round(max(lifespan, (lifespan + (2/3)*(snip.plant_dna.lifespan - lifespan)))))
set_endurance(round(max(endurance, (endurance + (2/3)*(snip.plant_dna.endurance - endurance)))))
set_production(round(max(production, (production + (2/3)*(snip.plant_dna.production - production)))))
set_weed_rate(round(max(weed_rate, (weed_rate + (2/3)*(snip.plant_dna.weed_rate - weed_rate)))))
set_weed_chance(round(max(weed_chance, (weed_chance+ (2/3)*(snip.plant_dna.weed_chance - weed_chance)))))
set_yield(round(max(yield, (yield + (2/3)*(snip.plant_dna.yield - yield)))))
// Add in any reagents, too.
reagents_from_genes()
// Return the new trait so the tray can know we were successful and display its name.
return new_trait
/*
* Both `/item/food/grown` and `/item/grown` implement a seed variable which tracks
* plant statistics, genes, traits, etc. This proc gets the seed for either grown food or
* grown inedibles and returns it, or returns null if it's not a plant.
*
* Returns an `/obj/item/seeds` ref for grown foods or grown inedibles.
* - returned seed CAN be null in weird cases but in all applications it SHOULD NOT be.
* Returns null if it is not a plant.
*/
/obj/item/proc/get_plant_seed()
return null
/obj/item/food/grown/get_plant_seed()
return seed
/obj/item/grown/get_plant_seed()
return seed
/obj/item/seeds/proc/perform_reagent_pollination(obj/item/seeds/donor)
var/list/datum/plant_gene/reagent/valid_reagents = list()
for(var/datum/plant_gene/reagent/donor_reagent in donor.genes)
var/repeated = FALSE
for(var/datum/plant_gene/reagent/receptor_reagent in genes)
if(donor_reagent.reagent_id == receptor_reagent.reagent_id)
if(receptor_reagent.rate < donor_reagent.rate)
receptor_reagent.rate = donor_reagent.rate
// sucessful pollination/upgrade, we stop here.
reagents_from_genes()
return
else
repeated = TRUE
break
if(!repeated)
valid_reagents += donor_reagent
if(length(valid_reagents))
// pick a valid reagent that our receptor seed don't have and add the gene to it
var/datum/plant_gene/reagent/selected_reagent = pick(valid_reagents)
genes += selected_reagent.Copy()
reagents_from_genes()
/// Returns a mutable appearance to be used as an overlay for the plant in hydro trays.
/obj/item/seeds/proc/get_tray_overlay(age, status, tray_offset)
var/mutable_appearance/plant_overlay = mutable_appearance(growing_icon, layer = OBJ_LAYER + 0.01)
switch(status)
if(HYDROTRAY_PLANT_DEAD)
plant_overlay.icon_state = icon_dead
if(HYDROTRAY_PLANT_HARVESTABLE)
plant_overlay.icon_state = icon_harvest || "[icon_grow][growthstages]"
else
var/t_growthstate = clamp(round((age / maturation) * growthstages), 1, growthstages)
plant_overlay.icon_state = "[icon_grow][t_growthstate]"
plant_overlay.pixel_z = plant_icon_offset + tray_offset
return plant_overlay
/// Called when the seed is set in a tray
/obj/item/seeds/proc/on_planted(obj/machinery/hydroponics/parent)
return
/// Called when the seed is removed from a tray - possibly from being harvested, possibly from being uprooted
/obj/item/seeds/proc/on_unplanted(obj/machinery/hydroponics/parent)
return