mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2025-12-21 15:14:17 +00:00
* basic mobs & co no longer indiscriminately perform close-range actions in the presence of obstacles. (#75687) ## About The Pull Request Currently, we don't have any such thing as a general ai_behavior flag for behaviors that need a check for if the current_movement_target is within reach or not. We could fix it case by case by slapping a `CanReach()` check in the `performBehavior()` definition of every `ai_behavior` datum that warrants it, the general issue will keep resurfacing as long as new behaviors are added to the game anyhow, while there's a lot less copypasta involved easier to apply solution to current and future instances of such issue. Worth mentioning not all ai_behaviors with required_range of 1 have this flag. Some are fairly innocuous, such as the follow command, some others kind of handle it already in a more peculiar or complex way, which is also an argument against making it a hardcoded heck for when the required_range is 1 or 0. This has been tested, though there are some rough edges and oddities also unrelated to his PR that might have evaded scrutiny. ## Why It's Good For The Game This should fix #74823, fix #69254, and fix #74713 (I guess? it could have been phrased better). ## Changelog 🆑 fix: basic mobs & co no longer indiscriminately perform close-range actions in the presence of obstacles such as directional windows between them and their target. fix: Doggos should look at you with longing eyes once again if you dare pick up an edible they are trying to eat. /🆑 * basic mobs & co no longer indiscriminately perform close-range actions in the presence of obstacles. --------- Co-authored-by: Ghom <42542238+Ghommie@users.noreply.github.com>
150 lines
5.6 KiB
Plaintext
150 lines
5.6 KiB
Plaintext
/**
|
|
* Traverse to a target with the intention of picking it up.
|
|
* If we can't do that, add it to a list of ignored items.
|
|
*/
|
|
/datum/ai_behavior/fetch_seek
|
|
behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT|AI_BEHAVIOR_REQUIRE_REACH
|
|
|
|
/datum/ai_behavior/fetch_seek/setup(datum/ai_controller/controller, target_key, delivery_key)
|
|
. = ..()
|
|
var/obj/item/fetch_thing = controller.blackboard[target_key]
|
|
// It stopped existing
|
|
if (QDELETED(fetch_thing))
|
|
return FALSE
|
|
set_movement_target(controller, fetch_thing)
|
|
|
|
/datum/ai_behavior/fetch_seek/perform(seconds_per_tick, datum/ai_controller/controller, target_key, delivery_key)
|
|
. = ..()
|
|
var/obj/item/fetch_thing = controller.blackboard[target_key]
|
|
|
|
// It stopped existing
|
|
if (QDELETED(fetch_thing))
|
|
finish_action(controller, FALSE, target_key, delivery_key)
|
|
return
|
|
// We can't pick this up
|
|
if (fetch_thing.anchored)
|
|
finish_action(controller, FALSE, target_key, delivery_key)
|
|
return
|
|
|
|
finish_action(controller, TRUE, target_key, delivery_key)
|
|
|
|
/datum/ai_behavior/fetch_seek/finish_action(datum/ai_controller/controller, success, target_key, delivery_key)
|
|
. = ..()
|
|
if (success)
|
|
return
|
|
// Blacklist item if we failed
|
|
var/obj/item/target = controller.blackboard[target_key]
|
|
if (target)
|
|
controller.set_blackboard_key_assoc_lazylist(BB_FETCH_IGNORE_LIST, target, TRUE)
|
|
controller.clear_blackboard_key(target_key)
|
|
controller.clear_blackboard_key(delivery_key)
|
|
|
|
/**
|
|
* The second half of fetching, deliver the item to a target.
|
|
*/
|
|
/datum/ai_behavior/deliver_fetched_item
|
|
behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT|AI_BEHAVIOR_REQUIRE_REACH
|
|
|
|
/datum/ai_behavior/deliver_fetched_item/setup(datum/ai_controller/controller, delivery_key, storage_key)
|
|
. = ..()
|
|
var/mob/living/return_target = controller.blackboard[delivery_key]
|
|
if(QDELETED(return_target)) // Guess it's mine now
|
|
return FALSE
|
|
set_movement_target(controller, return_target)
|
|
|
|
/datum/ai_behavior/deliver_fetched_item/perform(seconds_per_tick, datum/ai_controller/controller, delivery_key, storage_key)
|
|
. = ..()
|
|
var/mob/living/return_target = controller.blackboard[delivery_key]
|
|
if(QDELETED(return_target))
|
|
finish_action(controller, FALSE, delivery_key)
|
|
return
|
|
|
|
deliver_item(controller, return_target, storage_key)
|
|
finish_action(controller, TRUE, delivery_key)
|
|
|
|
/datum/ai_behavior/deliver_fetched_item/finish_action(datum/ai_controller/controller, success, delivery_key)
|
|
. = ..()
|
|
controller.clear_blackboard_key(delivery_key)
|
|
controller.clear_blackboard_key(BB_ACTIVE_PET_COMMAND)
|
|
|
|
/// Actually deliver the fetched item to the target, if we still have it
|
|
/datum/ai_behavior/deliver_fetched_item/proc/deliver_item(datum/ai_controller/controller, return_target, storage_key)
|
|
var/mob/pawn = controller.pawn
|
|
var/obj/item/carried_item = controller.blackboard[storage_key]
|
|
if(QDELETED(carried_item) || carried_item.loc != pawn)
|
|
pawn.visible_message(span_notice("[pawn] looks around as if [pawn.p_they()] [pawn.p_have()] lost something."))
|
|
finish_action(controller, FALSE)
|
|
return
|
|
|
|
pawn.visible_message(span_notice("[pawn] delivers [carried_item] to [return_target]."))
|
|
carried_item.forceMove(get_turf(return_target))
|
|
controller.clear_blackboard_key(storage_key)
|
|
return TRUE
|
|
|
|
/**
|
|
* The alternate second half of fetching, attack the item if we can eat it.
|
|
* Or make pleading eyes at someone who has picked it up.
|
|
*
|
|
* Unfortunately this doesn't work because food can't currently be eaten by mobs.
|
|
*/
|
|
/datum/ai_behavior/eat_fetched_snack
|
|
behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT
|
|
action_cooldown = 0.8 SECONDS
|
|
|
|
/datum/ai_behavior/eat_fetched_snack/setup(datum/ai_controller/controller, target_key, delivery_key)
|
|
. = ..()
|
|
var/obj/item/snack = controller.blackboard[target_key]
|
|
if(!istype(snack) || !IS_EDIBLE(snack) || !(isturf(snack.loc) || ishuman(snack.loc)))
|
|
return FALSE // This isn't food at all!
|
|
set_movement_target(controller, snack)
|
|
|
|
/datum/ai_behavior/eat_fetched_snack/perform(seconds_per_tick, datum/ai_controller/controller, target_key, delivery_key)
|
|
. = ..()
|
|
var/obj/item/snack = controller.blackboard[target_key]
|
|
var/is_living_loc = isliving(snack.loc)
|
|
if(QDELETED(snack) || (!isturf(snack.loc) && !is_living_loc))
|
|
finish_action(controller, FALSE) // Where did it go?
|
|
return
|
|
|
|
var/mob/living/basic/basic_pawn = controller.pawn
|
|
if(is_living_loc)
|
|
if(SPT_PROB(10, seconds_per_tick))
|
|
basic_pawn.manual_emote("Stares at [snack.loc]'s [snack.name] intently.")
|
|
return
|
|
|
|
if(!basic_pawn.Adjacent(snack))
|
|
return
|
|
|
|
basic_pawn.melee_attack(snack) // snack attack!
|
|
|
|
if(QDELETED(snack)) // we ate it!
|
|
finish_action(controller, TRUE, target_key, delivery_key)
|
|
|
|
/datum/ai_behavior/eat_fetched_snack/finish_action(datum/ai_controller/controller, succeeded, target_key, delivery_key)
|
|
. = ..()
|
|
controller.clear_blackboard_key(target_key)
|
|
controller.clear_blackboard_key(delivery_key)
|
|
controller.clear_blackboard_key(BB_ACTIVE_PET_COMMAND)
|
|
|
|
/**
|
|
* Clear our failed fetch list every so often
|
|
*/
|
|
/datum/ai_behavior/forget_failed_fetches
|
|
/// How long to wait between resetting the list
|
|
var/cooldown_duration = AI_FETCH_IGNORE_DURATION
|
|
/// Time until we should forget things we failed to pick up
|
|
COOLDOWN_DECLARE(reset_ignore_cooldown)
|
|
|
|
/datum/ai_behavior/forget_failed_fetches/setup(datum/ai_controller/controller, ...)
|
|
. = ..()
|
|
if (!COOLDOWN_FINISHED(src, reset_ignore_cooldown))
|
|
return FALSE
|
|
if (!length(controller.blackboard[BB_FETCH_IGNORE_LIST]))
|
|
return
|
|
|
|
/datum/ai_behavior/forget_failed_fetches/perform(seconds_per_tick, datum/ai_controller/controller)
|
|
. = ..()
|
|
COOLDOWN_START(src, reset_ignore_cooldown, cooldown_duration)
|
|
controller.clear_blackboard_key(BB_FETCH_IGNORE_LIST)
|
|
finish_action(controller, TRUE)
|