mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-08-23 13:10:02 +01:00
## About The Pull Request Two main things. Multiple instances of use of a proc in AI controllers seemingly assuming the default behaviour will work for them, but what ends up happening is `search_tactic` gets redefined and redefined with no defaut search range parameter, so nothing ends up passed to the `search_tactic` child procs, so they all call `oview` with `null` and this... somehow doesn't runtime? Has behaviour that works some of the time??? I hate this fucking language. Anyway. Stoat steal items behaviour was completely broken and apparently was not tested once since it was merged in. I've made the corrections I can, but I haven't figured out why stoat AI never enters idle, so we have a behaviour that leads to the stoat running up to an item, grabbing it, and then just staying there, unmoving. I've sunk too many hours into this, I'm just going to call it fixed and let someone else figure out what exciting additions there need to be to a behaviour that was never functional in the first place. ## Why It's Good For The Game i don't know man i just want the pain to stop okay, generally speaking, when people write AI behaviours, they want those AI behaviours to do something and not just silently fail for six months or longer ## Changelog 🆑 fix: Stoats have a chance to try and grab items like they always should have. /🆑
221 lines
8.4 KiB
Plaintext
221 lines
8.4 KiB
Plaintext
/**find and set
|
|
* Finds an item near themselves, sets a blackboard key as it. Very useful for ais that need to use machines or something.
|
|
* if you want to do something more complicated than find a single atom, change the search_tactic() proc
|
|
* cool tip: search_tactic() can set lists
|
|
*/
|
|
/datum/ai_behavior/find_and_set
|
|
action_cooldown = 2 SECONDS
|
|
|
|
/datum/ai_behavior/find_and_set/perform(seconds_per_tick, datum/ai_controller/controller, set_key, locate_path, search_range)
|
|
if (controller.blackboard_key_exists(set_key))
|
|
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED
|
|
if(QDELETED(controller.pawn))
|
|
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED
|
|
var/find_this_thing = search_tactic(controller, locate_path, search_range)
|
|
if(isnull(find_this_thing))
|
|
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED
|
|
controller.set_blackboard_key(set_key, find_this_thing)
|
|
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED
|
|
|
|
/datum/ai_behavior/find_and_set/proc/search_tactic(datum/ai_controller/controller, locate_path, search_range = 3)
|
|
return locate(locate_path) in oview(search_range, controller.pawn)
|
|
|
|
/**
|
|
* Variant of find and set that fails if the living pawn doesn't hold something
|
|
*/
|
|
/datum/ai_behavior/find_and_set/pawn_must_hold_item
|
|
|
|
/datum/ai_behavior/find_and_set/pawn_must_hold_item/search_tactic(datum/ai_controller/controller)
|
|
var/mob/living/living_pawn = controller.pawn
|
|
if(!living_pawn.get_num_held_items())
|
|
return //we want to fail the search if we don't have something held
|
|
return ..()
|
|
|
|
/**
|
|
* Variant of find and set that also requires the item to be edible. checks hands too
|
|
*/
|
|
/datum/ai_behavior/find_and_set/food_or_drink
|
|
var/force_find_drinks = FALSE
|
|
|
|
/datum/ai_behavior/find_and_set/food_or_drink/search_tactic(datum/ai_controller/controller, locate_path, search_range = SEARCH_TACTIC_DEFAULT_RANGE)
|
|
var/mob/living/living_pawn = controller.pawn
|
|
var/find_drinks = force_find_drinks || controller.blackboard[BB_IGNORE_DRINKS] || FALSE
|
|
|
|
for(var/atom/held_candidate in living_pawn.held_items)
|
|
if(is_food_or_drink(controller, held_candidate, find_drinks))
|
|
return held_candidate
|
|
|
|
for(var/atom/local_candidate in oview(search_range, controller.pawn))
|
|
if(is_food_or_drink(controller, local_candidate, find_drinks) && istype(local_candidate, locate_path))
|
|
return local_candidate
|
|
|
|
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
|
|
*/
|
|
/datum/ai_behavior/find_and_set/in_hands
|
|
|
|
/datum/ai_behavior/find_and_set/in_hands/search_tactic(datum/ai_controller/controller, locate_path)
|
|
var/mob/living/living_pawn = controller.pawn
|
|
return locate(locate_path) in living_pawn.held_items
|
|
|
|
/datum/ai_behavior/find_and_set/in_hands/given_list
|
|
|
|
/datum/ai_behavior/find_and_set/in_hands/given_list/search_tactic(datum/ai_controller/controller, locate_paths)
|
|
var/list/found = typecache_filter_list(controller.pawn, locate_paths)
|
|
if(length(found))
|
|
return pick(found)
|
|
|
|
/**
|
|
* Variant of find and set that takes a list of things to find.
|
|
*/
|
|
/datum/ai_behavior/find_and_set/in_list
|
|
|
|
/datum/ai_behavior/find_and_set/in_list/search_tactic(datum/ai_controller/controller, locate_paths, search_range = SEARCH_TACTIC_DEFAULT_RANGE)
|
|
var/list/found = typecache_filter_list(oview(search_range, controller.pawn), locate_paths)
|
|
if(length(found))
|
|
return pick(found)
|
|
|
|
/// Like find_and_set/in_list, but we return the turf location of the item instead of the item itself.
|
|
/datum/ai_behavior/find_and_set/in_list/turf_location
|
|
|
|
/datum/ai_behavior/find_and_set/in_list/turf_location/search_tactic(datum/ai_controller/controller, locate_paths, search_range)
|
|
. = ..()
|
|
if(isnull(.))
|
|
return null
|
|
|
|
return get_turf(.)
|
|
|
|
/**
|
|
* Variant of find and set which returns an object which can be animated with a staff of change
|
|
*/
|
|
/datum/ai_behavior/find_and_set/animatable
|
|
|
|
/datum/ai_behavior/find_and_set/animatable/search_tactic(datum/ai_controller/controller, locate_path, search_range = SEARCH_TACTIC_DEFAULT_RANGE)
|
|
var/mob/living/living_pawn = controller.pawn
|
|
|
|
var/list/nearby_items = list()
|
|
for (var/obj/new_friend in oview(search_range, controller.pawn))
|
|
if (!isitem(new_friend) && !isstructure(new_friend))
|
|
continue
|
|
if (is_type_in_list(new_friend, GLOB.animatable_blacklist))
|
|
continue
|
|
if (living_pawn.see_invisible < new_friend.invisibility)
|
|
continue
|
|
nearby_items += new_friend
|
|
|
|
if(nearby_items.len)
|
|
return pick(nearby_items)
|
|
|
|
/**
|
|
* Variant of find and set which returns the nearest wall which isn't invulnerable
|
|
*/
|
|
/datum/ai_behavior/find_and_set/nearest_wall
|
|
|
|
/datum/ai_behavior/find_and_set/nearest_wall/search_tactic(datum/ai_controller/controller, locate_path, search_range = SEARCH_TACTIC_DEFAULT_RANGE)
|
|
var/mob/living/living_pawn = controller.pawn
|
|
|
|
var/list/nearby_walls = list()
|
|
for (var/turf/closed/new_wall in oview(search_range, controller.pawn))
|
|
if (isindestructiblewall(new_wall))
|
|
continue
|
|
nearby_walls += new_wall
|
|
|
|
if(nearby_walls.len)
|
|
return get_closest_atom(/turf/closed/, nearby_walls, living_pawn)
|
|
|
|
/**
|
|
* Variant of find and set which returns corpses who share your faction
|
|
*/
|
|
/datum/ai_behavior/find_and_set/friendly_corpses
|
|
|
|
/datum/ai_behavior/find_and_set/friendly_corpses/search_tactic(datum/ai_controller/controller, locate_path, search_range = SEARCH_TACTIC_DEFAULT_RANGE)
|
|
var/mob/living/living_pawn = controller.pawn
|
|
var/list/nearby_bodies = list()
|
|
for (var/mob/living/dead_pal in oview(search_range, controller.pawn))
|
|
if (!isturf(dead_pal.loc))
|
|
continue
|
|
if (!dead_pal.stat || dead_pal.health > 0)
|
|
continue
|
|
if (living_pawn.see_invisible < dead_pal.invisibility)
|
|
continue
|
|
if (!living_pawn.faction_check_atom(dead_pal))
|
|
continue
|
|
nearby_bodies += dead_pal
|
|
|
|
if (nearby_bodies.len)
|
|
return pick(nearby_bodies)
|
|
|
|
/**
|
|
* A variant that looks for a human who is not dead or incapacitated, and has a mind
|
|
*/
|
|
/datum/ai_behavior/find_and_set/conscious_person
|
|
|
|
/datum/ai_behavior/find_and_set/conscious_person/search_tactic(datum/ai_controller/controller, locate_path, search_range = SEARCH_TACTIC_DEFAULT_RANGE)
|
|
var/list/customers = list()
|
|
for(var/mob/living/carbon/human/target in oview(search_range, controller.pawn))
|
|
if(IS_DEAD_OR_INCAP(target) || !target.mind)
|
|
continue
|
|
customers += target
|
|
|
|
if(customers.len)
|
|
return pick(customers)
|
|
|
|
return null
|
|
|
|
/datum/ai_behavior/find_and_set/nearby_friends
|
|
action_cooldown = 2 SECONDS
|
|
|
|
/datum/ai_behavior/find_and_set/nearby_friends/search_tactic(datum/ai_controller/controller, locate_path, search_range = SEARCH_TACTIC_DEFAULT_RANGE)
|
|
var/atom/friend = locate(/mob/living/carbon/human) in oview(search_range, controller.pawn)
|
|
|
|
if(isnull(friend))
|
|
return null
|
|
|
|
var/mob/living/living_pawn = controller.pawn
|
|
var/potential_friend = living_pawn.faction.Find(REF(friend)) ? friend : null
|
|
return potential_friend
|
|
|
|
|
|
/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 = SEARCH_TACTIC_DEFAULT_RANGE)
|
|
var/list/found = RANGE_TURFS(search_range, controller.pawn)
|
|
shuffle_inplace(found)
|
|
for(var/turf/possible_turf as anything in found)
|
|
if(!is_type_in_typecache(possible_turf, locate_paths))
|
|
continue
|
|
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 = SEARCH_TACTIC_DEFAULT_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
|