mirror of
https://github.com/CHOMPStation2/CHOMPStation2.git
synced 2025-12-24 17:12:00 +00:00
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.
253 lines
7.2 KiB
Plaintext
253 lines
7.2 KiB
Plaintext
// This code is used to give a very rough estimate of how screwed an individual player might be at any given moment when fighting monsters.
|
|
// You could use this to have an effect trigger when someone is in serious danger, or as a means for an AI to guess which mob needs to die first.
|
|
// The idea and the code structure was taken from Dungeon Crawl Stone Soup.
|
|
|
|
/atom/movable/proc/get_threat(var/mob/living/threatened)
|
|
return 0
|
|
|
|
|
|
/atom/movable/proc/guess_threat_level(var/mob/living/threatened)
|
|
return 0
|
|
|
|
/mob/living/simple_mob
|
|
var/threat_level = null // Set this if you want an explicit danger rating.
|
|
|
|
/mob/living/simple_mob/guess_threat_level(var/mob/living/threatened)
|
|
if(threat_level) // If they have a predefined number, use it.
|
|
return threat_level
|
|
// Otherwise we need to guess how scary this thing is.
|
|
var/threat_guess = 0
|
|
|
|
// First lets consider their attack ability.
|
|
var/will_point_blank = FALSE
|
|
if(has_AI())
|
|
will_point_blank = ai_holder.pointblank
|
|
|
|
var/potential_damage = 0
|
|
if(!projectiletype || ( ( get_dist(src, threatened) >= 1) && !will_point_blank ) ) // Melee damage.
|
|
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 + 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.
|
|
potential_damage = P.agony / 2
|
|
else
|
|
potential_damage = P.damage
|
|
if(P.damage_type == HALLOSS) // Not sure if any projectiles do this, but can't be too safe.
|
|
potential_damage /= 2
|
|
// Rubber bullets, I guess.
|
|
potential_damage += P.agony / 2
|
|
qdel(P)
|
|
|
|
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.
|
|
|
|
threat_guess += potential_damage
|
|
|
|
// Then consider their defense.
|
|
threat_guess += getMaxHealth() / 5 // 100 health translates to 20 threat.
|
|
|
|
return threat_guess
|
|
|
|
/mob/living/get_threat(var/mob/living/threatened)
|
|
if(stat)
|
|
return 0
|
|
|
|
|
|
/mob/living/simple_mob/get_threat(var/mob/living/threatened)
|
|
. = ..()
|
|
|
|
if(has_AI())
|
|
if(!ai_holder.hostile)
|
|
return 0 // Can't hurt anyone.
|
|
|
|
if(incapacitated(INCAPACITATION_DISABLED))
|
|
return 0 // Can't currently hurt you if it's stunned.
|
|
|
|
var/friendly = threatened.faction == faction
|
|
|
|
var/threat = guess_threat_level(threatened)
|
|
|
|
// Hurt entities contribute less tension.
|
|
threat *= health
|
|
threat /= getMaxHealth()
|
|
|
|
// Allies reduce tension instead of adding.
|
|
if(friendly)
|
|
threat = -threat
|
|
|
|
else
|
|
if(threatened.invisibility > see_invisible)
|
|
threat /= 2 // Target cannot be seen by src.
|
|
if(invisibility > threatened.see_invisible)
|
|
threat *= 2 // Target cannot see src.
|
|
|
|
// Handle statuses.
|
|
if(confused)
|
|
threat /= 2
|
|
|
|
if(has_modifier_of_type(/datum/modifier/berserk))
|
|
threat *= 2
|
|
|
|
// Handle ability to harm.
|
|
// Being five tiles away from some spiders is a lot less scary than being in melee range of five spiders at once.
|
|
if(!projectiletype)
|
|
threat /= max(get_dist(src, threatened), 1)
|
|
|
|
return threat
|
|
|
|
// Carbon / mostly Human threat check.
|
|
/mob/living/carbon/get_threat(var/mob/living/threatened)
|
|
. = ..()
|
|
|
|
if(has_AI())
|
|
if(!ai_holder.hostile)
|
|
return 0
|
|
|
|
if(incapacitated(INCAPACITATION_DISABLED))
|
|
return 0
|
|
|
|
var/friendly = (IIsAlly(threatened) && a_intent == I_HELP)
|
|
|
|
var/threat = guess_threat_level(threatened)
|
|
|
|
threat *= health
|
|
threat /= getMaxHealth()
|
|
|
|
// Allies reduce tension instead of adding.
|
|
if(friendly)
|
|
threat = -threat
|
|
|
|
else
|
|
if(threatened.invisibility > see_invisible)
|
|
threat /= 2 // Target cannot be seen by src.
|
|
if(invisibility > threatened.see_invisible)
|
|
threat *= 2 // Target cannot see src.
|
|
|
|
// Handle statuses.
|
|
if(confused)
|
|
threat /= 2
|
|
|
|
if(has_modifier_of_type(/datum/modifier/berserk))
|
|
threat *= 2
|
|
|
|
return threat
|
|
|
|
/mob/living/carbon/guess_threat_level(var/mob/living/threatened)
|
|
var/threat_guess = 0
|
|
|
|
// First lets consider their attack ability.
|
|
var/will_point_blank = FALSE
|
|
if(has_AI())
|
|
will_point_blank = ai_holder.pointblank
|
|
|
|
. = ..()
|
|
|
|
var/obj/item/I = get_active_hand()
|
|
if(!I || !istype(I))
|
|
var/damage_guess = 0
|
|
if(ishuman(src) && ishuman(threatened))
|
|
var/mob/living/carbon/human/H = src
|
|
var/datum/unarmed_attack/attack = H.get_unarmed_attack(threatened, BP_TORSO)
|
|
if(!attack)
|
|
damage_guess += 5
|
|
|
|
var/punch_damage = attack.get_unarmed_damage(H)
|
|
if(H.gloves)
|
|
if(istype(H.gloves, /obj/item/clothing/gloves))
|
|
var/obj/item/clothing/gloves/G = H.gloves
|
|
punch_damage += G.punch_force
|
|
|
|
damage_guess += punch_damage
|
|
|
|
else
|
|
damage_guess += 5
|
|
|
|
for(var/datum/modifier/M in modifiers)
|
|
if(!isnull(M.outgoing_melee_damage_percent))
|
|
damage_guess *= M.outgoing_melee_damage_percent
|
|
|
|
threat_guess += damage_guess
|
|
|
|
else
|
|
var/weapon_attack_speed = get_attack_speed(I) / (1 SECOND)
|
|
var/weapon_damage = I.force
|
|
|
|
for(var/datum/modifier/M in modifiers)
|
|
if(!isnull(M.outgoing_melee_damage_percent))
|
|
weapon_damage *= M.outgoing_melee_damage_percent
|
|
|
|
if(istype(I, /obj/item/weapon/gun))
|
|
will_point_blank = TRUE
|
|
var/obj/item/weapon/gun/G = I
|
|
var/obj/item/projectile/P
|
|
|
|
P = new G.projectile_type()
|
|
|
|
if(P) // Does the gun even have a projectile type?
|
|
weapon_damage = P.damage
|
|
if(will_point_blank && a_intent == I_HURT)
|
|
weapon_damage *= 1.5
|
|
weapon_attack_speed = G.fire_delay / (1 SECOND)
|
|
qdel(P)
|
|
|
|
var/average_damage = weapon_damage / weapon_attack_speed
|
|
|
|
threat_guess += average_damage
|
|
|
|
// Consider intent.
|
|
switch(a_intent)
|
|
if(I_HELP) // Not likely to fight us.
|
|
threat_guess *= 0.4
|
|
if(I_DISARM) // Might engage us, but unlikely to be with the intent to kill.
|
|
threat_guess *= 0.8
|
|
if(I_GRAB) // May try to restrain us. This is here for reference, or later tweaking if needed.
|
|
threat_guess *= 1
|
|
if(I_HURT) // May try to hurt us.
|
|
threat_guess *= 1.25
|
|
|
|
// Then consider their defense.
|
|
threat_guess += getMaxHealth() / 5 // 100 health translates to 20 threat.
|
|
|
|
return threat_guess
|
|
|
|
// Gives a rough idea of how much danger someone is in. Meant to be used for PvE things since PvP has too many unknown variables.
|
|
/mob/living/proc/get_tension()
|
|
var/tension = 0
|
|
var/list/potential_threats = list()
|
|
|
|
// First, get everything threatening to us.
|
|
for(var/thing in view(src))
|
|
if(isliving(thing))
|
|
potential_threats += thing
|
|
if(istype(thing, /obj/machinery/porta_turret))
|
|
potential_threats += thing
|
|
|
|
var/danger = FALSE
|
|
// Now to get all the threats.
|
|
for(var/atom/movable/AM in potential_threats)
|
|
var/tension_from_AM = AM.get_threat(src)
|
|
tension += tension_from_AM
|
|
if(tension_from_AM > 0)
|
|
danger = TRUE
|
|
|
|
if(!danger)
|
|
return 0
|
|
|
|
// Tension is roughly doubled when about to fall into crit.
|
|
var/max_health = getMaxHealth()
|
|
tension *= 2 * max_health / (health + max_health)
|
|
|
|
// Being unable to act is really tense.
|
|
if(incapacitated(INCAPACITATION_DISABLED) && !lying)
|
|
tension *= 10
|
|
return tension
|
|
|
|
if(confused)
|
|
tension *= 2
|
|
|
|
return tension
|