diff --git a/code/game/objects/auras/auras.dm b/code/game/objects/auras/auras.dm index 2085b5e12af..6a4c49698a7 100644 --- a/code/game/objects/auras/auras.dm +++ b/code/game/objects/auras/auras.dm @@ -15,6 +15,15 @@ They should also be used for when you want to effect the ENTIRE mob, like having return COMPONENT_BULLET_BLOCKED +/obj/aura/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit) + var/result = ..() + if(result == BULLET_ACT_BLOCK) //The bullet was blocked, don't check the remaining auras. + return AURA_FALSE|AURA_CANCEL + else if(result == BULLET_ACT_FORCE_PIERCE) //The bullet was forced to pierce, check the remaining auras to see if they block. + return AURA_FALSE + else + return + /obj/aura/Destroy() if(user) user.remove_aura(src) diff --git a/code/modules/heavy_vehicle/components/armor.dm b/code/modules/heavy_vehicle/components/armor.dm index 8f1a64c3256..91e5a27b094 100644 --- a/code/modules/heavy_vehicle/components/armor.dm +++ b/code/modules/heavy_vehicle/components/armor.dm @@ -11,6 +11,7 @@ RAD = ARMOR_RAD_MINOR ) origin_tech = list(TECH_MATERIAL = 1) + var/emp_protection = 0 /obj/item/robot_parts/robot_component/armor/mech/Initialize() . = ..() @@ -33,8 +34,8 @@ origin_tech = list(TECH_MATERIAL = 3) /obj/item/robot_parts/robot_component/armor/mech/em - name = "EM-shielded armor plating" - desc = "A shielded plating that sorrounds the eletronics and protects them from electromagnetic radiation." + name = "anti EM reactive armor plating" + desc = "An advanced active-defense system that insulates the eletronics and destructively interferes with electromagnetic radiation." icon_state = "armor_e" icon_state_broken = "armor_e_broken" armor = list( @@ -47,6 +48,7 @@ RAD = ARMOR_RAD_SMALL ) origin_tech = list(TECH_MATERIAL = 3) + emp_protection = 1 /obj/item/robot_parts/robot_component/armor/mech/combat name = "heavy combat plating" diff --git a/code/modules/heavy_vehicle/components/body.dm b/code/modules/heavy_vehicle/components/body.dm index 38ec7742978..ec8d2ea0430 100644 --- a/code/modules/heavy_vehicle/components/body.dm +++ b/code/modules/heavy_vehicle/components/body.dm @@ -19,7 +19,7 @@ var/obj/item/robot_parts/robot_component/diagnosis_unit/diagnostics var/obj/item/cell/mecha/cell var/cell_type = /obj/item/cell/mecha - var/obj/item/robot_parts/robot_component/armor/mech_armor + var/obj/item/robot_parts/robot_component/armor/mech/mech_armor var/obj/structure/machinery/portable_atmospherics/canister/air_supply var/datum/gas_mixture/cockpit var/pilot_offset_x = 0 diff --git a/code/modules/heavy_vehicle/equipment/combat.dm b/code/modules/heavy_vehicle/equipment/combat.dm index 931219f1c8c..09376c49205 100644 --- a/code/modules/heavy_vehicle/equipment/combat.dm +++ b/code/modules/heavy_vehicle/equipment/combat.dm @@ -296,11 +296,12 @@ desc = "The Hephaestus Armature system is a well liked energy deflector system designed to stop any projectile before it has a chance to become a threat." icon_state = "shield_droid" var/obj/aura/mechshield/aura - var/max_charge = 150 - var/charge = 150 + var/max_charge = 100 + var/charge = 100 var/last_recharge = 0 var/charging_rate = 7500 * CELLRATE var/cooldown = 3.5 SECONDS // Time until we can recharge again after a blocked impact + var/efficiency = 0.1 // How much power is required to recharge 1 point of shield charge restricted_hardpoints = list(HARDPOINT_BACK) restricted_software = list(MECH_SOFTWARE_WEAPONS) module_hints = list( @@ -331,7 +332,7 @@ last_recharge = world.time - if(difference > 0) + if(difference >= 0) for(var/mob/pilot in owner.pilots) to_chat(pilot, FONT_LARGE(SPAN_WARNING("Warning: Deflector shield failure detected, shutting down."))) toggle() @@ -379,8 +380,10 @@ if((world.time - last_recharge) < cooldown) return - var/actual_required_power = clamp(max_charge - charge, 0, charging_rate) - owner.use_cell_power(actual_required_power) + var/actual_required_power = clamp(max_charge * efficiency - charge * efficiency, 0, charging_rate) + var/actual_power_used = owner.use_cell_power(actual_required_power) + if(actual_power_used > 0) + charge = clamp(charge + actual_power_used, 0, max_charge) /obj/item/mecha_equipment/shield/get_hardpoint_status_value() return charge / max_charge @@ -434,24 +437,51 @@ else icon_state = "shield_null" +/obj/aura/mechshield/handle_bullet_act(datum/source, obj/projectile/projectile) + if(!active || !shields || !shields.charge) + return FALSE + + var/incoming_attack_direction = projectile.starting ? get_dir(shields, projectile.starting) : null + if(!(dir & incoming_attack_direction)) + return FALSE + + var/damage = projectile.get_structure_damage() + if(!damage) + damage = projectile.damage + + projectile.damage = shields.stop_damage(damage) + user.visible_message(SPAN_WARNING("\The [shields.owner]'s shields flash and crackle.")) + flick("shield_impact", src) + playsound(user, 'sound/effects/basscannon.ogg', 35, TRUE) + //light up the night. + new /obj/effect/smoke/illumination(get_turf(src), 5, 4, 1, "#ffffff") + spark(get_turf(src), 5, GLOB.alldirs) + playsound(get_turf(src), SFX_SPARKS, 25, TRUE) + return COMPONENT_BULLET_BLOCKED + /obj/aura/mechshield/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit) - if(!active) - return + SHOULD_CALL_PARENT(FALSE) - . = ..() + if(!active || !shields || !shields.charge) + return FALSE - if(shields?.charge) - hitting_projectile.damage = shields.stop_damage(hitting_projectile.damage) - user.visible_message(SPAN_WARNING("\The [shields.owner]'s shields flash and crackle.")) - flick("shield_impact", src) - playsound(user, 'sound/effects/basscannon.ogg', 35, TRUE) - //light up the night. - new /obj/effect/smoke/illumination(get_turf(src), 5, 4, 1, "#ffffff") - if(hitting_projectile.damage <= 0) - return AURA_FALSE|AURA_CANCEL + var/incoming_attack_direction = hitting_projectile.starting ? get_dir(shields, hitting_projectile.starting) : null + if(!(dir & incoming_attack_direction)) + return FALSE - spark(get_turf(src), 5, GLOB.alldirs) - playsound(get_turf(src), SFX_SPARKS, 25, TRUE) + var/damage = hitting_projectile.get_structure_damage() + if(!damage) + damage = hitting_projectile.damage + + hitting_projectile.damage = shields.stop_damage(damage) + user.visible_message(SPAN_WARNING("\The [shields.owner]'s shields flash and crackle.")) + flick("shield_impact", src) + playsound(user, 'sound/effects/basscannon.ogg', 35, TRUE) + //light up the night. + new /obj/effect/smoke/illumination(get_turf(src), 5, 4, 1, "#ffffff") + spark(get_turf(src), 5, GLOB.alldirs) + playsound(get_turf(src), SFX_SPARKS, 25, TRUE) + return AURA_FALSE|AURA_CANCEL /obj/aura/mechshield/hitby(atom/movable/hitting_atom, skipcatch, hitpush, blocked, datum/thrownthing/throwingdatum) . = ..() diff --git a/code/modules/heavy_vehicle/mech_damage.dm b/code/modules/heavy_vehicle/mech_damage.dm index ae0bec5b19b..22cdc53ceb6 100644 --- a/code/modules/heavy_vehicle/mech_damage.dm +++ b/code/modules/heavy_vehicle/mech_damage.dm @@ -1,8 +1,35 @@ +///Returns TRUE if an incoming attack should target the pilot instead of the mech, FALSE otherwise. +/mob/living/heavy_vehicle/proc/should_target_pilot(atom/attack_source = null) + if(!body || !LAZYLEN(pilots)) //If there is no pilot, or the mech somehow doesn't have a body don't try hit the pilot. + return FALSE + + var/incoming_attack_direction = attack_source ? get_dir(src, attack_source) : null + + if (istype(hardpoints[HARDPOINT_BACK], /obj/item/mecha_equipment/shield)) //If the mech has a shield. + var/obj/item/mecha_equipment/shield/shield = hardpoints[HARDPOINT_BACK] + if (shield.active && shield.aura && shield.aura.active) //If the shield is active. + if (incoming_attack_direction && (dir & incoming_attack_direction)) //If the attack comes from the front and the shield is active, the pilot is protected. + return FALSE + + if(!prob(body.pilot_coverage)) //If the cockpit doesn't cover the pilot completely, attacks have a chance to hit the pilot instead of the mech. + return TRUE + + if(!hatch_closed) //Open hatches only expose the pilot to attacks from the front and a little bit from the sides. + if(incoming_attack_direction) + if (dir & incoming_attack_direction) + if(prob(80)) //80% chance to hit the pilot from the front if the hatch is open. If this is 100% turrets will shoot mechs with open hatches forever. + return TRUE + if ((turn(dir, 90) & incoming_attack_direction) || (turn(dir, -90) & incoming_attack_direction)) + if(prob(20)) //20% chance to hit the pilot from the sides if the hatch is open. + return TRUE + + return FALSE + /mob/living/heavy_vehicle/apply_effect(var/effect = 0,var/effecttype = STUN, var/blocked = 0) if(!effect || (blocked >= 100)) return 0 - if(LAZYLEN(pilots) && (!hatch_closed || !prob(body.pilot_coverage))) + if(should_target_pilot()) if(effect > 0 && effecttype == DAMAGE_RADIATION) var/mob/living/pilot = pick(pilots) return pilot.apply_effect(effect, effecttype, blocked) @@ -10,11 +37,18 @@ . = ..() /mob/living/heavy_vehicle/hitby(atom/movable/hitting_atom, skipcatch, hitpush, blocked, datum/thrownthing/throwingdatum) - if(LAZYLEN(pilots) && (!hatch_closed || !prob(body.pilot_coverage))) + if(should_target_pilot(hitting_atom)) var/mob/living/pilot = pick(pilots) return pilot.hitby(arglist(args)) . = ..() +/mob/living/heavy_vehicle/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit) + if(should_target_pilot(hitting_projectile?.starting)) + var/mob/living/pilot = pick(pilots) + if(pilot) + return pilot.bullet_act(hitting_projectile, def_zone, piercing_hit) + return ..() + /mob/living/heavy_vehicle/get_armors_by_zone(def_zone, damage_type, damage_flags) . = ..() if(body) @@ -82,6 +116,13 @@ return 0 var/target = zoneToComponent(def_zone) + + if(target == body && body.damage_state == MECH_COMPONENT_DAMAGE_DAMAGED_TOTAL) //If the cockpit is destroyed, subsequent damage is applied to the pilot, modified by the mech's armour. + if(body && LAZYLEN(pilots)) + var/mob/living/pilot = pick(pilots) + visible_message(SPAN_DANGER("\The [used_weapon] pierces the mangled cockpit of \the [src], striking the pilot inside!")) + pilot.apply_damage(damage, damagetype, def_zone, used_weapon, damage_flags, armor_pen, silent = FALSE) + //Only 2 types of damage concern mechs and vehicles switch(damagetype) if(DAMAGE_BRUTE) @@ -114,25 +155,26 @@ /mob/living/heavy_vehicle/emp_act(severity) . = ..() + if(. & EMP_PROTECT_SELF) + var/obj/item/cell/C = get_cell() + if(C.charge > 1000) //Don't completely run a mech out of power this way. + var/power_used = use_cell_power(1000 / severity) + var/percent_power_used = 0 + if(C && C.maxcharge) + percent_power_used = round((power_used / C.maxcharge) * 100) + visible_message(SPAN_NOTICE("\The [src]'s active EM defenses flash brightly, negating the EMP!")) + for(var/pilot in pilots) + if(ismob(pilot)) + to_chat(pilot, SPAN_NOTICE("Your mech reports that negating the EMP cost [(percent_power_used)]% charge.")) + else + for(var/pilot in pilots) + if(ismob(pilot)) + to_chat(pilot, SPAN_DANGER("Your mech's EMP countermeasures deactivate as power levels drop too low.")) + else + var/ratio = get_blocked_ratio(null, DAMAGE_BURN, null, (4-severity) * 20) + emp_damage += round((12 - (severity*3))*( 1 - ratio)) - var/ratio = get_blocked_ratio(null, DAMAGE_BURN, null, (4-severity) * 20) - - if(ratio >= 0.5) - for(var/mob/living/m in pilots) - to_chat(m, SPAN_NOTICE("Your Faraday shielding absorbed the pulse!")) - return - else if(ratio > 0) - for(var/mob/living/m in pilots) - to_chat(m, SPAN_NOTICE("Your Faraday shielding mitigated the pulse!")) - - emp_damage += round((12 - (severity*3))*( 1 - ratio)) - - for(var/obj/item/thing in list(arms,legs,head,body)) - thing.emp_act(severity) - if(!hatch_closed || !prob(body.pilot_coverage)) - for(var/thing in pilots) - var/mob/pilot = thing - pilot.emp_act(severity) + update_emp_protection() /mob/living/heavy_vehicle/fall_impact(levels_fallen, stopped_early = FALSE, var/damage_mod = 1) // No gravity, stop falling into spess! diff --git a/code/modules/heavy_vehicle/mech_helpers.dm b/code/modules/heavy_vehicle/mech_helpers.dm index 9a19ba24964..d45b4a65420 100644 --- a/code/modules/heavy_vehicle/mech_helpers.dm +++ b/code/modules/heavy_vehicle/mech_helpers.dm @@ -161,20 +161,32 @@ /mob/living/heavy_vehicle/proc/use_cell_power(var/power_to_use) var/power_used = get_cell()?.use(power_to_use) - if(power_used <= 0) + if(power_used < power_to_use - 1) //Self recharging cells would otherwise allow a mech to run indefinately at full functionality with 0.1% charge. for(var/hardpoint in hardpoints) var/obj/item/mecha_equipment/ME = hardpoints[hardpoint] if(ME) ME.deactivate() + if(power == MECH_POWER_ON) + playsound(src, 'sound/mecha/mech-shutdown.ogg', 100, 0) + power = MECH_POWER_OFF + update_emp_protection() + if(hud_power_control) + SSicon_update.add_to_queue(hud_power_control) return power_used /mob/living/heavy_vehicle/proc/drain_cell_power(var/power_to_drain) var/power_used = get_cell()?.drain_power(0, 0, power_to_drain) - if(power_used <= 0) + if(power_used < power_to_drain - 1) //Self recharging cells would otherwise allow a mech to run indefinately at full functionality with 0.1% charge. for(var/hardpoint in hardpoints) var/obj/item/mecha_equipment/ME = hardpoints[hardpoint] if(ME) ME.deactivate() + if(power == MECH_POWER_ON) + playsound(src, 'sound/mecha/mech-shutdown.ogg', 100, 0) + power = MECH_POWER_OFF + update_emp_protection() + if(hud_power_control) + SSicon_update.add_to_queue(hud_power_control) return power_used /mob/living/heavy_vehicle/proc/checked_use_cell(var/power_to_drain) diff --git a/code/modules/heavy_vehicle/mecha.dm b/code/modules/heavy_vehicle/mecha.dm index aaa75e5f4be..ba1d7a5b045 100644 --- a/code/modules/heavy_vehicle/mecha.dm +++ b/code/modules/heavy_vehicle/mecha.dm @@ -266,6 +266,12 @@ /mob/living/heavy_vehicle/return_air() return (body && body.pilot_coverage >= 100 && hatch_closed) ? body.cockpit : loc?.return_air() +/mob/living/heavy_vehicle/proc/update_emp_protection() + RemoveElement(/datum/element/empprotection, EMP_PROTECT_ALL) + if(power == MECH_POWER_ON && body.mech_armor && body.mech_armor.emp_protection) + if(get_cell().charge > 500) + AddElement(/datum/element/empprotection, EMP_PROTECT_ALL) + /mob/living/heavy_vehicle/GetIdCard() return access_card @@ -288,18 +294,22 @@ to_chat(reciever, SPAN_NOTICE("Power transition in progress. Please wait.")) else if(power == MECH_POWER_ON) //Turning it off is instant power = MECH_POWER_OFF - else if(get_cell(TRUE)) + update_emp_protection() + else if(get_cell(TRUE).check_charge(1000)) //Check if we have enough charge to power on //Start power up sequence power = MECH_POWER_TRANSITION playsound(src, 'sound/mecha/powerup.ogg', 50, 0) if(do_after(reciever, 1.5 SECONDS) && power == MECH_POWER_TRANSITION) power = MECH_POWER_ON + update_emp_protection() sound_looping = TRUE soundloop.start() else to_chat(reciever, SPAN_WARNING("You abort the powerup sequence.")) power = MECH_POWER_OFF - hud_power_control?.queue_icon_update() + update_emp_protection() + if(hud_power_control) + SSicon_update.add_to_queue(hud_power_control) else to_chat(reciever, SPAN_WARNING("Error: No power cell was detected.")) diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm index 86390aa97cb..a6f17c32188 100644 --- a/code/modules/mob/living/living_defense.dm +++ b/code/modules/mob/living/living_defense.dm @@ -56,8 +56,7 @@ //Auras, essentially magic and encompassing around us, gets checked first if(!src.aura_check(AURA_TYPE_BULLET, hitting_projectile, def_zone, piercing_hit)) blocked = 100 - return BULLET_ACT_FORCE_PIERCE - + return BULLET_ACT_BLOCK //Shields, in front of us (hopefully), check the bullet before it reaches us var/shield_check = check_shields(hitting_projectile.damage, hitting_projectile, hitting_projectile.firer, def_zone, "the [hitting_projectile.name]") diff --git a/html/changelogs/Fenodyree-MechBugfixes.yml b/html/changelogs/Fenodyree-MechBugfixes.yml new file mode 100644 index 00000000000..727e98bbe2d --- /dev/null +++ b/html/changelogs/Fenodyree-MechBugfixes.yml @@ -0,0 +1,10 @@ +author: Fenodyree +delete-after: True +changes: + - bugfix: "Fixes EMP armor for mechs." + - bugfix: "Fixes mech pilot's getting EMP'd twice for every EMP hitting the mech." + - bugfix: "Fixes shields for mechs." + - bugfix: "Fixes mechs being able to draw infinite power, as long as there was any power in their cell." + - bugfix: "Fixes cockpit coverage not letting the pilot be hit." + - bugfix: "Fixes legacy icon updates." + - rscadd: "Adds a chance for attacks to hit the mech's pilot after the cockpit has been destroyed. This still accounts for the mech's armor and shields."