Shielding Auras (#8760)

Ports Baystation12/Baystation12#19992 and Baystation12/Baystation12#27266.

    Added a personal shield device to the traitor tools uplink.
    Added a radiant shielding aura spell to Battlemage and Cleric.
    Added an exosuit shield drone to the mechfab.

This works, but I'm not entirely happy with the sprites. The personal shield has no on-mob shimmer, it uses a mindbatterer grenade as an icon sprite. The wizard radiant spell uses a pretty big and janky sprite as an on-mob, and the exosuit's shield sprite doesn't follow the dir the mech faces, but that one I can maybe fix on my own. If anyone knows about that last one, lemme know, Bay had code for it that we do not.
This commit is contained in:
Geeves
2020-05-15 22:37:27 +02:00
committed by GitHub
parent 3a4dc8e0f2
commit 097ea6cfbc
27 changed files with 439 additions and 128 deletions
@@ -287,3 +287,157 @@
if(istype(C))
return C.charge/C.maxcharge
return null
/obj/item/mecha_equipment/shield
name = "exosuit shield droid"
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/last_recharge = 0
var/charging_rate = 7500 * CELLRATE
var/cooldown = 3.5 SECONDS // Time until we can recharge again after a blocked impact
restricted_hardpoints = list(HARDPOINT_BACK)
restricted_software = list(MECH_SOFTWARE_WEAPONS)
/obj/item/mecha_equipment/shield/installed(mob/living/heavy_vehicle/_owner)
. = ..()
aura = new /obj/aura/mechshield(_owner)
aura.added_to(_owner)
aura.set_holder(src)
/obj/item/mecha_equipment/shield/uninstalled()
QDEL_NULL(aura)
. = ..()
/obj/item/mecha_equipment/shield/attack_self(mob/user)
. = ..()
if(.)
toggle()
/obj/item/mecha_equipment/shield/proc/stop_damage(var/damage)
var/difference = damage - charge
charge = Clamp(charge - damage, 0, max_charge)
last_recharge = world.time
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()
playsound(get_turf(owner),'sound/mecha/internaldmgalarm.ogg', 35, TRUE)
return difference
else
return FALSE
/obj/item/mecha_equipment/shield/proc/toggle()
if(!aura)
return
aura.toggle()
aura.dir = owner.dir
if(aura.dir == NORTH)
aura.layer = MECH_UNDER_LAYER
else
aura.layer = ABOVE_MOB_LAYER
playsound(owner,'sound/weapons/flash.ogg', 35, TRUE)
update_icon()
if(aura.active)
START_PROCESSING(SSprocessing, src)
else
STOP_PROCESSING(SSprocessing, src)
owner.update_icon()
/obj/item/mecha_equipment/shield/update_icon()
. = ..()
if(!aura)
return
if(aura.active)
icon_state = "shield_droid_a"
else
icon_state = "shield_droid"
/obj/item/mecha_equipment/shield/process()
if(charge >= max_charge)
return
if((world.time - last_recharge) < cooldown)
return
var/obj/item/cell/cell = owner.get_cell()
var/actual_required_power = Clamp(max_charge - charge, 0, charging_rate)
charge += cell.use(actual_required_power)
/obj/item/mecha_equipment/shield/get_hardpoint_status_value()
return charge / max_charge
/obj/item/mecha_equipment/shield/get_hardpoint_maptext()
return "[(aura && aura.active) ? "ONLINE" : "OFFLINE"]: [round((charge / max_charge) * 100)]%"
/obj/aura/mechshield
icon = 'icons/mecha/shield.dmi'
name = "mechshield"
var/obj/item/mecha_equipment/shield/shields
var/active = FALSE
layer = ABOVE_MOB_LAYER
pixel_x = 8
pixel_y = 4
mouse_opacity = 0
/obj/aura/mechshield/added_to(mob/living/target)
..()
target.vis_contents += src
dir = target.dir
/obj/aura/mechshield/proc/set_holder(var/obj/item/mecha_equipment/shield/holder)
shields = holder
/obj/aura/mechshield/Destroy()
if(user)
user.vis_contents -= src
shields = null
. = ..()
/obj/aura/mechshield/proc/toggle()
active = !active
update_icon()
if(active)
flick("shield_raise", src)
else
flick("shield_drop", src)
/obj/aura/mechshield/update_icon()
. = ..()
if(active)
icon_state = "shield"
else
icon_state = "shield_null"
/obj/aura/mechshield/bullet_act(obj/item/projectile/P, var/def_zone)
if(!active)
return
if(shields?.charge)
P.damage = shields.stop_damage(P.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/effect/smoke/illumination(get_turf(src), 5, 4, 1, "#ffffff")
if(P.damage <= 0)
return AURA_FALSE|AURA_CANCEL
spark(get_turf(src), 5, global.alldirs)
playsound(get_turf(src), "sparks", 25, TRUE)
/obj/aura/mechshield/hitby(atom/movable/M, var/speed)
. = ..()
if(!active)
return
if(shields.charge && speed <= 5)
user.visible_message(SPAN_WARNING("\The [shields.owner]'s shields flash briefly as they deflect \the [M]."))
flick("shield_impact", src)
playsound(user, 'sound/effects/basscannon.ogg', 10, TRUE)
return AURA_FALSE|AURA_CANCEL
//Too fast!
@@ -307,6 +307,14 @@
playsound(src.loc,legs.mech_turn_sound,40,1)
next_move = world.time + legs.turn_delay
set_dir(direction)
if(istype(hardpoints[HARDPOINT_BACK], /obj/item/mecha_equipment/shield))
var/obj/item/mecha_equipment/shield/S = hardpoints[HARDPOINT_BACK]
if(S.aura)
S.aura.dir = direction
if(S.aura.dir == NORTH)
S.aura.layer = MECH_UNDER_LAYER
else
S.aura.layer = ABOVE_MOB_LAYER
update_icon()
/mob/living/heavy_vehicle/Move()
-124
View File
@@ -1,124 +0,0 @@
/mob/living/exosuit
movement_handlers = list(
/datum/movement_handler/mob/space/exosuit,
/datum/movement_handler/mob/exosuit
)
/mob/living/exosuit/Move()
. = ..()
if(. && !istype(loc, /turf/space) && legs.mech_step_sound)
playsound(src.loc, mech_step_sound, 40, 1)
//Override this and space move once a way to travel vertically is in
///mob/living/exosuit/can_ztravel()
// if(Allow_Spacemove()) //Handle here
// return 1
// for(var/turf/simulated/T in trange(1,src))
// if(T.density)
// return 1
/mob/living/exosuit/can_fall(var/anchor_bypass = FALSE, var/turf/location_override = src.loc)
//mechs are always anchored, so falling should always ignore it
if(..(TRUE, location_override))
return !(can_overcome_gravity())
/datum/movement_handler/mob/exosuit
expected_host_type = /mob/living/exosuit
var/next_move
/datum/movement_handler/mob/exosuit/MayMove(var/mob/mover, var/is_external)
var/mob/living/exosuit/exosuit = host
if(world.time < next_move)
return MOVEMENT_STOP
if((!(mover in exosuit.pilots) && mover != exosuit) || exosuit.incapacitated() || mover.incapacitated())
return MOVEMENT_STOP
if(!exosuit.legs)
to_chat(mover, "<span class='warning'>\The [exosuit] has no means of propulsion!</span>")
next_move = world.time + 3 // Just to stop them from getting spammed with messages.
return MOVEMENT_STOP
if(!exosuit.legs.motivator || !exosuit.legs.motivator.is_functional())
to_chat(mover, "<span class='warning'>Your motivators are damaged! You can't move!</span>")
next_move = world.time + 15
return MOVEMENT_STOP
if(exosuit.maintenance_protocols)
to_chat(mover, "<span class='warning'>Maintenance protocols are in effect.</span>")
next_move = world.time + 3 // Just to stop them from getting spammed with messages.
return MOVEMENT_STOP
if(exosuit.lockdown)
to_chat(mover, span("warning", "You cannot move while the exosuit's lockdown mode is active."))
next_move = world.time + 3 // Just to stop them from getting spammed with messages.
return MOVEMENT_STOP
var/obj/item/cell/C = exosuit.get_cell()
if(!C || !C.check_charge(exosuit.legs.power_use * CELLRATE))
to_chat(mover, "<span class='warning'>The power indicator flashes briefly.</span>")
next_move = world.time + 3 //On fast exosuits this got annoying fast
return MOVEMENT_STOP
next_move = world.time + (exosuit.legs ? exosuit.legs.move_delay : 3)
return MOVEMENT_PROCEED
/datum/movement_handler/mob/exosuit/DoMove(var/direction, var/mob/mover, var/is_external)
var/mob/living/exosuit/exosuit = host
var/moving_dir = direction
var/failed = FALSE
if(exosuit.emp_damage >= EMP_MOVE_DISRUPT && prob(30))
failed = TRUE
if(failed)
moving_dir = pick(cardinal - exosuit.dir)
exosuit.get_cell().use(exosuit.legs.power_use * CELLRATE)
if(exosuit.dir != moving_dir)
playsound(exosuit.loc, exosuit.mech_turn_sound, 40,1)
exosuit.set_dir(moving_dir)
next_move = world.time + exosuit.legs.turn_delay
else
var/turf/target_loc = get_step(exosuit, direction)
if(target_loc && exosuit.legs && exosuit.legs.can_move_on(exosuit.loc, target_loc) && exosuit.MayEnterTurf(target_loc))
exosuit.Move(target_loc)
return MOVEMENT_HANDLED
/datum/movement_handler/mob/space/exosuit
expected_host_type = /mob/living/exosuit
// Space movement
/datum/movement_handler/mob/space/exosuit/DoMove(var/direction, var/mob/mover)
if(!mob.check_solid_ground())
mob.anchored = FALSE
var/allowmove = mob.Allow_Spacemove(0)
if(!allowmove)
return MOVEMENT_HANDLED
else if(allowmove == -1 && mob.handle_spaceslipping()) //Check to see if we slipped
return MOVEMENT_HANDLED
else
mob.inertia_dir = 0 //If not then we can reset inertia and move
else mob.anchored = TRUE
/datum/movement_handler/mob/space/exosuit/MayMove(var/mob/mover, var/is_external)
if((mover != host) && is_external)
return MOVEMENT_PROCEED
if(!mob.check_solid_ground())
if(!mob.Allow_Spacemove(0))
return MOVEMENT_STOP
return MOVEMENT_PROCEED
/mob/living/exosuit/lost_in_space()
for(var/atom/movable/AM in contents)
if(!AM.lost_in_space())
return FALSE
return !pilots.len
/mob/living/exosuit/fall_damage()
return 100 //Exosuits are big and heavy
/mob/living/exosuit/handle_fall_effect(var/turf/landing)
// Return here if for any reason you shouldnt take damage
..()
var/damage = 30 //Enough to cause a malfunction if unlucky
apply_damage(rand(0, damage), BRUTE, BP_R_LEG) //Any leg is good, will damage both
@@ -11,6 +11,7 @@
h_l_hand = /obj/item/mecha_equipment/mounted_system/taser/laser
h_r_hand = /obj/item/mecha_equipment/mounted_system/taser/ion
h_back = /obj/item/mecha_equipment/shield
/obj/item/mech_component/manipulators/heavy
name = "heavy arms"
@@ -149,6 +149,11 @@ There are several things that need to be remembered:
if(species.has_floating_eyes)
ovr += species.get_eyes(src)
for(var/aura in auras)
var/obj/aura/A = aura
var/icon/aura_overlay = icon(A.icon, icon_state = A.icon_state)
ovr += aura_overlay
add_overlay(ovr)
if (lying_prev != lying || size_multiplier != 1)
+2
View File
@@ -25,6 +25,8 @@
//Random events (vomiting etc)
handle_random_events()
aura_check(AURA_TYPE_LIFE)
. = 1
//Handle temperature/pressure differences between body and environment
+22
View File
@@ -906,3 +906,25 @@ default behaviour is:
Paralyse(rand(8,16))
make_jittery(rand(150,200))
adjustHalLoss(rand(50,60))
/mob/living/update_icons()
for(var/aura in auras)
var/obj/aura/A = aura
var/icon/aura_overlay = icon(A.icon, icon_state = A.icon_state)
add_overlay(aura_overlay)
/mob/living/proc/add_aura(var/obj/aura/aura)
LAZYDISTINCTADD(auras, aura)
update_icons()
return TRUE
/mob/living/proc/remove_aura(var/obj/aura/aura)
LAZYREMOVE(auras, aura)
update_icons()
return TRUE
/mob/living/Destroy()
if(auras)
for(var/a in auras)
remove_aura(a)
return ..()
+24
View File
@@ -100,6 +100,28 @@
P.on_hit(src, absorb, def_zone)
return absorb
/mob/living/proc/aura_check(var/type)
if(!auras)
return TRUE
. = TRUE
var/list/newargs = args - args[1]
for(var/a in auras)
var/obj/aura/aura = a
var/result = 0
switch(type)
if(AURA_TYPE_WEAPON)
result = aura.attackby(arglist(newargs))
if(AURA_TYPE_BULLET)
result = aura.bullet_act(arglist(newargs))
if(AURA_TYPE_THROWN)
result = aura.hitby(arglist(newargs))
if(AURA_TYPE_LIFE)
result = aura.life_tick()
if(result & AURA_FALSE)
. = FALSE
if(result & AURA_CANCEL)
break
//For visuals, blood splatters and so on.
/mob/living/proc/bullet_impact_visuals(var/obj/item/projectile/P, var/def_zone, var/damage)
var/list/impact_sounds = LAZYACCESS(P.impact_sounds, get_bullet_impact_effect_type(def_zone))
@@ -170,6 +192,8 @@
//this proc handles being hit by a thrown atom
/mob/living/hitby(atom/movable/AM as mob|obj,var/speed = THROWFORCE_SPEED_DIVISOR)//Standardization and logging -Sieve
if(!aura_check(AURA_TYPE_THROWN, AM, speed))
return
if(istype(AM,/obj/))
var/obj/O = AM
var/dtype = O.damtype
+2 -1
View File
@@ -69,4 +69,5 @@
var/burn_mod = 1
var/brute_mod = 1
var/limb_breaking = FALSE // used to limit people from queuing up limb-breaks
var/limb_breaking = FALSE // used to limit people from queuing up limb-breaks
var/list/obj/aura/auras //Basically a catch-all aura/force-field thing.
+2
View File
@@ -174,6 +174,8 @@
var/result = PROJECTILE_FORCE_MISS
if(hit_zone)
def_zone = hit_zone //set def_zone, so if the projectile ends up hitting someone else later (to be implemented), it is more likely to hit the same part
if(!target_mob.aura_check(AURA_TYPE_BULLET, src, def_zone))
return TRUE
result = target_mob.bullet_act(src, def_zone)
if(result == PROJECTILE_FORCE_MISS && (can_miss == 0)) //if you're shooting at point blank you can't miss.
@@ -267,4 +267,11 @@
/datum/design/item/mechfab/exosuit/passenger_compartment
name = "Mounted Passenger Compartment"
materials = list(DEFAULT_WALL_MATERIAL = 10000)
build_path = /obj/item/mecha_equipment/sleeper/passenger_compartment
build_path = /obj/item/mecha_equipment/sleeper/passenger_compartment
/datum/design/item/mechfab/exosuit/mechshields
name = "Energy Shield Drone"
time = 90
materials = list(MATERIAL_STEEL = 20000, MATERIAL_SILVER = 12000, MATERIAL_GOLD = 12000)
req_tech = list(TECH_MATERIAL = 4, TECH_MAGNET = 4, TECH_POWER = 4, TECH_COMBAT = 2)
build_path = /obj/item/mecha_equipment/shield
@@ -22,6 +22,7 @@
/spell/aoe_turf/conjure/mirage = 1,
/spell/targeted/shapeshift/corrupt_form = 1,
/spell/targeted/flesh_to_stone = 1,
/spell/radiant_aura = 1,
/spell/noclothes = 1,
/obj/structure/closet/wizard/armor = 1,
/obj/item/gun/energy/staff/focus = 1,
@@ -24,6 +24,7 @@
/spell/aoe_turf/knock = 1,
/spell/targeted/equip_item/holy_relic = 1,
/spell/aoe_turf/conjure/grove/sanctuary = 1,
/spell/radiant_aura = 1,
/spell/targeted/projectile/dumbfire/fireball = 2,
/spell/aoe_turf/conjure/forcewall = 1,
/spell/targeted/subjugation = 1,
@@ -0,0 +1,27 @@
/spell/radiant_aura
name = "Radiant Aura"
desc = "Form a protective layer of light around you, making you immune to laser fire."
school = "transmutation"
feedback = "ra"
invocation_type = SpI_EMOTE
invocation = "conjures a sphere of fire around themselves."
school = "conjuration"
spell_flags = NEEDSCLOTHES
charge_max = 300
cooldown_min = 200
level_max = list(Sp_TOTAL = 2, Sp_SPEED = 2, Sp_POWER = 0)
cast_sound = 'sound/effects/snap.ogg'
duration = 250
hud_state = "gen_immolate"
/spell/radiant_aura/choose_targets()
return list(holder)
/spell/radiant_aura/cast(var/list/targets, mob/user)
var/obj/aura/radiant_aura/A = new /obj/aura/radiant_aura(user)
A.added_to(user)
QDEL_IN(A, duration)
/spell/radiant_aura/starlight
spell_flags = 0
charge_max = 400