Merge pull request #11929 from Ghommie/Ghommie-cit692
Shielded Component
This commit is contained in:
@@ -302,8 +302,8 @@ GLOBAL_LIST_INIT(shove_disarming_types, typecacheof(list(
|
||||
|
||||
/// Block priorities
|
||||
#define BLOCK_PRIORITY_HELD_ITEM 100
|
||||
#define BLOCK_PRIORITY_CLOTHING 50
|
||||
#define BLOCK_PRIORITY_WEAR_SUIT 75
|
||||
#define BLOCK_PRIORITY_CLOTHING 50
|
||||
#define BLOCK_PRIORITY_UNIFORM 25
|
||||
|
||||
#define BLOCK_PRIORITY_DEFAULT BLOCK_PRIORITY_HELD_ITEM
|
||||
|
||||
@@ -200,6 +200,7 @@
|
||||
#define COMSIG_LIVING_RUN_BLOCK "living_do_run_block" //from base of mob/living/do_run_block(): (real_attack, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone)
|
||||
#define COMSIG_LIVING_COMBAT_ENABLED "combatmode_enabled" //from base of mob/living/enable_combat_mode() (was_forced)
|
||||
#define COMSIG_LIVING_COMBAT_DISABLED "combatmode_disabled" //from base of mob/living/disable_combat_mode() (was_forced)
|
||||
#define COMSIG_LIVING_GET_BLOCKING_ITEMS "get_blocking_items" //from base of mob/living/get_blocking_items(): (list/items)
|
||||
|
||||
//ALL OF THESE DO NOT TAKE INTO ACCOUNT WHETHER AMOUNT IS 0 OR LOWER AND ARE SENT REGARDLESS!
|
||||
#define COMSIG_LIVING_STATUS_STUN "living_stun" //from base of mob/living/Stun() (amount, update, ignore)
|
||||
|
||||
@@ -0,0 +1,191 @@
|
||||
/datum/component/shielded
|
||||
dupe_mode = COMPONENT_DUPE_ALLOWED
|
||||
can_transfer = TRUE
|
||||
var/charges = 3
|
||||
var/max_charges = 3
|
||||
var/recharge_delay = 20 SECONDS //How long after we've been attacked before we can start recharging.
|
||||
var/recharge_rate = 1 //How quickly the shield recharges once it starts charging. Can be a decimal. set to zero to disable.
|
||||
var/last_time_used //Last time the shield attempted to stop an attack.
|
||||
var/accepted_slots
|
||||
var/shield_state = "shield-old" //the state of the shield overlay.
|
||||
var/broken_state //null by default.
|
||||
var/recharge_sound = 'sound/magic/charge.ogg'
|
||||
var/recharge_end_sound = 'sound/machines/ding.ogg'
|
||||
var/mob/living/holder //who is currently benefiting from the shield.
|
||||
var/dissipating = FALSE //Is this shield meant to dissipate over time instead of recharging.
|
||||
var/del_on_overload = FALSE //will delete itself once it has no charges left.
|
||||
|
||||
/datum/component/shielded/Initialize(current, max = 3, delay = 20 SECONDS, rate = 1, slots, state = "shield-old", broken, \
|
||||
sound = 'sound/magic/charge.ogg', end_sound = 'sound/machines/ding.ogg', diss = FALSE, del_overload = FALSE)
|
||||
var/isitem = isitem(parent)
|
||||
if(!isitem && !isliving(parent))
|
||||
return COMPONENT_INCOMPATIBLE
|
||||
max_charges = max
|
||||
charges = !isnull(current) ? current : max_charges
|
||||
recharge_delay = delay
|
||||
recharge_rate = rate
|
||||
accepted_slots = slots
|
||||
shield_state = state
|
||||
broken_state = broken
|
||||
recharge_sound = sound
|
||||
recharge_end_sound = end_sound
|
||||
dissipating = diss
|
||||
del_on_overload = del_overload
|
||||
if(dissipating && recharge_rate > 0)
|
||||
recharge_rate = -recharge_rate
|
||||
if(recharge_delay && recharge_rate && (charges < max_charges || dissipating))
|
||||
START_PROCESSING(SSdcs, src)
|
||||
|
||||
/datum/component/shielded/RegisterWithParent()
|
||||
. = ..()
|
||||
if(isitem(parent))
|
||||
RegisterSignal(parent, COMSIG_ITEM_EQUIPPED, .proc/on_equip)
|
||||
RegisterSignal(parent, COMSIG_ITEM_DROPPED, .proc/on_drop)
|
||||
else //it's a mob
|
||||
var/mob/living/L = parent
|
||||
RegisterSignal(L, COMSIG_LIVING_RUN_BLOCK, .proc/living_block)
|
||||
holder = L
|
||||
var/to_add = charges >= 1 ? shield_state : broken_state
|
||||
if(to_add)
|
||||
var/mutable_appearance/M = mutable_appearance('icons/effects/effects.dmi', to_add)
|
||||
M.layer = (L.layer > MOB_LAYER ? L.layer : MOB_LAYER) + 0.01
|
||||
holder.add_overlay(M, TRUE)
|
||||
|
||||
/datum/component/shielded/UnregisterFromParent()
|
||||
. = ..()
|
||||
if(parent != holder) //not a mob, thus an item.
|
||||
UnregisterSignal(parent, list(COMSIG_ITEM_RUN_BLOCK,COMSIG_ITEM_CHECK_BLOCK,COMSIG_ITEM_EQUIPPED,COMSIG_ITEM_DROPPED))
|
||||
if(holder)
|
||||
UnregisterSignal(holder, list(COMSIG_LIVING_RUN_BLOCK, COMSIG_LIVING_GET_BLOCKING_ITEMS))
|
||||
var/to_remove = charges >= 1 ? shield_state : broken_state
|
||||
if(to_remove)
|
||||
holder.cut_overlay(mutable_appearance('icons/effects/effects.dmi', to_remove), TRUE)
|
||||
holder = null
|
||||
|
||||
/datum/component/shielded/process()
|
||||
if(world.time < last_time_used && !dissipating)
|
||||
return
|
||||
var/old_charges = charges
|
||||
charges = CLAMP(charges + recharge_rate, 0, max_charges)
|
||||
if(round(old_charges) >= round(charges)) //only send outputs if it effectively gained at least one charge
|
||||
return
|
||||
var/sound = recharge_sound
|
||||
if(dissipating ? !charges : charges == max_charges )
|
||||
STOP_PROCESSING(SSdcs, src)
|
||||
sound = recharge_end_sound
|
||||
if(parent && sound)
|
||||
playsound(parent, sound, 50, 1)
|
||||
if(charges < 1 && del_on_overload)
|
||||
if(holder)
|
||||
holder.visible_message("[holder]'s shield overloads!")
|
||||
qdel(src)
|
||||
return
|
||||
if(holder && (old_charges < 1 && charges >= 1) || (!del_on_overload && old_charges >= 1 && charges < 1))
|
||||
update_shield_overlay(charges < 1)
|
||||
|
||||
/datum/component/shielded/proc/adjust_charges(amount)
|
||||
var/old_charges = charges
|
||||
charges = CLAMP(charges + amount, 0, max_charges)
|
||||
if(recharge_delay && recharge_rate && (dissipating ? !charges : charges == max_charges))
|
||||
STOP_PROCESSING(SSdcs, src)
|
||||
if(charges < 1 && del_on_overload)
|
||||
if(holder)
|
||||
holder.visible_message("[holder]'s shield overloads!")
|
||||
qdel(src)
|
||||
return
|
||||
if(holder && (old_charges < 1 && charges >= 1) || (!del_on_overload && old_charges >= 1 && charges < 1))
|
||||
update_shield_overlay(charges < 1)
|
||||
|
||||
/datum/component/shielded/proc/update_shield_overlay(broken)
|
||||
if(!holder)
|
||||
return
|
||||
var/to_remove = broken ? shield_state : broken_state
|
||||
var/to_add = broken ? broken_state : shield_state
|
||||
if(to_remove)
|
||||
holder.cut_overlay(mutable_appearance('icons/effects/effects.dmi', to_remove), TRUE)
|
||||
if(to_add)
|
||||
var/mutable_appearance/M = mutable_appearance('icons/effects/effects.dmi', to_add)
|
||||
M.layer = (holder.layer > MOB_LAYER ? holder.layer : MOB_LAYER) + 0.01
|
||||
holder.add_overlay(M, TRUE)
|
||||
|
||||
/datum/component/shielded/proc/on_equip(obj/item/source, mob/living/equipper, slot)
|
||||
if(!(accepted_slots & slotdefine2slotbit(slot)))
|
||||
return
|
||||
holder = equipper
|
||||
RegisterSignal(parent, COMSIG_ITEM_RUN_BLOCK, .proc/on_run_block)
|
||||
RegisterSignal(parent, COMSIG_ITEM_CHECK_BLOCK, .proc/on_check_block)
|
||||
RegisterSignal(equipper, COMSIG_LIVING_GET_BLOCKING_ITEMS, .proc/include_shield)
|
||||
var/to_add = charges >= 1 ? shield_state : broken_state
|
||||
if(to_add)
|
||||
var/mutable_appearance/M = mutable_appearance('icons/effects/effects.dmi', to_add)
|
||||
M.layer = (holder.layer > MOB_LAYER ? holder.layer : MOB_LAYER) + 0.01
|
||||
equipper.add_overlay(M, TRUE)
|
||||
|
||||
/datum/component/shielded/proc/on_drop(obj/item/source, mob/dropper)
|
||||
if(holder == dropper)
|
||||
UnregisterSignal(holder, COMSIG_LIVING_GET_BLOCKING_ITEMS)
|
||||
UnregisterSignal(parent, list(COMSIG_ITEM_RUN_BLOCK, COMSIG_ITEM_CHECK_BLOCK))
|
||||
var/to_remove = charges >= 1 ? shield_state : broken_state
|
||||
if(to_remove)
|
||||
holder.cut_overlay(mutable_appearance('icons/effects/effects.dmi', to_remove), TRUE)
|
||||
holder = null
|
||||
|
||||
/datum/component/shielded/proc/include_shield(mob/source, list/items)
|
||||
items += parent
|
||||
|
||||
/datum/component/shielded/proc/on_run_block(obj/item/source, mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return)
|
||||
if(block_return[BLOCK_RETURN_NORMAL_BLOCK_CHANCE] >= 100) //already blocked by another shielded item, don't do anything.
|
||||
block_return[BLOCK_RETURN_BLOCK_CAPACITY] += round(charges)
|
||||
return BLOCK_NONE
|
||||
last_time_used = world.time + recharge_delay
|
||||
if(charges < 1)
|
||||
return BLOCK_NONE
|
||||
var/datum/effect_system/spark_spread/s = new
|
||||
s.set_up(2, 1, source)
|
||||
s.start()
|
||||
owner.visible_message("<span class='danger'>[holder]'s shields deflect [attack_text] in a shower of sparks!</span>")
|
||||
charges--
|
||||
var/rounded_charges = round(charges)
|
||||
if(recharge_delay && recharge_rate && !dissipating)
|
||||
START_PROCESSING(SSdcs, src)
|
||||
if(charges < 1)
|
||||
owner.visible_message("[holder]'s shield overloads!")
|
||||
if(del_on_overload)
|
||||
qdel(src)
|
||||
else
|
||||
update_shield_overlay(TRUE)
|
||||
block_return[BLOCK_RETURN_NORMAL_BLOCK_CHANCE] = 100
|
||||
block_return[BLOCK_RETURN_BLOCK_CAPACITY] += rounded_charges
|
||||
return BLOCK_SUCCESS | BLOCK_PHYSICAL_EXTERNAL
|
||||
|
||||
/datum/component/shielded/proc/on_check_block(obj/item/source, mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return)
|
||||
if(charges >= 1)
|
||||
block_return[BLOCK_RETURN_NORMAL_BLOCK_CHANCE] = 100
|
||||
block_return[BLOCK_RETURN_BLOCK_CAPACITY] += round(charges)
|
||||
|
||||
/datum/component/shielded/proc/living_block(mob/living/source, real_attack, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, return_list)
|
||||
if(!real_attack)
|
||||
if(charges >= 1)
|
||||
return_list[BLOCK_RETURN_NORMAL_BLOCK_CHANCE] = 100
|
||||
return_list[BLOCK_RETURN_BLOCK_CAPACITY] = round(charges)
|
||||
return
|
||||
last_time_used = world.time + recharge_delay
|
||||
if(charges < 1)
|
||||
return BLOCK_NONE
|
||||
var/datum/effect_system/spark_spread/s = new
|
||||
s.set_up(2, 1, source)
|
||||
s.start()
|
||||
source.visible_message("<span class='danger'>[source]'s shields deflect [attack_text] in a shower of sparks!</span>")
|
||||
charges--
|
||||
var/rounded_charges = round(charges)
|
||||
if(recharge_delay && recharge_rate && !dissipating)
|
||||
START_PROCESSING(SSdcs, src)
|
||||
if(charges < 1)
|
||||
source.visible_message("[source]'s shield overloads!")
|
||||
if(del_on_overload)
|
||||
qdel(src)
|
||||
else
|
||||
update_shield_overlay(TRUE)
|
||||
return_list[BLOCK_RETURN_NORMAL_BLOCK_CHANCE] = 100
|
||||
return_list[BLOCK_RETURN_BLOCK_CAPACITY] += rounded_charges
|
||||
return BLOCK_SUCCESS | BLOCK_PHYSICAL_EXTERNAL
|
||||
@@ -748,63 +748,20 @@
|
||||
allowed = null
|
||||
armor = list("melee" = 30, "bullet" = 15, "laser" = 30, "energy" = 10, "bomb" = 10, "bio" = 100, "rad" = 50, "fire" = 100, "acid" = 100)
|
||||
resistance_flags = FIRE_PROOF | ACID_PROOF
|
||||
var/current_charges = 3
|
||||
var/max_charges = 3 //How many charges total the shielding has
|
||||
var/current_charges //if null, will default to max_chargs
|
||||
var/recharge_delay = 200 //How long after we've been shot before we can start recharging. 20 seconds here
|
||||
var/recharge_cooldown = 0 //Time since we've last been shot
|
||||
var/recharge_rate = 1 //How quickly the shield recharges once it starts charging
|
||||
var/shield_state = "shield-old"
|
||||
var/shield_on = "shield-old"
|
||||
|
||||
/obj/item/clothing/suit/space/hardsuit/shielded/Initialize()
|
||||
. = ..()
|
||||
if(!allowed)
|
||||
allowed = GLOB.advanced_hardsuit_allowed
|
||||
|
||||
/obj/item/clothing/suit/space/hardsuit/shielded/check_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return)
|
||||
if(current_charges > 0)
|
||||
block_return[BLOCK_RETURN_NORMAL_BLOCK_CHANCE] = 100
|
||||
block_return[BLOCK_RETURN_BLOCK_CAPACITY] = (block_return[BLOCK_RETURN_BLOCK_CAPACITY] || 0) + current_charges
|
||||
return ..()
|
||||
|
||||
/obj/item/clothing/suit/space/hardsuit/shielded/run_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return)
|
||||
recharge_cooldown = world.time + recharge_delay
|
||||
if(current_charges > 0)
|
||||
var/datum/effect_system/spark_spread/s = new
|
||||
s.set_up(2, 1, src)
|
||||
s.start()
|
||||
owner.visible_message("<span class='danger'>[owner]'s shields deflect [attack_text] in a shower of sparks!</span>")
|
||||
current_charges--
|
||||
if(recharge_rate)
|
||||
START_PROCESSING(SSobj, src)
|
||||
if(current_charges <= 0)
|
||||
owner.visible_message("[owner]'s shield overloads!")
|
||||
shield_state = "broken"
|
||||
owner.update_inv_wear_suit()
|
||||
return BLOCK_SUCCESS | BLOCK_PHYSICAL_EXTERNAL
|
||||
return BLOCK_NONE
|
||||
|
||||
/obj/item/clothing/suit/space/hardsuit/shielded/Destroy()
|
||||
STOP_PROCESSING(SSobj, src)
|
||||
return ..()
|
||||
|
||||
/obj/item/clothing/suit/space/hardsuit/shielded/process()
|
||||
if(world.time > recharge_cooldown && current_charges < max_charges)
|
||||
current_charges = CLAMP((current_charges + recharge_rate), 0, max_charges)
|
||||
playsound(loc, 'sound/magic/charge.ogg', 50, 1)
|
||||
if(current_charges == max_charges)
|
||||
playsound(loc, 'sound/machines/ding.ogg', 50, 1)
|
||||
STOP_PROCESSING(SSobj, src)
|
||||
shield_state = "[shield_on]"
|
||||
if(ishuman(loc))
|
||||
var/mob/living/carbon/human/C = loc
|
||||
C.update_inv_wear_suit()
|
||||
|
||||
/obj/item/clothing/suit/space/hardsuit/shielded/worn_overlays(isinhands, icon_file, used_state, style_flags = NONE)
|
||||
/obj/item/clothing/suit/space/hardsuit/shielded/ComponentInitialize()
|
||||
. = ..()
|
||||
if(!isinhands)
|
||||
var/file2use = style_flags & STYLE_ALL_TAURIC ? 'modular_citadel/icons/mob/64x32_effects.dmi' : 'icons/effects/effects.dmi'
|
||||
. += mutable_appearance(file2use, shield_state, MOB_LAYER + 0.01)
|
||||
AddComponent(/datum/component/shielded, current_charges, max_charges, recharge_delay, recharge_rate, ITEM_SLOT_OCLOTHING, shield_state)
|
||||
|
||||
/obj/item/clothing/head/helmet/space/hardsuit/shielded
|
||||
resistance_flags = FIRE_PROOF | ACID_PROOF
|
||||
@@ -833,7 +790,6 @@
|
||||
hardsuit_type = "ert_security"
|
||||
helmettype = /obj/item/clothing/head/helmet/space/hardsuit/shielded/ctf/red
|
||||
shield_state = "shield-red"
|
||||
shield_on = "shield-red"
|
||||
|
||||
/obj/item/clothing/suit/space/hardsuit/shielded/ctf/blue
|
||||
name = "blue shielded hardsuit"
|
||||
@@ -896,7 +852,6 @@
|
||||
item_state = "swat_suit"
|
||||
hardsuit_type = "syndi"
|
||||
max_charges = 4
|
||||
current_charges = 4
|
||||
recharge_delay = 15
|
||||
armor = list("melee" = 80, "bullet" = 80, "laser" = 50, "energy" = 50, "bomb" = 100, "bio" = 100, "rad" = 100, "fire" = 100, "acid" = 100)
|
||||
strip_delay = 130
|
||||
|
||||
@@ -199,10 +199,9 @@
|
||||
icon_state = "battlemage"
|
||||
item_state = "battlemage"
|
||||
recharge_rate = 0
|
||||
max_charges = INFINITY
|
||||
current_charges = 15
|
||||
recharge_cooldown = INFINITY
|
||||
shield_state = "shield-red"
|
||||
shield_on = "shield-red"
|
||||
min_cold_protection_temperature = ARMOR_MIN_TEMP_PROTECT
|
||||
max_heat_protection_temperature = ARMOR_MAX_TEMP_PROTECT
|
||||
helmettype = /obj/item/clothing/head/helmet/space/hardsuit/shielded/wizard
|
||||
@@ -243,6 +242,7 @@
|
||||
if(!istype(W))
|
||||
to_chat(user, "<span class='warning'>The rune can only be used on battlemage armour!</span>")
|
||||
return
|
||||
W.current_charges += 8
|
||||
to_chat(user, "<span class='notice'>You charge \the [W]. It can now absorb [W.current_charges] hits.</span>")
|
||||
var/datum/component/shielded/S = GetComponent(/datum/component/shielded)
|
||||
S.adjust_charges(8)
|
||||
to_chat(user, "<span class='notice'>You charge \the [W]. It can now absorb [S.charges] hits.</span>")
|
||||
qdel(src)
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
/mob/living/carbon/human/get_blocking_items()
|
||||
. = ..()
|
||||
if(wear_suit)
|
||||
. += wear_suit
|
||||
. |= wear_suit
|
||||
if(w_uniform)
|
||||
. += w_uniform
|
||||
. |= w_uniform
|
||||
if(wear_neck)
|
||||
. += wear_neck
|
||||
. |= wear_neck
|
||||
|
||||
@@ -71,6 +71,7 @@
|
||||
/// Gets an unsortedlist of objects to run block checks on.
|
||||
/mob/living/proc/get_blocking_items()
|
||||
. = list()
|
||||
SEND_SIGNAL(src, COMSIG_LIVING_GET_BLOCKING_ITEMS, .)
|
||||
for(var/obj/item/I in held_items)
|
||||
// this is a bad check but i am not removing it until a better catchall is made
|
||||
if(istype(I, /obj/item/clothing))
|
||||
@@ -85,11 +86,13 @@
|
||||
|
||||
/// Runs block and returns flag for do_run_block to process.
|
||||
/obj/item/proc/run_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return)
|
||||
SEND_SIGNAL(src, COMSIG_ITEM_RUN_BLOCK, owner, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, final_block_chance, block_return)
|
||||
. = SEND_SIGNAL(src, COMSIG_ITEM_RUN_BLOCK, owner, object, damage, attack_text, attack_type, armour_penetration, attacker, def_zone, final_block_chance, block_return)
|
||||
if(. & BLOCK_SUCCESS)
|
||||
return
|
||||
if(prob(final_block_chance))
|
||||
owner.visible_message("<span class='danger'>[owner] blocks [attack_text] with [src]!</span>")
|
||||
return BLOCK_SUCCESS | BLOCK_PHYSICAL_EXTERNAL
|
||||
return BLOCK_NONE
|
||||
return . | BLOCK_SUCCESS | BLOCK_PHYSICAL_EXTERNAL
|
||||
return . | BLOCK_NONE
|
||||
|
||||
/// Returns block information using list/block_return. Used for check_block() on mobs.
|
||||
/obj/item/proc/check_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return)
|
||||
|
||||
@@ -410,6 +410,7 @@
|
||||
#include "code\datums\components\remote_materials.dm"
|
||||
#include "code\datums\components\riding.dm"
|
||||
#include "code\datums\components\rotation.dm"
|
||||
#include "code\datums\components\shielded.dm"
|
||||
#include "code\datums\components\shrapnel.dm"
|
||||
#include "code\datums\components\shrink.dm"
|
||||
#include "code\datums\components\sizzle.dm"
|
||||
|
||||
Reference in New Issue
Block a user