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

Gun Safeties #2443

Merged
merged 7 commits into from
Nov 10, 2023
Merged
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
54 changes: 51 additions & 3 deletions code/modules/projectiles/gun.dm
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,12 @@
///Color of the muzzle flash effect.
var/muzzle_flash_color = COLOR_VERY_SOFT_YELLOW

//gun saftey
///Does this gun have a saftey and thus can toggle it?
var/has_safety = FALSE
///If the saftey on? If so, we can't fire the weapon
var/safety = FALSE

/obj/item/gun/Initialize()
. = ..()
RegisterSignal(src, COMSIG_TWOHANDED_WIELD, .proc/on_wield)
Expand Down Expand Up @@ -198,6 +204,9 @@
else if(can_bayonet)
. += "It has a <b>bayonet</b> lug on it."

if(has_safety)
. += "The safety is [safety ? "<span class='green'>ON</span>" : "<span class='red'>OFF</span>"]. Ctrl-Click to toggle the safety."

/obj/item/gun/equipped(mob/living/user, slot)
. = ..()
if(zoomed && user.get_active_held_item() != src)
Expand All @@ -210,11 +219,16 @@
//check if there's enough ammo/energy/whatever to shoot one time
//i.e if clicking would make it shoot
/obj/item/gun/proc/can_shoot()
if(safety)
return FALSE
return TRUE

/obj/item/gun/proc/shoot_with_empty_chamber(mob/living/user as mob|obj)
to_chat(user, "<span class='danger'>*[dry_fire_text]*</span>") //WS Edit - Dry firing
playsound(src, dry_fire_sound, 30, TRUE)
if(!safety)
to_chat(user, "<span class='danger'>*[dry_fire_text]*</span>")
playsound(src, dry_fire_sound, 30, TRUE)
return
to_chat(user, "<span class='danger'>Safeties are active on the [src]! Turn them off to fire!</span>")


/obj/item/gun/proc/shoot_live_shot(mob/living/user, pointblank = 0, atom/pbtarget = null, message = 1)
Expand Down Expand Up @@ -484,6 +498,25 @@
else
return ..()

/obj/item/gun/CtrlClick(mob/user)
. = ..()
if(!has_safety)
return

if(src == !user.get_active_held_item())
return

playsound(user, 'sound/weapons/gun/general/selector.ogg', 100, TRUE)
safety = !safety

user.visible_message(
span_notice("[user] turns the safety on [src] [safety ? "<span class='green'>ON</span>" : "<span class='red'>OFF</span>"]."),
span_notice("You turn the safety on [src] [safety ? "<span class='green'>ON</span>" : "<span class='red'>OFF</span>"]."),
vision_distance = COMBAT_MESSAGE_RANGE
)
update_appearance()


/obj/item/gun/screwdriver_act(mob/living/user, obj/item/I)
. = ..()
if(.)
Expand Down Expand Up @@ -634,13 +667,19 @@
var/datum/action/A = X
A.UpdateButtonIcon()

/obj/item/gun/attack_hand(mob/user)
. = ..()
update_appearance()

/obj/item/gun/pickup(mob/user)
..()
. = ..()
update_appearance()
if(azoom)
azoom.Grant(user)

/obj/item/gun/dropped(mob/user)
. = ..()
update_appearance()
if(azoom)
azoom.Remove(user)
if(zoomed)
Expand Down Expand Up @@ -669,6 +708,15 @@
knife_overlay.pixel_y = knife_y_offset
. += knife_overlay

if(ismob(loc) && has_safety)
var/mutable_appearance/safety_overlay
safety_overlay = mutable_appearance('icons/obj/guns/safety.dmi')
if(safety)
safety_overlay.icon_state = "safety-on"
else
safety_overlay.icon_state = "safety-off"
. += safety_overlay

/obj/item/gun/proc/handle_suicide(mob/living/carbon/human/user, mob/living/carbon/human/target, params, bypass_timer)
if(!ishuman(user) || !ishuman(target))
return
Expand Down
7 changes: 6 additions & 1 deletion code/modules/projectiles/guns/ballistic.dm
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
icon_state = "pistol"
w_class = WEIGHT_CLASS_NORMAL

has_safety = TRUE
safety = TRUE

///sound when inserting magazine
var/load_sound = 'sound/weapons/gun/general/magazine_insert_full.ogg'
///sound when inserting an empty magazine
Expand Down Expand Up @@ -88,7 +91,7 @@
///Whether the gun can be tacloaded by slapping a fresh magazine directly on it
var/tac_reloads = TRUE //Snowflake mechanic no more.
///If we have the 'snowflake mechanic,' how long should it take to reload?
var/tactical_reload_delay = 1.2 SECONDS
var/tactical_reload_delay = 1 SECONDS

/obj/item/gun/ballistic/Initialize()
. = ..()
Expand Down Expand Up @@ -242,6 +245,8 @@
update_appearance()

/obj/item/gun/ballistic/can_shoot()
if(safety)
return FALSE
return chambered

/obj/item/gun/ballistic/attackby(obj/item/A, mob/user, params)
Expand Down
3 changes: 3 additions & 0 deletions code/modules/projectiles/guns/ballistic/pistol.dm
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,9 @@
can_suppress = FALSE
var/random_icon = TRUE

has_safety = FALSE //thing barely costs anything, why would it have a safety?
safety = FALSE

/obj/item/gun/ballistic/automatic/pistol/disposable/Initialize()
. = ..()
var/picked = pick("none","red","purple","yellow","green","dark")
Expand Down
3 changes: 3 additions & 0 deletions code/modules/projectiles/guns/ballistic/revolver.dm
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@
bolt_wording = "hammer"
wield_slowdown = 0.3

has_safety = FALSE //irl revolvers dont have safetys. i think. maybe
safety = FALSE

/obj/item/gun/ballistic/revolver/examine(mob/user)
. = ..()
. += "<span class='info'>You can use the revolver with your <b>other empty hand</b> to empty the cylinder.</span>"
Expand Down
9 changes: 7 additions & 2 deletions code/modules/projectiles/guns/energy.dm
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
muzzleflash_iconstate = "muzzle_flash_laser"
muzzle_flash_color = COLOR_SOFT_RED

has_safety = TRUE
safety = TRUE

var/obj/item/stock_parts/cell/gun/cell //What type of power cell this uses
var/cell_type = /obj/item/stock_parts/cell/gun
var/modifystate = 0
Expand Down Expand Up @@ -147,7 +150,9 @@
eject_cell(user)
return ..()

/obj/item/gun/energy/can_shoot()
/obj/item/gun/energy/can_shoot(visuals)
if(safety && !visuals)
return FALSE
var/obj/item/ammo_casing/energy/shot = ammo_type[select]
return !QDELETED(cell) ? (cell.charge >= shot.e_cost) : FALSE

Expand Down Expand Up @@ -247,7 +252,7 @@

///Used by update_icon_state() and update_overlays()
/obj/item/gun/energy/proc/get_charge_ratio()
return can_shoot() ? CEILING(clamp(cell.charge / cell.maxcharge, 0, 1) * charge_sections, 1) : 0
return can_shoot(visuals = TRUE) ? CEILING(clamp(cell.charge / cell.maxcharge, 0, 1) * charge_sections, 1) : 0
// Sets the ratio to 0 if the gun doesn't have enough charge to fire, or if its power cell is removed.

/obj/item/gun/energy/vv_edit_var(var_name, var_value)
Expand Down
2 changes: 1 addition & 1 deletion code/modules/projectiles/guns/energy/special.dm
Original file line number Diff line number Diff line change
Expand Up @@ -384,7 +384,7 @@
return
return ..()

/obj/item/gun/energy/gravity_gun/can_shoot()
/obj/item/gun/energy/gravity_gun/can_shoot(visuals)
if(!firing_core)
return FALSE
return ..()
Expand Down
1 change: 1 addition & 0 deletions code/modules/unit_tests/projectiles.dm
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
gunner.put_in_hands(test_gun, forced=TRUE)
var/expected_damage = loaded_bullet.damage
loaded_bullet.def_zone = BODY_ZONE_CHEST
test_gun.safety = FALSE //So we can shoot the gun
var/did_we_shoot = test_gun.afterattack(victim, gunner)
TEST_ASSERT(did_we_shoot, "Gun does not appeared to have successfully fired.")
TEST_ASSERT_EQUAL(victim.getBruteLoss(), expected_damage, "Victim took incorrect amount of damage, expected [expected_damage], got [victim.getBruteLoss()].")
Expand Down
Binary file added icons/obj/guns/safety.dmi
Binary file not shown.