Files
CHOMPStation2/code/game/objects/items/weapons/shields.dm
CHOMPStation2StaffMirrorBot f7de0bb70b [MIRROR] Start of TG Click Code Port (#12071)
Co-authored-by: Cameron Lennox <killer65311@gmail.com>
2025-12-06 03:18:32 -05:00

267 lines
9.2 KiB
Plaintext

//** Shield Helpers
//These are shared by various items that have shield-like behaviour
//bad_arc is the ABSOLUTE arc of directions from which we cannot block. If you want to fix it to e.g. the user's facing you will need to rotate the dirs yourself.
/proc/check_shield_arc(mob/user, var/bad_arc, atom/damage_source = null, mob/attacker = null)
//check attack direction
var/attack_dir = 0 //direction from the user to the source of the attack
if(istype(damage_source, /obj/item/projectile))
var/obj/item/projectile/P = damage_source
attack_dir = get_dir(get_turf(user), P.starting)
else if(attacker)
attack_dir = get_dir(get_turf(user), get_turf(attacker))
else if(damage_source)
attack_dir = get_dir(get_turf(user), get_turf(damage_source))
if(!(attack_dir && (attack_dir & bad_arc)))
return 1
return 0
/proc/default_parry_check(mob/user, mob/attacker, atom/damage_source)
//parry only melee attacks
if(istype(damage_source, /obj/item/projectile) || (attacker && get_dist(user, attacker) > 1) || user.incapacitated())
return 0
//block as long as they are not directly behind us
var/bad_arc = reverse_direction(user.dir) //arc of directions from which we cannot block
if(!check_shield_arc(user, bad_arc, damage_source, attacker))
return 0
return 1
/obj/item/proc/unique_parry_check(mob/user, mob/attacker, atom/damage_source) // An overrideable version of the above proc.
return default_parry_check(user, attacker, damage_source)
/obj/item/shield
name = "shield"
var/base_block_chance = 50
preserve_item = 1
item_icons = list(
slot_l_hand_str = 'icons/mob/items/lefthand_melee.dmi',
slot_r_hand_str = 'icons/mob/items/righthand_melee.dmi',
)
/obj/item/shield/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack")
if(user.incapacitated())
return 0
//block as long as they are not directly behind us
var/bad_arc = reverse_direction(user.dir) //arc of directions from which we cannot block
if(check_shield_arc(user, bad_arc, damage_source, attacker))
if(prob(get_block_chance(user, damage, damage_source, attacker)))
user.visible_message(span_danger("\The [user] blocks [attack_text] with \the [src]!"))
return 1
return 0
/obj/item/shield/proc/get_block_chance(mob/user, var/damage, atom/damage_source = null, mob/attacker = null)
return base_block_chance
/obj/item/shield/riot
name = "riot shield"
desc = "A shield adept for close quarters engagement. It's also capable of protecting from less powerful projectiles."
icon = 'icons/obj/weapons.dmi'
icon_state = "riot"
slot_flags = SLOT_BACK
force = 5.0
throwforce = 5.0
throw_speed = 1
throw_range = 4
w_class = ITEMSIZE_LARGE
origin_tech = list(TECH_MATERIAL = 2)
matter = list(MAT_GLASS = 7500, MAT_STEEL = 1000)
attack_verb = list("shoved", "bashed")
var/cooldown = 0 //shield bash cooldown. based on world.time
/obj/item/shield/riot/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack")
if(user.incapacitated())
return 0
//block as long as they are not directly behind us
var/bad_arc = reverse_direction(user.dir) //arc of directions from which we cannot block
if(check_shield_arc(user, bad_arc, damage_source, attacker))
if(prob(get_block_chance(user, damage, damage_source, attacker)))
//At this point, we succeeded in our roll for a block attempt, however these kinds of shields struggle to stand up
//to strong bullets and lasers. They still do fine to pistol rounds of all kinds, however.
if(istype(damage_source, /obj/item/projectile))
var/obj/item/projectile/P = damage_source
if((is_sharp(P) && P.armor_penetration >= 10) || istype(P, /obj/item/projectile/beam))
//If we're at this point, the bullet/beam is going to go through the shield, however it will hit for less damage.
//Bullets get slowed down, while beams are diffused as they hit the shield, so these shields are not /completely/
//useless. Extremely penetrating projectiles will go through the shield without less damage.
user.visible_message(span_danger("\The [user]'s [src.name] is pierced by [attack_text]!"))
if(P.armor_penetration < 30) //PTR bullets and x-rays will bypass this entirely.
P.damage = P.damage / 2
return 0
//Otherwise, if we're here, we're gonna stop the attack entirely.
user.visible_message(span_danger("\The [user] blocks [attack_text] with \the [src]!"))
playsound(src, 'sound/weapons/genhit.ogg', 50, 1)
return 1
return 0
/obj/item/shield/riot/attackby(obj/item/W as obj, mob/user as mob)
if(istype(W, /obj/item/melee/baton))
if(cooldown < world.time - 25)
user.visible_message(span_warning("[user] bashes [src] with [W]!"))
playsound(src, 'sound/effects/shieldbash.ogg', 50, 1)
cooldown = world.time
else
..()
/*
* Energy Shield
*/
/obj/item/shield/energy
name = "energy combat shield"
desc = "A shield capable of stopping most projectile and melee attacks. It can be retracted, expanded, and stored anywhere."
icon = 'icons/obj/weapons.dmi'
icon_state = "eshield"
item_state = "eshield"
slot_flags = SLOT_EARS
flags = NOCONDUCT
force = 3.0
throwforce = 5.0
throw_speed = 1
throw_range = 4
w_class = ITEMSIZE_SMALL
var/lrange = 1.5
var/lpower = 1.5
var/lcolor = "#006AFF"
origin_tech = list(TECH_MATERIAL = 4, TECH_MAGNET = 3, TECH_ILLEGAL = 4)
attack_verb = list("shoved", "bashed")
var/active = 0
item_icons = list(
slot_l_hand_str = 'icons/mob/items/lefthand_melee.dmi',
slot_r_hand_str = 'icons/mob/items/righthand_melee.dmi',
)
/obj/item/shield/energy/handle_shield(mob/user)
if(!active)
return 0 //turn it on first!
. = ..()
if(.)
var/datum/effect/effect/system/spark_spread/spark_system = new /datum/effect/effect/system/spark_spread()
spark_system.set_up(5, 0, user.loc)
spark_system.start()
playsound(src, 'sound/weapons/blade1.ogg', 50, 1)
/obj/item/shield/energy/get_block_chance(mob/user, var/damage, atom/damage_source = null, mob/attacker = null)
if(istype(damage_source, /obj/item/projectile))
var/obj/item/projectile/P = damage_source
if((is_sharp(P) && damage > 10) || istype(P, /obj/item/projectile/beam))
return (base_block_chance - round(damage / 3)) //block bullets and beams using the old block chance
return base_block_chance
/obj/item/shield/energy/attack_self(mob/living/user as mob)
if ((CLUMSY in user.mutations) && prob(50))
to_chat(user, span_warning("You beat yourself in the head with [src]."))
user.take_organ_damage(5)
active = !active
if (active)
force = 10
update_icon()
w_class = ITEMSIZE_LARGE
slot_flags = null
playsound(src, 'sound/weapons/saberon.ogg', 50, 1)
to_chat(user, span_notice("\The [src] is now active."))
else
force = 3
update_icon()
w_class = ITEMSIZE_TINY
slot_flags = SLOT_EARS
playsound(src, 'sound/weapons/saberoff.ogg', 50, 1)
to_chat(user, span_notice("\The [src] can now be concealed."))
if(ishuman(user))
var/mob/living/carbon/human/H = user
H.update_inv_l_hand()
H.update_inv_r_hand()
add_fingerprint(user)
return
/obj/item/shield/energy/update_icon()
var/mutable_appearance/blade_overlay = mutable_appearance(icon, "[icon_state]_blade")
if(lcolor)
blade_overlay.color = lcolor
color = lcolor
cut_overlays() //So that it doesn't keep stacking overlays non-stop on top of each other
if(active)
add_overlay(blade_overlay)
item_state = "[icon_state]_blade"
set_light(lrange, lpower, lcolor)
else
color = "FFFFFF"
set_light(0)
item_state = "[icon_state]"
if(ishuman(usr))
var/mob/living/carbon/human/H = usr
H.update_inv_l_hand()
H.update_inv_r_hand()
/obj/item/shield/energy/click_alt(mob/living/user)
if(!in_range(src, user)) //Basic checks to prevent abuse
return
if(user.incapacitated() || !istype(user))
to_chat(user, span_warning("You can't do that right now!"))
return
if(tgui_alert(user, "Are you sure you want to recolor your shield?", "Confirm Recolor", list("Yes", "No")) == "Yes")
var/energy_color_input = tgui_color_picker(user,"","Choose Energy Color",lcolor)
if(energy_color_input)
lcolor = sanitize_hexcolor(energy_color_input)
update_icon()
/obj/item/shield/energy/examine(mob/user)
. = ..()
. += span_notice("Alt-click to recolor it.")
/obj/item/shield/riot/tele
name = "telescopic shield"
desc = "An advanced riot shield made of lightweight materials that collapses for easy storage."
icon = 'icons/obj/weapons.dmi'
icon_state = "teleriot0"
slot_flags = null
force = 3
throwforce = 3
throw_speed = 3
throw_range = 4
w_class = ITEMSIZE_NORMAL
var/active = 0
/*
/obj/item/shield/energy/IsShield()
if(active)
return 1
else
return 0
*/
/obj/item/shield/riot/tele/attack_self(mob/living/user)
active = !active
icon_state = "teleriot[active]"
playsound(src, 'sound/weapons/empty.ogg', 50, 1)
if(active)
force = 8
throwforce = 5
throw_speed = 2
w_class = ITEMSIZE_LARGE
slot_flags = SLOT_BACK
to_chat(user, span_notice("You extend \the [src]."))
else
force = 3
throwforce = 3
throw_speed = 3
w_class = ITEMSIZE_NORMAL
slot_flags = null
to_chat(user, span_notice("[src] can now be concealed."))
if(ishuman(user))
var/mob/living/carbon/human/H = user
H.update_inv_l_hand()
H.update_inv_r_hand()
add_fingerprint(user)
return