Replace basic mob attack telegraph with slower reaction times (#90585)

## About The Pull Request

This PR removes the `basic_mob_attack_telegraph` component from all mobs
which were using it and instead implements an attack delay directly into
the basic melee AI behaviour.
This delay simulates "moving your mouse into position", human reaction
times, and the fact that the previous implentation of simple mobs would
only try to melee attack on a predictable timer you could "juke" around.

The way that this delay works is that it starts as soon as the mob
enters melee range and resets if you are ever out of the mob's reach, so
there will be no delay if you are foolish enough (or unfortunately
preventing from doing otherwise) to continue standing next to a mob but
there is enough of a one to duck in and out of melee range with a
goliath while using a crusher without getting hit, although trying to
run past one usually won't work.

This delay defaults to 0.3 seconds which in my testing experience was
roughly enough to dip in and out of range without getting hit but not
enough to run all the way past a mob without getting hit. It can be
overriden via the blackboard, although currently no mob does this.
I _was_ testing this locally with no latency though so I guess we'll
listen out to see if miners start yelling.

## Why It's Good For The Game

The visible attack broadcast is very cool but its visibility made melee
combat with any mob that had it significantly easier to such a degree
that any mob with it on could not really be expected to do melee damage
to any character that wasn't suffering some kind of immobilisation
effect which was never really the intention.

Removing it also removes any additional handicap that was applied to
sapient versions of these mobs, which was never really necessary or
intended in the first place.

I did not remove the component entirely from the game for two reasons:
- Out of the hope that there is some use for it somewhere, because I
think it's cool, but more importantly:
- Because it's still being used by simple mob megafauna as a workaround
for a bug we couldn't figure out.

## Changelog

🆑
balance: Most mobs will now hesitate for a moment before attacking
rather than instantly hitting anything that enters melee range, to
better simulate human behaviour. Please report if this delay seems too
short, or too long.
balance: Most mobs which telegraphed their basic attacks on you now will
not do that
/🆑
This commit is contained in:
Jacquerel
2025-04-17 02:03:17 +01:00
committed by Shadow-Quill
parent afaea8a319
commit 6a556355e8
9 changed files with 30 additions and 14 deletions
@@ -1,6 +1,9 @@
/// Amount of time to wait before executing attack if not specified
#define DEFAULT_ATTACK_DELAY (0.4 SECONDS)
/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_MOVE_AND_PERFORM | AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION
///do we finish this action after hitting once?
var/terminate_after_action = FALSE
@@ -16,20 +19,32 @@
set_movement_target(controller, target)
/datum/ai_behavior/basic_melee_attack/perform(seconds_per_tick, datum/ai_controller/controller, target_key, targeting_strategy_key, hiding_location_key)
var/atom/target = controller.blackboard[target_key]
if (isnull(target))
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED
if (!controller.pawn.CanReach(target))
controller.clear_blackboard_key(BB_BASIC_MOB_MELEE_COOLDOWN_TIMER)
return AI_BEHAVIOR_INSTANT
var/can_attack_time = controller.blackboard[BB_BASIC_MOB_MELEE_COOLDOWN_TIMER]
if (isnull(can_attack_time))
var/blackboard_delay = controller.blackboard[BB_BASIC_MOB_MELEE_DELAY]
var/attack_delay = isnull(blackboard_delay) ? DEFAULT_ATTACK_DELAY : blackboard_delay
controller.set_blackboard_key(BB_BASIC_MOB_MELEE_COOLDOWN_TIMER, world.time + attack_delay)
return AI_BEHAVIOR_INSTANT
if (can_attack_time > world.time)
return AI_BEHAVIOR_INSTANT
if (isliving(controller.pawn))
var/mob/living/pawn = controller.pawn
if (world.time < pawn.next_move)
return AI_BEHAVIOR_INSTANT
var/mob/living/basic/basic_mob = controller.pawn
//targeting strategy will kill the action if not real anymore
var/atom/target = controller.blackboard[target_key]
var/datum/targeting_strategy/targeting_strategy = GET_TARGETING_STRATEGY(controller.blackboard[targeting_strategy_key])
if(!targeting_strategy.can_attack(basic_mob, target))
if(!targeting_strategy.can_attack(controller.pawn, target))
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED
var/hiding_target = targeting_strategy.find_hidden_mobs(basic_mob, target) //If this is valid, theyre hidden in something!
var/hiding_target = targeting_strategy.find_hidden_mobs(controller.pawn, target) //If this is valid, theyre hidden in something!
controller.set_blackboard_key(hiding_location_key, hiding_target)
@@ -41,6 +56,7 @@
/datum/ai_behavior/basic_melee_attack/finish_action(datum/ai_controller/controller, succeeded, target_key, targeting_strategy_key, hiding_location_key)
. = ..()
controller.clear_blackboard_key(BB_BASIC_MOB_MELEE_COOLDOWN_TIMER)
if(!succeeded)
controller.clear_blackboard_key(target_key)
@@ -145,3 +161,5 @@
/datum/ai_behavior/basic_ranged_attack/avoid_friendly_fire
avoid_friendly_fire = TRUE
#undef DEFAULT_ATTACK_DELAY