From 3905d5ec49b89a682ab6e824f0e7c61742ce8d45 Mon Sep 17 00:00:00 2001 From: Ben10Omintrix <138636438+Ben10Omintrix@users.noreply.github.com> Date: Thu, 24 Oct 2024 18:06:00 +0300 Subject: [PATCH] penguins can now fish (and fishing ai expansions) (#87267) ## About The Pull Request penguins are now able to fish! they will drill water holes in ice using their beaks and catch fish from there. also expands the fishing ai a bit to make it a bit more customizable. animals will now fish only when they're hungry, otherwise there will be a cooldown between every fishing attempt, defaulted to 45 seconds but can be edited depending on the mob. ## Why It's Good For The Game makes the fishing behavior alot more generic, before it was only compatible with lobstrosities but this makes it so it can be applied to any mob. also reduces lobstrosity fishing frequency by a tad bit as they were too spammy before. ## Changelog :cl: add: penguins will now fish from water holes /:cl: --- code/__DEFINES/ai/ai_blackboard.dm | 20 ++++- code/__DEFINES/traits/declarations.dm | 3 + code/_globalvars/traits/_traits.dm | 1 + code/datums/ai/_ai_controller.dm | 6 +- .../interact_with_target.dm | 4 +- .../ai/basic_mobs/basic_subtrees/fishing.dm | 44 +++++++++++ code/datums/ai/generic/find_and_set.dm | 10 ++- .../ai/hunting_behavior/hunting_behaviors.dm | 9 ++- code/game/turfs/open/ice.dm | 16 +++- .../basic/lavaland/lobstrosity/lobstrosity.dm | 2 + .../lavaland/lobstrosity/lobstrosity_ai.dm | 15 ++-- .../basic/pets/{ => penguin}/penguin.dm | 56 ++----------- .../living/basic/pets/penguin/penguin_ai.dm | 79 +++++++++++++++++++ tgstation.dme | 4 +- 14 files changed, 195 insertions(+), 74 deletions(-) create mode 100644 code/datums/ai/basic_mobs/basic_subtrees/fishing.dm rename code/modules/mob/living/basic/pets/{ => penguin}/penguin.dm (72%) create mode 100644 code/modules/mob/living/basic/pets/penguin/penguin_ai.dm diff --git a/code/__DEFINES/ai/ai_blackboard.dm b/code/__DEFINES/ai/ai_blackboard.dm index 2b25d0cfb31..24461464c3e 100644 --- a/code/__DEFINES/ai/ai_blackboard.dm +++ b/code/__DEFINES/ai/ai_blackboard.dm @@ -172,8 +172,26 @@ ///Text we display when we befriend someone #define BB_FRIENDLY_MESSAGE "friendly_message" +//fishing! + ///our fishing target -#define BB_FISHING_TARGET "fishing_target" +#define BB_FISHING_TARGET "BB_fishing_target" + +///key holding the list of things we are able to fish from +#define BB_FISHABLE_LIST "BB_fishable_list" + +///key holding our cooldown between fishing attempts +#define BB_FISHING_COOLDOWN "BB_fishing_cooldown" + +///key that holds the next time we will start fishing +#define BB_FISHING_TIMER "BB_fishing_timer" + +///are we ONLY allowed to fish when we're hungry? +#define BB_ONLY_FISH_WHILE_HUNGRY "BB_only_fish_while_hungry" + +///drillable ice we can make holes in +#define BB_DRILLABLE_ICE "BB_drillable_ice" + // Keys used by one and only one behavior // Used to hold state without making bigass lists diff --git a/code/__DEFINES/traits/declarations.dm b/code/__DEFINES/traits/declarations.dm index db012679363..de93661e169 100644 --- a/code/__DEFINES/traits/declarations.dm +++ b/code/__DEFINES/traits/declarations.dm @@ -1334,6 +1334,9 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai ///Trait which silences all chemical reactions in its container #define TRAIT_SILENT_REACTIONS "silent_reactions" +///Trait given to mobs that can dig +#define TRAIT_MOB_CAN_DIG "mob_can_dig" + /** * * This trait is used in some interactions very high in the interaction chain to allow diff --git a/code/_globalvars/traits/_traits.dm b/code/_globalvars/traits/_traits.dm index 13cb39b7e78..9eef84860b5 100644 --- a/code/_globalvars/traits/_traits.dm +++ b/code/_globalvars/traits/_traits.dm @@ -335,6 +335,7 @@ GLOBAL_LIST_INIT(traits_by_type, list( "TRAIT_MINDSHIELD" = TRAIT_MINDSHIELD, "TRAIT_MIND_TEMPORARILY_GONE" = TRAIT_MIND_TEMPORARILY_GONE, "TRAIT_MOB_BREEDER" = TRAIT_MOB_BREEDER, + "TRAIT_MOB_CAN_DIG" = TRAIT_MOB_CAN_DIG, "TRAIT_MOB_EATER" = TRAIT_MOB_EATER, "TRAIT_MOB_HATCHED" = TRAIT_MOB_HATCHED, "TRAIT_MOB_HIDE_HAPPINESS" = TRAIT_MOB_HIDE_HAPPINESS, diff --git a/code/datums/ai/_ai_controller.dm b/code/datums/ai/_ai_controller.dm index d6230ec8d35..65e3acf5f6f 100644 --- a/code/datums/ai/_ai_controller.dm +++ b/code/datums/ai/_ai_controller.dm @@ -268,11 +268,6 @@ multiple modular subtrees with behaviors return AI_STATUS_IDLE return AI_STATUS_ON -/datum/ai_controller/proc/get_current_turf() - var/mob/living/mob_pawn = pawn - var/turf/pawn_turf = get_turf(mob_pawn) - to_chat(world, "[pawn_turf]") - ///Called when the AI controller pawn changes z levels, we check if there's any clients on the new one and wake up the AI if there is. /datum/ai_controller/proc/on_changed_z_level(atom/source, turf/old_turf, turf/new_turf, same_z_layer, notify_contents) SIGNAL_HANDLER @@ -380,6 +375,7 @@ multiple modular subtrees with behaviors if(isnull(current_movement_target)) fail_behavior(current_behavior) return + ///Stops pawns from performing such actions that should require the target to be adjacent. var/atom/movable/moving_pawn = pawn var/can_reach = !(current_behavior.behavior_flags & AI_BEHAVIOR_REQUIRE_REACH) || moving_pawn.CanReach(current_movement_target) diff --git a/code/datums/ai/basic_mobs/basic_ai_behaviors/interact_with_target.dm b/code/datums/ai/basic_mobs/basic_ai_behaviors/interact_with_target.dm index 3b0c4245656..e3b202c5e16 100644 --- a/code/datums/ai/basic_mobs/basic_ai_behaviors/interact_with_target.dm +++ b/code/datums/ai/basic_mobs/basic_ai_behaviors/interact_with_target.dm @@ -3,6 +3,8 @@ behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_REQUIRE_REACH ///should we be clearing the target after the fact? var/clear_target = TRUE + ///should our combat mode be off during interaction? + var/combat_mode = TRUE /datum/ai_behavior/interact_with_target/setup(datum/ai_controller/controller, target_key) . = ..() @@ -15,7 +17,7 @@ var/atom/target = controller.blackboard[target_key] if(QDELETED(target) || !pre_interact(controller, target)) return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - controller.ai_interact(target) + controller.ai_interact(target, combat_mode) return AI_BEHAVIOR_SUCCEEDED | AI_BEHAVIOR_DELAY /datum/ai_behavior/interact_with_target/finish_action(datum/ai_controller/controller, succeeded, target_key) diff --git a/code/datums/ai/basic_mobs/basic_subtrees/fishing.dm b/code/datums/ai/basic_mobs/basic_subtrees/fishing.dm new file mode 100644 index 00000000000..e17f50740fa --- /dev/null +++ b/code/datums/ai/basic_mobs/basic_subtrees/fishing.dm @@ -0,0 +1,44 @@ +#define FISHING_COOLDOWN 45 SECONDS + +///subtree for fishing and eating food! +/datum/ai_planning_subtree/fish + ///behavior we use to find fishable objects + var/datum/ai_behavior/find_fishable_behavior = /datum/ai_behavior/find_and_set/in_list + ///behavior we use to fish! + var/datum/ai_behavior/fishing_behavior = /datum/ai_behavior/interact_with_target/fishing + ///blackboard key storing things we can fish from + var/fishable_list_key = BB_FISHABLE_LIST + ///key where we store found fishable items + var/fishing_target_key = BB_FISHING_TARGET + ///key where we store our fishing cooldown + var/fishing_cooldown_key = BB_FISHING_COOLDOWN + ///our fishing range + var/fishing_range = 5 + +/datum/ai_planning_subtree/fish/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) + if(controller.blackboard[BB_ONLY_FISH_WHILE_HUNGRY] && controller.blackboard[BB_NEXT_FOOD_EAT] > world.time) + return + if(controller.blackboard[BB_FISHING_TIMER] > world.time) + return + if(!controller.blackboard_key_exists(fishing_target_key)) + controller.queue_behavior(find_fishable_behavior, fishing_target_key, controller.blackboard[fishable_list_key], fishing_range) + return + controller.queue_behavior(/datum/ai_behavior/interact_with_target/fishing, fishing_target_key, fishing_cooldown_key) + return SUBTREE_RETURN_FINISH_PLANNING + +///less expensive fishing behavior! +/datum/ai_planning_subtree/fish/fish_from_turfs + find_fishable_behavior = /datum/ai_behavior/find_and_set/in_list/closest_turf + +/datum/ai_behavior/interact_with_target/fishing + clear_target = FALSE + combat_mode = FALSE + +/datum/ai_behavior/interact_with_target/fishing/finish_action(datum/ai_controller/controller, succeeded, fishing_target_key, fishing_cooldown_key) + . = ..() + if(!succeeded) + return + var/cooldown = controller.blackboard[fishing_cooldown_key] || FISHING_COOLDOWN + controller.set_blackboard_key(BB_FISHING_TIMER, world.time + cooldown) + +#undef FISHING_COOLDOWN diff --git a/code/datums/ai/generic/find_and_set.dm b/code/datums/ai/generic/find_and_set.dm index 5a424f304f2..0096ff0f2eb 100644 --- a/code/datums/ai/generic/find_and_set.dm +++ b/code/datums/ai/generic/find_and_set.dm @@ -181,7 +181,6 @@ /datum/ai_behavior/find_and_set/in_list/turf_types - /datum/ai_behavior/find_and_set/in_list/turf_types/search_tactic(datum/ai_controller/controller, locate_paths, search_range) var/list/found = RANGE_TURFS(search_range, controller.pawn) shuffle_inplace(found) @@ -191,3 +190,12 @@ if(can_see(controller.pawn, possible_turf, search_range)) return possible_turf return null + +/datum/ai_behavior/find_and_set/in_list/closest_turf + +/datum/ai_behavior/find_and_set/in_list/closest_turf/search_tactic(datum/ai_controller/controller, locate_paths, search_range) + var/list/found = RANGE_TURFS(search_range, controller.pawn) + for(var/turf/possible_turf as anything in found) + if(!is_type_in_typecache(possible_turf, locate_paths) || !can_see(controller.pawn, possible_turf, search_range)) + found -= possible_turf + return (length(found)) ? get_closest_atom(/turf, found, controller.pawn) : null diff --git a/code/datums/ai/hunting_behavior/hunting_behaviors.dm b/code/datums/ai/hunting_behavior/hunting_behaviors.dm index c202c4be6a7..0ab14ff0d32 100644 --- a/code/datums/ai/hunting_behavior/hunting_behaviors.dm +++ b/code/datums/ai/hunting_behavior/hunting_behaviors.dm @@ -50,11 +50,13 @@ /// Finds a specific atom type to hunt. /datum/ai_behavior/find_hunt_target + ///is this only meant to search for turf types? + var/search_turf_types = FALSE /datum/ai_behavior/find_hunt_target/perform(seconds_per_tick, datum/ai_controller/controller, hunting_target_key, types_to_hunt, hunt_range) var/mob/living/living_mob = controller.pawn - - for(var/atom/possible_dinner as anything in typecache_filter_list(range(hunt_range, living_mob), types_to_hunt)) + var/list/interesting_objects = search_turf_types ? RANGE_TURFS(hunt_range, living_mob) : oview(hunt_range, living_mob) + for(var/atom/possible_dinner as anything in typecache_filter_list(interesting_objects, types_to_hunt)) if(!valid_dinner(living_mob, possible_dinner, hunt_range, controller, seconds_per_tick)) continue controller.set_blackboard_key(hunting_target_key, possible_dinner) @@ -69,6 +71,9 @@ return can_see(source, dinner, radius) +/datum/ai_behavior/find_hunt_target/search_turf_types + search_turf_types = TRUE + /// Hunts down a specific atom type. /datum/ai_behavior/hunt_target behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_REQUIRE_REACH diff --git a/code/game/turfs/open/ice.dm b/code/game/turfs/open/ice.dm index f28bc8dd4b0..39abe6ebfee 100644 --- a/code/game/turfs/open/ice.dm +++ b/code/game/turfs/open/ice.dm @@ -42,15 +42,23 @@ if(can_make_hole) . += span_info("You could use a [EXAMINE_HINT("shovel")] or a [EXAMINE_HINT("pick")] to dig a fishing hole here.") +/turf/open/misc/ice/attack_animal(mob/living/animal, list/modifiers) + . = ..() + if(HAS_TRAIT(animal, TRAIT_MOB_CAN_DIG)) + dig_hole(animal) + /turf/open/misc/ice/item_interaction(mob/living/user, obj/item/tool, list/modifiers) - if(!can_make_hole) - return NONE if(tool.tool_behaviour != TOOL_SHOVEL && tool.tool_behaviour != TOOL_MINING) return NONE + return dig_hole(user) ? ITEM_INTERACT_SUCCESS : NONE + +/turf/open/misc/ice/proc/dig_hole(mob/living/user) + if(!can_make_hole) + return FALSE balloon_alert(user, "digging...") playsound(src, 'sound/effects/shovel_dig.ogg', 50, TRUE) if(!do_after(user, 5 SECONDS, src)) - return NONE + return FALSE balloon_alert(user, "dug hole") AddComponent(/datum/component/fishing_spot, GLOB.preset_fish_sources[/datum/fish_source/ice_fishing]) ADD_TRAIT(src, TRAIT_CATCH_AND_RELEASE, INNATE_TRAIT) @@ -58,7 +66,7 @@ can_make_hole = FALSE RemoveElement(/datum/element/contextual_screentip_tools, tool_screentips) flags_1 &= ~HAS_CONTEXTUAL_SCREENTIPS_1 - return ITEM_INTERACT_SUCCESS + return TRUE /turf/open/misc/ice/smooth icon_state = "ice_turf-255" diff --git a/code/modules/mob/living/basic/lavaland/lobstrosity/lobstrosity.dm b/code/modules/mob/living/basic/lavaland/lobstrosity/lobstrosity.dm index 625dc4af907..d7f5a024ace 100644 --- a/code/modules/mob/living/basic/lavaland/lobstrosity/lobstrosity.dm +++ b/code/modules/mob/living/basic/lavaland/lobstrosity/lobstrosity.dm @@ -55,6 +55,8 @@ charge = new charge_type(src) charge.Grant(src) ai_controller.set_blackboard_key(BB_TARGETED_ACTION, charge) + var/static/list/fishable_turfs = typecacheof(list(/turf/open/lava)) + ai_controller.set_blackboard_key(BB_FISHABLE_LIST, fishable_turfs) /mob/living/basic/mining/lobstrosity/Destroy() QDEL_NULL(charge) diff --git a/code/modules/mob/living/basic/lavaland/lobstrosity/lobstrosity_ai.dm b/code/modules/mob/living/basic/lavaland/lobstrosity/lobstrosity_ai.dm index de6ca4a0cc1..647aeac9474 100644 --- a/code/modules/mob/living/basic/lavaland/lobstrosity/lobstrosity_ai.dm +++ b/code/modules/mob/living/basic/lavaland/lobstrosity/lobstrosity_ai.dm @@ -14,6 +14,8 @@ BB_LOBSTROSITY_FINGER_LUST = 0, BB_LOBSTROSITY_NAIVE_HUNTER = FALSE, BB_BASIC_MOB_FLEE_DISTANCE = 8, + BB_EAT_FOOD_COOLDOWN = 3 MINUTES, + BB_ONLY_FISH_WHILE_HUNGRY = TRUE, BB_TARGET_PRIORITY_TRAIT = TRAIT_SCARY_FISHERMAN, BB_OWNER_SELF_HARM_RESPONSES = SHRIMP_HARM_RESPONSES, ) @@ -31,7 +33,7 @@ /datum/ai_planning_subtree/attack_obstacle_in_path, /datum/ai_planning_subtree/basic_melee_attack_subtree/lobster, /datum/ai_planning_subtree/find_food, - /datum/ai_planning_subtree/find_and_hunt_target/lobster_fishing, + /datum/ai_planning_subtree/fish/fish_from_turfs, /datum/ai_planning_subtree/find_fingers, ) @@ -65,7 +67,7 @@ /datum/ai_planning_subtree/attack_obstacle_in_path, /datum/ai_planning_subtree/basic_melee_attack_subtree/lobster, /datum/ai_planning_subtree/find_food, - /datum/ai_planning_subtree/find_and_hunt_target/lobster_fishing, + /datum/ai_planning_subtree/fish/fish_from_turfs, /datum/ai_planning_subtree/find_fingers, ) @@ -81,7 +83,7 @@ /datum/ai_planning_subtree/attack_obstacle_in_path, /datum/ai_planning_subtree/basic_melee_attack_subtree/lobster, /datum/ai_planning_subtree/find_food, - /datum/ai_planning_subtree/find_and_hunt_target/lobster_fishing, + /datum/ai_planning_subtree/fish/fish_from_turfs, /datum/ai_planning_subtree/find_fingers, ) @@ -97,15 +99,10 @@ /datum/ai_planning_subtree/attack_obstacle_in_path, /datum/ai_planning_subtree/basic_melee_attack_subtree/lobster, /datum/ai_planning_subtree/find_food, - /datum/ai_planning_subtree/find_and_hunt_target/lobster_fishing, + /datum/ai_planning_subtree/fish/fish_from_turfs, /datum/ai_planning_subtree/find_fingers, ) -/datum/ai_planning_subtree/find_and_hunt_target/lobster_fishing - target_key = BB_FISHING_TARGET - hunt_targets = list(/turf/open/lava) - hunting_behavior = /datum/ai_behavior/hunt_target/interact_with_target/reset_target_combat_mode_off - /datum/ai_planning_subtree/basic_melee_attack_subtree/lobster melee_attack_behavior = /datum/ai_behavior/basic_melee_attack/lobster diff --git a/code/modules/mob/living/basic/pets/penguin.dm b/code/modules/mob/living/basic/pets/penguin/penguin.dm similarity index 72% rename from code/modules/mob/living/basic/pets/penguin.dm rename to code/modules/mob/living/basic/pets/penguin/penguin.dm index c1afe10187c..5a473203998 100644 --- a/code/modules/mob/living/basic/pets/penguin.dm +++ b/code/modules/mob/living/basic/pets/penguin/penguin.dm @@ -35,6 +35,7 @@ AddElement(/datum/element/ai_flee_while_injured) AddElement(/datum/element/pet_bonus, "honk") AddElementTrait(TRAIT_WADDLING, INNATE_TRAIT, /datum/element/waddling) + ADD_TRAIT(src, TRAIT_MOB_CAN_DIG, INNATE_TRAIT) if(!can_lay_eggs) return AddComponent(\ @@ -47,6 +48,11 @@ max_eggs_held = 1,\ egg_laid_callback = CALLBACK(src, PROC_REF(lay_penguin_egg)),\ ) + var/static/list/fishable_objects = typecacheof(list(/turf/open/misc/ice)) + ai_controller.set_blackboard_key(BB_FISHABLE_LIST, fishable_objects) + var/static/list/delicious_food = list(/obj/item/fish) + AddElement(/datum/element/basic_eating, heal_amt = 10, food_types = delicious_food) + ai_controller.set_blackboard_key(BB_BASIC_FOODS, typecacheof(delicious_food)) /mob/living/basic/pet/penguin/UnarmedAttack(atom/attack_target, proximity_flag, list/modifiers) . = ..() @@ -93,38 +99,6 @@ carried_egg = null cut_overlay("penguin_egg_overlay") -/datum/ai_controller/basic_controller/penguin - blackboard = list( - BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, - ) - - ai_traits = STOP_MOVING_WHEN_PULLED - ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk - - planning_subtrees = list( - /datum/ai_planning_subtree/find_nearest_thing_which_attacked_me_to_flee, - /datum/ai_planning_subtree/flee_target, - /datum/ai_planning_subtree/find_and_hunt_target/penguin_egg, - /datum/ai_planning_subtree/random_speech/penguin, - ) - -/datum/ai_planning_subtree/find_and_hunt_target/penguin_egg - target_key = BB_LOW_PRIORITY_HUNTING_TARGET - hunting_behavior = /datum/ai_behavior/hunt_target/penguin_egg - finding_behavior = /datum/ai_behavior/find_hunt_target/penguin_egg - hunt_targets = list(/obj/item/food/egg/penguin_egg) - hunt_range = 7 - -/datum/ai_behavior/find_hunt_target/penguin_egg/valid_dinner(mob/living/source, atom/dinner, radius) - return can_see(source, dinner, radius) && !(dinner in source.contents) -/datum/ai_behavior/hunt_target/penguin_egg - hunt_cooldown = 15 SECONDS - always_reset_target = TRUE - -/datum/ai_behavior/hunt_target/penguin_egg/target_caught(mob/living/basic/hunter, obj/item/food/egg/target) - hunter.UnarmedAttack(target, TRUE) - /mob/living/basic/pet/penguin/emperor name = "emperor penguin" real_name = "penguin" @@ -163,7 +137,6 @@ ///will it grow up? var/can_grow_up = TRUE - /mob/living/basic/pet/penguin/baby/Initialize(mapload) . = ..() if(!can_grow_up) @@ -187,23 +160,6 @@ /mob/living/basic/pet/penguin/baby/proc/ready_to_grow() return (stat == CONSCIOUS) -/datum/ai_controller/basic_controller/penguin/baby - blackboard = list( - BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, - BB_FIND_MOM_TYPES = list(/mob/living/basic/pet/penguin), - BB_IGNORE_MOM_TYPES = list(/mob/living/basic/pet/penguin/baby), - ) - - ai_traits = STOP_MOVING_WHEN_PULLED - ai_movement = /datum/ai_movement/basic_avoidance - idle_behavior = /datum/idle_behavior/idle_random_walk - - planning_subtrees = list( - /datum/ai_planning_subtree/find_nearest_thing_which_attacked_me_to_flee, - /datum/ai_planning_subtree/flee_target, - /datum/ai_planning_subtree/look_for_adult, - ) - /mob/living/basic/pet/penguin/baby/permanent can_grow_up = FALSE diff --git a/code/modules/mob/living/basic/pets/penguin/penguin_ai.dm b/code/modules/mob/living/basic/pets/penguin/penguin_ai.dm new file mode 100644 index 00000000000..90d12e87941 --- /dev/null +++ b/code/modules/mob/living/basic/pets/penguin/penguin_ai.dm @@ -0,0 +1,79 @@ + +/datum/ai_controller/basic_controller/penguin + blackboard = list( + BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, + BB_ONLY_FISH_WHILE_HUNGRY = TRUE, + ) + + ai_traits = STOP_MOVING_WHEN_PULLED + ai_movement = /datum/ai_movement/basic_avoidance + idle_behavior = /datum/idle_behavior/idle_random_walk + + planning_subtrees = list( + /datum/ai_planning_subtree/find_nearest_thing_which_attacked_me_to_flee, + /datum/ai_planning_subtree/flee_target, + /datum/ai_planning_subtree/find_food, + /datum/ai_planning_subtree/fish/drilled_ice, + /datum/ai_planning_subtree/find_and_hunt_target/drill_ice, + /datum/ai_planning_subtree/find_and_hunt_target/penguin_egg, + /datum/ai_planning_subtree/random_speech/penguin, + ) + +///subtree to find baby eggs! +/datum/ai_planning_subtree/find_and_hunt_target/penguin_egg + target_key = BB_LOW_PRIORITY_HUNTING_TARGET + hunting_behavior = /datum/ai_behavior/hunt_target/interact_with_target/reset_target + finding_behavior = /datum/ai_behavior/find_hunt_target/penguin_egg + hunt_targets = list(/obj/item/food/egg/penguin_egg) + hunt_range = 7 + +/datum/ai_behavior/find_hunt_target/penguin_egg/valid_dinner(mob/living/source, atom/dinner, radius) + return can_see(source, dinner, radius) && !(dinner in source.contents) + +///subtree to find diggable ice we can fish from! +/datum/ai_planning_subtree/find_and_hunt_target/drill_ice + target_key = BB_DRILLABLE_ICE + hunting_behavior = /datum/ai_behavior/hunt_target/interact_with_target/reset_target + finding_behavior = /datum/ai_behavior/find_hunt_target/search_turf_types/drillable_ice + hunt_targets = list(/turf/open/misc/ice) + hunt_range = 7 + +/datum/ai_behavior/find_hunt_target/search_turf_types/drillable_ice + +/datum/ai_behavior/find_hunt_target/search_turf_types/drillable_ice/valid_dinner(mob/living/source, turf/open/misc/ice/ice, radius) + return ice.can_make_hole && can_see(source, ice, radius) + +/datum/ai_planning_subtree/find_and_hunt_target/drill_ice/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) + if(controller.blackboard_key_exists(BB_FISHING_TARGET)) + return + return ..() + +/datum/ai_planning_subtree/fish/drilled_ice + find_fishable_behavior = /datum/ai_behavior/find_and_set/in_list/drilled_ice + +/datum/ai_behavior/find_and_set/in_list/drilled_ice/search_tactic(datum/ai_controller/controller, locate_paths, search_range) + for(var/atom/possible_ice as anything in RANGE_TURFS(search_range, controller.pawn)) + if(!istype(possible_ice, /turf/open/misc/ice)) + continue + if(HAS_TRAIT(possible_ice, TRAIT_FISHING_SPOT)) + return possible_ice + return null + +///ai controller for the baby penguin +/datum/ai_controller/basic_controller/penguin/baby + blackboard = list( + BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, + BB_FIND_MOM_TYPES = list(/mob/living/basic/pet/penguin), + BB_IGNORE_MOM_TYPES = list(/mob/living/basic/pet/penguin/baby), + ) + + ai_traits = STOP_MOVING_WHEN_PULLED + ai_movement = /datum/ai_movement/basic_avoidance + idle_behavior = /datum/idle_behavior/idle_random_walk + + planning_subtrees = list( + /datum/ai_planning_subtree/find_nearest_thing_which_attacked_me_to_flee, + /datum/ai_planning_subtree/flee_target, + /datum/ai_planning_subtree/find_food, + /datum/ai_planning_subtree/look_for_adult, + ) diff --git a/tgstation.dme b/tgstation.dme index f6b649ee670..f2fc8af67b8 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -927,6 +927,7 @@ #include "code\datums\ai\basic_mobs\basic_subtrees\find_paper_and_write.dm" #include "code\datums\ai\basic_mobs\basic_subtrees\find_parent.dm" #include "code\datums\ai\basic_mobs\basic_subtrees\find_targets_prioritize_traits.dm" +#include "code\datums\ai\basic_mobs\basic_subtrees\fishing.dm" #include "code\datums\ai\basic_mobs\basic_subtrees\flee_target.dm" #include "code\datums\ai\basic_mobs\basic_subtrees\go_for_swim.dm" #include "code\datums\ai\basic_mobs\basic_subtrees\maintain_distance.dm" @@ -4971,7 +4972,6 @@ #include "code\modules\mob\living\basic\minebots\minebot_remote_control.dm" #include "code\modules\mob\living\basic\minebots\minebot_upgrades.dm" #include "code\modules\mob\living\basic\pets\fox.dm" -#include "code\modules\mob\living\basic\pets\penguin.dm" #include "code\modules\mob\living\basic\pets\pet.dm" #include "code\modules\mob\living\basic\pets\pet_designer.dm" #include "code\modules\mob\living\basic\pets\sloth.dm" @@ -5000,6 +5000,8 @@ #include "code\modules\mob\living\basic\pets\parrot\parrot_ai\parrot_hoarding.dm" #include "code\modules\mob\living\basic\pets\parrot\parrot_ai\parrot_perching.dm" #include "code\modules\mob\living\basic\pets\parrot\parrot_ai\parroting_action.dm" +#include "code\modules\mob\living\basic\pets\penguin\penguin.dm" +#include "code\modules\mob\living\basic\pets\penguin\penguin_ai.dm" #include "code\modules\mob\living\basic\pets\pet_cult\pet_cult_abilities.dm" #include "code\modules\mob\living\basic\pets\pet_cult\pet_cult_ai.dm" #include "code\modules\mob\living\basic\ruin_defender\cybersun_aicore.dm"