Files
CHOMPStation2/code/modules/tension/tension.dm
Anewbe c7e21a5412 Merge pull request #6505 from Heroman3003/tension
Fixes runtime in tension.dm
2019-10-30 21:01:34 -04:00

253 lines
7.0 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)
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)
// 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