Fix basic mobs triggering click cooldown erroneously. (#95906)

## About The Pull Request

Basic Mobs used to always trigger cooldown on clicks. This resulted in
missed attacks causing you to have a delay before you could attack
again, this makes it really punishing to play as a basic mob.

To resolve this I did a mini-refactor on basic mob's attack chain to
allow for the return values to determine whether we go on cooldown or
not. Preventing attacks that did nothing (due to not passing checks, or
simply not having any behavior) from causing cooldown.

This fixes #95605

## Why It's Good For The Game

being able to perform melee with the same rules as /human is only fair

## Changelog

🆑 DresserOnFire
fix: Fixes a bug where player-controlled basic mobs would get a cooldown
when their attacks miss
refactor: basic mob attack chain can now decided whether an attack
resulted in a cooldown or not.
/🆑
This commit is contained in:
CabinetOnFire
2026-04-29 21:59:01 +01:00
committed by GitHub
parent b01715e5e7
commit 2ede6af8bc
26 changed files with 107 additions and 91 deletions
@@ -96,16 +96,16 @@
/mob/living/basic/eyeball/early_melee_attack(atom/target, list/modifiers, ignore_cooldown)
. = ..()
if(!.)
return FALSE
if(.)
return
if(!ishuman(target))
return TRUE
return BASIC_MOB_CONTINUE_ATTACK_CHAIN
var/mob/living/carbon/human_target = target
var/obj/item/organ/eyes/eyes = human_target.get_organ_slot(ORGAN_SLOT_EYES)
if(isnull(eyes) || eyes.damage < 10)
return TRUE
return BASIC_MOB_CONTINUE_ATTACK_CHAIN
heal_eye_damage(human_target, eyes)
return FALSE
return BASIC_MOB_END_ATTACK_CHAIN_COOLDOWN
/mob/living/basic/eyeball/proc/heal_eye_damage(mob/living/target, obj/item/organ/eyes/eyes)
if(!COOLDOWN_FINISHED(src, eye_healing))
@@ -99,16 +99,16 @@
/mob/living/basic/hivebot/mechanic/early_melee_attack(atom/target, list/modifiers, ignore_cooldown)
. = ..()
if(!.)
return FALSE
if(.)
return
if(ismachinery(target))
repair_machine(target)
return FALSE
return BASIC_MOB_END_ATTACK_CHAIN_COOLDOWN
if(istype(target, /mob/living/basic/hivebot))
repair_hivebot(target)
return FALSE
return BASIC_MOB_END_ATTACK_CHAIN_COOLDOWN
/mob/living/basic/hivebot/mechanic/proc/repair_machine(obj/machinery/fixable)
if(fixable.get_integrity() >= fixable.max_integrity)
@@ -149,29 +149,29 @@
/// Handles the logic for attacking anything.
/mob/living/basic/morph/early_melee_attack(atom/target, list/modifiers, ignore_cooldown)
. = ..()
if(!.)
return FALSE
if(.)
return
if(HAS_TRAIT(src, TRAIT_DISGUISED) && (melee_damage_disguised <= 0))
balloon_alert(src, "can't attack while disguised!")
return FALSE
return BASIC_MOB_END_ATTACK_CHAIN_COOLDOWN
if(isliving(target)) //Eat Corpses to regen health
var/mob/living/living_target = target
if(living_target.stat != DEAD)
return TRUE
return BASIC_MOB_CONTINUE_ATTACK_CHAIN
eat(eatable = living_target, delay = 3 SECONDS, update_health = -50)
return FALSE
return BASIC_MOB_END_ATTACK_CHAIN_COOLDOWN
if(!isitem(target)) //Eat items just to be annoying
return TRUE
return BASIC_MOB_CONTINUE_ATTACK_CHAIN
var/obj/item/item_target = target
if(item_target.anchored)
return TRUE
return BASIC_MOB_CONTINUE_ATTACK_CHAIN
eat(eatable = item_target, delay = 2 SECONDS)
return FALSE
return BASIC_MOB_END_ATTACK_CHAIN_COOLDOWN
/// Eat stuff. Delicious. Return TRUE if we ate something, FALSE otherwise.
/// Required: `eatable` is the thing (item or mob) that we are going to eat.
@@ -171,22 +171,22 @@
/// Checks if we are able to attack this object, as well as send out the signal to see if we get any special regal rat interactions.
/mob/living/basic/regal_rat/early_melee_attack(atom/target, list/modifiers, ignore_cooldown)
. = ..()
if(!.)
return FALSE
if(.)
return
if(DOING_INTERACTION(src, REGALRAT_INTERACTION) || !allowed_to_attack(target))
return FALSE
return BASIC_MOB_END_ATTACK_CHAIN
if(SEND_SIGNAL(target, COMSIG_RAT_INTERACT, src) & COMPONENT_RAT_INTERACTED)
return FALSE
return BASIC_MOB_END_ATTACK_CHAIN_COOLDOWN
if(isnull(mind) || combat_mode)
return TRUE
return BASIC_MOB_CONTINUE_ATTACK_CHAIN
if(poison_target(target))
return FALSE
return BASIC_MOB_END_ATTACK_CHAIN_COOLDOWN
return TRUE
return BASIC_MOB_CONTINUE_ATTACK_CHAIN
/// Checks if we are allowed to attack this mob. Will return TRUE if we are potentially allowed to attack, but if we end up in a case where we should NOT attack, return FALSE.
/mob/living/basic/regal_rat/proc/allowed_to_attack(atom/the_target)
@@ -49,8 +49,8 @@
/// Proc that we call on attacking something to dust 'em.
/mob/living/basic/supermatter_spider/early_melee_attack(atom/target, list/modifiers, ignore_cooldown)
. = ..()
if(!.)
return FALSE
if(.)
return
if(isliving(target))
var/mob/living/victim = target
@@ -59,14 +59,14 @@
victim.dust()
if(single_use)
death()
return FALSE
return BASIC_MOB_END_ATTACK_CHAIN_COOLDOWN
if(!isturf(target))
dust_feedback(target)
qdel(target)
if(single_use)
death()
return FALSE
return BASIC_MOB_END_ATTACK_CHAIN_COOLDOWN
/// Simple proc that plays the supermatter dusting sound and sends a visible message.
/mob/living/basic/supermatter_spider/proc/dust_feedback(atom/target)