cleans up so many to_chats so they use vchat filters, unsorted chat filter for everything else

This commit is contained in:
Seris02
2023-03-08 23:26:14 -05:00
committed by VirgoBot
parent 115b8a933b
commit dd1e58acc7
63 changed files with 1112 additions and 409 deletions
+29 -1
View File
@@ -210,7 +210,11 @@
if(G.get_amount() >= G.max_amount)
continue
G.attackby(NG, user)
<<<<<<< HEAD
to_chat(user, "You add the newly-formed wood to the stack. It now contains [NG.get_amount()] planks.")
=======
to_chat(user, "<span class='filter_notice'>You add the newly-formed wood to the stack. It now contains [NG.amount] planks.</span>")
>>>>>>> 75577bd3ca9... cleans up so many to_chats so they use vchat filters, unsorted chat filter for everything else (#9006)
qdel(src)
return
@@ -221,31 +225,51 @@
return
if(seed.kitchen_tag == "potato" || !isnull(seed.chems["potato"]))
<<<<<<< HEAD
to_chat(user, "You slice \the [src] into sticks.")
new /obj/item/weapon/reagent_containers/food/snacks/rawsticks(get_turf(src))
=======
to_chat(user, "<span class='filter_notice'>You slice \the [src] into sticks.</span>")
new /obj/item/reagent_containers/food/snacks/rawsticks(get_turf(src))
>>>>>>> 75577bd3ca9... cleans up so many to_chats so they use vchat filters, unsorted chat filter for everything else (#9006)
qdel(src)
return
if(!isnull(seed.chems["carrotjuice"]))
<<<<<<< HEAD
to_chat(user, "You slice \the [src] into sticks.")
new /obj/item/weapon/reagent_containers/food/snacks/carrotfries(get_turf(src))
=======
to_chat(user, "<span class='filter_notice'>You slice \the [src] into sticks.</span>")
new /obj/item/reagent_containers/food/snacks/carrotfries(get_turf(src))
>>>>>>> 75577bd3ca9... cleans up so many to_chats so they use vchat filters, unsorted chat filter for everything else (#9006)
qdel(src)
return
if(!isnull(seed.chems["pineapplejuice"]))
<<<<<<< HEAD
to_chat(user, "You slice \the [src] into rings.")
new /obj/item/weapon/reagent_containers/food/snacks/pineapple_ring(get_turf(src))
=======
to_chat(user, "<span class='filter_notice'>You slice \the [src] into rings.</span>")
new /obj/item/reagent_containers/food/snacks/pineapple_ring(get_turf(src))
>>>>>>> 75577bd3ca9... cleans up so many to_chats so they use vchat filters, unsorted chat filter for everything else (#9006)
qdel(src)
return
if(!isnull(seed.chems["soymilk"]))
<<<<<<< HEAD
to_chat(user, "You roughly chop up \the [src].")
new /obj/item/weapon/reagent_containers/food/snacks/soydope(get_turf(src))
=======
to_chat(user, "<span class='filter_notice'>You roughly chop up \the [src].</span>")
new /obj/item/reagent_containers/food/snacks/soydope(get_turf(src))
>>>>>>> 75577bd3ca9... cleans up so many to_chats so they use vchat filters, unsorted chat filter for everything else (#9006)
qdel(src)
return
if(seed.get_trait(TRAIT_FLESH_COLOUR))
to_chat(user, "You slice up \the [src].")
to_chat(user, "<span class='filter_notice'>You slice up \the [src].</span>")
var/slices = rand(3,5)
var/reagents_to_transfer = round(reagents.total_volume/slices)
for(var/i=1; i<=slices; i++)
@@ -316,7 +340,11 @@
if(NG.get_amount() >= NG.max_amount)
continue
NG.attackby(G, user)
<<<<<<< HEAD
to_chat(user, "You add the newly-formed carpet to the stack. It now contains [G.get_amount()] tiles.")
=======
to_chat(user, "<span class='filter_notice'>You add the newly-formed grass to the stack. It now contains [G.amount] tiles.</span>")
>>>>>>> 75577bd3ca9... cleans up so many to_chats so they use vchat filters, unsorted chat filter for everything else (#9006)
qdel(src)
return
+1 -1
View File
@@ -792,7 +792,7 @@
to_chat(user, "<span class='danger'>You fail to harvest anything useful.</span>")
else
if(istype(user))
to_chat(user, "You [harvest_sample ? "take a sample" : "harvest"] from the [display_name].")
to_chat(user, "<span class='filter_notice'>You [harvest_sample ? "take a sample" : "harvest"] from the [display_name].</span>")
//This may be a new line. Update the global if it is.
if(name == "new line" || !(name in SSplants.seeds))
+168
View File
@@ -0,0 +1,168 @@
// Attempts to offload processing for the spreading plants from the MC.
// Processes vines/spreading plants.
#define PLANTS_PER_TICK 500 // Cap on number of plant segments processed.
#define PLANT_TICK_TIME 75 // Number of ticks between the plant processor cycling.
// Debug for testing seed genes.
/client/proc/show_plant_genes()
set category = "Debug"
set name = "Show Plant Genes"
set desc = "Prints the round's plant gene masks."
if(!holder) return
if(!plant_controller || !plant_controller.gene_tag_masks)
to_chat(usr, "<span class='filter_notice'>Gene masks not set.</span>")
return
for(var/mask in plant_controller.gene_tag_masks)
to_chat(usr, "[mask]: [plant_controller.gene_tag_masks[mask]]")
var/global/datum/controller/plants/plant_controller // Set in New().
/datum/controller/plants
var/plants_per_tick = PLANTS_PER_TICK
var/plant_tick_time = PLANT_TICK_TIME
var/list/product_descs = list() // Stores generated fruit descs.
var/list/plant_queue = list() // All queued plants.
var/list/seeds = list() // All seed data stored here.
var/list/gene_tag_masks = list() // Gene obfuscation for delicious trial and error goodness.
var/list/plant_icon_cache = list() // Stores images of growth, fruits and seeds.
var/list/plant_sprites = list() // List of all growth sprites plus number of growth stages.
var/list/accessible_plant_sprites = list() // List of all plant sprites allowed to appear in random generation.
var/list/plant_product_sprites = list() // List of all harvested product sprites.
var/list/accessible_product_sprites = list() // List of all product sprites allowed to appear in random generation.
var/processing = 0 // Off/on.
var/list/gene_masked_list = list() // Stored gene masked list, rather than recreating it when needed.
var/list/plant_gene_datums = list() // Stored datum versions of the gene masked list.
/datum/controller/plants/New()
if(plant_controller && plant_controller != src)
log_debug("Rebuilding plant controller.")
qdel(plant_controller)
plant_controller = src
setup()
process()
// Predefined/roundstart varieties use a string key to make it
// easier to grab the new variety when mutating. Post-roundstart
// and mutant varieties use their uid converted to a string instead.
// Looks like shit but it's sort of necessary.
/datum/controller/plants/proc/setup()
// Build the icon lists.
for(var/icostate in cached_icon_states('icons/obj/hydroponics_growing.dmi'))
var/split = findtext(icostate,"-")
if(!split)
// invalid icon_state
continue
var/ikey = copytext(icostate,(split+1))
if(ikey == "dead")
// don't count dead icons
continue
ikey = text2num(ikey)
var/base = copytext(icostate,1,split)
if(!(plant_sprites[base]) || (plant_sprites[base]<ikey))
plant_sprites[base] = ikey
if(!(base in GLOB.forbidden_plant_growth_sprites))
accessible_plant_sprites[base] = ikey
for(var/icostate in cached_icon_states('icons/obj/hydroponics_products.dmi'))
var/split = findtext(icostate,"-")
var/base = copytext(icostate,1,split)
if(split)
plant_product_sprites |= base
if(!(base in GLOB.forbidden_plant_product_sprites))
accessible_product_sprites |= base
// Populate the global seed datum list.
for(var/type in typesof(/datum/seed)-/datum/seed)
var/datum/seed/S = new type
seeds[S.name] = S
S.uid = "[seeds.len]"
S.roundstart = 1
// Make sure any seed packets that were mapped in are updated
// correctly (since the seed datums did not exist a tick ago).
for(var/obj/item/seeds/S in all_seed_packs)
S.update_seed()
//Might as well mask the gene types while we're at it.
var/list/gene_datums = decls_repository.get_decls_of_subtype(/decl/plantgene)
var/list/used_masks = list()
var/list/plant_traits = ALL_GENES
while(plant_traits && plant_traits.len)
var/gene_tag = pick(plant_traits)
var/gene_mask
do
gene_mask = random_hex_text(2, TRUE)
while (gene_mask in used_masks)
var/decl/plantgene/G
for(var/D in gene_datums)
var/decl/plantgene/P = gene_datums[D]
if(gene_tag == P.gene_tag)
G = P
gene_datums -= D
used_masks += gene_mask
plant_traits -= gene_tag
gene_tag_masks[gene_tag] = gene_mask
plant_gene_datums[gene_mask] = G
gene_masked_list.Add(list(list("tag" = gene_tag, "mask" = gene_mask)))
// Proc for creating a random seed type.
/datum/controller/plants/proc/create_random_seed(var/survive_on_station)
var/datum/seed/seed = new()
seed.randomize()
seed.uid = plant_controller.seeds.len + 1
seed.name = "[seed.uid]"
seeds[seed.name] = seed
if(survive_on_station)
if(seed.consume_gasses)
seed.consume_gasses["phoron"] = null
seed.consume_gasses["carbon_dioxide"] = null
if(seed.chems && !isnull(seed.chems["pacid"]))
seed.chems["pacid"] = null // Eating through the hull will make these plants completely inviable, albeit very dangerous.
seed.chems -= null // Setting to null does not actually remove the entry, which is weird.
seed.set_trait(TRAIT_IDEAL_HEAT,293)
seed.set_trait(TRAIT_HEAT_TOLERANCE,20)
seed.set_trait(TRAIT_IDEAL_LIGHT,8)
seed.set_trait(TRAIT_LIGHT_TOLERANCE,5)
seed.set_trait(TRAIT_LOWKPA_TOLERANCE,25)
seed.set_trait(TRAIT_HIGHKPA_TOLERANCE,200)
return seed
/datum/controller/plants/process()
processing = 1
spawn(0)
set background = 1
var/processed = 0
while(1)
if(!processing)
sleep(plant_tick_time)
else
processed = 0
if(plant_queue.len)
var/target_to_process = min(plant_queue.len,plants_per_tick)
for(var/x=0;x<target_to_process;x++)
if(!plant_queue.len)
break
var/obj/effect/plant/plant = pick(plant_queue)
plant_queue -= plant
if(!istype(plant))
continue
plant.process()
processed++
sleep(1) // Stagger processing out so previous tick can resolve (overlapping plant segments etc)
sleep(max(1,(plant_tick_time-processed)))
/datum/controller/plants/proc/add_plant(var/obj/effect/plant/plant)
plant_queue |= plant
/datum/controller/plants/proc/remove_plant(var/obj/effect/plant/plant)
plant_queue -= plant
+57 -8
View File
@@ -17,7 +17,7 @@
if(genes.len)
var/choice = tgui_alert(user, "Are you sure you want to wipe the disk?", "Xenobotany Data", list("No", "Yes"))
if(src && user && genes && choice && choice == "Yes" && user.Adjacent(get_turf(src)))
to_chat(user, "You wipe the disk data.")
to_chat(user, "<span class='filter_notice'>You wipe the disk data.</span>")
name = initial(name)
desc = initial(name)
genes = list()
@@ -68,30 +68,40 @@
active = 0
if(failed_task)
failed_task = 0
<<<<<<< HEAD
visible_message("\icon[src][bicon(src)] [src] pings unhappily, flashing a red warning light.")
else
visible_message("\icon[src][bicon(src)] [src] pings happily.")
=======
visible_message("<span class='filter_notice'>[bicon(src)] [src] pings unhappily, flashing a red warning light.</span>")
else
visible_message("<span class='filter_notice'>[bicon(src)] [src] pings happily.</span>")
>>>>>>> 75577bd3ca9... cleans up so many to_chats so they use vchat filters, unsorted chat filter for everything else (#9006)
if(eject_disk)
eject_disk = 0
if(loaded_disk)
loaded_disk.loc = get_turf(src)
<<<<<<< HEAD
visible_message("\icon[src][bicon(src)] [src] beeps and spits out [loaded_disk].")
=======
visible_message("<span class='filter_notice'>[bicon(src)] [src] beeps and spits out [loaded_disk].</span>")
>>>>>>> 75577bd3ca9... cleans up so many to_chats so they use vchat filters, unsorted chat filter for everything else (#9006)
loaded_disk = null
/obj/machinery/botany/attackby(obj/item/weapon/W as obj, mob/user as mob)
if(istype(W,/obj/item/seeds))
if(seed)
to_chat(user, "There is already a seed loaded.")
to_chat(user, "<span class='filter_notice'>There is already a seed loaded.</span>")
return
var/obj/item/seeds/S =W
if(S.seed && S.seed.get_trait(TRAIT_IMMUTABLE) > 0)
to_chat(user, "That seed is not compatible with our genetics technology.")
to_chat(user, "<span class='filter_notice'>That seed is not compatible with our genetics technology.</span>")
else
user.drop_from_inventory(W)
W.loc = src
seed = W
to_chat(user, "You load [W] into [src].")
to_chat(user, "<span class='filter_notice'>You load [W] into [src].</span>")
return
if(default_deconstruction_screwdriver(user, W))
@@ -105,24 +115,24 @@
// return
if(istype(W,/obj/item/weapon/disk/botany))
if(loaded_disk)
to_chat(user, "There is already a data disk loaded.")
to_chat(user, "<span class='filter_notice'>There is already a data disk loaded.</span>")
return
else
var/obj/item/weapon/disk/botany/B = W
if(B.genes && B.genes.len)
if(!disk_needs_genes)
to_chat(user, "That disk already has gene data loaded.")
to_chat(user, "<span class='filter_notice'>That disk already has gene data loaded.</span>")
return
else
if(disk_needs_genes)
to_chat(user, "That disk does not have any gene data loaded.")
to_chat(user, "<span class='filter_notice'>That disk does not have any gene data loaded.</span>")
return
user.drop_from_inventory(W)
W.loc = src
loaded_disk = W
to_chat(user, "You load [W] into [src].")
to_chat(user, "<span class='filter_notice'>You load [W] into [src].</span>")
return
..()
@@ -169,7 +179,46 @@
data["hasGenetics"] = 0
data["sourceName"] = 0
<<<<<<< HEAD
return data
=======
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open)
if (!ui)
ui = new(user, src, ui_key, "botany_isolator.tmpl", "Lysis-isolation Centrifuge UI", 470, 450)
ui.set_initial_data(data)
ui.open()
ui.set_auto_update(1)
/obj/machinery/botany/Topic(href, href_list)
if(..())
return 1
if(href_list["eject_packet"])
if(!seed) return
seed.loc = get_turf(src)
if(seed.seed.name == "new line" || isnull(plant_controller.seeds[seed.seed.name]))
seed.seed.uid = plant_controller.seeds.len + 1
seed.seed.name = "[seed.seed.uid]"
plant_controller.seeds[seed.seed.name] = seed.seed
seed.update_seed()
visible_message("<span class='filter_notice'>[bicon(src)] [src] beeps and spits out [seed].</span>")
seed = null
if(href_list["eject_disk"])
if(!loaded_disk) return
loaded_disk.loc = get_turf(src)
visible_message("<span class='filter_notice'>[bicon(src)] [src] beeps and spits out [loaded_disk].</span>")
loaded_disk = null
usr.set_machine(src)
src.add_fingerprint(usr)
/obj/machinery/botany/extractor/Topic(href, href_list)
>>>>>>> 75577bd3ca9... cleans up so many to_chats so they use vchat filters, unsorted chat filter for everything else (#9006)
/obj/machinery/botany/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state)
if(..())
+4 -4
View File
@@ -389,7 +389,7 @@
/obj/machinery/seed_storage/attackby(var/obj/item/O as obj, var/mob/user as mob)
if (istype(O, /obj/item/seeds) && !lockdown)
add(O)
user.visible_message("[user] puts \the [O.name] into \the [src].", "You put \the [O] into \the [src].")
user.visible_message("<span class ='filter_notice'>[user] puts \the [O.name] into \the [src].</span>", "<span class ='filter_notice'>You put \the [O] into \the [src].</span>")
return
else if (istype(O, /obj/item/weapon/storage/bag/plants) && !lockdown)
var/obj/item/weapon/storage/P = O
@@ -398,17 +398,17 @@
++loaded
add(G)
if (loaded)
user.visible_message("[user] puts the seeds from \the [O.name] into \the [src].", "You put the seeds from \the [O.name] into \the [src].")
user.visible_message("<span class ='filter_notice'>[user] puts the seeds from \the [O.name] into \the [src].</span>", "<span class ='filter_notice'>You put the seeds from \the [O.name] into \the [src].</span>")
else
to_chat(user, "<span class='notice'>There are no seeds in \the [O.name].</span>")
return
else if(O.is_wrench())
playsound(src, O.usesound, 50, 1)
anchored = !anchored
to_chat(user, "You [anchored ? "wrench" : "unwrench"] \the [src].")
to_chat(user, "<span class ='filter_notice'>You [anchored ? "wrench" : "unwrench"] \the [src].</span>")
else if(O.is_screwdriver())
panel_open = !panel_open
to_chat(user, "You [panel_open ? "open" : "close"] the maintenance panel.")
to_chat(user, "<span class ='filter_notice'>You [panel_open ? "open" : "close"] the maintenance panel.</span>")
playsound(src, O.usesound, 50, 1)
cut_overlays()
if(panel_open)
+21 -21
View File
@@ -166,13 +166,13 @@
if(weedlevel > 0)
nymph.reagents.add_reagent("glucose", weedlevel)
weedlevel = 0
nymph.visible_message("<font color='blue'><b>[nymph]</b> begins rooting through [src], ripping out weeds and eating them noisily.</font>","<font color='blue'>You begin rooting through [src], ripping out weeds and eating them noisily.</font>")
nymph.visible_message("<span class='notice'><b>[nymph]</b> begins rooting through [src], ripping out weeds and eating them noisily.</span>","<span class='notice'>You begin rooting through [src], ripping out weeds and eating them noisily.</span>")
else if(nymph.nutrition > 100 && nutrilevel < 10)
nymph.nutrition -= ((10-nutrilevel)*5)
nutrilevel = 10
nymph.visible_message("<font color='blue'><b>[nymph]</b> secretes a trickle of green liquid, refilling [src].</font>","<font color='blue'>You secrete a trickle of green liquid, refilling [src].</font>")
nymph.visible_message("<span class='notice'><b>[nymph]</b> secretes a trickle of green liquid, refilling [src].</span>","<span class='notice'>You secrete a trickle of green liquid, refilling [src].</span>")
else
nymph.visible_message("<font color='blue'><b>[nymph]</b> rolls around in [src] for a bit.</font>","<font color='blue'>You roll around in [src] for a bit.</font>")
nymph.visible_message("<span class='notice'><b>[nymph]</b> rolls around in [src] for a bit.</span>","<span class='notice'>You roll around in [src] for a bit.</span>")
return
/obj/machinery/portable_atmospherics/hydroponics/Initialize()
@@ -330,7 +330,7 @@
if(closed_system)
if(user)
to_chat(user, "You can't harvest from the plant while the lid is shut.")
to_chat(user, "<span class='filter_notice'>You can't harvest from the plant while the lid is shut.</span>")
return
if(user)
@@ -357,7 +357,7 @@
if(!user || !dead) return
if(closed_system)
to_chat(user, "You can't remove the dead plant while the lid is shut.")
to_chat(user, "<span class='filter_notice'>You can't remove the dead plant while the lid is shut.</span>")
return
seed = null
@@ -367,7 +367,7 @@
yield_mod = 0
mutation_mod = 0
to_chat(user, "You remove the dead plant.")
to_chat(user, "<span class='filter_notice'>You remove the dead plant.</span>")
lastproduce = 0
check_health()
return
@@ -423,11 +423,11 @@
return
if(ishuman(usr) || istype(usr, /mob/living/silicon/robot))
if(labelled)
to_chat(usr, "You remove the label.")
to_chat(usr, "<span class='filter_notice'>You remove the label.</span>")
labelled = null
update_icon()
else
to_chat(usr, "There is no label to remove.")
to_chat(usr, "<span class='filter_notice'>There is no label to remove.</span>")
return
/obj/machinery/portable_atmospherics/hydroponics/verb/setlight()
@@ -441,7 +441,7 @@
var/new_light = tgui_input_list(usr, "Specify a light level.", "Light Level", list(0,1,2,3,4,5,6,7,8,9,10))
if(new_light)
tray_light = new_light
to_chat(usr, "You set the tray to a light level of [tray_light] lumens.")
to_chat(usr, "<span class='filter_notice'>You set the tray to a light level of [tray_light] lumens.</span>")
return
/obj/machinery/portable_atmospherics/hydroponics/proc/check_level_sanity()
@@ -477,7 +477,7 @@
weedlevel = 0
update_icon()
visible_message("<span class='danger'>The </span><span class='notice'>[previous_plant]</span><span class='danger'> has suddenly mutated into </span><span class='notice'>[seed.display_name]!</span>")
visible_message("<span class='danger'>The <span class='notice'>[previous_plant]</span> has suddenly mutated into <span class='notice'>[seed.display_name]!</span></span>")
return
@@ -489,15 +489,15 @@
if(O.is_wirecutter() || istype(O, /obj/item/weapon/surgical/scalpel))
if(!seed)
to_chat(user, "There is nothing to take a sample from in \the [src].")
to_chat(user, "<span class='filter_notice'>There is nothing to take a sample from in \the [src].</span>")
return
if(sampled)
to_chat(user, "You have already sampled from this plant.")
to_chat(user, "<span class='filter_notice'>You have already sampled from this plant.</span>")
return
if(dead)
to_chat(user, "The plant is dead.")
to_chat(user, "<span class='filter_notice'>The plant is dead.</span>")
return
// Create a sample.
@@ -522,14 +522,14 @@
if(seed)
return ..()
else
to_chat(user, "There's no plant to inject.")
to_chat(user, "<span class='filter_notice'>There's no plant to inject.</span>")
return 1
else
if(seed)
//Leaving this in in case we want to extract from plants later.
to_chat(user, "You can't get any extract out of this plant.")
to_chat(user, "<span class='filter_notice'>You can't get any extract out of this plant.</span>")
else
to_chat(user, "There's nothing to draw something from.")
to_chat(user, "<span class='filter_notice'>There's nothing to draw something from.</span>")
return 1
else if (istype(O, /obj/item/seeds))
@@ -540,11 +540,11 @@
user.remove_from_mob(O)
if(!S.seed)
to_chat(user, "The packet seems to be empty. You throw it away.")
to_chat(user, "<span class='filter_notice'>The packet seems to be empty. You throw it away.</span>")
qdel(O)
return
to_chat(user, "You plant the [S.seed.seed_name] [S.seed.seed_noun].")
to_chat(user, "<span class='filter_notice'>You plant the [S.seed.seed_name] [S.seed.seed_noun].</span>")
plant_seeds(S)
else
@@ -576,7 +576,7 @@
toxins += spray.toxicity
pestlevel -= spray.pest_kill_str
weedlevel -= spray.weed_kill_str
to_chat(user, "You spray [src] with [O].")
to_chat(user, "<span class='filter_notice'>You spray [src] with [O].</span>")
playsound(src, 'sound/effects/spray3.ogg', 50, 1, -6)
qdel(O)
check_health()
@@ -589,7 +589,7 @@
playsound(src, O.usesound, 50, 1)
anchored = !anchored
to_chat(user, "You [anchored ? "wrench" : "unwrench"] \the [src].")
to_chat(user, "<span class='filter_notice'>You [anchored ? "wrench" : "unwrench"] \the [src].</span>")
else if(istype(O,/obj/item/device/multitool))
if(!anchored)
@@ -691,5 +691,5 @@
/obj/machinery/portable_atmospherics/hydroponics/proc/close_lid(var/mob/living/user)
closed_system = !closed_system
to_chat(user, "You [closed_system ? "close" : "open"] the tray's lid.")
to_chat(user, "<span class='filter_notice'>You [closed_system ? "close" : "open"] the tray's lid.</span>")
update_icon()
@@ -39,11 +39,25 @@
ui = new(user, src, "PlantAnalyzer", name)
ui.open()
<<<<<<< HEAD
/obj/item/device/analyzer/plant_analyzer/tgui_state(mob/user)
return GLOB.tgui_inventory_state
/obj/item/device/analyzer/plant_analyzer/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state)
var/list/data = ..()
=======
/obj/item/analyzer/plant_analyzer/proc/print_report(var/mob/living/user)
if(!last_data)
to_chat(user, "<span class='filter_notice'>There is no scan data to print.</span>")
return
var/obj/item/paper/P = new /obj/item/paper(get_turf(src))
P.name = "paper - [form_title]"
P.info = "[last_data]"
if(istype(user,/mob/living/carbon/human))
user.put_in_hands(P)
user.visible_message("<span class='filter_notice'>\The [src] spits out a piece of paper.</span>")
return
>>>>>>> 75577bd3ca9... cleans up so many to_chats so they use vchat filters, unsorted chat filter for everything else (#9006)
var/datum/seed/grown_seed = last_seed
if(!istype(grown_seed))