Skip to content

Commit

Permalink
Fix monkeys not being able to eat food then getting stuck forever (#1106
Browse files Browse the repository at this point in the history
)

* fix monkey eating

* clean this up
  • Loading branch information
Kapu1178 authored Oct 28, 2024
1 parent edd6fcc commit 815b636
Show file tree
Hide file tree
Showing 7 changed files with 58 additions and 58 deletions.
2 changes: 2 additions & 0 deletions code/__DEFINES/ai.dm
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@
#define AI_BEHAVIOR_KEEP_MOVING_TOWARDS_TARGET_ON_FINISH (1<<3)
///Does this behavior NOT block planning?
#define AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION (1<<4)
///Does this require the current_movement_target to be adjacent and in reach?
#define AI_BEHAVIOR_REQUIRE_REACH (1<<1)

///AI flags
#define STOP_MOVING_WHEN_PULLED (1<<0)
Expand Down
76 changes: 34 additions & 42 deletions code/datums/ai/_ai_controller.dm
Original file line number Diff line number Diff line change
Expand Up @@ -161,52 +161,42 @@ multiple modular subtrees with behaviors
SSmove_manager.stop_looping(pawn) //stop moving
return //this should remove them from processing in the future through event-based stuff.

if(!LAZYLEN(current_behaviors) && default_behavior)
if(behavior_cooldowns[GET_AI_BEHAVIOR(default_behavior)] > world.time)
return

queue_behavior(default_behavior)

if(current_movement_target && get_dist(pawn, current_movement_target) > max_target_distance) //The distance is out of range
CancelActions()
return

for(var/datum/ai_behavior/current_behavior as anything in current_behaviors)

// Convert the current behaviour action cooldown to realtime seconds from deciseconds.current_behavior
// Then pick the max of this and the delta_time passed to ai_controller.process()
// Action cooldowns cannot happen faster than delta_time, so delta_time should be the value used in this scenario.
var/action_delta_time = max(current_behavior.action_cooldown * 0.1, delta_time)
// Then pick the max of this and the seconds_per_tick passed to ai_controller.process()
// Action cooldowns cannot happen faster than seconds_per_tick, so seconds_per_tick should be the value used in this scenario.
var/action_seconds_per_tick = max(current_behavior.get_cooldown(src) * 0.1, delta_time)

if(current_behavior.behavior_flags & AI_BEHAVIOR_REQUIRE_MOVEMENT) //Might need to move closer
if(!current_movement_target)
stack_trace("[pawn] wants to perform action type [current_behavior.type] which requires movement, but has no current movement target!")
return //This can cause issues, so don't let these slide.

if(current_behavior.required_distance >= get_dist(pawn, current_movement_target)) ///Are we close enough to engage?
if(ai_movement.moving_controllers[src] == current_movement_target) //We are close enough, if we're moving stop.
ai_movement.stop_moving_towards(src)
if(!(current_behavior.behavior_flags & AI_BEHAVIOR_REQUIRE_MOVEMENT))
if(behavior_cooldowns[current_behavior] > world.time) //Still on cooldown
continue
ProcessBehavior(action_seconds_per_tick, current_behavior)
return

if(behavior_cooldowns[current_behavior] > world.time) //Still on cooldown
continue
if(isnull(current_movement_target))
fail_behavior(current_behavior)
return

ProcessBehavior(action_delta_time, 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)
if(can_reach && current_behavior.required_distance >= get_dist(moving_pawn, current_movement_target)) ///Are we close enough to engage?
if(ai_movement.moving_controllers[src] == current_movement_target) //We are close enough, if we're moving stop.
ai_movement.stop_moving_towards(src)

else if(ai_movement.moving_controllers[src] != current_movement_target) //We're too far, if we're not already moving start doing it.
ai_movement.start_moving_towards(src, current_movement_target, current_behavior.required_distance) //Then start moving
if(behavior_cooldowns[current_behavior] > world.time) //Still on cooldown
continue
ProcessBehavior(action_seconds_per_tick, current_behavior)
return

if(current_behavior.behavior_flags & AI_BEHAVIOR_MOVE_AND_PERFORM) //If we can move and perform then do so.
if(behavior_cooldowns[current_behavior] > world.time) //Still on cooldown
continue
ProcessBehavior(action_delta_time, current_behavior)
return
if(ai_movement.moving_controllers[src] != current_movement_target) //We're too far, if we're not already moving start doing it.
ai_movement.start_moving_towards(src, current_movement_target, current_behavior.required_distance) //Then start moving

else //No movement required
if(current_behavior.behavior_flags & AI_BEHAVIOR_MOVE_AND_PERFORM) //If we can move and perform then do so.
if(behavior_cooldowns[current_behavior] > world.time) //Still on cooldown
continue

ProcessBehavior(action_delta_time, current_behavior)
ProcessBehavior(action_seconds_per_tick, current_behavior)
return

///This is where you decide what actions are taken by the AI.
Expand Down Expand Up @@ -320,13 +310,15 @@ multiple modular subtrees with behaviors
/datum/ai_controller/proc/CancelActions()
if(!LAZYLEN(current_behaviors))
return
for(var/i in current_behaviors)
var/datum/ai_behavior/current_behavior = i
var/list/arguments = list(src, FALSE)
var/list/stored_arguments = behavior_args[current_behavior.type]
if(stored_arguments)
arguments += stored_arguments
current_behavior.finish_action(arglist(arguments))
for(var/datum/ai_behavior/current_behavior as anything in current_behaviors)
fail_behavior(current_behavior)

/datum/ai_controller/proc/fail_behavior(datum/ai_behavior/current_behavior)
var/list/arguments = list(src, FALSE)
var/list/stored_arguments = behavior_args[current_behavior.type]
if(stored_arguments)
arguments += stored_arguments
current_behavior.finish_action(arglist(arguments))

/datum/ai_controller/proc/get_movement_delay()
if(isliving(pawn))
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/datum/ai_behavior/basic_melee_attack
action_cooldown = 0.2 SECONDS // We gotta check unfortunately often because we're in a race condition with nextmove
behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT //| AI_BEHAVIOR_REQUIRE_REACH | AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION
behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_REQUIRE_REACH | AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION
///do we finish this action after hitting once?
var/terminate_after_action = FALSE

Expand Down
13 changes: 6 additions & 7 deletions code/datums/ai/generic/generic_behaviors.dm
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@
/// Use the currently held item, or unarmed, on a weakref to an object in the world
/datum/ai_behavior/use_on_object
required_distance = 1
behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT
behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_REQUIRE_REACH

/datum/ai_behavior/use_on_object/setup(datum/ai_controller/controller, target_key)
. = ..()
Expand Down Expand Up @@ -105,7 +105,7 @@

/datum/ai_behavior/give
required_distance = 1
behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT
behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_REQUIRE_REACH


/datum/ai_behavior/give/setup(datum/ai_controller/controller, target_key)
Expand Down Expand Up @@ -142,7 +142,7 @@

/datum/ai_behavior/consume
required_distance = 1
behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT
behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_REQUIRE_REACH
action_cooldown = 2 SECONDS

/datum/ai_behavior/consume/setup(datum/ai_controller/controller, target_key)
Expand All @@ -157,7 +157,7 @@
if(QDELETED(target))
return BEHAVIOR_PERFORM_COOLDOWN | BEHAVIOR_PERFORM_FAILURE

if(!(target in living_pawn.held_items))
if(!(living_pawn.is_holding(target)))
if(!living_pawn.put_in_hands(target))
return BEHAVIOR_PERFORM_COOLDOWN | BEHAVIOR_PERFORM_FAILURE

Expand Down Expand Up @@ -255,19 +255,18 @@
/datum/ai_behavior/drop_item

/datum/ai_behavior/drop_item/perform(delta_time, datum/ai_controller/controller)
. = ..()
var/mob/living/living_pawn = controller.pawn
var/obj/item/best_held = GetBestWeapon(controller, null, living_pawn.held_items)
for(var/obj/item/held as anything in living_pawn.held_items)
if(!held || held == best_held)
continue
living_pawn.dropItemToGround(held)

return BEHAVIOR_PERFORM_COOLDOWN
return BEHAVIOR_PERFORM_SUCCESS

/// This behavior involves attacking a target.
/datum/ai_behavior/attack
behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_MOVE_AND_PERFORM
behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_MOVE_AND_PERFORM | AI_BEHAVIOR_REQUIRE_REACH
required_distance = 1

/datum/ai_behavior/attack/perform(delta_time, datum/ai_controller/controller)
Expand Down
19 changes: 13 additions & 6 deletions code/datums/ai/generic/generic_subtrees.dm
Original file line number Diff line number Diff line change
Expand Up @@ -46,17 +46,24 @@
* * BB_NEXT_HUNGRY - set by this subtree, is when the controller is next hungry
*/
/datum/ai_planning_subtree/generic_hunger/SelectBehaviors(datum/ai_controller/controller, delta_time)
//inits the blackboard timer
if(!controller.blackboard[BB_NEXT_HUNGRY])
controller.set_blackboard_key(BB_NEXT_HUNGRY, world.time + rand(0, 30 SECONDS))
var/next_eat = controller.blackboard[BB_NEXT_HUNGRY]
if(!next_eat)
//inits the blackboard timer
next_eat = world.time + rand(0, 30 SECONDS)
controller.set_blackboard_key(BB_NEXT_HUNGRY, next_eat)

if(world.time < controller.blackboard[BB_NEXT_HUNGRY])
if(world.time < next_eat)
return

if(!controller.blackboard[BB_FOOD_TARGET])
var/atom/movable/food_target = controller.blackboard[BB_FOOD_TARGET]
if(!food_target)
controller.queue_behavior(/datum/ai_behavior/find_and_set/edible, BB_FOOD_TARGET, /obj/item, 2)
return

controller.queue_behavior(/datum/ai_behavior/drop_item)

var/mob/living/pawn = controller.pawn
if(isitem(food_target) && !pawn.is_holding(food_target) && !pawn.get_empty_held_index())
controller.queue_behavior(/datum/ai_behavior/drop_item)

controller.queue_behavior(/datum/ai_behavior/consume, BB_FOOD_TARGET, BB_NEXT_HUNGRY)
return SUBTREE_RETURN_FINISH_PLANNING
2 changes: 1 addition & 1 deletion code/datums/ai/hunting_behavior/hunting_behaviors.dm
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
return BEHAVIOR_PERFORM_COOLDOWN | BEHAVIOR_PERFORM_FAILURE

/datum/ai_behavior/hunt_target
behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT
behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_REQUIRE_REACH

/datum/ai_behavior/hunt_target/setup(datum/ai_controller/controller, hunting_target_key, hunting_cooldown_key)
. = ..()
Expand Down
2 changes: 1 addition & 1 deletion code/datums/ai/monkey/monkey_behaviors.dm
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
screeches = list("roar","screech")

/datum/ai_behavior/monkey_equip
behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT
behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_REQUIRE_REACH

/datum/ai_behavior/monkey_equip/finish_action(datum/ai_controller/controller, success)
. = ..()
Expand Down

0 comments on commit 815b636

Please sign in to comment.