Files
Bubberstation/code/datums/ai/basic_mobs/basic_subtrees/target_retaliate.dm
T
SkyratBot b4989ba05b [MIRROR] Unit Tests for AI Planning Subtrees not having required element/component [MDB IGNORE] (#23047)
* Unit Tests for AI Planning Subtrees not having required element/component (#77539)

## About The Pull Request

Hey there,

I've personally fallen for this stupid thing twice (in #77503 and #75627
(d3575161ca)), so I decided to spend a few
hours to crack out a unit test to ensure that I (and no one else) falls
for this stupid thing again.

Let me know if there's a smarter way to code something like this, but I
couldn't figure out a better way to accomodate the current framework and
be as agnostic to certain oddities as possible.
## Why It's Good For The Game
Catches stuff like this:

```txt
[2023-08-11 21:10:04.019]     FAILURE #1: The mob Garden Gnome does not have ANY instances of TRAIT_SUBTREE_REQUIRED_ELEMENT, but has a planning subtree (/datum/ai_planning_subtree/target_retaliate) that requires it! at code/modules/unit_tests/ensure_subtree_element.dm:45
 -     FAILURE #2: The mob the morph does not have ANY instances of TRAIT_SUBTREE_REQUIRED_ELEMENT, but has a planning subtree (/datum/ai_planning_subtree/target_retaliate) that requires it! at code/modules/unit_tests/ensure_subtree_element.dm:45
 -     FAILURE #3: The mob the guard spiderling (946) does not have ANY instances of TRAIT_SUBTREE_REQUIRED_ELEMENT, but has a planning subtree (/datum/ai_planning_subtree/target_retaliate/to_flee) that requires it! at code/modules/unit_tests/ensure_subtree_element.dm:45
 -     FAILURE #4: The mob the ambush spiderling (255) does not have ANY instances of TRAIT_SUBTREE_REQUIRED_ELEMENT, but has a planning subtree (/datum/ai_planning_subtree/target_retaliate/to_flee) that requires it! at code/modules/unit_tests/ensure_subtree_element.dm:45
 -     FAILURE #5: The mob the scout spiderling (375) does not have ANY instances of TRAIT_SUBTREE_REQUIRED_ELEMENT, but has a planning subtree (/datum/ai_planning_subtree/target_retaliate/to_flee) that requires it! at code/modules/unit_tests/ensure_subtree_element.dm:45
 -     FAILURE #6: The mob the flesh spiderling (337) does not have ANY instances of TRAIT_SUBTREE_REQUIRED_ELEMENT, but has a planning subtree (/datum/ai_planning_subtree/target_retaliate/to_flee) that requires it! at code/modules/unit_tests/ensure_subtree_element.dm:45
 -     FAILURE #7: The mob the hunter spiderling (869) does not have ANY instances of TRAIT_SUBTREE_REQUIRED_ELEMENT, but has a planning subtree (/datum/ai_planning_subtree/target_retaliate/to_flee) that requires it! at code/modules/unit_tests/ensure_subtree_element.dm:45
 -     FAILURE #8: The mob the nurse spiderling (629) does not have ANY instances of TRAIT_SUBTREE_REQUIRED_ELEMENT, but has a planning subtree (/datum/ai_planning_subtree/target_retaliate/to_flee) that requires it! at code/modules/unit_tests/ensure_subtree_element.dm:45
 -     FAILURE #9: The mob the tangle spiderling (19) does not have ANY instances of TRAIT_SUBTREE_REQUIRED_ELEMENT, but has a planning subtree (/datum/ai_planning_subtree/target_retaliate/to_flee) that requires it! at code/modules/unit_tests/ensure_subtree_element.dm:45
 -     FAILURE #10: The mob the broodmother spiderling (855) does not have ANY instances of TRAIT_SUBTREE_REQUIRED_ELEMENT, but has a planning subtree (/datum/ai_planning_subtree/target_retaliate/to_flee) that requires it! at code/modules/unit_tests/ensure_subtree_element.dm:45
 -     FAILURE #11: The mob the viper spiderling (519) does not have ANY instances of TRAIT_SUBTREE_REQUIRED_ELEMENT, but has a planning subtree (/datum/ai_planning_subtree/target_retaliate/to_flee) that requires it! at code/modules/unit_tests/ensure_subtree_element.dm:45
 -     FAILURE #12: The mob the tarantula spiderling (963) does not have ANY instances of TRAIT_SUBTREE_REQUIRED_ELEMENT, but has a planning subtree (/datum/ai_planning_subtree/target_retaliate/to_flee) that requires it! at code/modules/unit_tests/ensure_subtree_element.dm:45
 -     FAILURE #13: The mob the spiderling (100) does not have ANY instances of TRAIT_SUBTREE_REQUIRED_ELEMENT, but has a planning subtree (/datum/ai_planning_subtree/target_retaliate/to_flee) that requires it! at code/modules/unit_tests/ensure_subtree_element.dm:45
```

(ignore the part about gnomes and morphs, this was an earlier version of
the unit test. everything else was relevant and is fixed)
## Changelog
🆑
fix: Growing spiders will now retaliate against you like they were
always meant to.
/🆑

* Unit Tests for AI Planning Subtrees not having required element/component

---------

Co-authored-by: san7890 <the@san7890.com>
2023-08-12 18:34:53 -04:00

71 lines
3.4 KiB
Plaintext

/// Sets the BB target to a mob which you can see and who has recently attacked you
/datum/ai_planning_subtree/target_retaliate
operational_datums = list(/datum/element/ai_retaliate, /datum/component/ai_retaliate_advanced)
/// Blackboard key which tells us how to select valid targets
var/targetting_datum_key = BB_TARGETTING_DATUM
/// Blackboard key in which to store selected target
var/target_key = BB_BASIC_MOB_CURRENT_TARGET
/// Blackboard key in which to store selected target's hiding place
var/hiding_place_key = BB_BASIC_MOB_CURRENT_TARGET_HIDING_LOCATION
/datum/ai_planning_subtree/target_retaliate/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick)
. = ..()
controller.queue_behavior(/datum/ai_behavior/target_from_retaliate_list, BB_BASIC_MOB_RETALIATE_LIST, target_key, targetting_datum_key, hiding_place_key)
/// Places a mob which you can see and who has recently attacked you into some 'run away from this' AI keys
/// Can use a different targetting datum than you use to select attack targets
/// Not required if fleeing is the only target behaviour or uses the same target datum
/datum/ai_planning_subtree/target_retaliate/to_flee
targetting_datum_key = BB_FLEE_TARGETTING_DATUM
target_key = BB_BASIC_MOB_FLEE_TARGET
hiding_place_key = BB_BASIC_MOB_FLEE_TARGET_HIDING_LOCATION
/**
* Picks a target from a provided list of atoms who have been pissing you off
* You will probably need /datum/element/ai_retaliate to take advantage of this unless you're populating the blackboard yourself
*/
/datum/ai_behavior/target_from_retaliate_list
action_cooldown = 2 SECONDS
/// How far can we see stuff?
var/vision_range = 9
/datum/ai_behavior/target_from_retaliate_list/perform(seconds_per_tick, datum/ai_controller/controller, shitlist_key, target_key, targetting_datum_key, hiding_location_key)
. = ..()
var/mob/living/living_mob = controller.pawn
var/datum/targetting_datum/targetting_datum = controller.blackboard[targetting_datum_key]
if(!targetting_datum)
CRASH("No target datum was supplied in the blackboard for [controller.pawn]")
var/list/enemies_list = controller.blackboard[shitlist_key]
if (!length(enemies_list))
finish_action(controller, succeeded = FALSE)
return
if (controller.blackboard[target_key] in enemies_list) // Don't bother changing
finish_action(controller, succeeded = FALSE)
return
var/atom/new_target = pick_final_target(controller, enemies_list)
controller.set_blackboard_key(target_key, new_target)
var/atom/potential_hiding_location = targetting_datum.find_hidden_mobs(living_mob, new_target)
if(potential_hiding_location) //If they're hiding inside of something, we need to know so we can go for that instead initially.
controller.set_blackboard_key(hiding_location_key, potential_hiding_location)
finish_action(controller, succeeded = TRUE)
/// Returns true if this target is valid for attacking based on current conditions
/datum/ai_behavior/target_from_retaliate_list/proc/can_attack_target(mob/living/living_mob, atom/target, datum/targetting_datum/targetting_datum)
if (!target)
return FALSE
if (target == living_mob)
return FALSE
if (!can_see(living_mob, target, vision_range))
return FALSE
return targetting_datum.can_attack(living_mob, target)
/// Returns the desired final target from the filtered list of enemies
/datum/ai_behavior/target_from_retaliate_list/proc/pick_final_target(datum/ai_controller/controller, list/enemies_list)
return pick(enemies_list)