Files
Paradise/code/modules/hydroponics/grown.dm
Charlie Nolan 3c043374d1 Botany rework V2 (#24342)
* Many botany QoL changes.

* Give observers a plant analyzer.

* Hooks should be executable.

* Completely new botany mutation mechanics.

* Minor fixes, changed vendor contents.

* Add charcoal to NutriMax extended inventory.

* Lint.

* Reset somatoray beam effects in a few more places.

* Mapped in seed sorting trays and mutrient.

* Slimmed down the seed sorting tray.

* Update unsorted seeds with analyzer, ghost-analyzer, and HUD support.

* Make english_list null-safe (and slightly faster).

* Fixed a mutation focus bug, added plant anayzer attackby to unsorted seeds.

* Made self-sufficient trays display their nutrient as Earthsblood.

* Improved descriptions and on-use texts.

* Added 5 dropers and 3 seed sorting trays to NutriMax.

* Mark stuff as notices, special case for duplicate doping.

* New way to get kudzu, QoL changes.

* Yield somatoray adds a message to the yield stat.
* Unsorted seeds produce a message when you try to put them in a tray or
  machine.
* Potassium iodide and pentetic acid will now neutralize the tray's
  mutagen.
* Using a plant bag on a seed extractor will now process plants into
  seeds if there are no seeds in it.

* Bonk.

* Don't try to extract no seeds.

* Butterfingers.

* Bonk more.

* Add seed sorting trays to perma.

* Upgraded Seed Extractor GUI.

* Do not drop that which you do not extract.

* Don't bug out when removing the last seed.

* You want a tgui rebuild? Have a tgui rebuild.

* Drop trait-adding threshold by 10.

Before:
Needs modified mutation level >50.
With the x2 from 9+ seeds, this happens at base mutation level 25.
With maximum mutation level of 35 (L4Z+mutagen+somatoray), chance was
20%.
Required either L4Z (yield set to 1), or a somatoray (external help) for
any chance.

After:
Needs modified mutation level >40.
With the x2 from 9+ seeds, this happens at base mutation level 20.
With maximum mutation level of 35 (L4Z+mutagen+somatoray), chance is
30%.
Can get 10% chance with mutation level 25 (Mut+mutagen).

* Renamed mutagen level -> mutagen tank

* Left-align seed extractor, more efficient icon sending.

* New sprite thanks to McRamon.

* tgui rebuild

---------

Co-authored-by: FunnyMan3595 (Charlie Nolan) <funnyman@google.com>
2024-04-15 20:29:49 +00:00

235 lines
7.8 KiB
Plaintext

// ***********************************************************
// Foods that are produced from hydroponics ~~~~~~~~~~
// Data from the seeds carry over to these grown foods
// ***********************************************************
// Base type. Subtypes are found in /grown dir.
/obj/item/food/snacks/grown
icon = 'icons/obj/hydroponics/harvest.dmi'
/// The seed of this plant. Starts as a type path, gets converted to an item on New()
var/obj/item/seeds/seed = null
/// The unsorted seed of this plant, if any. Used by the seed extractor.
var/obj/item/unsorted_seeds/unsorted_seed = null
var/plantname = ""
var/bitesize_mod = 0 // If set, bitesize = 1 + round(reagents.total_volume / bitesize_mod)
var/splat_type = /obj/effect/decal/cleanable/plant_smudge
var/can_distill = TRUE //If FALSE, this object cannot be distilled into an alcohol.
var/distill_reagent //If NULL and this object can be distilled, it uses a generic fruit_wine reagent and adjusts its variables.
var/wine_flavor //If NULL, this is automatically set to the fruit's flavor. Determines the flavor of the wine if distill_reagent is NULL.
var/wine_power = 0.1 //Determines the boozepwr of the wine if distill_reagent is NULL. Uses 0.1 - 1.2 not tg's boozepower (divide by 100) else you'll end up with 1000% proof alcohol!
dried_type = -1 // Saves us from having to define each stupid grown's dried_type as itself. If you don't want a plant to be driable (watermelons) set this to null in the time definition.
resistance_flags = FLAMMABLE
origin_tech = "biotech=1"
/obj/item/food/snacks/grown/Initialize(mapload, obj/new_seed = null)
. = ..()
if(!tastes)
tastes = list("[name]" = 1)
if(istype(new_seed, /obj/item/seeds))
var/obj/item/seeds/S = new_seed
seed = S.Copy()
else if(istype(new_seed, /obj/item/unsorted_seeds))
var/obj/item/unsorted_seeds/S = new_seed
unsorted_seed = S.Copy()
seed = S.seed_data.original_seed.Copy()
else if(seed)
seed = new seed
pixel_x = rand(-5, 5)
pixel_y = rand(-5, 5)
if(dried_type == -1)
dried_type = type
if(seed)
for(var/datum/plant_gene/trait/T in seed.genes)
T.on_new(src)
seed.prepare_result(src)
transform *= TRANSFORM_USING_VARIABLE(seed.potency, 100) + 0.5 //Makes the resulting produce's sprite larger or smaller based on potency!
add_juice()
if(seed.variant)
name += " \[[seed.variant]]"
/obj/item/food/snacks/grown/Destroy()
QDEL_NULL(seed)
return ..()
/obj/item/food/snacks/grown/proc/add_juice()
if(reagents)
if(bitesize_mod)
bitesize = 1 + round(reagents.total_volume / bitesize_mod)
return 1
return 0
/obj/item/food/snacks/grown/examine(user)
. = ..()
if(seed)
for(var/datum/plant_gene/trait/T in seed.genes)
if(T.examine_line)
. += T.examine_line
/obj/item/food/snacks/grown/attackby(obj/item/O, mob/user, params)
..()
if(slices_num && slice_path)
var/inaccurate = TRUE
if(O.sharp)
if(istype(O, /obj/item/kitchen/knife) || istype(O, /obj/item/scalpel))
inaccurate = FALSE
if(!isturf(loc) || !(locate(/obj/structure/table) in loc) && !(locate(/obj/machinery/optable) in loc) && !(locate(/obj/item/storage/bag/tray) in loc))
to_chat(user, "<span class='warning'>You cannot slice [src] here! You need a table or at least a tray to do it.</span>")
return TRUE
var/slices_lost = 0
if(!inaccurate)
user.visible_message("<span class='notice'>[user] slices [src] with [O]!</span>", "<span class='notice'>You slice [src]!</span>")
else
user.visible_message("<span class='notice'>[user] crudely slices [src] with [O]!</span>", "<span class='notice'>You crudely slice [src] with your [O]!</span>")
slices_lost = rand(1, min(1, round(slices_num / 2)))
var/reagents_per_slice = reagents.total_volume/slices_num
for(var/i = 1 to (slices_num - slices_lost))
var/obj/slice = new slice_path (loc)
reagents.trans_to(slice, reagents_per_slice)
slice.pixel_x = rand(-7, 7)
slice.pixel_y = rand(-7, 7)
qdel(src)
return ..()
if(istype(O, /obj/item/plant_analyzer))
send_plant_details(user)
else
if(seed)
for(var/datum/plant_gene/trait/T in seed.genes)
T.on_attackby(src, O, user)
// Various gene procs
/obj/item/food/snacks/grown/attack_self(mob/user)
if(seed && seed.get_gene(/datum/plant_gene/trait/squash))
squash(user)
..()
/obj/item/food/snacks/grown/throw_impact(atom/hit_atom)
if(!..()) //was it caught by a mob?
if(seed)
log_action(locateUID(thrownby), hit_atom, "Thrown [src] at")
for(var/datum/plant_gene/trait/T in seed.genes)
T.on_throw_impact(src, hit_atom)
if(seed.get_gene(/datum/plant_gene/trait/squash))
squash(hit_atom)
/obj/item/food/snacks/grown/proc/squash(atom/target)
var/turf/T = get_turf(target)
if(ispath(splat_type, /obj/effect/decal/cleanable/plant_smudge))
if(filling_color)
var/obj/O = new splat_type(T)
O.color = filling_color
O.name = "[name] smudge"
else if(splat_type)
new splat_type(T)
if(trash)
generate_trash(T)
visible_message("<span class='warning'>[src] has been squashed.</span>","<span class='italics'>You hear a smack.</span>")
if(seed)
for(var/datum/plant_gene/trait/trait in seed.genes)
trait.on_squash(src, target)
reagents.reaction(T)
for(var/A in T)
if(reagents)
reagents.reaction(A)
qdel(src)
/obj/item/food/snacks/grown/On_Consume(mob/M, mob/user)
if(iscarbon(M))
if(seed)
for(var/datum/plant_gene/trait/T in seed.genes)
T.on_consume(src, M)
..()
/obj/item/food/snacks/grown/after_slip(mob/living/carbon/human/H)
if(!seed)
return
for(var/datum/plant_gene/trait/T in seed.genes)
T.on_slip(src, H)
// Glow gene procs
/obj/item/food/snacks/grown/generate_trash(atom/location)
if(trash && ispath(trash, /obj/item/grown))
. = new trash(location, seed)
trash = null
return
return ..()
/obj/item/food/snacks/grown/decompile_act(obj/item/matter_decompiler/C, mob/user)
if(isdrone(user))
C.stored_comms["wood"] += 4
qdel(src)
return TRUE
return ..()
// For item-containing growns such as eggy or gatfruit
/obj/item/food/snacks/grown/shell/attack_self(mob/user)
if(!do_after(user, 1.5 SECONDS, target = user))
return
user.unEquip(src)
if(trash)
var/obj/item/T = generate_trash()
user.put_in_hands(T)
to_chat(user, "<span class='notice'>You open [src]\'s shell, revealing \a [T].</span>")
qdel(src)
// Diona Nymphs can eat these as well as weeds to gain nutrition.
/obj/item/food/snacks/grown/attack_animal(mob/living/simple_animal/M)
if(isnymph(M))
var/mob/living/simple_animal/diona/D = M
D.consume(src)
else
return ..()
/obj/item/food/snacks/grown/proc/log_action(mob/user, atom/target, what_done)
var/reagent_str = reagents.log_list()
var/genes_str = "No genes"
if(seed && length(seed.genes))
var/list/plant_gene_names = list()
for(var/thing in seed.genes)
var/datum/plant_gene/G = thing
if(G.dangerous)
plant_gene_names += G.name
genes_str = english_list(plant_gene_names)
add_attack_logs(user, target, "[what_done] ([reagent_str] | [genes_str])")
/obj/item/food/snacks/grown/extinguish_light(force = FALSE)
if(!force)
return
if(seed.get_gene(/datum/plant_gene/trait/glow/shadow))
return
set_light(0)
/obj/item/food/snacks/grown/proc/send_plant_details(mob/user)
var/msg = "<span class='info'>This is \a </span><span class='name'>[src].</span>\n"
if(seed)
msg += seed.get_analyzer_text()
var/reag_txt = ""
if(seed)
for(var/reagent_id in seed.reagents_add)
var/datum/reagent/R = GLOB.chemical_reagents_list[reagent_id]
var/amt = reagents.get_reagent_amount(reagent_id)
reag_txt += "\n<span class='info'>- [R.name]: [amt]</span>"
if(reag_txt)
msg += reag_txt
to_chat(user, msg)
/obj/item/food/snacks/grown/attack_ghost(mob/dead/observer/user)
if(!istype(user)) // Make sure user is actually an observer. Revenents also use attack_ghost, but do not have the toggle plant analyzer var.
return
if(user.plant_analyzer)
send_plant_details(user)