From 50ca772413bd067dabcbe08feec51ebed3dd87e7 Mon Sep 17 00:00:00 2001 From: MistakeNot4892 Date: Sun, 28 Jul 2024 19:17:26 +1000 Subject: [PATCH] Bee and insect swarm rework (beewrite). --- code/__defines/hydroponics.dm | 1 + code/game/machinery/centrifuge.dm | 168 ++++++++++ code/game/objects/items/__item.dm | 33 ++ .../circuitboards/machinery/biogenerator.dm | 2 +- .../circuitboards/machinery/household.dm | 19 ++ .../imprinter/designs_misc_circuits.dm | 5 +- .../genetics/plants/gene_biochemistry.dm | 4 + code/modules/genetics/plants/trait_pollen.dm | 3 + code/modules/hydroponics/grown.dm | 12 + code/modules/hydroponics/seed.dm | 1 + code/modules/hydroponics/trays/tray.dm | 3 + .../modules/hydroponics/trays/tray_process.dm | 3 + .../liquids/materials_liquid_toxins.dm | 2 +- icons/obj/machines/centrifuge.dmi | Bin 0 -> 715 bytes icons/obj/virology.dmi | Bin 2266 -> 1593 bytes maps/away/unishi/unishi.dm | 1 + maps/exodus/exodus-2.dmm | 2 +- maps/exodus/exodus.dm | 2 +- maps/ministation/ministation-1.dmm | 10 +- maps/ministation/ministation.dm | 2 +- maps/tradeship/tradeship-0.dmm | 2 +- maps/tradeship/tradeship.dm | 2 +- mods/content/beekeeping/apiary_bees_etc.dmi | Bin 3301 -> 0 bytes mods/content/beekeeping/hives/_hive.dm | 166 ---------- mods/content/beekeeping/icons/beekeeping.dmi | Bin 5053 -> 0 bytes mods/content/insects/_insects.dm | 0 mods/content/insects/_insects.dme | 18 ++ mods/content/insects/centrifuge.dm | 3 + mods/content/insects/closets.dm | 14 + mods/content/insects/hives/_hive.dm | 187 +++++++++++ mods/content/insects/hives/hive_frame.dm | 89 +++++ mods/content/insects/hives/hive_insects.dm | 183 +++++++++++ mods/content/insects/hives/hive_queen.dm | 24 ++ mods/content/insects/hives/hive_structure.dm | 76 +++++ mods/content/insects/hives/hive_swarm.dm | 306 ++++++++++++++++++ mods/content/insects/icons/apiary.dmi | Bin 0 -> 345 bytes mods/content/insects/icons/bee_pack.dmi | Bin 0 -> 366 bytes mods/content/insects/icons/beehive.dmi | Bin 0 -> 319 bytes mods/content/insects/icons/comb.dmi | Bin 0 -> 1077 bytes mods/content/insects/icons/frame.dmi | Bin 0 -> 1311 bytes mods/content/insects/icons/smoker.dmi | Bin 0 -> 480 bytes mods/content/insects/icons/swarm.dmi | Bin 0 -> 1016 bytes mods/content/insects/items.dm | 7 + mods/content/insects/materials.dm | 20 ++ mods/content/insects/recipes.dm | 6 + mods/content/insects/trading.dm | 17 + nebula.dme | 2 + 47 files changed, 1216 insertions(+), 179 deletions(-) create mode 100644 code/game/machinery/centrifuge.dm create mode 100644 code/modules/genetics/plants/trait_pollen.dm create mode 100644 icons/obj/machines/centrifuge.dmi delete mode 100644 mods/content/beekeeping/apiary_bees_etc.dmi delete mode 100644 mods/content/beekeeping/hives/_hive.dm delete mode 100644 mods/content/beekeeping/icons/beekeeping.dmi create mode 100644 mods/content/insects/_insects.dm create mode 100644 mods/content/insects/_insects.dme create mode 100644 mods/content/insects/centrifuge.dm create mode 100644 mods/content/insects/closets.dm create mode 100644 mods/content/insects/hives/_hive.dm create mode 100644 mods/content/insects/hives/hive_frame.dm create mode 100644 mods/content/insects/hives/hive_insects.dm create mode 100644 mods/content/insects/hives/hive_queen.dm create mode 100644 mods/content/insects/hives/hive_structure.dm create mode 100644 mods/content/insects/hives/hive_swarm.dm create mode 100644 mods/content/insects/icons/apiary.dmi create mode 100644 mods/content/insects/icons/bee_pack.dmi create mode 100644 mods/content/insects/icons/beehive.dmi create mode 100644 mods/content/insects/icons/comb.dmi create mode 100644 mods/content/insects/icons/frame.dmi create mode 100644 mods/content/insects/icons/smoker.dmi create mode 100644 mods/content/insects/icons/swarm.dmi create mode 100644 mods/content/insects/items.dm create mode 100644 mods/content/insects/materials.dm create mode 100644 mods/content/insects/recipes.dm create mode 100644 mods/content/insects/trading.dm diff --git a/code/__defines/hydroponics.dm b/code/__defines/hydroponics.dm index 6aa4a63f761..e40a83850d8 100644 --- a/code/__defines/hydroponics.dm +++ b/code/__defines/hydroponics.dm @@ -4,6 +4,7 @@ // Defining these to point to the relevant decl types just to avoid a massive changeset. // TODO: rename to PLANT_TRAIT or bare decls to avoid mixing up with GetTrait etc. #define TRAIT_CHEMS /decl/plant_trait/chems +#define TRAIT_POLLEN /decl/plant_trait/pollen #define TRAIT_EXUDE_GASSES /decl/plant_trait/exude_gasses #define TRAIT_ALTER_TEMP /decl/plant_trait/alter_temp #define TRAIT_POTENCY /decl/plant_trait/potency diff --git a/code/game/machinery/centrifuge.dm b/code/game/machinery/centrifuge.dm new file mode 100644 index 00000000000..13503261579 --- /dev/null +++ b/code/game/machinery/centrifuge.dm @@ -0,0 +1,168 @@ +/datum/storage/hopper/industrial/centrifuge + can_hold = list(/obj/item/food) // also handles hive frames; see insects modpack. + expected_type = /obj/machinery/centrifuge + +/datum/storage/hopper/industrial/centrifuge/can_be_inserted(obj/item/W, mob/user, stop_messages = 0, click_params = null) + . = ..() + if(. && !W.reagents?.total_volume) + if(user) + to_chat(user, SPAN_WARNING("\The [W] is empty.")) + return FALSE + +/obj/machinery/centrifuge + name = "industrial centrifuge" + desc = "A machine used to extract reagents and materials from objects via spinning them at extreme speed." + icon = 'icons/obj/machines/centrifuge.dmi' + icon_state = ICON_STATE_WORLD + anchored = TRUE + density = TRUE + construct_state = /decl/machine_construction/default/panel_closed + uncreated_component_parts = null + storage = /datum/storage/hopper/industrial/centrifuge + base_type = /obj/machinery/centrifuge + stat_immune = 0 + + // Reference to our reagent container. Set to a path to create on init. + var/obj/item/loaded_beaker + + // Stolen from fabricators. + var/sound_id + var/datum/sound_token/sound_token + var/work_sound = 'sound/machines/fabricator_loop.ogg' + +/obj/machinery/centrifuge/mapped + loaded_beaker = /obj/item/chems/glass/beaker/large + +/obj/machinery/centrifuge/Initialize() + . = ..() + if(ispath(loaded_beaker)) + loaded_beaker = new loaded_beaker + +/obj/machinery/centrifuge/Destroy() + QDEL_NULL(loaded_beaker) + return ..() + +/obj/machinery/centrifuge/dismantle() + if(loaded_beaker) + loaded_beaker.dropInto(loc) + loaded_beaker = null + return ..() + +/obj/machinery/centrifuge/components_are_accessible(path) + return use_power != POWER_USE_ACTIVE && ..() + +/obj/machinery/centrifuge/cannot_transition_to(state_path, mob/user) + if(use_power == POWER_USE_ACTIVE) + return SPAN_NOTICE("You must wait for \the [src] to finish first!") + return ..() + +/obj/machinery/centrifuge/attackby(obj/item/used_item, mob/user) + + if(use_power == POWER_USE_ACTIVE) + to_chat(user, SPAN_NOTICE("\The [src] is currently spinning, wait until it's finished.")) + return TRUE + + if((. = component_attackby(used_item, user))) + return + + // Load in a new container for products. + if(istype(used_item, /obj/item/chems/glass/beaker)) + if(loaded_beaker) + to_chat(user, SPAN_WARNING("\The [src] already has a beaker loaded.")) + return TRUE + if(user.try_unequip(used_item, src)) + loaded_beaker = used_item + to_chat(user, SPAN_NOTICE("You load \the [loaded_beaker] into \the [src].")) + return TRUE + + // Parent call handles inserting the frame into our contents, + return ..() + +/obj/machinery/centrifuge/attack_hand(mob/user) + + if(use_power == POWER_USE_ACTIVE) + user.visible_message("\The [user] disengages \the [src].") + update_use_power(POWER_USE_IDLE) + return TRUE + + if(use_power == POWER_USE_IDLE) + if(!loaded_beaker || QDELETED(loaded_beaker)) + to_chat(user, SPAN_WARNING("\The [src] has no beaker loaded to receive any products.")) + loaded_beaker = null // just in case + return TRUE + + if(length(get_stored_inventory())) + user.visible_message("\The [user] engages \the [src].") + update_use_power(POWER_USE_ACTIVE) + else + to_chat(user, SPAN_WARNING("\The [src]'s hopper is empty.")) + return TRUE + + if(use_power == POWER_USE_OFF || !operable()) + to_chat(user, SPAN_WARNING("\The [src]'s interface is unresponsive.")) + return TRUE + + return ..() + +/obj/machinery/centrifuge/Process(wait, tick) + ..() + + if(use_power != POWER_USE_ACTIVE) + return + + if(!loaded_beaker) + visible_message("\The [src] stops spinning and flashes a red light.") + update_use_power(POWER_USE_IDLE) + return + + var/list/processing_items = get_stored_inventory() + if(!length(processing_items)) + visible_message("\The [src] stops spinning and flashes a green light.") + update_use_power(POWER_USE_IDLE) + return + + var/obj/item/thing = processing_items[1] + thing.handle_centrifuge_process(src) + if(!QDELETED(thing) && loc) + thing.dropInto(loc) + +/obj/machinery/centrifuge/Initialize() + sound_id = "[work_sound]" + return ..() + +/obj/machinery/centrifuge/Destroy() + QDEL_NULL(sound_token) + return ..() + +/obj/machinery/centrifuge/update_use_power() + . = ..() + if(use_power == POWER_USE_ACTIVE) + if(!sound_token) + sound_token = play_looping_sound(src, sound_id, work_sound, volume = 30) + else + QDEL_NULL(sound_token) + +/obj/machinery/centrifuge/on_update_icon() + icon_state = initial(icon_state) + if(stat & BROKEN) + icon_state = "[icon_state]-broken" + else if(use_power == POWER_USE_OFF || !operable()) + icon_state = "[icon_state]-off" + else if(use_power == POWER_USE_ACTIVE) + icon_state = "[icon_state]-working" + +/obj/machinery/centrifuge/get_alt_interactions(var/mob/user) + . = ..() + if(loaded_beaker) + LAZYADD(., list(/decl/interaction_handler/remove_centrifuge_beaker)) + +/decl/interaction_handler/remove_centrifuge_beaker + name = "Remove Beaker" + expected_target_type = /obj/machinery/centrifuge + +/decl/interaction_handler/remove_centrifuge_beaker/invoked(atom/target, mob/user, obj/item/prop) + var/obj/machinery/centrifuge/centrifuge = target + if(centrifuge.loaded_beaker) + centrifuge.loaded_beaker.dropInto(centrifuge.loc) + user.put_in_hands(centrifuge.loaded_beaker) + centrifuge.loaded_beaker = null diff --git a/code/game/objects/items/__item.dm b/code/game/objects/items/__item.dm index 27bc8a998ed..340d4a75d5e 100644 --- a/code/game/objects/items/__item.dm +++ b/code/game/objects/items/__item.dm @@ -1320,3 +1320,36 @@ modules/mob/living/human/life.dm if you die, you will be zoomed out. coating_string = FONT_COLORED(coating.get_color(), coating_string) return coating_string return ..() + + +// Bespoke proc for handling when a centrifuge smooshes us, only currently used by growns and hive frames. +/obj/item/proc/handle_centrifuge_process(obj/machinery/centrifuge/centrifuge) + SHOULD_CALL_PARENT(TRUE) + return istype(centrifuge) && !QDELETED(centrifuge.loaded_beaker) && istype(centrifuge.loaded_beaker) + +/obj/item/proc/convert_matter_to_lumps(skip_qdel = FALSE) + + var/list/scrap_matter = list() + for(var/mat in matter) + var/mat_amount = matter[mat] + var/obj/item/stack/material/mat_stack = /obj/item/stack/material/lump + var/mat_per_stack = SHEET_MATERIAL_AMOUNT * initial(mat_stack.matter_multiplier) + var/sheet_amount = round(mat_amount / mat_per_stack) + if(sheet_amount) + var/obj/item/stack/material/lump/lump = new(loc, sheet_amount, mat) + LAZYADD(., lump) + mat_amount -= sheet_amount * mat_per_stack + if(mat_amount) + scrap_matter[mat] += mat_amount + + if(length(scrap_matter)) + var/obj/item/debris/scraps/scraps = new(loc) + scraps.matter = scrap_matter.Copy() + scraps.update_primary_material() + LAZYADD(., scraps) + + matter = null + material = null + if(!skip_qdel) + qdel(src) + diff --git a/code/game/objects/items/circuitboards/machinery/biogenerator.dm b/code/game/objects/items/circuitboards/machinery/biogenerator.dm index d41c8526e4b..c6fe3d9bb3b 100644 --- a/code/game/objects/items/circuitboards/machinery/biogenerator.dm +++ b/code/game/objects/items/circuitboards/machinery/biogenerator.dm @@ -11,4 +11,4 @@ /obj/item/stock_parts/console_screen = 1, /obj/item/stock_parts/keyboard = 1, /obj/item/stock_parts/power/apc/buildable = 1 - ) \ No newline at end of file + ) diff --git a/code/game/objects/items/circuitboards/machinery/household.dm b/code/game/objects/items/circuitboards/machinery/household.dm index 1e2b70aba16..65cb3e988e4 100644 --- a/code/game/objects/items/circuitboards/machinery/household.dm +++ b/code/game/objects/items/circuitboards/machinery/household.dm @@ -46,6 +46,25 @@ /obj/item/stock_parts/circuitboard/cooker/get_buildable_types() return subtypesof(/obj/machinery/cooker) +/obj/item/stock_parts/circuitboard/centrifuge + name = "circuitboard (industrial centrifuge)" + build_path = /obj/machinery/centrifuge + board_type = "machine" + origin_tech = @'{"biotech":2,"engineering":1}' + req_components = list( + /obj/item/stock_parts/manipulator = 2, + /obj/item/stock_parts/matter_bin = 2) + +/obj/item/stock_parts/circuitboard/seed_extractor + name = "circuitboard (seed extractor)" + build_path = /obj/machinery/seed_extractor + board_type = "machine" + origin_tech = @'{"biotech":2,"engineering":1}' + req_components = list( + /obj/item/stock_parts/manipulator = 2, + /obj/item/stock_parts/matter_bin = 2 + ) + /obj/item/stock_parts/circuitboard/seed_storage name = "circuitboard (seed storage)" build_path = /obj/machinery/seed_storage diff --git a/code/modules/fabrication/designs/imprinter/designs_misc_circuits.dm b/code/modules/fabrication/designs/imprinter/designs_misc_circuits.dm index 3e5b152aeee..5cd11e01b85 100644 --- a/code/modules/fabrication/designs/imprinter/designs_misc_circuits.dm +++ b/code/modules/fabrication/designs/imprinter/designs_misc_circuits.dm @@ -345,7 +345,7 @@ path = /obj/item/stock_parts/circuitboard/cooker /datum/fabricator_recipe/imprinter/circuit/seed_extractor - path = /obj/item/stock_parts/circuitboard/seed + path = /obj/item/stock_parts/circuitboard/seed_extractor /datum/fabricator_recipe/imprinter/circuit/vending path = /obj/item/stock_parts/circuitboard/vending @@ -487,3 +487,6 @@ /datum/fabricator_recipe/imprinter/circuit/central_atmos path = /obj/item/stock_parts/circuitboard/central_atmos + +/datum/fabricator_recipe/imprinter/circuit/centrifuge + path = /obj/item/stock_parts/circuitboard/centrifuge diff --git a/code/modules/genetics/plants/gene_biochemistry.dm b/code/modules/genetics/plants/gene_biochemistry.dm index 9239b95f292..296eebe4fbf 100644 --- a/code/modules/genetics/plants/gene_biochemistry.dm +++ b/code/modules/genetics/plants/gene_biochemistry.dm @@ -17,6 +17,8 @@ if(seed.get_trait(trait) > 0) seed.set_trait(trait, seed.get_trait(trait), null, 1, 0.85) + seed.produces_pollen = LAZYACCESS(gene.values, TRAIT_POLLEN) + LAZYINITLIST(seed.chems) var/list/gene_value = LAZYACCESS(gene.values, TRAIT_CHEMS) for(var/rid in gene_value) @@ -42,10 +44,12 @@ LAZYREMOVE(gene.values, TRAIT_EXUDE_GASSES) LAZYREMOVE(gene.values, TRAIT_CHEMS) + LAZYREMOVE(gene.values, TRAIT_POLLEN) return ..() /decl/plant_gene/biochemistry/copy_initial_seed_values(datum/plantgene/gene, datum/seed/seed) LAZYSET(gene.values, TRAIT_CHEMS, seed.chems?.Copy()) LAZYSET(gene.values, TRAIT_EXUDE_GASSES, seed.exude_gasses?.Copy()) + LAZYSET(gene.values, TRAIT_POLLEN, seed.produces_pollen) return ..() diff --git a/code/modules/genetics/plants/trait_pollen.dm b/code/modules/genetics/plants/trait_pollen.dm new file mode 100644 index 00000000000..7dbbcbbd0df --- /dev/null +++ b/code/modules/genetics/plants/trait_pollen.dm @@ -0,0 +1,3 @@ +/decl/plant_trait/pollen + name = "pollen" + requires_master_gene = FALSE diff --git a/code/modules/hydroponics/grown.dm b/code/modules/hydroponics/grown.dm index 7083722c41c..b252d8a572f 100644 --- a/code/modules/hydroponics/grown.dm +++ b/code/modules/hydroponics/grown.dm @@ -15,6 +15,18 @@ var/seeds_extracted = FALSE var/datum/seed/seed +// This is sort of pointless while food is a valid input on the ChemMaster but maybe +// in the future there will be some more interesting ways to process growns/food. +/obj/item/food/grown/handle_centrifuge_process(obj/machinery/centrifuge/centrifuge) + if(!(. = ..())) + return + if(reagents.total_volume) + reagents.trans_to_holder(centrifuge.loaded_beaker.reagents, reagents.total_volume) + for(var/obj/item/thing in contents) + thing.dropInto(centrifuge.loc) + for(var/atom/movable/thing in convert_matter_to_lumps()) + thing.dropInto(centrifuge.loc) + /obj/item/food/grown/examine(mob/user, distance) . = ..() if(user && distance <= 1 && seed && user.skill_check(work_skill, SKILL_BASIC)) diff --git a/code/modules/hydroponics/seed.dm b/code/modules/hydroponics/seed.dm index 1065eb1d8a2..d61f42d51ba 100644 --- a/code/modules/hydroponics/seed.dm +++ b/code/modules/hydroponics/seed.dm @@ -32,6 +32,7 @@ var/scannable_result var/grown_is_seed = FALSE var/product_w_class = ITEM_SIZE_SMALL + var/produces_pollen = 0 // Dissection values. var/min_seed_extracted = 1 diff --git a/code/modules/hydroponics/trays/tray.dm b/code/modules/hydroponics/trays/tray.dm index cc12942e6f0..179017d53bc 100644 --- a/code/modules/hydroponics/trays/tray.dm +++ b/code/modules/hydroponics/trays/tray.dm @@ -40,6 +40,9 @@ var/force_update // Set this to bypass the cycle time check. var/obj/temp_chem_holder // Something to hold reagents during process_reagents() + // Counter used by bees. + var/pollen = 0 + // Seed details/line data. var/datum/seed/seed = null // The currently planted seed diff --git a/code/modules/hydroponics/trays/tray_process.dm b/code/modules/hydroponics/trays/tray_process.dm index dbcf6f19447..a62c81a8d1e 100644 --- a/code/modules/hydroponics/trays/tray_process.dm +++ b/code/modules/hydroponics/trays/tray_process.dm @@ -65,6 +65,9 @@ mutate((rand(100) < 15) ? 2 : 1) mutation_level = 0 + if(pollen < 10) + pollen += seed?.produces_pollen + // Maintain tray nutrient and water levels. if(seed.get_trait(TRAIT_REQUIRES_NUTRIENTS) && seed.get_trait(TRAIT_NUTRIENT_CONSUMPTION) > 0 && nutrilevel > 0 && prob(25)) nutrilevel -= max(0,seed.get_trait(TRAIT_NUTRIENT_CONSUMPTION) * growth_rate) diff --git a/code/modules/materials/definitions/liquids/materials_liquid_toxins.dm b/code/modules/materials/definitions/liquids/materials_liquid_toxins.dm index 08eaa22b0c9..ad32f11030a 100644 --- a/code/modules/materials/definitions/liquids/materials_liquid_toxins.dm +++ b/code/modules/materials/definitions/liquids/materials_liquid_toxins.dm @@ -65,7 +65,7 @@ name = "spider venom" uid = "liquid_spider_venom" lore_text = "A deadly necrotic toxin produced by giant spiders to disable their prey." - taste_description = "absolutely vile" + taste_description = "vile poison" color = "#91d895" toxicity_targets_organ = BP_LIVER toxicity = 5 diff --git a/icons/obj/machines/centrifuge.dmi b/icons/obj/machines/centrifuge.dmi new file mode 100644 index 0000000000000000000000000000000000000000..6cae1e66cf3235f822686b1eb7c65b7b0e730798 GIT binary patch literal 715 zcmV;+0yO=JP)#Jyup`A0H=$iIls+$+(0taz!dObI+py0004WQchCV=-0C=2JR&a84_w-Y6@%7{?OD!tS%+FJ>RWQ*r;NmRL zOex6#a*U0*I5Sc+(=$pSoZ^zil2jm5sXV_ZCq;>iGbOXA7|1u|;!G<_%uR)`QB~i|rDIoZ1{6)rNvs43>%bwp5&3CpxGYmvaP@Nmdl&$0H%4!!ge-{w z00Fm2L_t(oh3%IyQ^GJ9hCdv7j3#JxYA9hOx95A)rq|x(xdQwy2w}%$RNi-cgB!=-ep%n}u3VR3o!j~ivpt-9 zn7EiY8n>r5tSYe>gh`StuAlb{!Zb~zj?Q{72jcn#))okk zsL86|?GU@>4l&m_NmA9z!B{@yomx=J0L$RR^Ss;lU|x-(Is1AG@vI^P#M}A2D%Yt~ zoV*_naNZB4{DU7#S)Ttw0L0PQ#U|(cAP!G85X`B`IX{RC3_pmommTnfuloTr%2=5n z2(y{t158+{ALNdP55%O@4}wh}h!ua`4{6p9Ns{%0;);GyoIC$_ob>~*{ZQw>r1nGY xhhy?Xu@AEU69;`zKh!yvd;;?tXJWJc$shm#002ovPDHLkV1mzJMcM!W literal 0 HcmV?d00001 diff --git a/icons/obj/virology.dmi b/icons/obj/virology.dmi index 7fcf76d7f9f2fef2e3e4c68a1cb15f293351f19a..925adfe56be878fbc98242da0f3b784fb9b59a07 100644 GIT binary patch delta 1505 zcmV<71s?j^5xEQ{iBL{Q4GJ0x0000DNk~Le0001>0001h2m=5B0K2`CDF6TflaVD- z4>>tGBO@bRTwFmxK`t&Xk%k_Bz`(#|Y;bUTf0KqwteZ?!TVH>2H_EFU7)*>W00001 zbW%=J06^y0W&i*Hwt7@pbVOxyV{&P5bZKvH004NLQ&wnh2z(K-^5k#rhAn$yxdjGqT% zn->*@sF|WdQF30Eb3tV}%ZrnOxg@6XrVVq%L`l~bTJ5_ z3g^XX!NqdWg{eVGj?l5-^72wCpfiVf|N7N(xq|qT6#$$TNPJCye%@`Epgq`Z3Nu$; zzuK}bSowA+uy9%+ZU5bRz2011U0=WdAWXMzvn3C@Xs_al~+6EBjVh^KsTir-Qj znDW6lkq;hEfqCYG_}d_C%?H7JZ?$yfdn_3`CI9Ow;|7Cjx-|rJmh_4i0DMUcp!A9s z7}GNr?->j#`CQEm0{I^;0C-Icp!|;(*z-sC1Z&p>Yj}d4L12Q+pf!KA9fSNk%5j4B z{I8p!mGcRI+Vj6|g0}n-`jBM*DNdjNQGb4k!k9H`wj>h&@#8qHwPUKiDG;_iA8P9G=|3TQF!<6z*F&pWMc%Rp#19T`@9(t%uJ-2U_CWaE zp8PTKTT%e=11SJsPYP5n6>x)kgSNQ``Mra?U*Fnz-@sZR?!Zr?*xakZq1yv~jOv^p zW3T21fQ!eVwSg=|Z-1ikS2W;?#C;m5-}pO4;yw*>zmnvBEw)!4vF(ShO$;<>mSj-=UHHi> z>n4Lq4La^icu z-rOFveD75T|Bh`Ch~Ihx(pzjWD80p_8{`u-=5x#dk|$_52Bis(e+&Zo95ayiG^50S zT1r004M;k9>EEaZO{bYkUbUnfgn6JrIGHK?Uag8@+{ zgLWQh(2>t&U{I2If|?8q*X(>$gT@IUo@wElpTXSIt-SRQx3&(lB9-s600000NkvXX Hu0mjfE&s#{ delta 2167 zcmV--2#EK&4B8PTiBL{Q4GJ0x0000DNk~Le0002M0001>2m=5B0B!b@ZvX%Qw~-}M z4 zf0dHY3WG2V#?SUCie6=?`*+%9D8s{i1*safpzUa@!?$0VxCyK1$mL7;HTe?i;A?b? z6CB=S)Ps!aWqxzkRcOedW#G=jf6$7TNx0$9LAlWhX8ISZJCnkMxL z{N_P44a4(NVK|NY#d*N_MX7V!1U~aH+y`7sx@R=w;&#!R@0AA|=F97Yp%+&#G{H)f2a)rw^_7fLdV`c$3-;Xak z0P>;(Fqa(wdC`GdenOs~Wt?0DXtGY=GZz;MUA7B}&pbFA8!|8Do^y7ue-TAL|Duj4 zs^5$F&H3G4^y_cxh-`O*UULzkRcM{SXC9nQMtX&#i8+DioNbaxMfFYOH}}96X~&}| zG!#XB;z2YDrxk^xs82kI#^GqByd(a6NQg&7<^+E8KoS--oiZrZC(iDb zM2AMB@>lh4$#>2IxYUi3e|Qn~iK`Y>)F&Qvej&2!4v{&3C;q-G!Mpil5sJgqyk0={ z`@(75Wmkfn|1J)X3&0ZO+%As_z!E&HfDZiqqZZJGzwa&plv%(j{QdnYTya-|2b@Mv z;H*V|l!{aV&eLea4wU{r?%xY%_Hch+eE+Z);Of8}Do=9LUFHTnf9B8g0L)$H0akZg z*z?u`@C4bfWub7z6slJRH~oEX|6=|;55W9=9soaIuor+P$oc(p#r`c<{DWC7>KokHzUjPbNiNl_tR^aO4;qjjF&kOdWc)3E0Js>*(`}48`u)i-m06ky8 zJD>`vBq;c5`32&de>yq~$plwu0Y!mdnH&WW`}_L6yrl!S1l1l`m3ss$prX_G``R9` zCa4vt_By|PwH45fzwb)0&Z}oB{S7+t_gx9HU#24`Whq2L|!Rh^d_m#Nu z-Do%dzVluPy4zkX>YKO=f8UiL<7!ukKQ8yL%mTWxRa^<)e=U}O77|~5mY=2OwE{Zw z_gx7pKA#s{EwFk4-T3>i1kD2BJ zleX^gQQYM=f6o0-K=%c>%vD;h5`6cw6E9AqZJwQY(Sc|hK0TLuvRO(mucclpyn($< z%2k5TPoSzZskmI@EnLf{_BIt43AS-954E?C&9wx_`$cXoz|P(FHZ9*LsLNIWz#G`x zr2H5KROGU^kIiL*HCc>pfO~NrXk&as7kaK!@In70#au~(^SI@BIv&UC^?JhugcKYs zpy7Br{`R(JEOhFv11KI5Pj5?~qizgZVit9;aEB1~7MwPgI7`c#dO)L-S|xdXsRT zBxx)HGCSX-DQ^SFUE>p#pp~-^xA=$;oIlOj;!b39WQBQ_+Bka;@iq{5j2Ee=$aucK ze=!csAEet{A=$(v-==jgS{TFJHNFf0#}`ZN(D{ROm&$h#!#QVvsqt8@UXZ)S>-hoW z%lr@Rz za}lu1{}o5P&hwpRf?fVy97zIm*Z3yfe>K027Xeaylh`MaVwVz>yT-Sj-^ME);3Z-m z$W!PI_~`G*_=emyzlFE)c!rS$o+e2QTzAQqW15pMeW~Hi`>5_FM!&=Coh27VZWy@ zfY`q$FM!&=rxv+?PhSABe}9Ss+W7OX=LaV$Ke@|Zl twSP}7djFnUgMUA0(4aws1`Rq~{{b3wsj4%NU~&Ke002ovPDHLkV1ionE|&lR diff --git a/maps/away/unishi/unishi.dm b/maps/away/unishi/unishi.dm index 6808619093c..efdaf75c29f 100644 --- a/maps/away/unishi/unishi.dm +++ b/maps/away/unishi/unishi.dm @@ -1,6 +1,7 @@ #include "unishi_areas.dm" #include "unishi_jobs.dm" #include "../../../mods/content/xenobiology/_xenobiology.dme" +#include "../../../mods/content/insects/_insects.dme" #include "../../../mods/content/supermatter/_supermatter.dme" /obj/abstract/submap_landmark/joinable_submap/unishi diff --git a/maps/exodus/exodus-2.dmm b/maps/exodus/exodus-2.dmm index f323c39ad5f..2c90ba1f9c2 100644 --- a/maps/exodus/exodus-2.dmm +++ b/maps/exodus/exodus-2.dmm @@ -26489,7 +26489,7 @@ pixel_x = -21; pixel_y = -10 }, -/obj/machinery/honey_extractor, +/obj/machinery/centrifuge/mapped, /turf/floor/tiled/steel_grid, /area/exodus/hydroponics/garden) "bev" = ( diff --git a/maps/exodus/exodus.dm b/maps/exodus/exodus.dm index 47e23abbfe0..8dc28ef892b 100644 --- a/maps/exodus/exodus.dm +++ b/maps/exodus/exodus.dm @@ -3,7 +3,7 @@ #include "../../mods/content/mundane.dm" #include "../../mods/content/scaling_descriptors.dm" - #include "../../mods/content/beekeeping/_beekeeping.dme" + #include "../../mods/content/insects/_insects.dme" #include "../../mods/content/bigpharma/_bigpharma.dme" #include "../../mods/content/corporate/_corporate.dme" #include "../../mods/content/government/_government.dme" diff --git a/maps/ministation/ministation-1.dmm b/maps/ministation/ministation-1.dmm index 68b588d80e8..fa85bbafa22 100644 --- a/maps/ministation/ministation-1.dmm +++ b/maps/ministation/ministation-1.dmm @@ -5716,9 +5716,9 @@ /obj/item/tool/hoe/mini, /obj/item/tool/spade, /obj/item/tool/spade, -/obj/item/honey_frame, -/obj/item/honey_frame, -/obj/item/honey_frame, +/obj/item/hive_frame/crafted, +/obj/item/hive_frame/crafted, +/obj/item/hive_frame/crafted, /turf/floor/tiled, /area/ministation/hydro) "yg" = ( @@ -6040,7 +6040,7 @@ "zj" = ( /obj/effect/floor_decal/corner/beige/half, /obj/structure/table/glass, -/obj/machinery/honey_extractor, +/obj/machinery/centrifuge/mapped, /turf/floor/tiled, /area/ministation/hydro) "zl" = ( @@ -8744,7 +8744,7 @@ /turf/floor/tiled, /area/ministation/hall/e2) "Oo" = ( -/obj/machinery/beehive, +/obj/structure/apiary/mapped, /turf/floor/fake_grass, /area/ministation/hydro) "Op" = ( diff --git a/maps/ministation/ministation.dm b/maps/ministation/ministation.dm index 4b103d995e4..890bd7b889e 100644 --- a/maps/ministation/ministation.dm +++ b/maps/ministation/ministation.dm @@ -43,7 +43,7 @@ Twice... #include "../../mods/mobs/dionaea/_dionaea.dme" #include "../../mods/mobs/borers/_borers.dme" - #include "../../mods/content/beekeeping/_beekeeping.dme" + #include "../../mods/content/insects/_insects.dme" #include "../../mods/content/tabloids/_tabloids.dme" #include "../../mods/species/ascent/_ascent.dme" #include "../../mods/species/tajaran/_tajaran.dme" diff --git a/maps/tradeship/tradeship-0.dmm b/maps/tradeship/tradeship-0.dmm index ff571e169d9..05ce95d98f9 100644 --- a/maps/tradeship/tradeship-0.dmm +++ b/maps/tradeship/tradeship-0.dmm @@ -1562,7 +1562,7 @@ /turf/floor/tiled/steel_grid, /area/ship/trade/loading_bay) "yT" = ( -/obj/machinery/honey_extractor, +/obj/machinery/centrifuge/mapped, /obj/item/seeds/tomatoseed, /turf/floor, /area/ship/trade/aft_port_underside_maint) diff --git a/maps/tradeship/tradeship.dm b/maps/tradeship/tradeship.dm index 5a866a9c8b8..84352818b6d 100644 --- a/maps/tradeship/tradeship.dm +++ b/maps/tradeship/tradeship.dm @@ -11,7 +11,7 @@ #include "../../mods/content/government/away_sites/icarus/icarus.dm" #include "../../mods/content/corporate/away_sites/lar_maria/lar_maria.dm" - #include "../../mods/content/beekeeping/_beekeeping.dme" + #include "../../mods/content/insects/_insects.dme" #include "../../mods/content/mundane.dm" #include "../../mods/content/scaling_descriptors.dm" diff --git a/mods/content/beekeeping/apiary_bees_etc.dmi b/mods/content/beekeeping/apiary_bees_etc.dmi deleted file mode 100644 index 130120bb08fc84f15d808d0b4c8e302fb2bb46a3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3301 zcmb7{cU05a632fDEfSC}C`fZ@5{f7YigZwjKm?Z(j8qW`Md?yZL;@n9*cMcjB1Mo0 zC`m{_+9HS|-OvmHDOq|C36St`_nmj%pYQ!K_w&!3IdkVbbI;sVgu_{3!9#)o00`Tj zvvS(w*e`?d@12LV0>}5zAA8X)!s<%6Z`if#5!XUP03askPYfJO;aHQ3? z;Hj9@jKO?vvo4cbIBkLNX;`WZIPk@%P8!F!5U-03uF@3AT!L?z6`V zqERtb;~FAI_IObBIREXYL^`i)7oQ3NaY)AMNseUb4Yysdr@Dg<@6TEtCYS1~=wD__ z$h3@Kt^YoV56LN;!}@V)Gx7Q_DqJXxFMay59QF9FfH*ymq>^Imm@xOGl6%%M@}XkU zHy@&O)X11yt|~pO8sMJges+DKj;2BnJVY2F4gk;&TPyR6F*ysgGa+*A2U}MHEu|3@ zrm&>pg<`L}pH!g=X15(Hv3gCa1~MIYZGFnMG4+*2d97Q?H69SGuZG5NuWcT| zE5gnple@1+Cm6L}1V;~K`KD@DQlq-nj-wC@3j!o_R^^>%+HaW5+u}7+!U!vA$#a?L zdB=aCvzVwIyu7gOvB~7%Su2$~Q^`U})>@VPIR{IsWfRss*yiau6?NiudQ+|AhVQ2OPxYORMsH zW4zEqYu*%5)sQ7O&{~yfrDr9*}faZB~C9gVXfeQ7pQYijR zs-o#pf*wUkarL#6=AB<@b$=_O|@*tP8Gve1O@f z_}m{4KbZE*-|K4Z(tG<@BM0!{GZ#t_^$(tEQ8URNxS8FNU-yQnEB`MLo}1D-2|0ol zo;b|MIf5O;xx_wOuiITvCc`FwVteO^f8j+%UltL zeRg+1yuh;j{@jh@ff3tK7aKLO+%89oUK!VPx z@MRv7FM(ZUI|_QeNmk1?5?FzpxgYy?J^Y;tMr>RwF+b9U)eHFkd(!?}oJJ{F(uf!PRF$a6o)@e# z(9-LRTVb%9Vipys*xJs7eOtp(VHf!|+Vn8-rFPfmA~_8bHqRy_8+%Eo?DyWjj3Uko zrVZ~Na3KupM$t7*SP8s4)Y|LIp?~6ar+gb6ICpb_9ZuxtqHB#jD{RoLt@#eWG}-Uq zR-#=|A<>_VG)LpFb#RGc8AUr|A5Y8M&wtR_x^pv&a zSli5>9t;)C{WNBOfJf$3st2vEdu{U)qJ5%p`{|*aY&%r~P;9a^ljdrPx6sj$CLApp zxO#P>cH%~*oC!l*&6}ud9QHgV2z^?mHf3#~ldRJ_&}+>7IP#eg>ddm()loUo`Qt=Z3&#~q%V=CxqR0NAmY1~-BGAT1$xH$hZv$A442}pNcmPtt1#7&iqS?;Sorx-1 zLiuet3X))&I-01909X-DI2)6o8 zs*d0Sf1glVqzgN|`)VO1BY!|O-_HIXn}L}zjPs1~M2|D8>ze>=sYHbNTYacOgX|Ug z&qcN83Q?W?3E8efM#U?>CNTy@UrEetd;<MwZMP>@aC^Crf)de_wv~!3a&e`#K*Ie!~?mPr>rmY_Z}MS_$Rhk%HNn za<0JuQ)%;?K)$UszC90lJWpIhLhol}BVyo4F))1;(udZ{Xz)=B|81MxFTTB9MOC3| z_OEt%jCHRpxo-;vd-Td0S!+l&O~d{&_VSPbuQ8^w`CdA5%W;?Wo*fy=Wrthv#c5`8 z=|tEW?P5K**!VKS1SErxD2bo4+=$U0bwo<=&lzj?PS&$bq2RzbtWv3|2-0T z8E3rlD%?OfsucJSM&Y#26osA+?#_wD4KVPA6Zpv4;+Tsyp75*ZwbTN(jjs7V=w)#j8=mN&X#S2rlFQ)OcWtc#VXpI^Z4?dg&Xry#mAWkLVk* z$S8TwB;Pea54|4b~m>nA1|P3go@MMc#G diff --git a/mods/content/beekeeping/hives/_hive.dm b/mods/content/beekeeping/hives/_hive.dm deleted file mode 100644 index 5fe7f93c8d4..00000000000 --- a/mods/content/beekeeping/hives/_hive.dm +++ /dev/null @@ -1,166 +0,0 @@ -/obj/machinery/beehive - name = "apiary" - icon = 'mods/content/beekeeping/icons/beekeeping.dmi' - icon_state = "beehive-0" - desc = "A wooden box designed specifically to house our buzzling buddies. Far more efficient than traditional hives. Just insert a frame and a queen, close it up, and you're good to go!" - density = TRUE - anchored = TRUE - layer = BELOW_OBJ_LAYER - - var/closed = 0 - var/bee_count = 0 // Percent - var/smoked = 0 // Timer - var/honeycombs = 0 // Percent - var/frames = 0 - var/maxFrames = 5 - -/obj/machinery/beehive/Initialize() - . = ..() - update_icon() - -/obj/machinery/beehive/on_update_icon() - overlays.Cut() - icon_state = "beehive-[closed]" - if(closed) - overlays += "lid" - if(frames) - overlays += "empty[frames]" - if(honeycombs >= 100) - overlays += "full[round(honeycombs / 100)]" - if(!smoked) - switch(bee_count) - if(1 to 20) - overlays += "bees1" - if(21 to 40) - overlays += "bees2" - if(41 to 60) - overlays += "bees3" - if(61 to 80) - overlays += "bees4" - if(81 to 100) - overlays += "bees5" - -/obj/machinery/beehive/examine(mob/user) - . = ..() - if(!closed) - to_chat(user, "The lid is open.") - -/obj/machinery/beehive/attackby(var/obj/item/I, var/mob/user) - if(IS_CROWBAR(I)) - closed = !closed - user.visible_message("\The [user] [closed ? "closes" : "opens"] \the [src].", "You [closed ? "close" : "open"] \the [src].") - update_icon() - return TRUE - else if(IS_WRENCH(I)) - anchored = !anchored - user.visible_message("\The [user] [anchored ? "wrenches" : "unwrenches"] \the [src].", "You [anchored ? "wrench" : "unwrench"] \the [src].") - return TRUE - else if(istype(I, /obj/item/bee_smoker)) - if(closed) - to_chat(user, "You need to open \the [src] with a crowbar before smoking the bees.") - return TRUE - user.visible_message("\The [user] smokes the bees in \the [src].", "You smoke the bees in \the [src].") - smoked = 30 - update_icon() - return TRUE - else if(istype(I, /obj/item/honey_frame)) - if(closed) - to_chat(user, "You need to open \the [src] with a crowbar before inserting \the [I].") - return TRUE - if(frames >= maxFrames) - to_chat(user, "There is no place for an another frame.") - return TRUE - var/obj/item/honey_frame/H = I - if(H.honey) - to_chat(user, "\The [I] is full with beeswax and honey, empty it in the extractor first.") - return TRUE - ++frames - user.visible_message("\The [user] loads \the [I] into \the [src].", "You load \the [I] into \the [src].") - update_icon() - qdel(I) - return TRUE - else if(istype(I, /obj/item/bee_pack)) - var/obj/item/bee_pack/B = I - if(B.full && bee_count) - to_chat(user, "\The [src] already has bees inside.") - return TRUE - if(!B.full && bee_count < 90) - to_chat(user, "\The [src] is not ready to split.") - return TRUE - if(!B.full && !smoked) - to_chat(user, "Smoke \the [src] first!") - return TRUE - if(closed) - to_chat(user, "You need to open \the [src] with a crowbar before moving the bees.") - return TRUE - if(B.full) - user.visible_message("\The [user] puts the queen and the bees from \the [I] into \the [src].", "You put the queen and the bees from \the [I] into \the [src].") - bee_count = 20 - B.empty() - else - user.visible_message("\The [user] puts bees and larvae from \the [src] into \the [I].", "You put bees and larvae from \the [src] into \the [I].") - bee_count /= 2 - B.fill() - update_icon() - return TRUE - else if(istype(I, /obj/item/scanner/plant)) - to_chat(user, "Scan result of \the [src]...") - to_chat(user, "Beehive is [bee_count ? "[round(bee_count)]% full" : "empty"].[bee_count > 90 ? " Colony is ready to split." : ""]") - if(frames) - to_chat(user, "[frames] frames installed, [round(honeycombs / 100)] filled.") - if(honeycombs < frames * 100) - to_chat(user, "Next frame is [round(honeycombs % 100)]% full.") - else - to_chat(user, "No frames installed.") - if(smoked) - to_chat(user, "The hive is smoked.") - return TRUE - else if(IS_SCREWDRIVER(I)) - if(bee_count) - to_chat(user, "You can't dismantle \the [src] with these bees inside.") - return TRUE - to_chat(user, "You start dismantling \the [src]...") - playsound(loc, 'sound/items/Screwdriver.ogg', 50, 1) - if(do_after(user, 30, src)) - user.visible_message("\The [user] dismantles \the [src].", "You dismantle \the [src].") - new /obj/item/beehive_assembly(loc) - qdel(src) - return TRUE - return FALSE // this should probably not be a machine, so don't do any component interactions - -/obj/machinery/beehive/physical_attack_hand(var/mob/user) - if(closed) - return FALSE - . = TRUE - if(honeycombs < 100) - to_chat(user, "There are no filled honeycombs.") - return - if(!smoked && bee_count) - to_chat(user, "The bees won't let you take the honeycombs out like this, smoke them first.") - return - user.visible_message("\The [user] starts taking the honeycombs out of \the [src].", "You start taking the honeycombs out of \the [src]...") - while(honeycombs >= 100 && do_after(user, 30, src)) - new /obj/item/honey_frame/filled(loc) - honeycombs -= 100 - --frames - update_icon() - if(honeycombs < 100) - to_chat(user, "You take all filled honeycombs out.") - -/obj/machinery/beehive/Process() - if(closed && !smoked && bee_count) - pollinate_flowers() - update_icon() - smoked = max(0, smoked - 1) - if(!smoked && bee_count) - bee_count = min(bee_count * 1.005, 100) - update_icon() - -/obj/machinery/beehive/proc/pollinate_flowers() - var/coef = bee_count / 100 - var/trays = 0 - for(var/obj/machinery/portable_atmospherics/hydroponics/H in view(7, src)) - if(H.seed && !H.dead) - H.plant_health += 0.05 * coef - ++trays - honeycombs = min(honeycombs + 0.1 * coef * min(trays, 5), frames * 100) diff --git a/mods/content/beekeeping/icons/beekeeping.dmi b/mods/content/beekeeping/icons/beekeeping.dmi deleted file mode 100644 index 569122d4a58aa6cf16ca0e753e42a20d8684ffb1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5053 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 LF0}L^{MG*ef&6Bj diff --git a/mods/content/insects/_insects.dm b/mods/content/insects/_insects.dm new file mode 100644 index 00000000000..e69de29bb2d diff --git a/mods/content/insects/_insects.dme b/mods/content/insects/_insects.dme new file mode 100644 index 00000000000..db69acc46b8 --- /dev/null +++ b/mods/content/insects/_insects.dme @@ -0,0 +1,18 @@ +#ifndef MODPACK_INSECTS +#define MODPACK_INSECTS +// BEGIN_INCLUDE +#include "_insects.dm" +#include "centrifuge.dm" +#include "closets.dm" +#include "items.dm" +#include "materials.dm" +#include "recipes.dm" +#include "trading.dm" +#include "hives\_hive.dm" +#include "hives\hive_frame.dm" +#include "hives\hive_insects.dm" +#include "hives\hive_queen.dm" +#include "hives\hive_structure.dm" +#include "hives\hive_swarm.dm" +// END_INCLUDE +#endif diff --git a/mods/content/insects/centrifuge.dm b/mods/content/insects/centrifuge.dm new file mode 100644 index 00000000000..a6a10f2ec8b --- /dev/null +++ b/mods/content/insects/centrifuge.dm @@ -0,0 +1,3 @@ +/datum/storage/hopper/industrial/centrifuge/New() + can_hold |= /obj/item/hive_frame + ..() diff --git a/mods/content/insects/closets.dm b/mods/content/insects/closets.dm new file mode 100644 index 00000000000..dfbc8d91fa2 --- /dev/null +++ b/mods/content/insects/closets.dm @@ -0,0 +1,14 @@ +/obj/structure/closet/crate/hydroponics/beekeeping + name = "beekeeping crate" + desc = "All you need to set up your own beehive, except the beehive." + +/obj/structure/closet/crate/hydroponics/beekeeping/Initialize() + . = ..() + new /obj/item/bee_smoker(src) + new /obj/item/hive_frame/crafted(src) + new /obj/item/hive_frame/crafted(src) + new /obj/item/hive_frame/crafted(src) + new /obj/item/hive_frame/crafted(src) + new /obj/item/hive_frame/crafted(src) + new /obj/item/bee_pack(src) + new /obj/item/crowbar(src) diff --git a/mods/content/insects/hives/_hive.dm b/mods/content/insects/hives/_hive.dm new file mode 100644 index 00000000000..b50e1dd4720 --- /dev/null +++ b/mods/content/insects/hives/_hive.dm @@ -0,0 +1,187 @@ +/datum/extension/insect_hive + base_type = /datum/extension/insect_hive + expected_type = /obj/structure + flags = EXTENSION_FLAG_IMMEDIATE + /// The species of insect that made this hive. + var/decl/insect_species/holding_species + /// References to our current swarm effects gathering for the hive. + var/list/swarms + var/current_health = 100 + var/material = 10 + var/raw_reserves = 0 + /// Tracker for the last world.time that a frame was removed. + var/frame_last_removed = 0 + +/datum/extension/insect_hive/New(datum/holder, _species_decl) + ..() + holding_species = istype(_species_decl, /decl/insect_species) ? _species_decl : GET_DECL(_species_decl) + if(!istype(holding_species)) + CRASH("Insect hive extension instantiated with invalid insect species: '[_species_decl]'.") + START_PROCESSING(SSprocessing, src) + +/datum/extension/insect_hive/Destroy() + STOP_PROCESSING(SSprocessing, src) + if(length(swarms)) + for(var/obj/effect/insect_swarm/swarm as anything in swarms) + swarm.owner = null + swarms = null + var/atom/movable/hive = holder + if(istype(hive) && !QDELETED(hive)) + hive.queue_icon_update() + return ..() + +/datum/extension/insect_hive/Process() + holding_species.process_hive(src) + create_hive_products() + +/datum/extension/insect_hive/proc/handle_item_interaction(mob/user, obj/item/item) + return FALSE + +/datum/extension/insect_hive/proc/drop_nest(atom/drop_loc) + return + +/datum/extension/insect_hive/proc/get_nest_condition() + switch(current_health) + if(0, 10) + return "dying" + if(10, 30) + return "struggling" + if(30, 60) + return "sickly" + if(60, 90) + return null + return "thriving" + +/datum/extension/insect_hive/proc/get_nest_name() + return holding_species?.nest_name + +/datum/extension/insect_hive/proc/examined(mob/user, show_detail) + var/nest_descriptor = get_nest_condition() + if(nest_descriptor) + to_chat(user, SPAN_NOTICE("It contains \a [nest_descriptor] [get_nest_name()].")) + else + to_chat(user, SPAN_NOTICE("It contains \a [get_nest_name()].")) + +/datum/extension/insect_hive/proc/frame_removed(obj/item/frame) + frame_last_removed = world.time + +/datum/extension/insect_hive/proc/try_hand_harvest(mob/user) + return FALSE + +/datum/extension/insect_hive/proc/try_tool_harvest(mob/user, obj/item/tool) + return FALSE + +/datum/extension/insect_hive/proc/swarm_destroyed(obj/effect/insect_swarm/swarm) + LAZYREMOVE(swarms, swarm) + +/datum/extension/insect_hive/proc/swarm_at_hive() + for(var/atom/movable/swarm as anything in swarms) + if(get_turf(swarm) == get_turf(holder)) + return swarm + +/datum/extension/insect_hive/proc/has_material(amt) + return amt <= material + +/datum/extension/insect_hive/proc/consume_material(amt) + if(has_material(amt)) + material = clamp(material-amt, 0, 100) + return TRUE + return FALSE + +/datum/extension/insect_hive/proc/add_material(amt) + material = clamp(material+amt, 0, 100) + return TRUE + +/datum/extension/insect_hive/proc/add_reserves(amt) + raw_reserves = clamp(raw_reserves+amt, 0, 100) + return TRUE + +/datum/extension/insect_hive/proc/has_reserves(amt, raw_reserves_only = TRUE) + if(raw_reserves >= amt) + return TRUE + if(raw_reserves_only) + return FALSE + var/reserve = 0 + for(var/obj/item/frame in holder) + reserve += frame.reagents?.total_volume + if(reserve >= amt) + return TRUE + return FALSE + +/datum/extension/insect_hive/proc/consume_reserves(amt, raw_reserves_only = TRUE) + if(!has_reserves(amt, raw_reserves_only)) + return FALSE + if(raw_reserves >= amt) + raw_reserves -= amt + return TRUE + if(raw_reserves_only) + return FALSE + amt -= raw_reserves + raw_reserves = 0 + for(var/obj/item/frame in holder) + if(!frame.reagents?.total_volume) + continue + var/consume = min(amt, frame.reagents.total_volume) + frame.reagents.remove_any(consume) + amt -= consume + if(amt <= 0) + return TRUE + return FALSE + +/datum/extension/insect_hive/proc/adjust_health(amt) + current_health = clamp(current_health + amt, 0, 100) + if(current_health <= 0) + var/atom/movable/hive = holder + hive.visible_message(SPAN_DANGER("\The [holding_species.nest_name] sags and collapses.")) + remove_extension(holder, base_type) + +/datum/extension/insect_hive/proc/create_hive_products() + + var/atom/movable/hive = holder + if(!istype(hive) || !holding_species) + return TRUE + + if(!swarm_at_hive()) // nobody home to do the work + return TRUE + + // Naturally build up enough material for a new frame (or repairs). + if(!has_material(20)) + add_material(1) + + // Damaged hives cannot produce combs or honey. + if(current_health < 100) + if(consume_material(5)) + adjust_health(rand(3,5)) + return TRUE + + if(!has_reserves(20)) + return TRUE + + var/list/holder_contents = hive.get_contained_external_atoms() + for(var/obj/item/hive_frame/frame in holder_contents) + if(frame.reagents?.total_volume >= frame.reagents?.maximum_volume) + continue + var/fill_cost = min(frame.reagents.total_volume, REAGENTS_FREE_SPACE(frame.reagents)) + if(consume_material(5) && consume_reserves(fill_cost)) + holding_species.fill_hive_frame(frame) + return TRUE + + var/obj/item/native_frame = holding_species.native_frame_type + var/native_frame_size = initial(native_frame.w_class) + var/space_left = hive.storage.max_storage_space + for(var/obj/item/thing in hive.get_stored_inventory()) + space_left -= thing.w_class + if(space_left < native_frame_size) + return + + // Put a timer check on this to avoid a hive filling up with combs the moment you take 2 frames out. + if(world.time > (frame_last_removed + 2 MINUTES) && space_left >= native_frame_size && consume_material(20)) + // Frames start empty, and will be filled next run. + // Native 'frames' (combs) are bigger than crafted ones and aren't reusable. + new native_frame(holder, holding_species.produce_material) + hive.storage.update_ui_after_item_insertion() + +/datum/extension/insect_hive/proc/get_total_swarm_intensity() + . = 0 + for(var/obj/effect/insect_swarm/swarm as anything in swarms) + . += swarm.swarm_intensity diff --git a/mods/content/insects/hives/hive_frame.dm b/mods/content/insects/hives/hive_frame.dm new file mode 100644 index 00000000000..e66804728eb --- /dev/null +++ b/mods/content/insects/hives/hive_frame.dm @@ -0,0 +1,89 @@ +/obj/item/hive_frame + abstract_type = /obj/item/hive_frame + icon_state = ICON_STATE_WORLD + w_class = ITEM_SIZE_SMALL + material_alteration = MAT_FLAG_ALTERATION_ALL + var/destroy_on_centrifuge = FALSE + +/obj/item/hive_frame/Initialize(ml, material_key) + . = ..() + initialize_reagents() + +/obj/item/hive_frame/initialize_reagents(populate = TRUE) + create_reagents(20) + . = ..() + +/obj/item/hive_frame/on_reagent_change() + . = ..() + if(reagents?.total_volume) + SetName("filled [initial(name)] ([reagents.get_primary_reagent_name()])") + else + SetName(initial(name)) + queue_icon_update() + +/obj/item/hive_frame/on_update_icon() + . = ..() + var/mesh_state = "[icon_state]-mesh" + if(check_state_in_icon(mesh_state, icon)) + add_overlay(overlay_image(icon, mesh_state, COLOR_WHITE, RESET_COLOR)) + if(reagents?.total_volume) + var/comb_state = "[icon_state]-comb" + if(check_state_in_icon(comb_state, icon)) + add_overlay(overlay_image(icon, comb_state, reagents.get_color(), RESET_COLOR)) + +/obj/item/hive_frame/handle_centrifuge_process(obj/machinery/centrifuge/centrifuge) + if(!(. = ..())) + return + if(reagents.total_volume) + reagents.trans_to_holder(centrifuge.loaded_beaker.reagents, reagents.total_volume) + for(var/obj/item/thing in contents) + thing.dropInto(centrifuge.loc) + if(destroy_on_centrifuge) + for(var/atom/movable/thing in convert_matter_to_lumps()) + thing.dropInto(centrifuge.loc) + +/obj/item/hive_frame/honey/populate_reagents() + . = ..() + var/decl/insect_species/bees = GET_DECL(/decl/insect_species/honeybees) + bees.fill_hive_frame(src) + +/obj/item/hive_frame/Move() + var/datum/extension/insect_hive/hive = get_extension(loc, /datum/extension/insect_hive) + . = ..() + if(. && istype(hive) && loc != hive.holder) + hive.frame_removed(src) + +// Crafted frame used in apiaries. +/obj/item/hive_frame/crafted + name = "hive frame" + desc = "A wooden frame for insect hives that the workers will fill with products like honey." + icon = 'mods/content/insects/icons/frame.dmi' + material = /decl/material/solid/organic/wood + +// Raw version of honeycomb for wild hives. +/obj/item/hive_frame/comb + name = "comb" + icon = 'mods/content/insects/icons/comb.dmi' + material = /decl/material/solid/organic/wax + destroy_on_centrifuge = TRUE + material_alteration = MAT_FLAG_ALTERATION_COLOR + is_spawnable_type = FALSE + w_class = ITEM_SIZE_NORMAL // Larger than crafted frames, because you should use crafted frames in your hive. + +/obj/item/hive_frame/comb/Initialize(ml, material_key, decl/insect_species/spawning_hive) + . = ..() + if(istype(spawning_hive)) + SetName(spawning_hive.native_frame_name) + desc = spawning_hive.native_frame_desc + spawning_hive.fill_hive_frame(src) + +// Subtype for ant nests. +/obj/item/hive_frame/comb/honeypot_ant + +// Comb subtype for mapping a debugging. +/obj/item/hive_frame/comb/honey + is_spawnable_type = TRUE + color = COLOR_GOLD + +/obj/item/hive_frame/comb/honey/Initialize(ml, material_key) + return ..(ml, material_key, GET_DECL(/decl/insect_species/honeybees)) diff --git a/mods/content/insects/hives/hive_insects.dm b/mods/content/insects/hives/hive_insects.dm new file mode 100644 index 00000000000..756a483a6fd --- /dev/null +++ b/mods/content/insects/hives/hive_insects.dm @@ -0,0 +1,183 @@ +/decl/insect_species + abstract_type = /decl/insect_species + + // Descriptive strings for individual insects and swarms. + var/name_singular + var/name_plural + var/insect_desc + + // Vars for nest description and products. + var/nest_name + var/constructed_nest_name + var/list/produce_reagents + var/decl/material/produce_material + var/produce_material_amount = 1 + var/native_frame_name = "comb" + var/native_frame_desc = "A wax comb from an insect nest." + var/native_frame_type = /obj/item/hive_frame/comb + + // Visual appearance and behavior of swarms. + var/swarm_desc + var/swarm_color = COLOR_BROWN + var/swarm_icon = 'mods/content/insects/icons/swarm.dmi' + var/swarm_type = /obj/effect/insect_swarm + var/max_swarm_intensity = 100 + + // Venom delivered by swarms whens stinging a victim. + var/sting_reagent + var/sting_amount + +/decl/insect_species/Initialize() + if(produce_material) + produce_material = GET_DECL(produce_material) + return ..() + +/decl/insect_species/validate() + . = ..() + + if(!name_singular) + . += "no singular name set" + if(!name_plural) + . += "no plural name set" + if(!nest_name) + . += "no nest name set" + if(!constructed_nest_name) + . += "no constructed nest name set" + if(!insect_desc) + . += "no insect desc set" + + if(swarm_type) + if(!ispath(swarm_type, /obj/effect/insect_swarm)) + . += "invalid swarm path (must be /obj/effect/insect_swarm or subtype): '[swarm_type]'" + if(!swarm_desc) + . += "no swarm description set" + + if(produce_reagents) + if(!length(produce_reagents) || !islist(produce_reagents)) + . += "empty or non-list produce_reagents" + else + var/total = 0 + for(var/reagent in produce_reagents) + if(!ispath(reagent, /decl/material)) + . += "non-material produce_reagents entry '[reagent]'" + continue + var/amt = produce_reagents[reagent] + if(!isnum(amt) || amt <= 0) + . += "non-numerical or 0 produce_reagents value: '[reagent]', '[amt]'" + total += amt + if(total != 1) + . += "produce_reagents weighting does not sum to 1: '[total]'" + + if(produce_material) + if(!isnum(produce_material_amount) || produce_material_amount <= 0) + . += "non-numeric or zero produce amount: '[produce_material_amount]'" + if(!istype(produce_material, /decl/material)) + . += "non-material product material type: '[produce_material]'" + +/decl/insect_species/proc/fill_hive_frame(obj/item/frame) + + if(!istype(frame) || QDELETED(frame)) + return + + if(frame.reagents?.maximum_volume && length(produce_reagents)) + var/reagent_split = max(1, floor(min(REAGENTS_FREE_SPACE(frame.reagents), 20) / length(produce_reagents))) + for(var/reagent in produce_reagents) + frame.reagents.add_reagent(reagent, max(1, (reagent_split * produce_reagents[reagent])), defer_update = TRUE) + frame.reagents.update_total() + + if(produce_material && (frame.material != produce_material) && !(locate(/obj/item/stack/material/lump) in frame)) + produce_material.create_object(frame, produce_material_amount, /obj/item/stack/material/lump) + +/decl/insect_species/proc/try_sting(obj/effect/insect_swarm/swarm, atom/loc) + if(!istype(swarm) || QDELETED(swarm) || !istype(loc)) + return FALSE + // If we're agitated, always sting. Otherwise, % chance equal to a quarter of our overall swarm intensity. + if(!swarm.is_agitated() && !prob(max(1, round(swarm.swarm_intensity/4)))) + return FALSE + var/sting_mult = sting_amount * clamp(round(swarm.swarm_intensity/10), 1, 10) + for(var/mob/living/victim in loc) + if(!victim.simulated || victim.stat || victim.current_posture?.prone) + continue + var/datum/reagents/injected_reagents = victim.get_injected_reagents() + var/obj/item/organ/external/affecting = victim.get_organ(pick(global.all_limb_tags)) + if(!affecting || BP_IS_PROSTHETIC(affecting) || BP_IS_CRYSTAL(affecting)) + continue + if(injected_reagents && victim.can_inject(victim, affecting.organ_tag)) + injected_reagents.add_reagent(sting_reagent, sting_mult) + affecting.add_pain(sting_mult) + if(sting_mult <= sting_amount * 2) + to_chat(victim, SPAN_DANGER("You are stung on your [affecting.name] by \a [swarm]!")) + else + to_chat(victim, SPAN_DANGER("You are stung multiple times on your [affecting.name] by \a [swarm]!")) + . = TRUE + +/decl/insect_species/proc/process_hive(datum/extension/insect_hive/hive_metadata) + + // Sanity check. + var/atom/movable/hive = hive_metadata.holder + if(!istype(hive) || !swarm_type || !istype(hive_metadata)) + return + + // Reduce swarms if we have too many. + var/swarm_intensity = hive_metadata.get_total_swarm_intensity() + if(swarm_intensity > max_swarm_intensity) + var/obj/effect/insect_swarm/swarm = hive_metadata.swarms[1] + swarm.adjust_swarm_intensity(-(swarm_intensity-max_swarm_intensity)) + return + + // Try to grow an existing swarm until we're at our max. + if(hive_metadata.has_reserves(15) && length(hive_metadata.swarms)) + for(var/obj/effect/insect_swarm/swarm as anything in hive_metadata.swarms) + if(swarm.can_grow() && hive_metadata.consume_reserves(15)) + swarm.adjust_swarm_intensity(rand(3,5)) + return + + // Create a new swarm. + if(swarm_intensity < max_swarm_intensity && hive.loc && hive_metadata.has_reserves(15)) + + var/obj/effect/insect_swarm/swarm + for(var/obj/effect/insect_swarm/check_swarm as anything in hive_metadata.swarms) + if(check_swarm.loc == hive.loc && check_swarm.swarm_intensity < 100) + swarm = check_swarm + break + + if(!swarm) + swarm = new swarm_type(hive.loc, src, hive_metadata) + + if(!QDELETED(swarm) && istype(swarm) && hive_metadata.consume_reserves(15)) + LAZYDISTINCTADD(hive_metadata.swarms, swarm) + swarm.adjust_swarm_intensity(min((max_swarm_intensity-swarm_intensity), rand(3,5))) + +// Specific subtypes follow. +/decl/insect_species/honeybees + name_singular = "honeybee" + name_plural = "honeybees" + nest_name = "beehive" + native_frame_name = "honeycomb" + native_frame_desc = "A lattice of hexagonal wax cells usually filled with honey." + native_frame_type = /obj/item/hive_frame/comb/honeypot_ant + constructed_nest_name = "apiary" + swarm_desc = "A swarm of buzzing honeybees." + insect_desc = "A single buzzing honeybee." + swarm_color = COLOR_GOLD + swarm_type = /obj/effect/insect_swarm/pollinator + sting_reagent = /decl/material/liquid/bee_venom + sting_amount = 1 + produce_reagents = list(/decl/material/liquid/nutriment/honey = 1) + produce_material = /decl/material/solid/organic/wax + +/decl/insect_species/ants + name_singular = "ant" + name_plural = "ants" + nest_name = "anthill" + native_frame_name = "honeypot ant" + native_frame_desc = "A wiggling, bulbous honeypot ant." + constructed_nest_name = "ant farm" + swarm_desc = "A column of marching ants." + insect_desc = "A single ant." + swarm_color = COLOR_GRAY20 + swarm_type = /obj/effect/insect_swarm/forager + sting_reagent = /decl/material/liquid/bee_venom + sting_amount = 1 + produce_reagents = list(/decl/material/liquid/nutriment/honey = 1) + produce_material = /decl/material/solid/organic/wax diff --git a/mods/content/insects/hives/hive_queen.dm b/mods/content/insects/hives/hive_queen.dm new file mode 100644 index 00000000000..06137a81faf --- /dev/null +++ b/mods/content/insects/hives/hive_queen.dm @@ -0,0 +1,24 @@ + +/obj/item/bee_pack + name = "bee pack" + desc = "Contains a queen bee and some worker bees. Everything you'll need to start a hive!" + icon = 'mods/content/insects/icons/bee_pack.dmi' + material = /decl/material/solid/organic/plastic + var/contains_insects = /decl/insect_species/honeybees + +/obj/item/bee_pack/Initialize() + . = ..() + update_icon() + +/obj/item/bee_pack/on_update_icon() + . = ..() + if(contains_insects) + add_overlay("[icon_state]-full") + else + add_overlay("[icon_state]-empty") + +/obj/item/bee_pack/proc/empty() + SetName("empty [initial(name)]") + desc = "A stasis pack for moving bees. It's empty." + contains_insects = null + update_icon() diff --git a/mods/content/insects/hives/hive_structure.dm b/mods/content/insects/hives/hive_structure.dm new file mode 100644 index 00000000000..40660aa67ec --- /dev/null +++ b/mods/content/insects/hives/hive_structure.dm @@ -0,0 +1,76 @@ +/obj/structure/attackby(obj/item/used_item, mob/user) + if((. = ..())) + return + var/datum/extension/insect_hive/hive = get_extension(src, /datum/extension/insect_hive) + if(istype(hive) && hive.handle_item_interaction(user, used_item)) + return TRUE + +/obj/structure/attack_hand(mob/user) + if(has_extension(src, /datum/extension/insect_hive)) + var/datum/extension/insect_hive/hive = get_extension(src, /datum/extension/insect_hive) + if(hive.try_hand_harvest(user)) + return TRUE + return ..() + +/obj/structure/attackby(obj/item/used_item, mob/user) + if(has_extension(src, /datum/extension/insect_hive)) + var/datum/extension/insect_hive/hive = get_extension(src, /datum/extension/insect_hive) + if(hive.try_tool_harvest(user, used_item)) + return TRUE + return ..() + +/obj/structure/examine(mob/user, distance) + . = ..() + var/datum/extension/insect_hive/hive = get_extension(src, /datum/extension/insect_hive) + if(istype(hive)) + hive.examined(user, (distance <= 1)) + +/obj/structure/dismantle_structure(mob/user) + if(isatom(loc)) + var/datum/extension/insect_hive/hive = get_extension(src, /datum/extension/insect_hive) + if(istype(hive)) + hive.drop_nest(loc) + return ..() + +// 'proper' nest structure for building and mapping +/obj/structure/apiary + name = "apiary" + desc = "An artificial hive for raising insects, like bees, and harvesting products like honey." + icon = 'mods/content/insects/icons/apiary.dmi' + icon_state = ICON_STATE_WORLD + color = WOOD_COLOR_GENERIC + storage = /datum/storage/apiary + material = /decl/material/solid/organic/wood + material_alteration = MAT_FLAG_ALTERATION_ALL + +/obj/structure/apiary/attackby(obj/item/used_item, mob/user) + + if(istype(used_item, /obj/item/bee_pack)) + var/datum/extension/insect_hive/hive = get_extension(src, /datum/extension/insect_hive) + if(istype(hive)) + to_chat(user, SPAN_WARNING("\The [src] already contains \a [hive.holding_species.nest_name].")) + return TRUE + var/obj/item/bee_pack/pack = used_item + if(!pack.contains_insects) + to_chat(user, SPAN_WARNING("\The [pack] is empty!")) + return TRUE + user.visible_message(SPAN_NOTICE("\The [user] transfers the contents of \the [pack] into \the [src].")) + set_extension(src, /datum/extension/insect_hive, pack.contains_insects) + pack.empty() + return TRUE + + . = ..() + +/datum/storage/apiary + can_hold = list(/obj/item/hive_frame) + max_w_class = ITEM_SIZE_NORMAL + max_storage_space = ITEM_SIZE_SMALL * 5 // Five regular frames. + +/obj/structure/apiary/mapped/Initialize(ml, _mat, _reinf_mat) + . = ..() + for(var/_ = 1 to 5) + new /obj/item/hive_frame/crafted(src) + +/obj/structure/apiary/bees/Initialize(ml, _mat, _reinf_mat) + set_extension(src, /datum/extension/insect_hive, /decl/insect_species/honeybees) + . = ..() diff --git a/mods/content/insects/hives/hive_swarm.dm b/mods/content/insects/hives/hive_swarm.dm new file mode 100644 index 00000000000..ea5c79cdc33 --- /dev/null +++ b/mods/content/insects/hives/hive_swarm.dm @@ -0,0 +1,306 @@ +/obj/effect/insect_swarm + anchored = TRUE + is_spawnable_type = FALSE + icon_state = "0" + gender = NEUTER + default_pixel_z = 8 + layer = ABOVE_HUMAN_LAYER + movement_handlers = list(/datum/movement_handler/delay/insect_swarm) + + var/atom/move_target + var/datum/extension/insect_hive/owner + var/decl/insect_species/insect_type + var/swarm_agitation = 0 // A counter for disturbances to the hive or this swarm, causes them to sting people. + var/swarm_intensity = 1 // Percentage value; if it drops to 0, the swarm will be destroyed. + var/const/MAX_SWARM_STATE = 6 // if more states are added to swarm.dmi, increase this + var/next_work = 0 + +/datum/movement_handler/delay/insect_swarm + delay = 1 SECOND + +/datum/movement_handler/delay/insect_swarm/DoMove(direction, mob/mover, is_external) + ..() + step(host, direction) + return MOVEMENT_HANDLED + +/obj/effect/insect_swarm/debug/Initialize(mapload) + . = ..(mapload, _insect_type = /decl/insect_species/honeybees) + +/obj/effect/insect_swarm/Initialize(mapload, _insect_type, _hive) + . = ..() + insect_type = istype(_insect_type, /decl/insect_species) ? _insect_type : GET_DECL(_insect_type) + owner = _hive + if(!istype(insect_type)) + PRINT_STACK_TRACE("Insect swarm created with invalid insect type: '[_insect_type]'") + return INITIALIZE_HINT_QDEL + if(!istype(owner)) + PRINT_STACK_TRACE("Insect swarm created with invalid hive: '[owner]'") + return INITIALIZE_HINT_QDEL + color = insect_type.swarm_color + icon = insect_type.swarm_icon + update_swarm() + START_PROCESSING(SSobj, src) + +/obj/effect/insect_swarm/Destroy() + if(owner) + owner.swarm_destroyed(src) + owner = null + stop_automove() + STOP_PROCESSING(SSobj, src) + return ..() + +/obj/effect/insect_swarm/proc/update_swarm() + icon_state = num2text(ceil((swarm_intensity / 100) * MAX_SWARM_STATE)) + if(icon_state == "1") + SetName(insect_type.name_singular) + desc = insect_type.insect_desc + gender = NEUTER + else + SetName(insect_type.name_plural) + desc = insect_type.swarm_desc + gender = PLURAL + + // Some icon variation via transform. + if(prob(75)) + var/matrix/swarm_transform = matrix() + swarm_transform.Turn(pick(90, 180, 270)) + +/obj/effect/insect_swarm/proc/is_agitated() + return QDELETED(owner) + +/obj/effect/insect_swarm/proc/find_target() + for(var/mob/living/victim in view(7, src)) + if(!victim.simulated || victim.stat || victim.current_posture?.prone) + continue + if(victim.isSynthetic()) + continue + return victim + +/obj/effect/insect_swarm/proc/merge(obj/effect/insect_swarm/other_swarm) + + // If we can fit into one swarm, just merge us together. + var/total_intensity = swarm_intensity + other_swarm.swarm_intensity + if(total_intensity <= 100) + swarm_intensity = total_intensity + swarm_agitation = max(swarm_agitation, other_swarm.swarm_agitation) + update_swarm() + qdel(other_swarm) + return + + // Otherwise equalize between swarms. + swarm_intensity = floor(total_intensity / 2) + other_swarm.swarm_intensity = total_intensity - swarm_intensity + swarm_agitation = max(swarm_agitation, other_swarm.swarm_agitation) + other_swarm.swarm_agitation = max(swarm_agitation, other_swarm.swarm_agitation) + update_swarm() + other_swarm.update_swarm() + +/obj/effect/insect_swarm/Move() + . = ..() + // Swarms from the same hive in the same loc merge together. + if(. && loc && !QDELETED(src)) + try_consolidate_swarms() + +/obj/effect/insect_swarm/Process() + + // Swarms on a loc should try to merge if possible. + try_consolidate_swarms() + if(QDELETED(src)) + return + + // Swarms with no hive gradually decay to nothing. + if(!owner) + adjust_swarm_intensity(-(rand(1,3))) + if(QDELETED(src)) + return + + // Angry swarms move with purpose. + if(!move_target || !(move_target in view(5, src))) + stop_automove() + if(is_agitated()) + if(!move_target) + move_target = find_target() + if(move_target) + start_automove(move_target) + return + + // Large swarms split if they aren't agitated. + if(can_split() && isturf(loc)) + var/turf/our_turf = loc + for(var/turf/swarm_turf as anything in RANGE_TURFS(our_turf, 1)) + if(swarm_turf == loc || !swarm_turf.CanPass(src)) + continue + var/new_intensity = round(swarm_intensity/2) + var/obj/effect/insect_swarm/new_swarm = new type(swarm_turf, insect_type, owner) + new_swarm.swarm_intensity = new_intensity + new_swarm.swarm_agitation = swarm_agitation + new_swarm.update_swarm() + LAZYADD(owner.swarms, new_swarm) + swarm_intensity -= new_intensity + update_swarm() + break + + if(insect_type.sting_amount || insect_type.sting_reagent) + insect_type.try_sting(src, loc) + + // Hive behavior is dictated by the hive. + if(owner) + handle_hive_behavior() + return + + // If we're not agitated and don't have a hive, we probably shouldn't be pathing somewhere. + stop_automove() + + // Idle swarms with no hive just wander around. + if(prob(5)) + SelfMove(pick(global.alldirs)) + +/obj/effect/insect_swarm/proc/is_first_swarm_at_hive() + var/atom/movable/hive = owner?.holder + if(!isturf(hive?.loc) || loc != hive.loc) + return FALSE + if(length(owner?.swarms) == 1) + return TRUE + for(var/obj/effect/insect_swarm/swarm in hive.loc) + if(swarm == src) + return TRUE + if(swarm in owner.swarms) + break + return FALSE + +/obj/effect/insect_swarm/get_automove_target(datum/automove_metadata/metadata) + return move_target + +/obj/effect/insect_swarm/stop_automove() + SHOULD_CALL_PARENT(FALSE) + move_target = null + //. = ..() // TODO work out why they're not automoving + walk(src, 0) + +/obj/effect/insect_swarm/start_automove(target, movement_type, datum/automove_metadata/metadata) + SHOULD_CALL_PARENT(FALSE) + move_target = target + //. = ..() // TODO work out why they're not automoving + if(move_target) + walk_to(src, move_target, 0, 7) + else + walk(src, 0) + +/obj/effect/insect_swarm/proc/handle_hive_behavior() + // If we are the first (or only) of our owner swarms in the loc, we don't move. Hive needs workers. + var/atom/movable/hive = owner?.holder + if(is_first_swarm_at_hive()) + stop_automove() + return + + if(!hive_has_swarm() && loc != hive.loc) + start_automove(owner.holder) + return + + do_work() + +/obj/effect/insect_swarm/proc/do_work() + stop_automove() + if(prob(25)) + var/step_dir = pick(global.alldirs) + if(get_dist(owner.holder, get_step(loc, step_dir)) <= 2) + SelfMove(step_dir) + +/obj/effect/insect_swarm/proc/hive_has_swarm() + var/atom/movable/hive = owner?.holder + if(!isturf(hive?.loc)) + return FALSE + for(var/obj/item/swarm as anything in owner.swarms) + if(swarm.loc == hive.loc) + return TRUE + return FALSE + +/obj/effect/insect_swarm/proc/adjust_swarm_intensity(amount) + var/old_intensity = swarm_intensity + swarm_intensity = clamp(swarm_intensity + amount, 0, 100) + if(old_intensity != swarm_intensity) + if(swarm_intensity <= 0) + qdel(src) + else + update_swarm() + +/obj/effect/insect_swarm/proc/can_grow() + // higher swarm intensity is only seen during agitated states when they converge on a victim and merge. + return swarm_intensity < 50 + +/obj/effect/insect_swarm/proc/can_merge() + return swarm_intensity < (is_agitated() ? 100 : 50) + +/obj/effect/insect_swarm/proc/can_split() + return !is_agitated() && swarm_intensity >= 50 + +/obj/effect/insect_swarm/proc/try_consolidate_swarms() + if(!can_merge()) + return + for(var/obj/effect/insect_swarm/other_swarm in loc) + if(other_swarm == src || !other_swarm.can_merge() || other_swarm.owner != owner || other_swarm.insect_type != insect_type) + continue + merge(other_swarm) + return + +/obj/effect/insect_swarm/pollinator + var/pollen = 0 + +/obj/effect/insect_swarm/pollinator/do_work() + + // Have a rest/do some work. + if(world.time < next_work) + return + + // Unload pollen into hive. + if(pollen) + if(loc == get_turf(owner.holder)) + visible_message("\ref[src] unloading pollen.") + owner.add_reserves(pollen) + pollen = 0 + next_work = world.time + 5 SECONDS + stop_automove() + else + visible_message("\ref[src] returning with pollen.") + start_automove(owner.holder) + return + + // Move to flowers. + if(move_target) + if(!(move_target in view(src, 7))) + visible_message("\ref[src] lost target.") + move_target = null + stop_automove() + else + visible_message("\ref[src] moving to existing target.") + start_automove(move_target) + return + + // Harvest from flowers in our loc. + for(var/obj/machinery/portable_atmospherics/hydroponics/flower in loc) + if(!flower.pollen) + continue + pollen += flower.pollen + flower.pollen = 0 + next_work = world.time + 5 SECONDS + stop_automove() + visible_message("\ref[src] harvesting pollen.") + return + + // Find a flower. + var/closest_dist + var/atom/closest_target + for(var/obj/machinery/portable_atmospherics/hydroponics/flower in view(src, 7)) + if(!flower.pollen) + continue + var/next_dist = get_dist(src, closest_target) + if(isnull(closest_dist) || next_dist < closest_dist) + closest_target = flower + closest_dist = next_dist + + if(closest_target) + start_automove(closest_target) + else + start_automove(owner.holder) + +/obj/effect/insect_swarm/forager diff --git a/mods/content/insects/icons/apiary.dmi b/mods/content/insects/icons/apiary.dmi new file mode 100644 index 0000000000000000000000000000000000000000..7ad80a4408f3f53bf80d2991b201ac53255a1200 GIT binary patch literal 345 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnF3?v&v(vJfvp#Yx{*8>L*bal;3O)5Kd@Z|sh z|2^Gf=I13z0c9CWg8YIR9G=}s19CE}LLy3BQj3#|G7CyF^Ya)OD&_=-6%>_z{}NpA z@#_;UZ(Xf(XU+$22sOB9{NRz!c^}P_3`IS?J1mTYj4vB|Da?5^>ByuIh2WJo`!&>E zJG+flH?NR5VgBTikG9tt-_{LNMPvTP95}|rV9g|D%#fx16liIKr;B5V#>C`=1zZUt zEzFD?7$$Ep6p_$!V2xS8px3alsYI6}Tal^9t3aV4mUVJ~hKR-?mV~xLtO?hE*iFNk zwI^ZP0hT$5X)_#HA}$_o7S%|9vSbm@5+((vHiw-KfeMN|q7D*WOPF{HRynk|Gwj+L m!01xp!0Pl+pL*Xr`G?I$Q%3*s){B zB`@dyUyI#M6gxUPfKqQ#KSu*47)yfuf*Bm1-ADs+Dyu>wN?cNllZ!G7N;32F7#J$% z1cwzAm45#cT=4Ph6D@CDt#fD22X6>9xM=*~kW=W z2U{2ZKQ;Y0!?BF;slHbwFC2)R&3N5mGIM5w%7cT8s~euO-DuV0;O1~`mufsSDCboFyt I=akR{0DX&+ApigX literal 0 HcmV?d00001 diff --git a/mods/content/insects/icons/beehive.dmi b/mods/content/insects/icons/beehive.dmi new file mode 100644 index 0000000000000000000000000000000000000000..9b58ec19c1b74072539d0b269117bffdb25863f9 GIT binary patch literal 319 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnF3?v&v(vJfvp#Yx{*8>L*bal;3O)C5U|No(b zCq3O`G6EF?fwGJxL4Lsu4$p3+0XdmfArU1msl~}fnFS@8`FRWs6?1~a3W`dz*aDW`eZT9n~V9_3xE38Pxk{*69CrV=-0C=2r%DoDJAQ(pB+3zY|YA*U)UBU>5x`#?jp%PT;_8XcS+RlL|Mcegq zgcu}U+6U`Sa5Q;A?6xWzaMTqsqwQwmJ0VU{gCn_}-&I0PSs%br5?lRr=(oQrTih}K z(G=Y%AO=Yf@ZToKT(=aW000AcNkl^8@84&$*)NSoL?)Fc`E- zrP7;3BJqpI-tHCs~&JUa!|4jYhVnX}>lajn~O! zvKoy>yKeS&Re)g_uq+Fjropl-Twh-!6bd00i(xPrAP@*391f$`>*46=2=#jXX^9`_ zvzn%vmSwRwH#Z1{LVw0$vD?95uoege`r&Z6-|O|5!^1;!9{!q}H%XEN01!nHj^lt3 zf^<5KcDoJJG_kd{gLYPb@!?xS)H>PR6df+pJkZvZE zVRv_TyKece=Kw`f#)e@)k|d;3DG)+Xuh(H&76>5_MG=$NCn^?;&o&DvigGr?7YHHU z2R=&(>58IgJ;D!vZ{N8lK}nKe7zXzD_u)7WR8pLLP)V=-0C=2JR&a84_w-Y6@%7{?OD!tS%+FJ>RWQ*r;NmRLOex7wuvIWN;^NFm z%}mcIfpCgT5=&AQY!#Hs^NVs)l(;xkGK-28Y!wW-IMa#}b5kK~R8_jUsl^#YDoxJM zO~R#AS;5uMMZv|_lM4XHeKTkF(4LV100dh}L_t(|ob8%VNF!Gq$GM|Y zp(G6p;z1&F5Zcj$f<1@_K}0Aicr3jwvPVzL_Ts&NZh{06JbE+ks@NcuQUYO28dgm- zf2MIrCTkp%^iT|OQ8VVxK5gc6N#+f_-}m=@zc+brUUWX653NiaX#u3s3?MClG@1dV z1(2rY4WRC7^!xuVH9F8yFo=ckzST9l-+H;z004OT^7~uY@80_H^*7vuZSUUt%ilk4 zKMDrx7TXN&G~g&0yp;?99Q+=5@cO>ChuWV4_f~+~1fOHlmFVtU3cq>%YeSy{ORpB6 z-24X~FaY?^1bcrUe*W#vKPM+AU>F99qTuT43Jk*_7K?#pS;S(o^1WCrhFmTOv)POv ze^@r#`}^vC`0+e1))GG&jeb$B?|Y{}C81CVcDo%H7Z=E8vj9;3j6@;;fOtHP?(S}| zEQ@$NUURaaI=(6coK7chMxedD{mv0cBofGGv!E!dJOYiPnekOu0DxQ0R*rz(Zim5O zD7WeLdL$BwMiAU+`0B+A{Pg;l znt#(8u>-tsLG$6m2LPz6+8$g90`)D4pX&0rlBx#$-)Z?bLYe`j1&~HFfV2S8Xal0-?8MD6eIf7a2_k=fkb z{Bm}7w))BB6F`*#%gf7uS}c|eo6XifFfb6Cn3#wqlS#eRYV8;r8cLf?rt5q@f9>&j z&cfkv=gP{;ACr@lsb-6B1gZ=W1Oc)vTRa|5NS0+wBocw!?Y^3tnulAA zOL@Is%iP>tN)UwRDsK>~PlkA&-xdS`E|&{yYilgWaj8Hc09lqT9LJ>`4u^bm|Aw8N zo$+e@KC2fZE|&{}AYgue{(YfPkf*1oWuwuU^85XkT9SVXJnC$a=lSis+UrT4bOk)> zo#0JkJkM`eCi(I8bAtHkFzE_t`K7RC0BHfF(F`ChfHax`qy>;hGk~-J(zLt*{{mX> Vni&pM#1Q}h002ovPDHLkV1g+&Tdx2B literal 0 HcmV?d00001 diff --git a/mods/content/insects/icons/smoker.dmi b/mods/content/insects/icons/smoker.dmi new file mode 100644 index 0000000000000000000000000000000000000000..e6b14a8576cda18f059629c075ec5a46516c1f7d GIT binary patch literal 480 zcmV<60U!Q}P)V=-0C=2JR&a84_w-Y6@%7{?OD!tS%+FJ>RWQ*r;NmRLOex6#a*U0*I5Sc+ z(=$pSoZ^zil2jm5sXV_ZCq;>iGbOXA7|1u|;!G<_%uR)`;i@u9a})FOGgB0j@>42x zi*xcz;*(NyN)Y-?h}KsWpOKiCLXOTHygHQ?T>V_YK>z@Og;3&NQFc85007iUL_t(& zf$f+}4#F@DMC%3n7^&d^h=Xv0cEJ(2{}o7AILJzHrb4Qiw`$Vn$^FTrOcNnHqA5Y^ z<$A{o5LHZYzCEy8$WKr(t73u+SV7tKGlSDa8m?6o?A z7*hU)*i$2f5JG;4c#*B1Y`0EbJ;7?$NW1%sFR-Idd~gMe9d+V^d$yWnFDHZ$@_(2- z;O2$C;GGBDyzqs$HjDt|*t`IL WG7y)tHV;++0000V=-0C=2J zR&a84_w-Y6@%7{?OD!tS%+FJ>RWQ*r;NmRLOex6#a*U0*I5Sc+(=$pSoZ^zil2jm5 z$v}yVGbOXA7|1r{;!G<_%uR)`;VKNVt1ttKrsgD80+~iSaEQ=mgk75@R&D5-jmfjq zggkAg1VIpP=E)`bg$(EWxzFaRs2vf)T9ilpwumJRE7-|%>5mY;1#v)<|-QZ z?Z|);!<5D>7emjJaG);mUL>xr3`Q>=44Ts^k_1Tb{>d<1-dOSwJhG$OCBi z33*DnTR8cE zW(1R{(<5Yh3|70j(MBpjZ@r9{GxgFa0>h~}7S&HrMK2+v`cpQ@mG|o4U1&7fb_^!> zB7flXLdJQVrgMGKC#latr(he^sia*+p$pqY4;e2r6Myx_-%2A;Q;fjoMKr8!KQs5V z%h7LB_3o0mff5Qc(1(`gXifcPuZIhrpCeRP?3KXMioX