Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Shield belts #11821

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions beestation.dme
Original file line number Diff line number Diff line change
Expand Up @@ -1426,6 +1426,7 @@
#include "code\game\objects\items\colorizers\colorizer.dm"
#include "code\game\objects\items\colorizers\items.dm"
#include "code\game\objects\items\colorizers\mobs.dm"
#include "code\game\objects\items\combat\shield_belt.dm"
#include "code\game\objects\items\deployable\barricade.dm"
#include "code\game\objects\items\deployable\bodybag.dm"
#include "code\game\objects\items\deployable\bot.dm"
Expand Down
8 changes: 6 additions & 2 deletions code/__DEFINES/combat.dm
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,10 @@ GLOBAL_LIST_INIT(shove_disarming_types, typecacheof(list(

// Flags for energy shields
/// Energy shields will block projectiles
#define ENERGY_SHEILD_BLOCK_PROJECTILES (1 << 0)
#define ENERGY_SHIELD_BLOCK_PROJECTILES (1 << 0)
/// Energy shields will block melee attacks
#define ENERGY_SHEILD_BLOCK_MELEE (1 << 1)
#define ENERGY_SHIELD_BLOCK_MELEE (1 << 1)
/// Energy shield will not have a visible shield
#define ENERGY_SHIELD_INVISIBLE (1 << 2)
/// Energy shield will take max damage when EMP'd
#define ENERGY_SHIELD_EMP_VULNERABLE (1 << 3)
4 changes: 4 additions & 0 deletions code/__DEFINES/dcs/signals/signals_mob/signals_mob.dm
Original file line number Diff line number Diff line change
Expand Up @@ -69,3 +69,7 @@
///Called after a client connects to a mob and all UI elements have been setup
#define COMSIG_MOB_CLIENT_LOGIN "comsig_mob_client_login"
#define COMSIG_MOB_MOUSE_SCROLL_ON "comsig_mob_mouse_scroll_on" //! from base of /mob/MouseWheelOn(): (atom/A, delta_x, delta_y, params)

/// Called before a mob fires a gun (mob/source, obj/item/gun, atom/target, aimed)
#define COMSIG_MOB_BEFORE_FIRE_GUN "before_fire_gun"
#define GUN_HIT_SELF (1 << 0)
73 changes: 67 additions & 6 deletions code/datums/components/shielded.dm
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,26 @@
/// Do we still shield if we're being held in-hand? If FALSE, it needs to be equipped to a slot to work
var/shield_inhand = FALSE
/// Energy shield flags
var/shield_flags = ENERGY_SHEILD_BLOCK_PROJECTILES | ENERGY_SHEILD_BLOCK_MELEE
var/shield_flags = ENERGY_SHIELD_BLOCK_PROJECTILES | ENERGY_SHIELD_BLOCK_MELEE
/// Energy shield alpha
var/shield_alpha = 180
/// The cooldown tracking when we were last hit
COOLDOWN_DECLARE(recently_hit_cd)
/// The cooldown tracking when we last replenished a charge
COOLDOWN_DECLARE(charge_add_cd)
/// Callback for when the health of the shield changes
/// Parameters: mob/living/user, current_integrity
var/datum/callback/on_integrity_changed
/// A callback for the sparks/message that play when a charge is used, see [/datum/component/shielded/proc/default_run_hit_callback]
var/datum/callback/on_hit_effects
/// Have effects been activated
VAR_PRIVATE/_effects_activated
/// Invoked when the mob equips the shield
/// Parameters: mob/living/user, current_integrity
var/datum/callback/on_active_effects
/// Invoked when the mob unequips the shield
/// Parameters: mob/living/user, current_integrity
var/datum/callback/on_deactive_effects

/datum/component/shielded/Initialize(
max_integrity = 60,
Expand All @@ -40,9 +51,12 @@
shield_icon_file = 'icons/effects/effects.dmi',
shield_icon = "shield-old",
shield_inhand = FALSE,
shield_flags = ENERGY_SHEILD_BLOCK_PROJECTILES | ENERGY_SHEILD_BLOCK_MELEE,
shield_flags = ENERGY_SHIELD_BLOCK_PROJECTILES | ENERGY_SHIELD_BLOCK_MELEE,
shield_alpha = 160,
run_hit_callback
run_hit_callback,
on_active_effects,
on_deactive_effects,
on_integrity_changed,
)
if(!isitem(parent) || max_integrity <= 0)
return COMPONENT_INCOMPATIBLE
Expand All @@ -57,6 +71,9 @@
src.shield_flags = shield_flags
src.shield_alpha = shield_alpha
src.on_hit_effects = run_hit_callback || CALLBACK(src, PROC_REF(default_run_hit_callback))
src.on_active_effects = on_active_effects
src.on_deactive_effects = on_deactive_effects
src.on_integrity_changed = on_integrity_changed

current_integrity = max_integrity
if(charge_recovery)
Expand All @@ -67,6 +84,9 @@
shield_icon = "broken"
UnregisterSignal(wearer, COMSIG_ATOM_UPDATE_OVERLAYS)
wearer.update_appearance(UPDATE_ICON)
if (_effects_activated)
on_deactive_effects?.Invoke(wearer, current_integrity)
_effects_activated = FALSE
wearer = null
QDEL_NULL(on_hit_effects)
return ..()
Expand All @@ -76,9 +96,11 @@
RegisterSignal(parent, COMSIG_ITEM_DROPPED, PROC_REF(lost_wearer))
RegisterSignal(parent, COMSIG_ITEM_HIT_REACT, PROC_REF(on_hit_react))
RegisterSignal(parent, COMSIG_PARENT_ATTACKBY, PROC_REF(check_recharge_rune))
if (shield_flags & ENERGY_SHIELD_EMP_VULNERABLE)
RegisterSignal(parent, COMSIG_ATOM_EMP_ACT, PROC_REF(emp_destruction))

/datum/component/shielded/UnregisterFromParent()
UnregisterSignal(parent, list(COMSIG_ITEM_EQUIPPED, COMSIG_ITEM_DROPPED, COMSIG_ITEM_HIT_REACT, COMSIG_PARENT_ATTACKBY))
UnregisterSignal(parent, list(COMSIG_ITEM_EQUIPPED, COMSIG_ITEM_DROPPED, COMSIG_ITEM_HIT_REACT, COMSIG_PARENT_ATTACKBY, COMSIG_ATOM_EMP_ACT))

// Handle recharging, if we want to
/datum/component/shielded/process(delta_time)
Expand All @@ -98,11 +120,36 @@
if(current_integrity == max_integrity)
playsound(item_parent, 'sound/machines/ding.ogg', 50, TRUE)

/datum/component/shielded/proc/emp_destruction(datum/source, severity)
SIGNAL_HANDLER
if (!current_integrity)
return
COOLDOWN_START(src, recently_hit_cd, recharge_start_delay)
current_integrity = 0
on_integrity_changed?.Invoke(wearer, current_integrity)

if(!charge_recovery) // if charge_recovery is 0, we don't recharge
qdel(src)
return

// Remove effects on shield break
if (_effects_activated)
on_deactive_effects?.Invoke(wearer)
_effects_activated = FALSE
wearer.update_appearance(UPDATE_ICON)

START_PROCESSING(SSdcs, src) // if we DO recharge, start processing so we can do that

/datum/component/shielded/proc/adjust_charge(change)
var/needs_update = current_integrity == 0
current_integrity = clamp(current_integrity + change, 0, max_integrity)
on_integrity_changed?.Invoke(wearer, current_integrity)
if(wearer && needs_update)
wearer.update_appearance(UPDATE_ICON)
// re-add effects when the shield recovers
if (!_effects_activated)
on_active_effects?.Invoke(wearer, current_integrity)
_effects_activated = TRUE

/// Check if we've been equipped to a valid slot to shield
/datum/component/shielded/proc/on_equipped(datum/source, mob/user, slot)
Expand All @@ -113,6 +160,9 @@
return

wearer = user
if (!_effects_activated)
on_active_effects?.Invoke(user, current_integrity)
_effects_activated = TRUE
RegisterSignal(wearer, COMSIG_ATOM_UPDATE_OVERLAYS, PROC_REF(on_update_overlays))
RegisterSignal(wearer, COMSIG_PARENT_QDELETING, PROC_REF(lost_wearer))
if(current_integrity)
Expand All @@ -123,6 +173,9 @@
SIGNAL_HANDLER

if(wearer)
if (_effects_activated)
on_deactive_effects?.Invoke(user, current_integrity)
_effects_activated = FALSE
UnregisterSignal(wearer, list(COMSIG_ATOM_UPDATE_OVERLAYS, COMSIG_PARENT_QDELETING))
wearer.update_appearance(UPDATE_ICON)
wearer = null
Expand All @@ -131,6 +184,9 @@
/datum/component/shielded/proc/on_update_overlays(atom/parent_atom, list/overlays)
SIGNAL_HANDLER

if (shield_flags & ENERGY_SHIELD_INVISIBLE)
return

var/mutable_appearance/shield_image = mutable_appearance(shield_icon_file, (current_integrity > 0 ? shield_icon : "broken"), MOB_SHIELD_LAYER)
shield_image.alpha = shield_alpha
overlays += shield_image
Expand All @@ -144,15 +200,16 @@

COOLDOWN_START(src, recently_hit_cd, recharge_start_delay)

if ((attack_type == PROJECTILE_ATTACK || attack_type == THROWN_PROJECTILE_ATTACK) && !(shield_flags & ENERGY_SHEILD_BLOCK_PROJECTILES))
if ((attack_type == PROJECTILE_ATTACK || attack_type == THROWN_PROJECTILE_ATTACK) && !(shield_flags & ENERGY_SHIELD_BLOCK_PROJECTILES))
return
else if (!(attack_type == PROJECTILE_ATTACK || attack_type == THROWN_PROJECTILE_ATTACK) && !(shield_flags & ENERGY_SHEILD_BLOCK_MELEE))
else if (!(attack_type == PROJECTILE_ATTACK || attack_type == THROWN_PROJECTILE_ATTACK) && !(shield_flags & ENERGY_SHIELD_BLOCK_MELEE))
return

if(current_integrity <= 0)
return
. = COMPONENT_HIT_REACTION_BLOCK
current_integrity = max(current_integrity - damage, 0)
on_integrity_changed?.Invoke(wearer, current_integrity)

INVOKE_ASYNC(src, PROC_REF(actually_run_hit_callback), owner, attack_text, current_integrity)

Expand All @@ -163,6 +220,10 @@
return

if (!current_integrity)
// Remove effects on shield break
if (_effects_activated)
on_deactive_effects?.Invoke(wearer)
_effects_activated = FALSE
wearer.update_appearance(UPDATE_ICON)

START_PROCESSING(SSdcs, src) // if we DO recharge, start processing so we can do that
Expand Down
45 changes: 45 additions & 0 deletions code/game/objects/items/combat/shield_belt.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#define COLOUR_GOOD "#5eabeb"
#define COLOUR_BAD "#cb5858"

/obj/item/shield_belt
name = "shield belt"
desc = "A belt that engulfs the user in a shield that blocks both incoming and outgoing high-energy projectiles."
slot_flags = ITEM_SLOT_BELT
icon = 'icons/obj/clothing/belts.dmi'
icon_state = "shield_belt"
item_state = "security"
worn_icon_state = "shield_belt"
lefthand_file = 'icons/mob/inhands/equipment/belt_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/belt_righthand.dmi'
var/max_shield_integrity = 80

/obj/item/shield_belt/ComponentInitialize()
. = ..()
AddComponent(/datum/component/shielded, max_integrity = max_shield_integrity, charge_recovery = 10, shield_flags = ENERGY_SHIELD_BLOCK_PROJECTILES | ENERGY_SHIELD_INVISIBLE | ENERGY_SHIELD_EMP_VULNERABLE, on_active_effects = CALLBACK(src, PROC_REF(add_shield_effects)), on_deactive_effects = CALLBACK(src, PROC_REF(remove_shield_effects)), on_integrity_changed = CALLBACK(src, PROC_REF(update_shield_health)))

/obj/item/shield_belt/proc/add_shield_effects(mob/living/wearer, current_integrity)
RegisterSignal(wearer, COMSIG_MOB_BEFORE_FIRE_GUN, PROC_REF(intercept_gun_fire))
update_shield_health(wearer, current_integrity)

/obj/item/shield_belt/proc/remove_shield_effects(mob/living/wearer, current_integrity)
UnregisterSignal(wearer, COMSIG_MOB_BEFORE_FIRE_GUN)
wearer.remove_filter("shield_filter")

/obj/item/shield_belt/proc/update_shield_health(mob/living/wearer, current_integrity)
var/list/good = rgb2num(COLOUR_GOOD)
var/list/bad = rgb2num(COLOUR_BAD)
var/proportion = current_integrity / max_shield_integrity
var/colour_first = rgb(proportion * good[1] + (1 - proportion) * bad[1], proportion * good[2] + (1 - proportion) * bad[2], proportion * good[3] + (1 - proportion) * bad[3], 70)
var/colour_second = rgb((proportion * good[1] + (1 - proportion) * bad[1]) * 0.8, (proportion * good[2] + (1 - proportion) * bad[2]) * 0.8, (proportion * good[3] + (1 - proportion) * bad[3]) * 0.8, 70)
wearer.add_filter("shield_filter", 10, outline_filter(2, colour_first))
// Do the animation
wearer.transition_filter("shield_filter", 2 SECONDS, list(size = 2, color = colour_second), easing = SINE_EASING, loop = -1)
animate(time = 2 SECONDS, color = colour_first, easing = SINE_EASING, loop = -1)

/// Intercept outgoing gunfire
/obj/item/shield_belt/proc/intercept_gun_fire(mob/source, obj/item/gun, atom/target, aimed)
SIGNAL_HANDLER
return GUN_HIT_SELF

#undef COLOUR_GOOD
#undef COLOUR_BAD
2 changes: 1 addition & 1 deletion code/game/objects/items/stunbaton.dm
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@
..()
//Only mob/living types have stun handling
if(turned_on && prob(throw_hit_chance) && iscarbon(hit_atom))
baton_stun(hit_atom)
baton_stun(hit_atom, throwingdatum.thrower)

/obj/item/melee/baton/loaded //this one starts with a cell pre-installed.
preload_cell_type = /obj/item/stock_parts/cell/high
Expand Down
2 changes: 1 addition & 1 deletion code/modules/antagonists/cult/cult_items.dm
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,7 @@ Striking a noncultist, however, will tear their flesh."}
charge_recovery = 0 SECONDS, \
shield_icon_file = 'icons/effects/cult_effects.dmi', \
shield_icon = "shield-cult", \
shield_flags = ENERGY_SHEILD_BLOCK_PROJECTILES, \
shield_flags = ENERGY_SHIELD_BLOCK_PROJECTILES, \
shield_alpha = 120, \
run_hit_callback = CALLBACK(src, PROC_REF(shield_damaged)) \
)
Expand Down
2 changes: 1 addition & 1 deletion code/modules/holoparasite/holoparasite_manifest.dm
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
mr_hud?.begin_timer(HOLOPARASITE_MANIFEST_COOLDOWN)
playsound(loc, 'sound/creatures/holopara_summon.ogg', vol = 45, extrarange = HOLOPARA_MANIFEST_SOUND_EXTRARANGE, frequency = 1)
add_filter("holopara_manifest", 1, gauss_blur_filter(size = 4))
transition_filter("holopara_manifest", 1.2 SECONDS, list("size" = 0), easing = SINE_EASING, loop = FALSE)
transition_filter("holopara_manifest", 1.2 SECONDS, list("size" = 0), easing = SINE_EASING, loop = 0)
Rukofamicom marked this conversation as resolved.
Show resolved Hide resolved
addtimer(CALLBACK(src, TYPE_PROC_REF(/atom, remove_filter), "holopara_manifest"), 1.25 SECONDS, TIMER_UNIQUE | TIMER_OVERRIDE)
SEND_SIGNAL(src, COMSIG_HOLOPARA_POST_MANIFEST, forced)
return TRUE
Expand Down
17 changes: 8 additions & 9 deletions code/modules/mob/living/carbon/human/human_defense.dm
Original file line number Diff line number Diff line change
Expand Up @@ -141,15 +141,14 @@
if(I.hit_reaction(src, AM, attack_text, damage, attack_type))
I.on_block(src, AM, attack_text, damage, attack_type)
return 1
if(wear_suit)
if(wear_suit.hit_reaction(src, AM, attack_text, damage, attack_type))
return TRUE
if(w_uniform)
if(w_uniform.hit_reaction(src, AM, attack_text, damage, attack_type))
return TRUE
if(wear_neck)
if(wear_neck.hit_reaction(src, AM, attack_text, damage, attack_type))
return TRUE
if(wear_suit?.hit_reaction(src, AM, attack_text, damage, attack_type))
return TRUE
if(w_uniform?.hit_reaction(src, AM, attack_text, damage, attack_type))
return TRUE
if(wear_neck?.hit_reaction(src, AM, attack_text, damage, attack_type))
return TRUE
if(belt?.hit_reaction(src, AM, attack_text, damage, attack_type))
return TRUE
return FALSE

/mob/living/carbon/human/proc/check_block()
Expand Down
5 changes: 2 additions & 3 deletions code/modules/projectiles/autofire.dm
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,8 @@ Everything else should be handled for you. Good luck soldier.

#define COMSIG_AUTOFIRE_END "stop_autofiring"

/obj/item/gun
var/full_auto = FALSE //Set this if your gun uses full auto. ONLY guns that go brr should use this. Not pistols!
var/datum/component/full_auto/autofire_component = null //Repeated calls to getComponent aren't really ideal. So we'll take the memory hit instead.
/obj/item/gun/var/full_auto = FALSE //Set this if your gun uses full auto. ONLY guns that go brr should use this. Not pistols!
/obj/item/gun/var/datum/component/full_auto/autofire_component = null //Repeated calls to getComponent aren't really ideal. So we'll take the memory hit instead.

/obj/item/gun/vv_edit_var(var_name, var_value)
. = ..()
Expand Down
9 changes: 7 additions & 2 deletions code/modules/projectiles/gun.dm
Original file line number Diff line number Diff line change
Expand Up @@ -408,7 +408,9 @@
else //Smart spread
sprd = round((((rand_spr/burst_size) * iteration) - (0.5 + (rand_spr * 0.25))) * (randomized_gun_spread + randomized_bonus_spread))
sprd = max(min_gun_sprd, abs(sprd)) * SIGN(sprd)
before_firing(target,user)
var/result = before_firing(target,user)
if (result & GUN_HIT_SELF)
target = user
if(!chambered.fire_casing(target, user, params, ,suppressed, zone_override, sprd, spread_multiplier, src))
shoot_with_empty_chamber(user)
firing_burst = FALSE
Expand Down Expand Up @@ -468,7 +470,9 @@
return
sprd = round((rand() - 0.5) * DUALWIELD_PENALTY_EXTRA_MULTIPLIER * (spread + bonus_spread))
sprd = max(min_gun_sprd, abs(sprd)) * SIGN(sprd)
before_firing(target, user, aimed)
var/result = before_firing(target, user, aimed)
if (result & GUN_HIT_SELF)
target = user
if(!chambered.fire_casing(target, user, params, , suppressed, zone_override, sprd, spread_multiplier, src))
shoot_with_empty_chamber(user)
return
Expand Down Expand Up @@ -663,6 +667,7 @@
if(aimed == GUN_AIMED_POINTBLANK)
chambered.BB.speed = initial(chambered.BB.speed) * 0.25 // Much faster bullets because you're holding them literally at the barrel of the gun
chambered.BB.damage = initial(chambered.BB.damage) * 4 // Execution
return SEND_SIGNAL(user, COMSIG_MOB_BEFORE_FIRE_GUN, src, target, aimed)

/////////////
// ZOOMING //
Expand Down
14 changes: 14 additions & 0 deletions code/modules/research/designs/weapon_designs.dm
Original file line number Diff line number Diff line change
Expand Up @@ -419,3 +419,17 @@
build_path = /obj/item/flashbulb
category = list("Weapons")
departmental_flags = DEPARTMENTAL_FLAG_SECURITY

/////////////////////////////////////////
/////////////////Armour//////////////////
/////////////////////////////////////////

/datum/design/shield_belt
name = "Shield Belt"
desc = "A belt which evelops the user inside a high-energy shield which blocks incoming and outgoing high-energy projectiles."
id = "shieldbelt"
build_type = PROTOLATHE
materials = list(/datum/material/iron = 1 * MINERAL_MATERIAL_AMOUNT, /datum/material/uranium = 1 * MINERAL_MATERIAL_AMOUNT)
build_path = /obj/item/shield_belt
category = list("Weapons")
departmental_flags = DEPARTMENTAL_FLAG_SCIENCE
9 changes: 6 additions & 3 deletions code/modules/research/techweb/all_nodes.dm
Original file line number Diff line number Diff line change
Expand Up @@ -1650,7 +1650,7 @@
"pin_testing",
"tele_shield",
)
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 10000)
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 5000)
export_price = 5000

/datum/techweb_node/smartmine
Expand All @@ -1676,8 +1676,11 @@
"adv_engi",
"weaponry",
)
design_ids = list("pin_loyalty")
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 10000)
design_ids = list(
"pin_loyalty",
"shieldbelt"
)
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 5000)
export_price = 5000

/datum/techweb_node/advmine
Expand Down
Binary file modified icons/mob/clothing/belt.dmi
Binary file not shown.
Binary file modified icons/obj/clothing/belts.dmi
Binary file not shown.
Loading