Simplemob Injury System

This adds a lightweight simulation of injury for simplemobs. As simplemobs grow more injured, the time between attacks and movement will increase, same as for players.

Formula here: `round(rand(2,6) * damage_fatigue_mult * clamp(((rand(2,5) * (h / getMaxHealth())) - rand(0,2)), 1, 20))`

Also includes a tiny stealthbuff to snipers - they will now fire instantly and then go on cooldown. No more ridiculous windup that lets you dodge out of the way, meaning they're no longer trivial.
This commit is contained in:
Rykka Stormheart
2023-03-02 22:50:57 -08:00
parent 2bbcc296f4
commit 25845e8495
4 changed files with 53 additions and 12 deletions
+7 -7
View File
@@ -14,9 +14,9 @@
handle_attack_delay(A, melee_attack_delay) // This will sleep this proc for a bit, which is why waitfor is false.
// Cooldown testing is done at click code (for players) and interface code (for AI).
setClickCooldown(get_attack_speed())
setClickCooldown(get_attack_speed() + ((injury_level / 2) SECONDS)) // CHOMPStation Edit: Delay how fast we can attack by our injury level / 2
// Returns a value, but will be lost if
// Returns a value, but will be lost if
. = do_attack(A, their_T)
if(melee_attack_delay)
@@ -90,7 +90,7 @@
if(!istype(A) || QDELETED(A))
return
setClickCooldown(get_attack_speed())
setClickCooldown(get_attack_speed() + ((injury_level / 2) SECONDS)) // CHOMPStation Edit: Delay how fast we can attack by our injury level / 2
face_atom(A)
@@ -102,7 +102,7 @@
if(reload_count >= reload_max)
try_reload()
return FALSE
//CHOMP Addition: This section here is special snowflake code for metroids only, or for whatever else in the future that you want to have move and shoot at the same time. Basically, this is a non-stupid version of the above intended for ranged vore mobs i.e. metroids. ranged_attack_delay is stupid because it sleeps the entire mob. This new ranged_cooldown_time is smarter in the sense that it is an internalized timer. Try not to confuse the names.
if(ranged_cooldown_time) //If you have a non-zero number in a mob's variables, this pattern begins.
if(ranged_cooldown <= world.time) //Further down, a timer keeps adding to the ranged_cooldown variable automatically.
@@ -110,7 +110,7 @@
shoot(A) //Perform the shoot action
if(casingtype) //If the mob is designated to leave casings...
new casingtype(loc) //... leave the casing.
ranged_cooldown = world.time + ranged_cooldown_time //Special addition here. This is a timer. Keeping updating the time after shooting. Add that ranged cooldown time specified in the mob to the world time.
ranged_cooldown = world.time + ranged_cooldown_time + ((injury_level / 2) SECONDS) //Special addition here. This is a timer. Keeping updating the time after shooting. Add that ranged cooldown time specified in the mob to the world time.
return TRUE //End these commands here.
visible_message("<span class='danger'><b>\The [src]</b> fires at \the [A]!</span>")
@@ -120,7 +120,7 @@
if(ranged_attack_delay)
ranged_post_animation(A)
return TRUE
// Shoot a bullet at something.
@@ -268,4 +268,4 @@
/mob/living/simple_mob/proc/special_pre_animation(atom/A)
do_windup_animation(A, special_attack_delay) // Semi-placeholder.
/mob/living/simple_mob/proc/special_post_animation(atom/A)
/mob/living/simple_mob/proc/special_post_animation(atom/A)
@@ -170,6 +170,11 @@
var/obj/item/weapon/card/id/mobcard = null //VOREStation Edit
var/list/mobcard_access = list() //VOREStation Edit
var/mobcard_provided = FALSE //VOREStation Edit
// CHOMPStation Add: Move/Shoot/Attack delays based on damage
var/damage_fatigue_mult = 1 // Our multiplier for how heavily mobs are affected by injury. [UPDATE THIS IF THE FORMULA CHANGES]: Formula = injury_level = round(rand(2,6) * damage_fatigue_mult * clamp(((rand(2,5) * (health / getMaxHealth())) - rand(0,2)), 1, 20))
var/injury_level = 0 // What our injury level is. Rather than being the flat damage, this is the amount added to various delays to simulate injuries in a manner as lightweight as possible.
var/threshold = 0.6 // When we start slowing down. Configure this setting per-mob. Default is 60%
// CHOMPStation Add End
/mob/living/simple_mob/Initialize()
verbs -= /mob/verb/observe
@@ -269,9 +274,11 @@
if(m_intent == "walk")
. *= 1.5
. += config.animal_delay
. += injury_level // CHOMPStation Edit: Adding our injury level delay to our total
. += ..()
. += config.animal_delay
. += ..()
/mob/living/simple_mob/Stat()
@@ -327,3 +334,21 @@
else
..()
//Vorestation Add End
/*
* CHOMPStation Add
* How injured are we? Returns a number that is then added to movement cooldown and firing/melee delay respectively.
* Called by movement_delay and our firing/melee delay checks
*/
/mob/living/simple_mob/proc/get_injury_level(var/mob/living/simple_mob/M)
var/h = getMaxHealth() - getOxyLoss() - getToxLoss() - getFireLoss() - getBruteLoss() - getCloneLoss() - halloss // We're not updating our actual health here bc we want updatehealth() and other checks to handle that
if(h > 0) // Safety to prevent division by 0 errors
if((h / getMaxHealth()) <= threshold) // Essentially, did our health go down? We don't modify want to modify our total slowdown if we didn't actually take damage, and aren't below our threshold %
var/totaldelay = round(rand(2,6) * damage_fatigue_mult * clamp(((rand(2,5) * (h / getMaxHealth())) - rand(0,2)), 1, 20)) // totaldelay is how much delay we're going to feed into attacks and movement. Do NOT change this formula unless you know how to math.
injury_level = totaldelay // Adds our returned slowdown to the mob's injury level
/mob/living/simple_mob/updatehealth() // We don't want to fully override the check, just hook our own code in
get_injury_level() // We check how injured we are, then actually update the mob on how hurt we are.
. = ..() // Calling parent here, actually updating our mob on how hurt we are.
// CHOMPStation Add End
@@ -286,7 +286,8 @@
ai_holder_type = /datum/ai_holder/simple_mob/merc/ranged/sniper
ranged_attack_delay = 2.5 SECONDS
//ranged_attack_delay = 2.5 SECONDS // CHOMPStation Removal: Ranged attack delay is stupid.
ranged_cooldown_time = 2 SECONDS // CHOMPStation Add: Use this for sniper cooldown instead.
loot_list = list(/obj/item/sniper_rifle_part/barrel = 50,
/obj/item/sniper_rifle_part/stock = 50,
@@ -326,6 +327,21 @@
try_reload()
return FALSE
/*
* CHOMP Addition: This section here is (duplicated) special snowflake code because sniper does not call parent. Basically, this is a non-stupid version of the above intended for ranged mobs.
* ranged_attack_delay is stupid because it sleeps the entire mob.
* This new ranged_cooldown_time is smarter in the sense that it is an internalized timer. Try not to confuse the names.
*/
if(ranged_cooldown_time) //If you have a non-zero number in a mob's variables, this pattern begins.
if(ranged_cooldown <= world.time) //Further down, a timer keeps adding to the ranged_cooldown variable automatically.
visible_message("<span class='danger'><b>\The [src]</b> fires at \the [A]!</span>") //Leave notice of shooting.
shoot(A) //Perform the shoot action
if(casingtype) //If the mob is designated to leave casings...
new casingtype(loc) //... leave the casing.
ranged_cooldown = world.time + ranged_cooldown_time + ((injury_level / 2) SECONDS) //Special addition here. This is a timer. Keeping updating the time after shooting. Add that ranged cooldown time specified in the mob to the world time.
return TRUE //End these commands here.
// CHOMPAddition End
visible_message("<span class='danger'><b>\The [src]</b> fires at \the [orig_targ]!</span>")
shoot(A)
if(casingtype)
+2 -2
View File
@@ -28,7 +28,7 @@
potential_damage = (melee_damage_lower + melee_damage_upper) / 2
// Treat potential_damage as estimated DPS. If the enemy attacks twice as fast as usual, it will double the number.
potential_damage *= 1 SECOND / (base_attack_cooldown + melee_attack_delay)
potential_damage *= 1 SECOND / (base_attack_cooldown + melee_attack_delay + injury_level) // CHOMPStation Add: Injury level should affect potential damage, thereby increasing the threat level
else
var/obj/item/projectile/P = new projectiletype(src)
if(P.nodamage || P.taser_effect) // Tasers are somewhat less scary.
@@ -41,7 +41,7 @@
potential_damage += P.agony / 2
qdel(P)
potential_damage *= 1 SECOND / (base_attack_cooldown + ranged_attack_delay)
potential_damage *= 1 SECOND / (base_attack_cooldown + ranged_attack_delay + injury_level) // CHOMPStation Add: Injury level should affect potential damage, thereby increasing the threat level
// Special attacks are ultra-specific to the mob so a generic threat assessment isn't really possible.