mirror of
https://github.com/Aurorastation/Aurora.3.git
synced 2026-08-22 20:43:10 +01:00
More Mech Damage Feedback (#22118)
This PR adds some new "Damage Feedback" interactions to mechs, which I'm adding in response to what I think is player perception of mechs being invincible. Previously mech manipulators and torsos had no actual mechanics for damage states, and having 0 damage on a mechs arms was functionally the same thing as having 100 damage, so long as the arm wasn't completely destroyed. This PR changes that by making it so that mech manipulators have a chance to fail (loudly and visibly) on use with a probability that varies based on how much damage the arms have taken. Additionally, there's also now a mechanic for mech ejections and hatch opening to fail if the torso has taken sufficient damage, with chances that also vary based on how much damage said torso has taken. In both cases, the mech plays audible and visual effects to indicate to the players involved that it is being hindered by damage. https://github.com/user-attachments/assets/934e4561-1fda-4c47-9770-438960834aa9 Yes this does mean that you can smash someone's hatch closed, to a point you'll need a welder to get them out. <img width="772" height="181" alt="image" src="https://github.com/user-attachments/assets/75f98060-7c4c-468b-bd31-e6c4ec4c0be2" />
This commit is contained in:
@@ -9,13 +9,19 @@
|
||||
var/total_damage = 0
|
||||
var/brute_damage = 0
|
||||
var/burn_damage = 0
|
||||
|
||||
/// The maximum combined brute and burn damage that the mech component can take before becoming completely broken.
|
||||
var/max_damage = 120
|
||||
|
||||
var/damage_state = 1
|
||||
var/list/has_hardpoints = list()
|
||||
var/power_use = 0
|
||||
matter = list(DEFAULT_WALL_MATERIAL = 15000, MATERIAL_PLASTIC = 1000, MATERIAL_OSMIUM = 500)
|
||||
dir = SOUTH
|
||||
|
||||
/// Half of the baseline chance that attempting to use these arms will create sparks. Actual chance is twice this since the motivator damage contributes to the ratio.
|
||||
var/spark_chance_ratio = 50
|
||||
|
||||
/obj/item/mech_component/feedback_hints(mob/user, distance, is_adjacent)
|
||||
. += ..()
|
||||
if(ready_to_install())
|
||||
|
||||
@@ -32,6 +32,13 @@
|
||||
var/hide_pilot = TRUE
|
||||
has_hardpoints = list(HARDPOINT_BACK, HARDPOINT_LEFT_SHOULDER, HARDPOINT_RIGHT_SHOULDER)
|
||||
|
||||
/**
|
||||
* Maximum % chance for an ejection to fail based on how much damage the mech's torso has taken.
|
||||
* If you're stuck, better hope there's a friend available to bail you out..
|
||||
* Or you can keep the hatch open if you would rather risk getting zipped by incoming fire.
|
||||
*/
|
||||
var/ejection_fail_chance = 75
|
||||
|
||||
/obj/item/mech_component/chassis/Initialize()
|
||||
. = ..()
|
||||
AddComponent(/datum/component/armor, armor, ARMOR_TYPE_STANDARD|ARMOR_TYPE_EXOSUIT)
|
||||
|
||||
@@ -211,6 +211,8 @@
|
||||
/atom/movable/screen/mecha/toggle/air/toggled()
|
||||
toggled = !toggled
|
||||
owner.use_air = toggled
|
||||
if (owner.use_air)
|
||||
playsound(owner.loc, 'sound/effects/internals.ogg', 75, 1)
|
||||
var/main_color = owner.use_air ? "#d1d1d1" : "#525252"
|
||||
maptext = "<span style=\"font-family: 'Small Fonts'; color: [main_color]; -dm-text-outline: 1 #242424; font-size: 6px;\">AIR</span>"
|
||||
notify_user(usr, SPAN_NOTICE("Auxiliary atmospheric system [owner.use_air ? "enabled" : "disabled"]."))
|
||||
@@ -286,6 +288,7 @@
|
||||
maptext_y = 11
|
||||
maptext_x = 5
|
||||
notify_user(usr, SPAN_NOTICE("The [owner.body.hatch_descriptor] is [owner.hatch_locked ? "now" : "no longer" ] locked."))
|
||||
playsound(owner.loc, 'sound/effects/magnetclamp.ogg', 100, 1)
|
||||
|
||||
/atom/movable/screen/mecha/toggle/hatch_open
|
||||
name = "open or close hatch"
|
||||
@@ -298,15 +301,29 @@
|
||||
maptext_x = owner.hatch_closed ? 4 : 3
|
||||
|
||||
/atom/movable/screen/mecha/toggle/hatch_open/toggled(var/notify_user = TRUE)
|
||||
var/obj/item/mech_component/chassis/body = owner.body
|
||||
if(!body) // Edge case for the mech SOMEHOW not having a body. Players shouldn't ever see this. A mech without a body should be in the process of exploding.
|
||||
notify_user(usr, SPAN_WARNING("What hatch? There's nothing left to open. If you're seeing this, please submit a bug report to our github issue tracker and tell us how!"))
|
||||
return FALSE
|
||||
|
||||
if(owner.hatch_locked && owner.hatch_closed)
|
||||
notify_user(usr, SPAN_WARNING("You cannot open the hatch while it is locked."))
|
||||
return
|
||||
return FALSE
|
||||
|
||||
if(owner.hatch_closed && body.total_damage > 1 && prob((body.total_damage / body.max_damage) * body.ejection_fail_chance))
|
||||
to_chat(usr, SPAN_WARNING("You pull the hatch release. The hatch bolts hiss and clang with a deafening screech, but the hatch fails to open!"))
|
||||
spark(owner, 6, GLOB.alldirs)
|
||||
playsound(owner.loc, 'sound/effects/metalhit.ogg', 75, 1)
|
||||
return FALSE
|
||||
|
||||
toggled = !toggled
|
||||
owner.hatch_closed = toggled
|
||||
playsound(owner.loc, owner.hatch_closed ? 'sound/effects/metal_close.ogg' : 'sound/effects/air_seal.ogg', 75, 1)
|
||||
if(notify_user)
|
||||
notify_user(usr, SPAN_NOTICE("The [owner.body.hatch_descriptor] is now [owner.hatch_closed ? "closed" : "open" ]."))
|
||||
update_icon()
|
||||
owner.update_icon()
|
||||
return TRUE
|
||||
|
||||
// This is basically just a holder for the updates the mech does.
|
||||
/atom/movable/screen/mecha/health
|
||||
|
||||
@@ -43,24 +43,29 @@
|
||||
setClickCooldown(5)
|
||||
return
|
||||
|
||||
if(!(user in pilots) && user != src)
|
||||
return
|
||||
|
||||
// Are we facing the target?
|
||||
if(!(get_dir(src, A) & dir))
|
||||
return
|
||||
|
||||
if(!canClick())
|
||||
if(/* Do we have a pilot? */\
|
||||
!(user in pilots) && user != src \
|
||||
/* Are we facing the target? */\
|
||||
|| !(get_dir(src, A) & dir) \
|
||||
/* Can we click on them? */\
|
||||
|| !canClick())
|
||||
return
|
||||
|
||||
// Check if the arms were completely destroyed.
|
||||
if(!arms)
|
||||
to_chat(user, SPAN_WARNING("\The [src] has no manipulators!"))
|
||||
setClickCooldown(3)
|
||||
// Give some visible feedback for the mech damage preventing this.
|
||||
spark(src, 2, GLOB.alldirs)
|
||||
return
|
||||
|
||||
if(!arms.motivator || !arms.motivator.is_functional())
|
||||
var/obj/item/robot_parts/robot_component/actuator/motivator = arms.motivator
|
||||
// Check if the motivators were broken.
|
||||
if(!motivator || !motivator.is_functional())
|
||||
to_chat(user, SPAN_WARNING("Your motivators are damaged! You can't use your manipulators!"))
|
||||
setClickCooldown(15)
|
||||
// Give some visible feedback for the mech damage preventing this.
|
||||
spark(src, 2, GLOB.alldirs)
|
||||
return
|
||||
|
||||
if(!checked_use_cell(arms.power_use * CELLRATE))
|
||||
@@ -74,10 +79,18 @@
|
||||
else
|
||||
zone_sel.set_selected_zone("chest", user)
|
||||
|
||||
// Get the ratio of damage over max damage
|
||||
var/spark_chance = (motivator.total_dam / motivator.max_dam + arms.total_damage / arms.max_damage) \
|
||||
/* Multiply by the spark chance */\
|
||||
* arms.spark_chance_ratio \
|
||||
/* Then add the contribution from EMP damage. */\
|
||||
+ emp_damage
|
||||
|
||||
// You may attack the target with your exosuit FIST if you're malfunctioning.
|
||||
var/failed = FALSE
|
||||
if(emp_damage > EMP_ATTACK_DISRUPT && prob(emp_damage*2))
|
||||
to_chat(user, SPAN_WARNING("The wiring sparks as you attempt to control the exosuit!"))
|
||||
if(prob(spark_chance))
|
||||
to_chat(user, SPAN_WARNING("Your exosuit's manipulators spark as you attempt to control them!"))
|
||||
spark(src, 3, GLOB.alldirs)
|
||||
failed = TRUE
|
||||
|
||||
if(!failed)
|
||||
@@ -146,7 +159,7 @@
|
||||
D.TryToSwitchState(user)
|
||||
return
|
||||
else if(adj)
|
||||
setClickCooldown(arms ? arms.action_delay : 15)
|
||||
setClickCooldown(arms.action_delay)
|
||||
playsound(src.loc, arms.punch_sound, 45 + 25 * (arms.melee_damage / 50), -1 )
|
||||
if(ismob(A))
|
||||
var/mob/target = A
|
||||
@@ -246,7 +259,8 @@
|
||||
if(hatch_locked)
|
||||
if(!silent) to_chat(user, SPAN_WARNING("The [body.hatch_descriptor] is locked."))
|
||||
return
|
||||
hud_open.toggled(FALSE)
|
||||
if(!hud_open.toggled(FALSE))
|
||||
return
|
||||
if(!silent)
|
||||
to_chat(user, SPAN_NOTICE("You open the hatch and climb out of \the [src]."))
|
||||
else
|
||||
@@ -362,7 +376,7 @@
|
||||
|
||||
/mob/living/heavy_vehicle/Move(atom/newloc, direct, glide_size_override = 0, update_dir = TRUE)
|
||||
// They shouldn't get to this proc without legs in the first place, but its okay to guard here.
|
||||
if (!legs)
|
||||
if (!legs || !legs.motivator)
|
||||
return
|
||||
. = ..()
|
||||
set_glide_size(DELAY_TO_GLIDE_SIZE(next_mecha_move - world.time))
|
||||
@@ -372,6 +386,21 @@
|
||||
use_cell_power(legs.power_use * CELLRATE)
|
||||
update_icon()
|
||||
|
||||
// Handling for leg damage feedback on movement.
|
||||
var/obj/item/robot_parts/robot_component/actuator/motivator = legs.motivator
|
||||
|
||||
// Get the ratio of damage over max damage.
|
||||
var/spark_chance = (motivator.total_dam / motivator.max_dam + legs.total_damage / legs.max_damage) \
|
||||
/* Then multiply by the spark chance. */\
|
||||
* legs.spark_chance_ratio \
|
||||
/* And finally add the contribution from EMP damage. */\
|
||||
+ emp_damage
|
||||
|
||||
if(prob(spark_chance))
|
||||
for (var/mob/pilot in pilots)
|
||||
to_chat(pilot, SPAN_WARNING("Your exosuit's legs spark as you attempt to control them!"))
|
||||
spark(src, 3, GLOB.alldirs)
|
||||
|
||||
/mob/living/heavy_vehicle/Post_Incorpmove()
|
||||
if(istype(hardpoints[HARDPOINT_BACK], /obj/item/mecha_equipment/phazon))
|
||||
var/obj/item/mecha_equipment/phazon/PZ = hardpoints[HARDPOINT_BACK]
|
||||
@@ -537,28 +566,41 @@
|
||||
return ..()
|
||||
|
||||
/mob/living/heavy_vehicle/attack_hand(var/mob/user)
|
||||
// Drag the pilot out if possible.
|
||||
if(user.a_intent == I_GRAB)
|
||||
if(!LAZYLEN(pilots))
|
||||
to_chat(user, SPAN_WARNING("There is nobody inside \the [src]."))
|
||||
else if(!hatch_closed)
|
||||
// Try to attack on harm intent.
|
||||
if(user.a_intent == I_HURT)
|
||||
attack_generic(user)
|
||||
return
|
||||
|
||||
// Otherwise try to toggle the hatch.
|
||||
if(!body) // Edge case for the mech SOMEHOW not having a body. Players shouldn't ever see this. A mech without a body should be in the process of exploding.
|
||||
to_chat(user, SPAN_WARNING("What hatch? There's nothing left to open. If you're seeing this, please submit a bug report to our github issue tracker and tell us how!"))
|
||||
return
|
||||
|
||||
if(hatch_locked)
|
||||
to_chat(user, SPAN_WARNING("The [body.hatch_descriptor] is locked."))
|
||||
return
|
||||
|
||||
if(!hatch_closed)
|
||||
// Drag the pilot out if possible.
|
||||
if(user.a_intent == I_GRAB)
|
||||
if(!LAZYLEN(pilots))
|
||||
to_chat(user, SPAN_WARNING("There is nobody inside \the [src]."))
|
||||
return
|
||||
|
||||
var/mob/pilot = pick(pilots)
|
||||
user.visible_message(SPAN_DANGER("\The [user] is trying to pull \the [pilot] out of \the [src]!"))
|
||||
if(do_after(user, 30) && user.Adjacent(src) && (pilot in pilots) && !hatch_closed)
|
||||
user.visible_message(SPAN_DANGER("\The [user] drags \the [pilot] out of \the [src]!"))
|
||||
eject(pilot, silent=1)
|
||||
|
||||
return
|
||||
else if (body.total_damage > 1 && prob((body.total_damage / body.max_damage) * body.ejection_fail_chance))
|
||||
to_chat(user, SPAN_WARNING("You pull the hatch release. The hatch bolts hiss and clang with a deafening screech, but the hatch fails to open!"))
|
||||
spark(src, 6, GLOB.alldirs)
|
||||
playsound(loc, 'sound/effects/metalhit.ogg', 75, 1)
|
||||
return
|
||||
|
||||
if(user.a_intent == I_HURT)
|
||||
attack_generic(user)
|
||||
return
|
||||
|
||||
// Otherwise toggle the hatch.
|
||||
if(hatch_locked)
|
||||
to_chat(user, SPAN_WARNING("The [body.hatch_descriptor] is locked."))
|
||||
return
|
||||
hatch_closed = !hatch_closed
|
||||
playsound(loc, hatch_closed ? 'sound/effects/metal_close.ogg' : 'sound/effects/air_seal.ogg', 75, 1)
|
||||
to_chat(user, SPAN_NOTICE("You [hatch_closed ? "close" : "open"] the [body.hatch_descriptor]."))
|
||||
hud_open.update_icon()
|
||||
update_icon()
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
icon_state = "combat_arms"
|
||||
melee_damage = 30
|
||||
action_delay = 5
|
||||
max_damage = 130
|
||||
max_damage = 195
|
||||
power_use = 2500
|
||||
has_hardpoints = list(HARDPOINT_LEFT_SHOULDER, HARDPOINT_RIGHT_SHOULDER)
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
icon_state = "cult_arms"
|
||||
melee_damage = 50
|
||||
action_delay = 5
|
||||
max_damage = 90
|
||||
max_damage = 135
|
||||
power_use = 3500
|
||||
punch_sound = 'sound/mecha/mech_punch_slow.ogg'
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
desc = "Designed to function where any other piece of equipment would have long fallen apart, the Hephaestus Superheavy Lifter series can take a beating and excel at delivering it."
|
||||
melee_damage = 50
|
||||
action_delay = 15
|
||||
max_damage = 200
|
||||
max_damage = 300
|
||||
power_use = 3500
|
||||
punch_sound = 'sound/mecha/mech_punch_slow.ogg'
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
icon_state = "light_arms"
|
||||
melee_damage = 15
|
||||
action_delay = 15
|
||||
max_damage = 100
|
||||
max_damage = 150
|
||||
power_use = 1500
|
||||
desc = "As flexible as they are fragile, these Bishop Cybernetics manipulators can follow a pilot's movements in close to real time."
|
||||
punch_sound = 'sound/mecha/mech_punch_fast.ogg'
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
desc = "These basic, heavy plasteel mounts are used for mounting only the meanest weapons. Probably good at breaking things, too."
|
||||
melee_damage = 50
|
||||
action_delay = 15
|
||||
max_damage = 4000
|
||||
max_damage = 900
|
||||
power_use = 3500
|
||||
has_hardpoints = list(HARDPOINT_LEFT_SHOULDER, HARDPOINT_RIGHT_SHOULDER)
|
||||
punch_sound = 'sound/mecha/mech_punch_slow.ogg'
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
/obj/item/mech_component/manipulators/ripley
|
||||
name = "exosuit arms"
|
||||
exosuit_desc_string = "heavy-duty industrial lifters"
|
||||
max_damage = 150
|
||||
max_damage = 225
|
||||
power_use = 1000
|
||||
melee_damage = 40
|
||||
desc = "The Xion Manufacturing Group Digital Interaction Manifolds allow you poke untold dangers from the relative safety of your cockpit."
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
icon_state = "egg_arms"
|
||||
melee_damage = 15
|
||||
action_delay = 5
|
||||
max_damage = 100
|
||||
max_damage = 150
|
||||
power_use = 2500
|
||||
has_hardpoints = list(HARDPOINT_RIGHT_HAND, HARDPOINT_LEFT_HAND)
|
||||
|
||||
@@ -81,7 +81,7 @@
|
||||
desc = "Armored robotics arms designed to carry large weapons."
|
||||
icon_state = "strong_egg_arms"
|
||||
melee_damage = 20
|
||||
max_damage = 120
|
||||
max_damage = 180
|
||||
|
||||
/obj/item/mech_component/sensors/pra_egg/armored
|
||||
name = "armored P'kus-3 sensors"
|
||||
|
||||
Reference in New Issue
Block a user