mirror of
https://github.com/CHOMPStation2/CHOMPStation2.git
synced 2025-12-11 02:34:00 +00:00
Adds Directional Shields
Implements shields which block projectiles from outside, but allow projectiles to leave from inside. Also adds a projector item which makes these, in two variants, rectangle and line. It was made an item so that down the road making stationary/wearable/etc versions will be easier as they can just contain the item. Currently there is no way to obtain this without adminspawn. I have Future Plans(tm) for these.
This commit is contained in:
@@ -533,3 +533,23 @@
|
||||
|
||||
/mob/proc/update_gravity()
|
||||
return
|
||||
|
||||
// The real Move() proc is above, but touching that massive block just to put this in isn't worth it.
|
||||
/mob/Move(var/newloc, var/direct)
|
||||
. = ..(newloc, direct)
|
||||
if(.)
|
||||
post_move(newloc, direct)
|
||||
|
||||
// Called when a mob successfully moves.
|
||||
// Would've been an /atom/movable proc but it caused issues.
|
||||
/mob/proc/post_move(var/newloc, var/direct)
|
||||
for(var/obj/O in contents)
|
||||
O.on_loc_moved(newloc, direct)
|
||||
|
||||
// Received from post_move(), useful for items that need to know that their loc just moved.
|
||||
/obj/proc/on_loc_moved(var/newloc, var/direct)
|
||||
return
|
||||
|
||||
/obj/item/weapon/storage/on_loc_moved(var/newloc, var/direct)
|
||||
for(var/obj/O in contents)
|
||||
O.on_loc_moved(newloc, direct)
|
||||
286
code/modules/shieldgen/directional_shield.dm
Normal file
286
code/modules/shieldgen/directional_shield.dm
Normal file
@@ -0,0 +1,286 @@
|
||||
// This is the actual shield. The projector is a different item.
|
||||
/obj/effect/directional_shield
|
||||
name = "directional combat shield"
|
||||
desc = "A wide shield, which has the property to block incoming projectiles but allow outgoing projectiles to pass it. \
|
||||
Slower moving objects are not blocked, so people can walk in and out of the barrier, and things can be thrown into and out \
|
||||
of it."
|
||||
icon = 'icons/effects/effects.dmi'
|
||||
icon_state = "directional_shield"
|
||||
density = FALSE // People can move pass these shields.
|
||||
opacity = FALSE
|
||||
anchored = TRUE
|
||||
unacidable = TRUE
|
||||
layer = MOB_LAYER + 0.1
|
||||
mouse_opacity = FALSE
|
||||
var/obj/item/shield_projector/projector = null // The thing creating the shield.
|
||||
var/x_offset = 0 // Offset from the 'center' of where the projector is, so that if it moves, the shield can recalc its position.
|
||||
var/y_offset = 0 // Ditto.
|
||||
|
||||
/obj/effect/directional_shield/New(var/newloc, var/new_projector)
|
||||
if(new_projector)
|
||||
projector = new_projector
|
||||
var/turf/us = get_turf(src)
|
||||
var/turf/them = get_turf(projector)
|
||||
if(them)
|
||||
x_offset = us.x - them.x
|
||||
y_offset = us.y - them.y
|
||||
..(newloc)
|
||||
|
||||
/obj/effect/directional_shield/proc/relocate()
|
||||
if(!projector)
|
||||
return // Nothing to follow.
|
||||
var/turf/T = get_turf(projector)
|
||||
if(!T)
|
||||
return
|
||||
var/turf/new_pos = locate(T.x + x_offset, T.y + y_offset, T.z)
|
||||
if(new_pos)
|
||||
forceMove(new_pos)
|
||||
else
|
||||
qdel(src)
|
||||
|
||||
/obj/effect/directional_shield/Destroy()
|
||||
if(projector)
|
||||
projector.active_shields -= src
|
||||
projector = null
|
||||
return ..()
|
||||
|
||||
/obj/effect/directional_shield/CanPass(atom/movable/mover, turf/target, height=0, air_group=0)
|
||||
if(air_group || (height==0))
|
||||
return TRUE
|
||||
else if(istype(mover, /obj/item/projectile))
|
||||
var/obj/item/projectile/P = mover
|
||||
var/bad_arc = reverse_direction(dir) // Arc of directions from which we cannot block.
|
||||
if(check_shield_arc(src, bad_arc, P)) // This is actually for mobs but it will work for our purposes as well.
|
||||
return FALSE
|
||||
else
|
||||
return TRUE
|
||||
return TRUE
|
||||
|
||||
/obj/effect/directional_shield/bullet_act(var/obj/item/projectile/P)
|
||||
adjust_health(-P.get_structure_damage())
|
||||
P.on_hit()
|
||||
playsound(get_turf(src), 'sound/effects/EMPulse.ogg', 75, 1)
|
||||
|
||||
// All the shields tied to their projector are one 'unit', and don't have individualized health values like most other shields.
|
||||
/obj/effect/directional_shield/proc/adjust_health(amount)
|
||||
if(projector)
|
||||
projector.adjust_health(amount) // Projector will kill the shield if needed.
|
||||
// If the shield lacks a projector, then it was probably spawned in by an admin for bus, so it's indestructable.
|
||||
|
||||
|
||||
// This actually creates the shields. It's an item so that it can be carried, but it could also be placed inside a stationary object if desired.
|
||||
// It should work inside the contents of any mob.
|
||||
/obj/item/shield_projector
|
||||
name = "combat shield projector"
|
||||
desc = "A miniturized and compact shield projector. This type has been optimized to diffuse lasers or block high velocity projectiles from the outside, \
|
||||
but allow those projectiles to leave the shield from the inside. Blocking too many damaging projectiles will cause the shield to fail."
|
||||
icon = 'icons/obj/device.dmi'
|
||||
icon_state = "signmaker_sec"
|
||||
var/active = FALSE // If it's on.
|
||||
var/shield_health = 400 // How much damage the shield blocks before breaking. This is a shared health pool for all shields attached to this projector.
|
||||
var/max_shield_health = 400 // Ditto. This is fairly high, but shields are really big, you can't miss them, and laser carbines pump out so much hurt.
|
||||
var/shield_regen_amount = 20 // How much to recharge every process(), after the delay.
|
||||
var/shield_regen_delay = 5 SECONDS // If the shield takes damage, it won't recharge for this long.
|
||||
var/last_damaged_time = null // world.time when the shields took damage, used for the delay.
|
||||
var/list/active_shields = list() // Shields that are active and deployed.
|
||||
var/always_on = FALSE // If true, will always try to reactivate if disabled for whatever reason, ideal if AI mobs are holding this.
|
||||
|
||||
/obj/item/shield_projector/New()
|
||||
processing_objects += src
|
||||
..()
|
||||
|
||||
/obj/item/shield_projector/Destroy()
|
||||
destroy_shields()
|
||||
processing_objects -= src
|
||||
return ..()
|
||||
|
||||
/obj/item/shield_projector/proc/create_shield(var/newloc, var/new_dir)
|
||||
var/obj/effect/directional_shield/S = new(newloc, src)
|
||||
S.dir = new_dir
|
||||
active_shields += S
|
||||
|
||||
/obj/item/shield_projector/proc/create_shields() // Override this for a specific shape. Be sure to call ..() for the checks, however.
|
||||
if(active) // Already made.
|
||||
return FALSE
|
||||
if(shield_health <= 0)
|
||||
return FALSE
|
||||
active = TRUE
|
||||
return TRUE
|
||||
|
||||
/obj/item/shield_projector/proc/destroy_shields()
|
||||
for(var/obj/effect/directional_shield/S in active_shields)
|
||||
active_shields -= S
|
||||
qdel(S)
|
||||
active = FALSE
|
||||
|
||||
/obj/item/shield_projector/proc/update_shield_positions()
|
||||
for(var/obj/effect/directional_shield/S in active_shields)
|
||||
S.relocate()
|
||||
|
||||
/obj/item/shield_projector/proc/adjust_health(amount)
|
||||
shield_health = between(0, shield_health + amount, max_shield_health)
|
||||
if(amount < 0)
|
||||
if(shield_health <= 0)
|
||||
destroy_shields()
|
||||
var/turf/T = get_turf(src)
|
||||
T.visible_message("<span class='danger'>\The [src] overloads and the shield vanishes!</span>")
|
||||
playsound(get_turf(src), 'sound/machines/defib_failed.ogg', 75, 0)
|
||||
else
|
||||
if(shield_health < max_shield_health / 4) // Play a more urgent sounding beep if it's at 25% health.
|
||||
playsound(get_turf(src), 'sound/machines/defib_success.ogg', 75, 0)
|
||||
else
|
||||
playsound(get_turf(src), 'sound/machines/defib_SafetyOn.ogg', 75, 0)
|
||||
last_damaged_time = world.time
|
||||
|
||||
/obj/item/shield_projector/attack_self(var/mob/living/user)
|
||||
if(active)
|
||||
if(always_on)
|
||||
to_chat(user, "<span class='warning'>You can't seem to deactivate \the [src].</span>")
|
||||
return
|
||||
|
||||
destroy_shields()
|
||||
else
|
||||
set_dir(user.dir) // Needed for linear shields.
|
||||
create_shields()
|
||||
visible_message("<span class='notice'>\The [user] [!active ? "de":""]activates \the [src].</span>")
|
||||
|
||||
/obj/item/shield_projector/process()
|
||||
if(shield_health < max_shield_health && ( (last_damaged_time + shield_regen_delay) < world.time) )
|
||||
adjust_health(shield_regen_amount)
|
||||
if(always_on && !active) // Make shields as soon as possible if this is set.
|
||||
create_shields()
|
||||
if(shield_health == max_shield_health)
|
||||
playsound(get_turf(src), 'sound/machines/defib_ready.ogg', 75, 0)
|
||||
else
|
||||
playsound(get_turf(src), 'sound/machines/defib_safetyOff.ogg', 75, 0)
|
||||
|
||||
/obj/item/shield_projector/examine(var/mob/user)
|
||||
..()
|
||||
if(get_dist(src, user) <= 1)
|
||||
to_chat(user, "\The [src]'s shield matrix is at [round( (shield_health / max_shield_health) * 100, 0.01)]% strength.")
|
||||
|
||||
/obj/item/shield_projector/emp_act(var/severity)
|
||||
adjust_health(-max_shield_health / severity) // A strong EMP will kill the shield instantly, but weaker ones won't on the first hit.
|
||||
|
||||
/obj/item/shield_projector/Move(var/newloc, var/direct)
|
||||
..(newloc, direct)
|
||||
update_shield_positions()
|
||||
|
||||
/obj/item/shield_projector/on_loc_moved(var/newloc, var/direct)
|
||||
update_shield_positions()
|
||||
|
||||
|
||||
// Subtypes
|
||||
|
||||
/obj/item/shield_projector/rectangle
|
||||
name = "rectangular combat shield projector"
|
||||
description_info = "This creates a shield in a rectangular shape, which allows projectiles to leave from inside but blocks projectiles from outside. \
|
||||
Everything else can pass through the shield freely, including other people and thrown objects. The shield also cannot block certain effects which \
|
||||
take place over an area, such as flashbangs or explosions."
|
||||
var/size_x = 3 // How big the rectangle will be, in tiles from the center.
|
||||
var/size_y = 3 // Ditto.
|
||||
|
||||
// Horrible implementation below.
|
||||
/obj/item/shield_projector/rectangle/create_shields()
|
||||
if(!..())
|
||||
return FALSE
|
||||
|
||||
// Make a rectangle in a really terrible way.
|
||||
var/x_dist = size_x
|
||||
var/y_dist = size_y
|
||||
|
||||
var/turf/T = get_turf(src)
|
||||
if(!T)
|
||||
return FALSE
|
||||
// Top left corner.
|
||||
var/turf/T1 = locate(T.x - x_dist, T.y + y_dist, T.z)
|
||||
// Bottom right corner.
|
||||
var/turf/T2 = locate(T.x + x_dist, T.y - y_dist, T.z)
|
||||
if(!T1 || !T2) // If we're on the edge of the map then don't bother.
|
||||
return FALSE
|
||||
|
||||
// Build half of the corners first, as they are 'anchors' for the rest of the code below.
|
||||
create_shield(T1, NORTHWEST)
|
||||
create_shield(T2, SOUTHEAST)
|
||||
|
||||
// Build the edges.
|
||||
// First start with the north side.
|
||||
var/current_x = T1.x + 1 // Start next to the top left corner.
|
||||
var/current_y = T1.y
|
||||
var/length = (x_dist * 2) - 1
|
||||
for(var/i = 1 to length)
|
||||
create_shield(locate(current_x, current_y, T.z), NORTH)
|
||||
current_x++
|
||||
|
||||
// Make the top right corner.
|
||||
create_shield(locate(current_x, current_y, T.z), NORTHEAST)
|
||||
|
||||
// Now for the west edge.
|
||||
current_x = T1.x
|
||||
current_y = T1.y - 1
|
||||
length = (y_dist * 2) - 1
|
||||
for(var/i = 1 to length)
|
||||
create_shield(locate(current_x, current_y, T.z), WEST)
|
||||
current_y--
|
||||
|
||||
// Make the bottom left corner.
|
||||
create_shield(locate(current_x, current_y, T.z), SOUTHWEST)
|
||||
|
||||
// Switch to the second corner, and make the east edge.
|
||||
current_x = T2.x
|
||||
current_y = T2.y + 1
|
||||
length = (y_dist * 2) - 1
|
||||
for(var/i = 1 to length)
|
||||
create_shield(locate(current_x, current_y, T.z), EAST)
|
||||
current_y++
|
||||
|
||||
// There are no more corners to create, so we can just go build the south edge now.
|
||||
current_x = T2.x - 1
|
||||
current_y = T2.y
|
||||
length = (x_dist * 2) - 1
|
||||
for(var/i = 1 to length)
|
||||
create_shield(locate(current_x, current_y, T.z), SOUTH)
|
||||
current_x--
|
||||
// Finally done.
|
||||
return TRUE
|
||||
|
||||
/obj/item/shield_projector/line
|
||||
name = "linear combat shield projector"
|
||||
description_info = "This creates a shield in a straight line perpendicular to the direction where the user was facing when it was activated. \
|
||||
The shield allows projectiles to leave from inside but blocks projectiles from outside. Everything else can pass through the shield freely, \
|
||||
including other people and thrown objects. The shield also cannot block certain effects which take place over an area, such as flashbangs or explosions."
|
||||
var/line_length = 5 // How long the line is. Recommended to be an odd number.
|
||||
var/offset_from_center = 2 // How far from the projector will the line's center be.
|
||||
|
||||
/obj/item/shield_projector/line/create_shields()
|
||||
if(!..())
|
||||
return FALSE
|
||||
|
||||
var/turf/T = get_turf(src) // This is another 'anchor', or will be once we move away from the projector.
|
||||
for(var/i = 1 to offset_from_center)
|
||||
T = get_step(T, dir)
|
||||
if(!T) // We went off the map or something.
|
||||
return
|
||||
// We're at the right spot now. Build the center piece.
|
||||
create_shield(T, dir)
|
||||
|
||||
var/length_to_build = round( (line_length - 1) / 2)
|
||||
var/turf/temp_T = T
|
||||
|
||||
// First loop, we build the left (from a north perspective) side of the line.
|
||||
for(var/i = 1 to length_to_build)
|
||||
temp_T = get_step(temp_T, turn(dir, 90) )
|
||||
if(!temp_T)
|
||||
break
|
||||
create_shield(temp_T, i == length_to_build ? turn(dir, 45) : dir)
|
||||
|
||||
temp_T = T
|
||||
|
||||
// Second loop, we build the right side.
|
||||
for(var/i = 1 to length_to_build)
|
||||
temp_T = get_step(temp_T, turn(dir, -90) )
|
||||
if(!temp_T)
|
||||
break
|
||||
create_shield(temp_T, i == length_to_build ? turn(dir, -45) : dir)
|
||||
// Finished.
|
||||
return TRUE
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 245 KiB After Width: | Height: | Size: 253 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 58 KiB After Width: | Height: | Size: 59 KiB |
@@ -2080,6 +2080,7 @@
|
||||
#include "code\modules\scripting\Scanner\Tokens.dm"
|
||||
#include "code\modules\security levels\keycard authentication.dm"
|
||||
#include "code\modules\security levels\security levels.dm"
|
||||
#include "code\modules\shieldgen\directional_shield.dm"
|
||||
#include "code\modules\shieldgen\emergency_shield.dm"
|
||||
#include "code\modules\shieldgen\energy_field.dm"
|
||||
#include "code\modules\shieldgen\handheld_defuser.dm"
|
||||
|
||||
Reference in New Issue
Block a user