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