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",