Skip to content

Commit

Permalink
Rework the mech shield generator to have an internal capacitor, refac…
Browse files Browse the repository at this point in the history
…tor shield absorption code (#8469)

* Refactor mech shield code, add capacitor mechanic

* Code improvements
  • Loading branch information
XElectricX authored Apr 26, 2024
1 parent 7fefac9 commit 400b2a5
Show file tree
Hide file tree
Showing 4 changed files with 117 additions and 73 deletions.
2 changes: 2 additions & 0 deletions code/modules/mechs/_mech_defines.dm
Original file line number Diff line number Diff line change
Expand Up @@ -67,3 +67,5 @@ GLOBAL_LIST_INIT(mech_weapon_overlays, icon_states(MECH_WEAPON_OVERLAYS_ICON))
#define EQUIPFLAG_UPDTMOVE 1
/// It will have pretick() called on it before the mech checks wheter or not is powered
#define EQUIPFLAG_PRETICK 2
///Any module with this flag will have their Process() called via exosuit/Life()
#define EQUIPFLAG_PROCESS 4
166 changes: 99 additions & 67 deletions code/modules/mechs/equipment/combat.dm
Original file line number Diff line number Diff line change
Expand Up @@ -811,10 +811,17 @@
icon_state = "mech_atmoshield"
restricted_hardpoints = list(HARDPOINT_BACK)
origin_tech = list(TECH_MATERIAL = 3, TECH_ENGINEERING = 6, TECH_PLASMA = 5)
// so it has update icon called everytime it moves
equipment_flags = EQUIPFLAG_UPDTMOVE
/// Defines the amount of power drained per hit thats blocked
var/damage_to_power_drain = 30
// so it has update icon called everytime it moves; and run Process() too
equipment_flags = EQUIPFLAG_UPDTMOVE|EQUIPFLAG_PROCESS
active_power_use = 0
///How much power the shield uses every time Process() is called; using active_power_use would result in some double dipping
var/power_cost = 10
///The max amount of charge the capacitor can hold
var/max_capacitor_charge = 500
///Internal charge of the shield
var/current_capacitor_charge = 0
///What portion of damage is absorbed by the shield; should be a number from 1 to 0, where 1 is 100% absorption and 0 is 0% absorption
var/absorption_ratio = 1
/// Are we toggled on ?
var/on = FALSE
/// last time we toggled ,. stores world.time
Expand All @@ -830,6 +837,7 @@
visual_bluff.icon_state = "shield_null"
visual_bluff.vis_flags = VIS_INHERIT_DIR | VIS_INHERIT_ID | VIS_INHERIT_PLANE
visual_bluff.layer = ABOVE_ALL_MOB_LAYER
current_capacitor_charge = max_capacitor_charge

/obj/item/mech_equipment/shield_generator/Destroy()
. = ..()
Expand All @@ -839,28 +847,36 @@
QDEL_NULL(visual_bluff)

/obj/item/mech_equipment/shield_generator/uninstalled()
owner.vis_contents.Remove(visual_bluff)
maptext = null
if(on)
on = FALSE
update_icon()
toggle_shield()
owner.vis_contents.Remove(visual_bluff)
. = ..()

/obj/item/mech_equipment/shield_generator/deactivate()
. = ..()
if(!on)
return

power_failure()

/obj/item/mech_equipment/shield_generator/attack_self(mob/user)
. = ..()
if(.)
on = !on
to_chat(user, "You toggle \the [src] [on ? "on" : "off"].")
last_toggle = world.time
update_icon()
if(on)
playsound(src,'sound/mechs/shield_raise.ogg', 50, 1)
else
playsound(src,'sound/mechs/shield_drop.ogg', 50, 1)
toggle_shield(user)

///Toggle the shield between on and off
/obj/item/mech_equipment/shield_generator/proc/toggle_shield(mob/user)
on = !on
last_toggle = world.time
update_icon()
playsound(get_turf(src), on ? 'sound/mechs/shield_raise.ogg' : 'sound/mechs/shield_drop.ogg', 50, 3)
active = on

// Used to tell how effective we are against damage,
/obj/item/mech_equipment/shield_generator/proc/getEffectiveness()
return on
///Proc that plays an alarm and then toggle the shield
/obj/item/mech_equipment/shield_generator/proc/power_failure()
playsound(get_turf(src), 'sound/mechs/internaldmgalarm.ogg', 50, 3)
toggle_shield()

/obj/item/mech_equipment/shield_generator/update_icon(skip)
. = ..()
Expand All @@ -884,37 +900,53 @@
else
flick("shield_drop", visual_bluff)

/obj/item/mech_equipment/shield_generator/proc/absorbDamages(list/damages)
var/mob/living/exosuit/mech = loc
var/obj/item/cell/power = mech.get_cell()
if(!on)
return damages
if(!power || (power && power.charge <= damage_to_power_drain * 3))
last_toggle = world.time
on = FALSE
update_icon()
return damages
/obj/item/mech_equipment/shield_generator/proc/absorbDamages(damage)
if(!on || !damage) //Don't bother if the damage is 0
return damage

if(!current_capacitor_charge) //If somehow the shield is on and capacitor is empty, just turn it off
power_failure()
return damage

flick("shield_impact", visual_bluff)
for(var/damage in damages)
while(power.charge >= damage_to_power_drain && damages[damage] > 0)
damages[damage] -= 1
power.use(damage_to_power_drain)
// if it blows
if(QDELETED(power))
last_toggle = world.time
on = FALSE
playsound(get_turf(src),'sound/mechs/internaldmgalarm.ogg', 50, 1)
update_icon()
return damages

return damages

//Absorb as much as the capacitor can and only what the shield can absorb
var/damage_absorbed = (damage * absorption_ratio >= current_capacitor_charge * absorption_ratio ? current_capacitor_charge : damage) * absorption_ratio
damage -= damage_absorbed
current_capacitor_charge -= damage_absorbed
if(!current_capacitor_charge) //Turn it off if the capacitor is empty
power_failure()

return damage

/obj/item/mech_equipment/shield_generator/Process(delta_time)
//Capactor loses power just maintaining the shield
if(on)
current_capacitor_charge -= power_cost
if(current_capacitor_charge <= 0)
power_failure()

var/obj/item/cell/cell = owner.get_cell()
if(!cell?.charge) //No battery or no charge
maptext = "<span class='maptext' style=text-align:center>[!current_capacitor_charge ? "0" : (current_capacitor_charge / max_capacitor_charge) * 100]%"
return

//Transfer in increments of power_cost or the remaining charge
var/transfer_amount = min(power_cost, max_capacitor_charge - current_capacitor_charge, cell.charge)
cell.use(transfer_amount)
current_capacitor_charge += transfer_amount
maptext = "<span class='maptext' style=text-align:center>[!current_capacitor_charge ? "0" : (current_capacitor_charge / max_capacitor_charge) * 100]%"

/obj/item/mech_equipment/shield_generator/ballistic
name = "ballistic mech shield"
desc = "A large, bulky shield meant to protect hunkering mechs."
icon_state = "mech_shield"
equipment_flags = EQUIPFLAG_UPDTMOVE
restricted_hardpoints = list(HARDPOINT_LEFT_HAND, HARDPOINT_RIGHT_HAND)
origin_tech = list(TECH_MATERIAL = 5, TECH_ENGINEERING = 3)
absorption_ratio = 0.66 //66% of damage is absorbed
///Boolean to know if the shield is currently being deployed or retracted
var/performing_action = FALSE

/obj/item/mech_equipment/shield_generator/ballistic/Initialize()
. = ..()
Expand All @@ -929,22 +961,12 @@
visual_bluff.icon_state = "mech_shield_[hardpoint]"
update_icon()

/obj/item/mech_equipment/shield_generator/ballistic/uninstalled()
owner.vis_contents.Remove(visual_bluff)
. = ..()

// 66% efficient when deployed
/obj/item/mech_equipment/shield_generator/ballistic/getEffectiveness()
return on ? 0.66 : 0
/obj/item/mech_equipment/shield_generator/ballistic/absorbDamages(damage)
if(!on || !damage)
return damage

/obj/item/mech_equipment/shield_generator/ballistic/absorbDamages(list/damages)
if(!on)
return damages
for(var/damage in damages)
/// blocks 66% of damage
damages[damage] -= round(damages[damage]/1.5)
playsound(get_turf(src), 'sound/weapons/shield/shieldblock.ogg', 50, 8)
return damages
return damage * absorption_ratio

/obj/item/mech_equipment/shield_generator/ballistic/update_icon()
/// Not needed since we already have handling for visual bluffs layering
Expand Down Expand Up @@ -981,18 +1003,25 @@
visual_bluff.layer = MECH_ABOVE_LAYER
return

/obj/item/mech_equipment/shield_generator/ballistic/attack_self(mob/user)
var/mob/living/exosuit/mech = loc
if(!istype(mech))
/obj/item/mech_equipment/shield_generator/ballistic/toggle_shield(mob/user)
if(canremove || performing_action) //From what I gather, canremove is only TRUE when not installed
return
to_chat(user , SPAN_NOTICE("[on ? "Retracting" : "Deploying"] \the [src]..."))
var/time = on ? 0.5 SECONDS : 3 SECONDS
if(do_after(user, time, src, FALSE))
on = !on
to_chat(user, "You [on ? "deploy" : "retract"] \the [src].")
mech.visible_message(SPAN_DANGER("\The [mech] [on ? "deploys" : "retracts"] \the [src]!"), "", "You hear the sound of a heavy metal plate hitting the floor!", 8)
playsound(get_turf(src), 'sound/weapons/shield/shieldblock.ogg', 300, 8)
/// movement blocking is handled in MoveBlock()

performing_action = TRUE
//Check if there is a user because in the event this is called when being uninstalled, just skip the do_after and message
if(user)
if(!do_after(user, 0.5 SECONDS, owner, FALSE))
performing_action = FALSE
return
owner.visible_message(SPAN_DANGER("\The [owner] [on ? "deploys" : "retracts"] \the [src]!"), "", "You hear the sound of a heavy metal plate hitting the floor!", 8)

on = !on
playsound(get_turf(src), 'sound/weapons/shield/shieldblock.ogg', 300, 8)
/// movement blocking is handled in MoveBlock()

performing_action = FALSE
update_icon()
active = on

/// Pass all attack attempts to afterattack if we're installed
/obj/item/mech_equipment/shield_generator/ballistic/resolve_attackby(atom/A, mob/user, params)
Expand Down Expand Up @@ -1030,7 +1059,10 @@
knockable.damage_through_armor(20, BRUTE, BP_CHEST, ARMOR_MELEE, 2, src, FALSE, FALSE, 1)

if(length(targets))
playsound(get_turf(src), 'sound/effects/shieldbash.ogg', 100, 1)
playsound(get_turf(src), 'sound/effects/shieldbash.ogg', 100, 3)

/obj/item/mech_equipment/shield_generator/ballistic/Process(delta_time)
STOP_PROCESSING(SSobj, src) //Ballistic shield doesn't need to process anything

/obj/item/mech_equipment/mounted_system/baton
name = "\improper IHS \"Compliance\" baton "
Expand Down
12 changes: 6 additions & 6 deletions code/modules/mechs/mech_damage.dm
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
if(user.dir & reverse_dir[dir])
var/obj/item/mech_equipment/shield_generator/gen = getShield()
if(gen)
damages = gen.absorbDamages(damages)
damages[BRUTE] = gen.absorbDamages(damages[BRUTE])
if(damages[BRUTE] == 0)
return
var/list/picks = list()
Expand Down Expand Up @@ -77,7 +77,7 @@
var/obj/item/mech_equipment/thing = hardpoints[hardpoint]
if(istype(thing , /obj/item/mech_equipment/shield_generator))
var/obj/item/mech_equipment/shield_generator/gen = thing
if(!chosen || (chosen && (chosen.getEffectiveness() < gen.getEffectiveness())))
if(!chosen || (chosen && (chosen.absorption_ratio < gen.absorption_ratio)))
chosen = gen
return chosen

Expand All @@ -90,7 +90,7 @@
var/list/damages = list(BRUTE = I.force)
var/obj/item/mech_equipment/shield_generator/gen = getShield()
if(gen)
damages = gen.absorbDamages(damages)
damages[BRUTE] = gen.absorbDamages(damages[BRUTE])
// not enough made it in
if(damages[BRUTE] < round(I.force / 2))
visible_message("\The [src]'s shields block the blow!", 1, 2 ,5)
Expand Down Expand Up @@ -253,9 +253,9 @@
IgniteMob()
var/obj/item/mech_equipment/shield_generator/gen = getShield()
var/list/damages = P.damage_types
if(hit_dir & reverse_dir[dir])
if(gen)
damages = gen.absorbDamages(damages)
if(gen && hit_dir & reverse_dir[dir])
for(var/damage in damages) //Loop once just to get the key from the list
damages[damage] = gen.absorbDamages(damages[damage])
var/coverage = getPilotCoverage(hit_dir)
if(def_zone == body)
// enforce frontal attacks for first case. Second case just enforce a prob check on coverage.
Expand Down
10 changes: 10 additions & 0 deletions code/modules/mechs/mech_life.dm
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,16 @@
var/obj/item/mech_equipment/M = hardpoints[hardpoint]
if(istype(M) && M.active && M.passive_power_use)
M.deactivate()
else
//Loop through modules to process them if they're flagged to do so
for(var/hardpoint in hardpoints)
if(!hardpoints[hardpoint])
continue

var/obj/item/mech_equipment/module = hardpoints[hardpoint]
if(module.equipment_flags & EQUIPFLAG_PROCESS)
module.Process()

// for chassis charging cells
var/chargeUsed = 0
if(powered && body && body.cell_charge_rate && mech_cell.charge > 1000)
Expand Down

0 comments on commit 400b2a5

Please sign in to comment.