mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2025-12-10 17:52:36 +00:00
Monkey eating glowup (#93759)
## About The Pull Request 1. Monkeys will only seek out food to eat if they are actually hungry, rather than on an arbitrary cooldown. 2. Monkeys will no longer teleport-yoink food out of your hands. Instead, they may get angry at you for stealing their food, and fight you over it. The hungrier the monkey, the more likely they are to fight. 3. Monkeys will discard trash and empty glasses (on the floor) after eating or drinking them. 4. Monkeys can target soup to eat 5. PunPun will no longer seek out drinks if they are hungry. 6. PunPun will now, if the bartender is absent and there are multiple patrons around, attempt to find filled glasses or food to hand out to patrons. 7. Several places that sought edible items no longer include drinking glasses as edible items <img width="656" height="185" alt="image" src="https://github.com/user-attachments/assets/8b3a6ac1-ae2c-41a0-919f-b471ad93bb0f" /> ## Why It's Good For The Game PunPun shouldn't be yoinking glasses out of patron's hands - their intended behavior is to serve drinks not steal them Otherwise, monkey eating was a bit jank due to it being some of our oldest ai code. I largely just brought it up to more modern ai standards. ## Changelog 🆑 Melbert add: If the bartender is absent, PunPun will serve filled drink glasses to patrons that don't have one. add: PunPun will now ignore filled drinks and items being held when looking for stuff to eat. add: Monkeys can eat soup. add: Monkeys will no longer seek out food if they are not hungry. add: Hungry monkeys might fight you over the food you are holding. The hungrier the monkey, the angrier the monkey. fix: Monkeys can no longer teleport items out of your hands to eat. /🆑
This commit is contained in:
@@ -4,6 +4,8 @@
|
|||||||
#define BB_CURRENT_MIN_MOVE_DISTANCE "min_move_distance"
|
#define BB_CURRENT_MIN_MOVE_DISTANCE "min_move_distance"
|
||||||
///time until we should next eat, set by the generic hunger subtree
|
///time until we should next eat, set by the generic hunger subtree
|
||||||
#define BB_NEXT_HUNGRY "BB_NEXT_HUNGRY"
|
#define BB_NEXT_HUNGRY "BB_NEXT_HUNGRY"
|
||||||
|
///When looking for food, ignore drinks
|
||||||
|
#define BB_IGNORE_DRINKS "bb_ignore_drinks"
|
||||||
///what we're going to eat next
|
///what we're going to eat next
|
||||||
#define BB_FOOD_TARGET "bb_food_target"
|
#define BB_FOOD_TARGET "bb_food_target"
|
||||||
///How close a mob must be for us to select it as a target, if that is less than how far we can maintain it as a target
|
///How close a mob must be for us to select it as a target, if that is less than how far we can maintain it as a target
|
||||||
@@ -237,5 +239,3 @@
|
|||||||
// Used to hold state without making bigass lists
|
// Used to hold state without making bigass lists
|
||||||
/// For /datum/ai_behavior/find_potential_targets, what if any field are we using currently
|
/// For /datum/ai_behavior/find_potential_targets, what if any field are we using currently
|
||||||
#define BB_FIND_TARGETS_FIELD(type) "bb_find_targets_field_[type]"
|
#define BB_FIND_TARGETS_FIELD(type) "bb_find_targets_field_[type]"
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -15,3 +15,11 @@
|
|||||||
#define BB_MONKEY_DISPOSING "BB_monkey_disposing"
|
#define BB_MONKEY_DISPOSING "BB_monkey_disposing"
|
||||||
#define BB_MONKEY_RECRUIT_COOLDOWN "BB_monkey_recruit_cooldown"
|
#define BB_MONKEY_RECRUIT_COOLDOWN "BB_monkey_recruit_cooldown"
|
||||||
#define BB_RESISTING "BB_resisting"
|
#define BB_RESISTING "BB_resisting"
|
||||||
|
/// Monkey is not necessarily a wild animal so it won't resort to fighting over food and such
|
||||||
|
#define BB_MONKEY_TAMED "BB_monkey_tamed"
|
||||||
|
/// Chance to give our held item to a nearby mob
|
||||||
|
#define BB_MONKEY_GIVE_CHANCE "BB_monkey_give_chance"
|
||||||
|
/// If set, the monkey will prefer this type of object when pressing things
|
||||||
|
#define BB_MONKEY_PRESS_TYPEPATH "BB_monkey_press_typepath"
|
||||||
|
/// The item the monkey is currently serving to someone
|
||||||
|
#define BB_MONKEY_CURRENT_SERVED_ITEM "BB_monkey_current_served_item"
|
||||||
|
|||||||
@@ -30,6 +30,8 @@
|
|||||||
#define MONKEY_HATRED_AMOUNT 4
|
#define MONKEY_HATRED_AMOUNT 4
|
||||||
/// amount of aggro to add to an enemy when a monkey is recruited
|
/// amount of aggro to add to an enemy when a monkey is recruited
|
||||||
#define MONKEY_RECRUIT_HATED_AMOUNT 2
|
#define MONKEY_RECRUIT_HATED_AMOUNT 2
|
||||||
|
/// amount of aggro to add if someone stole the food we wanted
|
||||||
|
#define MONKEY_FOOD_HATRED_AMOUNT 2
|
||||||
/// probability of reducing aggro by one when the monkey attacks
|
/// probability of reducing aggro by one when the monkey attacks
|
||||||
#define MONKEY_HATRED_REDUCTION_PROB 20
|
#define MONKEY_HATRED_REDUCTION_PROB 20
|
||||||
|
|
||||||
|
|||||||
@@ -28,15 +28,3 @@
|
|||||||
top_force = item.force
|
top_force = item.force
|
||||||
|
|
||||||
return top_force_item
|
return top_force_item
|
||||||
|
|
||||||
///returns if something can be consumed, drink or food
|
|
||||||
/proc/IsEdible(obj/item/thing)
|
|
||||||
if(!istype(thing))
|
|
||||||
return FALSE
|
|
||||||
if(IS_EDIBLE(thing))
|
|
||||||
return TRUE
|
|
||||||
if(istype(thing, /obj/item/reagent_containers/cup/glass/drinkingglass))
|
|
||||||
var/obj/item/reagent_containers/cup/glass/drinkingglass/glass = thing
|
|
||||||
if(glass.reagents.total_volume) // The glass has something in it, time to drink the mystery liquid!
|
|
||||||
return TRUE
|
|
||||||
return FALSE
|
|
||||||
|
|||||||
@@ -34,21 +34,42 @@
|
|||||||
/**
|
/**
|
||||||
* Variant of find and set that also requires the item to be edible. checks hands too
|
* Variant of find and set that also requires the item to be edible. checks hands too
|
||||||
*/
|
*/
|
||||||
/datum/ai_behavior/find_and_set/edible
|
/datum/ai_behavior/find_and_set/food_or_drink
|
||||||
|
var/force_find_drinks = FALSE
|
||||||
|
|
||||||
/datum/ai_behavior/find_and_set/edible/search_tactic(datum/ai_controller/controller, locate_path, search_range)
|
/datum/ai_behavior/find_and_set/food_or_drink/search_tactic(datum/ai_controller/controller, locate_path, search_range)
|
||||||
var/mob/living/living_pawn = controller.pawn
|
var/mob/living/living_pawn = controller.pawn
|
||||||
|
var/find_drinks = force_find_drinks || controller.blackboard[BB_IGNORE_DRINKS] || FALSE
|
||||||
|
|
||||||
for(var/atom/held_candidate as anything in living_pawn.held_items)
|
for(var/atom/held_candidate in living_pawn.held_items)
|
||||||
if(IsEdible(held_candidate))
|
if(is_food_or_drink(controller, held_candidate, find_drinks))
|
||||||
return held_candidate
|
return held_candidate
|
||||||
|
|
||||||
for(var/atom/local_candidate as anything in oview(search_range, controller.pawn))
|
for(var/atom/local_candidate in oview(search_range, controller.pawn))
|
||||||
if(IsEdible(local_candidate) && istype(local_candidate, locate_path))
|
if(is_food_or_drink(controller, local_candidate, find_drinks) && istype(local_candidate, locate_path))
|
||||||
return local_candidate
|
return local_candidate
|
||||||
|
|
||||||
return null
|
return null
|
||||||
|
|
||||||
|
/datum/ai_behavior/find_and_set/food_or_drink/proc/is_food_or_drink(datum/ai_controller/controller, obj/item/thing, find_drinks = FALSE)
|
||||||
|
return is_food(thing) || (find_drinks && is_drink(thing))
|
||||||
|
|
||||||
|
/datum/ai_behavior/find_and_set/food_or_drink/proc/is_food(obj/item/thing)
|
||||||
|
if(IS_EDIBLE(thing))
|
||||||
|
return TRUE
|
||||||
|
if(istype(thing, /obj/item/reagent_containers/cup/bowl))
|
||||||
|
return thing.reagents.total_volume > 0
|
||||||
|
return FALSE
|
||||||
|
|
||||||
|
/datum/ai_behavior/find_and_set/food_or_drink/proc/is_drink(obj/item/thing)
|
||||||
|
if(istype(thing, /obj/item/reagent_containers/cup/glass))
|
||||||
|
return thing.reagents.total_volume > 0
|
||||||
|
return FALSE
|
||||||
|
|
||||||
|
/datum/ai_behavior/find_and_set/food_or_drink/to_eat
|
||||||
|
|
||||||
|
/datum/ai_behavior/find_and_set/food_or_drink/to_serve
|
||||||
|
force_find_drinks = TRUE
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Variant of find and set that only checks in hands, search range should be excluded for this
|
* Variant of find and set that only checks in hands, search range should be excluded for this
|
||||||
|
|||||||
@@ -91,17 +91,11 @@
|
|||||||
set_movement_target(controller, target)
|
set_movement_target(controller, target)
|
||||||
|
|
||||||
/datum/ai_behavior/use_on_object/perform(seconds_per_tick, datum/ai_controller/controller, target_key)
|
/datum/ai_behavior/use_on_object/perform(seconds_per_tick, datum/ai_controller/controller, target_key)
|
||||||
var/mob/living/pawn = controller.pawn
|
|
||||||
var/obj/item/held_item = pawn.get_item_by_slot(pawn.get_active_hand())
|
|
||||||
var/atom/target = controller.blackboard[target_key]
|
var/atom/target = controller.blackboard[target_key]
|
||||||
if(QDELETED(target))
|
if(QDELETED(target))
|
||||||
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED
|
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED
|
||||||
|
|
||||||
if(held_item)
|
controller.ai_interact(target = target, combat_mode = FALSE)
|
||||||
held_item.melee_attack_chain(pawn, target)
|
|
||||||
else
|
|
||||||
controller.ai_interact(target = target, combat_mode = FALSE)
|
|
||||||
|
|
||||||
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED
|
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED
|
||||||
|
|
||||||
/datum/ai_behavior/give
|
/datum/ai_behavior/give
|
||||||
@@ -120,10 +114,14 @@
|
|||||||
if(!held_item) //if held_item is null, we pretend that action was successful
|
if(!held_item) //if held_item is null, we pretend that action was successful
|
||||||
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED
|
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED
|
||||||
|
|
||||||
if(!target || !isliving(target))
|
if(QDELETED(target) || !target.IsReachableBy(pawn))
|
||||||
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED
|
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED
|
||||||
|
|
||||||
var/mob/living/living_target = target
|
var/mob/living/living_target = target
|
||||||
|
if(!isliving(living_target)) // target should reasonably only ever be set to a living mob
|
||||||
|
stack_trace("Tried to give an item to a non-living target!")
|
||||||
|
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED
|
||||||
|
|
||||||
var/perform_flags = try_to_give_item(controller, living_target, held_item)
|
var/perform_flags = try_to_give_item(controller, living_target, held_item)
|
||||||
if(perform_flags & AI_BEHAVIOR_FAILED)
|
if(perform_flags & AI_BEHAVIOR_FAILED)
|
||||||
return perform_flags
|
return perform_flags
|
||||||
@@ -163,36 +161,73 @@
|
|||||||
target.put_in_hands(held_item)
|
target.put_in_hands(held_item)
|
||||||
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED
|
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED
|
||||||
|
|
||||||
|
/datum/ai_behavior/give/finish_action(datum/ai_controller/controller, succeeded, target_key)
|
||||||
|
. = ..()
|
||||||
|
controller.clear_blackboard_key(target_key)
|
||||||
|
|
||||||
/datum/ai_behavior/consume
|
/datum/ai_behavior/consume
|
||||||
|
action_cooldown = 2 SECONDS
|
||||||
|
|
||||||
|
/datum/ai_behavior/consume/perform(seconds_per_tick, datum/ai_controller/controller, target_key, hunger_timer_key)
|
||||||
|
var/mob/living/living_pawn = controller.pawn
|
||||||
|
var/obj/item/target = controller.blackboard[target_key]
|
||||||
|
if(QDELETED(target) || !living_pawn.is_holding(target))
|
||||||
|
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED
|
||||||
|
|
||||||
|
controller.ai_interact(target = living_pawn, combat_mode = FALSE)
|
||||||
|
|
||||||
|
return AI_BEHAVIOR_DELAY | (is_content(living_pawn, target) ? AI_BEHAVIOR_SUCCEEDED : AI_BEHAVIOR_FAILED)
|
||||||
|
|
||||||
|
/datum/ai_behavior/consume/finish_action(datum/ai_controller/controller, succeeded, target_key, hunger_timer_key)
|
||||||
|
. = ..()
|
||||||
|
if(!succeeded)
|
||||||
|
return
|
||||||
|
controller.set_blackboard_key(hunger_timer_key, world.time + rand(12 SECONDS, 60 SECONDS))
|
||||||
|
|
||||||
|
var/mob/living/living_pawn = controller.pawn
|
||||||
|
var/obj/item/target = controller.blackboard[target_key]
|
||||||
|
if(!QDELETED(target) && !DOING_INTERACTION_WITH_TARGET(living_pawn, target))
|
||||||
|
controller.clear_blackboard_key(target_key)
|
||||||
|
living_pawn.dropItemToGround(target) // drops empty drink glasses
|
||||||
|
for(var/obj/item/trash/trash in living_pawn.held_items)
|
||||||
|
living_pawn.dropItemToGround(trash) // drops spawned trash items
|
||||||
|
|
||||||
|
/// Check if the target is fully consumed, or being actively consumed, or if we're just bored of eating it
|
||||||
|
/datum/ai_behavior/consume/proc/is_content(mob/living/living_pawm, obj/item/target)
|
||||||
|
if(QDELETED(target))
|
||||||
|
return TRUE
|
||||||
|
if(DOING_INTERACTION_WITH_TARGET(living_pawm, target))
|
||||||
|
return TRUE
|
||||||
|
if(target.reagents?.total_volume <= 0)
|
||||||
|
return TRUE
|
||||||
|
// Even if we don't finish it all we can randomly decide to be done
|
||||||
|
return prob(10)
|
||||||
|
|
||||||
|
// navigate to target item and pick it up if we can
|
||||||
|
/datum/ai_behavior/navigate_to_and_pick_up
|
||||||
behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_REQUIRE_REACH
|
behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_REQUIRE_REACH
|
||||||
action_cooldown = 2 SECONDS
|
action_cooldown = 2 SECONDS
|
||||||
|
|
||||||
/datum/ai_behavior/consume/setup(datum/ai_controller/controller, target_key)
|
/datum/ai_behavior/navigate_to_and_pick_up/setup(datum/ai_controller/controller, target_key, drop_held = TRUE)
|
||||||
. = ..()
|
. = ..()
|
||||||
set_movement_target(controller, controller.blackboard[target_key])
|
set_movement_target(controller, controller.blackboard[target_key])
|
||||||
|
|
||||||
/datum/ai_behavior/consume/perform(seconds_per_tick, datum/ai_controller/controller, target_key, hunger_timer_key)
|
/datum/ai_behavior/navigate_to_and_pick_up/setup(datum/ai_controller/controller, target_key, drop_held = TRUE)
|
||||||
var/mob/living/living_pawn = controller.pawn
|
var/mob/living/living_pawn = controller.pawn
|
||||||
var/obj/item/target = controller.blackboard[target_key]
|
var/obj/item/target = controller.blackboard[target_key]
|
||||||
if(QDELETED(target))
|
if(QDELETED(target))
|
||||||
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED
|
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED
|
||||||
|
|
||||||
if(!(target in living_pawn.held_items))
|
if(living_pawn.is_holding(target)) // already in hands
|
||||||
if(!living_pawn.get_empty_held_indexes() || !living_pawn.put_in_hands(target))
|
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED
|
||||||
|
if(!target.IsReachableBy(living_pawn)) // can't reach it, despite being adjacent
|
||||||
|
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED
|
||||||
|
if(living_pawn.get_active_held_item()) // something is in our hands already
|
||||||
|
if(!drop_held || !living_pawn.dropItemToGround(living_pawn.get_active_held_item()))
|
||||||
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED
|
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED
|
||||||
|
|
||||||
target.melee_attack_chain(living_pawn, living_pawn)
|
controller.ai_interact(target, combat_mode = FALSE)
|
||||||
|
return AI_BEHAVIOR_DELAY | (target.loc == living_pawn ? AI_BEHAVIOR_SUCCEEDED : AI_BEHAVIOR_FAILED)
|
||||||
if(QDELETED(target) || prob(10)) // Even if we don't finish it all we can randomly decide to be done
|
|
||||||
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED
|
|
||||||
|
|
||||||
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED
|
|
||||||
|
|
||||||
/datum/ai_behavior/consume/finish_action(datum/ai_controller/controller, succeeded, target_key, hunger_timer_key)
|
|
||||||
. = ..()
|
|
||||||
if(succeeded)
|
|
||||||
controller.set_blackboard_key(hunger_timer_key, world.time + rand(12 SECONDS, 60 SECONDS))
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Drops items in hands, very important for future behaviors that require the pawn to grab stuff
|
* Drops items in hands, very important for future behaviors that require the pawn to grab stuff
|
||||||
|
|||||||
@@ -53,7 +53,13 @@
|
|||||||
* relevant blackboards:
|
* relevant blackboards:
|
||||||
* * BB_NEXT_HUNGRY - set by this subtree, is when the controller is next hungry
|
* * BB_NEXT_HUNGRY - set by this subtree, is when the controller is next hungry
|
||||||
*/
|
*/
|
||||||
|
/datum/ai_planning_subtree/generic_hunger
|
||||||
|
|
||||||
/datum/ai_planning_subtree/generic_hunger/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick)
|
/datum/ai_planning_subtree/generic_hunger/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick)
|
||||||
|
var/mob/living/living_pawn = controller.pawn
|
||||||
|
if(living_pawn.nutrition > NUTRITION_LEVEL_HUNGRY)
|
||||||
|
return
|
||||||
|
|
||||||
var/next_eat = controller.blackboard[BB_NEXT_HUNGRY]
|
var/next_eat = controller.blackboard[BB_NEXT_HUNGRY]
|
||||||
if(!next_eat)
|
if(!next_eat)
|
||||||
//inits the blackboard timer
|
//inits the blackboard timer
|
||||||
@@ -63,16 +69,30 @@
|
|||||||
if(world.time < next_eat)
|
if(world.time < next_eat)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
// find food
|
||||||
var/atom/food_target = controller.blackboard[BB_FOOD_TARGET]
|
var/atom/food_target = controller.blackboard[BB_FOOD_TARGET]
|
||||||
|
|
||||||
if(isnull(food_target))
|
if(isnull(food_target))
|
||||||
controller.queue_behavior(/datum/ai_behavior/find_and_set/edible, BB_FOOD_TARGET, /obj/item, 2)
|
controller.queue_behavior(/datum/ai_behavior/find_and_set/food_or_drink/to_eat, BB_FOOD_TARGET, /obj/item, 2)
|
||||||
return
|
|
||||||
|
|
||||||
var/mob/living/living_pawn = controller.pawn
|
|
||||||
if(!length(living_pawn.get_empty_held_indexes()) && !(food_target in living_pawn.held_items))
|
|
||||||
controller.queue_behavior(/datum/ai_behavior/drop_item)
|
|
||||||
return SUBTREE_RETURN_FINISH_PLANNING
|
return SUBTREE_RETURN_FINISH_PLANNING
|
||||||
|
|
||||||
controller.queue_behavior(/datum/ai_behavior/consume, BB_FOOD_TARGET, BB_NEXT_HUNGRY)
|
if(living_pawn.is_holding(food_target))
|
||||||
|
controller.queue_behavior(/datum/ai_behavior/consume, BB_FOOD_TARGET, BB_NEXT_HUNGRY)
|
||||||
|
// it's been moved since we found it
|
||||||
|
else if(!isturf(food_target.loc))
|
||||||
|
// someone took it. we will fight over it!
|
||||||
|
if(isliving(food_target.loc) && will_fight_for_food(food_target.loc, living_pawn, controller))
|
||||||
|
controller.add_blackboard_key_assoc(BB_MONKEY_ENEMIES, food_target.loc, MONKEY_FOOD_HATRED_AMOUNT)
|
||||||
|
// eh, find something else
|
||||||
|
else
|
||||||
|
controller.clear_blackboard_key(BB_FOOD_TARGET)
|
||||||
|
return SUBTREE_RETURN_FINISH_PLANNING
|
||||||
|
else
|
||||||
|
controller.queue_behavior(/datum/ai_behavior/navigate_to_and_pick_up, BB_FOOD_TARGET, TRUE)
|
||||||
return SUBTREE_RETURN_FINISH_PLANNING
|
return SUBTREE_RETURN_FINISH_PLANNING
|
||||||
|
|
||||||
|
/datum/ai_planning_subtree/generic_hunger/proc/will_fight_for_food(mob/living/thief, mob/living/monkey, datum/ai_controller/controller)
|
||||||
|
if(controller.blackboard[BB_MONKEY_AGGRESSIVE])
|
||||||
|
return TRUE
|
||||||
|
if(controller.blackboard[BB_MONKEY_TAMED])
|
||||||
|
return FALSE
|
||||||
|
return prob(100 * ((NUTRITION_LEVEL_HUNGRY - monkey.nutrition) / NUTRITION_LEVEL_HUNGRY))
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ have ways of interacting with a specific mob and control it.
|
|||||||
BB_MONKEY_GUN_NEURONS_ACTIVATED = FALSE,
|
BB_MONKEY_GUN_NEURONS_ACTIVATED = FALSE,
|
||||||
BB_SONG_LINES = MONKEY_SONG,
|
BB_SONG_LINES = MONKEY_SONG,
|
||||||
BB_RESISTING = FALSE,
|
BB_RESISTING = FALSE,
|
||||||
|
BB_MONKEY_GIVE_CHANCE = 5,
|
||||||
)
|
)
|
||||||
idle_behavior = /datum/idle_behavior/idle_monkey
|
idle_behavior = /datum/idle_behavior/idle_monkey
|
||||||
|
|
||||||
@@ -63,12 +64,24 @@ have ways of interacting with a specific mob and control it.
|
|||||||
planning_subtrees = list(
|
planning_subtrees = list(
|
||||||
/datum/ai_planning_subtree/generic_resist,
|
/datum/ai_planning_subtree/generic_resist,
|
||||||
/datum/ai_planning_subtree/monkey_combat,
|
/datum/ai_planning_subtree/monkey_combat,
|
||||||
|
/datum/ai_planning_subtree/serve_food,
|
||||||
/datum/ai_planning_subtree/generic_hunger,
|
/datum/ai_planning_subtree/generic_hunger,
|
||||||
/datum/ai_planning_subtree/generic_play_instrument,
|
/datum/ai_planning_subtree/generic_play_instrument,
|
||||||
/datum/ai_planning_subtree/punpun_shenanigans,
|
/datum/ai_planning_subtree/monkey_shenanigans,
|
||||||
)
|
)
|
||||||
idle_behavior = /datum/idle_behavior/idle_monkey/pun_pun
|
idle_behavior = /datum/idle_behavior/idle_monkey/pun_pun
|
||||||
|
|
||||||
|
/datum/ai_controller/monkey/pun_pun/TryPossessPawn(atom/new_pawn)
|
||||||
|
. = ..()
|
||||||
|
if(. & AI_CONTROLLER_INCOMPATIBLE)
|
||||||
|
return
|
||||||
|
pawn = new_pawn
|
||||||
|
set_blackboard_key(BB_IGNORE_DRINKS, TRUE)
|
||||||
|
set_blackboard_key(BB_MONKEY_TAMED, TRUE)
|
||||||
|
set_blackboard_key(BB_MONKEY_GIVE_CHANCE, 30)
|
||||||
|
set_blackboard_key(BB_MONKEY_PRESS_TYPEPATH, /obj/structure/desk_bell)
|
||||||
|
set_trip_mode(mode = FALSE)
|
||||||
|
|
||||||
/datum/ai_controller/monkey/angry
|
/datum/ai_controller/monkey/angry
|
||||||
|
|
||||||
/datum/ai_controller/monkey/angry/TryPossessPawn(atom/new_pawn)
|
/datum/ai_controller/monkey/angry/TryPossessPawn(atom/new_pawn)
|
||||||
|
|||||||
@@ -7,21 +7,22 @@
|
|||||||
return
|
return
|
||||||
|
|
||||||
if(!controller.blackboard[BB_MONKEY_CURRENT_PRESS_TARGET])
|
if(!controller.blackboard[BB_MONKEY_CURRENT_PRESS_TARGET])
|
||||||
controller.queue_behavior(/datum/ai_behavior/find_nearby, BB_MONKEY_CURRENT_PRESS_TARGET)
|
if(controller.blackboard[BB_MONKEY_PRESS_TYPEPATH])
|
||||||
return
|
controller.queue_behavior(/datum/ai_behavior/find_and_set, BB_MONKEY_CURRENT_PRESS_TARGET, controller.blackboard[BB_MONKEY_PRESS_TYPEPATH], 2)
|
||||||
|
else
|
||||||
if(prob(50))
|
controller.queue_behavior(/datum/ai_behavior/find_nearby, BB_MONKEY_CURRENT_PRESS_TARGET)
|
||||||
|
else if(prob(50))
|
||||||
controller.queue_behavior(/datum/ai_behavior/use_on_object, BB_MONKEY_CURRENT_PRESS_TARGET)
|
controller.queue_behavior(/datum/ai_behavior/use_on_object, BB_MONKEY_CURRENT_PRESS_TARGET)
|
||||||
return SUBTREE_RETURN_FINISH_PLANNING
|
return SUBTREE_RETURN_FINISH_PLANNING
|
||||||
|
|
||||||
if(!controller.blackboard[BB_MONKEY_CURRENT_GIVE_TARGET])
|
if(!controller.blackboard[BB_MONKEY_CURRENT_GIVE_TARGET])
|
||||||
controller.queue_behavior(/datum/ai_behavior/find_and_set/pawn_must_hold_item, BB_MONKEY_CURRENT_GIVE_TARGET, /mob/living, 2)
|
controller.queue_behavior(/datum/ai_behavior/find_and_set/pawn_must_hold_item, BB_MONKEY_CURRENT_GIVE_TARGET, /mob/living/carbon/human, 2)
|
||||||
else
|
else if(prob(controller.blackboard[BB_MONKEY_GIVE_CHANCE]))
|
||||||
if(prob(5))
|
controller.queue_behavior(/datum/ai_behavior/give, BB_MONKEY_CURRENT_GIVE_TARGET)
|
||||||
controller.queue_behavior(/datum/ai_behavior/give, BB_MONKEY_CURRENT_GIVE_TARGET)
|
return SUBTREE_RETURN_FINISH_PLANNING
|
||||||
return SUBTREE_RETURN_FINISH_PLANNING
|
|
||||||
|
|
||||||
controller.TryFindWeapon()
|
if(!controller.blackboard[BB_MONKEY_TAMED])
|
||||||
|
controller.TryFindWeapon()
|
||||||
|
|
||||||
///monkey combat subtree.
|
///monkey combat subtree.
|
||||||
/datum/ai_planning_subtree/monkey_combat/SelectBehaviors(datum/ai_controller/monkey/controller, seconds_per_tick)
|
/datum/ai_planning_subtree/monkey_combat/SelectBehaviors(datum/ai_controller/monkey/controller, seconds_per_tick)
|
||||||
@@ -70,3 +71,42 @@
|
|||||||
|
|
||||||
controller.queue_behavior(/datum/ai_behavior/disposal_mob, BB_MONKEY_CURRENT_ATTACK_TARGET, BB_MONKEY_TARGET_DISPOSAL)
|
controller.queue_behavior(/datum/ai_behavior/disposal_mob, BB_MONKEY_CURRENT_ATTACK_TARGET, BB_MONKEY_TARGET_DISPOSAL)
|
||||||
return SUBTREE_RETURN_FINISH_PLANNING
|
return SUBTREE_RETURN_FINISH_PLANNING
|
||||||
|
|
||||||
|
/// Finds food or drinks, picks them up, then gives them to nearby humans
|
||||||
|
/datum/ai_planning_subtree/serve_food
|
||||||
|
|
||||||
|
/datum/ai_planning_subtree/serve_food/SelectBehaviors(datum/ai_controller/monkey/controller, seconds_per_tick)
|
||||||
|
var/mob/living/living_pawn = controller.pawn
|
||||||
|
var/list/nearby_patrons = list()
|
||||||
|
for(var/mob/living/carbon/human/human_mob in oview(5, living_pawn))
|
||||||
|
if(istype(human_mob.mind?.assigned_role, /datum/job/bartender))
|
||||||
|
return // my boss is on duty!
|
||||||
|
if(human_mob.stat != CONSCIOUS || ismonkey(human_mob))
|
||||||
|
continue
|
||||||
|
if(!human_mob.get_empty_held_indexes())
|
||||||
|
continue
|
||||||
|
nearby_patrons += human_mob
|
||||||
|
|
||||||
|
// Need at least 2 patrons to bother serving (bearing in mind the
|
||||||
|
if(length(nearby_patrons) < 1)
|
||||||
|
return
|
||||||
|
|
||||||
|
var/obj/item/serving = controller.blackboard[BB_MONKEY_CURRENT_SERVED_ITEM]
|
||||||
|
if(QDELETED(serving) || serving.reagents.total_volume <= 0)
|
||||||
|
controller.queue_behavior(/datum/ai_behavior/find_and_set/food_or_drink/to_serve, BB_MONKEY_CURRENT_SERVED_ITEM, /obj/item, 2)
|
||||||
|
return
|
||||||
|
|
||||||
|
// we have something to serve, pick a patron and go hand it over
|
||||||
|
if(living_pawn.is_holding(serving))
|
||||||
|
controller.blackboard[BB_MONKEY_CURRENT_GIVE_TARGET] ||= pick(nearby_patrons)
|
||||||
|
controller.queue_behavior(/datum/ai_behavior/give, BB_MONKEY_CURRENT_GIVE_TARGET)
|
||||||
|
return SUBTREE_RETURN_FINISH_PLANNING
|
||||||
|
|
||||||
|
// we have something to serve but aren't holding it yet
|
||||||
|
if(isturf(serving.loc))
|
||||||
|
// fetch the drink
|
||||||
|
controller.queue_behavior(/datum/ai_behavior/navigate_to_and_pick_up, BB_MONKEY_CURRENT_SERVED_ITEM, TRUE)
|
||||||
|
else
|
||||||
|
// give up on the dream
|
||||||
|
controller.clear_blackboard_key(BB_MONKEY_CURRENT_SERVED_ITEM)
|
||||||
|
return SUBTREE_RETURN_FINISH_PLANNING
|
||||||
|
|||||||
@@ -1,22 +0,0 @@
|
|||||||
/datum/ai_planning_subtree/punpun_shenanigans/SelectBehaviors(datum/ai_controller/monkey/controller, seconds_per_tick)
|
|
||||||
|
|
||||||
controller.set_trip_mode(mode = FALSE) // pun pun doesn't fuck around
|
|
||||||
|
|
||||||
if(prob(5))
|
|
||||||
controller.queue_behavior(/datum/ai_behavior/use_in_hand)
|
|
||||||
|
|
||||||
if(!SPT_PROB(MONKEY_SHENANIGAN_PROB, seconds_per_tick))
|
|
||||||
return
|
|
||||||
|
|
||||||
if(!controller.blackboard[BB_MONKEY_CURRENT_PRESS_TARGET])
|
|
||||||
controller.queue_behavior(/datum/ai_behavior/find_and_set, BB_MONKEY_CURRENT_PRESS_TARGET, /obj/structure/desk_bell, 2)
|
|
||||||
else if(prob(50))
|
|
||||||
controller.queue_behavior(/datum/ai_behavior/use_on_object, BB_MONKEY_CURRENT_PRESS_TARGET)
|
|
||||||
return SUBTREE_RETURN_FINISH_PLANNING
|
|
||||||
|
|
||||||
if(!controller.blackboard[BB_MONKEY_CURRENT_GIVE_TARGET])
|
|
||||||
controller.queue_behavior(/datum/ai_behavior/find_and_set/pawn_must_hold_item, BB_MONKEY_CURRENT_GIVE_TARGET, /mob/living, 2)
|
|
||||||
else if(prob(30))
|
|
||||||
controller.queue_behavior(/datum/ai_behavior/give, BB_MONKEY_CURRENT_GIVE_TARGET)
|
|
||||||
return SUBTREE_RETURN_FINISH_PLANNING
|
|
||||||
|
|
||||||
@@ -155,7 +155,7 @@
|
|||||||
LAZYADD(grilled_food.intrinsic_food_materials, original_food.intrinsic_food_materials)
|
LAZYADD(grilled_food.intrinsic_food_materials, original_food.intrinsic_food_materials)
|
||||||
grilled_result.set_custom_materials(original_object.custom_materials)
|
grilled_result.set_custom_materials(original_object.custom_materials)
|
||||||
|
|
||||||
if(IsEdible(grilled_result) && positive_result)
|
if(IS_EDIBLE(grilled_result) && positive_result)
|
||||||
BLACKBOX_LOG_FOOD_MADE(grilled_result.type)
|
BLACKBOX_LOG_FOOD_MADE(grilled_result.type)
|
||||||
//make space and tranfer reagents if it has any, also let any bad result handle removing or converting the transferred reagents on its own terms
|
//make space and tranfer reagents if it has any, also let any bad result handle removing or converting the transferred reagents on its own terms
|
||||||
if(grilled_result.reagents && original_object.reagents)
|
if(grilled_result.reagents && original_object.reagents)
|
||||||
|
|||||||
@@ -81,7 +81,7 @@
|
|||||||
if (COOLDOWN_FINISHED(src, clack_cooldown))
|
if (COOLDOWN_FINISHED(src, clack_cooldown))
|
||||||
click_clack()
|
click_clack()
|
||||||
return ..()
|
return ..()
|
||||||
if (!IsEdible(attacked) || attacked.w_class > WEIGHT_CLASS_NORMAL || !isnull(tonged))
|
if (!IS_EDIBLE(attacked) || attacked.w_class > WEIGHT_CLASS_NORMAL || !isnull(tonged))
|
||||||
return ..()
|
return ..()
|
||||||
tonged = attacked
|
tonged = attacked
|
||||||
attacked.do_pickup_animation(src)
|
attacked.do_pickup_animation(src)
|
||||||
|
|||||||
@@ -57,7 +57,7 @@
|
|||||||
SIGNAL_HANDLER
|
SIGNAL_HANDLER
|
||||||
if (ai_controller?.blackboard[BB_GOOSE_PANICKED])
|
if (ai_controller?.blackboard[BB_GOOSE_PANICKED])
|
||||||
return COMSIG_MOB_CANCEL_EAT
|
return COMSIG_MOB_CANCEL_EAT
|
||||||
if (potential_food.has_material_type(/datum/material/plastic) || IsEdible(potential_food))
|
if (potential_food.has_material_type(/datum/material/plastic) || IS_EDIBLE(potential_food))
|
||||||
return NONE// Geese only eat FOOD or PLASTIC
|
return NONE// Geese only eat FOOD or PLASTIC
|
||||||
return COMSIG_MOB_CANCEL_EAT
|
return COMSIG_MOB_CANCEL_EAT
|
||||||
|
|
||||||
|
|||||||
@@ -62,7 +62,7 @@
|
|||||||
|
|
||||||
var/list/filtered = list()
|
var/list/filtered = list()
|
||||||
for (var/obj/item/thing as anything in found)
|
for (var/obj/item/thing as anything in found)
|
||||||
if (IsEdible(thing) || thing.has_material_type(/datum/material/plastic))
|
if (IS_EDIBLE(thing) || thing.has_material_type(/datum/material/plastic))
|
||||||
filtered += thing
|
filtered += thing
|
||||||
|
|
||||||
if(length(filtered))
|
if(length(filtered))
|
||||||
|
|||||||
@@ -333,7 +333,7 @@
|
|||||||
if (isliving(nomnom)) // NO VORE ALLOWED
|
if (isliving(nomnom)) // NO VORE ALLOWED
|
||||||
return 0
|
return 0
|
||||||
// Yeah maybe don't, if something edible ended up here it should either handle itself or not be digested
|
// Yeah maybe don't, if something edible ended up here it should either handle itself or not be digested
|
||||||
if (IsEdible(nomnom))
|
if (IS_EDIBLE(nomnom))
|
||||||
return 0
|
return 0
|
||||||
if (HAS_TRAIT(owner, TRAIT_STRONG_STOMACH))
|
if (HAS_TRAIT(owner, TRAIT_STRONG_STOMACH))
|
||||||
return 10
|
return 10
|
||||||
@@ -521,7 +521,7 @@
|
|||||||
/obj/item/organ/stomach/cybernetic/tier2/stomach_acid_power(atom/movable/nomnom)
|
/obj/item/organ/stomach/cybernetic/tier2/stomach_acid_power(atom/movable/nomnom)
|
||||||
if (isliving(nomnom))
|
if (isliving(nomnom))
|
||||||
return 0
|
return 0
|
||||||
if (IsEdible(nomnom))
|
if (IS_EDIBLE(nomnom))
|
||||||
return 0
|
return 0
|
||||||
return 20
|
return 20
|
||||||
|
|
||||||
@@ -537,7 +537,7 @@
|
|||||||
/obj/item/organ/stomach/cybernetic/tier3/stomach_acid_power(atom/movable/nomnom)
|
/obj/item/organ/stomach/cybernetic/tier3/stomach_acid_power(atom/movable/nomnom)
|
||||||
if (isliving(nomnom))
|
if (isliving(nomnom))
|
||||||
return 0
|
return 0
|
||||||
if (IsEdible(nomnom))
|
if (IS_EDIBLE(nomnom))
|
||||||
return 0
|
return 0
|
||||||
return 35
|
return 35
|
||||||
|
|
||||||
|
|||||||
@@ -1031,7 +1031,6 @@
|
|||||||
#include "code\datums\ai\monkey\monkey_behaviors.dm"
|
#include "code\datums\ai\monkey\monkey_behaviors.dm"
|
||||||
#include "code\datums\ai\monkey\monkey_controller.dm"
|
#include "code\datums\ai\monkey\monkey_controller.dm"
|
||||||
#include "code\datums\ai\monkey\monkey_subtrees.dm"
|
#include "code\datums\ai\monkey\monkey_subtrees.dm"
|
||||||
#include "code\datums\ai\monkey\punpun_subtrees.dm"
|
|
||||||
#include "code\datums\ai\movement\_ai_movement.dm"
|
#include "code\datums\ai\movement\_ai_movement.dm"
|
||||||
#include "code\datums\ai\movement\ai_movement_basic_avoidance.dm"
|
#include "code\datums\ai\movement\ai_movement_basic_avoidance.dm"
|
||||||
#include "code\datums\ai\movement\ai_movement_complete_stop.dm"
|
#include "code\datums\ai\movement\ai_movement_complete_stop.dm"
|
||||||
|
|||||||
Reference in New Issue
Block a user