Files
CHOMPStation2/code/modules/shieldgen/energy_shield.dm
2021-06-18 04:23:09 +00:00

358 lines
11 KiB
Plaintext

//
// This is the shield effect object for the supercool shield gens.
//
/obj/effect/shield
name = "energy shield"
desc = "An impenetrable field of energy, capable of blocking anything as long as it's active."
icon = 'icons/obj/machines/shielding_vr.dmi'
icon_state = "shield"
anchored = 1
plane = MOB_PLANE
layer = ABOVE_MOB_LAYER
density = 1
invisibility = 0
var/obj/machinery/power/shield_generator/gen = null // Owning generator
var/disabled_for = 0
var/diffused_for = 0
can_atmos_pass = ATMOS_PASS_YES
var/enabled_icon_state
/obj/effect/shield/proc/update_visuals()
update_iconstate()
update_color()
update_glow()
update_opacity()
/obj/effect/shield/proc/update_iconstate()
if(!enabled_icon_state)
enabled_icon_state = icon_state
if(disabled_for || diffused_for)
icon_state = "shield_broken"
overlays.Cut() // Snowflake handling, avoiding SSoverlays
else
icon_state = enabled_icon_state
flags |= OVERLAY_QUEUED //Trick SSoverlays
SSoverlays.queue += src
/obj/effect/shield/proc/update_color()
if(disabled_for || diffused_for)
color = "#FFA500"
else if(gen?.check_flag(MODEFLAG_OVERCHARGE))
color = "#FE6666"
else
color = "#00AAFF"
/obj/effect/shield/proc/update_glow()
if(density)
set_light(3, 3, "#66FFFF")
else
set_light(0)
/obj/effect/shield/proc/update_opacity()
if(gen?.check_flag(MODEFLAG_PHOTONIC) && !disabled_for && !diffused_for)
set_opacity(1)
else
set_opacity(0)
// Prevents singularities and pretty much everything else from moving the field segments away.
// The only thing that is allowed to move us is the Destroy() proc.
/obj/effect/shield/forceMove()
if(QDELING(src))
return ..()
return 0
/obj/effect/shield/Destroy()
if(can_atmos_pass != ATMOS_PASS_YES)
update_nearby_tiles() //Force ZAS update
. = ..()
if(gen)
if(src in gen.field_segments)
gen.field_segments -= src
if(src in gen.damaged_segments)
gen.damaged_segments -= src
gen = null
// Temporarily collapses this shield segment.
/obj/effect/shield/proc/fail(var/duration)
if(duration <= 0)
return
if(gen)
gen.damaged_segments |= src
disabled_for += duration
set_density(0)
update_visuals()
update_nearby_tiles() //Force ZAS update
update_explosion_resistance()
// Regenerates this shield segment.
/obj/effect/shield/proc/regenerate()
if(!gen)
return
disabled_for = max(0, disabled_for - 1)
diffused_for = max(0, diffused_for - 1)
if(!disabled_for && !diffused_for)
set_density(1)
update_visuals()
update_nearby_tiles() //Force ZAS update
update_explosion_resistance()
gen.damaged_segments -= src
/obj/effect/shield/proc/diffuse(var/duration)
// The shield is trying to counter diffusers. Cause lasting stress on the shield.
if(gen?.check_flag(MODEFLAG_BYPASS) && !disabled_for)
take_damage(duration * rand(8, 12), SHIELD_DAMTYPE_EM)
return
diffused_for = max(duration, 0)
gen?.damaged_segments |= src
set_density(0)
update_visuals()
update_nearby_tiles() //Force ZAS update
update_explosion_resistance()
/obj/effect/shield/attack_generic(var/source, var/damage, var/emote)
take_damage(damage, SHIELD_DAMTYPE_PHYSICAL)
if(gen.check_flag(MODEFLAG_OVERCHARGE) && istype(source, /mob/living/))
overcharge_shock(source)
..(source, damage, emote)
// Fails shield segments in specific range. Range of 1 affects the shielded turf only.
/obj/effect/shield/proc/fail_adjacent_segments(var/range, var/hitby = null)
if(hitby)
visible_message("<span class='danger'>\The [src] flashes a bit as \the [hitby] collides with it, eventually fading out in a rain of sparks!</span>")
else
visible_message("<span class='danger'>\The [src] flashes a bit as it eventually fades out in a rain of sparks!</span>")
fail(range * 2)
for(var/obj/effect/shield/S in range(range, src))
// Don't affect shields owned by other shield generators
if(S.gen != src.gen)
continue
// The closer we are to impact site, the longer it takes for shield to come back up.
S.fail(-(-range + get_dist(src, S)) * 2)
// Small visual effect, makes the shield tiles brighten up by becoming more opaque for a moment, and spreads to nearby shields.
/obj/effect/shield/proc/flash_adjacent_segments(var/range)
range = between(1, range, 10) // Sanity check
for(var/obj/effect/shield/S in range(range, src))
// Don't affect shields owned by other shield generators
if(S.gen != src.gen || S == src)
continue
// Note: Range is a non-exact aproximation of the spread effect. If it doesn't look good
// we'll need to switch to actually walking along the shields to get exact number of steps away.
addtimer(CALLBACK(S, .proc/impact_flash), get_dist(src, S) * 2)
impact_flash()
// Small visual effect, makes the shield tiles brighten up by becoming more opaque for a moment
/obj/effect/shield/proc/impact_flash()
alpha = 100
animate(src, alpha = initial(alpha), time = 1 SECOND)
// Just for fun
/obj/effect/shield/attack_hand(var/user)
flash_adjacent_segments(3)
/obj/effect/shield/take_damage(var/damage, var/damtype, var/hitby)
if(!gen)
qdel(src)
return
if(!damtype)
crash_with("CANARY: shield.take_damage() callled without damtype.")
if(!damage)
return
damage = round(damage)
new /obj/effect/temp_visual/shield_impact_effect(get_turf(src))
switch(gen.deal_shield_damage(damage, damtype))
if(SHIELD_ABSORBED)
flash_adjacent_segments(round(damage/10)) // Nice visual effect only.
return
if(SHIELD_BREACHED_MINOR)
fail_adjacent_segments(rand(1, 3), hitby)
return
if(SHIELD_BREACHED_MAJOR)
fail_adjacent_segments(rand(2, 5), hitby)
return
if(SHIELD_BREACHED_CRITICAL)
fail_adjacent_segments(rand(4, 8), hitby)
return
if(SHIELD_BREACHED_FAILURE)
fail_adjacent_segments(rand(8, 16), hitby)
return
// As we have various shield modes, this handles whether specific things can pass or not.
/obj/effect/shield/CanPass(var/atom/movable/mover, var/turf/target)
// Somehow we don't have a generator. This shouldn't happen. Delete the shield.
if(!gen)
qdel(src)
return 1
if(disabled_for || diffused_for)
return 1
if(mover)
return mover.can_pass_shield(gen)
return 1
/obj/effect/shield/proc/set_can_atmos_pass(var/new_value)
if(new_value == can_atmos_pass)
return
can_atmos_pass = new_value
update_nearby_tiles() //Force ZAS update
// EMP. It may seem weak but keep in mind that multiple shield segments are likely to be affected.
/obj/effect/shield/emp_act(var/severity)
if(!disabled_for)
take_damage(rand(30,60) / severity, SHIELD_DAMTYPE_EM)
// Explosions
/obj/effect/shield/ex_act(var/severity)
if(!disabled_for)
take_damage(rand(10,15) / severity, SHIELD_DAMTYPE_PHYSICAL)
// Fire
/obj/effect/shield/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume)
if(!disabled_for)
take_damage(rand(5,10), SHIELD_DAMTYPE_HEAT)
// Projectiles
/obj/effect/shield/bullet_act(var/obj/item/projectile/proj)
if(proj.damage_type == BURN)
take_damage(proj.get_structure_damage(), SHIELD_DAMTYPE_HEAT)
else if (proj.damage_type == BRUTE)
take_damage(proj.get_structure_damage(), SHIELD_DAMTYPE_PHYSICAL)
else //TODO - This will never happen because of get_structure_damage() only returning values for BRUTE and BURN damage types
take_damage(proj.get_structure_damage(), SHIELD_DAMTYPE_EM)
// Attacks with hand tools. Blocked by Hyperkinetic flag.
/obj/effect/shield/attackby(var/obj/item/weapon/I as obj, var/mob/user as mob)
user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
user.do_attack_animation(src)
if(gen.check_flag(MODEFLAG_HYPERKINETIC))
user.visible_message("<span class='danger'>\The [user] hits \the [src] with \the [I]!</span>")
if(I.damtype == BURN)
take_damage(I.force, SHIELD_DAMTYPE_HEAT)
else if (I.damtype == BRUTE)
take_damage(I.force, SHIELD_DAMTYPE_PHYSICAL)
else
take_damage(I.force, SHIELD_DAMTYPE_EM)
else
user.visible_message("<span class='danger'>\The [user] tries to attack \the [src] with \the [I], but it passes through!</span>")
// Special treatment for meteors because they would otherwise penetrate right through the shield.
/obj/effect/shield/Bumped(var/atom/movable/mover)
if(!gen)
qdel(src)
return 0
mover.shield_impact(src)
return ..()
// Meteors call this instad of Bumped for some reason
/obj/effect/shield/handle_meteor_impact(var/obj/effect/meteor/meteor)
meteor.shield_impact(src)
return !QDELETED(meteor) // If it was stopped it will have been deleted
/obj/effect/shield/proc/overcharge_shock(var/mob/living/M)
M.adjustFireLoss(rand(20, 40))
M.Weaken(5)
to_chat(M, "<span class='danger'>As you come into contact with \the [src] a surge of energy paralyses you!</span>")
take_damage(10, SHIELD_DAMTYPE_EM)
// Called when a flag is toggled. Can be used to add on-toggle behavior, such as visual changes.
/obj/effect/shield/proc/flags_updated()
if(!gen)
qdel(src)
return
// Update airflow - If atmospheric we block air as long as we're enabled (density works for this)
set_can_atmos_pass(gen.check_flag(MODEFLAG_ATMOSPHERIC) ? ATMOS_PASS_DENSITY : ATMOS_PASS_YES)
update_visuals()
update_explosion_resistance()
/obj/effect/shield/proc/update_explosion_resistance()
if(gen && gen.check_flag(MODEFLAG_HYPERKINETIC))
explosion_resistance = INFINITY
else
explosion_resistance = 0
//
// Visual effect of shield taking impact
//
/obj/effect/temp_visual/shield_impact_effect
name = "shield impact"
icon = 'icons/obj/machines/shielding_vr.dmi'
icon_state = "shield_impact"
plane = MOB_PLANE
layer = ABOVE_MOB_LAYER
duration = 2 SECONDS
randomdir = FALSE
//
// Shield collision checks below
//
// Called only if shield is active/not destroyed etc.
/atom/movable/proc/can_pass_shield(var/obj/machinery/power/shield_generator/gen)
return 1
// Other mobs
/mob/living/can_pass_shield(var/obj/machinery/power/shield_generator/gen)
return !gen.check_flag(MODEFLAG_NONHUMANS)
// Human mobs
/mob/living/carbon/human/can_pass_shield(var/obj/machinery/power/shield_generator/gen)
if(isSynthetic())
return !gen.check_flag(MODEFLAG_ANORGANIC)
return !gen.check_flag(MODEFLAG_HUMANOIDS)
// Silicon mobs
/mob/living/silicon/can_pass_shield(var/obj/machinery/power/shield_generator/gen)
return !gen.check_flag(MODEFLAG_ANORGANIC)
// Generic objects. Also applies to bullets and meteors.
/obj/can_pass_shield(var/obj/machinery/power/shield_generator/gen)
return !gen.check_flag(MODEFLAG_HYPERKINETIC)
// Beams
/obj/item/projectile/beam/can_pass_shield(var/obj/machinery/power/shield_generator/gen)
return !gen.check_flag(MODEFLAG_PHOTONIC)
// Shield on-impact logic here. This is called only if the object is actually blocked by the field (can_pass_shield applies first)
/atom/movable/proc/shield_impact(var/obj/effect/shield/S)
return
/mob/living/shield_impact(var/obj/effect/shield/S)
if(!S.gen.check_flag(MODEFLAG_OVERCHARGE))
return
S.overcharge_shock(src)
/obj/effect/meteor/shield_impact(var/obj/effect/shield/S)
if(!S.gen.check_flag(MODEFLAG_HYPERKINETIC))
return
S.take_damage(get_shield_damage(), SHIELD_DAMTYPE_PHYSICAL, src)
visible_message("<span class='danger'>\The [src] breaks into dust!</span>")
make_debris()
qdel(src)