Shielded element.

This commit is contained in:
Ghommie
2020-04-19 01:48:51 +02:00
parent 19328b8710
commit e3fb978465
8 changed files with 229 additions and 59 deletions
+1 -1
View File
@@ -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
+1
View File
@@ -197,6 +197,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)
+210
View File
@@ -0,0 +1,210 @@
/datum/element/shielded
element_flags = ELEMENT_BESPOKE|ELEMENT_DETACH
id_arg_index = 3
var/max_charges = 3
var/recharge_delay = 20 SECONDS //How long after we've been shot before we can start recharging.
var/recharge_rate = 1 //How quickly the shield recharges once it starts charging. Can be a decimal.
var/accepted_slots
var/shield_state = "shield-old" //the state of the shield overlay.
var/broken_state //null
var/recharge_sound = 'sound/magic/charge.ogg'
var/recharge_end_sound = 'sound/machines/ding.ogg'
var/list/charges_per_atom = list() //How many charges each atom has.
var/list/last_use_per_atom = list() //Last time an atom has deflected (or failed due to no charges) an attack.
var/list/overlay_per_mob = list() //List of mutable overlays per atom.
var/list/shields_per_mob = list() //number of shields this guy is on, damn.
/datum/element/shielded/Attach(datum/target, current, _max = 3, _delay = 20 SECONDS, _rate = 1, _slots, _state = "shield-old", _broken, _sound = 'sound/magic/charge.ogg', _end_sound = 'sound/machines/ding.ogg')
. = ..()
var/isitem = isitem(target)
if(. == ELEMENT_INCOMPATIBLE || (!isitem && !isliving(target)))
return ELEMENT_INCOMPATIBLE
max_charges = _max
recharge_delay = _delay
recharge_rate = _rate
accepted_slots = _slots
shield_state = _state
broken_state = _broken
recharge_sound = 'sound/magic/charge.ogg'
recharge_end_sound = 'sound/machines/ding.ogg'
if(isitem)
RegisterSignal(target, COMSIG_ITEM_RUN_BLOCK, .proc/on_run_block)
RegisterSignal(target, COMSIG_ITEM_CHECK_BLOCK, .proc/on_check_block)
RegisterSignal(target, COMSIG_ITEM_EQUIPPED, .proc/on_equip)
RegisterSignal(target, COMSIG_ITEM_DROPPED, .proc/on_drop)
else
RegisterSignal(target, COMSIG_LIVING_RUN_BLOCK, .proc/living_block)
var/prior_shields = shields_per_mob[target]
if(prior_shields)
if(!islist(prior_shields))
shields_per_mob[target] = list(prior_shields, target)
else
prior_shields += target
else
var/mob/living/L = target
var/mutable_appearance/M = mutable_appearance('icons/effects/effects.dmi', shield_state, MOB_LAYER + 0.01)
overlay_per_mob[L] = M
L.add_overlay(M, TRUE)
shields_per_mob[L] = L
charges_per_atom[target] = !isnull(current) ? current : max_charges
last_use_per_atom[target] = 0
if(recharge_delay)
START_PROCESSING(SSdcs, src)
/datum/element/shielded/Detach(atom/target)
var/mob/living/L
if(isitem(target))
UnregisterSignal(target, list(COMSIG_ITEM_RUN_BLOCK,COMSIG_ITEM_CHECK_BLOCK,COMSIG_ITEM_EQUIPPED,COMSIG_ITEM_DROPPED))
L = isliving(target.loc) ? target.loc : null
else
UnregisterSignal(target, COMSIG_LIVING_RUN_BLOCK)
L = src
if(L)
var/list/shields = shields_per_mob[L]
if(!shields)
return ..()
if(shields == target) //nothing left.
shields_per_mob -= L
var/mutable_appearance/M = overlay_per_mob[L]
L.cut_overlay(M, TRUE)
overlay_per_mob -= L
UnregisterSignal(target, COMSIG_LIVING_GET_BLOCKING_ITEMS)
else //more layers of shielding.
shields -= target
if(length(shields) == 1)
shields_per_mob[L] = shields[1]
charges_per_atom -= target
last_use_per_atom -= target
if(recharge_delay && !length(charges_per_atom)) //nothing left to process.
STOP_PROCESSING(SSdcs, src)
return ..()
/datum/element/shielded/process()
var/list/checked = list()
for(var/i in last_use_per_atom)
var/atom/movable/A = i
recharge(A, recharge_rate, checked)
/datum/element/shielded/proc/recharge(atom/movable/A, amount, list/checked = list())
var/old_charges = charges_per_atom[A]
if(old_charges >= max_charges || world.time < last_use_per_atom[A])
return
var/new_charges = min(old_charges + recharge_rate, max_charges)
charges_per_atom[A] = new_charges
if(new_charges % 1)
return
var/mob/living/L
var/skip_in = FALSE
if(isitem(A))
L = isliving(A.loc) ? A.loc : null
else
L = A
skip_in = TRUE
if(L && checked[L])
return
playsound(A, recharge_sound, 50, 1)
if(new_charges == max_charges)
playsound(A, recharge_sound, 50, 1)
if(L && (skip_in || (A in shields_per_mob[L])))
var/mutable_appearance/M = overlay_per_mob[L]
M.icon_state = shield_state
checked[L] = TRUE
/datum/element/shielded/proc/on_equip(obj/item/source, mob/equipper, slot)
if(!(accepted_slots & slotdefine2slotbit(slot)))
return
var/list/shields = shields_per_mob[equipper]
if(!shields) //They have none
RegisterSignal(equipper, COMSIG_LIVING_GET_BLOCKING_ITEMS, .proc/get_shields)
shields_per_mob[equipper] = source
var/mutable_appearance/M = mutable_appearance('icons/effects/effects.dmi', charges_per_atom[source] ? shield_state : broken_state, MOB_LAYER + 0.01)
overlay_per_mob[equipper] = M
equipper.add_overlay(M, TRUE)
else if(!islist(shields)) //They have one
if(shields == equipper)
RegisterSignal(equipper, COMSIG_LIVING_GET_BLOCKING_ITEMS, .proc/get_shields)
shields_per_mob[equipper] = list(shields, source)
else //They have more.
shields += source
/datum/element/shielded/proc/on_drop(obj/item/source, mob/dropper)
var/list/shields = shields_per_mob[dropper]
if(!shields)
return
if(shields == source)
UnregisterSignal(dropper, COMSIG_LIVING_GET_BLOCKING_ITEMS)
var/mutable_appearance/M = overlay_per_mob[dropper]
dropper.cut_overlay(M, TRUE)
overlay_per_mob -= dropper
shields_per_mob -= dropper
else
shields -= source
if(length(shields) == 1)
shields_per_mob[dropper] = shields[1]
/datum/element/shielded/proc/get_shields(mob/source, list/items)
items += shields_per_mob[source]
/datum/element/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_per_atom[source])
return BLOCK_NONE
last_use_per_atom[source] = world.time + recharge_delay
if(charges_per_atom[source] < 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'>[owner]'s shields deflect [attack_text] in a shower of sparks!</span>")
var/charges_left = --charges_per_atom[source]
if(charges_left < 1)
var/list/shields = shields_per_mob[owner]
var/vis_change = TRUE
if(istype(shields))
for(var/A in shields)
if(charges_per_atom >= 1)
vis_change = FALSE
break
owner.visible_message("[owner]'s shield overloads!")
if(vis_change)
var/mutable_appearance/M = overlay_per_mob[owner]
M.icon_state = broken_state
block_return[BLOCK_RETURN_NORMAL_BLOCK_CHANCE] = 100
block_return[BLOCK_RETURN_BLOCK_CAPACITY] += charges_left
return BLOCK_SUCCESS | BLOCK_PHYSICAL_EXTERNAL //it's an energy field surrounding you after all.
/datum/element/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_per_atom[source] < 1)
return
block_return[BLOCK_RETURN_NORMAL_BLOCK_CHANCE] = 100
block_return[BLOCK_RETURN_BLOCK_CAPACITY] += round(charges_per_atom[source])
/datum/element/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_per_atom[source] >= 1)
return_list[BLOCK_RETURN_NORMAL_BLOCK_CHANCE] = 100
return_list[BLOCK_RETURN_BLOCK_CAPACITY] = round(charges_per_atom[source])
return
last_use_per_atom[source] = world.time + recharge_delay
if(charges_per_atom[source] < 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>")
var/charges_left = --charges_per_atom[source]
if(charges_left < 1)
var/list/shields = shields_per_mob[source]
var/vis_change = TRUE
if(istype(shields))
for(var/A in shields)
if(charges_per_atom >= 1)
vis_change = FALSE
break
source.visible_message("[source]'s shield overloads!")
if(vis_change)
var/mutable_appearance/M = overlay_per_mob[source]
M.icon_state = broken_state
return_list[BLOCK_RETURN_NORMAL_BLOCK_CHANCE] = 100
return_list[BLOCK_RETURN_BLOCK_CAPACITY] += charges_left
return BLOCK_SUCCESS | BLOCK_PHYSICAL_EXTERNAL
+3 -48
View File
@@ -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)
AddElement(/datum/element/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
+4 -4
View File
@@ -195,10 +195,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
@@ -239,6 +238,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/element/shielded/S = SSdcs.GetElement(list(/datum/element/shielded, 0, W.max_charges, W.recharge_delay, W.recharge_rate, ITEM_SLOT_OCLOTHING, W.shield_state))
S.recharge(W, 8)
to_chat(user, "<span class='notice'>You charge \the [W]. It can now absorb [S.charges_per_atom[W]] 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
+6 -3
View File
@@ -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)
+1
View File
@@ -514,6 +514,7 @@
#include "code\datums\elements\ghost_role_eligibility.dm"
#include "code\datums\elements\mob_holder.dm"
#include "code\datums\elements\polychromic.dm"
#include "code\datums\elements\shielded.dm"
#include "code\datums\elements\spellcasting.dm"
#include "code\datums\elements\swimming.dm"
#include "code\datums\elements\sword_point.dm"