mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-07-21 04:57:57 +01:00
[MIRROR] Goats will randomly attack you [MDB IGNORE] (#24312)
* Goats will randomly attack you (#78930) ## About The Pull Request We accidentally lost this behaviour when we converted goats to basic mobs. _Formerly_ (and now again) goats had a 0.5% chance per second to simply decide to attack you for no reason at all. While attacking you they also have a 10% chance per second to get bored of doing that and stop. Additionally, we were outputting a fluff message every time you attacked a goat which would spam chat if you were trying to fist fight each other. I added a 20 second cooldown onto it. As is often the case, implementing this led me down a bit of a rabbit hole. We were previously bypassing faction checks via a mixture of flags on AI behaviours and blackboard keys. I have moved this _entirely_ to the blackboard now, rather than making targetting subtypes just to skip faction checks. This entails having one blackboard key which is "by default do we care about factions?" and another which is "are we currently ignoring factions for some other reason?" Retaliatory AI will generally enable the second flag, so you can get pissed off at someone you would usually not mind hanging out with if they start something with you. Certain mobs which want to hunt other mobs but not be hunted in return just ignore factions entirely all the time and use the former. The upshot of this is that the default behaviour for expected default retaliatory AI shouldn't require you to set any specific kind of targetting datum and will Just Work. In a similar vein because I was touching largely the same mobs I made the "flee when injured" component apply its "don't flee because not injured" flag instantly upon application rather than needing to manually set it in the blackboard definition, so that also Just Works. ## Changelog 🆑 fix: Pete's anger management training has worn off, and he will once again sometimes pick a fight with you for absolutely no reason. qol: Attacking a goat will not spam messages so frequently. /🆑 --------- Co-authored-by: san7890 <the@ san7890.com> * Goats will randomly attack you --------- Co-authored-by: Jacquerel <hnevard@gmail.com> Co-authored-by: san7890 <the@ san7890.com>
This commit is contained in:
@@ -0,0 +1,69 @@
|
||||
/// Add or remove people to our retaliation shitlist just on an arbitrary whim
|
||||
/datum/ai_planning_subtree/capricious_retaliate
|
||||
/// Blackboard key which tells us how to select valid targets
|
||||
var/targetting_datum_key = BB_TARGETTING_DATUM
|
||||
/// Whether we should skip checking faction for our decision
|
||||
var/ignore_faction = TRUE
|
||||
|
||||
/datum/ai_planning_subtree/capricious_retaliate/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick)
|
||||
. = ..()
|
||||
controller.queue_behavior(/datum/ai_behavior/capricious_retaliate, targetting_datum_key, ignore_faction)
|
||||
|
||||
/// Add or remove people to our retaliation shitlist just on an arbitrary whim
|
||||
/datum/ai_behavior/capricious_retaliate
|
||||
action_cooldown = 1 SECONDS
|
||||
|
||||
/datum/ai_behavior/capricious_retaliate/perform(seconds_per_tick, datum/ai_controller/controller, targetting_datum_key, ignore_faction)
|
||||
. = ..()
|
||||
var/atom/pawn = controller.pawn
|
||||
if (controller.blackboard_key_exists(BB_BASIC_MOB_RETALIATE_LIST))
|
||||
var/deaggro_chance = controller.blackboard[BB_RANDOM_DEAGGRO_CHANCE] || 10
|
||||
if (!SPT_PROB(deaggro_chance, seconds_per_tick))
|
||||
finish_action(controller, TRUE, ignore_faction) // "true" here means "don't clear our ignoring factions status"
|
||||
return
|
||||
pawn.visible_message(span_notice("[pawn] calms down.")) // We can blackboard key this if anyone else actually wants to customise it
|
||||
controller.clear_blackboard_key(BB_BASIC_MOB_RETALIATE_LIST)
|
||||
finish_action(controller, FALSE, ignore_faction)
|
||||
controller.CancelActions() // Otherwise they will try and get one last kick in
|
||||
return
|
||||
|
||||
var/aggro_chance = controller.blackboard[BB_RANDOM_AGGRO_CHANCE] || 0.5
|
||||
if (!SPT_PROB(aggro_chance, seconds_per_tick))
|
||||
finish_action(controller, FALSE, ignore_faction)
|
||||
return
|
||||
|
||||
var/aggro_range = controller.blackboard[BB_AGGRO_RANGE] || 9
|
||||
var/list/potential_targets = hearers(aggro_range, get_turf(pawn)) - pawn
|
||||
if (!length(potential_targets))
|
||||
failed_targetting(controller, pawn, ignore_faction)
|
||||
return
|
||||
|
||||
var/datum/targetting_datum/target_helper = controller.blackboard[targetting_datum_key]
|
||||
|
||||
var/mob/living/final_target = null
|
||||
if (ignore_faction)
|
||||
controller.set_blackboard_key(BB_TEMPORARILY_IGNORE_FACTION, TRUE)
|
||||
while (isnull(final_target) && length(potential_targets))
|
||||
var/mob/living/test_target = pick_n_take(potential_targets)
|
||||
if (target_helper.can_attack(pawn, test_target, vision_range = aggro_range))
|
||||
final_target = test_target
|
||||
|
||||
if (isnull(final_target))
|
||||
failed_targetting(controller, pawn, ignore_faction)
|
||||
return
|
||||
|
||||
controller.insert_blackboard_key_lazylist(BB_BASIC_MOB_RETALIATE_LIST, final_target)
|
||||
pawn.visible_message(span_warning("[pawn] glares grumpily at [final_target]!"))
|
||||
finish_action(controller, TRUE, ignore_faction)
|
||||
|
||||
/// Called if we try but fail to target something
|
||||
/datum/ai_behavior/capricious_retaliate/proc/failed_targetting(datum/ai_controller/controller, atom/pawn, ignore_faction)
|
||||
finish_action(controller, FALSE, ignore_faction)
|
||||
pawn.visible_message(span_notice("[pawn] grumbles.")) // We're pissed off but with no outlet to vent our frustration upon
|
||||
|
||||
/datum/ai_behavior/capricious_retaliate/finish_action(datum/ai_controller/controller, succeeded, ignore_faction)
|
||||
. = ..()
|
||||
if (succeeded || !ignore_faction)
|
||||
return
|
||||
var/usually_ignores_faction = controller.blackboard[BB_ALWAYS_IGNORE_FACTION] || FALSE
|
||||
controller.set_blackboard_key(BB_TEMPORARILY_IGNORE_FACTION, usually_ignores_faction)
|
||||
@@ -41,23 +41,27 @@
|
||||
if(!targetting_datum)
|
||||
CRASH("No target datum was supplied in the blackboard for [controller.pawn]")
|
||||
|
||||
var/list/shitlist = controller.blackboard[shitlist_key]
|
||||
var/atom/existing_target = controller.blackboard[target_key]
|
||||
|
||||
if (!check_faction)
|
||||
controller.set_blackboard_key(BB_TEMPORARILY_IGNORE_FACTION, TRUE)
|
||||
|
||||
if (!QDELETED(existing_target) && (locate(existing_target) in shitlist) && targetting_datum.can_attack(living_mob, existing_target, vision_range))
|
||||
finish_action(controller, succeeded = TRUE, check_faction = check_faction)
|
||||
return
|
||||
|
||||
var/list/enemies_list = list()
|
||||
|
||||
for(var/mob/living/potential_target as anything in controller.blackboard[shitlist_key])
|
||||
if(!targetting_datum.can_attack(living_mob, potential_target, vision_range, check_faction))
|
||||
for(var/mob/living/potential_target as anything in shitlist)
|
||||
if(!targetting_datum.can_attack(living_mob, potential_target, vision_range))
|
||||
continue
|
||||
enemies_list += potential_target
|
||||
|
||||
|
||||
if(!length(enemies_list))
|
||||
controller.clear_blackboard_key(target_key)
|
||||
finish_action(controller, succeeded = FALSE, check_faction = check_faction)
|
||||
return
|
||||
|
||||
if (controller.blackboard[target_key] in enemies_list) // Don't bother changing
|
||||
finish_action(controller, succeeded = TRUE, check_faction = check_faction)
|
||||
return
|
||||
|
||||
var/atom/new_target = pick_final_target(controller, enemies_list)
|
||||
controller.set_blackboard_key(target_key, new_target)
|
||||
|
||||
@@ -72,9 +76,9 @@
|
||||
/datum/ai_behavior/target_from_retaliate_list/proc/pick_final_target(datum/ai_controller/controller, list/enemies_list)
|
||||
return pick(enemies_list)
|
||||
|
||||
|
||||
/datum/ai_behavior/target_from_retaliate_list/finish_action(datum/ai_controller/controller, succeeded, target_key, check_faction)
|
||||
/datum/ai_behavior/target_from_retaliate_list/finish_action(datum/ai_controller/controller, succeeded, check_faction)
|
||||
. = ..()
|
||||
if(check_faction)
|
||||
if (succeeded || check_faction)
|
||||
return
|
||||
controller.set_blackboard_key(BB_BASIC_MOB_SKIP_FACTION_CHECK, succeeded)
|
||||
var/usually_ignores_faction = controller.blackboard[BB_ALWAYS_IGNORE_FACTION] || FALSE
|
||||
controller.set_blackboard_key(BB_TEMPORARILY_IGNORE_FACTION, usually_ignores_faction)
|
||||
|
||||
Reference in New Issue
Block a user