diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm index 0bfccb6b9113..628fd38d2e5e 100644 --- a/code/modules/projectiles/gun.dm +++ b/code/modules/projectiles/gun.dm @@ -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_REF(on_wield)) @@ -198,6 +204,9 @@ else if(can_bayonet) . += "It has a bayonet lug on it." + if(has_safety) + . += "The safety is [safety ? "ON" : "OFF"]. Ctrl-Click to toggle the safety." + /obj/item/gun/equipped(mob/living/user, slot) . = ..() if(zoomed && user.get_active_held_item() != src) @@ -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, "*[dry_fire_text]*") //WS Edit - Dry firing - playsound(src, dry_fire_sound, 30, TRUE) + if(!safety) + to_chat(user, "*[dry_fire_text]*") + playsound(src, dry_fire_sound, 30, TRUE) + return + to_chat(user, "Safeties are active on the [src]! Turn them off to fire!") /obj/item/gun/proc/shoot_live_shot(mob/living/user, pointblank = 0, atom/pbtarget = null, message = 1) @@ -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 ? "ON" : "OFF"]."), + span_notice("You turn the safety on [src] [safety ? "ON" : "OFF"]."), + vision_distance = COMBAT_MESSAGE_RANGE + ) + update_appearance() + + /obj/item/gun/screwdriver_act(mob/living/user, obj/item/I) . = ..() if(.) @@ -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) @@ -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 diff --git a/code/modules/projectiles/guns/ballistic.dm b/code/modules/projectiles/guns/ballistic.dm index 90bcf0c73508..a8e2a201c81a 100644 --- a/code/modules/projectiles/guns/ballistic.dm +++ b/code/modules/projectiles/guns/ballistic.dm @@ -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 @@ -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() . = ..() @@ -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) diff --git a/code/modules/projectiles/guns/ballistic/pistol.dm b/code/modules/projectiles/guns/ballistic/pistol.dm index b466f2811dd4..591bc7eafa3d 100644 --- a/code/modules/projectiles/guns/ballistic/pistol.dm +++ b/code/modules/projectiles/guns/ballistic/pistol.dm @@ -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") diff --git a/code/modules/projectiles/guns/ballistic/revolver.dm b/code/modules/projectiles/guns/ballistic/revolver.dm index 1e922d26aed0..74c7f95d0db2 100644 --- a/code/modules/projectiles/guns/ballistic/revolver.dm +++ b/code/modules/projectiles/guns/ballistic/revolver.dm @@ -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) . = ..() . += "You can use the revolver with your other empty hand to empty the cylinder." diff --git a/code/modules/projectiles/guns/energy.dm b/code/modules/projectiles/guns/energy.dm index 2a16164c6119..681d79338a52 100644 --- a/code/modules/projectiles/guns/energy.dm +++ b/code/modules/projectiles/guns/energy.dm @@ -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 @@ -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 @@ -252,7 +257,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) diff --git a/code/modules/projectiles/guns/energy/special.dm b/code/modules/projectiles/guns/energy/special.dm index 9226a587e4aa..2037e77fbed0 100644 --- a/code/modules/projectiles/guns/energy/special.dm +++ b/code/modules/projectiles/guns/energy/special.dm @@ -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 ..() diff --git a/code/modules/unit_tests/projectiles.dm b/code/modules/unit_tests/projectiles.dm index 4950be10c1a6..e93d20910af0 100644 --- a/code/modules/unit_tests/projectiles.dm +++ b/code/modules/unit_tests/projectiles.dm @@ -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()].") diff --git a/icons/obj/guns/safety.dmi b/icons/obj/guns/safety.dmi new file mode 100644 index 000000000000..072a483fa795 Binary files /dev/null and b/icons/obj/guns/safety.dmi differ