Files
Bubberstation/code/modules/hydroponics/grown.dm
SyncIt21 e4029ed9cd More code compression for reagent holder (#79796)
## About The Pull Request
Part 2 of #79686 where we trim down the size of `holder.dm` even further
and in the process give some procs more advanced features as they get
merged with their counterparts.

**1. Removes & merges `get_multiple_reagent_amounts()` proc with
`get_reagent_amount()`**
The proc `get_multiple_reagent_amounts()` was only used by bio generator
and 1 other item with its only use being finding the sum of all reagents
present in the list returned by
`typesof(datum/reagent/consumable/nutrient)`. Currently the approach is
very inefficient because.
- `typesof()` is an expensive call which returns a long list of reagents
- `get_multiple_reagent_amounts()` would then use 2 nested for loops.
One to loop over every reagent in this holder & another inner for loop
to loop over every reagent returned by `typesof()` operator so the time
complexity of this proc is overall multiplicative which in lamen terms
means "Bad"

We can replicate the same behaviour of `typesof()` by using the
`type2parent()` proc and 1 more direct type check to get the exact same
behaviour but with much faster results, therefore reducing overall code

**2. Removes & merges `get_reagent()` proc with `has_reagent()`**
The proc `has_reagent()` is way more advanced than `get_reagent()` with
arguments requesting for a specific amount, metabolization and now even
has a new argument i.e. `chemical flag`. `has_reagent()` has always
returned the reagent reference directly and not a simple TRUE/FALSE so
it is a perfect replacement for `get_reagent()`, therefore reducing
overall code

**3. Removes & merges `has_chemical_flag()` proc with `has_reagent()`**
The proc `has_reagent()` can now look for a specific reagent with a
specific chemical flag as well as mentioned above thus it can replace
`has_chemical_flag()` therefore reducing overall code


## Changelog
🆑
code: Removes & merges `get_multiple_reagent_amounts()` proc with
`get_reagent_amount()` inside reagent holder
code: Removes & merges `get_reagent()` proc with `has_reagent()` inside
reagent holder
code: Removes & merges `has_chemical_flag()` proc with `has_reagent()`
inside reagent holder
refactor: Reagent holder code has been further compressed. Report bugs
on github
/🆑
2023-11-19 10:40:09 -08:00

161 lines
7.1 KiB
Plaintext

// ***********************************************************
// Foods that are produced from hydroponics ~~~~~~~~~~
// Data from the seeds carry over to these grown foods
// ***********************************************************
// A few defines for use in calculating our plant's bite size.
/// When calculating bite size, potency is multiplied by this number.
#define BITE_SIZE_POTENCY_MULTIPLIER 0.05
/// When calculating bite size, max_volume is multiplied by this number.
#define BITE_SIZE_VOLUME_MULTIPLIER 0.01
// Base type. Subtypes are found in /grown dir. Lavaland-based subtypes can be found in mining/ash_flora.dm
/obj/item/food/grown
icon = 'icons/obj/service/hydroponics/harvest.dmi'
icon_state = "berrypile"
worn_icon = 'icons/mob/clothing/head/hydroponics.dmi'
name = "fresh produce" // so recipe text doesn't say 'snack'
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.
var/obj/item/seeds/seed = null
///Name of the plant
var/plantname = ""
/// The modifier applied to the plant's bite size. If a plant has a large amount of reagents naturally, this should be increased to match.
var/bite_consumption_mod = 1
/// The typepath made when the plant is splatted with liquid contents.
var/splat_type = /obj/effect/decal/cleanable/food/plant_smudge
/// If TRUE, this object needs to be dry to be ground up
var/dry_grind = FALSE
/// If FALSE, this object cannot be distilled into an alcohol.
var/can_distill = TRUE
/// The reagent this plant distill to. If NULL, it uses a generic fruit_wine reagent and adjusts its variables.
var/distill_reagent
/// Flavor of the plant's wine if NULL distll_reagent. If NULL, this is automatically set to the fruit's flavor.
var/wine_flavor
/// Boozepwr of the wine if NULL distill_reagent
var/wine_power = 10
/// Color of the grown object, for use in coloring greyscale splats.
var/filling_color
/// If the grown food has an alternative icon state to use in places.
var/alt_icon
/// Should we pixel offset ourselves at init? for mapping
var/offset_at_init = TRUE
/obj/item/food/grown/New(loc, obj/item/seeds/new_seed)
return ..()
/obj/item/food/grown/Initialize(mapload, obj/item/seeds/new_seed)
if(!tastes)
tastes = list("[name]" = 1) //This happens first else the component already inits
if(istype(new_seed))
seed = new_seed.Copy()
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.adjust_potency(50-seed.potency)
else if(!seed)
stack_trace("Grown object created without a seed. WTF")
return INITIALIZE_HINT_QDEL
if(offset_at_init)
pixel_x = base_pixel_x + rand(-5, 5)
pixel_y = base_pixel_y + rand(-5, 5)
make_dryable()
// Go through all traits in their genes and call on_new_plant from them.
for(var/datum/plant_gene/trait/trait in seed.genes)
trait.on_new_plant(src, loc)
// Set our default bitesize: bite size = 1 + (potency * 0.05) * (max_volume * 0.01) * modifier
// A 100 potency, non-densified plant = 1 + (5 * 1 * modifier) = 6u bite size
// For reference, your average 100 potency tomato has 14u of reagents - So, with no modifier it is eaten in 3 bites
bite_consumption = 1 + round(max((seed.potency * BITE_SIZE_POTENCY_MULTIPLIER), 1) * (max_volume * BITE_SIZE_VOLUME_MULTIPLIER) * bite_consumption_mod)
. = ..() //Only call it here because we want all the genes and shit to be applied before we add edibility. God this code is a mess.
reagents.clear_reagents()
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!
/obj/item/food/grown/Destroy()
if(isatom(seed))
QDEL_NULL(seed)
return ..()
/obj/item/food/grown/proc/make_dryable()
AddElement(/datum/element/dryable, type)
/obj/item/food/grown/make_leave_trash()
if(trash_type)
AddElement(/datum/element/food_trash, trash_type, FOOD_TRASH_OPENABLE, TYPE_PROC_REF(/obj/item/food/grown/, generate_trash))
return
/// Generates a piece of trash based on our plant item. Used by [/datum/element/food_trash].
/// location - Optional. If passed, generates the item at the passed location instead of at src's drop location.
/obj/item/food/grown/proc/generate_trash(atom/location)
// If this is some type of grown thing, we pass a seed arg into its Inititalize()
if(ispath(trash_type, /obj/item/grown) || ispath(trash_type, /obj/item/food/grown))
return new trash_type(location || drop_location(), seed)
return new trash_type(location || drop_location())
/obj/item/food/grown/grind_requirements()
if(dry_grind && !HAS_TRAIT(src, TRAIT_DRIED))
to_chat(usr, span_warning("[src] needs to be dry before it can be ground up!"))
return
return TRUE
/// Turns the nutriments and vitamins into the distill reagent or fruit wine
/obj/item/food/grown/proc/ferment()
var/reagent_purity = seed.get_reagent_purity()
var/purity_above_base = clamp((reagent_purity - 0.5) * 2, 0, 1)
var/quality_min = DRINK_NICE
var/quality_max = DRINK_FANTASTIC
var/quality = round(LERP(quality_min, quality_max, purity_above_base))
for(var/datum/reagent/reagent in reagents.reagent_list)
if(!istype(reagent, /datum/reagent/consumable))
continue
if(distill_reagent)
var/data = list()
var/datum/reagent/consumable/ethanol/booze = distill_reagent
data["quality"] = quality
data["boozepwr"] = round(initial(booze.boozepwr) * reagent_purity * 2) // default boozepwr at 50% purity
reagents.add_reagent(distill_reagent, reagent.volume, data, added_purity = reagent_purity)
else
var/data = list()
data["names"] = list("[initial(name)]" = 1)
data["color"] = filling_color || reagent.color // filling_color is not guaranteed to be set for every plant. try to use it if we have it, otherwise use the reagent's color var
data["boozepwr"] = round(wine_power * reagent_purity * 2) // default boozepwr at 50% purity
data["quality"] = quality
if(wine_flavor)
data["tastes"] = list(wine_flavor = 1)
else
data["tastes"] = list(tastes[1] = 1)
reagents.add_reagent(/datum/reagent/consumable/ethanol/fruit_wine, reagent.volume, data, added_purity = reagent_purity)
reagents.del_reagent(reagent.type)
/obj/item/food/grown/grind(datum/reagents/target_holder, mob/user)
if(on_grind() == -1)
return FALSE
var/grind_results_num = LAZYLEN(grind_results)
if(grind_results_num)
var/average_purity = reagents.get_average_purity()
var/total_nutriment_amount = reagents.get_reagent_amount(/datum/reagent/consumable/nutriment, type_check = REAGENT_SUB_TYPE)
var/single_reagent_amount = grind_results_num > 1 ? round(total_nutriment_amount / grind_results_num, CHEMICAL_QUANTISATION_LEVEL) : total_nutriment_amount
reagents.remove_reagent(/datum/reagent/consumable/nutriment, total_nutriment_amount, include_subtypes = TRUE)
for(var/reagent in grind_results)
reagents.add_reagent(reagent, single_reagent_amount, added_purity = average_purity)
if(reagents && target_holder)
reagents.trans_to(target_holder, reagents.total_volume, transferred_by = user)
return TRUE
#undef BITE_SIZE_POTENCY_MULTIPLIER
#undef BITE_SIZE_VOLUME_MULTIPLIER