/** * Finds and extracts seeds from an object * * Checks if the object is such that creates a seed when extracted. Used by seed * extractors or posably anything that would create seeds in some way. The seeds * are dropped either at the extractor, if it exists, or where the original object * was and it qdel's the object * * Arguments: * * O - Object containing the seed, can be the loc of the dumping of seeds * * t_max - Amount of seed copies to dump, -1 is ranomized * * extractor - Seed Extractor, used as the dumping loc for the seeds and seed multiplier * * user - checks if we can remove the object from the inventory * * */ /proc/seedify(obj/item/object, t_max, obj/machinery/seed_extractor/extractor, mob/living/user) //try to get the seed from this item var/obj/item/seeds/seed = object.get_plant_seed() if(isnull(seed)) return null //generate a random multiplier if value is not specified var/list/seeds = list() if(t_max == -1) if(extractor) t_max = rand(1,4) * extractor.seed_multiplier else t_max = rand(1,4) //drop location for the newly generated seeds var/seedloc = object.loc if(extractor) seedloc = extractor.loc //multiply the seeds and delete the item if(user && !user.temporarilyRemoveItemFromInventory(object)) //couldn't drop the item return null for(var/_ in 0 to t_max) var/obj/item/seeds/t_prod = seed.Copy() seeds.Add(t_prod) t_prod.forceMove(seedloc) qdel(object) return seeds /obj/machinery/seed_extractor name = "seed extractor" desc = "Extracts and bags seeds from produce." icon = 'icons/obj/service/hydroponics/equipment.dmi' icon_state = "sextractor" density = TRUE circuit = /obj/item/circuitboard/machine/seed_extractor /// Associated list of seeds, they are all weak refs. We check the len to see how many refs we have for each // seed var/list/piles = list() var/max_seeds = 1000 var/seed_multiplier = 1 /obj/machinery/seed_extractor/Initialize(mapload, obj/item/seeds/new_seed) . = ..() register_context() /obj/machinery/seed_extractor/add_context( atom/source, list/context, obj/item/held_item, mob/living/user, ) if(held_item?.get_plant_seed()) context[SCREENTIP_CONTEXT_LMB] = "Make seeds" context[SCREENTIP_CONTEXT_RMB] = "Make & Store seeds" return CONTEXTUAL_SCREENTIP_SET if(istype(held_item, /obj/item/storage/bag/plants) && (locate(/obj/item/seeds) in held_item.contents)) context[SCREENTIP_CONTEXT_LMB] = "Store seeds" return CONTEXTUAL_SCREENTIP_SET return NONE /obj/machinery/seed_extractor/RefreshParts() . = ..() for(var/datum/stock_part/matter_bin/matter_bin in component_parts) max_seeds = initial(max_seeds) * matter_bin.tier for(var/datum/stock_part/servo/servo in component_parts) seed_multiplier = initial(seed_multiplier) * servo.tier /obj/machinery/seed_extractor/examine(mob/user) . = ..() if(in_range(user, src) || isobserver(user)) . += span_notice("The status display reads: Extracting [seed_multiplier] to [seed_multiplier * 4] seed(s) per piece of produce.
Machine can store up to [max_seeds] seeds.") /obj/machinery/seed_extractor/wrench_act(mob/living/user, obj/item/tool) . = ..() default_unfasten_wrench(user, tool) return ITEM_INTERACT_SUCCESS /obj/machinery/seed_extractor/attackby(obj/item/attacking_item, mob/living/user, list/modifiers, list/attack_modifiers) if(!isliving(user) || user.combat_mode) return ..() if(default_deconstruction_screwdriver(user, "sextractor_open", "sextractor", attacking_item)) return TRUE if(default_pry_open(attacking_item, close_after_pry = TRUE)) return TRUE if(default_deconstruction_crowbar(attacking_item)) return TRUE if(istype(attacking_item, /obj/item/storage/bag/plants)) var/loaded = 0 for(var/obj/item/seeds/to_store in attacking_item.contents) if(contents.len >= max_seeds) to_chat(user, span_warning("[src] is full.")) break if(!add_seed(to_store, attacking_item)) continue loaded += 1 if(loaded) to_chat(user, span_notice("You put as many seeds from [attacking_item] into [src] as you can.")) else to_chat(user, span_warning("There are no seeds in [attacking_item].")) return TRUE var/list/generated_seeds = seedify(attacking_item, -1, src, user) if(!isnull(generated_seeds)) if(LAZYACCESS(modifiers, RIGHT_CLICK)) //find all seeds lying on the turf and add them to the machine for(var/obj/item/seeds/seed as anything in generated_seeds) //machine is full if(contents.len >= max_seeds) to_chat(user, span_warning("[src] is full.")) break //add seed to machine. second argument is null which means just force move into the machine add_seed(seed) to_chat(user, span_notice("You extract some seeds.")) return TRUE else if(istype(attacking_item, /obj/item/seeds)) if(contents.len >= max_seeds) to_chat(user, span_warning("[src] is full.")) else if(add_seed(attacking_item, user)) to_chat(user, span_notice("You add [attacking_item] to [src].")) else to_chat(user, span_warning("You can't seem to add [attacking_item] to [src].")) return TRUE else if(!attacking_item.tool_behaviour) // Using the wrong tool shouldn't assume you want to turn it into seeds. to_chat(user, span_warning("You can't extract any seeds from [attacking_item]!")) return TRUE return ..() /** * Generate seed string * * Creates a string based of the traits of a seed. We use this string as a bucket for all * seeds that match as well as the key the ui uses to get the seed. We also use the key * for the data shown in the ui. Javascript parses this string to display * * Arguments: * * O - seed to generate the string from */ /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. * * Adds the seeds to the contents and to an associated list that pregenerates the data * needed to go to the ui handler * * to_add - what seed are we adding? * taking_from - where are we taking the seed from? A mob, a bag, etc? If null its means it's just laying on the turf so force move it in **/ /obj/machinery/seed_extractor/proc/add_seed(obj/item/seeds/to_add, atom/taking_from) var/seed_id = generate_seed_hash(to_add) var/list/seed_data var/has_seed_data // so we remember to add a seed obj weakref to piles[seed_id] at the end of the proc. That way if some reason we runtime in this proc it won't incorrectly add data to the list if(piles[seed_id]) has_seed_data = TRUE else 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 )) var/datum/plant_gene/trait/maxchem/volume_trait = locate(/datum/plant_gene/trait/maxchem) in to_add.genes var/datum/plant_gene/trait/modified_volume/volume_unit_trait = locate(/datum/plant_gene/trait/modified_volume) in to_add.genes seed_data["volume_mod"] = volume_trait ? volume_trait.rate : 1 seed_data["volume_units"] = volume_unit_trait ? volume_unit_trait.new_capcity : PLANT_REAGENT_VOLUME seed_data["mutatelist"] = list() for(var/obj/item/seeds/mutant as anything in to_add.mutatelist) seed_data["mutatelist"] += initial(mutant.plantname) if(to_add.product) var/obj/item/food/grown/product = new to_add.product var/datum/reagent/product_distill_reagent = product.distill_reagent seed_data["distill_reagent"] = initial(product_distill_reagent.name) var/datum/reagent/product_juice_typepath = product.juice_typepath seed_data["juice_name"] = initial(product_juice_typepath.name) seed_data["grind_results"] = list() for(var/datum/reagent/reagent as anything in product.grind_results) seed_data["grind_results"] += initial(reagent.name) qdel(product) if(!isnull(taking_from)) if(ismob(taking_from)) var/mob/mob_loc = taking_from if(!mob_loc.transferItemToLoc(to_add, src)) return FALSE else if(!taking_from.atom_storage?.attempt_remove(to_add, src, silent = TRUE)) return FALSE else to_add.forceMove(src) // do this at the end, in case any of the previous steps failed if(has_seed_data) piles[seed_id]["refs"] += WEAKREF(to_add) else piles[seed_id] = seed_data return TRUE /obj/machinery/seed_extractor/ui_state(mob/user) return GLOB.notcontained_state /obj/machinery/seed_extractor/ui_interact(mob/user, datum/tgui/ui) ui = SStgui.try_update_ui(user, src, ui) if(!ui) ui = new(user, src, "SeedExtractor", name) ui.open() /obj/machinery/seed_extractor/ui_data() 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"] = 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/datum/plant_gene/trait as anything in GLOB.plant_traits) var/trait_data = list(list( "path" = trait.type, "name" = trait.get_name(), "icon" = trait.icon, "description" = trait.description )) data["trait_db"] += trait_data return data /obj/machinery/seed_extractor/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state) . = ..() if(.) return switch(action) 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]["refs"][1] var/obj/item/seeds/found_seed = found_seed_weakref.resolve() if(!found_seed) return piles[item]["refs"] -= found_seed_weakref if(usr) var/mob/user = usr if(user.put_in_hands(found_seed)) to_chat(user, span_notice("You take [found_seed] out of the slot.")) else to_chat(user, span_notice("[found_seed] falls onto the floor.")) else 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_batched/seeds) )