diff --git a/code/__DEFINES/dcs/signals/atom/signals_gun.dm b/code/__DEFINES/dcs/signals/atom/signals_gun.dm
new file mode 100644
index 0000000000..51b8c25fce
--- /dev/null
+++ b/code/__DEFINES/dcs/signals/atom/signals_gun.dm
@@ -0,0 +1,31 @@
+#define COMSIG_GUN_FIRE "gun_fire"
+#define COMSIG_GUN_STOP_FIRE "gun_stop_fire"
+#define COMSIG_GUN_FIRE_MODE_TOGGLE "gun_fire_mode_toggle"
+#define COMSIG_GUN_AUTOFIREDELAY_MODIFIED "gun_autofiredelay_modified"
+#define COMSIG_GUN_BURST_SHOTS_TO_FIRE_MODIFIED "gun_burst_shots_to_fire_modified"
+#define COMSIG_GUN_BURST_SHOT_DELAY_MODIFIED "gun_burst_shot_delay_modified"
+
+#define COMSIG_GUN_VULTURE_FIRED_ONEHAND "gun_vulture_fired_onehand"
+#define COMSIG_VULTURE_SCOPE_MOVED "vulture_scope_moved"
+#define COMSIG_VULTURE_SCOPE_SCOPED "vulture_scope_scoped"
+#define COMSIG_VULTURE_SCOPE_UNSCOPED "vulture_scope_unscoped"
+
+/// from /obj/item/weapon/gun/proc/recalculate_attachment_bonuses() : ()
+#define COMSIG_GUN_RECALCULATE_ATTACHMENT_BONUSES "gun_recalculate_attachment_bonuses"
+
+/// from /obj/item/weapon/gun/proc/load_into_chamber() : ()
+#define COMSIG_GUN_INTERRUPT_FIRE "gun_interrupt_fire"
+
+//Signals for automatic fire at component
+#define COMSIG_AUTOMATIC_SHOOTER_START_SHOOTING_AT "start_shooting_at"
+#define COMSIG_AUTOMATIC_SHOOTER_STOP_SHOOTING_AT "stop_shooting_at"
+#define COMSIG_AUTOMATIC_SHOOTER_SHOOT "shoot"
+
+//Signals for gun auto fire component
+#define COMSIG_GET_BURST_FIRE "get_burst_fire"
+ #define BURST_FIRING (1<<0)
+
+/// Called before a gun fires a projectile, note NOT point blanks, /obj/item/weapon/gun/proc/handle_fire()
+#define COMSIG_GUN_BEFORE_FIRE "gun_before_fire"
+ #define COMPONENT_CANCEL_GUN_BEFORE_FIRE (1<<0) //continue full-auto/burst attempts
+ #define COMPONENT_HARD_CANCEL_GUN_BEFORE_FIRE (1<<1) //hard stop firing
diff --git a/code/__DEFINES/dcs/signals/atom/signals_item.dm b/code/__DEFINES/dcs/signals/atom/signals_item.dm
index 6c31b77f76..9c2f3b92ba 100644
--- a/code/__DEFINES/dcs/signals/atom/signals_item.dm
+++ b/code/__DEFINES/dcs/signals/atom/signals_item.dm
@@ -38,30 +38,3 @@
#define COMSIG_ITEM_ZOOM "item_zoom"
/// from /obj/item/proc/unzoom() : (mob/user)
#define COMSIG_ITEM_UNZOOM "item_unzoom"
-
-//Signals for automatic fire at component
-#define COMSIG_AUTOMATIC_SHOOTER_START_SHOOTING_AT "start_shooting_at"
-#define COMSIG_AUTOMATIC_SHOOTER_STOP_SHOOTING_AT "stop_shooting_at"
-#define COMSIG_AUTOMATIC_SHOOTER_SHOOT "shoot"
-
-//Signals for gun auto fire component
-#define COMSIG_GET_BURST_FIRE "get_burst_fire"
- #define BURST_FIRING (1<<0)
-
-#define COMSIG_GUN_FIRE "gun_fire"
-#define COMSIG_GUN_STOP_FIRE "gun_stop_fire"
-#define COMSIG_GUN_FIRE_MODE_TOGGLE "gun_fire_mode_toggle"
-#define COMSIG_GUN_AUTOFIREDELAY_MODIFIED "gun_autofiredelay_modified"
-#define COMSIG_GUN_BURST_SHOTS_TO_FIRE_MODIFIED "gun_burst_shots_to_fire_modified"
-#define COMSIG_GUN_BURST_SHOT_DELAY_MODIFIED "gun_burst_shot_delay_modified"
-
-#define COMSIG_GUN_VULTURE_FIRED_ONEHAND "gun_vulture_fired_onehand"
-#define COMSIG_VULTURE_SCOPE_MOVED "vulture_scope_moved"
-#define COMSIG_VULTURE_SCOPE_SCOPED "vulture_scope_scoped"
-#define COMSIG_VULTURE_SCOPE_UNSCOPED "vulture_scope_unscoped"
-
-/// from /obj/item/weapon/gun/proc/recalculate_attachment_bonuses() : ()
-#define COMSIG_GUN_RECALCULATE_ATTACHMENT_BONUSES "gun_recalculate_attachment_bonuses"
-
-/// from /obj/item/weapon/gun/proc/load_into_chamber() : ()
-#define COMSIG_GUN_INTERRUPT_FIRE "gun_interrupt_fire"
diff --git a/code/modules/projectiles/ammo_datums.dm b/code/modules/projectiles/ammo_datums.dm
index 783b982f10..a6f60281e8 100644
--- a/code/modules/projectiles/ammo_datums.dm
+++ b/code/modules/projectiles/ammo_datums.dm
@@ -1773,7 +1773,7 @@
max_range = 12
accuracy = HIT_ACCURACY_TIER_4
- damage = 30
+ damage = 36
penetration = 0
/datum/ammo/bullet/smartgun/armor_piercing
@@ -1782,7 +1782,7 @@
accurate_range = 12
accuracy = HIT_ACCURACY_TIER_2
- damage = 20
+ damage = 24
penetration = ARMOR_PENETRATION_TIER_8
damage_armor_punch = 1
diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm
index e9ab9aecc3..35aed4c9c3 100644
--- a/code/modules/projectiles/gun.dm
+++ b/code/modules/projectiles/gun.dm
@@ -1191,6 +1191,17 @@ and you're good to go.
click_empty(user)
return NONE
+ var/before_fire_cancel = SEND_SIGNAL(src, COMSIG_GUN_BEFORE_FIRE, projectile_to_fire, target, user)
+
+ if(before_fire_cancel)
+ if(before_fire_cancel & COMPONENT_CANCEL_GUN_BEFORE_FIRE)
+ return TRUE
+
+ if(before_fire_cancel & COMPONENT_HARD_CANCEL_GUN_BEFORE_FIRE)
+ return NONE
+
+ play_firing_sounds(projectile_to_fire, user)
+
if(targloc != curloc)
simulate_recoil(dual_wield, user, target)
@@ -1410,6 +1421,8 @@ and you're good to go.
user.track_shot(initial(name))
apply_bullet_effects(projectile_to_fire, user, bullets_fired, dual_wield) //We add any damage effects that we need.
+ play_firing_sounds(projectile_to_fire, user)
+
SEND_SIGNAL(projectile_to_fire, COMSIG_BULLET_USER_EFFECTS, user)
SEND_SIGNAL(user, COMSIG_BULLET_DIRECT_HIT, attacked_mob)
simulate_recoil(1, user)
@@ -1610,12 +1623,6 @@ not all weapons use normal magazines etc. load_into_chamber() itself is designed
//This proc applies some bonus effects to the shot/makes the message when a bullet is actually fired.
/obj/item/weapon/gun/proc/apply_bullet_effects(obj/projectile/projectile_to_fire, mob/user, reflex = 0, dual_wield = 0)
- var/actual_sound = fire_sound
- if(isnull(fire_sound))
- actual_sound = pick(fire_sounds)
- if(projectile_to_fire.ammo && projectile_to_fire.ammo.sound_override)
- actual_sound = projectile_to_fire.ammo.sound_override
-
var/gun_accuracy_mult = accuracy_mult_unwielded
var/gun_scatter = scatter_unwielded
@@ -1666,26 +1673,39 @@ not all weapons use normal magazines etc. load_into_chamber() itself is designed
projectile_to_fire.shot_from = src
- if(user) //The gun only messages when fired by a user.
- projectile_to_fire.firer = user
- if(isliving(user)) projectile_to_fire.def_zone = user.zone_selected
- //Guns with low ammo have their firing sound
- var/firing_sndfreq = (current_mag && (current_mag.current_rounds / current_mag.max_rounds) > GUN_LOW_AMMO_PERCENTAGE) ? FALSE : SOUND_FREQ_HIGH
- //firing from an attachment
- if(active_attachable && active_attachable.flags_attach_features & ATTACH_PROJECTILE)
- if(active_attachable.fire_sound) //If we're firing from an attachment, use that noise instead.
- playsound(user, active_attachable.fire_sound, 50)
- else
- if(!(flags_gun_features & GUN_SILENCED))
- if (firing_sndfreq && fire_rattle)
- playsound(user, fire_rattle, firesound_volume, FALSE)//if the gun has a unique 'mag rattle' SFX play that instead of pitch shifting.
- else
- playsound(user, actual_sound, firesound_volume, firing_sndfreq)
- else
- playsound(user, actual_sound, 25, firing_sndfreq)
-
return 1
+/obj/item/weapon/gun/proc/play_firing_sounds(obj/projectile/projectile_to_fire, mob/user)
+ if(!user) //The gun only messages when fired by a user.
+ return
+
+ var/actual_sound = fire_sound
+ if(isnull(fire_sound))
+ actual_sound = pick(fire_sounds)
+
+ if(projectile_to_fire.ammo && projectile_to_fire.ammo.sound_override)
+ actual_sound = projectile_to_fire.ammo.sound_override
+
+ projectile_to_fire.firer = user
+ if(isliving(user))
+ projectile_to_fire.def_zone = user.zone_selected
+
+ //Guns with low ammo have their firing sound
+ var/firing_sndfreq = (current_mag && (current_mag.current_rounds / current_mag.max_rounds) > GUN_LOW_AMMO_PERCENTAGE) ? FALSE : SOUND_FREQ_HIGH
+
+ //firing from an attachment
+ if(active_attachable && active_attachable.flags_attach_features & ATTACH_PROJECTILE)
+ if(active_attachable.fire_sound) //If we're firing from an attachment, use that noise instead.
+ playsound(user, active_attachable.fire_sound, 50)
+ else
+ if(!(flags_gun_features & GUN_SILENCED))
+ if (firing_sndfreq && fire_rattle)
+ playsound(user, fire_rattle, firesound_volume, FALSE)//if the gun has a unique 'mag rattle' SFX play that instead of pitch shifting.
+ else
+ playsound(user, actual_sound, firesound_volume, firing_sndfreq)
+ else
+ playsound(user, actual_sound, 25, firing_sndfreq)
+
/obj/item/weapon/gun/proc/simulate_scatter(obj/projectile/projectile_to_fire, atom/target, turf/curloc, turf/targloc, mob/user, bullets_fired = 1)
var/fire_angle = Get_Angle(curloc, targloc)
var/total_scatter_angle = projectile_to_fire.scatter
diff --git a/code/modules/projectiles/guns/smartgun.dm b/code/modules/projectiles/guns/smartgun.dm
index 830882add8..8c6ff50b30 100644
--- a/code/modules/projectiles/guns/smartgun.dm
+++ b/code/modules/projectiles/guns/smartgun.dm
@@ -28,15 +28,15 @@
ammo = /datum/ammo/bullet/smartgun
actions_types = list(
/datum/action/item_action/smartgun/toggle_accuracy_improvement,
- ///datum/action/item_action/smartgun/toggle_ammo_type,
+ /datum/action/item_action/smartgun/toggle_ammo_type,
/datum/action/item_action/smartgun/toggle_auto_fire,
- ///datum/action/item_action/smartgun/toggle_lethal_mode,
+ /datum/action/item_action/smartgun/toggle_lethal_mode,
///datum/action/item_action/smartgun/toggle_motion_detector,
/datum/action/item_action/smartgun/toggle_recoil_compensation,
)
var/datum/ammo/ammo_primary = /datum/ammo/bullet/smartgun //Toggled ammo type
var/datum/ammo/ammo_secondary = /datum/ammo/bullet/smartgun/armor_piercing //Toggled ammo type
- var/iff_enabled = FALSE //Begin with the safety on.
+ var/iff_enabled = TRUE //Begin with the safety on.
var/secondary_toggled = 0 //which ammo we use
var/recoil_compensation = 0
var/accuracy_improvement = 0
@@ -51,6 +51,9 @@
var/recycletime = 120
var/cover_open = FALSE
+ /// Cooldown used for the delay on sound and to_chat() when IFF encounters a friendly target while trying to fire
+ COOLDOWN_DECLARE(iff_halt_cooldown)
+
unacidable = 1
indestructible = 1
@@ -106,6 +109,7 @@
LAZYADD(traits_to_give, list(
BULLET_TRAIT_ENTRY_ID("iff", /datum/element/bullet_trait_iff)
))
+ RegisterSignal(src, COMSIG_GUN_BEFORE_FIRE, PROC_REF(check_firing_lane))
/obj/item/weapon/gun/smartgun/get_examine_text(mob/user)
. = ..()
@@ -114,7 +118,7 @@
rounds = current_mag.current_rounds
var/message = "[rounds ? "Ammo counter shows [rounds] round\s remaining." : "It's dry."]"
. += message
- //. += "The restriction system is [iff_enabled ? "on" : "off"]."
+ . += "The restriction system is [iff_enabled ? "on" : "off"]."
if(battery && get_dist(user, src) <= 1)
. += "A small gauge on [battery] reads: Power: [battery.power_cell.charge] / [battery.power_cell.maxcharge]."
@@ -325,6 +329,47 @@
to_chat(H, SPAN_WARNING("You can't fire \the [src] with the feed cover open! (alt-click to close)"))
return FALSE
+#define SMARTGUN_IFF_RANGE_CHECK 7
+#define SMARTGUN_IFF_HALT_COOLDOWN (0.5 SECONDS)
+
+/obj/item/weapon/gun/smartgun/proc/check_firing_lane(obj/item/weapon/gun/smartgun/firing_weapon, obj/projectile/projectile_to_fire, atom/target, mob/living/user)
+ SIGNAL_HANDLER
+
+ var/angle = get_angle(user, target)
+
+ var/range_to_check = SMARTGUN_IFF_RANGE_CHECK
+
+ var/extended_target_turf = get_angle_target_turf(user, angle, range_to_check)
+
+ var/turf/current_turf = get_turf(user)
+
+ if(!current_turf || !extended_target_turf)
+ return COMPONENT_CANCEL_GUN_BEFORE_FIRE
+
+ var/list/checked_turfs = getline2(current_turf, extended_target_turf)
+
+ checked_turfs -= current_turf
+
+ for(var/turf/checked_turf as anything in checked_turfs)
+
+ //Wall, should block the bullet so we're good to stop checking.
+ if(istype(checked_turf, /turf/closed))
+ return
+
+ for(var/mob/living/checked_living in checked_turf)
+ if(!checked_living.lying && checked_living.get_target_lock(user.faction_group))
+ if(COOLDOWN_FINISHED(src, iff_halt_cooldown))
+ playsound_client(user.client, 'sound/weapons/smartgun_fail.ogg', src, 25)
+ to_chat(user, SPAN_WARNING("[src] halts firing as an IFF marked target crosses your field of fire!"))
+ COOLDOWN_START(src, iff_halt_cooldown, SMARTGUN_IFF_HALT_COOLDOWN)
+
+ return COMPONENT_CANCEL_GUN_BEFORE_FIRE
+
+ return //if we have a target we *can* hit and find it before any IFF targets we want to fire
+
+#undef SMARTGUN_IFF_RANGE_CHECK
+#undef SMARTGUN_IFF_HALT_COOLDOWN
+
/obj/item/weapon/gun/smartgun/unique_action(mob/user)
if(isobserver(usr) || isxeno(usr))
return
@@ -353,10 +398,12 @@
secondary_toggled = FALSE
if(iff_enabled)
add_bullet_trait(BULLET_TRAIT_ENTRY_ID("iff", /datum/element/bullet_trait_iff))
+ RegisterSignal(src, COMSIG_GUN_BEFORE_FIRE, PROC_REF(check_firing_lane))
drain += 10
MD.iff_signal = initial(MD.iff_signal)
if(!iff_enabled)
remove_bullet_trait("iff")
+ UnregisterSignal(src, COMSIG_GUN_BEFORE_FIRE)
drain -= 10
MD.iff_signal = null
@@ -654,8 +701,8 @@
. += SPAN_NOTICE("It is registered to [linked_human] but has its fire restrictions unlocked.")
else
. += SPAN_NOTICE("It's unregistered. Pick it up to register yourself as its owner.")
-// if(!iff_enabled)
-// . += SPAN_WARNING("Its IFF restrictions are disabled.")
+ if(!iff_enabled)
+ . += SPAN_WARNING("Its IFF restrictions are disabled.")
/obj/item/weapon/gun/smartgun/co/proc/remove_idlock()
SIGNAL_HANDLER
diff --git a/colonialmarines.dme b/colonialmarines.dme
index 890a339b19..87b0abd4db 100644
--- a/colonialmarines.dme
+++ b/colonialmarines.dme
@@ -123,6 +123,7 @@ s// DM Environment file for colonialmarines.dme.
#include "code\__DEFINES\dcs\signals\signals_global.dm"
#include "code\__DEFINES\dcs\signals\signals_subsystem.dm"
#include "code\__DEFINES\dcs\signals\atom\signals_atom.dm"
+#include "code\__DEFINES\dcs\signals\atom\signals_gun.dm"
#include "code\__DEFINES\dcs\signals\atom\signals_item.dm"
#include "code\__DEFINES\dcs\signals\atom\signals_movable.dm"
#include "code\__DEFINES\dcs\signals\atom\signals_obj.dm"
diff --git a/sound/weapons/smartgun_fail.ogg b/sound/weapons/smartgun_fail.ogg
new file mode 100644
index 0000000000..2d33cf7d13
Binary files /dev/null and b/sound/weapons/smartgun_fail.ogg differ