From ae4489cb2fa6bbb07ef9144496315cfb92655818 Mon Sep 17 00:00:00 2001
From: PollardTheDragon <144391971+PollardTheDragon@users.noreply.github.com>
Date: Tue, 28 Oct 2025 16:53:18 -0400
Subject: [PATCH] Converts bees to basic mobs (#30662)
* Converts bees to basic mobs
* Undefs
* Linters
---
.../RandomRuins/SpaceRuins/abandonedzoo.dmm | 18 +-
_maps/map_files/stations/emeraldstation.dmm | 2 +-
code/__DEFINES/ai/ai_defines.dm | 2 +
code/__DEFINES/ai/blackboard_defines.dm | 6 +
code/datums/diseases/beesease.dm | 2 +-
.../objects/items/weapons/bee_briefcase.dm | 5 +-
.../objects/items/weapons/melee/melee_misc.dm | 4 +-
.../events/shuttle_loan/shuttle_loan_datum.dm | 2 +-
code/modules/hydroponics/beekeeping/beebox.dm | 34 +-
code/modules/hydroponics/hydroponics_tray.dm | 7 +
.../basic/hostile/bees/bee_ai_behaviors.dm | 106 +++++
.../basic/hostile/bees/bee_ai_subtree.dm | 108 +++++
.../mob/living/basic/hostile/bees/bees.dm | 284 +++++++++++++
.../living/carbon/human/species/tajaran.dm | 2 +-
.../mob/living/carbon/human/species/unathi.dm | 2 +-
.../living/carbon/human/species/vulpkanin.dm | 2 +-
.../mob/living/simple_animal/hostile/bees.dm | 394 ------------------
code/modules/mob/mob_grab.dm | 4 +-
.../chemistry/recipes/pyrotechnics.dm | 2 +-
code/modules/research/strange_objects.dm | 2 +-
paradise.dme | 4 +-
tools/ci/check_simplemob_additions.py | 3 -
22 files changed, 561 insertions(+), 434 deletions(-)
create mode 100644 code/modules/mob/living/basic/hostile/bees/bee_ai_behaviors.dm
create mode 100644 code/modules/mob/living/basic/hostile/bees/bee_ai_subtree.dm
create mode 100644 code/modules/mob/living/basic/hostile/bees/bees.dm
delete mode 100644 code/modules/mob/living/simple_animal/hostile/bees.dm
diff --git a/_maps/map_files/RandomRuins/SpaceRuins/abandonedzoo.dmm b/_maps/map_files/RandomRuins/SpaceRuins/abandonedzoo.dmm
index 88ee989e5be..d12186835e9 100644
--- a/_maps/map_files/RandomRuins/SpaceRuins/abandonedzoo.dmm
+++ b/_maps/map_files/RandomRuins/SpaceRuins/abandonedzoo.dmm
@@ -81,16 +81,16 @@
/turf/simulated/floor/grass,
/area/ruin/space/unpowered)
"am" = (
-/mob/living/simple_animal/hostile/poison/bees,
-/mob/living/simple_animal/hostile/poison/bees,
-/mob/living/simple_animal/hostile/poison/bees,
-/mob/living/simple_animal/hostile/poison/bees,
-/mob/living/simple_animal/hostile/poison/bees,
-/mob/living/simple_animal/hostile/poison/bees,
+/mob/living/basic/bee,
+/mob/living/basic/bee,
+/mob/living/basic/bee,
+/mob/living/basic/bee,
+/mob/living/basic/bee,
+/mob/living/basic/bee,
/turf/simulated/floor/grass,
/area/ruin/space/unpowered)
"an" = (
-/mob/living/simple_animal/hostile/poison/bees,
+/mob/living/basic/bee,
/turf/simulated/floor/grass,
/area/ruin/space/unpowered)
"ao" = (
@@ -120,7 +120,7 @@
/area/ruin/space/unpowered)
"au" = (
/obj/structure/flora/ausbushes/leafybush,
-/mob/living/simple_animal/hostile/poison/bees,
+/mob/living/basic/bee,
/turf/simulated/floor/grass,
/area/ruin/space/unpowered)
"av" = (
@@ -163,7 +163,7 @@
/area/ruin/space/unpowered)
"aB" = (
/obj/machinery/light,
-/mob/living/simple_animal/hostile/poison/bees,
+/mob/living/basic/bee,
/turf/simulated/floor/grass,
/area/ruin/space/unpowered)
"aC" = (
diff --git a/_maps/map_files/stations/emeraldstation.dmm b/_maps/map_files/stations/emeraldstation.dmm
index 8af2f62e1e1..3a1d98b369c 100644
--- a/_maps/map_files/stations/emeraldstation.dmm
+++ b/_maps/map_files/stations/emeraldstation.dmm
@@ -73977,7 +73977,7 @@
/obj/structure/closet{
name = "Bee-keeping suits"
},
-/mob/living/simple_animal/hostile/poison/bees,
+/mob/living/basic/bee,
/obj/effect/decal/cleanable/dirt,
/turf/simulated/floor/plating,
/area/station/maintenance/security/aft_port)
diff --git a/code/__DEFINES/ai/ai_defines.dm b/code/__DEFINES/ai/ai_defines.dm
index 7923d351393..4c893f37f0e 100644
--- a/code/__DEFINES/ai/ai_defines.dm
+++ b/code/__DEFINES/ai/ai_defines.dm
@@ -61,6 +61,8 @@
/// Continue processing while in stasis
#define AI_FLAG_CAN_ACT_IN_STASIS (1<<3)
+#define PASSIVE_AI_FLAGS (AI_FLAG_PAUSE_DURING_DO_AFTER | AI_FLAG_STOP_MOVING_WHEN_PULLED)
+
// Base Subtree defines
/// This subtree should cancel any further planning, (Including from other subtrees)
diff --git a/code/__DEFINES/ai/blackboard_defines.dm b/code/__DEFINES/ai/blackboard_defines.dm
index d6f98e271c5..49a8b290ba1 100644
--- a/code/__DEFINES/ai/blackboard_defines.dm
+++ b/code/__DEFINES/ai/blackboard_defines.dm
@@ -233,6 +233,12 @@
/// Are we active?
#define BB_MALF_DRONE_PASSIVE "BB_MALF_DRONE_PASSIVE"
+// bee keys
+/// the bee hive we live inside
+#define BB_CURRENT_HOME "BB_current_home"
+/// the hydro we will pollinate
+#define BB_TARGET_HYDRO "BB_target_hydro"
+
// Goliath AI keys
/// Key where we store the tentacleing ability
#define BB_GOLIATH_TENTACLES "BB_GOLIATH_TENTACLES"
diff --git a/code/datums/diseases/beesease.dm b/code/datums/diseases/beesease.dm
index ef4f0279737..cfc1038e092 100644
--- a/code/datums/diseases/beesease.dm
+++ b/code/datums/diseases/beesease.dm
@@ -34,5 +34,5 @@
if(prob(1))
affected_mob.visible_message("[affected_mob] coughs up a swarm of bees!", \
"You cough up a swarm of bees!")
- new /mob/living/simple_animal/hostile/poison/bees(affected_mob.loc)
+ new /mob/living/basic/bee(affected_mob.loc)
return
diff --git a/code/game/objects/items/weapons/bee_briefcase.dm b/code/game/objects/items/weapons/bee_briefcase.dm
index e81bd33ee98..f1697aa814e 100644
--- a/code/game/objects/items/weapons/bee_briefcase.dm
+++ b/code/game/objects/items/weapons/bee_briefcase.dm
@@ -65,7 +65,8 @@
//Release up to 5 bees per use. Without using Lazarus Reagent, that means two uses. WITH Lazarus Reagent, you can get more if you don't release the last bee
for(var/bee = min(5, bees_left), bee > 0, bee--)
- var/mob/living/simple_animal/hostile/poison/bees/syndi/B = new /mob/living/simple_animal/hostile/poison/bees/syndi(get_turf(user)) // RELEASE THE BEES!
- B.master_and_friends = blood_list.Copy() //Doesn't automatically add the person who opens the case, so the bees will attack the user unless they gave their blood
+ var/mob/living/basic/bee/syndi/B = new /mob/living/basic/bee/syndi(get_turf(user)) // RELEASE THE BEES!
+ for(var/mob/living/fren in blood_list)
+ B.befriend(fren)
bees_released++
bees_left -= bees_released
diff --git a/code/game/objects/items/weapons/melee/melee_misc.dm b/code/game/objects/items/weapons/melee/melee_misc.dm
index 4e641d61386..cdc5d3b4614 100644
--- a/code/game/objects/items/weapons/melee/melee_misc.dm
+++ b/code/game/objects/items/weapons/melee/melee_misc.dm
@@ -50,11 +50,11 @@
/obj/item/melee/flyswatter/Initialize(mapload)
. = ..()
strong_against = typecacheof(list(
- /mob/living/simple_animal/hostile/poison/bees/,
+ /mob/living/basic/bee/,
/mob/living/basic/butterfly,
/mob/living/basic/cockroach,
/obj/item/queen_bee))
- strong_against -= /mob/living/simple_animal/hostile/poison/bees/syndi // Syndi-bees have special anti-flyswatter tech installed
+ strong_against -= /mob/living/basic/bee/syndi // Syndi-bees have special anti-flyswatter tech installed
/obj/item/melee/flyswatter/attack__legacy__attackchain(mob/living/M, mob/living/user, def_zone)
. = ..()
diff --git a/code/modules/events/shuttle_loan/shuttle_loan_datum.dm b/code/modules/events/shuttle_loan/shuttle_loan_datum.dm
index d14f107bc69..d395a409a85 100644
--- a/code/modules/events/shuttle_loan/shuttle_loan_datum.dm
+++ b/code/modules/events/shuttle_loan/shuttle_loan_datum.dm
@@ -80,7 +80,7 @@
for(var/i in 1 to 8)
// A large influx of bees, directly into the cargo workplace
- spawn_list.Add(/mob/living/simple_animal/hostile/poison/bees)
+ spawn_list.Add(/mob/living/basic/bee)
for(var/i in 1 to 5)
var/decal = pick(/obj/effect/decal/cleanable/blood, /obj/effect/decal/cleanable/insectguts)
diff --git a/code/modules/hydroponics/beekeeping/beebox.dm b/code/modules/hydroponics/beekeeping/beebox.dm
index 6b2363ca660..2a2e943bed2 100644
--- a/code/modules/hydroponics/beekeeping/beebox.dm
+++ b/code/modules/hydroponics/beekeeping/beebox.dm
@@ -11,7 +11,7 @@
return FALSE
-/mob/living/simple_animal/hostile/poison/bees/bee_friendly()
+/mob/living/basic/bee/bee_friendly()
return TRUE
/mob/living/simple_animal/bot/bee_friendly()
@@ -37,7 +37,7 @@
icon_state = "beebox"
anchored = TRUE
density = TRUE
- var/mob/living/simple_animal/hostile/poison/bees/queen/queen_bee = null
+ var/mob/living/basic/bee/queen/queen_bee = null
var/list/bees = list() //bees owned by the box, not those inside it
var/list/honeycombs = list()
var/list/honey_frames = list()
@@ -51,7 +51,7 @@
/obj/structure/beebox/Destroy()
STOP_PROCESSING(SSobj, src)
- for(var/mob/living/simple_animal/hostile/poison/bees/B in bees)
+ for(var/mob/living/basic/bee/B in bees)
B.beehome = null
bees.Cut()
bees = null
@@ -82,7 +82,7 @@
honey_frames += HF
for(var/i in 1 to get_max_bees())
- var/mob/living/simple_animal/hostile/poison/bees/B = new(src)
+ var/mob/living/basic/bee/B = new(src)
bees += B
B.beehome = src
B.assign_reagent(R)
@@ -107,7 +107,7 @@
if((bee_resources >= BEE_RESOURCE_NEW_BEE_COST && prob(BEE_PROB_NEW_BEE)) || freebee)
if(!freebee)
bee_resources = max(bee_resources - BEE_RESOURCE_NEW_BEE_COST, 0)
- var/mob/living/simple_animal/hostile/poison/bees/B = new(get_turf(src))
+ var/mob/living/basic/bee/B = new(get_turf(src))
B.beehome = src
B.assign_reagent(queen_bee.beegent)
bees += B
@@ -178,7 +178,7 @@
visible_message("[user] sets [qb] down inside the apiary, making it [qb.p_their()] new home.")
var/relocated = 0
for(var/b in bees)
- var/mob/living/simple_animal/hostile/poison/bees/B = b
+ var/mob/living/basic/bee/B = b
if(B.reagent_incompatible(queen_bee))
bees -= B
B.beehome = null
@@ -209,7 +209,7 @@
default_unfasten_wrench(user, I, time = 20)
/obj/structure/beebox/attack_animal(mob/living/simple_animal/M)
- if(!istype(M, /mob/living/simple_animal/hostile/poison/bees))
+ if(!istype(M, /mob/living/basic/bee))
return ..()
M.forceMove(src)
@@ -222,15 +222,15 @@
/obj/structure/beebox/attack_hand(mob/user)
if(ishuman(user))
if(!user.bee_friendly())
- //Time to get stung!
+ // Time to get stung!
var/bees = FALSE
- for(var/b in bees) //everyone who's ever lived here now instantly hates you, suck it assistant!
- var/mob/living/simple_animal/hostile/poison/bees/B = b
- if(B.isqueen)
+ for(var/b in bees) // everyone who's ever lived here now instantly hates you, suck it assistant!
+ var/mob/living/basic/bee/B = b
+ if(B.is_queen)
continue
if(B.loc == src)
B.forceMove(drop_location())
- B.target = user
+ B.ai_controller.set_blackboard_key(BB_BASIC_MOB_CURRENT_TARGET, user)
bees = TRUE
if(bees)
visible_message("[user] disturbs the bees!")
@@ -281,7 +281,7 @@
if(disassembled)
mat_drop = 40
new /obj/item/stack/sheet/wood(loc, mat_drop)
- for(var/mob/living/simple_animal/hostile/poison/bees/B in bees)
+ for(var/mob/living/basic/bee/B in bees)
if(B.loc == src)
B.forceMove(drop_location())
for(var/obj/item/honey_frame/HF in honey_frames)
@@ -292,6 +292,14 @@
/obj/structure/beebox/unwrenched
anchored = FALSE
+/obj/structure/beebox/proc/habitable(mob/living/basic/target)
+ if(!istype(target, /mob/living/basic/bee))
+ return FALSE
+ var/mob/living/basic/bee/citizen = target
+ if(citizen.reagent_incompatible(queen_bee) || bees.len >= get_max_bees())
+ return FALSE
+ return TRUE
+
#undef BEEBOX_MAX_FRAMES
#undef BEES_RATIO
#undef BEE_PROB_NEW_BEE
diff --git a/code/modules/hydroponics/hydroponics_tray.dm b/code/modules/hydroponics/hydroponics_tray.dm
index a7e559d8bc1..d2b94ba4051 100644
--- a/code/modules/hydroponics/hydroponics_tray.dm
+++ b/code/modules/hydroponics/hydroponics_tray.dm
@@ -1138,3 +1138,10 @@
myseed.mutate(roll / 2, get_mutation_focus())
if(top_range > 30 && prob(roll / 10))
mut_beamed = TRUE
+
+/obj/machinery/hydroponics/proc/can_bee_pollinate()
+ if(isnull(myseed))
+ return FALSE
+ if(dead || recent_bee_visit)
+ return FALSE
+ return TRUE
diff --git a/code/modules/mob/living/basic/hostile/bees/bee_ai_behaviors.dm b/code/modules/mob/living/basic/hostile/bees/bee_ai_behaviors.dm
new file mode 100644
index 00000000000..cb3b5732a05
--- /dev/null
+++ b/code/modules/mob/living/basic/hostile/bees/bee_ai_behaviors.dm
@@ -0,0 +1,106 @@
+/// if we have a hive, this will be our aggro distance
+#define AGGRO_DISTANCE_FROM_HIVE 2
+/datum/ai_behavior/hunt_target/pollinate
+ always_reset_target = TRUE
+
+/datum/ai_behavior/hunt_target/pollinate/target_caught(mob/living/hunter, obj/machinery/hydroponics/hydro_target)
+ var/datum/callback/callback = CALLBACK(hunter, TYPE_PROC_REF(/mob/living/basic/bee, pollinate), hydro_target)
+ callback.Invoke()
+
+/datum/ai_behavior/find_hunt_target/pollinate
+ action_cooldown = 10 SECONDS
+
+/datum/ai_behavior/find_hunt_target/pollinate/valid_dinner(mob/living/source, obj/machinery/hydroponics/dinner, radius)
+ if(!dinner.can_bee_pollinate())
+ return FALSE
+ return can_see(source, dinner, radius)
+
+/datum/ai_behavior/enter_exit_hive
+ behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_REQUIRE_REACH
+ action_cooldown = 10 SECONDS
+
+/datum/ai_behavior/enter_exit_hive/setup(datum/ai_controller/controller, target_key, attack_key)
+ . = ..()
+ var/atom/target = controller.blackboard[target_key]
+ if(QDELETED(target))
+ return FALSE
+ set_movement_target(controller, target)
+
+/datum/ai_behavior/enter_exit_hive/perform(seconds_per_tick, datum/ai_controller/controller, target_key, attack_key)
+ var/obj/structure/beebox/current_home = controller.blackboard[target_key]
+ var/atom/attack_target = controller.blackboard[attack_key]
+
+ if(attack_target) // forget about who we attacking when we go home
+ controller.clear_blackboard_key(attack_key)
+
+ controller.ai_interact(target = current_home, intent = INTENT_HELP)
+ return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED
+
+/datum/ai_behavior/inhabit_hive
+ behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_REQUIRE_REACH
+
+/datum/ai_behavior/inhabit_hive/setup(datum/ai_controller/controller, target_key)
+ . = ..()
+ var/atom/target = controller.blackboard[target_key]
+ if(QDELETED(target))
+ return FALSE
+ set_movement_target(controller, target)
+
+/datum/ai_behavior/inhabit_hive/perform(seconds_per_tick, datum/ai_controller/controller, target_key)
+ var/obj/structure/beebox/potential_home = controller.blackboard[target_key]
+ var/mob/living/bee_pawn = controller.pawn
+
+ if(!potential_home.habitable(bee_pawn)) //the house become full before we get to it
+ return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED
+
+ controller.ai_interact(target = potential_home, intent = INTENT_HELP)
+ return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED
+
+/datum/ai_behavior/inhabit_hive/finish_action(datum/ai_controller/controller, succeeded, target_key)
+ . = ..()
+ if(!succeeded)
+ controller.clear_blackboard_key(target_key) //failed to make it our home so find another
+
+/datum/ai_behavior/find_and_set/bee_hive
+ action_cooldown = 10 SECONDS
+
+/datum/ai_behavior/find_and_set/bee_hive/search_tactic(datum/ai_controller/controller, locate_path, search_range)
+ var/list/valid_hives = list()
+ var/mob/living/bee_pawn = controller.pawn
+
+ if(istype(bee_pawn.loc, /obj/structure/beebox))
+ return bee_pawn.loc //for premade homes
+
+ for(var/obj/structure/beebox/potential_home in oview(search_range, bee_pawn))
+ if(!potential_home.habitable(bee_pawn))
+ continue
+ valid_hives += potential_home
+
+ if(valid_hives.len)
+ return pick(valid_hives)
+
+/datum/targeting_strategy/basic/bee
+
+/datum/targeting_strategy/basic/bee/can_attack(mob/living/owner, atom/target, vision_range)
+ if(!isliving(target))
+ return FALSE
+ . = ..()
+ if(!.)
+ return FALSE
+
+ var/mob/living/mob_target = target
+
+ if(mob_target.mob_biotypes & MOB_PLANT)
+ return FALSE
+
+ var/datum/ai_controller/basic_controller/bee_ai = owner.ai_controller
+ if(isnull(bee_ai))
+ return FALSE
+
+ var/atom/bee_hive = bee_ai.blackboard[BB_CURRENT_HOME]
+ if(bee_hive && get_dist(target, bee_hive) > AGGRO_DISTANCE_FROM_HIVE && can_see(owner, bee_hive, 9))
+ return FALSE
+
+ return !(mob_target.bee_friendly())
+
+#undef AGGRO_DISTANCE_FROM_HIVE
diff --git a/code/modules/mob/living/basic/hostile/bees/bee_ai_subtree.dm b/code/modules/mob/living/basic/hostile/bees/bee_ai_subtree.dm
new file mode 100644
index 00000000000..6efcf08864c
--- /dev/null
+++ b/code/modules/mob/living/basic/hostile/bees/bee_ai_subtree.dm
@@ -0,0 +1,108 @@
+/datum/ai_controller/basic_controller/bee
+ blackboard = list(
+ BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic/bee,
+ )
+
+ ai_traits = PASSIVE_AI_FLAGS
+ ai_movement = /datum/ai_movement/basic_avoidance
+ idle_behavior = /datum/idle_behavior/idle_random_walk
+
+ planning_subtrees = list(
+ /datum/ai_planning_subtree/find_valid_home,
+ /datum/ai_planning_subtree/enter_exit_home,
+ /datum/ai_planning_subtree/find_and_hunt_target/pollinate,
+ /datum/ai_planning_subtree/simple_find_target/bee,
+ /datum/ai_planning_subtree/basic_melee_attack_subtree,
+ )
+
+/datum/ai_controller/basic_controller/queen_bee
+ blackboard = list(
+ BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic/bee,
+ )
+
+ ai_traits = PASSIVE_AI_FLAGS
+ ai_movement = /datum/ai_movement/basic_avoidance
+ idle_behavior = /datum/idle_behavior/idle_random_walk
+
+ planning_subtrees = list(
+ /datum/ai_planning_subtree/find_valid_home,
+ /datum/ai_planning_subtree/enter_exit_home/queen,
+ /datum/ai_planning_subtree/simple_find_target,
+ /datum/ai_planning_subtree/basic_melee_attack_subtree,
+ )
+
+/datum/ai_controller/basic_controller/syndibee
+ blackboard = list(
+ BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic/not_friends,
+ )
+
+ ai_movement = /datum/ai_movement/jps
+ idle_behavior = /datum/idle_behavior/idle_random_walk
+ planning_subtrees = list(
+ /datum/ai_planning_subtree/simple_find_target,
+ /datum/ai_planning_subtree/basic_melee_attack_subtree,
+ )
+
+/datum/ai_planning_subtree/simple_find_target/bee/select_behaviors(datum/ai_controller/controller, seconds_per_tick)
+ var/atom/hydro_target = controller.blackboard[BB_TARGET_HYDRO]
+ if(hydro_target)
+ return SUBTREE_RETURN_FINISH_PLANNING
+ return ..()
+
+/datum/ai_planning_subtree/find_valid_home
+
+/datum/ai_planning_subtree/find_valid_home/select_behaviors(datum/ai_controller/controller, seconds_per_tick)
+ var/mob/living/work_bee = controller.pawn
+
+ var/obj/structure/beebox/current_home = controller.blackboard[BB_CURRENT_HOME]
+
+ if(QDELETED(current_home))
+ controller.queue_behavior(/datum/ai_behavior/find_and_set/bee_hive, BB_CURRENT_HOME, /obj/structure/beebox)
+ return
+
+ if(work_bee in current_home.bees)
+ return
+
+ controller.queue_behavior(/datum/ai_behavior/inhabit_hive, BB_CURRENT_HOME)
+ return SUBTREE_RETURN_FINISH_PLANNING
+
+/datum/ai_planning_subtree/enter_exit_home
+ /// chance we go back home
+ var/flyback_chance = 15
+ /// chance we exit the home
+ var/exit_chance = 35
+
+/datum/ai_planning_subtree/enter_exit_home/select_behaviors(datum/ai_controller/controller, seconds_per_tick)
+
+ var/obj/structure/beebox/current_home = controller.blackboard[BB_CURRENT_HOME]
+
+ if(QDELETED(current_home))
+ return
+
+ var/mob/living/bee_pawn = controller.pawn
+ var/action_prob = (bee_pawn.loc == current_home) ? exit_chance : flyback_chance
+
+ if(!SPT_PROB(action_prob, seconds_per_tick))
+ return
+
+ controller.queue_behavior(/datum/ai_behavior/enter_exit_hive, BB_CURRENT_HOME, BB_BASIC_MOB_CURRENT_TARGET)
+ return SUBTREE_RETURN_FINISH_PLANNING
+
+// the queen spends more time in the hive
+/datum/ai_planning_subtree/enter_exit_home/queen
+ flyback_chance = 85
+ exit_chance = 5
+
+/datum/ai_planning_subtree/find_and_hunt_target/pollinate
+ target_key = BB_TARGET_HYDRO
+ hunting_behavior = /datum/ai_behavior/hunt_target/pollinate
+ finding_behavior = /datum/ai_behavior/find_hunt_target/pollinate
+ hunt_targets = list(/obj/machinery/hydroponics)
+ hunt_range = 10
+ hunt_chance = 85
+
+/datum/ai_planning_subtree/find_and_hunt_target/pollinate/select_behaviors(datum/ai_controller/controller, seconds_per_tick)
+ var/atom/atom_pawn = controller.pawn
+ if(!isturf(atom_pawn.loc))
+ return
+ return ..()
diff --git a/code/modules/mob/living/basic/hostile/bees/bees.dm b/code/modules/mob/living/basic/hostile/bees/bees.dm
new file mode 100644
index 00000000000..2f6cb6760df
--- /dev/null
+++ b/code/modules/mob/living/basic/hostile/bees/bees.dm
@@ -0,0 +1,284 @@
+#define BEE_TRAY_RECENT_VISIT 20 SECONDS /// How long in deciseconds until a tray can be visited by a bee again
+#define BEE_DEFAULT_COLOUR "#e5e500" /// the colour we make the stripes of the bee if our reagent has no colour (or we have no reagent)
+#define BEE_POLLINATE_YIELD_CHANCE 33 /// chance to increase yield of plant
+#define BEE_POLLINATE_PEST_CHANCE 33 /// chance to decrease pest of plant
+#define BEE_POLLINATE_POTENCY_CHANCE 50 /// chance to increase potancy of plant
+#define BEE_FOODGROUPS RAW | MEAT | GORE | BUGS /// the bee food contents
+
+/mob/living/basic/bee
+ name = "bee"
+ desc = "Buzzy buzzy bee, stingy sti- Ouch!"
+ icon_state = ""
+ icon = 'icons/mob/bees.dmi'
+ gender = FEMALE
+ speak_emote = list("buzzes")
+
+ melee_damage_lower = 1
+ melee_damage_upper = 1
+ attack_verb_continuous = "stings"
+ attack_verb_simple = "sting"
+ response_help_continuous = "shoos"
+ response_help_simple = "shoo"
+ response_disarm_continuous = "swats away"
+ response_disarm_simple = "swat away"
+ response_harm_continuous = "squashes"
+ response_harm_simple = "squash"
+
+ speed = 0.5
+ maxHealth = 10
+ health = 10
+ melee_damage_lower = 1
+ melee_damage_upper = 1
+ melee_attack_cooldown_min = 1.5 SECONDS
+ melee_attack_cooldown_max = 2.5 SECONDS
+ attack_sound = null // Stings are quiet
+ faction = list("hostile")
+ pass_flags = PASSTABLE | PASSGRILLE | PASSMOB
+ ventcrawler = VENTCRAWLER_ALWAYS
+ mob_size = MOB_SIZE_TINY
+ mob_biotypes = MOB_ORGANIC|MOB_BUG
+ density = FALSE
+ atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
+ minimum_survivable_temperature = 0
+ gold_core_spawnable = FRIENDLY_SPAWN
+ basic_mob_flags = DEL_ON_DEATH
+ initial_traits = list(TRAIT_FLYING)
+ ai_controller = /datum/ai_controller/basic_controller/bee
+ /// the reagent the bee has
+ var/datum/reagent/beegent = null
+ /// the house we live in
+ var/obj/structure/beebox/beehome = null
+ /// our icon base
+ var/icon_base = "bee"
+ /// Icon creation
+ var/static/list/bee_icons = list()
+ /// the bee is a queen?
+ var/is_queen = FALSE
+ /// Is this a syndibee?
+ var/bee_syndicate = FALSE
+
+/mob/living/basic/bee/Initialize(mapload)
+ . = ..()
+ generate_bee_visuals()
+ AddComponent(/datum/component/swarming)
+
+/mob/living/basic/bee/Process_Spacemove(movement_dir = 0, continuous_move = FALSE)
+ return TRUE
+
+/mob/living/basic/bee/examine(mob/user)
+ . = ..()
+ if(!bee_syndicate && !beehome)
+ . += "This bee is homeless!"
+
+/mob/living/basic/bee/Destroy()
+ if(beehome)
+ beehome.bees -= src
+ beehome = null
+ beegent = null
+ return ..()
+
+/mob/living/basic/bee/death(gibbed)
+ . = ..()
+ if(!.)
+ return
+ if(beehome)
+ if(beehome.bees)
+ beehome.bees.Remove(src)
+ beehome = null
+
+// All bee sprites are made up of overlays. They do not have any special sprite overlays for items placed on them, such as collars, so this proc is unneeded.
+/mob/living/basic/bee/regenerate_icons()
+ return
+
+/mob/living/basic/bee/proc/generate_bee_visuals()
+ overlays.Cut()
+
+ var/col = BEE_DEFAULT_COLOUR
+ if(beegent && beegent.color)
+ col = beegent.color
+
+ var/image/base
+ if(!bee_icons["[icon_base]_base"])
+ bee_icons["[icon_base]_base"] = image(icon = 'icons/mob/bees.dmi', icon_state = "[icon_base]_base")
+ base = bee_icons["[icon_base]_base"]
+ overlays += base
+
+ var/image/greyscale
+ if(!bee_icons["[icon_base]_grey_[col]"])
+ bee_icons["[icon_base]_grey_[col]"] = image(icon = 'icons/mob/bees.dmi', icon_state = "[icon_base]_grey")
+ greyscale = bee_icons["[icon_base]_grey_[col]"]
+ greyscale.color = col
+ overlays += greyscale
+
+ var/image/wings
+ if(!bee_icons["[icon_base]_wings"])
+ bee_icons["[icon_base]_wings"] = image(icon = 'icons/mob/bees.dmi', icon_state = "[icon_base]_wings")
+ wings = bee_icons["[icon_base]_wings"]
+ overlays += wings
+
+/mob/living/basic/bee/melee_attack(atom/target, list/modifiers, ignore_cooldown)
+ if(istype(target, /obj/machinery/hydroponics))
+ var/obj/machinery/hydroponics/hydro = target
+ pollinate(hydro)
+ return FALSE
+
+ if(istype(target, /obj/structure/beebox))
+ var/obj/structure/beebox/hive = target
+ handle_habitation(hive)
+ return FALSE
+
+ . = ..()
+ if(. && isliving(target) && (!client || a_intent == INTENT_HARM))
+ make_opaque()
+ var/mob/living/L = target
+ if(L.reagents)
+ if(beegent)
+ beegent.reaction_mob(L, REAGENT_INGEST)
+ L.reagents.add_reagent(beegent.id, rand(1, 5))
+ else
+ L.reagents.add_reagent("spidertoxin", 5)
+
+/mob/living/basic/bee/proc/make_opaque()
+ // If a bee attacks someone, make it very easy to hit for a while
+ mouse_opacity = MOUSE_OPACITY_OPAQUE
+
+/mob/living/basic/bee/proc/handle_habitation(obj/structure/beebox/hive)
+ if(hive == beehome) // if its our home, we enter or exit it
+ var/drop_location = (src in beehome.contents) ? get_turf(beehome) : beehome
+ forceMove(drop_location)
+ return
+ if(!isnull(hive.queen_bee) && is_queen) // if we are queen and house already have a queen, dont inhabit
+ return
+ if(!hive.habitable(src) || !isnull(beehome)) // if not habitable or we already have a home
+ return
+ beehome = hive
+ beehome.bees += src
+ if(is_queen)
+ beehome.queen_bee = src
+
+/mob/living/basic/bee/proc/reagent_incompatible(mob/living/basic/bee/ruler)
+ if(!ruler)
+ return FALSE
+ if(ruler.beegent?.type != beegent?.type)
+ return TRUE
+ return FALSE
+
+/mob/living/basic/bee/proc/pollinate(obj/machinery/hydroponics/Hydro)
+ if(!istype(Hydro) || !Hydro.myseed || Hydro.dead || Hydro.recent_bee_visit || Hydro.lid_closed)
+ return
+
+ Hydro.recent_bee_visit = TRUE
+ addtimer(VARSET_CALLBACK(Hydro, recent_bee_visit, FALSE), BEE_TRAY_RECENT_VISIT)
+
+ var/growth = health // Health also means how many bees are in the swarm, roughly.
+ // better healthier plants!
+ Hydro.adjustHealth(growth*0.5)
+ if(prob(BEE_POLLINATE_PEST_CHANCE))
+ Hydro.adjustPests(-10)
+ if(prob(BEE_POLLINATE_YIELD_CHANCE) && !Hydro.self_sustaining)
+ Hydro.yieldmod = 2
+
+ if(beehome)
+ beehome.bee_resources = min(beehome.bee_resources + growth, 100)
+
+/mob/living/basic/bee/proc/assign_reagent(datum/reagent/R)
+ if(istype(R))
+ beegent = R
+ name = "[initial(name)] ([R.name])"
+ generate_bee_visuals()
+
+/mob/living/basic/bee/queen
+ name = "queen bee"
+ desc = "She's the queen of bees, BZZ BZZ!"
+ icon_base = "queen"
+ is_queen = TRUE
+ mouse_opacity = MOUSE_OPACITY_OPAQUE
+ ai_controller = /datum/ai_controller/basic_controller/queen_bee
+
+// leave pollination for the peasent bees
+/mob/living/basic/bee/queen/melee_attack(atom/target, list/modifiers, ignore_cooldown)
+ . = ..()
+ if(. && beegent && isliving(target))
+ var/mob/living/L = target
+ beegent.reaction_mob(L, REAGENT_TOUCH)
+ L.reagents.add_reagent(beegent.id, rand(1, 5))
+
+/mob/living/basic/bee/queen/pollinate()
+ return
+
+/obj/item/queen_bee
+ name = "queen bee"
+ desc = "She's the queen of bees, BZZ BZZ!"
+ icon = 'icons/mob/bees.dmi'
+ icon_state = "queen_item"
+ gender = FEMALE
+ new_attack_chain = TRUE
+ /// Associated bee mob
+ var/mob/living/basic/bee/queen/queen
+
+/obj/item/queen_bee/item_interaction(mob/living/user, obj/item/I, list/modifiers)
+ if(!istype(I, /obj/item/reagent_containers/syringe))
+ return ..()
+ var/obj/item/reagent_containers/syringe/S = I
+ if(S.reagents.has_reagent("royal_bee_jelly")) // We check it twice because if we use an if/else statement, it won't catch the check for blacklisted chemicals below
+ if(!S.reagents.has_reagent("royal_bee_jelly", 5))
+ to_chat(user, "You don't have enough royal bee jelly to split a bee in two!")
+ return
+ S.reagents.remove_reagent("royal_bee_jelly", 5)
+ var/obj/item/queen_bee/qb = new(user.drop_location())
+ qb.queen = new(qb)
+ if(queen?.beegent)
+ qb.queen.assign_reagent(queen.beegent) //Bees use the global singleton instances of reagents, so we don't need to worry about one bee being deleted and her copies losing their reagents.
+ user.put_in_active_hand(qb)
+ user.visible_message("[user] injects [src] with royal bee jelly, causing it to split into two bees, MORE BEES!", "You inject [src] with royal bee jelly, causing it to split into two bees, MORE BEES!")
+ return
+
+ var/datum/reagent/R = GLOB.chemical_reagents_list[S.reagents.get_master_reagent_id()]
+ if(R && S.reagents.has_reagent(R.id, 5))
+ S.reagents.remove_reagent(R.id, 5) // Whether or not the chemical is blocked, we want it gone just because you tried to
+ if(R.id in GLOB.blocked_chems)
+ to_chat(user, "The [src]'s immune system rejects [R.name]!")
+ return
+ queen.assign_reagent(R)
+ user.visible_message("[user] injects [src]'s genome with [R.name], mutating its DNA!", "You inject [src]'s genome with [R.name], mutating its DNA!")
+ name = queen.name
+ else
+ to_chat(user, "You don't have enough units of that chemical to modify the bee's DNA!")
+
+/obj/item/queen_bee/bought/Initialize(mapload)
+ . = ..()
+ queen = new(src)
+
+/obj/item/queen_bee/Destroy()
+ QDEL_NULL(queen)
+ return ..()
+
+// Syndicate Bees
+/mob/living/basic/bee/syndi
+ name = "syndi-bee"
+ desc = "The result of a large influx of BEES!"
+ melee_damage_lower = 5
+ melee_damage_upper = 5
+ maxHealth = 25
+ health = 25
+ faction = list("hostile", "syndicate")
+ bee_syndicate = TRUE
+ mouse_opacity = MOUSE_OPACITY_OPAQUE
+ ai_controller = /datum/ai_controller/basic_controller/syndibee
+
+/mob/living/basic/bee/syndi/Initialize(mapload)
+ . = ..()
+ beegent = GLOB.chemical_reagents_list["facid"] // Prepare to die
+
+/mob/living/basic/bee/syndi/assign_reagent(datum/reagent/R)
+ return
+
+/mob/living/basic/bee/syndi/pollinate() // No Pollination
+ return
+
+#undef BEE_TRAY_RECENT_VISIT
+#undef BEE_DEFAULT_COLOUR
+#undef BEE_POLLINATE_YIELD_CHANCE
+#undef BEE_POLLINATE_PEST_CHANCE
+#undef BEE_POLLINATE_POTENCY_CHANCE
+#undef BEE_FOODGROUPS
diff --git a/code/modules/mob/living/carbon/human/species/tajaran.dm b/code/modules/mob/living/carbon/human/species/tajaran.dm
index a66999c9936..676c838e209 100644
--- a/code/modules/mob/living/carbon/human/species/tajaran.dm
+++ b/code/modules/mob/living/carbon/human/species/tajaran.dm
@@ -50,7 +50,7 @@
/mob/living/basic/chick,
/mob/living/basic/butterfly,
/mob/living/simple_animal/parrot,
- /mob/living/simple_animal/hostile/poison/bees,
+ /mob/living/basic/bee,
)
suicide_messages = list(
diff --git a/code/modules/mob/living/carbon/human/species/unathi.dm b/code/modules/mob/living/carbon/human/species/unathi.dm
index 9adaf02510f..f64f66afd46 100644
--- a/code/modules/mob/living/carbon/human/species/unathi.dm
+++ b/code/modules/mob/living/carbon/human/species/unathi.dm
@@ -56,7 +56,7 @@
/mob/living/basic/crab,
/mob/living/basic/butterfly,
/mob/living/simple_animal/parrot,
- /mob/living/simple_animal/hostile/poison/bees,
+ /mob/living/basic/bee,
)
suicide_messages = list(
diff --git a/code/modules/mob/living/carbon/human/species/vulpkanin.dm b/code/modules/mob/living/carbon/human/species/vulpkanin.dm
index 03df4a7e5b3..f08a39deec7 100644
--- a/code/modules/mob/living/carbon/human/species/vulpkanin.dm
+++ b/code/modules/mob/living/carbon/human/species/vulpkanin.dm
@@ -46,7 +46,7 @@
/mob/living/basic/crab,
/mob/living/basic/butterfly,
/mob/living/simple_animal/parrot,
- /mob/living/simple_animal/hostile/poison/bees,
+ /mob/living/basic/bee,
)
suicide_messages = list(
diff --git a/code/modules/mob/living/simple_animal/hostile/bees.dm b/code/modules/mob/living/simple_animal/hostile/bees.dm
deleted file mode 100644
index 043614b3e61..00000000000
--- a/code/modules/mob/living/simple_animal/hostile/bees.dm
+++ /dev/null
@@ -1,394 +0,0 @@
-#define BEE_IDLE_ROAMING 70 //The value of idle at which a bee in a beebox will try to wander
-#define BEE_IDLE_GOHOME 0 //The value of idle at which a bee will try to go home
-#define BEE_PROB_GOHOME 35 //Probability to go home when idle is below BEE_IDLE_GOHOME
-#define BEE_PROB_GOROAM 5 //Probability to go roaming when idle is above BEE_IDLE_ROAMING
-#define BEE_TRAY_RECENT_VISIT 200 //How long in deciseconds until a tray can be visited by a bee again
-#define BEE_DEFAULT_COLOUR "#e5e500" //the colour we make the stripes of the bee if our reagent has no colour (or we have no reagent)
-
-#define BEE_POLLINATE_YIELD_CHANCE 33
-#define BEE_POLLINATE_PEST_CHANCE 33
-
-/mob/living/simple_animal/hostile/poison/bees
- name = "bee"
- desc = "Buzzy buzzy bee, stingy sti- Ouch!"
- icon_state = ""
- icon = 'icons/mob/bees.dmi'
- gender = FEMALE
- speak_emote = list("buzzes")
- emote_hear = list("buzzes")
- turns_per_move = 0
- melee_damage_lower = 1
- melee_damage_upper = 1
- attacktext = "stings"
- response_help = "shoos"
- response_disarm = "swats away"
- response_harm = "squashes"
- maxHealth = 10
- health = 10
- faction = list("hostile")
- move_to_delay = 0
- obj_damage = 0
- environment_smash = 0
- pass_flags = PASSTABLE | PASSGRILLE | PASSMOB
- density = FALSE
- mob_size = MOB_SIZE_TINY
- mob_biotypes = MOB_ORGANIC | MOB_BUG
- gold_core_spawnable = HOSTILE_SPAWN
- search_objects = TRUE //have to find those plant trays!
-
- //Spaceborn beings don't get hurt by space
- atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
- minbodytemp = 0
- del_on_death = TRUE
-
- initial_traits = list(TRAIT_FLYING)
-
- var/datum/reagent/beegent = null //hehe, beegent
- var/obj/structure/beebox/beehome = null
- var/idle = 0
- var/isqueen = FALSE
- var/bee_syndicate = FALSE
- var/icon_base = "bee"
- var/last_attack = 0
- var/opacity_time = 1 MINUTES
- var/static/list/bee_icons = list()
- var/static/beehometypecache = typecacheof(/obj/structure/beebox)
- var/static/hydroponicstypecache = typecacheof(/obj/machinery/hydroponics)
-
-/mob/living/simple_animal/hostile/poison/bees/Process_Spacemove(movement_dir = 0, continuous_move = FALSE)
- return TRUE
-
-/mob/living/simple_animal/hostile/poison/bees/Initialize(mapload)
- . = ..()
- AddComponent(/datum/component/swarming)
- generate_bee_visuals()
-
-/mob/living/simple_animal/hostile/poison/bees/Destroy()
- beegent = null
- if(beehome)
- if(beehome.bees)
- beehome.bees.Remove(src)
- beehome = null
- return ..()
-
-/mob/living/simple_animal/hostile/poison/bees/death(gibbed)
- . = ..()
- if(!.)
- return
- if(beehome)
- if(beehome.bees)
- beehome.bees.Remove(src)
- beehome = null
-
-/mob/living/simple_animal/hostile/poison/bees/examine(mob/user)
- . = ..()
- if(!bee_syndicate && !beehome)
- . += "This bee is homeless!"
-
-/mob/living/simple_animal/hostile/poison/bees/ListTargets() // Bee processing is expessive, so we override them finding targets here.
- if(!search_objects) //In case we want to have purely hostile bees
- return ..()
- else
- . = list() // The following code is only very slightly slower than just returning oview(vision_range, targets_from), but it saves us much more work down the line
- var/list/searched_for = oview(vision_range, targets_from)
- for(var/obj/A in searched_for)
- . += A
- for(var/mob/A in searched_for)
- . += A
-
-// All bee sprites are made up of overlays. They do not have any special sprite overlays for items placed on them, such as collars, so this proc is unneeded.
-/mob/living/simple_animal/hostile/poison/bees/regenerate_icons()
- return
-
-/mob/living/simple_animal/hostile/poison/bees/proc/generate_bee_visuals()
- overlays.Cut()
-
- var/col = BEE_DEFAULT_COLOUR
- if(beegent && beegent.color)
- col = beegent.color
-
- var/image/base
- if(!bee_icons["[icon_base]_base"])
- bee_icons["[icon_base]_base"] = image(icon = 'icons/mob/bees.dmi', icon_state = "[icon_base]_base")
- base = bee_icons["[icon_base]_base"]
- overlays += base
-
- var/image/greyscale
- if(!bee_icons["[icon_base]_grey_[col]"])
- bee_icons["[icon_base]_grey_[col]"] = image(icon = 'icons/mob/bees.dmi', icon_state = "[icon_base]_grey")
- greyscale = bee_icons["[icon_base]_grey_[col]"]
- greyscale.color = col
- overlays += greyscale
-
- var/image/wings
- if(!bee_icons["[icon_base]_wings"])
- bee_icons["[icon_base]_wings"] = image(icon = 'icons/mob/bees.dmi', icon_state = "[icon_base]_wings")
- wings = bee_icons["[icon_base]_wings"]
- overlays += wings
-
-//We don't attack beekeepers/people dressed as bees//Todo: bee costume
-/mob/living/simple_animal/hostile/poison/bees/CanAttack(atom/the_target)
- . = ..()
- if(!.)
- return FALSE
- if(!bee_syndicate && isliving(the_target))
- var/mob/living/H = the_target
- return !H.bee_friendly()
-
-/mob/living/simple_animal/hostile/poison/bees/Found(atom/A)
- if(isliving(A))
- var/mob/living/H = A
- return !H.bee_friendly()
- if(istype(A, /obj/machinery/hydroponics))
- var/obj/machinery/hydroponics/Hydro = A
- if(Hydro.myseed && !Hydro.dead && !Hydro.recent_bee_visit)
- wanted_objects |= hydroponicstypecache //so we only hunt them while they're alive/seeded/not visisted
- return TRUE
- return FALSE
-
-/mob/living/simple_animal/hostile/poison/bees/AttackingTarget()
- //Pollinate
- if(istype(target, /obj/machinery/hydroponics))
- var/obj/machinery/hydroponics/Hydro = target
- pollinate(Hydro)
- else if(istype(target, /obj/structure/beebox))
- if(target == beehome)
- var/obj/structure/beebox/BB = target
- forceMove(BB)
- toggle_ai(AI_IDLE)
- target = null
- wanted_objects -= beehometypecache //so we don't attack beeboxes when not going home
- return //no don't attack the goddamm box
- else
- . = ..()
- if(. && isliving(target) && (!client || a_intent == INTENT_HARM))
- make_opaque()
- var/mob/living/L = target
- if(L.reagents)
- if(beegent)
- beegent.reaction_mob(L, REAGENT_INGEST)
- L.reagents.add_reagent(beegent.id, rand(1, 5))
- else
- L.reagents.add_reagent("spidertoxin", 5)
-
-/mob/living/simple_animal/hostile/poison/bees/proc/make_opaque()
- // If a bee attacks someone, make it very easy to hit for a while
- last_attack = world.time
- mouse_opacity = MOUSE_OPACITY_OPAQUE
-
-/mob/living/simple_animal/hostile/poison/bees/Life(seconds, times_fired)
- . = ..()
- if(mind || (mouse_opacity == initial(mouse_opacity)))
- return
- var/diff = world.time - last_attack
- if(diff >= opacity_time)
- mouse_opacity = initial(mouse_opacity)
-
-/mob/living/simple_animal/hostile/poison/bees/sentience_act()
- . = ..()
- make_opaque()
-
-/mob/living/simple_animal/hostile/poison/bees/proc/assign_reagent(datum/reagent/R)
- if(istype(R))
- beegent = R
- name = "[initial(name)] ([R.name])"
- generate_bee_visuals()
-
-/mob/living/simple_animal/hostile/poison/bees/proc/pollinate(obj/machinery/hydroponics/Hydro)
- if(!istype(Hydro) || !Hydro.myseed || Hydro.dead || Hydro.recent_bee_visit || Hydro.lid_closed)
- target = null
- return
-
- target = null //so we pick a new hydro tray next FindTarget(), instead of loving the same plant for eternity
- wanted_objects -= hydroponicstypecache //so we only hunt them while they're alive/seeded/not visisted
- Hydro.recent_bee_visit = TRUE
- spawn(BEE_TRAY_RECENT_VISIT)
- if(Hydro)
- Hydro.recent_bee_visit = FALSE
-
- var/growth = health //Health also means how many bees are in the swarm, roughly.
- //better healthier plants!
- Hydro.adjustHealth(growth*0.5)
- if(prob(BEE_POLLINATE_PEST_CHANCE))
- Hydro.adjustPests(-10)
- if(prob(BEE_POLLINATE_YIELD_CHANCE) && !Hydro.self_sustaining)
- Hydro.yieldmod = 2
-
- if(beehome)
- beehome.bee_resources = min(beehome.bee_resources + growth, 100)
-
-/mob/living/simple_animal/hostile/poison/bees/handle_automated_action()
- . = ..()
- if(!.)
- return
-
- if(!bee_syndicate)
- if(!isqueen)
- if(loc == beehome)
- idle = min(100, ++idle)
- if(idle >= BEE_IDLE_ROAMING && prob(BEE_PROB_GOROAM))
- forceMove(beehome.drop_location())
- else
- idle = max(0, --idle)
- if(idle <= BEE_IDLE_GOHOME && prob(BEE_PROB_GOHOME))
- if(!FindTarget())
- wanted_objects |= beehometypecache //so we don't attack beeboxes when not going home
- target = beehome
- if(!beehome) //add ourselves to a beebox (of the same reagent) if we have no home
- for(var/obj/structure/beebox/BB in view(vision_range, src))
- if(reagent_incompatible(BB.queen_bee) || length(BB.bees) >= BB.get_max_bees())
- continue
- BB.bees |= src
- beehome = BB
- break // End loop after the first compatible find.
-
-//Botany Queen Bee
-/mob/living/simple_animal/hostile/poison/bees/queen
- name = "queen bee"
- desc = "She's the queen of bees, BZZ BZZ!"
- icon_base = "queen"
- isqueen = TRUE
- mouse_opacity = MOUSE_OPACITY_OPAQUE
-
-
-//the Queen doesn't leave the box on her own, and she CERTAINLY doesn't pollinate by herself
-/mob/living/simple_animal/hostile/poison/bees/queen/Found(atom/A)
- return FALSE
-
-//leave pollination for the peasent bees
-/mob/living/simple_animal/hostile/poison/bees/queen/AttackingTarget()
- . = ..()
- if(. && beegent && isliving(target))
- var/mob/living/L = target
- beegent.reaction_mob(L, REAGENT_TOUCH)
- L.reagents.add_reagent(beegent.id, rand(1, 5))
-
-//PEASENT BEES
-/mob/living/simple_animal/hostile/poison/bees/queen/pollinate()
- return
-
-/mob/living/simple_animal/hostile/poison/bees/proc/reagent_incompatible(mob/living/simple_animal/hostile/poison/bees/B)
- if(!B)
- return FALSE
- if(B.beegent && beegent && B.beegent.id != beegent.id || B.beegent && !beegent || !B.beegent && beegent)
- return TRUE
- return FALSE
-
-
-/obj/item/queen_bee
- name = "queen bee"
- desc = "She's the queen of bees, BZZ BZZ!"
- icon = 'icons/mob/bees.dmi'
- icon_state = "queen_item"
- gender = FEMALE
- var/mob/living/simple_animal/hostile/poison/bees/queen/queen
-
-/obj/item/queen_bee/attackby__legacy__attackchain(obj/item/I, mob/user, params)
- if(!istype(I, /obj/item/reagent_containers/syringe))
- return ..()
- var/obj/item/reagent_containers/syringe/S = I
- if(S.reagents.has_reagent("royal_bee_jelly")) // We check it twice because if we use an if/else statement, it won't catch the check for blacklisted chemicals below
- if(!S.reagents.has_reagent("royal_bee_jelly", 5))
- to_chat(user, "You don't have enough royal bee jelly to split a bee in two!")
- return
- S.reagents.remove_reagent("royal_bee_jelly", 5)
- var/obj/item/queen_bee/qb = new(user.drop_location())
- qb.queen = new(qb)
- if(queen?.beegent)
- qb.queen.assign_reagent(queen.beegent) //Bees use the global singleton instances of reagents, so we don't need to worry about one bee being deleted and her copies losing their reagents.
- user.put_in_active_hand(qb)
- user.visible_message("[user] injects [src] with royal bee jelly, causing it to split into two bees, MORE BEES!", "You inject [src] with royal bee jelly, causing it to split into two bees, MORE BEES!")
- return
-
- var/datum/reagent/R = GLOB.chemical_reagents_list[S.reagents.get_master_reagent_id()]
- if(R && S.reagents.has_reagent(R.id, 5))
- S.reagents.remove_reagent(R.id, 5) // Whether or not the chemical is blocked, we want it gone just because you tried to
- if(R.id in GLOB.blocked_chems)
- to_chat(user, "The [src]'s immune system rejects [R.name]!")
- return
- queen.assign_reagent(R)
- user.visible_message("[user] injects [src]'s genome with [R.name], mutating its DNA!", "You inject [src]'s genome with [R.name], mutating its DNA!")
- name = queen.name
- else
- to_chat(user, "You don't have enough units of that chemical to modify the bee's DNA!")
-
-/obj/item/queen_bee/bought/Initialize(mapload)
- . = ..()
- queen = new(src)
-
-/obj/item/queen_bee/Destroy()
- QDEL_NULL(queen)
- return ..()
-
-/mob/living/simple_animal/hostile/poison/bees/queen/consider_wakeup()
- if(beehome && loc == beehome) // The queen is home and doesn't have to do anything.
- return
- return ..()
-
-/mob/living/simple_animal/hostile/poison/bees/consider_wakeup()
- if(beehome && loc == beehome) // If bees are chilling in their nest, they're not actively looking for targets
- idle = min(100, ++idle)
- if(idle >= BEE_IDLE_ROAMING && prob(BEE_PROB_GOROAM))
- toggle_ai(AI_ON)
- forceMove(beehome.drop_location())
- else
- ..()
-
-//Syndicate Bees
-/mob/living/simple_animal/hostile/poison/bees/syndi
- name = "syndi-bee"
- desc = "The result of a large influx of BEES!"
- melee_damage_lower = 5
- melee_damage_upper = 5
- maxHealth = 25
- health = 25
- faction = list("hostile", "syndicate")
- search_objects = FALSE //these bees don't care about trivial things like plants, especially when there is havoc to sow
- bee_syndicate = TRUE
- var/list/master_and_friends = list()
- mouse_opacity = MOUSE_OPACITY_OPAQUE
-
-/mob/living/simple_animal/hostile/poison/bees/syndi/Initialize(mapload)
- . = ..()
- beegent = GLOB.chemical_reagents_list["facid"] //Prepare to die
-
-/mob/living/simple_animal/hostile/poison/bees/syndi/Destroy()
- master_and_friends.Cut()
- return ..()
-
-/mob/living/simple_animal/hostile/poison/bees/syndi/assign_reagent(datum/reagent/R)
- return
-
-/mob/living/simple_animal/hostile/poison/bees/syndi/pollinate() // No Pollination
- return
-
-/mob/living/simple_animal/hostile/poison/bees/syndi/Found(atom/A) //Typical usual hostile mob targeting list
- return
-
-/mob/living/simple_animal/hostile/poison/bees/syndi/CanAttack(atom/the_target)
- . = ..()
- if(!.)
- return FALSE
- if(isliving(the_target))
- var/mob/living/L = the_target
- if(ishuman(L))
- var/mob/living/carbon/human/H = L
- if(H in master_and_friends)
- return FALSE
- return TRUE
-
-/mob/living/simple_animal/hostile/poison/bees/syndi/AttackingTarget()
- . = ..()
- if(. && target && isliving(target))
- var/mob/living/L = target
- if(L.stat)
- LoseTarget()
-
-#undef BEE_IDLE_ROAMING
-#undef BEE_IDLE_GOHOME
-#undef BEE_PROB_GOHOME
-#undef BEE_PROB_GOROAM
-#undef BEE_TRAY_RECENT_VISIT
-#undef BEE_DEFAULT_COLOUR
-#undef BEE_POLLINATE_YIELD_CHANCE
-#undef BEE_POLLINATE_PEST_CHANCE
diff --git a/code/modules/mob/mob_grab.dm b/code/modules/mob/mob_grab.dm
index 27fcb5e4e4a..551b5948ad1 100644
--- a/code/modules/mob/mob_grab.dm
+++ b/code/modules/mob/mob_grab.dm
@@ -474,9 +474,9 @@
user.visible_message("[user] devours \the [affecting]!")
if(affecting.mind)
add_attack_logs(attacker, affecting, "Devoured")
- if(istype(affecting, /mob/living/simple_animal/hostile/poison/bees)) //Eating a bee will end up damaging you
+ if(istype(affecting, /mob/living/basic/bee)) //Eating a bee will end up damaging you
var/obj/item/organ/external/mouth = user.get_organ(BODY_ZONE_PRECISE_MOUTH)
- var/mob/living/simple_animal/hostile/poison/bees/B = affecting
+ var/mob/living/basic/bee/B = affecting
mouth.receive_damage(1)
if(B.beegent)
B.beegent.reaction_mob(assailant, REAGENT_INGEST)
diff --git a/code/modules/reagents/chemistry/recipes/pyrotechnics.dm b/code/modules/reagents/chemistry/recipes/pyrotechnics.dm
index 41e8658f2f4..cb988bde0d2 100644
--- a/code/modules/reagents/chemistry/recipes/pyrotechnics.dm
+++ b/code/modules/reagents/chemistry/recipes/pyrotechnics.dm
@@ -49,7 +49,7 @@
beeagents += R
var/bee_amount = round(created_volume * 0.2)
for(var/i in 1 to bee_amount)
- var/mob/living/simple_animal/hostile/poison/bees/new_bee = new(location)
+ var/mob/living/basic/bee/new_bee = new(location)
if(LAZYLEN(beeagents))
new_bee.assign_reagent(pick(beeagents))
diff --git a/code/modules/research/strange_objects.dm b/code/modules/research/strange_objects.dm
index bffb202d6f0..6a90a3de3bb 100644
--- a/code/modules/research/strange_objects.dm
+++ b/code/modules/research/strange_objects.dm
@@ -135,7 +135,7 @@
/mob/living/simple_animal/pet/dog/pug,
/mob/living/basic/bear/black,
/mob/living/basic/bear/brown,
- /mob/living/simple_animal/hostile/poison/bees,
+ /mob/living/basic/bee,
/mob/living/basic/carp
)
diff --git a/paradise.dme b/paradise.dme
index a8191cb67d3..b3d9ad3584f 100644
--- a/paradise.dme
+++ b/paradise.dme
@@ -2511,6 +2511,9 @@
#include "code\modules\mob\living\basic\hostile\alien\alien_mob_maid.dm"
#include "code\modules\mob\living\basic\hostile\alien\alien_mob_queen.dm"
#include "code\modules\mob\living\basic\hostile\alien\alien_mob_sentinel.dm"
+#include "code\modules\mob\living\basic\hostile\bees\bee_ai_behaviors.dm"
+#include "code\modules\mob\living\basic\hostile\bees\bee_ai_subtree.dm"
+#include "code\modules\mob\living\basic\hostile\bees\bees.dm"
#include "code\modules\mob\living\basic\hostile\giant_spider\giant_spider.dm"
#include "code\modules\mob\living\basic\hostile\giant_spider\giant_spider_actions.dm"
#include "code\modules\mob\living\basic\hostile\giant_spider\giant_spider_ai.dm"
@@ -2723,7 +2726,6 @@
#include "code\modules\mob\living\simple_animal\friendly\friendly_emote.dm"
#include "code\modules\mob\living\simple_animal\friendly\pet.dm"
#include "code\modules\mob\living\simple_animal\hostile\angel_statue.dm"
-#include "code\modules\mob\living\simple_animal\hostile\bees.dm"
#include "code\modules\mob\living\simple_animal\hostile\deathsquid.dm"
#include "code\modules\mob\living\simple_animal\hostile\floorcluwne.dm"
#include "code\modules\mob\living\simple_animal\hostile\headslug.dm"
diff --git a/tools/ci/check_simplemob_additions.py b/tools/ci/check_simplemob_additions.py
index 09bef70e839..41c193f93ea 100644
--- a/tools/ci/check_simplemob_additions.py
+++ b/tools/ci/check_simplemob_additions.py
@@ -132,9 +132,6 @@ BURNDOWN_LIST = {
"/mob/living/simple_animal/hostile/morph/wizard",
"/mob/living/simple_animal/hostile/mushroom",
"/mob/living/simple_animal/hostile/poison",
- "/mob/living/simple_animal/hostile/poison/bees",
- "/mob/living/simple_animal/hostile/poison/bees/queen",
- "/mob/living/simple_animal/hostile/poison/bees/syndi",
"/mob/living/simple_animal/hostile/poison/terror_spider",
"/mob/living/simple_animal/hostile/poison/terror_spider/black",
"/mob/living/simple_animal/hostile/poison/terror_spider/brown",